By popular demand, here's my mimedefang-filter script. If you use it, please be sure to change the email addresses in it. :)
The only part that I still do in /etc/procmailrc is to check for 'X-Spam-Flag: YES' and redirect the mail accordingly. (And yes, I know that mimedefang says you shouldn't use action_bounce() with viruses, but, IMHO, what happens to the mail after I reject it isn't my problem.) Any questions, just ask. Dan.
# -*- Perl -*- #*********************************************************************** # # mimedefang-filter # # Suggested minimum-protection filter for Microsoft Windows clients, plus # SpamAssassin checks. # # Copyright (C) 2002 Roaring Penguin Software Inc. # # This program may be distributed under the terms of the GNU General # Public License, Version 2, or (at your option) any later version. # # $Id: filter-using-spamassassin,v 1.5 2002/04/12 18:49:34 dfs Exp $ #*********************************************************************** #*********************************************************************** # Set administrator's name here. The administrator receives # quarantine messages and is listed as the contact for site-wide # MIMEDefang policy. A good example would be '[EMAIL PROTECTED]' #*********************************************************************** $AdminAddress = '[EMAIL PROTECTED]'; $AdminName = "Daniel Rogers"; #*********************************************************************** # Set the e-mail address from which MIMEDefang quarantine warnings and # user notifications appear to come. A good example would be # '[EMAIL PROTECTED]'. Make sure to have an alias for this # address if you want replies to it to work. #*********************************************************************** $DaemonAddress = '[EMAIL PROTECTED]'; #*********************************************************************** # Set various stupid things your mail client does below. #*********************************************************************** # Set the next one if your mail client cannot handle nested multipart # messages $Stupidity{"flatten"} = 0; # Set the next one if your mail client cannot handle multiple "inline" # parts (*cough* Exchange *cough* Outlook) $Stupidity{"NoMultipleInlines"} = 1; #*********************************************************************** # %PROCEDURE: filter_begin # %ARGUMENTS: # None # %RETURNS: # Nothing # %DESCRIPTION: # Called just before e-mail parts are processed #*********************************************************************** sub filter_begin { my($code, $category, $action) = message_contains_virus_nai(); if ($action eq "tempfail") { action_tempfail("Problem with virus scanner: $VirusScannerMessages"); } $VirusFound = ($category eq "virus"); $Boilerplate = ""; # Spam checks if SpamAssassin is installed if ($Features{"SpamAssassin"}) { if (-s "./INPUTMSG" < 100*1024) { # Only scan messages smaller than 100kB. Larger messages # are extremely unlikely to be spam, and SpamAssassin is # dreadfully slow on very large messages. my($hits, $req, $names, $report) = spam_assassin_check(); if ($hits >= $req) { if ($hits > 10) { return action_bounce( "Mail rejected from $RelayAddr as it has exceeded our criteria, qualifying it as spam. SpamAssassin scored $hits on tests $names. If you feel we have erred in classifying this message as spam, please contact support\@island.net."); } else { action_change_header("X-Spam-Status", "Yes, hits=$hits required=$req tests=$names"); action_change_header("X-Spam-Flag","YES"); $Boilerplate = $report; } } else { action_change_header("X-Spam-Status", "No, hits=$hits required=$req tests=$names"); } } } } #*********************************************************************** # %PROCEDURE: filter # %ARGUMENTS: # entity -- a Mime::Entity object (see MIME-tools documentation for details) # fname -- the suggested filename, taken from the MIME Content-Disposition: # header. If no filename was suggested, then fname is "" # ext -- the file extension (everything from the last period in the name # to the end of the name, including the period.) # type -- the MIME type, taken from the Content-Type: header. # # NOTE: There are two likely and one unlikely place for a filename to # appear in a MIME message: In Content-Disposition: filename, in # Content-Type: name, and in Content-Description. If you are paranoid, # you will use the re_match and re_match_ext functions, which return true # if ANY of these possibilities match. re_match checks the whole name; # re_match_ext checks the extension. See the sample filter below for usage. # %RETURNS: # Nothing # %DESCRIPTION: # This function is called once for each part of a MIME message. # There are many action_*() routines which can decide the fate # of each part; see the mimedefang-filter man page. #*********************************************************************** sub filter { my($entity, $fname, $ext, $type) = @_; if ($VirusFound) { $VirusScannerMessages = ""; my($code, $category, $action) = entity_contains_virus_nai($entity); if ($action eq "tempfail") { return action_tempfail("Problem with virus scanner: $VirusScannerMessages"); } if ($action eq "quarantine") { my ($foo, $mine, $bar) = split ("\n", $VirusScannerMessages); $mine =~ s/Found the (.*) virus/{$virus = $1;}/eg; return action_bounce( "Mail rejected from $RelayHostname [$RelayAddr] due to viral infection. Virus detected was $virus."); } } return action_accept(); } # If SpamAssassin found SPAM, append report. We do it as a separate # attachment of type text/plain sub filter_end { my($entity) = @_; # No sense doing any extra work return if message_rejected(); if ($Boilerplate ne "") { action_add_part($entity, "text/plain", "-suggest", "$Boilerplate\n", "SpamAssassinReport.txt", "inline"); } } #*********************************************************************** # %PROCEDURE: filter_multipart # %ARGUMENTS: # entity -- a Mime::Entity object (see MIME-tools documentation for details) # fname -- the suggested filename, taken from the MIME Content-Disposition: # header. If no filename was suggested, then fname is "" # ext -- the file extension (everything from the last period in the name # to the end of the name, including the period.) # type -- the MIME type, taken from the Content-Type: header. # %RETURNS: # Nothing # %DESCRIPTION: # This is called for multipart "container" parts such as message/rfc822. # You cannot replace the body (because multipart parts have no body), # but you should check for bad filenames. #*********************************************************************** sub filter_multipart { my($entity, $fname, $ext, $type) = @_; return action_accept(); } #*********************************************************************** # %PROCEDURE: defang_warning # %ARGUMENTS: # oldfname -- the old file name of an attachment # fname -- the new "defanged" name # %RETURNS: # A warning message # %DESCRIPTION: # This function customizes the warning message when an attachment # is defanged. #*********************************************************************** sub defang_warning { my($oldfname, $fname) = @_; return "An attachment named '$oldfname' was converted to '$fname'.\n" . "To recover the file, right-click on the attachment and Save As\n" . "'$oldfname'\n"; } # DO NOT delete the next line, or Perl will complain. 1;