diff options
Diffstat (limited to 'src/mailman/commands')
| -rw-r--r-- | src/mailman/commands/cli_control.py | 2 | ||||
| -rw-r--r-- | src/mailman/commands/cli_withlist.py | 4 | ||||
| -rw-r--r-- | src/mailman/commands/docs/commands.rst | 8 | ||||
| -rw-r--r-- | src/mailman/commands/docs/membership.rst | 16 | ||||
| -rw-r--r-- | src/mailman/commands/eml_confirm.py | 6 | ||||
| -rw-r--r-- | src/mailman/commands/eml_membership.py | 39 | ||||
| -rw-r--r-- | src/mailman/commands/tests/test_confirm.py | 10 | ||||
| -rw-r--r-- | src/mailman/commands/tests/test_membership.py | 54 |
8 files changed, 110 insertions, 29 deletions
diff --git a/src/mailman/commands/cli_control.py b/src/mailman/commands/cli_control.py index af22c3b06..5ba0b5427 100644 --- a/src/mailman/commands/cli_control.py +++ b/src/mailman/commands/cli_control.py @@ -93,7 +93,7 @@ class Start: self.parser.error( _('A previous run of GNU Mailman did not exit ' 'cleanly. Try using --force.')) - def log(message): # noqa: E301 + def log(message): # noqa: E306 if not args.quiet: print(message) # Try to find the path to a valid, existing configuration file, and diff --git a/src/mailman/commands/cli_withlist.py b/src/mailman/commands/cli_withlist.py index 306fb846a..23250c1a5 100644 --- a/src/mailman/commands/cli_withlist.py +++ b/src/mailman/commands/cli_withlist.py @@ -49,13 +49,13 @@ def _start_ipython1(overrides, banner, *, debug=False): if debug: print_exc() return None - return InteractiveShellEmbed(banner1=banner, user_ns=overrides) + return InteractiveShellEmbed.instance(banner1=banner, user_ns=overrides) def _start_ipython4(overrides, banner, *, debug=False): try: from IPython.terminal.embed import InteractiveShellEmbed - shell = InteractiveShellEmbed() + shell = InteractiveShellEmbed.instance() except ImportError: if debug: print_exc() diff --git a/src/mailman/commands/docs/commands.rst b/src/mailman/commands/docs/commands.rst new file mode 100644 index 000000000..eb0c0fee6 --- /dev/null +++ b/src/mailman/commands/docs/commands.rst @@ -0,0 +1,8 @@ +======== +Commands +======== + +.. toctree:: + :glob: + + ./* diff --git a/src/mailman/commands/docs/membership.rst b/src/mailman/commands/docs/membership.rst index 49e80511d..18c473c5b 100644 --- a/src/mailman/commands/docs/membership.rst +++ b/src/mailman/commands/docs/membership.rst @@ -113,11 +113,11 @@ Mailman has sent her the confirmation message. <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. + 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 me... + keeping the Subject header intact. <BLANKLINE> - If you do not wish to register this email address simply disregard this + 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> @@ -217,9 +217,13 @@ list. ``unsubscribe`` is an alias for ``leave``. You may be asked to confirm your request. Anne is a member of the ``baker@example.com`` mailing list, when she decides -to leave it. She sends a message to the ``-leave`` address for the list and -is sent a confirmation message for her request. +to leave it. Because the mailing list allows for *open* unsubscriptions +(i.e. no confirmation is needed), when she sends a message to the ``-leave`` +address for the list, she is immediately removed. + >>> from mailman.interfaces.mailinglist import SubscriptionPolicy + >>> mlist_2.unsubscription_policy = SubscriptionPolicy.open + >>> mlist.unsubscription_policy = SubscriptionPolicy.open >>> results = Results() >>> print(leave.process(mlist_2, msg, {}, (), results)) ContinueProcessing.yes diff --git a/src/mailman/commands/eml_confirm.py b/src/mailman/commands/eml_confirm.py index 6a3e389bd..6787b0987 100644 --- a/src/mailman/commands/eml_confirm.py +++ b/src/mailman/commands/eml_confirm.py @@ -20,8 +20,7 @@ from mailman import public from mailman.core.i18n import _ from mailman.interfaces.command import ContinueProcessing, IEmailCommand -from mailman.interfaces.registrar import IRegistrar -from mailman.interfaces.subscriptions import TokenOwner +from mailman.interfaces.subscriptions import ISubscriptionManager, TokenOwner from zope.interface import implementer @@ -50,7 +49,8 @@ class Confirm: tokens.add(token) results.confirms = tokens try: - new_token, token_owner, member = IRegistrar(mlist).confirm(token) + new_token, token_owner, member = ISubscriptionManager( + mlist).confirm(token) if new_token is None: assert token_owner is TokenOwner.no_one, token_owner assert member is not None, member diff --git a/src/mailman/commands/eml_membership.py b/src/mailman/commands/eml_membership.py index 7d3704e14..e658e7b58 100644 --- a/src/mailman/commands/eml_membership.py +++ b/src/mailman/commands/eml_membership.py @@ -22,8 +22,8 @@ from mailman import public from mailman.core.i18n import _ from mailman.interfaces.command import ContinueProcessing, IEmailCommand from mailman.interfaces.member import DeliveryMode, MemberRole -from mailman.interfaces.registrar import IRegistrar -from mailman.interfaces.subscriptions import ISubscriptionService +from mailman.interfaces.subscriptions import ( + ISubscriptionManager, ISubscriptionService) from mailman.interfaces.usermanager import IUserManager from zope.component import getUtility from zope.interface import implementer @@ -31,7 +31,7 @@ from zope.interface import implementer def match_subscriber(email, display_name): # Return something matching the email which should be used as the - # subscriber by the IRegistrar interface. + # subscriber by the ISubscriptionManager interface. manager = getUtility(IUserManager) # Is there a user with a preferred address matching the email? user = manager.get_user(email) @@ -101,7 +101,7 @@ used. print(_('$person is already a member'), file=results) return ContinueProcessing.yes subscriber = match_subscriber(email, display_name) - IRegistrar(mlist).register(subscriber) + ISubscriptionManager(mlist).register(subscriber) print(_('Confirmation email sent to $person'), file=results) return ContinueProcessing.yes @@ -173,6 +173,7 @@ You may be asked to confirm your request.""") print(_('Invalid or unverified email address: $email'), file=results) return ContinueProcessing.no + already_left = msgdata.setdefault('leaves', set()) for user_address in user.addresses: # Only recognize verified addresses. if user_address.verified_on is None: @@ -181,14 +182,28 @@ You may be asked to confirm your request.""") if member is not None: break else: - # None of the user's addresses are subscribed to this mailing list. - print(_( - '$self.name: $email is not a member of $mlist.fqdn_listname'), - file=results) - return ContinueProcessing.no - member.unsubscribe() - person = formataddr((user.display_name, email)) # noqa: F841 - print(_('$person left $mlist.fqdn_listname'), file=results) + # There are two possible situations. Either none of the user's + # addresses are subscribed to this mailing list, or this command + # email *already* unsubscribed the user from the mailing list. + # E.g. if a message was sent to the -leave address and it + # contained the 'leave' command. Don't send a bogus response in + # this case, just ignore subsequent leaves of the same address. + if email not in already_left: + print(_('$self.name: $email is not a member of ' + '$mlist.fqdn_listname'), file=results) + return ContinueProcessing.no + if email in already_left: + return ContinueProcessing.yes + # Ignore any subsequent 'leave' commands. + already_left.add(email) + manager = ISubscriptionManager(mlist) + token, token_owner, member = manager.unregister(user_address) + person = formataddr((user.display_name, email)) # noqa + if member is None: + print(_('$person left $mlist.fqdn_listname'), file=results) + else: + print(_('Confirmation email sent to $person to leave' + ' $mlist.fqdn_listname'), file=results) return ContinueProcessing.yes diff --git a/src/mailman/commands/tests/test_confirm.py b/src/mailman/commands/tests/test_confirm.py index 7cce4c3c7..e0f816f24 100644 --- a/src/mailman/commands/tests/test_confirm.py +++ b/src/mailman/commands/tests/test_confirm.py @@ -25,7 +25,7 @@ from mailman.config import config from mailman.email.message import Message from mailman.interfaces.command import ContinueProcessing from mailman.interfaces.mailinglist import SubscriptionPolicy -from mailman.interfaces.registrar import IRegistrar +from mailman.interfaces.subscriptions import ISubscriptionManager from mailman.interfaces.usermanager import IUserManager from mailman.runners.command import CommandRunner, Results from mailman.testing.helpers import get_queue_messages, make_testable_runner @@ -42,8 +42,8 @@ class TestConfirm(unittest.TestCase): self._mlist = create_list('test@example.com') anne = getUtility(IUserManager).create_address( 'anne@example.com', 'Anne Person') - self._token, token_owner, member = IRegistrar(self._mlist).register( - anne) + self._token, token_owner, member = ISubscriptionManager( + self._mlist).register(anne) self._command = Confirm() # Clear the virgin queue. get_queue_messages('virgin') @@ -88,8 +88,8 @@ class TestEmailResponses(unittest.TestCase): 'bart@example.com', 'Bart Person') # Clear any previously queued confirmation messages. get_queue_messages('virgin') - self._token, token_owner, member = IRegistrar(self._mlist).register( - bart) + self._token, token_owner, member = ISubscriptionManager( + self._mlist).register(bart) # There should now be one email message in the virgin queue, i.e. the # confirmation message sent to Bart. items = get_queue_messages('virgin', expected_count=1) diff --git a/src/mailman/commands/tests/test_membership.py b/src/mailman/commands/tests/test_membership.py new file mode 100644 index 000000000..14074b3a9 --- /dev/null +++ b/src/mailman/commands/tests/test_membership.py @@ -0,0 +1,54 @@ +# Copyright (C) 2016 by the Free Software Foundation, Inc. +# +# This file is part of GNU Mailman. +# +# GNU Mailman 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 3 of the License, or (at your option) +# any later version. +# +# GNU Mailman 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 +# GNU Mailman. If not, see <http://www.gnu.org/licenses/>. + +"""Test the Leave command.""" + +import unittest + +from mailman.app.lifecycle import create_list +from mailman.commands.eml_membership import Leave +from mailman.email.message import Message +from mailman.interfaces.mailinglist import SubscriptionPolicy +from mailman.interfaces.usermanager import IUserManager +from mailman.runners.command import Results +from mailman.testing.helpers import set_preferred +from mailman.testing.layers import ConfigLayer +from zope.component import getUtility + + +class TestLeave(unittest.TestCase): + layer = ConfigLayer + + def setUp(self): + self._mlist = create_list('ant@example.com') + self._command = Leave() + + def test_confirm_leave_not_a_member(self): + self._mlist.unsubscription_policy = SubscriptionPolicy.confirm + # Try to unsubscribe someone who is not a member. Anne is a real + # user, with a validated address, but she is not a member of the + # mailing list. + anne = getUtility(IUserManager).create_user('anne@example.com') + set_preferred(anne) + # Initiate an unsubscription. + msg = Message() + msg['From'] = 'anne@example.com' + results = Results() + self._command.process(self._mlist, msg, {}, (), results) + self.assertEqual( + str(results).splitlines()[-1], + 'leave: anne@example.com is not a member of ant@example.com') |
