diff options
| author | Barry Warsaw | 2012-03-05 12:58:20 -0500 |
|---|---|---|
| committer | Barry Warsaw | 2012-03-05 12:58:20 -0500 |
| commit | f4b98f8b8e8b9fdcab8c352019f09b1469c93b24 (patch) | |
| tree | 8a43643beb240ca299ffb379df8e90567b44305c | |
| parent | 40347db84550a85f43e6befa4641693200d30509 (diff) | |
| download | mailman-f4b98f8b8e8b9fdcab8c352019f09b1469c93b24.tar.gz mailman-f4b98f8b8e8b9fdcab8c352019f09b1469c93b24.tar.zst mailman-f4b98f8b8e8b9fdcab8c352019f09b1469c93b24.zip | |
| -rw-r--r-- | src/mailman/mta/base.py | 8 | ||||
| -rw-r--r-- | src/mailman/mta/docs/decorating.rst | 9 | ||||
| -rw-r--r-- | src/mailman/mta/tests/test_delivery.py | 144 | ||||
| -rw-r--r-- | src/mailman/pipeline/decorate.py | 23 | ||||
| -rw-r--r-- | src/mailman/runners/lmtp.py | 13 |
5 files changed, 168 insertions, 29 deletions
diff --git a/src/mailman/mta/base.py b/src/mailman/mta/base.py index 375d79fb8..7873d66e9 100644 --- a/src/mailman/mta/base.py +++ b/src/mailman/mta/base.py @@ -139,7 +139,7 @@ class IndividualDelivery(BaseDelivery): def __init__(self): """See `BaseDelivery`.""" - # + # super(IndividualDelivery, self).__init__() self.callbacks = [] @@ -162,6 +162,12 @@ class IndividualDelivery(BaseDelivery): # That way the subclass's _get_sender() override can encode the # recipient address in the sender, e.g. for VERP. msgdata_copy['recipient'] = recipient + # See if the recipient is a member of the mailing list, and if so, + # squirrel this information away for use by other modules, such as + # the header/footer decorator. XXX 2012-03-05 this is probably + # highly inefficient on the database. + member = mlist.members.get_member(recipient) + msgdata_copy['member'] = member for callback in self.callbacks: callback(mlist, message_copy, msgdata_copy) status = self._deliver_to_recipients( diff --git a/src/mailman/mta/docs/decorating.rst b/src/mailman/mta/docs/decorating.rst index 44559edb3..cf595b0d5 100644 --- a/src/mailman/mta/docs/decorating.rst +++ b/src/mailman/mta/docs/decorating.rst @@ -43,7 +43,6 @@ We start by writing the site-global header and footer template. >>> with open(myfooter_path, 'w') as fp: ... print >> fp, """\ ... User name: $user_name - ... Password: $user_password ... Language: $user_language ... Options: $user_optionsurl ... """ @@ -54,7 +53,7 @@ these are site-global templates, we can use a shorted URL. >>> mlist = create_list('test@example.com') >>> mlist.header_uri = 'mailman:///myheader.txt' >>> mlist.footer_uri = 'mailman:///myfooter.txt' - + >>> transaction.commit() >>> msg = message_from_string("""\ @@ -87,17 +86,14 @@ list. >>> user_manager = getUtility(IUserManager) >>> anne = user_manager.create_user('aperson@example.com', 'Anne Person') - >>> anne.password = b'AAA' >>> mlist.subscribe(list(anne.addresses)[0], MemberRole.member) <Member: Anne Person <aperson@example.com> ... >>> bart = user_manager.create_user('bperson@example.com', 'Bart Person') - >>> bart.password = b'BBB' >>> mlist.subscribe(list(bart.addresses)[0], MemberRole.member) <Member: Bart Person <bperson@example.com> ... >>> cris = user_manager.create_user('cperson@example.com', 'Cris Person') - >>> cris.password = b'CCC' >>> mlist.subscribe(list(cris.addresses)[0], MemberRole.member) <Member: Cris Person <cperson@example.com> ... @@ -129,7 +125,6 @@ The decorations happen when the message is delivered. Subscribed address: aperson@example.com This is a test. User name: Anne Person - Password: AAA Language: English (USA) Options: http://example.com/aperson@example.com ---------- @@ -148,7 +143,6 @@ The decorations happen when the message is delivered. Subscribed address: bperson@example.com This is a test. User name: Bart Person - Password: BBB Language: English (USA) Options: http://example.com/bperson@example.com ---------- @@ -167,7 +161,6 @@ The decorations happen when the message is delivered. Subscribed address: cperson@example.com This is a test. User name: Cris Person - Password: CCC Language: English (USA) Options: http://example.com/cperson@example.com ---------- diff --git a/src/mailman/mta/tests/test_delivery.py b/src/mailman/mta/tests/test_delivery.py new file mode 100644 index 000000000..1e362d755 --- /dev/null +++ b/src/mailman/mta/tests/test_delivery.py @@ -0,0 +1,144 @@ +# 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 various aspects of email delivery.""" + +from __future__ import absolute_import, print_function, unicode_literals + +__metaclass__ = type +__all__ = [ + 'TestIndividualDelivery', + ] + + +import os +import shutil +import tempfile +import unittest + +from mailman.app.lifecycle import create_list +from mailman.app.membership import add_member +from mailman.config import config +from mailman.interfaces.mailinglist import Personalization +from mailman.interfaces.member import DeliveryMode +from mailman.mta.deliver import Deliver +from mailman.testing.helpers import ( + specialized_message_from_string as mfs) +from mailman.testing.layers import ConfigLayer + + + +# Global test capture. +_deliveries = [] + +# Derive this from the default individual delivery class. The point being +# that we don't want to *actually* attempt delivery of the message to the MTA, +# we just want to capture the messages and metadata dictionaries for +# inspection. +class DeliverTester(Deliver): + def _deliver_to_recipients(self, mlist, msg, msgdata, recipients): + _deliveries.append((mlist, msg, msgdata, recipients)) + # Nothing gets refused. + return [] + + + +class TestIndividualDelivery(unittest.TestCase): + """Test personalized delivery details.""" + + layer = ConfigLayer + + def setUp(self): + self._mlist = create_list('test@example.com') + self._mlist.personalize = Personalization.individual + # Make Anne a member of this mailing list. + self._anne = add_member(self._mlist, + 'anne@example.org', 'Anne Person', + 'xyz', DeliveryMode.regular, 'en') + # Clear out any results from the previous test. + del _deliveries[:] + self._msg = mfs("""\ +From: anne@example.org +To: test@example.com +Subject: test + +""") + # Set up a personalized footer for decoration. + self._template_dir = tempfile.mkdtemp() + path = os.path.join(self._template_dir, + 'site', 'en', 'member-footer.txt') + os.makedirs(os.path.dirname(path)) + with open(path, 'w') as fp: + print("""\ +address : $user_address +delivered: $user_delivered_to +language : $user_language +name : $user_name +options : $user_optionsurl +""", file=fp) + config.push('templates', """ + [paths.testing] + template_dir: {0} + """.format(self._template_dir)) + self._mlist.footer_uri = 'mailman:///member-footer.txt' + + def tearDown(self): + # Free references. + del _deliveries[:] + shutil.rmtree(self._template_dir) + config.pop('templates') + + def test_member_key(self): + # 'personalize' should end up in the metadata dictionary so that + # $user_* keys in headers and footers get filled in correctly. + msgdata = dict(recipients=['anne@example.org']) + agent = DeliverTester() + refused = agent.deliver(self._mlist, self._msg, msgdata) + self.assertEqual(len(refused), 0) + self.assertEqual(len(_deliveries), 1) + _mlist, _msg, _msgdata, _recipients = _deliveries[0] + member = _msgdata.get('member') + self.assertEqual(member, self._anne) + + def test_decoration(self): + msgdata = dict(recipients=['anne@example.org']) + agent = DeliverTester() + refused = agent.deliver(self._mlist, self._msg, msgdata) + self.assertEqual(len(refused), 0) + self.assertEqual(len(_deliveries), 1) + _mlist, _msg, _msgdata, _recipients = _deliveries[0] + try: + eq = self.assertMultiLineEqual + except AttributeError: + # Python 2.6 + eq = self.assertEqual + eq(_msg.as_string(), """\ +From: anne@example.org +To: test@example.com +Subject: test +MIME-Version: 1.0 +Content-Type: text/plain; charset="us-ascii" +Content-Transfer-Encoding: 7bit + + +address : anne@example.org +delivered: anne@example.org +language : English (USA) +name : Anne Person +options : http://example.com/anne@example.org + +""") diff --git a/src/mailman/pipeline/decorate.py b/src/mailman/pipeline/decorate.py index a8fc340f8..b785e5ecf 100644 --- a/src/mailman/pipeline/decorate.py +++ b/src/mailman/pipeline/decorate.py @@ -51,21 +51,16 @@ def process(mlist, msg, msgdata): if msgdata.get('isdigest') or msgdata.get('nodecorate'): return d = {} - if msgdata.get('personalize'): - # Calculate the extra personalization dictionary. Note that the - # length of the recips list better be exactly 1. - recipient = msgdata['recipient'] - user = getUtility(IUserManager).get_user(recipient) - member = mlist.members.get_member(recipient) + member = msgdata.get('member') + if member is not None: + # Calculate the extra personalization dictionary. + recipient = msgdata.get('recipient', member.address.original_email) d['user_address'] = recipient - if user is not None and member is not None: - d['user_delivered_to'] = member.address.original_email - # BAW: Hmm, should we allow this? - d['user_password'] = user.password - d['user_language'] = member.preferred_language.description - d['user_name'] = (user.real_name if user.real_name - else member.address.original_email) - d['user_optionsurl'] = member.options_url + d['user_delivered_to'] = member.address.original_email + d['user_language'] = member.preferred_language.description + d['user_name'] = (member.user.real_name if member.user.real_name + else member.address.original_email) + d['user_optionsurl'] = member.options_url # These strings are descriptive for the log file and shouldn't be i18n'd d.update(msgdata.get('decoration-data', {})) try: diff --git a/src/mailman/runners/lmtp.py b/src/mailman/runners/lmtp.py index 483b63049..6824491eb 100644 --- a/src/mailman/runners/lmtp.py +++ b/src/mailman/runners/lmtp.py @@ -47,6 +47,7 @@ from mailman.interfaces.listmanager import IListManager elog = logging.getLogger('mailman.error') qlog = logging.getLogger('mailman.runner') +slog = logging.getLogger('mailman.smtp') # We only care about the listname and the sub-addresses as in listname@ or @@ -147,7 +148,7 @@ class LMTPRunner(Runner, smtpd.SMTPServer): def handle_accept(self): conn, addr = self.accept() Channel(self, conn, addr) - qlog.debug('LMTP accept from %s', addr) + slog.debug('LMTP accept from %s', addr) @txn def process_message(self, peer, mailfrom, rcpttos, data): @@ -156,7 +157,7 @@ class LMTPRunner(Runner, smtpd.SMTPServer): # since the set of mailing lists could have changed. listnames = set(getUtility(IListManager).names) # Parse the message data. If there are any defects in the - # message, reject it right away; it's probably spam. + # message, reject it right away; it's probably spam. msg = email.message_from_string(data, Message) msg.original_size = len(data) if msg.defects: @@ -177,7 +178,7 @@ class LMTPRunner(Runner, smtpd.SMTPServer): try: to = parseaddr(to)[1].lower() listname, subaddress, domain = split_recipient(to) - qlog.debug('%s to: %s, list: %s, sub: %s, dom: %s', + slog.debug('%s to: %s, list: %s, sub: %s, dom: %s', message_id, to, listname, subaddress, domain) listname += '@' + domain if listname not in listnames: @@ -197,7 +198,7 @@ class LMTPRunner(Runner, smtpd.SMTPServer): queue = 'in' elif canonical_subaddress is None: # The subaddress was bogus. - elog.error('%s unknown sub-address: %s', + slog.error('%s unknown sub-address: %s', message_id, subaddress) status.append(ERR_550) continue @@ -214,11 +215,11 @@ class LMTPRunner(Runner, smtpd.SMTPServer): # a success status for this recipient. if queue is not None: config.switchboards[queue].enqueue(msg, msgdata) - qlog.debug('%s subaddress: %s, queue: %s', + slog.debug('%s subaddress: %s, queue: %s', message_id, canonical_subaddress, queue) status.append('250 Ok') except Exception: - elog.exception('Queue detection: %s', msg['message-id']) + slog.exception('Queue detection: %s', msg['message-id']) config.db.abort() status.append(ERR_550) # All done; returning this big status string should give the expected |
