diff options
author | Kyle Meyer <kyle@kyleam.com> | 2020-06-06 21:11:45 -0400 |
---|---|---|
committer | Kyle Meyer <kyle@kyleam.com> | 2020-06-07 01:48:33 -0400 |
commit | 7c75007c197620b11ec8c4417e05e12bd8f0fe63 (patch) | |
tree | 86cedbd57be0740ec78fc11a48856aac3242e546 | |
parent | df480c9d52416c6a8cbacd7ed7abbdacc43be4a9 (diff) | |
download | piem-7c75007c197620b11ec8c4417e05e12bd8f0fe63.tar.gz |
Add command for downloading/injecting t.mbox.gz into a Maildir
I follow some public-inbox-archived projects over NNTP with Gnus.
However, once I get involved in a thread, I usually want the entire
thread in my local mail/Notmuch database (or at least the message I'm
replying to).
With this new command, if I see a message in Gnus (or EWW or Elfeed)
that I want to reply to, I can get it into Notmuch, assuming
piem-inboxes and piem-maildir-directory are wired up. And through
piem-after-mail-injection-functions, it's possible to jump straight to
the message with notmuch-show.
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | piem.el | 98 |
2 files changed, 99 insertions, 1 deletions
@@ -25,7 +25,7 @@ piem-eww.elc: piem-eww.el piem.elc piem-gnus.elc: piem-gnus.el piem.elc piem-maildir.elc: piem-maildir.el piem-notmuch.elc: piem-notmuch.el piem.elc -piem.elc: piem.el +piem.elc: piem.el piem-maildir.elc .SUFFIXES: .el .elc .texi .info @@ -26,9 +26,12 @@ ;;; Code: +(require 'cl-lib) (require 'message) +(require 'piem-maildir) (require 'rfc2047) (require 'subr-x) +(require 'url) ;;;; Options @@ -163,6 +166,17 @@ the following information about the patch series: The reported base commit of the patch, if any." :type 'function) +(defcustom piem-maildir-directory nil + "Inject public-inbox threads into this directory. +If non-nil, this must be an existing Maildir directory." + :type 'string) + +(defcustom piem-after-mail-injection-functions nil + "Functions called after writing messages to `piem-maildir-directory'. +Functions should accept one argument, the message ID given to +`piem-inject-thread-into-maildir'." + :type 'hook) + ;;;; Subprocess handling @@ -296,6 +310,90 @@ Callers are responsible for killing the buffer." nil)))) +;;;; Maildir injection + +(defun piem--write-mbox-to-maildir () + (let ((n-messages 0)) + (while (and (not (eobp)) + (search-forward "From mboxrd@z" nil t)) + (let* ((beg (line-beginning-position 2)) + (end (or (and (search-forward "From mboxrd@z" nil t) + (progn (goto-char (line-beginning-position 0)) + (point-marker))) + (point-max-marker))) + (basename (piem-maildir-make-uniq-maildir-id)) + (tmpfile (concat piem-maildir-directory "/tmp/" basename))) + (goto-char beg) + ;; TODO: Consider supporting a caller-specified predicate that + ;; could be used, for example, to skip messages already in + ;; their local mail. + (let ((case-fold-search nil)) + (while (re-search-forward + (rx line-start ">" (group (zero-or-more ">") "From ")) + end t) + (replace-match "\\1" t))) + (write-region beg end tmpfile nil nil nil 'excl) + (piem-maildir-move-tmp-to-new piem-maildir-directory + basename) + (delete-file tmpfile) + (cl-incf n-messages) + (goto-char end))) + n-messages)) + +(defun piem--inject-thread-callback (status mid message-only) + (let ((buffer (current-buffer))) + (unwind-protect + (let ((error-status (plist-get status :error))) + (if error-status + (signal (car error-status) (cdr error-status)) + (search-forward "\n\n") + (delete-region (point-min) (point)) + (unless message-only + (unless (= 0 (call-process-region nil nil "gunzip" nil t)) + (error "Decompressing t.mbox.gz failed")) + (delete-region (point) (point-max))) + (goto-char (point-min)) + (let ((message-count (piem--write-mbox-to-maildir))) + (message "%d message(s) for %s moved to %s" + message-count mid piem-maildir-directory)) + (run-hook-with-args 'piem-after-mail-injection-functions mid))) + (and (buffer-live-p buffer) + (kill-buffer buffer))))) + +(defvar piem--has-gunzip) + +;;;###autoload +(defun piem-inject-thread-into-maildir (mid &optional message-only) + "Inject thread containing MID into `piem-maildir-directory'. + +If prefix argument MESSAGE-ONLY is non-nil, inject just the +message for MID, not the entire thread. + +This function depends on :url being configured for entries in +`piem-inboxes'." + (interactive + (list (or (piem-mid) + (user-error "No message ID found for the current buffer")) + current-prefix-arg)) + (unless (or message-only (boundp 'piem--has-gunzip)) + (setq piem--has-gunzip (executable-find "gunzip"))) + (cond + ((not piem-maildir-directory) + (user-error "`piem-maildir-directory' is not configured")) + ((not (piem-maildir-dir-is-maildir-p piem-maildir-directory)) + (user-error + "`piem-maildir-directory' does not look like a Maildir directory")) + ((not (or message-only piem--has-gunzip)) + (user-error "gunzip executable not found"))) + (url-retrieve (concat (or (piem-inbox-url) + (user-error + "Could not find inbox URL for current buffer")) + mid + (if message-only "/raw" "/t.mbox.gz")) + #'piem--inject-thread-callback + (list mid message-only))) + + ;;;; Patch handling (defun piem-extract-mbox-info (&optional buffer) |