[PATCH v7 4/4] emacs: resume messages
Mark Walters
markwalters1009 at gmail.com
Sun Nov 13 06:08:50 PST 2016
From: David Bremner <david at tethera.net>
Provide functionality to resume editing a message previously saved with
notmuch-draft-save, including decoding the X-Notmuch-Emacs-Secure
header.
Resume gets the raw file from notmuch and using the emacs function
mime-to-mml reconstructs the message (including attachments).
'e' is bound to resume a draft from show or tree mode.
---
emacs/notmuch-draft.el | 62 ++++++++++++++++++++++++++++++++++++++++++++++++
emacs/notmuch-show.el | 8 +++++++
emacs/notmuch-tree.el | 10 ++++++++
test/T630-emacs-draft.sh | 15 ++++++++++++
4 files changed, 95 insertions(+)
diff --git a/emacs/notmuch-draft.el b/emacs/notmuch-draft.el
index 1fb049a..496e11f 100644
--- a/emacs/notmuch-draft.el
+++ b/emacs/notmuch-draft.el
@@ -25,6 +25,7 @@
(require 'notmuch-maildir-fcc)
(require 'notmuch-tag)
+(require 'notmuch-mua)
(declare-function notmuch-show-get-message-id "notmuch-show" (&optional bare))
@@ -118,6 +119,27 @@ Used when a new version is saved, or the message is sent."
(goto-char (+ (match-beginning 0) 2))
(insert "!"))))))
+(defun notmuch-draft-unquote-some-mml ()
+ "Unquote the mml tags in `notmuch-draft-quoted-tags`."
+ (save-excursion
+ (when notmuch-draft-quoted-tags
+ (let ((re (concat "<#!+/?\\("
+ (mapconcat 'regexp-quote notmuch-draft-quoted-tags "\\|")
+ "\\)")))
+ (message-goto-body)
+ (while (re-search-forward re nil t)
+ ;; Remove one ! from after the #.
+ (goto-char (+ (match-beginning 0) 2))
+ (delete-char 1))))
+ (let (secure-tag)
+ (save-restriction
+ (message-narrow-to-headers)
+ (setq secure-tag (message-fetch-field "X-Notmuch-Emacs-Secure" 't))
+ (message-remove-header "X-Notmuch-Emacs-Secure"))
+ (message-goto-body)
+ (when secure-tag
+ (insert secure-tag "\n")))))
+
(defun notmuch-draft--has-encryption-tag ()
"Returns t if there is an mml secure tag."
(save-excursion
@@ -198,6 +220,46 @@ applied to newly inserted messages)."
(notmuch-draft-save)
(kill-buffer))
+(defun notmuch-draft-resume (id)
+ "Resume editing of message with id ID."
+ (let* ((tags (process-lines notmuch-command "search" "--output=tags"
+ "--exclude=false" id))
+ (draft (equal tags (notmuch-update-tags tags notmuch-draft-tags))))
+ (when (or draft
+ (yes-or-no-p "Message does not appear to be a draft: really resume? "))
+ (switch-to-buffer (get-buffer-create (concat "*notmuch-draft-" id "*")))
+ (setq buffer-read-only nil)
+ (erase-buffer)
+ (let ((coding-system-for-read 'no-conversion))
+ (call-process notmuch-command nil t nil "show" "--format=raw" id))
+ (mime-to-mml)
+ (goto-char (point-min))
+ (when (re-search-forward "^$" nil t)
+ (replace-match mail-header-separator t t))
+ ;; Remove the Date and Message-ID headers (unless the user has
+ ;; explicitly customized emacs to tell us not to) as they will
+ ;; be replaced when the message is sent.
+ (save-restriction
+ (message-narrow-to-headers)
+ (when (member 'Message-ID message-deletable-headers)
+ (message-remove-header "Message-ID"))
+ (when (member 'Date message-deletable-headers)
+ (message-remove-header "Date"))
+ ;; The X-Notmuch-Emacs-Draft header is a more reliable
+ ;; indication of whether the message really is a draft.
+ (setq draft (> (message-remove-header "X-Notmuch-Emacs-Draft") 0)))
+ ;; If the message is not a draft we should not unquote any mml.
+ (when draft
+ (notmuch-draft-unquote-some-mml))
+ (notmuch-message-mode)
+ (message-goto-body)
+ (set-buffer-modified-p nil)
+ ;; If the resumed message was a draft then set the draft
+ ;; message-id so that we can delete the current saved draft if the
+ ;; message is resaved or sent.
+ (setq notmuch-draft-id (when draft id)))))
+
+
(add-hook 'message-send-hook 'notmuch-draft--mark-deleted)
diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el
index fcf7e6e..364004b 100644
--- a/emacs/notmuch-show.el
+++ b/emacs/notmuch-show.el
@@ -38,6 +38,7 @@
(require 'notmuch-mua)
(require 'notmuch-crypto)
(require 'notmuch-print)
+(require 'notmuch-draft)
(declare-function notmuch-call-notmuch-process "notmuch" (&rest args))
(declare-function notmuch-search-next-thread "notmuch" nil)
@@ -50,6 +51,7 @@
(&optional query query-context target buffer-name open-target))
(declare-function notmuch-tree-get-message-properties "notmuch-tree" nil)
(declare-function notmuch-read-query "notmuch" (prompt))
+(declare-function notmuch-draft-resume "notmuch-draft" (id))
(defcustom notmuch-message-headers '("Subject" "To" "Cc" "Date")
"Headers that should be shown in a message, in this order.
@@ -1445,6 +1447,7 @@ reset based on the original query."
(define-key map "|" 'notmuch-show-pipe-message)
(define-key map "w" 'notmuch-show-save-attachments)
(define-key map "V" 'notmuch-show-view-raw-message)
+ (define-key map "e" 'notmuch-show-resume-message)
(define-key map "c" 'notmuch-show-stash-map)
(define-key map "h" 'notmuch-show-toggle-visibility-headers)
(define-key map "k" 'notmuch-tag-jump)
@@ -1982,6 +1985,11 @@ to show, nil otherwise."
(setq buffer-read-only t)
(view-buffer buf 'kill-buffer-if-not-modified)))
+(defun notmuch-show-resume-message ()
+ "Resume EDITING the current draft message."
+ (interactive)
+ (notmuch-draft-resume (notmuch-show-get-message-id)))
+
(put 'notmuch-show-pipe-message 'notmuch-doc
"Pipe the contents of the current message to a command.")
(put 'notmuch-show-pipe-message 'notmuch-prefix-doc
diff --git a/emacs/notmuch-tree.el b/emacs/notmuch-tree.el
index 8398eb1..7bebdba 100644
--- a/emacs/notmuch-tree.el
+++ b/emacs/notmuch-tree.el
@@ -293,6 +293,7 @@ FUNC."
(define-key map "*" 'notmuch-tree-tag-thread)
(define-key map " " 'notmuch-tree-scroll-or-next)
(define-key map (kbd "DEL") 'notmuch-tree-scroll-message-window-back)
+ (define-key map "e" 'notmuch-tree-resume-message)
map))
(fset 'notmuch-tree-mode-map notmuch-tree-mode-map)
@@ -405,6 +406,15 @@ NOT change the database."
(list (notmuch-read-tag-changes (notmuch-tree-get-tags) "Tag message" "-")))
(notmuch-tree-tag tag-changes))
+(defun notmuch-tree-resume-message ()
+ "Resume EDITING the current draft message."
+ (interactive)
+ (notmuch-tree-close-message-window)
+ (let ((id (notmuch-tree-get-message-id)))
+ (if id
+ (notmuch-draft-resume id)
+ (message "No message to resume!"))))
+
;; The next two functions close the message window before calling
;; notmuch-search or notmuch-tree but they do so after the user has
;; entered the query (in case the user was basing the query on
diff --git a/test/T630-emacs-draft.sh b/test/T630-emacs-draft.sh
index 689ccfb..46fc356 100755
--- a/test/T630-emacs-draft.sh
+++ b/test/T630-emacs-draft.sh
@@ -52,4 +52,19 @@ count2=$(notmuch count subject:draft-test-0004)
test_expect_equal "$count1,$count2" "3,0"
+test_begin_subtest "Resuming a signed draft"
+
+test_emacs '(notmuch-show "subject:draft-test-0003")
+ (notmuch-show-resume-message)
+ (test-output)'
+notmuch_dir_sanitize OUTPUT > OUTPUT.clean
+cat <<EOF | notmuch_dir_sanitize >EXPECTED
+From: Notmuch Test Suite <test_suite at notmuchmail.org>
+To:
+Subject: draft-test-0003
+Fcc: MAIL_DIR/sent
+--text follows this line--
+<#secure method=pgpmime mode=sign>
+EOF
+test_expect_equal_file EXPECTED OUTPUT.clean
test_done
--
2.1.4
More information about the notmuch
mailing list