Syncing mails with mbsync (instead of OfflineIMAP)

Update: Ryan Lue pointed me to his little_red_flag daemon which triggers mbsync synchronization if changes happen both locally and remotely. Try it out!

A lot of people recommend OfflineIMAP for syncing mails between a remote IMAP account and a local maildir mailbox for mutt1 pleasure. However, OfflineIMAP’s quality is far from what I think is necessary for a tool handling sensitive data such as mail. Looking for an alternative, I stumbled upon [mbsync][] which was maintained by Ted Ts’o for a while. Setting it up to synchronize with the Google mail servers is pretty simple once you know the quirks of Google’s IMAP implementation and mbsync’s weird configuration approach.

To get it working with Google I had to compile mbsync from source — the packaged version didn’t work for me. mbsync just wouldn’t download any mails. So, go ahead, download the mbsync source and configure && make && make install it. mbsync groups its .mbsyncrc configuration into account details, stores and channels that connect stores for synchronization. A typical GMail account would look like this:

IMAPAccount gmail
Host imap.gmail.com
User foo.bar@gmail.com
Pass secret
UseIMAPS yes
CertificateFile /etc/ssl/certs/ca-certificates.crt

Saving the password plaintext is not the best idea. Fortunately, the latest mbsync sources come with the PassCmd setting, which is just a command that is run in a subprocess of mbsync. With a tiny Python script such as this:

#!/usr/bin/env python

import argparse
import keyring
import getpass

if __name__ == '__main__':
    SERVICE = 'mbsync'

    parser = argparse.ArgumentParser()
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument('--set', '-s', type=str, help='Account to save password')
    group.add_argument('--get', '-g', type=str, help='Account to get password')

    args = parser.parse_args()

    if args.set:
        password = getpass.getpass()
        keyring.set_password(SERVICE, args.set, password)
    else:
        print(keyring.get_password(SERVICE, args.get))

and PassCmd set to:

PassCmd "imap-pass -g foo.bar@gmail.com"

we have a relatively secure way of retrieving the login password through our system’s keyring. Now we have to define a proxy store for the IMAP account and a store for our local maildir

IMAPStore gmail-remote
Account gmail

MaildirStore gmail-local
Path ~/mail/gmail/
Inbox ~/mail/gmail/INBOX

To sync between those stores, you have to set up a channel. Most options are self-explanatory, however you must take care to use the correct label names for the sent, draft, starred etc. mail. The names depend on the language set in the web interface! For me, the verbose flag -V came in handy because this displays the communication with the IMAP server, including listing all the available IMAP directories.

Channel gmail
Master :gmail-remote:
Slave :gmail-local:
Patterns "INBOX" "[Gmail]/Sent Mail" "[Gmail]/Drafts" "[Gmail]/Starred"
Create Both
Expunge Both
SyncState *

Now, running mbsync gmail syncs mail on the Google servers with your local maildir in ~/mail/gmail.

1

I wanted to write a post about mutt for quite a while now, but I am not ready with my configuration nor do I have much time to document my progress. Hence, I will present different aspects one by one. [mbsync]: http://isync.sourceforge.net/mbsync.html