diff options
| -rw-r--r-- | Mailman/Handlers/Hold.py | 10 | ||||
| -rw-r--r-- | Mailman/app/bounces.py | 50 | ||||
| -rw-r--r-- | Mailman/docs/hold.txt | 37 | ||||
| -rw-r--r-- | Mailman/docs/suspicious.txt | 37 | ||||
| -rw-r--r-- | Mailman/rules/suspicious.py | 96 |
5 files changed, 133 insertions, 97 deletions
diff --git a/Mailman/Handlers/Hold.py b/Mailman/Handlers/Hold.py index a5b56cbd4..96ed02c93 100644 --- a/Mailman/Handlers/Hold.py +++ b/Mailman/Handlers/Hold.py @@ -43,7 +43,6 @@ from Mailman import Errors from Mailman import Message from Mailman import Utils from Mailman import i18n -from Mailman.app.bounces import has_matching_bounce_header from Mailman.app.moderator import hold_message from Mailman.app.replybot import autorespond_to_sender from Mailman.configuration import config @@ -156,15 +155,6 @@ def process(mlist, msg, msgdata): # delivering. This feature does not appear to be configurable. *Boggle*. if not sender or sender[:len(listname)+6] == adminaddr: sender = msg.get_sender(use_envelope=0) - # - # Suspicious headers? - if mlist.bounce_matching_headers: - triggered = has_matching_bounce_header(mlist, msg) - if triggered: - # TBD: Darn - can't include the matching line for the admin - # message because the info would also go to the sender - hold_for_approval(mlist, msg, msgdata, SuspiciousHeaders) - # no return diff --git a/Mailman/app/bounces.py b/Mailman/app/bounces.py index 7c3f6d894..3f38cbf67 100644 --- a/Mailman/app/bounces.py +++ b/Mailman/app/bounces.py @@ -19,7 +19,6 @@ __all__ = [ 'bounce_message', - 'has_matching_bounce_header', ] import re @@ -61,52 +60,3 @@ def bounce_message(mlist, msg, e=None): bmsg.attach(txt) bmsg.attach(MIMEMessage(msg)) bmsg.send(mlist) - - - -def _parse_matching_header_opt(mlist): - """Return a list of triples [(field name, regex, line), ...].""" - # - Blank lines and lines with '#' as first char are skipped. - # - Leading whitespace in the matchexp is trimmed - you can defeat - # that by, eg, containing it in gratuitous square brackets. - all = [] - for line in mlist.bounce_matching_headers.splitlines(): - line = line.strip() - # Skip blank lines and lines *starting* with a '#'. - if not line or line.startswith('#'): - continue - i = line.find(':') - if i < 0: - # This didn't look like a header line. BAW: should do a - # better job of informing the list admin. - log.error('bad bounce_matching_header line: %s\n%s', - mlist.real_name, line) - else: - header = line[:i] - value = line[i+1:].lstrip() - try: - cre = re.compile(value, re.IGNORECASE) - except re.error, e: - # The regexp was malformed. BAW: should do a better - # job of informing the list admin. - log.error("""\ -bad regexp in bounce_matching_header line: %s -\n%s (cause: %s)""", mlist.real_name, value, e) - else: - all.append((header, cre, line)) - return all - - -def has_matching_bounce_header(mlist, msg): - """Does the message have a matching bounce header? - - :param mlist: The mailing list the message is destined for. - :param msg: The email message object. - :return: True if a header field matches a regexp in the - bounce_matching_header mailing list variable. - """ - for header, cre, line in _parse_matching_header_opt(mlist): - for value in msg.get_all(header, []): - if cre.search(value): - return True - return False diff --git a/Mailman/docs/hold.txt b/Mailman/docs/hold.txt index 16948331f..1b8ecea59 100644 --- a/Mailman/docs/hold.txt +++ b/Mailman/docs/hold.txt @@ -57,43 +57,6 @@ handler returns immediately. {'approved': True} -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.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 - ... - ... """) - >>> process(mlist, msg, {}) - Traceback (most recent call last): - ... - SuspiciousHeaders - >>> clear() - -But if the header doesn't match the regular expression, it'll get posted just -fine. This one comes from a .org address. - - >>> msg = message_from_string("""\ - ... From: aperson@example.org - ... To: _xtest@example.com - ... Subject: An implicit message - ... - ... """) - >>> msgdata = {} - >>> process(mlist, msg, msgdata) - >>> print msgdata - {} - -Just a bit of clean up. - - >>> mlist.bounce_matching_headers = None X Hold Notifications diff --git a/Mailman/docs/suspicious.txt b/Mailman/docs/suspicious.txt new file mode 100644 index 000000000..8646e1b81 --- /dev/null +++ b/Mailman/docs/suspicious.txt @@ -0,0 +1,37 @@ +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. + + >>> from Mailman.configuration import config + >>> mlist = config.db.list_manager.create(u'_xtest@example.com') + >>> from Mailman.app.rules import find_rule + >>> rule = find_rule('suspicious-header') + >>> 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(u"""\ + ... 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(u"""\ + ... From: aperson@example.org + ... To: _xtest@example.com + ... Subject: An implicit message + ... + ... """) + >>> rule.check(mlist, msg, {}) + False diff --git a/Mailman/rules/suspicious.py b/Mailman/rules/suspicious.py new file mode 100644 index 000000000..6384f8b9e --- /dev/null +++ b/Mailman/rules/suspicious.py @@ -0,0 +1,96 @@ +# Copyright (C) 2007 by the Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. + +"""The historical 'suspicious header' rule.""" + +__all__ = ['suspicious_header'] +__metaclass__ = type + + +import re +from email.utils import getaddresses +from zope.interface import implements + +from Mailman.i18n import _ +from Mailman.interfaces import IRule + + + +class SuspiciousHeader: + """The historical 'suspicious header' rule.""" + implements(IRule) + + name = 'suspicious-header' + description = _('Catch messages with suspicious headers.') + + def check(self, mlist, msg, msgdata): + """See `IRule`.""" + return (mlist.bounce_matching_headers and + has_matching_bounce_header(mlist, msg)) + + + +def _parse_matching_header_opt(mlist): + """Return a list of triples [(field name, regex, line), ...].""" + # - Blank lines and lines with '#' as first char are skipped. + # - Leading whitespace in the matchexp is trimmed - you can defeat + # that by, eg, containing it in gratuitous square brackets. + all = [] + for line in mlist.bounce_matching_headers.splitlines(): + line = line.strip() + # Skip blank lines and lines *starting* with a '#'. + if not line or line.startswith('#'): + continue + i = line.find(':') + if i < 0: + # This didn't look like a header line. BAW: should do a + # better job of informing the list admin. + log.error('bad bounce_matching_header line: %s\n%s', + mlist.real_name, line) + else: + header = line[:i] + value = line[i+1:].lstrip() + try: + cre = re.compile(value, re.IGNORECASE) + except re.error, e: + # The regexp was malformed. BAW: should do a better + # job of informing the list admin. + log.error("""\ +bad regexp in bounce_matching_header line: %s +\n%s (cause: %s)""", mlist.real_name, value, e) + else: + all.append((header, cre, line)) + return all + + +def has_matching_bounce_header(mlist, msg): + """Does the message have a matching bounce header? + + :param mlist: The mailing list the message is destined for. + :param msg: The email message object. + :return: True if a header field matches a regexp in the + bounce_matching_header mailing list variable. + """ + for header, cre, line in _parse_matching_header_opt(mlist): + for value in msg.get_all(header, []): + if cre.search(value): + return True + return False + + + +suspicious_header = SuspiciousHeader() |
