Support new callbacks, including negotiate
This commit is contained in:
+119
-40
@@ -30,13 +30,124 @@ def uniqueID():
|
||||
_seq_lock.release()
|
||||
return seqno
|
||||
|
||||
class Milter:
|
||||
"""A simple class interface to the milter module.
|
||||
"""
|
||||
def nocallback(func):
|
||||
func.milter_protocol = 'NO'
|
||||
return func
|
||||
def noreply(func):
|
||||
func.milter_protocol = 'NR'
|
||||
return func
|
||||
|
||||
class DisabledAction(RuntimeError):
|
||||
pass
|
||||
|
||||
class Base(object):
|
||||
def __init__(self):
|
||||
self.__actions = CURR_ACTS # all actions enabled
|
||||
def _setctx(self,ctx):
|
||||
self.__ctx = ctx
|
||||
if ctx:
|
||||
ctx.setpriv(self)
|
||||
@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 eom(self): return CONTINUE
|
||||
@nocallback
|
||||
def abort(self): return CONTINUE
|
||||
@nocallback
|
||||
def unknown(self,cmd): return CONTINUE
|
||||
@nocallback
|
||||
def close(self): return CONTINUE
|
||||
def negotiate(self,opts):
|
||||
try:
|
||||
self.__actions,p,f1,f2 = opts
|
||||
for func,nr,nc in (
|
||||
(self.connect,P_NR_CONN,P_NOCONNECT),
|
||||
(self.hello,P_NR_HELO,P_NOHELO),
|
||||
(self.envfrom,P_NR_MAIL,P_NOMAIL),
|
||||
(self.envrcpt,P_NR_RCPT,P_NORCPT),
|
||||
(self.data,P_NR_DATA,P_NODATA),
|
||||
(self.unknown,P_NR_UNKN,P_NOUNKNOWN),
|
||||
(self.eoh,P_NR_EOH,P_NOEOH),
|
||||
(self.body,P_NR_BODY,P_NOBODY),
|
||||
(self.header,P_NR_HDR,P_NOHDRS)
|
||||
):
|
||||
ca = getattr(func,'milter_protocol',None)
|
||||
if ca != 'NR': p &= ~nr
|
||||
elif p & nr: print func.__name__,'NOREPLY'
|
||||
if ca != 'NO': p &= ~nc
|
||||
elif p & nc: print func.__name__,'NOCALLBACK'
|
||||
p[1] = p & ~P_RCPT_REJ & ~P_HDR_LEADSPC
|
||||
except:
|
||||
# don't change anything if something went wrong
|
||||
return ALL_OPTS
|
||||
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)
|
||||
|
||||
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()
|
||||
|
||||
class Milter(Base):
|
||||
"""A simple class interface to the milter module.
|
||||
"""
|
||||
|
||||
# user replaceable callbacks
|
||||
def log(self,*msg):
|
||||
@@ -96,42 +207,6 @@ class Milter:
|
||||
self.log("close")
|
||||
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
|
||||
|
||||
def connectcallback(ctx,hostname,family,hostaddr):
|
||||
@@ -214,7 +289,11 @@ def runmilter(name,socketname,timeout = 0):
|
||||
milter.setconn(socketname)
|
||||
if timeout > 0: milter.settimeout(timeout)
|
||||
# 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=lambda ctx,opt: ctx.getpriv().negotiate(opt)
|
||||
)
|
||||
start_seq = _seq
|
||||
try:
|
||||
milter.main()
|
||||
|
||||
@@ -35,6 +35,9 @@ $ python setup.py help
|
||||
libraries=["milter","smutil","resolv"]
|
||||
|
||||
* $Log$
|
||||
* 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.
|
||||
*
|
||||
@@ -1367,6 +1370,27 @@ milter_progress(PyObject *self, PyObject *args) {
|
||||
}
|
||||
#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[] = {
|
||||
{ "getsymval", milter_getsymval, METH_VARARGS, milter_getsymval__doc__},
|
||||
{ "setreply", milter_setreply, METH_VARARGS, milter_setreply__doc__},
|
||||
@@ -1385,6 +1409,9 @@ static PyMethodDef context_methods[] = {
|
||||
#endif
|
||||
#ifdef SMFIF_CHGFROM
|
||||
{ "chgfrom", milter_chgfrom, METH_VARARGS, milter_chgfrom__doc__},
|
||||
#endif
|
||||
#ifdef SMFIF_SETSMLIST
|
||||
{ "setsmlist", milter_setsmlist, METH_VARARGS, milter_setsmlist__doc__},
|
||||
#endif
|
||||
{ NULL, NULL }
|
||||
};
|
||||
@@ -1505,8 +1532,34 @@ initmilter(void) {
|
||||
#ifdef SMFIF_CHGFROM
|
||||
setitem(d,"CHGFROM",SMFIF_CHGFROM);
|
||||
#endif
|
||||
#ifdef SMFIF_SETSMLIST
|
||||
setitem(d,"SETSMLIST",SMFIF_SETSMLIST);
|
||||
#endif
|
||||
#ifdef SMFIF_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_NR_CONN);
|
||||
setitem(d,"P_NOHELO",SMFIP_NR_HELO);
|
||||
setitem(d,"P_NOMAIL",SMFIP_NR_MAIL);
|
||||
setitem(d,"P_NORCPT",SMFIP_NR_RCPT);
|
||||
setitem(d,"P_NODATA",SMFIP_NR_DATA);
|
||||
setitem(d,"P_NOUNKNOWN",SMFIP_NR_UNKN);
|
||||
setitem(d,"P_NOEOH",SMFIP_NR_EOH);
|
||||
setitem(d,"P_NOBODY",SMFIP_NR_BODY);
|
||||
setitem(d,"P_NOHDRS",SMFIP_NR_HDR);
|
||||
setitem(d,"P_HDR_LEADSPC",SMFIP_HDR_LEADSPC);
|
||||
setitem(d,"P_SKIP",SMFIP_SKIP);
|
||||
setitem(d,"ALL_OPTS",SMFIF_ALL_OPTS);
|
||||
setitem(d,"SKIP",SMFIS_SKIP);
|
||||
setitem(d,"NOREPLY",SMFIS_NOREPLY);
|
||||
#endif
|
||||
setitem(d,"CONTINUE", SMFIS_CONTINUE);
|
||||
setitem(d,"REJECT", SMFIS_REJECT);
|
||||
|
||||
Reference in New Issue
Block a user