summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Mailman/Handlers/Hold.py10
-rw-r--r--Mailman/app/bounces.py50
-rw-r--r--Mailman/docs/hold.txt37
-rw-r--r--Mailman/docs/suspicious.txt37
-rw-r--r--Mailman/rules/suspicious.py96
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()