summaryrefslogtreecommitdiff
path: root/mailman/queue/docs
diff options
context:
space:
mode:
Diffstat (limited to 'mailman/queue/docs')
-rw-r--r--mailman/queue/docs/OVERVIEW.txt78
-rw-r--r--mailman/queue/docs/archiver.txt34
-rw-r--r--mailman/queue/docs/command.txt170
-rw-r--r--mailman/queue/docs/incoming.txt200
-rw-r--r--mailman/queue/docs/lmtp.txt103
-rw-r--r--mailman/queue/docs/news.txt157
-rw-r--r--mailman/queue/docs/outgoing.txt75
-rw-r--r--mailman/queue/docs/runner.txt72
-rw-r--r--mailman/queue/docs/switchboard.txt182
9 files changed, 0 insertions, 1071 deletions
diff --git a/mailman/queue/docs/OVERVIEW.txt b/mailman/queue/docs/OVERVIEW.txt
deleted file mode 100644
index 643fa8a5c..000000000
--- a/mailman/queue/docs/OVERVIEW.txt
+++ /dev/null
@@ -1,78 +0,0 @@
-Alias overview
-==============
-
-A typical Mailman list exposes nine aliases which point to seven different
-wrapped scripts. E.g. for a list named `mylist', you'd have:
-
- mylist-bounces -> bounces
- mylist-confirm -> confirm
- mylist-join -> join (-subscribe is an alias)
- mylist-leave -> leave (-unsubscribe is an alias)
- mylist-owner -> owner
- mylist -> post
- mylist-request -> request
-
--request, -join, and -leave are a robot addresses; their sole purpose is to
-process emailed commands, although the latter two are hardcoded to
-subscription and unsubscription requests. -bounces is the automated bounce
-processor, and all messages to list members have their return address set to
--bounces. If the bounce processor fails to extract a bouncing member address,
-it can optionally forward the message on to the list owners.
-
--owner is for reaching a human operator with minimal list interaction (i.e. no
-bounce processing). -confirm is another robot address which processes replies
-to VERP-like confirmation notices.
-
-So delivery flow of messages look like this:
-
- joerandom ---> mylist ---> list members
- | |
- | |[bounces]
- | mylist-bounces <---+ <-------------------------------+
- | | |
- | +--->[internal bounce processing] |
- | ^ | |
- | | | [bounce found] |
- | [bounces *] +--->[register and discard] |
- | | | | |
- | | | |[*] |
- | [list owners] |[no bounce found] | |
- | ^ | | |
- | | | | |
- +-------> mylist-owner <--------+ | |
- | | |
- | data/owner-bounces.mbox <--[site list] <---+ |
- | |
- +-------> mylist-join--+ |
- | | |
- +------> mylist-leave--+ |
- | | |
- | v |
- +-------> mylist-request |
- | | |
- | +---> [command processor] |
- | | |
- +-----> mylist-confirm ----> +---> joerandom |
- | |
- |[bounces] |
- +----------------------+
-
-A person can send an email to the list address (for posting), the -owner
-address (to reach the human operator), or the -confirm, -join, -leave, and
--request mailbots. Message to the list address are then forwarded on to the
-list membership, with bounces directed to the -bounces address.
-
-[*] Messages sent to the -owner address are forwarded on to the list
-owner/moderators. All -owner destined messages have their bounces directed to
-the site list -bounces address, regardless of whether a human sent the message
-or the message was crafted internally. The intention here is that the site
-owners want to be notified when one of their list owners' addresses starts
-bouncing (yes, the will be automated in a future release).
-
-Any messages to site owners has their bounces directed to a special
-"loop-killer" address, which just dumps the message into
-data/owners-bounces.mbox.
-
-Finally, message to any of the mailbots causes the requested action to be
-performed. Results notifications are sent to the author of the message, which
-all bounces pointing back to the -bounces address.
diff --git a/mailman/queue/docs/archiver.txt b/mailman/queue/docs/archiver.txt
deleted file mode 100644
index 601857cd9..000000000
--- a/mailman/queue/docs/archiver.txt
+++ /dev/null
@@ -1,34 +0,0 @@
-Archiving
-=========
-
-Mailman can archive to any number of archivers that adhere to the IArchiver
-interface. By default, there's a Pipermail archiver.
-
- >>> from mailman.app.lifecycle import create_list
- >>> mlist = create_list(u'test@example.com')
- >>> commit()
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... To: test@example.com
- ... Subject: My first post
- ... Message-ID: <first>
- ...
- ... First post!
- ... """)
-
- >>> archiver_queue = config.switchboards['archive']
- >>> ignore = archiver_queue.enqueue(msg, {}, listname=mlist.fqdn_listname)
-
- >>> from mailman.queue.archive import ArchiveRunner
- >>> from mailman.testing.helpers import make_testable_runner
- >>> runner = make_testable_runner(ArchiveRunner)
- >>> runner.run()
-
- # The best we can do is verify some landmark exists. Let's use the
- # Pipermail pickle file exists.
- >>> listname = mlist.fqdn_listname
- >>> import os
- >>> os.path.exists(os.path.join(
- ... config.PUBLIC_ARCHIVE_FILE_DIR, listname, 'pipermail.pck'))
- True
diff --git a/mailman/queue/docs/command.txt b/mailman/queue/docs/command.txt
deleted file mode 100644
index 0b384de01..000000000
--- a/mailman/queue/docs/command.txt
+++ /dev/null
@@ -1,170 +0,0 @@
-The command queue runner
-========================
-
-This queue runner's purpose is to process and respond to email commands.
-Commands are extensible using the Mailman plugin system, but Mailman comes
-with a number of email commands out of the box. These are processed when a
-message is sent to the list's -request address.
-
- >>> from mailman.app.lifecycle import create_list
- >>> mlist = create_list(u'test@example.com')
-
-
-A command in the Subject
-------------------------
-
-For example, the 'echo' command simply echoes the original command back to the
-sender. The command can be in the Subject header.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... To: test-request@example.com
- ... Subject: echo hello
- ... Message-ID: <aardvark>
- ...
- ... """)
-
- >>> from mailman.inject import inject_message
- >>> inject_message(mlist, msg, switchboard='command')
- >>> from mailman.queue.command import CommandRunner
- >>> from mailman.testing.helpers import make_testable_runner
- >>> command = make_testable_runner(CommandRunner)
- >>> command.run()
-
-And now the response is in the virgin queue.
-
- >>> from mailman.queue import Switchboard
- >>> virgin_queue = config.switchboards['virgin']
- >>> len(virgin_queue.files)
- 1
- >>> from mailman.testing.helpers import get_queue_messages
- >>> item = get_queue_messages('virgin')[0]
- >>> print item.msg.as_string()
- Subject: The results of your email commands
- From: test-bounces@example.com
- To: aperson@example.com
- ...
- <BLANKLINE>
- The results of your email command are provided below.
- <BLANKLINE>
- - Original message details:
- From: aperson@example.com
- Subject: echo hello
- Date: ...
- Message-ID: <aardvark>
- <BLANKLINE>
- - Results:
- echo hello
- <BLANKLINE>
- - Done.
- <BLANKLINE>
- >>> sorted(item.msgdata.items())
- [..., ('listname', u'test@example.com'), ...,
- ('recips', [u'aperson@example.com']),
- ...]
-
-
-A command in the body
----------------------
-
-The command can also be found in the body of the message, as long as the
-message is plain text.
-
- >>> msg = message_from_string("""\
- ... From: bperson@example.com
- ... To: test-request@example.com
- ... Message-ID: <bobcat>
- ...
- ... echo foo bar
- ... """)
-
- >>> inject_message(mlist, msg, switchboard='command')
- >>> command.run()
- >>> len(virgin_queue.files)
- 1
- >>> item = get_queue_messages('virgin')[0]
- >>> print item.msg.as_string()
- Subject: The results of your email commands
- From: test-bounces@example.com
- To: bperson@example.com
- ...
- Precedence: bulk
- <BLANKLINE>
- The results of your email command are provided below.
- <BLANKLINE>
- - Original message details:
- From: bperson@example.com
- Subject: n/a
- Date: ...
- Message-ID: <bobcat>
- <BLANKLINE>
- - Results:
- echo foo bar
- <BLANKLINE>
- - Done.
- <BLANKLINE>
-
-
-Stopping command processing
----------------------------
-
-The 'end' command stops email processing, so that nothing following is looked
-at by the command queue.
-
- >>> msg = message_from_string("""\
- ... From: cperson@example.com
- ... To: test-request@example.com
- ... Message-ID: <caribou>
- ...
- ... echo foo bar
- ... end ignored
- ... echo baz qux
- ... """)
-
- >>> inject_message(mlist, msg, switchboard='command')
- >>> command.run()
- >>> len(virgin_queue.files)
- 1
- >>> item = get_queue_messages('virgin')[0]
- >>> print item.msg.as_string()
- Subject: The results of your email commands
- ...
- <BLANKLINE>
- - Results:
- echo foo bar
- <BLANKLINE>
- - Unprocessed:
- echo baz qux
- <BLANKLINE>
- - Done.
- <BLANKLINE>
-
-The 'stop' command is an alias for 'end'.
-
- >>> msg = message_from_string("""\
- ... From: cperson@example.com
- ... To: test-request@example.com
- ... Message-ID: <caribou>
- ...
- ... echo foo bar
- ... stop ignored
- ... echo baz qux
- ... """)
-
- >>> inject_message(mlist, msg, switchboard='command')
- >>> command.run()
- >>> len(virgin_queue.files)
- 1
- >>> item = get_queue_messages('virgin')[0]
- >>> print item.msg.as_string()
- Subject: The results of your email commands
- ...
- <BLANKLINE>
- - Results:
- echo foo bar
- <BLANKLINE>
- - Unprocessed:
- echo baz qux
- <BLANKLINE>
- - Done.
- <BLANKLINE>
diff --git a/mailman/queue/docs/incoming.txt b/mailman/queue/docs/incoming.txt
deleted file mode 100644
index deb340e71..000000000
--- a/mailman/queue/docs/incoming.txt
+++ /dev/null
@@ -1,200 +0,0 @@
-The incoming queue runner
-=========================
-
-This runner's sole purpose in life is to decide the disposition of the
-message. It can either be accepted for delivery, rejected (i.e. bounced),
-held for moderator approval, or discarded.
-
-The runner operates by processing chains on a message/metadata pair in the
-context of a mailing list. Each mailing list may have a 'start chain' where
-processing begins, with a global default. This chain is processed with the
-message eventually ending up in one of the four disposition states described
-above.
-
- >>> from mailman.app.lifecycle import create_list
- >>> mlist = create_list(u'_xtest@example.com')
- >>> mlist.start_chain
- u'built-in'
-
-
-Accepted messages
------------------
-
-We have a message that is going to be sent to the mailing list. This message
-is so perfectly fine for posting that it will be accepted and forward to the
-pipeline queue.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... To: _xtest@example.com
- ... Subject: My first post
- ... Message-ID: <first>
- ...
- ... First post!
- ... """)
-
-Normally, the upstream mail server would drop the message in the incoming
-queue, but this is an effective simulation.
-
- >>> from mailman.inject import inject_message
- >>> inject_message(mlist, msg)
-
-The incoming queue runner runs until it is empty.
-
- >>> from mailman.queue.incoming import IncomingRunner
- >>> from mailman.testing.helpers import make_testable_runner
- >>> incoming = make_testable_runner(IncomingRunner, 'in')
- >>> incoming.run()
-
-And now the message is in the pipeline queue.
-
- >>> pipeline_queue = config.switchboards['pipeline']
- >>> len(pipeline_queue.files)
- 1
- >>> incoming_queue = config.switchboards['in']
- >>> len(incoming_queue.files)
- 0
- >>> from mailman.testing.helpers import get_queue_messages
- >>> item = get_queue_messages('pipeline')[0]
- >>> print item.msg.as_string()
- From: aperson@example.com
- To: _xtest@example.com
- Subject: My first post
- Message-ID: <first>
- Date: ...
- X-Mailman-Rule-Misses: approved; emergency; loop; administrivia;
- implicit-dest;
- max-recipients; max-size; news-moderation; no-subject;
- suspicious-header
- <BLANKLINE>
- First post!
- <BLANKLINE>
- >>> sorted(item.msgdata.items())
- [...('envsender', u'noreply@example.com')...('tolist', True)...]
-
-
-Held messages
--------------
-
-The list moderator sets the emergency flag on the mailing list. The built-in
-chain will now hold all posted messages, so nothing will show up in the
-pipeline queue.
-
- # XXX This checks the vette log file because there is no other evidence
- # that this chain has done anything.
- >>> import os
- >>> fp = open(os.path.join(config.LOG_DIR, 'vette'))
- >>> fp.seek(0, 2)
-
- >>> mlist.emergency = True
- >>> inject_message(mlist, msg)
- >>> file_pos = fp.tell()
- >>> incoming.run()
- >>> len(pipeline_queue.files)
- 0
- >>> len(incoming_queue.files)
- 0
- >>> fp.seek(file_pos)
- >>> print 'LOG:', fp.read()
- LOG: ... HOLD: _xtest@example.com post from aperson@example.com held,
- message-id=<first>: n/a
- <BLANKLINE>
-
- >>> mlist.emergency = False
-
-
-Discarded messages
-------------------
-
-Another possibility is that the message would get immediately discarded. The
-built-in chain does not have such a disposition by default, so let's craft a
-new chain and set it as the mailing list's start chain.
-
- >>> from mailman.chains.base import Chain, Link
- >>> from mailman.interfaces.chain import LinkAction
- >>> truth_rule = config.rules['truth']
- >>> discard_chain = config.chains['discard']
- >>> test_chain = Chain('always-discard', u'Testing discards')
- >>> link = Link(truth_rule, LinkAction.jump, discard_chain)
- >>> test_chain.append_link(link)
- >>> mlist.start_chain = u'always-discard'
-
- >>> inject_message(mlist, msg)
- >>> file_pos = fp.tell()
- >>> incoming.run()
- >>> len(pipeline_queue.files)
- 0
- >>> len(incoming_queue.files)
- 0
- >>> fp.seek(file_pos)
- >>> print 'LOG:', fp.read()
- LOG: ... DISCARD: <first>
- <BLANKLINE>
-
- >>> del config.chains['always-discard']
-
-
-Rejected messages
------------------
-
-Similar to discarded messages, a message can be rejected, or bounced back to
-the original sender. Again, the built-in chain doesn't support this so we'll
-just create a new chain that does.
-
- >>> reject_chain = config.chains['reject']
- >>> test_chain = Chain('always-reject', u'Testing rejections')
- >>> link = Link(truth_rule, LinkAction.jump, reject_chain)
- >>> test_chain.append_link(link)
- >>> mlist.start_chain = u'always-reject'
-
-The virgin queue needs to be cleared out due to artifacts from the previous
-tests above.
-
- >>> virgin_queue = config.switchboards['virgin']
- >>> ignore = get_queue_messages('virgin')
-
- >>> inject_message(mlist, msg)
- >>> file_pos = fp.tell()
- >>> incoming.run()
- >>> len(pipeline_queue.files)
- 0
- >>> len(incoming_queue.files)
- 0
-
- >>> len(virgin_queue.files)
- 1
- >>> item = get_queue_messages('virgin')[0]
- >>> print item.msg.as_string()
- Subject: My first post
- From: _xtest-owner@example.com
- To: aperson@example.com
- ...
- <BLANKLINE>
- --===============...
- Content-Type: text/plain; charset="us-ascii"
- MIME-Version: 1.0
- Content-Transfer-Encoding: 7bit
- <BLANKLINE>
- [No bounce details are available]
- --===============...
- Content-Type: message/rfc822
- MIME-Version: 1.0
- <BLANKLINE>
- From: aperson@example.com
- To: _xtest@example.com
- Subject: My first post
- Message-ID: <first>
- Date: ...
- <BLANKLINE>
- First post!
- <BLANKLINE>
- --===============...
-
- >>> sorted(item.msgdata.items())
- [...('recips', [u'aperson@example.com'])...]
- >>> fp.seek(file_pos)
- >>> print 'LOG:', fp.read()
- LOG: ... REJECT: <first>
- <BLANKLINE>
-
- >>> del config.chains['always-reject']
diff --git a/mailman/queue/docs/lmtp.txt b/mailman/queue/docs/lmtp.txt
deleted file mode 100644
index 75e91fd4e..000000000
--- a/mailman/queue/docs/lmtp.txt
+++ /dev/null
@@ -1,103 +0,0 @@
-LTMP server
-===========
-
-Mailman can accept messages via LMTP (RFC 2033). Most modern mail servers
-support LMTP local delivery, so this is a very portable way to connect Mailman
-with your mail server.
-
-Our LMTP server is fairly simple though; all it does is make sure that the
-message is destined for a valid endpoint, e.g. mylist-join@example.com.
-
-Let's start a testable LMTP queue runner.
-
- >>> from mailman.testing import helpers
- >>> master = helpers.TestableMaster()
- >>> master.start('lmtp')
-
-It also helps to have a nice LMTP client.
-
- >>> lmtp = helpers.get_lmtp_client()
- (220, '... Python LMTP queue runner 1.0')
- >>> lmtp.lhlo('remote.example.org')
- (250, ...)
-
-
-Posting address
----------------
-
-If the mail server tries to send a message to a nonexistent mailing list, it
-will get a 550 error.
-
- >>> lmtp.sendmail(
- ... 'anne.person@example.com',
- ... ['mylist@example.com'], """\
- ... From: anne.person@example.com
- ... To: mylist@example.com
- ... Subject: An interesting message
- ... Message-ID: <aardvark>
- ...
- ... This is an interesting message.
- ... """)
- Traceback (most recent call last):
- ...
- SMTPDataError: (550, 'Requested action not taken: mailbox unavailable')
-
-Once the mailing list is created, the posting address is valid.
-
- >>> from mailman.app.lifecycle import create_list
- >>> create_list(u'mylist@example.com')
- <mailing list "mylist@example.com" at ...>
- >>> commit()
- >>> lmtp.sendmail(
- ... 'anne.person@example.com',
- ... ['mylist@example.com'], """\
- ... From: anne.person@example.com
- ... To: mylist@example.com
- ... Subject: An interesting message
- ... Message-ID: <badger>
- ...
- ... This is an interesting message.
- ... """)
- {}
-
-
-Sub-addresses
--------------
-
-The LMTP server understands each of the list's sub-addreses, such as -join,
--leave, -request and so on. If the message is posted to an invalid
-sub-address though, it is rejected.
-
- >>> lmtp.sendmail(
- ... 'anne.person@example.com',
- ... ['mylist-bogus@example.com'], """\
- ... From: anne.person@example.com
- ... To: mylist-bogus@example.com
- ... Subject: Help
- ... Message-ID: <cow>
- ...
- ... Please help me.
- ... """)
- Traceback (most recent call last):
- ...
- SMTPDataError: (550, 'Requested action not taken: mailbox unavailable')
-
-But the message is accepted if posted to a valid sub-address.
-
- >>> lmtp.sendmail(
- ... 'anne.person@example.com',
- ... ['mylist-request@example.com'], """\
- ... From: anne.person@example.com
- ... To: mylist-request@example.com
- ... Subject: Help
- ... Message-ID: <dog>
- ...
- ... Please help me.
- ... """)
- {}
-
-
-Clean up
---------
-
- >>> master.stop()
diff --git a/mailman/queue/docs/news.txt b/mailman/queue/docs/news.txt
deleted file mode 100644
index 3375b3d54..000000000
--- a/mailman/queue/docs/news.txt
+++ /dev/null
@@ -1,157 +0,0 @@
-The news runner
-===============
-
-The news runner is the queue runner that gateways mailing list messages to an
-NNTP newsgroup. One of the most important things this runner does is prepare
-the message for Usenet (yes, I know that NNTP is not Usenet, but this runner
-was originally written to gate to Usenet, which has its own rules).
-
- >>> from mailman.queue.news import prepare_message
- >>> mlist = config.db.list_manager.create(u'_xtest@example.com')
- >>> mlist.linked_newsgroup = u'comp.lang.python'
-
-Some NNTP servers such as INN reject messages containing a set of prohibited
-headers, so one of the things that the news runner does is remove these
-prohibited headers.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... To: _xtest@example.com
- ... NNTP-Posting-Host: news.example.com
- ... NNTP-Posting-Date: today
- ... X-Trace: blah blah
- ... X-Complaints-To: abuse@dom.ain
- ... Xref: blah blah
- ... Xref: blah blah
- ... Date-Received: yesterday
- ... Posted: tomorrow
- ... Posting-Version: 99.99
- ... Relay-Version: 88.88
- ... Received: blah blah
- ...
- ... A message
- ... """)
- >>> msgdata = {}
- >>> prepare_message(mlist, msg, msgdata)
- >>> msgdata['prepped']
- True
- >>> print msg.as_string()
- From: aperson@example.com
- To: _xtest@example.com
- Newsgroups: comp.lang.python
- Message-ID: ...
- Lines: 1
- <BLANKLINE>
- A message
- <BLANKLINE>
-
-Some NNTP servers will reject messages where certain headers are duplicated,
-so the news runner must collapse or move these duplicate headers to an
-X-Original-* header that the news server doesn't care about.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... To: _xtest@example.com
- ... To: two@example.com
- ... Cc: three@example.com
- ... Cc: four@example.com
- ... Cc: five@example.com
- ... Content-Transfer-Encoding: yes
- ... Content-Transfer-Encoding: no
- ... Content-Transfer-Encoding: maybe
- ...
- ... A message
- ... """)
- >>> msgdata = {}
- >>> prepare_message(mlist, msg, msgdata)
- >>> msgdata['prepped']
- True
- >>> print msg.as_string()
- From: aperson@example.com
- Newsgroups: comp.lang.python
- Message-ID: ...
- Lines: 1
- To: _xtest@example.com
- X-Original-To: two@example.com
- CC: three@example.com
- X-Original-CC: four@example.com
- X-Original-CC: five@example.com
- Content-Transfer-Encoding: yes
- X-Original-Content-Transfer-Encoding: no
- X-Original-Content-Transfer-Encoding: maybe
- <BLANKLINE>
- A message
- <BLANKLINE>
-
-But if no headers are duplicated, then the news runner doesn't need to modify
-the message.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... To: _xtest@example.com
- ... Cc: someother@example.com
- ... Content-Transfer-Encoding: yes
- ...
- ... A message
- ... """)
- >>> msgdata = {}
- >>> prepare_message(mlist, msg, msgdata)
- >>> msgdata['prepped']
- True
- >>> print msg.as_string()
- From: aperson@example.com
- To: _xtest@example.com
- Cc: someother@example.com
- Content-Transfer-Encoding: yes
- Newsgroups: comp.lang.python
- Message-ID: ...
- Lines: 1
- <BLANKLINE>
- A message
- <BLANKLINE>
-
-
-Newsgroup moderation
---------------------
-
-When the newsgroup is moderated, an Approved: header with the list's posting
-address is added for the benefit of the Usenet system.
-
- >>> from mailman.interfaces import NewsModeration
- >>> mlist.news_moderation = NewsModeration.open_moderated
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... To: _xtest@example.com
- ... Approved: this gets deleted
- ...
- ... """)
- >>> prepare_message(mlist, msg, {})
- >>> msg['approved']
- u'_xtest@example.com'
-
- >>> mlist.news_moderation = NewsModeration.moderated
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... To: _xtest@example.com
- ... Approved: this gets deleted
- ...
- ... """)
- >>> prepare_message(mlist, msg, {})
- >>> msg['approved']
- u'_xtest@example.com'
-
-But if the newsgroup is not moderated, the Approved: header is not chnaged.
-
- >>> mlist.news_moderation = NewsModeration.none
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... To: _xtest@example.com
- ... Approved: this doesn't get deleted
- ...
- ... """)
- >>> prepare_message(mlist, msg, {})
- >>> msg['approved']
- u"this doesn't get deleted"
-
-
-XXX More of the NewsRunner should be tested.
diff --git a/mailman/queue/docs/outgoing.txt b/mailman/queue/docs/outgoing.txt
deleted file mode 100644
index 6722dee84..000000000
--- a/mailman/queue/docs/outgoing.txt
+++ /dev/null
@@ -1,75 +0,0 @@
-Outgoing queue runner
-=====================
-
-The outgoing queue runner is the process that delivers messages to the
-directly upstream SMTP server. It is this external SMTP server that performs
-final delivery to the intended recipients.
-
-Messages that appear in the outgoing queue are processed individually through
-a 'delivery module', essentially a pluggable interface for determining how the
-recipient set will be batched, whether messages will be personalized and
-VERP'd, etc. The outgoing runner doesn't itself support retrying but it can
-move messages to the 'retry queue' for handling delivery failures.
-
- >>> from mailman.app.lifecycle import create_list
- >>> mlist = create_list(u'test@example.com')
-
- >>> from mailman.app.membership import add_member
- >>> from mailman.interfaces.member import DeliveryMode
- >>> add_member(mlist, u'aperson@example.com', u'Anne Person',
- ... u'password', DeliveryMode.regular, u'en')
- >>> add_member(mlist, u'bperson@example.com', u'Bart Person',
- ... u'password', DeliveryMode.regular, u'en')
- >>> add_member(mlist, u'cperson@example.com', u'Cris Person',
- ... u'password', DeliveryMode.regular, u'en')
-
-By setting the mailing list to personalize messages, each recipient will get a
-unique copy of the message, with certain headers tailored for that recipient.
-
- >>> from mailman.interfaces.mailinglist import Personalization
- >>> mlist.personalize = Personalization.individual
- >>> commit()
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... To: test@example.com
- ... Subject: My first post
- ... Message-ID: <first>
- ...
- ... First post!
- ... """)
-
-Normally, messages would show up in the outgoing queue after the message has
-been processed by the rule set and pipeline. But we can simulate that here by
-injecting a message directly into the outgoing queue.
-
- >>> msgdata = {}
- >>> handler = config.handlers['calculate-recipients']
- >>> handler.process(mlist, msg, msgdata)
-
- >>> outgoing_queue = config.switchboards['out']
- >>> ignore = outgoing_queue.enqueue(
- ... msg, msgdata,
- ... verp=True, listname=mlist.fqdn_listname, tolist=True,
- ... _plaintext=True)
-
-Running the outgoing queue runner processes the message, delivering it to the
-upstream SMTP, which happens to be our test server.
-
- >>> from mailman.queue.outgoing import OutgoingRunner
- >>> from mailman.testing.helpers import make_testable_runner
- >>> outgoing = make_testable_runner(OutgoingRunner, 'out')
- >>> outgoing.run()
-
-Three messages have been delivered to our SMTP server, one for each recipient.
-
- >>> from operator import itemgetter
- >>> messages = sorted(smtpd.messages, key=itemgetter('sender'))
- >>> len(messages)
- 3
-
- >>> for message in messages:
- ... print message['sender']
- test-bounces+aperson=example.com@example.com
- test-bounces+bperson=example.com@example.com
- test-bounces+cperson=example.com@example.com
diff --git a/mailman/queue/docs/runner.txt b/mailman/queue/docs/runner.txt
deleted file mode 100644
index d24a8334c..000000000
--- a/mailman/queue/docs/runner.txt
+++ /dev/null
@@ -1,72 +0,0 @@
-Queue runners
-=============
-
-The queue runners (qrunner) are the processes that move messages around the
-Mailman system. Each qrunner is responsible for a slice of the hash space in
-a queue directory. It processes all the files in its slice, sleeps a little
-while, then wakes up and runs through its queue files again.
-
-
-Basic architecture
-------------------
-
-The basic architecture of qrunner is implemented in the base class that all
-runners inherit from. This base class implements a .run() method that runs
-continuously in a loop until the .stop() method is called.
-
- >>> mlist = config.db.list_manager.create(u'_xtest@example.com')
- >>> mlist.preferred_language = u'en'
-
-Here is a very simple derived qrunner class. Queue runners use a
-configuration section in the configuration files to determine run
-characteristics, such as the queue directory to use. Here we push a
-configuration section for the test runner.
-
- >>> config.push('test-runner', """
- ... [qrunner.test]
- ... max_restarts: 1
- ... """)
-
- >>> from mailman.queue import Runner
- >>> class TestableRunner(Runner):
- ... def _dispose(self, mlist, msg, msgdata):
- ... self.msg = msg
- ... self.msgdata = msgdata
- ... return False
- ...
- ... def _do_periodic(self):
- ... self.stop()
- ...
- ... def _snooze(self, filecnt):
- ... return
-
- >>> runner = TestableRunner('test')
-
-This qrunner doesn't do much except run once, storing the message and metadata
-on instance variables.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... To: _xtest@example.com
- ...
- ... A test message.
- ... """)
- >>> switchboard = config.switchboards['test']
- >>> filebase = switchboard.enqueue(msg, listname=mlist.fqdn_listname,
- ... foo='yes', bar='no')
- >>> runner.run()
- >>> print runner.msg.as_string()
- From: aperson@example.com
- To: _xtest@example.com
- <BLANKLINE>
- A test message.
- <BLANKLINE>
- >>> dump_msgdata(runner.msgdata)
- _parsemsg: False
- bar : no
- foo : yes
- lang : en
- listname : _xtest@example.com
- version : 3
-
-XXX More of the Runner API should be tested.
diff --git a/mailman/queue/docs/switchboard.txt b/mailman/queue/docs/switchboard.txt
deleted file mode 100644
index 88ab6ea93..000000000
--- a/mailman/queue/docs/switchboard.txt
+++ /dev/null
@@ -1,182 +0,0 @@
-The switchboard
-===============
-
-The switchboard is subsystem that moves messages between queues. Each
-instance of a switchboard is responsible for one queue directory.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... To: _xtest@example.com
- ...
- ... A test message.
- ... """)
-
-Create a switchboard by giving its queue directory.
-
- >>> import os
- >>> queue_directory = os.path.join(config.QUEUE_DIR, 'test')
- >>> from mailman.queue import Switchboard
- >>> switchboard = Switchboard(queue_directory)
- >>> switchboard.queue_directory == queue_directory
- True
-
-Here's a helper function for ensuring things work correctly.
-
- >>> def check_qfiles(directory=None):
- ... if directory is None:
- ... directory = queue_directory
- ... files = {}
- ... for qfile in os.listdir(directory):
- ... root, ext = os.path.splitext(qfile)
- ... files[ext] = files.get(ext, 0) + 1
- ... if len(files) == 0:
- ... print 'empty'
- ... for ext in sorted(files):
- ... print '{0}: {1}'.format(ext, files[ext])
-
-
-Enqueing and dequeing
----------------------
-
-The message can be enqueued with metadata specified in the passed in
-dictionary.
-
- >>> filebase = switchboard.enqueue(msg)
- >>> check_qfiles()
- .pck: 1
-
-To read the contents of a queue file, dequeue it.
-
- >>> msg, msgdata = switchboard.dequeue(filebase)
- >>> print msg.as_string()
- From: aperson@example.com
- To: _xtest@example.com
- <BLANKLINE>
- A test message.
- <BLANKLINE>
- >>> dump_msgdata(msgdata)
- _parsemsg: False
- version : 3
- >>> check_qfiles()
- .bak: 1
-
-To complete the dequeing process, removing all traces of the message file,
-finish it (without preservation).
-
- >>> switchboard.finish(filebase)
- >>> check_qfiles()
- empty
-
-When enqueing a file, you can provide additional metadata keys by using
-keyword arguments.
-
- >>> filebase = switchboard.enqueue(msg, {'foo': 1}, bar=2)
- >>> msg, msgdata = switchboard.dequeue(filebase)
- >>> switchboard.finish(filebase)
- >>> dump_msgdata(msgdata)
- _parsemsg: False
- bar : 2
- foo : 1
- version : 3
-
-Keyword arguments override keys from the metadata dictionary.
-
- >>> filebase = switchboard.enqueue(msg, {'foo': 1}, foo=2)
- >>> msg, msgdata = switchboard.dequeue(filebase)
- >>> switchboard.finish(filebase)
- >>> dump_msgdata(msgdata)
- _parsemsg: False
- foo : 2
- version : 3
-
-
-Iterating over files
---------------------
-
-There are two ways to iterate over all the files in a switchboard's queue.
-Normally, queue files end in .pck (for 'pickle') and the easiest way to
-iterate over just these files is to use the .files attribute.
-
- >>> filebase_1 = switchboard.enqueue(msg, foo=1)
- >>> filebase_2 = switchboard.enqueue(msg, foo=2)
- >>> filebase_3 = switchboard.enqueue(msg, foo=3)
- >>> filebases = sorted((filebase_1, filebase_2, filebase_3))
- >>> sorted(switchboard.files) == filebases
- True
- >>> check_qfiles()
- .pck: 3
-
-You can also use the .get_files() method if you want to iterate over all the
-file bases for some other extension.
-
- >>> for filebase in switchboard.get_files():
- ... msg, msgdata = switchboard.dequeue(filebase)
- >>> bakfiles = sorted(switchboard.get_files('.bak'))
- >>> bakfiles == filebases
- True
- >>> check_qfiles()
- .bak: 3
- >>> for filebase in switchboard.get_files('.bak'):
- ... switchboard.finish(filebase)
- >>> check_qfiles()
- empty
-
-
-Recovering files
-----------------
-
-Calling .dequeue() without calling .finish() leaves .bak backup files in
-place. These can be recovered when the switchboard is instantiated.
-
- >>> filebase_1 = switchboard.enqueue(msg, foo=1)
- >>> filebase_2 = switchboard.enqueue(msg, foo=2)
- >>> filebase_3 = switchboard.enqueue(msg, foo=3)
- >>> for filebase in switchboard.files:
- ... msg, msgdata = switchboard.dequeue(filebase)
- ... # Don't call .finish()
- >>> check_qfiles()
- .bak: 3
- >>> switchboard_2 = Switchboard(queue_directory, recover=True)
- >>> check_qfiles()
- .pck: 3
-
-The files can be recovered explicitly.
-
- >>> for filebase in switchboard.files:
- ... msg, msgdata = switchboard.dequeue(filebase)
- ... # Don't call .finish()
- >>> check_qfiles()
- .bak: 3
- >>> switchboard.recover_backup_files()
- >>> check_qfiles()
- .pck: 3
-
-But the files will only be recovered at most three times before they are
-considered defective. In order to prevent mail bombs and loops, once this
-maximum is reached, the files will be preserved in the 'bad' queue.
-
- >>> for filebase in switchboard.files:
- ... msg, msgdata = switchboard.dequeue(filebase)
- ... # Don't call .finish()
- >>> check_qfiles()
- .bak: 3
- >>> switchboard.recover_backup_files()
- >>> check_qfiles()
- empty
-
- >>> bad = config.switchboards['bad']
- >>> check_qfiles(bad.queue_directory)
- .psv: 3
-
-Clean up
-
- >>> for file in os.listdir(bad.queue_directory):
- ... os.remove(os.path.join(bad.queue_directory, file))
- >>> check_qfiles(bad.queue_directory)
- empty
-
-
-Queue slices
-------------
-
-XXX Add tests for queue slices.