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.

>>> mlist = create_list('_xtest@example.com')
>>> print mlist.start_chain
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>
>>> dump_msgdata(item.msgdata)
_parsemsg    : False
envsender    : noreply@example.com
...

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', 'Testing discards')
>>> link = Link(truth_rule, LinkAction.jump, discard_chain)
>>> test_chain.append_link(link)
>>> mlist.start_chain = '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', 'Testing rejections')
>>> link = Link(truth_rule, LinkAction.jump, reject_chain)
>>> test_chain.append_link(link)
>>> mlist.start_chain = '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>
--===============...

>>> dump_msgdata(item.msgdata)
_parsemsg           : False
...
recipients          : [u'aperson@example.com']
...

>>> fp.seek(file_pos)
>>> print 'LOG:', fp.read()
LOG: ... REJECT: <first>
<BLANKLINE>

>>> del config.chains['always-reject']

Table Of Contents

Previous topic

Digesting

Next topic

LTMP server

This Page