When I use my computer, the most fundamental thing I do is edit text. This means that either my text editor should be extended to do everything, or my text editor should be easily called from other programs. Emacs takes the first approach, and vim takes the second. Having arbitrarily chosen emacs a few years ago, I’ve never looked back.
Recently, I set emacs up to read email, using the gnus mail user agent. The documentation is pretty good, but because there are so many customisation options, it feels like a build-your-own-mail-reader kit. I wanted to be able to read mail offline as the connection here is terrible. I also wanted as few moving parts as possible (some other people use dovecot to run a local IMAP server, sync mail with offlineimap and never touch the gnus agent). Here’s my setup:
; The first is for the default MUA, the second for Message/Gnus.
(setq send-mail-function 'smtpmail-send-it
message-send-mail-function 'smtpmail-send-it)
; SMTP authentication.
(setq smtpmail-smtp-server "smtp.gmail.com"
smtpmail-smtp-service 587
smtpmail-auth-credentials '(("smtp.gmail.com" 587
USERNAME nil))
smtpmail-starttls-credentials '(("smtp.gmail.com" 587 nil nil)))
; Mail reading.
(setq read-mail-command 'gnus
gnus-agent-go-online t
gnus-inhibit-startup-message t
gnus-large-newsgroup nil
gnus-select-method '(nnimap "mail"
(nnimap-address "imap.gmail.com")
(nnimap-authinfo-file "~/.netrc")
(nnimap-stream ssl))
gnus-summary-line-format "%U%R%z%B%(%[%4L: %-23,23f%]%) %s\n")
This is the first part, setting up authentication and some minor customisations. Now things get intersting: when I unplug the gnus agent (go into offline mode), I want to queue outgoing mail, regardless of the source (gnus maintains its own queue when it’s offline). When I plug back in, I want to stop queuing mail.
; My net is spotty, so batch sending when gnus is offline.
(add-hook 'gnus-agent-plugged-hook (lambda () (setq smtpmail-queue-mail nil)))
(add-hook 'gnus-agent-unplugged-hook (lambda () (setq smtpmail-queue-mail t)))
(setq smtpmail-queue-mail t)
When I empty gnus’ queue, I need to flush any other outgoing mail. I do this by advising the gnus-group-send-queue function. Advice is a really cool elisp feature that lets you hook into arbitrary functions, mess with incoming arguments, change return values or just do something before or after a function. Here I flush the smtpmail queue after the gnus queue.
; Gnus keeps its own queue of mails to send, but if we send mail via
; other methods, we might queue it via stmpmail-queue-mail t. So we
; have two queues. Advise gnus-group-send-queue that it needs to flush
; the smtpmail queue when it's done.
(defadvice gnus-group-send-queue (after smtp-flush-queue activate)
"Empty the smtpmail queue after emptying the gnus send queue."
(smtpmail-send-queued-mail))
When gnus starts, it can start online or offline. The smtpmail queue needs to match the gnus queue, so I define a couple more pieces of advice:
(defadvice gnus (after gnus-queue-off activate)
"Turn off and flush the smtpmail queue when starting a plugged gnus."
(setq smtpmail-queue-mail nil)
(smtpmail-send-queued-mail))
(defadvice gnus-unplugged (after gnus-queue-on activate)
"Turn on the smtpmail queue when starting an unplugged gnus."
(setq smtpmail-queue-mail t))
Finally, I define a sync function. It syncs any flags that changed when offline and downloads any new mail. I also bind it to the key sequence “v s” as anything starting with “v” is reserved for the user.
(defun jdk-mailsync ()
"Sync tags, empty the queue and download all mail."
(interactive)
(gnus-agent-while-plugged
(gnus-agent-synchronize-flags)
(gnus-group-get-new-news)
(gnus-agent-fetch-session)))
(add-hook 'gnus-group-mode-hook
(lambda ()
(define-key gnus-group-mode-map (kbd "v s") 'jdk-mailsync)))
As I do other interesting things with emacs, I’ll post them online, too.