Commit 7a00167687
Unsigned
Layout: unified · split
content/blog/2024-04-18-mu4e.org added +300
| @@ -0,0 +1,300 @@ | |||
| 1 | #+title: Email in Doom Emacs with Mu4e on macOS | ||
| 2 | #+date: <2024-04-18 Thu 10:49:00> | ||
| 3 | #+description: A short tutorial on configuring the Mu4e email client for Doom Emacs. | ||
| 4 | #+filetags: emacs, org-mode, productivity | ||
| 5 | #+slug: mu4e | ||
| 6 | |||
| 7 | This post was heavily inspired by [[https://macowners.club/posts/email-emacs-mu4e-macos/][Email setup in Emacs with Mu4e on macOS]], but | ||
| 8 | with my own tweaks for a single-account configuration and some Doom-specific | ||
| 9 | configurations. | ||
| 10 | |||
| 11 | * Overview | ||
| 12 | |||
| 13 | [[https://github.com/emacsmirror/mu4e][Mu4e]] is an Emacs-based email client based on [[https://www.djcbsoftware.nl/code/mu/][mu]], an indexer that stores email in | ||
| 14 | the Maildir format. | ||
| 15 | |||
| 16 | This blog post covers the installation of Mu4e in Doom Emacs on macOS. This | ||
| 17 | guide should be very similar to GNU Emacs and Linux-based systems, with a few | ||
| 18 | tweaks required in the various configuration files. | ||
| 19 | |||
| 20 | * Installation | ||
| 21 | ** Prerequisites | ||
| 22 | *** Create the Folder Structure | ||
| 23 | Start by creating the base folder structure: | ||
| 24 | |||
| 25 | #+begin_src sh | ||
| 26 | mkdir ~/.maildir | ||
| 27 | mkdir ~/.maildir/example # use whatever name you want to call your email account | ||
| 28 | mkdir ~/.maildir/certificates # used to store system root certificates | ||
| 29 | #+end_src | ||
| 30 | |||
| 31 | *** Store Email Account Passwords in macOS Keychain | ||
| 32 | Next, I will be using the macOS Keychain to store my email account passwords | ||
| 33 | using the command below. | ||
| 34 | |||
| 35 | #+begin_src sh | ||
| 36 | security add-generic-password -s mu4e-example -a you@example.com -w | ||
| 37 | #+end_src | ||
| 38 | |||
| 39 | This will prompt you to input your password twice to confirm. Keep the | ||
| 40 | =mu4e-example= name in mind, as you will need to reference it later in the IMAP | ||
| 41 | and SMTP configuration files. | ||
| 42 | |||
| 43 | *** Store Root Certificates | ||
| 44 | |||
| 45 | In order to use IMAP and SMTP, we need to provide certificates to the local | ||
| 46 | services. We will use the macOS defaults for this. | ||
| 47 | |||
| 48 | 1. Open =Keychain Access.app=. | ||
| 49 | 2. Select =System Roots= in the sidebar. | ||
| 50 | 3. Select all items with =CMD + a=. | ||
| 51 | 4. Export selected items with =SHIFT + CMD + a= to the file | ||
| 52 | =~/.maildir/certificates/root-certificates.pem=. | ||
| 53 | |||
| 54 | *** Install Dependencies | ||
| 55 | |||
| 56 | Install =mbsync= (via =isync=) to fetch emails via IMAP, =mu= to index emails, | ||
| 57 | and =msmtp= to send emails via SMTP. | ||
| 58 | |||
| 59 | #+begin_src sh | ||
| 60 | brew install mu isync msmtp | ||
| 61 | #+end_src | ||
| 62 | |||
| 63 | ** Installing Mu4e | ||
| 64 | |||
| 65 | Within Doom Emacs, we can install Mu4e by enabling the package. | ||
| 66 | |||
| 67 | #+begin_src sh | ||
| 68 | nano ~/.doom.d/init.el | ||
| 69 | #+end_src | ||
| 70 | |||
| 71 | In this file, uncomment the =mu4e= line within the =:email= section. You can | ||
| 72 | also enable the =+org= and =+gmail= options if you prefer. | ||
| 73 | |||
| 74 | #+begin_src elisp | ||
| 75 | (doom! :input | ||
| 76 | ... | ||
| 77 | |||
| 78 | mu4e | ||
| 79 | ;;(mu4e +org +gmail) | ||
| 80 | ... | ||
| 81 | (default +bindings +smartparens)) | ||
| 82 | #+end_src | ||
| 83 | |||
| 84 | * Configuration | ||
| 85 | As an overall suggestion, I create the following configuration files in the | ||
| 86 | =~/.maildir= directory and using symlinks to their proper locations so that I | ||
| 87 | can backup and restore these files easily. | ||
| 88 | |||
| 89 | #+begin_src sh | ||
| 90 | touch ~/.maildir/.mbsyncrc && \ | ||
| 91 | touch ~/.maildir/.msmtprc && \ | ||
| 92 | ln -s /Users/username/.maildir/.mbsyncrc /Users/username/.mbsyncrc && \ | ||
| 93 | ln -s /Users/username/.maildir/.msmtprc /Users/username/.msmtprc | ||
| 94 | #+end_src | ||
| 95 | |||
| 96 | You can also create these files in your home directory and skip the symlinking | ||
| 97 | process above. | ||
| 98 | |||
| 99 | ** IMAP | ||
| 100 | Next, let's configure =mbsync= in the file created above. Paste the following | ||
| 101 | information and customize it to match your mail provider's information. | ||
| 102 | |||
| 103 | #+begin_src sh | ||
| 104 | nano ~/.maildir/.mbsyncrc | ||
| 105 | #+end_src | ||
| 106 | |||
| 107 | #+begin_src conf | ||
| 108 | IMAPAccount example | ||
| 109 | Host imap.example.com | ||
| 110 | User dummy@example.com | ||
| 111 | PassCmd "security find-generic-password -s mu4e-example -a dummy@example.com -w" | ||
| 112 | Port 993 | ||
| 113 | SSLType IMAPS | ||
| 114 | AuthMechs Login | ||
| 115 | CertificateFile ~/.maildir/certificates/root-certificates.pem | ||
| 116 | |||
| 117 | IMAPStore example-remote | ||
| 118 | Account example | ||
| 119 | |||
| 120 | MaildirStore example-local | ||
| 121 | SubFolders Verbatim | ||
| 122 | Path ~/.maildir/example/ | ||
| 123 | Inbox ~/.maildir/example/INBOX | ||
| 124 | |||
| 125 | Channel example | ||
| 126 | Far :example-remote: | ||
| 127 | Near :example-local: | ||
| 128 | Patterns * | ||
| 129 | Create Near | ||
| 130 | Sync All | ||
| 131 | Expunge Both | ||
| 132 | SyncState * | ||
| 133 | #+end_src | ||
| 134 | |||
| 135 | ** SMTP | ||
| 136 | Next, let's configured =msmtprc= in the file created above. Paste the following | ||
| 137 | information and customize it to match your mail provider's information. | ||
| 138 | |||
| 139 | #+begin_src sh | ||
| 140 | nano ~/.maildir/.msmtprc | ||
| 141 | #+end_src | ||
| 142 | |||
| 143 | ,#+begin_src conf | ||
| 144 | defaults | ||
| 145 | logfile ~/.maildir/msmtp.log | ||
| 146 | tls_trust_file ~/.maildir/certificates/root-certificates.pem | ||
| 147 | |||
| 148 | account example | ||
| 149 | auth on | ||
| 150 | host smtp.example.com | ||
| 151 | port 465 | ||
| 152 | protocol smtp | ||
| 153 | from you@example.com | ||
| 154 | user you@example.com | ||
| 155 | passwordeval security find-generic-password -s mu4e-example -a you@example.com -w | ||
| 156 | tls on | ||
| 157 | tls_starttls off | ||
| 158 | |||
| 159 | account default : example | ||
| 160 | #+end_src | ||
| 161 | |||
| 162 | ** Doom Emacs | ||
| 163 | Finally, we need to configure Doom Emacs to use the proper packages and set some | ||
| 164 | variables and functions. | ||
| 165 | |||
| 166 | #+begin_src sh | ||
| 167 | nano ~/.doom.d/config.el | ||
| 168 | #+end_src | ||
| 169 | |||
| 170 | #+begin_src elisp | ||
| 171 | ;; load packages and programs | ||
| 172 | (use-package mu4e | ||
| 173 | :load-path "/Users/username/.emacs.d/modules/email/mu4e/") | ||
| 174 | (require 'smtpmail) | ||
| 175 | (setq mu4e-mu-binary (executable-find "mu")) | ||
| 176 | |||
| 177 | ;; set base directory | ||
| 178 | (setq mu4e-maildir "~/.maildir") | ||
| 179 | |||
| 180 | ;; sync imap servers | ||
| 181 | (setq mu4e-get-mail-command (concat (executable-find "mbsync") " -a")) | ||
| 182 | |||
| 183 | ;; how often to sync in seconds | ||
| 184 | (setq mu4e-update-interval 300) | ||
| 185 | |||
| 186 | ;; save attachments to defined directory | ||
| 187 | (setq mu4e-attachment-dir "~/Downloads") | ||
| 188 | |||
| 189 | ;; rename files when moving - needed for mbsync: | ||
| 190 | (setq mu4e-change-filenames-when-moving t) | ||
| 191 | |||
| 192 | ;; list of your email adresses: | ||
| 193 | (setq mu4e-user-mail-address-list '("you@example.com")) | ||
| 194 | |||
| 195 | ;; check your ~/.maildir to see naming of subdirectories | ||
| 196 | (setq mu4e-maildir-shortcuts | ||
| 197 | '(("/example/INBOX" . ?e) | ||
| 198 | ("/example/Sent" . ?E))) | ||
| 199 | |||
| 200 | (setq mu4e-contexts | ||
| 201 | `(,(make-mu4e-context | ||
| 202 | :name "example" | ||
| 203 | :enter-func | ||
| 204 | (lambda () (mu4e-message "Enter you@example.com context")) | ||
| 205 | :leave-func | ||
| 206 | (lambda () (mu4e-message "Leave you@example.com context")) | ||
| 207 | :match-func | ||
| 208 | (lambda (msg) | ||
| 209 | (when msg | ||
| 210 | (mu4e-message-contact-field-matches msg | ||
| 211 | :to "you@example.com"))) | ||
| 212 | :vars '((user-mail-address . "you@example.com") | ||
| 213 | (user-full-name . "Christian Cleberg") | ||
| 214 | ;; check your ~/.maildir to see how the subdirectories are called | ||
| 215 | ;; e.g `ls ~/.maildir/example' | ||
| 216 | (mu4e-drafts-folder . "/example/Drafts") | ||
| 217 | (mu4e-refile-folder . "/example/Archive") | ||
| 218 | (mu4e-sent-folder . "/example/Sent") | ||
| 219 | (mu4e-trash-folder . "/example/Trash"))))) | ||
| 220 | |||
| 221 | (setq mu4e-context-policy 'pick-first) ;; start with the first (default) context; | ||
| 222 | (setq mu4e-compose-context-policy 'ask) ;; ask for context if no context matches; | ||
| 223 | |||
| 224 | ;; gpg encryptiom & decryption: | ||
| 225 | ;; this can be left alone | ||
| 226 | (require 'epa-file) | ||
| 227 | (epa-file-enable) | ||
| 228 | (setq epa-pinentry-mode 'loopback) | ||
| 229 | (auth-source-forget-all-cached) | ||
| 230 | |||
| 231 | ;; don't keep message compose buffers around after sending: | ||
| 232 | (setq message-kill-buffer-on-exit t) | ||
| 233 | |||
| 234 | ;; send function: | ||
| 235 | (setq send-mail-function 'sendmail-send-it | ||
| 236 | message-send-mail-function 'sendmail-send-it) | ||
| 237 | |||
| 238 | ;; send program: | ||
| 239 | (setq sendmail-program (executable-find "msmtp")) | ||
| 240 | |||
| 241 | ;; select the right sender email from the context. | ||
| 242 | (setq message-sendmail-envelope-from 'header) | ||
| 243 | |||
| 244 | ;; mu4e cc & bcc | ||
| 245 | (add-hook 'mu4e-compose-mode-hook | ||
| 246 | (defun timu/add-cc-and-bcc () | ||
| 247 | "My Function to automatically add Cc & Bcc: headers. | ||
| 248 | This is in the mu4e compose mode." | ||
| 249 | (save-excursion (message-add-header "Cc:\n")) | ||
| 250 | (save-excursion (message-add-header "Bcc:\n")))) | ||
| 251 | |||
| 252 | ;; mu4e address completion | ||
| 253 | (add-hook 'mu4e-compose-mode-hook 'company-mode) | ||
| 254 | #+end_src | ||
| 255 | |||
| 256 | Be sure to sync Doom to update the current configurations. | ||
| 257 | |||
| 258 | #+begin_src sh | ||
| 259 | doom sync | ||
| 260 | #+end_src | ||
| 261 | |||
| 262 | If you have Doom open, execute =SPC h r r= to reload the new configurations. | ||
| 263 | |||
| 264 | * Initial Sync | ||
| 265 | Once you have configured all of the relevant files, you can perform an initial | ||
| 266 | sync. Note that you can perform syncing within Mu4e itself afer this. | ||
| 267 | |||
| 268 | #+begin_src sh | ||
| 269 | mbsync -aV | ||
| 270 | #+end_src | ||
| 271 | |||
| 272 | Once you sync the data, you can index the emails. | ||
| 273 | |||
| 274 | #+begin_src sh | ||
| 275 | mu init -m ~/.maildir --my-address you@example.com && \ | ||
| 276 | mu index | ||
| 277 | #+end_src | ||
| 278 | |||
| 279 | The emails will now to be ready to use! | ||
| 280 | |||
| 281 | * Screenshots | ||
| 282 | |||
| 283 | You can now launch Doom and open Mu4e with =SPC o m=. You can also explore the | ||
| 284 | Mu4e options with =SPC : mu4e=. | ||
| 285 | |||
| 286 | The home page shows various options and metdata about the account you've opened. | ||
| 287 | |||
| 288 | #+caption: Mu4e Home Page | ||
| 289 | [[https://img.cleberg.net/blog/20240418-mu4e/mu4e.png]] | ||
| 290 | |||
| 291 | When composing an email, you can see the metadata fields at the top and compose | ||
| 292 | the message below the indicator line. | ||
| 293 | |||
| 294 | #+caption: Composition Screen | ||
| 295 | [[https://img.cleberg.net/blog/20240418-mu4e/draft.png]] | ||
| 296 | |||
| 297 | When in a folder, you can view emails in a pane and perform actions as needed. | ||
| 298 | |||
| 299 | #+caption: Inbox | ||
| 300 | [[https://img.cleberg.net/blog/20240418-mu4e/inbox.png]] | ||