Compare commits

...

40 Commits

Author SHA1 Message Date
Stuart D. Gathman 5675adeb3c Work with berkeleydb and try importing it first. 2024-05-29 18:08:30 -04:00
Stuart D. Gathman 35416dfc46 Support MTAs with colon separator 2024-05-29 11:15:26 -04:00
Stuart D. Gathman c33de064ee Merge branch 'master' of github.com:sdgathman/pymilter 2024-05-29 11:14:19 -04:00
Jaime Marquínez Ferrándiz 1c05080768 Remove calls to the deprecated method "assertEquals" (#57)
It has been removed in python 3.12
2024-03-11 22:27:18 -04:00
Stuart D. Gathman dce7c0080a Adapt to MTAs that use ':' as key terminator and/or add null char to
key/value.
2022-07-15 19:00:41 -04:00
Stuart D. Gathman 7deec90a59 Drop paragraph about python 2.0 compatibility 2021-12-31 00:56:54 -05:00
Stuart D. Gathman c73b533acb Update README.md to satisfy PiPy 2021-12-31 00:49:54 -05:00
Stuart D. Gathman 102e042a38 Fix deprecation warnings 2021-12-15 23:35:02 -05:00
Stuart D. Gathman 7a5c942d54 Make PY_SSIZE_T_CLEAN 2021-11-09 18:43:26 -05:00
Barry de Graaff 1b2c48d8a9 adding required argument message_from_binary_file (#43)
* adding required argument message_from_binary_file

message_from_binary_file now has a required argument `policy` 
https://docs.python.org/3/library/email.parser.html#email.message_from_binary_file

In a future version a default will be added, but as of now, calling message_from_binary_file without policy will throw an error and not work
https://docs.python.org/3/library/email.parser.html#email.parser.BytesFeedParser

Also added some code to show how to iterate through attachments and how to get attachment filename, type, extension, attachment data and attachment object

* Update template.py
2021-11-01 18:46:59 -04:00
Stuart D. Gathman 866201ca52 Merge branch 'master' of github.com:sdgathman/pymilter 2021-07-14 08:43:05 -04:00
Stuart D. Gathman 2744175998 Use a more generally runnable socketname 2021-07-14 08:39:58 -04:00
Barry de Graaff 599277855c Update template.py (#40)
fixes `milter.error: cannot opensocket`

My name is NOT stuart
2021-07-14 08:37:24 -04:00
Stuart D. Gathman e7592c6a96 Fix some test cases and bugs found on py3 bmsmilter install. 2021-01-09 21:49:13 -05:00
Stuart D. Gathman 7df236127b Add sendmail style MTA policy query module 2020-07-04 22:29:28 -04:00
Stuart D. Gathman 1234869dd6 Add MTA policy module 2020-07-04 21:22:56 -04:00
Stuart D. Gathman f37090371b Milter.utils.parse_header returns string, other py3 fixes 2020-06-25 19:47:38 -04:00
Stuart D. Gathman 7ea839cfb1 Update docs for @decode callback. 2020-06-18 19:53:43 -04:00
Stuart D. Gathman 879e65bc31 bytes optimization 2020-06-18 16:40:54 -04:00
Stuart D. Gathman 4c7c76fca4 First cut at encoding error decorator 2020-06-17 13:55:26 -04:00
Stuart D. Gathman 132e8326b5 Consistently use surrogate escape by default. 2020-06-17 12:54:58 -04:00
Stuart D. Gathman 0efddd316a Fix bug found by pysrs unit tests 2020-06-16 20:10:33 -04:00
Stuart D. Gathman 588153078b New config test case with fix 2020-06-16 19:50:07 -04:00
Stuart D. Gathman 4ed12cf825 config test passes 2020-06-16 19:40:37 -04:00
Stuart D. Gathman c098f9df6b Test case for Milter.config (still failing) 2020-06-16 18:45:03 -04:00
Stuart D. Gathman cdae26af47 More py3 fixes, switch to setuptools. 2020-06-12 16:51:38 -04:00
Stuart D. Gathman bf3108b938 Fix doco nit from qzrrbz@github 2020-06-02 15:08:21 -04:00
Stuart D. Gathman d5f9f86bba Use utf-8 decoding with surrogateescape for invalid utf-8 for env and hdr val 2020-04-23 15:52:20 -04:00
Stuart D. Gathman 805825438c Change __version__ 2020-04-21 18:27:49 -04:00
Stuart D. Gathman 3844751ef0 Envelope and header values consistently decoded from utf-8. See RFC 8616. 2020-04-21 18:20:16 -04:00
Stuart D. Gathman 2b1b01c1ef Decode header values as utf-8. Add header_bytes method which can be overridden. 2020-04-21 15:07:27 -04:00
John Vandenberg 222afcd555 setup.py: Update URL (#36)
good catch.  A shame pythonhosted went away.
2020-01-27 10:25:28 -05:00
Stuart D. Gathman 4251fbc151 Work in python2 and python3 2019-08-27 22:29:38 -04:00
Stuart D. Gathman 4749f0ff98 Change header callback to bytes, but default Milter to convert
to str with surrogateescape.
2019-08-27 21:47:26 -04:00
Stuart D. Gathman 18186a3c11 Read header encoding tests as binary 2019-08-27 19:24:06 -04:00
Stuart D. Gathman a01f598e37 Test case for invalid utf8 bytes in header. 2019-08-20 18:37:35 -04:00
Stuart D. Gathman d0d45c5e61 ZipFile.setpassword() takes bytes in python3 2019-08-12 17:46:45 -04:00
Stuart D. Gathman a1714f4838 Get denatured viruses from encrypted zip to avoid alarming scanners,
this allows test cases to pass again after last commit.
2019-08-10 20:34:03 -04:00
Stuart D. Gathman edc2f73375 Store denatured viruses in encrypted zip, password "denatured".
This is for those complaining about signature scanners triggering on them.
2019-07-09 12:05:18 -04:00
Stuart D. Gathman 6373f8965b Release 1.0.4 2019-04-19 10:32:09 -04:00
38 changed files with 664 additions and 809 deletions
+1 -1
View File
@@ -31,7 +31,7 @@ PROJECT_NAME = pymilter
# This could be handy for archiving the generated documentation or
# if some version control system is used.
PROJECT_NUMBER = 1.0.2
PROJECT_NUMBER = 1.0.5
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put.
+1 -1
View File
@@ -2,7 +2,7 @@ include COPYING
include TODO
include NEWS
include CREDITS
include README
include README.md
include ChangeLog
include MANIFEST.in
include testsample.py
+90 -7
View File
@@ -9,11 +9,12 @@
# This code is under the GNU General Public License. See COPYING for details.
from __future__ import print_function
__version__ = '1.0.4'
__version__ = '1.0.5'
import os
import re
import milter
import sys
try:
import thread
except:
@@ -180,15 +181,41 @@ def noreply(func):
wrapper.milter_protocol = nr_mask
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.
# By default, the MTA sends all macros defined for a callback.
# If some or all of these are unused, the bandwidth can be saved
# by listing the ones that are used.
# @since 1.0.2
def symlist(*syms):
if len(syms) > 5:
raise ValueError('@symlist limited to 5 macros by MTA: '+func.__name__)
def setsyms(func):
if len(syms) > 5:
raise ValueError('@symlist limited to 5 macros by MTA: '+func.__name__)
if func.__name__ not in MACRO_CALLBACKS:
raise ValueError('@symlist applied to non-symlist method: '+func.__name__)
func._symlist = syms
@@ -320,6 +347,20 @@ class Base(object):
# this almost always results in terminating the connection.
@nocallback
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
# <a href="milter_api/xxfi_envfrom.html">
# xxfi_envfrom</a> callback.
@@ -330,7 +371,21 @@ class Base(object):
# <a href="http://tools.ietf.org/html/rfc5322">RFC 5322</a>,
# see @link #header the header callback @endlink.
@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
# <a href="milter_api/xxfi_envrcpt.html">
# xxfi_envrcpt</a> callback.
@@ -348,7 +403,30 @@ class Base(object):
# @since 0.9.2
@nocallback
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.
# @param field name decoded as ascii
# @param value field value decoded as utf-8 on python3
@nocallback
def header(self,field,value): return CONTINUE
## Called at the blank line that terminates the header fields.
@@ -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
# arbitrary keywords without crashing. We do provide envcallback and
# dictfromlist to make parsing the ESMTP args convenient.
milter.set_envfrom_callback(lambda ctx,*str: ctx.getpriv().envfrom(*str))
milter.set_envrcpt_callback(lambda ctx,*str: ctx.getpriv().envrcpt(*str))
milter.set_header_callback(lambda ctx,fld,val: ctx.getpriv().header(fld,val))
if sys.version < '3.0.0':
milter.set_envfrom_callback(lambda ctx,*s: ctx.getpriv().envfrom(*s))
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_body_callback(lambda ctx,chunk: ctx.getpriv().body(chunk))
milter.set_eom_callback(lambda ctx: ctx.getpriv().eom())
+12 -7
View File
@@ -1,4 +1,7 @@
from ConfigParser import ConfigParser
try:
from configparser import ConfigParser
except:
from ConfigParser import ConfigParser
import os.path
class MilterConfigParser(ConfigParser):
@@ -11,10 +14,10 @@ class MilterConfigParser(ConfigParser):
# which screws up iterating over all options in a section.
# Worse, passing "defaults" with vars= overrides the config file!
# So we roll our own defaults.
def get(self,sect,opt):
if not self.has_option(sect,opt) and opt in self.defaults:
def get(self,sect,opt,fallback=None,**kwds):
if not self.has_option(sect,opt) and not fallback and opt in self.defaults:
return self.defaults[opt]
return ConfigParser.get(self,sect,opt)
return ConfigParser.get(self,sect,opt,fallback=fallback,**kwds)
def getlist(self,sect,opt):
if self.has_option(sect,opt):
@@ -31,7 +34,8 @@ class MilterConfigParser(ConfigParser):
if q.startswith('file:'):
domain = q[5:].lower()
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:
user,domain = q.split('@')
d.setdefault(domain.lower(),[]).append(user)
@@ -49,8 +53,9 @@ class MilterConfigParser(ConfigParser):
addr = addr.strip()
if addr.startswith('file:'):
fname = os.path.join(dir,addr[5:])
for a in open(fname,'r').read().split():
d[a] = q
with open(fname,'r') as fp:
for a in fp.read().split():
d[a] = q
else:
d[addr] = q
return d
+5 -2
View File
@@ -72,10 +72,13 @@
from __future__ import print_function
import smtplib
import socket
from email.Message import Message
try:
from email.message import Message
except:
from email.Message import Message
import Milter
import Milter.dns as dns
import time
import dns
## Send DSN.
# Try the published MX names in order, rejecting obviously bogus entries
+87
View File
@@ -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
View File
@@ -1,4 +1,3 @@
"""A parser for SGML, using the derived class as a static DTD."""
# XXX This only supports those SGML features used by HTML.
+9 -2
View File
@@ -46,6 +46,11 @@ class TestBase(object):
## The macros returned by protocol stage
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):
for i in msg: print(i,file=self.logfp,end=None)
print(file=self.logfp)
@@ -59,7 +64,7 @@ class TestBase(object):
def getsymval(self,name):
stage = self._stage
if stage >= 0:
if stage is not None and stage >= 0:
syms = self._symlist[stage]
if syms is not None and name not in syms:
return None
@@ -178,7 +183,7 @@ class TestBase(object):
if rc != Milter.CONTINUE: return rc
# header
for h,val in msg.items():
rc = self.header(h,val)
rc = self.header_bytes(h,val.encode())
if rc != Milter.CONTINUE: return rc
# eoh
self._stage = Milter.M_EOH
@@ -204,6 +209,8 @@ class TestBase(object):
self._body.write(header)
self._body.write(b'\n\n')
self._body.write(body)
self.close()
self._close()
return rc
## Feed an email contained in a file to the %milter.
+17 -2
View File
@@ -6,6 +6,7 @@
from __future__ import print_function
from socket import AF_INET,AF_INET6
from sys import version as VERSION
import time
import mime
try:
@@ -14,7 +15,6 @@ except:
from StringIO import StringIO as BytesIO
import Milter
from Milter import utils
import mime
## Milter context for unit testing %milter applications.
# A substitute for milter.milterContext that can be passed to
@@ -219,7 +219,22 @@ class TestCtx(object):
return rc
def _header(self,fld,val):
return self._priv.header(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:
+2 -1
View File
@@ -216,7 +216,8 @@ def parse_header(val):
u.append(s.decode())
else:
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'):
try:
return u.encode(enc)
+47 -74
View File
@@ -1,5 +1,4 @@
Abstract
--------
# Abstract
This is a python extension module to enable python scripts to attach to
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
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
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
requires that the library functions it uses are thread safe; which is true
for the operating systems libmilter has been developed and tested on. On
some operating systems this requires special compile time options (e.g.,
not just -pthread). libmilter is currently known to work on (modulo problems
in the pthread support of some specific versions):
FreeBSD 3.x, 4.x
SunOS 5.x (x >= 5)
AIX 4.3.x
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.
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[a]:
```
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
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
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,
add something like:
```
INPUT_MAIL_FILTER(`pythonfilter', `S=local:/home/username/pythonsock, F=T, T=C:5m;S:20s;R:5m;E:5m')
```
to sendmail.mc instead.
Not-so-quick Installation
-------------------------
# Not-so-quick Installation
First install Sendmail. Make sure you read libmilter/README in the Sendmail
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
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
to filter incoming email. Add lines similar to the following to
sendmail.cf:
```
O InputMailFilters=pythonfilter
Xpythonfilter, S=local:/home/username/pythonsock
```
The "O" line tells sendmail which filters to use in what order; here we're
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.
NB: The above lines can be added in your .mc file with this line:
```
INPUT_MAIL_FILTER(`pythonfilter', `S=local:/home/username/pythonsock')
```
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
IPv6 Notes
----------
```
# IPv6 Notes
The IPv6 protocol is supported if your operation system supports it
and if sendmail was compiled with IPv6 support. To determine if your
sendmail supports IPv6, run "sendmail -d0" and check for the NETINET6
compilation option. To compile sendmail with IPv6 support, add this
declaration to your site.config.m4 before building it:
```
APPENDDEF(`confENVDEF', `-DNETINET6=1')
```
IPv6 support can show up in two places; the communications socket
between the milter and sendmail processes and in the host address
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
IPv6-only setup, your sendmail configuration should contain a line
similar to (first line is for sendmail.mc, second is sendmail.cf):
```
DAEMON_OPTIONS(`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
other over IPv6, you may use the "inet6" socket name prefix, as in:
```
Xpythonfilter, S=inet6:1234@fec0:0:0:7::5c
```
The connect() callback method in the milter class will pass the
IPv6-specific information in the 'hostaddr' argument as a tuple. Note
that the type of this value is dependent upon the protocol family, and
is not compatible with IPv4 connections. Therefore you should always
check the family argument before attempting to use the hostaddr
argument. A quick example showing this follows:
```
import socket
...
class ipv6awareMilter(Milter.Milter):
...
def connect(self,hostname,family,hostaddr):
if family==socket.AF_INET:
ipaddress, port = hostaddr
elif family==socket.AF_INET6:
ip6address, port, flowinfo, scopeid = hostaddr
elif family==socket.AF_UNIX:
socketpath = hostaddr
if family==socket.AF_INET:
ipaddress, port = hostaddr
elif family==socket.AF_INET6:
ip6address, port, flowinfo, scopeid = hostaddr
elif family==socket.AF_UNIX:
socketpath = hostaddr
```
The hostname argument is always safe to use without interpreting the
protocol family. For IPv6 connections for which the hostname can not
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
scopeid socket attributes, both of which are integers.
Authors
-------
# Authors
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
+2
View File
@@ -1,2 +1,4 @@
Test case for Milter/dsn.py
Lookup exact RFC syntax of real name / email and make
Milter.utils.parse_addr() pass all unit tests.
+6 -2
View File
@@ -1,14 +1,18 @@
web:
doxygen
test -L doc/html/milter_api || ln -sf /usr/share/doc/sendmail-milter-devel doc/html/milter_api
rsync -ravKk doc/html/ bmsi.com:/var/www/html/pymilter
rsync -ravKk doc/html/ pymilter.org:/var/www/html/milter/pymilter
cd doc/html; zip -r ../../doc .
VERSION=1.0.4
VERSION=1.0.5
PKG=pymilter-$(VERSION)
SRCTAR=$(PKG).tar.gz
$(SRCTAR):
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)
+18 -7
View File
@@ -43,6 +43,7 @@ $ python setup.py help
#error MAX_ML_REPLY must be 1 or 11 or 32
#endif
#define _FFR_MULTILINE (MAX_ML_REPLY > 1)
#define PY_SSIZE_T_CLEAN
//#include <pthread.h> // shouldn't be needed - use Python API
#include <Python.h> // Python C API
@@ -250,7 +251,7 @@ static const char milter_set_flags__doc__[] =
Set flags for filter capabilities; OR of one or more of:\n\
ADDHDRS - filter may add headers\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\
DELRCPT - filter may delete recipients\n\
CHGHDRS - filter may change/delete headers";
@@ -490,7 +491,7 @@ _generic_wrapper(milter_ContextObject *self, PyObject *cb, PyObject *arglist) {
int retval;
if (arglist == NULL) return _report_exception(self);
result = PyEval_CallObject(cb, arglist);
result = PyObject_CallObject(cb, arglist);
Py_DECREF(arglist);
if (result == NULL) return _report_exception(self);
#if PY_MAJOR_VERSION >= 3
@@ -643,7 +644,7 @@ generic_env_wrapper(SMFICTX *ctx, PyObject*cb, char **argv) {
/* There's some error checking performed in do_mkvalue() for a string */
/* that's not currently done here - it probably should be */
#if PY_MAJOR_VERSION >= 3
PyObject *o = PyUnicode_FromStringAndSize(argv[i], strlen(argv[i]));
PyObject *o = PyBytes_FromStringAndSize(argv[i], strlen(argv[i]));
#else
PyObject *o = PyString_FromStringAndSize(argv[i], strlen(argv[i]));
#endif
@@ -674,7 +675,12 @@ milter_wrap_header(SMFICTX *ctx, char *headerf, char *headerv) {
if (header_callback == NULL) return SMFIS_CONTINUE;
c = _get_context(ctx);
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);
#endif
return _generic_wrapper(c, header_callback, arglist);
}
@@ -704,9 +710,9 @@ milter_wrap_body(SMFICTX *ctx, u_char *bodyp, size_t bodylen) {
if (!c) return SMFIS_TEMPFAIL;
/* Unclear whether this should be s#, z#, or t# */
#if PY_MAJOR_VERSION >= 3
arglist = Py_BuildValue("(Oy#)", c, bodyp, bodylen);
arglist = Py_BuildValue("(Oy#)", c, bodyp, (Py_ssize_t)bodylen);
#else
arglist = Py_BuildValue("(Os#)", c, bodyp, bodylen);
arglist = Py_BuildValue("(Os#)", c, bodyp, (Py_ssize_t)bodylen);
#endif
return _generic_wrapper(c, body_callback, arglist);
}
@@ -898,7 +904,10 @@ milter_main(PyObject *self, PyObject *args) {
return NULL;
}
/* libmilter requires thread support */
#if PY_VERSION_HEX < 0x03070000
/* called in Py_Initialize beginning with 3.7 */
PyEval_InitThreads();
#endif
/* let other threads run while in smfi_main() */
interp = PyThreadState_Get()->interp;
_main = PyEval_SaveThread(); /* must be done before smfi_main() */
@@ -1129,6 +1138,8 @@ milter_chgfrom(PyObject *self, PyObject *args) {
SMFICTX *ctx;
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, &params))
return NULL;
ctx = _find_context(self);
@@ -1225,7 +1236,7 @@ can only be called from the EOM callback.";
static PyObject *
milter_replacebody(PyObject *self, PyObject *args) {
char *bodyp;
int bodylen;
Py_ssize_t bodylen;
SMFICTX *ctx;
PyThreadState *t;
@@ -1234,7 +1245,7 @@ milter_replacebody(PyObject *self, PyObject *args) {
if (ctx == NULL) return NULL;
t = PyEval_SaveThread();
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__[] =
+25 -92
View File
@@ -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
# This module provides a "defang" function to replace naughty attachments.
#
@@ -108,10 +24,11 @@ import email
from email.message import Message
try:
from email.generator import BytesGenerator
from email import message_from_binary_file
from email import message_from_binary_file, encoders
except:
from email.generator import Generator as BytesGenerator
from email import message_from_file as message_from_binary_file
from email import Encoders as encoders
from email.utils import quote
if not getattr(Message,'as_bytes',None):
@@ -208,7 +125,7 @@ class MimeMessage(Message):
"""Return a list of (attr,name) pairs of attributes that IE might
interpret as a name - and hence decide to execute this message."""
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):
# It's an RFC 2231 encoded parameter
newvalue = _unquotevalue(val)
@@ -278,6 +195,11 @@ class MimeMessage(Message):
def get_payload(self,i=None,decode=False):
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():
self.set_payload([msg])
return Message.get_payload(self,i,decode)
@@ -296,7 +218,11 @@ class MimeMessage(Message):
if t == 'message/rfc822' or t.startswith('multipart/'):
if not self.submsg:
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)
self.submsg = email.message_from_string(txt,MimeMessage)
for part in self.submsg.walk():
@@ -357,17 +283,24 @@ def check_name(msg,savname=None,ckname=check_ext,scan_zip=False):
msg["Content-Type"] = "text/plain; name="+name
return Milter.CONTINUE
def check_attachments(msg,check):
def check_attachments(msg,check,lev=None):
"""Scan attachments.
msg MimeMessage
check function(MimeMessage): int
Return CONTINUE, REJECT, ACCEPT
"""
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():
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
lev[-1] += 1
return Milter.CONTINUE
print('chk',lev,msg.get_content_type())
return check(msg)
# save call context for Python without nested_scopes
@@ -534,7 +467,7 @@ def check_html(msg,savname=None):
if htmlfilter.modified:
msg.set_payload(out) # remove embedded scripts
del msg["content-transfer-encoding"]
email.Encoders.encode_quopri(msg)
encoders.encode_quopri(msg)
return Milter.CONTINUE
if __name__ == '__main__':
@@ -548,7 +481,7 @@ if __name__ == '__main__':
return Milter.CONTINUE
for fname in sys.argv[1:]:
fp = open(fname,'rb')
msg = message_from_file(fp)
with open(fname,'rb') as fp:
msg = message_from_file(fp)
email.iterators._structure(msg)
check_attachments(msg,_list_attach)
+27
View File
@@ -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))
+5 -6
View File
@@ -2,7 +2,7 @@
%global sum Python interface to sendmail milter API
%global __provides_exclude_from ^(%{python2_sitearch})/.*\\.so$
%if 0%{?epel} == 7
%global python3 python34
%global python3 python36
%else
%global python3 python3
%endif
@@ -13,7 +13,7 @@ Version: 1.0.4
Release: 1%{?dist}
Url: http://bmsi.com/pymilter
Source: https://github.com/sdgathman/pymilter/archive/pymilter-%{version}.tar.gz
Source1: tmpfiles-python-pymilter.conf
#Source1: tmpfiles-python-pymilter.conf
# remove unit tests that require network for check
Patch: pymilter-check.patch
License: GPLv2+
@@ -79,11 +79,10 @@ with milters.
%prep
%setup -q -n pymilter-pymilter-%{version}
%patch -p1 -b .check
#patch -p1 -b .check
%build
%py2_build
#patch -p1 -b -z .py3 <milter.patch # not needed since 1.0.3
%py3_build
checkmodule -m -M -o pymilter.mod pymilter.te
semodule_package -o pymilter.pp -m pymilter.mod
@@ -95,8 +94,8 @@ semodule_package -o pymilter.pp -m pymilter.mod
mkdir -p %{buildroot}/run/milter
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
#mkdir -p %{buildroot}%{_prefix}/lib/tmpfiles.d
#install -m 0644 %{SOURCE1} %{buildroot}%{_prefix}/lib/tmpfiles.d/%{name}.conf
# install selinux modules
mkdir -p %{buildroot}%{_datadir}/selinux/targeted
+16 -8
View File
@@ -24,7 +24,12 @@ class sampleMilter(Milter.Milter):
def log(self,*msg):
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()
def __init__(self):
@@ -62,28 +67,31 @@ class sampleMilter(Milter.Milter):
self.log("rcpt to",to,str)
return Milter.CONTINUE
@Milter.decode('bytes')
def header(self,name,val):
lname = name.lower()
if lname == 'subject':
# even if we wanted the Taiwanese spam, we can't read Chinese
# (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.setreply('550','','Go away spammer')
return Milter.REJECT
# check for common spam keywords
if val.find("$$$") >= 0 or val.find("XXX") >= 0 \
or val.find("!!!") >= 0 or val.find("FREE") >= 0:
if val.find(b"$$$") >= 0 or val.find(b"XXX") >= 0 \
or val.find(b"!!!") >= 0 or val.find(b"FREE") >= 0:
self.log('REJECT: %s: %s' % (name,val))
#self.setreply('550','','Go away spammer')
return Milter.REJECT
# check for spam that pretends to be legal
lval = val.lower()
if lval.startswith("adv:") or lval.startswith("adv.") \
or lval.find('viagra') >= 0:
if lval.startswith(b"adv:") or lval.startswith(b"adv.") \
or lval.find(b'viagra') >= 0:
self.log('REJECT: %s: %s' % (name,val))
return Milter.REJECT
@@ -95,7 +103,7 @@ class sampleMilter(Milter.Milter):
# check for common bulk mailers
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.setreply('550','','Go away spammer')
return Milter.REJECT
@@ -104,7 +112,7 @@ class sampleMilter(Milter.Milter):
if lname in ('subject','x-mailer'):
self.log('%s: %s' % (name,val))
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
def eoh(self):
+8 -9
View File
@@ -1,10 +1,13 @@
import os
import sys
from distutils.core import setup, Extension
from setuptools import setup, Extension
if sys.version < '2.6.5':
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.
# On slackware and debian, leave it out entirely. It depends
# on how libmilter was built by the sendmail package.
@@ -14,20 +17,16 @@ libdirs = ["/usr/lib/libmilter"] # needed for Debian
modules = ["mime"]
# NOTE: importing Milter to obtain version fails when milter.so not built
setup(name = "pymilter", version = '1.0.4',
setup(name = "pymilter", version = '1.0.5',
description="Python interface to sendmail milter API",
long_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, and
sending DSNs or doing CBVs.
""",
long_description=long_description,
long_description_content_type='text/markdown',
author="Jim Niemira",
author_email="urmane@urmane.org",
maintainer="Stuart D. Gathman",
maintainer_email="stuart@gathman.org",
license="GPL",
url="https://pythonhosted.org/milter/",
url="https://www.pymilter.org/",
py_modules=modules,
packages = ['Milter'],
ext_modules=[
+45 -17
View File
@@ -1,6 +1,7 @@
## To roll your own milter, create a class that extends Milter.
# See the pymilter project at http://bmsi.com/python/milter.html
# based on Sendmail's milter API
# This is a useless example to show basic features of Milter.
# 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.
## Milter calls methods of your class at milter events.
@@ -10,21 +11,26 @@
from __future__ import print_function
import Milter
try:
from StringIO import StringIO
from StringIO import StringIO as BytesIO
except:
from io import StringIO
from io import BytesIO
import time
import email
from email import message_from_binary_file
from email import policy
import mimetypes
import os
import sys
from socket import AF_INET, AF_INET6
from Milter.utils import parse_addr
if True:
# for logging process - usually not needed
from multiprocessing import Process as Thread, Queue
else:
from threading import Thread
from Queue import Queue
logq = Queue(maxsize=4)
logq = None
class myMilter(Milter.Base):
@@ -78,9 +84,10 @@ class myMilter(Milter.Base):
# NOTE: self.fp is only an *internal* copy of message data. You
# must use addheader, chgheader, replacebody to change the message
# on the MTA.
self.fp = StringIO()
self.fp = BytesIO()
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
@@ -95,12 +102,12 @@ class myMilter(Milter.Base):
@Milter.noreply
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
@Milter.noreply
def eoh(self):
self.fp.write("\n") # terminate headers
self.fp.write(b'\n') # terminate headers
return Milter.CONTINUE
@Milter.noreply
@@ -110,7 +117,16 @@ class myMilter(Milter.Base):
def eom(self):
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()
# example of adding a Bcc:
self.addrcpt('<%s>' % 'spy@example.com')
@@ -128,13 +144,14 @@ class myMilter(Milter.Base):
## === Support Functions ===
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():
while True:
t = logq.get()
if not t: break
msg,id,ts = t
def logmsg(msg,id,ts):
print("%s [%d]" % (time.strftime('%Y%b%d %H:%M:%S',time.localtime(ts)),id),
end=None)
# 2005Oct13 02:34:11 [1] msg1 msg2 msg3 ...
@@ -142,12 +159,20 @@ def background():
print()
sys.stdout.flush()
def background():
while True:
t = logq.get()
if not t: break
logmsg(*t)
## ===
def main():
bt = Thread(target=background)
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
# Register to have the Milter factory create instances of your class:
Milter.factory = myMilter
@@ -163,4 +188,7 @@ def main():
print("%s bms milter shutdown" % time.strftime('%Y%b%d %H:%M:%S'))
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()
+4
View File
@@ -3,6 +3,8 @@ import testmime
import testsample
import testutils
import testgrey
import testcfg
import testpolicy
import os
def suite():
@@ -11,6 +13,8 @@ def suite():
s.addTest(testsample.suite())
s.addTest(testutils.suite())
s.addTest(testgrey.suite())
s.addTest(testcfg.suite())
s.addTest(testpolicy.suite())
return s
if __name__ == '__main__':
+10
View File
@@ -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
+32
View File
@@ -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
+6
View File
@@ -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?
BIN
View File
Binary file not shown.
-72
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -1,34 +1,10 @@
# $Log$
# Revision 1.5 2011/06/09 17:27:42 customdesigned
# Documentation updates.
#
# 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
#
# @author Stuart D. Gathman <stuart@bmsi.com>
# Copyright 2005,2009,2020 Business Management Systems, Inc.
# This code is under the GNU General Public License. See COPYING for details.
from __future__ import print_function
import unittest
import mime
import zipfile
import socket
try:
from StringIO import StringIO
@@ -51,6 +27,14 @@ hostname = socket.gethostname()
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
def testParam(self):
plist = mime._parseparam('; boundary="----=_NextPart_000_4e56_490d_48e3"')
@@ -90,8 +74,12 @@ class MimeTestCase(unittest.TestCase):
def testDefang(self,vname='virus1',part=1,
fname='LOVE-LETTER-FOR-YOU.TXT.vbs'):
with open('test/'+vname,"rb") as fp:
msg = mime.message_from_file(fp)
try:
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)
self.assertTrue(msg.ismodified(),"virus not removed")
oname = vname + '.out'
@@ -118,7 +106,7 @@ class MimeTestCase(unittest.TestCase):
# virus6 has no parts - the virus is directly inline
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)
mime.defang(msg)
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(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=""):
result = StringIO()
filter = mime.HTMLScriptFilter(result)
+57
View File
@@ -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:]
+63 -10
View File
@@ -1,7 +1,9 @@
import unittest
import Milter
import sample
import template
import mime
import zipfile
from Milter.test import TestBase
from Milter.testctx import TestCtx
@@ -12,6 +14,48 @@ class TestMilter(TestBase,sample.sampleMilter):
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
@@ -20,16 +64,18 @@ class BMSMilterTestCase(unittest.TestCase):
ctx._setsymval('j','mailhost')
rc = ctx._connect()
self.assertTrue(rc == Milter.CONTINUE)
rc = ctx._feedMsg(fname)
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.assertEquals(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
open('test/'+fname+".tstout","wb").write(fp.getvalue())
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)
@@ -44,14 +90,16 @@ class BMSMilterTestCase(unittest.TestCase):
milter.setsymval('j','mailhost')
rc = milter.connect()
self.assertTrue(rc == Milter.CONTINUE)
rc = milter.feedMsg(fname)
with self.zf.open(fname) as fp:
rc = milter.feedFile(fp)
self.assertTrue(milter.user == 'batman',"getsymval failed")
# setsymlist not working in TestBase
#self.assertTrue(milter.auth_type != 'batcomputer',"setsymlist failed")
self.assertTrue(rc == Milter.ACCEPT)
self.assertTrue(milter._bodyreplaced,"Message body not replaced")
fp = milter._body
open('test/'+fname+".tstout","wb").write(fp.getvalue())
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)
@@ -66,7 +114,8 @@ class BMSMilterTestCase(unittest.TestCase):
self.assertTrue(rc == Milter.ACCEPT)
self.assertFalse(milter._bodyreplaced,"Milter needlessly replaced 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()
def testDefang2(self):
@@ -75,18 +124,22 @@ class BMSMilterTestCase(unittest.TestCase):
rc = milter.feedMsg('samp1')
self.assertTrue(rc == Milter.ACCEPT)
self.assertFalse(milter._bodyreplaced,"Milter needlessly replaced body.")
rc = milter.feedMsg("virus3")
with self.zf.open("virus3") as fp:
rc = milter.feedFile(fp)
self.assertTrue(rc == Milter.ACCEPT)
self.assertTrue(milter._bodyreplaced,"Message body not replaced")
fp = milter._body
open("test/virus3.tstout","wb").write(fp.getvalue())
with open("test/virus3.tstout","wb") as f:
f.write(fp.getvalue())
#self.assertTrue(fp.getvalue() == open("test/virus3.out","r").read())
rc = milter.feedMsg("virus6")
with self.zf.open("virus6") as fp:
rc = milter.feedFile(fp)
self.assertTrue(rc == Milter.ACCEPT)
self.assertTrue(milter._bodyreplaced,"Message body not replaced")
self.assertTrue(milter._headerschanged,"Message headers not adjusted")
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()
def suite(): return unittest.makeSuite(BMSMilterTestCase,'test')
+6 -3
View File
@@ -24,11 +24,11 @@ class AddrCacheTestCase(unittest.TestCase):
self.assertTrue(cache.has_key('foo@bar.com'))
self.assertTrue(not cache.has_key('hello@bar.com'))
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()
self.assertTrue(len(s) == 2)
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
cache['temp@bar.com'] = None
self.assertTrue(not cache['temp@bar.com'])
@@ -43,7 +43,10 @@ class AddrCacheTestCase(unittest.TestCase):
def testParseHeader(self):
s='=?UTF-8?B?TGFzdCBGZXcgQ29sZHBsYXkgQWxidW0gQXJ0d29ya3MgQXZhaWxhYmxlAA?='
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
def testParseAddress(self):