Emacs as a Mail Client

Posted on March 8, 2021

I wanted to write a few posts on how I use Emacs. As a disclaimer, I'm using Pop!_OS, so these are all Linux posts. Sorry for not being able to contribute to the dearth of material available to Windows users. On an unrelated note, I think previously I said that I wanted to make content accessible, and this is probably a step in the wrong direction on that front, but I find myself just not writing with all the constraints I'm trying to place on myself. Anyway, this is the first in (hopefully) a series of posts covering each arbitrarily-defined category of tasks I use Emacs for. To illustrate, this post is about using Emacs for email. If you're bothering to read this, it may be helpful to look through my Emacs config for more context.

So, why use Emacs as a mail client? At first, I only set it up as a somewhat masochistic challenge to myself during some free time I had visiting in-laws. To be honest, I didn't really use it with any consistency until recently. There are a lot of advertised reasons to make this switch: you can have a mail client (almost) as configurable as Emacs itself, it provides yet another way to use the Emacs keybindings and quality of life niceties that you inevitably get attached to, you can avoid the UI supplied by your provider, and you can integrate email into your task management workflow (i.e. org-mode) to reap all the benefits there. All of these things are great, but truth be told, at this point in time I don't use org-mode regularly enough to take advantage of those benefits and I don't mind Gmail's UI that much. Really, the one thing that's keeping me here is that I spend a lot less time going through my email. The majority of my time is spent deleting stuff, and it's nice to delete a message and have it instantly disappear so you can delete the next one. There's no animation as the message goes away, and there's no time spent moving the mouse around to click buttons.

There are some things Mu4e doesn't excel at, most notably rendering HTML messages. Most of the time, when a message hits your inbox, there's both an HTML and plain text version. The quality of the plain text version varies: sometimes it's a great minimal version of the HTML, sometimes it's simply a notification saying you need to read the HTML version. For situations where HTML is your only option, there's mu4e-action-view-in-browser which, you guessed it, opens a message in your default web browser.


So let's say you want to move to Mu4e. In my config, there's a line that reads: mu4e-get-mail-command "mbsync -c ~/.config/emacs/mu4e/.mbsyncrc -a". Installing Mbsync is covered in my config, but the .mbsyncrc file could stand some explaining. From the tutorials I've seen the documentation is never referenced but is actually very good and found here. I'll briefly go through my config by item. The first item is the IMAPAccount for my Gmail account:

IMAPAccount gmail
Host imap.gmail.com
Port 993
User andrewwburch
PassCmd "gpg2 --decrypt ~/.authinfo.gpg | grep smtp.gmail.com | cut -d ' ' -f6"
AuthMechs LOGIN
SSLVersions TLSv1.2
CertificateFile /etc/ssl/certs/ca-certificates.crt

Every email account will be linked to its own IMAPAccount item. Within this item, you specify everything Mbsync needs to connect (host, port, SSL details, etc.). Instead of providing a password in plain text, it's advised to use PassCmd, which allows you to provide a shell command that will yield your password. I've recently started using Gnus Authinfo, which entails creating a .authinfo or an encrypted .authinfo.gpg file containing all of your passwords in a somewhat standard format. You can read up on it here, but the contents of your .authinfo file boil down essentially to this:

machine IDENTIFIER login USERNAME password PASSWORD port PORT

Everything in caps is something you replace, the rest you leave alone (or omit, not every service you use has a related port). For example, the IDENTIFIER field for Gmail would be smtp.gmail.com, USERNAME and PASSWORD are self-explanatory, and the relevant port is 587. Most packages I've dealt with that require auth of some kind have some kind of integration with Gnus Authinfo, so it's good to know the format, and, if you're like me and try to use Emacs for most things, it Gnus Authinfo provides a centralized and accessible location for secrets. With that, let's continue with Mbsync.

Since Mbsync isn't part of Emacs, it doesn't understand Gnus Authinfo, so the easiest way I could think of to make it work was to decrypt the file, grep the line I wanted, split the line into tokens, and pull the token that contained the password. Currently, the AuthMechs is set to LOGIN which isn't the recommended way; Gmail will inform you that Mbsync is not using up-to-date security standards and by default blocks the connection. This is easy enough to circumvent, but the better option long term is to move to OAuth. I might post something about that in the future, it's not that easy to find documentation on.

The next thing Mbsync needs is an IMAPStore:

IMAPStore gmail-remote
Account gmail

Each account needs an IMAPStore with its own name, which I have called gmail-remote. The IMAPStore contains all the information about the remote mail repository. Its only property is the Account which is set to the name of the IMAPAccount, in my case gmail.

Once you specify the remote, you need to provide information on your local mail repository via the MaildirStore item:

MaildirStore gmail-local
Path ~/Mail/
Inbox ~/Mail/Gmail

Moving down the line, each folder you want access to in your GMail account needs to be synced. Let's look at one:

Channel gmail-drafts
Master :gmail-remote:"[Gmail]/Drafts"
Slave :gmail-local:Drafts
Create Slave
Expunge Both
SyncState *

Each channel needs a unique name. I don't believe it really matters what it is since it's simply a mapping. The next line specifies the master. Here, we provide the name of the remote (gmail-remote) and the path on the remote ([Gmail]/Drafts). Note, that when you map the entire inbox, you don't need to specify a path, at least for Gmail. The path is platform specific. Unfortunately, I don't know of any place where this is documented, but there are many Mbsync tutorials out there, hopefully one matches your use case. The next line specifies the slave, or your local directory you want to map to (gmail-local) and the subdirectory (Drafts). The next line, as I understand it, governs sync behavior: Create Both provides two-way syncing between the remote and local, Create Slave prevents syncing local to the remote, Create Master prevents syncing remote to local. The Expunge Both line enforces that hard deletes locally or propagated to the remote. The last line, SyncState * specifies that the sync file is placed in the root of the local mail directory.

Once all of your channels are laid out, you can assign them to a group:

Group gmail
Channel gmail-inbox
Channel gmail-drafts
Channel gmail-sent
Channel gmail-all
Channel gmail-trash

Groups essentially provide namespacing for channels. Beyond that, they also provide command line functionality I don't have much knowledge on this.


With Mbsync taken care of, all that's left for Mu4e before using it in Emacs is some initialization. If you installed the mail dependencies from my config, you'll have Mu accessible. The initialization process is two simple lines:

mu init --maildir=~/Mail
mu index

Adjust your --maildir flag as necessary. Once that completes, you can start viewing email in Emacs using M-x and typing mu4e.

This is the potentially overwhelming part. There are a lot of different directions you can explore from here, but the first question that I recommend answering is: how do you want to deal with HTML mail? I think I took the simplest approach, which is to deal with none of it in Emacs. My configuration essentially forces plain text email, and if there is clearly missing information then I open the message in a browser. The lines governing this behavior are:

(setq mu4e-view-html-plaintext-ratio-heuristic most-positive-fixnum)
(setq mu4e-view-prefer-html nil)

You can also specify a command for converting HTML to text:

(setq mu4e-html2text-command "html2text -utf8")

Personally, I don't use this presently, I find it often does more harm than good. I have found it easier to just view HTML-only mail in the browser instead of trying to coerce it. Your mileage may vary, though. I could go through my whole Mu4e configuration, but I think the better option is to start minimal, or with someone else's setup from a tutorial and start whittling it down. Chances are, there are a lot of options in there that you don't need. Luckily, Emacs is pretty good at being self-documenting, so good ol' C-h v along with the config option in question will go a long way here. If the short blurb doesn't quite satiate you, you can always scour the internet for more information.

If this has somehow convinced you to try Mu4e, I wish you luck. My opinion is that if you're curious enough to not mind looking a lot of things up, it can be fun. What keeps me going, personally, is that there's not really any hurry. If you don't like how something is configured, you can always change it, but you don't have to right away. For the most part, everything works as is, you just might not like it that much. Plus, as long as change things one at a time, they're never difficult to fix.