diff options
| author | Barry Warsaw | 2008-09-24 22:59:05 -0400 |
|---|---|---|
| committer | Barry Warsaw | 2008-09-24 22:59:05 -0400 |
| commit | 12e5731cb50716590c86bdd55a1dae442b8d256c (patch) | |
| tree | e63e9d6ffcf552472de3076c0739d4871400e586 /mailman/commands | |
| parent | 9be793725d86c2bbd0bb0791bce4aecd80838e48 (diff) | |
| parent | c129b48a91d11fd3fb69666b8109c3a9372b2f8f (diff) | |
| download | mailman-12e5731cb50716590c86bdd55a1dae442b8d256c.tar.gz mailman-12e5731cb50716590c86bdd55a1dae442b8d256c.tar.zst mailman-12e5731cb50716590c86bdd55a1dae442b8d256c.zip | |
thread merge
Diffstat (limited to '')
| -rw-r--r-- | mailman/commands/__init__.py | 2 | ||||
| -rw-r--r-- | mailman/commands/cmd_join.py | 20 | ||||
| -rw-r--r-- | mailman/commands/cmd_subscribe.py | 133 | ||||
| -rw-r--r-- | mailman/commands/docs/echo.txt | 31 | ||||
| -rw-r--r-- | mailman/commands/docs/end.txt | 38 | ||||
| -rw-r--r-- | mailman/commands/docs/join.txt | 172 | ||||
| -rw-r--r-- | mailman/commands/echo.py | 4 | ||||
| -rw-r--r-- | mailman/commands/end.py (renamed from mailman/commands/cmd_end.py) | 37 | ||||
| -rw-r--r-- | mailman/commands/join.py | 127 |
9 files changed, 399 insertions, 165 deletions
diff --git a/mailman/commands/__init__.py b/mailman/commands/__init__.py index 81035e44a..044ffd6a5 100644 --- a/mailman/commands/__init__.py +++ b/mailman/commands/__init__.py @@ -17,4 +17,6 @@ __all__ = [ 'echo', + 'end', + 'join', ] diff --git a/mailman/commands/cmd_join.py b/mailman/commands/cmd_join.py deleted file mode 100644 index 7a80cd72b..000000000 --- a/mailman/commands/cmd_join.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (C) 2002-2008 by the Free Software Foundation, Inc. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -"""The `join' command is synonymous with `subscribe'. -""" - -from mailman.Commands.cmd_subscribe import process diff --git a/mailman/commands/cmd_subscribe.py b/mailman/commands/cmd_subscribe.py deleted file mode 100644 index e1f7e6721..000000000 --- a/mailman/commands/cmd_subscribe.py +++ /dev/null @@ -1,133 +0,0 @@ -# Copyright (C) 2002-2008 by the Free Software Foundation, Inc. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -""" - subscribe [password] [digest|nodigest] [address=<address>] - Subscribe to this mailing list. Your password must be given to - unsubscribe or change your options, but if you omit the password, one - will be generated for you. You may be periodically reminded of your - password. - - The next argument may be either: `nodigest' or `digest' (no quotes!). - If you wish to subscribe an address other than the address you sent - this request from, you may specify `address=<address>' (no brackets - around the email address, and no quotes!) -""" - -from email.Utils import parseaddr -from email.Header import decode_header, make_header - -from mailman import Utils -from mailman import Errors -from mailman.UserDesc import UserDesc -from mailman.i18n import _ - -STOP = 1 - - - -def gethelp(mlist): - return _(__doc__) - - - -def process(res, args): - mlist = res.mlist - digest = None - password = None - address = None - realname = None - # Parse the args - argnum = 0 - for arg in args: - if arg.startswith('address='): - address = arg[8:] - elif argnum == 0: - password = arg - elif argnum == 1: - if arg.lower() not in ('digest', 'nodigest'): - res.results.append(_('Bad digest specifier: %(arg)s')) - return STOP - if arg.lower() == 'digest': - digest = 1 - else: - digest = 0 - else: - res.results.append(_('Usage:')) - res.results.append(gethelp(mlist)) - return STOP - argnum += 1 - # Fill in empty defaults - if digest is None: - digest = mlist.digest_is_default - if password is None: - password = Utils.MakeRandomPassword() - if address is None: - realname, address = parseaddr(res.msg['from']) - if not address: - # Fall back to the sender address - address = res.msg.get_sender() - if not address: - res.results.append(_('No valid address found to subscribe')) - return STOP - # Watch for encoded names - try: - h = make_header(decode_header(realname)) - # BAW: in Python 2.2, use just unicode(h) - realname = h.__unicode__() - except UnicodeError: - realname = u'' - # Coerce to byte string if uh contains only ascii - try: - realname = realname.encode('us-ascii') - except UnicodeError: - pass - # Create the UserDesc record and do a non-approved subscription - listowner = mlist.GetOwnerEmail() - userdesc = UserDesc(address, realname, password, digest) - remote = res.msg.get_sender() - try: - mlist.AddMember(userdesc, remote) - except Errors.MembershipIsBanned: - res.results.append(_("""\ -The email address you supplied is banned from this mailing list. -If you think this restriction is erroneous, please contact the list -owners at %(listowner)s.""")) - return STOP - except Errors.InvalidEmailAddress: - res.results.append(_("""\ -Mailman won't accept the given email address as a valid address.""")) - return STOP - except Errors.MMAlreadyAMember: - res.results.append(_('You are already subscribed!')) - return STOP - except Errors.MMCantDigestError: - res.results.append( - _('No one can subscribe to the digest of this list!')) - return STOP - except Errors.MMMustDigestError: - res.results.append(_('This list only supports digest subscriptions!')) - return STOP - except Errors.MMSubscribeNeedsConfirmation: - # We don't need to respond /and/ send a confirmation message. - res.respond = 0 - except Errors.MMNeedApproval: - res.results.append(_("""\ -Your subscription request has been forwarded to the list administrator -at %(listowner)s for review.""")) - else: - # Everything is a-ok - res.results.append(_('Subscription request succeeded.')) diff --git a/mailman/commands/docs/echo.txt b/mailman/commands/docs/echo.txt new file mode 100644 index 000000000..d2781d330 --- /dev/null +++ b/mailman/commands/docs/echo.txt @@ -0,0 +1,31 @@ +The 'echo' command +================== + +The mail command 'echo' simply replies with the original command and arguments +to the sender. + + >>> from mailman.configuration import config + >>> command = config.commands['echo'] + >>> command.name + 'echo' + >>> command.argument_description + '[args]' + >>> command.description + u'Echo an acknowledgement. Arguments are return unchanged.' + +The original message is ignored, but the results receive the echoed command. + + >>> from mailman.app.lifecycle import create_list + >>> mlist = create_list(u'test@example.com') + + >>> from mailman.queue.command import Results + >>> results = Results() + + >>> from mailman.Message import Message + >>> print command.process(mlist, Message(), {}, ('foo', 'bar'), results) + ContinueProcessing.yes + >>> print unicode(results) + The results of your email command are provided below. + <BLANKLINE> + echo foo bar + <BLANKLINE> diff --git a/mailman/commands/docs/end.txt b/mailman/commands/docs/end.txt new file mode 100644 index 000000000..bd632de48 --- /dev/null +++ b/mailman/commands/docs/end.txt @@ -0,0 +1,38 @@ +The 'end' command +================= + +The mail command processor recognized an 'end' command which tells it to stop +processing email messages. + + >>> from mailman.configuration import config + >>> command = config.commands['end'] + >>> command.name + 'end' + >>> command.description + u'Stop processing commands.' + +The 'end' command takes no arguments. + + >>> command.argument_description + '' + +The command itself is fairly simple; it just stops command processing, and the +message isn't even looked at. + + >>> from mailman.app.lifecycle import create_list + >>> mlist = create_list(u'test@example.com') + >>> from mailman.Message import Message + >>> print command.process(mlist, Message(), {}, (), None) + ContinueProcessing.no + +The 'stop' command is a synonym for 'end'. + + >>> command = config.commands['stop'] + >>> command.name + 'stop' + >>> command.description + u'Stop processing commands.' + >>> command.argument_description + '' + >>> print command.process(mlist, Message(), {}, (), None) + ContinueProcessing.no diff --git a/mailman/commands/docs/join.txt b/mailman/commands/docs/join.txt new file mode 100644 index 000000000..492297787 --- /dev/null +++ b/mailman/commands/docs/join.txt @@ -0,0 +1,172 @@ +The 'join' command +================== + +The mail command 'join' subscribes an email address to the mailing list. +'subscribe' is an alias for 'join'. + + >>> from mailman.configuration import config + >>> command = config.commands['join'] + >>> print command.name + join + >>> print command.description + Join this mailing list. You will be asked to confirm your subscription + request and you may be issued a provisional password. + <BLANKLINE> + By using the 'digest' option, you can specify whether you want digest + delivery or not. If not specified, the mailing list's default will be + used. You can also subscribe an alternative address by using the + 'address' option. For example: + <BLANKLINE> + join address=myotheraddress@example.com + <BLANKLINE> + >>> print command.argument_description + [digest=<yes|no>] [address=<address>] + + +No address to join +------------------ + + >>> from mailman.Message import Message + >>> from mailman.app.lifecycle import create_list + >>> from mailman.queue.command import Results + >>> mlist = create_list(u'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. + + >>> results = Results() + >>> print command.process(mlist, Message(), {}, (), results) + ContinueProcessing.no + >>> print unicode(results) + The results of your email command are provided below. + <BLANKLINE> + join: No valid address found to subscribe + <BLANKLINE> + +The 'subscribe' command is an alias. + + >>> subscribe = config.commands['subscribe'] + >>> print subscribe.name + subscribe + >>> results = Results() + >>> print subscribe.process(mlist, Message(), {}, (), results) + ContinueProcessing.no + >>> print unicode(results) + The results of your email command are provided below. + <BLANKLINE> + subscribe: No valid address found to subscribe + <BLANKLINE> + + +Joining the sender +------------------ + +When the message has a From field, that address will be subscribed. + + >>> msg = message_from_string("""\ + ... From: Anne Person <anne@example.com> + ... + ... """) + >>> results = Results() + >>> print command.process(mlist, msg, {}, (), results) + ContinueProcessing.yes + >>> print unicode(results) + The results of your email command are provided below. + <BLANKLINE> + Confirmation email sent to Anne Person <anne@example.com> + <BLANKLINE> + +Anne is not yet a member because she must confirm her subscription request +first. + + >>> print config.db.user_manager.get_user(u'anne@example.com') + None + +Mailman has sent her the confirmation message. + + >>> from mailman.queue import Switchboard + >>> virginq = Switchboard(config.VIRGINQUEUE_DIR) + >>> qmsg, qdata = virginq.dequeue(virginq.files[0]) + >>> print qmsg.as_string() + MIME-Version: 1.0 + ... + Subject: confirm ... + From: confirm-...@example.com + To: anne@example.com + ... + <BLANKLINE> + Email Address Registration Confirmation + <BLANKLINE> + Hello, this is the GNU Mailman server at example.com. + <BLANKLINE> + We have received a registration request for the email address + <BLANKLINE> + anne@example.com + <BLANKLINE> + Before you can start using GNU Mailman at this site, you must first + confirm that this is your email address. You can do this by replying to + this message, keeping the Subject header intact. Or you can visit this + web page + <BLANKLINE> + http://www.example.com/confirm/... + <BLANKLINE> + If you do not wish to register this email address simply disregard this + message. If you think you are being maliciously subscribed to the list, or + have any other questions, you may contact + <BLANKLINE> + postmaster@example.com + <BLANKLINE> + +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.registrar import IRegistrar + >>> registrar = IRegistrar(config.domains['example.com']) + >>> registrar.confirm(token) + True + + >>> user = config.db.user_manager.get_user(u'anne@example.com') + >>> print user.real_name + Anne Person + >>> list(user.addresses) + [<Address: Anne Person <anne@example.com> [verified] at ...>] + +Anne is also now a member of the mailing list. + + >>> mlist.members.get_member(u'anne@example.com') + <Member: Anne Person <anne@example.com> + on alpha@example.com as MemberRole.member> + + +Joining a second list +--------------------- + + >>> mlist_2 = create_list(u'baker@example.com') + >>> msg = message_from_string("""\ + ... From: Anne Person <anne@example.com> + ... + ... """) + >>> print command.process(mlist_2, msg, {}, (), Results()) + ContinueProcessing.yes + +Anne of course, is still registered. + + >>> print config.db.user_manager.get_user(u'anne@example.com') + <User "Anne Person" at ...> + +But she is not a member of the mailing list. + + >>> print mlist_2.members.get_member(u'anne@example.com') + None + +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 + + >>> print mlist_2.members.get_member(u'anne@example.com') + <Member: Anne Person <anne@example.com> + on baker@example.com as MemberRole.member> diff --git a/mailman/commands/echo.py b/mailman/commands/echo.py index d95e72aa1..547f6a9b2 100644 --- a/mailman/commands/echo.py +++ b/mailman/commands/echo.py @@ -25,7 +25,7 @@ __all__ = [ from zope.interface import implements from mailman.i18n import _ -from mailman.interfaces import IEmailCommand +from mailman.interfaces import ContinueProcessing, IEmailCommand SPACE = ' ' @@ -44,4 +44,4 @@ class Echo: def process(self, mlist, msg, msgdata, arguments, results): """See `IEmailCommand`.""" print >> results, 'echo', SPACE.join(arguments) - return True + return ContinueProcessing.yes diff --git a/mailman/commands/cmd_end.py b/mailman/commands/end.py index 81cb15a4a..6e76e1eb2 100644 --- a/mailman/commands/cmd_end.py +++ b/mailman/commands/end.py @@ -14,20 +14,37 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -""" - end - Stop processing commands. Use this if your mail program automatically - adds a signature file. -""" +"""The email commands 'end' and 'stop'.""" + +__metaclass__ = type +__all__ = [ + 'End', + 'Stop', + ] + + +from zope.interface import implements from mailman.i18n import _ +from mailman.interfaces import ContinueProcessing, IEmailCommand -def gethelp(mlist): - return _(__doc__) +class End: + """The email 'end' command.""" + implements(IEmailCommand) + name = 'end' + argument_description = '' + description = _('Stop processing commands.') - -def process(res, args): - return 1 # STOP + def process(self, mlist, msg, msgdata, arguments, results): + """See `IEmailCommand`.""" + # Ignore all arguments. + return ContinueProcessing.no + + +class Stop(End): + """The email 'stop' command (an alias for 'end').""" + + name = 'stop' diff --git a/mailman/commands/join.py b/mailman/commands/join.py new file mode 100644 index 000000000..639a74a99 --- /dev/null +++ b/mailman/commands/join.py @@ -0,0 +1,127 @@ +# Copyright (C) 2002-2008 by the Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +"""The email commands 'join' and 'subscribe'.""" + +__metaclass__ = type +__all__ = [ + 'Join', + 'Subscribe', + ] + + +from email.header import decode_header, make_header +from email.utils import formataddr, parseaddr +from zope.interface import implements + +from mailman.Utils import MakeRandomPassword +from mailman.configuration import config +from mailman.i18n import _ +from mailman.interfaces import ( + ContinueProcessing, DeliveryMode, IEmailCommand) +from mailman.interfaces.registrar import IRegistrar + + + +class Join: + """The email 'join' command.""" + implements(IEmailCommand) + + name = 'join' + argument_description = '[digest=<yes|no>] [address=<address>]' + description = _("""\ +Join this mailing list. You will be asked to confirm your subscription +request and you may be issued a provisional password. + +By using the 'digest' option, you can specify whether you want digest delivery +or not. If not specified, the mailing list's default will be used. You can +also subscribe an alternative address by using the 'address' option. For +example: + + join address=myotheraddress@example.com +""") + + def process(self, mlist, msg, msgdata, arguments, results): + """See `IEmailCommand`.""" + # Parse the arguments. + address, delivery_mode = self._parse_arguments(arguments) + if address is None: + real_name, address = parseaddr(msg['from']) + # Address could be None or the empty string. + if not address: + address = msg.get_sender() + if not address: + print >> results, _( + '$self.name: No valid address found to subscribe') + return ContinueProcessing.no + domain = config.domains[mlist.host_name] + registrar = IRegistrar(domain) + registrar.register(address, real_name, mlist) + person = formataddr((real_name, address)) + print >> results, _('Confirmation email sent to $person') + return ContinueProcessing.yes + + def _parse_arguments(self, arguments): + """Parse command arguments. + + :param arguments: The sequences of arguments as given to the + `process()` method. + :return: address, delivery_mode + """ + address = None + delivery_mode = None + for argument in arguments: + parts = argument.split('=', 1) + if parts[0].lower() == 'digest': + if digest is not None: + print >> results, self.name, \ + _('duplicate argument: $argument') + return ContinueProcessing.no + if len(parts) == 0: + # We treat just plain 'digest' as 'digest=yes'. We don't + # yet support the other types of digest delivery. + delivery_mode = DeliveryMode.mime_digests + else: + if parts[1].lower() == 'yes': + delivery_mode = DeliveryMode.mime_digests + elif parts[1].lower() == 'no': + delivery_mode = DeliveryMode.regular + else: + print >> results, self.name, \ + _('bad argument: $argument') + return ContinueProcessing.no + elif parts[0].lower() == 'address': + if address is not None: + print >> results, self.name, \ + _('duplicate argument $argument') + return ContinueProcessing.no + if len(parts) == 0: + print >> results, self.name, \ + _('missing argument value: $argument') + return ContinueProcessing.no + if len(parts) > 1: + print >> results, self.name, \ + _('too many argument values: $argument') + return ContinueProcessing.no + address = parts[1] + return address, delivery_mode + + + +class Subscribe(Join): + """The email 'subscribe' command (an alias for 'join').""" + + name = 'subscribe' |
