aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Mailman/Handlers/SpamDetect.py83
1 files changed, 79 insertions, 4 deletions
diff --git a/Mailman/Handlers/SpamDetect.py b/Mailman/Handlers/SpamDetect.py
index 7597b9e9..6d6cce13 100644
--- a/Mailman/Handlers/SpamDetect.py
+++ b/Mailman/Handlers/SpamDetect.py
@@ -1,17 +1,17 @@
-# Copyright (C) 1998,1999,2000,2001,2002 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2003 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
+# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
"""Do more detailed spam detection.
@@ -25,20 +25,75 @@ TBD: This needs to be made more configurable and robust.
"""
import re
+from cStringIO import StringIO
+
+from email.Generator import Generator
from Mailman import mm_cfg
from Mailman import Errors
+from Mailman import i18n
+from Mailman.Handlers.Hold import hold_for_approval
+
+try:
+ True, False
+except NameError:
+ True = 1
+ False = 0
+
+# First, play footsie with _ so that the following are marked as translated,
+# but aren't actually translated until we need the text later on.
+def _(s):
+ return s
class SpamDetected(Errors.DiscardMessage):
"""The message contains known spam"""
+class HeaderMatchHold(Errors.HoldMessage):
+ reason = _('The message headers matched a filter rule')
+
+
+# And reset the translator
+_ = i18n._
+
+
+
+class Tee:
+ def __init__(self, outfp_a, outfp_b):
+ self._outfp_a = outfp_a
+ self._outfp_b = outfp_b
+
+ def write(self, s):
+ self._outfp_a.write(s)
+ self._outfp_b.write(s)
+
+
+# Class to capture the headers separate from the message body
+class HeaderGenerator(Generator):
+ def __init__(self, outfp, mangle_from_=True, maxheaderlen=78):
+ Generator.__init__(self, outfp, mangle_from_, maxheaderlen)
+ self._headertxt = ''
+
+ def _write_headers(self, msg):
+ sfp = StringIO()
+ oldfp = self._fp
+ self._fp = Tee(oldfp, sfp)
+ try:
+ Generator._write_headers(self, msg)
+ finally:
+ self._fp = oldfp
+ self._headertxt = sfp.getvalue()
+
+ def header_text(self):
+ return self._headertxt
+
def process(mlist, msg, msgdata):
if msgdata.get('approved'):
return
+ # First do site hard coded header spam checks
for header, regex in mm_cfg.KNOWN_SPAMMERS:
cre = re.compile(regex, re.IGNORECASE)
value = msg[header]
@@ -48,3 +103,23 @@ def process(mlist, msg, msgdata):
if mo:
# we've detected spam, so throw the message away
raise SpamDetected
+ # Now do header_filter_rules
+ g = HeaderGenerator(StringIO())
+ g.flatten(msg)
+ headers = g.header_text()
+ for patterns, action, empty in mlist.header_filter_rules:
+ if action == mm_cfg.DEFER:
+ continue
+ for pattern in patterns.splitlines():
+ if pattern.startswith('#'):
+ continue
+ if re.search(pattern, headers, re.IGNORECASE):
+ if action == mm_cfg.DISCARD:
+ raise Errors.DiscardMessage
+ if action == mm_cfg.REJECT:
+ raise Errors.RejectMessage(
+ _('Message rejected by filter rule match'))
+ if action == mm_cfg.HOLD:
+ hold_for_approval(mlist, msg, msgdata, HeaderMatchHold)
+ if action == mm_cfg.ACCEPT:
+ return