From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost (localhost [127.0.0.1]) by arlo.cworth.org (Postfix) with ESMTP id 253D76DE0C81 for ; Sun, 13 Nov 2016 06:09:05 -0800 (PST) X-Virus-Scanned: Debian amavisd-new at cworth.org X-Spam-Flag: NO X-Spam-Score: 0.06 X-Spam-Level: X-Spam-Status: No, score=0.06 tagged_above=-999 required=5 tests=[AWL=-0.070, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, SPF_PASS=-0.001] autolearn=disabled Received: from arlo.cworth.org ([127.0.0.1]) by localhost (arlo.cworth.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 86YOwTfEIQL9 for ; Sun, 13 Nov 2016 06:09:04 -0800 (PST) Received: from mail-wm0-f50.google.com (mail-wm0-f50.google.com [74.125.82.50]) by arlo.cworth.org (Postfix) with ESMTPS id DFA786DE0008 for ; Sun, 13 Nov 2016 06:08:59 -0800 (PST) Received: by mail-wm0-f50.google.com with SMTP id f82so56147523wmf.1 for ; Sun, 13 Nov 2016 06:08:59 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=k3vDR98S0qftJ/ZEYQj7jvTqW8OuFMFT2BGliOBjYy4=; b=xJPq1K6sWNtLleLFbfIpP1JxpoY3YjuoaBHs2EYjpqEi7JQiAIztW9tgfXt+mPwQWw 4xTWT7p9Asq1D3lETQOurpouipucd8Sp3wuR8JRdeOYIO4aIh8Oj/sFYC4VjszyQtLSY vzON4q+48kQOdwnQkZyttK9M3lDi566VjF8Du0T/RgmjEbo6xaxkm32lncLi8WzYyzyp +iYq5pQD7mmU37Smu5GeLCFD+LXjlg+/+KY31XXdieAvezkWtCoGsBYxZVmwBDwYtivb pB+N6Y2BsXkkoorFKCrmmNC+XfqLqTjAhdj7Np/q1z6kQNEPIqQ/3Bvx1d2HIDHYEI2e 1ZAg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=k3vDR98S0qftJ/ZEYQj7jvTqW8OuFMFT2BGliOBjYy4=; b=gY+5bhbl/bOaWtKtBIziUxT3PGcX8TTpQeoraLxiO+grdziKxebQ40bqmTtjYJP2Sh 6seizPBUiwsxZfH1S2ZgOTtok2CG1aFZhn4MvcJgMUB8OeRPURIB62n3APrgo9G8rIUQ 9li8n8iOjFUxsMM44kYRdRn4l1jLGaYkUYmXsBQKeEZSDWn8HxwFrwdL8X9DjkUT0ilW l+5GT+rPH13A9Uw7init7dexN2kbc3s9idI/JRSgg+i6gLqgMnD3s8KClKZdEZoGpt9n mj5ciZmGaAM5N3IWxdvTFams0F0FS99k5n7v3AswfV4QfyZ3mPuepVOUZDat3y/vtBIK voHg== X-Gm-Message-State: ABUngvc/+d9LWifci3MlihFxkSXmKmUvt4SLTcbWe3h+x+gZM+hjr9JPBYly96+ZWjaXcQ== X-Received: by 10.28.166.208 with SMTP id p199mr6191455wme.27.1479046138139; Sun, 13 Nov 2016 06:08:58 -0800 (PST) Received: from localhost (5751dfa2.skybroadband.com. [87.81.223.162]) by smtp.gmail.com with ESMTPSA id v10sm22690113wji.29.2016.11.13.06.08.57 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 13 Nov 2016 06:08:57 -0800 (PST) From: Mark Walters To: notmuch@notmuchmail.org Subject: [PATCH v7 4/4] emacs: resume messages Date: Sun, 13 Nov 2016 14:08:50 +0000 Message-Id: <1479046130-2716-5-git-send-email-markwalters1009@gmail.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1479046130-2716-1-git-send-email-markwalters1009@gmail.com> References: <1479046130-2716-1-git-send-email-markwalters1009@gmail.com> X-BeenThere: notmuch@notmuchmail.org X-Mailman-Version: 2.1.22 Precedence: list List-Id: "Use and development of the notmuch mail system." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 13 Nov 2016 14:09:05 -0000 From: David Bremner 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 <EXPECTED +From: Notmuch Test Suite +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