Move AddrCache to Milter package.

This commit is contained in:
Stuart Gathman
2007-01-05 21:25:40 +00:00
parent 702ec2d4ca
commit 732e7317f1
5 changed files with 120 additions and 78 deletions
+108
View File
@@ -0,0 +1,108 @@
# Email address list with expiration
#
# This class acts like a map. Entries with a value of None are persistent,
# but disappear after a time limit. This is useful for automatic whitelists
# and blacklists with expiration. The persistent store is a simple ascii
# file with sender and timestamp on each line. Entries can be appended
# to the store, and will be picked up the next time it is loaded.
#
# Entries with other values are not persistent. This is used to hold failed
# CBV results.
#
# $Log$
# Author: Stuart D. Gathman <stuart@bmsi.com>
# Copyright 2001,2002,2003,2004,2005 Business Management Systems, Inc.
# This code is under the GNU General Public License. See COPYING for details.
import time
class AddrCache(object):
time_format = '%Y%b%d %H:%M:%S %Z'
def __init__(self,renew=7,fname=None):
self.age = renew
self.cache = {}
self.fname = fname
def load(self,fname,age=0):
"Load address cache from persistent store."
if not age:
age = self.age
self.fname = fname
cache = {}
self.cache = cache
now = time.time()
try:
too_old = now - age*24*60*60 # max age in days
for ln in open(self.fname):
try:
rcpt,ts = ln.strip().split(None,1)
l = time.strptime(ts,AddrCache.time_format)
t = time.mktime(l)
if t > too_old:
cache[rcpt.lower()] = (t,None)
except:
cache[ln.strip().lower()] = (now,None)
except IOError: pass
def has_key(self,sender):
"True if sender is cached and has not expired."
try:
lsender = 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:
return True
del self.cache[lsender]
try:
user,host = sender.split('@',1)
return self.has_key(host)
except ValueError:
pass
except KeyError:
try:
user,host = sender.split('@',1)
return self.has_key(host)
except ValueError:
pass
return False
def __getitem__(self,sender):
try:
lsender = 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:
return res
del self.cache[lsender]
raise KeyError, sender
except KeyError,x:
try:
user,host = sender.split('@',1)
return self.__getitem__(host)
except ValueError:
raise x
def addperm(self,sender,res=None):
"Add a permanent sender."
lsender = sender.lower()
if self.has_key(lsender):
ts,res = self.cache[lsender]
if not ts: return # already permanent
self.cache[lsender] = (None,res)
if not res:
print >>open(self.fname,'a'),sender
def __setitem__(self,sender,res):
lsender = sender.lower()
now = time.time()
cached = self.has_key(sender)
if not cached:
self.cache[lsender] = (now,res)
if not res and self.fname:
s = time.strftime(AddrCache.time_format,time.localtime(now))
print >>open(self.fname,'a'),sender,s # log refreshed senders
def __len__(self):
return len(self.cache)
+3 -4
View File
@@ -1,7 +1,6 @@
When bms.py can't find templates, it passes None to dsn.create_msg(), DONE When bms.py can't find templates, it passes None to dsn.create_msg(),
which uses local variable as backup, which no longer exist. which uses local variable as backup, which no longer exist. Do plain
CBV in that case instead.
Purge old GOSSiP records nightly.
Find and use X-GOSSiP: header for SPAM: and FP: submissions. Would need to Find and use X-GOSSiP: header for SPAM: and FP: submissions. Would need to
keep tags longer. keep tags longer.
+4 -73
View File
@@ -1,6 +1,9 @@
#!/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.78 2007/01/04 18:01:10 customdesigned
# Do plain CBV when template missing.
#
# Revision 1.77 2006/12/31 03:07:20 customdesigned # Revision 1.77 2006/12/31 03:07:20 customdesigned
# Use HELO identity if good when MAILFROM is bad. # Use HELO identity if good when MAILFROM is bad.
# #
@@ -510,79 +513,7 @@ def iniplist(ipaddr,iplist):
return True return True
return False return False
class AddrCache(object): from Milter.cache import AddrCache
time_format = '%Y%b%d %H:%M:%S %Z'
def __init__(self,renew=7):
self.age = renew
def load(self,fname,age=0):
if not age:
age = self.age
self.fname = fname
cache = {}
self.cache = cache
now = time.time()
try:
too_old = now - age*24*60*60 # max age in days
for ln in open(self.fname):
try:
rcpt,ts = ln.strip().split(None,1)
l = time.strptime(ts,AddrCache.time_format)
t = time.mktime(l)
if t > too_old:
cache[rcpt.lower()] = (t,None)
except:
cache[ln.strip().lower()] = (now,None)
except IOError: pass
def has_key(self,sender):
try:
ts,res = self.cache[sender.lower()]
too_old = time.time() - self.age*24*60*60 # max age in days
if ts > too_old:
return True
del self.cache[sender.lower()]
try:
user,host = sender.split('@',1)
return self.has_key(host)
except ValueError:
pass
except KeyError:
try:
user,host = sender.split('@',1)
return self.has_key(host)
except ValueError:
pass
return False
def __getitem__(self,sender):
try:
ts,res = self.cache[sender.lower()]
too_old = time.time() - self.age*24*60*60 # max age in days
if ts > too_old:
return res
del self.cache[sender.lower()]
raise KeyError, sender
except KeyError,x:
try:
user,host = sender.split('@',1)
return self.__getitem__(host)
except ValueError:
raise x
def __setitem__(self,sender,res):
lsender = sender.lower()
now = time.time()
cached = self.has_key(sender)
if not cached:
self.cache[lsender] = (now,res)
if not res:
s = time.strftime(AddrCache.time_format,time.localtime(now))
print >>open(self.fname,'a'),sender,s # log refreshed senders
def __len__(self):
return len(self.cache)
cbv_cache = AddrCache(renew=7) cbv_cache = AddrCache(renew=7)
cbv_cache.load('send_dsn.log',age=7) cbv_cache.load('send_dsn.log',age=7)
+3 -1
View File
@@ -11,7 +11,8 @@
# some systems dont have initrddir defined # some systems dont have initrddir defined
%{?_initrddir:%define _initrddir /etc/rc.d/init.d} %{?_initrddir:%define _initrddir /etc/rc.d/init.d}
%if %{redhat7} # Redhat 7.x and earlier (multiple ps lines per thread) %if %{redhat7}
# Redhat 7.x and earlier (multiple ps lines per thread)
%define sysvinit milter.rc7 %define sysvinit milter.rc7
%else %else
%define sysvinit milter.rc %define sysvinit milter.rc
@@ -167,6 +168,7 @@ rm -rf $RPM_BUILD_ROOT
%config /var/log/milter/bms.py %config /var/log/milter/bms.py
%config(noreplace) /var/log/milter/strike3.txt %config(noreplace) /var/log/milter/strike3.txt
%config(noreplace) /var/log/milter/softfail.txt %config(noreplace) /var/log/milter/softfail.txt
%config(noreplace) /var/log/milter/fail.txt
%config(noreplace) /var/log/milter/neutral.txt %config(noreplace) /var/log/milter/neutral.txt
%config(noreplace) /var/log/milter/quarantine.txt %config(noreplace) /var/log/milter/quarantine.txt
%config(noreplace) /var/log/milter/permerror.txt %config(noreplace) /var/log/milter/permerror.txt
+2
View File
@@ -2,6 +2,7 @@ import unittest
import testbms import testbms
import testmime import testmime
import testsample import testsample
import testcache
import os import os
def suite(): def suite():
@@ -9,6 +10,7 @@ def suite():
s.addTest(testbms.suite()) s.addTest(testbms.suite())
s.addTest(testmime.suite()) s.addTest(testmime.suite())
s.addTest(testsample.suite()) s.addTest(testsample.suite())
s.addTest(testcache.suite())
return s return s
if __name__ == '__main__': if __name__ == '__main__':