Category: E-mail
Author/Contact Info Nigel Horne njh@despammed.com
Description: POP3 mail retrieve, with SPAM protection and Virus removal. This script can be executed from rc.local to pick up messages queued on the server. Was in the snippets area, but it's grown too big for there.
#!/usr/bin/perl -w

# Spamassassin filter for POP3 mail accounts

# Copyright (C) 2003 Nigel Horne, Wharfedale Computers Ltd.
# njh@despammed.com
# 21/11/02:    Use DNS to check originating host exists to help stop s
+pam
# 22/11/02:    Use MX DNS records.
# 25/11/02:    Flush items put in sendmail's queue.
# 2/12/02:    Handle e-mail addresses without the realname
# 21/1/03:    Added spam blocker
# 22/1/03:    Don't forward spam message. Improved spam warning messag
+es
# 24/1/03:    Added virus checker.

use strict;
use Net::POP3;
use Net::DNS;
use Mail::SpamAssassin;

my $hostname = 'smsltd.demon.co.uk';
my $server = 'pop3.demon.co.uk';
my $user = 'smsltd';
my $password = 'xyzzy';

my $pop3 = Net::POP3->new($server, Timeout => 60) or
    die "Cannot connect to $server: $!";

my $nummessages = $pop3->login($user, $password);

if(!defined $nummessages) {
    die "Cannot login to $server: $!";
}

if($nummessages == 0) {
    print "Nothing to do\n";
    $pop3->quit();
    exit 0;
}

print "Retrieving $nummessages messages from $server\n";

my $messages = $pop3->list() or
    die "Cannot get list of messages from $server: $!";

my $resolver = Net::DNS::Resolver->new;

sub CATCH_PIPE { };
$SIG{'PIPE'} = "CATCH_PIPE";

MESSAGE: foreach my $msgid (keys %$messages) {
    print "Forwarding message $msgid ...";

    my $message = $pop3->get($msgid);

    defined $message or die "\nCan't retrieve message $msgid: $!";

    print "\n";

    my $from = "";

    my @messageArray = @$message;

    foreach (@messageArray) {
        if(/^From: (.+)/) {
            $from = $1;
            if(($from =~ /.*\<.+\@(.+)\>/) ||
               ($from =~ /.+\@(.+)/)) {
                unless(mx($resolver, $1)) {
                    print "Unknown host $1 in $from, message not forwa
+rded\n";
                    # You may wish to comment this out
                    $pop3->delete($msgid);
                    next MESSAGE;
                }
            } else {
                $from = 'unknown';
            }
            last;
        }
    }

    if(open(CLAM, "|clamscan --disable-summary -i --mbox -")) {
        print CLAM "From $from\n";

        foreach (@messageArray) {
            print CLAM $_;
        }

        if(close CLAM != 0) {
            if(($from ne "") && open(MAIL, "|/usr/lib/sendmail \"-f$fr
+om\" -t -odq")) {
                print MAIL "From MAILER-DAEMON\n";
                print MAIL "To: $from\n";
                print MAIL "Cc: postmaster\n";
                print MAIL "Subject: Virus intercepted\n\n";

                print MAIL "A message sent from you contained a virus.
+\n";
                print MAIL "It has not been forwarded to the $hostname
+ domain.\n";
                close MAIL;
            }
            print "Not forwarding message containing a virus\n";
            # You may wish to comment this out
            $pop3->delete($msgid);
            next MESSAGE;
        }
    }

    my $ma = Mail::SpamAssassin::NoMailAudit->new(data => \@messageArr
+ay);

    # This often gives this error:
    #    Use of uninitialized value in split at /usr/lib/perl5/5.8.0/F
+ile/Spec/Unix.pm line 216
    my $sa = Mail::SpamAssassin->new;
    my $status = $sa->check($ma);

    if($status->is_spam()) {
        $status->rewrite_mail();

        # Don't report if the from is forged as us, or if it's a bounc
+e
        # of a bounce
        my $isbounce = 0;
        foreach (@messageArray) {
            if(/^From: (.+)\@$hostname(.*)/) {
                $isbounce = 1;
                last;
            }
        }
        $sa->report_as_spam($ma) unless $isbounce;

        print "Not forwarding spam message\n";
        # You may wish to comment this out...
        $pop3->delete($msgid);

        $status->finish();

        next MESSAGE;
    }

    $status->finish();
    undef $status;
    undef $sa;
    undef $ma;

    # If we're here it probably isn't spam
    if($from eq "") {
        $from = 'unknown';
    } elsif(open(MAIL, "|/usr/lib/sendmail \"-f$from\" -t -odq")) {
        print MAIL "From njh\@despammed.com\n";
        print MAIL "To: $from\n";
        print MAIL "From: Nigel Horne <njh\@despammed.com>\n";
        print MAIL "Subject: Please update your address book\n\n";

        print MAIL "This is an automatic message that has been sent\n"
+;
        print MAIL "in response to a message sent to $hostname.\n\n";
        print MAIL "The domain $hostname is about to disappear.\n";
        print MAIL "Please update your address book to point to bandsm
+an.co.uk.\n";
        close MAIL;
    }

    if(open(MAIL, "|/usr/lib/sendmail \"-f$from\" -t")) {
        # Send TO field first
        foreach (@$message) {
            if(/^To: (.+)\@$hostname(.*)/i) {
                print MAIL "To: $1$2\n";
                print "To: $1$2\n";
                last;
            } elsif (/^To: (.+)/i) {
                print MAIL "To: $1\n";
                print "To: $1\n";
                last;
            } elsif (/^$/) {
                # End of headers. No To: found. Must be spam...
                print "Not forwarding spam message\n";
                $pop3->delete($msgid);

                close MAIL;

                next MESSAGE;
            }
        }
        my $inhead = 1;
        foreach (@$message) {
            if($inhead) {
                (/^From: (.+)/) && print "From: $1\n";
                (/^Subject: (.+)/) && print "Subject: $1\n";

                unless (/^To: .+\@$hostname.*/) {
                    print MAIL $_ || die "Sendmail has disappeared: $!
+\n";
                }
                $inhead = 0 if(/^$/);
            } else {
                print MAIL $_ || die "Sendmail has disappeared: $!\n";
            }
        }

        print MAIL "\nForwarded from the pop3 account at $server.";
        print MAIL "\nThis service will terminate soon. You *have* bee
+n warned.\n";

        if(close MAIL) {
            $pop3->delete($msgid);
        } else {
            warn "e-mail may have failed\n";
            # You may wish to comment this out...
            $pop3->delete($msgid);
        }
    }
}

print "Finished\n";

$pop3->quit();