Skip to content
Snippets Groups Projects
Commit 9f8cef5e authored by Stuart Gathman's avatar Stuart Gathman
Browse files

Get user feedback.

parent 4b0e7b22
Branches
No related tags found
No related merge requests found
...@@ -10,6 +10,9 @@ ...@@ -10,6 +10,9 @@
# CBV results. # CBV results.
# #
# $Log$ # $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 # Revision 1.1 2007/01/05 21:25:40 customdesigned
# Move AddrCache to Milter package. # Move AddrCache to Milter package.
# #
...@@ -52,7 +55,7 @@ class AddrCache(object): ...@@ -52,7 +55,7 @@ class AddrCache(object):
def has_key(self,sender): def has_key(self,sender):
"True if sender is cached and has not expired." "True if sender is cached and has not expired."
try: try:
lsender = sender.lower() lsender = sender and sender.lower()
ts,res = self.cache[lsender] ts,res = self.cache[lsender]
too_old = time.time() - self.age*24*60*60 # max age in days too_old = time.time() - self.age*24*60*60 # max age in days
if not ts or ts > too_old: if not ts or ts > too_old:
...@@ -67,8 +70,7 @@ class AddrCache(object): ...@@ -67,8 +70,7 @@ class AddrCache(object):
try: try:
user,host = sender.split('@',1) user,host = sender.split('@',1)
return self.has_key(host) return self.has_key(host)
except ValueError: except: pass
pass
return False return False
__contains__ = has_key __contains__ = has_key
......
# 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
#!/usr/bin/env python #!/usr/bin/env python
# A simple milter that has grown quite a bit. # A simple milter that has grown quite a bit.
# $Log$ # $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 # Revision 1.81 2007/01/05 23:33:55 customdesigned
# Make blacklist an AddrCache # Make blacklist an AddrCache
# #
...@@ -54,7 +57,6 @@ import Milter ...@@ -54,7 +57,6 @@ import Milter
import tempfile import tempfile
import time import time
import socket import socket
import struct
import re import re
import shutil import shutil
import gc import gc
...@@ -168,7 +170,7 @@ logging.basicConfig( ...@@ -168,7 +170,7 @@ logging.basicConfig(
milter_log = logging.getLogger('milter') milter_log = logging.getLogger('milter')
if gossip: if gossip:
gossip_node = Gossip('gossip4.db',120) gossip_node = Gossip('gossip4.db',1000)
def read_config(list): def read_config(list):
cp = MilterConfigParser({ cp = MilterConfigParser({
...@@ -1149,6 +1151,15 @@ class bmsMilter(Milter.Milter): ...@@ -1149,6 +1151,15 @@ class bmsMilter(Milter.Milter):
if not blind_wiretap: if not blind_wiretap:
self.addheader('Cc',rcpt) 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 # check spaminess for recipients in dictionary groups
# if there are multiple users getting dspammed, then # if there are multiple users getting dspammed, then
# a signature tag for each is added to the message. # a signature tag for each is added to the message.
...@@ -1158,6 +1169,7 @@ class bmsMilter(Milter.Milter): ...@@ -1158,6 +1169,7 @@ class bmsMilter(Milter.Milter):
def check_spam(self): def check_spam(self):
"return True/False if self.fp, else return Milter.REJECT/TEMPFAIL/etc" "return True/False if self.fp, else return Milter.REJECT/TEMPFAIL/etc"
self.screened = False
if not dspam_userdir: return False if not dspam_userdir: return False
ds = Dspam.DSpamDirectory(dspam_userdir) ds = Dspam.DSpamDirectory(dspam_userdir)
ds.log = self.log ds.log = self.log
...@@ -1173,9 +1185,11 @@ class bmsMilter(Milter.Milter): ...@@ -1173,9 +1185,11 @@ class bmsMilter(Milter.Milter):
if user == 'spam' and self.internal_connection: if user == 'spam' and self.internal_connection:
sender = dspam_users.get(self.canon_from) sender = dspam_users.get(self.canon_from)
if sender: 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) ds.add_spam(sender,txt)
txt = None txt = None
self.fp.seek(0)
self.gossip_header()
self.fp = None self.fp = None
return Milter.DISCARD return Milter.DISCARD
elif user == 'falsepositive' and self.internal_connection: elif user == 'falsepositive' and self.internal_connection:
...@@ -1184,6 +1198,7 @@ class bmsMilter(Milter.Milter): ...@@ -1184,6 +1198,7 @@ class bmsMilter(Milter.Milter):
self.log("FP: %s" % sender) # log user for FP self.log("FP: %s" % sender) # log user for FP
txt = ds.false_positive(sender,txt) txt = ds.false_positive(sender,txt)
self.fp = StringIO.StringIO(txt) self.fp = StringIO.StringIO(txt)
self.gossip_header()
self.delrcpt('<%s>' % rcpt) self.delrcpt('<%s>' % rcpt)
self.recipients = None self.recipients = None
self.rejectvirus = False self.rejectvirus = False
...@@ -1287,6 +1302,9 @@ class bmsMilter(Milter.Milter): ...@@ -1287,6 +1302,9 @@ class bmsMilter(Milter.Milter):
ds.check_spam(screener,txt,self.recipients, ds.check_spam(screener,txt,self.recipients,
force_result=dspam.DSR_ISINNOCENT) force_result=dspam.DSR_ISINNOCENT)
return False return False
# log spam score for screened messages
self.add_header("X-DSpam-Score",'%f' % ds.probability)
self.screened = True
return modified return modified
# train late in eom(), after failed CBV # train late in eom(), after failed CBV
...@@ -1453,6 +1471,8 @@ class bmsMilter(Milter.Milter): ...@@ -1453,6 +1471,8 @@ class bmsMilter(Milter.Milter):
rc = self.send_dsn(q,msg,template_name) rc = self.send_dsn(q,msg,template_name)
self.cbv_needed = None self.cbv_needed = None
if rc == Milter.REJECT: if rc == Milter.REJECT:
if gossip and self.umis:
gossip_node.feedback(self.umis,1)
self.train_spam() self.train_spam()
return Milter.DISCARD return Milter.DISCARD
if rc != Milter.CONTINUE: if rc != Milter.CONTINUE:
...@@ -1474,6 +1494,8 @@ class bmsMilter(Milter.Milter): ...@@ -1474,6 +1494,8 @@ class bmsMilter(Milter.Milter):
fout.close() fout.close()
if not defanged and not spam_checked: 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) os.remove(self.tempname)
self.tempname = None # prevent re-removal self.tempname = None # prevent re-removal
self.log("eom") self.log("eom")
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment