Here is a history of user visible changes to Python milter.
0.8.8	move AddrCache, parse_addr, iniplist, parse_header to Milter package
	fix plock for missing source and can't change owner/group
	add sample spfmilter.py milter
	private_relay config option
0.8.7	Move spf module to pyspf
	Prevent PTR cache poisoning
	More lame bounce heuristics
	Do plain CBV when template is missing
0.8.6	Support CBV timeout
	Support fail template, headers in templates
	Create GOSSiP record only when connection will procede to DATA.
	More SPF lax heuristics
	Don't require SPF pass for white/black listing mail from trusted relay.
	Support localpart wildcard for white and black lists.
	Delay reject of unsigned RCPT for postmaster and abuse only
	Fix dsn reporting of hard permerror
	Resolve FIXME for wrap_close in miltermodule.c
	Add Message-ID to DSNs
	Use signed Message-ID in delayed reject to blacklist senders
	Auto-train via blacklist and auto-whitelist
	Don't check userlist for signed MFROM
	Accept but skip DSPAM training for whitelisted senders without SPF PASS
	Report GC stats 
	Support CIDR matching for IP lists
	Support pysrs sign feature
	Support localpart specific SPF policy in access file
0.8.5	Simple trusted_forwarder implementation.
	Fix access_file neutral policy
	Move Received-SPF header to beginning of headers
	Supply keyword info for all results in Received-SPF header.
	Move guessed SPF result to separate header
	Activate smfi_insheader only when SMFIR_INSHEADER defined
	Handle NULL MX in spf.py
	in-process GOSSiP server support (to be extended later)
	Expire CBV cache and renew auto-whitelist entries
0.8.4	Auto-whitelist recipients of outgoing email.
	Fix SPF policy via sendmail access map (case insensitive keys).
	Train screener on whitelisted messages
	Optional idx parameter to addheader to invoke smfi_insheader
	Activate progress API when SMFIR_PROGRESS defined
0.8.3   Keep screened honeypot mail, but optionally discard honeypot only mail.
	spf_accept_fail option for braindead SPF senders 
	  (treats fail like softfail)
	Option to set SPF policy via sendmail access map.
	Option to supply Sender header from MAIL FROM when missing.
	Consider SMTP AUTH connections internal.
	Send DSN for SPF errors corrected by extended processing.
	Send DSN before SCREENED mail is quarantined
	Use logging package to keep log lines atomic.
0.8.2	Strict processing limits per SPF RFC
	Fixed several parsing bugs under RFC 
	Support official IANA SPF record (type99)
	Honeypot support (requires pydspam-1.1.9)
	Extended SPF processing results beyond strict RFC limits
	Support original SES for bounce protection (requires pysrs-0.30.10)
	Callback exception processing option in milter module
	Handle corrupt ZIP attachments
0.8.1	Fix zip in zip loop in mime.py
	Fix HeaderParseError in bms.py header callback
	Check internal_domains for outgoing mail
	Fix inconsistent results from send_dsn
0.8.0	Move Milter module to subpackage.
	DSN support for Three strikes rule and SPF SOFTFAIL
	Move /*mime*/ and dynip to Milter subpackage
	Fix SPF unknown mechanism list not cleared
	Make banned extensions configurable.
	Option to scan zipfiles for bad extensions.
	Properly log pydspam exceptions
0.7.3	Experimental release with python2.4 support
0.7.2	Return unknown for invalid ip address in mechanism
	Recognize dynamic PTR names, and don't count them as authentication.
	Three strikes and yer out rule.
	Block softfail by default when no PTR or HELO
	Return unknown for null mechanism
	Try best guess on HELO also
	Expand setreply for common errors
	make rhsbl.m4 hack available for sendmail.mc
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
	Check M$ style XML CID records by converting to SPF
	Recognize, but never match ip6 - until we properly support it.
	Option to reject when no PTR and no SPF
0.6.9	Reject invalid SRS immediately for benefit of callback verifiers
	Fix include bug in spf.py
	Fix check_header bug
	Fix setup.py to work with python < 2.2.3, thanks to Eric S. Johansson
	Test driver for SPF test suite.  Fix bugs and add features to
	pass most of test suite.
	Use best_guess() and get_header() in bms.py for SPF support
0.6.8	Defang message/rfc822 content_type with boundary 
	Support SPF delegation
	Reject neutral SPF result for selected domains
	Support SPF default (best_guess)
	Don't report "spoofed" unless rcpt looks like SRS
	Check for bounce with multiple rcpts
	Make dspam see Received-SPF headers
	Fix sysv init for Redhat 9 and other single ps line per process systems
0.6.7	Fix failure to remove explicit unix socket thanks to Alexander again.
	Support SRS forgery detection.
	Detect thread resource starvation in Milter.py.
	Decode obfuscated subject headers.
0.6.6	Another memory leak plugged by Alexander Kourakos.
	Support SPF checking:  http://spf.pobox.com
	Hello blacklist
	RPM compiled for python2.3 and sendmail-8.12
0.6.5	Plug memory leak in wrap_connect thanks to Alexander Kourakos.
	Support progress notification.
	Log Received header for trusted relay.
	Support wildcard user for smart alias.
0.6.4	Exempt entire domains.
	Tweak SMTP error codes reported.
	Suppress traceback for Dspam lock timeouts.
	Dspam internal mail for dspam users.
	Match hostname for internal connection test, even if no ipaddr.
	Fix for not saving defang of false positive triggered rejecting it
	as a virus from self.
	Size limit for dspam to work around dspam-2.6.5.2 bug.
	(dspam-2.8 still showstopper buggy for libdspam API.)
	Whitelist for dspam.
	Reject list for dspam (REJECT rather than quarantine SCREENed
	spam for listed domains).
	Report dspam header changes to sendmail, fix headerChange
	to handle deleting absent header.
	dspam feature requires pydspam-1.1.5
0.6.3	dspam screening (with pydspam-1.1.4)
	Don't write "defang" file for false positive feedback
0.6.2	Work around email package bug in get_filename().
	add dspam_exempt list to milter.cfg
	REJECT messages with missing MIME boundaries (almost always spam)
	DISCARD messages which any dspam user flags as spam 
	start.sh was calling python instead of python2 on Linux
0.6.1	Work with python-2.2.3
	Integrate full dspam application
0.6.0	Use email package in python-2.2.2
0.5.6	Include dspam interface for Bayesian filtering
0.5.5	Allow passing None to setreply and chgheader thanks to George Graf.
	Experimental IPv6 support thanks to Deron Meranda.
	Allow removing callbacks by passing None to set_XXX_callback.
	Recognize internal connections in bms.py.
	Give users a clue when rejecting banned subjects.
0.5.4	Wiretap redirection feature, smart alias feature, QUARANTINE support
0.5.3	Tweak to run under 2.2 in production
0.5.2	Fix and add to unit test another parsing failure.
0.5.1	Properly handle modifications to rfc822 attachments.
	Handle encoded rfc822 attachments.
0.5.0	Use config file so users don't have to keep syncing the
	bms.py script.  Keep bms.py marked as %config for a while
	to avoid wiping out their customizations just yet.
0.4.5	Work with sgmlop package to speed up HTML parsing.
	Reduce various local hacks to config variables.
0.4.4	Bug fixes for HTML encoding.
0.4.3	Handle quoted-printable HTML attachments.  Remove entire
	attachment when HTML can't be parsed.
0.4.2	Parse HTML attachments to remove <script ...>...</script>.
        Klez virus uses malformed MIME part separators to prevent
	the multifile module and other virus scanners from seeing its
	HTML attachment (which contains Javascript and VBScript).  Outhouse
	happily accepts and executes the malformed attachments, but
	we still kill the Klez virus because we:
	Defang attachment when any Content-Type attribute ends with
	a banned extension  - one of the Outhouse bugs exploited by the
	Klez virus.  Outhouse really, really stinks . . .
0.4.1	Bug fix from Jason Erikson for NULL hostaddr in connect callback.
0.4.0	New check_attachments(msg,check) function in mime module allows
	filtering based on attachment contents.  Distribution now includes
	bms.py, an example milter used in production - including use of the
	new check_attachments(msg,check) API.
	Report hostname in WARNING.TXT.
	More parameter list bug fixes.

0.3.10	Parse quotes in parameter lists to handle embedded ';'.
	Move test data to subdirectory, write non-junit output to
	log file in test subdirectory.
0.3.9	Handle non-multipart messages with executable content in sample.py,
	add more extensions to banned list.
0.3.8	Handle malformed Content-Type in mime.py.  Test viruses have
	been deactivated by deleting most of the viral code.
0.3.7	Put back hint on running sample.py.  Add .bat as banned extension.
	More sample spam filtering logic.
0.3.6	Ran through pychecker-0.8.5.  Most systems will name the sendmail
	user library (used by the milter extension module) 'libsm', but AIX
	still needs to call it 'libsmutil' because there is a system library
	called 'libsm'.
0.3.5	Enhanced logging.  Fix bug in sample milter where headers were
	included in body when removing a virus.
0.3.4	Tested distribution on RH6.2 and updated sample.py and docs.
	Tested with gcc-2.95.2, python-2.1.1, sendmail-8.11.6-2.6.x
	The RH6.2 spec file to enable libmilter for sendmail-8.11.6
	can be obtained from http://www.bmsi.com/linux/sendmail-rhmilter.spec
	The SRPM can be obtained from http://www.redhat.com

0.3.3	Remove reference to sa_len - not supported by linux.

0.3.2	Rename and add more hints to the sample milter.

0.3.1	Pass a more useful hostaddr to the connect callback.

0.3	Interface now uses a milterContext extension object instead of
an index.  A PyThreadContext is now created for each milterContext so that
"simultaneously" processing multiple messages at once (as often happens
on a busy server) actually works.

	Many milter methods are now object methods of the milterContext
extension object.  No compatibility API is provided for this change due
to the limited user base at this stage.  The setname method has been removed,
and the name is now passed to register.

	A simple class to provide an OO wrapper to the milter API is
provided.

	A simple class to parse multipart mime messages into parts and replace
selected parts is provided.  The sample filter will eventually use the mimelib
package instead,  but mimelib currently requires reading the entire message
into memory.

	A sample filter that replaces attachments with naughty extensions
with a warning message is provided.