Kategorien

Navigation

Feeds/Meta

Blog RSS Feed
Kommentare RSS Feed

DerPeit - Twitter
Manuel Josupeit-Walter - XING Profil
Manuel Josupeit-Walter - Amazon Wunschliste
Manuel Josupeit-Walter - XMPP (Jabber)
Manuel Josupeit-Walter - ICQ
Manuel Josupeit-Walter - Skype

Bloggeramt.de
BlogAlm
Add to Technorati Favorites

SpamPoison

josupeit.com > Weblog > Informatik und Technik > Linux und Serversoftware > SpamAssassin anhand von IMAP Flags lernen lassen

SpamAssassin anhand von IMAP Flags lernen lassen

Datum:   30.01.2010, 14:38 Uhr
Kategorie:   Linux und Serversoftware Feed dieser Kategorie abonnieren
Kommentare:   0, Neuen Kommentar schreiben

All meine eingehenden E-Mails werden direkt meinem Homeserver zugestellt. Dort wird auch ein Großteil an Spam gefiltert. In der Vergangenheit trainierte ich den dort laufenden SpamAssassin durch bouncen der entsprechenden Mails an spezielle E-Mail Adressen, die die Mails dann an sa-learn pipeten. Da mir das immer wieder einmal auf den Keks ging fragte ich mich, ob es nicht viel einfacher möglich wäre, Spam anhand der Thunderbird Junk-Markierung lernen zu lassen.

Glücklicherweise bietet das IMAP4 Protokoll laut RFC 3501 die Möglichkeit, Mails mit Flags zu versehen. Von Hause aus gibt es beispielsweise Flags, wie \Seen oder \Deleted, die Kennzeichnen, ob eine Nachricht gelesen oder gelöscht wurde, jedoch erlaubt das Protokoll auch die Vergabe eigener Flags. Diese benutzerdefinierten Flags werden allerdings als Keywords bezeichnet. Thunderbird verwendet für die Markierung, ob eine Mail Spam oder Ham (also kein Spam) ist, die Keywords Junk und NonJunk, wobei nur Mails, die als Junk markiert wurden und explizit auf "Kein Junk" geklickt wurde, als NonJunk markiert werden.

Nun verwende ich ein Perl-Skript, das alle 15 Minuten als Cronjob gestartet wird, mit den Benutzerdaten des Administrators zu meinem Cyrus-IMAP Server verbindet und alle Ordner aller Benutzer auf Mails durchsucht, die entsprechend gekennzeichnet wurden und den Inhalt dann an SpamAssassin übergibt. Anschließend werden die Mails entsprechend als LernedJunk und LernedNonJunk gekennzeichnet. Dadurch werden automatisch alle Mails, die Thunderbird als Junk einstuft oder von Hand eingestuft werden bereits serverseitig gelernt und entsprechend stetig besser gefiltert.

Dieses Skript stelle ich hiermit unter GPLv3 zur Verfügung, vielleicht findet ja noch jemand Verwendung dafür:

#!/usr/bin/perl
use Mail::IMAPClient;
use MIME::Base64;

do('/etc/bayes_learn_spam.conf')
  or die "Unable to read configuration file /etc/bayes_learn_spam.conf";

my $imap = Mail::IMAPClient->new(
             Server => $host,
             Port => $port,
             Debug => 0)

  or die "Unable to connect to imap server";

$imap->has_capability("STARTTLS") and $imap->starttls;
$imap->tag_and_run("AUTHENTICATE PLAIN " . encode_base64("\0" . $authuser . "\0" . $password))
  or die "Unable to login to imap server with user " . $authuser;

my @folders = $imap->folders
  or die "Unable to retrieve user list from imap server";

my @users;

foreach $folder (@folders) {
  if ( $folder =~ /^user\..*$/i ) {
    $folder =~ s/^user\.([^\.]*).*/\1/i;

    if (! grep /$folder/, @users) {
      push (@users, $folder);
    }
  }
}

$imap->disconnect
  or die "Unable to close connection to imap server";

foreach $user (@users) {
  print "Processing messages for user " . $user . "...\n";
  undef($imap);

  my $imap = Mail::IMAPClient->new(
               Server => $host,
               Port => $port,
               Uid => 0,
               Peek => 1,
               Debug => 0)

    or die "Unable to connect to imap server";

  $imap->has_capability("STARTTLS") and $imap->starttls;
  $imap->tag_and_run("AUTHENTICATE PLAIN " . encode_base64($user . "\0" . $authuser . "\0" . $password))
    or die "Unable to do plain auth";

  foreach $folder ($imap->folders) {
    print "Processing folder " . $folder . "...\n";
    $imap->select($folder)
      or die "Unable to select imap folder " . $folder . " for user " . $user;

    @spam = $imap->search("UNDELETED KEYWORD " . $junktag .    " UNKEYWORD " . $learnedjunktag);
    @ham  = $imap->search("UNDELETED KEYWORD " . $nonjunktag . " UNKEYWORD " . $learnednonjunktag);

    # Process spam
    print "Learning " . @spam . " spam messages...\n";
    foreach (@spam) {
      $imap->store($_, "-FLAGS", $learnednonjunktag)
        or die "Unable to flag message " . $_ . " in folder " . $folder . " for user " . $user;

      @msg = $imap->message_string($_)
        or die "Unable to fetch spammy mail " . $_ . " from folder " . $folder . " for user " . $user;

      open(my $sa, "| " . $salearn . " --spam")
        or die ("Unable to pipe to sa-learn");

      print $sa @msg;
      close($sa);
      undef($sa);

      $imap->store($_, "+FLAGS", $learnedjunktag)
        or die "Unable to flag message " . $_ . " in folder " . $folder . " for user " . $user;
    }

    # Process ham
    print "Done. Learning " . @ham . " ham messages...\n";
    foreach (@ham) {
      $imap->store($_, "-FLAGS", $learnedjunktag)
        or die "Unable to flag message " . $_ . " in folder " . $folder . " for user " . $user;

      @msg = $imap->message_string($_)
        or die "Unable to fetch hammy mail " . $_ . " from folder " . $folder . " for user " . $user;

      open(my $sa, "| " . $salearn . " --ham")
        or die ("Unable to pipe to sa-learn");

      print $sa @msg;
      close($sa);
      undef($sa);

      $imap->store($_, "+FLAGS", $learnednonjunktag)
        or die "Unable to flag message " . $_ . " in folder " . $folder . " for user " . $user;
    }

    print "Done learning messages from folder " . $folder . ".\n";
  }

  $imap->disconnect;
  print "All messages for user " . $user . " have been processed.\n";
}


Die Konfiguration erfolgt über die Datei /etc/bayes_learn_spam.conf, die (entsprechend Zugriffsgeschützt) folgende Daten enthält:

$host              = 'localhost';
$port              = 143;
$authuser          = 'cyrus';
$password          = 'passwort';

$salearn           = '/usr/bin/sa-learn';

$junktag           = 'Junk';
$nonjunktag        = 'NonJunk';
$learnedjunktag    = 'LearnedJunk';
$learnednonjunktag = 'LearnedNonJunk';

 
Viel Spaß beim Trainieren. :-) Übrigens: Thunderbird bietet die Möglichkeit, benutzerdefinierte Schlüsselworte zu definieren und farbig hervorzuheben, so hat man immer und überall den Überblick, was gelernt wurde und was nicht...


GPLv3



Bisherige Kommentare: RSS Feed der Kommentare
Keine
Bisherige Trackbacks:
Keine