Configuring e-mail with mu4e in Emacs
These are my notes for setting up e-mail client in Emacs (mu4e) with isync and msmtp. I have two mail accounts that I want to use in mu4e and one of them is gmail account. The other one is from self-hosted mail service. I should mention that I’m using Doom Emacs. This post also contains additional information on my sway + ly keyring configuration.
Here is list of all the dependencies needed:
- Arch packages:
- isync
- msmtp
- gnome-keyring
- libsecret
- AUR packages:
- oama
- mu
- cyrus-sasl-xoauth2-git
IMAP setup
For IMAP I use mbsync (isync) to synchronize my maildirs. This configuration is pretty straight forward,
there are only some specificities for gmail.
~/.config/isyncrc
IMAPAccount gmail Host imap.gmail.com User user@gmail.com PassCmd "oama access user@gmail.com" AuthMechs XOAUTH2 TLSType IMAPS CertificateFile /etc/ssl/certs/ca-certificates.crt IMAPStore gmail-remote Account gmail MaildirStore gmail-local Subfolders Verbatim Path ~/.mail/Gmail/ Inbox ~/.mail/Gmail/Inbox Channel gmail Far :gmail-remote: Near :gmail-local: Patterns * ![Gmail]* "[Gmail]/Sent Mail" "[Gmail]/Starred" "[Gmail]/All Mail" "[Gmail]/Bin" Create Both Expunge Both SyncState * IMAPAccount soukev Host mail.soukev.xyz Port 143 User soukev@soukev.xyz PassCmd "secret-tool lookup foo bar" TLSType STARTTLS TLSVersions +1.3 CertificateFile /etc/ssl/certs/ca-certificates.crt AuthMechs LOGIN IMAPStore soukev-remote Account soukev MaildirStore soukev-local Path ~/.mail/Soukev/ Inbox ~/.mail/Soukev/INBOX/ Trash ~/.mail/Soukev/Trash/ SubFolders Verbatim Channel soukev Far :soukev-remote: Near :soukev-local: Patterns * Create Both SyncState *
For gmail it’s important to include Expunge Both
and set all Patterns
correctly. Trash folder name
may vary e.g. [Gmail]/Bin
, [Gmail]/Trash
.
We also need to set up two more things for this configuration to work oama and gnome-keyring. Oama is there, because gmail requires oauth. And I use gnome-keyring for oama to store gmail token and also for my own password to the other mail account.
Keyring
Here I use gnome-keyring and secret-tool. I don’t use any specific configuration for these programs. Only thing that I have configured is automatic start and unlocking of the keyring.
In /etc/pam.d/ly
I added following:
auth optional pam_gnome_keyring.so session optional pam_gnome_keyring.so auto_start
This allows for passing login password to gnome keyring.
And in my sway configuration I have:
exec --no-startup-id /usr/bin/gnome-keyring-daemon --start --components=secrets,ssh,gpg,pkcs11
To store password for non-oauth account we can then do:
$ secret-tool store --label='foo-bar' foo bar
Oama setup
For oama to properly works we need to first get client id and secret for oauth. To get this we need to create google project and oauth client which should provide us with necessary credentials. I’m sorry, but I’m not going to provide you with exact steps to set this up in google cloud because they tend to change how it works, and I refuse to update this guide to keep it up-to-date.
When we get our id and secret we can configure oama.
~/.config/oama/config.yaml
encryption: tag: KEYRING services: google: client_id: CLIENT-ID client_secret: CLIENT-SECRET
Setting encryption - tag to keyring means it tries to use gnome keyring, alternatively GNU PG, KDE Wallet or KeePassXC can be used.
Now we need to run the following for the first time to authorize our device:
$ oama authorize google user@gmail.com
google rant
First, fuck oauth. It’s just weaponized security.
Google refuses to provide standard way of moving mail to maildirs (including deletion). Instead, it uses labels. So by default when we move mail to different maildir, it only adds new label when synced with IMAP. This is why I have Expunge Both in mbsync config, which seems to solve this issue.
Gmail also has weirdly specific way of handling mail trashing and deletion. The trash maildir name may vary based on your account localization (e.g. British English = Bin, American English = Trash). Great job, google!
In other mail services deleting mail through IMAP means completely removing, if we want to just trash it, we move it to trash maildir. Pretty simple.
In google they didn’t like it. Deleting mail removes all labels and leaves it ’archived’ in ’All mail’ by default. Fortunately we can change gmail settings to fix this, but I’m guessing most people are unaware of this. Before using mu4e I was thunderbird user and I didn’t know that my mail wasn’t deleted at all.
It seems the only reason that google does this, is to clutter user’s email box, if they don’t use google app, so that they’re incentivized to buy more space. They just want to do everything in their power to force users to use their apps so they could be tracked.
The only reason google provides their APIs and IMAP, SMTP is to have deniability if accused of monopolization.
“Oh noo, they don’t have to use gmail web app, they can use other mail clients for suuuurreee…” – Probably some google lawyer.
Additional Gmail setup
To make gmail behave as we want to, we need to change one important setting in the gmail web client. In gmail go to Settings -> ’See all settings’ -> ’Forwarding and POP/IMAP’, set ’Auto-Expunge off’ and then ’Move the message to the Bin’. This should ensure that mails are correctly moved to trash maildir on removal.
Finalize IMAP setup
Now we should be able to sync our IMAP maildirs and index them with mu:
$ mbsync -a $ mu init --maildir=~/.mail --my-address=user@gmail.com --my-address=soukev@soukev.xyz $ mu index
SMTP configuration
For SMTP configuration we also need to use oama and secret-tool for authentication.
~/.config/.msmtprc
# Set default values for all following accounts. defaults auth on tls on tls_trust_file /etc/ssl/certs/ca-certificates.crt logfile ~/.msmtp.log # Gmail account Gmail from user@gmail.com user user@gmail.com auth oauthbearer passwordeval oama access user@gmail.com host smtp.gmail.com port 587 # Soukev account Soukev from soukev@soukev.xyz user soukev@soukev.xyz passwordeval secret-tool lookup foo bar host mail.soukev.xyz port 587
mu4e configuration
This is straight forward now, just make sure trash folder is correctly set for gmail.
;; Base mail setup (setq mu4e-get-mail-command "mbsync -a" mu4e-update-interval 900 mu4e-trash-without-flag t mu4e-change-filenames-when-moving t mu4e-context-policy 'ask mu4e-compose-context-policy 'ask) (after! mu4e (setq sendmail-program (executable-find "msmtp") send-mail-function #'smtpmail-send-it message-sendmail-f-is-evil t message-sendmail-extra-arguments '("--read-envelope-from") message-send-mail-function #'message-send-mail-with-sendmail)) (set-email-account! "Gmail" '((user-mail-address . "user@gmail.com") (user-full-name . "user") (mu4e-sent-folder . "/Gmail/[Gmail]/Sent Mail") (mu4e-drafts-folder . "/Gmail/[Gmail]/Drafts") (mu4e-trash-folder . "/Gmail/[Gmail]/Bin") (mu4e-refile-folder . "/Gmail/[Gmail]/All Mail") (mu4e-maildir-shortcuts . (("/Gmail/Inbox" . ?i) ("/Gmail/[Gmail]/Sent Mail" . ?s) ("/Gmail/[Gmail]/Bin" . ?t) ("/Gmail/[Gmail]/All Mail" . ?a)))) t) ;; t = make this the default account (set-email-account! "Soukev" '((user-mail-address . "soukev@soukev.xyz") (user-full-name . "Soukev") (mu4e-sent-folder . "/Soukev/Sent") (mu4e-drafts-folder . "/Soukev/Drafts") (mu4e-trash-folder . "/Soukev/Trash") (mu4e-refile-folder . "/Soukev/Archive") (mu4e-maildir-shortcuts . (("/Soukev/INBOX" . ?i) ("/Soukev/Sent" . ?s) ("/Soukev/Trash" . ?t)))))