summaryrefslogtreecommitdiff
path: root/src/mailman/pipeline/docs
diff options
context:
space:
mode:
Diffstat (limited to 'src/mailman/pipeline/docs')
-rw-r--r--src/mailman/pipeline/docs/ack-headers.rst43
-rw-r--r--src/mailman/pipeline/docs/acknowledge.rst174
-rw-r--r--src/mailman/pipeline/docs/after-delivery.rst30
-rw-r--r--src/mailman/pipeline/docs/archives.rst133
-rw-r--r--src/mailman/pipeline/docs/avoid-duplicates.rst175
-rw-r--r--src/mailman/pipeline/docs/calc-recips.rst114
-rw-r--r--src/mailman/pipeline/docs/cleanse.rst100
-rw-r--r--src/mailman/pipeline/docs/cook-headers.rst129
-rw-r--r--src/mailman/pipeline/docs/decorate.rst348
-rw-r--r--src/mailman/pipeline/docs/digests.rst113
-rw-r--r--src/mailman/pipeline/docs/file-recips.rst111
-rw-r--r--src/mailman/pipeline/docs/filtering.rst347
-rw-r--r--src/mailman/pipeline/docs/nntp.rst68
-rw-r--r--src/mailman/pipeline/docs/reply-to.rst131
-rw-r--r--src/mailman/pipeline/docs/replybot.rst343
-rw-r--r--src/mailman/pipeline/docs/rfc-2369.rst206
-rw-r--r--src/mailman/pipeline/docs/subject-munging.rst249
-rw-r--r--src/mailman/pipeline/docs/tagger.rst238
-rw-r--r--src/mailman/pipeline/docs/to-outgoing.rst42
19 files changed, 0 insertions, 3094 deletions
diff --git a/src/mailman/pipeline/docs/ack-headers.rst b/src/mailman/pipeline/docs/ack-headers.rst
deleted file mode 100644
index dba2169e2..000000000
--- a/src/mailman/pipeline/docs/ack-headers.rst
+++ /dev/null
@@ -1,43 +0,0 @@
-======================
-Acknowledgment headers
-======================
-
-Messages that flow through the global pipeline get their headers `cooked`,
-which basically means that their headers go through several mostly unrelated
-transformations. Some headers get added, others get changed. Some of these
-changes depend on mailing list settings and others depend on how the message
-is getting sent through the system. We'll take things one-by-one.
-
- >>> mlist = create_list('_xtest@example.com')
- >>> mlist.subject_prefix = ''
-
-When the message's metadata has a `noack` key set, an ``X-Ack: no`` header is
-added.
-::
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ...
- ... A message of great import.
- ... """)
-
- >>> from mailman.pipeline.cook_headers import process
- >>> process(mlist, msg, dict(noack=True))
- >>> print msg.as_string()
- From: aperson@example.com
- X-Ack: no
- ...
-
-Any existing ``X-Ack`` header in the original message is removed.
-
- >>> msg = message_from_string("""\
- ... X-Ack: yes
- ... From: aperson@example.com
- ...
- ... A message of great import.
- ... """)
- >>> process(mlist, msg, dict(noack=True))
- >>> print msg.as_string()
- From: aperson@example.com
- X-Ack: no
- ...
diff --git a/src/mailman/pipeline/docs/acknowledge.rst b/src/mailman/pipeline/docs/acknowledge.rst
deleted file mode 100644
index 479aa4ea6..000000000
--- a/src/mailman/pipeline/docs/acknowledge.rst
+++ /dev/null
@@ -1,174 +0,0 @@
-======================
-Message acknowledgment
-======================
-
-When a user posts a message to a mailing list, and that user has chosen to
-receive acknowledgments of their postings, Mailman will sent them such an
-acknowledgment.
-::
-
- >>> mlist = create_list('test@example.com')
- >>> mlist.display_name = 'Test'
- >>> mlist.preferred_language = 'en'
- >>> # XXX This will almost certainly change once we've worked out the web
- >>> # space layout for mailing lists now.
-
- >>> # Ensure that the virgin queue is empty, since we'll be checking this
- >>> # for new auto-response messages.
- >>> from mailman.testing.helpers import get_queue_messages
- >>> get_queue_messages('virgin')
- []
-
-Subscribe a user to the mailing list.
-::
-
- >>> from mailman.interfaces.usermanager import IUserManager
- >>> from zope.component import getUtility
- >>> user_manager = getUtility(IUserManager)
-
- >>> from mailman.interfaces.member import MemberRole
- >>> user_1 = user_manager.create_user('aperson@example.com')
- >>> address_1 = list(user_1.addresses)[0]
- >>> mlist.subscribe(address_1, MemberRole.member)
- <Member: aperson@example.com on test@example.com as MemberRole.member>
-
-
-Non-member posts
-================
-
-Non-members can't get acknowledgments of their posts to the mailing list.
-::
-
- >>> msg = message_from_string("""\
- ... From: bperson@example.com
- ...
- ... """)
-
- >>> handler = config.handlers['acknowledge']
- >>> handler.process(mlist, msg, {})
- >>> get_queue_messages('virgin')
- []
-
-We can also specify the original sender in the message's metadata. If that
-person is also not a member, no acknowledgment will be sent either.
-
- >>> msg = message_from_string("""\
- ... From: bperson@example.com
- ...
- ... """)
- >>> handler.process(mlist, msg,
- ... dict(original_sender='cperson@example.com'))
- >>> get_queue_messages('virgin')
- []
-
-
-No acknowledgment requested
-===========================
-
-Unless the user has requested acknowledgments, they will not get one.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ...
- ... """)
- >>> handler.process(mlist, msg, {})
- >>> get_queue_messages('virgin')
- []
-
-Similarly if the original sender is specified in the message metadata, and
-that sender is a member but not one who has requested acknowledgments, none
-will be sent.
-::
-
- >>> user_2 = user_manager.create_user('dperson@example.com')
- >>> address_2 = list(user_2.addresses)[0]
- >>> mlist.subscribe(address_2, MemberRole.member)
- <Member: dperson@example.com on test@example.com as MemberRole.member>
-
- >>> handler.process(mlist, msg,
- ... dict(original_sender='dperson@example.com'))
- >>> get_queue_messages('virgin')
- []
-
-
-Requested acknowledgments
-=========================
-
-If the member requests acknowledgments, Mailman will send them one when they
-post to the mailing list.
-
- >>> user_1.preferences.acknowledge_posts = True
-
-The receipt will include the original message's subject in the response body,
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... Subject: Something witty and insightful
- ...
- ... """)
- >>> handler.process(mlist, msg, {})
- >>> messages = get_queue_messages('virgin')
- >>> len(messages)
- 1
- >>> dump_msgdata(messages[0].msgdata)
- _parsemsg : False
- listname : test@example.com
- nodecorate : True
- recipients : set([u'aperson@example.com'])
- reduced_list_headers: True
- ...
- >>> print messages[0].msg.as_string()
- ...
- MIME-Version: 1.0
- ...
- Subject: Test post acknowledgment
- From: test-bounces@example.com
- To: aperson@example.com
- ...
- Precedence: bulk
- <BLANKLINE>
- Your message entitled
- <BLANKLINE>
- Something witty and insightful
- <BLANKLINE>
- was successfully received by the Test mailing list.
- <BLANKLINE>
- List info page: http://lists.example.com/listinfo/test@example.com
- Your preferences: http://example.com/aperson@example.com
- <BLANKLINE>
-
-If there is no subject, then the receipt will use a generic message.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ...
- ... """)
- >>> handler.process(mlist, msg, {})
- >>> messages = get_queue_messages('virgin')
- >>> len(messages)
- 1
- >>> dump_msgdata(messages[0].msgdata)
- _parsemsg : False
- listname : test@example.com
- nodecorate : True
- recipients : set([u'aperson@example.com'])
- reduced_list_headers: True
- ...
- >>> print messages[0].msg.as_string()
- MIME-Version: 1.0
- ...
- Subject: Test post acknowledgment
- From: test-bounces@example.com
- To: aperson@example.com
- ...
- Precedence: bulk
- <BLANKLINE>
- Your message entitled
- <BLANKLINE>
- (no subject)
- <BLANKLINE>
- was successfully received by the Test mailing list.
- <BLANKLINE>
- List info page: http://lists.example.com/listinfo/test@example.com
- Your preferences: http://example.com/aperson@example.com
- <BLANKLINE>
diff --git a/src/mailman/pipeline/docs/after-delivery.rst b/src/mailman/pipeline/docs/after-delivery.rst
deleted file mode 100644
index c3e393cf2..000000000
--- a/src/mailman/pipeline/docs/after-delivery.rst
+++ /dev/null
@@ -1,30 +0,0 @@
-==============
-After delivery
-==============
-
-After a message is delivered, or more correctly, after it has been processed
-by the rest of the handlers in the incoming queue pipeline, a couple of
-bookkeeping pieces of information are updated.
-
- >>> import datetime
- >>> mlist = create_list('_xtest@example.com')
- >>> post_time = datetime.datetime.now() - datetime.timedelta(minutes=10)
- >>> mlist.last_post_time = post_time
- >>> mlist.post_id = 10
-
-Processing a message with this handler updates the last_post_time and post_id
-attributes.
-::
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ...
- ... Something interesting.
- ... """)
-
- >>> handler = config.handlers['after-delivery']
- >>> handler.process(mlist, msg, {})
- >>> mlist.last_post_time > post_time
- True
- >>> mlist.post_id
- 11
diff --git a/src/mailman/pipeline/docs/archives.rst b/src/mailman/pipeline/docs/archives.rst
deleted file mode 100644
index 323d121e8..000000000
--- a/src/mailman/pipeline/docs/archives.rst
+++ /dev/null
@@ -1,133 +0,0 @@
-========
-Archives
-========
-
-Updating the archives with posted messages is handled by a separate queue,
-which allows for better memory management and prevents blocking the main
-delivery processes while messages are archived. This also allows external
-archivers to work in a separate process from the main Mailman delivery
-processes.
-
- >>> handler = config.handlers['to-archive']
- >>> mlist = create_list('_xtest@example.com')
- >>> switchboard = config.switchboards['archive']
-
-A helper function.
-
- >>> def clear():
- ... for filebase in switchboard.files:
- ... msg, msgdata = switchboard.dequeue(filebase)
- ... switchboard.finish(filebase)
-
-The purpose of this handler is to make a simple decision as to whether the
-message should get archived and if so, to drop the message in the archiving
-queue. Really the most important things are to determine when a message
-should *not* get archived.
-
-For example, no digests should ever get archived.
-
- >>> mlist.archive = True
- >>> msg = message_from_string("""\
- ... Subject: A sample message
- ...
- ... A message of great import.
- ... """)
- >>> handler.process(mlist, msg, dict(isdigest=True))
- >>> switchboard.files
- []
-
-If the mailing list is not configured to archive, then even regular deliveries
-won't be archived.
-
- >>> mlist.archive = False
- >>> handler.process(mlist, msg, {})
- >>> switchboard.files
- []
-
-There are two de-facto standards for a message to indicate that it does not
-want to be archived. We've seen both in the wild so both are supported. The
-``X-No-Archive:`` header can be used to indicate that the message should not
-be archived. Confusingly, this header's value is actually ignored.
-
- >>> mlist.archive = True
- >>> msg = message_from_string("""\
- ... Subject: A sample message
- ... X-No-Archive: YES
- ...
- ... A message of great import.
- ... """)
- >>> handler.process(mlist, msg, dict(isdigest=True))
- >>> switchboard.files
- []
-
-Even a ``no`` value will stop the archiving of the message.
-
- >>> msg = message_from_string("""\
- ... Subject: A sample message
- ... X-No-Archive: No
- ...
- ... A message of great import.
- ... """)
- >>> handler.process(mlist, msg, dict(isdigest=True))
- >>> switchboard.files
- []
-
-Another header that's been observed is the ``X-Archive:`` header. Here, the
-header's case folded value must be ``no`` in order to prevent archiving.
-
- >>> msg = message_from_string("""\
- ... Subject: A sample message
- ... X-Archive: No
- ...
- ... A message of great import.
- ... """)
- >>> handler.process(mlist, msg, dict(isdigest=True))
- >>> switchboard.files
- []
-
-But if the value is ``yes``, then the message will be archived.
-
- >>> msg = message_from_string("""\
- ... Subject: A sample message
- ... X-Archive: Yes
- ...
- ... A message of great import.
- ... """)
- >>> handler.process(mlist, msg, {})
- >>> len(switchboard.files)
- 1
- >>> filebase = switchboard.files[0]
- >>> qmsg, qdata = switchboard.dequeue(filebase)
- >>> switchboard.finish(filebase)
- >>> print qmsg.as_string()
- Subject: A sample message
- X-Archive: Yes
- <BLANKLINE>
- A message of great import.
- <BLANKLINE>
- >>> dump_msgdata(qdata)
- _parsemsg: False
- version : 3
-
-Without either archiving header, and all other things being the same, the
-message will get archived.
-
- >>> msg = message_from_string("""\
- ... Subject: A sample message
- ...
- ... A message of great import.
- ... """)
- >>> handler.process(mlist, msg, {})
- >>> len(switchboard.files)
- 1
- >>> filebase = switchboard.files[0]
- >>> qmsg, qdata = switchboard.dequeue(filebase)
- >>> switchboard.finish(filebase)
- >>> print qmsg.as_string()
- Subject: A sample message
- <BLANKLINE>
- A message of great import.
- <BLANKLINE>
- >>> dump_msgdata(qdata)
- _parsemsg: False
- version : 3
diff --git a/src/mailman/pipeline/docs/avoid-duplicates.rst b/src/mailman/pipeline/docs/avoid-duplicates.rst
deleted file mode 100644
index 1e46793c2..000000000
--- a/src/mailman/pipeline/docs/avoid-duplicates.rst
+++ /dev/null
@@ -1,175 +0,0 @@
-================
-Avoid duplicates
-================
-
-This handler implements several strategies to reduce the reception of
-duplicate messages. It does this by removing certain recipients from the list
-of recipients calculated earlier.
-
- >>> mlist = create_list('_xtest@example.com')
-
-Create some members we're going to use.
-::
-
- >>> from mailman.interfaces.usermanager import IUserManager
- >>> from zope.component import getUtility
- >>> user_manager = getUtility(IUserManager)
-
- >>> address_a = user_manager.create_address('aperson@example.com')
- >>> address_b = user_manager.create_address('bperson@example.com')
-
- >>> from mailman.interfaces.member import MemberRole
- >>> member_a = mlist.subscribe(address_a, MemberRole.member)
- >>> member_b = mlist.subscribe(address_b, MemberRole.member)
- >>> # This is the message metadata dictionary as it would be produced by
- >>> # the CalcRecips handler.
- >>> recips = dict(
- ... recipients=['aperson@example.com', 'bperson@example.com'])
-
-
-Short circuiting
-================
-
-The module short-circuits if there are no recipients.
-::
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... Subject: A message of great import
- ...
- ... Something
- ... """)
- >>> msgdata = {}
-
- >>> handler = config.handlers['avoid-duplicates']
- >>> handler.process(mlist, msg, msgdata)
- >>> msgdata
- {}
- >>> print msg.as_string()
- From: aperson@example.com
- Subject: A message of great import
- <BLANKLINE>
- Something
- <BLANKLINE>
-
-
-Suppressing the list copy
-=========================
-
-Members can elect not to receive a list copy of any message on which they are
-explicitly named as a recipient. This is done by setting their
-``receive_list_copy`` preference to ``False``. However, if they aren't
-mentioned in one of the recipient headers (i.e. ``To``, ``CC``, ``Resent-To``,
-or ``Resent-CC``), then they will get a list copy.
-
- >>> member_a.preferences.receive_list_copy = False
- >>> msg = message_from_string("""\
- ... From: Claire Person <cperson@example.com>
- ...
- ... Something of great import.
- ... """)
- >>> msgdata = recips.copy()
- >>> handler.process(mlist, msg, msgdata)
- >>> sorted(msgdata['recipients'])
- [u'aperson@example.com', u'bperson@example.com']
- >>> print msg.as_string()
- From: Claire Person <cperson@example.com>
- <BLANKLINE>
- Something of great import.
- <BLANKLINE>
-
-If they're mentioned on the ``CC`` line, they won't get a list copy.
-
- >>> msg = message_from_string("""\
- ... From: Claire Person <cperson@example.com>
- ... CC: aperson@example.com
- ...
- ... Something of great import.
- ... """)
- >>> msgdata = recips.copy()
- >>> handler.process(mlist, msg, msgdata)
- >>> sorted(msgdata['recipients'])
- [u'bperson@example.com']
- >>> print msg.as_string()
- From: Claire Person <cperson@example.com>
- CC: aperson@example.com
- <BLANKLINE>
- Something of great import.
- <BLANKLINE>
-
-But if they're mentioned on the ``CC`` line and have ``receive_list_copy`` set
-to ``True`` (the default), then they still get a list copy.
-
- >>> msg = message_from_string("""\
- ... From: Claire Person <cperson@example.com>
- ... CC: bperson@example.com
- ...
- ... Something of great import.
- ... """)
- >>> msgdata = recips.copy()
- >>> handler.process(mlist, msg, msgdata)
- >>> sorted(msgdata['recipients'])
- [u'aperson@example.com', u'bperson@example.com']
- >>> print msg.as_string()
- From: Claire Person <cperson@example.com>
- CC: bperson@example.com
- <BLANKLINE>
- Something of great import.
- <BLANKLINE>
-
-Other headers checked for recipients include the ``To``...
-
- >>> msg = message_from_string("""\
- ... From: Claire Person <cperson@example.com>
- ... To: aperson@example.com
- ...
- ... Something of great import.
- ... """)
- >>> msgdata = recips.copy()
- >>> handler.process(mlist, msg, msgdata)
- >>> sorted(msgdata['recipients'])
- [u'bperson@example.com']
- >>> print msg.as_string()
- From: Claire Person <cperson@example.com>
- To: aperson@example.com
- <BLANKLINE>
- Something of great import.
- <BLANKLINE>
-
-... ``Resent-To`` ...
-
- >>> msg = message_from_string("""\
- ... From: Claire Person <cperson@example.com>
- ... Resent-To: aperson@example.com
- ...
- ... Something of great import.
- ... """)
- >>> msgdata = recips.copy()
- >>> handler.process(mlist, msg, msgdata)
- >>> sorted(msgdata['recipients'])
- [u'bperson@example.com']
- >>> print msg.as_string()
- From: Claire Person <cperson@example.com>
- Resent-To: aperson@example.com
- <BLANKLINE>
- Something of great import.
- <BLANKLINE>
-
-...and ``Resent-CC`` headers.
-
- >>> msg = message_from_string("""\
- ... From: Claire Person <cperson@example.com>
- ... Resent-Cc: aperson@example.com
- ...
- ... Something of great import.
- ... """)
- >>> msgdata = recips.copy()
- >>> handler.process(mlist, msg, msgdata)
- >>> sorted(msgdata['recipients'])
- [u'bperson@example.com']
- >>> print msg.as_string()
- From: Claire Person <cperson@example.com>
- Resent-Cc: aperson@example.com
- <BLANKLINE>
- Something of great import.
- <BLANKLINE>
diff --git a/src/mailman/pipeline/docs/calc-recips.rst b/src/mailman/pipeline/docs/calc-recips.rst
deleted file mode 100644
index 6dca85816..000000000
--- a/src/mailman/pipeline/docs/calc-recips.rst
+++ /dev/null
@@ -1,114 +0,0 @@
-======================
-Calculating recipients
-======================
-
-Every message that makes it through to the list membership gets sent to a set
-of recipient addresses. These addresses are calculated by one of the handler
-modules and depends on a host of factors.
-
- >>> mlist = create_list('_xtest@example.com')
-
-Recipients are calculate from the list members, so add a bunch of members to
-start out with. First, create a bunch of addresses...
-::
-
- >>> from mailman.interfaces.usermanager import IUserManager
- >>> from zope.component import getUtility
- >>> user_manager = getUtility(IUserManager)
-
- >>> address_a = user_manager.create_address('aperson@example.com')
- >>> address_b = user_manager.create_address('bperson@example.com')
- >>> address_c = user_manager.create_address('cperson@example.com')
- >>> address_d = user_manager.create_address('dperson@example.com')
- >>> address_e = user_manager.create_address('eperson@example.com')
- >>> address_f = user_manager.create_address('fperson@example.com')
-
-...then subscribe these addresses to the mailing list as members...
-
- >>> from mailman.interfaces.member import MemberRole
- >>> member_a = mlist.subscribe(address_a, MemberRole.member)
- >>> member_b = mlist.subscribe(address_b, MemberRole.member)
- >>> member_c = mlist.subscribe(address_c, MemberRole.member)
- >>> member_d = mlist.subscribe(address_d, MemberRole.member)
- >>> member_e = mlist.subscribe(address_e, MemberRole.member)
- >>> member_f = mlist.subscribe(address_f, MemberRole.member)
-
-...then make some of the members digest members.
-
- >>> from mailman.core.constants import DeliveryMode
- >>> member_d.preferences.delivery_mode = DeliveryMode.plaintext_digests
- >>> member_e.preferences.delivery_mode = DeliveryMode.mime_digests
- >>> member_f.preferences.delivery_mode = DeliveryMode.summary_digests
-
-
-Short-circuiting
-================
-
-Sometimes, the list of recipients already exists in the message metadata.
-This can happen for example, when a message was previously delivered to some
-but not all of the recipients.
-::
-
- >>> msg = message_from_string("""\
- ... From: Xavier Person <xperson@example.com>
- ...
- ... Something of great import.
- ... """)
- >>> recipients = set(('qperson@example.com', 'zperson@example.com'))
- >>> msgdata = dict(recipients=recipients)
-
- >>> handler = config.handlers['calculate-recipients']
- >>> handler.process(mlist, msg, msgdata)
- >>> dump_list(msgdata['recipients'])
- qperson@example.com
- zperson@example.com
-
-
-Regular delivery recipients
-===========================
-
-Regular delivery recipients are those people who get messages from the list as
-soon as they are posted. In other words, these folks are not digest members.
-
- >>> msgdata = {}
- >>> handler.process(mlist, msg, msgdata)
- >>> dump_list(msgdata['recipients'])
- aperson@example.com
- bperson@example.com
- cperson@example.com
-
-Members can elect not to receive a list copy of their own postings.
-
- >>> member_c.preferences.receive_own_postings = False
- >>> msg = message_from_string("""\
- ... From: Claire Person <cperson@example.com>
- ...
- ... Something of great import.
- ... """)
- >>> msgdata = {}
- >>> handler.process(mlist, msg, msgdata)
- >>> dump_list(msgdata['recipients'])
- aperson@example.com
- bperson@example.com
-
-Members can also elect not to receive a list copy of any message on which they
-are explicitly named as a recipient. However, see the `avoid duplicates`_
-handler for details.
-
-
-Digest recipients
-=================
-
-XXX Test various digest deliveries.
-
-
-Urgent messages
-===============
-
-XXX Test various urgent deliveries:
- * test_urgent_moderator()
- * test_urgent_admin()
- * test_urgent_reject()
-
-
-.. _`avoid duplicates`: avoid-duplicates.html
diff --git a/src/mailman/pipeline/docs/cleanse.rst b/src/mailman/pipeline/docs/cleanse.rst
deleted file mode 100644
index 61dfa8f52..000000000
--- a/src/mailman/pipeline/docs/cleanse.rst
+++ /dev/null
@@ -1,100 +0,0 @@
-=================
-Cleansing headers
-=================
-
-All messages posted to a list get their headers cleansed. Some headers are
-related to additional permissions that can be granted to the message and other
-headers can be used to fish for membership.
-
- >>> mlist = create_list('_xtest@example.com')
-
-Headers such as ``Approved``, ``Approve``, (as well as their ``X-`` variants)
-and ``Urgent`` are used to grant special permissions to individual messages.
-All may contain a password; the first two headers are used by list
-administrators to pre-approve a message normal held for approval. The latter
-header is used to send a regular message to all members, regardless of whether
-they get digests or not. Because all three headers contain passwords, they
-must be removed from any posted message. ::
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... Approved: foobar
- ... Approve: barfoo
- ... X-Approved: bazbar
- ... X-Approve: barbaz
- ... Urgent: notreally
- ... Subject: A message of great import
- ...
- ... Blah blah blah
- ... """)
-
- >>> handler = config.handlers['cleanse']
- >>> handler.process(mlist, msg, {})
- >>> print msg.as_string()
- From: aperson@example.com
- Subject: A message of great import
- <BLANKLINE>
- Blah blah blah
- <BLANKLINE>
-
-Other headers can be used by list members to fish the list for membership, so
-we don't let them go through. These are a mix of standard headers and custom
-headers supported by some mail readers. For example, ``X-PMRC`` is supported
-by Pegasus mail. I don't remember what program uses ``X-Confirm-Reading-To``
-though (Some Microsoft product perhaps?).
-
- >>> msg = message_from_string("""\
- ... From: bperson@example.com
- ... Reply-To: bperson@example.org
- ... Sender: asystem@example.net
- ... Return-Receipt-To: another@example.com
- ... Disposition-Notification-To: athird@example.com
- ... X-Confirm-Reading-To: afourth@example.com
- ... X-PMRQC: afifth@example.com
- ... Subject: a message to you
- ...
- ... How are you doing?
- ... """)
- >>> handler.process(mlist, msg, {})
- >>> print msg.as_string()
- From: bperson@example.com
- Reply-To: bperson@example.org
- Sender: asystem@example.net
- Subject: a message to you
- <BLANKLINE>
- How are you doing?
- <BLANKLINE>
-
-
-Anonymous lists
-===============
-
-Anonymous mailing lists also try to cleanse certain identifying headers from
-the original posting, so that it is at least a bit more difficult to determine
-who sent the message. This isn't perfect though, for example, the body of the
-messages are never scrubbed (though that might not be a bad idea). The
-``From`` and ``Reply-To`` headers in the posted message are taken from list
-attributes.
-
-Hotmail apparently sets ``X-Originating-Email``.
-
- >>> mlist.anonymous_list = True
- >>> mlist.description = 'A Test Mailing List'
- >>> mlist.preferred_language = 'en'
- >>> msg = message_from_string("""\
- ... From: bperson@example.com
- ... Reply-To: bperson@example.org
- ... Sender: asystem@example.net
- ... X-Originating-Email: cperson@example.com
- ... Subject: a message to you
- ...
- ... How are you doing?
- ... """)
- >>> handler.process(mlist, msg, {})
- >>> print msg.as_string()
- Subject: a message to you
- From: A Test Mailing List <_xtest@example.com>
- Reply-To: _xtest@example.com
- <BLANKLINE>
- How are you doing?
- <BLANKLINE>
diff --git a/src/mailman/pipeline/docs/cook-headers.rst b/src/mailman/pipeline/docs/cook-headers.rst
deleted file mode 100644
index e0313f53a..000000000
--- a/src/mailman/pipeline/docs/cook-headers.rst
+++ /dev/null
@@ -1,129 +0,0 @@
-===============
-Cooking headers
-===============
-
-Messages that flow through the global pipeline get their headers 'cooked',
-which basically means that their headers go through several mostly unrelated
-transformations. Some headers get added, others get changed. Some of these
-changes depend on mailing list settings and others depend on how the message
-is getting sent through the system. We'll take things one-by-one.
-
- >>> mlist = create_list('test@example.com')
- >>> mlist.subject_prefix = ''
-
-
-Saving the original sender
-==========================
-
-Because the original sender headers may get deleted or changed, this handler
-will place the sender in the message metadata for safe keeping.
-::
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ...
- ... A message of great import.
- ... """)
- >>> msgdata = {}
-
- >>> from mailman.pipeline.cook_headers import process
- >>> process(mlist, msg, msgdata)
- >>> print msgdata['original_sender']
- aperson@example.com
-
-But if there was no original sender, then the empty string will be saved.
-
- >>> msg = message_from_string("""\
- ... Subject: No original sender
- ...
- ... A message of great import.
- ... """)
- >>> msgdata = {}
- >>> process(mlist, msg, msgdata)
- >>> print msgdata['original_sender']
- <BLANKLINE>
-
-
-Mailman version header
-======================
-
-Mailman will also insert an ``X-Mailman-Version`` header...
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ...
- ... A message of great import.
- ... """)
- >>> process(mlist, msg, {})
- >>> from mailman.version import VERSION
- >>> msg['x-mailman-version'] == VERSION
- True
-
-...but only if one doesn't already exist.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... X-Mailman-Version: 3000
- ...
- ... A message of great import.
- ... """)
- >>> process(mlist, msg, {})
- >>> print msg['x-mailman-version']
- 3000
-
-
-Precedence header
-=================
-
-Mailman will insert a ``Precedence`` header, which is a de-facto standard for
-telling automatic reply software (e.g. ``vacation(1)``) not to respond to this
-message.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ...
- ... A message of great import.
- ... """)
- >>> process(mlist, msg, {})
- >>> print msg['precedence']
- list
-
-But Mailman will only add that header if the original message doesn't already
-have one of them.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... Precedence: junk
- ...
- ... A message of great import.
- ... """)
- >>> process(mlist, msg, {})
- >>> print msg['precedence']
- junk
-
-
-Personalization
-===============
-
-The ``To`` field normally contains the list posting address. However when
-messages are fully personalized, that header will get overwritten with the
-address of the recipient. The list's posting address will be added to one of
-the recipient headers so that users will be able to reply back to the list.
-
- >>> from mailman.interfaces.mailinglist import (
- ... Personalization, ReplyToMunging)
- >>> mlist.personalize = Personalization.full
- >>> mlist.reply_goes_to_list = ReplyToMunging.no_munging
- >>> mlist.description = 'My test mailing list'
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ...
- ... """)
- >>> process(mlist, msg, {})
- >>> print msg.as_string()
- From: aperson@example.com
- X-Mailman-Version: ...
- Precedence: list
- Cc: My test mailing list <test@example.com>
- <BLANKLINE>
- <BLANKLINE>
diff --git a/src/mailman/pipeline/docs/decorate.rst b/src/mailman/pipeline/docs/decorate.rst
deleted file mode 100644
index 6fa8212ac..000000000
--- a/src/mailman/pipeline/docs/decorate.rst
+++ /dev/null
@@ -1,348 +0,0 @@
-==================
-Message decoration
-==================
-
-Message decoration is the process of adding headers and footers to the
-original message. A handler module takes care of this based on the settings
-of the mailing list and the type of message being processed.
-
- >>> mlist = create_list('_xtest@example.com')
- >>> msg_text = """\
- ... From: aperson@example.org
- ...
- ... Here is a message.
- ... """
- >>> msg = message_from_string(msg_text)
-
-
-Short circuiting
-================
-
-Digest messages get decorated during the digest creation phase so no extra
-decorations are added for digest messages.
-::
-
- >>> from mailman.pipeline.decorate import process
- >>> process(mlist, msg, dict(isdigest=True))
- >>> print msg.as_string()
- From: aperson@example.org
- <BLANKLINE>
- Here is a message.
-
- >>> process(mlist, msg, dict(nodecorate=True))
- >>> print msg.as_string()
- From: aperson@example.org
- <BLANKLINE>
- Here is a message.
-
-
-Simple decorations
-==================
-
-Message decorations are specified by URI and can be specialized by the mailing
-list and language. Internal Mailman decorations can be referenced by using
-the ``mailman://`` URL scheme. Here we create a simple English header and
-footer for all mailing lists in our site.
-::
-
- >>> import os, tempfile
- >>> template_dir = tempfile.mkdtemp()
- >>> site_dir = os.path.join(template_dir, 'site', 'en')
- >>> os.makedirs(site_dir)
- >>> config.push('templates', """
- ... [paths.testing]
- ... template_dir: {0}
- ... """.format(template_dir))
-
- >>> myheader_path = os.path.join(site_dir, 'myheader.txt')
- >>> with open(myheader_path, 'w') as fp:
- ... print >> fp, 'header'
- >>> myfooter_path = os.path.join(site_dir, 'myfooter.txt')
- >>> with open(myfooter_path, 'w') as fp:
- ... print >> fp, 'footer'
-
-Setting these attributes on the mailing list causes it to use these
-templates. Since these are site-global templates, we can use a shorter path.
-
- >>> mlist.header_uri = 'mailman:///myheader.txt'
- >>> mlist.footer_uri = 'mailman:///myfooter.txt'
-
-Text messages that have no declared content type are, by default encoded in
-ASCII. When the mailing list's preferred language is ``en`` (i.e. English),
-the character set of the mailing list and of the message will match, allowing
-Mailman to simply prepend the header and append the footer verbatim.
-
- >>> mlist.preferred_language = 'en'
- >>> process(mlist, msg, {})
- >>> print msg.as_string()
- From: aperson@example.org
- ...
- <BLANKLINE>
- header
- Here is a message.
- footer
-
-Mailman supports a number of interpolation variables, placeholders in the
-header and footer for information to be filled in with mailing list specific
-data. An example of such information is the mailing list's `real name` (a
-short descriptive name for the mailing list).
-::
-
- >>> with open(myheader_path, 'w') as fp:
- ... print >> fp, '$display_name header'
- >>> with open(myfooter_path, 'w') as fp:
- ... print >> fp, '$display_name footer'
-
- >>> msg = message_from_string(msg_text)
- >>> mlist.display_name = 'XTest'
- >>> process(mlist, msg, {})
- >>> print msg.as_string()
- From: aperson@example.org
- ...
- XTest header
- Here is a message.
- XTest footer
-
-You can't just pick any interpolation variable though; if you do, the variable
-will remain in the header or footer unchanged.
-::
-
- >>> with open(myheader_path, 'w') as fp:
- ... print >> fp, '$dummy header'
- >>> with open(myfooter_path, 'w') as fp:
- ... print >> fp, '$dummy footer'
-
- >>> msg = message_from_string(msg_text)
- >>> process(mlist, msg, {})
- >>> print msg.as_string()
- From: aperson@example.org
- ...
- $dummy header
- Here is a message.
- $dummy footer
-
-
-Handling RFC 3676 'format=flowed' parameters
-============================================
-
-RFC 3676 describes a standard by which text/plain messages can marked by
-generating MUAs for better readability in compatible receiving MUAs. The
-``format`` parameter on the text/plain ``Content-Type`` header gives hints as
-to how the receiving MUA may flow and delete trailing whitespace for better
-display in a proportional font.
-
-When Mailman sees text/plain messages with such RFC 3676 parameters, it
-preserves these parameters when it concatenates headers and footers to the
-message payload.
-::
-
- >>> with open(myheader_path, 'w') as fp:
- ... print >> fp, 'header'
- >>> with open(myfooter_path, 'w') as fp:
- ... print >> fp, 'footer'
-
- >>> mlist.preferred_language = 'en'
- >>> msg = message_from_string("""\
- ... From: aperson@example.org
- ... Content-Type: text/plain; format=flowed; delsp=no
- ...
- ... Here is a message\x20
- ... with soft line breaks.
- ... """)
- >>> process(mlist, msg, {})
- >>> # Don't use 'print' here as above because it won't be obvious from the
- >>> # output that the soft-line break space at the end of the 'Here is a
- >>> # message' line will be retained in the output.
- >>> print msg['content-type']
- text/plain; format="flowed"; delsp="no"; charset="us-ascii"
- >>> for line in msg.get_payload().splitlines():
- ... print '>{0}<'.format(line)
- >header<
- >Here is a message <
- >with soft line breaks.<
- >footer<
-
-
-Decorating mixed-charset messages
-=================================
-
-When a message has no explicit character set, it is assumed to be ASCII.
-However, if the mailing list's preferred language has a different character
-set, Mailman will still try to concatenate the header and footer, but it will
-convert the text to utf-8 and base-64 encode the message payload.
-::
-
- # 'ja' = Japanese; charset = 'euc-jp'
- >>> mlist.preferred_language = 'ja'
-
- >>> with open(myheader_path, 'w') as fp:
- ... print >> fp, '$description header'
- >>> with open(myfooter_path, 'w') as fp:
- ... print >> fp, '$description footer'
- >>> mlist.description = '\u65e5\u672c\u8a9e'
-
- >>> from email.message import Message
- >>> msg = Message()
- >>> msg.set_payload('Fran\xe7aise', 'iso-8859-1')
- >>> print msg.as_string()
- MIME-Version: 1.0
- Content-Type: text/plain; charset="iso-8859-1"
- Content-Transfer-Encoding: quoted-printable
- <BLANKLINE>
- Fran=E7aise
- >>> process(mlist, msg, {})
- >>> print msg.as_string()
- MIME-Version: 1.0
- Content-Type: text/plain; charset="utf-8"
- Content-Transfer-Encoding: base64
- <BLANKLINE>
- 5pel5pys6KqeIGhlYWRlcgpGcmFuw6dhaXNlCuaXpeacrOiqniBmb290ZXIK
-
-Sometimes the message even has an unknown character set. In this case,
-Mailman has no choice but to decorate the original message with MIME
-attachments.
-::
-
- >>> mlist.preferred_language = 'en'
- >>> with open(myheader_path, 'w') as fp:
- ... print >> fp, 'header'
- >>> with open(myfooter_path, 'w') as fp:
- ... print >> fp, 'footer'
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.org
- ... Content-Type: text/plain; charset=unknown
- ... Content-Transfer-Encoding: 7bit
- ...
- ... Here is a message.
- ... """)
-
- >>> process(mlist, msg, {})
- >>> msg.set_boundary('BOUNDARY')
- >>> print msg.as_string()
- From: aperson@example.org
- Content-Type: multipart/mixed; boundary="BOUNDARY"
- <BLANKLINE>
- --BOUNDARY
- Content-Type: text/plain; charset="us-ascii"
- MIME-Version: 1.0
- Content-Transfer-Encoding: 7bit
- Content-Disposition: inline
- <BLANKLINE>
- header
- --BOUNDARY
- Content-Type: text/plain; charset=unknown
- Content-Transfer-Encoding: 7bit
- <BLANKLINE>
- Here is a message.
- <BLANKLINE>
- --BOUNDARY
- Content-Type: text/plain; charset="us-ascii"
- MIME-Version: 1.0
- Content-Transfer-Encoding: 7bit
- Content-Disposition: inline
- <BLANKLINE>
- footer
- --BOUNDARY--
-
-
-Decorating multipart messages
-=============================
-
-Multipart messages have to be decorated differently. The header and footer
-cannot be simply concatenated into the payload because that will break the
-MIME structure of the message. Instead, the header and footer are attached as
-separate MIME subparts.
-
-When the outer part is ``multipart/mixed``, the header and footer can have a
-``Content-Disposition`` of ``inline`` so that MUAs can display these headers
-as if they were simply concatenated.
-
- >>> part_1 = message_from_string("""\
- ... From: aperson@example.org
- ...
- ... Here is the first message.
- ... """)
- >>> part_2 = message_from_string("""\
- ... From: bperson@example.com
- ...
- ... Here is the second message.
- ... """)
- >>> from email.mime.multipart import MIMEMultipart
- >>> msg = MIMEMultipart('mixed', boundary='BOUNDARY',
- ... _subparts=(part_1, part_2))
- >>> process(mlist, msg, {})
- >>> print msg.as_string()
- Content-Type: multipart/mixed; boundary="BOUNDARY"
- MIME-Version: 1.0
- <BLANKLINE>
- --BOUNDARY
- Content-Type: text/plain; charset="us-ascii"
- MIME-Version: 1.0
- Content-Transfer-Encoding: 7bit
- Content-Disposition: inline
- <BLANKLINE>
- header
- --BOUNDARY
- From: aperson@example.org
- <BLANKLINE>
- Here is the first message.
- <BLANKLINE>
- --BOUNDARY
- From: bperson@example.com
- <BLANKLINE>
- Here is the second message.
- <BLANKLINE>
- --BOUNDARY
- Content-Type: text/plain; charset="us-ascii"
- MIME-Version: 1.0
- Content-Transfer-Encoding: 7bit
- Content-Disposition: inline
- <BLANKLINE>
- footer
- --BOUNDARY--
-
-
-Decorating other content types
-==============================
-
-Non-multipart non-text content types will get wrapped in a ``multipart/mixed``
-so that the header and footer can be added as attachments.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.org
- ... Content-Type: image/x-beautiful
- ...
- ... IMAGEDATAIMAGEDATAIMAGEDATA
- ... """)
- >>> process(mlist, msg, {})
- >>> msg.set_boundary('BOUNDARY')
- >>> print msg.as_string()
- From: aperson@example.org
- ...
- --BOUNDARY
- Content-Type: text/plain; charset="us-ascii"
- MIME-Version: 1.0
- Content-Transfer-Encoding: 7bit
- Content-Disposition: inline
- <BLANKLINE>
- header
- --BOUNDARY
- Content-Type: image/x-beautiful
- <BLANKLINE>
- IMAGEDATAIMAGEDATAIMAGEDATA
- <BLANKLINE>
- --BOUNDARY
- Content-Type: text/plain; charset="us-ascii"
- MIME-Version: 1.0
- Content-Transfer-Encoding: 7bit
- Content-Disposition: inline
- <BLANKLINE>
- footer
- --BOUNDARY--
-
-.. Clean up
-
- >>> config.pop('templates')
- >>> import shutil
- >>> shutil.rmtree(template_dir)
diff --git a/src/mailman/pipeline/docs/digests.rst b/src/mailman/pipeline/docs/digests.rst
deleted file mode 100644
index d4d563180..000000000
--- a/src/mailman/pipeline/docs/digests.rst
+++ /dev/null
@@ -1,113 +0,0 @@
-=======
-Digests
-=======
-
-Digests are a way for a user to receive list traffic in collections instead of
-as individual messages when immediately posted. There are several forms of
-digests, although only two are currently supported: MIME digests and RFC 1153
-(a.k.a. plain text) digests.
-
- >>> mlist = create_list('xtest@example.com')
-
-This is a helper function used to iterate through all the accumulated digest
-messages, in the order in which they were posted. This makes it easier to
-update the tests when we switch to a different mailbox format.
-::
-
- >>> from mailman.testing.helpers import digest_mbox
- >>> from itertools import count
- >>> from string import Template
-
- >>> def message_factory():
- ... for i in count(1):
- ... text = Template("""\
- ... From: aperson@example.com
- ... To: xtest@example.com
- ... Subject: Test message $i
- ...
- ... Here is message $i
- ... """).substitute(i=i)
- ... yield message_from_string(text)
- >>> message_factory = message_factory()
-
-
-Short circuiting
-================
-
-When a message is posted to the mailing list, it is generally added to a
-mailbox, unless the mailing list does not allow digests.
-
- >>> mlist.digestable = False
- >>> msg = next(message_factory)
- >>> process = config.handlers['to-digest'].process
- >>> process(mlist, msg, {})
- >>> sum(1 for msg in digest_mbox(mlist))
- 0
- >>> digest_queue = config.switchboards['digest']
- >>> digest_queue.files
- []
-
-...or they may allow digests but the message is already a digest.
-
- >>> mlist.digestable = True
- >>> process(mlist, msg, dict(isdigest=True))
- >>> sum(1 for msg in digest_mbox(mlist))
- 0
- >>> digest_queue.files
- []
-
-
-Sending a digest
-================
-
-For messages which are not digests, but which are posted to a digesting
-mailing list, the messages will be stored until they reach a criteria
-triggering the sending of the digest. If none of those criteria are met, then
-the message will just sit in the mailbox for a while.
-
- >>> mlist.digest_size_threshold = 10000
- >>> process(mlist, msg, {})
- >>> digest_queue.files
- []
- >>> digest = digest_mbox(mlist)
- >>> sum(1 for msg in digest)
- 1
- >>> import os
- >>> os.remove(digest._path)
-
-When the size of the digest mailbox reaches the maximum size threshold, a
-marker message is placed into the digest runner's queue. The digest is not
-actually crafted by the handler.
-
- >>> mlist.digest_size_threshold = 1
- >>> mlist.volume = 2
- >>> mlist.next_digest_number = 10
- >>> size = 0
- >>> for msg in message_factory:
- ... process(mlist, msg, {})
- ... size += len(str(msg))
- ... if size >= mlist.digest_size_threshold * 1024:
- ... break
-
- >>> sum(1 for msg in digest_mbox(mlist))
- 0
- >>> len(digest_queue.files)
- 1
-
-The digest has been moved to a unique file.
-
- >>> from mailman.utilities.mailbox import Mailbox
- >>> from mailman.testing.helpers import get_queue_messages
- >>> item = get_queue_messages('digest')[0]
- >>> for msg in Mailbox(item.msgdata['digest_path']):
- ... print msg['subject']
- Test message 2
- Test message 3
- Test message 4
- Test message 5
- Test message 6
- Test message 7
- Test message 8
- Test message 9
-
-Digests are actually crafted and sent by a separate digest runner.
diff --git a/src/mailman/pipeline/docs/file-recips.rst b/src/mailman/pipeline/docs/file-recips.rst
deleted file mode 100644
index 7d157ccc5..000000000
--- a/src/mailman/pipeline/docs/file-recips.rst
+++ /dev/null
@@ -1,111 +0,0 @@
-===============
-File recipients
-===============
-
-Mailman can calculate the recipients for a message from a Sendmail-style
-include file. This file must be called ``members.txt`` and it must live in
-the list's data directory.
-
- >>> mlist = create_list('_xtest@example.com')
-
-
-Short circuiting
-================
-
-If the message's metadata already has recipients, this handler immediately
-returns.
-::
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ...
- ... A message.
- ... """)
- >>> msgdata = {'recipients': 7}
-
- >>> handler = config.handlers['file-recipients']
- >>> handler.process(mlist, msg, msgdata)
- >>> print msg.as_string()
- From: aperson@example.com
- <BLANKLINE>
- A message.
- <BLANKLINE>
- >>> dump_msgdata(msgdata)
- recipients: 7
-
-
-Missing file
-============
-
-The include file must live inside the list's data directory, under the name
-``members.txt``. If the file doesn't exist, the list of recipients will be
-empty.
-
- >>> import os
- >>> file_path = os.path.join(mlist.data_path, 'members.txt')
- >>> open(file_path)
- Traceback (most recent call last):
- ...
- IOError: [Errno ...]
- No such file or directory: u'.../_xtest@example.com/members.txt'
- >>> msgdata = {}
- >>> handler.process(mlist, msg, msgdata)
- >>> dump_list(msgdata['recipients'])
- *Empty*
-
-
-Existing file
-=============
-
-If the file exists, it contains a list of addresses, one per line. These
-addresses are returned as the set of recipients.
-::
-
- >>> fp = open(file_path, 'w')
- >>> try:
- ... print >> fp, 'bperson@example.com'
- ... print >> fp, 'cperson@example.com'
- ... print >> fp, 'dperson@example.com'
- ... print >> fp, 'eperson@example.com'
- ... print >> fp, 'fperson@example.com'
- ... print >> fp, 'gperson@example.com'
- ... finally:
- ... fp.close()
-
- >>> msgdata = {}
- >>> handler.process(mlist, msg, msgdata)
- >>> dump_list(msgdata['recipients'])
- bperson@example.com
- cperson@example.com
- dperson@example.com
- eperson@example.com
- fperson@example.com
- gperson@example.com
-
-However, if the sender of the original message is a member of the list and
-their address is in the include file, the sender's address is *not* included
-in the recipients list.
-::
-
- >>> from mailman.interfaces.usermanager import IUserManager
- >>> from zope.component import getUtility
- >>> address_1 = getUtility(IUserManager).create_address(
- ... 'cperson@example.com')
-
- >>> from mailman.interfaces.member import MemberRole
- >>> mlist.subscribe(address_1, MemberRole.member)
- <Member: cperson@example.com on _xtest@example.com as MemberRole.member>
-
- >>> msg = message_from_string("""\
- ... From: cperson@example.com
- ...
- ... A message.
- ... """)
- >>> msgdata = {}
- >>> handler.process(mlist, msg, msgdata)
- >>> dump_list(msgdata['recipients'])
- bperson@example.com
- dperson@example.com
- eperson@example.com
- fperson@example.com
- gperson@example.com
diff --git a/src/mailman/pipeline/docs/filtering.rst b/src/mailman/pipeline/docs/filtering.rst
deleted file mode 100644
index fd0b33d3b..000000000
--- a/src/mailman/pipeline/docs/filtering.rst
+++ /dev/null
@@ -1,347 +0,0 @@
-=================
-Content filtering
-=================
-
-Mailman can filter the content of messages posted to a mailing list by
-stripping MIME subparts, and possibly reorganizing the MIME structure of a
-message.
-
- >>> mlist = create_list('test@example.com')
-
-Several mailing list options control content filtering. First, the feature
-must be enabled, then there are two options that control which MIME types get
-filtered and which get passed. Finally, there is an option to control whether
-``text/html`` parts will get converted to plain text. Let's set up some
-defaults for these variables, then we'll explain them in more detail below.
-
- >>> mlist.filter_content = True
- >>> mlist.filter_types = []
- >>> mlist.pass_types = []
- >>> mlist.convert_html_to_plaintext = False
-
-
-Filtering the outer content type
-================================
-
-A simple filtering setting will just search the content types of the messages
-parts, discarding all parts with a matching MIME type. If the message's outer
-content type matches the filter, the entire message will be discarded.
-::
-
- >>> from mailman.interfaces.mime import FilterAction
-
- >>> mlist.filter_types = ['image/jpeg']
- >>> mlist.filter_action = FilterAction.discard
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... Content-Type: image/jpeg
- ... MIME-Version: 1.0
- ...
- ... xxxxx
- ... """)
-
- >>> process = config.handlers['mime-delete'].process
- >>> process(mlist, msg, {})
- Traceback (most recent call last):
- ...
- DiscardMessage: The message's content type was explicitly disallowed
-
-However, if we turn off content filtering altogether, then the handler
-short-circuits.
-
- >>> mlist.filter_content = False
- >>> msgdata = {}
- >>> process(mlist, msg, msgdata)
- >>> print msg.as_string()
- From: aperson@example.com
- Content-Type: image/jpeg
- MIME-Version: 1.0
- <BLANKLINE>
- xxxxx
- >>> msgdata
- {}
-
-Similarly, no content filtering is performed on digest messages, which are
-crafted internally by Mailman.
-
- >>> mlist.filter_content = True
- >>> msgdata = {'isdigest': True}
- >>> process(mlist, msg, msgdata)
- >>> print msg.as_string()
- From: aperson@example.com
- Content-Type: image/jpeg
- MIME-Version: 1.0
- <BLANKLINE>
- xxxxx
- >>> msgdata
- {u'isdigest': True}
-
-
-Simple multipart filtering
-==========================
-
-If one of the subparts in a multipart message matches the filter type, then
-just that subpart will be stripped.
-::
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... Content-Type: multipart/mixed; boundary=BOUNDARY
- ... MIME-Version: 1.0
- ...
- ... --BOUNDARY
- ... Content-Type: image/jpeg
- ... MIME-Version: 1.0
- ...
- ... xxx
- ...
- ... --BOUNDARY
- ... Content-Type: image/gif
- ... MIME-Version: 1.0
- ...
- ... yyy
- ... --BOUNDARY--
- ... """)
-
- >>> process(mlist, msg, {})
- >>> print msg.as_string()
- From: aperson@example.com
- Content-Type: multipart/mixed; boundary=BOUNDARY
- MIME-Version: 1.0
- X-Content-Filtered-By: Mailman/MimeDel ...
- <BLANKLINE>
- --BOUNDARY
- Content-Type: image/gif
- MIME-Version: 1.0
- <BLANKLINE>
- yyy
- --BOUNDARY--
- <BLANKLINE>
-
-
-Collapsing multipart/alternative messages
-=========================================
-
-When content filtering encounters a ``multipart/alternative`` part, and the
-results of filtering leave only one of the subparts, then the
-``multipart/alternative`` may be collapsed. For example, in the following
-message, the outer content type is a ``multipart/mixed``. Inside this part is
-just a single subpart that has a content type of ``multipart/alternative``.
-This inner multipart has two subparts, a jpeg and a gif.
-
-Content filtering will remove the jpeg part, leaving the
-``multipart/alternative`` with only a single gif subpart. Because there's
-only one subpart left, the MIME structure of the message will be reorganized,
-removing the inner ``multipart/alternative`` so that the outer
-``multipart/mixed`` has just a single gif subpart.
-
- >>> mlist.collapse_alternatives = True
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... Content-Type: multipart/mixed; boundary=BOUNDARY
- ... MIME-Version: 1.0
- ...
- ... --BOUNDARY
- ... Content-Type: multipart/alternative; boundary=BOUND2
- ... MIME-Version: 1.0
- ...
- ... --BOUND2
- ... Content-Type: image/jpeg
- ... MIME-Version: 1.0
- ...
- ... xxx
- ...
- ... --BOUND2
- ... Content-Type: image/gif
- ... MIME-Version: 1.0
- ...
- ... yyy
- ... --BOUND2--
- ...
- ... --BOUNDARY--
- ... """)
- >>> process(mlist, msg, {})
- >>> print msg.as_string()
- From: aperson@example.com
- Content-Type: multipart/mixed; boundary=BOUNDARY
- MIME-Version: 1.0
- X-Content-Filtered-By: Mailman/MimeDel ...
- <BLANKLINE>
- --BOUNDARY
- Content-Type: image/gif
- MIME-Version: 1.0
- <BLANKLINE>
- yyy
- --BOUNDARY--
- <BLANKLINE>
-
-When the outer part is a ``multipart/alternative`` and filtering leaves this
-outer part with just one subpart, the entire message is converted to the left
-over part's content type. In other words, the left over inner part is
-promoted to being the outer part.
-::
-
- >>> mlist.filter_types = ['image/jpeg', 'text/html']
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... Content-Type: multipart/alternative; boundary=AAA
- ...
- ... --AAA
- ... Content-Type: text/html
- ...
- ... <b>This is some html</b>
- ... --AAA
- ... Content-Type: text/plain
- ...
- ... This is plain text
- ... --AAA--
- ... """)
-
- >>> process(mlist, msg, {})
- >>> print msg.as_string()
- From: aperson@example.com
- Content-Type: text/plain
- X-Content-Filtered-By: Mailman/MimeDel ...
- <BLANKLINE>
- This is plain text
-
-Clean up.
-
- >>> mlist.filter_types = ['image/jpeg']
-
-
-Conversion to plain text
-========================
-
-Many mailing lists prohibit HTML email, and in fact, such email can be a
-phishing or spam vector. However, many mail readers will send HTML email by
-default because users think it looks pretty. One approach to handling this
-would be to filter out ``text/html`` parts and rely on
-``multipart/alternative`` collapsing to leave just a plain text part. This
-works because many mail readers that send HTML email actually send a plain
-text part in the second subpart of such ``multipart/alternatives``.
-
-While this is a good suggestion for plain text-only mailing lists, often a
-mail reader will send only a ``text/html`` part with no plain text
-alternative. in this case, the site administer can enable ``text/html`` to
-``text/plain`` conversion by defining a conversion command. A list
-administrator still needs to enable such conversion for their list though.
-
- >>> mlist.convert_html_to_plaintext = True
-
-By default, Mailman sends the message through lynx, but since this program is
-not guaranteed to exist, we'll craft a simple, but stupid script to simulate
-the conversion process. The script expects a single argument, which is the
-name of the file containing the message payload to filter.
-
- >>> import os, sys
- >>> script_path = os.path.join(config.DATA_DIR, 'filter.py')
- >>> fp = open(script_path, 'w')
- >>> try:
- ... print >> fp, """\
- ... import sys
- ... print 'Converted text/html to text/plain'
- ... print 'Filename:', sys.argv[1]
- ... """
- ... finally:
- ... fp.close()
- >>> config.HTML_TO_PLAIN_TEXT_COMMAND = '%s %s %%(filename)s' % (
- ... sys.executable, script_path)
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... Content-Type: text/html
- ... MIME-Version: 1.0
- ...
- ... <html><head></head>
- ... <body></body></html>
- ... """)
- >>> process(mlist, msg, {})
- >>> print msg.as_string()
- From: aperson@example.com
- MIME-Version: 1.0
- Content-Type: text/plain
- X-Content-Filtered-By: Mailman/MimeDel ...
- <BLANKLINE>
- Converted text/html to text/plain
- Filename: ...
- <BLANKLINE>
-
-
-Discarding empty parts
-======================
-
-Similarly, if after filtering a multipart section ends up empty, then the
-entire multipart is discarded. For example, here's a message where an inner
-``multipart/mixed`` contains two jpeg subparts. Both jpegs are filtered out,
-so the entire inner ``multipart/mixed`` is discarded.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... Content-Type: multipart/mixed; boundary=AAA
- ...
- ... --AAA
- ... Content-Type: multipart/mixed; boundary=BBB
- ...
- ... --BBB
- ... Content-Type: image/jpeg
- ...
- ... xxx
- ... --BBB
- ... Content-Type: image/jpeg
- ...
- ... yyy
- ... --BBB---
- ... --AAA
- ... Content-Type: multipart/alternative; boundary=CCC
- ...
- ... --CCC
- ... Content-Type: text/html
- ...
- ... <h2>This is a header</h2>
- ...
- ... --CCC
- ... Content-Type: text/plain
- ...
- ... A different message
- ... --CCC--
- ... --AAA
- ... Content-Type: image/gif
- ...
- ... zzz
- ... --AAA
- ... Content-Type: image/gif
- ...
- ... aaa
- ... --AAA--
- ... """)
- >>> process(mlist, msg, {})
- >>> print msg.as_string()
- From: aperson@example.com
- Content-Type: multipart/mixed; boundary=AAA
- X-Content-Filtered-By: Mailman/MimeDel ...
- <BLANKLINE>
- --AAA
- MIME-Version: 1.0
- Content-Type: text/plain
- <BLANKLINE>
- Converted text/html to text/plain
- Filename: ...
- <BLANKLINE>
- --AAA
- Content-Type: image/gif
- <BLANKLINE>
- zzz
- --AAA
- Content-Type: image/gif
- <BLANKLINE>
- aaa
- --AAA--
- <BLANKLINE>
-
-
-Passing MIME types
-==================
-
-XXX Describe the pass_mime_types setting and how it interacts with
-``filter_mime_types``.
diff --git a/src/mailman/pipeline/docs/nntp.rst b/src/mailman/pipeline/docs/nntp.rst
deleted file mode 100644
index 874712397..000000000
--- a/src/mailman/pipeline/docs/nntp.rst
+++ /dev/null
@@ -1,68 +0,0 @@
-============
-NNTP Gateway
-============
-
-Mailman has an NNTP gateway, whereby messages posted to the mailing list can
-be forwarded onto an NNTP newsgroup. Typically this means Usenet, but since
-NNTP is to Usenet as IP is to the web, it's more general than that.
-
- >>> mlist = create_list('_xtest@example.com')
-
-Gatewaying from the mailing list to the newsgroup happens through a separate
-``nntp`` queue and happen immediately when the message is posted through to
-the list. Note that gatewaying from the newsgroup to the list happens via a
-cronjob (currently not shown).
-
-There are several situations which prevent a message from being gatewayed to
-the newsgroup. The feature could be disabled, as is the default.
-::
-
- >>> mlist.gateway_to_news = False
- >>> msg = message_from_string("""\
- ... Subject: An important message
- ...
- ... Something of great import.
- ... """)
-
- >>> handler = config.handlers['to-usenet']
- >>> handler.process(mlist, msg, {})
-
- >>> switchboard = config.switchboards['news']
- >>> switchboard.files
- []
-
-Even if enabled, messages that came from the newsgroup are never gated back to
-the newsgroup.
-
- >>> mlist.gateway_to_news = True
- >>> handler.process(mlist, msg, {'fromusenet': True})
- >>> switchboard.files
- []
-
-Neither are digests ever gated to the newsgroup.
-
- >>> handler.process(mlist, msg, {'isdigest': True})
- >>> switchboard.files
- []
-
-However, other posted messages get gated to the newsgroup via the nntp queue.
-The list owner can set the linked newsgroup and the nntp host that its
-messages are gated to.
-
- >>> mlist.linked_newsgroup = 'comp.lang.thing'
- >>> mlist.nntp_host = 'news.example.com'
- >>> handler.process(mlist, msg, {})
- >>> len(switchboard.files)
- 1
- >>> filebase = switchboard.files[0]
- >>> msg, msgdata = switchboard.dequeue(filebase)
- >>> switchboard.finish(filebase)
- >>> print msg.as_string()
- Subject: An important message
- <BLANKLINE>
- Something of great import.
- <BLANKLINE>
- >>> dump_msgdata(msgdata)
- _parsemsg: False
- listname : _xtest@example.com
- version : 3
diff --git a/src/mailman/pipeline/docs/reply-to.rst b/src/mailman/pipeline/docs/reply-to.rst
deleted file mode 100644
index e08fea81d..000000000
--- a/src/mailman/pipeline/docs/reply-to.rst
+++ /dev/null
@@ -1,131 +0,0 @@
-================
-Reply-to munging
-================
-
-Messages that flow through the global pipeline get their headers *cooked*,
-which basically means that their headers go through several mostly unrelated
-transformations. Some headers get added, others get changed. Some of these
-changes depend on mailing list settings and others depend on how the message
-is getting sent through the system. We'll take things one-by-one.
-
- >>> mlist = create_list('_xtest@example.com')
-
-*Reply-to munging* refers to the behavior where a mailing list can be
-configured to change or augment an existing ``Reply-To`` header in a message
-posted to the list. Reply-to munging is fairly controversial, with arguments
-made either for or against munging.
-
-The Mailman developers, and I believe the majority consensus is to do no
-reply-to munging, under several principles. Primarily, most reply-to munging
-is requested by people who do not have both a `Reply` and `Reply All` button
-on their mail reader. If you do not munge ``Reply-To``, then these buttons
-will work properly, but if you munge the header, it is impossible for these
-buttons to work right, because both will reply to the list. This leads to
-unfortunate accidents where a private message is accidentally posted to the
-entire list.
-
-However, Mailman gives list owners the option to do reply-To munging anyway,
-mostly as a way to shut up the really vocal minority who seem to insist on
-this mis-feature.
-
-
-Reply to list
-=============
-
-A list can be configured to add a ``Reply-To`` header pointing back to the
-mailing list's posting address. If there's no ``Reply-To`` header in the
-original message, the list's posting address simply gets inserted.
-::
-
- >>> from mailman.interfaces.mailinglist import ReplyToMunging
- >>> mlist.reply_goes_to_list = ReplyToMunging.point_to_list
- >>> mlist.preferred_language = 'en'
- >>> mlist.description = ''
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ...
- ... """)
-
- >>> from mailman.pipeline.cook_headers import process
- >>> process(mlist, msg, {})
- >>> len(msg.get_all('reply-to'))
- 1
- >>> print msg['reply-to']
- _xtest@example.com
-
-It's also possible to strip any existing ``Reply-To`` header first, before
-adding the list's posting address.
-
- >>> mlist.first_strip_reply_to = True
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... Reply-To: bperson@example.com
- ...
- ... """)
- >>> process(mlist, msg, {})
- >>> len(msg.get_all('reply-to'))
- 1
- >>> print msg['reply-to']
- _xtest@example.com
-
-If you don't first strip the header, then the list's posting address will just
-get appended to whatever the original version was.
-
- >>> mlist.first_strip_reply_to = False
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... Reply-To: bperson@example.com
- ...
- ... """)
- >>> process(mlist, msg, {})
- >>> len(msg.get_all('reply-to'))
- 1
- >>> print msg['reply-to']
- bperson@example.com, _xtest@example.com
-
-
-Explicit Reply-To
-=================
-
-The list can also be configured to have an explicit ``Reply-To`` header.
-
- >>> mlist.reply_goes_to_list = ReplyToMunging.explicit_header
- >>> mlist.reply_to_address = 'my-list@example.com'
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ...
- ... """)
- >>> process(mlist, msg, {})
- >>> len(msg.get_all('reply-to'))
- 1
- >>> print msg['reply-to']
- my-list@example.com
-
-And as before, it's possible to either strip any existing ``Reply-To``
-header...
-
- >>> mlist.first_strip_reply_to = True
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... Reply-To: bperson@example.com
- ...
- ... """)
- >>> process(mlist, msg, {})
- >>> len(msg.get_all('reply-to'))
- 1
- >>> print msg['reply-to']
- my-list@example.com
-
-...or not.
-
- >>> mlist.first_strip_reply_to = False
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... Reply-To: bperson@example.com
- ...
- ... """)
- >>> process(mlist, msg, {})
- >>> len(msg.get_all('reply-to'))
- 1
- >>> print msg['reply-to']
- my-list@example.com, bperson@example.com
diff --git a/src/mailman/pipeline/docs/replybot.rst b/src/mailman/pipeline/docs/replybot.rst
deleted file mode 100644
index 7cdd7c928..000000000
--- a/src/mailman/pipeline/docs/replybot.rst
+++ /dev/null
@@ -1,343 +0,0 @@
-==========================
-Automatic response handler
-==========================
-
-Mailman has a autoreply handler that sends automatic responses to messages it
-receives on its posting address, owner address, or robot address. Automatic
-responses are subject to various conditions, such as headers in the original
-message or the amount of time since the last auto-response.
-
- >>> mlist = create_list('_xtest@example.com')
- >>> mlist.display_name = 'XTest'
-
-
-Basic automatic responding
-==========================
-
-Basic automatic responding occurs when the list is set up to respond to either
-its ``-owner`` address, its ``-request`` address, or to the posting address,
-and a message is sent to one of these addresses. A mailing list also has an
-automatic response grace period which specifies how much time must pass before
-a second response will be sent, with 0 meaning "there is no grace period".
-::
-
- >>> import datetime
- >>> from mailman.interfaces.autorespond import ResponseAction
-
- >>> mlist.autorespond_owner = ResponseAction.respond_and_continue
- >>> mlist.autoresponse_grace_period = datetime.timedelta()
- >>> mlist.autoresponse_owner_text = 'owner autoresponse text'
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... To: _xtest-owner@example.com
- ...
- ... help
- ... """)
-
-The preceding message to the mailing list's owner will trigger an automatic
-response.
-::
-
- >>> from mailman.testing.helpers import get_queue_messages
-
- >>> handler = config.handlers['replybot']
- >>> handler.process(mlist, msg, dict(to_owner=True))
- >>> messages = get_queue_messages('virgin')
- >>> len(messages)
- 1
-
- >>> dump_msgdata(messages[0].msgdata)
- _parsemsg : False
- listname : _xtest@example.com
- nodecorate : True
- recipients : set([u'aperson@example.com'])
- reduced_list_headers: True
- version : 3
-
- >>> print messages[0].msg.as_string()
- MIME-Version: 1.0
- Content-Type: text/plain; charset="us-ascii"
- Content-Transfer-Encoding: 7bit
- Subject: Auto-response for your message to the "XTest" mailing list
- From: _xtest-bounces@example.com
- To: aperson@example.com
- X-Mailer: The Mailman Replybot
- X-Ack: No
- Message-ID: <...>
- Date: ...
- Precedence: bulk
- <BLANKLINE>
- owner autoresponse text
-
-
-Short circuiting
-================
-
-Several headers in the original message determine whether an automatic
-response should even be sent. For example, if the message has an
-``X-Ack: No`` header, no auto-response is sent.
-::
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... X-Ack: No
- ...
- ... help me
- ... """)
-
- >>> handler.process(mlist, msg, dict(to_owner=True))
- >>> get_queue_messages('virgin')
- []
-
-Mailman itself can suppress automatic responses for certain types of
-internally crafted messages, by setting the ``noack`` metadata key.
-::
-
- >>> msg = message_from_string("""\
- ... From: mailman@example.com
- ...
- ... help for you
- ... """)
-
- >>> handler.process(mlist, msg, dict(noack=True, to_owner=True))
- >>> get_queue_messages('virgin')
- []
-
-If there is a ``Precedence:`` header with any of the values ``bulk``,
-``junk``, or ``list``, then the automatic response is also suppressed.
-::
-
- >>> msg = message_from_string("""\
- ... From: asystem@example.com
- ... Precedence: bulk
- ...
- ... hey!
- ... """)
-
- >>> handler.process(mlist, msg, dict(to_owner=True))
- >>> get_queue_messages('virgin')
- []
-
- >>> msg.replace_header('precedence', 'junk')
- >>> handler.process(mlist, msg, dict(to_owner=True))
- >>> get_queue_messages('virgin')
- []
-
- >>> msg.replace_header('precedence', 'list')
- >>> handler.process(mlist, msg, dict(to_owner=True))
- >>> get_queue_messages('virgin')
- []
-
-Unless the ``X-Ack:`` header has a value of ``yes``, in which case, the
-``Precedence`` header is ignored.
-::
-
- >>> msg['X-Ack'] = 'yes'
- >>> handler.process(mlist, msg, dict(to_owner=True))
- >>> messages = get_queue_messages('virgin')
- >>> len(messages)
- 1
-
- >>> dump_msgdata(messages[0].msgdata)
- _parsemsg : False
- listname : _xtest@example.com
- nodecorate : True
- recipients : set([u'asystem@example.com'])
- reduced_list_headers: True
- version : 3
-
- >>> print messages[0].msg.as_string()
- MIME-Version: 1.0
- Content-Type: text/plain; charset="us-ascii"
- Content-Transfer-Encoding: 7bit
- Subject: Auto-response for your message to the "XTest" mailing list
- From: _xtest-bounces@example.com
- To: asystem@example.com
- X-Mailer: The Mailman Replybot
- X-Ack: No
- Message-ID: <...>
- Date: ...
- Precedence: bulk
- <BLANKLINE>
- owner autoresponse text
-
-
-Available auto-responses
-========================
-
-As shown above, a message sent to the ``-owner`` address will get an
-auto-response with the text set for owner responses. Two other types of email
-will get auto-responses: those sent to the ``-request`` address...
-::
-
- >>> mlist.autorespond_requests = ResponseAction.respond_and_continue
- >>> mlist.autoresponse_request_text = 'robot autoresponse text'
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... To: _xtest-request@example.com
- ...
- ... help me
- ... """)
-
- >>> handler.process(mlist, msg, dict(to_request=True))
- >>> messages = get_queue_messages('virgin')
- >>> len(messages)
- 1
-
- >>> print messages[0].msg.as_string()
- MIME-Version: 1.0
- Content-Type: text/plain; charset="us-ascii"
- Content-Transfer-Encoding: 7bit
- Subject: Auto-response for your message to the "XTest" mailing list
- From: _xtest-bounces@example.com
- To: aperson@example.com
- X-Mailer: The Mailman Replybot
- X-Ack: No
- Message-ID: <...>
- Date: ...
- Precedence: bulk
- <BLANKLINE>
- robot autoresponse text
-
-...and those sent to the posting address.
-::
-
- >>> mlist.autorespond_postings = ResponseAction.respond_and_continue
- >>> mlist.autoresponse_postings_text = 'postings autoresponse text'
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... To: _xtest@example.com
- ...
- ... help me
- ... """)
-
- >>> handler.process(mlist, msg, dict(to_list=True))
- >>> messages = get_queue_messages('virgin')
- >>> len(messages)
- 1
-
- >>> print messages[0].msg.as_string()
- MIME-Version: 1.0
- Content-Type: text/plain; charset="us-ascii"
- Content-Transfer-Encoding: 7bit
- Subject: Auto-response for your message to the "XTest" mailing list
- From: _xtest-bounces@example.com
- To: aperson@example.com
- X-Mailer: The Mailman Replybot
- X-Ack: No
- Message-ID: <...>
- Date: ...
- Precedence: bulk
- <BLANKLINE>
- postings autoresponse text
-
-
-Grace periods
-=============
-
-Automatic responses have a grace period, during which no additional responses
-will be sent. This is so as not to bombard the sender with responses. The
-grace period is measured in days.
-
- >>> mlist.autoresponse_grace_period = datetime.timedelta(days=10)
-
-When a response is sent to a person via any of the owner, request, or postings
-addresses, the response date is recorded. The grace period is usually
-measured in days.
-
- >>> msg = message_from_string("""\
- ... From: bperson@example.com
- ... To: _xtest-owner@example.com
- ...
- ... help
- ... """)
-
-This is the first response to bperson, so it gets sent.
-
- >>> handler.process(mlist, msg, dict(to_owner=True))
- >>> print len(get_queue_messages('virgin'))
- 1
-
-But with a grace period greater than zero, no subsequent response will be sent
-right now.
-
- >>> handler.process(mlist, msg, dict(to_owner=True))
- >>> print len(get_queue_messages('virgin'))
- 0
-
-Fast forward 9 days and you still don't get a response.
-::
-
- >>> from mailman.utilities.datetime import factory
- >>> factory.fast_forward(days=9)
-
- >>> handler.process(mlist, msg, dict(to_owner=True))
- >>> print len(get_queue_messages('virgin'))
- 0
-
-But tomorrow, the sender will get a new auto-response.
-
- >>> factory.fast_forward()
- >>> handler.process(mlist, msg, dict(to_owner=True))
- >>> print len(get_queue_messages('virgin'))
- 1
-
-Of course, everything works the same way for messages to the request
-address, even if the sender is the same person...
-::
-
- >>> msg = message_from_string("""\
- ... From: bperson@example.com
- ... To: _xtest-request@example.com
- ...
- ... help
- ... """)
-
- >>> handler.process(mlist, msg, dict(to_request=True))
- >>> print len(get_queue_messages('virgin'))
- 1
-
- >>> handler.process(mlist, msg, dict(to_request=True))
- >>> print len(get_queue_messages('virgin'))
- 0
-
- >>> factory.fast_forward(days=9)
- >>> handler.process(mlist, msg, dict(to_request=True))
- >>> print len(get_queue_messages('virgin'))
- 0
-
- >>> factory.fast_forward()
- >>> handler.process(mlist, msg, dict(to_request=True))
- >>> print len(get_queue_messages('virgin'))
- 1
-
-...and for messages to the posting address.
-::
-
- >>> msg = message_from_string("""\
- ... From: bperson@example.com
- ... To: _xtest@example.com
- ...
- ... help
- ... """)
-
- >>> handler.process(mlist, msg, dict(to_list=True))
- >>> print len(get_queue_messages('virgin'))
- 1
-
- >>> handler.process(mlist, msg, dict(to_list=True))
- >>> print len(get_queue_messages('virgin'))
- 0
-
- >>> factory.fast_forward(days=9)
- >>> handler.process(mlist, msg, dict(to_list=True))
- >>> print len(get_queue_messages('virgin'))
- 0
-
- >>> factory.fast_forward()
- >>> handler.process(mlist, msg, dict(to_list=True))
- >>> print len(get_queue_messages('virgin'))
- 1
diff --git a/src/mailman/pipeline/docs/rfc-2369.rst b/src/mailman/pipeline/docs/rfc-2369.rst
deleted file mode 100644
index 1b89f2354..000000000
--- a/src/mailman/pipeline/docs/rfc-2369.rst
+++ /dev/null
@@ -1,206 +0,0 @@
-=========================
-RFC 2919 and 2369 headers
-=========================
-
-`RFC 2919`_ and `RFC 2369`_ define headers for mailing list actions. These
-headers generally start with the `List-` prefix.
-
- >>> mlist = create_list('test@example.com')
- >>> mlist.preferred_language = 'en'
- >>> mlist.archive = False
-
-..
- This is a helper function for the following section.
- >>> def list_headers(msg, only=None):
- ... if isinstance(only, basestring):
- ... only = (only.lower(),)
- ... elif only is None:
- ... only = set(header.lower() for header in msg.keys()
- ... if header.lower().startswith('list-'))
- ... only.add('archived-at')
- ... else:
- ... only = set(header.lower() for header in only)
- ... print '---start---'
- ... for header in sorted(only):
- ... for value in sorted(msg.get_all(header, ())):
- ... print '%s: %s' % (header, value)
- ... print '---end---'
-
-The `rfc-2369` handler adds the `List-` headers. `List-Id` is always added.
-
- >>> from mailman.pipeline.rfc_2369 import process
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ...
- ... """)
- >>> process(mlist, msg, {})
- >>> list_headers(msg, 'list-id')
- ---start---
- list-id: <test.example.com>
- ---end---
-
-
-Fewer headers
-=============
-
-Some people don't like these headers because their mail readers aren't good
-about hiding them. A list owner can turn these headers off.
-
- >>> mlist.include_rfc2369_headers = False
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ...
- ... """)
- >>> process(mlist, msg, {})
- >>> list_headers(msg)
- ---start---
- ---end---
-
-Messages which Mailman generates itself, such as user or owner notifications,
-have a reduced set of `List-` headers. Specifically, there is no `List-Post`,
-`List-Archive` or `Archived-At` header.
-
- >>> mlist.include_rfc2369_headers = True
- >>> mlist.include_list_post_header = False
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ...
- ... """)
- >>> process(mlist, msg, dict(reduced_list_headers=True))
- >>> list_headers(msg)
- ---start---
- list-help: <mailto:test-request@example.com?subject=help>
- list-id: <test.example.com>
- list-subscribe: <http://lists.example.com/listinfo/test@example.com>,
- <mailto:test-join@example.com>
- list-unsubscribe: <http://lists.example.com/listinfo/test@example.com>,
- <mailto:test-leave@example.com>
- ---end---
-
-
-List-Post header
-================
-
-Discussion lists, to which any subscriber can post, also have a `List-Post`
-header which contains the `mailto:` URL used to send messages to the list.
-
- >>> mlist.include_list_post_header = True
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ...
- ... """)
- >>> process(mlist, msg, {})
- >>> list_headers(msg)
- ---start---
- list-help: <mailto:test-request@example.com?subject=help>
- list-id: <test.example.com>
- list-post: <mailto:test@example.com>
- list-subscribe: <http://lists.example.com/listinfo/test@example.com>,
- <mailto:test-join@example.com>
- list-unsubscribe: <http://lists.example.com/listinfo/test@example.com>,
- <mailto:test-leave@example.com>
- ---end---
-
-
-List-Id header
-==============
-
-If the mailing list has a description, then it is included in the ``List-Id``
-header.
-
- >>> mlist.description = 'My test mailing list'
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ...
- ... """)
- >>> process(mlist, msg, {})
- >>> list_headers(msg)
- ---start---
- list-help: <mailto:test-request@example.com?subject=help>
- list-id: My test mailing list <test.example.com>
- list-post: <mailto:test@example.com>
- list-subscribe: <http://lists.example.com/listinfo/test@example.com>,
- <mailto:test-join@example.com>
- list-unsubscribe: <http://lists.example.com/listinfo/test@example.com>,
- <mailto:test-leave@example.com>
- ---end---
-
-Any existing ``List-Id`` headers are removed from the original message.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... List-ID: <123.456.789>
- ...
- ... """)
-
- >>> process(mlist, msg, {})
- >>> list_headers(msg, only='list-id')
- ---start---
- list-id: My test mailing list <test.example.com>
- ---end---
-
-
-Archive headers
-===============
-
-When the mailing list is configured to enable archiving, a `List-Archive`
-header will be added.
-
- >>> mlist.archive = True
-
-`RFC 5064`_ defines the `Archived-At` header which contains the url to the
-individual message in the archives. Archivers which don't support
-pre-calculation of the archive url cannot add the `Archived-At` header.
-However, other archivers can calculate the url, and do add this header.
-
- >>> config.push('prototype', """
- ... [archiver.prototype]
- ... enable: yes
- ... [archiver.mail_archive]
- ... enable: no
- ... [archiver.mhonarc]
- ... enable: no
- ... [archiver.pipermail]
- ... enable: No
- ... """)
-
-The *prototype* archiver can calculate this archive url given a `Message-ID`.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... Message-ID: <first>
- ... X-Message-ID-Hash: 4CMWUN6BHVCMHMDAOSJZ2Q72G5M32MWB
- ...
- ... """)
- >>> process(mlist, msg, {})
- >>> list_headers(msg, only=('list-archive', 'archived-at'))
- ---start---
- archived-at: http://lists.example.com/4CMWUN6BHVCMHMDAOSJZ2Q72G5M32MWB
- list-archive: <http://lists.example.com>
- ---end---
-
-If the mailing list isn't being archived, neither the `List-Archive` nor
-`Archived-At` headers will be added.
-
- >>> config.pop('prototype')
- >>> mlist.archive = False
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ...
- ... """)
- >>> process(mlist, msg, {})
- >>> list_headers(msg)
- ---start---
- list-help: <mailto:test-request@example.com?subject=help>
- list-id: My test mailing list <test.example.com>
- list-post: <mailto:test@example.com>
- list-subscribe: <http://lists.example.com/listinfo/test@example.com>,
- <mailto:test-join@example.com>
- list-unsubscribe: <http://lists.example.com/listinfo/test@example.com>,
- <mailto:test-leave@example.com>
- ---end---
-
-
-.. _`RFC 2919`: http://www.faqs.org/rfcs/rfc2919.html
-.. _`RFC 2369`: http://www.faqs.org/rfcs/rfc2369.html
-.. _`RFC 5064`: http://www.faqs.org/rfcs/rfc5064.html
diff --git a/src/mailman/pipeline/docs/subject-munging.rst b/src/mailman/pipeline/docs/subject-munging.rst
deleted file mode 100644
index e7a6553ce..000000000
--- a/src/mailman/pipeline/docs/subject-munging.rst
+++ /dev/null
@@ -1,249 +0,0 @@
-===============
-Subject munging
-===============
-
-Messages that flow through the global pipeline get their headers *cooked*,
-which basically means that their headers go through several mostly unrelated
-transformations. Some headers get added, others get changed. Some of these
-changes depend on mailing list settings and others depend on how the message
-is getting sent through the system. We'll take things one-by-one.
-
- >>> mlist = create_list('_xtest@example.com')
-
-
-Inserting a prefix
-==================
-
-Another thing header cooking does is *munge* the ``Subject`` header by
-inserting the subject prefix for the list at the front. If there's no subject
-header in the original message, Mailman uses a canned default. In order to do
-subject munging, a mailing list must have a preferred language.
-::
-
- >>> mlist.subject_prefix = '[XTest] '
- >>> mlist.preferred_language = 'en'
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ...
- ... A message of great import.
- ... """)
- >>> msgdata = {}
-
- >>> from mailman.pipeline.cook_headers import process
- >>> process(mlist, msg, msgdata)
-
-The original subject header is stored in the message metadata. We must print
-the new ``Subject`` header because it gets converted from a string to an
-``email.header.Header`` instance which has an unhelpful ``repr``.
-
- >>> msgdata['origsubj']
- u''
- >>> print msg['subject']
- [XTest] (no subject)
-
-If the original message had a ``Subject`` header, then the prefix is inserted
-at the beginning of the header's value.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... Subject: Something important
- ...
- ... A message of great import.
- ... """)
- >>> msgdata = {}
- >>> process(mlist, msg, msgdata)
- >>> print msgdata['origsubj']
- Something important
- >>> print msg['subject']
- [XTest] Something important
-
-``Subject`` headers are not munged for digest messages.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... Subject: Something important
- ...
- ... A message of great import.
- ... """)
- >>> process(mlist, msg, dict(isdigest=True))
- >>> print msg['subject']
- Something important
-
-Nor are they munged for *fast tracked* messages, which are generally defined
-as messages that Mailman crafts internally.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... Subject: Something important
- ...
- ... A message of great import.
- ... """)
- >>> process(mlist, msg, dict(_fasttrack=True))
- >>> print msg['subject']
- Something important
-
-If a ``Subject`` header already has a prefix, usually following a ``Re:``
-marker, another one will not be added but the prefix will be moved to the
-front of the header text.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... Subject: Re: [XTest] Something important
- ...
- ... A message of great import.
- ... """)
- >>> process(mlist, msg, {})
- >>> print msg['subject']
- [XTest] Re: Something important
-
-If the ``Subject`` header has a prefix at the front of the header text, that's
-where it will stay. This is called *new style* prefixing and is the only
-option available in Mailman 3.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... Subject: [XTest] Re: Something important
- ...
- ... A message of great import.
- ... """)
- >>> process(mlist, msg, {})
- >>> print msg['subject']
- [XTest] Re: Something important
-
-
-Internationalized headers
-=========================
-
-Internationalization adds some interesting twists to the handling of subject
-prefixes. Part of what makes this interesting is the encoding of i18n headers
-using RFC 2047, and lists whose preferred language is in a different character
-set than the encoded header.
-
- >>> msg = message_from_string("""\
- ... Subject: =?iso-2022-jp?b?GyRCJWEhPCVrJV4lcxsoQg==?=
- ...
- ... """)
- >>> process(mlist, msg, {})
- >>> print msg['subject']
- [XTest] =?iso-2022-jp?b?GyRCJWEhPCVrJV4lcxsoQg==?=
- >>> unicode(msg['subject'])
- u'[XTest] \u30e1\u30fc\u30eb\u30de\u30f3'
-
-
-Prefix numbers
-==============
-
-Subject prefixes support a placeholder for the numeric post id. Every time a
-message is posted to the mailing list, a *post id* gets incremented. This is
-a purely sequential integer that increases monotonically. By added a ``%d``
-placeholder to the subject prefix, this post id can be included in the prefix.
-
- >>> mlist.subject_prefix = '[XTest %d] '
- >>> mlist.post_id = 456
- >>> msg = message_from_string("""\
- ... Subject: Something important
- ...
- ... """)
- >>> process(mlist, msg, {})
- >>> print msg['subject']
- [XTest 456] Something important
-
-This works even when the message is a reply, except that in this case, the
-numeric post id in the generated subject prefix is updated with the new post
-id.
-
- >>> msg = message_from_string("""\
- ... Subject: [XTest 123] Re: Something important
- ...
- ... """)
- >>> process(mlist, msg, {})
- >>> print msg['subject']
- [XTest 456] Re: Something important
-
-If the ``Subject`` header had old style prefixing, the prefix is moved to the
-front of the header text.
-
- >>> msg = message_from_string("""\
- ... Subject: Re: [XTest 123] Something important
- ...
- ... """)
- >>> process(mlist, msg, {})
- >>> print msg['subject']
- [XTest 456] Re: Something important
-
-
-And of course, the proper thing is done when posting id numbers are included
-in the subject prefix, and the subject is encoded non-ASCII.
-
- >>> msg = message_from_string("""\
- ... Subject: =?iso-2022-jp?b?GyRCJWEhPCVrJV4lcxsoQg==?=
- ...
- ... """)
- >>> process(mlist, msg, {})
- >>> print msg['subject']
- [XTest 456] =?iso-2022-jp?b?GyRCJWEhPCVrJV4lcxsoQg==?=
- >>> unicode(msg['subject'])
- u'[XTest 456] \u30e1\u30fc\u30eb\u30de\u30f3'
-
-Even more fun is when the internationalized ``Subject`` header already has a
-prefix, possibly with a different posting number.
-
- >>> msg = message_from_string("""\
- ... Subject: [XTest 123] Re: =?iso-2022-jp?b?GyRCJWEhPCVrJV4lcxsoQg==?=
- ...
- ... """)
- >>> process(mlist, msg, {})
- >>> print msg['subject']
- [XTest 456] Re: =?iso-2022-jp?b?GyRCJWEhPCVrJV4lcxsoQg==?=
-
-..
- # XXX This requires Python email patch #1681333 to succeed.
- # >>> unicode(msg['subject'])
- # u'[XTest 456] Re: \u30e1\u30fc\u30eb\u30de\u30f3'
-
-As before, old style subject prefixes are re-ordered.
-
- >>> msg = message_from_string("""\
- ... Subject: Re: [XTest 123] =?iso-2022-jp?b?GyRCJWEhPCVrJV4lcxsoQg==?=
- ...
- ... """)
- >>> process(mlist, msg, {})
- >>> print msg['subject']
- [XTest 456] Re:
- =?iso-2022-jp?b?GyRCJWEhPCVrJV4lcxsoQg==?=
-
-..
- # XXX This requires Python email patch #1681333 to succeed.
- # >>> unicode(msg['subject'])
- # u'[XTest 456] Re: \u30e1\u30fc\u30eb\u30de\u30f3'
-
-
-In this test case, we get an extra space between the prefix and the original
-subject. It's because the original is *crooked*. Note that a ``Subject``
-starting with '\n ' is generated by some version of Eudora Japanese edition.
-
- >>> mlist.subject_prefix = '[XTest] '
- >>> msg = message_from_string("""\
- ... Subject:
- ... Important message
- ...
- ... """)
- >>> process(mlist, msg, {})
- >>> print msg['subject']
- [XTest] Important message
-
-And again, with an RFC 2047 encoded header.
-
- >>> msg = message_from_string("""\
- ... Subject:
- ... =?iso-2022-jp?b?GyRCJWEhPCVrJV4lcxsoQg==?=
- ...
- ... """)
- >>> process(mlist, msg, {})
-
-..
- # XXX This one does not appear to work the same way as
- # test_subject_munging_prefix_crooked() in the old Python-based tests. I need
- # to get Tokio to look at this.
- # >>> print msg['subject']
- # [XTest] =?iso-2022-jp?b?IBskQiVhITwlayVeJXMbKEI=?=
diff --git a/src/mailman/pipeline/docs/tagger.rst b/src/mailman/pipeline/docs/tagger.rst
deleted file mode 100644
index 80e682119..000000000
--- a/src/mailman/pipeline/docs/tagger.rst
+++ /dev/null
@@ -1,238 +0,0 @@
-==============
-Message tagger
-==============
-
-Mailman has a topics system which works like this: a mailing list
-administrator sets up one or more topics, which is essentially a named regular
-expression. The topic name can be any arbitrary string, and the name serves
-double duty as the *topic tag*. Each message that flows the mailing list has
-its ``Subject:`` and ``Keywords:`` headers compared against these regular
-expressions. The message then gets tagged with the topic names of each hit.
-
- >>> mlist = create_list('_xtest@example.com')
-
-Topics must be enabled for Mailman to do any topic matching, even if topics
-are defined.
-::
-
- >>> mlist.topics = [('bar fight', '.*bar.*', 'catch any bars', False)]
- >>> mlist.topics_enabled = False
- >>> mlist.topics_bodylines_limit = 0
-
- >>> msg = message_from_string("""\
- ... Subject: foobar
- ... Keywords: barbaz
- ...
- ... """)
- >>> msgdata = {}
-
- >>> from mailman.pipeline.tagger import process
- >>> process(mlist, msg, msgdata)
- >>> print msg.as_string()
- Subject: foobar
- Keywords: barbaz
- <BLANKLINE>
- <BLANKLINE>
- >>> msgdata
- {}
-
-However, once topics are enabled, message will be tagged. There are two
-artifacts of tagging; an ``X-Topics:`` header is added with the topic name,
-and the message metadata gets a key with a list of matching topic names.
-
- >>> mlist.topics_enabled = True
- >>> msg = message_from_string("""\
- ... Subject: foobar
- ... Keywords: barbaz
- ...
- ... """)
- >>> msgdata = {}
- >>> process(mlist, msg, msgdata)
- >>> print msg.as_string()
- Subject: foobar
- Keywords: barbaz
- X-Topics: bar fight
- <BLANKLINE>
- <BLANKLINE>
- >>> msgdata['topichits']
- [u'bar fight']
-
-
-Scanning body lines
-===================
-
-The tagger can also look at a certain number of body lines, but only for
-``Subject:`` and ``Keyword:`` header-like lines. When set to zero, no body
-lines are scanned.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... Subject: nothing
- ... Keywords: at all
- ...
- ... X-Ignore: something else
- ... Subject: foobar
- ... Keywords: barbaz
- ... """)
- >>> msgdata = {}
- >>> process(mlist, msg, msgdata)
- >>> print msg.as_string()
- From: aperson@example.com
- Subject: nothing
- Keywords: at all
- <BLANKLINE>
- X-Ignore: something else
- Subject: foobar
- Keywords: barbaz
- <BLANKLINE>
- >>> msgdata
- {}
-
-But let the tagger scan a few body lines and the matching headers will be
-found.
-
- >>> mlist.topics_bodylines_limit = 5
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... Subject: nothing
- ... Keywords: at all
- ...
- ... X-Ignore: something else
- ... Subject: foobar
- ... Keywords: barbaz
- ... """)
- >>> msgdata = {}
- >>> process(mlist, msg, msgdata)
- >>> print msg.as_string()
- From: aperson@example.com
- Subject: nothing
- Keywords: at all
- X-Topics: bar fight
- <BLANKLINE>
- X-Ignore: something else
- Subject: foobar
- Keywords: barbaz
- <BLANKLINE>
- >>> msgdata['topichits']
- [u'bar fight']
-
-However, scanning stops at the first body line that doesn't look like a
-header.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... Subject: nothing
- ... Keywords: at all
- ...
- ... This is not a header
- ... Subject: foobar
- ... Keywords: barbaz
- ... """)
- >>> msgdata = {}
- >>> process(mlist, msg, msgdata)
- >>> print msg.as_string()
- From: aperson@example.com
- Subject: nothing
- Keywords: at all
- <BLANKLINE>
- This is not a header
- Subject: foobar
- Keywords: barbaz
- >>> msgdata
- {}
-
-When set to a negative number, all body lines will be scanned.
-
- >>> mlist.topics_bodylines_limit = -1
- >>> lots_of_headers = '\n'.join(['X-Ignore: zip'] * 100)
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... Subject: nothing
- ... Keywords: at all
- ...
- ... %s
- ... Subject: foobar
- ... Keywords: barbaz
- ... """ % lots_of_headers)
- >>> msgdata = {}
- >>> process(mlist, msg, msgdata)
- >>> # Rather than print out 100 X-Ignore: headers, let's just prove that
- >>> # the X-Topics: header exists, meaning that the tagger did its job.
- >>> print msg['x-topics']
- bar fight
- >>> msgdata['topichits']
- [u'bar fight']
-
-
-Scanning sub-parts
-==================
-
-The tagger will also scan the body lines of text subparts in a multipart
-message, using the same rules as if all those body lines lived in a single
-text payload.
-
- >>> msg = message_from_string("""\
- ... Subject: Was
- ... Keywords: Raw
- ... Content-Type: multipart/alternative; boundary="BOUNDARY"
- ...
- ... --BOUNDARY
- ... From: sabo
- ... To: obas
- ...
- ... Subject: farbaw
- ... Keywords: barbaz
- ...
- ... --BOUNDARY--
- ... """)
- >>> msgdata = {}
- >>> process(mlist, msg, msgdata)
- >>> print msg.as_string()
- Subject: Was
- Keywords: Raw
- Content-Type: multipart/alternative; boundary="BOUNDARY"
- X-Topics: bar fight
- <BLANKLINE>
- --BOUNDARY
- From: sabo
- To: obas
- <BLANKLINE>
- Subject: farbaw
- Keywords: barbaz
- <BLANKLINE>
- --BOUNDARY--
- <BLANKLINE>
- >>> msgdata['topichits']
- [u'bar fight']
-
-But the tagger will not descend into non-text parts.
-
- >>> msg = message_from_string("""\
- ... Subject: Was
- ... Keywords: Raw
- ... Content-Type: multipart/alternative; boundary=BOUNDARY
- ...
- ... --BOUNDARY
- ... From: sabo
- ... To: obas
- ... Content-Type: message/rfc822
- ...
- ... Subject: farbaw
- ... Keywords: barbaz
- ...
- ... --BOUNDARY
- ... From: sabo
- ... To: obas
- ... Content-Type: message/rfc822
- ...
- ... Subject: farbaw
- ... Keywords: barbaz
- ...
- ... --BOUNDARY--
- ... """)
- >>> msgdata = {}
- >>> process(mlist, msg, msgdata)
- >>> print msg['x-topics']
- None
- >>> msgdata
- {}
diff --git a/src/mailman/pipeline/docs/to-outgoing.rst b/src/mailman/pipeline/docs/to-outgoing.rst
deleted file mode 100644
index 816aa4ca6..000000000
--- a/src/mailman/pipeline/docs/to-outgoing.rst
+++ /dev/null
@@ -1,42 +0,0 @@
-====================
-The outgoing handler
-====================
-
-Mailman's outgoing queue is used as the wrapper around SMTP delivery to the
-upstream mail server. The to-outgoing handler does little more than drop the
-message into the outgoing queue.
-
- >>> mlist = create_list('test@example.com')
-
-Craft a message destined for the outgoing queue. Include some random metadata
-as if this message had passed through some other handlers.
-::
-
- >>> msg = message_from_string("""\
- ... Subject: Here is a message
- ...
- ... Something of great import.
- ... """)
-
- >>> msgdata = dict(foo=1, bar=2, verp=True)
- >>> handler = config.handlers['to-outgoing']
- >>> handler.process(mlist, msg, msgdata)
-
-While the queued message will not be changed, the queued metadata will have an
-additional key set: the mailing list name.
-
- >>> from mailman.testing.helpers import get_queue_messages
- >>> messages = get_queue_messages('out')
- >>> len(messages)
- 1
- >>> print messages[0].msg.as_string()
- Subject: Here is a message
- <BLANKLINE>
- Something of great import.
- >>> dump_msgdata(messages[0].msgdata)
- _parsemsg: False
- bar : 2
- foo : 1
- listname : test@example.com
- verp : True
- version : 3