Using mu4e
A thread on
Reddit
nudged me to try the Emacs mail readers again. I have tried a bunch of
them (Wanderlust, Gnus, mew) but found them either too idiosyncratic or
painful to use, and for the most part just read my e-mail by logging
into Google directly.
However, I just installed mu / mu4e and it works really well — well
enough that I prefer reading e-mail in Emacs now, as opposed to just
logging into Google mail and reading it there. I ended up archiving
about 10 years of mail (approximately 10k of messages) off Gmail and mu
can re-index it in seconds.
Installing offlineimap
I used offlineimap to sync everything with Gmail. Steal this
configuration
and put it in ~/.offlineimaprc (or whatever — but don’t name it
.offlineimap, because that will cause errors). For my laptop I added:
maxage = 5
autorefresh = 15
… to keep only 5 days of mail and to automatically update it every 15
minutes.
Updated 2016-12-21: Well, scratch that. Unless you want to allow
unsafe applications to access your data, you’re going to have to use Oauth.
The instructions are in the comments of this configuration file, but for posterity:
- Go to the Google Developer Console
- Create a new project
- On left bar, select Credentials
- Go to OAuth consent screen and fill in the minimum necessary to submit the form
- Go to Credentials tab and select “Oauth Client ID”
- Choose “other” application type and enter an appropriate name
So that gives you the client ID and the client secret. Wait, you’re not done. You need a refresh token.
- Clone out https://github.com/google/gmail-oauth2-tools.
- Run
python/oauth2.py --generate_oauth2_token --client_id=yourclient --client_secret=yoursecret
- This will have you go to your browser to get a verification token that you then enter. That will give you a refresh token.
In your .offlineimaprc
, you’ll have something like:
[Repository Personal_Remote]
...
oauth2_client_id = longstringofdigitsandnumbers.apps.googleusercontent.com
oauth2_client_secret = longstringofdigitsandnumbers
oauth2_request_url = https://accounts.google.com/o/oauth2/token
oauth2_refresh_token = 1/longstringofdigitsandnumbers
Installing mu4e
The version of mu4e available in Ubuntu 12.04LTS is really old and
throws spurious errors, so you’ll need to build your own. Get mu from
github and then rebuild. I needed a few
extra libraries in my installation.
sudo apt-get install libglib2.0-dev libgmime-2.6-dev libxapian-dev
sudo apt-get install texinfo
autoreconf -i
Installing into Emacs
Change into mu4e, do a make / sudo make install. I used pretty much just
the longer configuration in the
documentation,
although I put everything into a deferred load (code below), and there
are the following caveats:
- Viewing a message in a browser is handy, so I took the code from the
Emacs Wiki.
- I needed to override mu4e-html2text-command because about 10% of
HTML messages weren’t rendering.
- The
documentation
says to set mu4e-view-show-images to show images, but it’s actually
mu4e-show-images.
(defun email ()
(interactive)
(when (not (featurep 'mu4e))
(add-to-list 'load-path "/usr/local/share/emacs/site-lisp/mu4e/")
(require 'mu4e)
(require 'org-mu4e)
;; defaults
(setq mu4e-maildir "~/Maildir")
(setq mu4e-drafts-folder "/gmail/[Gmail].Drafts")
(setq mu4e-sent-folder "/gmail/[Gmail].Sent Mail")
(setq mu4e-trash-folder "/gmail/[Gmail].Trash")
;; don't save message to Sent Messages, Gmail/IMAP takes care of this
(setq mu4e-sent-messages-behavior 'delete)
;; setup some handy shortcuts
;; you can quickly switch to your Inbox -- press ``ji''
;; then, when you want archive some messages, move them to
;; the 'All Mail' folder by pressing ``ma''.
(setq mu4e-maildir-shortcuts
'( ("/gmail/INBOX" . ?i)
("/gmail/[Gmail].IMPORTANT" . ?!)
;; ("/gmail/[Gmail].Sent Mail" . ?s)
;; ("/gmail/[Gmail].Trash" . ?t)
("/gmail/[Gmail].All Mail" . ?a)))
;; allow for updating mail using 'U' in the main view:
;; I have this running in the background anyway
;; (setq mu4e-get-mail-command "offlineimap")
;; something about ourselves
(setq
user-mail-address "user@domain"
user-full-name "username"
message-signature nil)
;; sending mail -- replace USERNAME with your gmail username
;; also, make sure the gnutls command line utils are installed
;; package 'gnutls-bin' in Debian/Ubuntu
(require 'smtpmail)
(if (string< emacs-version "24")
(setq message-send-mail-function 'smtpmail-send-it
starttls-use-gnutls t
smtpmail-starttls-credentials '(("smtp.gmail.com" 587 nil nil))
smtpmail-auth-credentials
'(("smtp.gmail.com" 587 "user@domain" nil))
smtpmail-default-smtp-server "smtp.gmail.com"
smtpmail-smtp-server "smtp.gmail.com"
smtpmail-smtp-service 587)
;; alternatively, for emacs-24 you can use:
(setq message-send-mail-function 'smtpmail-send-it
smtpmail-stream-type 'starttls
smtpmail-default-smtp-server "smtp.gmail.com"
smtpmail-smtp-server "smtp.gmail.com"
smtpmail-smtp-service 587)
)
;; don't keep message buffers around
(setq message-kill-buffer-on-exit t)
;; show images
(setq mu4e-show-images t)
;; use imagemagick, if available
(when (fboundp 'imagemagick-register-types)
(imagemagick-register-types))
;;; message view action
(defun mu4e-msgv-action-view-in-browser (msg)
"View the body of the message in a web browser."
(interactive)
(let ((html (mu4e-msg-field (mu4e-message-at-point t) :body-html))
(tmpfile (format "%s/%d.html" temporary-file-directory (random))))
(unless html (error "No html part for this message"))
(with-temp-file tmpfile
(insert
""
""
html))
(browse-url (concat "file://" tmpfile))))
(add-to-list 'mu4e-view-actions
'("View in browser" . mu4e-msgv-action-view-in-browser) t)
;; convert org mode to HTML automatically
(setq org-mu4e-convert-to-html t)
;; need this to convert some e-mails properly
(setq mu4e-html2text-command "html2text -utf8 -width 72")
)
(mu4e)
)
(defalias 'org-mail 'org-mu4e-compose-org-mode)
Handy Keys in mu4e
- F — to forward
- C — to compose
- O — opens an attachment in detail view
- aV — views mail in HTML mode
- ji, j!, ja — jump to mailbox. (See mu4e-maildir-shortcuts above).
- mi, m!, ma — move message to mailbox
- V — turn on/off duplicates (handy if you’re looking at All Mail)
- W — turn on/off related messages
- / — narrow the current view via search
- ?, d, m — mark for unread / delete / move
- x — execute deferred marks
Org Mode
One of the neat things about mu4e is the fact that you can send messages
built in org-mode, although the current implementation is a little
flaky. Unless you do things the right way you’ll often get “Error 70,”
and a plain unadorned e-mail will be sent out. Unfortunately, my friends
and associates are not yet at that advanced stage of development that
they’ll read org-mode format directly.
To use org-mode via compose, you:
- Compose a message
- Run M-x org-mu4e-compose-org-mode (hah, note my alias above)
- And, finally, and most important: you must send the e-mail via \^C
\^S only when the cursor is in the header. If you try to send it
while the cursor is in the body of the e-mail, it won’t work.
Yeah, not ideal. But hopefully fixable and it isn’t too hard in
practice; only a small percentage of the e-mails I send out are rich
text.
You may want to change the default formatting options; I found that
having a Table of Contents in my e-mails was a bit too pretentious, and
the formatting for code blocks left too much white space. Apply the
following diff:
diff --git a/mu4e/org-mu4e.el b/mu4e/org-mu4e.el
index 44d5ea1..6697486 100644
--- a/mu4e/org-mu4e.el
+++ b/mu4e/org-mu4e.el
@@ -170,7 +170,9 @@ and images in a multipart/related part."
;; because we probably don't want to export a huge style file
(org-export-htmlize-output-type 'inline-css)
;; makes the replies with ">"s look nicer
- (org-export-preserve-breaks t)
+ (org-export-preserve-breaks nil)
+ ;; turn off table of contents
+ (org-export-with-toc nil)
;; dvipng for inline latex because MathJax doesn't work in mail
(org-export-with-LaTeX-fragments 'dvipng)
;; to hold attachments for inline html images
Org Mode Example
And, okay, to be honest it’s not entirely useful in the day to day, but
it is pretty neat that if you need to you can send an e-mail via:
Remember we were talking about functions of the form $f(x) = x^2$ the other day? I wanted to show you what it looked like.
#+name: python-data
#+BEGIN_SRC python :exports both :results replace
return [ (ix, ix*ix) for ix in xrange(0,11) ]
#+END_SRC
It's hard to visualize this, though, so it helps to look at the graph.
#+BEGIN_SRC R :var dat=python-data :exports both :results output graphics :file graph.png
library(ggplot2)
ggplot(data=dat, aes(x=V1, y=V2))+geom_point()
#+END_SRC
… to get
Discussion
Comments are moderated whenever I remember that I have a blog.