Streamline negotiate
This commit is contained in:
+59
-51
@@ -4,14 +4,14 @@
|
|||||||
|
|
||||||
# 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 *
|
||||||
|
|
||||||
__version__ = '0.9.2'
|
|
||||||
|
|
||||||
_seq_lock = thread.allocate_lock()
|
_seq_lock = thread.allocate_lock()
|
||||||
_seq = 0
|
_seq = 0
|
||||||
|
|
||||||
@@ -24,19 +24,32 @@ def uniqueID():
|
|||||||
_seq_lock.release()
|
_seq_lock.release()
|
||||||
return seqno
|
return seqno
|
||||||
|
|
||||||
OPTIONAL_CALLBACKS = frozenset((
|
OPTIONAL_CALLBACKS = {
|
||||||
'connect','hello','envfrom','envrcpt','data','header','eoh','body','unknown'))
|
'connect':(P_NR_CONN,P_NOCONNECT),
|
||||||
|
'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):
|
def nocallback(func):
|
||||||
if not func.__name__ in OPTIONAL_CALLBACKS:
|
try:
|
||||||
|
func.milter_protocol = OPTIONAL_CALLBACKS[func.__name__][1]
|
||||||
|
except KeyError:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
'@nocallback applied to non-optional method: '+func.__name__)
|
'@nocallback applied to non-optional method: '+func.__name__)
|
||||||
func.milter_protocol = 'NO'
|
|
||||||
return func
|
return func
|
||||||
|
|
||||||
def noreply(func):
|
def noreply(func):
|
||||||
if not func.__name__ in OPTIONAL_CALLBACKS:
|
try:
|
||||||
|
func.milter_protocol = OPTIONAL_CALLBACKS[func.__name__][0]
|
||||||
|
except KeyErro:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
'@noreply applied to non-optional method: '+func.__name__)
|
'@noreply applied to non-optional method: '+func.__name__)
|
||||||
func.milter_protocol = 'NR'
|
|
||||||
return func
|
return func
|
||||||
|
|
||||||
class DisabledAction(RuntimeError):
|
class DisabledAction(RuntimeError):
|
||||||
@@ -49,8 +62,8 @@ class Base(object):
|
|||||||
"The core class interface to the milter module."
|
"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._actions = CURR_ACTS # all actions enabled by default
|
||||||
if ctx:
|
if ctx:
|
||||||
ctx.setpriv(self)
|
ctx.setpriv(self)
|
||||||
def log(self,*msg): pass
|
def log(self,*msg): pass
|
||||||
@@ -76,32 +89,27 @@ class Base(object):
|
|||||||
def abort(self): return CONTINUE
|
def abort(self): return CONTINUE
|
||||||
def close(self): return CONTINUE
|
def close(self): return CONTINUE
|
||||||
|
|
||||||
|
# Return mask of SMFIP_N.. protocol 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
|
# Default negotiation sets P_NO* and P_NR* for callbacks
|
||||||
# marked @nocallback and @noreply respectively
|
# marked @nocallback and @noreply respectively
|
||||||
def negotiate(self,opts):
|
def negotiate(self,opts):
|
||||||
try:
|
try:
|
||||||
self.__actions,p,f1,f2 = opts
|
self._actions,p,f1,f2 = opts
|
||||||
for func,nr,nc in (
|
opts[1] = p & ~self.protocol_mask() & ~P_RCPT_REJ & ~P_HDR_LEADSPC
|
||||||
(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:
|
|
||||||
# self.log(func.__name__,'NOREPLY')
|
|
||||||
if ca != 'NO':
|
|
||||||
p &= ~nc
|
|
||||||
#elif p & nc:
|
|
||||||
# self.log(func.__name__,'NOCALLBACK')
|
|
||||||
opts[1] = p & ~P_RCPT_REJ & ~P_HDR_LEADSPC
|
|
||||||
opts[2] = 0
|
opts[2] = 0
|
||||||
opts[3] = 0
|
opts[3] = 0
|
||||||
self.log("Negotiated:",opts)
|
self.log("Negotiated:",opts)
|
||||||
@@ -112,53 +120,53 @@ class Base(object):
|
|||||||
|
|
||||||
# Milter methods which can be invoked from most callbacks
|
# Milter methods which can be invoked from most callbacks
|
||||||
def getsymval(self,sym):
|
def getsymval(self,sym):
|
||||||
return self.__ctx.getsymval(sym)
|
return self._ctx.getsymval(sym)
|
||||||
|
|
||||||
# If sendmail does not support setmlreply, then only the
|
# If sendmail does not support setmlreply, then only the
|
||||||
# first msg line is used.
|
# first msg line is used.
|
||||||
def setreply(self,rcode,xcode=None,msg=None,*ml):
|
def setreply(self,rcode,xcode=None,msg=None,*ml):
|
||||||
return self.__ctx.setreply(rcode,xcode,msg,*ml)
|
return self._ctx.setreply(rcode,xcode,msg,*ml)
|
||||||
|
|
||||||
# may only be called from negotiate callback
|
# may only be called from negotiate callback
|
||||||
def setsmlist(self,stage,macros):
|
def setsmlist(self,stage,macros):
|
||||||
if not self.__actions & SETSMLIST: raise DisabledAction("SETSMLIST")
|
if not self._actions & SETSMLIST: raise DisabledAction("SETSMLIST")
|
||||||
if type(macros) in (list,tuple):
|
if type(macros) in (list,tuple):
|
||||||
macros = ' '.join(macros)
|
macros = ' '.join(macros)
|
||||||
return self.__ctx.setsmlist(stage,macros)
|
return self._ctx.setsmlist(stage,macros)
|
||||||
|
|
||||||
# Milter methods which can only be called from eom callback.
|
# Milter methods which can only be called from eom callback.
|
||||||
def addheader(self,field,value,idx=-1):
|
def addheader(self,field,value,idx=-1):
|
||||||
if not self.__actions & ADDHDRS: raise DisabledAction("ADDHDRS")
|
if not self._actions & ADDHDRS: raise DisabledAction("ADDHDRS")
|
||||||
return self.__ctx.addheader(field,value,idx)
|
return self._ctx.addheader(field,value,idx)
|
||||||
|
|
||||||
def chgheader(self,field,idx,value):
|
def chgheader(self,field,idx,value):
|
||||||
if not self.__actions & CHGHDRS: raise DisabledAction("CHGHDRS")
|
if not self._actions & CHGHDRS: raise DisabledAction("CHGHDRS")
|
||||||
return self.__ctx.chgheader(field,idx,value)
|
return self._ctx.chgheader(field,idx,value)
|
||||||
|
|
||||||
def addrcpt(self,rcpt,params=None):
|
def addrcpt(self,rcpt,params=None):
|
||||||
if not self.__actions & ADDRCPT: raise DisabledAction("ADDRCPT")
|
if not self._actions & ADDRCPT: raise DisabledAction("ADDRCPT")
|
||||||
return self.__ctx.addrcpt(rcpt,params)
|
return self._ctx.addrcpt(rcpt,params)
|
||||||
|
|
||||||
def delrcpt(self,rcpt):
|
def delrcpt(self,rcpt):
|
||||||
if not self.__actions & DELRCPT: raise DisabledAction("DELRCPT")
|
if not self._actions & DELRCPT: raise DisabledAction("DELRCPT")
|
||||||
return self.__ctx.delrcpt(rcpt)
|
return self._ctx.delrcpt(rcpt)
|
||||||
|
|
||||||
def replacebody(self,body):
|
def replacebody(self,body):
|
||||||
if not self.__actions & MODBODY: raise DisabledAction("MODBODY")
|
if not self._actions & MODBODY: raise DisabledAction("MODBODY")
|
||||||
return self.__ctx.replacebody(body)
|
return self._ctx.replacebody(body)
|
||||||
|
|
||||||
def chgfrom(self,sender,params=None):
|
def chgfrom(self,sender,params=None):
|
||||||
if not self.__actions & CHGFROM: raise DisabledAction("CHGFROM")
|
if not self._actions & CHGFROM: raise DisabledAction("CHGFROM")
|
||||||
return self.__ctx.chgfrom(sender,params)
|
return self._ctx.chgfrom(sender,params)
|
||||||
|
|
||||||
# When quarantined, a message goes into the mailq as if to be delivered,
|
# When quarantined, a message goes into the mailq as if to be delivered,
|
||||||
# but delivery is deferred until the message is unquarantined.
|
# but delivery is deferred until the message is unquarantined.
|
||||||
def quarantine(self,reason):
|
def quarantine(self,reason):
|
||||||
if not self.__actions & QUARANTINE: raise DisabledAction("QUARANTINE")
|
if not self._actions & QUARANTINE: raise DisabledAction("QUARANTINE")
|
||||||
return self.__ctx.quarantine(reason)
|
return self._ctx.quarantine(reason)
|
||||||
|
|
||||||
def progress(self):
|
def progress(self):
|
||||||
return self.__ctx.progress()
|
return self._ctx.progress()
|
||||||
|
|
||||||
# A logging but otherwise do nothing Milter base class included
|
# A logging but otherwise do nothing Milter base class included
|
||||||
# for compatibility with previous versions of pymilter.
|
# for compatibility with previous versions of pymilter.
|
||||||
|
|||||||
+2
-1
@@ -12,7 +12,7 @@
|
|||||||
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.2
|
||||||
Release: 1%{dist}
|
Release: 2%{dist}
|
||||||
Source: http://downloads.sourceforge.net/pymilter/%{name}-%{version}.tar.gz
|
Source: http://downloads.sourceforge.net/pymilter/%{name}-%{version}.tar.gz
|
||||||
License: GPLv2+
|
License: GPLv2+
|
||||||
Group: Development/Libraries
|
Group: Development/Libraries
|
||||||
@@ -85,6 +85,7 @@ rm -rf $RPM_BUILD_ROOT
|
|||||||
%changelog
|
%changelog
|
||||||
* Thu May 28 2009 Stuart Gathman <stuart@bmsi.com> 0.9.2-1
|
* Thu May 28 2009 Stuart Gathman <stuart@bmsi.com> 0.9.2-1
|
||||||
- Add new callback support: data,negotiate,unknown
|
- Add new callback support: data,negotiate,unknown
|
||||||
|
- Auto-negotiate protocol steps
|
||||||
|
|
||||||
* Thu Feb 05 2009 Stuart Gathman <stuart@bmsi.com> 0.9.1-1
|
* Thu Feb 05 2009 Stuart Gathman <stuart@bmsi.com> 0.9.1-1
|
||||||
- Fix missing address of optional param to addrcpt
|
- Fix missing address of optional param to addrcpt
|
||||||
|
|||||||
Reference in New Issue
Block a user