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;

Reply via email to