Mu4easy
mu4e + mbsync configuration for multiple email accounts in Emacs.
Install / Use
/learn @danielfleischer/Mu4easyREADME
#+TITLE: mu4easy
#+html: <a href="https://www.gnu.org/software/emacs/"><img alt="GNU Emacs" src="https://github.com/minad/corfu/blob/screenshots/emacs.svg?raw=true"/></a> #+html: <a href="https://melpa.org/#/mu4easy"><img alt="MELPA" src="https://melpa.org/packages/mu4easy-badge.svg"/></a> #+html: <a href="https://stable.melpa.org/#/mu4easy"><img src="https://stable.melpa.org/packages/mu4easy-badge.svg"></a>
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.: #+begin_src elisp (use-package mu4easy :load-path "~/Code/mu4easy" :bind ("C-c u" . mu4e) :config (mu4easy-mode)) #+end_src
Or install with Melpa. Here is an example with one email account: #+begin_src elisp (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)))) #+end_src See later more details on the =mu4easy-context= macro.
Make sure you have =mu= in your in PATH.
- =mbsync= Configurations:
- [[#Google][Google]]
- [[#Apple][Apple]]
- [[#GMX][GMX]]
- [[#Outlook][Outlook]]
- [[#Proton][Proton]]
- [[#mu4e][mu4e configuration]]
Comments and suggestions are welcome.
** mbsync :PROPERTIES: :CREATED: [2021-05-09 Sun 22:39] :END:
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.
*** Google :PROPERTIES: :CREATED: [2021-05-09 Sun 22:42] :END:
#+begin_src conf 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 #+end_src
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.
*** Apple :PROPERTIES: :CREATED: [2021-05-09 Sun 22:46] :END: #+begin_src conf 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 #+end_src
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.
*** GMX :PROPERTIES: :CREATED: [2021-05-09 Sun 22:48] :END:
#+begin_src conf 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 * #+end_src
*** Outlook :PROPERTIES: :CREATED: [2024-08-30 Fri 15:43] :END: #+begin_src conf 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 #+end_src *** Proton :PROPERTIES: :CREATED: [2021-05-09 Sun 22:49] :END:
#+begin_src conf 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 * #+end_src
In order to us Proton, one needs to install a bridge application. It specifies the IMAP and SMTP ports to use (non-standard).
** mu4e :PROPERTIES: :CREATED: [2021-05-09 Sun 22:53] :END:
Let's go over the important parts of the elisp code.
#+begin_src elisp (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")))))) #+end_src
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.
#+begin_src elisp (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 ,mail (plist-get (car (mu4e-message-field msg :to)) :email))))
: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-ma
