Compare commits
40 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 |
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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."""
|
||||
|
||||
# XXX This only supports those SGML features used by HTML.
|
||||
|
||||
+9
-2
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
+48
-75
@@ -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
|
||||
|
||||
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
|
||||
```
|
||||
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
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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, ¶ms))
|
||||
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__[] =
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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=[
|
||||
|
||||
@@ -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()
|
||||
@@ -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
@@ -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$
|
||||
# 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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user