diff --git a/MANIFEST.in b/MANIFEST.in
index b94f91c2ccb6fca6b4f786e041fa12dfa228b7de..9c9d3866fb3d15c32c4ed0853ae0c26854e8c6be 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -8,8 +8,8 @@ include ChangeLog
 include MANIFEST.in
 include testsample.py
 include testmime.py
+include testcache.py
 include testbms.py
-include testdspam.py
 include rejects.py
 include report.py
 include bms.py
diff --git a/spfmilter.py b/spfmilter.py
new file mode 100644
index 0000000000000000000000000000000000000000..4d9877a62ab2bab061031b894062f702c60f263b
--- /dev/null
+++ b/spfmilter.py
@@ -0,0 +1,231 @@
+# A simple SPF milter.
+# You must install pyspf for this to work.
+
+# Author: Stuart D. Gathman <stuart@bmsi.com>
+# Copyright 2001 Business Management Systems, Inc.
+# This code is under GPL.  See COPYING for details.
+
+import sys
+import os
+import re
+import Milter
+import spf
+import struct
+import socket
+import syslog
+
+syslog.openlog('spfmilter',0,syslog.LOG_MAIL)
+
+# list of trusted forwarder domains.  An SPF record for a forwarder
+# domain lists IP addresses from which forwarded mail is accepted.
+trusted_forwarder = []
+# list of internal LAN ips.  No SPF check is done for these.
+internal_connect = ['127.0.0.1','192.168.0.0/16']
+# list of trusted relays.  These are typically secondary MXes, and
+# no SPF check is done for these.
+trusted_relay = []
+
+socketname = "/var/run/milter/spfmiltersock"
+#socketname = os.getenv("HOME") + "/pythonsock"
+
+ip4re = re.compile(r'^[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*$')
+
+# from spf.py
+def addr2bin(str):
+  "Convert a string IPv4 address into an unsigned integer."
+  return struct.unpack("!L", socket.inet_aton(str))[0]
+
+MASK = 0xFFFFFFFFL
+
+def cidr(i,n):
+  return ~(MASK >> n) & MASK & i
+
+def iniplist(ipaddr,iplist):
+  """Return whether ip is in cidr list
+  >>> iniplist('66.179.26.146',['127.0.0.1','66.179.26.128/26'])
+  True
+  >>> iniplist('127.0.0.1',['127.0.0.1','66.179.26.128/26'])
+  True
+  >>> iniplist('192.168.0.45',['192.168.0.*'])
+  True
+  """
+  ipnum = addr2bin(ipaddr)
+  for pat in iplist:
+    p = pat.split('/',1)
+    if ip4re.match(p[0]):
+      if len(p) > 1:
+	n = int(p[1])
+      else:
+        n = 32
+      if cidr(addr2bin(p[0]),n) == cidr(ipnum,n):
+        return True
+    elif fnmatchcase(ipaddr,pat):
+      return True
+  return False
+
+def parse_addr(t):
+  """Split email into user,domain.
+
+  >>> parse_addr('user@example.com')
+  ['user', 'example.com']
+  >>> parse_addr('"user@example.com"')
+  ['user@example.com']
+  >>> parse_addr('"user@bar"@example.com')
+  ['user@bar', 'example.com']
+  >>> parse_addr('foo')
+  ['foo']
+  """
+  if t.startswith('<') and t.endswith('>'): t = t[1:-1]
+  if t.startswith('"'):
+    if t.endswith('"'): return [t[1:-1]]
+    pos = t.find('"@')
+    if pos > 0: return [t[1:pos],t[pos+2:]]
+  return t.split('@')
+
+class spfMilter(Milter.Milter):
+  "Milter to check SPF."
+
+  def log(self,*msg):
+    syslog.syslog('[%d] %s' % (self.id,' '.join([str(m) for m in msg])))
+
+  def __init__(self):
+    self.mailfrom = None
+    self.id = Milter.uniqueID()
+
+  # addheader can only be called from eom().  This accumulates added headers
+  # which can then be applied by alter_headers()
+  def add_header(self,name,val,idx=-1):
+    self.new_headers.append((name,val,idx))
+    self.log('%s: %s' % (name,val))
+
+  def connect(self,hostname,unused,hostaddr):
+    self.internal_connection = False
+    self.trusted_relay = False
+    self.hello_name = None
+    # sometimes people put extra space in sendmail config, so we strip
+    self.receiver = self.getsymval('j').strip()
+    if hostaddr and len(hostaddr) > 0:
+      ipaddr = hostaddr[0]
+      if iniplist(ipaddr,internal_connect):
+	self.internal_connection = True
+      if iniplist(ipaddr,trusted_relay):
+        self.trusted_relay = True
+    else: ipaddr = ''
+    self.connectip = ipaddr
+    if self.internal_connection:
+      connecttype = 'INTERNAL'
+    else:
+      connecttype = 'EXTERNAL'
+    if self.trusted_relay:
+      connecttype += ' TRUSTED'
+    self.log("connect from %s at %s %s" % (hostname,hostaddr,connecttype))
+    return Milter.CONTINUE
+
+  def hello(self,hostname):
+    self.hello_name = hostname
+    self.log("hello from %s" % hostname)
+    return Milter.CONTINUE
+
+  # multiple messages can be received on a single connection
+  # envfrom (MAIL FROM in the SMTP protocol) seems to mark the start
+  # of each message.
+  def envfrom(self,f,*str):
+    self.log("mail from",f,str)
+    self.mailfrom = f
+    self.new_headers = []
+    t = parse_addr(f)
+    if len(t) == 2: t[1] = t[1].lower()
+    self.canon_from = '@'.join(t)
+    if not (self.internal_connection or self.trusted_relay) and self.connectip:
+      rc = self.check_spf()
+      if rc != Milter.CONTINUE: return rc
+    return Milter.CONTINUE
+
+  def envrcpt(self,f,*str):
+    return Milter.CONTINUE
+
+  def header(self,name,hval):
+    return Milter.CONTINUE
+
+  def eoh(self):
+    return Milter.CONTINUE
+
+  def eom(self):
+    for name,val,idx in self.new_headers:
+      try:
+	self.addheader(name,val,idx)
+      except:
+	self.addheader(name,val)	# older sendmail can't insheader
+    return Milter.CONTINUE
+
+  def close(self):
+    return Milter.CONTINUE
+
+  def check_spf(self):
+    receiver = self.receiver
+    for tf in trusted_forwarder:
+      q = spf.query(self.connectip,'',tf,receiver=receiver,strict=False)
+      res,code,txt = q.check()
+      if res == 'pass':
+        self.log("TRUSTED_FORWARDER:",tf)
+        break
+    else:
+      q = spf.query(self.connectip,self.canon_from,self.hello_name,
+	  receiver=receiver,strict=False)
+      q.set_default_explanation(
+	'SPF fail: see http://openspf.org/why.html?sender=%s&ip=%s' % (q.s,q.i))
+      res,code,txt = q.check()
+    if res not in ('pass','temperror'):
+      if self.mailfrom != '<>':
+	# check hello name via spf unless spf pass
+	h = spf.query(self.connectip,'',self.hello_name,receiver=receiver)
+	hres,hcode,htxt = h.check()
+	if hres in ('deny','fail','neutral','softfail'):
+	  self.log('REJECT: hello SPF: %s 550 %s' % (hres,htxt))
+	  self.setreply('550','5.7.1',htxt,
+	    "The hostname given in your MTA's HELO response is not listed",
+	    "as a legitimate MTA in the SPF records for your domain.  If you",
+	    "get this bounce, the message was not in fact a forgery, and you",
+	    "should IMMEDIATELY notify your email administrator of the problem."
+	  )
+	  return Milter.REJECT
+      else:
+        hres,hcode,htxt = res,code,txt
+    else: hres = None
+    if res == 'fail':
+      self.log('REJECT: SPF %s %i %s' % (res,code,txt))
+      self.setreply(str(code),'5.7.1',txt)
+      # A proper SPF fail error message would read:
+      # forger.biz [1.2.3.4] is not allowed to send mail with the domain
+      # "forged.org" in the sender address.  Contact <postmaster@forged.org>.
+      return Milter.REJECT
+    if res == 'permerror':
+      self.log('REJECT: SPF %s %i %s' % (res,code,txt))
+      # latest SPF draft recommends 5.5.2 instead of 5.7.1
+      self.setreply(str(code),'5.5.2',txt,
+	'There is a fatal syntax error in the SPF record for %s' % q.o,
+	'We cannot accept mail from %s until this is corrected.' % q.o
+      )
+      return Milter.REJECT
+    if res == 'temperror':
+      self.log('TEMPFAIL: SPF %s %i %s' % (res,code,txt))
+      self.setreply(str(code),'4.3.0',txt)
+      return Milter.TEMPFAIL
+    self.add_header('Received-SPF',q.get_header(res,receiver),0)
+    if hres and q.h != q.o:
+      self.add_header('X-Hello-SPF',hres,0)
+    return Milter.CONTINUE
+
+if __name__ == "__main__":
+  Milter.factory = spfMilter
+  Milter.set_flags(Milter.CHGHDRS + Milter.ADDHDRS)
+  print """To use this with sendmail, add the following to sendmail.cf:
+
+O InputMailFilters=pyspffilter
+Xpyspffilter,        S=local:%s
+
+See the sendmail README for libmilter.
+sample spfmilter startup""" % socketname
+  sys.stdout.flush()
+  Milter.runmilter("pyspffilter",socketname,240)
+  print "sample spfmilter shutdown"