Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 59f8c39e60 |
@@ -7,10 +7,6 @@ real, usable Python extension.
|
|||||||
|
|
||||||
Other contributors (in random order):
|
Other contributors (in random order):
|
||||||
|
|
||||||
arkanes@irc.freenode.net
|
|
||||||
for suggesting a class method to compute and cache protocol masks
|
|
||||||
habnabit@habnabit.org
|
|
||||||
for suggesting function attributes and decorators for protocol negotiation
|
|
||||||
Dwayne Litzenberger, B.A.Sc.
|
Dwayne Litzenberger, B.A.Sc.
|
||||||
for library_dirs patch to compile on Debian
|
for library_dirs patch to compile on Debian
|
||||||
Dave MacQuigg
|
Dave MacQuigg
|
||||||
|
|||||||
+59
-180
@@ -4,13 +4,19 @@
|
|||||||
|
|
||||||
# A thin OO wrapper for the milter module
|
# A thin OO wrapper for the milter module
|
||||||
|
|
||||||
__version__ = '0.9.2'
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import milter
|
import milter
|
||||||
import thread
|
import thread
|
||||||
|
|
||||||
from milter import *
|
from milter import ACCEPT,CONTINUE,REJECT,DISCARD,TEMPFAIL, \
|
||||||
|
set_flags, setdbg, setbacklog, settimeout, error, \
|
||||||
|
ADDHDRS, CHGBODY, ADDRCPT, DELRCPT, CHGHDRS, \
|
||||||
|
V1_ACTS, V2_ACTS, CURR_ACTS
|
||||||
|
|
||||||
|
try: from milter import QUARANTINE
|
||||||
|
except: pass
|
||||||
|
|
||||||
|
__version__ = '0.8.5'
|
||||||
|
|
||||||
_seq_lock = thread.allocate_lock()
|
_seq_lock = thread.allocate_lock()
|
||||||
_seq = 0
|
_seq = 0
|
||||||
@@ -24,181 +30,30 @@ def uniqueID():
|
|||||||
_seq_lock.release()
|
_seq_lock.release()
|
||||||
return seqno
|
return seqno
|
||||||
|
|
||||||
OPTIONAL_CALLBACKS = {
|
class Milter:
|
||||||
'connect':(P_NR_CONN,P_NOCONNECT),
|
"""A simple class interface to the milter module.
|
||||||
'hello':(P_NR_HELO,P_NOHELO),
|
"""
|
||||||
'envfrom':(P_NR_MAIL,P_NOMAIL),
|
|
||||||
'envrcpt':(P_NR_RCPT,P_NORCPT),
|
|
||||||
'data':(P_NR_DATA,P_NODATA),
|
|
||||||
'unknown':(P_NR_UNKN,P_NOUNKNOWN),
|
|
||||||
'eoh':(P_NR_EOH,P_NOEOH),
|
|
||||||
'body':(P_NR_BODY,P_NOBODY),
|
|
||||||
'header':(P_NR_HDR,P_NOHDRS)
|
|
||||||
}
|
|
||||||
|
|
||||||
def nocallback(func):
|
|
||||||
try:
|
|
||||||
func.milter_protocol = OPTIONAL_CALLBACKS[func.__name__][1]
|
|
||||||
except KeyError:
|
|
||||||
raise ValueError(
|
|
||||||
'@nocallback applied to non-optional method: '+func.__name__)
|
|
||||||
return func
|
|
||||||
|
|
||||||
def noreply(func):
|
|
||||||
try:
|
|
||||||
nr_mask = OPTIONAL_CALLBACKS[func.__name__][0]
|
|
||||||
except KeyErro:
|
|
||||||
raise ValueError(
|
|
||||||
'@noreply applied to non-optional method: '+func.__name__)
|
|
||||||
def wrapper(self,*args):
|
|
||||||
rc = func(self,*args)
|
|
||||||
if self._protocol & nr_mask: return NOREPLY
|
|
||||||
return rc
|
|
||||||
wrapper.milter_protocol = nr_mask
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
class DisabledAction(RuntimeError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# A do nothing Milter base class from which python milters should derive
|
|
||||||
# unless they are using the milter C module directly.
|
|
||||||
|
|
||||||
class Base(object):
|
|
||||||
"The core class interface to the milter module."
|
|
||||||
|
|
||||||
def _setctx(self,ctx):
|
def _setctx(self,ctx):
|
||||||
self._ctx = ctx
|
self.__ctx = ctx
|
||||||
self._actions = CURR_ACTS # all actions enabled by default
|
|
||||||
self._protocol = 0 # no protocol options by default
|
|
||||||
if ctx:
|
if ctx:
|
||||||
ctx.setpriv(self)
|
ctx.setpriv(self)
|
||||||
def log(self,*msg): pass
|
|
||||||
@nocallback
|
|
||||||
def connect(self,hostname,family,hostaddr): return CONTINUE
|
|
||||||
@nocallback
|
|
||||||
def hello(self,hostname): return CONTINUE
|
|
||||||
@nocallback
|
|
||||||
def envfrom(self,f,*str): return CONTINUE
|
|
||||||
@nocallback
|
|
||||||
def envrcpt(self,to,*str): return CONTINUE
|
|
||||||
@nocallback
|
|
||||||
def data(self): return CONTINUE
|
|
||||||
@nocallback
|
|
||||||
def header(self,field,value): return CONTINUE
|
|
||||||
@nocallback
|
|
||||||
def eoh(self): return CONTINUE
|
|
||||||
@nocallback
|
|
||||||
def body(self,unused): return CONTINUE
|
|
||||||
@nocallback
|
|
||||||
def unknown(self,cmd): return CONTINUE
|
|
||||||
def eom(self): return CONTINUE
|
|
||||||
def abort(self): return CONTINUE
|
|
||||||
def close(self): return CONTINUE
|
|
||||||
|
|
||||||
# Return mask of SMFIP_N.. protocol option bits to clear for this class
|
|
||||||
@classmethod
|
|
||||||
def protocol_mask(klass):
|
|
||||||
try:
|
|
||||||
return klass._protocol_mask
|
|
||||||
except AttributeError:
|
|
||||||
p = 0
|
|
||||||
for func,(nr,nc) in OPTIONAL_CALLBACKS.items():
|
|
||||||
func = getattr(klass,func)
|
|
||||||
ca = getattr(func,'milter_protocol',0)
|
|
||||||
#print func,hex(nr),hex(nc),hex(ca)
|
|
||||||
p |= (nr|nc) & ~ca
|
|
||||||
klass._protocol_mask = p
|
|
||||||
return p
|
|
||||||
|
|
||||||
# Default negotiation sets P_NO* and P_NR* for callbacks
|
|
||||||
# marked @nocallback and @noreply respectively
|
|
||||||
def negotiate(self,opts):
|
|
||||||
try:
|
|
||||||
self._actions,p,f1,f2 = opts
|
|
||||||
opts[1] = self._protocol = \
|
|
||||||
p & ~self.protocol_mask() & ~P_RCPT_REJ & ~P_HDR_LEADSPC
|
|
||||||
opts[2] = 0
|
|
||||||
opts[3] = 0
|
|
||||||
#self.log("Negotiated:",opts)
|
|
||||||
except:
|
|
||||||
# don't change anything if something went wrong
|
|
||||||
return ALL_OPTS
|
|
||||||
return CONTINUE
|
|
||||||
|
|
||||||
# Milter methods which can be invoked from most callbacks
|
|
||||||
def getsymval(self,sym):
|
|
||||||
return self._ctx.getsymval(sym)
|
|
||||||
|
|
||||||
# If sendmail does not support setmlreply, then only the
|
|
||||||
# first msg line is used.
|
|
||||||
def setreply(self,rcode,xcode=None,msg=None,*ml):
|
|
||||||
return self._ctx.setreply(rcode,xcode,msg,*ml)
|
|
||||||
|
|
||||||
# may only be called from negotiate callback
|
|
||||||
def setsmlist(self,stage,macros):
|
|
||||||
if not self._actions & SETSMLIST: raise DisabledAction("SETSMLIST")
|
|
||||||
if type(macros) in (list,tuple):
|
|
||||||
macros = ' '.join(macros)
|
|
||||||
return self._ctx.setsmlist(stage,macros)
|
|
||||||
|
|
||||||
# Milter methods which can only be called from eom callback.
|
|
||||||
def addheader(self,field,value,idx=-1):
|
|
||||||
if not self._actions & ADDHDRS: raise DisabledAction("ADDHDRS")
|
|
||||||
return self._ctx.addheader(field,value,idx)
|
|
||||||
|
|
||||||
def chgheader(self,field,idx,value):
|
|
||||||
if not self._actions & CHGHDRS: raise DisabledAction("CHGHDRS")
|
|
||||||
return self._ctx.chgheader(field,idx,value)
|
|
||||||
|
|
||||||
def addrcpt(self,rcpt,params=None):
|
|
||||||
if not self._actions & ADDRCPT: raise DisabledAction("ADDRCPT")
|
|
||||||
return self._ctx.addrcpt(rcpt,params)
|
|
||||||
|
|
||||||
def delrcpt(self,rcpt):
|
|
||||||
if not self._actions & DELRCPT: raise DisabledAction("DELRCPT")
|
|
||||||
return self._ctx.delrcpt(rcpt)
|
|
||||||
|
|
||||||
def replacebody(self,body):
|
|
||||||
if not self._actions & MODBODY: raise DisabledAction("MODBODY")
|
|
||||||
return self._ctx.replacebody(body)
|
|
||||||
|
|
||||||
def chgfrom(self,sender,params=None):
|
|
||||||
if not self._actions & CHGFROM: raise DisabledAction("CHGFROM")
|
|
||||||
return self._ctx.chgfrom(sender,params)
|
|
||||||
|
|
||||||
# When quarantined, a message goes into the mailq as if to be delivered,
|
|
||||||
# but delivery is deferred until the message is unquarantined.
|
|
||||||
def quarantine(self,reason):
|
|
||||||
if not self._actions & QUARANTINE: raise DisabledAction("QUARANTINE")
|
|
||||||
return self._ctx.quarantine(reason)
|
|
||||||
|
|
||||||
def progress(self):
|
|
||||||
return self._ctx.progress()
|
|
||||||
|
|
||||||
# A logging but otherwise do nothing Milter base class included
|
|
||||||
# for compatibility with previous versions of pymilter.
|
|
||||||
|
|
||||||
class Milter(Base):
|
|
||||||
"A simple class interface to the milter module."
|
|
||||||
|
|
||||||
|
# user replaceable callbacks
|
||||||
def log(self,*msg):
|
def log(self,*msg):
|
||||||
print 'Milter:',
|
print 'Milter:',
|
||||||
for i in msg: print i,
|
for i in msg: print i,
|
||||||
print
|
print
|
||||||
|
|
||||||
@noreply
|
|
||||||
def connect(self,hostname,family,hostaddr):
|
def connect(self,hostname,family,hostaddr):
|
||||||
"Called for each connection to sendmail."
|
"Called for each connection to sendmail."
|
||||||
self.log("connect from %s at %s" % (hostname,hostaddr))
|
self.log("connect from %s at %s" % (hostname,hostaddr))
|
||||||
return CONTINUE
|
return CONTINUE
|
||||||
|
|
||||||
@noreply
|
|
||||||
def hello(self,hostname):
|
def hello(self,hostname):
|
||||||
"Called after the HELO command."
|
"Called after the HELO command."
|
||||||
self.log("hello from %s" % hostname)
|
self.log("hello from %s" % hostname)
|
||||||
return CONTINUE
|
return CONTINUE
|
||||||
|
|
||||||
@noreply
|
|
||||||
def envfrom(self,f,*str):
|
def envfrom(self,f,*str):
|
||||||
"""Called to begin each message.
|
"""Called to begin each message.
|
||||||
f -> string message sender
|
f -> string message sender
|
||||||
@@ -207,24 +62,25 @@ class Milter(Base):
|
|||||||
self.log("mail from",f,str)
|
self.log("mail from",f,str)
|
||||||
return CONTINUE
|
return CONTINUE
|
||||||
|
|
||||||
@noreply
|
|
||||||
def envrcpt(self,to,*str):
|
def envrcpt(self,to,*str):
|
||||||
"Called for each message recipient."
|
"Called for each message recipient."
|
||||||
self.log("rcpt to",to,str)
|
self.log("rcpt to",to,str)
|
||||||
return CONTINUE
|
return CONTINUE
|
||||||
|
|
||||||
@noreply
|
|
||||||
def header(self,field,value):
|
def header(self,field,value):
|
||||||
"Called for each message header."
|
"Called for each message header."
|
||||||
self.log("%s: %s" % (field,value))
|
self.log("%s: %s" % (field,value))
|
||||||
return CONTINUE
|
return CONTINUE
|
||||||
|
|
||||||
@noreply
|
|
||||||
def eoh(self):
|
def eoh(self):
|
||||||
"Called after all headers are processed."
|
"Called after all headers are processed."
|
||||||
self.log("eoh")
|
self.log("eoh")
|
||||||
return CONTINUE
|
return CONTINUE
|
||||||
|
|
||||||
|
def body(self,unused):
|
||||||
|
"Called to transfer the message body."
|
||||||
|
return CONTINUE
|
||||||
|
|
||||||
def eom(self):
|
def eom(self):
|
||||||
"Called at the end of message."
|
"Called at the end of message."
|
||||||
self.log("eom")
|
self.log("eom")
|
||||||
@@ -240,23 +96,50 @@ class Milter(Base):
|
|||||||
self.log("close")
|
self.log("close")
|
||||||
return CONTINUE
|
return CONTINUE
|
||||||
|
|
||||||
|
# Milter methods which can be invoked from callbacks
|
||||||
|
def getsymval(self,sym):
|
||||||
|
return self.__ctx.getsymval(sym)
|
||||||
|
|
||||||
|
# If sendmail does not support setmlreply, then only the
|
||||||
|
# first msg line is used.
|
||||||
|
def setreply(self,rcode,xcode=None,msg=None,*ml):
|
||||||
|
return self.__ctx.setreply(rcode,xcode,msg,*ml)
|
||||||
|
|
||||||
|
# Milter methods which can only be called from eom callback.
|
||||||
|
def addheader(self,field,value,idx=-1):
|
||||||
|
return self.__ctx.addheader(field,value,idx)
|
||||||
|
|
||||||
|
def chgheader(self,field,idx,value):
|
||||||
|
return self.__ctx.chgheader(field,idx,value)
|
||||||
|
|
||||||
|
def addrcpt(self,rcpt,params=None):
|
||||||
|
return self.__ctx.addrcpt(rcpt,params)
|
||||||
|
|
||||||
|
def delrcpt(self,rcpt):
|
||||||
|
return self.__ctx.delrcpt(rcpt)
|
||||||
|
|
||||||
|
def replacebody(self,body):
|
||||||
|
return self.__ctx.replacebody(body)
|
||||||
|
|
||||||
|
def chgfrom(self,sender,params=None):
|
||||||
|
return self.__ctx.chgfrom(sender,params)
|
||||||
|
|
||||||
|
# When quarantined, a message goes into the mailq as if to be delivered,
|
||||||
|
# but delivery is deferred until the message is unquarantined.
|
||||||
|
def quarantine(self,reason):
|
||||||
|
return self.__ctx.quarantine(reason)
|
||||||
|
|
||||||
|
def progress(self):
|
||||||
|
return self.__ctx.progress()
|
||||||
|
|
||||||
factory = Milter
|
factory = Milter
|
||||||
|
|
||||||
def negotiate_callback(ctx,opts):
|
def connectcallback(ctx,hostname,family,hostaddr):
|
||||||
m = factory()
|
|
||||||
m._setctx(ctx)
|
|
||||||
return m.negotiate(opts)
|
|
||||||
|
|
||||||
def connect_callback(ctx,hostname,family,hostaddr,nr_mask=P_NR_CONN):
|
|
||||||
m = ctx.getpriv()
|
|
||||||
if not m:
|
|
||||||
# If not already created (because the current MTA doesn't support
|
|
||||||
# xmfi_negotiate), create the connection object.
|
|
||||||
m = factory()
|
m = factory()
|
||||||
m._setctx(ctx)
|
m._setctx(ctx)
|
||||||
return m.connect(hostname,family,hostaddr)
|
return m.connect(hostname,family,hostaddr)
|
||||||
|
|
||||||
def close_callback(ctx):
|
def closecallback(ctx):
|
||||||
m = ctx.getpriv()
|
m = ctx.getpriv()
|
||||||
if not m: return CONTINUE
|
if not m: return CONTINUE
|
||||||
try:
|
try:
|
||||||
@@ -313,7 +196,7 @@ def runmilter(name,socketname,timeout = 0):
|
|||||||
|
|
||||||
# The default flags set include everything
|
# The default flags set include everything
|
||||||
# milter.set_flags(milter.ADDHDRS)
|
# milter.set_flags(milter.ADDHDRS)
|
||||||
milter.set_connect_callback(connect_callback)
|
milter.set_connect_callback(connectcallback)
|
||||||
milter.set_helo_callback(lambda ctx, host: ctx.getpriv().hello(host))
|
milter.set_helo_callback(lambda ctx, host: ctx.getpriv().hello(host))
|
||||||
# For envfrom and envrcpt, we would like to convert ESMTP parms to keyword
|
# 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
|
# parms, but then all existing users would have to include **kw to accept
|
||||||
@@ -326,16 +209,12 @@ def runmilter(name,socketname,timeout = 0):
|
|||||||
milter.set_body_callback(lambda ctx,chunk: ctx.getpriv().body(chunk))
|
milter.set_body_callback(lambda ctx,chunk: ctx.getpriv().body(chunk))
|
||||||
milter.set_eom_callback(lambda ctx: ctx.getpriv().eom())
|
milter.set_eom_callback(lambda ctx: ctx.getpriv().eom())
|
||||||
milter.set_abort_callback(lambda ctx: ctx.getpriv().abort())
|
milter.set_abort_callback(lambda ctx: ctx.getpriv().abort())
|
||||||
milter.set_close_callback(close_callback)
|
milter.set_close_callback(closecallback)
|
||||||
|
|
||||||
milter.setconn(socketname)
|
milter.setconn(socketname)
|
||||||
if timeout > 0: milter.settimeout(timeout)
|
if timeout > 0: milter.settimeout(timeout)
|
||||||
# The name *must* match the X line in sendmail.cf (supposedly)
|
# The name *must* match the X line in sendmail.cf (supposedly)
|
||||||
milter.register(name,
|
milter.register(name)
|
||||||
data=lambda ctx: ctx.getpriv().data(),
|
|
||||||
unknown=lambda ctx,cmd: ctx.getpriv().unknown(cmd),
|
|
||||||
negotiate=negotiate_callback
|
|
||||||
)
|
|
||||||
start_seq = _seq
|
start_seq = _seq
|
||||||
try:
|
try:
|
||||||
milter.main()
|
milter.main()
|
||||||
|
|||||||
+4
-7
@@ -5,9 +5,6 @@
|
|||||||
# Send DSNs, do call back verification,
|
# Send DSNs, do call back verification,
|
||||||
# and generate DSN messages from a template
|
# and generate DSN messages from a template
|
||||||
# $Log$
|
# $Log$
|
||||||
# Revision 1.16 2007/09/25 01:24:59 customdesigned
|
|
||||||
# Allow arbitrary object, not just spf.query like, to provide data for create_msg
|
|
||||||
#
|
|
||||||
# Revision 1.15 2007/09/24 20:13:26 customdesigned
|
# Revision 1.15 2007/09/24 20:13:26 customdesigned
|
||||||
# Remove explicit spf dependency.
|
# Remove explicit spf dependency.
|
||||||
#
|
#
|
||||||
@@ -34,7 +31,7 @@ import Milter
|
|||||||
import time
|
import time
|
||||||
import dns
|
import dns
|
||||||
|
|
||||||
def send_dsn(mailfrom,receiver,msg=None,timeout=600,session=None,ourfrom=''):
|
def send_dsn(mailfrom,receiver,msg=None,timeout=600,session=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.
|
Mailfrom is original sender we are sending DSN or CBV to.
|
||||||
Receiver is the MTA sending the DSN.
|
Receiver is the MTA sending the DSN.
|
||||||
@@ -65,14 +62,14 @@ def send_dsn(mailfrom,receiver,msg=None,timeout=600,session=None,ourfrom=''):
|
|||||||
raise smtplib.SMTPHeloError(code, resp)
|
raise smtplib.SMTPHeloError(code, resp)
|
||||||
if msg:
|
if msg:
|
||||||
try:
|
try:
|
||||||
smtp.sendmail('<%s>'%ourfrom,mailfrom,msg)
|
smtp.sendmail('<>',mailfrom,msg)
|
||||||
except smtplib.SMTPSenderRefused:
|
except smtplib.SMTPSenderRefused:
|
||||||
# does not accept DSN, try postmaster (at the risk of mail loops)
|
# does not accept DSN, try postmaster (at the risk of mail loops)
|
||||||
smtp.sendmail('<postmaster@%s>'%receiver,mailfrom,msg)
|
smtp.sendmail('<postmaster@%s>'%receiver,mailfrom,msg)
|
||||||
else: # CBV
|
else: # CBV
|
||||||
code,resp = smtp.docmd('MAIL FROM: <%s>'%ourfrom)
|
code,resp = smtp.docmd('MAIL FROM: <>')
|
||||||
if code != 250:
|
if code != 250:
|
||||||
raise smtplib.SMTPSenderRefused(code, resp, '<%s>'%ourfrom)
|
raise smtplib.SMTPSenderRefused(code, resp, '<>')
|
||||||
code,resp = smtp.rcpt(mailfrom)
|
code,resp = smtp.rcpt(mailfrom)
|
||||||
if code not in (250,251):
|
if code not in (250,251):
|
||||||
return (code,resp) # permanent error
|
return (code,resp) # permanent error
|
||||||
|
|||||||
+10
-212
@@ -35,24 +35,6 @@ $ python setup.py help
|
|||||||
libraries=["milter","smutil","resolv"]
|
libraries=["milter","smutil","resolv"]
|
||||||
|
|
||||||
* $Log$
|
* $Log$
|
||||||
* Revision 1.22 2009/05/29 19:53:36 customdesigned
|
|
||||||
* Typo SMFIS_ALL_OPTS
|
|
||||||
*
|
|
||||||
* Revision 1.21 2009/05/29 19:49:40 customdesigned
|
|
||||||
* Typo calling helo instead of negotiate.
|
|
||||||
*
|
|
||||||
* Revision 1.20 2009/05/29 18:25:59 customdesigned
|
|
||||||
* Null terminate keyword list.
|
|
||||||
*
|
|
||||||
* Revision 1.19 2009/05/28 18:36:42 customdesigned
|
|
||||||
* Support new callbacks, including negotiate
|
|
||||||
*
|
|
||||||
* Revision 1.18 2009/05/21 21:53:05 customdesigned
|
|
||||||
* First cut at support unknown, data, negotiate callbacks.
|
|
||||||
*
|
|
||||||
* Revision 1.17 2009/02/06 04:28:08 customdesigned
|
|
||||||
* Oops! Missing options argument pointer for addrcpt.
|
|
||||||
*
|
|
||||||
* Revision 1.16 2008/12/16 04:21:05 customdesigned
|
* Revision 1.16 2008/12/16 04:21:05 customdesigned
|
||||||
* Fedora release
|
* Fedora release
|
||||||
*
|
*
|
||||||
@@ -399,7 +381,7 @@ generic_set_callback(PyObject *args,char *t,PyObject **cb) {
|
|||||||
callback = 0;
|
callback = 0;
|
||||||
else {
|
else {
|
||||||
if (!PyCallable_Check(callback)) {
|
if (!PyCallable_Check(callback)) {
|
||||||
PyErr_SetString(PyExc_TypeError, "callback parameter must be callable");
|
PyErr_SetString(PyExc_TypeError, "parameter must be callable");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
Py_INCREF(callback);
|
Py_INCREF(callback);
|
||||||
@@ -801,87 +783,6 @@ milter_wrap_abort(SMFICTX *ctx) {
|
|||||||
return generic_noarg_wrapper(ctx,abort_callback);
|
return generic_noarg_wrapper(ctx,abort_callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SMFIS_ALL_OPTS
|
|
||||||
static PyObject *unknown_callback = NULL;
|
|
||||||
static PyObject *data_callback = NULL;
|
|
||||||
static PyObject *negotiate_callback = NULL;
|
|
||||||
|
|
||||||
static int
|
|
||||||
milter_wrap_unknown(SMFICTX *ctx, const char *cmd) {
|
|
||||||
PyObject *arglist;
|
|
||||||
milter_ContextObject *c;
|
|
||||||
|
|
||||||
if (unknown_callback == NULL) return SMFIS_CONTINUE;
|
|
||||||
c = _get_context(ctx);
|
|
||||||
if (!c) return SMFIS_TEMPFAIL;
|
|
||||||
arglist = Py_BuildValue("(Os)", c, cmd);
|
|
||||||
return _generic_wrapper(c, unknown_callback, arglist);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
milter_wrap_data(SMFICTX *ctx) {
|
|
||||||
return generic_noarg_wrapper(ctx,data_callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
milter_wrap_negotiate(SMFICTX *ctx,
|
|
||||||
unsigned long f0,
|
|
||||||
unsigned long f1,
|
|
||||||
unsigned long f2,
|
|
||||||
unsigned long f3,
|
|
||||||
unsigned long *pf0,
|
|
||||||
unsigned long *pf1,
|
|
||||||
unsigned long *pf2,
|
|
||||||
unsigned long *pf3) {
|
|
||||||
PyObject *arglist, *optlist;
|
|
||||||
milter_ContextObject *c;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
if (negotiate_callback == NULL) return SMFIS_ALL_OPTS;
|
|
||||||
c = _get_context(ctx);
|
|
||||||
if (!c)
|
|
||||||
return SMFIS_REJECT; // do not contact us again for current connection
|
|
||||||
optlist = Py_BuildValue("[kkkk]",f0,f1,f2,f3);
|
|
||||||
if (optlist == NULL)
|
|
||||||
arglist = NULL;
|
|
||||||
else
|
|
||||||
arglist = Py_BuildValue("(OO)", c, optlist);
|
|
||||||
PyThreadState *t = c->t;
|
|
||||||
c->t = 0; // do not release thread in _generic_wrapper
|
|
||||||
rc = _generic_wrapper(c, negotiate_callback, arglist);
|
|
||||||
c->t = t;
|
|
||||||
if (rc == SMFIS_CONTINUE) {
|
|
||||||
#if 0 // PyArgs_Parse deprecated and going away
|
|
||||||
if (!PyArgs_Parse(optlist,"[kkkk]",pf0,pf1,pf2,pf3)) {
|
|
||||||
PyErr_Print();
|
|
||||||
PyErr_Clear(); /* must clear since not returning to python */
|
|
||||||
rc = SMFIS_REJECT;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
unsigned long *pa[4] = { pf0,pf1,pf2,pf3 };
|
|
||||||
unsigned long fa[4] = { f0,f1,f2,f3 };
|
|
||||||
int len = PyList_Size(optlist);
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < 4; ++i) {
|
|
||||||
*pa[i] = (i <= len)
|
|
||||||
? PyInt_AsUnsignedLongMask(PyList_GET_ITEM(optlist,i))
|
|
||||||
: fa[i];
|
|
||||||
}
|
|
||||||
if (PyErr_Occurred()) {
|
|
||||||
PyErr_Print();
|
|
||||||
PyErr_Clear();
|
|
||||||
rc = SMFIS_REJECT;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else if (rc != SMFIS_ALL_OPTS)
|
|
||||||
rc = SMFIS_REJECT;
|
|
||||||
Py_DECREF(optlist);
|
|
||||||
_release_thread(t);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
milter_wrap_close(SMFICTX *ctx) {
|
milter_wrap_close(SMFICTX *ctx) {
|
||||||
/* xxfi_close can be called out of order - even before connect.
|
/* xxfi_close can be called out of order - even before connect.
|
||||||
@@ -913,63 +814,17 @@ milter_wrap_close(SMFICTX *ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static char milter_register__doc__[] =
|
static char milter_register__doc__[] =
|
||||||
"register(name,unknown=,data=,negotiate=) -> None\n\
|
"register(name) -> None\n\
|
||||||
Registers the milter name with current callbacks, and flags.\n\
|
Registers the milter name with current callbacks, and flags.\n\
|
||||||
Required before main() is called.";
|
Required before main() is called.";
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
milter_register(PyObject *self, PyObject *args, PyObject *kwds) {
|
milter_register(PyObject *self, PyObject *args) {
|
||||||
static char *kwlist[] = { "name","unknown","data","negotiate", NULL };
|
if (!PyArg_ParseTuple(args, "s:register", &description.xxfi_name))
|
||||||
static PyObject** const cbp[3] =
|
|
||||||
{ &unknown_callback, &data_callback, &negotiate_callback };
|
|
||||||
PyObject *cb[3] = { NULL, NULL, NULL };
|
|
||||||
int i;
|
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|OOO:register", kwlist,
|
|
||||||
&description.xxfi_name, &cb[0],&cb[1],&cb[2]))
|
|
||||||
return NULL;
|
return NULL;
|
||||||
for (i = 0; i < 3; ++i) {
|
|
||||||
PyObject *callback = cb[i];
|
|
||||||
if (callback != NULL && callback != Py_None) {
|
|
||||||
if (!PyCallable_Check(callback)) {
|
|
||||||
char err[80];
|
|
||||||
sprintf(err,"%s parameter must be callable",kwlist[i]);
|
|
||||||
PyErr_SetString(PyExc_TypeError, err);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (i = 0; i < 3; ++i) {
|
|
||||||
PyObject *callback = cb[i];
|
|
||||||
if (callback != NULL) { // if keyword specified
|
|
||||||
if (callback == Py_None) {
|
|
||||||
callback = NULL;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Py_INCREF(callback);
|
|
||||||
}
|
|
||||||
PyObject *oldval = *cbp[i];
|
|
||||||
*cbp[i] = callback;
|
|
||||||
if (oldval) {
|
|
||||||
Py_DECREF(oldval);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return _generic_return(smfi_register(description), "cannot register");
|
return _generic_return(smfi_register(description), "cannot register");
|
||||||
}
|
}
|
||||||
|
|
||||||
static char milter_opensocket__doc__[] =
|
|
||||||
"opensocket(rmsock) -> None\n\
|
|
||||||
Attempts to create and open the socket provided with setconn.\n\
|
|
||||||
Removes the socket first if rmsock is True.";
|
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
milter_opensocket(PyObject *self, PyObject *args) {
|
|
||||||
char rmsock = 0;
|
|
||||||
if (!PyArg_ParseTuple(args, "b:opensocket", &rmsock))
|
|
||||||
return NULL;
|
|
||||||
return _generic_return(smfi_opensocket(rmsock), "cannot opensocket");
|
|
||||||
}
|
|
||||||
|
|
||||||
static char milter_main__doc__[] =
|
static char milter_main__doc__[] =
|
||||||
"main() -> None\n\
|
"main() -> None\n\
|
||||||
Main milter routine. Set any callbacks, and flags desired, then call\n\
|
Main milter routine. Set any callbacks, and flags desired, then call\n\
|
||||||
@@ -1275,7 +1130,8 @@ milter_delrcpt(PyObject *self, PyObject *args) {
|
|||||||
ctx = _find_context(self);
|
ctx = _find_context(self);
|
||||||
if (ctx == NULL) return NULL;
|
if (ctx == NULL) return NULL;
|
||||||
t = PyEval_SaveThread();
|
t = PyEval_SaveThread();
|
||||||
return _thread_return(t,smfi_delrcpt(ctx, rcpt), "cannot delete recipient");
|
return _thread_return(t,smfi_delrcpt(ctx, rcpt),
|
||||||
|
"cannot delete recipient");
|
||||||
}
|
}
|
||||||
|
|
||||||
static char milter_replacebody__doc__[] =
|
static char milter_replacebody__doc__[] =
|
||||||
@@ -1382,27 +1238,6 @@ milter_progress(PyObject *self, PyObject *args) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SMFIF_SETSMLIST
|
|
||||||
static char milter_setsmlist__doc__[] =
|
|
||||||
"setsmlist(stage,macrolist) -> None\n\
|
|
||||||
Tell the MTA which macro values we are interested in for a given stage";
|
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
milter_setsmlist(PyObject *self, PyObject *args) {
|
|
||||||
SMFICTX *ctx;
|
|
||||||
PyThreadState *t;
|
|
||||||
int stage = 0;
|
|
||||||
char *smlist = 0;
|
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "is:setsmlist",&stage, &smlist)) return NULL;
|
|
||||||
ctx = _find_context(self);
|
|
||||||
if (ctx == NULL) return NULL;
|
|
||||||
t = PyEval_SaveThread();
|
|
||||||
return _thread_return(t,smfi_setsmlist(ctx,stage,smlist),
|
|
||||||
"cannot set macro list");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static PyMethodDef context_methods[] = {
|
static PyMethodDef context_methods[] = {
|
||||||
{ "getsymval", milter_getsymval, METH_VARARGS, milter_getsymval__doc__},
|
{ "getsymval", milter_getsymval, METH_VARARGS, milter_getsymval__doc__},
|
||||||
{ "setreply", milter_setreply, METH_VARARGS, milter_setreply__doc__},
|
{ "setreply", milter_setreply, METH_VARARGS, milter_setreply__doc__},
|
||||||
@@ -1421,9 +1256,6 @@ static PyMethodDef context_methods[] = {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef SMFIF_CHGFROM
|
#ifdef SMFIF_CHGFROM
|
||||||
{ "chgfrom", milter_chgfrom, METH_VARARGS, milter_chgfrom__doc__},
|
{ "chgfrom", milter_chgfrom, METH_VARARGS, milter_chgfrom__doc__},
|
||||||
#endif
|
|
||||||
#ifdef SMFIF_SETSMLIST
|
|
||||||
{ "setsmlist", milter_setsmlist, METH_VARARGS, milter_setsmlist__doc__},
|
|
||||||
#endif
|
#endif
|
||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
@@ -1446,12 +1278,7 @@ static struct smfiDesc description = { /* Set some reasonable defaults */
|
|||||||
milter_wrap_body,
|
milter_wrap_body,
|
||||||
milter_wrap_eom,
|
milter_wrap_eom,
|
||||||
milter_wrap_abort,
|
milter_wrap_abort,
|
||||||
milter_wrap_close,
|
milter_wrap_close
|
||||||
#ifdef SMFIS_ALL_OPTS
|
|
||||||
milter_wrap_unknown,
|
|
||||||
milter_wrap_data,
|
|
||||||
milter_wrap_negotiate
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyMethodDef milter_methods[] = {
|
static PyMethodDef milter_methods[] = {
|
||||||
@@ -1466,9 +1293,9 @@ static PyMethodDef milter_methods[] = {
|
|||||||
{ "set_eom_callback", milter_set_eom_callback, METH_VARARGS, milter_set_eom_callback__doc__},
|
{ "set_eom_callback", milter_set_eom_callback, METH_VARARGS, milter_set_eom_callback__doc__},
|
||||||
{ "set_abort_callback", milter_set_abort_callback, METH_VARARGS, milter_set_abort_callback__doc__},
|
{ "set_abort_callback", milter_set_abort_callback, METH_VARARGS, milter_set_abort_callback__doc__},
|
||||||
{ "set_close_callback", milter_set_close_callback, METH_VARARGS, milter_set_close_callback__doc__},
|
{ "set_close_callback", milter_set_close_callback, METH_VARARGS, milter_set_close_callback__doc__},
|
||||||
{ "set_exception_policy", milter_set_exception_policy, METH_VARARGS, milter_set_exception_policy__doc__},
|
{ "set_exception_policy", milter_set_exception_policy,METH_VARARGS, milter_set_exception_policy__doc__},
|
||||||
{ "register", (PyCFunction)milter_register,METH_VARARGS|METH_KEYWORDS, milter_register__doc__},
|
{ "register", milter_register, METH_VARARGS, milter_register__doc__},
|
||||||
{ "opensocket", milter_opensocket, METH_VARARGS, milter_opensocket__doc__},
|
{ "register", milter_register, METH_VARARGS, milter_register__doc__},
|
||||||
{ "main", milter_main, METH_VARARGS, milter_main__doc__},
|
{ "main", milter_main, METH_VARARGS, milter_main__doc__},
|
||||||
{ "setdbg", milter_setdbg, METH_VARARGS, milter_setdbg__doc__},
|
{ "setdbg", milter_setdbg, METH_VARARGS, milter_setdbg__doc__},
|
||||||
{ "settimeout", milter_settimeout, METH_VARARGS, milter_settimeout__doc__},
|
{ "settimeout", milter_settimeout, METH_VARARGS, milter_settimeout__doc__},
|
||||||
@@ -1543,35 +1370,6 @@ initmilter(void) {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef SMFIF_CHGFROM
|
#ifdef SMFIF_CHGFROM
|
||||||
setitem(d,"CHGFROM",SMFIF_CHGFROM);
|
setitem(d,"CHGFROM",SMFIF_CHGFROM);
|
||||||
#endif
|
|
||||||
#ifdef SMFIF_SETSMLIST
|
|
||||||
setitem(d,"SETSMLIST",SMFIF_SETSMLIST);
|
|
||||||
#endif
|
|
||||||
#ifdef SMFIS_ALL_OPTS
|
|
||||||
setitem(d,"P_RCPT_REJ",SMFIP_RCPT_REJ);
|
|
||||||
setitem(d,"P_NR_CONN",SMFIP_NR_CONN);
|
|
||||||
setitem(d,"P_NR_HELO",SMFIP_NR_HELO);
|
|
||||||
setitem(d,"P_NR_MAIL",SMFIP_NR_MAIL);
|
|
||||||
setitem(d,"P_NR_RCPT",SMFIP_NR_RCPT);
|
|
||||||
setitem(d,"P_NR_DATA",SMFIP_NR_DATA);
|
|
||||||
setitem(d,"P_NR_UNKN",SMFIP_NR_UNKN);
|
|
||||||
setitem(d,"P_NR_EOH",SMFIP_NR_EOH);
|
|
||||||
setitem(d,"P_NR_BODY",SMFIP_NR_BODY);
|
|
||||||
setitem(d,"P_NR_HDR",SMFIP_NR_HDR);
|
|
||||||
setitem(d,"P_NOCONNECT",SMFIP_NOCONNECT);
|
|
||||||
setitem(d,"P_NOHELO",SMFIP_NOHELO);
|
|
||||||
setitem(d,"P_NOMAIL",SMFIP_NOMAIL);
|
|
||||||
setitem(d,"P_NORCPT",SMFIP_NORCPT);
|
|
||||||
setitem(d,"P_NODATA",SMFIP_NODATA);
|
|
||||||
setitem(d,"P_NOUNKNOWN",SMFIP_NOUNKNOWN);
|
|
||||||
setitem(d,"P_NOEOH",SMFIP_NOEOH);
|
|
||||||
setitem(d,"P_NOBODY",SMFIP_NOBODY);
|
|
||||||
setitem(d,"P_NOHDRS",SMFIP_NOHDRS);
|
|
||||||
setitem(d,"P_HDR_LEADSPC",SMFIP_HDR_LEADSPC);
|
|
||||||
setitem(d,"P_SKIP",SMFIP_SKIP);
|
|
||||||
setitem(d,"ALL_OPTS",SMFIS_ALL_OPTS);
|
|
||||||
setitem(d,"SKIP",SMFIS_SKIP);
|
|
||||||
setitem(d,"NOREPLY",SMFIS_NOREPLY);
|
|
||||||
#endif
|
#endif
|
||||||
setitem(d,"CONTINUE", SMFIS_CONTINUE);
|
setitem(d,"CONTINUE", SMFIS_CONTINUE);
|
||||||
setitem(d,"REJECT", SMFIS_REJECT);
|
setitem(d,"REJECT", SMFIS_REJECT);
|
||||||
|
|||||||
+8
-18
@@ -1,6 +1,5 @@
|
|||||||
# EL 3,4,5 supported, set to 0 for Fedora
|
# EL 3,4,5 supported, set to 0 for Fedora
|
||||||
%define el4 1
|
|
||||||
%define dist .el4
|
|
||||||
%if 0%{?el3} || 0%{?el4}
|
%if 0%{?el3} || 0%{?el4}
|
||||||
%define __python python2.4
|
%define __python python2.4
|
||||||
%endif
|
%endif
|
||||||
@@ -11,9 +10,11 @@
|
|||||||
|
|
||||||
Summary: Python interface to sendmail milter API
|
Summary: Python interface to sendmail milter API
|
||||||
Name: pymilter
|
Name: pymilter
|
||||||
Version: 0.9.2
|
Version: 0.9.1
|
||||||
Release: 3%{dist}
|
Release: 1%{dist}
|
||||||
Source: http://downloads.sourceforge.net/pymilter/%{name}-%{version}.tar.gz
|
Source: http://downloads.sourceforge.net/pymilter/%{name}-%{version}.tar.gz
|
||||||
|
Patch: %{name}-smutil.patch
|
||||||
|
Patch1: %{name}-start.patch
|
||||||
License: GPLv2+
|
License: GPLv2+
|
||||||
Group: Development/Libraries
|
Group: Development/Libraries
|
||||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
|
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
|
||||||
@@ -36,6 +37,8 @@ DSNs, and doing CBV.
|
|||||||
|
|
||||||
%prep
|
%prep
|
||||||
%setup -q
|
%setup -q
|
||||||
|
%patch -p0 -b .smutil
|
||||||
|
%patch1 -p0 -b .start
|
||||||
|
|
||||||
%build
|
%build
|
||||||
env CFLAGS="$RPM_OPT_FLAGS" %{__python} setup.py build
|
env CFLAGS="$RPM_OPT_FLAGS" %{__python} setup.py build
|
||||||
@@ -69,7 +72,7 @@ q
|
|||||||
EOF
|
EOF
|
||||||
chmod a+x $RPM_BUILD_ROOT%{libdir}/start.sh
|
chmod a+x $RPM_BUILD_ROOT%{libdir}/start.sh
|
||||||
|
|
||||||
# start.sh is used by spfmilter, srsmilter, and milter, and could be used by
|
# start.sh is used by spfmilter and milter, and could be used by
|
||||||
# other milters using pymilter.
|
# other milters using pymilter.
|
||||||
%files
|
%files
|
||||||
%defattr(-,root,root,-)
|
%defattr(-,root,root,-)
|
||||||
@@ -83,19 +86,6 @@ chmod a+x $RPM_BUILD_ROOT%{libdir}/start.sh
|
|||||||
rm -rf $RPM_BUILD_ROOT
|
rm -rf $RPM_BUILD_ROOT
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
* Tue Jun 02 2009 Stuart Gathman <stuart@bmsi.com> 0.9.2-3
|
|
||||||
- Change result of @noreply callbacks to NOREPLY when so negotiated.
|
|
||||||
|
|
||||||
* Tue Jun 02 2009 Stuart Gathman <stuart@bmsi.com> 0.9.2-2
|
|
||||||
- Cache callback negotiation
|
|
||||||
|
|
||||||
* Thu May 28 2009 Stuart Gathman <stuart@bmsi.com> 0.9.2-1
|
|
||||||
- Add new callback support: data,negotiate,unknown
|
|
||||||
- Auto-negotiate protocol steps
|
|
||||||
|
|
||||||
* Thu Feb 05 2009 Stuart Gathman <stuart@bmsi.com> 0.9.1-1
|
|
||||||
- Fix missing address of optional param to addrcpt
|
|
||||||
|
|
||||||
* Wed Jan 07 2009 Stuart Gathman <stuart@bmsi.com> 0.9.0-4
|
* Wed Jan 07 2009 Stuart Gathman <stuart@bmsi.com> 0.9.0-4
|
||||||
- Stop using INSTALLED_FILES to make Fedora happy
|
- Stop using INSTALLED_FILES to make Fedora happy
|
||||||
- Remove config flag from start.sh glue
|
- Remove config flag from start.sh glue
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ if sys.version < '2.2.3':
|
|||||||
DistributionMetadata.download_url = None
|
DistributionMetadata.download_url = None
|
||||||
|
|
||||||
# NOTE: importing Milter to obtain version fails when milter.so not built
|
# NOTE: importing Milter to obtain version fails when milter.so not built
|
||||||
setup(name = "pymilter", version = '0.9.2',
|
setup(name = "pymilter", version = '0.9.1',
|
||||||
description="Python interface to sendmail milter API",
|
description="Python interface to sendmail milter API",
|
||||||
long_description="""\
|
long_description="""\
|
||||||
This is a python extension module to enable python scripts to
|
This is a python extension module to enable python scripts to
|
||||||
|
|||||||
Reference in New Issue
Block a user