[Pymilter] simple milter design needed
Stuart D. Gathman
stuart at bmsi.com
Sat Apr 3 18:05:44 EST 2004
On Sat, 3 Apr 2004, Eric S. Johansson wrote:
> (www.camram.org) into a milter. What I need at invocation is a list of
> all recipients and the sender, and the message passing through the
> milter either the form of a string or a email.message object. It would
> be wonderful if I could find out what interface the message came in on
> but I have a backup plan in case that's not possible.
import Milter
import mime
import rfc822
class camramMilter(Milter.Milter):
# The connect callback tells you connecting IP. Furthermore, the connect
# interface is available as a "macro".
def connect(self,hostname,unused,hostaddr):
self.receiver = self.getsymval('j')
self.if_name = self.getsymval('if_name')
self.if_addr = self.getsymval('if_addr')
if hostaddr and len(hostaddr) > 0:
ipaddr = hostaddr[0]
self.connectip = ipaddr
else:
self.connectip = None
self.log("connect from %s at %s" % (hostname,hostaddr))
return Milter.CONTINUE
def hello(self,hostname):
self.hello_name = hostname
self.log("hello from %s" % hostname)
return Milter.CONTINUE
# The envfrom callback tells you who the message is (purportedly) from.
# multiple messages can be received on a single connection
# envfrom (MAIL FROM in the SMTP protocol) marks the start
# of each message.
def envfrom(self,f,*str):
self.log("mail from",f,str)
self.fp = StringIO.StringIO() # file to save message in
self.mailfrom = f
self.recipients = []
return Milter.CONTINUE
def envrcpt(self,to,*str):
self.log("rcpt to",to,str)
self.recipients.append(to)
return Milter.CONTINUE
def header(self,name,val):
self.fp.write("%s: %s\n" % (name,val)) # add header to buffer
return Milter.CONTINUE
def eoh(self):
# possibly camram would know by this time whether to discard the
# message
if self.check_camram():
return Milter.DISCARD
return Milter.CONTINUE
def body(self,chunk): # copy body to temp file
if self.fp:
self.fp.write(chunk) # IOError causes TEMPFAIL in milter
self.bodysize += len(chunk)
return Milter.CONTINUE
def _headerChange(self,msg,name,value):
if value: # add header
self.addheader(name,value)
else: # delete all headers with name
h = msg.getheaders(name)
if h:
for i in range(len(h),0,-1):
self.chgheader(name,i-1,'')
> on return, I would either pass the message back (modified) or nothing at
> all (i.e. message has been spamtrapped).
def eom(self):
#msgtxt = self.fp.getvalue() # get message as string
#
# get message as enhanced email.Message with bug fixes and support
#
# for changing attachments and propagating header changes
self.fp.seek(0)
msg = mime.MimeMessage(self.fp)
# pass header changes in top level message to sendmail
msg.headerchange = self._headerChange
# crunch message with camram
if self.camram(msg):
return Milter.DISCARD # camram says to ignore message
if not msg.ismodified():
return Milter.CONTINUE
# pass modified message to sendmail
out = StringIO.StringIO()
try:
msg.dump(out) # flatten modified message
out.seek(0)
msg = rfc822.Message(out) # just need to skip headers
msg.rewindbody() # skip headers
while True:
buf = out.read(8192)
if len(buf) == 0: break
self.replacebody(buf) # feed modified message to sendmail
if spam_checked: self.log("dspam")
return Milter.CONTINUE
except:
return Milter.TEMPFAIL
finally:
out.close()
def abort(self):
self.log('connection aborted!')
return Milter.CONTINUE
def close(self):
# do any cleanup here
return Milter.CONTINUE
> is there any glaring problems with these needs? Or will I have no
> problem creating a wrapper bridging between the milter data model and
> the camram data model?
Hope the quick tutorial helped.
This really brings home the need to create a plugin structure for Python milter
addons. Sendmail can, of course, run several milters in series. And that is
the best approach for C milters. However, with Python it would be better to
handle lots of optional features within the same VM.
--
Stuart D. Gathman <stuart at bmsi.com>
Business Management Systems Inc. Phone: 703 591-0911 Fax: 703 591-6154
"Very few of our customers are going to have a pure Unix
or pure Windows environment." - Dennis Oldroyd, Microsoft Corporation
More information about the Pymilter
mailing list