Skip to content
Snippets Groups Projects
Commit 60963b3c authored by Stuart Gathman's avatar Stuart Gathman
Browse files

Streamline negotiate

parent 6221f8b7
No related branches found
No related tags found
No related merge requests found
...@@ -4,14 +4,14 @@ ...@@ -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(): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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.
......
...@@ -12,7 +12,7 @@ ...@@ -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 ...@@ -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
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment