Migrating thousands of mailboxes to a new mailbox storage

Years ago we were faced with the following situation: We had thousands of mailboxes that were being served by a proprietary and unsupported version of email software. So we decided to move to a new architecture. While researching I selected a very good (IMHO) commercial product. But for reasons outside the scope of the post I opted for a F/OSS solution, and specifically the UW-IMAP toolkit. For those rushing to judge that price was the decisive factor I only have to reiterate Vladimir Butenko‘s words:

“Bottom line: you always pay. You need a simple thing – you pay a small amount, you need a big thing – you pay more.” (plain message and comp.mail.imap thread)

OK and now for the real question: How do you move thousands of mailboxes without your users noticing?

Since you do not know (and do not want to know) your users’ passwords the only thing you can do is to hack into the source code of your POP3 server (we do not offer IMAP yet). Whether it is the UW-IMAP toolkit, Cucipop, popa3d or any other server that you have access to its source, the server knows the correct password for your users when they authenticate. So when the authentication succeeds you fork(2) a program that logs into the old server and fetches the mailbox from the old server to the new one that runs the F/OSS software that you have selected. This can be fetchmail or any similar program. However I have found out that Net::POP3 is a better choice:

use Sys::Syslog;
use Net::POP3;
use DB_File;
$host = shift or die;
$user = shift or die;
$pass = shift or die;
tie %d, 'DB_File', "/etc/pop3.users.db", O_CREAT|O_RDWR, 0640, $DB_BTREE or die;
if ($d{$user}) {
        untie %d;
        exit 0;
$pop = Net::POP3->new($host) or die;
if ($pop->login($user, $pass) >= 0) {
        openlog("pop3cat-tmail", 'pid', 'mail') or die;
        syslog('info', 'fetching mail for user %s', $user);
        $msgnums = $pop->list or die;
        foreach $msgnum (keys %$msgnums) {
                $msg = $pop->get($msgnum);
                open T, "| /usr/bin/tmail $user" or die;
                print T @$msg;
                close T;
        $d{$user} = "OK";
        untie %d;
exit 0;

Where exactly in the server code you fork(2), exec(3) and wait(2) for the script depends on the source code. You need to find where in the server code the authentication procedure succeeds and patch from there, before the server actually opens the user’s mailbox.

This script basically checks whether it has already moved a user’s mailbox from the old server. If it has, then the user is found in /etc/pop3.users.db and the script exits. If not, then the mailbox is moved and the user is inserted in /etc/pop3.users.db. Simply using Net::POP3 allows you to move the old mailbox to the Unix traditional mailbox format. That is why we fork tmail, since I have chosen the mbx mailbox format to store messages (I compile UW-IMAP with CREATEPROTO=mbxproto). If your server supports a format like Maildir, then you have to customize the above script accordingly, since it is written for mbx, which means that the user’s mailbox is a single file.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s