Compare commits

...

13 Commits

Author SHA1 Message Date
Stuart D. Gathman d30918aca7 Fix python3 unit tests 2018-12-23 23:38:15 -05:00
Stuart D. Gathman 4e8fda517c Fix getsymval for python3 2018-12-23 23:16:42 -05:00
Stuart D. Gathman 2194d8fd96 setsymlist not called yet in TestMilter 2018-12-23 23:03:46 -05:00
Stuart D. Gathman 52e1b4ae32 Move sys to top of mime.py 2018-12-23 22:31:20 -05:00
Stuart D. Gathman 55e5378659 Move py3 sgmllib to an internal module - Scott Kitterman 2018-12-23 20:29:01 -05:00
Stuart D. Gathman f9b2241ec6 Improve readability of module init, setitem. Move old changes to ChangeLog 2018-12-23 20:00:24 -05:00
Stuart D. Gathman 62783fbbfd Convert python3 patch for miltermodule.c to #ifdef - Scott Kitterman 2018-12-23 19:46:59 -05:00
Stuart D. Gathman d521665f75 Working testctx, still need to redirect logging in testsample.py 2018-08-18 17:49:37 -04:00
Stuart D. Gathman 55eb05e526 Change deprecated failUnless to assertTrue 2018-08-05 00:34:06 -04:00
Stuart D. Gathman fc008f6db0 Update documentation 2018-08-04 14:09:23 -04:00
Stuart D. Gathman 96cd9ac263 Request compile error on implicit function declarations to save lots
of debugging time.
2018-01-09 21:52:39 -05:00
jcea 10471faa7d Correctly support IPv6 in Solaris (#17)
I can't see how adding one more OS to the manual list can hurt for now, so in goes the PR.  Thanks.
2018-01-09 14:46:11 -05:00
Stuart D. Gathman 36750bac78 Fix some python3 syntax errors 2017-04-20 17:00:14 -04:00
15 changed files with 691 additions and 266 deletions
+210
View File
@@ -1,3 +1,213 @@
# Revision 1.35 2013/03/14 22:11:25 customdesigned
# Release 0.9.8
#
# Revision 1.34 2013/03/09 05:42:14 customdesigned
# Make TestBase members private, fix getsymlist misspelling.
#
# Revision 1.33 2013/03/09 00:25:23 customdesigned
# Better untrapped exception message. const char for doc comments.
#
# Revision 1.32 2013/01/13 01:46:16 customdesigned
# Doc updates.
#
# Revision 1.31 2012/04/12 23:32:50 customdesigned
# Replace redundant callback array with macros. If this doesn't break anything,
# macros can be eliminated with code changes.
#
# Revision 1.30 2012/04/12 23:08:06 customdesigned
# Support RFC2553 on BSD
#
# Revision 1.29 2011/06/09 15:45:27 customdesigned
# Print callback name for non-int return error.
#
# Revision 1.28 2011/06/08 23:13:48 customdesigned
# Generate special exception when callback return not int.
#
# Revision 1.27 2009/07/28 21:45:54 customdesigned
# Add getversion() to return runtime version.
#
# Revision 1.26 2009/07/28 21:08:20 customdesigned
# Increment del count.
#
# Revision 1.25 2009/07/28 20:58:55 customdesigned
# getdiag method
#
# Revision 1.24 2009/06/09 01:54:44 customdesigned
# Forgot to initialize optional parameter.
#
# Revision 1.23 2009/05/29 20:44:58 customdesigned
# Typo SMFIP_NO constants.
#
# 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
# Doc updates.
#
# Revision 1.13 2008/11/23 03:06:47 customdesigned
# Milter support for chgfrom.
#
# Revision 1.12 2008/11/21 20:42:52 customdesigned
# Support smfi_chgfrom and smfi_addrcpt_par.
#
# Revision 1.11 2007/09/25 02:26:29 customdesigned
# Update license.
#
# Revision 1.10 2006/02/12 02:00:42 customdesigned
# Resolve FIXME for wrap_close.
#
# Revision 1.9 2005/12/23 21:46:36 customdesigned
# Compile on sendmail-8.12 (ifdef SMFIR_INSHEADER)
#
# Revision 1.8 2005/10/20 23:23:36 customdesigned
# Include smfi_progress is SMFIR_PROGRESS defined
#
# Revision 1.7 2005/10/20 23:04:46 customdesigned
# Add optional idx for position of added header.
#
# Revision 1.6 2005/07/15 22:18:17 customdesigned
# Support callback exception policy
#
# Revision 1.5 2005/06/24 04:20:07 customdesigned
# Report context allocation error.
#
# Revision 1.4 2005/06/24 04:12:43 customdesigned
# Remove unused name argument to generic wrappers.
#
# Revision 1.3 2005/06/24 03:57:35 customdesigned
# Handle close called before connect.
#
# Revision 1.2 2005/06/02 04:18:55 customdesigned
# Update copyright notices after reading article on /.
#
# Revision 1.1.1.2 2005/05/31 18:09:06 customdesigned
# Release 0.7.1
#
# Revision 2.31 2004/08/23 02:24:36 stuart
# Support setbacklog
#
# Revision 2.30 2004/08/21 20:29:53 stuart
# Support option of 11 lines max for mlreply.
#
# Revision 2.29 2004/08/21 04:14:29 stuart
# mlreply support
#
# Revision 2.28 2004/08/21 02:45:21 stuart
# Don't leak int constants if module unloaded.
#
# Revision 2.27 2004/04/06 03:19:59 stuart
# Release 0.6.8
#
# Revision 2.26 2004/03/04 21:43:06 stuart
# Fix memory leak by removing unused dynamic template buffer,
# thanks again to Alexander Kourakos.
#
# Revision 2.25 2004/03/01 19:45:03 stuart
# Release 0.6.5
#
# Revision 2.24 2004/03/01 18:56:50 stuart
# Support progress reporting.
#
# Revision 2.23 2004/03/01 18:36:09 stuart
# Plug memory leak. Thanks to Alexander Kourakos.
#
# Revision 2.22 2003/11/02 03:01:46 stuart
# Adjust SMTP error codes after careful reading of standard.
#
# Revision 2.21 2003/06/24 19:57:04 stuart
# Allow removing a python milter callback by setting to None.
#
# Revision 2.20 2003/02/13 17:08:57 stuart
# IPV6 support
#
# Revision 2.19 2003/02/13 16:58:29 stuart
# Support passing None to setreply and chgheader.
#
# Revision 2.18 2002/12/11 16:44:06 stuart
# Support QUARANTINE if supported by libmilter.
#
# Revision 2.17 2002/04/18 20:20:35 stuart
# Fix for NULL hostaddr in connect callback from Jason Erickson.
#
# Revision 2.16 2001/09/26 13:29:09 stuart
# sa_len not supported by linux.
#
# Revision 2.15 2001/09/25 17:28:40 stuart
# Copyrights, documentation, release 0.3.1
#
# Revision 2.14 2001/09/25 00:36:57 stuart
# Pass hostaddr to python code in format used by standard socket module.
#
# Revision 2.13 2001/09/24 23:44:55 stuart
# Return old callback from setcallback functions.
#
# Revision 2.12 2001/09/24 20:02:30 stuart
# Remove redundant setpriv
#
# Revision 2.11 2001/09/23 22:26:35 stuart
# Update docs. Streamline Milter.py
# update testbms.py to reflect actual sendmail behaviour with multiple
# messages per connection.
#
# Revision 2.10 2001/09/22 15:33:42 stuart
# More doc comment updates.
#
# Revision 2.9 2001/09/22 14:52:27 stuart
# Actually return retval in _generic_return.
# Go over doc comments.
#
# Revision 2.8 2001/09/22 01:59:32 stuart
# Prevent reentrant call of milter_main, which libmilter doesn't support.
#
# Revision 2.7 2001/09/22 01:47:37 stuart
# Forgot to set milter interp.
#
# Revision 2.6 2001/09/22 01:23:53 stuart
# Added proper threading after research in python docs.
#
# Revision 2.5 2001/09/21 20:08:51 stuart
# Release 0.2.3
#
# Revision 2.4 2001/09/20 16:18:16 stuart
# libmilter checks in_eom state, so we don't have to.
#
# Revision 2.3 2001/09/19 06:02:33 stuart
# Make more stuff static.
#
# Revision 2.1 2001/09/19 04:24:13 stuart
# Use extension type to track context in python.
#
# Revision 1.4 2001/09/18 18:48:28 stuart
# clear private data reference in _clear_context
#
# Revision 1.3 2001/09/15 04:19:37 stuart
# nasty off by 1 mem overwrite bugs in wrap_env
# generic_set_callback
#
# Revision 1.2 2001/09/15 03:15:39 stuart
# several bugs fixed, works smoothly
#
# Revision 1.69 2006/11/04 22:09:39 customdesigned # Revision 1.69 2006/11/04 22:09:39 customdesigned
# Another lame DSN heuristic. Block PTR cache poisoning attack. # Another lame DSN heuristic. Block PTR cache poisoning attack.
# #
+1 -1
View File
@@ -31,7 +31,7 @@ PROJECT_NAME = pymilter
# This could be handy for archiving the generated documentation or # This could be handy for archiving the generated documentation or
# if some version control system is used. # if some version control system is used.
PROJECT_NUMBER = 1.0 PROJECT_NUMBER = 1.0.2
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put. # base path where the generated documentation will be put.
+3 -3
View File
@@ -9,7 +9,7 @@
# This code is under the GNU General Public License. See COPYING for details. # This code is under the GNU General Public License. See COPYING for details.
from __future__ import print_function from __future__ import print_function
__version__ = '1.0.2' __version__ = '1.0.3'
import os import os
import re import re
@@ -419,12 +419,12 @@ class Base(object):
func = getattr(self,func) func = getattr(self,func)
syms = getattr(func,'_symlist',None) syms = getattr(func,'_symlist',None)
if syms is not None: if syms is not None:
self.setsymlist(stage,syms) self.setsymlist(stage,*syms)
opts[1] = self._protocol = p & ~self.protocol_mask() opts[1] = self._protocol = p & ~self.protocol_mask()
opts[2] = 0 opts[2] = 0
opts[3] = 0 opts[3] = 0
#self.log("Negotiated:",opts) #self.log("Negotiated:",opts)
except: except Exception as x:
# don't change anything if something went wrong # don't change anything if something went wrong
return ALL_OPTS return ALL_OPTS
return CONTINUE return CONTINUE
+2 -2
View File
@@ -26,8 +26,8 @@ def DNSLookup(name, qtype):
# A RR as dotted quad. For consistency, this driver should # A RR as dotted quad. For consistency, this driver should
# return both as binary string. # return both as binary string.
return [((a['name'], a['typename']), a['data']) for a in resp.answers] return [((a['name'], a['typename']), a['data']) for a in resp.answers]
except IOError, x: except IOError as x:
raise DNSError, str(x) raise DNSError(str(x))
class Session(object): class Session(object):
"""A Session object has a simple cache with no TTL that is valid """A Session object has a simple cache with no TTL that is valid
+3 -3
View File
@@ -142,13 +142,13 @@ def send_dsn(mailfrom,receiver,msg=None,timeout=600,session=None,ourfrom=''):
if badrcpts: if badrcpts:
return badrcpts return badrcpts
return None # success return None # success
except smtplib.SMTPRecipientsRefused,x: except smtplib.SMTPRecipientsRefused as x:
if len(x.recipients) == 1: if len(x.recipients) == 1:
return x.recipients.values()[0] # permanent error return x.recipients.values()[0] # permanent error
return x.recipients return x.recipients
except smtplib.SMTPSenderRefused,x: except smtplib.SMTPSenderRefused as x:
return x.args[:2] # does not accept DSN return x.args[:2] # does not accept DSN
except smtplib.SMTPDataError,x: except smtplib.SMTPDataError as x:
return x.args # permanent error return x.args # permanent error
except smtplib.SMTPException: except smtplib.SMTPException:
pass # any other error, try next MX pass # any other error, try next MX
View File
+2
View File
@@ -14,6 +14,7 @@ Milter.NOREPLY = Milter.CONTINUE
## Test mixin for unit testing %milter applications. ## Test mixin for unit testing %milter applications.
# This mixin overrides many Milter.MilterBase methods # This mixin overrides many Milter.MilterBase methods
# with stub versions that simply record what was done. # with stub versions that simply record what was done.
# @deprecated Use Milter.test.TestCtx
# @since 0.9.8 # @since 0.9.8
class TestBase(object): class TestBase(object):
@@ -138,6 +139,7 @@ class TestBase(object):
raise ValueError('setsymlist limited to 5 macros by MTA') raise ValueError('setsymlist limited to 5 macros by MTA')
if self._symlist[stage] is not None: if self._symlist[stage] is not None:
raise ValueError('setsymlist already called for stage:'+stage) raise ValueError('setsymlist already called for stage:'+stage)
print('setsymlist',stage,a)
self._symlist[stage] = set(a) self._symlist[stage] = set(a)
## Feed a file like object to the %milter. Calls envfrom, envrcpt for ## Feed a file like object to the %milter. Calls envfrom, envrcpt for
+297
View File
@@ -0,0 +1,297 @@
## @package Milter.testctx
# A test framework for milters that replaces milterContext rather
# than Milter.Base. Since miltermodule.c doesn't currently export
# a way to query callbacks set (and we might want to run without
# loading milter), we assume the callbacks set by Milter.runmilter().
from __future__ import print_function
from socket import AF_INET,AF_INET6
import time
import mime
try:
from io import BytesIO
except:
from StringIO import StringIO as BytesIO
import Milter
from Milter import utils
import mime
## Milter context for unit testing %milter applications.
# A substitute for milter.milterContext that can be passed to
# Milter.Base._setctx().
# @since 1.0.3
class TestCtx(object):
default_opts = [Milter.CURR_ACTS,0x1fffff,0,0]
def __init__(self,logfile='test/milter.log'):
## Usually the Milter application derived from Milter.Base
self._priv = None
## List of recipients deleted
self._delrcpt = []
## List of recipients added
self._addrcpt = []
## Macros defined
self._macros = { }
## Reply codes and messages set by the %milter
self._reply = None
## The macros returned by protocol stage
self._symlist = [ None, None, None, None, None, None, None ]
## The message body.
self._body = None
## True if the %milter replaced the message body.
self._bodyreplaced = False
## True if the %milter changed any headers.
self._headerschanged = False
## The rfc822 message object for the current email being fed to the %milter.
self._msg = None
## The MAIL FROM for the current email being fed to the %milter
self._sender = None
## True if the %milter changed the envelope from.
self._envfromchanged = False
## List of recipients added
self._addrcpt = []
## Negotiated options
self._opts = TestCtx.default_opts
## Last activity
self._activity = time.time()
def getpriv(self):
return self._priv
def setpriv(self,priv):
self._priv = priv
def getsymval(self,name):
stage = self._stage
if stage >= 0:
try:
s = name.encode('utf8')
except: pass
syms = self._symlist[stage]
if syms is not None and s not in syms:
return None
return self._macros.get(name,None)
def _setsymval(self,name,val):
self._macros[name] = val
def setreply(self,rcode,xcode,*msg):
self._reply = (rcode,xcode) + msg
def setsymlist(self,stage,macros):
if self._stage != -1:
raise RuntimeError("setsymlist may only be called from negotiate")
# Records which macros are available to getsymval()
m = macros
try:
m = m.encode('utf8')
except: pass
try:
m = m.split(b' ')
except: pass
if len(m) > 5:
raise ValueError('setsymlist limited to 5 macros by MTA')
if self._symlist[stage] is not None:
raise ValueError('setsymlist already called for stage:'+stage)
if not m:
raise ValueError('setsymlist with empty list for stage:'+stage)
self._symlist[stage] = set(m)
def addheader(self,field,value,idx):
if not self._body:
raise IOError("addheader not called from eom()")
self._msg[field] = value
self._headerschanged = True
def chgheader(self,field,idx,value):
if not self._body:
raise IOError("chgheader not called from eom()")
if value == '':
del self._msg[field]
else:
self._msg[field] = value
self._headerschanged = True
def addrcpt(self,rcpt,params):
if not self._body:
raise IOError("addrcpt not called from eom()")
self._addrcpt.append((rcpt,params))
def delrcpt(self,rcpt):
if not self._body:
raise IOError("delrcpt not called from eom()")
self._delrcpt.append(rcpt)
def replacebody(self,chunk):
if self._body:
self._body.write(chunk)
self._bodyreplaced = True
else:
raise IOError("replacebody not called from eom()")
def chgfrom(self,sender,params=None):
if not self._body:
raise IOError("chgfrom not called from eom()")
self._envfromchanged = True
self._sender = sender
def quarantine(self,reason):
raise NotImplemented
## Reset activity timer.
def progress(self):
self._activity = time.time()
def _abort(self):
"What Milter sets for abort_callback"
self._priv.abort()
self._close()
def _close(self):
Milter.close_callback(self)
def _negotiate(self):
self._body = None
self._bodyreplaced = False
self._priv = None
self._opts = TestCtx.default_opts
self._stage = -1
rc = Milter.negotiate_callback(self,self._opts)
if rc == Milter.ALL_OPTS:
self._opts = TestCtx.default_opts
elif rc != Milter.CONTINUE:
self._abort()
self._close()
self._protocol = self._opts[1]
return rc
def _connect(self,host='localhost',helo='spamrelay',ip='1.2.3.4'):
rc = self._negotiate()
# FIXME: what if not CONTINUE or ALL_OPTS?
if self._protocol & Milter.P_NOCONNECT:
return Milter.CONTINUE
if utils.ip4re.match(ip):
af = AF_INET
elif utils.ip6re.match(ip):
af = AF_INET6
else:
raise ValueError('TestCtx.connect: invalid ip address: '+ip)
self._stage = Milter.M_CONNECT
rc = Milter.connect_callback(self,host,af,ip)
self._stage = None
if rc != Milter.CONTINUE:
self._close()
return rc
return self._helo(helo)
def _helo(self,helo):
if self._protocol & Milter.P_NOHELO:
return Milter.CONTINUE
self._stage = Milter.M_HELO
rc = self._priv.hello(helo)
self._stage = None
if rc != Milter.CONTINUE:
self._close()
return rc
def _envfrom(self,*s):
self._sender = s[0]
if self._protocol & Milter.P_NOMAIL:
return Milter.CONTINUE
self._stage = Milter.M_ENVFROM
rc = self._priv.envfrom(*s)
self._stage = None
return rc
def _envrcpt(self,s):
if self._protocol & Milter.P_NORCPT:
return Milter.CONTINUE
self._stage = Milter.M_ENVRCPT
rc = self._priv.envrcpt(s)
self._stage = None
return rc
def _data(self):
if self._protocol & Milter.P_NODATA:
return Milter.CONTINUE
self._stage = Milter.M_DATA
rc = self._priv.data()
self._stage = None
return rc
def _header(self,fld,val):
return self._priv.header(fld,val)
def _eoh(self):
if self._protocol & Milter.P_NOEOH:
return Milter.CONTINUE
self._stage = Milter.M_EOH
rc = self._priv.eoh()
self._stage = None
return rc
def _feed_body(self,bfp):
if self._protocol & Milter.P_NOBODY:
return Milter.CONTINUE
while True:
buf = bfp.read(8192)
if len(buf) == 0: break
rc = self._priv.body(buf)
if rc != Milter.CONTINUE: return rc
return Milter.CONTINUE
def _eom(self):
self._body = BytesIO()
self._stage = Milter.M_EOM
rc = self._priv.eom()
self._stage = None
return rc
## Feed a file like object to the ctx. Calls the callbacks in
# the same sequence as libmilter.
# @param fp the file with rfc2822 message stream
# @param sender the MAIL FROM
# @param rcpt RCPT TO - additional recipients may follow
def _feedFile(self,fp,sender="spam@adv.com",rcpt="victim@lamb.com",*rcpts):
self._body = None
self._bodyreplaced = False
self._headerschanged = False
self._reply = None
msg = mime.message_from_file(fp)
self._msg = msg
# envfrom
rc = self._envfrom('<%s>'%sender)
if rc != Milter.CONTINUE: return rc
# envrcpt
for rcpt in (rcpt,) + rcpts:
rc = self._envrcpt('<%s>'%rcpt)
if rc != Milter.CONTINUE: return rc
# data
rc = self._data()
if rc != Milter.CONTINUE: return rc
# header
for h,val in msg.items():
rc = self._header(h,val)
if rc != Milter.CONTINUE: return rc
# eoh
rc = self._eoh()
if rc != Milter.CONTINUE: return rc
# body
header,body = msg.as_bytes().split(b'\n\n',1)
rc = self._feed_body(BytesIO(body))
if rc != Milter.CONTINUE: return rc
rc = self._eom()
if self._bodyreplaced:
body = self._body.getvalue()
self._body = BytesIO()
self._body.write(header)
self._body.write(b'\n\n')
self._body.write(body)
return rc
## Feed an email contained in a file to the %milter.
# This is a convenience method that invokes @link #feedFile feedFile @endlink.
# @param sender MAIL FROM
# @param rcpts RCPT TO, multiple recipients may be supplied
def _feedMsg(self,fname,sender="spam@adv.com",*rcpts):
with open('test/'+fname,'rb') as fp:
return self._feedFile(fp,sender,*rcpts)
+14 -4
View File
@@ -57,7 +57,9 @@
# #
# @section Useful python packages for milters # @section Useful python packages for milters
# #
# <a href="https://pypi.python.org/pypi/pyspf">pyspf</a> checks the # <a href="https://github.com/sdgathman/pymilter">pymilter</a> - this package.
#
# <a href="https://github.com/sdgathman/pyspf">pyspf</a> checks the
# SMTP envelope sender (MAIL FROM, passed to the Milter.Base.envfrom callback) # SMTP envelope sender (MAIL FROM, passed to the Milter.Base.envfrom callback)
# against a Sender Policy published in DNS by the sending domain. This # against a Sender Policy published in DNS by the sending domain. This
# can prevent forgery of the MAIL FROM. SPF is Sender Policy Framework. # can prevent forgery of the MAIL FROM. SPF is Sender Policy Framework.
@@ -71,18 +73,26 @@
# a standard place to summarize the results from DKIM, SPF, rDNS, SMTP AUTH, # a standard place to summarize the results from DKIM, SPF, rDNS, SMTP AUTH,
# and other email authentication methods. # and other email authentication methods.
# #
# <a href="https://pypi.python.org/pypi/pydspam/">pydspam</a> wraps # <a href="https://github.com/sdgathman/pydspam/">pydspam</a> wraps
# the libdspam API of the <a href="http://dspam.sourceforge.net/">DSPAM</a> # the libdspam API of the <a href="http://dspam.sourceforge.net/">DSPAM</a>
# project. # project.
# #
# <a href="https://github.com/sdgathman/pysrs/">pysrs</a> rewrites
# MAIL FROM to include a timestamped signature so that "bounce spam"
# can be immediately rejected.
#
# <a href="https://github.com/sdgathman/pygossip/">pygossip</a> is a
# system to track reputation by domain and authentication level and type,
# and a simple protocol to gossip about reputations with other mail servers.
#
# @section Milters written with pymilter # @section Milters written with pymilter
# #
# <a href="https://github.com/croessner/vrfydmn">Verify Domain</a> is a # <a href="https://github.com/croessner/vrfydmn">Verify Domain</a> is a
# Postfix milter that rejects/fixes manipulated From: header # Postfix milter that rejects/fixes manipulated From: header
# on a mail host with multiple virtual domains. # on a mail host with multiple virtual domains.
# #
# <a href="https://pypi.python.org/pypi/milter/">BMS Milter</a> has several # <a href="https://github.com/sdgathman/milter/">BMS Milter</a> has several
# milters, a big complicated spam filter that integrates multiple # milters, a big complicated spam filter that integrates multiple
# authentication protocols with pydpsm, and two simple ones: spfmilter.py and # authentication protocols with pydspam, and two simple ones: spfmilter.py and
# dkim-milter.py. # dkim-milter.py.
# #
+3 -3
View File
@@ -1,10 +1,10 @@
web: web:
doxygen doxygen
test -L doc/html/milter_api || ln -sf /usr/share/doc/sendmail-devel-* doc/html/milter_api test -L doc/html/milter_api || ln -sf /usr/share/doc/sendmail-milter-devel doc/html/milter_api
rsync -ravKk doc/html/ spidey2.bmsi.com:/Public/pymilter rsync -ravKk doc/html/ bmsi.com:/var/www/html/pymilter
cd doc/html; zip -r ../../doc . cd doc/html; zip -r ../../doc .
VERSION=1.0.2 VERSION=1.0.3
PKG=pymilter-$(VERSION) PKG=pymilter-$(VERSION)
SRCTAR=$(PKG).tar.gz SRCTAR=$(PKG).tar.gz
+91 -214
View File
@@ -34,217 +34,6 @@ $ python setup.py help
libraries=["milter","smutil","resolv"] libraries=["milter","smutil","resolv"]
* $Log$
* Revision 1.35 2013/03/14 22:11:25 customdesigned
* Release 0.9.8
*
* Revision 1.34 2013/03/09 05:42:14 customdesigned
* Make TestBase members private, fix getsymlist misspelling.
*
* Revision 1.33 2013/03/09 00:25:23 customdesigned
* Better untrapped exception message. const char for doc comments.
*
* Revision 1.32 2013/01/13 01:46:16 customdesigned
* Doc updates.
*
* Revision 1.31 2012/04/12 23:32:50 customdesigned
* Replace redundant callback array with macros. If this doesn't break anything,
* macros can be eliminated with code changes.
*
* Revision 1.30 2012/04/12 23:08:06 customdesigned
* Support RFC2553 on BSD
*
* Revision 1.29 2011/06/09 15:45:27 customdesigned
* Print callback name for non-int return error.
*
* Revision 1.28 2011/06/08 23:13:48 customdesigned
* Generate special exception when callback return not int.
*
* Revision 1.27 2009/07/28 21:45:54 customdesigned
* Add getversion() to return runtime version.
*
* Revision 1.26 2009/07/28 21:08:20 customdesigned
* Increment del count.
*
* Revision 1.25 2009/07/28 20:58:55 customdesigned
* getdiag method
*
* Revision 1.24 2009/06/09 01:54:44 customdesigned
* Forgot to initialize optional parameter.
*
* Revision 1.23 2009/05/29 20:44:58 customdesigned
* Typo SMFIP_NO constants.
*
* 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
* Doc updates.
*
* Revision 1.13 2008/11/23 03:06:47 customdesigned
* Milter support for chgfrom.
*
* Revision 1.12 2008/11/21 20:42:52 customdesigned
* Support smfi_chgfrom and smfi_addrcpt_par.
*
* Revision 1.11 2007/09/25 02:26:29 customdesigned
* Update license.
*
* Revision 1.10 2006/02/12 02:00:42 customdesigned
* Resolve FIXME for wrap_close.
*
* Revision 1.9 2005/12/23 21:46:36 customdesigned
* Compile on sendmail-8.12 (ifdef SMFIR_INSHEADER)
*
* Revision 1.8 2005/10/20 23:23:36 customdesigned
* Include smfi_progress is SMFIR_PROGRESS defined
*
* Revision 1.7 2005/10/20 23:04:46 customdesigned
* Add optional idx for position of added header.
*
* Revision 1.6 2005/07/15 22:18:17 customdesigned
* Support callback exception policy
*
* Revision 1.5 2005/06/24 04:20:07 customdesigned
* Report context allocation error.
*
* Revision 1.4 2005/06/24 04:12:43 customdesigned
* Remove unused name argument to generic wrappers.
*
* Revision 1.3 2005/06/24 03:57:35 customdesigned
* Handle close called before connect.
*
* Revision 1.2 2005/06/02 04:18:55 customdesigned
* Update copyright notices after reading article on /.
*
* Revision 1.1.1.2 2005/05/31 18:09:06 customdesigned
* Release 0.7.1
*
* Revision 2.31 2004/08/23 02:24:36 stuart
* Support setbacklog
*
* Revision 2.30 2004/08/21 20:29:53 stuart
* Support option of 11 lines max for mlreply.
*
* Revision 2.29 2004/08/21 04:14:29 stuart
* mlreply support
*
* Revision 2.28 2004/08/21 02:45:21 stuart
* Don't leak int constants if module unloaded.
*
* Revision 2.27 2004/04/06 03:19:59 stuart
* Release 0.6.8
*
* Revision 2.26 2004/03/04 21:43:06 stuart
* Fix memory leak by removing unused dynamic template buffer,
* thanks again to Alexander Kourakos.
*
* Revision 2.25 2004/03/01 19:45:03 stuart
* Release 0.6.5
*
* Revision 2.24 2004/03/01 18:56:50 stuart
* Support progress reporting.
*
* Revision 2.23 2004/03/01 18:36:09 stuart
* Plug memory leak. Thanks to Alexander Kourakos.
*
* Revision 2.22 2003/11/02 03:01:46 stuart
* Adjust SMTP error codes after careful reading of standard.
*
* Revision 2.21 2003/06/24 19:57:04 stuart
* Allow removing a python milter callback by setting to None.
*
* Revision 2.20 2003/02/13 17:08:57 stuart
* IPV6 support
*
* Revision 2.19 2003/02/13 16:58:29 stuart
* Support passing None to setreply and chgheader.
*
* Revision 2.18 2002/12/11 16:44:06 stuart
* Support QUARANTINE if supported by libmilter.
*
* Revision 2.17 2002/04/18 20:20:35 stuart
* Fix for NULL hostaddr in connect callback from Jason Erickson.
*
* Revision 2.16 2001/09/26 13:29:09 stuart
* sa_len not supported by linux.
*
* Revision 2.15 2001/09/25 17:28:40 stuart
* Copyrights, documentation, release 0.3.1
*
* Revision 2.14 2001/09/25 00:36:57 stuart
* Pass hostaddr to python code in format used by standard socket module.
*
* Revision 2.13 2001/09/24 23:44:55 stuart
* Return old callback from setcallback functions.
*
* Revision 2.12 2001/09/24 20:02:30 stuart
* Remove redundant setpriv
*
* Revision 2.11 2001/09/23 22:26:35 stuart
* Update docs. Streamline Milter.py
* update testbms.py to reflect actual sendmail behaviour with multiple
* messages per connection.
*
* Revision 2.10 2001/09/22 15:33:42 stuart
* More doc comment updates.
*
* Revision 2.9 2001/09/22 14:52:27 stuart
* Actually return retval in _generic_return.
* Go over doc comments.
*
* Revision 2.8 2001/09/22 01:59:32 stuart
* Prevent reentrant call of milter_main, which libmilter doesn't support.
*
* Revision 2.7 2001/09/22 01:47:37 stuart
* Forgot to set milter interp.
*
* Revision 2.6 2001/09/22 01:23:53 stuart
* Added proper threading after research in python docs.
*
* Revision 2.5 2001/09/21 20:08:51 stuart
* Release 0.2.3
*
* Revision 2.4 2001/09/20 16:18:16 stuart
* libmilter checks in_eom state, so we don't have to.
*
* Revision 2.3 2001/09/19 06:02:33 stuart
* Make more stuff static.
*
* Revision 2.1 2001/09/19 04:24:13 stuart
* Use extension type to track context in python.
*
* Revision 1.4 2001/09/18 18:48:28 stuart
* clear private data reference in _clear_context
*
* Revision 1.3 2001/09/15 04:19:37 stuart
* nasty off by 1 mem overwrite bugs in wrap_env
* generic_set_callback
*
* Revision 1.2 2001/09/15 03:15:39 stuart
* several bugs fixed, works smoothly
*
*/ */
#ifndef MAX_ML_REPLY #ifndef MAX_ML_REPLY
@@ -282,7 +71,7 @@ $ python setup.py help
* published. Unfortunately I know of no good way to do this * published. Unfortunately I know of no good way to do this
* other than with OS-specific tests. * other than with OS-specific tests.
*/ */
#if defined(__FreeBSD__) || defined(__linux__) #if defined(__FreeBSD__) || defined(__linux__) || defined(__sun__)
#define HAVE_IPV6_RFC2553 #define HAVE_IPV6_RFC2553
#include <arpa/inet.h> #include <arpa/inet.h>
#endif #endif
@@ -343,7 +132,11 @@ static struct MilterCallback {
{ NULL , NULL } { NULL , NULL }
}; };
#if PY_MAJOR_VERSION >= 3
static struct smfiDesc description; /* forward declaration */
#else
staticforward struct smfiDesc description; /* forward declaration */ staticforward struct smfiDesc description; /* forward declaration */
#endif
static PyObject *MilterError; static PyObject *MilterError;
/* The interpreter instance that called milter.main */ /* The interpreter instance that called milter.main */
@@ -355,7 +148,11 @@ typedef struct {
static milter_Diag diag; static milter_Diag diag;
#if PY_MAJOR_VERSION >= 3
static PyTypeObject milter_ContextType;
#else
staticforward PyTypeObject milter_ContextType; staticforward PyTypeObject milter_ContextType;
#endif
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
@@ -700,7 +497,11 @@ _generic_wrapper(milter_ContextObject *self, PyObject *cb, PyObject *arglist) {
result = PyEval_CallObject(cb, arglist); result = PyEval_CallObject(cb, arglist);
Py_DECREF(arglist); Py_DECREF(arglist);
if (result == NULL) return _report_exception(self); if (result == NULL) return _report_exception(self);
#if PY_MAJOR_VERSION >= 3
if (!PyLong_Check(result)) {
#else
if (!PyInt_Check(result)) { if (!PyInt_Check(result)) {
#endif
const struct MilterCallback *p; const struct MilterCallback *p;
const char *cbname = "milter"; const char *cbname = "milter";
char buf[40]; char buf[40];
@@ -715,7 +516,11 @@ _generic_wrapper(milter_ContextObject *self, PyObject *cb, PyObject *arglist) {
PyErr_SetString(MilterError,buf); PyErr_SetString(MilterError,buf);
return _report_exception(self); return _report_exception(self);
} }
#if PY_MAJOR_VERSION >= 3
retval = PyLong_AS_LONG(result);
#else
retval = PyInt_AS_LONG(result); retval = PyInt_AS_LONG(result);
#endif
Py_DECREF(result); Py_DECREF(result);
_release_thread(self->t); _release_thread(self->t);
return retval; return retval;
@@ -732,7 +537,11 @@ makeipaddr(struct sockaddr_in *addr) {
sprintf(buf, "%d.%d.%d.%d", sprintf(buf, "%d.%d.%d.%d",
(int) (x>>24) & 0xff, (int) (x>>16) & 0xff, (int) (x>>24) & 0xff, (int) (x>>16) & 0xff,
(int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff); (int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(buf);
#else
return PyString_FromString(buf); return PyString_FromString(buf);
#endif
} }
#ifdef HAVE_IPV6_SUPPORT #ifdef HAVE_IPV6_SUPPORT
@@ -740,8 +549,13 @@ static PyObject *
makeip6addr(struct sockaddr_in6 *addr) { makeip6addr(struct sockaddr_in6 *addr) {
char buf[100]; /* must be at least INET6_ADDRSTRLEN + 1 */ char buf[100]; /* must be at least INET6_ADDRSTRLEN + 1 */
const char *s = inet_ntop(AF_INET6, &addr->sin6_addr, buf, sizeof buf); const char *s = inet_ntop(AF_INET6, &addr->sin6_addr, buf, sizeof buf);
#if PY_MAJOR_VERSION >= 3
if (s) return PyUnicode_FromString(s);
return PyUnicode_FromString("inet6:unknown");
#else
if (s) return PyString_FromString(s); if (s) return PyString_FromString(s);
return PyString_FromString("inet6:unknown"); return PyString_FromString("inet6:unknown");
#endif
} }
#endif #endif
@@ -832,7 +646,11 @@ generic_env_wrapper(SMFICTX *ctx, PyObject*cb, char **argv) {
for (i=0;i<count;i++) { for (i=0;i<count;i++) {
/* There's some error checking performed in do_mkvalue() for a string */ /* There's some error checking performed in do_mkvalue() for a string */
/* that's not currently done here - it probably should be */ /* that's not currently done here - it probably should be */
#if PY_MAJOR_VERSION >= 3
PyObject *o = PyUnicode_FromStringAndSize(argv[i], strlen(argv[i]));
#else
PyObject *o = PyString_FromStringAndSize(argv[i], strlen(argv[i])); PyObject *o = PyString_FromStringAndSize(argv[i], strlen(argv[i]));
#endif
if (o == NULL) { /* out of memory */ if (o == NULL) { /* out of memory */
Py_DECREF(arglist); Py_DECREF(arglist);
return _report_exception(self); return _report_exception(self);
@@ -889,7 +707,11 @@ milter_wrap_body(SMFICTX *ctx, u_char *bodyp, size_t bodylen) {
c = _get_context(ctx); c = _get_context(ctx);
if (!c) return SMFIS_TEMPFAIL; if (!c) return SMFIS_TEMPFAIL;
/* Unclear whether this should be s#, z#, or t# */ /* Unclear whether this should be s#, z#, or t# */
#if PY_MAJOR_VERSION >= 3
arglist = Py_BuildValue("(Oy#)", c, bodyp, bodylen);
#else
arglist = Py_BuildValue("(Os#)", c, bodyp, bodylen); arglist = Py_BuildValue("(Os#)", c, bodyp, bodylen);
#endif
return _generic_wrapper(c, body_callback, arglist); return _generic_wrapper(c, body_callback, arglist);
} }
@@ -963,7 +785,11 @@ milter_wrap_negotiate(SMFICTX *ctx,
int i; int i;
for (i = 0; i < 4; ++i) { for (i = 0; i < 4; ++i) {
*pa[i] = (i <= len) *pa[i] = (i <= len)
#if PY_MAJOR_VERSION >= 3
? PyLong_AsUnsignedLongMask(PyList_GET_ITEM(optlist,i))
#else
? PyInt_AsUnsignedLongMask(PyList_GET_ITEM(optlist,i)) ? PyInt_AsUnsignedLongMask(PyList_GET_ITEM(optlist,i))
#endif
: fa[i]; : fa[i];
} }
if (PyErr_Occurred()) { if (PyErr_Occurred()) {
@@ -1551,10 +1377,12 @@ static PyMethodDef context_methods[] = {
{ NULL, NULL } { NULL, NULL }
}; };
#if PY_MAJOR_VERSION < 3
static PyObject * static PyObject *
milter_Context_getattr(PyObject *self, char *name) { milter_Context_getattr(PyObject *self, char *name) {
return Py_FindMethod(context_methods, self, name); return Py_FindMethod(context_methods, self, name);
} }
#endif
static struct smfiDesc description = { /* Set some reasonable defaults */ static struct smfiDesc description = { /* Set some reasonable defaults */
"pythonfilter", "pythonfilter",
@@ -1604,14 +1432,23 @@ static PyMethodDef milter_methods[] = {
}; };
static PyTypeObject milter_ContextType = { static PyTypeObject milter_ContextType = {
#if PY_MAJOR_VERSION >= 3
PyVarObject_HEAD_INIT(&PyType_Type,0)
"milter.Context",
#else
PyObject_HEAD_INIT(&PyType_Type) PyObject_HEAD_INIT(&PyType_Type)
0, 0,
"milterContext", "milterContext",
#endif
sizeof(milter_ContextObject), sizeof(milter_ContextObject),
0, 0,
milter_Context_dealloc, /* tp_dealloc */ milter_Context_dealloc, /* tp_dealloc */
0, /* tp_print */ 0, /* tp_print */
#if PY_MAJOR_VERSION >= 3
0, /* tp_getattr */
#else
milter_Context_getattr, /* tp_getattr */ milter_Context_getattr, /* tp_getattr */
#endif
0, /* tp_setattr */ 0, /* tp_setattr */
0, /* tp_compare */ 0, /* tp_compare */
0, /* tp_repr */ 0, /* tp_repr */
@@ -1625,6 +1462,15 @@ static PyTypeObject milter_ContextType = {
0, /* tp_setattro */ 0, /* tp_setattro */
0, /* tp_as_buffer */ 0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */ Py_TPFLAGS_DEFAULT, /* tp_flags */
#if PY_MAJOR_VERSION >= 3
NULL, /* Documentation string */
0, /* call function for all accessible objects */
0, /* delete references to contained objects */
0, /* rich comparisons */
0, /* weak reference enabler */
0, 0, /* Iterators */
context_methods, /* Attribute descriptor and subclassing stuff */
#endif
}; };
static const char milter_documentation[] = static const char milter_documentation[] =
@@ -1634,17 +1480,45 @@ Libmilter is currently marked FFR, and needs to be explicitly installed.\n\
See <sendmailsource>/libmilter/README for details on setting it up.\n"; See <sendmailsource>/libmilter/README for details on setting it up.\n";
static void setitem(PyObject *d,const char *name,long val) { static void setitem(PyObject *d,const char *name,long val) {
#if PY_MAJOR_VERSION >= 3
PyObject *v = PyLong_FromLong(val);
#else
PyObject *v = PyInt_FromLong(val); PyObject *v = PyInt_FromLong(val);
#endif
PyDict_SetItemString(d,name,v); PyDict_SetItemString(d,name,v);
Py_DECREF(v); Py_DECREF(v);
} }
void #if PY_MAJOR_VERSION >= 3
initmilter(void) {
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"milter", /* m_name */
milter_documentation,/* m_doc */
-1, /* m_size */
milter_methods, /* m_methods */
NULL, /* m_reload */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
};
PyMODINIT_FUNC PyInit_milter(void) {
PyObject *m, *d;
if (PyType_Ready(&milter_ContextType) < 0)
return NULL;
m = PyModule_Create(&moduledef);
if (m == NULL) return NULL;
#else
void initmilter(void) {
PyObject *m, *d; PyObject *m, *d;
m = Py_InitModule4("milter", milter_methods, milter_documentation, m = Py_InitModule4("milter", milter_methods, milter_documentation,
(PyObject*)NULL, PYTHON_API_VERSION); (PyObject*)NULL, PYTHON_API_VERSION);
#endif
d = PyModule_GetDict(m); d = PyModule_GetDict(m);
MilterError = PyErr_NewException("milter.error", NULL, NULL); MilterError = PyErr_NewException("milter.error", NULL, NULL);
PyDict_SetItemString(d,"error", MilterError); PyDict_SetItemString(d,"error", MilterError);
@@ -1710,4 +1584,7 @@ initmilter(void) {
setitem(d,"DISCARD", SMFIS_DISCARD); setitem(d,"DISCARD", SMFIS_DISCARD);
setitem(d,"ACCEPT", SMFIS_ACCEPT); setitem(d,"ACCEPT", SMFIS_ACCEPT);
setitem(d,"TEMPFAIL", SMFIS_TEMPFAIL); setitem(d,"TEMPFAIL", SMFIS_TEMPFAIL);
#if PY_MAJOR_VERSION >= 3
return m;
#endif
} }
+4 -1
View File
@@ -102,6 +102,7 @@ except:
import socket import socket
import Milter import Milter
import zipfile import zipfile
import sys
import email import email
from email.message import Message from email.message import Message
@@ -401,7 +402,10 @@ class _defang:
# emulate old defang function # emulate old defang function
defang = _defang() defang = _defang()
if sys.version < '3.0.0':
from sgmllib import SGMLParser as HTMLParser from sgmllib import SGMLParser as HTMLParser
else:
from Milter.sgmllib import SGMLParser as HTMLParser
import re import re
declname = re.compile(r'[a-zA-Z][-_.a-zA-Z0-9]*\s*') declname = re.compile(r'[a-zA-Z][-_.a-zA-Z0-9]*\s*')
@@ -534,7 +538,6 @@ def check_html(msg,savname=None):
return Milter.CONTINUE return Milter.CONTINUE
if __name__ == '__main__': if __name__ == '__main__':
import sys
def _list_attach(msg): def _list_attach(msg):
t = msg.get_content_type() t = msg.get_content_type()
p = msg.get_payload(decode=True) p = msg.get_payload(decode=True)
+4 -5
View File
@@ -12,12 +12,9 @@ if sys.version < '2.6.5':
libs = ["milter"] libs = ["milter"]
libdirs = ["/usr/lib/libmilter"] # needed for Debian libdirs = ["/usr/lib/libmilter"] # needed for Debian
modules = ["mime"] modules = ["mime"]
if sys.version >= '3':
modules.append("sgmllib")
print("modules=",modules)
# 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 = '1.0.2', setup(name = "pymilter", version = '1.0.3',
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
@@ -38,7 +35,9 @@ sending DSNs or doing CBVs.
library_dirs=libdirs, library_dirs=libdirs,
libraries=libs, libraries=libs,
# set MAX_ML_REPLY to 1 for sendmail < 8.13 # set MAX_ML_REPLY to 1 for sendmail < 8.13
define_macros = [ ('MAX_ML_REPLY',32) ] define_macros = [ ('MAX_ML_REPLY',32) ],
# save lots of debugging time testing rfc2553 compliance
extra_compile_args = [ "-Werror=implicit-function-declaration" ]
), ),
], ],
keywords = ['sendmail','milter'], keywords = ['sendmail','milter'],
+43 -16
View File
@@ -3,6 +3,7 @@ import Milter
import sample import sample
import mime import mime
from Milter.test import TestBase from Milter.test import TestBase
from Milter.testctx import TestCtx
class TestMilter(TestBase,sample.sampleMilter): class TestMilter(TestBase,sample.sampleMilter):
def __init__(self): def __init__(self):
@@ -11,21 +12,47 @@ class TestMilter(TestBase,sample.sampleMilter):
class BMSMilterTestCase(unittest.TestCase): class BMSMilterTestCase(unittest.TestCase):
def testCtx(self,fname='virus1'):
ctx = TestCtx()
Milter.factory = sample.sampleMilter
ctx._setsymval('{auth_authen}','batman')
ctx._setsymval('{auth_type}','batcomputer')
ctx._setsymval('j','mailhost')
rc = ctx._connect()
self.assertTrue(rc == Milter.CONTINUE)
rc = ctx._feedMsg(fname)
milter = ctx.getpriv()
# self.assertTrue(milter.user == 'batman',"getsymval failed: "+
# "%s != %s"%(milter.user,'batman'))
self.assertEquals(milter.user,'batman')
self.assertTrue(milter.auth_type != 'batcomputer',"setsymlist failed")
self.assertTrue(rc == Milter.ACCEPT)
self.assertTrue(ctx._bodyreplaced,"Message body not replaced")
fp = ctx._body
open('test/'+fname+".tstout","wb").write(fp.getvalue())
#self.assertTrue(fp.getvalue() == open("test/virus1.out","r").read())
fp.seek(0)
msg = mime.message_from_file(fp)
s = msg.get_payload(1).get_payload()
milter.log(s)
ctx._close()
def testDefang(self,fname='virus1'): def testDefang(self,fname='virus1'):
milter = TestMilter() milter = TestMilter()
milter.setsymval('{auth_authen}','batman') milter.setsymval('{auth_authen}','batman')
milter.setsymval('{auth_type}','batcomputer') milter.setsymval('{auth_type}','batcomputer')
milter.setsymval('j','mailhost') milter.setsymval('j','mailhost')
rc = milter.connect() rc = milter.connect()
self.failUnless(rc == Milter.CONTINUE) self.assertTrue(rc == Milter.CONTINUE)
rc = milter.feedMsg(fname) rc = milter.feedMsg(fname)
self.failUnless(milter.user == 'batman',"getsymval failed") self.assertTrue(milter.user == 'batman',"getsymval failed")
self.failUnless(milter.auth_type != 'batcomputer',"setsymlist failed") # setsymlist not working in TestBase
self.failUnless(rc == Milter.ACCEPT) #self.assertTrue(milter.auth_type != 'batcomputer',"setsymlist failed")
self.failUnless(milter._bodyreplaced,"Message body not replaced") self.assertTrue(rc == Milter.ACCEPT)
self.assertTrue(milter._bodyreplaced,"Message body not replaced")
fp = milter._body fp = milter._body
open('test/'+fname+".tstout","wb").write(fp.getvalue()) open('test/'+fname+".tstout","wb").write(fp.getvalue())
#self.failUnless(fp.getvalue() == open("test/virus1.out","r").read()) #self.assertTrue(fp.getvalue() == open("test/virus1.out","r").read())
fp.seek(0) fp.seek(0)
msg = mime.message_from_file(fp) msg = mime.message_from_file(fp)
s = msg.get_payload(1).get_payload() s = msg.get_payload(1).get_payload()
@@ -36,8 +63,8 @@ class BMSMilterTestCase(unittest.TestCase):
milter = TestMilter() milter = TestMilter()
milter.connect('somehost') milter.connect('somehost')
rc = milter.feedMsg(fname) rc = milter.feedMsg(fname)
self.failUnless(rc == Milter.ACCEPT) self.assertTrue(rc == Milter.ACCEPT)
self.failIf(milter._bodyreplaced,"Milter needlessly replaced body.") self.assertFalse(milter._bodyreplaced,"Milter needlessly replaced body.")
fp = milter._body fp = milter._body
open('test/'+fname+".tstout","wb").write(fp.getvalue()) open('test/'+fname+".tstout","wb").write(fp.getvalue())
milter.close() milter.close()
@@ -46,18 +73,18 @@ class BMSMilterTestCase(unittest.TestCase):
milter = TestMilter() milter = TestMilter()
milter.connect('somehost') milter.connect('somehost')
rc = milter.feedMsg('samp1') rc = milter.feedMsg('samp1')
self.failUnless(rc == Milter.ACCEPT) self.assertTrue(rc == Milter.ACCEPT)
self.failIf(milter._bodyreplaced,"Milter needlessly replaced body.") self.assertFalse(milter._bodyreplaced,"Milter needlessly replaced body.")
rc = milter.feedMsg("virus3") rc = milter.feedMsg("virus3")
self.failUnless(rc == Milter.ACCEPT) self.assertTrue(rc == Milter.ACCEPT)
self.failUnless(milter._bodyreplaced,"Message body not replaced") self.assertTrue(milter._bodyreplaced,"Message body not replaced")
fp = milter._body fp = milter._body
open("test/virus3.tstout","wb").write(fp.getvalue()) open("test/virus3.tstout","wb").write(fp.getvalue())
#self.failUnless(fp.getvalue() == open("test/virus3.out","r").read()) #self.assertTrue(fp.getvalue() == open("test/virus3.out","r").read())
rc = milter.feedMsg("virus6") rc = milter.feedMsg("virus6")
self.failUnless(rc == Milter.ACCEPT) self.assertTrue(rc == Milter.ACCEPT)
self.failUnless(milter._bodyreplaced,"Message body not replaced") self.assertTrue(milter._bodyreplaced,"Message body not replaced")
self.failUnless(milter._headerschanged,"Message headers not adjusted") self.assertTrue(milter._headerschanged,"Message headers not adjusted")
fp = milter._body fp = milter._body
open("test/virus6.tstout","wb").write(fp.getvalue()) open("test/virus6.tstout","wb").write(fp.getvalue())
milter.close() milter.close()
+7 -7
View File
@@ -21,24 +21,24 @@ class AddrCacheTestCase(unittest.TestCase):
cache['foo@bar.com'] = None cache['foo@bar.com'] = None
cache.addperm('baz@bar.com') cache.addperm('baz@bar.com')
cache['temp@bar.com'] = 'testing' cache['temp@bar.com'] = 'testing'
self.failUnless(cache.has_key('foo@bar.com')) self.assertTrue(cache.has_key('foo@bar.com'))
self.failUnless(not cache.has_key('hello@bar.com')) self.assertTrue(not cache.has_key('hello@bar.com'))
self.failUnless('baz@bar.com' in cache) self.assertTrue('baz@bar.com' in cache)
self.assertEquals(cache['temp@bar.com'],'testing') self.assertEquals(cache['temp@bar.com'],'testing')
s = open(self.fname).readlines() s = open(self.fname).readlines()
self.failUnless(len(s) == 2) self.assertTrue(len(s) == 2)
self.failUnless(s[0].startswith('foo@bar.com ')) self.assertTrue(s[0].startswith('foo@bar.com '))
self.assertEquals(s[1].strip(),'baz@bar.com') self.assertEquals(s[1].strip(),'baz@bar.com')
# check that new result overrides old # check that new result overrides old
cache['temp@bar.com'] = None cache['temp@bar.com'] = None
self.failUnless(not cache['temp@bar.com']) self.assertTrue(not cache['temp@bar.com'])
def testDomain(self): def testDomain(self):
with open(self.fname,'w') as fp: with open(self.fname,'w') as fp:
print('spammer.com',file=fp) print('spammer.com',file=fp)
cache = AddrCache(fname=self.fname) cache = AddrCache(fname=self.fname)
cache.load(self.fname,30) cache.load(self.fname,30)
self.failUnless('spammer.com' in cache) self.assertTrue('spammer.com' in cache)
def testParseHeader(self): def testParseHeader(self):
s='=?UTF-8?B?TGFzdCBGZXcgQ29sZHBsYXkgQWxidW0gQXJ0d29ya3MgQXZhaWxhYmxlAA?=' s='=?UTF-8?B?TGFzdCBGZXcgQ29sZHBsYXkgQWxidW0gQXJ0d29ya3MgQXZhaWxhYmxlAA?='