diff options
Diffstat (limited to 'src/mailman/rules/docs/approved.rst')
| -rw-r--r-- | src/mailman/rules/docs/approved.rst | 514 |
1 files changed, 514 insertions, 0 deletions
diff --git a/src/mailman/rules/docs/approved.rst b/src/mailman/rules/docs/approved.rst new file mode 100644 index 000000000..3e1206563 --- /dev/null +++ b/src/mailman/rules/docs/approved.rst @@ -0,0 +1,514 @@ +===================== +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" + <BLANKLINE> + An important message. + <BLANKLINE> + +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" + <BLANKLINE> + An important message. + <BLANKLINE> + +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" + <BLANKLINE> + An important message. + <BLANKLINE> + +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" + <BLANKLINE> + An important message. + <BLANKLINE> + + +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" + <BLANKLINE> + --AAA + Content-Type: application/x-ignore + <BLANKLINE> + Approve: 123456 + The above line will be ignored. + <BLANKLINE> + --AAA + Content-Transfer-Encoding: 7bit + MIME-Version: 1.0 + Content-Type: text/plain; charset="us-ascii" + <BLANKLINE> + An important message. + --AAA-- + <BLANKLINE> + +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" + <BLANKLINE> + --AAA + Content-Type: application/x-ignore + <BLANKLINE> + Approved: 123456 + The above line will be ignored. + <BLANKLINE> + --AAA + Content-Transfer-Encoding: 7bit + MIME-Version: 1.0 + Content-Type: text/plain; charset="us-ascii" + <BLANKLINE> + An important message. + --AAA-- + <BLANKLINE> + +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" + <BLANKLINE> + --AAA + Content-Type: application/x-ignore + <BLANKLINE> + Approve: abcxyz + The above line will be ignored. + <BLANKLINE> + --AAA + Content-Transfer-Encoding: 7bit + MIME-Version: 1.0 + Content-Type: text/plain; charset="us-ascii" + <BLANKLINE> + 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" + <BLANKLINE> + --AAA + Content-Type: application/x-ignore + <BLANKLINE> + Approved: abcxyz + The above line will be ignored. + <BLANKLINE> + --AAA + Content-Transfer-Encoding: 7bit + MIME-Version: 1.0 + Content-Type: text/plain; charset="us-ascii" + <BLANKLINE> + 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 + ... + ... <html> + ... <head></head> + ... <body> + ... <b>Approved: abcxyz</b> + ... <p>The above line will be ignored. + ... </body> + ... </html> + ... + ... --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" + <BLANKLINE> + --AAA + Content-Transfer-Encoding: 7bit + MIME-Version: 1.0 + Content-Type: text/html; charset="us-ascii" + <BLANKLINE> + <html> + <head></head> + <body> + <b></b> + <p>The above line will be ignored. + </body> + </html> + <BLANKLINE> + --AAA + Content-Transfer-Encoding: 7bit + MIME-Version: 1.0 + Content-Type: text/plain; charset="us-ascii" + <BLANKLINE> + An important message. + --AAA-- + <BLANKLINE> + +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 + ... + ... <html> + ... <head></head> + ... <body> + ... <b>Approve: 123456</b> + ... <p>The above line will be ignored. + ... </body> + ... </html> + ... + ... --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" + <BLANKLINE> + --AAA + Content-Transfer-Encoding: 7bit + MIME-Version: 1.0 + Content-Type: text/html; charset="us-ascii" + <BLANKLINE> + <html> + <head></head> + <body> + <b></b> + <p>The above line will be ignored. + </body> + </html> + <BLANKLINE> + --AAA + Content-Transfer-Encoding: 7bit + MIME-Version: 1.0 + Content-Type: text/plain; charset="us-ascii" + <BLANKLINE> + An important message. + --AAA-- + <BLANKLINE> |
