From df036eb55f61dd6b2be4f6ed88ef411f03ed67c5 Mon Sep 17 00:00:00 2001 From: Stuart Gathman Date: Sun, 14 Dec 2008 02:54:46 +0000 Subject: [PATCH] Remove project docs --- doc/Maxwells.gif | Bin 32642 -> 0 bytes doc/SPF.gif | Bin 1649 -> 0 bytes doc/changes.ht | 222 --------------------------------- doc/credits.ht | 55 --------- doc/faq.ht | 293 -------------------------------------------- doc/links.h | 24 ---- doc/logmsgs.ht | 91 -------------- doc/milter.ht | 264 --------------------------------------- doc/policy.ht | 249 ------------------------------------- doc/python55.gif | Bin 2723 -> 0 bytes doc/requirements.ht | 99 --------------- 11 files changed, 1297 deletions(-) delete mode 100644 doc/Maxwells.gif delete mode 100644 doc/SPF.gif delete mode 100644 doc/changes.ht delete mode 100644 doc/credits.ht delete mode 100644 doc/faq.ht delete mode 100644 doc/links.h delete mode 100644 doc/logmsgs.ht delete mode 100644 doc/milter.ht delete mode 100644 doc/policy.ht delete mode 100644 doc/python55.gif delete mode 100644 doc/requirements.ht diff --git a/doc/Maxwells.gif b/doc/Maxwells.gif deleted file mode 100644 index 3175fe9a083536cc3163e63985c20cd85538b529..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32642 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 BPuc(g diff --git a/doc/SPF.gif b/doc/SPF.gif deleted file mode 100644 index 88106472466a59a434cbad7ac9729403789aba76..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1649 zcmZ?wbhEHb3}-N6IKsg2NJcg|D(3r~sprmL`1kwWqBVCpILn^hys&5Ima~WV-hBD` z|G!VxR;xbeWc2rUynK9f-=T}=Za=JPI`HMqvw;Cyt)1At>RA>1`yBz5g=A9 zep}2@iZ?}|^M=)~B}FH5Saw``rx#5+xnn`3p~+Me}RvVt7bVmMEpIwi=&z{$+MOHxvh%lO7k zHGdx&DgL%Aj~+{LFz^aUNbnc3i)?iL`04YPub*vgze;n-AF_}U6pENtkTJ)CheL|N zp@8EsizA;#LWJAKMXssLLJB)3`OLM-V0Pk5$T&M+gkuTEf14X8ij+;#nT(Vy6f}fc z9hMe7Qz=-`=+7u8_d`ZY;UHrOzlO@Hg)WRNW%&l4b6+_oYWXya$rLm^Xgs&RnZqYT zA;IbVCZgxt%f73aMDump6N0N5_0(J z1WwnTd!g#t&o0vNXo6724sRwQUxNll4JiReX116ODN}?TG94Lt3=$d{#cr6-3Nvp@ zI;tm{w{51=bDIDMHo3$Ji;DRQo*tYO9`}H8ilS6PgHGtBqYW(OMh})IB)GLLZkA4A zIM>j%aOX`a0fEjW_Cg*9N4D#g4NDn$Y&z4YZEAR-zC4i6YDRBcn-eRW;i>X-It&ks z7qAzu+sweiQxV`eB`WHq!-}SRnT{+E7^aJ^dSfW)$i~#_+Tb*`@%vppi5RXE|Cs~} zcB!sgY54Os)1rHt2UU)_wdOQi7EEwtXmk}7shM))$kdD*i#QIhWp)TSz#{Gt#SqKN zBM_(gK`1C=lUK#!<&1nC+hYB46dV{6F155?U9(w4NOb4ZNs?)Yq;zr|7Sa`Bz@RH?jk``cOBXB}9%S=K+_FLS*558p}kf2{VF845R8{GBz8@78P0 zET6td|Bw2&o6(vTFPGHzTU?CSNVJP6+%cK)!8G$XoB;tJC%1-_30gUbu1sLFzV~n1 z^p^j<%NWEPE;4<(roG$O-+x!};ntXCOY0mzM3~P?_&+Vd(aATdc}Y~n^abjNJ#XX{ zs5vL5SU5QUi)`l9H4$TEo8ZV1^MFY}qJxF0{D_k0(T;Y{z;cB$J-JnRtqyNy99=R= z!F}dRL${kpLy9C0zLQqX$`CoTNNlkaL(GhqK5;dV{g)h^A3U>=A@y*QaFd2h_|GF6 z>`LdEPfQk@U$apo`$yOHDDg@1ZjJp1A|3Ly9?$=HmfZQ JbvPIqtN}*{)dBzj diff --git a/doc/changes.ht b/doc/changes.ht deleted file mode 100644 index 719d09b..0000000 --- a/doc/changes.ht +++ /dev/null @@ -1,222 +0,0 @@ -Title: Recent Changes - -

Recent Changes

- -

0.8.10

- -SRS rejections now log the recipient. -I have finally implemented plain CBV (no DSN). The CBV policy -will do a plain CBV from now on, and the DSN policy is required -if you want to send a DSN. -I started checking the MAIL FROM fullname (human readable part -of an email) for porn keywords. There is now a banned IP database. -IPs are banned for too many bad MAIL FROMs or RCPT TOs, and remain banned -for 7 days. - -

0.8.9

- -I use the %ifarch hack to build milter and milter-spf -packages as noarch, while pymilter is built as native. - -I removed the spf dependency from dsn.py, so pymilter can be used without -installing pyspf, and added a Milter.dns module to let python milters do -general DNS lookups without loading pyspf. - -

0.8.8

- -Programs do not belong in the /var/log directory. I moved the -milter apps to /usr/lib/pymilter. Since having the programs and -data in the same directory is convenient for debugging, it will -still use an executable present in the datadir. - -Several general utility classes and functions are now in the Milter package -for possible use by other python milters. In addition to the trivial example -milter, a simple SPF only milter is included as a realistic example. - -The spec file now build 3 RPMs: - -
    -
  • pymilter is the milter module and Milter package for use by all python - milters. -
  • milter is the all-singing, all-dancing python milter application, with - supporting /etc/init.d, logrotate and other scripts. -
  • milter-spf is the simple SPF only milter application. -
- -

0.8.7

- -The spf module has been moved to the -pyspf package. -Download here. - -

0.8.6

- -Python milter has been moved to -pymilter Sourceforge -project for development and release downloads. - -

0.8.5

- -Release 0.8.5 fixes some build bugs reported by Stephen Figgins. It -fixes many small things, like not auto-whitelisting recipients of -outgoing mail when the subject contains "autoreply:". There is a -simple trusted forwarder implementation. If you have more than -2 or so forwarders, we will need a way to "compile" SPF records into an -IP set and TTL for it to be efficient (like libspf2 does). - -

GOSSiP

-An alpha release of pygossip has been commited to -CVS, module pygossip. A version of the bms.py milter has been commited to CVS -which supports calling GOSSiP to track domain reputation in a local database. - -

New website design

- -Hey, I'm no artist, so I just used the -ht2html package -by Barry Warsaw. The mascot -is by Christian Hafner, -or maybe his wife. I chose Maxwell's daemon because it tirelessly -and invisibly sorts molecules, just as milters sort mail. -Christian has also provided a fun - -simulation that lets you try your hand at sorting molecules. - -

0.8.4

- -Release 0.8.4 makes configuring SPF policy via access.db actually work. -The honeypot idea is enhanced by auto-whitelisting recipients of -email sent from selected domains. Whitelisted messages are then used -to train the honeypot. This makes the honeypot screener entirely self -training. The smfi_progress() API is now automatically supported when present. -An optional idx parameter to milter.addheader() invokes smfi_insheader(). - -

0.8.3

- -Release 0.8.3 uses the standard logging module, and supports configuring -more detailed SPF policy via the sendmail access map. SMTP AUTH connections -are considered INTERNAL. Preventing forgery between internal domains is -just a matter of specifying the user-domain map - I'll define something -for the next version. We now send DSNs when mail is quarantined (rejecting -if DSN fails) and for SPF syntax errors (PermError). There is an -experimental option to add a Sender header when it is missing and the From -domain doesn't match the MAIL FROM domain. Next release, we may start -renaming and replacing an existing Sender header when neither it nor the -From domain matches MAIL FROM. Since bogus MAIL FROMs are rejected -(to varying degrees depending on the configured SPF policy), and -both Sender and From and displayed by default in many email clients, -this provides some phishing protection without rejecting mail based -on headers. - -

0.8.2

- -Release 0.8.2 has changes to SPF to bring it -in line with the newly official RFC. It adds -SES -support (the original SES without body hash) for pysrs-0.30.10, and honeypot -support for pydspam-1.1.9. There is a new method in the base milter module. -milter.set_exception_policy(i) lets you choose a policy of CONTINUE, REJECT, or -TEMPFAIL (default) for untrapped exceptions encountered in a milter callback. - -

0.8.0

- -Release 0.8.0 is the first Sourceforge -release. It supports Python-2.4, and provides an option to accept mail -that gets an SPF softfail or fails the 3 strikes rule, provided the -alleged sender accepts a DSN explaining the problem. Python-2.3 is -no longer supported by the reworked mime.py module, although API changes -could be backported. There are too many incompatible changes to the -python email package. - -

Older Releases

- -Release 0.7.2 tightens the authentication screws with a "3 strikes and -you're out" policy. A sender must have a valid PTR, HELO, or SPF record -to send email. Specific senders can be whitelisted using the -"delegate" option in the spf configuration section by adding a -default SPF record for them. The PTR and HELO are required -by RFC anyway, so this is not an unreasonable requirement. -There is now a coherent policy for an SPF softfail result. A softfail -is accepted if there is a valid PTR or HELO, or if the domain -is listed in the "accept_softfail" option of the spf configuration section. -A neutral result is accepted by default if there is a valid PTR or -HELO, (and the SPF record was not guessed), unless the domain is listed in the -"reject_neutral" option. Common forms of PTR records for dynamic IPs are -recognized, and do not count as a valid PTR. This does not prevent anyone -from sending mail from a dynamic IP - they just need to configure a -valid HELO name or publish an SPF record. -

-As SPF adoption continues to rise, forged spam is not getting through. So -spammers are publishing their SPF records as predicted. The 0.7.2 RPM -now provides the rhsbl sendmail hack so that spammer domains -can be blacklisted. With the RPM installed, add a line like the following -to your sendmail.mc. -

-HACK(rhsbl,`blackholes.example.com',"550 Rejected: " $&{RHS} " has been spamming our customers.")dnl
-
-

-Of course, spammers are now starting to register -throwaway domains. The next thing we need is a custom DNS server, -in Python, that -can recognize patterns. For instance, one spammer registers ded304.com, -ded305.com, ded306.com, etc. We also need the custom DNS server to -let SPF classic clients check SES (which will be part of pysrs). -The Twisted Python -framework provides a custom DNS server - but I -would like a smaller implementation for our use. -

-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 applied for 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. -

-Microsoft is pushing an anti-opensource license for their pending patent -along with their sender-ID proposal before the IETF. -It is royalty free - but requires anyone distributing a binary they've -compiled from source to sign a license agreement. The Apache Software -Foundation explains -the problem with sender-ID, and Debian concurs. Since -the Microsoft license is -incompatible with free -software in general and the GPL in -particular, Python milter will not be able to implement sender-ID in its -current form. This was, no doubt, Microsoft's intent all along. -

-Sender-ID attempts to do for RFC2822 headers what SPF does for RFC2821 headers. -Unlike SPF, it has never been tried, and is encumbered by a stupid patent. I -recommend ignoring it and continuing to implement and improve SPF until a -working and unencumbered proposal for RFC2822 headers surfaces. - -

- -SPF logo -Release 0.6.6 adds support for SPF, -a protocol to prevent forging of the envelope from address. -SPF support requires pydns. -The included spf.py module is an updated version of the original 1.6 -version at wayforward.net. -The updated version tracks the draft RFC and test suite. -

-The FAQ addresses how to get started with SPF. -

-Release 0.6.1 adds a full milter based dspam application. -

-I have selected the -dspam bayes filter project and -packaged it for python. -Release 0.6.0 offers a simple application of dspam I call "header triage", -which rejects messages with spammy headers. -To use header triage, you must have DSPAM installed, -and select a dictionary that is well moderated by someone who gets -lots of spam. That dictionary can be used to block spam that is -obvious from the headers (e.g. X-Mailer and Subject) before it ties -up any more resources. I have yet to see any false positives from this -approach (check the milter log), but if there are, the sender will -get a REJECT with the message "Your message looks spammy." - diff --git a/doc/credits.ht b/doc/credits.ht deleted file mode 100644 index 9973a4d..0000000 --- a/doc/credits.ht +++ /dev/null @@ -1,55 +0,0 @@ -Title: Credits - -

CREDITS

- -Jim Niemira -wrote the original C module and some quick -and dirty python to use it. -Stuart D. Gathman -took that kludge and added threading and context objects to it, wrote a proper -OO wrapper (Milter.py) that handles attachments, did lots of testing, packaged -it with distutils, and generally transformed it from a quick hack to a -real, usable Python extension. - -

Other contributors (in random order):

- -
-
Christian Hafner -
for the pymilter mascot image of - - Maxwell's daemon -
Stephen Figgins -
for reporting problems building with sendmail-8.12, and when - building milter.so for the first time. -
Dave MacQuigg -
for noticing that smfi_insheader wasn't supported, and creating - a template to help first time pymilter users create their own milter. -
Terence Way -
for providing a Python port of SPF -
Scott Kitterman -
for doing lots of testing and debugging of SPF against draft standard, - and for putting up a - web page that validates SPF records using spf.py -
Alexander Kourakos -
for plugging several memory leaks -
George Graf at Vienna University of Economics and Business Administration -
for handling None passed to setreply and chgheader. -
Deron Meranda -
for IPv6 patches -
Jason Erikson -
for handling NULL hostaddr in connect callback. -
John Draper -
for porting Python milter to OpenBSD, and starting to work on tutorials - then pointing out that it would be easier to just write the MTA in Python. -
Eric S. Johansson -
for helpful design discussions while working on camram -
Alex Savguira -
for finding bugs with international headers and - suggesting the scan_zip option. -
Business Management Systems -
for hosting the website, and providing paying clients who need milter - service so I can work on it as part of my day job. -
- -If I have left anybody out, send me a reminder: -stuart@bmsi.com diff --git a/doc/faq.ht b/doc/faq.ht deleted file mode 100644 index a40f774..0000000 --- a/doc/faq.ht +++ /dev/null @@ -1,293 +0,0 @@ -Title: Python Milter FAQ - -

Python Milter FAQ

- - -
  • Compiling Python Milter -
  • Running Python Milter -
  • Using SPF -
  • Using SRS -
  • - -
      - -

      Compiling Python Milter

      - -
    1. Q. I have tried to download the current milter code and my virus scan -traps several viruses in the download. -

      A. The milter source includes a number of deactivated viruses in -the test directory. All but the first and last lines of the base64 -encoded virus data has been removed. I suppose I should randomize -the first and last lines as well, since pymilter just deletes executables, -and doesn't look for signatures. -

    2. Q. I have installed sendmail from source, but Python milter won't -compile. -

      A. Even though libmilter is officially supported in sendmail-8.12, -you need to build and install it in separate steps. Take a look -at the RPM spec file for sendmail-8.12. -The %prep section shows you how to create -a site.config.m4 that enables MILTER. The %build section shows you how -to build libmilter in a separate invocation of make. The %install section -shows you how to install libmilter with a separate invocation of make. -

      - -

    3. Q. Why is mfapi.h not found when I try to compile Python milter on -RedHat 7.2? -

      A. RedHat forgot to include the header in the RPM. See the -RedHat 7.2 requirements. -

      -

    4. Q. Python milter compiles ok, but I get an error like this when - I try to import the milter module: -
      -ImportError: /usr/lib/python2.4/site-packages/milter.so: undefined symbol: smfi_setmlreply
      -
      -

      A. Your libmilter.a is from sendmail-8.12 or earlier. You need - sendmail-8.13 or later to support setmlreply. You can disable - setmlreply by changing setup.py. Change: -

      -            define_macros = [ ('MAX_ML_REPLY',32) ]
      -
      -in setup.py to -
      -            define_macros = [ ('MAX_ML_REPLY',1) ]
      -
      - -

      Running Python Milter

      - -
    5. Q. The sample.py milter prints a message, then just sits there. -
      -To use this with sendmail, add the following to sendmail.cf:
      -
      -O InputMailFilters=pythonfilter
      -Xpythonfilter,        S=local:inet:1030@localhost
      -
      -See the sendmail README for libmilter.
      -sample  milter startup
      -
      -

      A. You need to tell sendmail to connect to your milter. The -sample milter tells you what to add to your sendmail.cf to tell -sendmail to use the milter. You can also add an INPUT_MAIL_FILTER -macro to your sendmail.mc file and rebuild sendmail.cf - see the sendmail -README for milters. -

      - -

    6. Q. I've configured sendmail properly, but still nothing happens -when I send myself mail! -

      A. Sendmail only milters SMTP mail. Local mail is not miltered. -You can pipe a raw message through sendmail to test your milter: -

      -$ cat rawtextmsg | sendmail myname@my.full.domain
      -
      -Now check your milter log. -

      - -

    7. Q. Why do I get this ImportError exception? -
      -File "mime.py", line 370, in ?
      -    from sgmllib import declstringlit, declname
      -    ImportError: cannot import name declstringlit
      -
      -

      A. declstringlit is not provided by sgmllib in all versions -of python. For instance, python-2.2 does not have it. Upgrade to -milter-0.4.5 or later to remove this dependency. -

      - -

    8. Q. Why do I get milter.error: cannot add recipient? -
      -
      -

      A. You must tell libmilter how you might mutate the message with -set_flags() before calling runmilter(). For -instance, Milter.set_flags(Milter.ADDRCPT). You must add together -all of ADDHDRS, CHGBODY, ADDRCPT, DELRCPT, CHGHDRS that apply. -

      NOTE - recent versions default flags to enabling all features. You -must now call set_flags() if you wish to disable features for -efficiency. -

      - -

    9. Q. Why does sendmail sometimes print something like: -"...write(D) returned -1, expected 5: Broken pipe" -in the sendmail log? -

      A. Libmilter expects "rcpt to" shortly after getting "mail from". -"Shortly" is defined by the timeout parameter you passed to -Milter.runmilter() - or milter.settimeout(). If the timeout is 10 seconds, -and looking up the first recipient in DNS takes more than -10 seconds, libmilter will give up and break the connection. -Milter.runmilter() defaulted to 10 seconds in 0.3.4. In 0.3.5 -it will keep the libmilter default of 2 hours. -

      - -

    10. Q. Why does milter block messages with big5 encoding? What if I -want to receive them? -

      A. sample.py is a sample. It is supposed to be easily modified -for your specific needs. We will of course continue to move generic -code out of the sample as the project evolves. Think of sample.py as -an active config file. -

      -If you are running bms.py, then the block_chinese option in -/etc/mail/pymilter.cfg controls this feature. -

      - -

    11. Q. Why does sendmail coredump with milters on OpenBSD? -

      A. Sendmail has a problem with unix sockets on old versions of OpenBSD. -OpenBSD users report that this problem has been fixed, so upgrading -OpenBSD will fix this. Otherwise, you can -use an internet domain socket instead. For example, in -sendmail.cf use -

      -Xpythonfilter, S=inet:1234@localhost
      -
      -and change sample.py accordingly. -

      - -

    12. Q. How can I change the bounce message for an invalid recipient? -I can only change the recipient in the eom callback, but the eom callback -is never called when the recipient is invalid! -

      A. Configure sendmail to use virtusertable, and send all unknown -addresses to /dev/null. For example, -

      /etc/mail/virtusertable

      -
      -@mycorp.com	dev-null
      -dan@mycorp.com	dan
      -sally@mycorp.com	sally
      -
      -

      /etc/aliases

      -
      -dev-null:	/dev/null
      -
      -Now your milter will get to the eom callback, and can change the -envelope recipient at will. Thanks to Dredd at -milter.org for this solution. -

      - -

    13. Q. I am having trouble with the setreply method. It always outputs - "milter.error: cannot set reply". -

      A. Check the sendmail log for errors. If sendmail is getting -milter timeouts, then your milter is taking too long and sendmail gave -up waiting. You can adjust the timeouts in your sendmail config. Here -is a milter declaration for sendmail.cf with all timeouts specified: -

      -Xpythonfilter, S=local:/var/log/milter/pythonsock, F=T, T=C:5m;S:20s;R:60s;E:5m
      -
      -
    14. Q. There is a Python traceback in the log file! What happened to - my email? -

      A. By default, when the milter fails with an untrapped exception, a -TEMPFAIL result (451) is returned to the sender. The sender will then retry -every hour or so for several days. Hopefully, someone will notice the -traceback, and workaround or fix the problem. Beginning with milter-0.8.2, -you can call milter.set_exception_policy(milter.CONTINUE) -to cause an untrapped exception to continue processing with the -next callback or milter instead. For -completeness, you can also set the exception policy to -milter.REJECT. - -

    15. Q. I read some notes such as "Check valid domains allowed by internal - senders to detect PCs infected with spam trojans." but could not - understand the idea. Could you clarify the content ? - -

      A. The internal_domains configuration specifies which -MAIL FROM domains are used by internal connections. If an internal -PC tries to use some other domain, it is assumed to be a "Zombie". -

      -Here is a sample log line: -

      -2005Jun22 12:01:04 [12430] REJECT: zombie PC at  192.168.100.171  sending MAIL FROM  debby@fedex.com
      -
      -No, fedex.com does not use pymilter, and there is no one named debby at my -client. But the idiot using the PC at 192.168.100.171 has downloaded and -installed some stupid weatherbar/hotbar/aquariumscreensaver that is actually a -spam bot. -

      -The internal_domains option is simplistic, it assumes all -valid senders of the domains are internal. SPF provides a much more general -check of IP and MAIL FROM for external email. Pymilter should soon -have a local policy feature for more general checking of internal mail. -

    16. Q. mail_archive isn't working. Or I don't understand how - it's suppose to work. I have - mail_archive = /var/mail/mail_archive - in pymilter.cfg but nothing ever gets dumped into - /var/mail/mail_archive. -

      A. The 'mail' user needs to have write access. Permission failures - should be logged as a traceback in milter.log if it doesn't. - -

      Using SPF

      - -
    17. Q. So how do I use the SPF support? The sample.py milter doesn't seem - to use it. -

      A. The bms.py milter supports spf. The RedHat RPMs will set almost -everything up for you. For other systems: -

        -
      1. Arrange to run bms.py in the background (as a service perhaps) and - redirect output and errors to a logfile. For instance, on AIX you'll want - to use SRC (System Resource Controller). -
      2. Copy pymilter.cfg to the /etc/mail or the directory you run bms.py in, - and edit it. The comments should explain the options. -
      3. Start bms.py in the background as arranged. -
      4. Add Xpythonfilter to sendmail.cf or add an INPUT_MAIL_FILTER to - sendmail.mc. Regen sendmail.cf if you use sendmail.mc and restart - sendmail. -
      5. Arrange to rotate log files and remove old defang files in - tempdir. The RedHat RPM uses logrotate for - logfiles and a simple cron script using find to clean - tempdir. -
      - In CVS, there is spfmilter.py. Run that as a service, - and it does just SPF. It uses the sendmail access - file to configure SPF responses just like bms.py, but - supports only REJECT and OK. -
    18. Q. The SPF DSN is sent at least once for domains that don't publish a SPF. - How do I stop this behavior? -

      A. The SPF response is controlled by /etc/mail/access - (actually the file you specify with access_file in - the [spf] section of pymilter.cfg). - Responses are OK, CBV, and REJECT. CBV sends the DSN. -

      -You can change the defaults. For instance, I have: -

      -SPF-None:	REJECT
      -SPF-Neutral:	CBV
      -SPF-Softfail:	CBV
      -SPF-Permerror:	CBV
      -
      -I have best_guess = 1, so SPF none is converted to PASS/NEUTRAL for policy -lookup, and 3 strikes (no PTR, no HELO, no SPF) becomes "SPF NONE" for local -policy purposes (the Received-SPF header always shows the official SPF -result.) -

      -You can change the default for specific domains: -

      -# these guys aren't going to pay attention to CBVs anyway...
      -SPF-None:cia.gov	REJECT
      -SPF-None:fbi.gov	REJECT
      -SPF-Neutral:aol.com	REJECT
      -SPF-Softfail:ebay.com	REJECT
      -
      - -

      Using SRS

      - -
    19. Q. The SRS part doesn't seem to work as whenever I try to start - /etc/init.d/pysrs, I get this in - /var/log/milter/pysrs.log: -
      -ConfigParser.NoOptionError: No option 'fwdomain' in section: 'srs'
      -
      -

      A. You need to specify the forward domain - i.e. the domain you want - SRS to rewrite stuff too. -

      -For instance, I have: -

      -# sample SRS configuration
      -[srs]
      -secret = don't you wish
      -maxage = 8
      -hashlength = 5
      -;database=/var/log/milter/srs.db
      -fwdomain = bmsi.com
      -sign=bmsi.com,mail.bmsi.com,gathman.org
      -srs=bmsaix.bmsi.com,bmsred.bmsi.com,stl.gathman.org,bampa.gathman.org
      -
      -The sign is for local domains which are signed. -The srs list is for other domains which you are relaying, -and which need to have SRS checked/undone for bounces. - -
    diff --git a/doc/links.h b/doc/links.h deleted file mode 100644 index fdd8323..0000000 --- a/doc/links.h +++ /dev/null @@ -1,24 +0,0 @@ - -

    Subsections

    -
  • Introduction -
  • Changes -
  • Requirements -
  • Download -
  • GPG-KEY -
  • FAQ -
  • Policies -
  • Log Messages -
  • Mailing List -
  • CREDITS -
  • SourceForge.net Logo -

    Links

    -
  • C API -
  • Milter.Org -
  • Python.Org -
  • Sendmail.Org -
  • SPF -
  • pysrs -
  • pyspf -
  • pygossip -
  • pydspam -
  • libdspam diff --git a/doc/logmsgs.ht b/doc/logmsgs.ht deleted file mode 100644 index aa3863b..0000000 --- a/doc/logmsgs.ht +++ /dev/null @@ -1,91 +0,0 @@ -Title: Python Milter Log Documentation - - -

    Milter Log Documentation

    - -The milter log from the bms.py application has a variety of "tags" in it that -indicate what it did. - -
    -
    DSPAM: honeypot SCREENED -
    message was quarantined to the honeypot quarantine - -
    REJECT: hello SPF: fail 550 access denied -
    REJECT: hello SPF: softfail 550 domain in transition -
    REJECT: hello SPF: neutral 550 access neither permitted nor denied -
    message was rejected because there was an SPF policy for the -HELO name, and it did not pass. - -
    CBV: sender-17-44662668-643@bluepenmagic.com -
    we performed a call back verification - -
    dspam -
    dspam identifier was added to the message - -
    REJECT: spam from self: jsconnor.com -
    message was reject because HELO was us (jsconnor.com) - -
    INNOC: richh -
    message was used to update richh's dspam dictionary - -
    HONEYPOT: pooh@bwicorp.com -
    message was sent to a honeypot address (pooh@bwicorp.com), the -message was added to the honeypot dspam dictionary as spam - -
    REJECT: numeric hello name: 63.217.19.146 -
    message was rejected because helo name was invalid (numeric) - -
    eom -
    message was successfully received - -
    TEMPFAIL: CBV: 450 No MX servers available -
    we tried to do a call back verification but could not look up -MX record, we told the sender to try again later - -
    CBV: info@emailpizzahut.com (cached) -
    call back verification was needed, we had already done it recently - -
    abort after 0 body chars -
    sender hung up on us - -
    REJECT: SPF fail 550 SPF fail: see - http://openspf.com/why.html?sender=m.hendersonxk@163.net&ip=213.47.161.100 -
    message was reject because its sender's spf policy said to - -
    REJECT: Subject: Cialis - No prescription needed! -
    message was rejected because its subject contained a bad expression - -
    REJECT: zombie PC at 192.168.3.37 sending MAIL FROM seajdr@amritind.com -
    message was rejected because the connect ip was internal, but the -sender was not. This is usually because a Windows PC is infected with -malware. - -
    X-Guessed-SPF: pass -
    When the SPF result is NONE, we guess a result based on the generic -SPF policy "v=spf1 a/24 mx/24 ptr". - -
    DSPAM: tonyc tonyc@example.com -
    message was sent to tonyc@example.com and it was identified as spam -and placed in the tonyc dspam quarantine - -
    REJECT: CBV: 550 calvinalstonis@ix.netcom.com...User unknown -
    REJECT: CBV: 553 sorry, that domain isn't in my list -
    REJECT: CBV: 554 delivery error: dd This user doesn't have an account -
    message was rejected because call back verification gave us a fatal -error -
    Auto-Whitelist: user@example.com -
    recipient has been added to auto_whitelist.log because the message -was sent from an internal IP and the recipient is not internal. -
    WHITELIST user@example.com -
    message is whitelisted because sender appears in auto_whitelist.log -
    BLACKLIST user@example.com -
    message is blacklisted because sender appears in blacklist.log or - failed a CBV test. -
    TRAINSPAM: honeypot X-Dspam-Score: 0.002278 -
    message was used to train screener dictionary as spam -
    TRAIN: honeypot X-Dspam-Score: 0.980203 -
    message was used to train screener dictionary as ham -
    -
    diff --git a/doc/milter.ht b/doc/milter.ht deleted file mode 100644 index 844d662..0000000 --- a/doc/milter.ht +++ /dev/null @@ -1,264 +0,0 @@ -Title: Python Milters - -

    - - -Your vote? - - I Disagree - I Agree - -

    - - - - -
    -Maxwell's Daemon: pymilter mascot -Mascot by Christian Hafner - -

    Sendmail Milters in Python

    -

    by Jim Niemira - and - Stuart D. Gathman
    -This web page is written by Stuart D. Gathman
    and
    sponsored by -Business Management Systems, Inc.
    -Last updated Aug 26, 2008

    - -See the FAQ | Download now | -Subscribe to mailing list | -Overview | -pydspam | -libdspam -

    - -A Python -Sendmail introduced a - new API beginning with version 8.10 - -libmilter. The milter module for Python -provides a python interface to libmilter that exploits all its features. -

    -Sendmail 8.12 officially releases libmilter. -Version 8.12 seems to be more robust, and includes new privilege -separation features to enhance security. Even better, sendmail 8.13 -supports socket maps, which makes pysrs much more -efficient and secure. Sendmail 8.14 finally supports modifying -MAIL FROM via the milter API. Unfortunately, I haven't gotten around -to supporting that yet in python milter. -

    - -

    Overview

    - -This package provides a robust toolkit for Python milters, and the beginnings of a general purpose mail -filtering system written in Python. -

    -At the lowest level, the 'milter' module provides a thin wrapper around the - -sendmail libmilter API. This API lets you register callbacks for -a number of events in the -process of sendmail receiving a message via SMTP. -These events include the initial connection from a MTA, -the envelope sender and recipients, the top level mail headers, and -the message body. There are options to mangle all of these components -of the message as it passes through the milter. -

    -At the next level, the 'Milter' module (note the case difference) provides a -Python friendly object oriented wrapper for the low level API. To use the -Milter module, an application registers a 'factory' to create an object -for each connection from a MTA to sendmail. These connection objects -must provide methods corresponding to the libmilter callback events. -

    -Each event method returns a code to tell sendmail whether to proceed -with processing the message. This is a big advantage of milters over -other mail filtering systems. Unwanted mail can be stopped in its -tracks at the earliest possible point. -

    -The Milter.Milter class provides default implementations for event -methods that -do nothing, and also provides wrappers for the libmilter methods to mutate -the message. -

    -The 'spf' module provides an implementation of -SPF useful for detecting email forgery. -

    -The 'mime' module provides a wrapper for the Python email package that -fixes some bugs, and simplifies modifying selected parts of a MIME message. -

    -Finally, the bms.py application is both a sample of how to use the -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 pysrs package when available for -SRS/SES checking and the pydspam package for Bayesian -content filtering. SPF checking -requires -pydns. Configuration documentation is currently included as comments -in the sample config file for the bms.py milter. -See also the HOWTO and -Milter Log Message Tags. -

    -Python milter is under GPL. The authors can probably be convinced to -change this to LGPL if needed. - -

    What is a milter?

    - -Milters can run on the same machine as sendmail, or another machine. The -milter can even run with a different operating system or processor than -sendmail. -Sendmail talks to the milter via a local or internet socket. -Sendmail keeps the -milter informed of events as it processes a mail connection. At any -point, the milter can cut the conversation short by telling sendmail -to ACCEPT, REJECT, or DISCARD the message. After receiving a complete -message from sendmail, the milter can again REJECT or DISCARD it, but it -can also ACCEPT it with changes to the headers or body. - -

    What can you do with a milter?

    - - -
  • A milter can DISCARD or REJECT spam based based on algorithms scripted -in python rather than sendmail's cryptic "cf" language. -
  • A milter can alter or remove attachments from mail that are poisonous to -Windows. -
  • A milter can scan for viruses and clean them when detected. -
  • A milter scans outgoing as well as incoming mail. -
  • A milter can add and delete recipients to forward or secretly -copy mail. -
  • For more ideas, check the Milter Web Page. -
  • - - -Documentation for the C API is provided with sendmail. Miltermodule -provides a thin python wrapper for the C API. Milter.py provides a simple -OO wrapper on top of that. -

    -The Python milter package includes a sample milter that replaces dangerous -attachments with a warning message, discards mail addressed to -MAILER-DAEMON, and demonstrates several SPAM abatement strategies. -The MimeMessage class to do this used to be based on the -mimetools and multifile standard python packages. -As of milter version 0.6.0, it is based on the email standard -python packages, which were derived from the -mimelib project. -The MimeMessage class patches several bugs in the email package, -and provides some backward compatibility. - -

    -The "defang" function of the sample milter was inspired by -MIMEDefang, -a Perl milter with flexible attachment processing options. The latest -version of MIMEDefang uses an apache style process pool to avoid reloading -the Perl interpreter for each message. This makes it fast enough for -production without using Perl threading. -

    -mailchecker is -a Python project to provide flexible attachment processing for mail. I -will be looking at plugging mailchecker into a milter. -

    -TMDA is a Python project -to require confirmation the first time someone tries to send to your -mailbox. This would be a nice feature to have in a milter. -

    -There is also a Milter community website -where milter software and gory details of the API are discussed. - -

    Is a milter written in python efficient?

    - -The python milter process is multi-threaded and startup cost is incurred -only once. This is much more efficient than some implementations that -start a new interpreter for each connection. Testing in a production -environment did not use a significant percentage of the CPU. Furthermore, -python is easily extended in C for any step requiring expensive CPU -processing. -

    -For example, the HTML parsing feature to remove scripts from HTML attachments -is rather CPU intensive in pure python. Using the C replacement for sgmllib -greatly speeds things up. - -

    Goals

    - - -
  • Implement RRS - a backdoor for non-SRS forwarders. User lists non-SRS - forwarder accounts (perhaps in ~/.forwarders), and a util - provides a special local alias for the user to give to the forwarder. - Alias only works for mail from that forwarder. Milter gets forwarder - domain from alias and uses it to SPF check forwarder. Requires - milter to have read access to ~/.forwarders or else - a way for user to submit entries to milter database. -
  • The bms.py milter has too many features. Create a framework where - numerous small feature modules can be plugged together in the - configuration. -
  • Create a pure python substitute for miltermodule and libmilter that - implements the - libmilter protocol in python. -
  • Find or write a faster implementation of sgmllib. The - sgmlop package - is not very compatible with - - Python-2.1 sgmllib, but it is a start, and is supported in - milter-0.4.5 or later. -
  • Implement all or most of the features of - MIMEDefang. -
  • Follow the official - Python coding standards more closely. -
  • Make unit test code more like other python modules. -
  • - -

    Confirmed Installations

    - -Please email -me if you do not successfully install milter. The confirmed -installations are too numerous to list at this point. - -

    Enough Already!

    - -Nearly a dozen people have emailed me begging for a feature to copy -outgoing and/or incoming mail to a backup directory by user. Ok, it -looks like this is a most requested feature for 0.5.6. In the meantime, -here are some things to consider: -
      -
    • If you want to equivalent of a Bcc added to each message, this -is very easy to do in the python code for bms.py. See below. -
    • If you want to copy to a file in a directory (thus avoiding having to -set up aliases), this is slightly more involved. The bms.py milter already -copies the message to a temporary file for use in replacing the message body -when banned attachments are found. You have to open a file, and copy the -Mesage object to it in eom(). -
    • Finally, you are probably aware that most email clients already -keep a copy of outgoing mail? Presumably there is a good reason for -keeping another copy on the server. -
    -

    -To Bcc a message, call self.add_recipient(rcpt) in envfrom after -determining whether you want to copy (e.g. whether the sender is local). For -example, -

    -  def envfrom(...
    -    ...
    -    if len(t) == 2:
    -      self.rejectvirus = t[1] in reject_virus_from
    -      if t[0] in wiretap_users.get(t[1],()):
    -	self.add_recipient(wiretap_dest)
    -      if t[1] == 'mydomain.com':
    -        self.add_recipient('<copy-%s>' % t[0])
    -      ...
    -
    -

    -To make this a generic feature requires thinking about how the configuration -would look. Feel free to make specific suggestions about config file -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. - - -


    -

    - - [ Valid HTML 3.2! ] - - [ Powered By Red Hat Linux ] -

    diff --git a/doc/policy.ht b/doc/policy.ht deleted file mode 100644 index 3e57bc6..0000000 --- a/doc/policy.ht +++ /dev/null @@ -1,249 +0,0 @@ -Title: Python Milter Mail Policy - -

    Python Milter Mail Policy

    - -These are the policies implemented by the bms.py milter -application. The milter and Milter modules do not implement any policies -by themselves. - -

    Classify connection

    - -When the SMTP client connects, the connection IP address is -saved for later verification, and the connection -is classified as INTERNAL or EXTERNAL by matching the ip -address against the internal_connect configuration. -IP addresses with no PTR, and PTR names that look like -the kind assigned to dynamic IPs (as determined by a heuristic -algorithm) are flagged as DYNAMIC. IPs that match the -trusted_relay configuration are flagged as TRUSTED. -

    -Examples from the log file (not the SMTP error message returned): -

    -2005Jul29 13:56:53 [71207] connect from p50863492.dip0.t-ipconnect.de at ('80.134.52.146', 1858) EXTERNAL DYN
    -2005Jul29 18:10:15 [74511] connect from foopub at ('1.2.3.4', 46513) EXTERNAL TRUSTED
    -2005Jul29 14:41:00 [71805] connect from foobar at ('192.168.0.1', 41205) INTERNAL
    -2005Jul29 14:41:15 [71806] connect from cncln.online.ln.cn at ('218.25.240.137', 35992) EXTERNAL
    -
    -

    -Certain obviously evil PTR names are blocked at this point: -"localhost" (when IP is not 127.*) and ".". -

    -2005Jul29 14:49:50 [71918] connect from localhost at ('221.132.0.6', 50507) EXTERNAL
    -2005Jul29 14:49:50 [71918] REJECT: PTR is localhost
    -
    - -

    HELO Check

    - -The HELO name provided by the client is saved for later verification -(for example by SPF). We could validate the HELO at this point -by verifying that an A record for the HELO name matches the connect ip. -However, currently we only block certain obvious problems. -HELO names that look like an IP4 address -and ones that match the hello_blacklist configuration -are immediately rejected. The hello_blacklist typically contains -the current MTAs own HELO name or email domains. -Clients that attempt to skip HELO are immediately rejected. -
    -2005Jul29 18:10:15 [74512] hello from example.com
    -2005Jul29 18:10:15 [74512] REJECT: spam from self: example.com
    -2005Jul29 18:17:09 [74581] hello from 80.191.244.69
    -2005Jul29 18:17:09 [74581] REJECT: numeric hello name: 80.191.244.69
    -
    - -

    MAIL FROM Check

    - -Before calling our milter, sendmail checks a DNS blacklist to -block banned sender domains. We never see a blocked domain. -

    -The MAIL FROM address is saved for possible use by the smart-alias -feature. First, the internal_domains is used for -a simple screening if defined. If the MAIL FROM for an INTERNAL connection -is NOT in internal_domains, then it is rejected (the -PC is most likely infected and attempting to send out spam). -If the MAIL FROM for an EXTERNAL connection IS in -internal_domains, then the message is immediately rejected. -This is quick and effective for most small company MTAs. For more -complex mail networks, it is too simplistic, and should not be defined. -SPF will handle the complex cases. - -

    wiretap

    - -The wiretap feature can screen and/or monitor mail to/from certain -users. If the MAIL FROM is being wiretapped, the recipients are -altered accordingly. - - - -

    SPF check

    - -The MAIL FROM, connect IP, and HELO name are checked against -any SPF records published via DNS for the alleged sender (MAIL FROM) -to determine the official SPF policy result. -The offical SPF result is then logged in the Received-SPF header field, -but certain results are subjected to further processing to create -an effective result for policy purposes. -

    -If the official result is 'none', we try to turn it into an effective result of -'pass' or 'fail'. First, we check for a local substitute SPF record -under the domain defined in the [spf]delegate configuration. -It is often useful to add local SPF records for correspondents that are -too clueless to add their own. If there is no local substitute, we use a "best -guess" SPF record of "v=spf1 a/24 mx/24 ptr" for MAIL FROM or "v=spf1 a/24 -mx/24" for HELO. In addition, a HELO that is a subdomain of MAIL FROM and -resolves to the connect IP results in an effective result of 'pass'. -

    -If there is no local SPF record, and the effective result is still not -'pass', we check for either a valid HELO name or a valid PTR record for -the connect IP. A valid HELO or PTR cannot look like a dynamic name -as determined by the heuristic in Milter.dynip. -

    -If HELO has an SPF record, and the result is anything but pass, we reject -the connection: -

    -2005Jul30 19:45:16 [93991] connect from [221.200.41.54] at ('221.200.41.54', 3581) EXTERNAL DYN
    -2005Jul30 19:45:18 [93991] hello from adelphia.net
    -2005Jul30 19:45:19 [93991] mail from  ()
    -2005Jul30 19:45:19 [93991] REJECT: hello SPF: fail 550 access denied
    -
    -Note that HELO does not have any forwarding issues like MAIL FROM, and so -any result other than 'pass' or 'none' should be treated like 'fail'. -

    -Only if nothing about the SMTP envelope can be validated does the effective -result remain 'none. I call this the "3 strikes" rule. -

    -If the official result is 'permerror' (a syntax error in the sender's -policy), we use the 'lax' option in pyspf to try various heuristics to guess -what they really meant. For instance, the invalid mechanism "ip:1.2.3.4" is -treated as "ip4:1.2.3.4". The result of lax processing is then used -as the effective result for policy purposes. -

    -With an effective SPF result in hand, we consult the sendmail access -database to find our receiver policy for the sender. - - - - - - -
    REJECT -Reject the sender with a 550 5.7.1 SMTP code. The SMTP rejection -includes a detailed description of the problem. -
    CBV -Do a Call Back Validation by connecting to an MX of the sender -and checking that using the sender as the RCPT TO is not rejected. -We quit the CBV connection before actualling sending a message. -If the CBV is rejected, our SMTP connection is rejected with the -same error code and message. CBV results are cached. -
    DSN -Do a Call Back Validation by connecting to an MX of the sender -and checking that using the sender as the RCPT TO is not rejected. -Unlike a CBV, we continue on to data and send a detailed message -explaining the problem. This can be useful for reporting PermError -or SoftFail to the sender. Keep in mind that for any result other -than 'pass', the sender could be forged, and your DSN could annoy the -wrong person. However, a SoftFail result is requesting such feedback -for debugging and a PermError result needs to be fixed by the sender ASAP -whether forged or not. DSN results are cached so that senders are -annoyed only weekly. -
    OK -Accept the sender. The message may still be rejected via reputation -or content filtering. -
    - -

    SPF policy syntax

    - -First, the full sender is checked: -
    -SPF-Fail:abeb@adelphia.net     DSN
    -
    -This says to accept mail from that adelphia.net user despite the -SPF fail, but only after annoying them with a DSN about their ISP's broken -policy. -

    -If there is no match on the full sender, the domain is checked: -

    -SPF-Neutral:aol.com     REJECT
    -
    -This says to reject mail from AOL with an SPF result of neutral. -This means AOL users can't use their AOL address with another mail service -to send us mail. This is good because the other mail service is -likely a badly configured greeting card site or a virus. -

    -Finally, a default policy for the result is checked. While there are program -defaults, you should have defaults in the access database for SPF results: -

    -SPF-Neutral:            CBV
    -SPF-Softfail:           DSN
    -SPF-PermError:          DSN
    -SPF-TempError:          REJECT
    -SPF-None:               REJECT
    -SPF-Fail:               REJECT
    -SPF-Pass:               OK
    -
    - -

    Reputation

    - -If the sender has not been rejected by this point, and if a GOSSiP server is -configured, we consult GOSSiP for the reputation score of the sender and -SPF result. The score is a number from -100 to 100 with a confidence -percentage from 0 to 100. A really bad reputation (less than -50 with -confidence greater than 3) is rejected. Note that the reputation is tracked -independently for each SPF result and sender combination. So aol.com:neutral -might have a really bad reputation, while aol.com:pass would be ok. -Furthermore, when a sender finally publishes an SPF policy and starts -getting SPF pass, their reputation is effectively reset. - -

    Whitelists and Blacklists

    - -The administrator can whitelist or blacklist senders and sending domains by -appending them to ${datadir}/auto_whitelist.log or -${datadir}/blacklist.log respectively. In addition, -recipients of internal senders (except for automatic replies like vacation -messages and return receipts) are automatically whitelisted for 60 days, and -senders that fail CBV or DSN checks are automatically blacklisted for 30 days. -Whitelisted and blacklisted senders are used to automatically train the -bayesian content filter before being delivered or rejected, respectively. -

    -Real Soon Now users will be able to maintain their own whitelist and -blacklist that applies only when they are the recipient. - -

    Content Filter

    - -Most messages have been rejected or delivered by now, but spammers -are always finding new places to send their junk from. For instance, -we get around 10000 emails a day, of which around 500 are first time -spam senders. A bayesian filter is trained by the whitelists and -blacklists, and scores the message. What is likely spam is either -rejected or quarantined. If the sender is an effective SPF pass, -then they get a DSN notifying them that their message has been -quarantined. (A DSN failure gets the sender auto blacklisted.) -Else, if the reject_spam option is set, the message is rejected. -Otherwise, a CBV is done (failure gets the sender auto blacklisted) -and the message is silently quarantined. -

    -Normally, you don't want email messages to silently disappear into -a black hole, so you should set the reject_spam option. However, -if you don't want your correspondent's email to get rejected, you can -check your quarantine frequently instead. - -

    Honeypot

    - -You can also blacklist recipients by listing them as aliases of the -'honeypot' dspam user. These are collectively called -the honeypot. Any email to these recipients is used to train the -spam filter as spam and chalk up a reputation demerit for the sender, then -discarded. It might be a good idea to blacklist the sender if it has SPF pass -as well, but I'm afraid of accidents. - -

    Reputation

    - -Reputation is tracked by sending domain and effective SPF result. -The GOSSiP server tracks the spam/ham status of the last 1024 messages -for each domain:result combination. When the server is queried during -the SMTP envelope phase (MAIL FROM), it also queries any configured -peers, and the scores are combined. Domains with a history of spam for -a given SPF result are rejected at MAIL FROM. The GOSSiP system has -a command line utility to reset (delete) a reputation for cases where a -sender that was infected with malware is repaired. In addition, -the confidence score of a reputation decays with time, so a bad sender -will eventually be able to try again without manual intervention. diff --git a/doc/python55.gif b/doc/python55.gif deleted file mode 100644 index b7c0b9d59e1b7cf6e6216a9e664a0099c11e686f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2723 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 P7+LBl7+PAIFjxZsrd{}U diff --git a/doc/requirements.ht b/doc/requirements.ht deleted file mode 100644 index 0696f22..0000000 --- a/doc/requirements.ht +++ /dev/null @@ -1,99 +0,0 @@ -Title: Requirements - -

    Requirements

    - - -
  • While the miltermodule will work with python 1.5, you probably -want to use python 2.0 or better. The python code uses a number of -python 2 features. The email support requires python 2.4. -
  • Python must be configured with thread support. This is because -pymilter uses sendmail's libmilter which requires thread support. -
  • You must compile sendmail with libmilter enabled. In versions of -sendmail prior to 8.12 libmilter is marked FFR (For Future Release) and -is not installed by default. -Sendmail 8.12 still does not enable libmilter by default. You must -explicitly select the "MILTER" option when compiling. -
  • When compiling Python milter against sendmail versions earlier than -8.13, you must set MAX_ML_REPLY to 1 in setup.py. There is no way to tell from -the libmilter includes that smfi_setmlreply is not supported. -
  • You probably want to use sendmail-8.13, since that supports multi-line -SMTP error descriptions and SOCKETMAP. You want SOCKETMAP for use with -pysrs. -
  • Python milter has been tested against sendmail-8.11 through sendmail-8.13. -
  • Python milter must be compiled for the specific version of sendmail -it will run with. (Since the result is dynamically loaded, there could -conceivably be multiple versions available and selected at startup - but -that will have to wait.) This situation may only exist for sendmail -versions prior to 8.12. The protocol seems designed for backward -compatibility - and 8.12 is the first official milter release. -
  • Mea Culpa! After reading the Python Style guide, I realize that -my Python code is not up to snuff. Apparently mixed tabs and spaces -are anathema to those using Windows editors, where tabs can be expanded using -any arbitrary algorithm. Other than that, my -intuition matched Guido's pretty well - although I like to indent by 2 -rather than 4. I will arrange to have tabs expanded to spaces when -exporting new versions. Until then, beware! -
  • - -

    AIX 4.1.5 Requirements

    -To create sendmail RPMs for AIX, you can download my AIX 4.1.5 spec files -for sendmail-8.11.5 -or sendmail-8.12.3. If you have -not already set it up, I use a dummy RPM package -to represent the stuff that comes with AIX. You might also want -my python-2.1.1 spec file for AIX. It -does not include Tk or curses modules, sorry. If y'all trust me, you can -download rpms for AIX 4.x from my AIX RPM directory. -

    -Sendmail-8.12 renames -libsmutil.a to libsm.a. Unfortunately, libsm.a is an important AIX system -shared library. Therefore, I rename libsm.a back to libsmutil.a for -AIX. This presents a problem for setup.py. - -

    RedHat 7.2 Requirements

    - -If you are running Redhat 7.2, the distributed version of sendmail -now enables libmilter by default. RedHat 7.2 bundles -the development libraries with the main sendmail package, so -there is no sendmail-devel package. However, they forgot to include the -headers! So you'll have to get the SRPM and modify it. I suggest -moving the static libs to a devel package and adding the headers. If -this is too much trouble, you can get the mfapi.h -header for sendmail-8.6.11 from here and manually install it as -/usr/include/libmilter/mfapi.h. -

    -If you do modify the SRPM, I suggest renaming libsmutil.a -to libsm.a - just like sendmail-8.12 will. If you manually install -mfapi.h or don't rename libsmutil.a, you'll -need to force libs = ["milter", "smutil"] in setup.py. -

    -If you have installed python2, and want -python-milter to use python2, add python=python2 to setup.cfg -and build with python2 setup.py bdist_rpm. - -

    Redhat 6.2 Requirements

    - -If you are running Redhat 6.2, the distributed version of sendmail -does not enable libmilter. You can download the Redhat 7.2 sendmail.spec -modified to compile on RedHat 6.2: - -sendmail-rhmilter.spec. The -SRPM for sendmail-8.11.6 is available from -Redhat under - -Errata for RH6.2. But that doesn't include the latest security -patches since RH6.2 is no longer supported. -

    -If y'all trust me, you can pick up source and binary sendmail RPMs for RH6.2 -from my linux downloads directory. -The lastest RPMs were built by taking a RH7.2 SRPMS and removing some -RPM features from the spec file that RH6.2 doesn't support, then -recompiling on RH6.2. You can check this by installing the RH7.2 SRPM, -then diffing my sendmail.spec with theirs. Then run -"rpm -bb sendmail-rhmilter.spec" when you are satisfied. -

    -If you have installed python2, and want -python-milter to use python2, add python=python2 to setup.cfg -and build with python2 setup.py bdist_rpm. -You'll need to install the sendmail-devel package to compile milter.