A global minor mode that defines a full working setup for mu4e and mbsync
, based on mu 1.12+. Easily setup accounts and aliases from these providers: Google, Apple, GMX, Outlook, and Proton. Additional packages are installed and configured for a better experience, including mu4e-column-faces
, mu4e-alert
, and org-msg
. Some customizations are available, run (customize-group 'mu4easy)
.
Install locally and use the load-path
variable, e.g.:
(use-package mu4easy
:load-path "~/Code/mu4easy"
:bind ("C-c u" . mu4e)
:config (mu4easy-mode))
Or install with Melpa. Here is an example with one email account:
(use-package mu4easy
:ensure t
:bind ("C-c u" . mu4e)
:config (mu4easy-mode)
:custom
(mu4easy-contexts '((mu4easy-context
:c-name "Google"
:maildir "Gmail"
:mail "a@gmail.com"
:smtp "smtp.gmail.com"
:sent-action delete))))
See later more details on the mu4easy-context
macro.
Make sure you have mu
in your in PATH.
Comments and suggestions are welcome.
The important part is to normalize all accounts to have the same structure, containing the folders Inbox, Archive, Sent, Trash, Drafts and Spam. I would advice against syncing the Drafts folder because it can lead to issues. If you decide not to sync it, just remove the relevant lines from the .mbsyncrc
file. We assume isync 1.5.0 or newer.
Let’s go over each provider and examine the gotchas.
IMAPAccount Gmail
Host imap.gmail.com
User a@gmail.com
PassCmd "gpg -q --for-your-eyes-only --no-tty -d ~/.authinfo.gpg | awk '/machine imap.gmail.com login a@gmail.com/ {print $NF}'"
AuthMechs LOGIN
TLSType IMAPS
TLSVersions +1.2
CertificateFile /usr/local/etc/openssl@1.1/cert.pem
MaildirStore Gmail-local
Path ~/Mail/Gmail/
Inbox ~/Mail/Gmail/Inbox
SubFolders Verbatim
IMAPStore Gmail-remote
Account Gmail
Channel Gmail-inbox
Far :Gmail-remote:"INBOX"
Near :Gmail-local:"INBOX"
CopyArrivalDate yes
Create Both
Expunge Both
SyncState *
Channel Gmail-trash
Far :Gmail-remote:"[Gmail]/Trash"
Near :Gmail-local:"Trash"
CopyArrivalDate yes
Create Both
Expunge Both
SyncState *
Channel Gmail-spam
Far :Gmail-remote:"[Gmail]/Spam"
Near :Gmail-local:"Spam"
CopyArrivalDate yes
Create Both
Expunge Both
SyncState *
Channel Gmail-all
Far :Gmail-remote:"[Gmail]/All Mail"
Near :Gmail-local:"Archive"
CopyArrivalDate yes
Create Both
Expunge Both
SyncState *
Channel Gmail-drafts
Far :Gmail-remote:"[Gmail]/Drafts"
Near :Gmail-local:"Drafts"
CopyArrivalDate yes
Create Both
Expunge Both
SyncState *
Group Gmail
Channel Gmail-inbox
Channel Gmail-trash
Channel Gmail-all
Channel Gmail-spam
Channel Gmail-drafts
First is the PassCmd
. I assume all passwords (and application specific passwords) are in ~/.authinfo.gpg
with the password key-value pair last (see the NF
variable in awk
pointing to the last column).
Next, Google has the structure [Gmail]/...
so we use explicit far/near definitions. We’ll drop them in the next providers.
Finally, unlike other providers, we’re not going to sync the Sent folders because Google is saving all email in the All Mails folders and you’ll end up with duplicates locally.
IMAPAccount Apple
Host imap.mail.me.com
PORT 993
User a@icloud.com
PassCmd "gpg -q --for-your-eyes-only --no-tty -d ~/.authinfo.gpg | awk '/machine imap.mail.me.com/ {print $NF}'"
AuthMechs LOGIN
TLSType IMAPS
TLSVersions +1.2
CertificateFile /usr/local/etc/openssl@1.1/cert.pem
MaildirStore Apple-local
Path ~/Mail/Apple/
Inbox ~/Mail/Apple/Inbox
SubFolders Verbatim
IMAPStore Apple-remote
Account Apple
Channel Apple-all
Far :Apple-remote:
Near :Apple-local:
Patterns "INBOX" "Archive" "Trash" "Spam" "Drafts"
CopyArrivalDate yes
Create Both
Expunge Both
SyncState *
Channel Apple-sent
Far :Apple-remote:"Sent Messages"
Near :Apple-local:"Sent"
CopyArrivalDate yes
Create Both
Expunge Both
SyncState *
Group Apple
Channel Apple-sent
Channel Apple-all
Here we use the Patterns
key to quickly select the folders we’re interested in. It turns the Sent folders has many conventions; at Apple it’s called Sent Messages.
IMAPAccount GMX
Host imap.gmx.com
User a@gmx.com
PassCmd "gpg -q --for-your-eyes-only --no-tty -d ~/.authinfo.gpg | awk '/machine imap.gmx.com login a@gmx.com/ {print $NF}'"
AuthMechs LOGIN
TLSType IMAPS
TLSVersions +1.2
CertificateFile /usr/local/etc/openssl@1.1/cert.pem
MaildirStore GMX-local
Path ~/Mail/GMX/
Inbox ~/Mail/GMX/Inbox
SubFolders Verbatim
IMAPStore GMX-remote
Account GMX
Channel GMX
Far :GMX-remote:
Near :GMX-local:
Patterns "INBOX" "Archive" "Trash" "Spam" "Drafts" "Sent"
CopyArrivalDate yes
Create Both
Expunge Both
SyncState *
IMAPAccount Outlook
Host outlook.office365.com
User a@outlook.com
PassCmd "gpg -q --for-your-eyes-only --no-tty -d ~/.authinfo.gpg | awk '/machine outlook.office365.com login a@outlook.com/ {print $NF}'"
AuthMechs LOGIN
TLSType IMAPS
TLSVersions +1.2
MaildirStore Outlook-local
Path ~/Mail/Outlook/
Inbox ~/Mail/Outlook/Inbox
SubFolders Verbatim
IMAPStore Outlook-remote
Account Outlook
Channel Outlook-all
Far :Outlook-remote:
Near :Outlook-local:
Patterns "INBOX" "Archive" "Sent"
CopyArrivalDate yes
Create Both
Expunge Both
SyncState *
Channel Outlook-junk
Far :Outlook-remote:"Junk"
Near :Outlook-local:"Spam"
CopyArrivalDate yes
Create Both
Expunge Both
SyncState *
Channel Outlook-trash
Far :Outlook-remote:"Deleted"
Near :Outlook-local:"Trash"
CopyArrivalDate yes
Create Both
Expunge Both
SyncState *
Group Outlook
Channel Outlook-all
Channel Outlook-junk
Channel Outlook-trash
IMAPAccount Proton
Host 127.0.0.1
PORT 1111
User a@protonmail.com
PassCmd "gpg -q --for-your-eyes-only --no-tty -d ~/.authinfo.gpg | awk '/machine 127.0.0.1/ {print $NF}'"
AuthMechs LOGIN
TLSType STARTTLS
TLSVersions +1.2
CertificateFile /usr/local/etc/openssl@1.1/cert.pem
MaildirStore Proton-local
Path ~/Mail/Proton/
Inbox ~/Mail/Proton/Inbox
SubFolders Verbatim
IMAPStore Proton-remote
Account Proton
Channel Proton
Far :Proton-remote:
Near :Proton-local:
Patterns "INBOX" "Archive" "Trash" "Spam" "Drafts" "Sent"
CopyArrivalDate yes
Create Both
Expunge Both
SyncState *
In order to us Proton, one needs to install a bridge application. It specifies the IMAP and SMTP ports to use (non-standard).
Let’s go over the important parts of the elisp code.
(setf (alist-get 'refile mu4e-marks)
'(:char ("r" . "▶")
:prompt "refile"
:dyn-target (lambda (target msg) (mu4e-get-refile-folder msg))
;; Notice the special treatment for Gmail.
:action (lambda (docid msg target)
(let ((maildir (mu4e-message-field msg :maildir)))
(if (string-match-p "Gmail\\|Google" maildir)
(mu4e--server-remove docid)
(mu4e--server-move docid (mu4e--mark-check-target target) "+S-u-N"))))))
Gmail requires special treatment for archiving and deletion, since folders are labels and everything exist in the All Mail
, in addition to other “locations” like Inbox
, Sent
, etc. Here, we deal with Gmail by regex matching with the maildir name; adapt if needed. In this example, archiving for Gmail means removing the email (from the Inbox) while for other providers it means moving the email. Similar notion for deletion.
(cl-defmacro mu4easy-context (&key c-name maildir mail smtp
(smtp-mail mail)
(smtp-port 587)
(smtp-type 'starttls)
(sent-action 'sent)
(name "Daniel Fleischer")
(sig "Daniel Fleischer"))
(let
((inbox (concat "/" maildir "/Inbox"))
(sent (concat "/" maildir "/Sent"))
(trash (concat "/" maildir "/Trash"))
(refile (concat "/" maildir "/Archive"))
(draft (concat "/" maildir "/Drafts")))
`(make-mu4e-context
:name ,c-name
:match-func (lambda (msg)
(when msg
(string-match-p (concat "^/" ,maildir "/")
(mu4e-message-field msg :maildir))))
:vars '((user-mail-address . ,mail)
(user-full-name . ,name)
(mu4e-sent-folder . ,sent)
(mu4e-drafts-folder . ,draft)
(mu4e-trash-folder . ,trash)
(mu4e-refile-folder . ,refile)
(mu4e-sent-messages-behavior . ,sent-action)
(smtpmail-stream-type . ,smtp-type)
(smtpmail-smtp-service . ,smtp-port)
(smtpmail-smtp-user . ,smtp-mail)
(smtpmail-smtp-server . ,smtp)
(smtpmail-debug-info . t)
(smtpmail-debug-verbose . t)
(org-msg-signature . ,sig)
(mu4e-maildir-shortcuts .
((,inbox . ?i)
(,sent . ?s)
(,trash . ?t)
(,refile . ?a)
(,draft . ?d)))))))
That’s the macro to create contexts or identities. It’s assuming you have a consistent maildirs structure, like specified in the mbsync
config, i.e. all account names are on a single level and then below them you have Inbox, Archive, Trash, Sent, Spam and Drafts for each one. To match the context I’m just looking at the maildir the message is in. Some defaults in the function are the SMTP encryption and what to do with sent messages (either delete them in the case of Google or save them in the Sent folder; more on that in the contexts examples).
Next there are some variables settings; these are set to taste, feel free to experiment with them. Next are the bookmarks, which are very convenient both for jumping and for reading the read/unread counts.
Tip: the bookmarks query can be either a function or a string. If it’s a function, there is no read/unread count. I’m using a string generated from a function; if you first eval the string and then set the variable, you do get counts.
For org-msg package users, notice that the package itself handles the signature, so you want to define org-msg-signature
like I did in the macro. It accepts org
formatting, e.g. *Daniel Fleischer*\n/Skynet Inc/
and then converts it into formatted HTML. Also, when using org-msg, reply style is top-posting so you need it to handle the signature correctly (above the replied text).
Another improvement is creating a customized link description; i.e. calling org-store-link
to save a link to an email, it uses mu4easy-mail-link-description
which will give a nice description of the form to/from: subject (ISO timestamp)
- works with org-capture
as well.
Added is a custom updating function that asks you which account to update, or by default updates all. It is bound to the usual “U”.
Finally, setting up the accounts, either using customization or using code:
(setq mu4easy-contexts
'((mu4easy-context
:c-name "Google"
:maildir "Gmail"
:mail "a@gmail.com"
:smtp "smtp.gmail.com"
:sent-action delete)
(mu4easy-context
:c-name "1-GMX"
:maildir "GMX"
:mail "a@gmx.com"
:smtp "mail.gmx.com")
(mu4easy-context
:c-name "2-GMX-alias"
:maildir "GMX"
:mail "a.alias@gmx.com"
:smtp "mail.gmx.com"
:smtp-mail "a@gmx.com")
(mu4easy-context
:c-name "Apple"
:maildir "Apple"
:mail "a@icloud.com"
:smtp "smtp.mail.me.com")
(mu4easy-context
:c-name "3-Apple-alias"
:maildir "Apple"
:mail "a@me.com"
:smtp "smtp.mail.me.com"
:smtp-mail "a@icloud.com")
(mu4easy-context
:c-name "Outlook"
:maildir "Outlook"
:mail "a@outlook.com"
:smtp "smtp-mail.outlook.com")
(mu4easy-context
:c-name "Proton"
:maildir "Proton"
:mail "a@protonmail.com"
:smtp "127.0.0.1"
:smtp-type ssl
:smtp-port 999)
(mu4easy-context
:c-name "4-Proton-alias"
:maildir "Proton"
:mail "a@pm.com"
:smtp "127.0.0.1"
:smtp-mail "a@protonmail.com"
:smtp-type ssl
:smtp-port 999)))
Important points:
- Jumping to contexts is based on their first (unique) letter, that’s why I’m using numbers in the
c-name
key. - Google saves the sent messages in the All Mail (Archive) folder so it is recommended to set the corresponding mu4e setting to delete sent messages (locally). It’s only for Google; for the other account, sent messages are saved in the Sent folder.
- The
2-GMX
account is an alias - not another GMX account (see the last comment). It has a different mail, but the SMTP authentication needs the real email address. The outgoing email still looks like it is coming from the alias. - Proton account needs SSL encryption for SMTP, it connects to
localhost
and uses non-standard ports for IMAP and SMTP (check the Proton bridge app for details). - If you have multiple accounts with the same providers, they should have different maildirs, e.g.
~/Mail/Gmail1/
,~/Mail/Gmail2/
. Here I showed aliases, not multiple accounts.
This setup is based upon a couple of weeks worth of tweaking, trial and error. It’s not perfect; some email were lost (those not saved into sent
), lessons were learned. I didn’t try it with Microsoft-based emails. Make sure to test everything you do - to see that emails are going in and out, saved in Archive
and Sent
, compare the results with the web-based interface until you get comfortable using it 100% of the time. Or not.