503 Service Unavailable

2007-11-22

Universal mailbox converter in 24 lines

Filed under: Programming,Software — rg3 @ 18:40

Sometimes you have a quantity of email messages stored in a specific mailbox under a given format and you need to convert them to another mailbox format, like from Maildir to mbox or MH to Maildir, etc. There are some solutions out there. Programs capable of translating from one format to another, or sometimes mail clients (MUAs) that can use the two formats in question and you can do the conversion by moving messages from within the client itself. Some days ago I found out, by accident, that Python has a mailbox module that can read and write to many different mailbox formats (at the time I’m writing this Python 2.5.1 understands Maildir, mbox, MH, Babyl, and MMDF). This module can be used to create a very simple “universal mailbox converter” easily, like the following one:

#!/usr/bin/env python
import mailbox
import os.path
import re
import sys

try:
    appname = os.path.basename(sys.argv[0])
    source = sys.argv[1]
    dest = sys.argv[2]

    (sfmt, dfmt) = re.match(r'^(.+)2(.+)', appname).groups()
    sbox = mailbox.__dict__[sfmt](source)
    dbox = mailbox.__dict__[dfmt](dest)
    
    for key in sbox.iterkeys():
        dbox.add(sbox.get_message(key))

except IndexError:
    sys.exit('Usage: %s source destination' % appname)
except (AttributeError, KeyError):
    sys.exit('ERROR: invalid mailbox type')
except mailbox.Error, err:
    sys.exit('ERROR: %s' % err)

This 24 lines program uses the mailbox module to do the conversion. The type of conversion it performs depends on the name under which it’s invoked. If you call the program “Maildir2mbox” it translates between those two formats. In general, the name should be “2[destination format]” where the source and destination formats are the ones I mentioned above: Maildir, mbox, MH, Babyl, MMDF. It expects two arguments, which are the paths to the source and destination mailboxes, respectively. If more formats are added in the future they will be automatically supported by invoking the program with the appropriate name. For example, in my hard drive I have called the program “Maildir2mbox” and I have created symbolic links to every other permutation of formats:

$ ls -l
lrwxrwxrwx 1 root root   12 2007-11-22 12:36 Babyl2MH -> Maildir2mbox
lrwxrwxrwx 1 root root   12 2007-11-22 12:36 Babyl2MMDF -> Maildir2mbox
lrwxrwxrwx 1 root root   12 2007-11-22 12:36 Babyl2Maildir -> Maildir2mbox
lrwxrwxrwx 1 root root   12 2007-11-22 12:36 Babyl2mbox -> Maildir2mbox
lrwxrwxrwx 1 root root   12 2007-11-22 12:36 MH2Babyl -> Maildir2mbox
lrwxrwxrwx 1 root root   12 2007-11-22 12:36 MH2MMDF -> Maildir2mbox
lrwxrwxrwx 1 root root   12 2007-11-22 12:36 MH2Maildir -> Maildir2mbox
lrwxrwxrwx 1 root root   12 2007-11-22 12:36 MH2mbox -> Maildir2mbox
lrwxrwxrwx 1 root root   12 2007-11-22 12:36 MMDF2Babyl -> Maildir2mbox
lrwxrwxrwx 1 root root   12 2007-11-22 12:36 MMDF2MH -> Maildir2mbox
lrwxrwxrwx 1 root root   12 2007-11-22 12:36 MMDF2Maildir -> Maildir2mbox
lrwxrwxrwx 1 root root   12 2007-11-22 12:36 MMDF2mbox -> Maildir2mbox
lrwxrwxrwx 1 root root   12 2007-11-22 12:36 Maildir2Babyl -> Maildir2mbox
lrwxrwxrwx 1 root root   12 2007-11-22 12:36 Maildir2MH -> Maildir2mbox
lrwxrwxrwx 1 root root   12 2007-11-22 12:36 Maildir2MMDF -> Maildir2mbox
-rwxr-xr-x 1 root root  566 2007-11-22 12:22 Maildir2mbox
lrwxrwxrwx 1 root root   12 2007-11-22 12:36 mbox2Babyl -> Maildir2mbox
lrwxrwxrwx 1 root root   12 2007-11-22 12:36 mbox2MH -> Maildir2mbox
lrwxrwxrwx 1 root root   12 2007-11-22 12:36 mbox2MMDF -> Maildir2mbox
lrwxrwxrwx 1 root root   12 2007-11-22 12:36 mbox2Maildir -> Maildir2mbox

For Python programmers, the trick is very simple. The mailbox module has a general class called Mailbox with one subclass for each supported mailbox type. For example, mailbox.Maildir is the class implementing the Maildir mailboxes. Fortunately, you can create objects by a string representing their class name in Python, thanks to the __dict__ dictionary or map every Python module has, that maps each class name to the corresponding class. To create a mailbox.Maildir object I can use mailbox.Maildir(…) or mailbox.__dict__[‘Maildir’](…). The program simply extracts the mailbox format names from the program name obtained with sys.argv[0] and uses the method to create mailboxes in the right format.

Advertisements

1 Comment »

  1. I adapted the script to work on Python 2.6.

    As originally designed, the script file name must be Maildir2mbox or Maildir2mbox.py.
    (“M” must be capitalized as the working mailbox format name is “Maildir”, not “maildir”.

    Maildir2mbox.py:

    #!/usr/bin/python
    import mailbox
    import os.path
    import re
    import sys

    try:
    appname = os.path.split( sys.argv[0] )[1]
    basename = os.path.splitext( appname )[0]
    source = sys.argv[1]
    dest = sys.argv[2]

    # This breaks maildir2mbox into “maildir” and “mbox”.
    (sfmt, dfmt) = re.match(r’^(.+)2(.+)’, basename).groups()

    # Samples for the following lines are:
    # sbox = mailbox.__dict__[‘Maildir’](‘../maildir’, factory=None)
    # dbox = mailbox.__dict__[‘mbox’](‘Inbox_mbox’)

    # Avoid problematic default rfc822.Message factory,
    # which caused this error:
    # File “/usr/lib/python2.6/mailbox.py”, line 331,
    # in get_message msg.set_subdir(subdir)
    # AttributeError: Message instance has no attribute ‘set_subdir’
    sbox = mailbox.__dict__[sfmt](source, factory=None)
    dbox = mailbox.__dict__[dfmt](dest)

    for key in sbox.iterkeys():
    dbox.add( sbox.get_message(key) )

    except IndexError:
    sys.exit(‘Usage: %s source destination’ % appname)
    except (AttributeError, KeyError):
    sys.exit(‘ERROR: invalid mailbox type’)
    except mailbox.Error, err:
    sys.exit(‘ERROR: %s’ % err)

    I only tested it once to solve my own problem and share the result of my effort for others to try out and possibly save some time.

    Regards,

    Daniel.

    Comment by Daniel — 2010-10-27 @ 15:45 | Reply


RSS feed for comments on this post.

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 )

Google+ photo

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

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: