Release 0.7.1
This commit is contained in:
@@ -8,15 +8,12 @@ import milter
|
||||
import thread
|
||||
|
||||
from milter import ACCEPT,CONTINUE,REJECT,DISCARD,TEMPFAIL, \
|
||||
set_flags, setdbg, \
|
||||
set_flags, setdbg, setbacklog, settimeout, \
|
||||
ADDHDRS, CHGBODY, ADDRCPT, DELRCPT, CHGHDRS, \
|
||||
V1_ACTS, V2_ACTS, CURR_ACTS
|
||||
|
||||
try:
|
||||
from milter import QUARANTINE
|
||||
except:
|
||||
#print 'No QUARANTINE support'
|
||||
pass
|
||||
try: from milter import QUARANTINE
|
||||
except: pass
|
||||
|
||||
_seq_lock = thread.allocate_lock()
|
||||
_seq = 0
|
||||
@@ -100,8 +97,10 @@ class Milter:
|
||||
def getsymval(self,sym):
|
||||
return self.__ctx.getsymval(sym)
|
||||
|
||||
def setreply(self,rcode,xcode,msg):
|
||||
return self.__ctx.setreply(rcode,xcode,msg)
|
||||
# If sendmail does not support setmlreply, then only the
|
||||
# first msg line is used.
|
||||
def setreply(self,rcode,xcode=None,msg=None,*ml):
|
||||
return self.__ctx.setreply(rcode,xcode,msg,*ml)
|
||||
|
||||
# Milter methods which can only be called from eom callback.
|
||||
def addheader(self,field,value):
|
||||
@@ -119,6 +118,8 @@ class Milter:
|
||||
def replacebody(self,body):
|
||||
return self.__ctx.replacebody(body)
|
||||
|
||||
# When quarantined, a message goes into the mailq as if to be delivered,
|
||||
# but delivery is deferred until the message is unquarantined.
|
||||
def quarantine(self,reason):
|
||||
return self.__ctx.quarantine(reason)
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
Here is a history of user visible changes to Python milter.
|
||||
|
||||
0.7.1 Handle modifying mislabeled multipart messages without an exception
|
||||
Support setbacklog, setmlreply
|
||||
Allow multi-recipient CBV
|
||||
Return TEMPFAIL for SPF softfail
|
||||
0.7.0 SPF check hello name
|
||||
Move pythonsock to /var/run/milter
|
||||
Move milter.cfg to /etc/mail/pymilter.cfg
|
||||
|
||||
@@ -1,17 +1,6 @@
|
||||
Message not saved for following traceback:
|
||||
Traceback (most recent call last):
|
||||
File "/usr/lib/python2.3/site-packages/Milter.py", line 188, in <lambda>
|
||||
milter.set_eom_callback(lambda ctx: ctx.getpriv().eom())
|
||||
File "bms.py", line 935, in eom
|
||||
msg.dump(out)
|
||||
File "/usr/lib/python2.3/site-packages/mime.py", line 347, in dump
|
||||
g.flatten(self,unixfrom=unixfrom)
|
||||
File "/var/tmp/python2.3-2.3.3-root/usr/lib/python2.3/email/Generator.py", line 102, in flatten
|
||||
File "/var/tmp/python2.3-2.3.3-root/usr/lib/python2.3/email/Generator.py", line 130, in _write
|
||||
File "/var/tmp/python2.3-2.3.3-root/usr/lib/python2.3/email/Generator.py", line 156, in _dispatch
|
||||
File "/var/tmp/python2.3-2.3.3-root/usr/lib/python2.3/email/Generator.py", line 199, in _handle_text
|
||||
TypeError: string payload expected: <type 'list'>
|
||||
------------
|
||||
Move milter,Milter,mime,spf modules to pymilter
|
||||
milter package will have bms.py application
|
||||
|
||||
spf.py has no recursion bound on CNAME lookup
|
||||
Support SMTP AUTH and disable SPF checks when connection is authorized.
|
||||
Web admin interface
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
#!/usr/bin/env python
|
||||
# A simple milter.
|
||||
# $Log$
|
||||
# Revision 1.117 2004/08/23 02:27:53 stuart
|
||||
# Allow multi rcpt CBV. Add some multiline replies.
|
||||
#
|
||||
# Revision 1.116 2004/08/20 22:27:52 stuart
|
||||
# Generate TEMPFAIL for SPF softfail.
|
||||
#
|
||||
# Revision 1.115 2004/08/19 20:55:49 stuart
|
||||
# Always show reversed SRS path.
|
||||
# Check if encodings are an ASCII superset. Some messages were encoded as
|
||||
# BIG5 and getting rejected even though chars were all in ascii subset.
|
||||
#
|
||||
# Revision 1.114 2004/07/27 00:40:12 stuart
|
||||
# Make reject on no PTR optional.
|
||||
#
|
||||
@@ -435,7 +446,10 @@ def parse_header(val):
|
||||
u = []
|
||||
for s,enc in h:
|
||||
if enc:
|
||||
try:
|
||||
u.append(unicode(s,enc))
|
||||
except LookupError:
|
||||
u.append(unicode(s))
|
||||
else:
|
||||
u.append(unicode(s))
|
||||
u = ''.join(u)
|
||||
@@ -443,13 +457,14 @@ def parse_header(val):
|
||||
try:
|
||||
return u.encode(enc)
|
||||
except UnicodeError: continue
|
||||
except UnicodeDecodeError:
|
||||
return val
|
||||
except LookupError:
|
||||
except UnicodeDecodeError: pass
|
||||
except LookupError: pass
|
||||
return val
|
||||
|
||||
class bmsMilter(Milter.Milter):
|
||||
"Milter to replace attachments poisonous to Windows with a WARNING message."
|
||||
"""Milter to replace attachments poisonous to Windows with a WARNING message,
|
||||
check SPF, and other anti-forgery features, and implement wiretapping
|
||||
and smart alias redirection."""
|
||||
|
||||
def log(self,*msg):
|
||||
print "%s [%d]" % (time.strftime('%Y%b%d %H:%M:%S'),self.id),
|
||||
@@ -592,8 +607,13 @@ class bmsMilter(Milter.Milter):
|
||||
# check hello name via spf
|
||||
hres,hcode,htxt = spf.check(self.connectip,'',self.hello_name)
|
||||
if hres in ('deny','fail','neutral','softfail'):
|
||||
self.log('REJECT: hello SPF: %s %i %s' % (hres,hcode,htxt))
|
||||
self.setreply('550','5.7.1',htxt)
|
||||
self.log('REJECT: hello SPF: %s 550 %s' % (hres,htxt))
|
||||
self.setreply('550','5.7.1',htxt,
|
||||
"The hostname given in your MTA's HELO response is not listed",
|
||||
"as a legitimate MTA in the SPF records for your domain.",
|
||||
"If you get this bounce, the message was not in fact a forgery,",
|
||||
"and you should notify your email administrator of the problem."
|
||||
)
|
||||
return Milter.REJECT
|
||||
if spf_best_guess:
|
||||
#self.log('SPF: no record published, guessing')
|
||||
@@ -612,10 +632,26 @@ class bmsMilter(Milter.Milter):
|
||||
self.log('REJECT: SPF %s %i %s' % (res,code,txt))
|
||||
self.setreply(str(code),'5.7.1',txt)
|
||||
return Milter.REJECT
|
||||
if res == 'softfail':
|
||||
self.log('TEMPFAIL: SPF %s 450 %s' % (res,txt))
|
||||
self.setreply('450','4.3.0',
|
||||
'SPF softfail: will keep trying until your SPF record is fixed.',
|
||||
'If you get this Delivery Status Notice, your email was probably',
|
||||
'legitimate. Your administrator has published SPF records in a',
|
||||
'testing mode. The SPF record reported your email as a forgery,',
|
||||
'which is a mistake if you are reading this. Please notify your',
|
||||
'administrator of the problem.'
|
||||
)
|
||||
return Milter.TEMPFAIL
|
||||
if res == 'neutral' and q.o in spf_reject_neutral:
|
||||
self.log('REJECT: SPF neutral for',q.s)
|
||||
self.setreply('550','5.7.1',
|
||||
'mail from %s must pass SPF: http://spf.pobox.com/why.html' % q.o
|
||||
'mail from %s must pass SPF: http://spf.pobox.com/why.html' % q.o,
|
||||
'The %s domain is one that spammers love to forge. Due to' % q.o,
|
||||
'the volume of forged mail, we can only accept mail that',
|
||||
'the SPF record for %s explicitly designates as legitimate.' % q.o,
|
||||
'Sending your email through the recommended outgoing SMTP',
|
||||
'servers for %s should accomplish this.' % q.o
|
||||
)
|
||||
return Milter.REJECT
|
||||
if res == 'error':
|
||||
@@ -639,17 +675,15 @@ class bmsMilter(Milter.Milter):
|
||||
if self.mailfrom == '<>' or self.canon_from.startswith('postmaster@') \
|
||||
or self.canon_from.startswith('mailer-daemon@'):
|
||||
if self.recipients:
|
||||
self.log('REJECT: Multiple bounce recipients')
|
||||
self.setreply('550','5.7.1','Multiple bounce recipients')
|
||||
return Milter.REJECT
|
||||
if srs and not (self.internal_connection or self.trusted_relay) \
|
||||
and domain == srs_fwdomain:
|
||||
self.data_allowed = False
|
||||
if srs and domain == srs_fwdomain:
|
||||
oldaddr = '@'.join(parse_addr(to))
|
||||
try:
|
||||
newaddr = srs.reverse(oldaddr)
|
||||
# Currently, a sendmail map reverses SRS. We just log it here.
|
||||
self.log("srs rcpt:",newaddr)
|
||||
except:
|
||||
if not (self.internal_connection or self.trusted_relay):
|
||||
if srsre.match(oldaddr):
|
||||
self.log("REJECT: srs spoofed:",oldaddr)
|
||||
self.setreply('550','5.7.1','Invalid SRS signature')
|
||||
@@ -748,8 +782,12 @@ class bmsMilter(Milter.Milter):
|
||||
|
||||
def header(self,name,hval):
|
||||
if not self.data_allowed:
|
||||
if len(self.recipients) > 1:
|
||||
self.log('REJECT: Multiple bounce recipients')
|
||||
self.setreply('550','5.7.1','Multiple bounce recipients')
|
||||
else:
|
||||
self.log('REJECT: bounce with no SRS encoding')
|
||||
self.setreply('550','5.7.1',"I did not send you this message.")
|
||||
self.setreply('550','5.7.1',"I did not send you that message.")
|
||||
return Milter.REJECT
|
||||
lname = name.lower()
|
||||
# decode near ascii text to unobfuscate
|
||||
@@ -757,8 +795,8 @@ class bmsMilter(Milter.Milter):
|
||||
if not self.internal_connection:
|
||||
# even if we wanted the Taiwanese spam, we can't read Chinese
|
||||
if block_chinese and lname == 'subject':
|
||||
if hval.startswith('=?big5') or hval.startswith('=?ISO-2022-JP'):
|
||||
self.log('REJECT: %s: %s' % (name,hval))
|
||||
if val.startswith('=?big5') or val.startswith('=?ISO-2022-JP'):
|
||||
self.log('REJECT: %s: %s' % (name,val))
|
||||
self.setreply('550','5.7.1',"We don't understand chinese")
|
||||
return Milter.REJECT
|
||||
rc = self.check_header(name,val)
|
||||
|
||||
+34
-17
@@ -1,52 +1,66 @@
|
||||
# features intended to filter or block incoming mail
|
||||
[milter]
|
||||
# the socket used to communicate with sendmail. Must match sendmail.cf
|
||||
;socket=/var/run/milter/pythonsock
|
||||
# where to save original copies of defanged and failed messages
|
||||
tempdir = /var/log/milter/save
|
||||
# how long to wait for a response from sendmail before giving up
|
||||
;timeout=600
|
||||
|
||||
# do virus scanning on attached messages also
|
||||
scan_rfc822 = 1
|
||||
# can be CPU intensive
|
||||
# Comment out scripts in HTML attachments. Can be CPU intensive.
|
||||
scan_html = 0
|
||||
# reject asian fonts because we can't read them
|
||||
# reject messages with asian fonts because we can't read them
|
||||
block_chinese = 1
|
||||
# users who hate forwarded mail
|
||||
# list users who hate forwarded mail
|
||||
;block_forward = egghead@mycorp.com, busybee@mycorp.com
|
||||
log_headers = 0
|
||||
# Reject mail for domains mentioned unless user is mentioned here also
|
||||
;check_user = joe@mycorp.com, mary@mycorp.com, file:bigcorp.com
|
||||
# porn words are case insensitive
|
||||
# reject mail with these case insensitive strings in the subject
|
||||
porn_words = penis, breast, pussy, horse cock, porn, xenical, diet pill, d1ck,
|
||||
vi*gra, vi-a-gra, viag, tits, p0rn, hunza, horny, sexy, c0ck,
|
||||
p-e-n-i-s, hydrocodone, vicodin, xanax, vicod1n, x@nax, diazepam,
|
||||
v1@gra, xan@x, cialis, ci@lis, frëe, xãnax, valíum, vãlium, via-gra,
|
||||
x@n3x, vicod3n, penís, c0d1n, phentermine, en1arge, dip1oma, v1codin
|
||||
# spam words are case sensitive
|
||||
# reject mail with these case sensitive strings in the subject
|
||||
spam_words = $$$, !!!, XXX, FREE, HGH
|
||||
|
||||
# connection ips and hostnames are matched against this glob style list
|
||||
# to recognize internal senders
|
||||
;internal_connect = 192.168.*.*
|
||||
|
||||
# mail that is not an internal_connect and claims to be from an
|
||||
# internal domain is rejected.
|
||||
# internal domain is rejected. You should enable SPF instead if you can.
|
||||
# SPF is much more comprehensive and flexible.
|
||||
;internal_domains = mycorp.com
|
||||
|
||||
# connections from a trusted relay can trust the first Received header
|
||||
# SPF checks are bypassed for internal connections and trusted relays.
|
||||
;trusted_relay = 1.2.3.4, 66.12.34.56
|
||||
|
||||
# reject external senders with hello names no legit external sender would use
|
||||
# SPF will do this also, but listing your own domain and mailserver here
|
||||
# will save some DNS lookups when rejecting certain viruses.
|
||||
;hello_blacklist = mycorp.com, 66.12.34.56
|
||||
|
||||
# See http://bmsi.com/python/pysrs.html for details
|
||||
[srs]
|
||||
config=/etc/mail/pysrs.cfg
|
||||
# SRS options can be set here also, but must match the sendmail plugin
|
||||
;secret="shhhh!"
|
||||
;maxage=21
|
||||
;hashlength=4
|
||||
;database=/var/log/milter/srsdata
|
||||
;fwdomain = mydomain.com
|
||||
# turn this on after a grace period
|
||||
# turn this on after a grace period to reject spoofed DSNs
|
||||
reject_spoofed = 0
|
||||
|
||||
# See http://spf.pobox.com for more info on SPF.
|
||||
[spf]
|
||||
# namespace where SPF records can be supplied for domains without one
|
||||
# records are search for under _spf.domain.com
|
||||
# records are searched for under _spf.domain.com
|
||||
;delegate = domain.com
|
||||
# domains where a neutral SPF result should cause mail to be rejected
|
||||
;reject_neutral = aol.com
|
||||
@@ -57,9 +71,9 @@ reject_spoofed = 0
|
||||
|
||||
# features intended to clean up outgoing mail
|
||||
[scrub]
|
||||
# domains that stupidly block visible private nodes
|
||||
# domains that block visible private nodes
|
||||
;hide_path = jcpenney.com
|
||||
# block, don't just replace with warning, viruses from these domains
|
||||
# reject, don't just replace with warning, viruses from these domains
|
||||
;reject_virus_from = mycorp.com
|
||||
|
||||
# features intended for spying on users and coworkers
|
||||
@@ -88,16 +102,18 @@ blind = 1
|
||||
;walter1 = cust@othercorp.com,walter@bigcorp.com,boss@bigcorp.com,
|
||||
; walter@bigcorp.com
|
||||
|
||||
# See http://bmsi.com/python/dspam.html
|
||||
[dspam]
|
||||
# Select a well moderated dspam dictionary to reject spammy headers
|
||||
# dspam-python must be installed to use: http://bmsi.com/python/dspam.html
|
||||
# Select a well moderated dspam dictionary to reject spammy headers.
|
||||
# To filter on the entire message, use the full setup below.
|
||||
# only EXTERNAL messages are dspam filtered
|
||||
;dspam_dict=/var/lib/dspam/moderator.dict
|
||||
|
||||
# Opt-opt recipients from dspam screening and header triage
|
||||
;dspam_exempt=getitall@mycorp.com
|
||||
# Do not scan mail (ostensibly) from these senders
|
||||
;dspam_whitelist=getitall@sender.com
|
||||
# Reject spam to these domains, perhaps because we are a backup MX server
|
||||
# Reject spam to these domains instead of quarantining it.
|
||||
;dspam_reject=othercorp.com
|
||||
|
||||
# directory for dspam user quarantine, signature db, and dictionaries
|
||||
@@ -115,8 +131,9 @@ blind = 1
|
||||
;spam=spam@foocorp.com
|
||||
# address to forward false positives to. milter will process and not deliver
|
||||
;falsepositive=ham@foocorp.com
|
||||
# the dspam_screener is used to screen mail for all recipients who are
|
||||
# not dspam_users. Spam goes to the screeners quarantine, and the original
|
||||
# recipients saved so that false positives can be properly delivered.
|
||||
|
||||
# the dspam_screener is a list of dspam users who screen mail for all
|
||||
# recipients who are not dspam_users. Spam goes to the screeners quarantine,
|
||||
# and the original recipients are saved so that false positives can be properly
|
||||
# delivered.
|
||||
;dspam_screener=david,goliath
|
||||
# The dspam CGI can also be used: logins must match dspam users
|
||||
|
||||
+52
-11
@@ -13,8 +13,8 @@ ALT="Viewable With Any Browser" BORDER="0"></A>
|
||||
usemap="#banner_4" alt="Your vote?">
|
||||
<map name="banner_4">
|
||||
<area shape="rect" coords="330,25,426,59"
|
||||
href="http://www.sepschool.org/survey/" alt="I Disagree">
|
||||
<area shape="rect" coords="234,28,304,57" href="http://sepschool.org/" alt="I Agree">
|
||||
href="http://education-survey.org/" alt="I Disagree">
|
||||
<area shape="rect" coords="234,28,304,57" href="http://www.honestEd.com/" alt="I Agree">
|
||||
</map>
|
||||
|
||||
</P>
|
||||
@@ -24,12 +24,14 @@ ALT="Viewable With Any Browser" BORDER="0"></A>
|
||||
Stuart D. Gathman</a><br>
|
||||
This web page is written by Stuart D. Gathman<br>and<br>sponsored by
|
||||
<a href="http://www.bmsi.com">Business Management Systems, Inc.</a> <br>
|
||||
Last updated Jun 08, 2004</h4>
|
||||
Last updated Aug 06, 2004</h4>
|
||||
|
||||
See the <a href="faq.html">FAQ</a> | <a href="#download">Download now</a> |
|
||||
<a href="/mailman/listinfo/pymilter">Subscribe to mailing list</a>
|
||||
<a href="/mailman/listinfo/pymilter">Subscribe to mailing list</a> |
|
||||
<a href="#overview">Overview</a>
|
||||
<p>
|
||||
<img src="python55.gif" align=left alt="A Python">
|
||||
<a href="//www.python.org">
|
||||
<img src="python55.gif" align=left alt="A Python"></a>
|
||||
<a href="//www.sendmail.org/">Sendmail</a> introduced a
|
||||
<a href="http://www.milter.org/milter_api/api.html"> new API</a> beginning with version 8.10 -
|
||||
libmilter. The milter module for <a href="//www.python.org">Python</a>
|
||||
@@ -41,7 +43,17 @@ separation features to enhance security.
|
||||
I recommend upgrading.
|
||||
|
||||
<h2> Recent Changes </h2>
|
||||
|
||||
The RPM for release 0.7.0 moves the config file and socket locations to
|
||||
/etc/mail and /var/run/milter respectively. We now parse Microsoft CID records
|
||||
- but only hotmail.com uses them. They seem to have a patent on the brilliant
|
||||
idea of examining the mail headers to see who the message is from.
|
||||
We aren't doing that here, so not to worry - but I am not a lawyer, so if you
|
||||
are worried, change spf.py around line 626 to return None instead of
|
||||
calling CIDParser(). There is a new option to reject mail with no PTR
|
||||
and no SPF.
|
||||
<p>
|
||||
<a href="http://spf.pobox.com">
|
||||
<img src="SPF.gif" align=left alt="SPF logo"></a>
|
||||
Release 0.6.6 adds support for <a href="http://spf.pobox.com/">SPF</a>,
|
||||
a protocol to prevent forging of the envelope from address.
|
||||
SPF support requires <a href="http://pydns.sourceforge.net/">pydns</a>.
|
||||
@@ -106,7 +118,7 @@ entries. Be sure to handle both Bcc and file copies, and designating what
|
||||
mail should be copied. How should "outgoing" be defined? Implementing it is
|
||||
easy once the configuration is designed.
|
||||
|
||||
<h3>Overview</h3>
|
||||
<h3><a name=overview>Overview</a></h3>
|
||||
|
||||
This package provides a robust toolkit for Python <a
|
||||
href="#milter">milters</a>, and the beginnings of a general purpose mail
|
||||
@@ -138,21 +150,50 @@ methods that
|
||||
do nothing, and also provides wrappers for the libmilter methods to mutate
|
||||
the message.
|
||||
<p>
|
||||
The 'spf' module provides an implementation of <a href="http://spf.pobox.com">
|
||||
SPF</a> useful for detecting email forgery.
|
||||
<p>
|
||||
The 'mime' module provides a wrapper for the Python email package that
|
||||
fixes some bugs, and simplifies modifying selected parts of a MIME message.
|
||||
<p>
|
||||
Finally, the bms.py application is both a sample of how to use the
|
||||
Milter module, and the beginnings of a general purpose SPAM filtering,
|
||||
wiretapping, and Win32 virus protection milter.
|
||||
Milter and spf modules, and the beginnings of a general purpose SPAM filtering,
|
||||
wiretapping, SPF checking, and Win32 virus protecting milter. It can
|
||||
make use of the <a href="pysrs.html">pysrs</a> package when available for
|
||||
SRS/SES checking and the <a href="dspam.html">pydspam</a> package for Bayesian
|
||||
content filtering. SPF checking
|
||||
requires <a href="http://pydns.sourceforge.net/">
|
||||
pydns</a>. Configuration documentation is currently included as comments
|
||||
in the <a href="milter.cfg">sample config file</a> for the bms.py milter.
|
||||
|
||||
<h3><a name=download>Downloading</a></h3>
|
||||
|
||||
The latest stable release is <a href="#stable">0.6.9</a>. A stable
|
||||
The latest stable release is <a href="#stable">0.7.0</a>. A stable
|
||||
release is one which has been installed (and working correctly) on
|
||||
production systems long enough to convince me that it is stable. As
|
||||
the package gains more features and complexity, stable will mean no
|
||||
bug reports from outside users either.
|
||||
<p>
|
||||
The latest version is 0.6.9-1. See the <a href=NEWS>Change Log</a>.
|
||||
The latest version is 0.7.0-1. See the <a href=NEWS>Change Log</a>.
|
||||
<p>
|
||||
<a name="stable"><b>Stable</b></a>
|
||||
<a href="http://bmsi.com/python/milter-0.7.0.tar.gz">
|
||||
milter-0.7.0.tar.gz</a> Move config file and default socket location.
|
||||
Parse M$ CID records.
|
||||
<br>
|
||||
<a href="http://bmsi.com/linux/rh72/milter-0.7.0-1.i386.rpm">
|
||||
milter-0.7.0-1.i386.rpm</a> Binary RPM for Redhat 7.x, now requires
|
||||
sendmail-8.12 and <a href="http://www.python.org/2.3.3/rpms.html">
|
||||
python2.3</a>.
|
||||
<br>
|
||||
<a href="http://bmsi.com/linux/rh9/milter-0.7.0-1rh9.i386.rpm">
|
||||
milter-0.7.0-1rh9.i386.rpm</a> Binary RPM for Redhat 9, requires
|
||||
sendmail-8.12 and <a href="http://www.python.org/2.3.3/rpms.html">
|
||||
python2.3</a>.
|
||||
<br>
|
||||
<a href="http://bmsi.com/linux/rh9/milter-0.7.0-1.src.rpm">
|
||||
milter-0.7.0-1.src.rpm</a> Source RPM for Redhat 9,7.x.
|
||||
<p>
|
||||
<a href="http://bmsi.com/python/milter-0.6.9.tar.gz">
|
||||
milter-0.6.9.tar.gz</a> Add SPF test suite driver, and validate
|
||||
spf.py against test suite. Add best_guess and get_header to spf.py.
|
||||
|
||||
+8
-3
@@ -1,5 +1,5 @@
|
||||
%define name milter
|
||||
%define version 0.7.0
|
||||
%define version 0.7.1
|
||||
%define release 1
|
||||
# Redhat 7.x and earlier (multiple ps lines per thread)
|
||||
%define sysvinit milter.rc7
|
||||
@@ -24,8 +24,8 @@ Prefix: %{_prefix}
|
||||
Vendor: Stuart D. Gathman <stuart@bmsi.com>
|
||||
Packager: Stuart D. Gathman <stuart@bmsi.com>
|
||||
Url: http://www.bmsi.com/python/milter.html
|
||||
Requires: %{python} >= 2.2.2, sendmail >= 8.12
|
||||
BuildRequires: %{python}-devel >= 2.2.2, sendmail-devel >= 8.12
|
||||
Requires: %{python} >= 2.2.2, sendmail >= 8.12.10
|
||||
BuildRequires: %{python}-devel >= 2.2.2, sendmail-devel >= 8.12.10
|
||||
|
||||
%description
|
||||
This is a python extension module to enable python scripts to
|
||||
@@ -132,6 +132,11 @@ rm -rf $RPM_BUILD_ROOT
|
||||
%config(noreplace) /etc/mail/pymilter.cfg
|
||||
|
||||
%changelog
|
||||
* Sun Aug 22 2004 Stuart Gathman <stuart@bmsi.com> 0.7.1-1
|
||||
- Handle modifying mislabeled multipart messages without an exception
|
||||
- Support setbacklog, setmlreply
|
||||
- allow multi-recipient CBV
|
||||
- return TEMPFAIL for SPF softfail
|
||||
* Fri Jul 23 2004 Stuart Gathman <stuart@bmsi.com> 0.7.0-1
|
||||
- SPF check hello name
|
||||
- Move pythonsock to /var/run/milter
|
||||
|
||||
+104
-23
@@ -33,6 +33,18 @@ $ python setup.py help
|
||||
libraries=["milter","smutil","resolv"]
|
||||
|
||||
* $Log$
|
||||
* Revision 2.31 2004/08/23 02:24:36 stuart
|
||||
* Support setbacklog
|
||||
*
|
||||
* Revision 2.30 2004/08/21 20:29:53 stuart
|
||||
* Support option of 11 lines max for mlreply.
|
||||
*
|
||||
* Revision 2.29 2004/08/21 04:14:29 stuart
|
||||
* mlreply support
|
||||
*
|
||||
* Revision 2.28 2004/08/21 02:45:21 stuart
|
||||
* Don't leak int constants if module unloaded.
|
||||
*
|
||||
* Revision 2.27 2004/04/06 03:19:59 stuart
|
||||
* Release 0.6.8
|
||||
*
|
||||
@@ -127,11 +139,20 @@ $ python setup.py help
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MAX_ML_REPLY
|
||||
#define MAX_ML_REPLY 32
|
||||
#endif
|
||||
#if MAX_ML_REPLY != 1 && MAX_ML_REPLY != 32 && MAX_ML_REPLY != 11
|
||||
#error MAX_ML_REPLY must be 1 or 11 or 32
|
||||
#endif
|
||||
#define _FFR_MULTILINE (MAX_ML_REPLY > 1)
|
||||
|
||||
#include <pthread.h>
|
||||
#include <netinet/in.h>
|
||||
#include <Python.h>
|
||||
#include <libmilter/mfapi.h>
|
||||
|
||||
|
||||
/* See if we have IPv4 and/or IPv6 support in this OS and in
|
||||
* libmilter. We need to make several macro tests because some OS's
|
||||
* may define some if IPv6 is only partially supported, and we may
|
||||
@@ -746,6 +767,18 @@ milter_setdbg(PyObject *self, PyObject *args) {
|
||||
return _generic_return(smfi_setdbg(val), "cannot set debug value");
|
||||
}
|
||||
|
||||
static char milter_setbacklog__doc__[] =
|
||||
"setbacklog(int) -> None\n\
|
||||
Set the TCP connection queue size for the milter socket.";
|
||||
|
||||
static PyObject *
|
||||
milter_setbacklog(PyObject *self, PyObject *args) {
|
||||
int val;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "i:setbacklog", &val)) return NULL;
|
||||
return _generic_return(smfi_setbacklog(val), "cannot set backlog");
|
||||
}
|
||||
|
||||
static char milter_settimeout__doc__[] =
|
||||
"settimeout(int) -> None\n\
|
||||
Set the time (in seconds) that sendmail will wait before\n\
|
||||
@@ -820,13 +853,54 @@ static PyObject *
|
||||
milter_setreply(PyObject *self, PyObject *args) {
|
||||
char *rcode;
|
||||
char *xcode;
|
||||
char *message;
|
||||
char *message[MAX_ML_REPLY];
|
||||
char fmt[MAX_ML_REPLY + 16];
|
||||
SMFICTX *ctx;
|
||||
if (!PyArg_ParseTuple(args, "szz:setreply", &rcode, &xcode, &message))
|
||||
int i;
|
||||
strcpy(fmt,"sz|");
|
||||
for (i = 0; i < MAX_ML_REPLY; ++i) {
|
||||
message[i] = 0;
|
||||
fmt[i+3] = 's';
|
||||
}
|
||||
strcpy(fmt+i+3,":setreply");
|
||||
if (!PyArg_ParseTuple(args, fmt,
|
||||
&rcode, &xcode, message
|
||||
#if MAX_ML_REPLY > 1
|
||||
,message+1,message+2,message+3,message+4,message+5,message+6,
|
||||
message+7,message+8,message+9,message+10
|
||||
#if MAX_ML_REPLY > 11
|
||||
,message+11,message+12,message+13,message+14,message+15,
|
||||
message+16,message+17,message+18,message+19,message+20,
|
||||
message+21,message+22,message+23,message+24,message+25,
|
||||
message+26,message+27,message+28,message+29,message+30,
|
||||
message+31
|
||||
#endif
|
||||
#endif
|
||||
))
|
||||
return NULL;
|
||||
ctx = _find_context(self);
|
||||
if (ctx == NULL) return NULL;
|
||||
return _generic_return(smfi_setreply(ctx, rcode, xcode, message),
|
||||
#if MAX_ML_REPLY > 1
|
||||
/*
|
||||
* C varargs might be convenient for some things, but they sure are a pain
|
||||
* when the number of args is not known at compile time.
|
||||
*/
|
||||
if (message[0] && message[1])
|
||||
return _generic_return(smfi_setmlreply(ctx, rcode, xcode,
|
||||
message[0],
|
||||
message[1],message[2],message[3],message[4],message[5],
|
||||
message[6],message[7],message[8],message[9],message[10],
|
||||
#if MAX_ML_REPLY > 11
|
||||
message[11],message[12],message[13],message[14],message[15],
|
||||
message[16],message[17],message[18],message[19],message[20],
|
||||
message[21],message[22],message[23],message[24],message[25],
|
||||
message[26],message[27],message[28],message[29],message[30],
|
||||
message[31],
|
||||
#endif
|
||||
(char *)0
|
||||
), "cannot set reply");
|
||||
#endif
|
||||
return _generic_return(smfi_setreply(ctx, rcode, xcode, message[0]),
|
||||
"cannot set reply");
|
||||
}
|
||||
|
||||
@@ -986,7 +1060,7 @@ milter_getpriv(PyObject *self, PyObject *args) {
|
||||
return o;
|
||||
}
|
||||
|
||||
#if _FFR_QUARANTINE
|
||||
#ifdef SMFIF_QUARANTINE
|
||||
static char milter_quarantine__doc__[] =
|
||||
"quarantine(string) -> None\n\
|
||||
Place the message in quarantine. A string with a description of the reason\n\
|
||||
@@ -1035,7 +1109,7 @@ static PyMethodDef context_methods[] = {
|
||||
{ "replacebody", milter_replacebody, METH_VARARGS, milter_replacebody__doc__},
|
||||
{ "setpriv", milter_setpriv, METH_VARARGS, milter_setpriv__doc__},
|
||||
{ "getpriv", milter_getpriv, METH_VARARGS, milter_getpriv__doc__},
|
||||
#if _FFR_QUARANTINE
|
||||
#ifdef SMFIF_QUARANTINE
|
||||
{ "quarantine", milter_quarantine, METH_VARARGS, milter_quarantine__doc__},
|
||||
#endif
|
||||
#if _FFR_SMFI_PROGRESS
|
||||
@@ -1081,6 +1155,7 @@ static PyMethodDef milter_methods[] = {
|
||||
{ "main", milter_main, METH_VARARGS, milter_main__doc__},
|
||||
{ "setdbg", milter_setdbg, METH_VARARGS, milter_setdbg__doc__},
|
||||
{ "settimeout", milter_settimeout, METH_VARARGS, milter_settimeout__doc__},
|
||||
{ "setbacklog", milter_setbacklog, METH_VARARGS, milter_setbacklog__doc__},
|
||||
{ "setconn", milter_setconn, METH_VARARGS, milter_setconn__doc__},
|
||||
{ "stop", milter_stop, METH_VARARGS, milter_stop__doc__},
|
||||
{ NULL, NULL }
|
||||
@@ -1116,6 +1191,12 @@ allowing one to write email filters directly in Python.\n\
|
||||
Libmilter is currently marked FFR, and needs to be explicitly installed.\n\
|
||||
See <sendmailsource>/libmilter/README for details on setting it up.\n";
|
||||
|
||||
static void setitem(PyObject *d,const char *name,long val) {
|
||||
PyObject *v = PyInt_FromLong(val);
|
||||
PyDict_SetItemString(d,name,v);
|
||||
Py_DECREF(v);
|
||||
}
|
||||
|
||||
void
|
||||
initmilter(void) {
|
||||
PyObject *m, *d;
|
||||
@@ -1125,24 +1206,24 @@ initmilter(void) {
|
||||
d = PyModule_GetDict(m);
|
||||
MilterError = PyErr_NewException("milter.error", NULL, NULL);
|
||||
PyDict_SetItemString(d,"error", MilterError);
|
||||
PyDict_SetItemString(d,"SUCCESS", PyInt_FromLong((long) MI_SUCCESS));
|
||||
PyDict_SetItemString(d,"FAILURE", PyInt_FromLong((long) MI_FAILURE));
|
||||
PyDict_SetItemString(d,"VERSION", PyInt_FromLong((long) SMFI_VERSION));
|
||||
PyDict_SetItemString(d,"ADDHDRS", PyInt_FromLong((long) SMFIF_ADDHDRS));
|
||||
PyDict_SetItemString(d,"CHGBODY", PyInt_FromLong((long) SMFIF_CHGBODY));
|
||||
PyDict_SetItemString(d,"MODBODY", PyInt_FromLong((long) SMFIF_MODBODY));
|
||||
PyDict_SetItemString(d,"ADDRCPT", PyInt_FromLong((long) SMFIF_ADDRCPT));
|
||||
PyDict_SetItemString(d,"DELRCPT", PyInt_FromLong((long) SMFIF_DELRCPT));
|
||||
PyDict_SetItemString(d,"CHGHDRS", PyInt_FromLong((long) SMFIF_CHGHDRS));
|
||||
PyDict_SetItemString(d,"V1_ACTS", PyInt_FromLong((long) SMFI_V1_ACTS));
|
||||
PyDict_SetItemString(d,"V2_ACTS", PyInt_FromLong((long) SMFI_V2_ACTS));
|
||||
PyDict_SetItemString(d,"CURR_ACTS", PyInt_FromLong((long) SMFI_CURR_ACTS));
|
||||
setitem(d,"SUCCESS", MI_SUCCESS);
|
||||
setitem(d,"FAILURE", MI_FAILURE);
|
||||
setitem(d,"VERSION", SMFI_VERSION);
|
||||
setitem(d,"ADDHDRS", SMFIF_ADDHDRS);
|
||||
setitem(d,"CHGBODY", SMFIF_CHGBODY);
|
||||
setitem(d,"MODBODY", SMFIF_MODBODY);
|
||||
setitem(d,"ADDRCPT", SMFIF_ADDRCPT);
|
||||
setitem(d,"DELRCPT", SMFIF_DELRCPT);
|
||||
setitem(d,"CHGHDRS", SMFIF_CHGHDRS);
|
||||
setitem(d,"V1_ACTS", SMFI_V1_ACTS);
|
||||
setitem(d,"V2_ACTS", SMFI_V2_ACTS);
|
||||
setitem(d,"CURR_ACTS", SMFI_CURR_ACTS);
|
||||
#ifdef SMFIF_QUARANTINE
|
||||
PyDict_SetItemString(d,"QUARANTINE",PyInt_FromLong((long)SMFIF_QUARANTINE));
|
||||
setitem(d,"QUARANTINE",SMFIF_QUARANTINE);
|
||||
#endif
|
||||
PyDict_SetItemString(d,"CONTINUE", PyInt_FromLong((long) SMFIS_CONTINUE));
|
||||
PyDict_SetItemString(d,"REJECT", PyInt_FromLong((long) SMFIS_REJECT));
|
||||
PyDict_SetItemString(d,"DISCARD", PyInt_FromLong((long) SMFIS_DISCARD));
|
||||
PyDict_SetItemString(d,"ACCEPT", PyInt_FromLong((long) SMFIS_ACCEPT));
|
||||
PyDict_SetItemString(d,"TEMPFAIL", PyInt_FromLong((long) SMFIS_TEMPFAIL));
|
||||
setitem(d,"CONTINUE", SMFIS_CONTINUE);
|
||||
setitem(d,"REJECT", SMFIS_REJECT);
|
||||
setitem(d,"DISCARD", SMFIS_DISCARD);
|
||||
setitem(d,"ACCEPT", SMFIS_ACCEPT);
|
||||
setitem(d,"TEMPFAIL", SMFIS_TEMPFAIL);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
# $Log$
|
||||
# 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
|
||||
#
|
||||
@@ -58,6 +61,18 @@ except: from email.Parser import nlcre as NLCRE
|
||||
|
||||
from email import Errors
|
||||
|
||||
class MimeGenerator(Generator):
|
||||
def _dispatch(self, msg):
|
||||
# Get the Content-Type: for the message, then try to dispatch to
|
||||
# self._handle_<maintype>_<subtype>(). If there's no handler for the
|
||||
# full MIME type, then dispatch to self._handle_<maintype>(). If
|
||||
# that's missing too, then dispatch to self._writeBody().
|
||||
main = msg.get_content_maintype()
|
||||
if msg.is_multipart() and main.lower() != 'multipart':
|
||||
self._handle_multipart(msg)
|
||||
else:
|
||||
Generator._dispatch(self,msg)
|
||||
|
||||
class MimeParser(Parser):
|
||||
|
||||
# This is a copy of _parsebody from email.Parser, with a fix
|
||||
@@ -349,9 +364,15 @@ class MimeMessage(Message):
|
||||
|
||||
def dump(self,file,unixfrom=False):
|
||||
"Write this message (and all subparts) to a file"
|
||||
g = Generator(file)
|
||||
g = MimeGenerator(file)
|
||||
g.flatten(self,unixfrom=unixfrom)
|
||||
|
||||
def as_string(self, unixfrom=False):
|
||||
"Return the entire formatted message as a string."
|
||||
fp = StringIO.StringIO()
|
||||
self.dump(fp,unixfrom=unixfrom)
|
||||
return fp.getvalue()
|
||||
|
||||
def getencoding(self):
|
||||
return self.get('content-transfer-encoding',None)
|
||||
|
||||
@@ -577,7 +598,6 @@ class HTMLScriptFilter(SGMLFilter):
|
||||
def handle_comment(self,comment):
|
||||
if not self.ignoring: SGMLFilter.handle_comment(self,comment)
|
||||
|
||||
|
||||
def check_html(msg,savname=None):
|
||||
"Remove scripts from HTML attachments."
|
||||
msgtype = msg.get_content_type().lower()
|
||||
|
||||
@@ -12,7 +12,7 @@ if sys.version < '2.2.3':
|
||||
DistributionMetadata.classifiers = None
|
||||
DistributionMetadata.download_url = None
|
||||
|
||||
setup(name = "milter", version = "0.7.0",
|
||||
setup(name = "milter", version = "0.7.1",
|
||||
description="Python interface to sendmail milter API",
|
||||
long_description="""\
|
||||
This is a python extension module to enable python scripts to
|
||||
@@ -28,7 +28,10 @@ querying SPF records.
|
||||
url="http://www.bmsi.com/python/milter.html",
|
||||
py_modules=["Milter","mime","spf"],
|
||||
ext_modules=[
|
||||
Extension("milter", ["miltermodule.c"],libraries=libs),
|
||||
Extension("milter", ["miltermodule.c"],
|
||||
libraries=libs,
|
||||
define_macros = [ ('MAX_ML_REPLY',32) ]
|
||||
),
|
||||
],
|
||||
keywords = ['sendmail','milter'],
|
||||
classifiers = [
|
||||
|
||||
@@ -45,6 +45,9 @@ For news, bugfixes, etc. visit the home page for this implementation at
|
||||
# Terrence is not responding to email.
|
||||
#
|
||||
# $Log$
|
||||
# Revision 1.14 2004/08/23 02:28:24 stuart
|
||||
# Remove Perl usage message.
|
||||
#
|
||||
# Revision 1.13 2004/07/23 19:23:12 stuart
|
||||
# Always fail to match on ip6, until we support it properly.
|
||||
#
|
||||
@@ -115,15 +118,6 @@ import xml.sax
|
||||
# (c) 2004 Python version by Stuart Gathman
|
||||
#
|
||||
# Date: 2004-02-25
|
||||
# Version: 1.0
|
||||
#
|
||||
# Usage:
|
||||
# ./cid2spf.pl "<ep xmlns='http://ms.net/1'>...</ep>"
|
||||
#
|
||||
# Note that the 'include' directives will also have to be checked and
|
||||
# "translated". Future versions of this script might be able to get a
|
||||
# domain name as an argument and "crawl" the DNS for the necessary
|
||||
# information.
|
||||
#
|
||||
# A complete reverse translation (SPF -> CID) might be impossible, since
|
||||
# there are no way to handle:
|
||||
|
||||
Reference in New Issue
Block a user