summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mailman/inject.py2
-rw-r--r--src/mailman/mta/decorating.py50
-rw-r--r--src/mailman/mta/docs/decorating.txt151
-rw-r--r--src/mailman/mta/personalized.py3
-rw-r--r--src/mailman/pipeline/avoid_duplicates.py4
-rw-r--r--src/mailman/pipeline/decorate.py7
-rw-r--r--src/mailman/pipeline/docs/avoid-duplicates.txt10
-rw-r--r--src/mailman/pipeline/docs/decorate.txt2
-rw-r--r--src/mailman/pipeline/docs/file-recips.txt10
-rw-r--r--src/mailman/pipeline/docs/replybot.txt4
-rw-r--r--src/mailman/pipeline/file_recipients.py6
-rw-r--r--src/mailman/pipeline/owner_recipients.py2
-rw-r--r--src/mailman/queue/bounce.py2
-rw-r--r--src/mailman/queue/digest.py4
-rw-r--r--src/mailman/queue/docs/command.txt2
-rw-r--r--src/mailman/queue/docs/digester.txt16
-rw-r--r--src/mailman/queue/docs/incoming.txt2
-rw-r--r--src/mailman/queue/outgoing.py2
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