summaryrefslogtreecommitdiff
path: root/src/mailman/commands
diff options
context:
space:
mode:
Diffstat (limited to 'src/mailman/commands')
-rw-r--r--src/mailman/commands/docs/membership.rst15
-rw-r--r--src/mailman/commands/eml_confirm.py6
-rw-r--r--src/mailman/commands/eml_membership.py39
-rw-r--r--src/mailman/commands/tests/test_confirm.py10
-rw-r--r--src/mailman/commands/tests/test_membership.py54
5 files changed, 98 insertions, 26 deletions
diff --git a/src/mailman/commands/docs/membership.rst b/src/mailman/commands/docs/membership.rst
index 49e80511d..5799e1e17 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,12 @@ 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
>>> 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')