Table of Contents

Email done right

 ____
< Mu >
 ----
        \   ^__^
         \  (**)\_______
            (__)\       )\/\
             U  ||----w |
                ||     ||

For something I use so much I've never really liked any of the tools I use for email. When I started using email in the mid nineties, I was using Eudora; after that I used QuickMail in my first fulltime job, then I used VM and later Lotus Notes while working at IBM, followed by Outlook, Mail.app and then Gmail. The least horrible of all these is without doubt Gmail, but the lack of a unified inbox for several accounts makes it really complicated to stay on top of my email.

Every now and then I've been trying out different non-GUI approaches to email, but none of them really sticked. Over the last month or so I've been using a combination of OfflineIMAP and msmtp for the backend stuff, and then Mutt for the frontend stuff while waiting for Thomas to provide an early alpha of his zen tool for email. With a minimal amount of tweaking this has let me write my email in Emacs, which means a lot. Just having autofill within reach saves me from looking like a complete idiot, all the keybindings are where they should be, and still … Mutt.

A few days ago, in the middle of a rant against OfflineIMAP, someone mentioned an Emacs based email client called mu4e - Mu for Emacs. I've been using it for an entire day, so I'm just about ready to conclude that the search for a nice email client has ended.

Mu4e is an Emacs frontend to Mu, which is a database for Maildir. As you probably know, Maildir is how most mail servers and tools like OfflineIMAP store email. In order to use Mu (after installing it, of course), you just tell it to go index all your email

mu index --maildir=~/Maildir

and then you're ready to start searching your email:

mu find poker
mu find 'subject:poker' from:buddy

To make this work, I'm using two dedicated programs:

  • OfflineIMAP, which downloads all email from three different different accounts into directories within the Maildir directory in my home folder.
  • Mu, which indexes all these messages and provides a really nice search syntax for structured searches.

I really like the way these programs don't need to know about each other to work; they simply put files into my Maildir directory, index them and search them. I can use tools like find, grep, cat and wc to learn what's in the mail, or I can use higher-level tools like Mu.

Mu4e

Although Mu is really nice, Mu for Emacs (mu4e) is the real gem here. It's an Emacs app for email, leaning on a lot of tools to provide a kick ass email experience:

  • OfflineIMAP gets the mail for you and puts the messages where they belong
  • Mu indexes the Maildir and provides a search interface
  • Sending mail is done by Emacs itself. I use msmtp to send mail via the Gmail servers; this way I don't need to upload messages to the server twice (once over smtp and once over IMAP)
  • Editing is done by Emacs
  • Configuration and extension is done in Emacs lisp

Mu4e is organized around searches, and starts by presenting you with a menu which looks like this on my computer:

Basics
      * [j]ump to some maildir
      * enter a [s]earch query
      * [C]ompose a new message

Bookmarks
      * [bm] Unread on the mailing list
      * [bz] Today's news
      * [bu] Unread messages
      * [bt] Today's messages
      * [bw] Last 7 days
      * [bp] Messages with images

Misc
      * [U]pdate email & database
      * [A]bout mu4e
      * [H]elp
      * [q]uit

Folder shortcuts

I've set up some folder shortcuts, so pressing j from the menu lets me jump directly to my Gmail account (z) by pressing jz, or my Gitorious inbox (g) using jg:

(setq mu4e-maildir-shortcuts
      '(("/Gitorious/inbox" . ?g)
        ("/Gmail/inbox" . ?z)))

Bookmarks

The bookmarks section lists bookmarks, which are saved searches. Mu4e ships with a few useful defaults ("Today's messages", "Unread messages"), but it's really easy to set up new ones. The "Unread on the mailing list" bookmark, for example, is defined here:

(add-to-list 'mu4e-bookmarks
             '("maildir:/Gmail/gitorious-ml flag:unread" "Unread on the mailing list" ?m))

Since it's just a mu search as a string, it's really easy to experiment with, simply use the mu program:

mu find maildir:/Gmail/gitorious-ml flag:unread

It doesn't stop here, however. Once you're looking at a list of messages, for example the mailing list bookmark above, you can narrow the list of messages further using /. Looking at the list of messages in the mailing list, I enter

/from:thomas<RET>

and the list is filtered to show only unread messages to this mailing list sent by someone named thomas. Widen the search again with a \. Oh, and did I mention that mu is really fast? These searches complete instantly. And contrary to many Emacs apps, especially for email, mu4e doesn't block Emacs - except when sending messages.

Should I start using it?

If you use email, the answer is yes. If you don't use Emacs yet, you need to set aside some time for this, but it's going to be worth it.

Personally I appreciate the learning involved in setting up my Emacs environment myself, but if you're the copy/paste, I've entered my complete setup below.

Mu4e has extensions points where I needed them, and since mu4e does not assume any special kind of structure in my ~/Maildir, it's not aware of my multi-account setup in any way.

My configuration

This is my mu4e configuration, included because the Github org-mode rendering leaves a little to be desired.

(add-to-list 'load-path (concat dotfiles-dir "contrib/mu4e"))
(require 'mu4e)
(setq mu4e-get-mail-command "offlineimap")
(setq mu4e-drafts-folder "/Gitorious/drafts")
(setq mu4e-user-mail-address-list (list "marius@shortcut.no" "marius@gitorious.org" "zmalltalker@zmalltalker.com" "marius.mathiesen@gmail.com" "marius@gitorious.com"))
;;  (setq user-mail-address-list "marius@gitorious.com")
(setq message-kill-buffer-on-exit t)
;; Use fancy chars
(setq mu4e-use-fancy-chars t)
;; Shortcuts
(setq mu4e-maildir-shortcuts
      '(("/Gitorious/inbox" . ?g)
        ("/Gmail/inbox" . ?z)
        ("/Shortcut/inbox" . ?s)
        ("/Gmail/gitorious-ml" . ?m)
        ))

;; Smart refile locations
(setq mu4e-refile-folder
      (lambda (msg)
        (cond
         ;; messages sent directly to me go to /archive
         ;; also `mu4e-user-mail-address-regexp' can be used
         ((mu4e-message-contact-field-matches msg :to "marius@gitorious")
          "/Gitorious/archive")
         ((mu4e-message-contact-field-matches msg :to "marius.mathiesen@gmail.com")
          "/Gmail/archive")
         ((mu4e-message-contact-field-matches msg :to "zmalltalker@zmalltalker.com")
          "/Gmail/archive")
         ((mu4e-message-contact-field-matches msg :to "marius@shortcut.no")
          "/Shortcut/archive")
         ;; everything else goes to /archive
         ;; important to have a catch-all at the end!
         (t  "/Gmail/archive"))))

;; don't save message to Sent Messages, Gmail/IMAP takes care of this
(setq mu4e-sent-messages-behavior 'delete)


;; Try to display images in mu4e
(setq
 mu4e-view-show-images t
 mu4e-view-image-max-width 800)

;; sending mail
(setq message-send-mail-function 'message-send-mail-with-sendmail
      sendmail-program "/usr/bin/msmtp"
      user-full-name "Marius Mårnes Mathiesen")


(setq mu4e-confirm-quit nil
      mu4e-headers-date-format "%d/%b/%Y %H:%M" ; date format
      mu4e-html2text-command "html2text -utf8 -width 72"
      )

;; Borrowed from http://ionrock.org/emacs-email-and-mu.html
;; Choose account label to feed msmtp -a option based on From header
;; in Message buffer; This function must be added to
;; message-send-mail-hook for on-the-fly change of From address before
;; sending message since message-send-mail-hook is processed right
;; before sending message.
(defun choose-msmtp-account ()
  (if (message-mail-p)
      (save-excursion
        (let*
            ((from (save-restriction
                     (message-narrow-to-headers)
                     (message-fetch-field "from")))
             (account
              (cond
               ((string-match "marius.mathiesen@gmail.com" from) "gmail")
               ((string-match "zmalltalker@zmalltalker.com" from) "gmail")
               ((string-match "marius@shortcut.no" from) "shortcut")
               ((string-match "marius@gitorious.com" from) "gitorious")
               ((string-match "marius@gitorious.org" from) "gitorious"))))
          (setq message-sendmail-extra-arguments (list '"-a" account))))))
(setq message-sendmail-envelope-from 'header)
(add-hook 'message-send-mail-hook 'choose-msmtp-account)
(add-to-list 'mu4e-bookmarks
             '("maildir:/Gitorious/inbox OR maildir:/Shortcut/inbox OR maildir:/Gmail/inbox flag:unread" "Today's news" ?z))
(add-to-list 'mu4e-bookmarks
             '("maildir:/Gmail/gitorious-ml flag:unread" "Unread on the mailing list" ?m))

Some further customizations below.

When replying to an email I want to use the address I received this message to as the sender of the reply. This is fairly trivial:

(add-hook 'mu4e-compose-pre-hook
          (defun my-set-from-address ()
            "Set the From address based on the To address of the original."
            (let ((msg mu4e-compose-parent-message)) ;; msg is shorter...
              (if msg
                  (setq user-mail-address
                        (cond
                         ((mu4e-message-contact-field-matches msg :to "@gitorious")
                          "marius@gitorious.com")
                         ((mu4e-message-contact-field-matches msg :to "marius@shortcut.no")
                          "marius@shortcut.no")
                         ((mu4e-message-contact-field-matches msg :to "marius.mathiesen@gmail.com")
                          "zmalltalker@zmalltalker.com")
                         ((mu4e-message-contact-field-matches msg :to "zmalltalker@zmalltalker.com")
                          "zmalltalker@zmalltalker.com")
                         (t "marius.mathiesen@gmail.com")))))))

Wouldn't it be awesome to be able to send files from dired using your mail client?

I'll need a special version of the gnus-dired-mail-buffers function so it understands mu4e buffers as well:

(require 'gnus-dired)
;; make the `gnus-dired-mail-buffers' function also work on
;; message-mode derived modes, such as mu4e-compose-mode
(defun gnus-dired-mail-buffers ()
  "Return a list of active message buffers."
  (let (buffers)
    (save-current-buffer
      (dolist (buffer (buffer-list t))
        (set-buffer buffer)
        (when (and (derived-mode-p 'message-mode)
                   (null message-sent-message-via))
          (push (buffer-name buffer) buffers))))
    (nreverse buffers)))

(setq gnus-dired-mail-mode 'mu4e-user-agent)
(add-hook 'dired-mode-hook 'turn-on-gnus-dired-mode)

With this, I can attach a file as an attachment to a new email message by entering C-c RET C-a, and I'm good to go.

Although U from the mu4e menu is simple, it's even better (maybe) to have mu4e fetch mail (and update the mu index) automatically. Simply set mu4e-update-interval to the number of seconds between each check. Let's go with every 10 minutes.

(setq mu4e-update-interval 600)
comments powered by Disqus