Tuesday, 2 June 2020
Recently, I’ve been seeing a few more articles on how to set up mu4e, an Emacs client to Emacs. I tried using it a few years back, and dispute the initial effort, I didn’t like it too much.
A while later, I used Gnus for about two years. It had its issues, but I found it more comfortable to work with than most other mail software (Thunderbird and Claws Mail1).
Earlier this year I started experimenting with Rmail. It is seen by some to be the standard Emacs mail reader, in the sense in which ed is the standard Unix text editor.
Nevertheless, I was pleasingly surprised, and have been using it since. In this text I want to give an outline of how it works, how to set it up in case you’re curious, and what it’s limitations are.
Those interested in managing and reading Email in Emacs, choose one of the above mentioned:
There are others, such as Wanderlust and Mew, but since I have never really used them I won’t risk making any wrong statements.
Rmail is a lot simpler, basically it’s a (special) major mode for mbox files, the old Unix
mailbox format. Whenever you have a mbox file, you can run
rmail-mode
, and you’ll be able to read the mailbox. Usually
you’d invoke rmail
though, which opens your “mail inbox”,
but more on that below.
I won’t be going into the keybindings, but there aren’t too many and the mnemonics are easy to comprehend (as opposed to other MUAs).
I have no idea how Rmail was intended to be used. Probably whatever RMS is doing. Therefore, I will just be going into whatever I configured for my workflow (two addresses, offline access, “inbox zero”, notifications in the mode line), hoping that it’s general enough to be useful for anyone who would like to try this out.
Two programmes I use are central to the entire workflow I’m going to describe. Neither is necessary, but I prefer it. These are the well known msmtp and less famous mpop, both by Martin Lambers (marlam). The former is used for sending mail, and the latter for receiving it.
The configuration format is quite straightforward, and they share a
lot of syntax. For example, my .mpoprc
looks something like
this:
defaults
tls on
tls_starttls off
tls_trust_file /etc/ssl/certs/ca-certificates.crt
account personal
host pop.homemail.net
user funny-me@homemail.net
delivery mbox ~/mail/in/personal.mbox
keep on
passwordeval authinfo login=homemail
account job
host pop.jobnet.org
user serious-me@jobnet.net
delivery mbox ~/mail/in/job.mbox
keep on
passwordeval authinfo login=jobnet
where the tls_trust_file
is necessary because of Debian,
and the passwordeval
line calls authinfo, a little script I wrote to
extract passwords from a authinfo.gpg
file. You might just
as well use gpg2
directly.
Either way, the configuration isn’t too important, and the manual
page is quite readable. The important thing with mpop is that it runs
regularly outside of Emacs (either using cron or systemd
timers), and saves incoming mail in a mbox file, in a specially
designating directory (in the case above, ~/mail/in
).
My configuration is (as of writing) not public, so I’ll be quoting and commenting excerpts that are relevant.
When calling rmail
(not rmail-mode
), we
want out mailbox to open. By default, Rmail would use the local
mail spool (mostly cron messages on my system), which is not so
interesting. So my configuration starts like follows3:
(setc rmail-primary-inbox-list"~/mail/in/personal.mbox"
'("~/mail/in/job.mbox")
"~/mail/inbox.mbox") rmail-file-name
This says that Rmail should check the two “inboxes” from above,
i.e. where my mail arrives, and if it finds something move it to
~/mail/inbox.mbox
.
When this is done, Rmail immediately presents me with the first message. This is one of the reasons I like it, no loading screens, no splash screens or intermediary interfaces. Messages first, lists or options come afterwards. But one thing bugs me, there are so many headers, many of which I don’t care about. Do I have to see the raw Autocrypt header? I don’t think so, so I filter the headers by default to a minimum:
"^\\(?:Cc\\|Date\\|From\\|Subject\\|To\\):") (setc rmail-displayed-headers
A central part of my reading-workflow is re-archiving. When a message
lands in ~/mail/inbox.mbox
, I keep it there for only as
long as it’s relevant, then I move it to another mailbox in
~/mail/old
(using rmail-outout
, bound to “o”).
Everything else is just directly deleted. This I configure like so:
"~/mail/old/"
(setc rmail-default-file t) rmail-delete-after-output
Finally, a few changes regarding MIME/attachments:
~/dl
, my download
directory.Both is easily changed:
"" "~/dl"))
(setc rmail-mime-attachment-dirs-alist '((nil) rmail-mime-prefer-html
The first line could be more fancy, but I just want everything (hence
the empty regular expression) to land in ~/dl/
. Consider
reading the docstring if you prefer to automatically place certain file
types in other places4.
message
My mail-sending setup is actually more complicated than what I need to read mail. The default
Part of the reason is that I now have to interface with an external tool (msmtp). This might be familiar to anyone who has configured other mail clients in Emacs, since msmtp is often recommended, especially if one has multiple addresses. Therefore I won’t have too much to add on the basics:
"msmtp"
(setc sendmail-program
message-send-mail-function 'message-send-mail-with-sendmail"--read-envelope-from")
message-sendmail-extra-arguments '(
message-sendmail-envelope-from 'headert) message-sendmail-f-is-evil
Basically this is what you need for msmtp to work as you would want it. It’s necessary and boring, and the understanding it is left as an exercise to the reader.
Here a few other changes I have applied:
Don’t generate autosave files:
nil) (setc message-auto-save-directory
Don’t wait until messages are sent:
nil) (setc message-interactive
Don’t keep sent email buffers around:
t) (setc message-kill-buffer-on-exit
Prevent messages to be sent to myself:
(setc mail-dont-reply-to-names"\\`\\(?:\\(?:funny-me@homemail\\|serious-me@jobnet\\)\\.net\\)\\'")
This is just a regular expression, matching exactly both my very real
email addresses. It might be worthwhile considering making it more
general, so that user+ignore@host.domain
messages are also
recognised.
The final change is to configure a “sent mail” directory, by using
the “FCC” header. This is interpreted by email to “send” a carbon copy
to a file (in ~/mail/out
), and will be deleted afterwards.
The easiest way to do this that I know of is like this:
defun local/add-fcc ()
("Generate a FCC header for the current month."
format "FCC: ~/mail/out/%s.mbox"
("%Y-%h"))))
(downcase (format-time-string
(setc message-default-headers #'local/add-fcc)
The great thing is that this needs no background process, such as with Gnus. Even a simple “C-x m” will save a copy.
I don’t have complicated searching needs. Since I manually archive mail, I can usually remember where to look for a particular message. But whenever that’s not the case, I can fall back onto search. For this I use mairix, mainly because Emacs has support for it built in by default.
Mairix needs an external configuration, by default it’s located in
~/.mairixrc
. In our example, it would look like this:
base=~/mail
mbox=inbox.mbox:out/...:old/...
mformat=mbox
database=~/mail/.mairix.db
This will look for messages in ~/mail/inbox.mbox
, all
files in ~/mail/out/
and ~/mail/old
.
On the Emacs side, not much more is required:
"~/mail/"
(setc mairix-file-path "search.mbox") mairix-search-file
Now calling mairix-search
would generate and open a mbox
archive of all messages matching the query.
I used to use BBDB, but because like with most things I didn’t dig in too deep, I realised that the “mailrc” format totally suffices my needs. This is basically a file of this form:
alias bge bug-gnu-emacs@gnu.org
alias boss "Mr Bossard <boss@jobnet.org>"
alias department bob hans li richard
alias bob "Bob Colleque <bob@jobnet.org>"
...
(an alias for the Emacs mailing list, an alias for my boss, and an alias for the group consisting of “bob”, “hans”, “li” and “richard” — all of which are further aliases).
I could keep the mailrc
file in my home directory, but I
prefer to have all mail related stuff in one place, so I change the
location where Emacs looks for it:
"~/etc/mail/aliases") (setc mail-personal-alias-file
The only thing left is to activate the mailrc subsystem:
(add-hook 'message-setup-hook #'mail-abbrevs-setup)
Now I can write boss
in the “To” field of a message, and
it will be expanded before sending it. This also works when resending
a message!
With a cronjob running in the background, I don’t want to be paranoid
and keep checking my inbox every 5 minutes. Instead I use
display-time-mode
to check for Mail, in
~/mail/in
. The basic setup doesn’t need anything more
than
"~/mail/in"
(setc display-time-mail-directory t) ;activate the minor mode display-time-mode
Currently, there’s the main annoyance with this workflow is when working on multiple devices. mpop can be configured to keep or delete mail after it has fetched it from the server, so you either end up with a fragmented inbox or re-read ever message you receive.
Further, synchronising between devices might be tricky. I use syncthing (which is why I wanted to keep all mail-related files in one subdirectory). This is one of the downsides of using mbox compared to maildir, and there’s not much one can do besides being careful.
Finally, Rmail is a bit sloppy by modern standards. I think the source code could be cleaner, it’s window management could be more respectful and MIME handling should be improved (I’d like to be able to apply patches directly from a message, but all I can change is where to store it; I’d like to open PDFs in a buffer, but all I can do is download them, etc.). When I get around to it, my hope is that I could fix a few of these complaints, but this isn’t a promise!
Either way, I hope to have shown that Rmail is a worthwhile MUA and depending on your Email needs, it might be enough. The documentation on the internet might be sparse, but ultimately you don’t have to change much for it to work well. I’ve used a few older file formats, tools and protocols, but my hope is that — just like with Emacs — if it worked 30 years ago — it’s going to work in another 30.
Though if I had to use a graphical client, I’d probably choose Claws Mail.↩︎
But it doesn’t have to for the bare minimum to work, eg. an online-only IMAP/SMTP setup.↩︎
setc
is a macro I use to
set user options. Its syntax is the same as setq
, just that
it invokes customize-set-variable
for the sake of forwards
compatibility. Using setq
shouldn’t change
anything, but in case you want to try setc
out, here’s my
definition:
defmacro setc (&rest args)
("Call `customize-set-variable' like `setq'."
let (body)
(
(while argsunless (cdr args)
(error "Not enough arguments"))
(push `(customize-set-variable
(pop args) ,(pop args)
',(" Set by `setc'")
body))cons 'progn (nreverse body)))) (
“Read the docstring” should be understood as a general recommendation for every variable I have and will introduce. Never just copy code into your configuration. This is a presentation, not a template.↩︎