remember Mutt's mail in Org-mode and jump back to them

As already anticipated, I've been implementing my own Getting Things Done work flow. This post documents one of its main bricks: the integration between Mutt and Org-mode.

As a geek, my main incoming stream of TODO items and information in general is email. As emails hit my INBOX, I either deal with them immediately (reply, archive, delete) or I need to store them elsewhere, possibly adding extra information such as a deadline, a personal note, the associated next action, the context in which it is actionable, etc. This need of adding extra information is what defeats the usage of my mail client (Mutt) as a list manager (in the GTD sense), and that's where I plugged Org-mode in. My main goals are:

  1. create Org-mode notes from Mutt, referencing the current email, and possibly inlining some of its metadata (e.g. subject, sender)

  2. quickly retrieve referenced emails from Org-mode notes; ideally that should happen in my usual email environment (i.e. Mutt), so that I can process the retrieved mail as usual (e.g. to inform the sender that I did something about it)

In fact, both would be straightforward to achieve if I were using some Emacs-based mail client such as Gnus, but I resist the Emacs operating system syndrome, and therefore I insist in using my beloved Mutt.

Let's see how the two parts of the interaction between Mutt and Org-mode work.

(1) Mutt → Org-mode (there ...)

The interaction from Mutt to Org-mode happens via org-protocol. Using it external applications can feed content to Org-mode note templates, which are then interactively edited (via emacsclient), and finally filed away.

The Mutt glue macro from my ~/.muttrc is as follow:

macro index \eR "|~/bin/remember-mail\n"

The remember-mail script is trivial: it parses the fed mail from STDIN (using a couple of legacy Perl modules) and then invokes org-protocol.

The relevant configuration from my ~/.emacs is reported below (see the Git repo for the most recent version of this); the comments explain the various parts:

;; standard org <-> remember stuff, RTFM
(require 'org-capture)

(setq org-default-notes-file "~/org/gtd.org")

(setq org-capture-templates
      (quote
       (("m"
         "Mail"
         entry
         (file+headline "~/org/gtd.org" "Incoming")
         "* TODO %^{Title}\n\n  Source: %u, %c\n\n  %i"
         :empty-lines 1)
        ;; ... more templates here ...
        )))
;; ensure that emacsclient will show just the note to be edited when invoked
;; from Mutt, and that it will shut down emacsclient once finished;
;; fallback to legacy behavior when not invoked via org-protocol.
(add-hook 'org-capture-mode-hook 'delete-other-windows)
(setq my-org-protocol-flag nil)
(defadvice org-capture-finalize (after delete-frame-at-end activate)
  "Delete frame at remember finalization"
  (progn (if my-org-protocol-flag (delete-frame))
         (setq my-org-protocol-flag nil)))
(defadvice org-capture-kill (after delete-frame-at-end activate)
  "Delete frame at remember abort"
  (progn (if my-org-protocol-flag (delete-frame))
         (setq my-org-protocol-flag nil)))
(defadvice org-protocol-capture (before set-org-protocol-flag activate)
  (setq my-org-protocol-flag t))

The result is that when you hit ESC-R in Mutt, emacsclient will be fired up in place presenting a note template that already contains relevant mail information (date, subject, from) and lets you add extra information before going away. Additionally, the email message-id will be hidden in the note as a mutt: hyperlink with anchor text "mail".

(2) Org-mode → Mutt (... and back again)

Going back means that clicking on a "mail" hyperlink within an Org-mode note should bring up a Mutt instance showing the original message, in its context (e.g. its own mailbox). Achieving that consists of 2 separate steps:

  1. looking up a specific message by Message-ID
  2. firing up Mutt on the looked up message

For the first part I use notmuch-mutt (see this other blog post of mine).

For the second part I use the mutt-open script which fires upon a Mutt instance on the maildir containing a specific message, and then "hits" the appropriate keys to open the message and shutdown the sidebar (if desired). It is a nicely reusable script, which I've being using elsewhere too.

The needed glue on the emacs side is just a function to invoke mutt-open in a brand new terminal, and its declaration as the handler for mutt: URLs.

(defun open-mail-in-mutt (message)
  "Open a mail message in Mutt, using an external terminal.

Message can be specified either by a path pointing inside a
Maildir, or by Message-ID."
  (interactive "MPath or Message-ID: ")
  (shell-command
   (format "gnome-terminal -e \"%s %s\""
       (substitute-in-file-name "$HOME/bin/mutt-open") message)))

;; add support for "mutt:ID" links
(org-add-link-type "mutt" 'open-mail-in-mutt)

Voilà!

Download

Scripts and configuration snippets discussed above are available from the org-mutt Git repository:


Update 22/01/2011: fix mutt-open to work with maildir-utils, version 7 or above
Update 17/02/2012: move scripts and conf to the org-mutt Git repo
Update 17/10/2013: point to notmuch-mutt blog post, getting rid of mu
Update 17/10/2013: update Emacs conf snippet for Org >= 0.8 (see Git repo)

thanks for your post [0]. Since I was reluctant to leave mutt (running in an M-x ansi-term anyway) for a pure elisp based solution (they all seemed overly complex to set up and unbearably slow with my HUGE imaps mailboxes) it helped me a great deal to get org integration working.

I left out the mu part entirely though and since I use sth. like (with-current-buffer "inbox" (term-send-raw-string (concat "/~i" message "\n\n")))) in my .emacs no shell script is needed.[1]

thanks again,

take care,

x

[0] http://upsilon.cc/~zack/blog/posts/2010/02/integrating_Mutt_with_Org-mode/ [1] this has the obvious disadvantage that I need to have the right mailbox open, but I am fine with that so far.

Comment by zack Sat 08 May 2010 09:19:57 AM CEST

Hello. Thank you so much for this post, I loved it! I am an old time mutt user, and I am starting to use org-mode, and this integration is awesome.

I just wanted to point out that it seems the letters for the mu command has changed in the 0.7 version. To get the path from a message id it's not mu find -f pMmessage-id, but now mu find -p l i:message-id

Comment by nwerneck [myopenid.com] Sat 02 Oct 2010 12:26:05 AM CEST

Many thanks for this, it really has improved my workflow! I currently run Mutt in Multi-term within Emacs and so just had to change the exec to drop the "-t" flag.

exec "emacsclient", "org-protocol:/remember:/m/mutt:$mid/mail/$note_body";

I really can't believe I never spent the time before configuring remember and templates. It really is quite a beautiful thing, and more so now with Mutt.

Thanks again.

Comment by 'Mash Thu 09 Dec 2010 01:28:51 PM CET

Hi

I ran into a problem with your mutt-open script when I tried to find a message whose message ID included "+" characters. I discovered that unfortunately Mutt interpreted those as regex characters.

To try and escape the msgid string I inserted the substitutions below:

 sleep 0.1
+msgid="${msgid//\+/\\\\\\\\+}"
+msgid="${msgid//\*/\\\\\\\\*}"
+msgid="${msgid//\?/\\\\\\\\?}"
 mutt_keys="$HIDE_SIDEBAR_CMD/~i$msgid\n\n"

And yes it really took all those backslashes to make it all the way into the mutt expression. There could easily be a better way to escape the string, I didn't even know bash could do string substitutions until a few minutes ago.

Comment by alienghic [livejournal.com] Tue 30 Aug 2011 12:22:38 AM CEST

So, I don’t use emacs much. The only reason I use it is orgmode actually.

Maybe I don’t understand something, but I can’t get the first part (mutt→orgmode) to work. Here’s what I do:

I open emacs (in gtk). I do M-x server-start.

I open mutt and press, Esc-R. Then it fires emacsclient so quickly I can’t really see what’s wrong, but I get this error in a buffer in the running emacs-gtk:

 Use M-x make-directory RET RET to create the directory and its parents
 Error: (error "No such directory found via CDPATH environment variable")

Comment by hugo Tue 17 Dec 2013 12:01:38 PM CET

Hi, I am using a slightly different approach when going from mutt to orgmode. I proceed through a bash script and then I call emacsclient. Mail processing and template capture is executed in elisp from within emacs, no need of org-protocol. Details here https://github.com/csantosb/config/tree/master/mutt under 'Annotate macro', what do you think ? c.

Comment by cayetano santos Thu 09 Jan 2014 04:11:03 PM CET