Email management, when heavily abstracted, is simple. To start reading email offline on your own PC you need three programs:

  • Sync email to/from IMAP server (mbsync)

  • Manage email on your PC (mu and mu4e)

  • Send email (msmtp)

Once these three parts are working together then email can be downloaded, viewed, and replied to. Getting these programs working is no easy task, however.

My motivation in doing this was the end goal of easily org capture-ing my emails and adding them to my master todo.org file. I thought that by keeping all of my actionable tasks in one central place I could be more productive. Time will tell if that assumption is true.

mbsync

mbsync is a tool written in python to facilitate the downloading and uploading of mail from an IMAP server. There are other programs for doing this (namely, OfflineIMAP), but I chose mbsync because it was the easiest for me to setup, didn't have any weird certificate errors with gmail, and it was incredibly fast.

My mbsync setup uses two gmail accounts. However, for the sake of brevity, I have trimmed it down to just use one. The whole configuration is in my dotfiles repository.

To store passwords on the command line I use the pass password manager. To access a command line password manager mbsync uses the PassCmd setting to define a command that will return the password. I use pam gnupg program to unlock my gnupg key on login I do not have to keep entering my GPG password to provide my email password to mbsync.

I dislike the [Gmail]. prefix that all folders have when downloaded by default. To solve this problem I have a channel for each of the folders I download and I can give each of them an explicit file name. For example, "[Gmail].Sent Mail" is renamed to sent in the configuration snippet below.

Channel sync-gmail-sent
Master :gmail-remote:"[Gmail]/Sent Mail"
Slave :gmail-local:sent
Create Slave

On top of renaming some folders I also disabled some of the folders I don't use in Gmail's settings. You can do this by going to settingsLabels.

Gmail IMAP settings to simplify the IMAP folder structure

All in all my mbsync config file is simple. If you are looking for more information about how to set up mbsync you can find it on the Arch Wiki.

IMAPAccount gmail
Host imap.gmail.com
User alexday135@gmail.com
PassCmd "pass mail/gmail"
SSLType IMAPS
CertificateFile /etc/ssl/certs/ca-certificates.crt

IMAPStore gmail-remote
Account gmail

MaildirStore gmail-local
Subfolders Verbatim
Path ~/.local/share/mail/gmail/
Inbox ~/.local/share/mail/gmail/INBOX

Channel sync-gmail-default
Master :gmail-remote:
Slave :gmail-local:
Patterns "INBOX"

# Create missing folders everywhere
SyncState *

Channel sync-gmail-sent
Master :gmail-remote:"[Gmail]/Sent Mail"
Slave :gmail-local:sent
Create Slave

Channel sync-gmail-archive
Master :gmail-remote:"archive"
Slave :gmail-local:archive
Create Slave

Channel sync-gmail-drafts
Master :gmail-remote:"[Gmail]/Drafts"
Slave :gmail-local:drafts
Create Slave

Channel sync-gmail-trash
Master :gmail-remote:"[Gmail]/Trash"
Slave :gmail-local:trash
Create Slave

# Get all the channels together into a group.
Group gmail
Channel sync-gmail-default
Channel sync-gmail-sent
Channel sync-gmail-trash
Channel sync-gmail-drafts
Channel sync-gmail-archive

Once mbsync is set and the root mail folder has been created up you can run mbsync -a to sync all mailboxes or sync a specific account by running mbsync followed by the account name.

mu/mu4e

mu is a super fast mail indexer that comes with mu4e which is an email client for emacs. Both of them work together to allow a powerful, search based email interface that is keyboard-driven. When you first start it up you will see a relatively bare main menu.

mu4e main menu

From there you can jump to your inbox and start reading and replying to mail. There are videos that can explain mu4e a lot better than I can so if you need an intro I would recommend Mike Zamansky's mu4e video.

I use doom emacs and I haven't tested this config in vanilla emacs so I cannot guarentee that this will work but I can see no reason why it wouldnt. Basicelly this config sets up a mail account with specific folders for the mail. It also sets up msmtp as the mechanism to send a message.

;; ~/.doom.d/config.el
(require 'mu4e)

;; use mu4e for e-mail in emacs
(setq mail-user-agent 'mu4e-user-agent)
(setq mu4e-maildir "/home/alex/.local/share/mail")

;; default
(setq mu4e-contexts
    `( ,(make-mu4e-context
        :name "clemson"
        :enter-func (lambda ()
                        (mu4e-message "Entering Clemson context")
                        ;; Quicky jump to/move a mail to different folders
                        (setq mu4e-maildir-shortcuts  '( ("/clemson/INBOX"   . ?i)
                                                         ("/clemson/sent"    . ?s)
                                                         ("/clemson/trash"   . ?t)
                                                         ("/clemson/drafts"  . ?d)
                                                         ("/clemson/archive" . ?r))))
        :leave-func (lambda ()
                        (mu4e-message "Leaving Clemson context"))
        :match-func (lambda (msg)
                        (when msg
                            ;; Clemson has two valid emails for each student
                            (or (mu4e-message-contact-field-matches msg
                                    :to "adday@clemson.edu")
                                (mu4e-message-contact-field-matches msg
                                    :to "adday@g.clemson.edu"))))

        :vars '( ( user-mail-address      . "adday@clemson.edu"  )
                 ( user-full-name         . "Alex Day" )
                 ( mu4e-drafts-folder     . "/clemson/drafts")
                 ( mu4e-sent-folder       . "/clemson/sent")
                 ( mu4e-trash-folder      . "/clemson/trash")
                 ( mu4e-refile-folder     . "/clemson/archive" )
                 ( mu4e-compose-signature . (concat "Alex Day"))))))


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

;; allow for updating mail using 'U' in the main view:
(setq mu4e-get-mail-command "mbsync -a")

(setq message-send-mail-function 'message-send-mail-with-sendmail)
(setq sendmail-program "/usr/bin/msmtp")
;; tell msmtp to choose the SMTP server by the 'from' field in the outgoing email
(setq message-sendmail-extra-arguments '("--read-envelope-from"))
(setq message-sendmail-f-is-evil 't)

As well as just reading messages in emacs I also wanted some way of capturing messages with org-capture. To do this I set up a capture template that I could use while either in a message or hovering over a message on the mu4e search.

(require 'org-mu4e)
(setq org-capture-templates
        ("e" "Email Todo" entry (file+headline "~/doc/org/todo.org" "Inbox")
         "* TODO %?\nProcess mail from %:fromname on %:subject\nSCHEDULED:%t\nDEADLINE: %(org-insert-time-stamp (org-read-date nil t \"+2d\"))\n:PROPERTIES:\n:CREATED: %U\n:END:\n %a" :prepend t))

msmtp

I use msmtp to send emails from mu4e. It handles multiple email addresses by reading the 'from' field that mu4e sends along with the email. This program is the only one I have had absolutely no problems with.

# ~/.config/msmtp/config
defaults
auth on
tls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile ~/.config/msmtp/msmtp.log

account clemson
host smtp.gmail.com
port 587
from adday@clemson.edu
user adday@g.clemson.edu
passwordeval "pass mail/clemson"

account default : clemson

Show Unread Mail in the Statusbar

I use this script to display the number of unread emails in my status bar. I use slstatus with the status2d patch for dwm to allow colors in the status bar. It should be plug and play if you change the directory it looks for new mail in and have font-awesome installed for the mailbox icon.

#!/usr/bin/env sh

# Get the number of new mails in all INBOX folders
NEWMAILS=$(du -a ~/.local/share/mail/*/INBOX/new/* 2>/dev/null | wc -l)

# Print the number of new mails to the statusbar if there are any
if [ "$NEWMAILS" -gt 0 ]; then
    printf "^b%s^" $(xgetres slstatus.color2)
    printf "^c%s^" $(xgetres slstatus.background)
    printf " ﯬ %s" $NEWMAILS
else
    # If there are no new mails then print a block the same color
    # as the background. This is because slstatus will show the previous
    # color if it is not overwriten
    printf "^c%s^^b%s^placeholder^d^" $(xgetres slstatus.background) $(xgetres slstatus.background)
fi

Conclusions

I have been reading and writing emails this way for the past couple of weeks and I could be happier. Namely I haven't really researched how to attach files to email and when sending emails back and forth from two accounts that mu4e is tracking it can get a little confusing. Apart from that, however, I am satisfied with the workflow that these config files set up.