===================== Pre-approved postings ===================== Messages can contain a pre-approval, which is used to bypass the message approval queue. This has several use cases: - A list administrator can send an emergency message to the mailing list from an unregistered address, say if they are away from their normal email. - An automated script can be programmed to send a message to an otherwise moderated list. In order to support this, a mailing list can be given a *moderator password* which is shared among all the administrators. >>> mlist = create_list('_xtest@example.com') >>> mlist.moderator_password = 'abcxyz' The ``approved`` rule determines whether the message contains the proper approval or not. >>> rule = config.rules['approved'] >>> print rule.name approved No approval =========== If the message has no ``Approve:`` or ``Approved:`` header (or their ``X-`` equivalents), then the rule does not match. >>> msg = message_from_string("""\ ... From: aperson@example.com ... ... An important message. ... """) >>> rule.check(mlist, msg, {}) False If the message has an ``Approve:``, ``Approved:``, ``X-Approve:``, or ``X-Approved:`` header with a value that does not match the moderator password, then the rule does not match. However, the header is still removed. :: >>> msg['Approve'] = '12345' >>> rule.check(mlist, msg, {}) False >>> print msg['approve'] None >>> del msg['approve'] >>> msg['Approved'] = '12345' >>> rule.check(mlist, msg, {}) False >>> print msg['approved'] None >>> del msg['approved'] >>> msg['X-Approve'] = '12345' >>> rule.check(mlist, msg, {}) False >>> print msg['x-approve'] None >>> del msg['x-approve'] >>> msg['X-Approved'] = '12345' >>> rule.check(mlist, msg, {}) False >>> print msg['x-approved'] None >>> del msg['x-approved'] Using an approval header ======================== If the moderator password is given in an ``Approve:`` header, then the rule matches, and the ``Approve:`` header is stripped. >>> msg['Approve'] = 'abcxyz' >>> rule.check(mlist, msg, {}) True >>> print msg['approve'] None Similarly, for the ``Approved:`` header. >>> del msg['approve'] >>> msg['Approved'] = 'abcxyz' >>> rule.check(mlist, msg, {}) True >>> print msg['approved'] None The headers ``X-Approve:`` and ``X-Approved:`` are treated the same way. :: >>> del msg['approved'] >>> msg['X-Approve'] = 'abcxyz' >>> rule.check(mlist, msg, {}) True >>> print msg['x-approve'] None >>> del msg['x-approve'] >>> msg['X-Approved'] = 'abcxyz' >>> rule.check(mlist, msg, {}) True >>> print msg['x-approved'] None >>> del msg['x-approved'] Using a pseudo-header ===================== Different mail user agents have varying degrees to which they support custom headers like ``Approve:`` and ``Approved:``. For this reason, Mailman also supports using a *pseudo-header*, which is really just the first non-whitespace line in the payload of the message. If this pseudo-header looks like a matching ``Approve:`` or ``Approved:`` header, the message is similarly allowed to pass. >>> msg = message_from_string("""\ ... From: aperson@example.com ... ... Approve: abcxyz ... An important message. ... """) >>> rule.check(mlist, msg, {}) True The pseudo-header is removed. >>> print msg.as_string() From: aperson@example.com Content-Transfer-Encoding: 7bit MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" An important message. Similarly for the ``Approved:`` header. :: >>> msg = message_from_string("""\ ... From: aperson@example.com ... ... Approved: abcxyz ... An important message. ... """) >>> rule.check(mlist, msg, {}) True >>> print msg.as_string() From: aperson@example.com Content-Transfer-Encoding: 7bit MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" An important message. As before, a mismatch in the pseudo-header does not approve the message, but the pseudo-header line is still removed. :: >>> msg = message_from_string("""\ ... From: aperson@example.com ... ... Approve: 123456 ... An important message. ... """) >>> rule.check(mlist, msg, {}) False >>> print msg.as_string() From: aperson@example.com Content-Transfer-Encoding: 7bit MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" An important message. Similarly for the ``Approved:`` header. :: >>> msg = message_from_string("""\ ... From: aperson@example.com ... ... Approved: 123456 ... An important message. ... """) >>> rule.check(mlist, msg, {}) False >>> print msg.as_string() From: aperson@example.com Content-Transfer-Encoding: 7bit MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" An important message. MIME multipart support ====================== Mailman searches for the pseudo-header as the first non-whitespace line in the first ``text/plain`` message part of the message. This allows the feature to be used with MIME documents. >>> msg = message_from_string("""\ ... From: aperson@example.com ... MIME-Version: 1.0 ... Content-Type: multipart/mixed; boundary="AAA" ... ... --AAA ... Content-Type: application/x-ignore ... ... Approve: 123456 ... The above line will be ignored. ... ... --AAA ... Content-Type: text/plain ... ... Approve: abcxyz ... An important message. ... --AAA-- ... """) >>> rule.check(mlist, msg, {}) True Like before, the pseudo-header is removed, but only from the text parts. >>> print msg.as_string() From: aperson@example.com MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="AAA" --AAA Content-Type: application/x-ignore Approve: 123456 The above line will be ignored. --AAA Content-Transfer-Encoding: 7bit MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" An important message. --AAA-- The same goes for the ``Approved:`` message. >>> msg = message_from_string("""\ ... From: aperson@example.com ... MIME-Version: 1.0 ... Content-Type: multipart/mixed; boundary="AAA" ... ... --AAA ... Content-Type: application/x-ignore ... ... Approved: 123456 ... The above line will be ignored. ... ... --AAA ... Content-Type: text/plain ... ... Approved: abcxyz ... An important message. ... --AAA-- ... """) >>> rule.check(mlist, msg, {}) True And the header is removed. >>> print msg.as_string() From: aperson@example.com MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="AAA" --AAA Content-Type: application/x-ignore Approved: 123456 The above line will be ignored. --AAA Content-Transfer-Encoding: 7bit MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" An important message. --AAA-- Here, the correct password is in the non-``text/plain`` part, so it is ignored. >>> msg = message_from_string("""\ ... From: aperson@example.com ... MIME-Version: 1.0 ... Content-Type: multipart/mixed; boundary="AAA" ... ... --AAA ... Content-Type: application/x-ignore ... ... Approve: abcxyz ... The above line will be ignored. ... ... --AAA ... Content-Type: text/plain ... ... Approve: 123456 ... An important message. ... --AAA-- ... """) >>> rule.check(mlist, msg, {}) False And yet the pseudo-header is still stripped. >>> print msg.as_string() From: aperson@example.com MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="AAA" --AAA Content-Type: application/x-ignore Approve: abcxyz The above line will be ignored. --AAA Content-Transfer-Encoding: 7bit MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" An important message. --AAA-- As before, the same goes for the ``Approved:`` header. >>> msg = message_from_string("""\ ... From: aperson@example.com ... MIME-Version: 1.0 ... Content-Type: multipart/mixed; boundary="AAA" ... ... --AAA ... Content-Type: application/x-ignore ... ... Approved: abcxyz ... The above line will be ignored. ... ... --AAA ... Content-Type: text/plain ... ... Approved: 123456 ... An important message. ... --AAA-- ... """) >>> rule.check(mlist, msg, {}) False And the pseudo-header is removed. >>> print msg.as_string() From: aperson@example.com MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="AAA" --AAA Content-Type: application/x-ignore Approved: abcxyz The above line will be ignored. --AAA Content-Transfer-Encoding: 7bit MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" An important message. --AAA-- Stripping text/html parts ========================= Because some mail readers will include both a ``text/plain`` part and a ``text/html`` alternative, the ``approved`` rule has to search the alternatives and strip anything that looks like an ``Approve:`` or ``Approved:`` headers. >>> msg = message_from_string("""\ ... From: aperson@example.com ... MIME-Version: 1.0 ... Content-Type: multipart/mixed; boundary="AAA" ... ... --AAA ... Content-Type: text/html ... ... ... ... ... Approved: abcxyz ...

The above line will be ignored. ... ... ... ... --AAA ... Content-Type: text/plain ... ... Approved: abcxyz ... An important message. ... --AAA-- ... """) >>> rule.check(mlist, msg, {}) True And the header-like text in the ``text/html`` part was stripped. >>> print msg.as_string() From: aperson@example.com MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="AAA" --AAA Content-Transfer-Encoding: 7bit MIME-Version: 1.0 Content-Type: text/html; charset="us-ascii"

The above line will be ignored. --AAA Content-Transfer-Encoding: 7bit MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" An important message. --AAA-- This is true even if the rule does not match. :: >>> msg = message_from_string("""\ ... From: aperson@example.com ... MIME-Version: 1.0 ... Content-Type: multipart/mixed; boundary="AAA" ... ... --AAA ... Content-Type: text/html ... ... ... ... ... Approve: 123456 ...

The above line will be ignored. ... ... ... ... --AAA ... Content-Type: text/plain ... ... Approve: 123456 ... An important message. ... --AAA-- ... """) >>> rule.check(mlist, msg, {}) False >>> print msg.as_string() From: aperson@example.com MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="AAA" --AAA Content-Transfer-Encoding: 7bit MIME-Version: 1.0 Content-Type: text/html; charset="us-ascii"

The above line will be ignored. --AAA Content-Transfer-Encoding: 7bit MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" An important message. --AAA--