summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBarry Warsaw2009-12-12 17:03:30 -0500
committerBarry Warsaw2009-12-12 17:03:30 -0500
commit725ebe36e2548c5da711087640bd423181411241 (patch)
tree8aae553f5b05d3889511dcbce8dd92f3fa7c469e /src
parent3c4c8f9c383b6e92e0a9a970079b296a4ac30e88 (diff)
downloadmailman-725ebe36e2548c5da711087640bd423181411241.tar.gz
mailman-725ebe36e2548c5da711087640bd423181411241.tar.zst
mailman-725ebe36e2548c5da711087640bd423181411241.zip
Diffstat (limited to 'src')
-rw-r--r--src/mailman/app/lifecycle.py2
-rw-r--r--src/mailman/app/registrar.py27
-rw-r--r--src/mailman/commands/docs/membership.txt130
-rw-r--r--src/mailman/commands/eml_confirm.py3
-rw-r--r--src/mailman/commands/eml_membership.py3
-rw-r--r--src/mailman/config/configure.zcml11
-rw-r--r--src/mailman/docs/domains.txt8
-rw-r--r--src/mailman/docs/registration.txt79
-rw-r--r--src/mailman/interfaces/domain.py9
-rw-r--r--src/mailman/model/domain.py4
-rw-r--r--src/mailman/queue/docs/command.txt3
11 files changed, 142 insertions, 137 deletions
diff --git a/src/mailman/app/lifecycle.py b/src/mailman/app/lifecycle.py
index 971769e05..f3f01779a 100644
--- a/src/mailman/app/lifecycle.py
+++ b/src/mailman/app/lifecycle.py
@@ -66,8 +66,6 @@ def create_list(fqdn_listname, owners=None):
for owner_address in owners:
addr = user_manager.get_address(owner_address)
if addr is None:
- # XXX Make this use an IRegistrar instead, but that requires
- # sussing out the IDomain stuff. For now, fake it.
user = user_manager.create_user(owner_address)
addr = list(user.addresses)[0]
addr.subscribe(mlist, MemberRole.owner)
diff --git a/src/mailman/app/registrar.py b/src/mailman/app/registrar.py
index 5cf9cc8a0..1894521cb 100644
--- a/src/mailman/app/registrar.py
+++ b/src/mailman/app/registrar.py
@@ -35,7 +35,6 @@ from mailman.config import config
from mailman.core.i18n import _
from mailman.email.message import UserNotification
from mailman.email.validate import validate
-from mailman.interfaces.domain import IDomain
from mailman.interfaces.listmanager import IListManager
from mailman.interfaces.member import MemberRole
from mailman.interfaces.pending import IPendable, IPendings
@@ -51,10 +50,9 @@ class PendableRegistration(dict):
class Registrar:
- implements(IRegistrar)
+ """Handle registrations and confirmations for subscriptions."""
- def __init__(self, context):
- self._context = context
+ implements(IRegistrar)
def register(self, mlist, address, real_name=None):
"""See `IUserRegistrar`."""
@@ -68,17 +66,18 @@ class Registrar:
real_name=real_name)
pendable['list_name'] = mlist.fqdn_listname
token = getUtility(IPendings).add(pendable)
- # Set up some local variables for translation interpolation.
- domain = IDomain(self._context)
- domain_name = _(domain.email_host)
- contact_address = domain.contact_address
- confirm_url = domain.confirm_url(token)
- confirm_address = domain.confirm_address(token)
- email_address = address
- # Calculate the message's Subject header. XXX Have to deal with
- # translating this subject header properly. XXX Must deal with
- # VERP_CONFIRMATIONS as well.
+ # There are three ways for a user to confirm their subscription. They
+ # can reply to the original message and let the VERP'd return address
+ # encode the token, they can reply to the robot and keep the token in
+ # the Subject header, or they can click on the URL in the body of the
+ # message and confirm through the web.
subject = 'confirm ' + token
+ confirm_address = mlist.confirm_address(token)
+ confirm_url = mlist.domain.confirm_url(token)
+ # For i18n interpolation.
+ email_address = address
+ domain_name = mlist.domain.email_host
+ contact_address = mlist.domain.contact_address
# Send a verification email to the address.
text = _(resource_string('mailman.templates.en', 'verify.txt'))
msg = UserNotification(address, confirm_address, subject, text)
diff --git a/src/mailman/commands/docs/membership.txt b/src/mailman/commands/docs/membership.txt
index b91dd17f1..a0af15ba7 100644
--- a/src/mailman/commands/docs/membership.txt
+++ b/src/mailman/commands/docs/membership.txt
@@ -1,14 +1,23 @@
-==================
-The 'join' command
-==================
+============================
+Membership changes via email
+============================
+
+Membership changes such as joining and leaving a mailing list, can be effected
+via the email interface. The Mailman email commands 'join', 'leave', and
+'confirm' are used.
+
+
+Joining a mailing list
+======================
The mail command 'join' subscribes an email address to the mailing list.
'subscribe' is an alias for 'join'.
- >>> command = config.commands['join']
- >>> print command.name
+ >>> from mailman.commands.eml_membership import Join
+ >>> join = Join()
+ >>> print join.name
join
- >>> print command.description
+ >>> print join.description
Join this mailing list. You will be asked to confirm your subscription
request and you may be issued a provisional password.
<BLANKLINE>
@@ -19,22 +28,23 @@ The mail command 'join' subscribes an email address to the mailing list.
<BLANKLINE>
join address=myotheraddress@example.com
<BLANKLINE>
- >>> print command.argument_description
+ >>> print join.argument_description
[digest=<yes|no>] [address=<address>]
No address to join
-==================
+------------------
- >>> from mailman.email.message import Message
- >>> from mailman.queue.command import Results
>>> mlist = create_list('alpha@example.com')
When no address argument is given, the message's From address will be used.
If that's missing though, then an error is returned.
+ >>> from mailman.queue.command import Results
>>> results = Results()
- >>> print command.process(mlist, Message(), {}, (), results)
+
+ >>> from mailman.email.message import Message
+ >>> print join.process(mlist, Message(), {}, (), results)
ContinueProcessing.no
>>> print unicode(results)
The results of your email command are provided below.
@@ -44,7 +54,8 @@ If that's missing though, then an error is returned.
The 'subscribe' command is an alias.
- >>> subscribe = config.commands['subscribe']
+ >>> from mailman.commands.eml_membership import Subscribe
+ >>> subscribe = Subscribe()
>>> print subscribe.name
subscribe
>>> results = Results()
@@ -58,7 +69,7 @@ The 'subscribe' command is an alias.
Joining the sender
-==================
+------------------
When the message has a From field, that address will be subscribed.
@@ -67,7 +78,7 @@ When the message has a From field, that address will be subscribed.
...
... """)
>>> results = Results()
- >>> print command.process(mlist, msg, {}, (), results)
+ >>> print join.process(mlist, msg, {}, (), results)
ContinueProcessing.yes
>>> print unicode(results)
The results of your email command are provided below.
@@ -86,13 +97,15 @@ first.
Mailman has sent her the confirmation message.
- >>> virginq = config.switchboards['virgin']
- >>> qmsg, qdata = virginq.dequeue(virginq.files[0])
- >>> print qmsg.as_string()
+ >>> from mailman.testing.helpers import get_queue_messages
+ >>> items = get_queue_messages('virgin')
+ >>> len(items)
+ 1
+ >>> print items[0].msg.as_string()
MIME-Version: 1.0
...
Subject: confirm ...
- From: confirm-...@example.com
+ From: alpha-confirm+...@example.com
To: anne@example.com
...
<BLANKLINE>
@@ -121,12 +134,27 @@ Mailman has sent her the confirmation message.
Once Anne confirms her registration, she will be made a member of the mailing
list.
- >>> token = str(qmsg['subject']).split()[1].strip()
- >>> from mailman.interfaces.domain import IDomainManager
- >>> from mailman.interfaces.registrar import IRegistrar
- >>> registrar = IRegistrar(getUtility(IDomainManager)['example.com'])
- >>> registrar.confirm(token)
- True
+ >>> def extract_token(message):
+ ... return str(message['subject']).split()[1].strip()
+ >>> token = extract_token(items[0].msg)
+
+ >>> from mailman.commands.eml_confirm import Confirm
+ >>> confirm = Confirm()
+ >>> msg = message_from_string("""\
+ ... To: alpha-confirm+{token}@example.com
+ ... From: anne@example.com
+ ... Subject: Re: confirm {token}
+ ...
+ ... """.format(token=token))
+
+ >>> results = Results()
+ >>> print confirm.process(mlist, msg, {}, (token,), results)
+ ContinueProcessing.yes
+ >>> print unicode(results)
+ The results of your email command are provided below.
+ <BLANKLINE>
+ Confirmed
+ <BLANKLINE>
>>> user = user_manager.get_user('anne@example.com')
>>> print user.real_name
@@ -142,14 +170,14 @@ Anne is also now a member of the mailing list.
Joining a second list
-=====================
+---------------------
>>> mlist_2 = create_list('baker@example.com')
>>> msg = message_from_string("""\
... From: Anne Person <anne@example.com>
...
... """)
- >>> print command.process(mlist_2, msg, {}, (), Results())
+ >>> print join.process(mlist_2, msg, {}, (), Results())
ContinueProcessing.yes
Anne of course, is still registered.
@@ -164,10 +192,25 @@ But she is not a member of the mailing list.
One Anne confirms this subscription, she becomes a member of the mailing list.
- >>> qmsg, qdata = virginq.dequeue(virginq.files[0])
- >>> token = str(qmsg['subject']).split()[1].strip()
- >>> registrar.confirm(token)
- True
+ >>> items = get_queue_messages('virgin')
+ >>> len(items)
+ 1
+ >>> token = extract_token(items[0].msg)
+ >>> msg = message_from_string("""\
+ ... To: baker-confirm+{token}@example.com
+ ... From: anne@example.com
+ ... Subject: Re: confirm {token}
+ ...
+ ... """.format(token=token))
+
+ >>> results = Results()
+ >>> print confirm.process(mlist_2, msg, {}, (token,), results)
+ ContinueProcessing.yes
+ >>> print unicode(results)
+ The results of your email command are provided below.
+ <BLANKLINE>
+ Confirmed
+ <BLANKLINE>
>>> print mlist_2.members.get_member('anne@example.com')
<Member: Anne Person <anne@example.com>
@@ -180,10 +223,11 @@ Leaving a mailing list
The mail command 'leave' unsubscribes an email address from the mailing list.
'unsubscribe' is an alias for 'leave'.
- >>> command = config.commands['leave']
- >>> print command.name
+ >>> from mailman.commands.eml_membership import Leave
+ >>> leave = Leave()
+ >>> print leave.name
leave
- >>> print command.description
+ >>> print leave.description
Leave this mailing list. You will be asked to confirm your request.
Anne is a member of the baker@example.com mailing list, when she decides to
@@ -191,7 +235,7 @@ leave it. She sends a message to the -leave address for the list and is sent
a confirmation message for her request.
>>> results = Results()
- >>> print command.process(mlist_2, msg, {}, (), results)
+ >>> print leave.process(mlist_2, msg, {}, (), results)
ContinueProcessing.yes
>>> print unicode(results)
The results of your email command are provided below.
@@ -225,7 +269,7 @@ will do.
Since Anne's alternative address has not yet been verified, it can't be used
to unsubscribe Anne from the alpha mailing list.
- >>> print command.process(mlist, msg, {}, (), results)
+ >>> print leave.process(mlist, msg, {}, (), results)
ContinueProcessing.no
>>> print unicode(results)
@@ -245,7 +289,7 @@ unsubscribe her from the list.
>>> address.verified_on = datetime.now()
>>> results = Results()
- >>> print command.process(mlist, msg, {}, (), results)
+ >>> print leave.process(mlist, msg, {}, (), results)
ContinueProcessing.yes
>>> print unicode(results)
@@ -268,21 +312,18 @@ Bart wants to join the alpha list, so he sends his subscription request.
...
... """)
- >>> command = config.commands['join']
- >>> print command.process(mlist, msg, {}, (), Results())
+ >>> print join.process(mlist, msg, {}, (), Results())
ContinueProcessing.yes
There are two messages in the virgin queue, one of which is the confirmation
message.
- >>> from mailman.testing.helpers import get_queue_messages
>>> for item in get_queue_messages('virgin'):
- ... subject = str(item.msg['subject'])
- ... if subject.startswith('confirm'):
+ ... if str(item.msg['subject']).startswith('confirm'):
... break
... else:
... raise AssertionError('No confirmation message')
- >>> token = subject.split()[1].strip()
+ >>> token = extract_token(item.msg)
Bart is still not a user.
@@ -295,14 +336,13 @@ a user of the system.
>>> msg = message_from_string("""\
... From: Bart Person <bart@example.com>
- ... To: alpha-confirm@example.com
+ ... To: alpha-confirm+{token}@example.com
... Subject: Re: confirm {token}
...
... """.format(token=token))
- >>> command = config.commands['confirm']
>>> results = Results()
- >>> print command.process(mlist, msg, {}, (token,), results)
+ >>> print confirm.process(mlist, msg, {}, (token,), results)
ContinueProcessing.yes
>>> print unicode(results)
diff --git a/src/mailman/commands/eml_confirm.py b/src/mailman/commands/eml_confirm.py
index 510033098..5232512b3 100644
--- a/src/mailman/commands/eml_confirm.py
+++ b/src/mailman/commands/eml_confirm.py
@@ -25,6 +25,7 @@ __all__ = [
]
+from zope.component import getUtility
from zope.interface import implements
from mailman.core.i18n import _
@@ -48,7 +49,7 @@ class Confirm:
if len(arguments) == 0:
print >> results, _('No confirmation token found')
return ContinueProcessing.no
- succeeded = IRegistrar(mlist.domain).confirm(arguments[0])
+ succeeded = getUtility(IRegistrar).confirm(arguments[0])
if succeeded:
print >> results, _('Confirmed')
return ContinueProcessing.yes
diff --git a/src/mailman/commands/eml_membership.py b/src/mailman/commands/eml_membership.py
index 2328e1a41..8b6c576f6 100644
--- a/src/mailman/commands/eml_membership.py
+++ b/src/mailman/commands/eml_membership.py
@@ -74,8 +74,7 @@ example:
print >> results, _(
'$self.name: No valid address found to subscribe')
return ContinueProcessing.no
- registrar = IRegistrar(mlist.domain)
- registrar.register(mlist, address, real_name)
+ getUtility(IRegistrar).register(mlist, address, real_name)
person = formataddr((real_name, address))
print >> results, _('Confirmation email sent to $person')
return ContinueProcessing.yes
diff --git a/src/mailman/config/configure.zcml b/src/mailman/config/configure.zcml
index 378feebf9..5e8de8527 100644
--- a/src/mailman/config/configure.zcml
+++ b/src/mailman/config/configure.zcml
@@ -5,12 +5,6 @@
<include package="mailman.rest" file="configure.zcml"/>
<adapter
- for="mailman.interfaces.domain.IDomain"
- provides="mailman.interfaces.registrar.IRegistrar"
- factory="mailman.app.registrar.Registrar"
- />
-
- <adapter
for="mailman.interfaces.mailinglist.IMailingList"
provides="mailman.interfaces.autorespond.IAutoResponseSet"
factory="mailman.model.autorespond.AutoResponseSet"
@@ -57,4 +51,9 @@
provides="mailman.interfaces.requests.IRequests"
/>
+ <utility
+ provides="mailman.interfaces.registrar.IRegistrar"
+ factory="mailman.app.registrar.Registrar"
+ />
+
</configure>
diff --git a/src/mailman/docs/domains.txt b/src/mailman/docs/domains.txt
index 3d1de7083..5673e6ee9 100644
--- a/src/mailman/docs/domains.txt
+++ b/src/mailman/docs/domains.txt
@@ -111,13 +111,9 @@ Non-existent domains cannot be removed.
Confirmation tokens
===================
-Confirmation tokens can be added to either the email confirmation address...
+Confirmation tokens can be added to the domain's url to generate the URL to a
+page users can use to confirm their subscriptions.
>>> domain = manager['example.net']
- >>> print domain.confirm_address('xyz')
- confirm-xyz@example.net
-
-...or the confirmation url.
-
>>> print domain.confirm_url('abc')
http://lists.example.net/confirm/abc
diff --git a/src/mailman/docs/registration.txt b/src/mailman/docs/registration.txt
index bf5329ec9..8f473403e 100644
--- a/src/mailman/docs/registration.txt
+++ b/src/mailman/docs/registration.txt
@@ -7,24 +7,14 @@ The only thing they must supply is an email address, although there is
additional information they may supply. All registered email addresses must
be verified before Mailman will send them any list traffic.
- >>> from mailman.app.registrar import Registrar
- >>> from mailman.interfaces.registrar import IRegistrar
-
The IUserManager manages users, but it does so at a fairly low level.
Specifically, it does not handle verifications, email address syntax validity
checks, etc. The IRegistrar is the interface to the object handling all this
stuff.
- >>> from mailman.interfaces.domain import IDomainManager
+ >>> from mailman.interfaces.registrar import IRegistrar
>>> from zope.component import getUtility
- >>> domain = getUtility(IDomainManager)['example.com']
-
-Get a registrar by adapting a domain.
-
- >>> from zope.interface.verify import verifyObject
- >>> registrar = IRegistrar(domain)
- >>> verifyObject(IRegistrar, registrar)
- True
+ >>> registrar = getUtility(IRegistrar)
Here is a helper function to check the token strings.
@@ -48,7 +38,7 @@ Invalid email addresses
Addresses are registered within the context of a mailing list, mostly so that
confirmation emails can come from some place. You also need the email
-address.
+address of the user who is registering.
>>> mlist = create_list('alpha@example.com')
@@ -107,7 +97,6 @@ There should be no records in the user manager for this address yet.
But this address is waiting for confirmation.
>>> from mailman.interfaces.pending import IPendings
- >>> from zope.component import getUtility
>>> pendingdb = getUtility(IPendings)
>>> dump_msgdata(pendingdb.confirm(token, expunge=False))
@@ -123,22 +112,17 @@ Verification by email
There is also a verification email sitting in the virgin queue now. This
message is sent to the user in order to verify the registered address.
- >>> switchboard = config.switchboards['virgin']
- >>> len(switchboard.files)
+ >>> from mailman.testing.helpers import get_queue_messages
+ >>> items = get_queue_messages('virgin')
+ >>> len(items)
1
- >>> filebase = switchboard.files[0]
- >>> qmsg, qdata = switchboard.dequeue(filebase)
- >>> switchboard.finish(filebase)
- >>> print qmsg.as_string()
+ >>> print items[0].msg.as_string()
MIME-Version: 1.0
- Content-Type: text/plain; charset="us-ascii"
- Content-Transfer-Encoding: 7bit
+ ...
Subject: confirm ...
- From: confirm-...@example.com
+ From: alpha-confirm+...@example.com
To: aperson@example.com
- Message-ID: <...>
- Date: ...
- Precedence: bulk
+ ...
<BLANKLINE>
Email Address Registration Confirmation
<BLANKLINE>
@@ -161,7 +145,7 @@ message is sent to the user in order to verify the registered address.
<BLANKLINE>
postmaster@example.com
<BLANKLINE>
- >>> dump_msgdata(qdata)
+ >>> dump_msgdata(items[0].msgdata)
_parsemsg : False
listname : alpha@example.com
nodecorate : True
@@ -179,7 +163,7 @@ appear in a URL in the body of the message.
The same token will appear in the From header.
- >>> qmsg['from'] == 'confirm-' + token + '@example.com'
+ >>> qmsg['from'] == 'alpha-confirm+' + token + '@example.com'
True
It will also appear in the Subject header.
@@ -187,9 +171,9 @@ It will also appear in the Subject header.
>>> qmsg['subject'] == 'confirm ' + token
True
-The user would then validate their just registered address by clicking on a
-url or responding to the message. Either way, the confirmation process
-extracts the token and uses that to confirm the pending registration.
+The user would then validate their registered address by clicking on a url or
+responding to the message. Either way, the confirmation process extracts the
+token and uses that to confirm the pending registration.
>>> registrar.confirm(token)
True
@@ -219,10 +203,10 @@ will work. The second one is ignored.
>>> token = registrar.register(mlist, 'bperson@example.com')
>>> check_token(token)
ok
- >>> filebase = switchboard.files[0]
- >>> qmsg, qdata = switchboard.dequeue(filebase)
- >>> switchboard.finish(filebase)
- >>> sent_token = extract_token(qmsg)
+ >>> items = get_queue_messages('virgin')
+ >>> len(items)
+ 1
+ >>> sent_token = extract_token(items[0].msg)
>>> token == sent_token
True
>>> registrar.confirm(token)
@@ -240,10 +224,11 @@ confirmation step is completed.
... mlist, 'cperson@example.com', 'Claire Person')
>>> print user_manager.get_user('cperson@example.com')
None
- >>> filebase = switchboard.files[0]
- >>> qmsg, qdata = switchboard.dequeue(filebase)
- >>> switchboard.finish(filebase)
- >>> registrar.confirm(token)
+ >>> items = get_queue_messages('virgin')
+ >>> len(items)
+ 1
+ >>> sent_token = extract_token(items[0].msg)
+ >>> registrar.confirm(sent_token)
True
>>> user_manager.get_user('cperson@example.com')
<User "Claire Person" at ...>
@@ -296,10 +281,12 @@ can be used.
>>> dperson.register('david.person@example.com', 'David Person')
<Address: David Person <david.person@example.com> [not verified] at ...>
>>> token = registrar.register(mlist, 'david.person@example.com')
- >>> filebase = switchboard.files[0]
- >>> qmsg, qdata = switchboard.dequeue(filebase)
- >>> switchboard.finish(filebase)
- >>> registrar.confirm(token)
+
+ >>> items = get_queue_messages('virgin')
+ >>> len(items)
+ 1
+ >>> sent_token = extract_token(items[0].msg)
+ >>> registrar.confirm(sent_token)
True
>>> user = user_manager.get_user('david.person@example.com')
>>> user is dperson
@@ -315,14 +302,14 @@ Corner cases
============
If you try to confirm a token that doesn't exist in the pending database, the
-confirm method will just return None.
+confirm method will just return False.
>>> registrar.confirm(bytes('no token'))
False
Likewise, if you try to confirm, through the IUserRegistrar interface, a token
-that doesn't match a registration even, you will get None. However, the
-pending even matched with that token will still be removed.
+that doesn't match a registration event, you will get None. However, the
+pending event matched with that token will still be removed.
>>> from mailman.interfaces.pending import IPendable
>>> from zope.interface import implements
diff --git a/src/mailman/interfaces/domain.py b/src/mailman/interfaces/domain.py
index 538cd0f74..a16d41466 100644
--- a/src/mailman/interfaces/domain.py
+++ b/src/mailman/interfaces/domain.py
@@ -80,15 +80,6 @@ class IDomain(Interface):
E.g. postmaster@example.com"""),
))
- def confirm_address(token=''):
- """The address used for various forms of email confirmation.
-
- :param token: The confirmation token to use in the email address.
- :type token: string
- :return: The email confirmation address.
- :rtype: string
- """
-
def confirm_url(token=''):
"""The url used for various forms of confirmation.
diff --git a/src/mailman/model/domain.py b/src/mailman/model/domain.py
index a30c9c581..a24b8a1bb 100644
--- a/src/mailman/model/domain.py
+++ b/src/mailman/model/domain.py
@@ -81,10 +81,6 @@ class Domain(Model):
# no netloc member; yes it does
return urlparse(self.base_url).netloc
- def confirm_address(self, token=''):
- """See `IDomain`."""
- return 'confirm-{0}@{1}'.format(token, self.email_host)
-
def confirm_url(self, token=''):
"""See `IDomain`."""
return urljoin(self.base_url, 'confirm/' + token)
diff --git a/src/mailman/queue/docs/command.txt b/src/mailman/queue/docs/command.txt
index c63c323b4..4ffb0323c 100644
--- a/src/mailman/queue/docs/command.txt
+++ b/src/mailman/queue/docs/command.txt
@@ -135,10 +135,9 @@ address, and the other is the results of his email command.
... return str(item.msg['subject'])
>>> messages = sorted(get_queue_messages('virgin'), key=sortkey)
- >>> from mailman.interfaces.domain import IDomainManager
>>> from mailman.interfaces.registrar import IRegistrar
>>> from zope.component import getUtility
- >>> registrar = IRegistrar(getUtility(IDomainManager)['example.com'])
+ >>> registrar = getUtility(IRegistrar)
>>> for item in messages:
... subject = item.msg['subject']
... print 'Subject:', subject