I had a recent problem at work. We needed to copy a folder in one IMAP mailbox to another. It wasn't as easy as it should have been. The folder in question is just below 15GB. The obvious route is to copy it using a mail client. The first one I tried was Evolution 2.24. Evolution does copy folders, but it only copies the mail out of folders you've opened. This is a problem with 3500+ folders. I'll probably file a bug with GNOME about this. The next client was Kmail.
Kmail 3.5.10 does the copying perfectly...until is crashes. Kmail is pathetic in the realm of stability. The KDE team needs to make Kmail more resilient. It should display an error instead of dying. I didn't have a Windows machine available to try Outlook Express on or I would have tried that too.

Since everything seems to have trouble with folders this size, I decided to try my hand at the task with Python. I ended up with this.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#!/usr/bin/env python

import getpass
import imaplib
import sys

def cpimap(user1, host1, pass1, user2, host2, pass2, target='/'):
    m1 = imaplib.IMAP4_SSL(host1, 993)
    m2 = imaplib.IMAP4_SSL(host2, 993)
    m1.login(user1,pass1)
    m2.login(user2,pass2)

    folders = [folder.split(' "/" ')[1][1:-1] for folder in m1.list(target)[1]]

    folders.insert(0, target) # Copy messages in the root of the target too

    print 'Copying', len(folders), 'folders...'

    for f in folders:
        if '\\' in f:
            print 'Skipping', f
            # imaplib does not support backslashes in mailbox names!
            continue
        print 'Copying', f
        m2.create(f)
        m1.select(f)
        print 'Fetching messages...'
        typ, data = m1.search(None, 'ALL')

        msgs = data[0].split()

        sys.stdout.write(" ".join(['Copying', str(len(msgs)), 'messages']))

        for num in msgs:
            typ, data = m1.fetch(num, '(RFC822)')
            sys.stdout.write('.')
            m2.append(f, None, None, data[0][1])
        sys.stdout.write('\n')

If you look through the code, you may notice that imaplib is a messy module to work with. At the very least, the user of imaplib doesn't need to know exactly how the IMAP protocol works. There is a lot more output that what is needed. With a few tutorials, like this one, you can get a usable piece of code in a few minutes.

One limitation of imaplib is operations on folders containing backslashes. The module does not properly escape them. If you try to do anything with a folder named back\slash, then it will try to apply the procedure on back\\slash.This is a bug in imaplib.

Posted by Tyler Lesmann on October 10, 2008 at 12:39
Tagged as: imaplib python
Post a comment