1 |
#!/usr/bin/perl |
2 |
# Spam Update |
3 |
# |
4 |
# Douglas Thrift |
5 |
# |
6 |
# $Id$ |
7 |
|
8 |
use strict; |
9 |
use warnings; |
10 |
|
11 |
use IO::Socket::SSL; |
12 |
use Mail::IMAPClient; |
13 |
use IPC::Open2; |
14 |
use Mail::SpamAssassin; |
15 |
use MIME::Base64; |
16 |
|
17 |
my $debug = 0; |
18 |
|
19 |
for my $arg (@ARGV) |
20 |
{ |
21 |
if ($arg eq "-debug") |
22 |
{ |
23 |
$debug = 1; |
24 |
} |
25 |
else |
26 |
{ |
27 |
print "Usage: $0 [-debug]\n"; |
28 |
|
29 |
exit 1; |
30 |
} |
31 |
} |
32 |
|
33 |
my $negative = 'Spam.False Negative'; |
34 |
my $positive = 'Spam.False Positive'; |
35 |
my $spamc = '/usr/local/bin/spamc'; |
36 |
|
37 |
print "Information: |
38 |
If you receive spam in your Inbox mailbox, copy it to the $negative mailbox. |
39 |
|
40 |
If you receive mail that is not spam in your Spam mailbox, copy it to the $positive mailbox. |
41 |
"; |
42 |
|
43 |
my $socket = new IO::Socket::SSL(Proto => 'tcp', PeerAddr => 'mail.douglasthrift.net', PeerPort => 993) or die "$0: $@\n"; |
44 |
my $imap = new Mail::IMAPClient(Debug => $debug, Socket => $socket); |
45 |
|
46 |
{ |
47 |
my $user = getpwuid $<; |
48 |
|
49 |
open PASS, "$ENV{HOME}/.SpamUpdate.pass" or die "$0: $!\n"; |
50 |
|
51 |
my $pass = <PASS>; |
52 |
|
53 |
chomp $pass; |
54 |
close PASS; |
55 |
|
56 |
$pass =~ tr/A-Za-z/N-ZA-Mn-za-m/; |
57 |
|
58 |
$imap->User($user); |
59 |
$imap->Password(decode_base64($pass)); |
60 |
} |
61 |
|
62 |
sub error |
63 |
{ |
64 |
die "$0: " . $imap->LastError; |
65 |
} |
66 |
|
67 |
$imap->login or error; |
68 |
|
69 |
for my $job (new Job($negative), new Job($positive)) |
70 |
{ |
71 |
$imap->select($job->mailbox) or error; |
72 |
|
73 |
printf ' |
74 |
%s and %s from the %s mailbox: |
75 |
', ucfirst $job->learning, $job->collabing, $job->mailbox; |
76 |
|
77 |
my @messages = $imap->search('UNDELETED'); |
78 |
|
79 |
error if ($@); |
80 |
|
81 |
my $total = 0; |
82 |
my $learned = 0; |
83 |
my $collabed = 0; |
84 |
|
85 |
if ($#messages != -1) |
86 |
{ |
87 |
for my $message (@messages) |
88 |
{ |
89 |
my @result = $imap->fetch($message, '(BODY[])') or error; |
90 |
|
91 |
die "$0: no message $message\n" if ($result[0] !~ /UID $message/); |
92 |
|
93 |
my $body = $result[1]; |
94 |
|
95 |
++$total; |
96 |
|
97 |
my $learn = open2(\*LEARN_OUT, \*LEARN_IN, $spamc, '-L', $job->learn); |
98 |
my $collab = open2(\*COLLAB_OUT, \*COLLAB_IN, $spamc, '-C', $job->collab); |
99 |
|
100 |
print LEARN_IN $body; |
101 |
print COLLAB_IN $body; |
102 |
close LEARN_IN; |
103 |
close COLLAB_IN; |
104 |
|
105 |
my $learn_out = <LEARN_OUT>; |
106 |
my $collab_out = <COLLAB_OUT>; |
107 |
|
108 |
close LEARN_OUT; |
109 |
close COLLAB_OUT; |
110 |
chomp($learn_out, $collab_out); |
111 |
|
112 |
print "learn_out = $learn_out\ncollab_out = $collab_out\n" if ($debug); |
113 |
|
114 |
++$learned if ($learn_out eq "Message successfully un/learned"); |
115 |
++$collabed if ($collab_out eq "Message successfully reported/revoked"); |
116 |
|
117 |
$imap->delete_message($message) or error; |
118 |
|
119 |
waitpid $learn, 0; |
120 |
waitpid $collab, 0; |
121 |
} |
122 |
} |
123 |
|
124 |
sub plural |
125 |
{ |
126 |
my $number = shift; |
127 |
|
128 |
return $number != 1 ? 's' : ''; |
129 |
} |
130 |
|
131 |
printf " $learned message%s %s and $collabed message%s %s ($total message%s total). |
132 |
", plural($learned), $job->learned, plural($collabed), $job->collabed, plural($total); |
133 |
} |
134 |
|
135 |
$imap->logout; |
136 |
|
137 |
{ |
138 |
package Job; |
139 |
|
140 |
sub new |
141 |
{ |
142 |
my $class = shift; |
143 |
my $self = {}; |
144 |
|
145 |
$self->{mailbox} = shift; |
146 |
$self->{negative} = $self->{mailbox} eq $negative; |
147 |
|
148 |
bless $self, $class; |
149 |
} |
150 |
|
151 |
sub learning |
152 |
{ |
153 |
my $self = shift; |
154 |
|
155 |
return $self->_learn . 'ing'; |
156 |
} |
157 |
|
158 |
sub collabing |
159 |
{ |
160 |
my $self = shift; |
161 |
|
162 |
return $self->_collab . 'ing'; |
163 |
} |
164 |
|
165 |
sub learned |
166 |
{ |
167 |
my $self = shift; |
168 |
|
169 |
return $self->_learn . 'ed'; |
170 |
} |
171 |
|
172 |
sub collabed |
173 |
{ |
174 |
my $self = shift; |
175 |
|
176 |
return $self->_collab . 'ed'; |
177 |
} |
178 |
|
179 |
sub learn |
180 |
{ |
181 |
my $self = shift; |
182 |
|
183 |
return $self->{negative} ? 'spam' : 'ham'; |
184 |
} |
185 |
|
186 |
sub collab |
187 |
{ |
188 |
my $self = shift; |
189 |
|
190 |
return $self->{negative} ? 'report' : 'revoke'; |
191 |
} |
192 |
|
193 |
sub mailbox |
194 |
{ |
195 |
my $self = shift; |
196 |
|
197 |
return $self->{mailbox}; |
198 |
} |
199 |
|
200 |
sub _learn |
201 |
{ |
202 |
my $self = shift; |
203 |
|
204 |
return ($self->{negative} ? '' : 'un') . 'learn'; |
205 |
} |
206 |
|
207 |
sub _collab |
208 |
{ |
209 |
my $self = shift; |
210 |
|
211 |
return 're' . ($self->{negative} ? 'port' : 'vok'); |
212 |
} |
213 |
} |