Move AddrCache to Milter package.
This commit is contained in:
+108
@@ -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)
|
||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
@@ -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,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__':
|
||||||
|
|||||||
Reference in New Issue
Block a user