Guest User

Untitled

a guest
Mar 18th, 2014
935
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.61 KB | None | 0 0
  1.  
  2. # A simple milter.
  3.  
  4. # Author: Stuart D. Gathman <[email protected]>
  5. # Copyright 2001 Business Management Systems, Inc.
  6. # This code is under GPL.  See COPYING for details.
  7.  
  8. import sys
  9. import os
  10. import StringIO
  11. import rfc822
  12. import mime
  13. import Milter
  14. import tempfile
  15. from time import strftime
  16. #import syslog
  17.  
  18. #syslog.openlog('milter')
  19.  
  20. class sampleMilter(Milter.Milter):
  21.   "Milter to replace attachments poisonous to Windows with a WARNING message."
  22.  
  23.   def log(self,*msg):
  24.     print "%s [%d]" % (strftime('%Y%b%d %H:%M:%S'),self.id),
  25.     for i in msg: print i,
  26.     print
  27.  
  28.   def __init__(self):
  29.     self.tempname = None
  30.     self.mailfrom = None
  31.     self.fp = None
  32.     self.bodysize = 0
  33.     self.id = Milter.uniqueID()
  34.  
  35.   # multiple messages can be received on a single connection
  36.   # envfrom (MAIL FROM in the SMTP protocol) seems to mark the start
  37.   # of each message.
  38.   @Milter.noreply
  39.   def envfrom(self,f,*str):
  40.     "start of MAIL transaction"
  41.     self.log("mail from",f,str)
  42.     self.fp = StringIO.StringIO()
  43.     self.tempname = None
  44.     self.mailfrom = f
  45.     self.bodysize = 0
  46.     return Milter.CONTINUE
  47.  
  48.   def envrcpt(self,to,*str):
  49.     # mail to MAILER-DAEMON is generally spam that bounced
  50.     if to.startswith('<MAILER-DAEMON@'):
  51.       self.log('DISCARD: RCPT TO:',to,str)
  52.       return Milter.DISCARD
  53.     self.log("rcpt to",to,str)
  54.     return Milter.CONTINUE
  55.  
  56.   def header(self,name,val):
  57.     lname = name.lower()
  58.     if lname == 'subject':
  59.  
  60.       # even if we wanted the Taiwanese spam, we can't read Chinese
  61.       # (delete if you read chinese mail)
  62.       if val.startswith('=?big5') or val.startswith('=?ISO-2022-JP'):
  63.     self.log('REJECT: %s: %s' % (name,val))
  64.     #self.setreply('550','','Go away spammer')
  65.     return Milter.REJECT
  66.  
  67.       # check for common spam keywords
  68.       if val.find("$$$") >= 0 or val.find("XXX") >= 0   \
  69.         or val.find("!!!") >= 0 or val.find("FREE") >= 0:
  70.     self.log('REJECT: %s: %s' % (name,val))
  71.     #self.setreply('550','','Go away spammer')
  72.     return Milter.REJECT
  73.  
  74.       # check for spam that pretends to be legal
  75.       lval = val.lower()
  76.       if lval.startswith("adv:") or lval.startswith("adv.") \
  77.         or lval.find('viagra') >= 0:
  78.     self.log('REJECT: %s: %s' % (name,val))
  79.     return Milter.REJECT
  80.  
  81.     # check for invalid message id
  82.     if lname == 'message-id' and len(val) < 4:
  83.       self.log('REJECT: %s: %s' % (name,val))
  84.       #self.setreply('550','','Go away spammer')
  85.       return Milter.REJECT
  86.  
  87.     # check for common bulk mailers
  88.     if lname == 'x-mailer' and \
  89.         val.lower() in ('direct email','calypso','mail bomber'):
  90.       self.log('REJECT: %s: %s' % (name,val))
  91.       #self.setreply('550','','Go away spammer')
  92.       return Milter.REJECT
  93.  
  94.     # log selected headers
  95.     if lname in ('subject','x-mailer'):
  96.       self.log('%s: %s' % (name,val))
  97.     if self.fp:
  98.       self.fp.write("%s: %s\n" % (name,val))    # add header to buffer
  99.     return Milter.CONTINUE
  100.  
  101.   def eoh(self):
  102.     if not self.fp: return Milter.TEMPFAIL  # not seen by envfrom
  103.     self.fp.write("\n")
  104.     self.fp.seek(0)
  105.     # copy headers to a temp file for scanning the body
  106.     headers = self.fp.getvalue()
  107.     self.fp.close()
  108.     self.tempname = fname = tempfile.mktemp(".defang")
  109.     self.fp = open(fname,"w+b")
  110.     self.fp.write(headers)  # IOError (e.g. disk full) causes TEMPFAIL
  111.     return Milter.CONTINUE
  112.  
  113.   def body(self,chunk):     # copy body to temp file
  114.     if self.fp:
  115.       self.fp.write(chunk)  # IOError causes TEMPFAIL in milter
  116.       self.bodysize += len(chunk)
  117.     return Milter.CONTINUE
  118.  
  119.   def _headerChange(self,msg,name,value):
  120.     if value:   # add header
  121.       self.addheader(name,value)
  122.     else:   # delete all headers with name
  123.       h = msg.getheaders(name)
  124.       cnt = len(h)
  125.       for i in range(cnt,0,-1):
  126.     self.chgheader(name,i-1,'')
  127.  
  128.   def eom(self):
  129.     if not self.fp: return Milter.ACCEPT
  130.     self.fp.seek(0)
  131.     msg = mime.message_from_file(self.fp)
  132.     msg.headerchange = self._headerChange
  133.     if not mime.defang(msg,self.tempname):
  134.       os.remove(self.tempname)
  135.       self.tempname = None  # prevent re-removal
  136.       self.log("eom")
  137.       return Milter.ACCEPT  # no suspicious attachments
  138.     self.log("Temp file:",self.tempname)
  139.     self.tempname = None    # prevent removal of original message copy
  140.     # copy defanged message to a temp file
  141.     out = tempfile.TemporaryFile()
  142.     try:
  143.       msg.dump(out)
  144.       out.seek(0)
  145.       msg = rfc822.Message(out)
  146.       msg.rewindbody()
  147.       while 1:
  148.     buf = out.read(8192)
  149.     if len(buf) == 0: break
  150.     self.replacebody(buf)   # feed modified message to sendmail
  151.       return Milter.ACCEPT  # ACCEPT modified message
  152.     finally:
  153.       out.close()
  154.     return Milter.TEMPFAIL
  155.  
  156.   def close(self):
  157.     sys.stdout.flush()      # make log messages visible
  158.     if self.tempname:
  159.       os.remove(self.tempname)  # remove in case session aborted
  160.     if self.fp:
  161.       self.fp.close()
  162.     return Milter.CONTINUE
  163.  
  164.   def abort(self):
  165.     self.log("abort after %d body chars" % self.bodysize)
  166.     return Milter.CONTINUE
  167.  
  168. if __name__ == "__main__":
  169.   #tempfile.tempdir = "/var/log/milter"
  170.   socketname = "/tmp/pythonsock"
  171.   #socketname = os.getenv("HOME") + "/pythonsock"
  172.   Milter.factory = sampleMilter
  173.   Milter.set_flags(Milter.CHGBODY + Milter.CHGHDRS + Milter.ADDHDRS)
  174.   print """To use this with sendmail, add the following to sendmail.cf:
  175.  
  176. O InputMailFilters=pythonfilter
  177. Xpythonfilter,        S=local:%s
  178.  
  179. See the sendmail README for libmilter.
  180. sample  milter startup""" % socketname
  181.   sys.stdout.flush()
  182.   Milter.runmilter("pythonfilter",socketname,240)
  183.   print "sample milter shutdown"
Advertisement
Add Comment
Please, Sign In to add comment