diff options
| author | Barry Warsaw | 2009-01-25 13:01:41 -0500 |
|---|---|---|
| committer | Barry Warsaw | 2009-01-25 13:01:41 -0500 |
| commit | eefd06f1b88b8ecbb23a9013cd223b72ca85c20d (patch) | |
| tree | 72c947fe16fce0e07e996ee74020b26585d7e846 /mailman/queue/docs | |
| parent | 07871212f74498abd56bef3919bf3e029eb8b930 (diff) | |
| download | mailman-eefd06f1b88b8ecbb23a9013cd223b72ca85c20d.tar.gz mailman-eefd06f1b88b8ecbb23a9013cd223b72ca85c20d.tar.zst mailman-eefd06f1b88b8ecbb23a9013cd223b72ca85c20d.zip | |
Diffstat (limited to 'mailman/queue/docs')
| -rw-r--r-- | mailman/queue/docs/OVERVIEW.txt | 78 | ||||
| -rw-r--r-- | mailman/queue/docs/archiver.txt | 34 | ||||
| -rw-r--r-- | mailman/queue/docs/command.txt | 170 | ||||
| -rw-r--r-- | mailman/queue/docs/incoming.txt | 200 | ||||
| -rw-r--r-- | mailman/queue/docs/lmtp.txt | 103 | ||||
| -rw-r--r-- | mailman/queue/docs/news.txt | 157 | ||||
| -rw-r--r-- | mailman/queue/docs/outgoing.txt | 75 | ||||
| -rw-r--r-- | mailman/queue/docs/runner.txt | 72 | ||||
| -rw-r--r-- | mailman/queue/docs/switchboard.txt | 182 |
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. |
