From 9f8cef5ee2666f2226a2ee4bb0e2a6f58d66de8d Mon Sep 17 00:00:00 2001 From: Stuart Gathman <stuart@gathman.org> Date: Mon, 8 Jan 2007 23:20:54 +0000 Subject: [PATCH] Get user feedback. --- Milter/cache.py | 8 +++++--- Milter/unsign.py | 17 +++++++++++++++++ bms.py | 28 +++++++++++++++++++++++++--- 3 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 Milter/unsign.py diff --git a/Milter/cache.py b/Milter/cache.py index 8de1fcf..8f9c824 100644 --- a/Milter/cache.py +++ b/Milter/cache.py @@ -10,6 +10,9 @@ # CBV results. # # $Log$ +# Revision 1.2 2007/01/05 23:33:55 customdesigned +# Make blacklist an AddrCache +# # Revision 1.1 2007/01/05 21:25:40 customdesigned # Move AddrCache to Milter package. # @@ -52,7 +55,7 @@ class AddrCache(object): def has_key(self,sender): "True if sender is cached and has not expired." try: - lsender = sender.lower() + lsender = sender and sender.lower() ts,res = self.cache[lsender] too_old = time.time() - self.age*24*60*60 # max age in days if not ts or ts > too_old: @@ -67,8 +70,7 @@ class AddrCache(object): try: user,host = sender.split('@',1) return self.has_key(host) - except ValueError: - pass + except: pass return False __contains__ = has_key diff --git a/Milter/unsign.py b/Milter/unsign.py new file mode 100644 index 0000000..a363a04 --- /dev/null +++ b/Milter/unsign.py @@ -0,0 +1,17 @@ +# Author: Stuart D. Gathman <stuart@bmsi.com> +# Copyright 2005 Business Management Systems, Inc. +# This code is under the GNU General Public License. See COPYING for details. + +# The localpart of SMTP return addresses is often signed. The format +# of the signing is application specific and doesn't concern us - +# except that we wish to extract some sort of fixed string from +# the variable signature which represents the "source" of the message. + +def unsign(s): + """Attempt to unsign localpart and return original email. + No attempt is made to verify the signature. + >>> unsign('SRS0=8Y3CZ=3U=jsconnor.com=bills@bmsi.com') + 'bills@jsconnor.com' + """ + # not implemented yet + return s diff --git a/bms.py b/bms.py index 3c61d83..f404af7 100644 --- a/bms.py +++ b/bms.py @@ -1,6 +1,9 @@ #!/usr/bin/env python # A simple milter that has grown quite a bit. # $Log$ +# Revision 1.82 2007/01/06 04:21:30 customdesigned +# Add config file to spfmilter +# # Revision 1.81 2007/01/05 23:33:55 customdesigned # Make blacklist an AddrCache # @@ -54,7 +57,6 @@ import Milter import tempfile import time import socket -import struct import re import shutil import gc @@ -168,7 +170,7 @@ logging.basicConfig( milter_log = logging.getLogger('milter') if gossip: - gossip_node = Gossip('gossip4.db',120) + gossip_node = Gossip('gossip4.db',1000) def read_config(list): cp = MilterConfigParser({ @@ -1149,6 +1151,15 @@ class bmsMilter(Milter.Milter): if not blind_wiretap: self.addheader('Cc',rcpt) + # + def gossip_header(self): + "Set UMIS from GOSSiP header." + msg = email.message_from_file(self.fp) + gh = msg.get('x-gossip') + if gh: + self.log('X-GOSSiP:',gh) + self.umis,_ = gh.split(',',1) + # check spaminess for recipients in dictionary groups # if there are multiple users getting dspammed, then # a signature tag for each is added to the message. @@ -1158,6 +1169,7 @@ class bmsMilter(Milter.Milter): def check_spam(self): "return True/False if self.fp, else return Milter.REJECT/TEMPFAIL/etc" + self.screened = False if not dspam_userdir: return False ds = Dspam.DSpamDirectory(dspam_userdir) ds.log = self.log @@ -1173,9 +1185,11 @@ class bmsMilter(Milter.Milter): if user == 'spam' and self.internal_connection: sender = dspam_users.get(self.canon_from) if sender: - self.log("SPAM: %s" % sender) # log user for FP + self.log("SPAM: %s" % sender) # log user for SPAM ds.add_spam(sender,txt) txt = None + self.fp.seek(0) + self.gossip_header() self.fp = None return Milter.DISCARD elif user == 'falsepositive' and self.internal_connection: @@ -1184,6 +1198,7 @@ class bmsMilter(Milter.Milter): self.log("FP: %s" % sender) # log user for FP txt = ds.false_positive(sender,txt) self.fp = StringIO.StringIO(txt) + self.gossip_header() self.delrcpt('<%s>' % rcpt) self.recipients = None self.rejectvirus = False @@ -1287,6 +1302,9 @@ class bmsMilter(Milter.Milter): ds.check_spam(screener,txt,self.recipients, force_result=dspam.DSR_ISINNOCENT) return False + # log spam score for screened messages + self.add_header("X-DSpam-Score",'%f' % ds.probability) + self.screened = True return modified # train late in eom(), after failed CBV @@ -1453,6 +1471,8 @@ class bmsMilter(Milter.Milter): rc = self.send_dsn(q,msg,template_name) self.cbv_needed = None if rc == Milter.REJECT: + if gossip and self.umis: + gossip_node.feedback(self.umis,1) self.train_spam() return Milter.DISCARD if rc != Milter.CONTINUE: @@ -1474,6 +1494,8 @@ class bmsMilter(Milter.Milter): fout.close() if not defanged and not spam_checked: + if gossip and self.umis and self.screened: + gossip_node.feedback(self.umis,0) os.remove(self.tempname) self.tempname = None # prevent re-removal self.log("eom") -- GitLab