summaryrefslogtreecommitdiff
path: root/src/mailman/queue/docs/incoming.txt
diff options
context:
space:
mode:
Diffstat (limited to 'src/mailman/queue/docs/incoming.txt')
-rw-r--r--src/mailman/queue/docs/incoming.txt200
1 files changed, 200 insertions, 0 deletions
diff --git a/src/mailman/queue/docs/incoming.txt b/src/mailman/queue/docs/incoming.txt
new file mode 100644
index 000000000..deb340e71
--- /dev/null
+++ b/src/mailman/queue/docs/incoming.txt
@@ -0,0 +1,200 @@
+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']