BANNED_FILES, Quarantine but send notice to original recipient

Olivier Nicole Olivier.Nicole at cs.ait.ac.th
Tue May 19 11:09:21 CEST 2015


Gregory,  

> ON> What I have implemented is a cron that send a daily summary of the
> ON> quarantined messages, so mething like:
> ON> You have received email(s) that is suspicious spam and was quarantined.
> Would you mind posting the bash/cron/perl/python/etc script you use to
> accomplish this? No sense in reinventing the wheel again, plus I have
> no idea where I'd start on this one.

In almost Perl and almost procmail...

I run this Perl script once a day, at midnight. I does a lot of things,
remove old virus, spam and banned files, based on the date. Clean some
temp files left ver by amavis... and send a summary of quarantined
messages.

It works in a maildir type of environment. I only procceed the mail in
the users Junk mailbox, those in virusmail/spam (with spam score >25 or
so) are not proceed.

#!/usr/local/bin/perl

sub do_dir{
    my $dir=$_[0];
    opendir DIR, "$dir";
    chdir "$dir";
#    print "user: $user\n";
    while ($file=readdir DIR) {
#	print "file: $file\n";
	next unless -f $file;
	($a, $b, $c, $d, $e, $f, $g, $h, $i, $mtime, @r)=stat $file;
	if ($today-$mtime>30*$interval) {
	    # Delete old user quarantined message (30 days)
	    unlink $file;
#	    print "removing $file\n";
	    next;
	}
	next unless $today-$mtime<$interval;
#	print "$file $today $mtime\n";
#	$body.="File: $file\n";

#	print "$file  ";
	undef $subject;
	undef $date;
	undef $from;
	undef $spam;

	open IN, "/usr/local/bin/formail -bYczf -X From: -X Subject: -X Date: -X X-Spam-Status: < $file |";
        # I use formail to access Subject: Date: etc. in a single message
	while (<IN>) {
	    $subject=$_ if /^Subject: /;
	    $date=$_ if /^Date: /;
	    $from=$_ if /^From: /;
	    $spam=$_ if /^X-Spam-Status: /;
	}
	$file=~s/\:.*$//; # Normalize the file name
	$subject{$file}=$subject if defined $subject;
	$date{$file}=$date if defined $date;
	$from{$file}=$from if defined $from;
	if ($spam=~/^X-Spam-Status: Yes, (hits|score)=(\d+(\.\d+)?) .*$/) {
#	    print "$2\n";
	    $spam{$file}=$2;
	}

#	$body.=$date.$from.$subject;
#	system "/bin/mv -f $file /var/spamassassin/spam/$file";
#	$body.="\n";	
    }
    closedir DIR;
}

$interval=24*60*60;
$today=time();


# Loop on the list of users (from LDAP)
### ou have to provide your own list

foreach $entry (@list_of_users) { 
    my $user=$entry->get_value('uid');
    my $home=$entry->get_value('homeDirectory');
#    print "$user\n";
    undef $body;
    undef %spam;
    undef %from;
    undef %date;
    undef %subject;
    # need to proceed .Junk/cur and .Junk/new in case the user had open
    # his mail
    do_dir("$home/Maildir/.Junk/cur");
    do_dir("$home/Maildir/.Junk/new");

        undef $i;
    foreach $key (sort { $spam{$a} <=> $spam{$b} } (keys %spam)) {
#	print "$key $spam{$key}\n";
	$i++;
	$body.=sprintf("%3.0d ", $i);
	$body.="File: $key\n";
	$body.="    Spam Level: $spam{$key}\n" if defined $spam{$key};
	$body.="    $date{$key}" if defined $date{$key};
	$body.="    $from{$key}" if defined $from{$key};
	$body.="    $subject{$key}" if defined $subject{$key};
	$body.="\n";
    }

    if (defined $body) {
	$body="You have received email(s) that is suspicious spam and was quarantined.

Quarantined messages are kept for 30 days before they are automatically
removed (http://www.cs.ait.ac.th/laboratory/email/spam.shtml#spam).

You can see any of the following messages in your Junk folder on CSIM
mail server.

If you wish to get any of the following message delivered to your
mailbox, reply to this email, including the lines with the File:
information below.

The word File: MUST be in your reply message, along with the filename.

".$body;
	
	use Mail::SendEasy ;
	
	my $mail = new Mail::SendEasy(
				      smtp => 'mail.cs.ait.ac.th'
				      ) ;
	
	my $status = $mail->send(
# you must provide your own sender address.
				 from    => "XXX" ,
				 to      => "$user\@cs.ait.ac.th" ,
				 subject => "Quarantined messages" ,
				 reply   => "XXX",
				 msg     => $body);
	if (!$status) { 
	    use Unix::Syslog qw(:macros);  # Syslog macros
	    use Unix::Syslog qw(:subs);    # Syslog functions
	    
	    openlog "move-spam", LOG_PID | LOG_PERROR, LOG_MAIL;
	    syslog LOG_ERR, "Could not send quarantive spam digest to $user; error is ".$mail->error ;
	}
    }
}

# Also clean the viruses
chdir "/var/virusmails/virus";
opendir DIR, "/var/virusmails/virus";
while ($file=readdir DIR) {
    next unless -f $file;
    
    ($a, $b, $c, $d, $e, $f, $g, $h, $i, $mtime, @r)=stat $file;
    if ($today-$mtime>30*$interval) {
	# Delete old virus (30 days)
	unlink $file;
    }
}
close DIR;

# Also clean the system quarantined spam
chdir "/var/virusmails/spam";
opendir DIR, "/var/virusmails/spam";
while ($file=readdir DIR) {
    next unless -f $file;
    
    ($a, $b, $c, $d, $e, $f, $g, $h, $i, $mtime, @r)=stat $file;
    if ($today-$mtime>30*$interval) {
	# Delete old system quarantined spam (30 days)
	unlink $file;
    }
}
close DIR;
# Also clean the system quarantined banned
chdir "/var/virusmails/banned";
opendir DIR, "/var/virusmails/banned";
while ($file=readdir DIR) {
    next unless -f $file;
    
    ($a, $b, $c, $d, $e, $f, $g, $h, $i, $mtime, @r)=stat $file;
    if ($today-$mtime>30*$interval) {
	# Delete old system quarantined spam (30 days)
	unlink $file;
    }
}
close DIR;
# Also clean the system quarantined bad header
chdir "/var/virusmails/badh";
opendir DIR, "/var/virusmails/badh";
while ($file=readdir DIR) {
    next unless -f $file;
    
    ($a, $b, $c, $d, $e, $f, $g, $h, $i, $mtime, @r)=stat $file;
    if ($today-$mtime>30*$interval) {
	# Delete old system quarantined spam (30 days)
	unlink $file;
    }
}
close DIR;
# Also clean amavis temp directory
chdir "/var/amavis/tmp";
opendir DIR, "/var/amavis/tmp";
while ($file=readdir DIR) {
    next unless -d $file;
    next if $file=~/^\.\.?$/;
    
    ($a, $b, $c, $d, $e, $f, $g, $h, $i, $mtime, @r)=stat $file;
    if ($today-$mtime>$interval) {
	# Delete Amavis tmp dir (1 day)
	system "/bin/rm -rf $file";
    }
}
close DIR;


--- And now the procmail part that restore the mail

# recover quarantine messages
:0
*^TOXXX whatever your quarantine mail account is
| /usr/local/sbin/recover-spam

# email pushed by the quarantine recovcery are delivered immediately
# I think this is not needed anymore (was used in time I had mbox mail)
:0
* ^From XXX
{ 
  EXCEPTION=1
}

# do not check the summary messages
:0
* ^From root
* ^Subject: Quarantined messages
* ^Reply-To: XXX
{ EXCEPTION=1 }

So a mail sent to the quarantine account will be procceeded by the
reover-spam script and a mail sent by the quarantine account will skip
any quarantine procedure.

--- and the recover-spam script

#!/usr/local/bin/perl
use Unix::Syslog qw(:macros);  # Syslog macros
use Unix::Syslog qw(:subs);    # Syslog functions

openlog "recover_spam", LOG_PID, LOG_MAIL;

while (<STDIN>) {
    # print "Header $_";
    $from=$1 if /^From:\s+(.*)$/;
    last if /^$/;
}

# remove the full name between parenthesis - old syntax
$from=~s/\s*\([^\)]*\)\s*//; 
# keep the address between angle brakets - new syntax
$from=$1 if $from=~/\<([^\>]*)\>/;
$from=~s/\s*//g;
$from=~s/\@.*$//;
# the user asking to recover spam should be using his real mail address
# that username extracted from the email address is used for "authentication"

syslog LOG_INFO, "Recovering spam for user %s", $from;
# Use LDAP or other directory to check the username and home directory
# the Junk folder is in $HOME_DIR/Maildir/.Junk


while (<STDIN>) {
    next if /File:$/;
    next if /File: MUST/;
    next unless /File: ([\w-\.]*)/;
    $file=$1;
#    $user=$file;
#    $user=~s/-spam\.\d+\.\d+//;
#    print LOG "$user $file\n";
#    next unless $user eq $uid
#    @user=getpwnam($uid);
    opendir DIR, "$home/Maildir/.Junk/cur/";
    undef $found;
    while ($f=readdir DIR) {
#	print LOG "$home/Maildir/.Junk/cur/$f\n";
	if ($f=~/^$file/) {
	    $found="$home/Maildir/.Junk/cur/$f"; 
	    $fn=$f;
	    last;
	}
    }
    close DIR;
    unless (defined $found) {
	opendir DIR, "$home/Maildir/.Junk/new/";
	while ($f=readdir DIR) {
	    if ($f=~/^$file/) {
		$found="$home/Maildir/.Junk/new/$f"; 
		$fn=$f;
		last;
	    }
	}
	close DIR;
    }

    # I think this aprt is simply moving a mail from the Junk folder
    # to the INBOX
    next unless (defined $found) && (-f $found);
    syslog LOG_INFO, "Recovering the file %s to ~/Maildir/cur/", $fn;
    $EUID=$uidnumber;
    my $f1=$found;
    $f1=~/^.*$/;
    $f1=$&;
    my $f2="$home/Maildir/new/$fn";
    $f2=~/^.*$/;
    $f2=$&;
    rename "$f1", "$f2";
#    unlink $found;
}
closelog;
exit 0;

sub ldap_error {
    syslog LOG_ERR, "LDAP serverproblem: %s", $msg->error;
    exit 1;
}


----------

Olivier


More information about the amavis-users mailing list