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