jao-custom-email.el 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. ;; -*- lexical-binding: t; -*-
  2. ;;; main email system
  3. (require 'jao-afio)
  4. (setq jao-afio-mail-function 'gnus)
  5. ;; (setq jao-afio-mail-function 'notmuch)
  6. (defvar jao-notmuch-enabled (eq jao-afio-mail-function 'notmuch))
  7. ;;; personal emails and others
  8. (defvar jao-mails)
  9. (defvar jao-extra-mails)
  10. (defvar jao-mails-regexp (regexp-opt jao-mails))
  11. ;;; gnus
  12. (setq gnus-init-file "~/.emacs.d/gnus.el"
  13. gnus-home-directory "~/.emacs.d/gnus"
  14. gnus-directory gnus-home-directory
  15. gnus-cache-directory (expand-file-name "cache" gnus-home-directory)
  16. gnus-kill-files-directory (expand-file-name "News" gnus-home-directory)
  17. message-directory (expand-file-name "Mail" gnus-home-directory)
  18. mail-source-directory (expand-file-name "Mail" gnus-home-directory))
  19. ;;; proton
  20. (use-package jao-proton-utils)
  21. ;;; message mode
  22. ;;;; customization
  23. (setq message-send-mail-function 'message-send-mail-with-sendmail
  24. message-sendmail-envelope-from 'header
  25. message-sendmail-f-is-evil nil)
  26. (setq imap-store-password t)
  27. (setq password-cache-expiry nil)
  28. (setq message-generate-headers-first t)
  29. (setq message-forward-before-signature nil)
  30. (setq message-alternative-emails
  31. (regexp-opt (append jao-mails jao-extra-mails)))
  32. (setq message-dont-reply-to-names
  33. (regexp-opt (append jao-mails '("noreply@" "@noreply"
  34. "no-reply@" "@no-reply"
  35. "notifications@github"))))
  36. (setq message-citation-line-format "On %a, %b %d %Y, %N wrote:\n")
  37. (setq message-citation-line-function 'message-insert-formatted-citation-line)
  38. (setq message-user-fqdn "mail.jao.io")
  39. (setq message-kill-buffer-on-exit t)
  40. (setq message-max-buffers 5)
  41. (setq message-insert-signature t)
  42. (setq message-from-style 'angles
  43. user-mail-address (car jao-mails)
  44. mail-host-address system-name
  45. message-syntax-checks '((sender . disabled))
  46. message-default-headers
  47. (concat
  48. "X-Attribution: jao\n"
  49. "X-Clacks-Overhead: GNU Terry Pratchett\n"
  50. "X-URL: <https://jao.io/>\n")
  51. message-hidden-headers
  52. '("^References:" "^Face:" "^X-Face:" "^X-Draft-From:")
  53. message-make-forward-subject-function 'message-forward-subject-fwd)
  54. (setq message-expand-name-standard-ui t)
  55. ;;;; adjust bcc
  56. (defvar jao-message--bcc-alist
  57. `((,(regexp-quote "mail@jao.io") . "proton@jao.io")
  58. (,(regexp-quote "jao@gnu.org") . "hacking@jao.io")))
  59. (defun jao-message-insert-bcc ()
  60. (when jao-notmuch-enabled
  61. (let ((f (or (message-fetch-field "From") "")))
  62. (when-let (b (seq-some (lambda (x) (when (string-match-p (car x) f) (cdr x)))
  63. jao-message--bcc-alist))
  64. (insert "Bcc: " b "\n")))))
  65. ;; (when jao-notmuch-enabled
  66. ;; (add-hook 'message-header-setup-hook #'jao-message-insert-bcc))
  67. ;;;; to->from
  68. (defvar jao-message-to-from nil)
  69. (defun jao-message-adjust-from ()
  70. (let ((to (concat (message-fetch-field "To") (message-fetch-field "Cc"))))
  71. (when-let* ((tf (seq-find (lambda (tf) (string-match-p (car tf) to))
  72. jao-message-to-from))
  73. (from (message-make-from "Jose A Ortega Ruiz" (cdr tf))))
  74. (save-restriction
  75. (widen)
  76. (message-replace-header "From" from)))))
  77. (when jao-notmuch-enabled
  78. (add-hook 'message-header-setup-hook #'jao-message-adjust-from))
  79. ;;;; encryption
  80. (setq gnutls-min-prime-bits nil)
  81. (setq gnus-buttonized-mime-types
  82. '("multipart/encrypted" "multipart/signed" "multipart/alternative"))
  83. (setq mm-verify-option 'always)
  84. (setq mm-decrypt-option 'always)
  85. (setq mm-sign-option 'guided)
  86. (setq mm-encrypt-option 'guided)
  87. (setq mml-secure-passphrase-cache-expiry (* 3600 24)
  88. password-cache-expiry (* 3600 24))
  89. (setq smime-CA-directory "/etc/ssl/certs/"
  90. smime-certificate-directory
  91. (expand-file-name "certs/" gnus-directory))
  92. (with-eval-after-load "mm-decode"
  93. ;; Tells Gnus to inline the part
  94. (add-to-list 'mm-inlined-types "application/pgp$")
  95. ;; Tells Gnus how to display the part when it is requested
  96. (add-to-list 'mm-inline-media-tests '("application/pgp$"
  97. mm-inline-text identity))
  98. ;; Tell Gnus not to wait for a request, just display the thing
  99. ;; straight away.
  100. (add-to-list 'mm-automatic-display "application/pgp$")
  101. ;; But don't display the signatures, please.
  102. (setq mm-automatic-display (remove "application/pgp-signature"
  103. mm-automatic-display)))
  104. ;; decide whether to encrypt or just sign outgoing messages
  105. (defvar jao-message-try-sign nil)
  106. (defun jao-message-maybe-sign ()
  107. (when (and jao-message-try-sign (y-or-n-p "Sign message? "))
  108. (if (y-or-n-p "Encrypt message? ")
  109. (let ((recipient (message-fetch-field "To")))
  110. (if (or (pgg-lookup-key recipient)
  111. (and (y-or-n-p (format "Fetch %s's key? " recipient))
  112. (pgg-fetch-key pgg-default-keyserver-address
  113. recipient)))
  114. (mml-secure-message-encrypt-pgp)
  115. (mml-secure-message-sign-pgp)))
  116. (mml-secure-message-sign-pgp))))
  117. ;; for ma gnus
  118. (eval-after-load "rfc2047"
  119. '(add-to-list 'rfc2047-header-encoding-alist
  120. '("User-Agent" . address-mime)))
  121. ;;;; check attachment
  122. (defvar jao-message-attachment-regexp "\\([Ww]e send\\|[Ii] send\\|attach\\)")
  123. (defun jao-message-check-attachment ()
  124. "Check if there is an attachment in the message if I claim it."
  125. (save-excursion
  126. (message-goto-body)
  127. (when (search-forward-regexp jao-message-attachment-regexp nil t nil)
  128. (message-goto-body)
  129. (unless (or (search-forward "<#part" nil t nil)
  130. (message-y-or-n-p
  131. "No attachment. Send the message? " nil nil))
  132. (error "No message sent")))))
  133. ;;;; check fcc/gcc
  134. (defun jao-message-check-gcc ()
  135. "Ask whether to keep a copy of message."
  136. (save-excursion
  137. (save-restriction
  138. (message-narrow-to-headers)
  139. (when (and (or (message-fetch-field "Gcc")
  140. (message-fetch-field "Fcc"))
  141. (not (y-or-n-p "Archive? ")))
  142. (message-remove-header "\\(?:[BFG]cc\\)")))))
  143. ;;;; check recipient
  144. (defun jao-message-check-recipient ()
  145. (save-excursion
  146. (save-restriction
  147. (message-narrow-to-headers)
  148. (when-let ((to (message-fetch-field "To")))
  149. (when (string-match-p jao-mails-regexp to)
  150. (unless (y-or-n-p "Message is addressed to yourself. Continue?")
  151. (error "Message not sent")))))))
  152. ;;;; randomsig
  153. (with-eval-after-load "message"
  154. (when (require 'randomsig nil t)
  155. (define-key message-mode-map (kbd "C-c s") 'randomsig-replace-sig)
  156. (define-key message-mode-map (kbd "C-c S") 'randomsig-select-sig)
  157. (setq randomsig-dir (expand-file-name "~/etc/config/emacs"))
  158. (setq randomsig-files '("signatures.txt"))
  159. ;; or (setq randomsig-files (randomsig-search-sigfiles))
  160. ;; or (setq randomsig-files 'randomsig-search-sigfiles)
  161. (setq message-signature 'randomsig-signature)
  162. (setq randomsig-delimiter-pattern "^%$"
  163. randomsig-delimiter "%")))
  164. ;;;; send mail hooks
  165. (dolist (h '(jao-message-check-gcc
  166. jao-message-check-recipient
  167. jao-message-maybe-sign))
  168. (add-hook 'message-send-hook h))
  169. (unless jao-notmuch-enabled
  170. (add-hook 'message-send-hook #'jao-message-check-attachment))
  171. ;;;; keybindings
  172. (with-eval-after-load "message"
  173. ;; (define-key message-mode-map [f7] 'mml-secure-message-sign-pgp)
  174. (define-key message-mode-map [f8] 'mml-secure-message-encrypt-pgp)
  175. (define-key message-mode-map (kbd "C-c y") #'yank-media))
  176. ;;; sendmail/smtp
  177. (defun jao-sendmail-gmail ()
  178. (setq smtpmail-auth-supported '(login cram-md5 plain))
  179. (setq smtpmail-smtp-server "smtp.gmail.com")
  180. (setq smtpmail-smtp-service 587))
  181. (defun jao-sendmail-local ()
  182. (setq send-mail-function 'sendmail-send-it)
  183. (setq smtpmail-auth-supported nil) ;; (cram-md5 plain login)
  184. (setq smtpmail-servers-requiring-authorization nil)
  185. (setq smtpmail-smtp-user nil)
  186. (setq smtpmail-smtp-server "127.0.0.1")
  187. (setq smtpmail-smtp-service 25))
  188. (defun jao-sendmail-msmtp ()
  189. (setq send-mail-function 'sendmail-send-it
  190. sendmail-program "/usr/bin/msmtp"
  191. mail-specify-envelope-from t
  192. message-sendmail-envelope-from 'header
  193. mail-envelope-from 'header))
  194. (jao-sendmail-local)
  195. ;;; bbdb
  196. ;; (jao-load-path "bbdb/lisp")
  197. (use-package bbdb
  198. :ensure t
  199. :init (setq bbdb-complete-name-allow-cycling t
  200. bbdb-completion-display-record nil
  201. bbdb-gui t
  202. bbdb-message-all-addresses t
  203. bbdb-complete-mail-allow-cycling t
  204. bbdb-north-american-phone-numbers-p nil
  205. bbdb-add-aka t
  206. bbdb-add-name 2
  207. bbdb-message-all-addresses t
  208. bbdb-mua-pop-up t ;; 'horiz
  209. bbdb-mua-pop-up-window-size 0.3
  210. bbdb-layout 'multi-line
  211. bbdb-mua-update-interactive-p '(query . create)
  212. bbdb-mua-auto-update-p 'bbdb-select-message
  213. bbdb-user-mail-address-re jao-mails-regexp
  214. bbdb-auto-notes-ignore-headers
  215. `(("From" . ,jao-mails-regexp)
  216. ("From" . ".*@.*github\.com.*")
  217. ("To" . ".*@.*github\.com.*")
  218. ("Reply-to" . ".*")
  219. ("References" . ".*"))
  220. bbdb-auto-notes-ignore-messages
  221. `(("To" . ".*@.*github\\.com.*")
  222. ("From" . ".*@.*github\\.com.*")
  223. ("From" . "info-list")
  224. ("From" . "no-?reply\\|deploy")
  225. ("X-Mailer" . "MailChimp"))
  226. bbdb-accept-message-alist
  227. `(("To" . ,jao-mails-regexp)
  228. ("Cc" . ,jao-mails-regexp)
  229. ("BCc" . ,jao-mails-regexp))
  230. bbdb-ignore-message-alist bbdb-auto-notes-ignore-messages)
  231. :config
  232. (add-hook 'message-setup-hook 'bbdb-mail-aliases)
  233. ;; (add-hook 'bbdb-notice-mail-hook 'bbdb-auto-notes)
  234. (add-hook 'bbdb-after-change-hook (lambda (arg) (bbdb-save)))
  235. (require 'bbdb-anniv) ;; BBDB 3.x this gets birthdays in org agenda and diary
  236. (add-hook 'diary-list-entries-hook 'bbdb-anniv-diary-entries)
  237. (setq bbdb-file (expand-file-name "~/.emacs.d/bbdb"))
  238. (if jao-notmuch-enabled
  239. (bbdb-initialize 'message 'notmuch)
  240. (bbdb-initialize 'message 'pgp 'gnus)))
  241. ;; (load "bbdb-loaddefs")
  242. ;;; narrowing
  243. (defvar jao-mail-consult-buffer-history nil)
  244. (defun jao-mail-buffer-p (b)
  245. (or (member (buffer-name b)
  246. '("*Calendar*" "inbox.org" "*Org Agenda*"
  247. "*Fancy Diary Entries*" "diary"))
  248. (with-current-buffer b
  249. (derived-mode-p 'notmuch-show-mode
  250. 'notmuch-search-mode
  251. 'notmuch-tree-mode
  252. 'notmuch-hello-mode
  253. 'notmuch-message-mode
  254. 'gnus-group-mode
  255. 'gnus-summary-mode
  256. 'gnus-article-mode
  257. 'message-mode))))
  258. (defvar jao-mail-consult-source
  259. (list :name "mail buffer"
  260. :category 'buffer
  261. :hidden t
  262. :narrow (cons ?n "mail buffer")
  263. :history 'jao-mail-consult-buffer-history
  264. :action (lambda (b)
  265. (when (not (string-blank-p (or b "")))
  266. (jao-afio-goto-mail)
  267. (if (get-buffer-window b)
  268. (pop-to-buffer b)
  269. (pop-to-buffer-same-window b))))
  270. :items (lambda ()
  271. (mapcar #'buffer-name
  272. (seq-filter #'jao-mail-buffer-p (buffer-list))))))
  273. (jao-consult-add-buffer-source 'jao-mail-consult-source)
  274. (require 'jao-custom-notmuch)
  275. ;;; .
  276. (provide 'jao-custom-email)