Emacs as Email Client

 Published on 11 Nov 2023 .  Filed in Notes .  1458 words

Emacs Mu4e

We can use Emacs to consult mails with the help of various programs. Among them, Mu4e is a mail client for Emacs and it is considered as an Emacs interface for mu the mail indexer. A typical example might be a mail sync program like isync which downloads mail from the remote Maildir folder to the local Maildir folder and then the mu indexer will be used to index the mail. Finally an Emacs mail client can read the mails.

Prerequisite

Install isync using your package manager, and you should install mu4e using the system package manager to avoid any compatibility issues. Here we are installing the apt package manager, but use whatever package manager you are using:

sudo apt update && sudo apt install mu4e isync

Syncing Mails

We will use isync program to download mails to local Maildir folder. We can also use a program called offlineimap which is a bit slower but it can work on Windows. Download mails from remote Maildir folder using .mbsyncrc configuration file with following settings:

Refer to "Offline Email with Isync" page for a more detailed explanation of Isync configuration.

Index mailbox

Once mails have been synced to local Maildir, run a initial indexing process by providing you e-mail address to mu program:

mu init --maildir=~/Mail --my-address=vithurshanselvarajah@gmail.com
mu index

NOTE: You will need to use –my-address for every e-mail address you use in a multiple account setup.

Email Client

Mu4e is an email client for Emacs and it's consider as Emacs interface for mu mail indexer. A typical example could be a mail syncing program like isync which download mail from remote Maildir folder to local Maildir folder and then mu indexer will be used to index mail. Then an Emacs mail client can read mails.

  ;; Start - Mail ------------------------------------------------------
  (use-package mu4e
    ;; Install mu4e using the distro's package manager to stay compatible with mbsync
    :ensure nil
    :defer 120 ; Wait until 2 min after startup
    ;; Path where the package manager is installed mu4e files
    :load-path "/usr/share/emacs/site-lisp/mu4e/"

    :config
    ;; Avoid mail syncing issues when using "mbsync"
    (setq mu4e-change-filenames-when-moving t)


    ;; Configure the function to use for sending mail
    (setq message-send-mail-function 'smtpmail-send-it)


    ;; Shortcut to Mu4e dashboard
    (global-set-key (kbd "C-c m") 'mu4e)
    ;; Distroy message buffers on exit
    (setq message-kill-buffer-on-exit t)
    ;; Disable prompt when quitting
    (setq mu4e-confirm-quit nil)


    ;; Refresh mail using isync every 10 minutes
    (setq mu4e-update-interval (* 10 60))
    (setq mu4e-get-mail-command "/usr/bin/mbsync -Va -c ~/.config/isync/mbsyncrc")
    (setq mu4e-maildir "~/.local/share/mail")

    ;; Set first context ("Laposte") as default
    (setq mu4e-context-policy 'pick-first)
    ;; Prompt for context when composing mail if a context hasn't been previously picked
    (setq mu4e-compose-context-policy 'ask-if-none)


    ;; Ask user to select a key when signing/encrypting
    (setq mm-sign-option 'guided)
    (setq mm-encrypt-option 'guided)


    ;; Common setting for all mail accounts
    ;; Use "flowed" format to display mails to recipient
    (setq mu4e-compose-format-flowed t)
    ;; Disable colors for HTML mails.
    (setq shr-use-colors nil)
    ;; Enable spell checking
    (add-hook 'mu4e-compose-mode-hook 'flyspell-mode)
    ;; Don't include related messages in queries
    (setq mu4e-search-include-related nil)
    (setq user-full-name  "Vithurshan SELVARJAH")


    ;;  If your Gmail is set up with a different lanugage you also need
    ;;  to translate the names of these folders. For Norwegian
    ;;  "[Gmail]/Corbeille" would be "[Gmail]/Papirkurv".
    (setq mu4e-contexts
          (list
           ;; Laposte - vithurshan@laposte.net
           (make-mu4e-context
            :name "Laposte"
            :match-func
            (lambda (msg)
              (when msg
                (string-prefix-p "/vithurshan@laposte.net" (mu4e-message-field msg :maildir))))
            :vars '((user-mail-address   . "vithurshan@laposte.net")
                    (mu4e-compose-signature . "Vithurshan SELVARAJAH\n* Mon site web – www.atomicl.net\n* LinkedIn – www.linkedin.com/in/vithurshan-selvarajah\n* RSS Feed - www.atomicl.net/index.xml")
                    ;; The SMTP password will be retrieved from the file
                    ;; defined in the "auth-sources" variable
                    (smtpmail-smtp-server  . "smtp.laposte.net")
                    (smtpmail-smtp-service . 465)
                    (smtpmail-stream-type  . ssl)
                    (mu4e-drafts-folder  . "/vithurshan@laposte.net/DRAFT")
                    (mu4e-sent-folder    . "/vithurshan@laposte.net/Sent Items")
                    (mu4e-refile-folder  . "/vithurshan@laposte.net/Archives")
                    (mu4e-trash-folder   . "/vithurshan@laposte.net/Deleted Items")
                    (mu4e-maildir-shortcuts . (("/vithurshan@laposte.net/Inbox"         . ?i)
                                               ("/vithurshan@laposte.net/Sent Items"    . ?s)
                                               ("/vithurshan@laposte.net/Deleted Items" . ?t)
                                               ("/vithurshan@laposte.net/DRAFT"         . ?d)))
                    ))

           ;; Gmail - vithurshanselvarajah@gmail.com
           (make-mu4e-context
            :name "Gmail"
            :match-func
            (lambda (msg)
              (when msg
                (string-prefix-p "/vithurshanselvarajah@gmail.com" (mu4e-message-field msg :maildir))))
            :vars '((user-mail-address . "vithurshanselvarajah@gmail.com")
                    (smtpmail-smtp-server  . "smtp.gmail.com")
                    (smtpmail-smtp-service . 465)
                    (smtpmail-stream-type  . ssl)
                    (mu4e-drafts-folder  . "/vithurshanselvarajah@gmail.com/[Gmail]/Brouillons")
                    (mu4e-sent-folder  . "/vithurshanselvarajah@gmail.com/[Gmail]/Messages envoy&AOk-s")
                    (mu4e-refile-folder  . "/vithurshanselvarajah@gmail.com/[Gmail]/Tous les messages")
                    (mu4e-trash-folder  . "/vithurshanselvarajah@gmail.com/[Gmail]/Corbeille")
                    (mu4e-maildir-shortcuts . (("/vithurshanselvarajah@gmail.com/Inbox"                        . ?i)
                                               ("/vithurshanselvarajah@gmail.com/[Gmail]/Messages envoy&AOk-s" . ?s)
                                               ("/vithurshanselvarajah@gmail.com/[Gmail]/Corbeille"            . ?t)
                                               ("/vithurshanselvarajah@gmail.com/[Gmail]/Brouillons"           . ?d)
                                               ("/vithurshanselvarajah@gmail.com/[Gmail]/Tous les messages"    . ?a)))
                    ))
           ))

  ;; Empty the initial bookmark list
  (setq mu4e-bookmarks '())

  (setq mu4e-bookmarks
        '((:name "All Inboxes" :query "maildir:/vithurshan@laposte.net/Inbox OR m:/vithurshanselvarajah@gmail.com/Inbox" :key ?i)
	  (:name "Sent Items" :query "m:/vithurshan@laposte.net/Sent\\ Items OR m:/vithurshanselvarajah@gmail.com/[Gmail]/Messages\\ envoy&AOk-s" :key ?s)
	  (:name "Deleted Mails" :query "m:/vithurshan@laposte.net/Deleted\\ Items OR m:/vithurshanselvarajah@gmail.com/[Gmail]/Corbeille" :key ?t)
	  (:name "Draft Mails" :query "m:/vithurshan@laposte.net/DRAFT OR m:/vithurshanselvarajah@gmail.com/[Gmail]/Brouillons" :key ?d)
          (:name "Today's messages" :query "(m:/vithurshan@laposte.net/Inbox OR m:/vithurshanselvarajah@gmail.com/Inbox) AND date:today..now" :key ?n)
          (:name "Last 7 days" :query "(m:/vithurshan@laposte.net/Inbox OR m:/vithurshanselvarajah@gmail.com/Inbox) AND date:7d..now" :hide-unread t :key ?w)
          (:name "Messages with images" :query "mime:image/*" :key ?p)))

    ;; Start mu4e in the background when Emacs starts and periodically sync mail at the interval defined above
    (mu4e t))
  ;; End - Mail --------------------------------------------------------

SMTP Conf

Now we need to set SMTP credentials in ~/.local/share/emacs/authinfo.gpg file as defined earlier for Mu4e to send mails. Whenever we save a file with ".gpg" extension, Emacs uses the epa (Easy PGP Assistant) library to encrypt the password using GnuPG:

  machine smtp.gmail.com login vithurshanselvarajah@gmail.com password <your-password> port 465

Passwords set in the file can also be extracted manually using (auth-source-search:host "smtp.gmail.com") Emacs-lisp function.

Do Not Wrap Lines

Traditional emails have a width limit of 79 characters for each line in the email body and by default Mu4e will use this limit to wrap lines in the email body. Instead, we can use the "flowed" format which tells the recipient's email client to adjust the width of line based on their screen size:

  ;; Use "flowed" format to display mails to recipient
  (setq mu4e-compose-format-flowed t)

Refer to this article for more details on "flowed" format.

Mu4e Dashboard [Optional]

Controlling the number of messages that will get displayed to user by mu4e by default:

  • mu4e-headers-results-limit: The number of messages to display in mail listings (default 500)
  • mu4e-headers-full-search: If t, shows all messages, ignoring limit.

You can toggle mu4e-headers-full-search with M-x mu4e-headers-toggle-full-search!

Search queries

Run the function mu4e-search or press letter s from within mu4e to start searching:

  • something - General text search for "something".
  • from:stallman - Emails from a particular sender.
  • date:today..now - Date range.
  • flag:attach - Emails with an attachment.
  • maildir:/Inbox - Search in a specific mail directory.

You can also use logic statements like and , not:

  • maildir:/Inbox and from:eli and docs

Documentation: https://www.djcbsoftware.nl/code/mu/mu4e/Queries.html

Keybinding

Run mu4e to see the landing page or email dashboard mu4e. When reading mail, you start out in the Headers buffer and when you select an email with RET, the View buffer is displayed in a window below the Headers buffer window.

Keybinding on Headers mode/buffer:

KeyEvilCommandDescription
Movement
C-njnext-lineMoves to the next header line
C-pkprevious-lineMoves to the previous header line
[[[[mu4e-headers-prev-unreadMoves to previous unread message
]]]]mu4e-headers-next-unreadMoves to next unread message
jJmu4e~headers-jump-to-maildirJump to another mail directory
Toggles
Pztmu4e-headers-toggle-threadingToggles threaded message display
Wzrmu4e-headers-toggle-include-relatedToggles related message display
Marking
ddmu4e-headers-mark-for-trashMarks message for deletion
mmmu4e-headers-mark-for-moveMarks message for move to folder
++mu4e-headers-mark-for-flagMarks message for flagging
--mu4e-headers-mark-for-unflagMarks message for unflagging
?mu4e-headers-mark-for-unreadMarks message as unread
!mu4e-headers-mark-for-readMarks message as read
%%mu4e-headers-mark-patternMarks based on a regex pattern
uumu4e-headers-mark-for-unmarkRemoves mark for message
UUmu4e-mark-unmark-allUnmarks all marks in the view
xxmu4e-mark-execute-allExecutes all marks in the view
Searching
ssmu4e-headers-searchSearch all e-mails
SSmu4e-headers-search-editEdit current search (useful!)
//mu4e-headers-search-narrowNarrow down the current results
bbmu4e-headers-search-bookmarkSelect a bookmark to search with
BBmu4e-headers-search-bookmark-editEdit bookmark before search
ggrmu4e-rerun-searchRerun the current search
Composing
CC, ccmu4e-compose-newCompose a new e-mail
RR, crmu4e-compose-replyCompose a reply to selected email
FF, cfmu4e-compose-forwardCompose a forward for selected email
C-c C-dmessage-dont-sendSave the composed mail as draft
EE, cemu4e-compose-editEdit selected draft message
C-c C-amml-attach-fileAttach a file
C-c RET s pmml-secure-message-sign-pgpmimeSign the message
C-c RET C pmml-secure-encrypt-pgpmimeEncrypt the message
Other Actions
qqmu4e~headers-quit-bufferQuit the headers view

Many of the same keybinding in Headers mode/buffer will work in View mode/buffer:

KeyEvilCommandDescription
Movement
C-njnext-lineMoves to the next line in message
C-pkprevious-lineMoves to the previous line in message
nC-jmu4e-view-headers-nextMoves to next email in header list
pC-kmu4e-view-headers-prevMoves to previous email in header list
[[[[mu4e-headers-prev-unreadMoves to previous unread message
]]]]mu4e-headers-next-unreadMoves to next unread message