Multiple IMAP Accounts with Mutt

2016年02月10日

Background

You should be more or less a fan of command line email if reading this.

Having been using mutt for several years, I have to say my mutt configuration evolves, either following changes in new technology or concepts with email processing.

In early days when POP3 was still the go-to option, mutt was setup to hook up with fetchmail to get email from server, then procmail/maildrop would do some processing, including piping to spamassassin or bogofilter, before finally messages reaches my local inbox. Multiple accounts was easy, because there was no synchronization of my local mailboxes back to remote server, basically messages all messages from different servers were put in the same directory, the local inbox. When a reply message was composed, its header from field was usually set using a send-hook based on the to field of the message replied to, so that I would reply from a wrong email account.

Now, IMAP dominates mainstream email servers, and it enables user to sync back their local mailboxes to remote server. It certainly benefits user more by allowing user to have the same view of email directories across different devices, but at the same time breaks the way multiple accounts could be used in mutt.

  1. An IMAP client that does two way synchronization to server is required. Popular options are offlineimap and isync. Personally I prefer isync, and it is more stable according to my experience. Mutt’s built-in IMAP functionality is not recommended; it is just too slow.

  2. Messages from different servers should be no more put in the same local mailbox. You do not want love letters from your girlfriend to be uploaded to your employer’s mail server.

  3. MDA like procmail is no more possible because neither offlineimap or isync will work with it. This as well means spamassassin or bogofilter should be removed from your configuration as well. In IMAP environment, you should pretty much leave spam identification to the server. This also means it becomes harder to hook up a small script to procmail to do automated email processing like auto-reply and etc. Now there is imapfilter, and you can pipe your emails for further processing.

Okay, so much for background knowledge. Below is how I make an easy to use multi-account mutt configuration for two IMAP accounts.

Sync email with isync / mbsync

The following is a ~/.mbsyncrc section for a gmail account, it stores all emails in subdirectories of ~/.mail/. For another account, a separate mailbox folder should be chosen, for example ~/.business_mail/.

Here I group several sync channels together, and each channel defines a pair of remote and local directories to sync. For example, [Gmail]/Sent Mail is paired with ~/.mail/sent. Doing this enables me to have simpler directory names locally.

IMAPAccount gmail

Host imap.gmail.com
User XXXXXXX@gmail.com
Pass XXXXXXX
SSLType IMAPS
AuthMechs LOGIN
CertificateFile /Users/roylez/.root.crt

IMAPStore gmail-remote
Account gmail

MaildirStore gmail-local
# The trailing "/" is important
Path ~/.mail/
Inbox ~/.mail/inbox

Channel gmail-default
Master :gmail-remote:
Slave :gmail-local:
Patterns INBOX

Channel gmail-sent
Master :gmail-remote:"[Gmail]/Sent Mail"
slave  :gmail-local:sent

Channel gmail-trash
Master :gmail-remote:"[Gmail]/Trash"
slave  :gmail-local:trash

Channel gmail-archive
Master :gmail-remote:"[Gmail]/All Mail"
slave  :gmail-local:all

# Automatically create missing mailboxes, both locally and on the server
Create Both
# Automatically delete messages on either side if they are found deleted on the other.
Expunge Both
# Save the synchronization state files in the relevant directory
SyncState *

Group gmail
Channel gmail-default
Channel gmail-trash
Channel gmail-archive
Channel gmail-sent

After setting up all accounts, I can use the following command to test synchronization.

mbsync -a

To automate it, a crontab entry is used. Since if network is down during synchronization will cause mbsync to hang indefinitely, I make it first try to kill any existing processes.

*/15 *   *   *   *   killall mbsync &>/dev/null; /usr/local/bin/mbsync -a -q

Setup Account Profiles

Now I have two accounts, say, one personal and one business. I then create separate account profiles for mutt to load.

The following is for ~/.mutt/account.personal, and there should be another similar one, namely ~/.mutt/account.business.

# vim: ft=muttrc
set folder    = "~/.mail"
set mbox      = "+inbox"
set record    = "+inbox"
set postponed = "+inbox"
set spoolfile = "~/.mail/inbox"
set from      = 'me@roylez.info'
set sendmail  = "~/.mutt/do_sendmail msmtp"

set signature = 'fortune -s|cowsay -f small -W 70|'
set status_format="===[ 个人: %f ]---[ 邮件:%?M?%M/?%m%?n? 新:%n?%?o? 旧:%o?%?d? 删除:%d?%?F? 星:%F?%?t? 标记:%t?%?p?? ]----%>-(%P %l)==="

macro index,pager d "<save-message>=trash<enter>" "Trash"
macro index,pager y "<save-message>=all<enter>" "Archive"

You can see in this profile, every mailbox points to a directory under ~/.mail/. Also you should notice I have a customized sendmail command. Because we should select different SMTP server for different accounts, this is almost a must. Luckily msmtp allows to select a profile from command line. Here the my script do_sendmail does some extra things, like attachment checking and collecting recipient email address for future query_command look up. You do not have to worry about this script, and it is perfectly okay if you put simply set sendmail="msmtp -a gmail" here.

Also, for this account I have tailed signature and mutt status_format. The latter is especially useful, because it enables me to tell which account I am dealing with.

The last two macro definitions are critcal. The first mimics “moving to trash” action on the server, and the second mimics an “archive” operation, which nowadays almost everyone cannot live without.

Attention: You may discover duplicated emails in “all” directory after an archive operation, but do not panic. After two more synchronization the extra copy will be gone because Gmail identifies and takes care of duplications on the server.

Switch profiles with ease

In my ~/.muttrc I have the following

# make c contextual according to archlinux wiki
macro index 'c' '<change-folder>?<change-dir><home>^K=<enter>'

source ~/.mutt/account.business
macro index <f2> '<enter-command>source ~/.mutt/account.personal<enter><change-folder>!<enter>'
macro index <f3> '<enter-command>source ~/.mutt/account.business<enter><change-folder>!<enter>'

# use tab to switch between accounts, inspired by
#  http://msmtp.sourceforge.net/doc/msmtp.html#Using-msmtp-with-Mutt
macro generic \Cx| "<enter-command>source"
macro generic \Cx& "<enter-command>macro index \\t \"\\Cx"
macro index <tab> "\Cx0"    # default change to account 1, this will be redefined once pressed
macro generic \Cx0 "\Cx| ~/.mutt/account.business\"\n\Cx&1\"<enter><change-folder>!<enter>"
macro generic \Cx1 "\Cx| ~/.mutt/account.personal\"\n\Cx&0\"<enter><change-folder>!<enter>"

In the first section, macro c redefinitions is required to force a <change-dir> reload after sourcing a new profile and changing mailbox folder.

Macro <f2> and <f3> are defined to be shortcuts to different accounts, respectively. However, it turns out that I do not use them much.

The last section defines <tab> to be the key to toggle between two different accounts. The selection of keymappings of \Cx| and alike is to make sure these utility macros would not be trigger by mistake. If you have more than two accounts, then you would have to add additional macros, say \Cx2 and etc to chain them up.