#!/usr/bin/perl # Spam Update # # Douglas Thrift # # $Id$ use strict; use warnings; use IO::Socket::SSL; use Mail::IMAPClient; use IPC::Open2; use Mail::SpamAssassin; use MIME::Base64; my $debug = 0; for my $arg (@ARGV) { if ($arg eq "-debug") { $debug = 1; } else { print "Usage: $0 [-debug]\n"; exit 1; } } my $negative = 'Spam.False Negative'; my $positive = 'Spam.False Positive'; my $spamc = '/usr/local/bin/spamc'; print "Information: If you receive spam in your Inbox mailbox, copy it to the $negative mailbox. If you receive mail that is not spam in your Spam mailbox, copy it to the $positive mailbox. "; my $socket = new IO::Socket::SSL(Proto => 'tcp', PeerAddr => 'mail.douglasthrift.net', PeerPort => 993, SSL_ca_file => '/home/douglas/cacert.pem', SSL_verify_mode => SSL_VERIFY_PEER) or die "$0: $@\n"; my $imap = new Mail::IMAPClient(Debug => $debug, Socket => $socket); { my $user = getpwuid $<; open PASS, "$ENV{HOME}/.SpamUpdate.pass" or die "$0: $!\n"; my $pass = ; chomp $pass; close PASS; $pass =~ tr/A-Za-z/N-ZA-Mn-za-m/; $imap->User($user); $imap->Password(decode_base64($pass)); } sub error { die "$0: " . $imap->LastError; } $imap->login or error; for my $job (new Job($negative), new Job($positive)) { $imap->select($job->mailbox) or error; printf ' %s and %s from the %s mailbox: ', ucfirst $job->learning, $job->collabing, $job->mailbox; my @messages = $imap->search('UNDELETED'); error if ($@); my $total = 0; my $learned = 0; my $collabed = 0; if ($#messages != -1) { for my $message (@messages) { my @result = $imap->fetch($message, '(BODY[])') or error; die "$0: no message $message\n" if ($result[0] !~ /UID $message/); my $body = $result[1]; ++$total; my $learn = open2(\*LEARN_OUT, \*LEARN_IN, $spamc, '-L', $job->learn, '-s', 256 * 1024 * 1024); my $collab = open2(\*COLLAB_OUT, \*COLLAB_IN, $spamc, '-C', $job->collab, '-s', 256 * 1024 * 1024); print LEARN_IN $body; print COLLAB_IN $body; close LEARN_IN; close COLLAB_IN; my $learn_out = ; my $collab_out = ; close LEARN_OUT; close COLLAB_OUT; chomp($learn_out, $collab_out); print "learn_out = $learn_out\ncollab_out = $collab_out\n" if ($debug); ++$learned if ($learn_out eq "Message successfully un/learned"); ++$collabed if ($collab_out eq "Message successfully reported/revoked"); $imap->delete_message($message) or error; waitpid $learn, 0; waitpid $collab, 0; } } sub plural { my $number = shift; return $number != 1 ? 's' : ''; } printf " $learned message%s %s and $collabed message%s %s ($total message%s total). ", plural($learned), $job->learned, plural($collabed), $job->collabed, plural($total); } $imap->logout; { package Job; sub new { my $class = shift; my $self = {}; $self->{mailbox} = shift; $self->{negative} = $self->{mailbox} eq $negative; bless $self, $class; } sub learning { my $self = shift; return $self->_learn . 'ing'; } sub collabing { my $self = shift; return $self->_collab . 'ing'; } sub learned { my $self = shift; return $self->_learn . 'ed'; } sub collabed { my $self = shift; return $self->_collab . 'ed'; } sub learn { my $self = shift; return $self->{negative} ? 'spam' : 'ham'; } sub collab { my $self = shift; return $self->{negative} ? 'report' : 'revoke'; } sub mailbox { my $self = shift; return $self->{mailbox}; } sub _learn { my $self = shift; return ($self->{negative} ? '' : 'un') . 'learn'; } sub _collab { my $self = shift; return 're' . ($self->{negative} ? 'port' : 'vok'); } }