Gmail web application is generally very good with many features. However, I struggled with its usability - the need to interact with a mouse in many places, browser spell checker, fast notification of a new email, multiple account management and lack of emacs way of writing the text. In the process, I tried Geary, which was good but sometimes crashed, and Nylas1, which had instant email notifications but became buggy and needed to be actively developed.
With getting to know emacs better, I was pushed into its keyboard binding, a beautiful way of extending functionality that made writing efficient and distraction-free. I looked up how email works on emacs and came across
mu4e with which I now work with three accounts. I can write, search and read emails very fast and do no need an internet connection. That sometimes is convenient, for example, to look at the booking of a hostel or a flight.
To get my almost perfect setup, I struggled a lot. I had to fight a bug which corrupted the index database; I had to develop a way to have multiple accounts while not having them all in one pot; Because emails were stored locally, I had to create a cron job to update them and the account for network connection failures; Learn to store them under encrypted folder; Dealing with emacs failures due to mu4e plugin running in the background; There are many problems which had overwhelmed me since last In October when I started to take email clients in emacs seriously. Fortunately, it has worked flawlessly for the previous two months, and I have solved my frustration with usability.
Before downloading an email, we need a safe encrypted place to store it. That can be easily done with Ubuntu, where you need to execute the following commands:
sudo apt-get install ecryptfs-utils ecrypt-setup-private
and follow the instructions in a terminal to create an encrypted
Private folder in your home directory.
To sync emails, I use
mbsync, which is installed with:
sudo apt-get install isysnc
This command asks for a configuration file which for my Gmail account looks as follows:
IMAPStore master Host imap.gmail.com User email@example.com Pass XXXXXXX UseIMAPS yes RequireSSL yes CertificateFile /etc/ssl/certs/ca-certificates.crt MaildirStore slave Path ~/Private/Email/akels14/ Channel inbox Master ":master:INBOX" Slave ":slave:inbox" Create Both Expunge Both SyncState * Channel sent Master ":master:[Gmail]/Sent Mail" Slave ":slave:sent" Create Both Expunge Both SyncState *
for the fields written, go to the man page of
man mbsync). I name this file
mbsync-ak.conf and then synchronise all my email into
~/Private/Email/akels14 folder with a command (since password is written in plaintext, I also put that in my
mbsync -c ~/Private/.config/mbsync-ak.conf inbox sent
The first run takes about an hour for me, but then it is done. One warning must be given Since
mbsync does synchronise emails between master and slave, it can also delete emails at master if a file is deleted locally (slave). Fortunately, all emails deleted except the trash directory can be found in the
Archive folder. That saved my ass a couple of times when I tried to make sense of
To be useful, synchronisation needs to work all the time (but some may prefer to do it on demand), which I accomplish with a cronjob. To start writing commands in crontab, execute
crontab -e, and write the following lines in it:
PATH=/opt/someApp/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin * * * * * flock -n /tmp/mbsync-ak.lock timeout 300 mbsync -c /home/janiserdmanis/Private/.config//mbsync-ak.conf inbox sent
The path variable is a neat workaround for avoiding writing full paths of applications - flock, timeout,
mbsync. This script will synchronise email every minute, and if the network connection fails or if large files need to be downloaded, cron will wait (flock) for full synchronisation for 300 seconds (timeout) until the command is executed again. And that is about all needed to have synchronisation at every minute to be set up, and adding multiple accounts follows the same pattern.
To read synchronised emails, we will need emacs and
mu4e installed by:
sudo apt-get install mu emacs
Now to run mu4e, we create an
init.el file with the following contents:
(require 'mu4e') (setq mu4e-maildir (expand-file-name "~/Private/Email/akels14"))
and run that with a simple command:
emacs --load init-simple.el --eval "(mu4e)"
where command after
--eval, which can be put at the end of
init.el, is executed after initial
init.el is loaded. To see new emails, you have to index them, which is done with keybinding
U (next time would be instantaneous). That updates by default db under
The last step to having a functional email client is configuring SMTP, responsible for sending emails. That can be done in the
init.el by adding the following lines:
(require 'smtpmail) (setq user-full-name "Janis Erdmanis") (setq mu4e-user-mail-address-list '("firstname.lastname@example.org" "XXXX@gmail.com")) (setq user-mail-address "email@example.com") (setq mu4e-reply-to-address "firstname.lastname@example.org")
One address list so mu4e know your mail addresses, one for mu4e to know which address to use for composing and the last address to be given to the recipient, so he is pushed to reply to a given address, which is a fairly good spot for your protected spam-free email address.
Then when I move into the navigation and find an email I want to reply to, I press
R. With the configuration so far presented, I would be asked for my account details to send a mail with SMTP, which would be stored in plaintext in
~/.authinfo. The contents of the file are as follows:
machine smtp.gmail.com login graphitewriter port 587 password xxxxxxx machine smtp.gmail.com login akels14 port 587 password xxxxxxxx
where the corresponding line to your email address used for sending will is picked up automatically, one warning must be emphasised do not store passwords in plaintext. To avoid that move, I moved
.authinfo file to the Private folder and symlink that with a command.
ln -s ~/Private/.config/authinfo ~/.authinfo
Similarly, one can move
~/.mu to the Private folder for a complete security.
So far, this is the most basic and helpful starting point for using emacs for mail reading and sending. One can pick up this setup and modify depending on his needs and put some snippets in
init.el, which can be found in Google as I usually do. Multiple account management and desktop integration are the most obvious next steps, which I will continue to reveal.
To simply manage multiple accounts, one can start emacs with a different
init.el files by adding/modifying the following lines:
(setq mu4e-maildir (expand-file-name "~/Private/Email/akels14")) (setq mu4e-mu-home (expand-file-name "~/Private/.mu/akels14"))
where the last one is needed to have a separate index for each maildir, that should work flawlessly as long as you would be satisfied with changing windows for each account.
I wanted to use multiple accounts in a single window with a keyboard shortcut. Thus, I wrote/modified some scripts on the internet to have the behaviour I wanted. Firstly I put my accounts in variables (I still append this to
(setq mu4e-user-mail-address-list '("XXXXXX@gmail.com" "email@example.com" "firstname.lastname@example.org")) (setq user-full-name "Janis Erdmanis") (defvar my-mu4e-account-alist `(("graphitewriter" (user-mail-address "XXXXX@gmail.com") (mu4e-reply-to-address "XXXXX@gmail.com") (mu4e-maildir ,(expand-file-name "~/Private/Email/graphitewriter")) (mu4e-mu-home ,(expand-file-name "~/Private/.mu/graphitewriter")) ) ("je11011" (user-mail-address "email@example.com") (mu4e-reply-to-address "firstname.lastname@example.org") (mu4e-maildir ,(expand-file-name "~/Private/Email/je11011")) (mu4e-mu-home ,(expand-file-name "~/Private/.mu/je11011")) ) ("akels14" (user-mail-address "email@example.com") (mu4e-reply-to-address "firstname.lastname@example.org") (mu4e-maildir ,(expand-file-name "~/Private/Email/akels14")) (mu4e-mu-home ,(expand-file-name "~/Private/.mu/akels14")) )))
Then to change the account I simply have to execute a following function with an account name:
(defun mu4e-set-account (account-name) (interactive) (mu4e~proc-kill) (let ((vars (cdr (assoc account-name my-mu4e-account-alist)))) (if vars (mapc #'(lambda (var) (set (car var) (cadr var))) vars) (error "No email account '%s' was found" account-name))) ;; For having fancy mode-line (setq mode-line-end-spaces (list (propertize " " 'display `(space :align-to (- right ,(length account-name)))) account-name)) ;; Also need to remove lock file which sometimes messes up (shell-command (concat "rm -rf " mu4e-mu-home "/xapian/flintlock")) )
which can be executed at any point in emacs with
M-x + mu4e-set-account + Enter + akels14.
By adding competition of account names and a keyboard shortcut
; to change accounts, I was satisfied with my setup:
(setq my-accounts nil) (defun printlist (mylist) "Filling my-accounts with alist keys from mylist" (push (car (car mylist)) my-accounts) (if (not (eq (length mylist) 1)) (printlist (cdr mylist)))) (printlist my-mu4e-account-alist) (defun mu4e-set-account-interactive () "Changes the email account" (interactive) ;(mu4e-set-account (read-string "Enter the account name:")) (setq mu4e-account (ido-completing-read "Enter the account name: " my-accounts)) (mu4e-set-account mu4e-account) ) (add-hook 'mu4e-main-mode-hook (lambda () (local-set-key (kbd ";") #'mu4e-set-account-interactive)))
This code part needs some reviewing, which would make it shorter, as I use global variables and make functions where I do not need them. Nevertheless, it works flawlessly.
The last step was to make desktop integration - to see instantaneously that a new email had arrived. I wanted an icon and badge showing the number of unread emails in Ubuntu. My approach uses DBus for that purpose, with which I make a daemon process and its ability to change
*.desktop icons in the panel. Thus first, we need to make a
.desktop icon for the emacs command.
The following init script is needed to let the X-window system know that a separate application is being started:
APPNAME=mu4e emacs --name $APPNAME -q --load init-simple.el & sleep 1 && xprop -name $APPNAME -f WM_CLASS 8s -set WM_CLASS "$APPNAME, $APPNAME"
In this script, I store this in
~/bin/mu4e, for which I add execution permissions with
chmod +x \~/bin/mu4e. A .desktop file is being stored in
\~/.local/share/applications/mu4e.desktop which has a following contents:
#!/usr/bin/env xdg-open [Desktop Entry] Version=1.0 Type=Application Terminal=false Exec=mu4e Name=Mu4e Comment=Simple email client based on emacs Icon=envelope StartupWMClass=mu4e
where icon is stored in
~/.local/share/icons/envelope.png. Then this application can be found in Unity Dash as
The next step is adding a badge for this application in the taskbar to show the email count. That I do with the following script:
#!/usr/bin/python ccommand = "ls ~/Private/Email/graphitewriter/inbox/new | wc -l" from gi.repository import Unity, Gio, GObject, Dbusmenu # The mu4e must be put into ~/.local/share/applicactions launcher = Unity.LauncherEntry.get_for_desktop_id("mu4e.desktop") launcher.set_property("count", 0) launcher.set_property("count_visible", True) ### Defining the loop in right way import commands def update_mail_count(): count = int(commands.getstatusoutput(ccommand)) launcher.set_property("count", count) launcher.set_property("count_visible", True) if count>0: launcher.set_property("urgent", True) else: launcher.set_property("urgent",False) return True loop = GObject.MainLoop() GObject.timeout_add_seconds(1, update_mail_count) loop.run()
which is stored in
chmod +x. When this script is started, it will add a badge icon showing several files in
~/Private/Email/graphitewriter/inbox/new where unread emails are stored and will update for changes each second. For convenience, I start this script on login by adding it in "Startup Applications".
This barebones version is good but needs some additional snippets for better use
(require 'mu4e-contrib) ;; For processing html emails (setq mu4e-html2text-command 'mu4e-shr2text) (setq shr-color-visible-luminance-min 80) ;; show images (setq mu4e-show-images t) ;; Allows to view email in browser (add-to-list 'mu4e-view-actions '("ViewInBrowser" . mu4e-action-view-in-browser) t) ;; No need to showing email twice (setq mu4e-headers-skip-duplicates t)
Useful keybindings in the mu4e are as follows:
----------------- ---------------------------------------------- a + V To view email in the browser C To compose a message R To reply F To forward E To edit a draft message Ctrl-c + Ctrl-c To send a message A + w To open an attachment with some application A + e To open an attachment with emacs e To save an attachment o To open an attachment with a default application ; To change an account q To quit, go back C-g To stop whatever d + x To delete a message P Toggle between thread view H for help W Includes related messages in view w On a link, copies it to the clipboard ----------------- ----------------------------------------------
Sometimes mu4e corrupts its indexed database. Then reindexing is being needed, which can be accomplished by removing