Text-based email setup with mbsync and mu

Composing mail without the assistance of emacs is painful for serious emacs users. KMail (supplemented with emacsclient) had been my email client of choice for more than a decade. While KMail continues to be an excellent mail client, I started looking more into moving my entire email flow into emacs. Sending mail from emacs is pretty easy with smtpmail; so the only issue was finding a way to fetch and display email. After trawling The Interwebz, mu4e seemed to fit in best with my workflow.

Initial setup

Setting up mu4e was pretty easy since its parent package mu can be set up as a git submodule (or as a git subtree) of an emacs initialization directory and can then be compiled in place. Most of the initial setup is straightforward since the built in manual is pretty comprehensive; Rob Stewart's excellent guide is a handy summary for the impatient. The only other initial setup choice was between offlineimap and mbsync. After initial attempts at setting up both, I ended up choosing mbsync.

Fetching email with mbsync

While the mbsync man page is pretty comprehensive, it can be a little intimidating to set it up for use with mu4e. The basic setup for one email account is similar to the following:

IMAPAccount MyAccountName
Host imap.gmail.com
User MyAccountName@gmail.com
PassCmd "gpg2 -q --for-your-eyes-only --no-tty -d ~/.authinfo.gpg | awk '/machine imap.gmail.com login MyAccountName@gmail.com/ {print $NF}'"
# Use SSL
UseIMAPS yes
CertificateFile /etc/pki/tls/certs/ca-bundle.crt

IMAPStore MyAccountName-remote
Account MyAccountName

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

Channel MyAccountName
Master :MyAccountName-remote:
Slave :MyAccountName-local:
# Exclude everything under the internal [Gmail] folder, except the interesting folders
Patterns * ![Gmail]* "[Gmail]/Sent Mail" "[Gmail]/Starred" "[Gmail]/All Mail" "[Gmail]/Drafts"
# Or include everything
#Patterns *
# Automatically create missing mailboxes, both locally and on the server
Create Both
# Save the synchronization state files in the relevant directory
SyncState *

The set up above is typical for Google mail. There are a couple of interesting bits, however. First, the use of SSL requires that certificate bundles be available to mbsync. On Fedora Linux 19 and later, the easiest way is to install ca-certificates.noarch using the package manager. The location of installed certificates is provided to mbsync via the CertificateFile directive.

The second and more interesting part is the handling of passwords.

Using GPG for mbsync passwords

The basic idea is that every time a password is needed, an particular file is decrypted and loaded. The key for the decryption can be prompted for and be stashed by gpg-agent. The first step is to create a GPG key, which is covered very well elsewhere. The standard authentication mechanism for gnus and smtpmail can be reused to store login information for mbsync. For any one account, the password for IMAP access and the password for sending email (usually the same) can be added to ~/.authinfo.gpg:

machine imap.gmail.com login MyAccountName@gmail.com password MYPASSWORD
machine smtp.gmail.com login MyAccountName@gmail.com password MYPASSWORD

The first line is used by mbsync and the second by smtpmail. The line:

PassCmd "gpg2 -q --for-your-eyes-only --no-tty -d ~/.authinfo.gpg | awk '/machine imap.gmail.com login MyAccountName@gmail.com/ {print $NF}'"

automatically fetches the password for the given account from authinfo.gpg. It is easy now with a couple of simple modifications to ~/.gnupg/gpg-agent.conf to make sure that the password prompt appears under X and that the key is stashed:

pinentry-program /usr/bin/pinentry-qt4
default-cache-ttl 720000
max-cache-ttl 720000

The stash time for passwords is set to 200 hours.

Initial mu4e setup

I chose to set up mu4e sources as a git submodule in my emacs directory with path site-lisp/mu, which necessitated the following changes to my emacs start up code:

(add-to-list 'Info-additional-directory-list (ravi/emacs-file "site-lisp/mu/mu4e"))
(setq mu4e-mu-binary (ravi/emacs-file "site-lisp/mu/mu/mu"))

The next part ensures that the main mu4e window is quickly accesible from a key binding without opening duplicate buffers:

(defun ravi/switch-to-mu4e ()
  (let ((buf (or (and (boundp 'mu4e~headers-buffer-name)
                       (get-buffer mu4e~headers-buffer-name))
                 (get-buffer "*mu4e-main*"))))
    (if buf
        (if (get-buffer-window buf t)
            (select-window (get-buffer-window buf t))
          (switch-to-buffer buf))
(bind-key "C-'" 'ravi/switch-to-mu4e)

That's all for now. We will cover setting up multiple accounts with different polling intervals in a future post.


Comments powered by Disqus