#!/usr/bin/perl # see below for filtering instruction specs # (C) 2004, Julian Haight # All Rights Reserved under GPL: http://www.gnu.org/licenses/gpl.txt # Current version available here: http://www.julianhaight.com/filtermail # Version history # V1.0 Accept login params on cmd-line, prompt for password sorta-secure # Added license, copyright, version history # cclient is kindof a PITA library - might want to compile without SSL # if you have trouble. use strict; use Mail::Cclient; use Term::ReadKey; my($start) = 1; # list of header fields we need to fetch my(@lines) = ('list-id', 'to', 'cc', 'sender', 'return-path'); # left side is the regex to match against the header, right side is the folder # where matching messages should be moved my(%patterns) = ( # move list messages to a folder based on list-id field '^(?:list-id):.*(somelist)' => 'listfolder', # move messages to a specific address to another folder '^(?:to|cc):.*(?:saltedaddr)' => 'saltedfolder', # move another list to another folder based on sender field '^sender: owner-another-list@example.com' => 'anotherlist', # move bounces to bounce folder if sent to main address # (use in conjunction with salter:) # http://www.julianhaight.com/salter '^return-path: <(?:mailer-daemon\@?[^\@\>\s]*)?\>.*to:[^\n]*(?:your_main_address\@example.com)' => 'bounces' ); filterMail(); sub filterMail { my($login, $host, $port, $pass); $login = $ARGV[0]; if ($login =~ m/(.*)\@([^\@\s]+)/) { $login = $1; $host = $2; if ($login =~ m/([^:\s]+)\:(.*)/) { $login = $1; $pass = $2; } if ($host =~ m/([^:]+)\:(\d+)/) { $host = $1; $port = $2; } else { $port = 143; # default imap port } } else { print STDERR "usage: filtermail username[:pass]\@imaphost[:port]\n"; exit 1; } unless ($pass) { print "Enter password for $login\n"; ReadMode('noecho'); $pass = ReadLine(0); ReadMode('restore'); } print "Filtering mail on $host, port $port, login $login - ctrl-c to exit\n"; while (1==1) { filterBox($host, $port, $login, $pass); sleep(60); } } sub filterBox { my($host, $port, $user, $pass) = @_; my($imapc, $max, $i, $head, $elt, $pattern, $uid, $seen); Mail::Cclient::parameters(undef, RSHTIMEOUT => 0, MAXLOGINTRIALS => 1); Mail::Cclient::set_callback ('login' => sub { return ($user, $pass); }, 'expunge' => sub { return; }); unless ($imapc = Mail::Cclient -> new("{$host:$port/imap}INBOX")) { print STDERR ("Login failure for $user\@$host:$port\n"); return; } $max = $imapc -> nmsgs; for ($i=$start; $i<=$max; $i++) { $imapc -> fetchflags($i); $elt = $imapc -> elt($i); $uid = $imapc -> uid($i); if ($seen < $uid) { $seen = $uid; } else { next; } unless (isFlagged($elt, '\Deleted')) { $head = $imapc -> fetchheader($i, \@lines); # print "Inspect msg $i ($uid)\n"; foreach $pattern (keys(%patterns)) { if ($head =~ m/$pattern/msi) { $imapc -> move($i, "INBOX.$patterns{$pattern}"); # print "$i ($uid) moved to $patterns{$pattern}\n"; } } } } $imapc -> close(); $imapc = undef(); } sub isFlagged { my($elt, $find) = @_; my($flags) = $elt->flags; my(@flags) = @$flags; my($flag); foreach $flag (@flags) { if ($flag eq $find) { return 1; } } return 0; }