summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mailman/app/registrar.py5
-rw-r--r--src/mailman/core/runner.py4
-rw-r--r--src/mailman/docs/NEWS.rst2
-rw-r--r--src/mailman/runners/command.py30
-rw-r--r--src/mailman/runners/tests/test_confirm.py96
5 files changed, 120 insertions, 17 deletions
diff --git a/src/mailman/app/registrar.py b/src/mailman/app/registrar.py
index e1d804167..a7c9dbb1b 100644
--- a/src/mailman/app/registrar.py
+++ b/src/mailman/app/registrar.py
@@ -25,8 +25,6 @@ __all__ = [
]
-import datetime
-
from pkg_resources import resource_string
from zope.component import getUtility
from zope.interface import implements
@@ -39,6 +37,7 @@ from mailman.interfaces.member import MemberRole
from mailman.interfaces.pending import IPendable, IPendings
from mailman.interfaces.registrar import IRegistrar
from mailman.interfaces.usermanager import IUserManager
+from mailman.utilities.datetime import now
@@ -131,7 +130,7 @@ class Registrar:
# The IAddress and linked IUser already exist, so all we need to
# do is verify the address.
pass
- address.verified_on = datetime.datetime.now()
+ address.verified_on = now()
# If this registration is tied to a mailing list, subscribe the person
# to the list right now.
list_name = pendable.get('list_name')
diff --git a/src/mailman/core/runner.py b/src/mailman/core/runner.py
index 30189600d..e86741c41 100644
--- a/src/mailman/core/runner.py
+++ b/src/mailman/core/runner.py
@@ -169,12 +169,12 @@ class Runner:
# Other work we want to do each time through the loop.
dlog.debug('[%s] doing periodic', me)
self._do_periodic()
+ dlog.debug('[%s] committing transaction', me)
+ config.db.commit()
dlog.debug('[%s] checking short circuit', me)
if self._short_circuit():
dlog.debug('[%s] short circuiting', me)
break
- dlog.debug('[%s] commiting', me)
- config.db.commit()
dlog.debug('[%s] ending oneloop: %s', me, len(files))
return len(files)
diff --git a/src/mailman/docs/NEWS.rst b/src/mailman/docs/NEWS.rst
index cdd81d49e..528009ef6 100644
--- a/src/mailman/docs/NEWS.rst
+++ b/src/mailman/docs/NEWS.rst
@@ -46,6 +46,8 @@ Interfaces
Commands
--------
* `bin/mailman shell` is an alias for `withlist`.
+ * The `confirm` email command now properly handles `Re:`-like prefixes, even
+ if they contain non-ASCII characters. (LP: #685261)
Bug fixes
---------
diff --git a/src/mailman/runners/command.py b/src/mailman/runners/command.py
index 33e320bff..c6c8900aa 100644
--- a/src/mailman/runners/command.py
+++ b/src/mailman/runners/command.py
@@ -78,9 +78,13 @@ class CommandFinder:
# Mail commands must be ASCII.
self.command_lines.append(subject.encode('us-ascii'))
except (HeaderParseError, UnicodeError, LookupError):
- # The Subject header was unparseable or not ASCII, so just ignore
- # it.
- pass
+ # The Subject header was unparseable or not ASCII. If the raw
+ # subject is a unicode object, convert it to ASCII ignoring all
+ # bogus characters. Otherwise, there's nothing in the subject
+ # that we can use.
+ if isinstance(raw_subject, unicode):
+ safe_subject = raw_subject.encode('us-ascii', errors='ignore')
+ self.command_lines.append(safe_subject)
# Find the first text/plain part of the message.
part = None
for part in typed_subpart_iterator(msg, 'text', 'plain'):
@@ -102,19 +106,14 @@ class CommandFinder:
self.ignored_lines.extend(lines[max_lines:])
def __iter__(self):
- """Return each command line, split into commands and arguments.
-
- :return: 2-tuples where the first element is the command and the
- second element is a tuple of the arguments.
- """
+ """Return each command line, split into space separated arguments."""
while self.command_lines:
line = self.command_lines.pop(0)
self.processed_lines.append(line)
parts = line.strip().split()
if len(parts) == 0:
continue
- command = parts.pop(0)
- yield command, tuple(parts)
+ yield parts
@@ -174,13 +173,20 @@ class CommandRunner(Runner):
print >> results, _(' Message-ID: $message_id')
print >> results, _('\n- Results:')
finder = CommandFinder(msg, msgdata, results)
- for command_name, arguments in finder:
+ for parts in finder:
+ # Try to find a command on this line. There may be a Re: prefix
+ # (possibly internationalized) so try with the first and second
+ # words on the line.
+ command_name = parts.pop(0)
command = config.commands.get(command_name)
if command is None:
+ command_name = parts.pop(0)
+ command = config.commands.get(command_name)
+ if command is None:
print >> results, _('No such command: $command_name')
else:
status = command.process(
- mlist, msg, msgdata, arguments, results)
+ mlist, msg, msgdata, parts, results)
assert status in ContinueProcessing, (
'Invalid status: %s' % status)
if status == ContinueProcessing.no:
diff --git a/src/mailman/runners/tests/test_confirm.py b/src/mailman/runners/tests/test_confirm.py
new file mode 100644
index 000000000..3e26bf542
--- /dev/null
+++ b/src/mailman/runners/tests/test_confirm.py
@@ -0,0 +1,96 @@
+# Copyright (C) 2012 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 `confirm` command."""
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ ]
+
+
+import unittest
+
+from datetime import datetime
+from zope.component import getUtility
+
+from mailman.app.lifecycle import create_list
+from mailman.config import config
+from mailman.interfaces.registrar import IRegistrar
+from mailman.interfaces.usermanager import IUserManager
+from mailman.runners.command import CommandRunner
+from mailman.testing.helpers import (
+ make_testable_runner,
+ specialized_message_from_string as mfs)
+from mailman.testing.layers import ConfigLayer
+
+
+
+class TestConfirm(unittest.TestCase):
+ """Test confirmations."""
+
+ layer = ConfigLayer
+
+ def setUp(self):
+ # Register a subscription requiring confirmation.
+ registrar = getUtility(IRegistrar)
+ self._mlist = create_list('test@example.com')
+ self._token = registrar.register(self._mlist, 'anne@example.org')
+ self._commandq = config.switchboards['command']
+ self._runner = make_testable_runner(CommandRunner, 'command')
+ config.db.commit()
+
+ def test_confirm_with_re_prefix(self):
+ subject = 'Re: confirm {0}'.format(self._token)
+ msg = mfs("""\
+From: anne@example.org
+To: test-confirm@example.com
+
+""")
+ msg['Subject'] = subject
+ self._commandq.enqueue(msg, dict(listname='test@example.com'))
+ self._runner.run()
+ # Anne is now a confirmed member so her user record and email address
+ # should exist in the database.
+ manager = getUtility(IUserManager)
+ user = manager.get_user('anne@example.org')
+ address = list(user.addresses)[0]
+ self.assertEqual(address.email, 'anne@example.org')
+ self.assertEqual(address.verified_on, datetime(2005, 8, 1, 7, 49, 23))
+ address = manager.get_address('anne@example.org')
+ self.assertEqual(address.email, 'anne@example.org')
+
+ def test_confirm_with_random_ascii_prefix(self):
+ subject = '\x99AW: confirm {0}'.format(self._token)
+ msg = mfs("""\
+From: anne@example.org
+To: test-confirm@example.com
+
+""")
+ msg['Subject'] = subject
+ self._commandq.enqueue(msg, dict(listname='test@example.com'))
+ self._runner.run()
+ # Anne is now a confirmed member so her user record and email address
+ # should exist in the database.
+ manager = getUtility(IUserManager)
+ user = manager.get_user('anne@example.org')
+ address = list(user.addresses)[0]
+ self.assertEqual(address.email, 'anne@example.org')
+ self.assertEqual(address.verified_on, datetime(2005, 8, 1, 7, 49, 23))
+ address = manager.get_address('anne@example.org')
+ self.assertEqual(address.email, 'anne@example.org')