From dc0919c9125b323306a59751a63181b67aee5b32 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Sat, 14 Jan 2012 16:46:17 +0200 Subject: emacs: add support for replying just to the sender Provide reply to sender counterparts to the search and show reply functions. Add key binding 'R' to reply to sender, while keeping 'r' as reply to all, both in search and show views. Signed-off-by: Jani Nikula --- emacs/notmuch-mua.el | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'emacs/notmuch-mua.el') diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el index 32e2e30..d8ab822 100644 --- a/emacs/notmuch-mua.el +++ b/emacs/notmuch-mua.el @@ -71,12 +71,15 @@ list." (push header message-hidden-headers))) notmuch-mua-hidden-headers)) -(defun notmuch-mua-reply (query-string &optional sender) +(defun notmuch-mua-reply (query-string &optional sender reply-all) (let (headers body (args '("reply"))) (if notmuch-show-process-crypto (setq args (append args '("--decrypt")))) + (if reply-all + (setq args (append args '("--reply-to=all"))) + (setq args (append args '("--reply-to=sender")))) (setq args (append args (list query-string))) ;; This make assumptions about the output of `notmuch reply', but ;; really only that the headers come first followed by a blank @@ -218,13 +221,13 @@ the From: address first." (notmuch-mua-forward-message)) (notmuch-mua-forward-message))) -(defun notmuch-mua-new-reply (query-string &optional prompt-for-sender) +(defun notmuch-mua-new-reply (query-string &optional prompt-for-sender reply-all) "Invoke the notmuch reply window." (interactive "P") (let ((sender (when prompt-for-sender (notmuch-mua-prompt-for-sender)))) - (notmuch-mua-reply query-string sender))) + (notmuch-mua-reply query-string sender reply-all))) (defun notmuch-mua-send-and-exit (&optional arg) (interactive "P") -- cgit v1.2.3 From 643ce61c1babf6e73ca7e03fb907282e7ee3b176 Mon Sep 17 00:00:00 2001 From: Pieter Praet Date: Mon, 16 Jan 2012 11:38:33 +0100 Subject: emacs: logically group def{custom,face}s To allow for expansion whilst keeping everything tidy and organized, move all defcustom/defface variables to the following subgroups, defined in notmuch-lib.el: - Hello - Search - Show - Send - Crypto - Hooks - External Commands - Appearance As an added benefit, defcustom keyword args are now consistently ordered as they appear @ defcustom's docstring (OCD much?). Proper defgroup docstrings and various other improvements by courtesy of Austin Clements. --- emacs/notmuch-address.el | 3 ++- emacs/notmuch-crypto.el | 22 ++++++++++++++-------- emacs/notmuch-hello.el | 33 ++++++++++++++++++--------------- emacs/notmuch-lib.el | 37 +++++++++++++++++++++++++++++++++++-- emacs/notmuch-maildir-fcc.el | 6 +++--- emacs/notmuch-message.el | 2 +- emacs/notmuch-mua.el | 21 +++++++++++---------- emacs/notmuch-show.el | 34 ++++++++++++++++++---------------- emacs/notmuch.el | 35 ++++++++++++++++++++++------------- 9 files changed, 124 insertions(+), 69 deletions(-) (limited to 'emacs/notmuch-mua.el') diff --git a/emacs/notmuch-address.el b/emacs/notmuch-address.el index 8eba7a0..2e8b840 100644 --- a/emacs/notmuch-address.el +++ b/emacs/notmuch-address.el @@ -28,7 +28,8 @@ single argument and output a list of possible matches, one per line." :type 'string - :group 'notmuch) + :group 'notmuch-send + :group 'notmuch-external) (defvar notmuch-address-message-alist-member '("^\\(Resent-\\)?\\(To\\|B?Cc\\|Reply-To\\|From\\|Mail-Followup-To\\|Mail-Copies-To\\):" diff --git a/emacs/notmuch-crypto.el b/emacs/notmuch-crypto.el index ac30098..80ac350 100644 --- a/emacs/notmuch-crypto.el +++ b/emacs/notmuch-crypto.el @@ -34,38 +34,44 @@ The effect of setting this variable can be seen temporarily by providing a prefix when viewing a signed or encrypted message, or by providing a prefix when reloading the message in notmuch-show mode." - :group 'notmuch - :type 'boolean) + :type 'boolean + :group 'notmuch-crypto) (defface notmuch-crypto-part-header '((t (:foreground "blue"))) "Face used for crypto parts headers." - :group 'notmuch) + :group 'notmuch-crypto + :group 'notmuch-faces) (defface notmuch-crypto-signature-good '((t (:background "green" :foreground "black"))) "Face used for good signatures." - :group 'notmuch) + :group 'notmuch-crypto + :group 'notmuch-faces) (defface notmuch-crypto-signature-good-key '((t (:background "orange" :foreground "black"))) "Face used for good signatures." - :group 'notmuch) + :group 'notmuch-crypto + :group 'notmuch-faces) (defface notmuch-crypto-signature-bad '((t (:background "red" :foreground "black"))) "Face used for bad signatures." - :group 'notmuch) + :group 'notmuch-crypto + :group 'notmuch-faces) (defface notmuch-crypto-signature-unknown '((t (:background "red" :foreground "black"))) "Face used for signatures of unknown status." - :group 'notmuch) + :group 'notmuch-crypto + :group 'notmuch-faces) (defface notmuch-crypto-decryption '((t (:background "purple" :foreground "black"))) "Face used for encryption/decryption status messages." - :group 'notmuch) + :group 'notmuch-crypto + :group 'notmuch-faces) (define-button-type 'notmuch-crypto-status-button-type 'action (lambda (button) (message (button-get button 'help-echo))) diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el index 02017ce..bff95ac 100644 --- a/emacs/notmuch-hello.el +++ b/emacs/notmuch-hello.el @@ -35,12 +35,12 @@ (defcustom notmuch-recent-searches-max 10 "The number of recent searches to store and display." :type 'integer - :group 'notmuch) + :group 'notmuch-hello) (defcustom notmuch-show-empty-saved-searches nil "Should saved searches with no messages be listed?" :type 'boolean - :group 'notmuch) + :group 'notmuch-hello) (defun notmuch-sort-saved-searches (alist) "Generate an alphabetically sorted saved searches alist." @@ -60,7 +60,7 @@ alist to be used." (const :tag "Sort alphabetically" notmuch-sort-saved-searches) (function :tag "Custom sort function" :value notmuch-sort-saved-searches)) - :group 'notmuch) + :group 'notmuch-hello) (defvar notmuch-hello-indent 4 "How much to indent non-headers.") @@ -68,12 +68,12 @@ alist to be used." (defcustom notmuch-show-logo t "Should the notmuch logo be shown?" :type 'boolean - :group 'notmuch) + :group 'notmuch-hello) (defcustom notmuch-show-all-tags-list nil "Should all tags be shown in the notmuch-hello view?" :type 'boolean - :group 'notmuch) + :group 'notmuch-hello) (defcustom notmuch-hello-tag-list-make-query nil "Function or string to generate queries for the all tags list. @@ -89,12 +89,12 @@ should return a filter for that tag, or nil to hide the tag." (string :tag "Custom filter" :value "tag:unread") (function :tag "Custom filter function")) - :group 'notmuch) + :group 'notmuch-hello) (defcustom notmuch-hello-hide-tags nil "List of tags to be hidden in the \"all tags\"-section." :type '(repeat string) - :group 'notmuch) + :group 'notmuch-hello) (defface notmuch-hello-logo-background '((((class color) @@ -104,7 +104,8 @@ should return a filter for that tag, or nil to hide the tag." (background light)) (:background "white"))) "Background colour for the notmuch logo." - :group 'notmuch) + :group 'notmuch-hello + :group 'notmuch-faces) (defcustom notmuch-column-control t "Controls the number of columns for saved searches/tags in notmuch view. @@ -126,11 +127,11 @@ So: 30. - if you don't want to worry about all of this nonsense, leave this set to `t'." - :group 'notmuch :type '(choice (const :tag "Automatically calculated" t) (integer :tag "Number of characters") - (float :tag "Fraction of window"))) + (float :tag "Fraction of window")) + :group 'notmuch-hello) (defcustom notmuch-hello-thousands-separator " " "The string used as a thousands separator. @@ -138,18 +139,20 @@ So: Typically \",\" in the US and UK and \".\" or \" \" in Europe. The latter is recommended in the SI/ISO 31-0 standard and by the International Bureau of Weights and Measures." - :group 'notmuch - :type 'string) + :type 'string + :group 'notmuch-hello) (defcustom notmuch-hello-mode-hook nil "Functions called after entering `notmuch-hello-mode'." - :group 'notmuch - :type 'hook) + :type 'hook + :group 'notmuch-hello + :group 'notmuch-hooks) (defcustom notmuch-hello-refresh-hook nil "Functions called after updating a `notmuch-hello' buffer." :type 'hook - :group 'notmuch) + :group 'notmuch-hello + :group 'notmuch-hooks) (defvar notmuch-hello-url "http://notmuchmail.org" "The `notmuch' web site.") diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el index 0f856bf..9242537 100644 --- a/emacs/notmuch-lib.el +++ b/emacs/notmuch-lib.el @@ -28,17 +28,50 @@ "Notmuch mail reader for Emacs." :group 'mail) +(defgroup notmuch-hello nil + "Overview of saved searches, tags, etc." + :group 'notmuch) + +(defgroup notmuch-search nil + "Searching and sorting mail." + :group 'notmuch) + +(defgroup notmuch-show nil + "Showing messages and threads." + :group 'notmuch) + +(defgroup notmuch-send nil + "Sending messages from Notmuch." + :group 'notmuch + :group 'message) + +(defgroup notmuch-crypto nil + "Processing and display of cryptographic MIME parts." + :group 'notmuch) + +(defgroup notmuch-hooks nil + "Running custom code on well-defined occasions." + :group 'notmuch) + +(defgroup notmuch-external nil + "Running external commands from within Notmuch." + :group 'notmuch) + +(defgroup notmuch-faces nil + "Graphical attributes for displaying text" + :group 'notmuch) + (defcustom notmuch-search-oldest-first t "Show the oldest mail first when searching." :type 'boolean - :group 'notmuch) + :group 'notmuch-search) ;; (defcustom notmuch-saved-searches nil "A list of saved searches to display." :type '(alist :key-type string :value-type string) - :group 'notmuch) + :group 'notmuch-hello) (defvar notmuch-folders nil "Deprecated name for what is now known as `notmuch-saved-searches'.") diff --git a/emacs/notmuch-maildir-fcc.el b/emacs/notmuch-maildir-fcc.el index 6fbf82d..dcfbc4b 100644 --- a/emacs/notmuch-maildir-fcc.el +++ b/emacs/notmuch-maildir-fcc.el @@ -51,13 +51,13 @@ the database.path option in the notmuch configuration file). You will be prompted to create the directory if it does not exist yet when sending a mail." - :require 'notmuch-fcc-initialization - :group 'notmuch :type '(choice (const :tag "No FCC header" nil) (string :tag "A single folder") (repeat :tag "A folder based on the From header" - (cons regexp (string :tag "Folder"))))) + (cons regexp (string :tag "Folder")))) + :require 'notmuch-fcc-initialization + :group 'notmuch-send) (defun notmuch-fcc-initialization () "If notmuch-fcc-directories is set, diff --git a/emacs/notmuch-message.el b/emacs/notmuch-message.el index 08e5b17..264a5b9 100644 --- a/emacs/notmuch-message.el +++ b/emacs/notmuch-message.el @@ -31,7 +31,7 @@ For example, if you wanted to add a \"replied\" tag and remove the \"inbox\" and \"todo\", you would set (\"replied\" \"-inbox\" \"-todo\"\)" :type 'list - :group 'notmuch) + :group 'notmuch-send) (defun notmuch-message-mark-replied () ;; get the in-reply-to header and parse it for the message id. diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el index d8ab822..023645e 100644 --- a/emacs/notmuch-mua.el +++ b/emacs/notmuch-mua.el @@ -28,25 +28,26 @@ (defcustom notmuch-mua-send-hook '(notmuch-mua-message-send-hook) "Hook run before sending messages." - :group 'notmuch - :type 'hook) + :type 'hook + :group 'notmuch-send + :group 'notmuch-hooks) (defcustom notmuch-mua-user-agent-function 'notmuch-mua-user-agent-full "Function used to generate a `User-Agent:' string. If this is `nil' then no `User-Agent:' will be generated." - :group 'notmuch :type '(choice (const :tag "No user agent string" nil) (const :tag "Full" notmuch-mua-user-agent-full) (const :tag "Notmuch" notmuch-mua-user-agent-notmuch) (const :tag "Emacs" notmuch-mua-user-agent-emacs) (function :tag "Custom user agent function" - :value notmuch-mua-user-agent-full))) + :value notmuch-mua-user-agent-full)) + :group 'notmuch-send) (defcustom notmuch-mua-hidden-headers '("^User-Agent:") "Headers that are added to the `message-mode' hidden headers list." - :group 'notmuch - :type '(repeat string)) + :type '(repeat string) + :group 'notmuch-send) ;; @@ -157,16 +158,16 @@ OTHER-ARGS are passed through to `message-mail'." If this variable is left unset, then a list will be constructed from the name and addresses configured in the notmuch configuration file." - :group 'notmuch - :type '(repeat string)) + :type '(repeat string) + :group 'notmuch-send) (defcustom notmuch-always-prompt-for-sender nil "Always prompt for the From: address when composing or forwarding a message. This is not taken into account when replying to a message, because in that case the From: header is already filled in by notmuch." - :group 'notmuch - :type 'boolean) + :type 'boolean + :group 'notmuch-send) (defvar notmuch-mua-sender-history nil) diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el index 03c1f6b..fc13462 100644 --- a/emacs/notmuch-show.el +++ b/emacs/notmuch-show.el @@ -47,8 +47,8 @@ For an open message, all of these headers will be made visible according to `notmuch-message-headers-visible' or can be toggled with `notmuch-show-toggle-headers'. For a closed message, only the first header in the list will be visible." - :group 'notmuch - :type '(repeat string)) + :type '(repeat string) + :group 'notmuch-show) (defcustom notmuch-message-headers-visible t "Should the headers be visible by default? @@ -58,13 +58,13 @@ If this value is non-nil, then all of the headers defined in of each message. Otherwise, these headers will be hidden and `notmuch-show-toggle-headers' can be used to make the visible for any given message." - :group 'notmuch - :type 'boolean) + :type 'boolean + :group 'notmuch-show) (defcustom notmuch-show-relative-dates t "Display relative dates in the message summary line." - :group 'notmuch - :type 'boolean) + :type 'boolean + :group 'notmuch-show) (defvar notmuch-show-markup-headers-hook '(notmuch-show-colour-headers) "A list of functions called to decorate the headers listed in @@ -72,27 +72,29 @@ any given message." (defcustom notmuch-show-hook nil "Functions called after populating a `notmuch-show' buffer." - :group 'notmuch - :type 'hook) + :type 'hook + :group 'notmuch-show + :group 'notmuch-hooks) (defcustom notmuch-show-insert-text/plain-hook '(notmuch-wash-wrap-long-lines notmuch-wash-tidy-citations notmuch-wash-elide-blank-lines notmuch-wash-excerpt-citations) "Functions used to improve the display of text/plain parts." - :group 'notmuch :type 'hook :options '(notmuch-wash-convert-inline-patch-to-part notmuch-wash-wrap-long-lines notmuch-wash-tidy-citations notmuch-wash-elide-blank-lines - notmuch-wash-excerpt-citations)) + notmuch-wash-excerpt-citations) + :group 'notmuch-show + :group 'notmuch-hooks) ;; Mostly useful for debugging. (defcustom notmuch-show-all-multipart/alternative-parts t "Should all parts of multipart/alternative parts be shown?" - :group 'notmuch - :type 'boolean) + :type 'boolean + :group 'notmuch-show) (defcustom notmuch-show-indent-messages-width 1 "Width of message indentation in threads. @@ -101,14 +103,14 @@ Messages are shown indented according to their depth in a thread. This variable determines the width of this indentation measured in number of blanks. Defaults to `1', choose `0' to disable indentation." - :group 'notmuch - :type 'integer) + :type 'integer + :group 'notmuch-show) (defcustom notmuch-show-indent-multipart nil "Should the sub-parts of a multipart/* part be indented?" ;; dme: Not sure which is a good default. - :group 'notmuch - :type 'boolean) + :type 'boolean + :group 'notmuch-show) (defmacro with-current-notmuch-show-message (&rest body) "Evaluate body with current buffer set to the text of current message" diff --git a/emacs/notmuch.el b/emacs/notmuch.el index ef4dcc7..3602361 100644 --- a/emacs/notmuch.el +++ b/emacs/notmuch.el @@ -70,7 +70,7 @@ For example: (setq notmuch-search-result-format \(\(\"authors\" . \"%-40s\"\) \(\"subject\" . \"%s\"\)\)\)" :type '(alist :key-type (string) :value-type (string)) - :group 'notmuch) + :group 'notmuch-search) (defvar notmuch-query-history nil "Variable to store minibuffer history for notmuch queries") @@ -199,7 +199,8 @@ For a mouse binding, return nil." "List of functions to call when notmuch displays the search results." :type 'hook :options '(hl-line-mode) - :group 'notmuch) + :group 'notmuch-search + :group 'notmuch-hooks) (defvar notmuch-search-mode-map (let ((map (make-sparse-keymap))) @@ -307,27 +308,32 @@ For a mouse binding, return nil." '((((class color) (background light)) (:background "#f0f0f0")) (((class color) (background dark)) (:background "#303030"))) "Face for the single-line message summary in notmuch-show-mode." - :group 'notmuch) + :group 'notmuch-show + :group 'notmuch-faces) (defface notmuch-search-date '((t :inherit default)) "Face used in search mode for dates." - :group 'notmuch) + :group 'notmuch-search + :group 'notmuch-faces) (defface notmuch-search-count '((t :inherit default)) "Face used in search mode for the count matching the query." - :group 'notmuch) + :group 'notmuch-search + :group 'notmuch-faces) (defface notmuch-search-subject '((t :inherit default)) "Face used in search mode for subjects." - :group 'notmuch) + :group 'notmuch-search + :group 'notmuch-faces) (defface notmuch-search-matching-authors '((t :inherit default)) "Face used in search mode for authors matching the query." - :group 'notmuch) + :group 'notmuch-search + :group 'notmuch-faces) (defface notmuch-search-non-matching-authors '((((class color) @@ -339,7 +345,8 @@ For a mouse binding, return nil." (t (:italic t))) "Face used in search mode for authors not matching the query." - :group 'notmuch) + :group 'notmuch-search + :group 'notmuch-faces) (defface notmuch-tag-face '((((class color) @@ -351,7 +358,8 @@ For a mouse binding, return nil." (t (:bold t))) "Face used in search mode face for tags." - :group 'notmuch) + :group 'notmuch-search + :group 'notmuch-faces) (defun notmuch-search-mode () "Major mode displaying results of a notmuch search. @@ -502,7 +510,7 @@ the messages that are about to be tagged" :type 'hook :options '(hl-line-mode) - :group 'notmuch) + :group 'notmuch-hooks) (defcustom notmuch-after-tag-hook nil "Hooks that are run after tags of a message are modified. @@ -513,7 +521,7 @@ a list of strings of the form \"+TAG\" or \"-TAG\". the messages that were tagged" :type 'hook :options '(hl-line-mode) - :group 'notmuch) + :group 'notmuch-hooks) (defun notmuch-search-set-tags (tags) (save-excursion @@ -669,7 +677,8 @@ attributes overriding earlier. A message having both \"delete\" and \"unread\" tags with the above settings would have a green foreground and blue background." :type '(alist :key-type (string) :value-type (custom-face-edit)) - :group 'notmuch) + :group 'notmuch-search + :group 'notmuch-faces) (defun notmuch-search-color-line (start end line-tag-list) "Colorize lines in `notmuch-show' based on tags." @@ -1004,7 +1013,7 @@ Note that the recommended way of achieving the same is using :type '(choice (const :tag "notmuch new" nil) (const :tag "Disabled" "") (string :tag "Custom script")) - :group 'notmuch) + :group 'notmuch-external) (defun notmuch-poll () "Run \"notmuch new\" or an external script to import mail. -- cgit v1.2.3 From ae438ccd8c77831158c7c30f19710d798ee4a6b4 Mon Sep 17 00:00:00 2001 From: Aaron Ecay Date: Fri, 3 Feb 2012 11:24:08 +0100 Subject: emacs: quote MML tags in replies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Emacs message-mode uses certain text strings to indicate how to attach files to outgoing mail. If these are present in the text of an email, and a user is tricked into replying to the message, the user’s files could be exposed. Edited-by: Pieter Praet : Rebased to release branch. --- NEWS | 11 +++++++++++ emacs/notmuch-mua.el | 7 ++++++- test/emacs | 1 - 3 files changed, 17 insertions(+), 2 deletions(-) (limited to 'emacs/notmuch-mua.el') diff --git a/NEWS b/NEWS index 3d2c2a8..a089e67 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,17 @@ Fix error handling in python bindings. exceptions to indicate the error condition. Any subsequent calls into libnotmuch caused segmentation faults. +Quote MML tags in replies + + MML tags are text codes that Emacs uses to indicate attachments + (among other things) in messages being composed. The Emacs + interface did not quote MML tags in the quoted text of a reply. + User could be tricked into replying to a maliciously formatted + message and not editing out the MML tags from the quoted text. This + could lead to files from the user's machine being attached to the + outgoing message. The Emacs interface now quotes these tags in + reply text, so that they do not effect outgoing messages. + Notmuch 0.11 (2012-01-13) ========================= diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el index 7114e48..3e93d7c 100644 --- a/emacs/notmuch-mua.el +++ b/emacs/notmuch-mua.el @@ -111,7 +111,12 @@ list." (insert body)) (set-buffer-modified-p nil) - (message-goto-body)) + (message-goto-body) + ;; Original message may contain (malicious) MML tags. We must + ;; properly quote them in the reply. Note that using `point-max' + ;; instead of `mark' here is wrong. The buffer may include user's + ;; signature which should not be MML-quoted. + (mml-quote-region (point) (point-max))) (defun notmuch-mua-forward-message () (message-forward) diff --git a/test/emacs b/test/emacs index db8e4ad..2d066ed 100755 --- a/test/emacs +++ b/test/emacs @@ -274,7 +274,6 @@ EOF test_expect_equal_file OUTPUT EXPECTED test_begin_subtest "Quote MML tags in reply" -test_subtest_known_broken message_id='test-emacs-mml-quoting@message.id' add_message [id]="$message_id" \ "[subject]='$test_subtest_name'" \ -- cgit v1.2.3 From 2c6710e3ba22f5af6e5813dad8bee732e6c5d02c Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sat, 4 Feb 2012 13:40:24 -0500 Subject: emacs: use mark instead of point-max in MML quoting. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As Aaron explains in id:"m2vco72tf3.fsf@wal122.wireless-pennnet.upenn.edu" Using point-max would include the signature in the quoting as well. It would probably be fairly odd to want to put an MML tag in one’s signature, but that doesn’t mean that we should break that usage. We had to use point-max in the 0.11.1 bug-fix release, because the mark functionality was added post 0.11. --- emacs/notmuch-mua.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'emacs/notmuch-mua.el') diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el index c07b67b..4be7c13 100644 --- a/emacs/notmuch-mua.el +++ b/emacs/notmuch-mua.el @@ -121,7 +121,7 @@ list." ;; properly quote them in the reply. Note that using `point-max' ;; instead of `mark' here is wrong. The buffer may include user's ;; signature which should not be MML-quoted. - (mml-quote-region (point) (point-max))) + (mml-quote-region (point) (mark))) (defun notmuch-mua-forward-message () (message-forward) -- cgit v1.2.3 From 90f310b4fb8903898ead674059584737e448eb8a Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Sun, 4 Mar 2012 10:25:38 +0200 Subject: emacs: fix MML quoting in replies The reply MML quoting added in commit ae438cc unintentionally MML quotes also the signature/encryption MML tags added via message-setup-hook, causing the reply not to be signed/encrypted. MML quote just the original message in the temp buffer before inserting it to the message buffer, to not interfere with message mode hooks or message construction in general. See [1] and [2] for bug reports. Thanks to Tim Bielawa for testing. [1] id:"87hay78x6l.fsf@wyzanski.jamesvasile.com" [2] id:"1330812262-28272-1-git-send-email-tbielawa@redhat.com". Signed-off-by: Jani Nikula --- emacs/notmuch-mua.el | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'emacs/notmuch-mua.el') diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el index 4be7c13..13244eb 100644 --- a/emacs/notmuch-mua.el +++ b/emacs/notmuch-mua.el @@ -95,6 +95,9 @@ list." (goto-char (point-min)) (setq headers (mail-header-extract))))) (forward-line 1) + ;; Original message may contain (malicious) MML tags. We must + ;; properly quote them in the reply. + (mml-quote-region (point) (point-max)) (setq body (buffer-substring (point) (point-max)))) ;; If sender is non-nil, set the From: header to its value. (when sender @@ -116,12 +119,7 @@ list." (push-mark)) (set-buffer-modified-p nil) - (message-goto-body) - ;; Original message may contain (malicious) MML tags. We must - ;; properly quote them in the reply. Note that using `point-max' - ;; instead of `mark' here is wrong. The buffer may include user's - ;; signature which should not be MML-quoted. - (mml-quote-region (point) (mark))) + (message-goto-body)) (defun notmuch-mua-forward-message () (message-forward) -- cgit v1.2.3 From 650123510cfa64caf6b20f5239f43433fa6f2941 Mon Sep 17 00:00:00 2001 From: Adam Wolfe Gordon Date: Sun, 18 Mar 2012 10:32:42 -0600 Subject: emacs: Use the new JSON reply format and message-cite-original Use the new JSON reply format to create replies in emacs. Quote HTML parts nicely by using mm-display-part to turn them into displayable text, then quoting them with message-cite-original. This is very useful for users who regularly receive HTML-only email. Use message-mode's message-cite-original function to create the quoted body for reply messages. In order to make this act like the existing notmuch defaults, you will need to set the following in your emacs configuration: message-citation-line-format "On %a, %d %b %Y, %f wrote:" message-citation-line-function 'message-insert-formatted-citation-line The tests have been updated to reflect the (ugly) emacs default. --- emacs/notmuch-lib.el | 30 ++++++++++++ emacs/notmuch-mua.el | 124 +++++++++++++++++++++++++++++++++----------------- emacs/notmuch-show.el | 31 +++---------- test/emacs | 8 ++-- 4 files changed, 123 insertions(+), 70 deletions(-) (limited to 'emacs/notmuch-mua.el') diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el index 7e3f110..c146748 100644 --- a/emacs/notmuch-lib.el +++ b/emacs/notmuch-lib.el @@ -206,6 +206,36 @@ the user hasn't set this variable with the old or new value." (setq seq (nconc (delete elem seq) (list elem)))))) seq)) +(defun notmuch-parts-filter-by-type (parts type) + "Given a list of message parts, return a list containing the ones matching +the given type." + (remove-if-not + (lambda (part) (notmuch-match-content-type (plist-get part :content-type) type)) + parts)) + +;; Helper for parts which are generally not included in the default +;; JSON output. +(defun notmuch-get-bodypart-internal (message-id part-number process-crypto) + (let ((args '("show" "--format=raw")) + (part-arg (format "--part=%s" part-number))) + (setq args (append args (list part-arg))) + (if process-crypto + (setq args (append args '("--decrypt")))) + (setq args (append args (list message-id))) + (with-temp-buffer + (let ((coding-system-for-read 'no-conversion)) + (progn + (apply 'call-process (append (list notmuch-command nil (list t nil) nil) args)) + (buffer-string)))))) + +(defun notmuch-get-bodypart-content (msg part nth process-crypto) + (or (plist-get part :content) + (notmuch-get-bodypart-internal (concat "id:" (plist-get msg :id)) nth process-crypto))) + +(defun notmuch-plist-to-alist (plist) + (loop for (key value . rest) on plist by #'cddr + collect (cons (substring (symbol-name key) 1) value))) + ;; Compatibility functions for versions of emacs before emacs 23. ;; ;; Both functions here were copied from emacs 23 with the following copyright: diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el index 13244eb..6aae3a0 100644 --- a/emacs/notmuch-mua.el +++ b/emacs/notmuch-mua.el @@ -19,11 +19,15 @@ ;; ;; Authors: David Edmondson +(require 'json) (require 'message) +(require 'format-spec) (require 'notmuch-lib) (require 'notmuch-address) +(eval-when-compile (require 'cl)) + ;; (defcustom notmuch-mua-send-hook '(notmuch-mua-message-send-hook) @@ -72,54 +76,92 @@ list." (push header message-hidden-headers))) notmuch-mua-hidden-headers)) +(defun notmuch-mua-get-quotable-parts (parts) + (loop for part in parts + if (notmuch-match-content-type (plist-get part :content-type) "multipart/alternative") + collect (let* ((subparts (plist-get part :content)) + (types (mapcar (lambda (part) (plist-get part :content-type)) subparts)) + (chosen-type (car (notmuch-multipart/alternative-choose types)))) + (loop for part in (reverse subparts) + if (notmuch-match-content-type (plist-get part :content-type) chosen-type) + return part)) + else if (notmuch-match-content-type (plist-get part :content-type) "multipart/*") + append (notmuch-mua-get-quotable-parts (plist-get part :content)) + else if (notmuch-match-content-type (plist-get part :content-type) "text/*") + collect part)) + (defun notmuch-mua-reply (query-string &optional sender reply-all) - (let (headers - body - (args '("reply"))) - (if notmuch-show-process-crypto - (setq args (append args '("--decrypt")))) + (let ((args '("reply" "--format=json")) + reply + original) + (when notmuch-show-process-crypto + (setq args (append args '("--decrypt")))) + (if reply-all (setq args (append args '("--reply-to=all"))) (setq args (append args '("--reply-to=sender")))) (setq args (append args (list query-string))) - ;; This make assumptions about the output of `notmuch reply', but - ;; really only that the headers come first followed by a blank - ;; line and then the body. + + ;; Get the reply object as JSON, and parse it into an elisp object. (with-temp-buffer (apply 'call-process (append (list notmuch-command nil (list t t) nil) args)) (goto-char (point-min)) - (if (re-search-forward "^$" nil t) - (save-excursion - (save-restriction - (narrow-to-region (point-min) (point)) - (goto-char (point-min)) - (setq headers (mail-header-extract))))) - (forward-line 1) - ;; Original message may contain (malicious) MML tags. We must - ;; properly quote them in the reply. - (mml-quote-region (point) (point-max)) - (setq body (buffer-substring (point) (point-max)))) - ;; If sender is non-nil, set the From: header to its value. - (when sender - (mail-header-set 'from sender headers)) - (let - ;; Overlay the composition window on that being used to read - ;; the original message. - ((same-window-regexps '("\\*mail .*"))) - (notmuch-mua-mail (mail-header 'to headers) - (mail-header 'subject headers) - (message-headers-to-generate headers t '(to subject)))) - ;; insert the message body - but put it in front of the signature - ;; if one is present - (goto-char (point-max)) - (if (re-search-backward message-signature-separator nil t) + (let ((json-object-type 'plist) + (json-array-type 'list) + (json-false 'nil)) + (setq reply (json-read)))) + + ;; Extract the original message to simplify the following code. + (setq original (plist-get reply :original)) + + ;; Extract the headers of both the reply and the original message. + (let* ((original-headers (plist-get original :headers)) + (reply-headers (plist-get reply :reply-headers))) + + ;; If sender is non-nil, set the From: header to its value. + (when sender + (plist-put reply-headers :From sender)) + (let + ;; Overlay the composition window on that being used to read + ;; the original message. + ((same-window-regexps '("\\*mail .*"))) + (notmuch-mua-mail (plist-get reply-headers :To) + (plist-get reply-headers :Subject) + (notmuch-plist-to-alist reply-headers))) + ;; Insert the message body - but put it in front of the signature + ;; if one is present + (goto-char (point-max)) + (if (re-search-backward message-signature-separator nil t) (forward-line -1) - (goto-char (point-max))) - (insert body) - (push-mark)) - (set-buffer-modified-p nil) - - (message-goto-body)) + (goto-char (point-max))) + + (let ((from (plist-get original-headers :From)) + (date (plist-get original-headers :Date)) + (start (point))) + + ;; message-cite-original constructs a citation line based on the From and Date + ;; headers of the original message, which are assumed to be in the buffer. + (insert "From: " from "\n") + (insert "Date: " date "\n\n") + + ;; Get the parts of the original message that should be quoted; this includes + ;; all the text parts, except the non-preferred ones in a multipart/alternative. + (let ((quotable-parts (notmuch-mua-get-quotable-parts (plist-get original :body)))) + (mapc (lambda (part) + (insert (notmuch-get-bodypart-content original part + (plist-get part :id) + notmuch-show-process-crypto))) + quotable-parts)) + + (set-mark (point)) + (goto-char start) + ;; Quote the original message according to the user's configured style. + (message-cite-original)))) + + (goto-char (point-max)) + (push-mark) + (message-goto-body) + (set-buffer-modified-p nil)) (defun notmuch-mua-forward-message () (message-forward) @@ -145,7 +187,7 @@ OTHER-ARGS are passed through to `message-mail'." (when (not (string= "" user-agent)) (push (cons "User-Agent" user-agent) other-headers)))) - (unless (mail-header 'from other-headers) + (unless (mail-header 'From other-headers) (push (cons "From" (concat (notmuch-user-name) " <" (notmuch-user-primary-email) ">")) other-headers)) @@ -208,7 +250,7 @@ the From: address first." (interactive "P") (let ((other-headers (when (or prompt-for-sender notmuch-always-prompt-for-sender) - (list (cons 'from (notmuch-mua-prompt-for-sender)))))) + (list (cons 'From (notmuch-mua-prompt-for-sender)))))) (notmuch-mua-mail nil nil other-headers))) (defun notmuch-mua-new-forward-message (&optional prompt-for-sender) diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el index ed938bf..0cd7d82 100644 --- a/emacs/notmuch-show.el +++ b/emacs/notmuch-show.el @@ -488,7 +488,7 @@ message at DEPTH in the current thread." (setq notmuch-show-process-crypto ,process-crypto) ;; Always acquires the part via `notmuch part', even if it is ;; available in the JSON output. - (insert (notmuch-show-get-bodypart-internal ,message-id ,nth)) + (insert (notmuch-get-bodypart-internal ,message-id ,nth notmuch-show-process-crypto)) ,@body)))) (defun notmuch-show-save-part (message-id nth &optional filename content-type) @@ -536,7 +536,7 @@ current buffer, if possible." ;; test whether we are able to inline it (which includes both ;; capability and suitability tests). (when (mm-inlined-p handle) - (insert (notmuch-show-get-bodypart-content msg part nth)) + (insert (notmuch-get-bodypart-content msg part nth notmuch-show-process-crypto)) (when (mm-inlinable-p handle) (set-buffer display-buffer) (mm-display-part handle) @@ -613,8 +613,8 @@ current buffer, if possible." ;; times (hundreds!), which results in many calls to ;; `notmuch part'. (unless content - (setq content (notmuch-show-get-bodypart-internal (concat "id:" message-id) - part-number)) + (setq content (notmuch-get-bodypart-internal (concat "id:" message-id) + part-number notmuch-show-process-crypto)) (with-current-buffer w3m-current-buffer (notmuch-show-w3m-cid-store-internal url message-id @@ -734,7 +734,7 @@ current buffer, if possible." ;; insert a header to make this clear. (if (> nth 1) (notmuch-show-insert-part-header nth declared-type content-type (plist-get part :filename))) - (insert (notmuch-show-get-bodypart-content msg part nth)) + (insert (notmuch-get-bodypart-content msg part nth notmuch-show-process-crypto)) (save-excursion (save-restriction (narrow-to-region start (point-max)) @@ -744,7 +744,7 @@ current buffer, if possible." (defun notmuch-show-insert-part-text/calendar (msg part content-type nth depth declared-type) (notmuch-show-insert-part-header nth declared-type content-type (plist-get part :filename)) (insert (with-temp-buffer - (insert (notmuch-show-get-bodypart-content msg part nth)) + (insert (notmuch-get-bodypart-content msg part nth notmuch-show-process-crypto)) (goto-char (point-min)) (let ((file (make-temp-file "notmuch-ical")) result) @@ -806,25 +806,6 @@ current buffer, if possible." (intern (concat "notmuch-show-insert-part-" content-type)))) result)) -;; Helper for parts which are generally not included in the default -;; JSON output. -(defun notmuch-show-get-bodypart-internal (message-id part-number) - (let ((args '("show" "--format=raw")) - (part-arg (format "--part=%s" part-number))) - (setq args (append args (list part-arg))) - (if notmuch-show-process-crypto - (setq args (append args '("--decrypt")))) - (setq args (append args (list message-id))) - (with-temp-buffer - (let ((coding-system-for-read 'no-conversion)) - (progn - (apply 'call-process (append (list notmuch-command nil (list t nil) nil) args)) - (buffer-string)))))) - -(defun notmuch-show-get-bodypart-content (msg part nth) - (or (plist-get part :content) - (notmuch-show-get-bodypart-internal (concat "id:" (plist-get msg :id)) nth))) - ;; (defun notmuch-show-insert-bodypart-internal (msg part content-type nth depth declared-type) diff --git a/test/emacs b/test/emacs index 01afdb6..8a28705 100755 --- a/test/emacs +++ b/test/emacs @@ -268,13 +268,13 @@ Subject: Re: Testing message sent via SMTP In-Reply-To: Fcc: ${MAIL_DIR}/sent --text follows this line-- -On 01 Jan 2000 12:00:00 -0000, Notmuch Test Suite wrote: +Notmuch Test Suite writes: + > This is a test that messages are sent via SMTP EOF test_expect_equal_file OUTPUT EXPECTED test_begin_subtest "Reply within emacs to a multipart/mixed message" -test_subtest_known_broken test_emacs '(notmuch-show "id:20091118002059.067214ed@hikari") (notmuch-show-reply) (test-output)' @@ -334,7 +334,6 @@ EOF test_expect_equal_file OUTPUT EXPECTED test_begin_subtest "Reply within emacs to a multipart/alternative message" -test_subtest_known_broken test_emacs '(notmuch-show "id:cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com") (notmuch-show-reply) (test-output)' @@ -385,7 +384,8 @@ Subject: Re: Quote MML tags in reply In-Reply-To: Fcc: ${MAIL_DIR}/sent --text follows this line-- -On Fri, 05 Jan 2001 15:43:57 +0000, Notmuch Test Suite wrote: +Notmuch Test Suite writes: + > <#!part disposition=inline> EOF test_expect_equal_file OUTPUT EXPECTED -- cgit v1.2.3 From 3737ca6e268e6f45353bfbcc4ac4b1d548c5908d Mon Sep 17 00:00:00 2001 From: Adam Wolfe Gordon Date: Sun, 1 Apr 2012 09:24:21 -0600 Subject: emacs: Fix two bugs in reply Bug 1: Replying from alternate addresses ---------------------------------------- The reply code was inconsistent in its use of symbols and strings for header names being passed to message.el functions. This caused the From header to be lookup up incorrectly, causing an additional From header to be added with the user's primary address instead of the correct alternate address. This is fixed by using symbols everywhere, i.e. never using strings for header names when interacting with message.el. This change also removes our use of `mail-header`, since we don't use it anywhere else, and using assq makes it clear how the header lists are expected to work. Bug 2: Duplicate headers in emacs 23.2 -------------------------------------- The message.el code in emacs 23.2 assumes that header names will always be passed as symbols, so our use of strings caused problems. The symptom was that on 23.2 (and presumably on earlier versions) the reply message would end up with two of some headers. Converting everything to symbols also fixes this issue. --- emacs/notmuch-lib.el | 7 +++++-- emacs/notmuch-mua.el | 10 +++++----- test/emacs | 1 - 3 files changed, 10 insertions(+), 8 deletions(-) (limited to 'emacs/notmuch-mua.el') diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el index c159dda..6907a5f 100644 --- a/emacs/notmuch-lib.el +++ b/emacs/notmuch-lib.el @@ -237,9 +237,12 @@ the given type." (or (plist-get part :content) (notmuch-get-bodypart-internal (notmuch-id-to-query (plist-get msg :id)) nth process-crypto))) -(defun notmuch-plist-to-alist (plist) +;; Converts a plist of headers to an alist of headers. The input plist should +;; have symbols of the form :Header as keys, and the resulting alist will have +;; symbols of the form 'Header as keys. +(defun notmuch-headers-plist-to-alist (plist) (loop for (key value . rest) on plist by #'cddr - collect (cons (substring (symbol-name key) 1) value))) + collect (cons (intern (substring (symbol-name key) 1)) value))) ;; Compatibility functions for versions of emacs before emacs 23. ;; diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el index 6aae3a0..cfa3d61 100644 --- a/emacs/notmuch-mua.el +++ b/emacs/notmuch-mua.el @@ -127,7 +127,7 @@ list." ((same-window-regexps '("\\*mail .*"))) (notmuch-mua-mail (plist-get reply-headers :To) (plist-get reply-headers :Subject) - (notmuch-plist-to-alist reply-headers))) + (notmuch-headers-plist-to-alist reply-headers))) ;; Insert the message body - but put it in front of the signature ;; if one is present (goto-char (point-max)) @@ -185,11 +185,11 @@ OTHER-ARGS are passed through to `message-mail'." (when notmuch-mua-user-agent-function (let ((user-agent (funcall notmuch-mua-user-agent-function))) (when (not (string= "" user-agent)) - (push (cons "User-Agent" user-agent) other-headers)))) + (push (cons 'User-Agent user-agent) other-headers)))) - (unless (mail-header 'From other-headers) - (push (cons "From" (concat - (notmuch-user-name) " <" (notmuch-user-primary-email) ">")) other-headers)) + (unless (assq 'From other-headers) + (push (cons 'From (concat + (notmuch-user-name) " <" (notmuch-user-primary-email) ">")) other-headers)) (apply #'message-mail to subject other-headers other-args) (message-sort-headers) diff --git a/test/emacs b/test/emacs index 576bc1f..30654bb 100755 --- a/test/emacs +++ b/test/emacs @@ -286,7 +286,6 @@ EOF test_expect_equal_file OUTPUT EXPECTED test_begin_subtest "Reply from alternate address within emacs" -test_subtest_known_broken add_message '[from]="Sender "' \ [to]=test_suite_other@notmuchmail.org -- cgit v1.2.3 From e4844fafec0dd789fe304b412c76f594850b833e Mon Sep 17 00:00:00 2001 From: Adam Wolfe Gordon Date: Sun, 1 Apr 2012 09:24:23 -0600 Subject: emacs: Fix the References header in reply In the new reply code, the References header gets inserted by message.el using a function called message-shorten-references. Unlike all the other header-inserting functions, it doesn't put a newline after the header, causing the next header to end up on the same line. In our case, this header happened to be User-Agent, so it's hard to notice. This is probably a bug in message.el, but we need to work around it. This fixes the problem by wrapping message-shorten-references in a function that inserts a newline after if necessary. This should protect against the message.el bug being fixed in the future. --- emacs/notmuch-mua.el | 28 +++++++++++++++++++++++++--- test/emacs | 6 ------ 2 files changed, 25 insertions(+), 9 deletions(-) (limited to 'emacs/notmuch-mua.el') diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el index cfa3d61..87bd88d 100644 --- a/emacs/notmuch-mua.el +++ b/emacs/notmuch-mua.el @@ -90,6 +90,15 @@ list." else if (notmuch-match-content-type (plist-get part :content-type) "text/*") collect part)) +;; There is a bug in emacs 23's message.el that results in a newline +;; not being inserted after the References header, so the next header +;; is concatenated to the end of it. This function fixes the problem, +;; while guarding against the possibility that some current or future +;; version of emacs has the bug fixed. +(defun notmuch-mua-insert-references (original-func header references) + (funcall original-func header references) + (unless (bolp) (insert "\n"))) + (defun notmuch-mua-reply (query-string &optional sender reply-all) (let ((args '("reply" "--format=json")) reply @@ -125,9 +134,22 @@ list." ;; Overlay the composition window on that being used to read ;; the original message. ((same-window-regexps '("\\*mail .*"))) - (notmuch-mua-mail (plist-get reply-headers :To) - (plist-get reply-headers :Subject) - (notmuch-headers-plist-to-alist reply-headers))) + + ;; We modify message-header-format-alist to get around a bug in message.el. + ;; See the comment above on notmuch-mua-insert-references. + (let ((message-header-format-alist + (loop for pair in message-header-format-alist + if (eq (car pair) 'References) + collect (cons 'References + (apply-partially + 'notmuch-mua-insert-references + (cdr pair))) + else + collect pair))) + (notmuch-mua-mail (plist-get reply-headers :To) + (plist-get reply-headers :Subject) + (notmuch-headers-plist-to-alist reply-headers)))) + ;; Insert the message body - but put it in front of the signature ;; if one is present (goto-char (point-max)) diff --git a/test/emacs b/test/emacs index 15cc778..c7510e9 100755 --- a/test/emacs +++ b/test/emacs @@ -267,7 +267,6 @@ EOF test_expect_equal_file OUTPUT EXPECTED test_begin_subtest "Reply within emacs" -test_subtest_known_broken test_emacs '(let ((message-hidden-headers ''())) (notmuch-search "subject:\"testing message sent via SMTP\"") (notmuch-test-wait) @@ -292,7 +291,6 @@ EOF test_expect_equal_file OUTPUT EXPECTED test_begin_subtest "Reply from alternate address within emacs" -test_subtest_known_broken add_message '[from]="Sender "' \ [to]=test_suite_other@notmuchmail.org @@ -318,7 +316,6 @@ EOF test_expect_equal_file OUTPUT EXPECTED test_begin_subtest "Reply from address in named group list within emacs" -test_subtest_known_broken add_message '[from]="Sender "' \ '[to]=group:test_suite@notmuchmail.org,someone@example.com\;' \ [cc]=test_suite_other@notmuchmail.org @@ -345,7 +342,6 @@ EOF test_expect_equal_file OUTPUT EXPECTED test_begin_subtest "Reply within emacs to a multipart/mixed message" -test_subtest_known_broken test_emacs '(let ((message-hidden-headers ''())) (notmuch-show "id:20091118002059.067214ed@hikari") (notmuch-show-reply) @@ -409,7 +405,6 @@ EOF test_expect_equal_file OUTPUT EXPECTED test_begin_subtest "Reply within emacs to a multipart/alternative message" -test_subtest_known_broken test_emacs '(let ((message-hidden-headers ''())) (notmuch-show "id:cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com") (notmuch-show-reply) @@ -450,7 +445,6 @@ EOF test_expect_equal_file OUTPUT EXPECTED test_begin_subtest "Quote MML tags in reply" -test_subtest_known_broken message_id='test-emacs-mml-quoting@message.id' add_message [id]="$message_id" \ "[subject]='$test_subtest_name'" \ -- cgit v1.2.3 From f6c170fabca8f39e74705e3813504137811bf162 Mon Sep 17 00:00:00 2001 From: Adam Wolfe Gordon Date: Sat, 5 May 2012 13:30:37 -0600 Subject: emacs: Correctly quote non-text/plain parts in reply Quote non-text parts nicely by displaying them with mm-display-part before calling message-cite-original to quote them. HTML-only emails can now be quoted correctly. We re-use some code from notmuch-show (notmuch-show-mm-display-part-inline), which has been moved to notmuch-lib.el. Mark the test for this feature as not broken. --- emacs/notmuch-lib.el | 19 +++++++++++++++++++ emacs/notmuch-mua.el | 15 ++++++++++----- emacs/notmuch-show.el | 19 +------------------ test/emacs | 1 - 4 files changed, 30 insertions(+), 24 deletions(-) (limited to 'emacs/notmuch-mua.el') diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el index 6907a5f..7fa441a 100644 --- a/emacs/notmuch-lib.el +++ b/emacs/notmuch-lib.el @@ -21,6 +21,8 @@ ;; This is an part of an emacs-based interface to the notmuch mail system. +(require 'mm-view) +(require 'mm-decode) (eval-when-compile (require 'cl)) (defvar notmuch-command "notmuch" @@ -237,6 +239,23 @@ the given type." (or (plist-get part :content) (notmuch-get-bodypart-internal (notmuch-id-to-query (plist-get msg :id)) nth process-crypto))) +(defun notmuch-mm-display-part-inline (msg part nth content-type process-crypto) + "Use the mm-decode/mm-view functions to display a part in the +current buffer, if possible." + (let ((display-buffer (current-buffer))) + (with-temp-buffer + (let* ((charset (plist-get part :content-charset)) + (handle (mm-make-handle (current-buffer) `(,content-type (charset . ,charset))))) + ;; If the user wants the part inlined, insert the content and + ;; test whether we are able to inline it (which includes both + ;; capability and suitability tests). + (when (mm-inlined-p handle) + (insert (notmuch-get-bodypart-content msg part nth process-crypto)) + (when (mm-inlinable-p handle) + (set-buffer display-buffer) + (mm-display-part handle) + t)))))) + ;; Converts a plist of headers to an alist of headers. The input plist should ;; have symbols of the form :Header as keys, and the resulting alist will have ;; symbols of the form 'Header as keys. diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el index 87bd88d..fc7ae07 100644 --- a/emacs/notmuch-mua.el +++ b/emacs/notmuch-mua.el @@ -21,6 +21,7 @@ (require 'json) (require 'message) +(require 'mm-view) (require 'format-spec) (require 'notmuch-lib) @@ -90,6 +91,14 @@ list." else if (notmuch-match-content-type (plist-get part :content-type) "text/*") collect part)) +(defun notmuch-mua-insert-quotable-part (message part) + (save-restriction + (narrow-to-region (point) (point)) + (notmuch-mm-display-part-inline message part (plist-get part :id) + (plist-get part :content-type) + notmuch-show-process-crypto) + (goto-char (point-max)))) + ;; There is a bug in emacs 23's message.el that results in a newline ;; not being inserted after the References header, so the next header ;; is concatenated to the end of it. This function fixes the problem, @@ -169,11 +178,7 @@ list." ;; Get the parts of the original message that should be quoted; this includes ;; all the text parts, except the non-preferred ones in a multipart/alternative. (let ((quotable-parts (notmuch-mua-get-quotable-parts (plist-get original :body)))) - (mapc (lambda (part) - (insert (notmuch-get-bodypart-content original part - (plist-get part :id) - notmuch-show-process-crypto))) - quotable-parts)) + (mapc (apply-partially 'notmuch-mua-insert-quotable-part original) quotable-parts)) (set-mark (point)) (goto-char start) diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el index 37f0ebb..d318430 100644 --- a/emacs/notmuch-show.el +++ b/emacs/notmuch-show.el @@ -524,23 +524,6 @@ message at DEPTH in the current thread." (let ((handle (mm-make-handle (current-buffer) (list content-type)))) (mm-interactively-view-part handle)))) -(defun notmuch-show-mm-display-part-inline (msg part nth content-type) - "Use the mm-decode/mm-view functions to display a part in the -current buffer, if possible." - (let ((display-buffer (current-buffer))) - (with-temp-buffer - (let* ((charset (plist-get part :content-charset)) - (handle (mm-make-handle (current-buffer) `(,content-type (charset . ,charset))))) - ;; If the user wants the part inlined, insert the content and - ;; test whether we are able to inline it (which includes both - ;; capability and suitability tests). - (when (mm-inlined-p handle) - (insert (notmuch-get-bodypart-content msg part nth notmuch-show-process-crypto)) - (when (mm-inlinable-p handle) - (set-buffer display-buffer) - (mm-display-part handle) - t)))))) - (defun notmuch-show-multipart/*-to-list (part) (mapcar (lambda (inner-part) (plist-get inner-part :content-type)) (plist-get part :content))) @@ -785,7 +768,7 @@ current buffer, if possible." (defun notmuch-show-insert-part-*/* (msg part content-type nth depth declared-type) ;; This handler _must_ succeed - it is the handler of last resort. (notmuch-show-insert-part-header nth content-type declared-type (plist-get part :filename)) - (notmuch-show-mm-display-part-inline msg part nth content-type) + (notmuch-mm-display-part-inline msg part nth content-type notmuch-show-process-crypto) t) ;; Functions for determining how to handle MIME parts. diff --git a/test/emacs b/test/emacs index 5f238d9..a615b39 100755 --- a/test/emacs +++ b/test/emacs @@ -445,7 +445,6 @@ EOF test_expect_equal_file OUTPUT EXPECTED test_begin_subtest "Reply within emacs to an html-only message" -test_subtest_known_broken add_message '[content-type]="text/html"' \ '[body]="Hi,
This is an HTML test message.

OK?"' test_emacs "(let ((message-hidden-headers '())) -- cgit v1.2.3 From 832fd1a7a63ced91d9519d217ba187ee08eea13c Mon Sep 17 00:00:00 2001 From: Thomas Jost Date: Fri, 4 May 2012 12:37:56 +0200 Subject: emacs: Let the user choose where to compose new mails Introduce a new defcustom notmuch-mua-compose-in that allows users to specify where new mails are composed, either in the current window or in a new window or frame. Signed-off-by: Jameson Rollins --- emacs/notmuch-mua.el | 45 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) (limited to 'emacs/notmuch-mua.el') diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el index fc7ae07..cb89db3 100644 --- a/emacs/notmuch-mua.el +++ b/emacs/notmuch-mua.el @@ -37,6 +37,26 @@ :group 'notmuch-send :group 'notmuch-hooks) +(defcustom notmuch-mua-compose-in 'current-window + (concat + "Where to create the mail buffer used to compose a new message. +Possible values are `current-window' (default), `new-window' and +`new-frame'. If set to `current-window', the mail buffer will be +displayed in the current window, so the old buffer will be +restored when the mail buffer is killed. If set to `new-window' +or `new-frame', the mail buffer will be displayed in a new +window/frame that will be destroyed when the buffer is killed. +You may want to customize `message-kill-buffer-on-exit' +accordingly." + (when (< emacs-major-version 24) + " Due to a known bug in Emacs 23, you should not set +this to `new-window' if `message-kill-buffer-on-exit' is +disabled: this would result in an incorrect behavior.")) + :group 'notmuch-send + :type '(choice (const :tag "Compose in the current window" current-window) + (const :tag "Compose mail in a new window" new-window) + (const :tag "Compose mail in a new frame" new-frame))) + (defcustom notmuch-mua-user-agent-function 'notmuch-mua-user-agent-full "Function used to generate a `User-Agent:' string. If this is `nil' then no `User-Agent:' will be generated." @@ -56,6 +76,23 @@ list." ;; +(defun notmuch-mua-get-switch-function () + "Get a switch function according to `notmuch-mua-compose-in'." + (cond ((eq notmuch-mua-compose-in 'current-window) + 'switch-to-buffer) + ((eq notmuch-mua-compose-in 'new-window) + 'switch-to-buffer-other-window) + ((eq notmuch-mua-compose-in 'new-frame) + 'switch-to-buffer-other-frame) + (t (error "Invalid value for `notmuch-mua-compose-in'")))) + +(defun notmuch-mua-maybe-set-window-dedicated () + "Set the selected window as dedicated according to +`notmuch-mua-compose-in'." + (when (or (eq notmuch-mua-compose-in 'new-frame) + (eq notmuch-mua-compose-in 'new-window)) + (set-window-dedicated-p (selected-window) t))) + (defun notmuch-mua-user-agent-full () "Generate a `User-Agent:' string suitable for notmuch." (concat (notmuch-mua-user-agent-notmuch) @@ -157,7 +194,8 @@ list." collect pair))) (notmuch-mua-mail (plist-get reply-headers :To) (plist-get reply-headers :Subject) - (notmuch-headers-plist-to-alist reply-headers)))) + (notmuch-headers-plist-to-alist reply-headers) + nil (notmuch-mua-get-switch-function)))) ;; Insert the message body - but put it in front of the signature ;; if one is present @@ -191,6 +229,7 @@ list." (set-buffer-modified-p nil)) (defun notmuch-mua-forward-message () + (funcall (notmuch-mua-get-switch-function) (current-buffer)) (message-forward) (when notmuch-mua-user-agent-function @@ -200,6 +239,7 @@ list." (message-sort-headers) (message-hide-headers) (set-buffer-modified-p nil) + (notmuch-mua-maybe-set-window-dedicated) (message-goto-to)) @@ -222,6 +262,7 @@ OTHER-ARGS are passed through to `message-mail'." (message-sort-headers) (message-hide-headers) (set-buffer-modified-p nil) + (notmuch-mua-maybe-set-window-dedicated) (message-goto-to)) @@ -278,7 +319,7 @@ the From: address first." (let ((other-headers (when (or prompt-for-sender notmuch-always-prompt-for-sender) (list (cons 'From (notmuch-mua-prompt-for-sender)))))) - (notmuch-mua-mail nil nil other-headers))) + (notmuch-mua-mail nil nil other-headers nil (notmuch-mua-get-switch-function)))) (defun notmuch-mua-new-forward-message (&optional prompt-for-sender) "Invoke the notmuch message forwarding window. -- cgit v1.2.3 From e02c179c8f6cddcc0b6457ccd0fd40186c75e3db Mon Sep 17 00:00:00 2001 From: Michal Sojka Date: Tue, 1 May 2012 23:10:47 +0200 Subject: emacs: Do not pass stderr of notmuch reply to JSON parser Sometimes, notmuch reply outputs something to stderr, for example: "Failed to verify signed part: Cannot verify multipart/signed part: unsupported signature protocol". When this happens, replying in emacs fails, because emacs cannot parse the error message as JSON. This patch causes emacs to ignore stderr when reading reply from notmuch. --- emacs/notmuch-mua.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'emacs/notmuch-mua.el') diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el index cb89db3..408b49e 100644 --- a/emacs/notmuch-mua.el +++ b/emacs/notmuch-mua.el @@ -159,7 +159,7 @@ list." ;; Get the reply object as JSON, and parse it into an elisp object. (with-temp-buffer - (apply 'call-process (append (list notmuch-command nil (list t t) nil) args)) + (apply 'call-process (append (list notmuch-command nil (list t nil) nil) args)) (goto-char (point-min)) (let ((json-object-type 'plist) (json-array-type 'list) -- cgit v1.2.3