This commit was manufactured by cvs2svn to create tag 'milter-0_8_1'.
Sprout from bmsi 2005-05-31 18:23:49 UTC Stuart Gathman <stuart@gathman.org> 'Development changes since 0.7.2' Cherrypick from master 2005-06-17 02:23:34 UTC Stuart Gathman <stuart@gathman.org> 'Release 0.8.1': COPYING MANIFEST.in Milter/__init__.py Milter/dsn.py Milter/dynip.py NEWS TODO bms.py faq.html milter.cfg milter.html milter.spec miltermodule.c mime.py setup.cfg setup.py softfail.txt spf.py spfquery.py strike3.txt test/zip1 test/ziploop testmime.py
This commit is contained in:
+25
-13
@@ -1,7 +1,8 @@
|
||||
|
||||
# Author: Stuart D. Gathman <stuart@bmsi.com>
|
||||
# Copyright 2001 Business Management Systems, Inc.
|
||||
# This code is under GPL. See COPYING for details.
|
||||
# This code is under the GNU General Public License. See COPYING for details.
|
||||
|
||||
# A thin OO wrapper for the milter module
|
||||
|
||||
import os
|
||||
import milter
|
||||
@@ -140,16 +141,28 @@ def closecallback(ctx):
|
||||
m._setctx(None) # release milterContext
|
||||
return rc
|
||||
|
||||
def dictfromlist(args):
|
||||
"Convert ESMTP parm list to keyword dictionary."
|
||||
kw = {}
|
||||
for s in args:
|
||||
pos = s.find('=')
|
||||
if pos > 0:
|
||||
kw[s[:pos].upper()] = s[pos+1:]
|
||||
return kw
|
||||
|
||||
def envcallback(c,args):
|
||||
"""Convert ESMTP parms to keyword parameters.
|
||||
"""Call function c with ESMTP parms converted to keyword parameters.
|
||||
Can be used in the envfrom and/or envrcpt callbacks to process
|
||||
ESMTP parameters as python keyword parameters."""
|
||||
kw = {}
|
||||
pargs = [args[0]]
|
||||
for s in args[1:]:
|
||||
pos = s.find('=')
|
||||
if pos > 0:
|
||||
kw[s[:pos]] = s[pos+1:]
|
||||
return apply(c,args,kw)
|
||||
kw[s[:pos].upper()] = s[pos+1:]
|
||||
else:
|
||||
pargs.append(s)
|
||||
return c(*pargs,**kw)
|
||||
|
||||
def runmilter(name,socketname,timeout = 0):
|
||||
# This bit is here on the assumption that you will be starting this filter
|
||||
@@ -176,14 +189,13 @@ def runmilter(name,socketname,timeout = 0):
|
||||
# milter.set_flags(milter.ADDHDRS)
|
||||
milter.set_connect_callback(connectcallback)
|
||||
milter.set_helo_callback(lambda ctx, host: ctx.getpriv().hello(host))
|
||||
milter.set_envfrom_callback(lambda ctx,*str:
|
||||
ctx.getpriv().envfrom(*str))
|
||||
# envcallback(ctx.getpriv().envfrom,str))
|
||||
milter.set_envrcpt_callback(lambda ctx,*str:
|
||||
ctx.getpriv().envrcpt(*str))
|
||||
# envcallback(ctx.getpriv().envrcpt,str))
|
||||
milter.set_header_callback(lambda ctx,fld,val:
|
||||
ctx.getpriv().header(fld,val))
|
||||
# For envfrom and envrcpt, we would like to convert ESMTP parms to keyword
|
||||
# parms, but then all existing users would have to include **kw to accept
|
||||
# arbitrary keywords without crashing. We do provide envcallback and
|
||||
# dictfromlist to make parsing the ESMTP args convenient.
|
||||
milter.set_envfrom_callback(lambda ctx,*str: ctx.getpriv().envfrom(*str))
|
||||
milter.set_envrcpt_callback(lambda ctx,*str: ctx.getpriv().envrcpt(*str))
|
||||
milter.set_header_callback(lambda ctx,fld,val: ctx.getpriv().header(fld,val))
|
||||
milter.set_eoh_callback(lambda ctx: ctx.getpriv().eoh())
|
||||
milter.set_body_callback(lambda ctx,chunk: ctx.getpriv().body(chunk))
|
||||
milter.set_eom_callback(lambda ctx: ctx.getpriv().eom())
|
||||
|
||||
+37
-17
@@ -1,9 +1,18 @@
|
||||
# Author: Stuart D. Gathman <stuart@bmsi.com>
|
||||
# Copyright 2005 Business Management Systems, Inc.
|
||||
# This code is under the GNU General Public License. See COPYING for details.
|
||||
|
||||
# Send DSNs, do call back verification,
|
||||
# and generate DSN messages from a template
|
||||
|
||||
import smtplib
|
||||
import spf
|
||||
import socket
|
||||
from email.Message import Message
|
||||
|
||||
nospf_msg = """This is an automatically generated Delivery Status Notification.
|
||||
nospf_msg = """Subject: Critical mail server configuration error
|
||||
|
||||
This is an automatically generated Delivery Status Notification.
|
||||
|
||||
THIS IS A WARNING MESSAGE ONLY.
|
||||
|
||||
@@ -65,11 +74,12 @@ If you need further assistance, please do not hesitate to
|
||||
contact me again.
|
||||
|
||||
Kind regards,
|
||||
Stuart D. Gathman
|
||||
|
||||
postmaster@%(receiver)s
|
||||
"""
|
||||
|
||||
softfail_msg = """
|
||||
softfail_msg = """Subject: SPF softfail (POSSIBLE FORGERY)
|
||||
|
||||
This is an automatically generated Delivery Status Notification.
|
||||
|
||||
THIS IS A WARNING MESSAGE ONLY.
|
||||
@@ -85,7 +95,10 @@ Received-SPF: %(spf_result)s
|
||||
"""
|
||||
|
||||
def send_dsn(mailfrom,receiver,msg=None):
|
||||
"Send DSN. If msg is None, do callback verification."
|
||||
"""Send DSN. If msg is None, do callback verification.
|
||||
Mailfrom is original sender we are sending DSN or CBV to.
|
||||
Receiver is the MTA sending the DSN.
|
||||
Return None for success or (code,msg) for failure."""
|
||||
user,domain = mailfrom.split('@')
|
||||
q = spf.query(None,None,None)
|
||||
mxlist = q.dns(domain,'MX')
|
||||
@@ -102,7 +115,7 @@ def send_dsn(mailfrom,receiver,msg=None):
|
||||
if resp.split()[0] == receiver:
|
||||
return (553,'Fraudulent MX for %s' % domain)
|
||||
if not (200 <= code <= 299):
|
||||
raise SMTPHeloError(code, resp)
|
||||
raise smtplib.SMTPHeloError(code, resp)
|
||||
if msg:
|
||||
try:
|
||||
smtp.sendmail('<>',mailfrom,msg)
|
||||
@@ -112,7 +125,7 @@ def send_dsn(mailfrom,receiver,msg=None):
|
||||
else: # CBV
|
||||
code,resp = smtp.docmd('MAIL FROM: <>')
|
||||
if code != 250:
|
||||
raise SMTPSenderRefused(code, resp, '<>')
|
||||
raise smtplib.SMTPSenderRefused(code, resp, '<>')
|
||||
code,resp = smtp.rcpt(mailfrom)
|
||||
if code not in (250,251):
|
||||
return (code,resp) # permanent error
|
||||
@@ -121,9 +134,9 @@ def send_dsn(mailfrom,receiver,msg=None):
|
||||
except smtplib.SMTPRecipientsRefused,x:
|
||||
return x.recipients[mailfrom] # permanent error
|
||||
except smtplib.SMTPSenderRefused,x:
|
||||
return x # does not accept DSN
|
||||
return x.args[:2] # does not accept DSN
|
||||
except smtplib.SMTPDataError,x:
|
||||
return x # permanent error
|
||||
return x.args # permanent error
|
||||
except smtplib.SMTPException:
|
||||
pass # any other error, try next MX
|
||||
except socket.error:
|
||||
@@ -131,7 +144,8 @@ def send_dsn(mailfrom,receiver,msg=None):
|
||||
smtp.close()
|
||||
return (450,'No MX servers available') # temp error
|
||||
|
||||
def create_msg(q,rcptlist,origmsg):
|
||||
def create_msg(q,rcptlist,origmsg=None,template=None):
|
||||
"Create a DSN message from a template. Template must be '\n' separated."
|
||||
heloname = q.h
|
||||
sender = q.s
|
||||
connectip = q.i
|
||||
@@ -145,24 +159,30 @@ def create_msg(q,rcptlist,origmsg):
|
||||
if not spf_result.startswith('softfail'):
|
||||
spf_result = None
|
||||
except: spf_result = None
|
||||
|
||||
msg = Message()
|
||||
|
||||
msg.add_header('To',sender)
|
||||
msg.add_header('From','postmaster@%s'%receiver)
|
||||
msg.add_header('Auto-Submitted','auto-generated (configuration error)')
|
||||
msg.set_type('text/plain')
|
||||
if spf_result:
|
||||
msg.add_header('Subject','SPF softfail (POSSIBLE FORGERY)')
|
||||
msg.set_payload(softfail_msg % locals())
|
||||
else:
|
||||
msg.add_header('Subject','Critical mail server configuration error')
|
||||
msg.set_payload(nospf_msg % locals())
|
||||
|
||||
if not template:
|
||||
if spf_result: template = softfail_msg
|
||||
else: template = nospf_msg
|
||||
hdrs,body = template.split('\n',1)
|
||||
for ln in hdrs.splitlines():
|
||||
name,val = ln.split(':',1)
|
||||
msg.add_header(name,(val % locals()).strip())
|
||||
msg.set_payload(body % locals())
|
||||
|
||||
return msg
|
||||
|
||||
if __name__ == '__main__':
|
||||
q = spf.query('192.168.9.50',
|
||||
'SRS0=pmeHL=RH=bmsi.com=stuart@bmsi.com',
|
||||
'bmsred.bmsi.com',receiver='mail.bmsi.com')
|
||||
msg = create_msg(q,'charlie@jsconnor.com')
|
||||
#print msg.as_string()
|
||||
msg = create_msg(q,['charlie@jsconnor.com'],None,None)
|
||||
print msg.as_string()
|
||||
# print send_dsn(f,msg.as_string())
|
||||
print send_dsn(q.s,'mail.bmsi.com',msg.as_string())
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
# Author: Stuart D. Gathman <stuart@bmsi.com>
|
||||
# Copyright 2005 Business Management Systems, Inc.
|
||||
# This code is under the GNU General Public License. See COPYING for details.
|
||||
|
||||
# Heuristically determine whether a domain name is for a dynamic IP.
|
||||
|
||||
# examples we don't yet recognize:
|
||||
#
|
||||
# wiley-268-8196.roadrunner.nf.net at ('205.251.174.46', 4810)
|
||||
|
||||
Reference in New Issue
Block a user