Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 10fdccf366 | |||
| 7d097fa4a0 | |||
| ca81502c85 | |||
| 50356d4710 | |||
| ec3fa46799 | |||
| 04e0b15640 | |||
| ff6a07ef10 | |||
| 7dfda0a3bd | |||
| 183ce91a61 | |||
| d30918aca7 | |||
| 4e8fda517c | |||
| 2194d8fd96 | |||
| 52e1b4ae32 | |||
| 55e5378659 | |||
| f9b2241ec6 | |||
| 62783fbbfd | |||
| d521665f75 | |||
| 55eb05e526 | |||
| fc008f6db0 | |||
| 96cd9ac263 | |||
| 10471faa7d | |||
| 36750bac78 | |||
| 42e7a02638 | |||
| bc9d8c622b | |||
| 2fa952e108 | |||
| 381e906b6a | |||
| 207278479f | |||
| a0bd76cded | |||
| 8e96c23ddc | |||
| 5ec4e2b34d | |||
| 28c3a6afd6 | |||
| 36df47f019 | |||
| e5c03665e9 | |||
| ea9ca0c12a | |||
| fb1da3b12b | |||
| 74d33126b5 |
@@ -2,3 +2,7 @@
|
|||||||
build/
|
build/
|
||||||
test/*.out
|
test/*.out
|
||||||
test/*.tstout
|
test/*.tstout
|
||||||
|
test/*.log
|
||||||
|
test.db
|
||||||
|
dist
|
||||||
|
MANIFEST
|
||||||
|
|||||||
@@ -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.
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ include testmime.py
|
|||||||
include testutils.py
|
include testutils.py
|
||||||
include test.py
|
include test.py
|
||||||
include sample.py
|
include sample.py
|
||||||
include sgmllib.py
|
|
||||||
include milter-template.py
|
include milter-template.py
|
||||||
include test/*
|
include test/*
|
||||||
include Milter/*.py
|
include Milter/*.py
|
||||||
|
|||||||
+37
-6
@@ -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.1'
|
__version__ = '1.0.4'
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
@@ -48,6 +48,12 @@ OPTIONAL_CALLBACKS = {
|
|||||||
'header':(P_NR_HDR,P_NOHDRS)
|
'header':(P_NR_HDR,P_NOHDRS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MACRO_CALLBACKS = {
|
||||||
|
'connect': M_CONNECT,
|
||||||
|
'hello': M_HELO, 'envfrom': M_ENVFROM, 'envrcpt': M_ENVRCPT,
|
||||||
|
'data': M_DATA, 'eom': M_EOM, 'eoh': M_EOH
|
||||||
|
}
|
||||||
|
|
||||||
## @private
|
## @private
|
||||||
R = re.compile(r'%+')
|
R = re.compile(r'%+')
|
||||||
|
|
||||||
@@ -141,6 +147,7 @@ def nocallback(func):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
'@nocallback applied to non-optional method: '+func.__name__)
|
'@nocallback applied to non-optional method: '+func.__name__)
|
||||||
|
@wraps(func)
|
||||||
def wrapper(self,*args):
|
def wrapper(self,*args):
|
||||||
if func(self,*args) != CONTINUE:
|
if func(self,*args) != CONTINUE:
|
||||||
raise RuntimeError('%s return code must be CONTINUE with @nocallback'
|
raise RuntimeError('%s return code must be CONTINUE with @nocallback'
|
||||||
@@ -173,6 +180,21 @@ def noreply(func):
|
|||||||
wrapper.milter_protocol = nr_mask
|
wrapper.milter_protocol = nr_mask
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
## Function decorator to set macros used in a callback.
|
||||||
|
# By default, the MTA sends all macros defined for a callback.
|
||||||
|
# If some or all of these are unused, the bandwidth can be saved
|
||||||
|
# by listing the ones that are used.
|
||||||
|
# @since 1.0.2
|
||||||
|
def symlist(*syms):
|
||||||
|
if len(syms) > 5:
|
||||||
|
raise ValueError('@symlist limited to 5 macros by MTA: '+func.__name__)
|
||||||
|
def setsyms(func):
|
||||||
|
if func.__name__ not in MACRO_CALLBACKS:
|
||||||
|
raise ValueError('@symlist applied to non-symlist method: '+func.__name__)
|
||||||
|
func._symlist = syms
|
||||||
|
return func
|
||||||
|
return setsyms
|
||||||
|
|
||||||
## Disabled action exception.
|
## Disabled action exception.
|
||||||
# set_flags() can tell the MTA that this application will not use certain
|
# set_flags() can tell the MTA that this application will not use certain
|
||||||
# features (such as CHGFROM). This can also be negotiated for each
|
# features (such as CHGFROM). This can also be negotiated for each
|
||||||
@@ -393,11 +415,16 @@ class Base(object):
|
|||||||
def negotiate(self,opts):
|
def negotiate(self,opts):
|
||||||
try:
|
try:
|
||||||
self._actions,p,f1,f2 = opts
|
self._actions,p,f1,f2 = opts
|
||||||
|
for func,stage in MACRO_CALLBACKS.items():
|
||||||
|
func = getattr(self,func)
|
||||||
|
syms = getattr(func,'_symlist',None)
|
||||||
|
if syms is not None:
|
||||||
|
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
|
||||||
@@ -443,23 +470,27 @@ class Base(object):
|
|||||||
# set. The protocol stages are M_CONNECT, M_HELO, M_ENVFROM, M_ENVRCPT,
|
# set. The protocol stages are M_CONNECT, M_HELO, M_ENVFROM, M_ENVRCPT,
|
||||||
# M_DATA, M_EOM, M_EOH.
|
# M_DATA, M_EOM, M_EOH.
|
||||||
#
|
#
|
||||||
# May only be called from negotiate callback.
|
# May only be called from negotiate callback. Hence, this is an advanced
|
||||||
|
# feature. Use the @@symlist function decorator to conviently set
|
||||||
|
# the macros used by a callback.
|
||||||
# @since 0.9.8, previous version was misspelled!
|
# @since 0.9.8, previous version was misspelled!
|
||||||
# @param stage the protocol stage to set to macro list for,
|
# @param stage the protocol stage to set to macro list for,
|
||||||
# one of the M_* constants defined in Milter
|
# one of the M_* constants defined in Milter
|
||||||
# @param macros space separated and/or lists of strings
|
# @param macros space separated and/or lists of strings
|
||||||
def setsymlist(self,stage,*macros):
|
def setsymlist(self,stage,*macros):
|
||||||
if not self._actions & SETSYMLIST: raise DisabledAction("SETSYMLIST")
|
if not self._actions & SETSYMLIST: raise DisabledAction("SETSYMLIST")
|
||||||
|
if len(macros) > 5:
|
||||||
|
raise ValueError('setsymlist limited to 5 macros by MTA')
|
||||||
a = []
|
a = []
|
||||||
for m in macros:
|
for m in macros:
|
||||||
try:
|
try:
|
||||||
m = m.encode('utf8')
|
m = m.encode('utf8')
|
||||||
except: pass
|
except: pass
|
||||||
try:
|
try:
|
||||||
m = m.split(' ')
|
m = m.split(b' ')
|
||||||
|
a += m
|
||||||
except: pass
|
except: pass
|
||||||
a += m
|
return self._ctx.setsymlist(stage,b' '.join(a))
|
||||||
return self._ctx.setsmlist(stage,' '.join(a))
|
|
||||||
|
|
||||||
# Milter methods which can only be called from eom callback.
|
# Milter methods which can only be called from eom callback.
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -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
@@ -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
|
||||||
|
|||||||
+44
-4
@@ -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):
|
||||||
|
|
||||||
@@ -40,6 +41,9 @@ class TestBase(object):
|
|||||||
self._reply = None
|
self._reply = None
|
||||||
## The rfc822 message object for the current email being fed to the %milter.
|
## The rfc822 message object for the current email being fed to the %milter.
|
||||||
self._msg = None
|
self._msg = None
|
||||||
|
## The protocol stage for macros returned
|
||||||
|
self._stage = None
|
||||||
|
## The macros returned by protocol stage
|
||||||
self._symlist = [ None, None, None, None, None, None, None ]
|
self._symlist = [ None, None, None, None, None, None, None ]
|
||||||
|
|
||||||
def log(self,*msg):
|
def log(self,*msg):
|
||||||
@@ -54,8 +58,12 @@ class TestBase(object):
|
|||||||
self._macros[name] = val
|
self._macros[name] = val
|
||||||
|
|
||||||
def getsymval(self,name):
|
def getsymval(self,name):
|
||||||
# FIXME: track stage, and use _symlist
|
stage = self._stage
|
||||||
return self._macros.get(name,'')
|
if stage >= 0:
|
||||||
|
syms = self._symlist[stage]
|
||||||
|
if syms is not None and name not in syms:
|
||||||
|
return None
|
||||||
|
return self._macros.get(name,None)
|
||||||
|
|
||||||
def replacebody(self,chunk):
|
def replacebody(self,chunk):
|
||||||
if self._body:
|
if self._body:
|
||||||
@@ -113,7 +121,10 @@ class TestBase(object):
|
|||||||
self._reply = (rcode,xcode) + msg
|
self._reply = (rcode,xcode) + msg
|
||||||
|
|
||||||
def setsymlist(self,stage,macros):
|
def setsymlist(self,stage,macros):
|
||||||
if not self._actions & SETSYMLIST: raise DisabledAction("SETSYMLIST")
|
if not self._actions & Milter.SETSYMLIST:
|
||||||
|
raise DisabledAction("SETSYMLIST")
|
||||||
|
if self._stage != -1:
|
||||||
|
raise RuntimeError("setsymlist may only be called from negotiate")
|
||||||
# not used yet, but just for grins we save the data
|
# not used yet, but just for grins we save the data
|
||||||
a = []
|
a = []
|
||||||
for m in macros:
|
for m in macros:
|
||||||
@@ -121,9 +132,14 @@ class TestBase(object):
|
|||||||
m = m.encode('utf8')
|
m = m.encode('utf8')
|
||||||
except: pass
|
except: pass
|
||||||
try:
|
try:
|
||||||
m = m.split(' ')
|
m = m.split(b' ')
|
||||||
except: pass
|
except: pass
|
||||||
a += m
|
a += m
|
||||||
|
if len(a) > 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)
|
||||||
|
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
|
||||||
@@ -144,16 +160,32 @@ class TestBase(object):
|
|||||||
self._reply = None
|
self._reply = None
|
||||||
self._sender = '<%s>'%sender
|
self._sender = '<%s>'%sender
|
||||||
msg = mime.message_from_file(fp)
|
msg = mime.message_from_file(fp)
|
||||||
|
# envfrom
|
||||||
|
self._stage = Milter.M_ENVFROM
|
||||||
rc = self.envfrom(self._sender)
|
rc = self.envfrom(self._sender)
|
||||||
|
self._stage = None
|
||||||
if rc != Milter.CONTINUE: return rc
|
if rc != Milter.CONTINUE: return rc
|
||||||
|
# envrcpt
|
||||||
for rcpt in (rcpt,) + rcpts:
|
for rcpt in (rcpt,) + rcpts:
|
||||||
|
self._stage = Milter.M_ENVRCPT
|
||||||
rc = self.envrcpt('<%s>'%rcpt)
|
rc = self.envrcpt('<%s>'%rcpt)
|
||||||
|
self._stage = None
|
||||||
if rc != Milter.CONTINUE: return rc
|
if rc != Milter.CONTINUE: return rc
|
||||||
|
# data
|
||||||
|
self._stage = Milter.M_DATA
|
||||||
|
rc = self.data()
|
||||||
|
self._stage = None
|
||||||
|
if rc != Milter.CONTINUE: return rc
|
||||||
|
# header
|
||||||
for h,val in msg.items():
|
for h,val in msg.items():
|
||||||
rc = self.header(h,val)
|
rc = self.header(h,val)
|
||||||
if rc != Milter.CONTINUE: return rc
|
if rc != Milter.CONTINUE: return rc
|
||||||
|
# eoh
|
||||||
|
self._stage = Milter.M_EOH
|
||||||
rc = self.eoh()
|
rc = self.eoh()
|
||||||
|
self._stage = None
|
||||||
if rc != Milter.CONTINUE: return rc
|
if rc != Milter.CONTINUE: return rc
|
||||||
|
# body
|
||||||
header,body = msg.as_bytes().split(b'\n\n',1)
|
header,body = msg.as_bytes().split(b'\n\n',1)
|
||||||
bfp = BytesIO(body)
|
bfp = BytesIO(body)
|
||||||
while 1:
|
while 1:
|
||||||
@@ -163,7 +195,9 @@ class TestBase(object):
|
|||||||
if rc != Milter.CONTINUE: return rc
|
if rc != Milter.CONTINUE: return rc
|
||||||
self._msg = msg
|
self._msg = msg
|
||||||
self._body = BytesIO()
|
self._body = BytesIO()
|
||||||
|
self._stage = Milter.M_EOM
|
||||||
rc = self.eom()
|
rc = self.eom()
|
||||||
|
self._stage = None
|
||||||
if self._bodyreplaced:
|
if self._bodyreplaced:
|
||||||
body = self._body.getvalue()
|
body = self._body.getvalue()
|
||||||
self._body = BytesIO()
|
self._body = BytesIO()
|
||||||
@@ -188,13 +222,19 @@ class TestBase(object):
|
|||||||
def connect(self,host='localhost',helo='spamrelay',ip='1.2.3.4'):
|
def connect(self,host='localhost',helo='spamrelay',ip='1.2.3.4'):
|
||||||
self._body = None
|
self._body = None
|
||||||
self._bodyreplaced = False
|
self._bodyreplaced = False
|
||||||
|
self._setctx(None)
|
||||||
opts = [ Milter.CURR_ACTS,~0,0,0 ]
|
opts = [ Milter.CURR_ACTS,~0,0,0 ]
|
||||||
|
self._stage = -1
|
||||||
rc = self.negotiate(opts)
|
rc = self.negotiate(opts)
|
||||||
|
self._stage = Milter.M_CONNECT
|
||||||
rc = super(TestBase,self).connect(host,1,(ip,1234))
|
rc = super(TestBase,self).connect(host,1,(ip,1234))
|
||||||
if rc != Milter.CONTINUE:
|
if rc != Milter.CONTINUE:
|
||||||
|
self._stage = None
|
||||||
self.close()
|
self.close()
|
||||||
return rc
|
return rc
|
||||||
|
self._stage = Milter.M_HELO
|
||||||
rc = self.hello(helo)
|
rc = self.hello(helo)
|
||||||
|
self._stage = None
|
||||||
if rc != Milter.CONTINUE:
|
if rc != Milter.CONTINUE:
|
||||||
self.close()
|
self.close()
|
||||||
return rc
|
return rc
|
||||||
|
|||||||
@@ -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)
|
||||||
+2
-3
@@ -8,6 +8,7 @@ import socket
|
|||||||
import email.errors
|
import email.errors
|
||||||
from email.header import decode_header
|
from email.header import decode_header
|
||||||
import email.base64mime
|
import email.base64mime
|
||||||
|
import email.utils
|
||||||
from fnmatch import fnmatchcase
|
from fnmatch import fnmatchcase
|
||||||
from binascii import a2b_base64
|
from binascii import a2b_base64
|
||||||
|
|
||||||
@@ -70,7 +71,7 @@ def iniplist(ipaddr,iplist):
|
|||||||
True
|
True
|
||||||
>>> iniplist('4.2.2.2',['b.resolvers.Level3.net'])
|
>>> iniplist('4.2.2.2',['b.resolvers.Level3.net'])
|
||||||
True
|
True
|
||||||
>>> iniplist('2607:f8b0:4004:801::',['google.com/40'])
|
>>> iniplist('2606:2800:220:1::',['example.com/40'])
|
||||||
True
|
True
|
||||||
>>> iniplist('4.2.2.2',['nothing.example.com'])
|
>>> iniplist('4.2.2.2',['nothing.example.com'])
|
||||||
False
|
False
|
||||||
@@ -134,8 +135,6 @@ def parseaddr(t):
|
|||||||
('God@heaven', 'jeff@spec.org')
|
('God@heaven', 'jeff@spec.org')
|
||||||
>>> parseaddr('Real Name ((comment)) <addr...@example.com>')
|
>>> parseaddr('Real Name ((comment)) <addr...@example.com>')
|
||||||
('Real Name (comment)', 'addr...@example.com')
|
('Real Name (comment)', 'addr...@example.com')
|
||||||
>>> parseaddr('a(WRONG)@b')
|
|
||||||
('WRONG', 'a@b')
|
|
||||||
"""
|
"""
|
||||||
#return email.utils.parseaddr(t)
|
#return email.utils.parseaddr(t)
|
||||||
res = email.utils.parseaddr(t)
|
res = email.utils.parseaddr(t)
|
||||||
|
|||||||
+14
-4
@@ -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.
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
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
|
VERSION=1.0.4
|
||||||
CVSTAG=pymilter-1_0
|
|
||||||
PKG=pymilter-$(VERSION)
|
PKG=pymilter-$(VERSION)
|
||||||
SRCTAR=$(PKG).tar.gz
|
SRCTAR=$(PKG).tar.gz
|
||||||
|
|
||||||
$(SRCTAR):
|
$(SRCTAR):
|
||||||
cvs export -r$(CVSTAG) -d $(PKG) pymilter
|
git archive --format=tar.gz --prefix=$(PKG)/ -o $(SRCTAR) $(PKG)
|
||||||
tar cvfz $(PKG).tar.gz $(PKG)
|
|
||||||
rm -r $(PKG)
|
|
||||||
|
|
||||||
cvstar: $(SRCTAR)
|
gittar: $(SRCTAR)
|
||||||
|
|||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
## A very simple milter to prevent mixing of internal and external mail.
|
## A very simple sample milter to prevent mixing of internal and external mail.
|
||||||
# Internal is defined as using one of a list of internal top level domains.
|
# Internal is defined as using one of a list of internal top level domains.
|
||||||
# This code is open-source on the same terms as Python.
|
# This code is open-source on the same terms as Python.
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ internal_tlds = ["corp", "personal"]
|
|||||||
# True if internal, False otherwise
|
# True if internal, False otherwise
|
||||||
def is_internal(hostname):
|
def is_internal(hostname):
|
||||||
components = hostname.split(".")
|
components = hostname.split(".")
|
||||||
return components.pop() in internal_tlds:
|
return components.pop() in internal_tlds
|
||||||
|
|
||||||
# Determine if internal and external hosts are mixed based on a list
|
# Determine if internal and external hosts are mixed based on a list
|
||||||
# of hostnames
|
# of hostnames
|
||||||
|
|||||||
-160
@@ -1,160 +0,0 @@
|
|||||||
diff --git a/miltermodule.c b/miltermodule.c
|
|
||||||
index aa10a08..af9a144 100644
|
|
||||||
--- a/miltermodule.c
|
|
||||||
+++ b/miltermodule.c
|
|
||||||
@@ -343,7 +343,7 @@ static struct MilterCallback {
|
|
||||||
{ NULL , NULL }
|
|
||||||
};
|
|
||||||
|
|
||||||
-staticforward struct smfiDesc description; /* forward declaration */
|
|
||||||
+static struct smfiDesc description; /* forward declaration */
|
|
||||||
|
|
||||||
static PyObject *MilterError;
|
|
||||||
/* The interpreter instance that called milter.main */
|
|
||||||
@@ -355,7 +355,7 @@ typedef struct {
|
|
||||||
|
|
||||||
static milter_Diag diag;
|
|
||||||
|
|
||||||
-staticforward PyTypeObject milter_ContextType;
|
|
||||||
+static PyTypeObject milter_ContextType;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
PyObject_HEAD
|
|
||||||
@@ -700,7 +700,7 @@ _generic_wrapper(milter_ContextObject *self, PyObject *cb, PyObject *arglist) {
|
|
||||||
result = PyEval_CallObject(cb, arglist);
|
|
||||||
Py_DECREF(arglist);
|
|
||||||
if (result == NULL) return _report_exception(self);
|
|
||||||
- if (!PyInt_Check(result)) {
|
|
||||||
+ if (!PyLong_Check(result)) {
|
|
||||||
const struct MilterCallback *p;
|
|
||||||
const char *cbname = "milter";
|
|
||||||
char buf[40];
|
|
||||||
@@ -715,7 +715,7 @@ _generic_wrapper(milter_ContextObject *self, PyObject *cb, PyObject *arglist) {
|
|
||||||
PyErr_SetString(MilterError,buf);
|
|
||||||
return _report_exception(self);
|
|
||||||
}
|
|
||||||
- retval = PyInt_AS_LONG(result);
|
|
||||||
+ retval = PyLong_AS_LONG(result);
|
|
||||||
Py_DECREF(result);
|
|
||||||
_release_thread(self->t);
|
|
||||||
return retval;
|
|
||||||
@@ -732,7 +732,7 @@ makeipaddr(struct sockaddr_in *addr) {
|
|
||||||
sprintf(buf, "%d.%d.%d.%d",
|
|
||||||
(int) (x>>24) & 0xff, (int) (x>>16) & 0xff,
|
|
||||||
(int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff);
|
|
||||||
- return PyString_FromString(buf);
|
|
||||||
+ return PyUnicode_FromString(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef HAVE_IPV6_SUPPORT
|
|
||||||
@@ -740,8 +740,8 @@ static PyObject *
|
|
||||||
makeip6addr(struct sockaddr_in6 *addr) {
|
|
||||||
char buf[100]; /* must be at least INET6_ADDRSTRLEN + 1 */
|
|
||||||
const char *s = inet_ntop(AF_INET6, &addr->sin6_addr, buf, sizeof buf);
|
|
||||||
- if (s) return PyString_FromString(s);
|
|
||||||
- return PyString_FromString("inet6:unknown");
|
|
||||||
+ if (s) return PyUnicode_FromString(s);
|
|
||||||
+ return PyUnicode_FromString("inet6:unknown");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@@ -832,7 +832,7 @@ generic_env_wrapper(SMFICTX *ctx, PyObject*cb, char **argv) {
|
|
||||||
for (i=0;i<count;i++) {
|
|
||||||
/* There's some error checking performed in do_mkvalue() for a string */
|
|
||||||
/* that's not currently done here - it probably should be */
|
|
||||||
- PyObject *o = PyString_FromStringAndSize(argv[i], strlen(argv[i]));
|
|
||||||
+ PyObject *o = PyUnicode_FromStringAndSize(argv[i], strlen(argv[i]));
|
|
||||||
if (o == NULL) { /* out of memory */
|
|
||||||
Py_DECREF(arglist);
|
|
||||||
return _report_exception(self);
|
|
||||||
@@ -963,7 +963,7 @@ milter_wrap_negotiate(SMFICTX *ctx,
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < 4; ++i) {
|
|
||||||
*pa[i] = (i <= len)
|
|
||||||
- ? PyInt_AsUnsignedLongMask(PyList_GET_ITEM(optlist,i))
|
|
||||||
+ ? PyLong_AsUnsignedLongMask(PyList_GET_ITEM(optlist,i))
|
|
||||||
: fa[i];
|
|
||||||
}
|
|
||||||
if (PyErr_Occurred()) {
|
|
||||||
@@ -1551,11 +1551,6 @@ static PyMethodDef context_methods[] = {
|
|
||||||
{ NULL, NULL }
|
|
||||||
};
|
|
||||||
|
|
||||||
-static PyObject *
|
|
||||||
-milter_Context_getattr(PyObject *self, char *name) {
|
|
||||||
- return Py_FindMethod(context_methods, self, name);
|
|
||||||
-}
|
|
||||||
-
|
|
||||||
static struct smfiDesc description = { /* Set some reasonable defaults */
|
|
||||||
"pythonfilter",
|
|
||||||
SMFI_VERSION,
|
|
||||||
@@ -1604,14 +1599,13 @@ static PyMethodDef milter_methods[] = {
|
|
||||||
};
|
|
||||||
|
|
||||||
static PyTypeObject milter_ContextType = {
|
|
||||||
- PyObject_HEAD_INIT(&PyType_Type)
|
|
||||||
- 0,
|
|
||||||
+ PyVarObject_HEAD_INIT(&PyType_Type,0)
|
|
||||||
"milterContext",
|
|
||||||
sizeof(milter_ContextObject),
|
|
||||||
0,
|
|
||||||
milter_Context_dealloc, /* tp_dealloc */
|
|
||||||
0, /* tp_print */
|
|
||||||
- milter_Context_getattr, /* tp_getattr */
|
|
||||||
+ 0, /* tp_getattr */
|
|
||||||
0, /* tp_setattr */
|
|
||||||
0, /* tp_compare */
|
|
||||||
0, /* tp_repr */
|
|
||||||
@@ -1625,6 +1619,13 @@ static PyTypeObject milter_ContextType = {
|
|
||||||
0, /* tp_setattro */
|
|
||||||
0, /* tp_as_buffer */
|
|
||||||
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
|
||||||
+ 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 */
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char milter_documentation[] =
|
|
||||||
@@ -1634,17 +1635,27 @@ Libmilter is currently marked FFR, and needs to be explicitly installed.\n\
|
|
||||||
See <sendmailsource>/libmilter/README for details on setting it up.\n";
|
|
||||||
|
|
||||||
static void setitem(PyObject *d,const char *name,long val) {
|
|
||||||
- PyObject *v = PyInt_FromLong(val);
|
|
||||||
+ PyObject *v = PyLong_FromLong(val);
|
|
||||||
PyDict_SetItemString(d,name,v);
|
|
||||||
Py_DECREF(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
-void
|
|
||||||
-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;
|
|
||||||
|
|
||||||
- m = Py_InitModule4("milter", milter_methods, milter_documentation,
|
|
||||||
- (PyObject*)NULL, PYTHON_API_VERSION);
|
|
||||||
+ m = PyModule_Create(&moduledef);
|
|
||||||
d = PyModule_GetDict(m);
|
|
||||||
MilterError = PyErr_NewException("milter.error", NULL, NULL);
|
|
||||||
PyDict_SetItemString(d,"error", MilterError);
|
|
||||||
@@ -1710,4 +1721,5 @@ initmilter(void) {
|
|
||||||
setitem(d,"DISCARD", SMFIS_DISCARD);
|
|
||||||
setitem(d,"ACCEPT", SMFIS_ACCEPT);
|
|
||||||
setitem(d,"TEMPFAIL", SMFIS_TEMPFAIL);
|
|
||||||
+ return m;
|
|
||||||
}
|
|
||||||
+97
-232
@@ -1,6 +1,6 @@
|
|||||||
/* Copyright (C) 2001 James Niemira (niemira@colltech.com, urmane@urmane.org)
|
/* Copyright (C) 2001 James Niemira (niemira@colltech.com, urmane@urmane.org)
|
||||||
* Portions Copyright (C) 2001,2002,2003,2004,2005,2006,2007
|
* Portions Copyright (C) 2001,2002,2003,2004,2005,2006,2007
|
||||||
* Stuart Gathman (stuart@bmsi.com)
|
* Stuart Gathman (stuart@gathman.org)
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify it
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
* under the terms of the GNU General Public License as published by the
|
* under the terms of the GNU General Public License as published by the
|
||||||
@@ -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_kernel__) || defined(__linux__)
|
#if defined(__FreeBSD__) || defined(__linux__) || defined(__sun__) || defined(__GLIBC__) || (defined(__APPLE__) && defined(__MACH__))
|
||||||
#define HAVE_IPV6_RFC2553
|
#define HAVE_IPV6_RFC2553
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -343,7 +132,13 @@ static struct MilterCallback {
|
|||||||
{ NULL , NULL }
|
{ NULL , NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
staticforward struct smfiDesc description; /* forward declaration */
|
#if PY_MAJOR_VERSION >= 3
|
||||||
|
static struct smfiDesc description; /* forward declaration */
|
||||||
|
static PyTypeObject milter_ContextType;
|
||||||
|
#else
|
||||||
|
staticforward struct smfiDesc description; /* forward declaration */
|
||||||
|
staticforward PyTypeObject milter_ContextType;
|
||||||
|
#endif
|
||||||
|
|
||||||
static PyObject *MilterError;
|
static PyObject *MilterError;
|
||||||
/* The interpreter instance that called milter.main */
|
/* The interpreter instance that called milter.main */
|
||||||
@@ -355,8 +150,6 @@ typedef struct {
|
|||||||
|
|
||||||
static milter_Diag diag;
|
static milter_Diag diag;
|
||||||
|
|
||||||
staticforward PyTypeObject milter_ContextType;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
SMFICTX *ctx; /* libmilter thread state */
|
SMFICTX *ctx; /* libmilter thread state */
|
||||||
@@ -586,7 +379,7 @@ Sets the Python function invoked for each body chunk. There may\n\
|
|||||||
be multiple body chunks passed to the filter. End-of-lines are\n\
|
be multiple body chunks passed to the filter. End-of-lines are\n\
|
||||||
represented as received from SMTP (normally Carriage-Return/Line-Feed).\n\
|
represented as received from SMTP (normally Carriage-Return/Line-Feed).\n\
|
||||||
Function takes args (ctx, chunk) -> int\n\
|
Function takes args (ctx, chunk) -> int\n\
|
||||||
chunk -> String - body data";
|
chunk -> bytes - body data";
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
milter_set_body_callback(PyObject *self, PyObject *args) {
|
milter_set_body_callback(PyObject *self, PyObject *args) {
|
||||||
@@ -700,7 +493,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 +512,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 +533,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 +545,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 (s) return PyString_FromString(s);
|
#if PY_MAJOR_VERSION >= 3
|
||||||
return PyString_FromString("inet6:unknown");
|
if (s) return PyUnicode_FromString(s);
|
||||||
|
return PyUnicode_FromString("inet6:unknown");
|
||||||
|
#else
|
||||||
|
if (s) return PyString_FromString(s);
|
||||||
|
return PyString_FromString("inet6:unknown");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -832,7 +642,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 +703,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -950,20 +768,17 @@ milter_wrap_negotiate(SMFICTX *ctx,
|
|||||||
rc = _generic_wrapper(c, negotiate_callback, arglist);
|
rc = _generic_wrapper(c, negotiate_callback, arglist);
|
||||||
c->t = t;
|
c->t = t;
|
||||||
if (rc == SMFIS_CONTINUE) {
|
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 *pa[4] = { pf0,pf1,pf2,pf3 };
|
||||||
unsigned long fa[4] = { f0,f1,f2,f3 };
|
unsigned long fa[4] = { f0,f1,f2,f3 };
|
||||||
int len = PyList_Size(optlist);
|
int len = PyList_Size(optlist);
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < 4; ++i) {
|
for (i = 0; i < 4; ++i) {
|
||||||
*pa[i] = (i <= len)
|
*pa[i] = (i <= len)
|
||||||
? PyInt_AsUnsignedLongMask(PyList_GET_ITEM(optlist,i))
|
#if PY_MAJOR_VERSION >= 3
|
||||||
|
? PyLong_AsUnsignedLongMask(PyList_GET_ITEM(optlist,i))
|
||||||
|
#else
|
||||||
|
? PyInt_AsUnsignedLongMask(PyList_GET_ITEM(optlist,i))
|
||||||
|
#endif
|
||||||
: fa[i];
|
: fa[i];
|
||||||
}
|
}
|
||||||
if (PyErr_Occurred()) {
|
if (PyErr_Occurred()) {
|
||||||
@@ -971,7 +786,6 @@ milter_wrap_negotiate(SMFICTX *ctx,
|
|||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
rc = SMFIS_REJECT;
|
rc = SMFIS_REJECT;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
else if (rc != SMFIS_ALL_OPTS)
|
else if (rc != SMFIS_ALL_OPTS)
|
||||||
rc = SMFIS_REJECT;
|
rc = SMFIS_REJECT;
|
||||||
@@ -1331,7 +1145,7 @@ Change/delete a header in the message. \n\
|
|||||||
It is not checked for standards compliance; the mail filter\n\
|
It is not checked for standards compliance; the mail filter\n\
|
||||||
must ensure that no protocols are violated as a result of adding this header.\n\
|
must ensure that no protocols are violated as a result of adding this header.\n\
|
||||||
field - header field name\n\
|
field - header field name\n\
|
||||||
int - the Nth occurence of this header\n\
|
int - the Nth occurrence of this header\n\
|
||||||
value - header field value\n\
|
value - header field value\n\
|
||||||
field and value are strings.\n\
|
field and value are strings.\n\
|
||||||
This function can only be called from the EOM callback.";
|
This function can only be called from the EOM callback.";
|
||||||
@@ -1551,10 +1365,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 +1420,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 +1450,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 +1468,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 +1572,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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|
||||||
from sgmllib import SGMLParser as HTMLParser
|
if sys.version < '3.0.0':
|
||||||
|
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)
|
||||||
|
|||||||
-192
@@ -1,192 +0,0 @@
|
|||||||
%if 0%{?rhel} == 7
|
|
||||||
%define pythonbase python34
|
|
||||||
%else
|
|
||||||
%define pythonbase python3
|
|
||||||
%endif
|
|
||||||
%define __python python3
|
|
||||||
|
|
||||||
%define libdir %{_libdir}/pymilter
|
|
||||||
%{!?python_sitearch: %define python_sitearch %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib(1)")}
|
|
||||||
|
|
||||||
Summary: Python interface to sendmail milter API
|
|
||||||
Name: %{pythonbase}-pymilter
|
|
||||||
Version: 1.0.1
|
|
||||||
Release: 1%{dist}
|
|
||||||
Source: https://github.com/sdgathman/pymilter/archive/pymilter-%{version}.tar.gz
|
|
||||||
Source1: pymilter.te
|
|
||||||
# Patch miltermodule to python3
|
|
||||||
# FIXME: replace with reverse patch at some point (make py3 the default)
|
|
||||||
Patch: milter.patch
|
|
||||||
License: GPLv2+
|
|
||||||
Group: Development/Libraries
|
|
||||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
|
|
||||||
Url: http://www.bmsi.com/python/milter.html
|
|
||||||
# python-2.6.4 gets RuntimeError: not holding the import lock
|
|
||||||
Requires: %{pythonbase} >= 2.6.5, sendmail-milter >= 8.13
|
|
||||||
%if 0%{?fedora} >= 23
|
|
||||||
# Need python2.6 specific pydns, not the version for system python
|
|
||||||
Recommends: %{pythonbase}-pydns
|
|
||||||
%endif
|
|
||||||
# Needed for callbacks, not a core function but highly useful for milters
|
|
||||||
BuildRequires: ed, %{pythonbase}-devel, sendmail-devel >= 8.13
|
|
||||||
|
|
||||||
%description
|
|
||||||
This is a python extension module to enable python scripts to
|
|
||||||
attach to sendmail's libmilter functionality. Additional python
|
|
||||||
modules provide for navigating and modifying MIME parts, sending
|
|
||||||
DSNs, and doing CBV.
|
|
||||||
|
|
||||||
%package selinux
|
|
||||||
Summary: SELinux policy module for pymilter
|
|
||||||
Group: System Environment/Base
|
|
||||||
Requires: policycoreutils, selinux-policy, %{name}
|
|
||||||
BuildRequires: policycoreutils, checkpolicy
|
|
||||||
%if 0%{?epel} >= 6
|
|
||||||
BuildRequires: policycoreutils-python
|
|
||||||
%else
|
|
||||||
BuildRequires: policycoreutils-python-utils
|
|
||||||
%endif
|
|
||||||
|
|
||||||
%description selinux
|
|
||||||
SELinux policy module for using pymilter with sendmail with selinux enforcing
|
|
||||||
|
|
||||||
%prep
|
|
||||||
%setup -q -n pymilter-%{version}
|
|
||||||
%patch -p1 -b .py3
|
|
||||||
cp %{SOURCE1} pymilter.te
|
|
||||||
|
|
||||||
%build
|
|
||||||
env CFLAGS="$RPM_OPT_FLAGS" %{__python} setup.py build
|
|
||||||
checkmodule -m -M -o pymilter.mod pymilter.te
|
|
||||||
semodule_package -o pymilter.pp -m pymilter.mod
|
|
||||||
|
|
||||||
%install
|
|
||||||
rm -rf $RPM_BUILD_ROOT
|
|
||||||
%{__python} setup.py install --root=$RPM_BUILD_ROOT
|
|
||||||
mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/run/milter
|
|
||||||
mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/log/milter
|
|
||||||
mkdir -p $RPM_BUILD_ROOT%{libdir}
|
|
||||||
|
|
||||||
# install selinux modules
|
|
||||||
mkdir -p %{buildroot}%{_datadir}/selinux/targeted
|
|
||||||
cp -p pymilter.pp %{buildroot}%{_datadir}/selinux/targeted
|
|
||||||
|
|
||||||
%files
|
|
||||||
%defattr(-,root,root,-)
|
|
||||||
%doc README ChangeLog NEWS TODO CREDITS sample.py milter-template.py
|
|
||||||
%{python_sitearch}/*
|
|
||||||
%{libdir}
|
|
||||||
%dir %attr(0755,mail,mail) %{_localstatedir}/run/milter
|
|
||||||
%dir %attr(0755,mail,mail) %{_localstatedir}/log/milter
|
|
||||||
|
|
||||||
%files selinux
|
|
||||||
%doc pymilter.te
|
|
||||||
%{_datadir}/selinux/targeted/*
|
|
||||||
|
|
||||||
%clean
|
|
||||||
rm -rf $RPM_BUILD_ROOT
|
|
||||||
|
|
||||||
%post selinux
|
|
||||||
/usr/sbin/semodule -s targeted -i %{_datadir}/selinux/targeted/pymilter.pp \
|
|
||||||
&>/dev/null || :
|
|
||||||
|
|
||||||
%postun selinux
|
|
||||||
if [ $1 -eq 0 ] ; then
|
|
||||||
/usr/sbin/semodule -s targeted -r pymilter &> /dev/null || :
|
|
||||||
fi
|
|
||||||
|
|
||||||
%changelog
|
|
||||||
* Tue Sep 20 2016 Stuart Gathman <stuart@gathman.org> 1.0.1-1
|
|
||||||
- Support python3
|
|
||||||
|
|
||||||
* Sat Mar 1 2014 Stuart Gathman <stuart@gathman.org> 1.0-2
|
|
||||||
- Remove start.sh to track EPEL repository, suggest daemonize as replacement
|
|
||||||
- Selinux subpackage should not care about pymilter version
|
|
||||||
|
|
||||||
* Wed Jun 26 2013 Stuart Gathman <stuart@gathman.org> 1.0-1
|
|
||||||
- Allow ACCEPT as untrapped exception policy
|
|
||||||
- Optional dir for getaddrset and getaddrdict in Milter.config
|
|
||||||
- Show registered milter name in untrapped exception message.
|
|
||||||
- Include selinux subpackage
|
|
||||||
- Provide Milter.greylist export and Milter.greylist import to migrate data
|
|
||||||
|
|
||||||
* Sat Mar 9 2013 Stuart Gathman <stuart@bmsi.com> 0.9.8-1
|
|
||||||
- Add Milter.test module for unit testing milters.
|
|
||||||
- Fix typo that prevented setsymlist from being active.
|
|
||||||
- Change untrapped exception message to:
|
|
||||||
- "pymilter: untrapped exception in milter app"
|
|
||||||
|
|
||||||
* Thu Apr 12 2012 Stuart Gathman <stuart@bmsi.com> 0.9.7-1
|
|
||||||
- Raise RuntimeError when result != CONTINUE for @noreply and @nocallback
|
|
||||||
- Remove redundant table in miltermodule
|
|
||||||
- Fix CNAME chain duplicating TXT records in Milter.dns (from pyspf).
|
|
||||||
|
|
||||||
* Sat Feb 25 2012 Stuart Gathman <stuart@bmsi.com> 0.9.6-1
|
|
||||||
- Raise ValueError on unescaped '%' passed to setreply
|
|
||||||
- Grace time at end of Greylist window
|
|
||||||
|
|
||||||
* Fri Aug 19 2011 Stuart Gathman <stuart@bmsi.com> 0.9.5-1
|
|
||||||
- Print milter.error for invalid callback return type.
|
|
||||||
(Since stacktrace is empty, the TypeError exception is confusing.)
|
|
||||||
- Fix milter-template.py
|
|
||||||
- Tweak Milter.utils.addr2bin and Milter.dynip to handle IP6
|
|
||||||
|
|
||||||
* Tue Mar 02 2010 Stuart Gathman <stuart@bmsi.com> 0.9.4-1
|
|
||||||
- Handle IP6 in Milter.utils.iniplist()
|
|
||||||
- python-2.6
|
|
||||||
|
|
||||||
* Thu Jul 02 2009 Stuart Gathman <stuart@bmsi.com> 0.9.3-1
|
|
||||||
- Handle source route in Milter.utils.parse_addr()
|
|
||||||
- Fix default arg in chgfrom.
|
|
||||||
- Disable negotiate callback for libmilter < 8.14.3 (1,0,1)
|
|
||||||
|
|
||||||
* 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
|
|
||||||
- Split pymilter into its own CVS module
|
|
||||||
- Support chgfrom and addrcpt_par
|
|
||||||
- Support NS records in Milter.dns
|
|
||||||
|
|
||||||
* Mon Aug 25 2008 Stuart Gathman <stuart@bmsi.com> 0.8.10-2
|
|
||||||
- /var/run/milter directory must be owned by mail
|
|
||||||
|
|
||||||
* Mon Aug 25 2008 Stuart Gathman <stuart@bmsi.com> 0.8.10-1
|
|
||||||
- improved parsing into email and fullname (still 2 self test failures)
|
|
||||||
- implement no-DSN CBV, reduce full DSNs
|
|
||||||
|
|
||||||
* 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
|
|
||||||
- Remove spf dependency from dsn.py, add dns.py
|
|
||||||
|
|
||||||
* Fri Jan 05 2007 Stuart Gathman <stuart@bmsi.com> 0.8.8-1
|
|
||||||
- move AddrCache, parse_addr, iniplist to Milter package
|
|
||||||
- move parse_header to Milter.utils
|
|
||||||
- fix plock for missing source and can't change owner/group
|
|
||||||
- split out pymilter and pymilter-spf packages
|
|
||||||
- move milter apps to /usr/lib/pymilter
|
|
||||||
|
|
||||||
* Sat Nov 04 2006 Stuart Gathman <stuart@bmsi.com> 0.8.7-1
|
|
||||||
- SPF moved to pyspf RPM
|
|
||||||
|
|
||||||
* Tue May 23 2006 Stuart Gathman <stuart@bmsi.com> 0.8.6-2
|
|
||||||
- Support CBV timeout
|
|
||||||
+172
-62
@@ -1,46 +1,72 @@
|
|||||||
%define __python python2
|
# we don't want to provide private python extension libs
|
||||||
%if 0%{?rhel} == 6
|
%global sum Python interface to sendmail milter API
|
||||||
%define pythonbase python
|
%global __provides_exclude_from ^(%{python2_sitearch})/.*\\.so$
|
||||||
|
%if 0%{?epel} == 7
|
||||||
|
%global python3 python34
|
||||||
%else
|
%else
|
||||||
%define pythonbase python2
|
%global python3 python3
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
%define libdir %{_libdir}/pymilter
|
Summary: %{sum}
|
||||||
%{!?python_sitearch: %define python_sitearch %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib(1)")}
|
Name: python-pymilter
|
||||||
|
Version: 1.0.4
|
||||||
Summary: Python interface to sendmail milter API
|
Release: 1%{?dist}
|
||||||
Name: %{pythonbase}-pymilter
|
Url: http://bmsi.com/pymilter
|
||||||
Version: 1.0.1
|
|
||||||
Release: 1%{dist}
|
|
||||||
Source: https://github.com/sdgathman/pymilter/archive/pymilter-%{version}.tar.gz
|
Source: https://github.com/sdgathman/pymilter/archive/pymilter-%{version}.tar.gz
|
||||||
Source1: pymilter.te
|
Source1: tmpfiles-python-pymilter.conf
|
||||||
# Patch miltermodule to python3
|
# remove unit tests that require network for check
|
||||||
# FIXME: replace with reverse patch at some point (make py3 the default)
|
Patch: pymilter-check.patch
|
||||||
Patch: milter.patch
|
|
||||||
License: GPLv2+
|
License: GPLv2+
|
||||||
Group: Development/Libraries
|
Group: Development/Libraries
|
||||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
|
BuildRequires: python2-devel, %{python3}-devel, sendmail-devel >= 8.13
|
||||||
Url: http://www.bmsi.com/python/milter.html
|
|
||||||
# python-2.6.4 gets RuntimeError: not holding the import lock
|
# python-2.6.4 gets RuntimeError: not holding the import lock
|
||||||
Requires: %{pythonbase} >= 2.6.5, sendmail-milter >= 8.13
|
|
||||||
%if 0%{?fedora} >= 23
|
|
||||||
# Need python2.6 specific pydns, not the version for system python
|
# Need python2.6 specific pydns, not the version for system python
|
||||||
Recommends: %{pythonbase}-pydns
|
BuildRequires: gcc
|
||||||
%endif
|
|
||||||
# Needed for callbacks, not a core function but highly useful for milters
|
|
||||||
BuildRequires: ed, %{pythonbase}-devel, sendmail-devel >= 8.13
|
|
||||||
|
|
||||||
%description
|
%global _description\
|
||||||
This is a python extension module to enable python scripts to
|
This is a python extension module to enable python scripts to\
|
||||||
attach to sendmail's libmilter functionality. Additional python
|
attach to sendmail's libmilter functionality. Additional python\
|
||||||
modules provide for navigating and modifying MIME parts, sending
|
modules provide for navigating and modifying MIME parts, sending\
|
||||||
DSNs, and doing CBV.
|
DSNs, and doing CBV.
|
||||||
|
|
||||||
|
%description %_description
|
||||||
|
|
||||||
|
%package -n python2-pymilter
|
||||||
|
Summary: %{sum}
|
||||||
|
%if 0%{?epel} >= 6
|
||||||
|
Requires: python-pydns
|
||||||
|
%else
|
||||||
|
Requires: python2-pydns
|
||||||
|
%endif
|
||||||
|
Requires: %{name}-common = %{version}-%{release}
|
||||||
|
%{?python_provide:%python_provide python2-pymilter}
|
||||||
|
|
||||||
|
%description -n python2-pymilter %_description
|
||||||
|
|
||||||
|
%package -n %{python3}-pymilter
|
||||||
|
Summary: %{sum}
|
||||||
|
%if 0%{?fedora} >= 26
|
||||||
|
Requires: %{python3}-py3dns
|
||||||
|
%endif
|
||||||
|
Requires: %{name}-common = %{version}-%{release}
|
||||||
|
%{?python_provide:%python_provide %{python3}-pymilter}
|
||||||
|
|
||||||
|
%description -n %{python3}-pymilter %_description
|
||||||
|
|
||||||
|
%package common
|
||||||
|
Summary: Common files and directories for python milters
|
||||||
|
BuildArch: noarch
|
||||||
|
|
||||||
|
%description common
|
||||||
|
Common files and directories used for python milters
|
||||||
|
|
||||||
%package selinux
|
%package selinux
|
||||||
Summary: SELinux policy module for pymilter
|
Summary: SELinux policy module for pymilter
|
||||||
Group: System Environment/Base
|
Group: System Environment/Base
|
||||||
Requires: policycoreutils, selinux-policy, %{name}
|
Requires: policycoreutils, selinux-policy-targeted
|
||||||
BuildRequires: policycoreutils, checkpolicy
|
Requires: %{name} = %{version}-%{release}
|
||||||
|
BuildArch: noarch
|
||||||
|
BuildRequires: policycoreutils, checkpolicy, selinux-policy-devel
|
||||||
%if 0%{?epel} >= 6
|
%if 0%{?epel} >= 6
|
||||||
BuildRequires: policycoreutils-python
|
BuildRequires: policycoreutils-python
|
||||||
%else
|
%else
|
||||||
@@ -48,66 +74,150 @@ BuildRequires: policycoreutils-python-utils
|
|||||||
%endif
|
%endif
|
||||||
|
|
||||||
%description selinux
|
%description selinux
|
||||||
SELinux policy module for using pymilter with sendmail with selinux enforcing
|
Give sendmail_t additional access to stream sockets used to communicate
|
||||||
|
with milters.
|
||||||
|
|
||||||
%prep
|
%prep
|
||||||
%setup -q -n pymilter-%{version}
|
%setup -q -n pymilter-pymilter-%{version}
|
||||||
cp %{SOURCE1} pymilter.te
|
%patch -p1 -b .check
|
||||||
|
|
||||||
%build
|
%build
|
||||||
env CFLAGS="$RPM_OPT_FLAGS" %{__python} setup.py build
|
%py2_build
|
||||||
|
#patch -p1 -b -z .py3 <milter.patch # not needed since 1.0.3
|
||||||
|
%py3_build
|
||||||
checkmodule -m -M -o pymilter.mod pymilter.te
|
checkmodule -m -M -o pymilter.mod pymilter.te
|
||||||
semodule_package -o pymilter.pp -m pymilter.mod
|
semodule_package -o pymilter.pp -m pymilter.mod
|
||||||
|
|
||||||
%install
|
%install
|
||||||
rm -rf $RPM_BUILD_ROOT
|
%py2_install
|
||||||
%{__python} setup.py install --root=$RPM_BUILD_ROOT
|
%py3_install
|
||||||
mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/run/milter
|
|
||||||
mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/log/milter
|
mkdir -p %{buildroot}/run/milter
|
||||||
mkdir -p $RPM_BUILD_ROOT%{libdir}
|
mkdir -p %{buildroot}%{_localstatedir}/log/milter
|
||||||
|
mkdir -p %{buildroot}%{_libexecdir}/milter
|
||||||
|
mkdir -p %{buildroot}%{_prefix}/lib/tmpfiles.d
|
||||||
|
install -m 0644 %{SOURCE1} %{buildroot}%{_prefix}/lib/tmpfiles.d/%{name}.conf
|
||||||
|
|
||||||
# install selinux modules
|
# install selinux modules
|
||||||
mkdir -p %{buildroot}%{_datadir}/selinux/targeted
|
mkdir -p %{buildroot}%{_datadir}/selinux/targeted
|
||||||
cp -p pymilter.pp %{buildroot}%{_datadir}/selinux/targeted
|
cp -p pymilter.pp %{buildroot}%{_datadir}/selinux/targeted
|
||||||
|
|
||||||
%files
|
%check
|
||||||
%defattr(-,root,root,-)
|
py2path=$(ls -d build/lib.linux-*-2.*)
|
||||||
|
py3path=$(ls -d build/lib.linux-*-3.*)
|
||||||
|
PYTHONPATH=${py2path}:. python2 test.py &&
|
||||||
|
PYTHONPATH=${py3path}:. python3 test.py
|
||||||
|
|
||||||
|
%files -n python2-pymilter
|
||||||
|
%license COPYING
|
||||||
%doc README ChangeLog NEWS TODO CREDITS sample.py milter-template.py
|
%doc README ChangeLog NEWS TODO CREDITS sample.py milter-template.py
|
||||||
%{python_sitearch}/*
|
%{python2_sitearch}/*
|
||||||
%{libdir}
|
|
||||||
%dir %attr(0755,mail,mail) %{_localstatedir}/run/milter
|
%files -n %{python3}-pymilter
|
||||||
|
%license COPYING
|
||||||
|
%doc README ChangeLog NEWS TODO CREDITS sample.py milter-template.py
|
||||||
|
%{python3_sitearch}/*
|
||||||
|
|
||||||
|
%files common
|
||||||
|
%dir %{_libexecdir}/milter
|
||||||
|
%{_prefix}/lib/tmpfiles.d/%{name}.conf
|
||||||
%dir %attr(0755,mail,mail) %{_localstatedir}/log/milter
|
%dir %attr(0755,mail,mail) %{_localstatedir}/log/milter
|
||||||
|
%dir %attr(0755,mail,mail) /run/milter
|
||||||
|
|
||||||
%files selinux
|
%files selinux
|
||||||
%doc pymilter.te
|
%doc pymilter.te
|
||||||
%{_datadir}/selinux/targeted/*
|
%{_datadir}/selinux/targeted/*
|
||||||
|
|
||||||
%clean
|
|
||||||
rm -rf $RPM_BUILD_ROOT
|
|
||||||
|
|
||||||
%post selinux
|
%post selinux
|
||||||
/usr/sbin/semodule -s targeted -i %{_datadir}/selinux/targeted/pymilter.pp \
|
%{_sbindir}/semodule -s targeted -i %{_datadir}/selinux/targeted/pymilter.pp \
|
||||||
&>/dev/null || :
|
&>/dev/null || :
|
||||||
|
|
||||||
%postun selinux
|
%postun selinux
|
||||||
if [ $1 -eq 0 ] ; then
|
if [ $1 -eq 0 ] ; then
|
||||||
/usr/sbin/semodule -s targeted -r pymilter &> /dev/null || :
|
%{_sbindir}/semodule -s targeted -r pymilter &> /dev/null || :
|
||||||
fi
|
fi
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
* Tue Sep 20 2016 Stuart Gathman <stuart@gathman.org> 1.0.1-1
|
* Wed Apr 17 2019 Stuart Gathman <stuart@gathman.org> - 1.0.4-1
|
||||||
- Support python3
|
- New upstream release: cleanup unused files, additional platform support
|
||||||
|
- Minor doc updates
|
||||||
|
|
||||||
* Sat Mar 1 2014 Stuart Gathman <stuart@gathman.org> 1.0-2
|
* Sun Dec 23 2018 Stuart Gathman <stuart@gathman.org> - 1.0.3-1
|
||||||
- Remove start.sh to track EPEL repository, suggest daemonize as replacement
|
- New upstream release
|
||||||
- Selinux subpackage should not care about pymilter version
|
- patch step for python3 no longer required in build
|
||||||
|
|
||||||
* Wed Jun 26 2013 Stuart Gathman <stuart@gathman.org> 1.0-1
|
* Sat Aug 4 2018 Stuart Gathman <stuart@gathman.org> - 1.0.2-4
|
||||||
- Allow ACCEPT as untrapped exception policy
|
- Add unit tests to %%check
|
||||||
- Optional dir for getaddrset and getaddrdict in Milter.config
|
|
||||||
- Show registered milter name in untrapped exception message.
|
* Sat Aug 4 2018 Stuart Gathman <stuart@gathman.org> - 1.0.2-3
|
||||||
- Include selinux subpackage
|
- use libexec instead of libdir
|
||||||
- Provide Milter.greylist export and Milter.greylist import to migrate data
|
|
||||||
|
* Sat Aug 4 2018 Stuart Gathman <stuart@gathman.org> - 1.0.2-2
|
||||||
|
- add python34 subpackage on el7
|
||||||
|
|
||||||
|
* Sat Aug 4 2018 Stuart Gathman <stuart@gathman.org> - 1.0.2-1
|
||||||
|
- build for both python2 and python3
|
||||||
|
- add selinux policy allowing sendmail_t access to milters
|
||||||
|
|
||||||
|
* Tue Jul 17 2018 Miro Hrončok <mhroncok@redhat.com> - 1.0-13
|
||||||
|
- Update Python macros to new packaging standards
|
||||||
|
(See https://fedoraproject.org/wiki/Changes/Move_usr_bin_python_into_separate_package)
|
||||||
|
|
||||||
|
* Sat Jul 14 2018 Fedora Release Engineering <releng@fedoraproject.org> - 1.0-12
|
||||||
|
- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
|
||||||
|
|
||||||
|
* Fri Feb 09 2018 Iryna Shcherbina <ishcherb@redhat.com> - 1.0-11
|
||||||
|
- Update Python 2 dependency declarations to new packaging standards
|
||||||
|
(See https://fedoraproject.org/wiki/FinalizingFedoraSwitchtoPython3)
|
||||||
|
|
||||||
|
* Fri Feb 09 2018 Fedora Release Engineering <releng@fedoraproject.org> - 1.0-10
|
||||||
|
- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
|
||||||
|
|
||||||
|
* Fri Feb 09 2018 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 1.0-9
|
||||||
|
- Escape macros in %%changelog
|
||||||
|
|
||||||
|
* Sat Aug 19 2017 Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> - 1.0-8
|
||||||
|
- Python 2 binary package renamed to python2-pymilter
|
||||||
|
See https://fedoraproject.org/wiki/FinalizingFedoraSwitchtoPython3
|
||||||
|
|
||||||
|
* Thu Aug 03 2017 Fedora Release Engineering <releng@fedoraproject.org> - 1.0-7
|
||||||
|
- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild
|
||||||
|
|
||||||
|
* Thu Jul 27 2017 Fedora Release Engineering <releng@fedoraproject.org> - 1.0-6
|
||||||
|
- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild
|
||||||
|
>>>>>>> 021796e51e5919812f1c300d1830ef9ed378db2d
|
||||||
|
|
||||||
|
* Sat Feb 11 2017 Fedora Release Engineering <releng@fedoraproject.org> - 1.0-5
|
||||||
|
- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
|
||||||
|
|
||||||
|
* Tue Jul 19 2016 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.0-4
|
||||||
|
- https://fedoraproject.org/wiki/Changes/Automatic_Provides_for_Python_RPM_Packages
|
||||||
|
|
||||||
|
* Thu Feb 04 2016 Fedora Release Engineering <releng@fedoraproject.org> - 1.0-3
|
||||||
|
- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild
|
||||||
|
|
||||||
|
* Thu Jun 18 2015 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.0-2
|
||||||
|
- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild
|
||||||
|
|
||||||
|
* Sat Sep 27 2014 Paul Wouters <pwouters@redhat.com> - 1.0-1
|
||||||
|
- Updated to 1.0
|
||||||
|
- Use tmpfiles and /run
|
||||||
|
|
||||||
|
* Sun Aug 17 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.9.8-6
|
||||||
|
- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild
|
||||||
|
|
||||||
|
* Sat Jun 07 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.9.8-5
|
||||||
|
- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild
|
||||||
|
|
||||||
|
* Fri Jan 10 2014 Paul Wouters <pwouters@redhat.com> - 0.9.8-4
|
||||||
|
- Add COPYING
|
||||||
|
- Fix buildroot macros and dist macro
|
||||||
|
|
||||||
|
* Fri Jan 10 2014 Paul Wouters <pwouters@redhat.com> - 0.9.8-3
|
||||||
|
- rebuilt with proper file permission
|
||||||
|
|
||||||
|
* Tue Jan 07 2014 Paul Wouters <pwouters@redhat.com> - 0.9.8-2
|
||||||
|
- Fixup for fedora release
|
||||||
|
|
||||||
* Sat Mar 9 2013 Stuart Gathman <stuart@bmsi.com> 0.9.8-1
|
* Sat Mar 9 2013 Stuart Gathman <stuart@bmsi.com> 0.9.8-1
|
||||||
- Add Milter.test module for unit testing milters.
|
- Add Milter.test module for unit testing milters.
|
||||||
@@ -115,13 +225,13 @@ fi
|
|||||||
- Change untrapped exception message to:
|
- Change untrapped exception message to:
|
||||||
- "pymilter: untrapped exception in milter app"
|
- "pymilter: untrapped exception in milter app"
|
||||||
|
|
||||||
* Thu Apr 12 2012 Stuart Gathman <stuart@bmsi.com> 0.9.7-1
|
* Sat Feb 25 2012 Stuart Gathman <stuart@bmsi.com> 0.9.7-1
|
||||||
- Raise RuntimeError when result != CONTINUE for @noreply and @nocallback
|
- Raise RuntimeError when result != CONTINUE for @noreply and @nocallback
|
||||||
- Remove redundant table in miltermodule
|
- Remove redundant table in miltermodule
|
||||||
- Fix CNAME chain duplicating TXT records in Milter.dns (from pyspf).
|
- Fix CNAME chain duplicating TXT records in Milter.dns (from pyspf).
|
||||||
|
|
||||||
* Sat Feb 25 2012 Stuart Gathman <stuart@bmsi.com> 0.9.6-1
|
* Sat Feb 25 2012 Stuart Gathman <stuart@bmsi.com> 0.9.6-1
|
||||||
- Raise ValueError on unescaped '%' passed to setreply
|
- Raise ValueError on unescaped '%%' passed to setreply
|
||||||
- Grace time at end of Greylist window
|
- Grace time at end of Greylist window
|
||||||
|
|
||||||
* Fri Aug 19 2011 Stuart Gathman <stuart@bmsi.com> 0.9.5-1
|
* Fri Aug 19 2011 Stuart Gathman <stuart@bmsi.com> 0.9.5-1
|
||||||
|
|||||||
@@ -33,18 +33,25 @@ class sampleMilter(Milter.Milter):
|
|||||||
self.fp = None
|
self.fp = None
|
||||||
self.bodysize = 0
|
self.bodysize = 0
|
||||||
self.id = Milter.uniqueID()
|
self.id = Milter.uniqueID()
|
||||||
|
self.user = None
|
||||||
|
|
||||||
# multiple messages can be received on a single connection
|
# multiple messages can be received on a single connection
|
||||||
# envfrom (MAIL FROM in the SMTP protocol) seems to mark the start
|
# envfrom (MAIL FROM in the SMTP protocol) seems to mark the start
|
||||||
# of each message.
|
# of each message.
|
||||||
|
@Milter.symlist('{auth_authen}')
|
||||||
@Milter.noreply
|
@Milter.noreply
|
||||||
def envfrom(self,f,*str):
|
def envfrom(self,f,*str):
|
||||||
"start of MAIL transaction"
|
"start of MAIL transaction"
|
||||||
self.log("mail from",f,str)
|
|
||||||
self.fp = BytesIO()
|
self.fp = BytesIO()
|
||||||
self.tempname = None
|
self.tempname = None
|
||||||
self.mailfrom = f
|
self.mailfrom = f
|
||||||
self.bodysize = 0
|
self.bodysize = 0
|
||||||
|
self.user = self.getsymval('{auth_authen}')
|
||||||
|
self.auth_type = self.getsymval('{auth_type}')
|
||||||
|
if self.user:
|
||||||
|
self.log("user",self.user,"sent mail from",f,str)
|
||||||
|
else:
|
||||||
|
self.log("mail from",f,str)
|
||||||
return Milter.CONTINUE
|
return Milter.CONTINUE
|
||||||
|
|
||||||
def envrcpt(self,to,*str):
|
def envrcpt(self,to,*str):
|
||||||
|
|||||||
@@ -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.1',
|
setup(name = "pymilter", version = '1.0.4',
|
||||||
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
|
||||||
@@ -28,9 +25,9 @@ sending DSNs or doing CBVs.
|
|||||||
author="Jim Niemira",
|
author="Jim Niemira",
|
||||||
author_email="urmane@urmane.org",
|
author_email="urmane@urmane.org",
|
||||||
maintainer="Stuart D. Gathman",
|
maintainer="Stuart D. Gathman",
|
||||||
maintainer_email="stuart@bmsi.com",
|
maintainer_email="stuart@gathman.org",
|
||||||
license="GPL",
|
license="GPL",
|
||||||
url="http://www.bmsi.com/python/milter.html",
|
url="https://pythonhosted.org/milter/",
|
||||||
py_modules=modules,
|
py_modules=modules,
|
||||||
packages = ['Milter'],
|
packages = ['Milter'],
|
||||||
ext_modules=[
|
ext_modules=[
|
||||||
@@ -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'],
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
appname="$1"
|
|
||||||
script="${2:-${appname}}"
|
|
||||||
datadir="/var/lib/milter"
|
|
||||||
logdir="/var/log/milter"
|
|
||||||
piddir="/var/run/milter"
|
|
||||||
libdir="/usr/lib/pymilter"
|
|
||||||
python="python2.4"
|
|
||||||
exec >>${logdir}/${appname}.log 2>&1
|
|
||||||
if test -s ${datadir}/${script}.py; then
|
|
||||||
cd ${datadir} # use version in data dir if it exists for debugging
|
|
||||||
elif test -s ${logdir}/${script}.py; then
|
|
||||||
cd ${logdir} # use version in log dir if it exists for debugging
|
|
||||||
else
|
|
||||||
cd ${libdir}
|
|
||||||
fi
|
|
||||||
|
|
||||||
${python} ${script}.py &
|
|
||||||
echo $! >${piddir}/${appname}.pid
|
|
||||||
+46
-14
@@ -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,16 +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_type}','batcomputer')
|
||||||
|
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(rc == Milter.ACCEPT)
|
self.assertTrue(milter.user == 'batman',"getsymval failed")
|
||||||
self.failUnless(milter._bodyreplaced,"Message body not replaced")
|
# setsymlist not working in TestBase
|
||||||
|
#self.assertTrue(milter.auth_type != 'batcomputer',"setsymlist failed")
|
||||||
|
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()
|
||||||
@@ -31,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()
|
||||||
@@ -41,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()
|
||||||
|
|||||||
+12
-7
@@ -21,30 +21,35 @@ 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?='
|
||||||
h = Milter.utils.parse_header(s)
|
h = Milter.utils.parse_header(s)
|
||||||
self.assertEqual(h,b'Last Few Coldplay Album Artworks Available\x00')
|
self.assertEqual(h,b'Last Few Coldplay Album Artworks Available\x00')
|
||||||
|
|
||||||
|
@unittest.expectedFailure
|
||||||
|
def testParseAddress(self):
|
||||||
|
s = Milter.utils.parseaddr('a(WRONG)@b')
|
||||||
|
self.assertEqual(s,('WRONG', 'a@b'))
|
||||||
|
|
||||||
def suite():
|
def suite():
|
||||||
s = unittest.makeSuite(AddrCacheTestCase,'test')
|
s = unittest.makeSuite(AddrCacheTestCase,'test')
|
||||||
s.addTest(doctest.DocTestSuite(Milter.utils))
|
s.addTest(doctest.DocTestSuite(Milter.utils))
|
||||||
|
|||||||
Reference in New Issue
Block a user