#! /usr/bin/perl
# Read a mail folder and compute statistics of
# the classification of messages in it by
# annoyance-filter. If a second file name is
# supplied after the required mail folder name,
# a CSV log will be written with a record for
# each message classified containing the following
# fields:
#
# 0 Classification (Mail, Junk, or Indeterminate)
# 1 Junk probability
# 2 From header line
# 3 To header line
# 4 Subject header line
# 5 Date header line
#
# The header line fields are all quoted, with embedded
# quotes doubled to force them. Control characters
# in header fields which might confuse CSV parsing
# are deleted.
# by John Walker http://www.fourmilab.ch/
# September 2002
$acmd = "./annoyance-filter --read dict.bin --trans - --test - |" .
"egrep '^X-Annoyance-Filter' | head -2 >score.tmp";
if ($#ARGV < 0) {
print("Usage: perl testfolder.pl mail_folder_file [CSV _log_file]\n");
exit(2);
}
$totalMail = 0;
$totalJunk = 0;
$totalMessages = 0;
$sumProbability = 0;
$writeLog = 0;
$inf = $ARGV[0];
if ($inf =~ m/\.gz$/) {
open(IN, "zcat $inf |") || die "Cannot open gzipped input file $inf";
} else {
open(IN, "<$inf") || die "Cannot open input file $inf";
}
if ($#ARGV > 0) {
open(CSV, ">$ARGV[1]") || die "Cannot create CSV log file $ARGV[1]";
$writeLog = 1;
}
while (($l = <IN>)) {
if ($l =~ m/^From /) {
last;
}
}
if (!$l) {
print("No messages in mail folder!\n");
exit(0);
}
$eof = 0;
$pattern = ''; # Initialise search pattern
while (!$eof) {
# Read next message from mail folder. At
# this point $l contains the first ("From ")
# line of the message.
$nlines = 0;
undef @message;
$msize = length($l);
&trim_end_of_line;
$message[$nlines++] = $l;
# Read the balance of the message into the
# @message array. Quit when the "From " line
# of the next message is encountered or the
# end of the folder is encountered.
while ($l = <IN>) {
if ($l =~ m/^From /) {
last;
} else {
&trim_end_of_line;
$message[$nlines++] = $l;
$msize += length($l);
}
}
if ($nlines > 0) {
&dispose_of_message;
}
$eof = !($l);
# if ($bail++ > 20) { $eof = 1; } # Quick bail-out for testing
}
if ($writeLog) {
close(CSV);
}
# Compute and display aggregate statistics
print("Total messages: $totalMessages\n");
print(" Mail: $totalMail\n");
print(" Junk: $totalJunk\n");
printf("Mean junk probability: %.4f\n", $sumProbability / $totalMessages);
# Dispose of the message in the @message
# array.
sub dispose_of_message {
local($i, $from, $to, $subject, $disp,
$afrom, $act, $score, $class);
# Parse message header for "interesting" items
for ($i = 0; $i < $nlines; $i++) {
if ((length($message[$i]) == 0) ||
($message[$i] =~ m/^\s*$/)) {
#print("Bail on line $i\n");
last;
}
if ($message[$i] =~ m/^From\s/) {
if (!($message[$i] =~ m/^From\s+-\s+/)) {
$from = $message[$i];
} else {
$afrom = $message[$i];
}
}
elsif ((!defined($from)) && ($message[$i] =~ m/^From:\s/)) {
$from = $message[$i];
}
elsif ($message[$i] =~ m/^To:\s/) {
$to = $message[$i];
}
elsif ($message[$i] =~ m/^Subject:\s/) {
$subject = $message[$i];
}
elsif ($message[$i] =~ m/^Date:\s/) {
$date = $message[$i];
}
}
if (!defined($from)) {
$from = $afrom;
}
# Open a pipe to annoyance-filter and feed the message
# to it. Since we cannot read the output from
# the run via a pipe, it's redirected to a temporary
# file from which we snarf it after closing the pipe.
open(OF, "|$acmd") || die "Cannot create pipeline $acmd";
for ($i = 0; $i < $nlines; $i++) {
print(OF "$message[$i]\n");
}
close(OF);
$score = `grep Probability score.tmp`;
$score =~ s/(^\S+: )(\S+)//;
$score = $2;
$class = `grep Classification score.tmp`;
$class =~ s/(^\S+: )(\S+)//;
$class = $2;
system("rm -f score.tmp");
if ($writeLog) {
print(CSV "$class,$score," .
&csvQuote($from) . "," .
&csvQuote($to) . "," .
&csvQuote($subject) . "," .
&csvQuote($date) . "\n");
}
print("$subject\n");
print("Score: $score $class\n");
$totalMessages++;
$sumProbability += $score;
if ($class eq 'Mail') {
$totalMail++;
} else {
$totalJunk++;
}
}
# Trim end of line sequences from line
sub trim_end_of_line {
$l =~ s/(.*)([\r\n]+)$/\1/;
}
# Quote string for output in CSV
sub csvQuote {
local($s) = @_[0];
$s =~ s/"/""/g;
$s =~ tr/\t\r\n//d;
return "\"$s\"";
}
syntax highlighted by Code2HTML, v. 0.9.1