#!/usr/bin/perl -w =head1 NAME pop2imap - synchronize mailboxes between a pop and an imap servers. $Revision: 1.12 $ =cut # comment =pod =head1 INSTALL Get pop2imap at http://www.linux-france.org/prj/pop2imap/dist/ tar xzvf pop2imap-x.xx.tgz # x.xx is the version number Read the INSTALL file. freshmeat record: http://freshmeat.net/projects/pop2imap/ =head1 SYNOPSIS pop2imap [options] pop2imap --help pop2imap pop2imap [--host1 server1] [--port1 ] [--user1 ] [--passfile1 ] [--host2 server2] [--port2 ] [--ssl2] [--user2 ] [--passfile2 ] [--folder ] [--delete] [--dry] [--quiet] [--debug] [--debugimap] [--debugpop] [--version] [--help] =head1 DESCRIPTION The command pop2imap is a tool allowing incremental transfer from one POP3 mailbox to an IMAP one. We sometimes need to transfer mailboxes from one POP3 server an IMAP server. This is called migration. pop2imap is the adequate tool because it reduces the amount of data transfered by not transfering a given message if it is already on both sides. You can stop the transfert at any time and restart it later, pop2imap is adapted to a bad connection. You can decide to delete the messages from the source mailbox after a successful transfert (it is a good feature when migrating). In that case, use the --delete option. You can also just synchronize a mailbox A from another mailbox B in case you just want to keep a "live" copy of B in A. =head1 OPTIONS Invoke: pop2imap --help =head1 AUTHOR Gilles LAMIRAL lamiral@linux-france.org =head1 LICENSE pop2imap is free, gratis and open source software cover by the GNU General Public License. See the GPL file included in the distribution or the web site http://www.gnu.org/licenses/licenses.html =head1 BUGS No known bug. Report any bugs to the author: lamiral@linux-france.org =head1 SIMILAR SOFTWARES None known. Feedback will be welcome. $Id: pop2imap,v 1.12 2003/08/19 01:21:00 gilles Exp $ =cut use Mail::POP3Client; use Mail::IMAPClient; use Getopt::Long; use strict; my($rcs, $VERSION, $error, $debug, $version, $help, $user1, $ssl1, $password1, $host1, $passfile1, $user2, $ssl2, $password2, $host2, $passfile2, $pop, $imap, $debugpop, $debugimap, $folder, $folder2, $port1, $port2, $delete, $dry, $expunge, $quiet, ); $rcs = ' $Id: pop2imap,v 1.12 2003/08/19 01:21:00 gilles Exp $ '; $rcs =~ m/,v (\d+\.\d+)/; $VERSION = ($1) ? $1 : "UNKNOWN"; $error=0; get_options(); if ($debug) { $quiet = 0 }; $host1 || missing_option("--host1") ; $port1 = (defined($port1)) ? $port1 : ($ssl1 ? 995 : 110); $user1 || missing_option("--user1"); $password1 || $passfile1 || missing_option("--passfile1 or --password1"); $password1 = (defined($passfile1)) ? firstline ($passfile1) : $password1; $host2 || missing_option("--host2") ; $port2 = (defined($port2)) ? $port2 : ($ssl2 ? 993 : 143); $user2 || missing_option("--user2"); $password2 || $passfile2 || missing_option("--passfile2 or --password2"); $password2 = (defined($passfile2)) ? firstline ($passfile2) : $password2; $folder2 = (defined($folder)) ? $folder : "INBOX"; $quiet || print "From pop3 server [$host1] ", ($ssl1 ? "[ssl] " : ""), "port [$port1] user [$user1]\n"; $quiet || print "To imap server [$host2] ", ($ssl2 ? "[ssl] " : ""), "port [$port2] user [$user2]\n"; sub missing_option { my ($option) = @_; die "$option option must be used, run $0 --help for help\n"; } $pop = new Mail::POP3Client( DEBUG => $debugpop, HOST => $host1, PORT => $port1, USESSL => $ssl1, ); $pop->User($user1); $pop->Pass($password1); $pop->Connect(); $pop->Alive() || die $pop->Message(); my %popmess; getpopheaders($pop); if ($ssl2) { use IO::Socket::SSL; # Open an SSL session to the IMAP server # Handles the SSL setup, and gives us back a socket my $issl = new IO::Socket::SSL->new("$host2:$port2"); die ("Error connecting imap server in ssl") unless defined $issl; $issl->autoflush(1); $imap = Mail::IMAPClient->new( User => $user2, Password => $password2, Peek => 1, Uid => 1, Debug => $debugimap, Socket => $issl, ) || die ""; # Tell Mail::IMAPClient we're connected $imap->State(Mail::IMAPClient::Connected); } else { $imap = Mail::IMAPClient->new( Server => $host2, Port => $port2, User => $user2, Password => $password2, Peek => 1, Uid => 1, Debug => $debugimap, ) || die ""; } $imap->select($folder2); foreach my $popid (keys(%popmess)) { $quiet || print "$popid\n"; my @search = $imap->search("HEADER", "Message-ID", "$popid"); if (scalar(@search) == 0) { $quiet || print "No Message-ID Need Transfert\n", "Pop num : ", $popmess{$popid}, "\n"; copypopimap($pop, $imap, $popid); }elsif (scalar(@search) > 1 ) { $quiet || print "Several Message-ID\n"; }else{ $quiet || print "Found $search[0]\n"; if ($delete) { unless($dry) { $pop->Delete($popmess{$popid}); $quiet || print "Deletion completed\n"; }else{ $quiet || print "Deletion not completed (dry mode)\n"; } } } } $pop->Close(); $imap->close; sub getpopheaders { my ($pop) = @_ ; my $count = $pop->Count(); $quiet || print "Found [$count] pop messages\n"; for (my $i = 1; $i <= $count; $i++) { foreach ( $pop->Head( $i ) ) { if (/^Message-Id:\s+(.*)/i) { my $id = $1; $quiet || print "$i ", $_, "\n", "$id", "\n"; if (exists($popmess{$id})) { warn "ID $id already exists"; }else{ $popmess{$id} = $i; } } } #print "\n"; } } sub copypopimap { my ($pop, $imap, $popid) = @_; my $mess = $pop->HeadAndBody($popmess{$popid}); #print $mess; unless($dry) { unless ($imap->append_string($folder2, "$mess")) { print "Transfert failed\n"; }else{ $quiet || print "Transfert completed\n"; if ($delete) { $pop->Delete($popmess{$popid}); $quiet || print "Deletion completed\n"; } } }else{ print "Transfert not done (dry mode)\n"; } } sub firstline { # extract the first line of a file (without \n) my($file) = @_; my $line = ""; open FILE, $file or die("$! $file"); chomp($line = ); close FILE; $line = ($line) ? $line : "!EMPTY! $file"; return $line; } sub get_options { my $numopt = scalar(@ARGV); my $opt_ret = GetOptions( "debug" => \$debug, "debugimap" => \$debugimap, "debugpop" => \$debugpop, "host1=s" => \$host1, "host2=s" => \$host2, "port1=i" => \$port1, "port2=i" => \$port2, "user1=s" => \$user1, "user2=s" => \$user2, "password1=s" => \$password1, "password2=s" => \$password2, "passfile1=s" => \$passfile1, "passfile2=s" => \$passfile2, "ssl1!" => \$ssl1, "ssl2!" => \$ssl2, "folder=s" => \$folder, "delete!" => \$delete, "dry!" => \$dry, "quiet!" => \$quiet, "version" => \$version, "help" => \$help, ); $debug and print "get options: [$opt_ret]\n"; print "$VERSION\n" and exit if ($version) ; usage() and exit if ($help or ! $numopt) ; exit unless ($opt_ret) ; } sub usage { print < : "from" POP server. Mandatory. --port1 : port to connect. Default is 110 (ssl:995). --user1 : user to login. Mandatory. --password1 : password for the user1. Dangerous, use --passfile1 --passfile1 : password file for the user1. Contains the password. --ssl1 : enable ssl on POP connect --host2 : "destination" IMAP server. Mandatory. --port2 : port to connect. Default is 143 (ssl:993). --user2 : user to login. Mandatory. --password2 : password for the user2. Dangerous, use --passfile2 --passfile2 : password file for the user2. Contains the password. --ssl2 : enable ssl on IMAP connect --folder : sync to this IMAP folder. --delete : delete messages in "from" POP server after a successful transfert. useful in case you want to migrate from one server to another one. They are really deleted when a QUIT command is send. --dry : do nothing, just print what would be done. --debug : debug mode. --debugimap : IMAP debug mode. --debugpop : POP debug mode. --quiet : Only print error messages --version : print sotfware version. --help : print this message. Example: to synchronise pop account "foo" on "pop3.truc.org" to imap account "bar" on "imap.trac.org" $0 \\ --host1 pop3.troc.org --user1 foo --passfile1 /etc/secret1 \\ --host2 imap.trac.org --user2 bar --passfile2 /etc/secret2 $rcs pop2imap copyleft is the GNU General Public License. EOF }