Files
emacs/extra/email.org
2025-09-11 14:55:19 -05:00

5.6 KiB

Incoming email is handled by notmuch. Outgoing is via msmtp

This is a pretty simple implementation without a lot of search queries, and list handling. As I get more comfortable with using emacs as an email client I'll try to get fancier.

;; Email/notmuch settings  -*- lexical-binding: t; -*-
(use-package notmuch
  :init
  (autoload 'notmuch "notmuch" "notmuch mail" t)
  :config
  (define-key notmuch-show-mode-map "d"
              (lambda ()
                "toggle deleted tag for message"
                (interactive)
                (if (member "deleted" (notmuch-show-get-tags))
                    (notmuch-show-tag (list "-deleted"))
                  (notmuch-show-tag (list "+deleted")))))

  (define-key notmuch-search-mode-map "d"
              (lambda ()
                "toggle deleted tag for message"
                (interactive)
                (if (member "deleted" (notmuch-search-get-tags))
                    (notmuch-search-tag (list "-deleted"))
                  (notmuch-search-tag (list "+deleted")))))

  (define-key notmuch-tree-mode-map "d"
              (lambda ()
                "toggle deleted tag for message"
                (interactive)
                (if (member "deleted" (notmuch-tree-get-tags))
                    (notmuch-tree-tag (list "-deleted"))
                  (notmuch-tree-tag (list "+deleted"))))))
  

;; Saved searches
(setq notmuch-saved-searches '((:name "cheapbsd"
                                      :query "tag:inbox and tag:cheapbsd"
                                      :count-query "tag:inbox and tag:cheapbsd and tag:unread")
                               (:name "icloud"
                                      :query "tag:inbox and tag:icloud"
                                      :count-query "tag:inbox and tag:icloud and tag:unread")
                               (:name "sdf"
                                      :query "tag:inbox and tag:sdf"
                                      :count-query "tag:inbox and tag:sdf and tag:unread")))

(setq mml-secure-openpgp-sign-with-sender t)

;; Sign messages by default.
(add-hook 'message-setup-hook 'mml-secure-sign-pgpmime)

;; Use msmtp
(setq send-mail-function 'sendmail-send-it
      sendmail-program "msmtp"
      mail-specify-envelope-from t
      message-sendmail-envelope-from 'header
      mail-envelope-from 'header)

(defun message-recipients ()
  "Return a list of all recipients in the message, looking at TO, CC and BCC.

Each recipient is in the format of `mail-extract-address-components'."
  (mapcan (lambda (header)
            (let ((header-value (message-fetch-field header)))
              (and
               header-value
               (mail-extract-address-components header-value t))))
          '("To" "Cc" "Bcc")))

(defun message-all-epg-keys-available-p ()
  "Return non-nil if the pgp keyring has a public key for each recipient."
  (require 'epa)
  (let ((context (epg-make-context epa-protocol)))
    (catch 'break
      (dolist (recipient (message-recipients))
        (let ((recipient-email (cadr recipient)))
          (when (and recipient-email (not (epg-list-keys context recipient-email)))
            (throw 'break nil))))
      t)))

(defun message-sign-encrypt-if-all-keys-available ()
  "Add MML tag to encrypt message when there is a key for each recipient.

Consider adding this function to `message-send-hook' to
systematically send encrypted emails when possible."
  (when (message-all-epg-keys-available-p)
    (mml-secure-message-sign-encrypt)))

(add-hook 'message-send-hook #'message-sign-encrypt-if-all-keys-available)

(setq notmuch-crypto-process-mime t)

(defvar notmuch-hello-refresh-count 0)

(defun notmuch-hello-refresh-status-message ()
  (let* ((new-count
          (string-to-number
           (car (process-lines notmuch-command "count"))))
         (diff-count (- new-count notmuch-hello-refresh-count)))
    (cond
     ((= notmuch-hello-refresh-count 0)
      (message "You have %s messages."
               (notmuch-hello-nice-number new-count)))
     ((> diff-count 0)
      (message "You have %s more messages since last refresh."
               (notmuch-hello-nice-number diff-count)))
     ((< diff-count 0)
      (message "You have %s fewer messages since last refresh."
               (notmuch-hello-nice-number (- diff-count)))))
    (setq notmuch-hello-refresh-count new-count)))

(add-hook 'notmuch-hello-refresh-hook 'notmuch-hello-refresh-status-message)

(defun color-inbox-if-unread () (interactive)
       (save-excursion
         (goto-char (point-min))
         (let ((cnt (car (process-lines "notmuch" "count" "tag:inbox and tag:unread"))))
           (when (> (string-to-number cnt) 0)
             (save-excursion
               (when (search-forward "inbox" (point-max) t)
                 (let* ((overlays (overlays-in (match-beginning 0) (match-end 0)))
                        (overlay (car overlays)))
                   (when overlay
                     (overlay-put overlay 'face '((:inherit bold) (:foreground "green")))))))))))
(add-hook 'notmuch-hello-refresh-hook 'color-inbox-if-unread)

(custom-set-variables
 ;; custom-set-variables was added by Custom.
 ;; If you edit it by hand, you could mess it up, so be careful.
 ;; Your init file should contain only one such instance.
 ;; If there is more than one, they won't work right.
 '(notmuch-search-oldest-first nil))
(custom-set-faces)
 ;; custom-set-faces was added by Custom.
 ;; If you edit it by hand, you could mess it up, so be careful.
 ;; Your init file should contain only one such instance.
 ;; If there is more than one, they won't work right.