Re: Flat search and threaded views
[notmuch-archives.git] / 72 / e137bf4b2ba58ccf8c36f430c67096447d65ef
1 Return-Path: <dmitry.kurochkin@gmail.com>\r
2 X-Original-To: notmuch@notmuchmail.org\r
3 Delivered-To: notmuch@notmuchmail.org\r
4 Received: from localhost (localhost [127.0.0.1])\r
5         by olra.theworths.org (Postfix) with ESMTP id 69C3B431FD0\r
6         for <notmuch@notmuchmail.org>; Fri, 18 Nov 2011 20:19:05 -0800 (PST)\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
8 X-Spam-Flag: NO\r
9 X-Spam-Score: -0.799\r
10 X-Spam-Level: \r
11 X-Spam-Status: No, score=-0.799 tagged_above=-999 required=5\r
12         tests=[DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1,\r
13         FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_LOW=-0.7] autolearn=disabled\r
14 Received: from olra.theworths.org ([127.0.0.1])\r
15         by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
16         with ESMTP id RJ5R0aw72fzB for <notmuch@notmuchmail.org>;\r
17         Fri, 18 Nov 2011 20:19:02 -0800 (PST)\r
18 Received: from mail-bw0-f53.google.com (mail-bw0-f53.google.com\r
19         [209.85.214.53]) (using TLSv1 with cipher RC4-SHA (128/128 bits))\r
20         (No client certificate requested)\r
21         by olra.theworths.org (Postfix) with ESMTPS id B2AE2429E25\r
22         for <notmuch@notmuchmail.org>; Fri, 18 Nov 2011 20:19:01 -0800 (PST)\r
23 Received: by bkaq10 with SMTP id q10so4618800bka.26\r
24         for <notmuch@notmuchmail.org>; Fri, 18 Nov 2011 20:19:00 -0800 (PST)\r
25 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma;\r
26         h=from:to:subject:date:message-id:x-mailer:in-reply-to:references\r
27         :mime-version:content-type:content-transfer-encoding;\r
28         bh=rOhasm1QfB63YVnBzgi7nzbobBkCa16LjqYxjB+KaEc=;\r
29         b=x55QJdXFaSw5IqOlY0ydG04tfe4Pqlri/sUzachC21FzHw5hZkunCG8P9I607JUGu/\r
30         vq/g5EcYUdXcbNmdXPM0YKmSkj5kjaarakP1/QIUhd2IJBjIWsUoCvSE/rp1cgi15A8l\r
31         MOM3uf3Rj+/FmRpTUyyqeAs8NtzHFtTHwbdAg=\r
32 Received: by 10.205.124.144 with SMTP id go16mr6006132bkc.119.1321676338557;\r
33         Fri, 18 Nov 2011 20:18:58 -0800 (PST)\r
34 Received: from localhost ([91.144.186.21])\r
35         by mx.google.com with ESMTPS id fu17sm2209739bkc.9.2011.11.18.20.18.56\r
36         (version=TLSv1/SSLv3 cipher=OTHER);\r
37         Fri, 18 Nov 2011 20:18:57 -0800 (PST)\r
38 From: Dmitry Kurochkin <dmitry.kurochkin@gmail.com>\r
39 To: notmuch@notmuchmail.org\r
40 Subject:\r
41  [PATCH v2] Output unmodified Content-Type header value for JSON format.\r
42 Date: Sat, 19 Nov 2011 08:18:41 +0400\r
43 Message-Id: <1321676321-27745-1-git-send-email-dmitry.kurochkin@gmail.com>\r
44 X-Mailer: git-send-email 1.7.7.3\r
45 In-Reply-To: <1321659905-24367-1-git-send-email-dmitry.kurochkin@gmail.com>\r
46 References: <1321659905-24367-1-git-send-email-dmitry.kurochkin@gmail.com>\r
47 MIME-Version: 1.0\r
48 Content-Type: text/plain; charset=UTF-8\r
49 Content-Transfer-Encoding: 8bit\r
50 X-BeenThere: notmuch@notmuchmail.org\r
51 X-Mailman-Version: 2.1.13\r
52 Precedence: list\r
53 List-Id: "Use and development of the notmuch mail system."\r
54         <notmuch.notmuchmail.org>\r
55 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
56         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
57 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
58 List-Post: <mailto:notmuch@notmuchmail.org>\r
59 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
60 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
61         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
62 X-List-Received-Date: Sat, 19 Nov 2011 04:19:05 -0000\r
63 \r
64 Before the change, notmuch used g_mime_content_type_to_string(3)\r
65 function to output Content-Type header value.  Turns out it outputs\r
66 only "type/subtype" part and ignores all parameters.  Also, if there\r
67 is no Content-Type header, default "text/plain" value is used.\r
68 \r
69 JSON is supposed to be a "low-level" structured format and should not\r
70 add missing values or throw away information.  The patch changes\r
71 notmuch show to use unmodified Content-Type value for JSON format.\r
72 Also, no default value is added if the header is missing.\r
73 \r
74 Corresponding changes to Emacs UI are made to handle full Content-Type\r
75 header values.  The header is parsed using MIME\r
76 `mail-header-parse-content-type' function.  All message part rendering\r
77 functions have access to full Content-Type value.  In particular, this\r
78 is important for `notmuch-show-mm-display-part-inline' which uses\r
79 `mm-display-part' to display parts that notmuch-show does not handle.\r
80 \r
81 Expected results for the tests are updated accordingly.\r
82 ---\r
83 \r
84 Changes in v2 since v1:\r
85 \r
86 * Use "text/plain; charset=us-ascii" for default Content-Type value in\r
87   `notmuch-show-get-content-type' function as defined in RFC 2045.\r
88 \r
89 \r
90  emacs/notmuch-show.el |   29 +++++++++++++++++++----------\r
91  notmuch-show.c        |   14 ++++++++++++--\r
92  test/crypto           |   23 ++++++++---------------\r
93  test/json             |    6 +++---\r
94  test/maildir-sync     |    1 -\r
95  test/multipart        |   36 ++++++++++++++++++------------------\r
96  6 files changed, 60 insertions(+), 49 deletions(-)\r
97 \r
98 diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el\r
99 index d5c95d8..cb801ae 100644\r
100 --- a/emacs/notmuch-show.el\r
101 +++ b/emacs/notmuch-show.el\r
102 @@ -261,120 +261,120 @@ message at DEPTH in the current thread."\r
103                (if (and header-value\r
104                         (not (string-equal "" header-value)))\r
105                    (notmuch-show-insert-header header header-value))))\r
106           notmuch-message-headers)\r
107      (save-excursion\r
108        (save-restriction\r
109         (narrow-to-region start (point-max))\r
110         (run-hooks 'notmuch-show-markup-headers-hook)))))\r
111  \r
112  (define-button-type 'notmuch-show-part-button-type\r
113    'action 'notmuch-show-part-button-action\r
114    'follow-link t\r
115    'face 'message-mml)\r
116  \r
117  (defun notmuch-show-insert-part-header (nth content-type declared-type &optional name comment)\r
118    (let ((button))\r
119      (setq button\r
120           (insert-button\r
121            (concat "[ "\r
122                    (if name (concat name ": ") "")\r
123 -                  declared-type\r
124 -                  (if (not (string-equal declared-type content-type))\r
125 -                      (concat " (as " content-type ")")\r
126 +                  (car declared-type)\r
127 +                  (if (not (string-equal (car declared-type) (car content-type)))\r
128 +                      (concat " (as " (car content-type) ")")\r
129                      "")\r
130                    (or comment "")\r
131                    " ]")\r
132            :type 'notmuch-show-part-button-type\r
133            :notmuch-part nth\r
134            :notmuch-filename name))\r
135      (insert "\n")\r
136      ;; return button\r
137      button))\r
138  \r
139  ;; Functions handling particular MIME parts.\r
140  \r
141  (defun notmuch-show-save-part (message-id nth &optional filename)\r
142    (let ((process-crypto notmuch-show-process-crypto))\r
143      (with-temp-buffer\r
144        (setq notmuch-show-process-crypto process-crypto)\r
145        ;; Always acquires the part via `notmuch part', even if it is\r
146        ;; available in the JSON output.\r
147        (insert (notmuch-show-get-bodypart-internal message-id nth))\r
148        (let ((file (read-file-name\r
149                    "Filename to save as: "\r
150                    (or mailcap-download-directory "~/")\r
151                    nil nil\r
152                    filename)))\r
153         ;; Don't re-compress .gz & al.  Arguably we should make\r
154         ;; `file-name-handler-alist' nil, but that would chop\r
155         ;; ange-ftp, which is reasonable to use here.\r
156         (mm-write-region (point-min) (point-max) file nil nil nil 'no-conversion t)))))\r
157  \r
158  (defun notmuch-show-mm-display-part-inline (msg part content-type content)\r
159    "Use the mm-decode/mm-view functions to display a part in the\r
160  current buffer, if possible."\r
161    (let ((display-buffer (current-buffer)))\r
162      (with-temp-buffer\r
163        (insert content)\r
164 -      (let ((handle (mm-make-handle (current-buffer) (list content-type))))\r
165 +      (let ((handle (mm-make-handle (current-buffer) content-type)))\r
166         (set-buffer display-buffer)\r
167         (if (and (mm-inlinable-p handle)\r
168                  (mm-inlined-p handle))\r
169             (progn\r
170               (mm-display-part handle)\r
171               t)\r
172           nil)))))\r
173  \r
174  (defvar notmuch-show-multipart/alternative-discouraged\r
175    '(\r
176      ;; Avoid HTML parts.\r
177      "text/html"\r
178      ;; multipart/related usually contain a text/html part and some associated graphics.\r
179      "multipart/related"\r
180      ))\r
181  \r
182  (defun notmuch-show-multipart/*-to-list (part)\r
183 -  (mapcar '(lambda (inner-part) (plist-get inner-part :content-type))\r
184 +  (mapcar '(lambda (inner-part) (car (notmuch-show-get-content-type inner-part)))\r
185           (plist-get part :content)))\r
186  \r
187  (defun notmuch-show-multipart/alternative-choose (types)\r
188    ;; Based on `mm-preferred-alternative-precedence'.\r
189    (let ((seq types))\r
190      (dolist (pref (reverse notmuch-show-multipart/alternative-discouraged))\r
191        (dolist (elem (copy-sequence seq))\r
192         (when (string-match pref elem)\r
193           (setq seq (nconc (delete elem seq) (list elem))))))\r
194      seq))\r
195  \r
196  (defun notmuch-show-insert-part-multipart/alternative (msg part content-type nth depth declared-type)\r
197    (notmuch-show-insert-part-header nth declared-type content-type nil)\r
198    (let ((chosen-type (car (notmuch-show-multipart/alternative-choose (notmuch-show-multipart/*-to-list part))))\r
199         (inner-parts (plist-get part :content))\r
200         (start (point)))\r
201      ;; This inserts all parts of the chosen type rather than just one,\r
202      ;; but it's not clear that this is the wrong thing to do - which\r
203      ;; should be chosen if there are more than one that match?\r
204      (mapc (lambda (inner-part)\r
205 -           (let ((inner-type (plist-get inner-part :content-type)))\r
206 +           (let ((inner-type (notmuch-show-get-content-type inner-part)))\r
207               (if (or notmuch-show-all-multipart/alternative-parts\r
208 -                     (string= chosen-type inner-type))\r
209 +                     (string= chosen-type (car inner-type)))\r
210                   (notmuch-show-insert-bodypart msg inner-part depth)\r
211                 (notmuch-show-insert-part-header (plist-get inner-part :id) inner-type inner-type nil " (not shown)"))))\r
212           inner-parts)\r
213  \r
214      (when notmuch-show-indent-multipart\r
215        (indent-rigidly start (point) 1)))\r
216    t)\r
217  \r
218  (defun notmuch-show-setup-w3m ()\r
219    "Instruct w3m how to retrieve content from a \"related\" part of a message."\r
220    (interactive)\r
221    (if (boundp 'w3m-cid-retrieve-function-alist)\r
222      (unless (assq 'notmuch-show-mode w3m-cid-retrieve-function-alist)\r
223        (push (cons 'notmuch-show-mode 'notmuch-show-w3m-cid-retrieve)\r
224             w3m-cid-retrieve-function-alist)))\r
225    (setq mm-inline-text-html-with-images t))\r
226  \r
227  (defvar w3m-current-buffer) ;; From `w3m.el'.\r
228  (defvar notmuch-show-w3m-cid-store nil)\r
229  (make-variable-buffer-local 'notmuch-show-w3m-cid-store)\r
230 @@ -557,41 +557,42 @@ current buffer, if possible."\r
231               (set-buffer (get-file-buffer file))\r
232               (setq result (buffer-substring (point-min) (point-max)))\r
233               (set-buffer-modified-p nil)\r
234               (kill-buffer (current-buffer))\r
235               (delete-file file)\r
236               result)))\r
237    t)\r
238  \r
239  (defun notmuch-show-insert-part-application/octet-stream (msg part content-type nth depth declared-type)\r
240    ;; If we can deduce a MIME type from the filename of the attachment,\r
241    ;; do so and pass it on to the handler for that type.\r
242    (if (plist-get part :filename)\r
243        (let ((extension (file-name-extension (plist-get part :filename)))\r
244             mime-type)\r
245         (if extension\r
246             (progn\r
247               (mailcap-parse-mimetypes)\r
248               (setq mime-type (mailcap-extension-to-mime extension))\r
249               (if (and mime-type\r
250                        (not (string-equal mime-type "application/octet-stream")))\r
251 -                 (notmuch-show-insert-bodypart-internal msg part mime-type nth depth content-type)\r
252 +                 (notmuch-show-insert-bodypart-internal msg part (list mime-type)\r
253 +                                                        nth depth content-type)\r
254                 nil))\r
255           nil))))\r
256  \r
257  (defun notmuch-show-insert-part-application/* (msg part content-type nth depth declared-type\r
258  )\r
259    ;; do not render random "application" parts\r
260    (notmuch-show-insert-part-header nth content-type declared-type (plist-get part :filename)))\r
261  \r
262  (defun notmuch-show-insert-part-*/* (msg part content-type nth depth declared-type)\r
263    ;; This handler _must_ succeed - it is the handler of last resort.\r
264    (notmuch-show-insert-part-header nth content-type declared-type (plist-get part :filename))\r
265    (let ((content (notmuch-show-get-bodypart-content msg part nth)))\r
266      (if content\r
267         (notmuch-show-mm-display-part-inline msg part content-type content)))\r
268    t)\r
269  \r
270  ;; Functions for determining how to handle MIME parts.\r
271  \r
272  (defun notmuch-show-split-content-type (content-type)\r
273    (split-string content-type "/"))\r
274 @@ -618,51 +619,51 @@ current buffer, if possible."\r
275  (defun notmuch-show-get-bodypart-internal (message-id part-number)\r
276    (let ((args '("show" "--format=raw"))\r
277         (part-arg (format "--part=%s" part-number)))\r
278      (setq args (append args (list part-arg)))\r
279      (if notmuch-show-process-crypto\r
280         (setq args (append args '("--decrypt"))))\r
281      (setq args (append args (list message-id)))\r
282      (with-temp-buffer\r
283        (let ((coding-system-for-read 'no-conversion))\r
284         (progn\r
285           (apply 'call-process (append (list notmuch-command nil (list t nil) nil) args))\r
286           (buffer-string))))))\r
287  \r
288  (defun notmuch-show-get-bodypart-content (msg part nth)\r
289    (or (plist-get part :content)\r
290        (notmuch-show-get-bodypart-internal (concat "id:" (plist-get msg :id)) nth)))\r
291  \r
292  ;; \f\r
293 \r
294  \r
295  (defun notmuch-show-insert-bodypart-internal (msg part content-type nth depth declared-type)\r
296 -  (let ((handlers (notmuch-show-handlers-for content-type)))\r
297 +  (let ((handlers (notmuch-show-handlers-for (car content-type))))\r
298      ;; Run the content handlers until one of them returns a non-nil\r
299      ;; value.\r
300      (while (and handlers\r
301                 (not (funcall (car handlers) msg part content-type nth depth declared-type)))\r
302        (setq handlers (cdr handlers))))\r
303    t)\r
304  \r
305  (defun notmuch-show-insert-bodypart (msg part depth)\r
306    "Insert the body part PART at depth DEPTH in the current thread."\r
307 -  (let ((content-type (downcase (plist-get part :content-type)))\r
308 +  (let ((content-type (notmuch-show-get-content-type part))\r
309         (nth (plist-get part :id)))\r
310      (notmuch-show-insert-bodypart-internal msg part content-type nth depth content-type))\r
311    ;; Some of the body part handlers leave point somewhere up in the\r
312    ;; part, so we make sure that we're down at the end.\r
313    (goto-char (point-max))\r
314    ;; Ensure that the part ends with a carriage return.\r
315    (if (not (bolp))\r
316        (insert "\n")))\r
317  \r
318  (defun notmuch-show-insert-body (msg body depth)\r
319    "Insert the body BODY at depth DEPTH in the current thread."\r
320    (mapc '(lambda (part) (notmuch-show-insert-bodypart msg part depth)) body))\r
321  \r
322  (defun notmuch-show-make-symbol (type)\r
323    (make-symbol (concat "notmuch-show-" type)))\r
324  \r
325  (defun notmuch-show-strip-re (string)\r
326    (replace-regexp-in-string "\\([Rr]e: *\\)+" "" string))\r
327  \r
328  (defvar notmuch-show-previous-subject "")\r
329 @@ -1054,40 +1055,48 @@ All currently available key bindings:\r
330    (save-excursion\r
331      (notmuch-show-move-to-message-top)\r
332      (get-text-property (point) :notmuch-message-properties)))\r
333  \r
334  (defun notmuch-show-set-prop (prop val &optional props)\r
335    (let ((inhibit-read-only t)\r
336         (props (or props\r
337                    (notmuch-show-get-message-properties))))\r
338      (plist-put props prop val)\r
339      (notmuch-show-set-message-properties props)))\r
340  \r
341  (defun notmuch-show-get-prop (prop &optional props)\r
342    (let ((props (or props\r
343                    (notmuch-show-get-message-properties))))\r
344      (plist-get props prop)))\r
345  \r
346  (defun notmuch-show-get-message-id ()\r
347    "Return the message id of the current message."\r
348    (concat "id:\"" (notmuch-show-get-prop :id) "\""))\r
349  \r
350 +(defun notmuch-show-get-content-type (&optional props)\r
351 +  "Return parsed Content-Type of the given message, or part, or\r
352 +current message if no `props` is given.  If there is no\r
353 +Content-Type header, it defaults to\r
354 +\"text/plain; charset=us-ascii\" as defined in RFC 2045."\r
355 +  (mail-header-parse-content-type (or (notmuch-show-get-prop :content-type props)\r
356 +                                     "text/plain; charset=us-ascii")))\r
357 +\r
358  ;; dme: Would it make sense to use a macro for many of these?\r
359  \r
360  (defun notmuch-show-get-filename ()\r
361    "Return the filename of the current message."\r
362    (notmuch-show-get-prop :filename))\r
363  \r
364  (defun notmuch-show-get-header (header)\r
365    "Return the named header of the current message, if any."\r
366    (plist-get (notmuch-show-get-prop :headers) header))\r
367  \r
368  (defun notmuch-show-get-cc ()\r
369    (notmuch-show-get-header :Cc))\r
370  \r
371  (defun notmuch-show-get-date ()\r
372    (notmuch-show-get-header :Date))\r
373  \r
374  (defun notmuch-show-get-from ()\r
375    (notmuch-show-get-header :From))\r
376  \r
377  (defun notmuch-show-get-subject ()\r
378 diff --git a/notmuch-show.c b/notmuch-show.c\r
379 index 603992a..da3e87f 100644\r
380 --- a/notmuch-show.c\r
381 +++ b/notmuch-show.c\r
382 @@ -3,40 +3,42 @@\r
383   * Copyright Â© 2009 Carl Worth\r
384   *\r
385   * This program is free software: you can redistribute it and/or modify\r
386   * it under the terms of the GNU General Public License as published by\r
387   * the Free Software Foundation, either version 3 of the License, or\r
388   * (at your option) any later version.\r
389   *\r
390   * This program is distributed in the hope that it will be useful,\r
391   * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
392   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
393   * GNU General Public License for more details.\r
394   *\r
395   * You should have received a copy of the GNU General Public License\r
396   * along with this program.  If not, see http://www.gnu.org/licenses/ .\r
397   *\r
398   * Author: Carl Worth <cworth@cworth.org>\r
399   */\r
400  \r
401  #include "notmuch-client.h"\r
402  \r
403 +static const char HEADER_CONTENT_TYPE[] = "Content-Type";\r
404 +\r
405  static void\r
406  format_message_text (unused (const void *ctx),\r
407                      notmuch_message_t *message,\r
408                      int indent);\r
409  static void\r
410  format_headers_text (const void *ctx,\r
411                      notmuch_message_t *message);\r
412  \r
413  static void\r
414  format_headers_message_part_text (GMimeMessage *message);\r
415  \r
416  static void\r
417  format_part_start_text (GMimeObject *part,\r
418                         int *part_count);\r
419  \r
420  static void\r
421  format_part_content_text (GMimeObject *part);\r
422  \r
423  static void\r
424  format_part_end_text (GMimeObject *part);\r
425 @@ -640,42 +642,50 @@ format_part_sigstatus_json (const GMimeSignatureValidity* validity)\r
426         }\r
427  \r
428         printf ("}");\r
429         signer = signer->next;\r
430      }\r
431  \r
432      printf ("]");\r
433  \r
434      talloc_free (ctx_quote);\r
435  }\r
436  \r
437  static void\r
438  format_part_content_json (GMimeObject *part)\r
439  {\r
440      GMimeContentType *content_type = g_mime_object_get_content_type (GMIME_OBJECT (part));\r
441      GMimeStream *stream_memory = g_mime_stream_mem_new ();\r
442      const char *cid = g_mime_object_get_content_id (part);\r
443      void *ctx = talloc_new (NULL);\r
444      GByteArray *part_content;\r
445  \r
446 -    printf (", \"content-type\": %s",\r
447 -           json_quote_str (ctx, g_mime_content_type_to_string (content_type)));\r
448 +    {\r
449 +       /* Output full Content-Type header value,\r
450 +        * g_mime_content_type_to_string(3) does not include\r
451 +        * parameters.  Content-Type header may be missing,\r
452 +        * g_mime_object_get_content_type(3) defaults to "text/plain"\r
453 +        * in this case. */\r
454 +       const char *const h = g_mime_object_get_header (part, HEADER_CONTENT_TYPE);\r
455 +       if (h)\r
456 +           printf (", \"content-type\": %s", json_quote_str (ctx, h));\r
457 +    }\r
458  \r
459      if (cid != NULL)\r
460             printf(", \"content-id\": %s", json_quote_str (ctx, cid));\r
461  \r
462      if (GMIME_IS_PART (part))\r
463      {\r
464         const char *filename = g_mime_part_get_filename (GMIME_PART (part));\r
465         if (filename)\r
466             printf (", \"filename\": %s", json_quote_str (ctx, filename));\r
467      }\r
468  \r
469      if (g_mime_content_type_is_type (content_type, "text", "*") &&\r
470         !g_mime_content_type_is_type (content_type, "text", "html"))\r
471      {\r
472         show_text_part_content (part, stream_memory);\r
473         part_content = g_mime_stream_mem_get_byte_array (GMIME_STREAM_MEM (stream_memory));\r
474  \r
475         printf (", \"content\": %s", json_quote_chararray (ctx, (char *) part_content->data, part_content->len));\r
476      }\r
477      else if (g_mime_content_type_is_type (content_type, "multipart", "*"))\r
478 diff --git a/test/crypto b/test/crypto\r
479 index 0af4aa8..b923d22 100755\r
480 --- a/test/crypto\r
481 +++ b/test/crypto\r
482 @@ -40,111 +40,108 @@ test_expect_success 'emacs delivery of signed message' \\r
483  test_begin_subtest "signature verification"\r
484  output=$(notmuch show --format=json --verify subject:"test signed message 001" \\r
485      | notmuch_json_show_sanitize \\r
486      | sed -e 's|"created": [1234567890]*|"created": 946728000|')\r
487  expected='[[[{"id": "XXXXX",\r
488   "match": true,\r
489   "filename": "YYYYY",\r
490   "timestamp": 946728000,\r
491   "date_relative": "2000-01-01",\r
492   "tags": ["inbox","signed"],\r
493   "headers": {"Subject": "test signed message 001",\r
494   "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",\r
495   "To": "test_suite@notmuchmail.org",\r
496   "Cc": "",\r
497   "Bcc": "",\r
498   "Date": "01 Jan 2000 12:00:00 -0000"},\r
499   "body": [{"id": 1,\r
500   "sigstatus": [{"status": "good",\r
501   "fingerprint": "'$FINGERPRINT'",\r
502   "created": 946728000}],\r
503 - "content-type": "multipart/signed",\r
504 + "content-type": "multipart/signed; boundary=\"=-=-=\";\tmicalg=pgp-sha1; protocol=\"application/pgp-signature\"",\r
505   "content": [{"id": 2,\r
506 - "content-type": "text/plain",\r
507   "content": "This is a test signed message.\n"},\r
508   {"id": 3,\r
509   "content-type": "application/pgp-signature"}]}]},\r
510   []]]]'\r
511  test_expect_equal \\r
512      "$output" \\r
513      "$expected"\r
514  \r
515  test_begin_subtest "signature verification with full owner trust"\r
516  # give the key full owner trust\r
517  echo "${FINGERPRINT}:6:" | gpg --no-tty --import-ownertrust >>"$GNUPGHOME"/trust.log 2>&1\r
518  gpg --no-tty --check-trustdb >>"$GNUPGHOME"/trust.log 2>&1\r
519  output=$(notmuch show --format=json --verify subject:"test signed message 001" \\r
520      | notmuch_json_show_sanitize \\r
521      | sed -e 's|"created": [1234567890]*|"created": 946728000|')\r
522  expected='[[[{"id": "XXXXX",\r
523   "match": true,\r
524   "filename": "YYYYY",\r
525   "timestamp": 946728000,\r
526   "date_relative": "2000-01-01",\r
527   "tags": ["inbox","signed"],\r
528   "headers": {"Subject": "test signed message 001",\r
529   "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",\r
530   "To": "test_suite@notmuchmail.org",\r
531   "Cc": "",\r
532   "Bcc": "",\r
533   "Date": "01 Jan 2000 12:00:00 -0000"},\r
534   "body": [{"id": 1,\r
535   "sigstatus": [{"status": "good",\r
536   "fingerprint": "'$FINGERPRINT'",\r
537   "created": 946728000,\r
538   "userid": " Notmuch Test Suite <test_suite@notmuchmail.org> (INSECURE!)"}],\r
539 - "content-type": "multipart/signed",\r
540 + "content-type": "multipart/signed; boundary=\"=-=-=\";\tmicalg=pgp-sha1; protocol=\"application/pgp-signature\"",\r
541   "content": [{"id": 2,\r
542 - "content-type": "text/plain",\r
543   "content": "This is a test signed message.\n"},\r
544   {"id": 3,\r
545   "content-type": "application/pgp-signature"}]}]},\r
546   []]]]'\r
547  test_expect_equal \\r
548      "$output" \\r
549      "$expected"\r
550  \r
551  test_begin_subtest "signature verification with signer key unavailable"\r
552  # move the gnupghome temporarily out of the way\r
553  mv "${GNUPGHOME}"{,.bak}\r
554  output=$(notmuch show --format=json --verify subject:"test signed message 001" \\r
555      | notmuch_json_show_sanitize \\r
556      | sed -e 's|"created": [1234567890]*|"created": 946728000|')\r
557  expected='[[[{"id": "XXXXX",\r
558   "match": true,\r
559   "filename": "YYYYY",\r
560   "timestamp": 946728000,\r
561   "date_relative": "2000-01-01",\r
562   "tags": ["inbox","signed"],\r
563   "headers": {"Subject": "test signed message 001",\r
564   "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",\r
565   "To": "test_suite@notmuchmail.org",\r
566   "Cc": "",\r
567   "Bcc": "",\r
568   "Date": "01 Jan 2000 12:00:00 -0000"},\r
569   "body": [{"id": 1,\r
570   "sigstatus": [{"status": "error",\r
571   "keyid": "'$(echo $FINGERPRINT | cut -c 25-)'",\r
572   "errors": 2}],\r
573 - "content-type": "multipart/signed",\r
574 + "content-type": "multipart/signed; boundary=\"=-=-=\";\tmicalg=pgp-sha1; protocol=\"application/pgp-signature\"",\r
575   "content": [{"id": 2,\r
576 - "content-type": "text/plain",\r
577   "content": "This is a test signed message.\n"},\r
578   {"id": 3,\r
579   "content-type": "application/pgp-signature"}]}]},\r
580   []]]]'\r
581  test_expect_equal \\r
582      "$output" \\r
583      "$expected"\r
584  mv "${GNUPGHOME}"{.bak,}\r
585  \r
586  # create a test encrypted message with attachment\r
587  cat <<EOF >TESTATTACHMENT\r
588  This is a test file.\r
589  EOF\r
590  test_expect_success 'emacs delivery of encrypted message with attachment' \\r
591  'emacs_deliver_message \\r
592      "test encrypted message 001" \\r
593      "This is a test encrypted message.\n" \\r
594      "(mml-attach-file \"TESTATTACHMENT\") (mml-secure-message-encrypt)"'\r
595  \r
596  test_begin_subtest "decryption, --format=text"\r
597 @@ -181,138 +178,135 @@ test_expect_equal \\r
598  \r
599  test_begin_subtest "decryption, --format=json"\r
600  output=$(notmuch show --format=json --decrypt subject:"test encrypted message 001" \\r
601      | notmuch_json_show_sanitize \\r
602      | sed -e 's|"created": [1234567890]*|"created": 946728000|')\r
603  expected='[[[{"id": "XXXXX",\r
604   "match": true,\r
605   "filename": "YYYYY",\r
606   "timestamp": 946728000,\r
607   "date_relative": "2000-01-01",\r
608   "tags": ["encrypted","inbox"],\r
609   "headers": {"Subject": "test encrypted message 001",\r
610   "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",\r
611   "To": "test_suite@notmuchmail.org",\r
612   "Cc": "",\r
613   "Bcc": "",\r
614   "Date": "01 Jan 2000 12:00:00 -0000"},\r
615   "body": [{"id": 1,\r
616   "encstatus": [{"status": "good"}],\r
617   "sigstatus": [],\r
618 - "content-type": "multipart/encrypted",\r
619 + "content-type": "multipart/encrypted; boundary=\"==-=-=\";\tprotocol=\"application/pgp-encrypted\"",\r
620   "content": [{"id": 2,\r
621   "content-type": "application/pgp-encrypted"},\r
622   {"id": 3,\r
623 - "content-type": "multipart/mixed",\r
624 + "content-type": "multipart/mixed; boundary=\"=-=-=\"",\r
625   "content": [{"id": 4,\r
626 - "content-type": "text/plain",\r
627   "content": "This is a test encrypted message.\n"},\r
628   {"id": 5,\r
629   "content-type": "application/octet-stream",\r
630   "filename": "TESTATTACHMENT"}]}]}]},\r
631   []]]]'\r
632  test_expect_equal \\r
633      "$output" \\r
634      "$expected"\r
635  \r
636  test_begin_subtest "decryption, --format=json, --part=4"\r
637  output=$(notmuch show --format=json --part=4 --decrypt subject:"test encrypted message 001" \\r
638      | notmuch_json_show_sanitize \\r
639      | sed -e 's|"created": [1234567890]*|"created": 946728000|')\r
640  expected='{"id": 4,\r
641 - "content-type": "text/plain",\r
642   "content": "This is a test encrypted message.\n"}'\r
643  test_expect_equal \\r
644      "$output" \\r
645      "$expected"\r
646  \r
647  test_begin_subtest "decrypt attachment (--part=5 --format=raw)"\r
648  notmuch show \\r
649      --format=raw \\r
650      --part=5 \\r
651      --decrypt \\r
652      subject:"test encrypted message 001" >OUTPUT\r
653  test_expect_equal_file OUTPUT TESTATTACHMENT\r
654  \r
655  test_begin_subtest "decryption failure with missing key"\r
656  mv "${GNUPGHOME}"{,.bak}\r
657  output=$(notmuch show --format=json --decrypt subject:"test encrypted message 001" \\r
658      | notmuch_json_show_sanitize \\r
659      | sed -e 's|"created": [1234567890]*|"created": 946728000|')\r
660  expected='[[[{"id": "XXXXX",\r
661   "match": true,\r
662   "filename": "YYYYY",\r
663   "timestamp": 946728000,\r
664   "date_relative": "2000-01-01",\r
665   "tags": ["encrypted","inbox"],\r
666   "headers": {"Subject": "test encrypted message 001",\r
667   "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",\r
668   "To": "test_suite@notmuchmail.org",\r
669   "Cc": "",\r
670   "Bcc": "",\r
671   "Date": "01 Jan 2000 12:00:00 -0000"},\r
672   "body": [{"id": 1,\r
673   "encstatus": [{"status": "bad"}],\r
674 - "content-type": "multipart/encrypted",\r
675 + "content-type": "multipart/encrypted; boundary=\"==-=-=\";\tprotocol=\"application/pgp-encrypted\"",\r
676   "content": [{"id": 2,\r
677   "content-type": "application/pgp-encrypted"},\r
678   {"id": 3,\r
679   "content-type": "application/octet-stream"}]}]},\r
680   []]]]'\r
681  test_expect_equal \\r
682      "$output" \\r
683      "$expected"\r
684  mv "${GNUPGHOME}"{.bak,}\r
685  \r
686  test_expect_success 'emacs delivery of encrypted + signed message' \\r
687  'emacs_deliver_message \\r
688      "test encrypted message 002" \\r
689      "This is another test encrypted message.\n" \\r
690      "(mml-secure-message-sign-encrypt)"'\r
691  \r
692  test_begin_subtest "decryption + signature verification"\r
693  output=$(notmuch show --format=json --decrypt subject:"test encrypted message 002" \\r
694      | notmuch_json_show_sanitize \\r
695      | sed -e 's|"created": [1234567890]*|"created": 946728000|')\r
696  expected='[[[{"id": "XXXXX",\r
697   "match": true,\r
698   "filename": "YYYYY",\r
699   "timestamp": 946728000,\r
700   "date_relative": "2000-01-01",\r
701   "tags": ["encrypted","inbox"],\r
702   "headers": {"Subject": "test encrypted message 002",\r
703   "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",\r
704   "To": "test_suite@notmuchmail.org",\r
705   "Cc": "",\r
706   "Bcc": "",\r
707   "Date": "01 Jan 2000 12:00:00 -0000"},\r
708   "body": [{"id": 1,\r
709   "encstatus": [{"status": "good"}],\r
710   "sigstatus": [{"status": "good",\r
711   "fingerprint": "'$FINGERPRINT'",\r
712   "created": 946728000,\r
713   "userid": " Notmuch Test Suite <test_suite@notmuchmail.org> (INSECURE!)"}],\r
714 - "content-type": "multipart/encrypted",\r
715 + "content-type": "multipart/encrypted; boundary=\"=-=-=\";\tprotocol=\"application/pgp-encrypted\"",\r
716   "content": [{"id": 2,\r
717   "content-type": "application/pgp-encrypted"},\r
718   {"id": 3,\r
719 - "content-type": "text/plain",\r
720   "content": "This is another test encrypted message.\n"}]}]},\r
721   []]]]'\r
722  test_expect_equal \\r
723      "$output" \\r
724      "$expected"\r
725  \r
726  test_begin_subtest "reply to encrypted message"\r
727  output=$(notmuch reply --decrypt subject:"test encrypted message 002" \\r
728      | grep -v -e '^In-Reply-To:' -e '^References:')\r
729  expected='From: Notmuch Test Suite <test_suite@notmuchmail.org>\r
730  Subject: Re: test encrypted message 002\r
731  \r
732  On 01 Jan 2000 12:00:00 -0000, Notmuch Test Suite <test_suite@notmuchmail.org> wrote:\r
733  > This is another test encrypted message.'\r
734  test_expect_equal \\r
735      "$output" \\r
736      "$expected"\r
737  \r
738  test_begin_subtest "signature verification with revoked key"\r
739  # generate revocation certificate and load it to revoke key\r
740 @@ -327,32 +321,31 @@ y\r
741      | gpg --no-tty --quiet --import\r
742  output=$(notmuch show --format=json --verify subject:"test signed message 001" \\r
743      | notmuch_json_show_sanitize \\r
744      | sed -e 's|"created": [1234567890]*|"created": 946728000|')\r
745  expected='[[[{"id": "XXXXX",\r
746   "match": true,\r
747   "filename": "YYYYY",\r
748   "timestamp": 946728000,\r
749   "date_relative": "2000-01-01",\r
750   "tags": ["inbox","signed"],\r
751   "headers": {"Subject": "test signed message 001",\r
752   "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",\r
753   "To": "test_suite@notmuchmail.org",\r
754   "Cc": "",\r
755   "Bcc": "",\r
756   "Date": "01 Jan 2000 12:00:00 -0000"},\r
757   "body": [{"id": 1,\r
758   "sigstatus": [{"status": "error",\r
759   "keyid": "6D92612D94E46381",\r
760   "errors": 8}],\r
761 - "content-type": "multipart/signed",\r
762 + "content-type": "multipart/signed; boundary=\"=-=-=\";\tmicalg=pgp-sha1; protocol=\"application/pgp-signature\"",\r
763   "content": [{"id": 2,\r
764 - "content-type": "text/plain",\r
765   "content": "This is a test signed message.\n"},\r
766   {"id": 3,\r
767   "content-type": "application/pgp-signature"}]}]},\r
768   []]]]'\r
769  test_expect_equal \\r
770      "$output" \\r
771      "$expected"\r
772  \r
773  test_done\r
774 diff --git a/test/json b/test/json\r
775 index 592b068..64f35cf 100755\r
776 --- a/test/json\r
777 +++ b/test/json\r
778 @@ -1,50 +1,50 @@\r
779  #!/usr/bin/env bash\r
780  test_description="--format=json output"\r
781  . ./test-lib.sh\r
782  \r
783  test_begin_subtest "Show message: json"\r
784  add_message "[subject]=\"json-show-subject\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"json-show-message\""\r
785  output=$(notmuch show --format=json "json-show-message")\r
786 -test_expect_equal "$output" "[[[{\"id\": \"${gen_msg_id}\", \"match\": true, \"filename\": \"${gen_msg_filename}\", \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"unread\"], \"headers\": {\"Subject\": \"json-show-subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"Cc\": \"\", \"Bcc\": \"\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 -0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"text/plain\", \"content\": \"json-show-message\n\"}]}, []]]]"\r
787 +test_expect_equal "$output" "[[[{\"id\": \"${gen_msg_id}\", \"match\": true, \"filename\": \"${gen_msg_filename}\", \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"unread\"], \"headers\": {\"Subject\": \"json-show-subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"Cc\": \"\", \"Bcc\": \"\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 -0000\"}, \"body\": [{\"id\": 1, \"content\": \"json-show-message\n\"}]}, []]]]"\r
788  \r
789  test_begin_subtest "Search message: json"\r
790  add_message "[subject]=\"json-search-subject\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"json-search-message\""\r
791  output=$(notmuch search --format=json "json-search-message" | notmuch_search_sanitize)\r
792  test_expect_equal "$output" "[{\"thread\": \"XXX\",\r
793  \"timestamp\": 946728000,\r
794  \"matched\": 1,\r
795  \"total\": 1,\r
796  \"authors\": \"Notmuch Test Suite\",\r
797  \"subject\": \"json-search-subject\",\r
798  \"tags\": [\"inbox\", \"unread\"]}]"\r
799  \r
800  test_begin_subtest "Show message: json, utf-8"\r
801  add_message "[subject]=\"json-show-utf8-body-sübjéct\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"jsön-show-méssage\""\r
802  output=$(notmuch show --format=json "jsön-show-méssage")\r
803 -test_expect_equal "$output" "[[[{\"id\": \"${gen_msg_id}\", \"match\": true, \"filename\": \"${gen_msg_filename}\", \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"unread\"], \"headers\": {\"Subject\": \"json-show-utf8-body-sübjéct\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"Cc\": \"\", \"Bcc\": \"\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 -0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"text/plain\", \"content\": \"jsön-show-méssage\n\"}]}, []]]]"\r
804 +test_expect_equal "$output" "[[[{\"id\": \"${gen_msg_id}\", \"match\": true, \"filename\": \"${gen_msg_filename}\", \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"unread\"], \"headers\": {\"Subject\": \"json-show-utf8-body-sübjéct\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"Cc\": \"\", \"Bcc\": \"\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 -0000\"}, \"body\": [{\"id\": 1, \"content\": \"jsön-show-méssage\n\"}]}, []]]]"\r
805  \r
806  test_begin_subtest "Show message: json, inline attachment filename"\r
807  subject='json-show-inline-attachment-filename'\r
808  id="json-show-inline-attachment-filename@notmuchmail.org"\r
809  emacs_deliver_message \\r
810      "$subject" \\r
811      'This is a test message with inline attachment with a filename' \\r
812      "(mml-attach-file \"$TEST_DIRECTORY/README\" nil nil \"inline\")\r
813       (message-goto-eoh)\r
814       (insert \"Message-ID: <$id>\n\")"\r
815  output=$(notmuch show --format=json "id:$id")\r
816  filename=$(notmuch search --output=files "id:$id")\r
817 -test_expect_equal "$output" "[[[{\"id\": \"$id\", \"match\": true, \"filename\": \"$filename\", \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\"], \"headers\": {\"Subject\": \"$subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"test_suite@notmuchmail.org\", \"Cc\": \"\", \"Bcc\": \"\", \"Date\": \"01 Jan 2000 12:00:00 -0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"multipart/mixed\", \"content\": [{\"id\": 2, \"content-type\": \"text/plain\", \"content\": \"This is a test message with inline attachment with a filename\"}, {\"id\": 3, \"content-type\": \"application/octet-stream\", \"filename\": \"README\"}]}]}, []]]]"\r
818 +test_expect_equal "$output" "[[[{\"id\": \"$id\", \"match\": true, \"filename\": \"$filename\", \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\"], \"headers\": {\"Subject\": \"$subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"test_suite@notmuchmail.org\", \"Cc\": \"\", \"Bcc\": \"\", \"Date\": \"01 Jan 2000 12:00:00 -0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"multipart/mixed; boundary=\\\"=-=-=\\\"\", \"content\": [{\"id\": 2, \"content\": \"This is a test message with inline attachment with a filename\"}, {\"id\": 3, \"content-type\": \"application/octet-stream\", \"filename\": \"README\"}]}]}, []]]]"\r
819  \r
820  test_begin_subtest "Search message: json, utf-8"\r
821  add_message "[subject]=\"json-search-utf8-body-sübjéct\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"jsön-search-méssage\""\r
822  output=$(notmuch search --format=json "jsön-search-méssage" | notmuch_search_sanitize)\r
823  test_expect_equal "$output" "[{\"thread\": \"XXX\",\r
824  \"timestamp\": 946728000,\r
825  \"matched\": 1,\r
826  \"total\": 1,\r
827  \"authors\": \"Notmuch Test Suite\",\r
828  \"subject\": \"json-search-utf8-body-sübjéct\",\r
829  \"tags\": [\"inbox\", \"unread\"]}]"\r
830  \r
831  test_done\r
832 diff --git a/test/maildir-sync b/test/maildir-sync\r
833 index a60854f..c7ca22f 100755\r
834 --- a/test/maildir-sync\r
835 +++ b/test/maildir-sync\r
836 @@ -41,41 +41,40 @@ add_message [subject]='"Adding replied tag"' [filename]='adding-replied-tag:2,S'\r
837  notmuch tag +replied subject:"Adding replied tag"\r
838  output=$(cd ${MAIL_DIR}/cur; ls -1 adding-replied*)\r
839  test_expect_equal "$output" "adding-replied-tag:2,RS"\r
840  \r
841  test_begin_subtest "notmuch show works with renamed file (without notmuch new)"\r
842  output=$(notmuch show --format=json id:${gen_msg_id} | filter_show_json)\r
843  test_expect_equal "$output" '[[[{"id": "adding-replied-tag@notmuch-test-suite",\r
844  "match": true,\r
845  "filename": "MAIL_DIR/cur/adding-replied-tag:2,RS",\r
846  "timestamp": 978709437,\r
847  "date_relative": "2001-01-05",\r
848  "tags": ["inbox","replied"],\r
849  "headers": {"Subject": "Adding replied tag",\r
850  "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",\r
851  "To": "Notmuch Test Suite <test_suite@notmuchmail.org>",\r
852  "Cc": "",\r
853  "Bcc": "",\r
854  "Date": "Tue,\r
855  05 Jan 2001 15:43:57 -0000"},\r
856  "body": [{"id": 1,\r
857 -"content-type": "text/plain",\r
858  "content": "This is just a test message (#3)\n"}]},\r
859  []]]]'\r
860  \r
861  test_expect_success 'notmuch reply works with renamed file (without notmuch new)' 'notmuch reply id:${gen_msg_id}'\r
862  \r
863  test_begin_subtest "notmuch new detects no file rename after tag->flag synchronization"\r
864  output=$(NOTMUCH_NEW)\r
865  test_expect_equal "$output" "No new mail."\r
866  \r
867  test_begin_subtest "When read, message moved from new to cur"\r
868  add_message [subject]='"Message to move to cur"' [date]='"Sat, 01 Jan 2000 12:00:00 -0000"' [filename]='message-to-move-to-cur' [dir]=new\r
869  notmuch tag -unread subject:"Message to move to cur"\r
870  output=$(cd "$MAIL_DIR/cur"; ls message-to-move*)\r
871  test_expect_equal "$output" "message-to-move-to-cur:2,S"\r
872  \r
873  test_begin_subtest "No rename should be detected by notmuch new"\r
874  output=$(NOTMUCH_NEW)\r
875  test_expect_equal "$output" "No new mail."\r
876  # (*) If notmuch new was not run we've got "Processed 1 file in almost\r
877  # no time" here. The reason is that removing unread tag in a previous\r
878 diff --git a/test/multipart b/test/multipart\r
879 index f83526b..ca4db71 100755\r
880 --- a/test/multipart\r
881 +++ b/test/multipart\r
882 @@ -306,140 +306,140 @@ test_expect_equal_file OUTPUT EXPECTED\r
883  \r
884  test_begin_subtest "--format=text --part=9, pgp signature (unverified)"\r
885  notmuch show --format=text --part=9 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT\r
886  cat <<EOF >EXPECTED\r
887  \f\r
888 part{ ID: 9, Content-type: application/pgp-signature\r
889  Non-text part: application/pgp-signature\r
890  \f\r
891 part}\r
892  EOF\r
893  test_expect_equal_file OUTPUT EXPECTED\r
894  \r
895  test_expect_success \\r
896      "--format=text --part=8, no part, expect error" \\r
897      "notmuch show --format=text --part=8 'id:87liy5ap00.fsf@yoom.home.cworth.org'"\r
898  \r
899  test_begin_subtest "--format=json --part=0, full message"\r
900  notmuch show --format=json --part=0 'id:87liy5ap00.fsf@yoom.home.cworth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT\r
901  echo >>OUTPUT # expect *no* newline at end of output\r
902  cat <<EOF >EXPECTED\r
903  \r
904  {"id": "87liy5ap00.fsf@yoom.home.cworth.org", "match": true, "filename": "${MAIL_DIR}/multipart", "timestamp": 978709437, "date_relative": "2001-01-05", "tags": ["attachment","inbox","signed","unread"], "headers": {"Subject": "Multipart message", "From": "Carl Worth <cworth@cworth.org>", "To": "cworth@cworth.org", "Cc": "", "Bcc": "", "Date": "Fri, 05 Jan 2001 15:43:57 +0000"}, "body": [\r
905 -{"id": 1, "content-type": "multipart/signed", "content": [\r
906 -{"id": 2, "content-type": "multipart/mixed", "content": [\r
907 +{"id": 1, "content-type": "multipart/signed; boundary=\"==-=-=\";\tmicalg=pgp-sha1; protocol=\"application/pgp-signature\"", "content": [\r
908 +{"id": 2, "content-type": "multipart/mixed; boundary=\"=-=-=\"", "content": [\r
909  {"id": 3, "content-type": "message/rfc822", "content": [{"headers": {"From": "Carl Worth <cworth@cworth.org>", "To": "cworth@cworth.org", "Subject": "html message", "Date": "Fri, 05 Jan 2001 15:42:57 +0000"}, "body": [\r
910 -{"id": 4, "content-type": "multipart/alternative", "content": [\r
911 +{"id": 4, "content-type": "multipart/alternative; boundary=\"==-=-==\"", "content": [\r
912  {"id": 5, "content-type": "text/html"}, \r
913  {"id": 6, "content-type": "text/plain", "content": "This is an embedded message, with a multipart/alternative part.\n"}]}]}]}, \r
914 -{"id": 7, "content-type": "text/plain", "filename": "attachment", "content": "This is a text attachment.\n"}, \r
915 -{"id": 8, "content-type": "text/plain", "content": "And this message is signed.\n\n-Carl\n"}]}, \r
916 +{"id": 7, "filename": "attachment", "content": "This is a text attachment.\n"}, \r
917 +{"id": 8, "content": "And this message is signed.\n\n-Carl\n"}]}, \r
918  {"id": 9, "content-type": "application/pgp-signature"}]}]}\r
919  EOF\r
920  test_expect_equal_file OUTPUT EXPECTED\r
921  \r
922  test_begin_subtest "--format=json --part=1, message body"\r
923  notmuch show --format=json --part=1 'id:87liy5ap00.fsf@yoom.home.cworth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT\r
924  echo >>OUTPUT # expect *no* newline at end of output\r
925  cat <<EOF >EXPECTED\r
926  \r
927 -{"id": 1, "content-type": "multipart/signed", "content": [\r
928 -{"id": 2, "content-type": "multipart/mixed", "content": [\r
929 +{"id": 1, "content-type": "multipart/signed; boundary=\"==-=-=\";\tmicalg=pgp-sha1; protocol=\"application/pgp-signature\"", "content": [\r
930 +{"id": 2, "content-type": "multipart/mixed; boundary=\"=-=-=\"", "content": [\r
931  {"id": 3, "content-type": "message/rfc822", "content": [{"headers": {"From": "Carl Worth <cworth@cworth.org>", "To": "cworth@cworth.org", "Subject": "html message", "Date": "Fri, 05 Jan 2001 15:42:57 +0000"}, "body": [\r
932 -{"id": 4, "content-type": "multipart/alternative", "content": [\r
933 +{"id": 4, "content-type": "multipart/alternative; boundary=\"==-=-==\"", "content": [\r
934  {"id": 5, "content-type": "text/html"}, \r
935  {"id": 6, "content-type": "text/plain", "content": "This is an embedded message, with a multipart/alternative part.\n"}]}]}]}, \r
936 -{"id": 7, "content-type": "text/plain", "filename": "attachment", "content": "This is a text attachment.\n"}, \r
937 -{"id": 8, "content-type": "text/plain", "content": "And this message is signed.\n\n-Carl\n"}]}, \r
938 +{"id": 7, "filename": "attachment", "content": "This is a text attachment.\n"}, \r
939 +{"id": 8, "content": "And this message is signed.\n\n-Carl\n"}]}, \r
940  {"id": 9, "content-type": "application/pgp-signature"}]}\r
941  EOF\r
942  test_expect_equal_file OUTPUT EXPECTED\r
943  \r
944  test_begin_subtest "--format=json --part=2, multipart/mixed"\r
945  notmuch show --format=json --part=2 'id:87liy5ap00.fsf@yoom.home.cworth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT\r
946  echo >>OUTPUT # expect *no* newline at end of output\r
947  cat <<EOF >EXPECTED\r
948  \r
949 -{"id": 2, "content-type": "multipart/mixed", "content": [\r
950 +{"id": 2, "content-type": "multipart/mixed; boundary=\"=-=-=\"", "content": [\r
951  {"id": 3, "content-type": "message/rfc822", "content": [{"headers": {"From": "Carl Worth <cworth@cworth.org>", "To": "cworth@cworth.org", "Subject": "html message", "Date": "Fri, 05 Jan 2001 15:42:57 +0000"}, "body": [\r
952 -{"id": 4, "content-type": "multipart/alternative", "content": [\r
953 +{"id": 4, "content-type": "multipart/alternative; boundary=\"==-=-==\"", "content": [\r
954  {"id": 5, "content-type": "text/html"}, \r
955  {"id": 6, "content-type": "text/plain", "content": "This is an embedded message, with a multipart/alternative part.\n"}]}]}]}, \r
956 -{"id": 7, "content-type": "text/plain", "filename": "attachment", "content": "This is a text attachment.\n"}, \r
957 -{"id": 8, "content-type": "text/plain", "content": "And this message is signed.\n\n-Carl\n"}]}\r
958 +{"id": 7, "filename": "attachment", "content": "This is a text attachment.\n"}, \r
959 +{"id": 8, "content": "And this message is signed.\n\n-Carl\n"}]}\r
960  EOF\r
961  test_expect_equal_file OUTPUT EXPECTED\r
962  \r
963  test_begin_subtest "--format=json --part=3, rfc822 part"\r
964  notmuch show --format=json --part=3 'id:87liy5ap00.fsf@yoom.home.cworth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT\r
965  echo >>OUTPUT # expect *no* newline at end of output\r
966  cat <<EOF >EXPECTED\r
967  \r
968  {"id": 3, "content-type": "message/rfc822", "content": [{"headers": {"From": "Carl Worth <cworth@cworth.org>", "To": "cworth@cworth.org", "Subject": "html message", "Date": "Fri, 05 Jan 2001 15:42:57 +0000"}, "body": [\r
969 -{"id": 4, "content-type": "multipart/alternative", "content": [\r
970 +{"id": 4, "content-type": "multipart/alternative; boundary=\"==-=-==\"", "content": [\r
971  {"id": 5, "content-type": "text/html"}, \r
972  {"id": 6, "content-type": "text/plain", "content": "This is an embedded message, with a multipart/alternative part.\n"}]}]}]}\r
973  EOF\r
974  test_expect_equal_file OUTPUT EXPECTED\r
975  \r
976  test_begin_subtest "--format=json --part=4, rfc822's multipart/alternative"\r
977  notmuch show --format=json --part=4 'id:87liy5ap00.fsf@yoom.home.cworth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT\r
978  echo >>OUTPUT # expect *no* newline at end of output\r
979  cat <<EOF >EXPECTED\r
980  \r
981 -{"id": 4, "content-type": "multipart/alternative", "content": [\r
982 +{"id": 4, "content-type": "multipart/alternative; boundary=\"==-=-==\"", "content": [\r
983  {"id": 5, "content-type": "text/html"}, \r
984  {"id": 6, "content-type": "text/plain", "content": "This is an embedded message, with a multipart/alternative part.\n"}]}\r
985  EOF\r
986  test_expect_equal_file OUTPUT EXPECTED\r
987  \r
988  test_begin_subtest "--format=json --part=5, rfc822's html part"\r
989  notmuch show --format=json --part=5 'id:87liy5ap00.fsf@yoom.home.cworth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT\r
990  echo >>OUTPUT # expect *no* newline at end of output\r
991  cat <<EOF >EXPECTED\r
992  \r
993  {"id": 5, "content-type": "text/html"}\r
994  EOF\r
995  test_expect_equal_file OUTPUT EXPECTED\r
996  \r
997  test_begin_subtest "--format=json --part=6, rfc822's text part"\r
998  notmuch show --format=json --part=6 'id:87liy5ap00.fsf@yoom.home.cworth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT\r
999  echo >>OUTPUT # expect *no* newline at end of output\r
1000  cat <<EOF >EXPECTED\r
1001  \r
1002  {"id": 6, "content-type": "text/plain", "content": "This is an embedded message, with a multipart/alternative part.\n"}\r
1003  EOF\r
1004  test_expect_equal_file OUTPUT EXPECTED\r
1005  \r
1006  test_begin_subtest "--format=json --part=7, inline attachment"\r
1007  notmuch show --format=json --part=7 'id:87liy5ap00.fsf@yoom.home.cworth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT\r
1008  echo >>OUTPUT # expect *no* newline at end of output\r
1009  cat <<EOF >EXPECTED\r
1010  \r
1011 -{"id": 7, "content-type": "text/plain", "filename": "attachment", "content": "This is a text attachment.\n"}\r
1012 +{"id": 7, "filename": "attachment", "content": "This is a text attachment.\n"}\r
1013  EOF\r
1014  test_expect_equal_file OUTPUT EXPECTED\r
1015  \r
1016  test_begin_subtest "--format=json --part=8, plain text part"\r
1017  notmuch show --format=json --part=8 'id:87liy5ap00.fsf@yoom.home.cworth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT\r
1018  echo >>OUTPUT # expect *no* newline at end of output\r
1019  cat <<EOF >EXPECTED\r
1020  \r
1021 -{"id": 8, "content-type": "text/plain", "content": "And this message is signed.\n\n-Carl\n"}\r
1022 +{"id": 8, "content": "And this message is signed.\n\n-Carl\n"}\r
1023  EOF\r
1024  test_expect_equal_file OUTPUT EXPECTED\r
1025  \r
1026  test_begin_subtest "--format=json --part=9, pgp signature (unverified)"\r
1027  notmuch show --format=json --part=9 'id:87liy5ap00.fsf@yoom.home.cworth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT\r
1028  echo >>OUTPUT # expect *no* newline at end of output\r
1029  cat <<EOF >EXPECTED\r
1030  \r
1031  {"id": 9, "content-type": "application/pgp-signature"}\r
1032  EOF\r
1033  test_expect_equal_file OUTPUT EXPECTED\r
1034  \r
1035  test_expect_success \\r
1036      "--format=json --part=10, no part, expect error" \\r
1037      "notmuch show --format=json --part=10 'id:87liy5ap00.fsf@yoom.home.cworth.org'"\r
1038  \r
1039  test_begin_subtest "--format=raw"\r
1040  notmuch show --format=raw 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT\r
1041  test_expect_equal_file OUTPUT "${MAIL_DIR}"/multipart\r
1042  \r
1043 -- \r
1044 1.7.7.3\r
1045 \r