smeserver-unjunkmgr/root/usr/local/unjunkmgr/spamreminder.pl

397 lines
13 KiB
Perl
Executable File

#!/usr/bin/perl -w
#############################################################################
#
# This script provides weekly overview of email stored in the junkmail
# folder and allows for unjunking. When an email is being unjunked the
# baysian filter in SpamAssassin is trained as ham.
#
# This script has been developed
# by Jesper Knudsen at http://sme.swerts-knudsen.dk
#
# Revision History:
#
# August 24, 2008: Initial version
#############################################################################
# internal modules (part of core perl distribution)
use Getopt::Long;
use Pod::Usage;
use POSIX qw/strftime floor/;
use Time::Local;
use Date::Manip;
use strict;
use MIME::Lite;
use esmith::db;
use esmith::ConfigDB;
use esmith::AccountsDB;
#############################################################################
# Configuration
#############################################################################
# The address which will be copied on the weekly summary emails (default: none)
my $admin_email_addr = '';
my $domain_name = esmith::ConfigDB->open()->get('DomainName')->value;
my $unjunkhost = $domain_name;
my $enabled;
my $useremails = 'yes';
my $db = esmith::ConfigDB->open()
|| die "Unable to open configuration dbase.";
my %db_conf = $db->get('unjunkmgr')->props;
while ( my ( $parameter, $value ) = each(%db_conf) ) {
if ( $parameter eq 'enabled' ) {
$enabled = $value;
}
if ( $parameter eq 'adminemails' and uc($value) eq 'YES' ) {
$admin_email_addr = 'admin';
}
if ( $parameter eq 'unjunkhost' ) {
$unjunkhost = $value;
}
if ( $parameter eq 'useremails' ) {
$useremails = $value;
}
}
# The address from which the weekly summary comes from
my $spamfilter_addr = 'Admin Junk Summary <UnJunkManager>';
# Which stylesheet to use for the summary email
my $css_file = '/usr/local/unjunkmgr/unjunkmgr.css';
# Debug enabled? - will send all reports to $admin_email_addr
my $debug = 0;
#############################################################################
# Parameters for the Junkmail Summary functionality
my $root_url = sprintf( "https://%s/unjunkmgr", $unjunkhost );
my $path = "/home/e-smith/files/users/";
my $end_path_cur = "/Maildir/.junkmail/cur";
my $end_path_new = "/Maildir/.junkmail/new";
my $sa_dbase = '/home/e-smith/db/configuration';
my $dbh = esmith::ConfigDB->open($sa_dbase)
|| die "Unable to open spamassassin configuration dbase.";
my %sa_conf = $dbh->get('spamassassin')->props;
my $disabled = 0;
my $days_to_keep = 5;
my $spam_mark = 7;
my $spam_discard = 10;
my $parameter = "";
my $value = "";
while ( ( $parameter, $value ) = each(%sa_conf) ) {
if ( $parameter eq 'status' and not $value eq 'enabled' ) {
$disabled = 1;
}
if ( $parameter eq 'MessageRetentionTime' ) {
$days_to_keep = $value;
}
if ( $parameter eq 'TagLevel' ) {
$spam_mark = $value;
}
if ( $parameter eq 'RejectLevel' ) {
$spam_discard = $value;
}
}
#printf("Enabled = %s\n",$disabled);
#printf("Retention = %s\n",$days_to_keep);
#printf("TagLevel = %s\n",$spam_mark);
#printf("RejectLevel = %s\n",$spam_discard);
if ( uc($useremails) eq 'YES' or lc($admin_email_addr) eq 'admin' ) {
Junkmail_Reminder();
}
#All done
exit 0;
#############################################################################
# Subroutines ###############################################################
#############################################################################
########################################
# Process parms #
########################################
sub parse_arg {
my $startdate = shift;
my $enddate = shift;
my $secsinday = 86400;
my $time = 0;
my $start = UnixDate( $startdate, "%s" );
my $end = UnixDate( $enddate, "%s" );
if ( !$start && !$end ) {
$end = time;
$start = $end - $secsinday;
return ( $start, $end );
}
if ( !$start ) {
$start = $end - $secsinday;
return ( $start, $end );
}
if ( !$end ) {
$end = $start + $secsinday;
return ( $start, $end );
}
if ( $start > $end ) {
return ( $end, $start );
}
return ( $start, $end );
}
sub get_email_details {
my $entry = shift;
my $score;
my $spam;
my $spamlimit;
my $spam_string = 'Unknown';
my $subject = 'Unknown';
my $from = 'Unknown';
my $to = 'Unknown';
open( ORIGINAL, "$entry" ); #OPEN FILE FOR READING
my @original = <ORIGINAL>; #READ FILE INTO AN ARRAY
#PROCESS THE ARRAY
foreach my $x (@original) {
if ( $x =~ m/^Subject:/ ) {
($subject) = $x =~ m/^Subject: (.*)$/;
if ( not defined($subject) ) {
$subject = 'Unknown';
}
else {
$subject =~ s/^[ \t]//g;
}
}
if ( $x =~ m/^To:/ ) {
($to)
= $x
=~ m/([a-zA-Z0-9._\%\+\-]+\@[a-zA-Z0-9\.\-]+\.[a-zA-Z]{2,4})/;
if ( not defined($to) ) {
$to = 'Unknown';
}
}
if ( $x =~ m/^From:/ ) {
($from)
= $x
=~ m/([a-zA-Z0-9._\%\+\-]+\@[a-zA-Z0-9\.\-]+\.[a-zA-Z]{2,4})/;
if ( not defined($from) ) {
$from = 'Unknown';
}
}
if ( $x =~ m/^X-Spam-Status:/ ) {
( $spam, $score, $spamlimit )
= $x
=~ m/^X-Spam-Status: ([^\,]+)\, hits=([^\ ]+)\ required=(.*)/;
if ( defined($spamlimit) ) {
if ( $spam eq 'Yes'
and $score > $spamlimit
and $score < $spam_discard )
{
$spam_string = sprintf( "Likely Spam (%s)", $score );
}
else {
$spam_string = sprintf( "Spam (%s)", $score );
}
}
else {
$spam_string = 'Unknown';
}
}
}
close(ORIGINAL);
return ( $subject, $from, $to, $spam_string );
}
################################################################################
# The function serializes a complex datastructure into a safe and compact
# single line string.
################################################################################
sub serialize_cmd {
my $cmd = shift;
my $out = $cmd;
# replace all newlines, CR and % with CGI-style encoded sequences
$out =~ s/([\;%\r\n])/sprintf("%%%02X", ord($1))/ge;
return $out;
}
sub Junkmail_Reminder {
my $found;
my $entry;
my $subject;
my $to;
my $from;
my $real_name;
my $name;
my $to_email;
my $score;
my $spamlist;
my $spamcount = 0;
my ( $oneweekago, $noew ) = parse_arg( "last week", "" );
my $adb = esmith::AccountsDB->open_ro()
|| die "Couldnt' open AccountsDB\n";
my @accounts;
push @accounts, $adb->users;
foreach my $account (@accounts) {
$name = $account->key;
$spamcount = 0;
$spamlist = undef;
$found = 0;
$real_name = sprintf( "%s %s",
$account->prop('FirstName'),
$account->prop('LastName') );
if ( $debug == 1 ) {
printf( "User : %s (%s)\n", $real_name, $name );
}
my @junkmail_dirs;
push @junkmail_dirs, "$path$name$end_path_new";
push @junkmail_dirs, "$path$name$end_path_cur";
foreach my $junkmail_dir (@junkmail_dirs) {
# Now get the content list for the directory.
opendir( QDIR, "$junkmail_dir" )
or die "Couldn't read directory $junkmail_dir";
my @sorted_dates = map $_->[1], sort { $b->[0] <=> $a->[0] }
map -f "$junkmail_dir/$_" ? [ ( stat _ )[9], $_ ] : (),
readdir(QDIR);
closedir(QDIR);
foreach $entry (@sorted_dates) {
next if $entry =~ /^\./;
$entry = $junkmail_dir . '/' . $entry;
my $modifytime = ( stat($entry) )[9];
# Now only report new emails..
if ( -f $entry and ( $modifytime > $oneweekago ) ) {
$found++;
( $subject, $from, $to, $score )
= get_email_details($entry);
# printf("Found Spam email: %s with score %s (%s)",$from, $score,$spamcount);
$spamlist->[$spamcount]{'user'} = $name;
$spamlist->[$spamcount]{'realname'} = $real_name;
$spamlist->[$spamcount]{'file'} = $entry;
$spamlist->[$spamcount]{'subject'} = $subject;
$spamlist->[$spamcount]{'from'} = $from;
$spamlist->[$spamcount]{'to'} = $to;
$spamlist->[ $spamcount++ ]{'score'} = $score;
}
}
}
if ( $spamcount > 0 and not $disabled ) {
my $email_msg;
$email_msg .= "<HTML><HEAD><TITLE>UnJunk Manager</TITLE>";
if ( open( CSS, "$css_file" ) ) {
my @css = <CSS>;
$email_msg .= "<style type=\"text/css\">";
foreach my $cssline (@css) {
$email_msg .= $cssline;
}
$email_msg .= "</style>";
}
$email_msg .= "</HEAD>";
$email_msg
.= sprintf "<H1>Junk Emails Blocked for %s: %s</H1><br>",
$real_name, $spamcount;
$email_msg .= sprintf
"The emails listed below have been placed in your personal Junk Box since your last Junk Box Summary and will be<br>";
$email_msg .= sprintf
"deleted after $days_to_keep days. To receive any of these messages, click UnJunk and the message will be delivered to your inbox.<br>";
$email_msg
.= sprintf "<table border=\"1\" width=\"900\"><tr>";
$email_msg .= sprintf
'<td width="600" align="left" valign="top" bgcolor="#C0C0C0" colspan=4>';
$email_msg
.= sprintf
"<b><font face=\"Verdana\"><H2>Emails sent to %s</H2></font></b></td></tr>",
$real_name;
$email_msg .= sprintf
"<td bgcolor=\"#C0C0C0\"><font size=\"2\">Action</td><td bgcolor=\"#C0C0C0\"><font size=\"2\">From</td><td bgcolor=\"#C0C0C0\"><font size=\"2\">Subject</td><td bgcolor=\"#C0C0C0\"><font size=\"2\">Threat</td></tr>";
foreach my $email ( @{$spamlist} ) {
my $spamchanger = $root_url . '/spamchanger.pl';
my $url
= sprintf "%s?user=%s&email=%s&subject=%s&from=%s",
$spamchanger, $email->{'user'}, $email->{'file'},
$email->{'subject'}, $email->{'from'};
$email_msg
.= sprintf
"<td><font size=\"2\"><a href=\"%s\">UnJunk</a></td>",
serialize_cmd($url);
$email_msg
.= sprintf
"<td><font size=\"2\">%-40.40s</td><td><font size=\"2\">%-50.50s</td><td><font size=\"2\">%s</td></tr>",
$email->{'from'}, $email->{'subject'},
$email->{'score'};
}
$email_msg .= sprintf "</table>";
# create a new MIME Lite based email
my $msg = MIME::Lite->new(
Subject =>
sprintf(
"Summary of junk emails blocked - %s Junk Emails Blocked",
$spamcount ),
From => $spamfilter_addr,
To => uc($useremails) eq 'YES'
? $name
: $admin_email_addr,
# To => $debug == 1 ? $admin_email_addr : $name,
# No cc email if debug..
# Cc => $admin_email_addr eq 'admin' ? $admin_email_addr : '',
Cc => (
$admin_email_addr eq 'admin'
and uc($useremails) eq 'YES'
) ? $admin_email_addr : '',
Type => 'text/html',
Data => $email_msg
);
$msg->send();
}
}
}