summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorjao <jao@gnu.org>2021-04-18 02:55:22 +0100
committerjao <jao@gnu.org>2021-04-18 02:56:13 +0100
commit7d8344ac8af19d7a88c3547fa0ab8ced01135e86 (patch)
tree490eb7b83a077b59ac51f3ca7c0e7245d7d17173
parent8ea756c5d0ff541d33fb1be56d4c53a0b12f90e4 (diff)
downloadelibs-7d8344ac8af19d7a88c3547fa0ab8ced01135e86.tar.gz
elibs-7d8344ac8af19d7a88c3547fa0ab8ced01135e86.tar.bz2
email.org, with some notmuch fine-tuning
-rw-r--r--email.org448
-rw-r--r--eww.org1
-rw-r--r--gnus.org22
-rw-r--r--init.org461
-rw-r--r--lib/eos/jao-afio.el21
-rw-r--r--lib/themes/jao-themes.el16
-rw-r--r--readme.org1
7 files changed, 503 insertions, 467 deletions
diff --git a/email.org b/email.org
new file mode 100644
index 0000000..f3e23c5
--- /dev/null
+++ b/email.org
@@ -0,0 +1,448 @@
+#+title: email handling (message mode, bbdb, notmuch, et al.)
+
+* Message mode
+*** Customization
+ #+begin_src emacs-lisp
+ (defvar jao-mails "")
+ (defvar jao-mails-regexp)
+ (setq jao-mails-regexp (regexp-opt jao-mails))
+
+ (require 'message)
+ (setq message-send-mail-function 'message-send-mail-with-sendmail
+ message-sendmail-envelope-from 'header
+ message-sendmail-f-is-evil nil)
+ (setq imap-store-password t)
+ (setq password-cache-expiry nil)
+ (setq message-generate-headers-first t)
+ (setq message-forward-before-signature nil)
+ (setq message-alternative-emails jao-mails-regexp)
+ (setq message-dont-reply-to-names
+ (format "%s\\|%s" jao-mails-regexp (regexp-opt '("noreply@" "@noreply"
+ "no-reply@" "@no-reply"
+ "notifications@github"))))
+ (setq message-citation-line-format "On %a, %b %d %Y, %N wrote:\n")
+ (setq message-citation-line-function 'message-insert-formatted-citation-line)
+
+ (setq message-user-fqdn "gnus.jao.io")
+
+ ;; writing messages
+ (setq message-kill-buffer-on-exit t)
+ (setq message-max-buffers 5)
+ (setq message-insert-signature t)
+ (setq message-from-style 'angles
+ user-mail-address (car jao-mails)
+ mail-host-address system-name
+ message-syntax-checks '((sender . disabled))
+ message-default-headers
+ (concat
+ "X-Attribution: jao\n"
+ "X-Clacks-Overhead: GNU Terry Pratchett\n"
+ "X-URL: <http://jao.io/>\n")
+ message-hidden-headers
+ '("^References:" "^Face:" "^X-Face:" "^X-Draft-From:")
+ message-make-forward-subject-function 'message-forward-subject-fwd)
+
+ (setq message-expand-name-standard-ui t)
+ #+end_src
+*** Encryption
+ #+BEGIN_SRC emacs-lisp
+ (require 'gnutls)
+ ;; avoiding bogus warning
+ (setq gnutls-min-prime-bits nil)
+ (setq gnus-buttonized-mime-types
+ '("multipart/encrypted" "multipart/signed" "multipart/alternative"))
+
+ (setq mm-verify-option 'always)
+ (setq mm-decrypt-option 'always)
+
+ (setq mm-sign-option 'guided)
+ (setq mm-encrypt-option 'guided)
+
+ (setq mml-secure-passphrase-cache-expiry (* 3600 24)
+ password-cache-expiry (* 3600 24))
+
+ (setq smime-CA-directory "/etc/ssl/certs/"
+ smime-certificate-directory"/home/jao/.emacs.d/gnus/Mail/certs/")
+
+ ;; Tells Gnus to inline the part
+ (eval-after-load "mm-decode"
+ '(add-to-list 'mm-inlined-types "application/pgp$"))
+ ;; Tells Gnus how to display the part when it is requested
+ (eval-after-load "mm-decode"
+ '(add-to-list 'mm-inline-media-tests '("application/pgp$"
+ mm-inline-text identity)))
+ ;; Tell Gnus not to wait for a request, just display the thing
+ ;; straight away.
+ (eval-after-load "mm-decode"
+ '(add-to-list 'mm-automatic-display "application/pgp$"))
+ ;; But don't display the signatures, please.
+ (eval-after-load "mm-decode"
+ (quote (setq mm-automatic-display (remove "application/pgp-signature"
+ mm-automatic-display))))
+
+ ;; decide whether to encrypt or just sign outgoing messages
+ (defvar jao-message-try-sign nil)
+ (defun jao-message-maybe-sign ()
+ (when (and jao-message-try-sign (y-or-n-p "Sign message? "))
+ (if (y-or-n-p "Encrypt message? ")
+ (let ((recipient (message-fetch-field "To")))
+ (if (or (pgg-lookup-key recipient)
+ (and (y-or-n-p (format "Fetch %s's key? " recipient))
+ (pgg-fetch-key pgg-default-keyserver-address
+ recipient)))
+ (mml-secure-message-encrypt-pgp)
+ (mml-secure-message-sign-pgp)))
+ (mml-secure-message-sign-pgp))))
+
+ ;; for ma gnus
+ (eval-after-load "rfc2047"
+ '(add-to-list 'rfc2047-header-encoding-alist
+ '("User-Agent" . address-mime)))
+
+ ;; (define-key message-mode-map [f7] 'mml-secure-message-sign-pgp)
+ (define-key message-mode-map [f8] 'mml-secure-message-encrypt-pgp)
+ #+END_SRC
+*** Attach image to message
+ Use ~C-c C-p~ in message-mode.
+*** Check attachment
+ #+BEGIN_SRC emacs-lisp
+ (defvar jao-message-attachment-regexp "\\([Ww]e send\\|[Ii] send\\|attach\\)")
+ (defun jao-message-check-attachment ()
+ "Check if there is an attachment in the message if I claim it."
+ (save-excursion
+ (message-goto-body)
+ (when (search-forward-regexp jao-message-attachment-regexp nil t nil)
+ (message-goto-body)
+ (unless (or (search-forward "<#part" nil t nil)
+ (message-y-or-n-p
+ "No attachment. Send the message? " nil nil))
+ (error "No message sent")))))
+ #+END_SRC
+*** Check Gcc
+ #+BEGIN_SRC emacs-lisp
+ (defun jao-message-check-gcc ()
+ "Ask whether to keep a copy of message."
+ (save-excursion
+ (save-restriction
+ (message-narrow-to-headers)
+ (when (and (message-fetch-field "Gcc")
+ (not (y-or-n-p "Archive? ")))
+ (message-remove-header "Gcc")))))
+
+ (defun jao-message-toggle-gcc ()
+ "Insert or remove the \"Gcc\" header."
+ (interactive)
+ (save-excursion
+ (save-restriction
+ (message-narrow-to-headers)
+ (if (message-fetch-field "Gcc")
+ (message-remove-header "Gcc")
+ (gnus-inews-insert-gcc)))))
+
+ ;; (define-key message-mode-map [(f6)] 'jao-message-toggle-gcc)
+ #+END_SRC
+*** Check recipient
+ #+begin_src emacs-lisp
+ (defun jao-message-check-recipient ()
+ (save-excursion
+ (save-restriction
+ (message-narrow-to-headers)
+ (when-let ((to (message-fetch-field "To")))
+ (when (string-match-p jao-mails-regexp to)
+ (unless (y-or-n-p "Message is addressed to yourself. Continue?")
+ (error "Message not sent")))))))
+ #+end_src
+*** Randomsig
+ #+BEGIN_SRC emacs-lisp
+ (when (require 'randomsig nil t)
+ (define-key message-mode-map (kbd "C-c s") 'randomsig-replace-sig)
+ (define-key message-mode-map (kbd "C-c S") 'randomsig-select-sig)
+ (eval-after-load "gnus-sum"
+ '(define-key gnus-summary-save-map "-" 'gnus/randomsig-summary-read-sig))
+ (setq randomsig-dir (expand-file-name "~/etc/config/emacs"))
+ (setq randomsig-files '("signatures.txt"))
+ ;; or (setq randomsig-files (randomsig-search-sigfiles))
+ ;; or (setq randomsig-files 'randomsig-search-sigfiles)
+ (setq message-signature 'randomsig-signature)
+ (setq randomsig-delimiter-pattern "^%$"
+ randomsig-delimiter "%"))
+ #+END_SRC
+*** Send mail hooks
+ #+BEGIN_SRC emacs-lisp
+ (dolist (h '(jao-message-check-gcc
+ jao-message-check-attachment
+ jao-message-check-recipient
+ jao-message-maybe-sign))
+ (add-hook 'message-send-hook h))
+ #+END_SRC
+* sendmail/smtp
+ #+BEGIN_SRC emacs-lisp
+ (require 'smtpmail)
+
+ (defun jao-sendmail-gmail ()
+ (setq smtpmail-auth-supported '(login cram-md5 plain))
+ (setq smtpmail-smtp-server "smtp.gmail.com")
+ (setq smtpmail-smtp-service 587))
+
+ (defun jao-sendmail-local ()
+ (setq smtpmail-auth-supported nil) ;; (cram-md5 plain login)
+ (setq smtpmail-smtp-server "127.0.0.1")
+ (setq smtpmail-smtp-service 25))
+
+ ;; (jao-sendmail-gmail)
+ (jao-sendmail-local)
+ #+END_SRC
+* notmuch
+*** Package configuration
+ #+begin_src emacs-lisp
+ (defun jao-notmuch--mboxes-search (box)
+ (mapcar (lambda (m)
+ `(:name ,m
+ :search-type tree
+ :query ,(format "folder:%s/%s and tag:unread" box m)))
+ (jao-list-mailboxes box)))
+
+ (use-package notmuch
+ :ensure t
+ :init
+ (setq notmuch-fcc-dirs '(("jao@bigml.com" . "bigml/sent")
+ (".*" . "jao/sent"))
+ notmuch-message-headers-visible t
+ notmuch-message-headers
+ '("Subject" "To" "Cc" "Date" "List-Id"
+ "X-Mailer" "User-Agent" "X-User-Agent")
+ notmuch-tagging-keys
+ '(("a" notmuch-archive-tags "Archive")
+ ("u" notmuch-show-mark-read-tags "Mark read")
+ ("f" ("+flagged") "Flag")
+ ("s" ("+spam" "-inbox") "Mark as spam")
+ ("d" ("+deleted" "-inbox") "Delete"))
+ notmuch-saved-searches
+ `((:name "jao" :key "j"
+ :query "folder:jao/inbox"
+ :search-type tree
+ :count-query "folder:jao/inbox and tag:unread")
+ (:name "bigml" :key "b"
+ :count-query "folder:bigml/inbox and tag:unread"
+ :search-type tree
+ :query "folder:bigml/inbox")
+ ,@(jao-notmuch--mboxes-search "jao")
+ ,@(jao-notmuch--mboxes-search "bigml")
+ ,@(jao-notmuch--mboxes-search "feeds")
+ (:name "unread" :query "tag:unread" :key "u" :search-type tree)
+ (:name "sent" :query "tag:sent" :key "t" :search-type tree)
+ (:name "drafts" :query "tag:draft" :key "d" :search-type tree)
+ (:name "all mail" :query "*" :count-query "tag:unread" :key "a"
+ :search-type tree))
+ notmuch-hello-sections
+ '(notmuch-hello-insert-saved-searches
+ notmuch-hello-insert-alltags
+ notmuch-hello-insert-header
+ ;; notmuch-hello-insert-footer
+ )
+ notmuch-show-all-multipart/alternative-parts nil
+ notmuch-show-all-tags-list t
+ notmuch-show-indent-messages-width 2
+ notmuch-show-part-button-default-action 'notmuch-show-view-part
+ notmuch-show-logo nil
+ notmuch-show-empty-saved-searches nil
+ notmuch-tree-result-format
+ '(("date" . "%12s ")
+ ("authors" . "%-30s")
+ ("subject" . " - %-90s")
+ ("tags" . " (%s)"))
+ notmuch-unthreaded-result-format notmuch-tree-result-format
+ notmuch-wash-wrap-lines-length 80)
+ :bind (:map notmuch-show-mode-map
+ ("C-c C-c" . jao-notmuch-goto-message-in-gnus)))
+ #+end_SRC
+* gnus
+*** notmuch -> gnus
+ #+begin_src emacs-lisp
+ (defun jao-notmuch-goto-message-in-gnus ()
+ "Open a summary buffer containing the current notmuch article."
+ (interactive)
+ (let ((group (jao-maildir-file-to-group (notmuch-show-get-filename)))
+ (message-id (replace-regexp-in-string "^id:"
+ ""
+ (notmuch-show-get-message-id))))
+ (if (and group message-id)
+ (org-gnus-follow-link group message-id)
+ (message "Couldn't get relevant infos for switching to Gnus."))))
+ #+end_src
+*** gnus.el tangling
+ The core of Gnus's configuration is tangled to the usual ~gnus.el~
+ from [[./gnus.org][gnus.org]].
+ #+begin_src emacs-lisp
+ (defalias 'jao-open-gnus-frame 'jao-afio--goto-mail)
+ (setq gnus-init-file (jao-maybe-tangle "gnus"))
+ #+end_src
+*** Directories
+ #+begin_src emacs-lisp
+ (setq gnus-home-directory "~/.emacs.d/gnus"
+ gnus-directory gnus-home-directory)
+
+ (defun jao-gnus-dir (dir)
+ (expand-file-name dir gnus-home-directory))
+
+ (setq smtpmail-queue-dir (jao-gnus-dir "Mail/queued-mail/"))
+
+ (setq mail-source-directory (jao-gnus-dir "Mail/")
+ message-auto-save-directory (jao-gnus-dir "Mail/drafts/")
+ message-directory (jao-gnus-dir "Mail/"))
+
+ (setq gnus-default-directory (expand-file-name "~")
+ gnus-startup-file (jao-gnus-dir "newsrc")
+ gnus-agent-directory (jao-gnus-dir "News/agent")
+ gnus-home-score-file (jao-gnus-dir "scores")
+ gnus-article-save-directory (jao-gnus-dir "saved/")
+ nntp-authinfo-file (jao-gnus-dir "authinfo")
+ nnmail-message-id-cache-file (jao-gnus-dir "nnmail-cache")
+ nndraft-directory (jao-gnus-dir "drafts")
+ nnrss-directory (jao-gnus-dir "rss"))
+ #+end_src
+* Visual message fill column
+ #+begin_src emacs-lisp
+ (use-package visual-fill-column
+ :ensure t
+ :init
+ (setq-default fringes-outside-margins nil
+ visual-fill-column-width 80
+ visual-fill-column-fringes-outside-margins nil)
+ (setq gnus-treat-fill-long-lines nil)
+ :config
+ (setq split-window-preferred-function
+ #'visual-fill-column-split-window-sensibly)
+ :bind ((:map ctl-x-map ("M-f" . visual-fill-column-mode))))
+
+ ;; (add-hook 'gnus-article-mode-hook #'visual-line-mode)
+ ;; (add-hook 'gnus-article-mode-hook #'visual-fill-column-mode)
+
+ ;; Name may be misleading, it does not set `fill-column' (which
+ ;; is still used by M-q) in `message-mode', but enables
+ ;; auto-filling on a given column.
+ ;; (setq message-fill-column nil)
+ ;; FIXME: There is no proper way to make fill commands to unfill.
+ ;; (add-hook 'message-mode-hook
+ ;; (lambda ()
+ ;; (setq-local fill-column most-positive-fixnum)))
+ ;; (when-require 'visual-fill-column
+ ;; (add-hook 'message-mode-hook #'visual-fill-column-mode))
+ #+end_src
+* Mail this buffer
+ #+BEGIN_SRC emacs-lisp
+ (defun jao-mail-this-file ()
+ (interactive)
+ (let ((file (buffer-file-name)))
+ (compose-mail)
+ (save-excursion
+ (message-goto-subject)
+ (insert (file-name-nondirectory file))
+ (message-goto-body)
+ (newline 2)
+ (mml-attach-file file (mm-default-file-encoding file)))))
+ #+END_SRC
+* mailcap
+ #+BEGIN_SRC emacs-lisp
+ (require 'mailcap)
+
+ (add-to-list 'mailcap-mime-extensions '(".JPEG" . "image/jpeg"))
+ (add-to-list 'mailcap-mime-extensions '(".JPG" . "image/jpeg"))
+
+ (let* ((apps (cdr (assoc "application" mailcap-mime-data)))
+ (apps (cl-remove-if (lambda (x) (string= (car x) "pdf")) apps)))
+ (setcdr (assoc "application" mailcap-mime-data) apps)
+ (mailcap-parse-mailcaps nil t))
+ #+END_SRC
+* frm
+ #+begin_src emacs-lisp
+ (use-package jao-frm
+ :init (setq jao-frm-mail-command 'jao-open-gnus-frame))
+
+ (defun jao-frm--formatter (mbox n)
+ (apply #'format "%s/%s: %s" `(,@(last (split-string mbox "/") 2) ,n)))
+
+ (defun jao-frm--show ()
+ (interactive)
+ (jao-frm-show-mail-numbers #'jao-frm--formatter))
+
+ (global-set-key [(f12)] 'jao-frm--show)
+ (global-set-key [(f8)] 'jao-frm)
+
+ #+end_src
+* maildirs
+ #+begin_src emacs-lisp
+ (defun jao-list-mailboxes (base)
+ (let ((dir (expand-file-name base "~/var/mail")))
+ (cl-remove-if (lambda (x)
+ (member x '("." ".." "sent" "inbox" "trash")))
+ (directory-files dir))))
+
+ (defvar jao-maildir-maildirs nil)
+ (defvar jao-maildir-tracked-maildirs nil)
+ (use-package jao-maildir
+ :config
+ (defun jao-maildir--ensure-counts ()
+ (when gnus-newsgroup-name
+ (when (string-match "^nnimap.*:\\(.+\\)" gnus-newsgroup-name)
+ (let ((mbox (format "/home/jao/var/mail/%s"
+ (match-string 1 gnus-newsgroup-name))))
+ (jao-maildir-update-info-string mbox)))))
+ (with-eval-after-load "gnus-sum"
+ (add-hook 'gnus-exit-group-hook #'jao-maildir--ensure-counts)))
+
+ (jao-maildir-setup jao-maildir-maildirs jao-maildir-tracked-maildirs -20)
+ #+end_src
+* bbdb
+ #+begin_src emacs-lisp
+ (use-package bbdb
+ :ensure t
+ :init (setq bbdb-complete-name-allow-cycling t
+ bbdb-completion-display-record nil
+ bbdb-gui t
+ bbdb-message-all-addresses t
+ bbdb-complete-mail-allow-cycling t
+ bbdb-north-american-phone-numbers-p nil
+ bbdb-add-aka t
+ bbdb-add-name 2
+ bbdb-message-all-addresses t
+ bbdb-mua-pop-up t ;; 'horiz
+ bbdb-mua-pop-up-window-size 0.3
+ bbdb-layout 'multi-line
+ bbdb-mua-update-interactive-p '(query . create)
+ bbdb-mua-auto-update-p 'bbdb-select-message
+ bbdb-user-mail-address-re jao-mails-regexp
+ bbdb-auto-notes-ignore-headers
+ `(("From" . ,jao-mails-regexp)
+ ("From" . ".*@.*github\.com.*")
+ ("To" . ".*@.*github\.com.*")
+ ("Reply-to" . ".*")
+ ("References" . ".*"))
+ bbdb-auto-notes-ignore-messages
+ `(("To" . ".*@.*github\\.com.*")
+ ("From" . ".*@.*github\\.com.*")
+ ("From" . "info-list")
+ ("From" . "no-?reply\\|deploy")
+ ("X-Mailer" . "MailChimp"))
+ bbdb-accept-message-alist
+ `(("To" . ,jao-mails-regexp)
+ ("Cc" . ,jao-mails-regexp)
+ ("BCc" . ,jao-mails-regexp))
+ bbdb-ignore-message-alist bbdb-auto-notes-ignore-messages)
+ :config
+ (add-hook 'message-setup-hook 'bbdb-mail-aliases)
+ ;; (add-hook 'bbdb-notice-mail-hook 'bbdb-auto-notes)
+ (add-hook 'bbdb-after-change-hook (lambda (arg) (bbdb-save)))
+ (require 'bbdb-anniv) ;; BBDB 3.x this gets birthdays in org agenda
+ ;; and diary - clever stuff
+ (add-hook 'diary-list-entries-hook 'bbdb-anniv-diary-entries)
+ (eval-after-load "gnus-sum"
+ '(progn
+ (define-key gnus-summary-mode-map ":" 'bbdb-mua-annotate-sender)
+ (define-key gnus-summary-mode-map ";" 'bbdb-mua-annotate-recipients))))
+
+ (require 'bbdb)
+ (bbdb-initialize 'gnus 'message 'pgp 'mail)
+ (bbdb-mua-auto-update-init 'gnus)
+ (setq bbdb-file (expand-file-name "bbdb" gnus-home-directory))
+ #+end_src
diff --git a/eww.org b/eww.org
index ee3096f..c668b04 100644
--- a/eww.org
+++ b/eww.org
@@ -25,6 +25,7 @@
(defun jao-eww-html-renderer (handle)
(let ((shr-use-colors nil)
(shr-use-fonts nil)
+ (mm-html-blocked-images nil)
(fill-column (min (window-width) 110)))
(mm-shr handle)))
(setq mm-text-html-renderer 'jao-eww-html-renderer)
diff --git a/gnus.org b/gnus.org
index 58ef9d3..44a5b8e 100644
--- a/gnus.org
+++ b/gnus.org
@@ -12,6 +12,28 @@
(defvar jao-gnus-use-nnml nil)
(defvar jao-gnus-use-maildirs nil)
#+end_src
+* Startup and kill
+ #+begin_src emacs-lisp
+ ;;;;; close gnus when closing emacs, but ask when exiting
+ (setq gnus-interactive-exit t)
+
+ (defun jao-gnus-started-hook ()
+ (add-hook 'before-kill-emacs-hook 'gnus-group-exit))
+
+ (add-hook 'gnus-started-hook 'jao-gnus-started-hook)
+
+ (defun jao-gnus-after-exiting-hook ()
+ (remove-hook 'before-kill-emacs-hook 'gnus-group-exit))
+
+ (add-hook 'gnus-after-exiting-gnus-hook 'jao-gnus-after-exiting-hook)
+
+ ;; define a wrapper around the save-buffers-kill-emacs
+ ;; to run the new hook before:
+ (defadvice save-buffers-kill-emacs
+ (before my-save-buffers-kill-emacs activate)
+ "Install hook when emacs exits before emacs asks to save this and that."
+ (run-hooks 'before-kill-emacs-hook))
+ #+end_src
* Looks
*** Verbosity
#+begin_src emacs-lisp
diff --git a/init.org b/init.org
index 8feef8b..f511dc5 100644
--- a/init.org
+++ b/init.org
@@ -1494,461 +1494,6 @@
;; for M-x biblio-lookup
;; (use-package biblio :ensure t)
#+END_SRC
-* Email
-*** message mode
-***** Customization
- #+begin_src emacs-lisp
- (defvar jao-mails "")
- (defvar jao-mails-regexp)
- (setq jao-mails-regexp (regexp-opt jao-mails))
-
- (require 'message)
- (setq message-send-mail-function 'message-send-mail-with-sendmail
- message-sendmail-envelope-from 'header
- message-sendmail-f-is-evil nil)
- (setq imap-store-password t)
- (setq password-cache-expiry nil)
- (setq message-generate-headers-first t)
- (setq message-forward-before-signature nil)
- (setq message-alternative-emails jao-mails-regexp)
- (setq message-dont-reply-to-names
- (format "%s\\|%s" jao-mails-regexp (regexp-opt '("noreply@" "@noreply"
- "no-reply@" "@no-reply"
- "notifications@github"))))
- (setq message-citation-line-format "On %a, %b %d %Y, %N wrote:\n")
- (setq message-citation-line-function 'message-insert-formatted-citation-line)
-
- (setq message-user-fqdn "gnus.jao.io")
-
- ;; writing messages
- (setq message-kill-buffer-on-exit t)
- (setq message-max-buffers 5)
- (setq message-insert-signature t)
- (setq message-from-style 'angles
- user-mail-address (car jao-mails)
- mail-host-address system-name
- message-syntax-checks '((sender . disabled))
- message-default-headers
- (concat
- "X-Attribution: jao\n"
- "X-Clacks-Overhead: GNU Terry Pratchett\n"
- "X-URL: <http://jao.io/>\n")
- message-hidden-headers
- '("^References:" "^Face:" "^X-Face:" "^X-Draft-From:")
- message-make-forward-subject-function 'message-forward-subject-fwd)
-
- (setq message-expand-name-standard-ui t)
- #+end_src
-***** Encryption
- #+BEGIN_SRC emacs-lisp
- (require 'gnutls)
- ;; avoiding bogus warning
- (setq gnutls-min-prime-bits nil)
- (setq gnus-buttonized-mime-types
- '("multipart/encrypted" "multipart/signed" "multipart/alternative"))
-
- (setq mm-verify-option 'always)
- (setq mm-decrypt-option 'always)
-
- (setq mm-sign-option 'guided)
- (setq mm-encrypt-option 'guided)
-
- (setq mml-secure-passphrase-cache-expiry (* 3600 24)
- password-cache-expiry (* 3600 24))
-
- (setq smime-CA-directory "/etc/ssl/certs/"
- smime-certificate-directory"/home/jao/.emacs.d/gnus/Mail/certs/")
-
- ;; Tells Gnus to inline the part
- (eval-after-load "mm-decode"
- '(add-to-list 'mm-inlined-types "application/pgp$"))
- ;; Tells Gnus how to display the part when it is requested
- (eval-after-load "mm-decode"
- '(add-to-list 'mm-inline-media-tests '("application/pgp$"
- mm-inline-text identity)))
- ;; Tell Gnus not to wait for a request, just display the thing
- ;; straight away.
- (eval-after-load "mm-decode"
- '(add-to-list 'mm-automatic-display "application/pgp$"))
- ;; But don't display the signatures, please.
- (eval-after-load "mm-decode"
- (quote (setq mm-automatic-display (remove "application/pgp-signature"
- mm-automatic-display))))
-
- ;; decide whether to encrypt or just sign outgoing messages
- (defvar jao-message-try-sign nil)
- (defun jao-message-maybe-sign ()
- (when (and jao-message-try-sign (y-or-n-p "Sign message? "))
- (if (y-or-n-p "Encrypt message? ")
- (let ((recipient (message-fetch-field "To")))
- (if (or (pgg-lookup-key recipient)
- (and (y-or-n-p (format "Fetch %s's key? " recipient))
- (pgg-fetch-key pgg-default-keyserver-address
- recipient)))
- (mml-secure-message-encrypt-pgp)
- (mml-secure-message-sign-pgp)))
- (mml-secure-message-sign-pgp))))
-
- ;; for ma gnus
- (eval-after-load "rfc2047"
- '(add-to-list 'rfc2047-header-encoding-alist
- '("User-Agent" . address-mime)))
-
- ;; (define-key message-mode-map [f7] 'mml-secure-message-sign-pgp)
- (define-key message-mode-map [f8] 'mml-secure-message-encrypt-pgp)
- #+END_SRC
-***** Attach image to message
- Use ~C-c C-p~ in message-mode.
-***** Check attachment
- #+BEGIN_SRC emacs-lisp
- (defvar jao-message-attachment-regexp "\\([Ww]e send\\|[Ii] send\\|attach\\)")
- (defun jao-message-check-attachment ()
- "Check if there is an attachment in the message if I claim it."
- (save-excursion
- (message-goto-body)
- (when (search-forward-regexp jao-message-attachment-regexp nil t nil)
- (message-goto-body)
- (unless (or (search-forward "<#part" nil t nil)
- (message-y-or-n-p
- "No attachment. Send the message? " nil nil))
- (error "No message sent")))))
- #+END_SRC
-***** Check Gcc
- #+BEGIN_SRC emacs-lisp
- (defun jao-message-check-gcc ()
- "Ask whether to keep a copy of message."
- (save-excursion
- (save-restriction
- (message-narrow-to-headers)
- (when (and (message-fetch-field "Gcc")
- (not (y-or-n-p "Archive? ")))
- (message-remove-header "Gcc")))))
-
- (defun jao-message-toggle-gcc ()
- "Insert or remove the \"Gcc\" header."
- (interactive)
- (save-excursion
- (save-restriction
- (message-narrow-to-headers)
- (if (message-fetch-field "Gcc")
- (message-remove-header "Gcc")
- (gnus-inews-insert-gcc)))))
-
- ;; (define-key message-mode-map [(f6)] 'jao-message-toggle-gcc)
- #+END_SRC
-***** Check recipient
- #+begin_src emacs-lisp
- (defun jao-message-check-recipient ()
- (save-excursion
- (save-restriction
- (message-narrow-to-headers)
- (when-let ((to (message-fetch-field "To")))
- (when (string-match-p jao-mails-regexp to)
- (unless (y-or-n-p "Message is addressed to yourself. Continue?")
- (error "Message not sent")))))))
- #+end_src
-***** Randomsig
- #+BEGIN_SRC emacs-lisp
- (when (require 'randomsig nil t)
- (define-key message-mode-map (kbd "C-c s") 'randomsig-replace-sig)
- (define-key message-mode-map (kbd "C-c S") 'randomsig-select-sig)
- (eval-after-load "gnus-sum"
- '(define-key gnus-summary-save-map "-" 'gnus/randomsig-summary-read-sig))
- (setq randomsig-dir (expand-file-name "~/etc/config/emacs"))
- (setq randomsig-files '("signatures.txt"))
- ;; or (setq randomsig-files (randomsig-search-sigfiles))
- ;; or (setq randomsig-files 'randomsig-search-sigfiles)
- (setq message-signature 'randomsig-signature)
- (setq randomsig-delimiter-pattern "^%$"
- randomsig-delimiter "%"))
- #+END_SRC
-***** Send mail hooks
- #+BEGIN_SRC emacs-lisp
- (dolist (h '(jao-message-check-gcc
- jao-message-check-attachment
- jao-message-check-recipient
- jao-message-maybe-sign))
- (add-hook 'message-send-hook h))
- #+END_SRC
-*** directories
- #+BEGIN_SRC emacs-lisp
- (setq gnus-home-directory "~/.emacs.d/gnus"
- gnus-directory gnus-home-directory)
-
- (defun jao-gnus-dir (dir)
- (expand-file-name dir gnus-home-directory))
-
- (setq smtpmail-queue-dir (jao-gnus-dir "Mail/queued-mail/"))
-
- (setq mail-source-directory (jao-gnus-dir "Mail/")
- message-auto-save-directory (jao-gnus-dir "Mail/drafts/")
- message-directory (jao-gnus-dir "Mail/"))
-
- (setq gnus-default-directory (expand-file-name "~")
- gnus-startup-file (jao-gnus-dir "newsrc")
- gnus-agent-directory (jao-gnus-dir "News/agent")
- gnus-home-score-file (jao-gnus-dir "scores")
- gnus-article-save-directory (jao-gnus-dir "saved/")
- nntp-authinfo-file (jao-gnus-dir "authinfo")
- nnmail-message-id-cache-file (jao-gnus-dir "nnmail-cache")
- nndraft-directory (jao-gnus-dir "drafts")
- nnrss-directory (jao-gnus-dir "rss"))
- #+END_SRC
-*** sendmail/smtp
- #+BEGIN_SRC emacs-lisp
- (require 'smtpmail)
-
- (defun jao-sendmail-gmail ()
- (setq smtpmail-auth-supported '(login cram-md5 plain))
- (setq smtpmail-smtp-server "smtp.gmail.com")
- (setq smtpmail-smtp-service 587))
-
- (defun jao-sendmail-local ()
- (setq smtpmail-auth-supported nil) ;; (cram-md5 plain login)
- (setq smtpmail-smtp-server "127.0.0.1")
- (setq smtpmail-smtp-service 25))
-
- ;; (jao-sendmail-gmail)
- (jao-sendmail-local)
- #+END_SRC
-*** Gnus
- The core of Gnus's configuration is tangled to the usual ~gnus.el~
- from [[./gnus.org][gnus.org]].
- #+BEGIN_SRC emacs-lisp
- (defalias 'jao-open-gnus-frame 'jao-afio--goto-gnus)
-
- (setq gnus-init-file (jao-maybe-tangle "gnus"))
-
- ;;;;; close gnus when closing emacs, but ask when exiting
- (setq gnus-interactive-exit t)
-
- (defun jao-gnus-started-hook ()
- "use that hook for its purpose "
- (add-hook 'before-kill-emacs-hook 'gnus-group-exit))
-
- (add-hook 'gnus-started-hook 'jao-gnus-started-hook)
-
- (defun jao-gnus-after-exiting-hook ()
- "how about removing this hook when exiting gnus in the conventional way?"
- (remove-hook 'before-kill-emacs-hook 'gnus-group-exit))
-
- (add-hook 'gnus-after-exiting-gnus-hook 'jao-gnus-after-exiting-hook)
-
- ;; define a wrapper around the save-buffers-kill-emacs
- ;; to run the new hook before:
- (defadvice save-buffers-kill-emacs
- (before my-save-buffers-kill-emacs activate)
- "Install hook when emacs exits before emacs asks to save this and that."
- (run-hooks 'before-kill-emacs-hook))
- #+END_SRC
-*** mbox listing
- #+begin_src emacs-lisp
- (defun jao-list-mailboxes (base)
- (let ((dir (expand-file-name base "~/var/mail")))
- (cl-remove-if (lambda (x)
- (member x '("." ".." "sent" "inbox" "trash")))
- (directory-files dir))))
- #+end_src
-*** notmuch
-***** Package configuration
- #+BEGIN_SRC emacs-lisp
- (defun jao-notmuch--mboxes-search (box)
- (mapcar (lambda (m)
- `(:name ,m
- :search-type tree
- :query ,(format "folder:%s/%s and tag:unread"
- box m)))
- (jao-list-mailboxes box)))
- (when (file-exists-p "~/var/mail")
- (use-package notmuch
- :ensure t
- :init
- (setq notmuch-fcc-dirs '(("jao@bigml.com" . "bigml/sent")
- (".*" . "jao/sent"))
- notmuch-tagging-keys
- '(("a" notmuch-archive-tags "Archive")
- ("u" notmuch-show-mark-read-tags "Mark read")
- ("f" ("+flagged") "Flag")
- ("s" ("+spam" "-inbox") "Mark as spam")
- ("d" ("+deleted" "-inbox") "Delete"))
- notmuch-saved-searches
- `((:name "jao" :key "j"
- :query "folder:jao/inbox"
- :search-type tree
- :count-query "folder:jao/inbox and tag:unread")
- (:name "bigml" :key "b"
- :count-query "folder:bigml/inbox and tag:unread"
- :search-type tree
- :query "folder:bigml/inbox")
- ,@(jao-notmuch--mboxes-search "jao")
- ,@(jao-notmuch--mboxes-search "bigml")
- ,@(jao-notmuch--mboxes-search "feeds")
- (:name "unread" :query "tag:unread" :key "u" :search-type tree)
- (:name "sent" :query "tag:sent" :key "t" :search-type tree)
- (:name "drafts" :query "tag:draft" :key "d" :search-type tree)
- (:name "all mail" :query "*" :count-query "tag:unread" :key "a"
- :search-type tree))
- notmuch-hello-sections
- '(notmuch-hello-insert-header
- notmuch-hello-insert-saved-searches
- notmuch-hello-insert-alltags
- notmuch-hello-insert-footer))
- :bind (:map notmuch-show-mode-map
- ("C-c C-c" . jao-notmuch-goto-message-in-gnus))))
- #+END_SRC
-***** notmuch -> gnus
- #+begin_src emacs-lisp
- (defun jao-notmuch-goto-message-in-gnus ()
- "Open a summary buffer containing the current notmuch article."
- (interactive)
- (let ((group (jao-maildir-file-to-group (notmuch-show-get-filename)))
- (message-id (replace-regexp-in-string "^id:"
- ""
- (notmuch-show-get-message-id))))
- (if (and group message-id)
- (org-gnus-follow-link group message-id)
- (message "Couldn't get relevant infos for switching to Gnus."))))
- #+end_src
-*** visual message fill column
- #+begin_src emacs-lisp
- (use-package visual-fill-column
- :ensure t
- :init
- (setq-default fringes-outside-margins nil
- visual-fill-column-width 80
- visual-fill-column-fringes-outside-margins nil)
- (setq gnus-treat-fill-long-lines nil)
- :config
- (setq split-window-preferred-function
- #'visual-fill-column-split-window-sensibly)
- :bind ((:map ctl-x-map ("M-f" . visual-fill-column-mode))))
-
- ;; (add-hook 'gnus-article-mode-hook #'visual-line-mode)
- ;; (add-hook 'gnus-article-mode-hook #'visual-fill-column-mode)
-
- ;; Name may be misleading, it does not set `fill-column' (which
- ;; is still used by M-q) in `message-mode', but enables
- ;; auto-filling on a given column.
- ;; (setq message-fill-column nil)
- ;; FIXME: There is no proper way to make fill commands to unfill.
- ;; (add-hook 'message-mode-hook
- ;; (lambda ()
- ;; (setq-local fill-column most-positive-fixnum)))
- ;; (when-require 'visual-fill-column
- ;; (add-hook 'message-mode-hook #'visual-fill-column-mode))
- #+end_src
-*** mail this buffer
- #+BEGIN_SRC emacs-lisp
- (defun jao-mail-this-file ()
- (interactive)
- (let ((file (buffer-file-name)))
- (compose-mail)
- (save-excursion
- (message-goto-subject)
- (insert (file-name-nondirectory file))
- (message-goto-body)
- (newline 2)
- (mml-attach-file file (mm-default-file-encoding file)))))
- #+END_SRC
-*** mailcap
- #+BEGIN_SRC emacs-lisp
- (require 'mailcap)
-
- (add-to-list 'mailcap-mime-extensions '(".JPEG" . "image/jpeg"))
- (add-to-list 'mailcap-mime-extensions '(".JPG" . "image/jpeg"))
-
- (let* ((apps (cdr (assoc "application" mailcap-mime-data)))
- (apps (cl-remove-if (lambda (x) (string= (car x) "pdf")) apps)))
- (setcdr (assoc "application" mailcap-mime-data) apps)
- (mailcap-parse-mailcaps nil t))
- #+END_SRC
-*** frm
- #+begin_src emacs-lisp
- (use-package jao-frm
- :init (setq jao-frm-mail-command 'jao-open-gnus-frame))
-
- (defun jao-frm--formatter (mbox n)
- (apply #'format "%s/%s: %s" `(,@(last (split-string mbox "/") 2) ,n)))
-
- (defun jao-frm--show ()
- (interactive)
- (jao-frm-show-mail-numbers #'jao-frm--formatter))
-
- (global-set-key [(f12)] 'jao-frm--show)
- (global-set-key [(f8)] 'jao-frm)
-
- #+end_src
-*** maildirs
- #+begin_src emacs-lisp
- (defvar jao-maildir-maildirs nil)
- (defvar jao-maildir-tracked-maildirs nil)
- (use-package jao-maildir
- :config
- (defun jao-maildir--ensure-counts ()
- (when gnus-newsgroup-name
- (when (string-match "^nnimap.*:\\(.+\\)" gnus-newsgroup-name)
- (let ((mbox (format "/home/jao/var/mail/%s"
- (match-string 1 gnus-newsgroup-name))))
- (jao-maildir-update-info-string mbox)))))
- (with-eval-after-load "gnus-sum"
- (add-hook 'gnus-exit-group-hook #'jao-maildir--ensure-counts)))
-
- (jao-maildir-setup jao-maildir-maildirs jao-maildir-tracked-maildirs -20)
- #+end_src
-*** BBDB
- #+BEGIN_SRC emacs-lisp
- (use-package bbdb
- :ensure t
- :init (setq bbdb-complete-name-allow-cycling t
- bbdb-completion-display-record nil
- bbdb-gui t
- bbdb-message-all-addresses t
- bbdb-complete-mail-allow-cycling t
- bbdb-north-american-phone-numbers-p nil
- bbdb-add-aka t
- bbdb-add-name 2
- bbdb-message-all-addresses t
- bbdb-mua-pop-up t ;; 'horiz
- bbdb-mua-pop-up-window-size 0.3
- bbdb-layout 'multi-line
- bbdb-mua-update-interactive-p '(query . create)
- bbdb-mua-auto-update-p 'bbdb-select-message
- bbdb-user-mail-address-re jao-mails-regexp
- bbdb-auto-notes-ignore-headers
- `(("From" . ,jao-mails-regexp)
- ("From" . ".*@.*github\.com.*")
- ("To" . ".*@.*github\.com.*")
- ("Reply-to" . ".*")
- ("References" . ".*"))
- bbdb-auto-notes-ignore-messages
- `(("To" . ".*@.*github\\.com.*")
- ("From" . ".*@.*github\\.com.*")
- ("From" . "info-list")
- ("From" . "no-?reply\\|deploy")
- ("X-Mailer" . "MailChimp"))
- bbdb-accept-message-alist
- `(("To" . ,jao-mails-regexp)
- ("Cc" . ,jao-mails-regexp)
- ("BCc" . ,jao-mails-regexp))
- bbdb-ignore-message-alist bbdb-auto-notes-ignore-messages)
- :config
- (add-hook 'message-setup-hook 'bbdb-mail-aliases)
- ;; (add-hook 'bbdb-notice-mail-hook 'bbdb-auto-notes)
- (add-hook 'bbdb-after-change-hook (lambda (arg) (bbdb-save)))
- (require 'bbdb-anniv) ;; BBDB 3.x this gets birthdays in org agenda
- ;; and diary - clever stuff
- (add-hook 'diary-list-entries-hook 'bbdb-anniv-diary-entries)
- (eval-after-load "gnus-sum"
- '(progn
- (define-key gnus-summary-mode-map ":" 'bbdb-mua-annotate-sender)
- (define-key gnus-summary-mode-map ";" 'bbdb-mua-annotate-recipients))))
-
- (require 'bbdb)
- (bbdb-initialize 'gnus 'message 'pgp 'mail)
- (bbdb-mua-auto-update-init 'gnus)
- (setq bbdb-file (expand-file-name "bbdb" gnus-home-directory))
-
- #+END_SRC
* Browsing
*** Variables
#+begin_src emacs-lisp
@@ -2169,6 +1714,12 @@
(jao-load-org "eww")
;; (jao-load-org "w3m")
#+end_src
+* Email
+ #+begin_src emacs-lisp
+ (setq jao-afio-mail-function 'notmuch
+ jao-afio-notmuch-in-web t)
+ (jao-load-org "email")
+ #+end_src
* PDFs
*** doc-view
#+begin_src emacs-lisp
diff --git a/lib/eos/jao-afio.el b/lib/eos/jao-afio.el
index 1410b80..e6f1e72 100644
--- a/lib/eos/jao-afio.el
+++ b/lib/eos/jao-afio.el
@@ -20,6 +20,12 @@
;;; Code:
+(defvar jao-open-doc-fun 'find-file)
+(defvar jao-afio-mail-function 'gnus)
+(defvar jao-afio-use-w3m nil)
+(defvar jao-afio-notmuch-in-web t)
+(defvar jao-afio-switch-hook nil)
+
(defvar jao-afio--configs '(?c ?w ?g ?p ?s))
(defvar jao-afio--current-config (car jao-afio--configs))
(defvar jao-afio--locker nil)
@@ -64,9 +70,6 @@
(jao-afio--goto-frame next)))
;;;###autoload
-(defvar jao-open-doc-fun 'find-file)
-
-;;;###autoload
(defun jao-afio-open-pdf-session ()
(interactive)
(dolist (doc (jao-doc-view-session))
@@ -86,10 +89,6 @@
(when (and (jao-doc-view-session) (y-or-n-p "Load saved session? "))
(jao-afio-open-pdf-session)))))
-(defvar jao-afio-mail-function 'gnus)
-(defvar jao-afio-use-w3m nil)
-(defvar jao-afio-notmuch-in-web t)
-
(declare w3m "w3m")
(declare w3m-alive-p "w3m")
(declare w3m-previous-buffer "w3m")
@@ -128,9 +127,11 @@
(interactive)
(if (or (null jao-afio-mail-function) (eq 'gnus jao-afio-mail-function))
(jao-afio-open-gnus)
- (delete-other-windows)
+ (jao-trisect)
+ (other-window 2)
+ (delete-window)
+ (other-window 1)
(funcall jao-afio-mail-function)
- (jao-bisect)
(other-window 1)
(find-file (expand-file-name "inbox.org" org-directory))
(split-window-below (/ (window-height) 3))
@@ -141,8 +142,6 @@
(switch-to-buffer "*Calendar*")
(other-window 1)))
-(defvar jao-afio-switch-hook nil)
-
(defun jao-afio--goto-frame (next &optional reset)
(when (or reset (not (eq next jao-afio--current-config)))
(let ((next-cfg (when (not reset) (get-register next))))
diff --git a/lib/themes/jao-themes.el b/lib/themes/jao-themes.el
index 6e55cc6..fbb4827 100644
--- a/lib/themes/jao-themes.el
+++ b/lib/themes/jao-themes.el
@@ -861,10 +861,24 @@
(nrepl-output-face (p f02))
(nrepl-prompt-face (p f00))
(nrepl-result-face nil)
+ (notmuch-crypto-decryption (~ success))
+ (notmuch-crypto-part-header (p f11))
+ (notmuch-crypto-signature-bad (p error) ul)
+ (notmuch-crypto-signature-good (~ success))
+ (notmuch-crypto-signature-unknown (p error) ul)
+ (notmuch-crypto-signature-good-key (~ success))
+ (notmuch-hello-logo-background nil)
+ (notmuch-message-summary-face (p f00))
(notmuch-search-count (p f00))
(notmuch-search-date (p f01))
+ (notmuch-search-flagged-face (p warn))
(notmuch-search-matching-authors (p f02))
- (notmuch-search-subject nil))
+ (notmuch-search-subject nil)
+ (notmuch-tag-face (p f00) it)
+ (notmuch-tag-unread (p warn))
+ (notmuch-tree-match-author-face (p f01))
+ (notmuch-tree-match-tag-face ul)
+ (notmuch-wash-cited-text (~ gnus-cite-1)))
`((orderless-match-face-0 ul)
(orderless-match-face-1 ul)
(orderless-match-face-2 ul)
diff --git a/readme.org b/readme.org
index 70bc090..72960cf 100644
--- a/readme.org
+++ b/readme.org
@@ -65,6 +65,7 @@
- [[./completion.org][completion.org]]: completion setup using company, consult and friends.
- [[./org.org][org.org]] org mode configuration.
- [[./blog.org][blog.org]]: blogging using org-static-blog.
+- [[./email.org][email.org]]: email handling outside of gnus.
- [[./gnus.org][gnus.org]]: tangled to gnus.el automatically by init.org, so that it's
ready for loading by Gnus.
- [[./w3m.org][w3m.org]]: browsing with emacs-w3m.