2 # $Id: spamscan.in,v 1.10 2005/07/22 21:37:31 don Exp $
6 # Performs SpamAssassin checks on a message before allowing it through to
7 # the main incoming queue.
9 # Uses up: incoming/S<code><bugnum>.nn
10 # Temps: incoming/R.nn
11 # Creates: incoming/I.nn
14 $config_path = '/etc/debbugs';
15 $lib_path = '/usr/lib/debbugs';
17 require "$config_path/config";
18 require "$lib_path/errorlib";
19 $ENV{PATH} = $lib_path . ':' . $ENV{PATH};
21 exit unless $gSpamScan;
23 chdir $gSpoolDir or die "chdir spool: $!\n";
26 use Mail::SpamAssassin;
28 use lib '/usr/lib/debbugs';
29 use Mail::CrossAssassin;
34 &filelock('incoming-spamscan');
38 ca_init('\b\d{3,8}(?:-(?:close|done|forwarded|maintonly|submitter|quiet))?\@bugs\.debian\.org', '/org/bugs.debian.org/CrossAssassinDb');
42 my $user_prefs = "$ENV{HOME}/.spamassassin/user_prefs";
45 $user_prefs_time = (stat $user_prefs)[9];
48 my $spam = Mail::SpamAssassin->new({
50 site_rules_filename => $gSpamRulesDir,
51 userprefs_filename => $user_prefs,
52 local_tests_only => ($gSpamLocalTestsOnly || 0),
53 # debug => ($ENV{DEBBUGS_SPAM_DEBUG} || 0),
54 # check_mx_delay => 2, # bit of a hack until we have parallelization
56 $spam->compile_now(1); # use all user preferences
63 sub header_or_empty ($$) {
64 my ($mail, $hdr) = @_;
65 my $value = $mail->get_header($hdr);
74 if (-f 'spamscan-stop') {
75 print "spamscan-stop file created\n";
79 if ($user_prefs_time != (stat $user_prefs)[9]) {
80 # stop and wait to be re-invoked from cron
86 opendir DIR, 'incoming' or die "opendir incoming: $!";
87 while (defined($_ = readdir DIR)) {
88 push @ids, $1 if /^S(.*)/;
96 unless (rename "incoming/S$id", "incoming/R$id") {
98 die "$id already fudged once! $!\n";
104 print "[$nf] $id scanning ...\n" or die "print log: $!";
106 open MESSAGE, "< incoming/R$id" or die "open incoming/R$id: $!";
108 # Kludge to work around Received: then From_ weirdness in receive;
109 # remove when receive is fixed? We may continue to need it for
110 # reprocessing old messages.
111 $textarray[0] = <MESSAGE>;
112 if ($textarray[0] =~ /^Received:/) {
113 my $maybefrom = <MESSAGE>;
114 if ($maybefrom =~ /^From /) {
115 $textarray[1] = $textarray[0];
116 $textarray[0] = $maybefrom;
118 $textarray[1] = $maybefrom;
121 push @textarray, <MESSAGE>;
123 my $mail = $spam->parse(\@textarray);
125 my $messageid = header_or_empty($mail, 'Message-Id');
126 print " From: ", header_or_empty($mail, 'From'), "\n";
127 print " Subject: ", header_or_empty($mail, 'Subject'), "\n";
128 print " Date: ", header_or_empty($mail, 'Date'), "\n";
129 print " Message-Id: $messageid\n";
130 my $ca_score = ca_set(ca_keys($mail->get_body));
131 if (exists $spamseen{$messageid}) {
132 # XXX THIS DOES NOT DO LOCKING
133 open OUT, ">> $gSpamMailbox" or die "open $gSpamMailbox failed: $!";
134 print OUT $mail->get_pristine or die "print $gSpamMailbox failed: $!";
135 close OUT or die "close $gSpamMailbox failed: $!";
136 unlink "incoming/R$id" or warn "unlink incoming/R$id: $!";
137 print " spam $spamseen{$messageid} duplicate\n"
138 or die "printf log: $!";
140 my $status = $spam->check($mail);
141 my $munged_mail = $status->rewrite_mail();
143 if ($status->is_spam()) {
144 # XXX THIS DOES NOT DO LOCKING
145 open OUT, ">> $gSpamMailbox" or die "open $gSpamMailbox failed: $!";
146 print OUT $munged_mail or die "print $gSpamMailbox failed: $!";
147 close OUT or die "close $gSpamMailbox failed: $!";
148 unlink "incoming/R$id" or warn "unlink incoming/R$id: $!";
149 my $score = sprintf "%.1f/%.1f %d",
150 $status->get_score(), $status->get_required_score(), $ca_score;
151 print " spam $score\n" or die "print log: $!";
152 $spamseen{$messageid} = $score;
153 } elsif ($status->get_score() > 0 && $ca_score >= 4) {
154 # XXX THIS DOES NOT DO LOCKING
155 open OUT, ">> $gCrossMailbox" or die "open $gCrossMailbox failed: $!";
156 print OUT $munged_mail or die "print $gCrossMailbox failed: $!";
157 close OUT or die "close $gCrossMailbox failed: $!";
158 unlink "incoming/R$id" or warn "unlink incoming/R$id: $!";
159 my $score = sprintf "%.1f/%.1f %d",
160 $status->get_score(), $status->get_required_score(), $ca_score;
161 printf " spam $score\n" or die "printf log: $!";
162 $spamseen{$messageid} = $score;
164 open OUT, "> incoming/I$id" or die "open incoming/I$id: $!";
165 my ($received,$from,$rest_of_message) = split /\n/, $munged_mail, 3;
166 my ($headers,$body) = split /\n\n/, $rest_of_message, 2;
167 if ($received =~ /^From /) {
168 ($received,$from) = ($from,$received);
170 print OUT map { "$_\n"} ($received,$from,$headers) or die "print incoming/I$id: $!";
172 print OUT "X-CrossAssassin-Score: $ca_score\n"
173 or die "print incoming/I$id: $!";
175 print OUT "\n" or die "print incoming/I$id: $!";
176 print OUT $body or die "print incoming/I$id: $!";
177 close OUT or die "close incoming/I$id: $!";
178 unlink "incoming/R$id" or warn "unlink incoming/R$id: $!";
179 printf " ok %.1f/%.1f %d\n",
180 $status->get_score(), $status->get_required_score(), $ca_score
181 or die "printf log: $!";