diff --git a/Milter/cache.py b/Milter/cache.py index 8f9c824bd4689c8a0a7120dbdbc7d91c67b08258..17b27659a93996014ed3f6805e7edde95f522fce 100644 --- a/Milter/cache.py +++ b/Milter/cache.py @@ -10,6 +10,9 @@ # CBV results. # # $Log$ +# Revision 1.3 2007/01/08 23:20:54 customdesigned +# Get user feedback. +# # Revision 1.2 2007/01/05 23:33:55 customdesigned # Make blacklist an AddrCache # @@ -22,6 +25,7 @@ # This code is under the GNU General Public License. See COPYING for details. import time +from plock import PLock class AddrCache(object): time_format = '%Y%b%d %H:%M:%S %Z' @@ -39,6 +43,8 @@ class AddrCache(object): cache = {} self.cache = cache now = time.time() + lock = PLock(self.fname) + wfp = lock.lock() try: too_old = now - age*24*60*60 # max age in days for ln in open(self.fname): @@ -46,11 +52,14 @@ class AddrCache(object): 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) + if t < too_old: continue + cache[rcpt.lower()] = (t,None) except: cache[ln.strip().lower()] = (now,None) - except IOError: pass + wfp.write(ln) + lock.commit(self.fname+'.old') + except IOError: + lock.unlock() def has_key(self,sender): "True if sender is cached and has not expired." diff --git a/Milter/plock.py b/Milter/plock.py new file mode 100644 index 0000000000000000000000000000000000000000..d4df9b768bb158f9452264353d954bb6ac36a615 --- /dev/null +++ b/Milter/plock.py @@ -0,0 +1,62 @@ +# Author: Stuart D. Gathman <stuart@bmsi.com> +# Copyright 2001 Business Management Systems, Inc. +# This code is under the GNU General Public License. See COPYING for details. + +import os +from time import sleep + +class PLock(object): + "A simple /etc/passwd style lock,update,rename protocol for updating files." + def __init__(self,basename): + self.basename = basename + self.fp = None + + def lock(self,lockname=None): + "Start an update transaction. Return FILE to write new version." + self.unlock() + if not lockname: + lockname = self.basename + '.lock' + self.lockname = lockname + st = os.stat(self.basename) + u = os.umask(0002) + try: + fd = os.open(lockname,os.O_WRONLY+os.O_CREAT+os.O_EXCL,st.st_mode|0660) + finally: + os.umask(u) + self.fp = os.fdopen(fd,'w') + try: + os.chown(self.lockname,-1,st.st_gid) + except: + self.unlock() + raise + return self.fp + + def wlock(self,lockname=None): + "Wait until lock is free, then start an update transaction." + while True: + try: + return self.lock(lockname) + except OSError: + sleep(2) + + def commit(self,backname=None): + "Commit update transaction with optional backup file." + if not self.fp: + raise IOError,"File not locked" + self.fp.close() + self.fp = None + if backname: + try: + os.remove(backname) + except OSError: pass + os.link(self.basename,backname) + os.rename(self.lockname,self.basename) + + def unlock(self): + "Cancel update transaction." + if self.fp: + try: + self.fp.close() + except: pass + self.fp = None + os.remove(self.lockname) diff --git a/bms.py b/bms.py index 4032c5d72e5e040315a04a8383445a0a5e8996a8..5b63069fc313a1d634345e87de6e603face92ae6 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.84 2007/01/10 04:44:25 customdesigned +# Documentation updates. +# # Revision 1.83 2007/01/08 23:20:54 customdesigned # Get user feedback. # @@ -1017,7 +1020,10 @@ class bmsMilter(Milter.Milter): val = parse_header(hval) if not self.internal_connection and not (self.blacklist or self.whitelist): rc = self.check_header(name,val) - if rc != Milter.CONTINUE: return rc + if rc != Milter.CONTINUE: + if gossip and self.umis: + gossip_node.feedback(self.umis,1) + return rc elif self.whitelist_sender and lname == 'subject': # check for AutoReplys vl = val.lower()