diff options
| author | Barry Warsaw | 2008-08-12 23:33:48 -0400 |
|---|---|---|
| committer | Barry Warsaw | 2008-08-12 23:33:48 -0400 |
| commit | 1822c649436477bbfc81ab036933529abe83bbf2 (patch) | |
| tree | fb829429cebc8c4d2129788ce7e8f52608bac5bc /mailman/commands | |
| parent | b42f3204f7223f3ce9ae306dcb2cec10853eca8d (diff) | |
| parent | ae24685f77661f19ee0357e9328737b6a3251596 (diff) | |
| download | mailman-1822c649436477bbfc81ab036933529abe83bbf2.tar.gz mailman-1822c649436477bbfc81ab036933529abe83bbf2.tar.zst mailman-1822c649436477bbfc81ab036933529abe83bbf2.zip | |
Merge in command refactoring branch.
Begin to flesh out the tests for the join command.
Refactor out notifications from the add_member() function.
Diffstat (limited to 'mailman/commands')
| -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 | 60 | ||||
| -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 | 192 |
9 files changed, 352 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..75a8ac8ea --- /dev/null +++ b/mailman/commands/docs/join.txt @@ -0,0 +1,60 @@ +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'test@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> + + 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..1cbf394e0 --- /dev/null +++ b/mailman/commands/join.py @@ -0,0 +1,192 @@ +# 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 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) + + + +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_mmode = self._parse_arguments(arguments) + if address is None: + realname, 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 + password = MakeRandomPassword() + try: + confirm_add_member(mlist, address, realname, password, + delivery_mode, mlist.preferred_language) + except XXX: + pass + print >> results, self.name, address, \ + (_('digest delivery') if digest else _('regular delivery')) + 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 + +def ignore(): + # 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.')) + + + +class Subscribe(Join): + """The email 'subscribe' command (an alias for 'join').""" + + name = 'subscribe' |
