X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=blobdiff_plain;f=emacs%2Fnotmuch-draft.el;h=759e6c9e47a00d539c31219c6f3f0e007920cd20;hp=b8a5e67d5cd16bb8e3742805da3eb014ed17f5ba;hb=dfb1b8eb89e814f4bf6f6e62b700c72aa1b4659a;hpb=d820b97477f5705640aa8dcf9dcf311e37508def diff --git a/emacs/notmuch-draft.el b/emacs/notmuch-draft.el index b8a5e67d..759e6c9e 100644 --- a/emacs/notmuch-draft.el +++ b/emacs/notmuch-draft.el @@ -2,6 +2,7 @@ ;; ;; Copyright © Mark Walters ;; Copyright © David Bremner +;; Copyright © Leo Gaspard ;; ;; This file is part of Notmuch. ;; @@ -20,6 +21,7 @@ ;; ;; Authors: Mark Walters ;; David Bremner +;; Leo Gaspard ;;; Code: @@ -27,6 +29,7 @@ (require 'notmuch-tag) (declare-function notmuch-show-get-message-id "notmuch-show" (&optional bare)) +(declare-function notmuch-message-mode "notmuch-mua") (defgroup notmuch-draft nil "Saving and editing drafts in Notmuch." @@ -71,8 +74,23 @@ postponing and resuming a message." :type '(repeat string) :group 'notmuch-send) +(defcustom notmuch-draft-save-plaintext 'ask + "Should notmuch save/postpone in plaintext messages that seem +like they are intended to be sent encrypted +(i.e with an mml encryption tag in it)." + :type '(radio + (const :tag "Never" nil) + (const :tag "Ask every time" ask) + (const :tag "Always" t)) + :group 'notmuch-draft + :group 'notmuch-crypto) + +(defvar notmuch-draft-encryption-tag-regex + "<#\\(part encrypt\\|secure.*mode=.*encrypt>\\)" + "Regular expression matching mml tags indicating encryption of part or message.") + (defvar notmuch-draft-id nil - "Message-id of the most recent saved draft of this message") + "Message-id of the most recent saved draft of this message.") (make-variable-buffer-local 'notmuch-draft-id) (defun notmuch-draft--mark-deleted () @@ -103,6 +121,50 @@ 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 + (message-goto-body) + (re-search-forward notmuch-draft-encryption-tag-regex nil 't))) + +(defun notmuch-draft--query-encryption () + "Checks if we should save a message that should be encrypted. + +`notmuch-draft-save-plaintext' controls the behaviour." + (cl-case notmuch-draft-save-plaintext + ((ask) + (unless (yes-or-no-p + "(Customize `notmuch-draft-save-plaintext' to avoid this warning) +This message contains mml tags that suggest it is intended to be encrypted. +Really save and index an unencrypted copy? ") + (error "Save aborted"))) + ((nil) + (error "Refusing to save draft with encryption tags (see `%s')" + 'notmuch-draft-save-plaintext)) + ((t) + (ignore)))) + (defun notmuch-draft--make-message-id () ;; message-make-message-id gives the id inside a "<" ">" pair, ;; but notmuch doesn't want that form, so remove them. @@ -115,6 +177,8 @@ This saves the current message in the database with tags `notmuch-draft-tags` (in addition to any default tags applied to newly inserted messages)." (interactive) + (when (notmuch-draft--has-encryption-tag) + (notmuch-draft--query-encryption)) (let ((id (notmuch-draft--make-message-id))) (with-temporary-notmuch-message-buffer ;; We insert a Date header and a Message-ID header, the former @@ -130,14 +194,16 @@ applied to newly inserted messages)." (message-remove-header "Message-ID") (message-add-header (concat "Message-ID: <" id ">"))) (t - (message "You have customized emacs so Message-ID is not a deletable header, so not changing it") + (message "You have customized emacs so Message-ID is not a %s" + "deletable header, so not changing it") (setq id nil))) (cond ((member 'Date message-deletable-headers) (message-remove-header "Date") (message-add-header (concat "Date: " (message-make-date)))) (t - (message "You have customized emacs so Date is not a deletable header, so not changing it"))) + (message "You have customized emacs so Date is not a deletable %s" + "header, so not changing it"))) (message-add-header "X-Notmuch-Emacs-Draft: True") (notmuch-draft-quote-some-mml) (notmuch-maildir-setup-message-for-saving) @@ -159,6 +225,47 @@ 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: edit as new? ")) + (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")) + (unless draft (notmuch-fcc-header-setup)) + ;; 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 (and draft id))))) + + (add-hook 'message-send-hook 'notmuch-draft--mark-deleted)