personal web log written by izabeera and dryobates

imap mutt

IMAP in Mutt

by dryobates

With current versions of Mutt you can directly connect to your IMAP account. I preffer use it offline and have my reasons for that.

Do you know the rule of three camels? When you go to desert you should get three camels with you. When you ride a camel and it dies of thirst, then it is very likely, that the other one will see it and run away. Then you have some chance to catch the third one.

How is that story to emails? Let say you use gmail. What would happen, when it would went down? Impossible? Some time ago Google has started offering backup of your data for money. That means they also consider scenario, that they can loose your data.

How I use email? I have two IMAP accounts: private and company. Both are accessible via web interface. The former is mentioned gmail and is quite handy. The latter is... not handy at all :/ On my home computer I have configured OfflineIMAP [1] to get emails from both my accounts. The same for my computer at work.

Now if my company account went down and admins were looking for backup, I would still have an access to my emails from both my computers. If I removed some important mail by accident on my home computer and synchronize with gmail I would still have a chance to recover it from my company computer.

So, to the point. Installation and configuration of OfflineIMAP as always is described at software page [1]. For FreeBSD installation I have used ports collection (with an aid for lazy guys ;) - portmaster [2]):

$ portmaster mail/offlineimap

Now configuration. In file "~/.offlineimaprc" define "general" section:

[general]
accounts = Work,Gmail
ui = ttyui
fsync = False
pythonfile=~/.mutt/offlineimaprc.py

In line 2 I declared two accounts: "Work" an "Gmail". We'll use those names in subsequent section names. In line 3 there is terminal type declaration. I use the simple one. There are many more described in documentation. Declaration in line 4 (fsync) means that I take a risk of some accidental data lose in order to get some speed. Of course you do not need to include that line in your configuration. Last line in general section points to python file with some helper functions. We'll use them in next parts of configuration file.

Next goes an accounts declaration:

[Account Gmail]
localrepository = Gmail-Local
remoterepository = Gmail-Remote
status_backend = sqlite

[Account Work]
localrepository = Work-Local
remoterepository = Work-Remote
status_backend = sqlite

For every account we declare separate section describing account. In an account section there have to be declared local (lines 2 and 6) and remote (3 and 7) repositories. Mails will be synchronized between those two accounts. Last option ("status_backend") tells to use "sqlite" as backend for caching state of synchronization. The other option is "plain" for using plain text file. If you do not already have sqlite [4] then better install it now:

$ portmaster databases/sqlite3

Now we have to declare remote repositories:

[Repository Gmail-Remote]
maxconnections = 1
type = Gmail
remoteusereval = get_username('Gmail')
remotepasseval = get_password('Gmail')
nametrans = lambda folder: get_folder_name(folder, 'Gmail')
folderfilter = lambda folder: folder in get_folders('Gmail')
cert_fingerprint = b0ba392bba326e6feb1add4d04fa0fb86cd173fa

[Repository Work-Remote]
maxconnections = 2
type = IMAP
remotehost = imap.grupazpr.pl
remoteusereval = get_username('Work')
remotepasseval = get_password('Work')
nametrans = lambda folder: get_folder_name(folder, 'Work')
folderfilter = lambda folder: folder in get_folders('Work')

For remote repository we have to define it's type and provide host (remotehost). Gmail is not exactly IMAP server, so it has it's one type "Gmail". Because there is only one such service, you do not need to provide host.

We have to provide username and password. We could set them directly using settings "remoteuser" and "remotepass" but keeping it in configuration file isn't wise. Let's use "remoteusereval" and "remotepasseval" settings. They allow as to provide a python function which will be evaluated and return username and password.

Those functions we declare in file mentioned in "pythonfile". For testing this file can look like:

 ACCOUNTS = {
     'Work': ('work.user@work.hard', 'XXX'),
     'Gmail': ('gmail.user', 'XXX'),
 }


def get_username(account_name):
    return ACCOUNTS[account_name][0]


def get_password(account_name):
    return ACCOUNTS[account_name][1]

You can adjust those functions to use eg. gnome-keyring or KDEWallet.

For remote repository we can also define nametrans function and folderfilter function. The former maps IMAP folder names to those on our computer. The latter selects which folders we want synchronize. I have defined mapping functions in the same "pythonfile". My looks that:

# -*- coding: utf-8 -*-

FOLDERS = {
    'Gmail': {
        u'INBOX': 'INBOX',
        u'[Gmail]/Wersje robocze': 'drafts',
        u'[Gmail]/Wysłane': 'sent',
        u'[Gmail]/Kosz': 'trash',
        u'[Gmail]/Wszystkie': 'read',
    },
    'Work': {
        'INBOX': 'INBOX',
        'Drafts': 'drafts',
        'Sent': 'sent',
        'error': 'error',
        'cron': 'cron',
        'read': 'read',
        'reply': 'reply',
        'waiting': 'waiting',
        'defer': 'defer',
    }
}


def unicode_to_utf7(value):
    return value.replace(u'&', u'+').encode('utf-7')


def get_folder_name(folder, account):
    folder_map = FOLDERS.get(account)
    return folder_map.get(unicode_to_utf7(folder), folder)


def get_folders(account):
    folder_map = FOLDERS.get(account)
    return [unicode_to_utf7(folder) for folder in folder_map.keys()]

If you have accounts with folder names other then ASCII then you have to map them to UTF-7 for use with IMAP. For example my Gmail folder "[Gmail]/Wysłane" translated to UTF-7 looks like "[Gmail]/Wys&AUI-ane".

There are two more settings in remote repository. Option "maxconnections" can speed up synchronization, but be careful not to be banned for to many connections at the same time. The last option is certificate hash used when connecting via SSL.

Local repository settings are simpler then remote:

[Repository Gmail-Local]
type = Maildir
localfolders = ~/.mail/gmail
nametrans = lambda folder: get_folder_name_rev(folder, 'Gmail')

[Repository Work-Local]
type = Maildir
localfolders = ~/.mail/work
nametrans = lambda folder: get_folder_name_rev(folder, 'Work')

We set type (I use Maildir format) and local folders, where mails will be stored. Option "nametrans" in this part translates from local folders to remove.

To check settings type:

$ offlineimap --info

And you will see something like that:

...

nametrans= lambda folder: get_folder_name(folder, 'Gmail')

Folderlist:
 [Gmail]/Oznaczone gwiazdk&AQU- (disabled)
 [Gmail]/Wys&AUI-ane (disabled)
 [Gmail]/Wersje robocze -> drafts
 INBOX
 [Gmail]/Wszystkie -> read
 [Gmail]/Kosz -> trash

Local repository 'Gmail-Local': type 'Maildir'
nametrans= lambda folder: get_folder_name_rev(folder, 'Gmail')

Folderlist:
 waiting
 flagged
 INBOX
 trash -> [Gmail]/Kosz
 drafts -> [Gmail]/Wersje robocze
 sent -> [Gmail]/Wys&AUI-ane
 read -> [Gmail]/Wszystkie
...

Under first "Folderlist" are mappings from remote repository to local. If there is "disabled" near line, then it won't be synchronized. Under section "Folderlist" are mappings for other direction.

If all mappings are correct type:

$ offlineimap

And go make a tea. If you have a lot of emails first synchronization can be very long. After some time OfflineIMAP will synchronize all your emails. Next time running the same command will be much faster. Only new, updated and deleted emails will be synchronized. You can add this command as your cron job if you want automatic synchronization and/or you can add it as macro in your Mutt [3] configuration. I have three such macros:

macro index O "<shell-escape>offlineimap<enter>" "run offlineimap to sync all mail"
macro index \Co "<shell-escape>offlineimap 2> /dev/null &<enter>" "run offlineimap to sync all mail in background"
macro index o "<shell-escape>offlineimap -qf INBOX<enter>" "run offlineimap to sync inbox"

As description states the first two commands starts synchronizing all emails (one runs it in background). The last one if for quick check that only INBOX folder has changed and if it is then synchronize them.

[1](1, 2) OfflineIMAP - IMAP synchronization utility: http://offlineimap.org/
[2]FreeBSD ports management script: http://portmaster.github.io/
[3]Mutt - e-mail client: http://www.mutt.org
[4]SQLite: http://www.sqlite.org/
dryobates
dryobates
Jakub Stolarski. Software engineer. I work professionally as programmer since 2005. Speeding up software development with Test Driven Development, task automation and optimization for performance are things that focus my mind from my early career up to now. If you ask me for my religion: Python, Vim and FreeBSD are my trinity ;) Email: jakub@stolarscy.com

Archive

Tag cloud