summaryrefslogtreecommitdiff
path: root/mailman/rules/docs
diff options
context:
space:
mode:
authorBarry Warsaw2009-01-25 13:01:41 -0500
committerBarry Warsaw2009-01-25 13:01:41 -0500
commiteefd06f1b88b8ecbb23a9013cd223b72ca85c20d (patch)
tree72c947fe16fce0e07e996ee74020b26585d7e846 /mailman/rules/docs
parent07871212f74498abd56bef3919bf3e029eb8b930 (diff)
downloadmailman-eefd06f1b88b8ecbb23a9013cd223b72ca85c20d.tar.gz
mailman-eefd06f1b88b8ecbb23a9013cd223b72ca85c20d.tar.zst
mailman-eefd06f1b88b8ecbb23a9013cd223b72ca85c20d.zip
Diffstat (limited to 'mailman/rules/docs')
-rw-r--r--mailman/rules/docs/administrivia.txt99
-rw-r--r--mailman/rules/docs/approve.txt472
-rw-r--r--mailman/rules/docs/emergency.txt72
-rw-r--r--mailman/rules/docs/header-matching.txt144
-rw-r--r--mailman/rules/docs/implicit-dest.txt75
-rw-r--r--mailman/rules/docs/loop.txt48
-rw-r--r--mailman/rules/docs/max-size.txt39
-rw-r--r--mailman/rules/docs/moderation.txt69
-rw-r--r--mailman/rules/docs/news-moderation.txt36
-rw-r--r--mailman/rules/docs/no-subject.txt33
-rw-r--r--mailman/rules/docs/recipients.txt40
-rw-r--r--mailman/rules/docs/rules.txt69
-rw-r--r--mailman/rules/docs/suspicious.txt35
-rw-r--r--mailman/rules/docs/truth.txt9
14 files changed, 0 insertions, 1240 deletions
diff --git a/mailman/rules/docs/administrivia.txt b/mailman/rules/docs/administrivia.txt
deleted file mode 100644
index dba882775..000000000
--- a/mailman/rules/docs/administrivia.txt
+++ /dev/null
@@ -1,99 +0,0 @@
-Administrivia
-=============
-
-The 'administrivia' rule matches when the message contains some common email
-commands in the Subject header or first few lines of the payload. This is
-used to catch messages posted to the list which should have been sent to the
--request robot address.
-
- >>> mlist = config.db.list_manager.create(u'_xtest@example.com')
- >>> mlist.administrivia = True
- >>> rule = config.rules['administrivia']
- >>> print rule.name
- administrivia
-
-For example, if the Subject header contains the word 'unsubscribe', the rule
-matches.
-
- >>> msg_1 = message_from_string("""\
- ... From: aperson@example.com
- ... Subject: unsubscribe
- ...
- ... """)
- >>> rule.check(mlist, msg_1, {})
- True
-
-Similarly, if the body of the message contains the word 'subscribe' in the
-first few lines of text, the rule matches.
-
- >>> msg_2 = message_from_string("""\
- ... From: aperson@example.com
- ... Subject: I wish to join your list
- ...
- ... subscribe
- ... """)
- >>> rule.check(mlist, msg_2, {})
- True
-
-In both cases, administrivia checking can be disabled.
-
- >>> mlist.administrivia = False
- >>> rule.check(mlist, msg_1, {})
- False
- >>> rule.check(mlist, msg_2, {})
- False
-
-To make the administrivia heuristics a little more robust, the rule actually
-looks for a minimum and maximum number of arguments, so that it really does
-seem like a mis-addressed email command. In this case, the 'confirm' command
-requires at least one argument. We don't give that here so the rule will not
-match.
-
- >>> mlist.administrivia = True
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... Subject: confirm
- ...
- ... """)
- >>> rule.check(mlist, msg, {})
- False
-
-But a real 'confirm' message will match.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... Subject: confirm 12345
- ...
- ... """)
- >>> rule.check(mlist, msg, {})
- True
-
-We don't show all the other possible email commands, but you get the idea.
-
-
-Non-administrivia
------------------
-
-Of course, messages that don't contain administrivia, don't match the rule.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... Subject: examine
- ...
- ... persuade
- ... """)
- >>> rule.check(mlist, msg, {})
- False
-
-Also, only text/plain parts are checked for administrivia, so any email
-commands in other content type subparts are ignored.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... Subject: some administrivia
- ... Content-Type: text/x-special
- ...
- ... subscribe
- ... """)
- >>> rule.check(mlist, msg, {})
- False
diff --git a/mailman/rules/docs/approve.txt b/mailman/rules/docs/approve.txt
deleted file mode 100644
index dda531a4c..000000000
--- a/mailman/rules/docs/approve.txt
+++ /dev/null
@@ -1,472 +0,0 @@
-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 = config.db.list_manager.create(u'_xtest@example.com')
- >>> mlist.moderator_password = u'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, 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 or 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'] = u'12345'
- >>> rule.check(mlist, msg, {})
- False
- >>> print msg['approve']
- None
-
- >>> del msg['approve']
- >>> msg['Approved'] = u'12345'
- >>> rule.check(mlist, msg, {})
- False
- >>> print msg['approved']
- None
-
- >>> del msg['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'] = u'abcxyz'
- >>> rule.check(mlist, msg, {})
- True
- >>> print msg['approve']
- None
-
-Similarly, for the Approved header.
-
- >>> msg['Approved'] = u'abcxyz'
- >>> rule.check(mlist, msg, {})
- True
- >>> print msg['approved']
- None
-
-
-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>
diff --git a/mailman/rules/docs/emergency.txt b/mailman/rules/docs/emergency.txt
deleted file mode 100644
index 9d80fdb40..000000000
--- a/mailman/rules/docs/emergency.txt
+++ /dev/null
@@ -1,72 +0,0 @@
-Emergency
-=========
-
-When the mailing list has its emergency flag set, all messages posted to the
-list are held for moderator approval.
-
- >>> from mailman.app.lifecycle import create_list
- >>> mlist = create_list(u'_xtest@example.com')
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... To: _xtest@example.com
- ... Subject: My first post
- ... Message-ID: <first>
- ...
- ... An important message.
- ... """)
-
-The emergency rule is matched as part of the built-in chain. The emergency
-rule matches if the flag is set on the mailing list.
-
- >>> from mailman.core.chains import process
- >>> mlist.emergency = True
- >>> process(mlist, msg, {}, 'built-in')
-
-There are two messages in the virgin queue. The one addressed to the original
-sender will contain a token we can use to grab the held message out of the
-pending requests.
-
- >>> virginq = config.switchboards['virgin']
-
- >>> def get_held_message():
- ... import re
- ... qfiles = []
- ... for filebase in virginq.files:
- ... qmsg, qdata = virginq.dequeue(filebase)
- ... virginq.finish(filebase)
- ... qfiles.append(qmsg)
- ... from operator import itemgetter
- ... qfiles.sort(key=itemgetter('to'))
- ... cookie = None
- ... for line in qfiles[1].get_payload().splitlines():
- ... mo = re.search('confirm/[^/]+/(?P<cookie>.*)$', line)
- ... if mo:
- ... cookie = mo.group('cookie')
- ... break
- ... assert cookie is not None, 'No confirmation token found'
- ... data = config.db.pendings.confirm(cookie)
- ... requestdb = config.db.requests.get_list_requests(mlist)
- ... rkey, rdata = requestdb.get_request(data['id'])
- ... return config.db.message_store.get_message_by_id(
- ... rdata['_mod_message_id'])
-
- >>> msg = get_held_message()
- >>> print msg.as_string()
- From: aperson@example.com
- To: _xtest@example.com
- Subject: My first post
- Message-ID: <first>
- X-Mailman-Rule-Hits: emergency
- X-Mailman-Rule-Misses: approved
- X-Message-ID-Hash: RXJU4JL6N2OUN3OYMXXPPSCR7P7JE2BW
- <BLANKLINE>
- An important message.
- <BLANKLINE>
-
-However, if the message metadata has a 'moderator_approved' key set, then even
-if the mailing list has its emergency flag set, the message still goes through
-to the membership.
-
- >>> process(mlist, msg, dict(moderator_approved=True), 'built-in')
- >>> len(virginq.files)
- 0
diff --git a/mailman/rules/docs/header-matching.txt b/mailman/rules/docs/header-matching.txt
deleted file mode 100644
index 417000d67..000000000
--- a/mailman/rules/docs/header-matching.txt
+++ /dev/null
@@ -1,144 +0,0 @@
-Header matching
-===============
-
-Mailman can do pattern based header matching during its normal rule
-processing. There is a set of site-wide default header matches specified in
-the configuration file under the [spam.headers] section.
-
- >>> from mailman.app.lifecycle import create_list
- >>> mlist = create_list(u'_xtest@example.com')
-
-Because the default [spam.headers] section is empty, we'll just extend the
-current header matching chain with a pattern that matches 4 or more stars,
-discarding the message if it hits.
-
- >>> chain = config.chains['header-match']
- >>> chain.extend('x-spam-score', '[*]{4,}', 'discard')
-
-First, if the message has no X-Spam-Score header, the message passes through
-the chain untouched (i.e. no disposition).
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... To: _xtest@example.com
- ... Subject: Not spam
- ... Message-ID: <one>
- ...
- ... This is a message.
- ... """)
-
- >>> from mailman.core.chains import process
-
-Pass through is seen as nothing being in the log file after processing.
-
- # 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)
- >>> file_pos = fp.tell()
- >>> process(mlist, msg, {}, 'header-match')
- >>> fp.seek(file_pos)
- >>> print 'LOG:', fp.read()
- LOG:
- <BLANKLINE>
-
-Now, if the header exists but does not match, then it also passes through
-untouched.
-
- >>> msg['X-Spam-Score'] = '***'
- >>> del msg['subject']
- >>> msg['Subject'] = 'This is almost spam'
- >>> del msg['message-id']
- >>> msg['Message-ID'] = '<two>'
- >>> file_pos = fp.tell()
- >>> process(mlist, msg, {}, 'header-match')
- >>> fp.seek(file_pos)
- >>> print 'LOG:', fp.read()
- LOG:
- <BLANKLINE>
-
-But now if the header matches, then the message gets discarded.
-
- >>> del msg['x-spam-score']
- >>> msg['X-Spam-Score'] = '****'
- >>> del msg['subject']
- >>> msg['Subject'] = 'This is spam, but barely'
- >>> del msg['message-id']
- >>> msg['Message-ID'] = '<three>'
- >>> file_pos = fp.tell()
- >>> process(mlist, msg, {}, 'header-match')
- >>> fp.seek(file_pos)
- >>> print 'LOG:', fp.read()
- LOG: ... DISCARD: <three>
- <BLANKLINE>
-
-For kicks, let's show a message that's really spammy.
-
- >>> del msg['x-spam-score']
- >>> msg['X-Spam-Score'] = '**********'
- >>> del msg['subject']
- >>> msg['Subject'] = 'This is really spammy'
- >>> del msg['message-id']
- >>> msg['Message-ID'] = '<four>'
- >>> file_pos = fp.tell()
- >>> process(mlist, msg, {}, 'header-match')
- >>> fp.seek(file_pos)
- >>> print 'LOG:', fp.read()
- LOG: ... DISCARD: <four>
- <BLANKLINE>
-
-Flush out the extended header matching rules.
-
- >>> chain.flush()
-
-
-List-specific header matching
------------------------------
-
-Each mailing list can also be configured with a set of header matching regular
-expression rules. These are used to impose list-specific header filtering
-with the same semantics as the global [spam.headers] section.
-
-The list administrator wants to match not on four stars, but on three plus
-signs, but only for the current mailing list.
-
- >>> mlist.header_matches = [('x-spam-score', '[+]{3,}', 'discard')]
-
-A message with a spam score of two pluses does not match.
-
- >>> del msg['x-spam-score']
- >>> msg['X-Spam-Score'] = '++'
- >>> del msg['message-id']
- >>> msg['Message-ID'] = '<five>'
- >>> file_pos = fp.tell()
- >>> process(mlist, msg, {}, 'header-match')
- >>> fp.seek(file_pos)
- >>> print 'LOG:', fp.read()
- LOG:
-
-A message with a spam score of three pluses does match.
-
- >>> del msg['x-spam-score']
- >>> msg['X-Spam-Score'] = '+++'
- >>> del msg['message-id']
- >>> msg['Message-ID'] = '<six>'
- >>> file_pos = fp.tell()
- >>> process(mlist, msg, {}, 'header-match')
- >>> fp.seek(file_pos)
- >>> print 'LOG:', fp.read()
- LOG: ... DISCARD: <six>
- <BLANKLINE>
-
-As does a message with a spam score of four pluses.
-
- >>> del msg['x-spam-score']
- >>> msg['X-Spam-Score'] = '+++'
- >>> del msg['message-id']
- >>> msg['Message-ID'] = '<seven>'
- >>> file_pos = fp.tell()
- >>> process(mlist, msg, {}, 'header-match')
- >>> fp.seek(file_pos)
- >>> print 'LOG:', fp.read()
- LOG: ... DISCARD: <seven>
- <BLANKLINE>
diff --git a/mailman/rules/docs/implicit-dest.txt b/mailman/rules/docs/implicit-dest.txt
deleted file mode 100644
index e5c340dcd..000000000
--- a/mailman/rules/docs/implicit-dest.txt
+++ /dev/null
@@ -1,75 +0,0 @@
-Implicit destination
-====================
-
-The 'implicit-dest' rule matches when the mailing list's posting address is
-not explicitly mentioned in the set of message recipients.
-
- >>> mlist = config.db.list_manager.create(u'_xtest@example.com')
- >>> rule = config.rules['implicit-dest']
- >>> print rule.name
- implicit-dest
-
-This rule matches messages that have implicit destination, meaning that the
-mailing list's posting address isn't included in the explicit recipients.
-
- >>> mlist.require_explicit_destination = True
- >>> mlist.acceptable_aliases = u''
- >>> msg = message_from_string("""\
- ... From: aperson@example.org
- ... Subject: An implicit message
- ...
- ... """)
- >>> rule.check(mlist, msg, {})
- True
-
-You can disable implicit destination checks for the mailing list.
-
- >>> mlist.require_explicit_destination = False
- >>> rule.check(mlist, msg, {})
- False
-
-Even with some recipients, if the posting address is not included, the rule
-will match.
-
- >>> mlist.require_explicit_destination = True
- >>> msg['To'] = 'myfriend@example.com'
- >>> rule.check(mlist, msg, {})
- True
-
-Add the posting address as a recipient and the rule will no longer match.
-
- >>> msg['Cc'] = '_xtest@example.com'
- >>> rule.check(mlist, msg, {})
- False
-
-Alternatively, if one of the acceptable aliases is in the recipients list,
-then the rule will not match.
-
- >>> del msg['cc']
- >>> rule.check(mlist, msg, {})
- True
- >>> mlist.acceptable_aliases = u'myfriend@example.com'
- >>> rule.check(mlist, msg, {})
- False
-
-A message gated from NNTP will obviously have an implicit destination. Such
-gated messages will not be held for implicit destination because it's assumed
-that Mailman pulled it from the appropriate news group.
-
- >>> rule.check(mlist, msg, dict(fromusenet=True))
- False
-
-
-Alias patterns
---------------
-
-It's also possible to specify an alias pattern, i.e. a regular expression to
-match against the recipients. For example, we can say that if there is a
-recipient in the example.net domain, then the rule does not match.
-
- >>> mlist.acceptable_aliases = u'^.*@example.net'
- >>> rule.check(mlist, msg, {})
- True
- >>> msg['To'] = 'you@example.net'
- >>> rule.check(mlist, msg, {})
- False
diff --git a/mailman/rules/docs/loop.txt b/mailman/rules/docs/loop.txt
deleted file mode 100644
index 61612cd75..000000000
--- a/mailman/rules/docs/loop.txt
+++ /dev/null
@@ -1,48 +0,0 @@
-Posting loops
-=============
-
-To avoid a posting loop, Mailman has a rule to check for the existence of an
-X-BeenThere header with the value of the list's posting address.
-
- >>> mlist = config.db.list_manager.create(u'_xtest@example.com')
- >>> rule = config.rules['loop']
- >>> print rule.name
- loop
-
-The header could be missing, in which case the rule does not match.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ...
- ... An important message.
- ... """)
- >>> rule.check(mlist, msg, {})
- False
-
-The header could be present, but not match the list's posting address.
-
- >>> msg['X-BeenThere'] = u'not-this-list@example.com'
- >>> rule.check(mlist, msg, {})
- False
-
-If the header is present and does match the posting address, the rule
-matches.
-
- >>> del msg['x-beenthere']
- >>> msg['X-BeenThere'] = mlist.posting_address
- >>> rule.check(mlist, msg, {})
- True
-
-Even if there are multiple X-BeenThere headers, as long as one with the
-posting address exists, the rule matches.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... X-BeenThere: not-this-list@example.com
- ... X-BeenThere: _xtest@example.com
- ... X-BeenThere: foo@example.com
- ...
- ... An important message.
- ... """)
- >>> rule.check(mlist, msg, {})
- True
diff --git a/mailman/rules/docs/max-size.txt b/mailman/rules/docs/max-size.txt
deleted file mode 100644
index 117691e59..000000000
--- a/mailman/rules/docs/max-size.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-Message size
-============
-
-The 'message-size' rule matches when the posted message is bigger than a
-specified maximum. Generally this is used to prevent huge attachments from
-getting posted to the list. This value is calculated in terms of KB (1024
-bytes).
-
- >>> mlist = config.db.list_manager.create(u'_xtest@example.com')
- >>> rule = config.rules['max-size']
- >>> print rule.name
- max-size
-
-For example, setting the maximum message size to 1 means that any message
-bigger than that will match the rule.
-
- >>> mlist.max_message_size = 1 # 1024 bytes
- >>> one_line = u'x' * 79
- >>> big_body = u'\n'.join([one_line] * 15)
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... To: _xtest@example.com
- ...
- ... """ + big_body)
- >>> rule.check(mlist, msg, {})
- True
-
-Setting the maximum message size to zero means no size check is performed.
-
- >>> mlist.max_message_size = 0
- >>> rule.check(mlist, msg, {})
- False
-
-Of course, if the maximum size is larger than the message's size, then it's
-still okay.
-
- >>> mlist.max_message_size = msg.original_size/1024.0 + 1
- >>> rule.check(mlist, msg, {})
- False
diff --git a/mailman/rules/docs/moderation.txt b/mailman/rules/docs/moderation.txt
deleted file mode 100644
index 65be0d7da..000000000
--- a/mailman/rules/docs/moderation.txt
+++ /dev/null
@@ -1,69 +0,0 @@
-Member moderation
-=================
-
-Each user has a moderation flag. When set, and the list is set to moderate
-postings, then only members with a cleared moderation flag will be able to
-email the list without having those messages be held for approval. The
-'moderation' rule determines whether the message should be moderated or not.
-
- >>> mlist = config.db.list_manager.create(u'_xtest@example.com')
- >>> rule = config.rules['moderation']
- >>> print rule.name
- moderation
-
-In the simplest case, the sender is not a member of the mailing list, so the
-moderation rule can't match.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.org
- ... To: _xtest@example.com
- ... Subject: A posted message
- ...
- ... """)
- >>> rule.check(mlist, msg, {})
- False
-
-Let's add the message author as a non-moderated member.
-
- >>> user = config.db.user_manager.create_user(
- ... u'aperson@example.org', u'Anne Person')
- >>> address = list(user.addresses)[0]
- >>> from mailman.interfaces.member import MemberRole
- >>> member = address.subscribe(mlist, MemberRole.member)
- >>> member.is_moderated
- False
- >>> rule.check(mlist, msg, {})
- False
-
-Once the member's moderation flag is set though, the rule matches.
-
- >>> member.is_moderated = True
- >>> rule.check(mlist, msg, {})
- True
-
-
-Non-members
------------
-
-There is another, related rule for matching non-members, which simply matches
-if the sender is /not/ a member of the mailing list.
-
- >>> rule = config.rules['non-member']
- >>> print rule.name
- non-member
-
-If the sender is a member of this mailing list, the rule does not match.
-
- >>> rule.check(mlist, msg, {})
- False
-
-But if the sender is not a member of this mailing list, the rule matches.
-
- >>> msg = message_from_string("""\
- ... From: bperson@example.org
- ... To: _xtest@example.com
- ... Subject: A posted message
- ...
- ... """)
- >>> rule.check(mlist, msg, {})
- True
diff --git a/mailman/rules/docs/news-moderation.txt b/mailman/rules/docs/news-moderation.txt
deleted file mode 100644
index 4c095cc81..000000000
--- a/mailman/rules/docs/news-moderation.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-Newsgroup moderation
-====================
-
-The 'news-moderation' rule matches all messages posted to mailing lists that
-gateway to a moderated newsgroup. The reason for this is that such messages
-must get forwarded on to the newsgroup moderator. From there it will get
-posted to the newsgroup, and from there, gated to the mailing list. It's a
-circuitous route, but it works nonetheless by holding all messages posted
-directly to the mailing list.
-
- >>> mlist = config.db.list_manager.create(u'_xtest@example.com')
- >>> rule = config.rules['news-moderation']
- >>> print rule.name
- news-moderation
-
-Set the list configuraiton variable to enable newsgroup moderation.
-
- >>> from mailman.interfaces import NewsModeration
- >>> mlist.news_moderation = NewsModeration.moderated
-
-And now all messages will match the rule.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.org
- ... Subject: An announcment
- ...
- ... Great things are happening.
- ... """)
- >>> rule.check(mlist, msg, {})
- True
-
-When moderation is turned off, the rule does not match.
-
- >>> mlist.news_moderation = NewsModeration.none
- >>> rule.check(mlist, msg, {})
- False
diff --git a/mailman/rules/docs/no-subject.txt b/mailman/rules/docs/no-subject.txt
deleted file mode 100644
index 576111cd7..000000000
--- a/mailman/rules/docs/no-subject.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-No Subject header
-=================
-
-This rule matches if the message has no Subject header, or if the header is
-the empty string when stripped.
-
- >>> mlist = config.db.list_manager.create(u'_xtest@example.com')
- >>> rule = config.rules['no-subject']
- >>> print rule.name
- no-subject
-
-A message with a non-empty subject does not match the rule.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.org
- ... To: _xtest@example.com
- ... Subject: A posted message
- ...
- ... """)
- >>> rule.check(mlist, msg, {})
- False
-
-Delete the Subject header and the rule matches.
-
- >>> del msg['subject']
- >>> rule.check(mlist, msg, {})
- True
-
-Even a Subject header with only whitespace still matches the rule.
-
- >>> msg['Subject'] = u' '
- >>> rule.check(mlist, msg, {})
- True
diff --git a/mailman/rules/docs/recipients.txt b/mailman/rules/docs/recipients.txt
deleted file mode 100644
index 3cd49d501..000000000
--- a/mailman/rules/docs/recipients.txt
+++ /dev/null
@@ -1,40 +0,0 @@
-Maximum number of recipients
-============================
-
-The 'max-recipients' rule matches when there are more than the maximum allowed
-number of explicit recipients addressed by the message.
-
- >>> mlist = config.db.list_manager.create(u'_xtest@example.com')
- >>> rule = config.rules['max-recipients']
- >>> print rule.name
- max-recipients
-
-In this case, we'll create a message with 5 recipients. These include all
-addresses in the To and CC headers.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... To: _xtest@example.com, bperson@example.com
- ... Cc: cperson@example.com
- ... Cc: dperson@example.com (Dan Person)
- ... To: Elly Q. Person <eperson@example.com>
- ...
- ... Hey folks!
- ... """)
-
-For backward compatibility, the message must have fewer than the maximum
-number of explicit recipients.
-
- >>> mlist.max_num_recipients = 5
- >>> rule.check(mlist, msg, {})
- True
-
- >>> mlist.max_num_recipients = 6
- >>> rule.check(mlist, msg, {})
- False
-
-Zero means any number of recipients are allowed.
-
- >>> mlist.max_num_recipients = 0
- >>> rule.check(mlist, msg, {})
- False
diff --git a/mailman/rules/docs/rules.txt b/mailman/rules/docs/rules.txt
deleted file mode 100644
index 095d11466..000000000
--- a/mailman/rules/docs/rules.txt
+++ /dev/null
@@ -1,69 +0,0 @@
-Rules
-=====
-
-Rules are applied to each message as part of a rule chain. Individual rules
-simply return a boolean specifying whether the rule matches or not. Chain
-links determine what happens when a rule matches.
-
-
-All rules
----------
-
-Rules are maintained in the configuration object as a dictionary mapping rule
-names to rule objects.
-
- >>> from zope.interface.verify import verifyObject
- >>> from mailman.interfaces.rules import IRule
- >>> for rule_name in sorted(config.rules):
- ... rule = config.rules[rule_name]
- ... print rule_name, verifyObject(IRule, rule)
- administrivia True
- any True
- approved True
- emergency True
- implicit-dest True
- loop True
- max-recipients True
- max-size True
- moderation True
- news-moderation True
- no-subject True
- non-member True
- suspicious-header True
- truth True
-
-You can get a rule by name.
-
- >>> rule = config.rules['emergency']
- >>> verifyObject(IRule, rule)
- True
-
-
-Rule checks
------------
-
-Individual rules can be checked to see if they match, by running the rule's
-`check()` method. This returns a boolean indicating whether the rule was
-matched or not.
-
- >>> mlist = config.db.list_manager.create(u'_xtest@example.com')
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ...
- ... An important message.
- ... """)
-
-For example, the emergency rule just checks to see if the emergency flag is
-set on the mailing list, and the message has not been pre-approved by the list
-administrator.
-
- >>> print rule.name
- emergency
- >>> mlist.emergency = False
- >>> rule.check(mlist, msg, {})
- False
- >>> mlist.emergency = True
- >>> rule.check(mlist, msg, {})
- True
- >>> rule.check(mlist, msg, dict(moderator_approved=True))
- False
diff --git a/mailman/rules/docs/suspicious.txt b/mailman/rules/docs/suspicious.txt
deleted file mode 100644
index 190a34aca..000000000
--- a/mailman/rules/docs/suspicious.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-Suspicious headers
-==================
-
-Suspicious headers are a way for Mailman to hold messages that match a
-particular regular expression. This mostly historical feature is fairly
-confusing to users, and the list attribute that controls this is misnamed.
-
- >>> mlist = config.db.list_manager.create(u'_xtest@example.com')
- >>> rule = config.rules['suspicious-header']
- >>> print rule.name
- suspicious-header
-
-Set the so-called suspicious header configuration variable.
-
- >>> mlist.bounce_matching_headers = u'From: .*person@(blah.)?example.com'
- >>> msg = message_from_string("""\
- ... From: aperson@example.com
- ... To: _xtest@example.com
- ... Subject: An implicit message
- ...
- ... """)
- >>> rule.check(mlist, msg, {})
- True
-
-But if the header doesn't match the regular expression, the rule won't match.
-This one comes from a .org address.
-
- >>> msg = message_from_string("""\
- ... From: aperson@example.org
- ... To: _xtest@example.com
- ... Subject: An implicit message
- ...
- ... """)
- >>> rule.check(mlist, msg, {})
- False
diff --git a/mailman/rules/docs/truth.txt b/mailman/rules/docs/truth.txt
deleted file mode 100644
index f331e852b..000000000
--- a/mailman/rules/docs/truth.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-Truth
-=====
-
-The 'truth' rule always matches. This makes it useful as a terminus rule for
-unconditionally jumping to another chain.
-
- >>> rule = config.rules['truth']
- >>> rule.check(False, False, False)
- True