summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBarry Warsaw2009-10-24 16:49:33 -0400
committerBarry Warsaw2009-10-24 16:49:33 -0400
commit19e6548e3df4719455ab1ed2a242acbc3e38d9e9 (patch)
treeffae2472bb9c49ee53b5cba37213c74c33284b4c
parent7f5818a6c2356211c41c9fcd7dd8fa3b5c4ac6f6 (diff)
downloadmailman-19e6548e3df4719455ab1ed2a242acbc3e38d9e9.tar.gz
mailman-19e6548e3df4719455ab1ed2a242acbc3e38d9e9.tar.zst
mailman-19e6548e3df4719455ab1ed2a242acbc3e38d9e9.zip
-rw-r--r--src/mailman/mta/bulk.py19
-rw-r--r--src/mailman/mta/docs/bulk.txt118
2 files changed, 135 insertions, 2 deletions
diff --git a/src/mailman/mta/bulk.py b/src/mailman/mta/bulk.py
index 2b400e8b5..ff00f3d20 100644
--- a/src/mailman/mta/bulk.py
+++ b/src/mailman/mta/bulk.py
@@ -115,6 +115,25 @@ class BulkDelivery:
recipients = msgdata.get('recipients')
if recipients is None:
return
+ # Blow away any existing Sender and Errors-To headers and substitute
+ # our own. Our interpretation of RFC 5322 $3.6.2 is that Mailman is
+ # the "agent responsible for actual transmission of the message"
+ # because what we send to list members is different than what the
+ # original author sent. RFC 2076 says Errors-To is "non-standard,
+ # discouraged" but we include it for historical purposes.
+ del msg['sender']
+ del msg['errors-to']
+ # The message metadata can override the calculation of the sender, but
+ # otherwise it falls to the list's -bounces robot. If this message is
+ # not intended for any specific mailing list, the site owner's address
+ # is used.
+ sender = msgdata.get('sender')
+ if sender is None:
+ sender = (config.mailman.site_owner
+ if mlist is None
+ else mlist.bounces_address)
+ msg['Sender'] = sender
+ msg['Errors-To'] = sender
for recipients in self.chunkify(msgdata['recipients']):
self._connection.sendmail(
'foo@example.com', recipients, msg.as_string())
diff --git a/src/mailman/mta/docs/bulk.txt b/src/mailman/mta/docs/bulk.txt
index 42c9e386a..a612e8ac9 100644
--- a/src/mailman/mta/docs/bulk.txt
+++ b/src/mailman/mta/docs/bulk.txt
@@ -173,8 +173,7 @@ message sent, with all the recipients packed into the envelope recipients
To: test@example.com
Subject: test one
Message-ID: <aardvark>
- X-Peer: ...
- X-MailFrom: ...
+ ...
X-RcptTo: person_...
person_...
...
@@ -202,3 +201,118 @@ each with 20 addresses in the RCPT TO header.
Number of recipients: 20
Number of recipients: 20
Number of recipients: 20
+
+
+Delivery headers
+================
+
+The message delivered to bulk recipients has its Sender and Errors-To headers
+set to some Mailman-responsible agent that can handle such errors, for example
+the -bounces robot for the list or the site owner's address. The relevant
+RFCs are 5321, 5322, and 2076.
+
+It is arguably incorrect for Mailman to touch the Sender header, if the
+interpretation is such that the original author's agent is considered the
+"agent responsible for actual transmission of the message" (RFC 5322 $3.6.2).
+On the other hand, a reasonable argument can be made that Mailman is the
+responsible agent since the message being received by list members is
+different than the message sent by the original author. We make the latter
+interpretation, and thus Mailman removes all existing Sender headers and
+inserts its own.
+
+RFC 2076 states that Errors-To is "non-standard, discouraged". We treat it
+the same as Sender for historical de-facto purposes.
+
+The bulk delivery module calculates the sending agent address first from the
+message metadata...
+
+ >>> bulk = BulkDelivery()
+ >>> recipients = set(['aperson@example.com'])
+ >>> msgdata = dict(recipients=recipients,
+ ... sender='asender@example.org')
+ >>> bulk.deliver(mlist, msg, msgdata)
+ >>> message = list(smtpd.messages)[0]
+ >>> print message.as_string()
+ From: aperson@example.org
+ To: test@example.com
+ Subject: test one
+ Message-ID: <aardvark>
+ Sender: asender@example.org
+ Errors-To: asender@example.org
+ X-Peer: ...
+ X-MailFrom: foo@example.com
+ X-RcptTo: aperson@example.com
+ <BLANKLINE>
+ This is a test.
+
+...followed by the mailing list's bounces robot address...
+
+ >>> del msgdata['sender']
+ >>> bulk.deliver(mlist, msg, msgdata)
+ >>> message = list(smtpd.messages)[0]
+ >>> print message.as_string()
+ From: aperson@example.org
+ To: test@example.com
+ Subject: test one
+ Message-ID: <aardvark>
+ Sender: test-bounces@example.com
+ Errors-To: test-bounces@example.com
+ X-Peer: ...
+ X-MailFrom: foo@example.com
+ X-RcptTo: aperson@example.com
+ <BLANKLINE>
+ This is a test.
+
+...and finally the site owner, if there is no mailing list target for this
+message.
+
+ >>> config.push('site-owner', """\
+ ... [mailman]
+ ... site_owner: site-owner@example.com
+ ... """)
+
+ >>> bulk.deliver(None, msg, msgdata)
+ >>> message = list(smtpd.messages)[0]
+ >>> print message.as_string()
+ From: aperson@example.org
+ To: test@example.com
+ Subject: test one
+ Message-ID: <aardvark>
+ Sender: site-owner@example.com
+ Errors-To: site-owner@example.com
+ X-Peer: ...
+ X-MailFrom: foo@example.com
+ X-RcptTo: aperson@example.com
+ <BLANKLINE>
+ This is a test.
+
+Any existing Sender or Errors-To headers in the original message are always
+deleted first.
+
+ >>> msg = message_from_string("""\
+ ... From: bperson@example.org
+ ... To: test@example.com
+ ... Subject: test two
+ ... Message-ID: <badger>
+ ... Sender: robot@example.org
+ ... Errors-To: robot@example.org
+ ...
+ ... This is a test.
+ ... """)
+
+ >>> bulk.deliver(mlist, msg, msgdata)
+ >>> message = list(smtpd.messages)[0]
+ >>> print message.as_string()
+ From: bperson@example.org
+ To: test@example.com
+ Subject: test two
+ Message-ID: <badger>
+ Sender: test-bounces@example.com
+ Errors-To: test-bounces@example.com
+ X-Peer: ...
+ X-MailFrom: foo@example.com
+ X-RcptTo: aperson@example.com
+ <BLANKLINE>
+ This is a test.
+
+ >>> config.pop('site-owner')