diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/mailman/inject.py | 2 | ||||
| -rw-r--r-- | src/mailman/mta/decorating.py | 50 | ||||
| -rw-r--r-- | src/mailman/mta/docs/decorating.txt | 151 | ||||
| -rw-r--r-- | src/mailman/mta/personalized.py | 3 | ||||
| -rw-r--r-- | src/mailman/pipeline/avoid_duplicates.py | 4 | ||||
| -rw-r--r-- | src/mailman/pipeline/decorate.py | 7 | ||||
| -rw-r--r-- | src/mailman/pipeline/docs/avoid-duplicates.txt | 10 | ||||
| -rw-r--r-- | src/mailman/pipeline/docs/decorate.txt | 2 | ||||
| -rw-r--r-- | src/mailman/pipeline/docs/file-recips.txt | 10 | ||||
| -rw-r--r-- | src/mailman/pipeline/docs/replybot.txt | 4 | ||||
| -rw-r--r-- | src/mailman/pipeline/file_recipients.py | 6 | ||||
| -rw-r--r-- | src/mailman/pipeline/owner_recipients.py | 2 | ||||
| -rw-r--r-- | src/mailman/queue/bounce.py | 2 | ||||
| -rw-r--r-- | src/mailman/queue/digest.py | 4 | ||||
| -rw-r--r-- | src/mailman/queue/docs/command.txt | 2 | ||||
| -rw-r--r-- | src/mailman/queue/docs/digester.txt | 16 | ||||
| -rw-r--r-- | src/mailman/queue/docs/incoming.txt | 2 | ||||
| -rw-r--r-- | src/mailman/queue/outgoing.py | 2 |
18 files changed, 239 insertions, 40 deletions
diff --git a/src/mailman/inject.py b/src/mailman/inject.py index eec096066..d22ae7128 100644 --- a/src/mailman/inject.py +++ b/src/mailman/inject.py @@ -67,7 +67,7 @@ def inject_message(mlist, msg, recips=None, switchboard=None, **kws): ) msgdata.update(kws) if recips is not None: - msgdata['recips'] = recips + msgdata['recipients'] = recips config.switchboards[switchboard].enqueue(msg, **msgdata) diff --git a/src/mailman/mta/decorating.py b/src/mailman/mta/decorating.py new file mode 100644 index 000000000..487fa8a1c --- /dev/null +++ b/src/mailman/mta/decorating.py @@ -0,0 +1,50 @@ +# Copyright (C) 2009 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/>. + +"""Individualized delivery with header/footer decorations.""" + +from __future__ import absolute_import, unicode_literals + +__metaclass__ = type +__all__ = [ + 'DecoratingDelivery', + 'DecoratingMixin', + ] + + +from mailman.config import config +from mailman.mta.verp import VERPDelivery + + + +class DecoratingMixin: + """Decorate a message with recipient-specific headers and footers.""" + + def decorate(self, mlist, msg, msgdata): + """Add recipient-specific headers and footers.""" + decorator = config.handlers['decorate'] + decorator.process(mlist, msg, msgdata) + + + +class DecoratingDelivery(DecoratingMixin, VERPDelivery): + """Add recipient-specific headers and footers.""" + + def __init__(self): + """See `IndividualDelivery`.""" + super(DecoratingDelivery, self).__init__() + self.callbacks.append(self.decorate) diff --git a/src/mailman/mta/docs/decorating.txt b/src/mailman/mta/docs/decorating.txt new file mode 100644 index 000000000..1047a9fb6 --- /dev/null +++ b/src/mailman/mta/docs/decorating.txt @@ -0,0 +1,151 @@ +======================= +Personalized decoration +======================= + +Personalized messages can be decorated by headers and footers containing +information specific to the recipient. + + >>> from mailman.mta.decorating import DecoratingDelivery + >>> decorating = DecoratingDelivery() + +Delivery strategies must implement the proper interface. + + >>> from mailman.interfaces.mta import IMailTransportAgentDelivery + >>> from zope.interface.verify import verifyObject + >>> verifyObject(IMailTransportAgentDelivery, decorating) + True + + +Decorations +=========== + +Decorations are added when the mailing list had a header and/or footer +defined, and the decoration handler is told to do personalized decorations. + + >>> mlist = create_list('test@example.com') + >>> mlist.msg_header = """\ + ... Delivery address: $user_address + ... Subscribed address: $user_delivered_to + ... """ + + >>> mlist.msg_footer = """\ + ... User name: $user_name + ... Password: $user_password + ... Language: $user_language + ... Options: $user_optionsurl + ... """ + + >>> transaction.commit() + + >>> msg = message_from_string("""\ + ... From: aperson@example.org + ... To: test@example.com + ... Subject: test one + ... Message-ID: <aardvark> + ... + ... This is a test. + ... """) + + >>> recipients = set([ + ... 'aperson@example.com', + ... 'bperson@example.com', + ... 'cperson@example.com', + ... ]) + + >>> msgdata = dict( + ... recipients=recipients, + ... personalize=True, + ... ) + +More information is included when the recipient is a member of the mailing +list. + + >>> from zope.component import getUtility + >>> from mailman.interfaces.member import MemberRole + >>> from mailman.interfaces.usermanager import IUserManager + >>> user_manager = getUtility(IUserManager) + + >>> anne = user_manager.create_user('aperson@example.com', 'Anne Person') + >>> anne.password = 'AAA' + >>> list(anne.addresses)[0].subscribe(mlist, MemberRole.member) + <Member: Anne Person <aperson@example.com> ... + + >>> bart = user_manager.create_user('bperson@example.com', 'Bart Person') + >>> bart.password = 'BBB' + >>> list(bart.addresses)[0].subscribe(mlist, MemberRole.member) + <Member: Bart Person <bperson@example.com> ... + + >>> cris = user_manager.create_user('cperson@example.com', 'Cris Person') + >>> cris.password = 'CCC' + >>> list(cris.addresses)[0].subscribe(mlist, MemberRole.member) + <Member: Cris Person <cperson@example.com> ... + +The decorations happen when the message is delivered. + + >>> decorating.deliver(mlist, msg, msgdata) + {} + >>> messages = list(smtpd.messages) + >>> len(messages) + 3 + + >>> from operator import itemgetter + >>> for message in sorted(messages, key=itemgetter('x-rcptto')): + ... print message.as_string() + ... print '----------' + From: aperson@example.org + To: test@example.com + Subject: test one + Message-ID: <aardvark> + MIME-Version: 1.0 + Content-Type: text/plain; charset="us-ascii" + Content-Transfer-Encoding: 7bit + X-Peer: ... + X-MailFrom: test-bounces+aperson=example.com@example.com + X-RcptTo: aperson@example.com + <BLANKLINE> + Delivery address: aperson@example.com + 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 + ---------- + From: aperson@example.org + To: test@example.com + Subject: test one + Message-ID: <aardvark> + MIME-Version: 1.0 + Content-Type: text/plain; charset="us-ascii" + Content-Transfer-Encoding: 7bit + X-Peer: ... + X-MailFrom: test-bounces+bperson=example.com@example.com + X-RcptTo: bperson@example.com + <BLANKLINE> + Delivery address: bperson@example.com + 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 + ---------- + From: aperson@example.org + To: test@example.com + Subject: test one + Message-ID: <aardvark> + MIME-Version: 1.0 + Content-Type: text/plain; charset="us-ascii" + Content-Transfer-Encoding: 7bit + X-Peer: ... + X-MailFrom: test-bounces+cperson=example.com@example.com + X-RcptTo: cperson@example.com + <BLANKLINE> + Delivery address: cperson@example.com + 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/personalized.py b/src/mailman/mta/personalized.py index 6fb73fdba..4d0243fc7 100644 --- a/src/mailman/mta/personalized.py +++ b/src/mailman/mta/personalized.py @@ -63,10 +63,11 @@ class PersonalizedMixin: msg.replace_header('To', formataddr((name, recipient))) + class PersonalizedDelivery(PersonalizedMixin, VERPDelivery): """Personalize the message's To header.""" def __init__(self): """See `IndividualDelivery`.""" - super(VERPDelivery, self).__init__() + super(PersonalizedDelivery, self).__init__() self.callbacks.append(self.personalize_to) diff --git a/src/mailman/pipeline/avoid_duplicates.py b/src/mailman/pipeline/avoid_duplicates.py index 0458e117c..840d5f8b2 100644 --- a/src/mailman/pipeline/avoid_duplicates.py +++ b/src/mailman/pipeline/avoid_duplicates.py @@ -52,7 +52,7 @@ class AvoidDuplicates: def process(self, mlist, msg, msgdata): """See `IHandler`.""" - recips = msgdata.get('recips') + recips = msgdata.get('recipients') # Short circuit if not recips: return @@ -109,7 +109,7 @@ class AvoidDuplicates: # having received this message. newrecips.add(r) # Set the new list of recipients. XXX recips should always be a set. - msgdata['recips'] = list(newrecips) + msgdata['recipients'] = list(newrecips) # RFC 2822 specifies zero or one CC header if cc_addresses: del msg['cc'] diff --git a/src/mailman/pipeline/decorate.py b/src/mailman/pipeline/decorate.py index c6b613fda..f9e41e177 100644 --- a/src/mailman/pipeline/decorate.py +++ b/src/mailman/pipeline/decorate.py @@ -52,10 +52,7 @@ def process(mlist, msg, msgdata): if msgdata.get('personalize'): # Calculate the extra personalization dictionary. Note that the # length of the recips list better be exactly 1. - recips = msgdata.get('recips', []) - assert len(recips) == 1, ( - 'The number of intended recipients must be exactly 1') - recipient = recips[0].lower() + recipient = msgdata['recipient'] user = getUtility(IUserManager).get_user(recipient) member = mlist.members.get_member(recipient) d['user_address'] = recipient @@ -63,7 +60,7 @@ def process(mlist, msg, msgdata): d['user_delivered_to'] = member.address.original_address # BAW: Hmm, should we allow this? d['user_password'] = user.password - d['user_language'] = member.preferred_language + d['user_language'] = member.preferred_language.description d['user_name'] = (user.real_name if user.real_name else member.address.original_address) d['user_optionsurl'] = member.options_url diff --git a/src/mailman/pipeline/docs/avoid-duplicates.txt b/src/mailman/pipeline/docs/avoid-duplicates.txt index 9b44d0ebe..b6d0133fd 100644 --- a/src/mailman/pipeline/docs/avoid-duplicates.txt +++ b/src/mailman/pipeline/docs/avoid-duplicates.txt @@ -69,7 +69,7 @@ will get a list copy. ... """) >>> msgdata = recips.copy() >>> handler.process(mlist, msg, msgdata) - >>> sorted(msgdata['recips']) + >>> sorted(msgdata['recipients']) [u'aperson@example.com', u'bperson@example.com'] >>> print msg.as_string() From: Claire Person <cperson@example.com> @@ -87,7 +87,7 @@ If they're mentioned on the CC line, they won't get a list copy. ... """) >>> msgdata = recips.copy() >>> handler.process(mlist, msg, msgdata) - >>> sorted(msgdata['recips']) + >>> sorted(msgdata['recipients']) [u'bperson@example.com'] >>> print msg.as_string() From: Claire Person <cperson@example.com> @@ -107,7 +107,7 @@ But if they're mentioned on the CC line and have receive_list_copy set to True ... """) >>> msgdata = recips.copy() >>> handler.process(mlist, msg, msgdata) - >>> sorted(msgdata['recips']) + >>> sorted(msgdata['recipients']) [u'aperson@example.com', u'bperson@example.com'] >>> print msg.as_string() From: Claire Person <cperson@example.com> @@ -145,7 +145,7 @@ Other headers checked for recipients include the To... ... """) >>> msgdata = recips.copy() >>> handler.process(mlist, msg, msgdata) - >>> sorted(msgdata['recips']) + >>> sorted(msgdata['recipients']) [u'bperson@example.com'] >>> print msg.as_string() From: Claire Person <cperson@example.com> @@ -164,7 +164,7 @@ Other headers checked for recipients include the To... ... """) >>> msgdata = recips.copy() >>> handler.process(mlist, msg, msgdata) - >>> sorted(msgdata['recips']) + >>> sorted(msgdata['recipients']) [u'bperson@example.com'] >>> print msg.as_string() From: Claire Person <cperson@example.com> diff --git a/src/mailman/pipeline/docs/decorate.txt b/src/mailman/pipeline/docs/decorate.txt index 42afe9a80..78b500409 100644 --- a/src/mailman/pipeline/docs/decorate.txt +++ b/src/mailman/pipeline/docs/decorate.txt @@ -312,7 +312,7 @@ provided in the message data, otherwise an exception occurs. And the number of intended recipients must be exactly 1. - >>> process(mlist, None, dict(personalize=True, recips=[1, 2, 3])) + >>> process(mlist, None, dict(personalize=True, recipients=[1, 2, 3])) Traceback (most recent call last): ... AssertionError: The number of intended recipients must be exactly 1 diff --git a/src/mailman/pipeline/docs/file-recips.txt b/src/mailman/pipeline/docs/file-recips.txt index 3401e7492..b84b2181d 100644 --- a/src/mailman/pipeline/docs/file-recips.txt +++ b/src/mailman/pipeline/docs/file-recips.txt @@ -21,7 +21,7 @@ returns. ... ... A message. ... """) - >>> msgdata = {'recips': 7} + >>> msgdata = {'recipients': 7} >>> handler = config.handlers['file-recipients'] >>> handler.process(mlist, msg, msgdata) @@ -31,7 +31,7 @@ returns. A message. <BLANKLINE> >>> msgdata - {u'recips': 7} + {u'recipients': 7} Missing file @@ -50,7 +50,7 @@ empty. No such file or directory: u'.../_xtest@example.com/members.txt' >>> msgdata = {} >>> handler.process(mlist, msg, msgdata) - >>> sorted(msgdata['recips']) + >>> sorted(msgdata['recipients']) [] @@ -73,7 +73,7 @@ addresses are returned as the set of recipients. >>> msgdata = {} >>> handler.process(mlist, msg, msgdata) - >>> sorted(msgdata['recips']) + >>> sorted(msgdata['recipients']) ['bperson@example.com', 'cperson@example.com', 'dperson@example.com', 'eperson@example.com', 'fperson@example.com', 'gperson@example.com'] @@ -97,6 +97,6 @@ in the recipients list. ... """) >>> msgdata = {} >>> handler.process(mlist, msg, msgdata) - >>> sorted(msgdata['recips']) + >>> sorted(msgdata['recipients']) ['bperson@example.com', 'dperson@example.com', 'eperson@example.com', 'fperson@example.com', 'gperson@example.com'] diff --git a/src/mailman/pipeline/docs/replybot.txt b/src/mailman/pipeline/docs/replybot.txt index 36bc6198f..f02b90254 100644 --- a/src/mailman/pipeline/docs/replybot.txt +++ b/src/mailman/pipeline/docs/replybot.txt @@ -49,7 +49,7 @@ response. _parsemsg : False listname : _xtest@example.com nodecorate : True - recips : [u'aperson@example.com'] + recipients : [u'aperson@example.com'] reduced_list_headers: True version : 3 @@ -137,7 +137,7 @@ header is ignored. _parsemsg : False listname : _xtest@example.com nodecorate : True - recips : [u'asystem@example.com'] + recipients : [u'asystem@example.com'] reduced_list_headers: True version : 3 diff --git a/src/mailman/pipeline/file_recipients.py b/src/mailman/pipeline/file_recipients.py index fd2db596a..c3d995a9c 100644 --- a/src/mailman/pipeline/file_recipients.py +++ b/src/mailman/pipeline/file_recipients.py @@ -45,7 +45,7 @@ class FileRecipients: def process(self, mlist, msg, msgdata): """See `IHandler`.""" - if 'recips' in msgdata: + if 'recipients' in msgdata: return filename = os.path.join(mlist.data_path, 'members.txt') try: @@ -54,11 +54,11 @@ class FileRecipients: except IOError, e: if e.errno <> errno.ENOENT: raise - msgdata['recips'] = set() + msgdata['recipients'] = set() return # If the sender is a member of the list, remove them from the file # recipients. member = mlist.members.get_member(msg.sender) if member is not None: addrs.discard(member.address.address) - msgdata['recips'] = addrs + msgdata['recipients'] = addrs diff --git a/src/mailman/pipeline/owner_recipients.py b/src/mailman/pipeline/owner_recipients.py index ceb6ae0a1..ca6c17bd9 100644 --- a/src/mailman/pipeline/owner_recipients.py +++ b/src/mailman/pipeline/owner_recipients.py @@ -28,7 +28,7 @@ __all__ = [ def process(mlist, msg, msgdata): # The recipients are the owner and the moderator - msgdata['recips'] = mlist.owner + mlist.moderator + msgdata['recipients'] = mlist.owner + mlist.moderator # Don't decorate these messages with the header/footers msgdata['nodecorate'] = True msgdata['personalize'] = False diff --git a/src/mailman/queue/bounce.py b/src/mailman/queue/bounce.py index c966729bb..3d2ef6140 100644 --- a/src/mailman/queue/bounce.py +++ b/src/mailman/queue/bounce.py @@ -181,7 +181,7 @@ class BounceRunner(Runner, BounceMixin): # get stuck in a bounce loop. config.switchboards['out'].enqueue( msg, msgdata, - recips=[config.mailman.site_owner], + recipients=[config.mailman.site_owner], envsender=config.mailman.noreply_address, ) # List isn't doing bounce processing? diff --git a/src/mailman/queue/digest.py b/src/mailman/queue/digest.py index 30705b8be..3dbfc7917 100644 --- a/src/mailman/queue/digest.py +++ b/src/mailman/queue/digest.py @@ -360,10 +360,10 @@ class DigestRunner(Runner): # Send the digests to the virgin queue for final delivery. queue = config.switchboards['virgin'] queue.enqueue(mime, - recips=mime_recipients, + recipients=mime_recipients, listname=mlist.fqdn_listname, isdigest=True) queue.enqueue(rfc1153, - recips=rfc1153_recipients, + recipients=rfc1153_recipients, listname=mlist.fqdn_listname, isdigest=True) diff --git a/src/mailman/queue/docs/command.txt b/src/mailman/queue/docs/command.txt index 73be64de0..ae5b2612d 100644 --- a/src/mailman/queue/docs/command.txt +++ b/src/mailman/queue/docs/command.txt @@ -60,7 +60,7 @@ And now the response is in the virgin queue. <BLANKLINE> >>> sorted(item.msgdata.items()) [..., ('listname', u'test@example.com'), ..., - ('recips', [u'aperson@example.com']), + ('recipients', [u'aperson@example.com']), ...] diff --git a/src/mailman/queue/docs/digester.txt b/src/mailman/queue/docs/digester.txt index c61f0fe3d..55f93cf40 100644 --- a/src/mailman/queue/docs/digester.txt +++ b/src/mailman/queue/docs/digester.txt @@ -522,12 +522,12 @@ and the other is the RFC 1153 digest. Only wperson and xperson get the MIME digests. - >>> sorted(mime.msgdata['recips']) + >>> sorted(mime.msgdata['recipients']) [u'wperson@example.com', u'xperson@example.com'] Only yperson and zperson get the RFC 1153 digests. - >>> sorted(rfc1153.msgdata['recips']) + >>> sorted(rfc1153.msgdata['recipients']) [u'yperson@example.com', u'zperson@example.com'] Now uperson decides that they would like to start receiving digests too. @@ -541,10 +541,10 @@ Now uperson decides that they would like to start receiving digests too. 2 >>> mime, rfc1153 = mime_rfc1153(messages) - >>> sorted(mime.msgdata['recips']) + >>> sorted(mime.msgdata['recipients']) [u'uperson@example.com', u'wperson@example.com', u'xperson@example.com'] - >>> sorted(rfc1153.msgdata['recips']) + >>> sorted(rfc1153.msgdata['recipients']) [u'yperson@example.com', u'zperson@example.com'] At this point, both uperson and wperson decide that they'd rather receive @@ -566,10 +566,10 @@ as much and does not want to receive one last digest. 2 >>> mime, rfc1153 = mime_rfc1153(messages) - >>> sorted(mime.msgdata['recips']) + >>> sorted(mime.msgdata['recipients']) [u'uperson@example.com', u'xperson@example.com'] - >>> sorted(rfc1153.msgdata['recips']) + >>> sorted(rfc1153.msgdata['recipients']) [u'yperson@example.com', u'zperson@example.com'] Since uperson has received their last digest, they will not get any more of @@ -583,8 +583,8 @@ them. 2 >>> mime, rfc1153 = mime_rfc1153(messages) - >>> sorted(mime.msgdata['recips']) + >>> sorted(mime.msgdata['recipients']) [u'xperson@example.com'] - >>> sorted(rfc1153.msgdata['recips']) + >>> sorted(rfc1153.msgdata['recipients']) [u'yperson@example.com', u'zperson@example.com'] diff --git a/src/mailman/queue/docs/incoming.txt b/src/mailman/queue/docs/incoming.txt index 8635f2872..74326820f 100644 --- a/src/mailman/queue/docs/incoming.txt +++ b/src/mailman/queue/docs/incoming.txt @@ -195,7 +195,7 @@ tests above. >>> dump_msgdata(item.msgdata) _parsemsg : False ... - recips : [u'aperson@example.com'] + recipients : [u'aperson@example.com'] ... >>> fp.seek(file_pos) diff --git a/src/mailman/queue/outgoing.py b/src/mailman/queue/outgoing.py index e64e8c57b..7776b1b54 100644 --- a/src/mailman/queue/outgoing.py +++ b/src/mailman/queue/outgoing.py @@ -118,7 +118,7 @@ class OutgoingRunner(Runner, BounceMixin): config.mta.delivery_retry_period) msgdata['last_recip_count'] = len(recips) msgdata['deliver_until'] = deliver_until - msgdata['recips'] = recips + msgdata['recipients'] = recips self._retryq.enqueue(msg, msgdata) # We've successfully completed handling of this message return False |
