Mail with Emacs
Maintaining Secrets
GPG Encryption
(require 'epa-file) (epa-file-enable)
auth-sources
- Customize auth-sources
To enable debug information:
(setq auth-source-debug t)
To clear the Emacs authentication cache:
(auth-source-forget-all-cached)
The current set of sources:
auth-sources
- ~/.emacs.d/secrets/.authinfo.gpg
- macos-keychain-internet
Authentication Secret Look-up
Require the
auth-sources
library(require 'auth-source)
auth-sources
usageThe
auth-source-search
function takes a “search spec” plist and returns a list with all matching entries. The results themselves are plists containing full entries that matched the search spec.(auth-source-search :user "toby.tripp")
The
:secret
entry will be a lambda that, when evaluated, will return the password.(let* ((matches (auth-source-search :user "toby.tripp" :max 1 :require '(:secret))) (entry (nth 0 matches)) (secret (plist-get entry :secret))) (list (list :user (plist-get entry :user)) (list :host (plist-get entry :host)) (list :port (plist-get entry :port)) (list :secret (funcall secret))))
:user toby.tripp :host example.server :port 80 :secret incredible-secret We will define a function for returning a single
auth-sources
result as analist
:(defun toby/auth-info (&rest search-spec) "Given a LOGIN to search for, return `auth-sources' that match. Valid search keys are: - :user - :host - :port Setting :max will have no result as this function only returns a single result. If there are multiple matches, the first will be returned. Results are returned as an alist with the `:secret' property pre-evaluated." (dolist (default '((:max . 1) (:require . (:secret)))) (plist-put search-spec (car default) (cdr default))) (let ((entry (nth 0 (apply 'auth-source-search search-spec)))) (mapcar (lambda (e) (let ((prop (car e)) (value (if (functionp (cadr e)) (funcall (cadr e)) (cadr e)))) (cons prop value))) (seq-partition entry 2))))
For example, given an
.authinfo
file containing:machine example.server login toby.tripp port 80 password incredible-secret
and then the following call:
(toby/auth-info :user "toby.tripp")
((:host . "example.server") (:user . "toby.tripp") (:port . "80") (:secret . "incredible-secret"))
We can then, for example, get just the password like this:
(alist-get :secret (toby/auth-info :user "toby.tripp"))
"incredible-secret"
Secret Configurations
Facilities for the evaluation of encrypted elisp configuration.
(defun toby/get-secret-var (sym) "Get the value of the symbol `sym' from the custom.el.gpg customization/configuration file." (with-temp-buffer (insert-file-contents "~/.emacs.d/secrets/custom.el.gpg") (eval-buffer) (alist-get sym toby/secret-custom)))
(toby/get-secret-var (intern property))
ADOPTED mbsync
- State "EXPERIMENTAL" from "TODO"
- State "TODO" from
- ArchLinux Help Page
- mbsync man page
- Pragmatic Emacs mbsync Page
# -*- mode: conf -*- Expunge Both SyncState *
GMail
- A script for retrieving encrypted authinfo
gpg2 -q --for-your-eyes-only --no-tty -d ~/.emacs.d/secrets/.authinfo.gpg | awk '/imap\.gmail/ { print $8 }'
- The mbsync configuration for the gmail account
IMAPAccount gmail Host imap.gmail.com Port 993 User << get-secret(property="gmail-user") >> AuthMechs LOGIN SSLType IMAPS SystemCertificates no CertificateFile /usr/local/etc/openssl/cert.pem PassCmd "<<gmail-password>>" IMAPStore gmail-remote Account gmail
Trailing slashes in mail directory entries are important.
MaildirStore gmail-local Path ~/.mail/gmail/ Inbox ~/.mail/gmail/Inbox SubFolders Verbatim
Patterns match remote mail folders for syncing:
#+tblname pattern-examples
Pattern | Meaning |
---|---|
* | Match everything |
!DIR | Exclude DIR |
DIR | Include DIR |
For example:
Patterns ![Gmail]* "[Gmail]/Sent Mail" "[Gmail]/Starred" "[Gmail]/All Mail"
Channel gmail-inbox Master :gmail-remote: Slave :gmail-local: Patterns "INBOX" "Record*" "Group*" Create Both Channel gmail-trash Master :gmail-remote:"[Gmail]/Trash" Slave :gmail-local:trash Create Both Expunge Both Channel gmail-sent Master :gmail-remote:"[Gmail]/Sent Mail" Slave :gmail-local:sent Create Both Expunge Both Channel gmail-archive Master ":gmail-remote:[Gmail]/All Mail" Slave :gmail-local:archive Create Both
Groups put together Channels, so that we can invoke mbsync
on a Group to
sync all Channels, for instance: mbsync gmail
gets mail from
"gmail-inbox", "gmail-sent", and "gmail-trash"
Group gmail Channel gmail-inbox Channel gmail-sent Channel gmail-trash Channel gmail-archive
mkdir -p ~/.mail/gmail mbsync -al
iCloud
This configuration uses authentication info stored in the MacOS Keychain. I'm not convinced this is better or worse than authinfo.
IMAPAccount icloud Host imap.mail.me.com Port 993 User <<get-secret(property="icloud-user")>> PassCmd "security find-internet-password -s imap.mail.me.com -w" AuthMechs LOGIN PLAIN SSLType IMAPS SSLVersions TLSv1.2 SystemCertificates no CertificateFile /usr/local/etc/openssl/cert.pem IMAPStore icloud-remote Account icloud
MaildirStore icloud-local Path ~/.mail/icloud/ Inbox ~/.mail/icloud/Inbox Trash Trash
Channel icloud-folders Master :icloud-remote: Slave :icloud-local: Patterns "INBOX" "Saved" "Drafts" "Archive" "Sent*" "Trash" Create Both Expunge Both SyncState *
Group icloud Channel icloud-folders
mkdir -p ~/.mail/icloud mbsync -l icloud
ADOPTED Wanderlust
- State "ADOPTED" from "EXPERIMENTAL"
- State "EXPERIMENTAL" from
(require-package 'wanderlust)
Wanderlust Customization
Initialization
(require 'mime-setup) (autoload 'wl "wl" "Wanderlust" t) (autoload 'wl-draft "wl-draft" "Write draft with Wanderlust." t) (setq elmo-maildir-folder-path "~/.mail" wl-stay-folder-window t ;; show the folder pane (left) wl-folder-window-width 25 ;; toggle on/off with 'i' ;; hide many fields from message buffers wl-message-ignored-field-list '("^.*:") wl-message-visible-field-list '("^\\(To\\|Cc\\):" "^Subject:" "^\\(From\\|Reply-To\\):" "^Organization:" "^Message-Id:" "^Delivered-To:" "^\\(Posted\\|Date\\):" "^X-Keywords:") wl-message-sort-field-list '("^From" "^Organization:" "^X-Attribution:" "^Subject" "^Date" "^To" "^Cc"))
ADOPTED Mail indexing with mu
- State "EXPERIMENTAL" from
- Emacs-Fu: Searching emails with wanderlust and mu
A sync script for use by the OS Scheduler:
#!/usr/bin/env bash exec &> >(while read line; do echo "$(date -R) $line"; done;) which -s mbsync if [[ $? -ne 0 ]]; then brew install isync fi which -s mu if [[ $? -ne 0 ]]; then brew install mu fi echo Mail Sync && mbsync -Va && echo Mail Index && mu index --maildir=~/.mail --lazy-check
Under MacOS, this script can be triggered by
launchd
with the following plist:<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>EnvironmentVariables</key> <dict> <key>PATH</key> <string>/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin</string> </dict> <key>KeepAlive</key> <false/> <key>Label</key> <string>org.thetripps.isync</string> <key>ProgramArguments</key> <array> <string>~/bin/mail-sync</string> </array> <key>StartInterval</key> <integer>300</integer> <key>RunAtLoad</key> <true /> <key>StandardOutPath</key> <string>/var/log/agents.user/org.thetripps.isync.log</string> <key>StandardErrorPath</key> <string>/var/log/agents.user/org.thetripps.isync.log</string> </dict> </plist>
ADOPTED Searching Emails
- State "EXPERIMENTAL" from
Search mu
index by typing g
in folder or summary.
(require 'elmo-search) (elmo-search-register-engine 'mu 'local-file :prog "/usr/local/bin/mu" :args '("find" pattern "--fields" "l") :charset 'utf-8) (setq elmo-search-default-engine 'mu)
Also man mu-find
.
ADOPTED org-capture
Integration
- State "EXPERIMENTAL" from
(global-unset-key (kbd "<f3>")) (global-set-key (kbd "<f3>") 'wl) (eval-after-load 'wl '(progn (require 'org-wl) (define-key wl-folder-mode-map (kbd "q") 'bury-buffer) (define-key wl-folder-mode-map (kbd "Q") 'wl-exit) (fullframe wl bury-buffer nil)))
[X]
Figure out how to auto-load this package on wanderlust init- the approach in BBDB for Contacts would probably work
With this, you can mark text in a Wanderlust email view and type C-c c e
to create an org
node with the marked contents. Meta-data about the
current email will automatically be included.
Starting Up for Reading
M-x wl
ADOPTED Setting wanderlust as the Emacs Mail composer
- State "ADOPTED" from "EXPERIMENTAL"
- State "EXPERIMENTAL" from
Open a message for writing with wl-draft
(autoload 'wl-user-agent-compose "wl-draft" nil t) (if (boundp 'mail-user-agent) (setq mail-user-agent 'wl-user-agent)) (if (fboundp 'define-mail-user-agent) (define-mail-user-agent 'wl-user-agent 'wl-user-agent-compose 'wl-draft-send 'wl-draft-kill 'mail-send-hook))
ADOPTED Try GMail for SMTP
- State "ADOPTED" from "TODO"
Incoming Mail (IMAP) Server | imap.gmail.com | |
---|---|---|
Requires SSL: | Yes | |
Port: | 993 | |
Outgoing Mail (SMTP) Server | smtp.gmail.com | |
Requires SSL | Yes | |
Requires TLS | Yes1 | |
Port for SSL | 465 | |
Port for TLS/STARTTLS | 587 |
(let* ((smtp-server (toby/get-secret-var 'smtp-server)) (smtp-config (toby/auth-info :host smtp-server))) (setq wl-smtp-authenticate-type "plain" wl-smtp-posting-server (alist-get :host smtp-config) wl-smtp-posting-port (alist-get :port smtp-config) wl-smtp-posting-user (alist-get :user smtp-config) wl-from (toby/get-secret-var 'toby/mail-from) wl-smtp-connection-type (quote ssl)))
TODO BBDB for Contacts [0/1]
- State "ADOPTED" from "EXPERIMENTAL"
- State "EXPERIMENTAL" from "TODO"
- State "TODO" from
- Referenced
[ ]
BBDB Wanderlust Integration[ ]
“BBDB: MUA ‘mime-view-mode’ Not Supported”- bbdb-init some change (it's unclear which) fixed this
[ ]
Turns out that it didn't There is a problem with the hook inwl-message-redisplay-hook
, currently set to:bbdb-mua-auto-update
. This function appears unable or unwilling to parse the headers from a message viewed withmime-view-mode
. BBDB Source Repo.
(require-package 'bbdb (version-to-list "3.2")) (eval-after-load 'wl '(progn (require 'bbdb-wl) (bbdb-initialize 'wl) (bbdb-insinuate-wl) (bbdb-mua-auto-update-init 'wl)))
Package Declaration Here
(provide 'mail-in-emacs)
Archive ARCHIVE
Footnotes:
If available