Compare commits

..

1 Commits

Author SHA1 Message Date
cvs2svn fd530ebf86 This commit was manufactured by cvs2svn to create tag 'pymilter-0_9_0'.
Sprout from master 2008-12-14 03:01:43 UTC Stuart Gathman <stuart@gathman.org> 'Release 0.9.0'
Cherrypick from bmsi 2005-05-31 18:23:49 UTC Stuart Gathman <stuart@gathman.org> 'Development changes since 0.7.2':
    sample.py
    test/amazon
    test/big5
    test/bounce
    test/bounce1
    test/bound
    test/honey
    test/missingboundary
    test/samp1
    test/spam44
    test/spam7
    test/spam8
    test/test1
    test/test8
    test/virus1
    test/virus13
    test/virus2
    test/virus3
    test/virus4
    test/virus5
    test/virus6
    test/virus7
    testsample.py
2008-12-14 03:01:44 +00:00
7 changed files with 126 additions and 502 deletions
-4
View File
@@ -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
+61 -184
View File
@@ -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,29 +96,54 @@ 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 = factory()
m._setctx(ctx) 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._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: rc = m.close()
rc = m.close() m._setctx(None) # release milterContext
finally:
m._setctx(None) # release milterContext
return rc return rc
def dictfromlist(args): def dictfromlist(args):
@@ -313,7 +194,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 +207,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
View File
@@ -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
+14 -222
View File
@@ -35,30 +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
* Fedora release
*
* Revision 1.15 2008/12/13 20:29:56 customdesigned
* Split off milter applications.
*
* Revision 1.14 2008/12/04 19:43:00 customdesigned * Revision 1.14 2008/12/04 19:43:00 customdesigned
* Doc updates. * Doc updates.
* *
@@ -399,7 +375,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 +777,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,61 +808,15 @@ 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;
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");
}
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 NULL;
return _generic_return(smfi_opensocket(rmsock), "cannot opensocket"); return _generic_return(smfi_register(description), "cannot register");
} }
static char milter_main__doc__[] = static char milter_main__doc__[] =
@@ -1245,7 +1094,7 @@ milter_addrcpt(PyObject *self, PyObject *args) {
PyThreadState *t; PyThreadState *t;
int rc; int rc;
if (!PyArg_ParseTuple(args, "s|z:addrcpt", &rcpt, &params)) return NULL; if (!PyArg_ParseTuple(args, "s|z:addrcpt", &rcpt)) return NULL;
ctx = _find_context(self); ctx = _find_context(self);
if (ctx == NULL) return NULL; if (ctx == NULL) return NULL;
t = PyEval_SaveThread(); t = PyEval_SaveThread();
@@ -1275,7 +1124,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__[] =
@@ -1296,8 +1146,8 @@ milter_replacebody(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_replacebody(ctx, return _thread_return(t,smfi_replacebody(ctx, bodyp, bodylen),
(unsigned char *)bodyp, bodylen), "cannot replace message body"); "cannot replace message body");
} }
static char milter_setpriv__doc__[] = static char milter_setpriv__doc__[] =
@@ -1382,27 +1232,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 +1250,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 +1272,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 +1287,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 +1364,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);
+39 -74
View File
@@ -1,32 +1,23 @@
# EL 3,4,5 supported, set to 0 for Fedora
%define el4 1
%define dist .el4
%if 0%{?el3} || 0%{?el4}
%define __python python2.4 %define __python python2.4
%endif %define version 0.9.0
%define release 1.el4
%define libdir %{_libdir}/pymilter %define libdir %{_libdir}/pymilter
%{!?python_sitearch: %define python_sitearch %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib(1)")} %define name pymilter
%define pythonbase %(basename %{__python}) %define redhat7 0
Summary: Python interface to sendmail milter API Summary: Python interface to sendmail milter API
Name: pymilter Name: %{name}
Version: 0.9.2 Version: %{version}
Release: 3%{dist} Release: %{release}
Source: http://downloads.sourceforge.net/pymilter/%{name}-%{version}.tar.gz Source: %{name}-%{version}.tar.gz
#Patch: %{name}-%{version}.patch
License: GPLv2+ License: GPLv2+
Group: Development/Libraries Group: Development/Libraries
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root BuildRoot: %{_tmppath}/%{name}-buildroot
Vendor: Stuart D. Gathman <stuart@bmsi.com>
Url: http://www.bmsi.com/python/milter.html Url: http://www.bmsi.com/python/milter.html
Requires: %{pythonbase} >= 2.4, sendmail >= 8.13 Requires: %{__python} >= 2.4, sendmail >= 8.13
%if 0%{?el3} || 0%{?el4} BuildRequires: %{__python}-devel >= 2.4, sendmail-devel >= 8.13
# Need python2.4 specific pydns, not the version for system python
Requires: pydns
%else
# Needed for callbacks, not a core function but highly useful for milters
Requires: python-pydns
%endif
BuildRequires: ed, %{pythonbase}-devel >= 2.4, sendmail-devel >= 8.13
%description %description
This is a python extension module to enable python scripts to This is a python extension module to enable python scripts to
@@ -36,30 +27,30 @@ DSNs, and doing CBV.
%prep %prep
%setup -q %setup -q
#patch -p0 -b .bms
%build %build
env CFLAGS="$RPM_OPT_FLAGS" %{__python} setup.py build %if %{redhat7}
LDFLAGS="-s"
%else # Redhat builds debug packages after 7.3
LDFLAGS="-g"
%endif
env CFLAGS="$RPM_OPT_FLAGS" LDFLAGS="$LDFLAGS" %{__python} setup.py build
%install %install
rm -rf $RPM_BUILD_ROOT rm -rf $RPM_BUILD_ROOT
%{__python} setup.py install --root=$RPM_BUILD_ROOT %{__python} setup.py install --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES
mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/run/milter mkdir -p $RPM_BUILD_ROOT/var/run/milter
mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/log/milter
mkdir -p $RPM_BUILD_ROOT%{libdir} mkdir -p $RPM_BUILD_ROOT%{libdir}
%ifos aix4.1
cat >$RPM_BUILD_ROOT%{libdir}/start.sh <<'EOF'
#!/bin/sh
cd /var/log/milter
exec /usr/local/bin/python bms.py >>milter.log 2>&1
EOF
%else # not aix4.1
cp start.sh $RPM_BUILD_ROOT%{libdir} cp start.sh $RPM_BUILD_ROOT%{libdir}
ed $RPM_BUILD_ROOT%{libdir}/start.sh <<'EOF' ed $RPM_BUILD_ROOT%{libdir}/start.sh <<'EOF'
/^datadir=/
c
datadir="%{_localstatedir}/log/milter"
.
/^piddir=/
c
piddir="%{_localstatedir}/run/milter"
.
/^libdir=/
c
libdir="%{libdir}"
.
/^python=/ /^python=/
c c
python="%{__python}" python="%{__python}"
@@ -67,69 +58,43 @@ python="%{__python}"
w w
q q
EOF EOF
%endif
chmod a+x $RPM_BUILD_ROOT%{libdir}/start.sh chmod a+x $RPM_BUILD_ROOT%{libdir}/start.sh
%if !%{redhat7}
#grep '.pyc$' INSTALLED_FILES | sed -e 's/c$/o/' >>INSTALLED_FILES
%endif
# 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 running on redhat
%files %files -f INSTALLED_FILES
%defattr(-,root,root,-) %defattr(-,root,root)
%doc README ChangeLog NEWS TODO CREDITS sample.py milter-template.py %doc README ChangeLog NEWS TODO CREDITS sample.py milter-template.py
%{python_sitearch}/* %config %{libdir}/start.sh
%{libdir} %dir %attr(0755,mail,mail) /var/run/milter
%dir %attr(0755,mail,mail) %{_localstatedir}/run/milter
%dir %attr(0755,mail,mail) %{_localstatedir}/log/milter
%clean %clean
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
- Stop using INSTALLED_FILES to make Fedora happy
- Remove config flag from start.sh glue
- Own /var/log/milter
- Use _localstatedir
* Wed Jan 07 2009 Stuart Gathman <stuart@bmsi.com> 0.9.0-2
- Changes to meet Fedora standards
* Mon Nov 24 2008 Stuart Gathman <stuart@bmsi.com> 0.9.0-1 * Mon Nov 24 2008 Stuart Gathman <stuart@bmsi.com> 0.9.0-1
- Split pymilter into its own CVS module - Split pymilter into its own CVS module
- Support chgfrom and addrcpt_par - Support chgfrom and addrcpt_par
- Support NS records in Milter.dns - Support NS records in Milter.dns
* Mon Aug 25 2008 Stuart Gathman <stuart@bmsi.com> 0.8.10-2 * Mon Aug 25 2008 Stuart Gathman <stuart@bmsi.com> 0.8.10-2
- /var/run/milter directory must be owned by mail - /var/run/milter directory must be owned by mail
* Mon Aug 25 2008 Stuart Gathman <stuart@bmsi.com> 0.8.10-1 * Mon Aug 25 2008 Stuart Gathman <stuart@bmsi.com> 0.8.10-1
- improved parsing into email and fullname (still 2 self test failures) - improved parsing into email and fullname (still 2 self test failures)
- implement no-DSN CBV, reduce full DSNs - implement no-DSN CBV, reduce full DSNs
* Mon Sep 24 2007 Stuart Gathman <stuart@bmsi.com> 0.8.9-1 * Mon Sep 24 2007 Stuart Gathman <stuart@bmsi.com> 0.8.9-1
- Use ifarch hack to build milter and milter-spf packages as noarch - Use ifarch hack to build milter and milter-spf packages as noarch
- Remove spf dependency from dsn.py, add dns.py - Remove spf dependency from dsn.py, add dns.py
* Fri Jan 05 2007 Stuart Gathman <stuart@bmsi.com> 0.8.8-1 * Fri Jan 05 2007 Stuart Gathman <stuart@bmsi.com> 0.8.8-1
- move AddrCache, parse_addr, iniplist to Milter package - move AddrCache, parse_addr, iniplist to Milter package
- move parse_header to Milter.utils - move parse_header to Milter.utils
- fix plock for missing source and can't change owner/group - fix plock for missing source and can't change owner/group
- split out pymilter and pymilter-spf packages - split out pymilter and pymilter-spf packages
- move milter apps to /usr/lib/pymilter - move milter apps to /usr/lib/pymilter
* Sat Nov 04 2006 Stuart Gathman <stuart@bmsi.com> 0.8.7-1 * Sat Nov 04 2006 Stuart Gathman <stuart@bmsi.com> 0.8.7-1
- SPF moved to pyspf RPM - SPF moved to pyspf RPM
* Tue May 23 2006 Stuart Gathman <stuart@bmsi.com> 0.8.6-2 * Tue May 23 2006 Stuart Gathman <stuart@bmsi.com> 0.8.6-2
- Support CBV timeout - Support CBV timeout
+4 -5
View File
@@ -2,11 +2,10 @@ import os
import sys import sys
from distutils.core import setup, Extension from distutils.core import setup, Extension
# FIXME: on some versions of sendmail, smutil is renamed to sm. # FIXME: on some versions of sendmail, smutil is renamed to sm
# On slackware and debian, leave it out entirely. It depends # on slackware and debian, leave it out entirely. It depends
# on how libmilter was built by the sendmail package. # on how libmilter was built by the sendmail package.
#libs = ["milter", "smutil"] libs = ["milter", "smutil"]
libs = ["milter"]
libdirs = ["/usr/lib/libmilter"] # needed for Debian libdirs = ["/usr/lib/libmilter"] # needed for Debian
# patch distutils if it can't cope with the "classifiers" or # patch distutils if it can't cope with the "classifiers" or
@@ -17,7 +16,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.0',
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
+3 -5
View File
@@ -1,16 +1,14 @@
#!/bin/sh #!/bin/sh
appname="$1" appname="$1"
script="${2:-${appname}}" script="${2:-${appname}}"
datadir="/var/log/milter" datadir=/var/log/milter
piddir="/var/run/milter"
libdir="/usr/lib/pymilter"
python="python2.4" python="python2.4"
exec >>${datadir}/${appname}.log 2>&1 exec >>${datadir}/${appname}.log 2>&1
if test -s ${datadir}/${script}.py; then if test -s ${datadir}/${script}.py; then
cd ${datadir} # use version in log dir if it exists for debugging cd ${datadir} # use version in log dir if it exists for debugging
else else
cd ${libdir} cd /usr/lib/pymilter
fi fi
${python} ${script}.py & ${python} ${script}.py &
echo $! >${piddir}/${appname}.pid echo $! >/var/run/milter/${appname}.pid