Missing headers when forwarding html message as RFC822
[notmuch-archives.git] / e6 / b86d6a43159508f0ccd9d2d14e8ead0ef6e473
1 Return-Path: <jani@nikula.org>\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 1AC7C431FC2\r
6         for <notmuch@notmuchmail.org>; Sun,  9 Mar 2014 14:41:24 -0700 (PDT)\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
8 X-Amavis-Alert: BAD HEADER SECTION, Duplicate header field: "References"\r
9 X-Spam-Flag: NO\r
10 X-Spam-Score: -0.7\r
11 X-Spam-Level: \r
12 X-Spam-Status: No, score=-0.7 tagged_above=-999 required=5\r
13         tests=[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 siLmyBakeKKZ for <notmuch@notmuchmail.org>;\r
17         Sun,  9 Mar 2014 14:41:17 -0700 (PDT)\r
18 Received: from mail-la0-f53.google.com (mail-la0-f53.google.com\r
19         [209.85.215.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 CF652431FBC\r
22         for <notmuch@notmuchmail.org>; Sun,  9 Mar 2014 14:40:53 -0700 (PDT)\r
23 Received: by mail-la0-f53.google.com with SMTP id b8so4086318lan.26\r
24         for <notmuch@notmuchmail.org>; Sun, 09 Mar 2014 14:40:52 -0700 (PDT)\r
25 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;\r
26         d=1e100.net; s=20130820;\r
27         h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\r
28         :references:in-reply-to:references;\r
29         bh=5P7cWv0MKfmrzhA3hqpPcC8poLNJT/d6bD3EPFCE+vo=;\r
30         b=TEaTWEB9KEE3hUkitsQ7yOipy7SwdEfThMDtdkUIMVDLN+nHLMqt1Ie7sL+CgFn3Rc\r
31         IRflk5iE3O0d5cIC0CWg3HkjevpThbAV4KJ53h6pHPgaCl7dYcReBp+Jd4uQXSbG5OQB\r
32         kYfI03C2cP64DGJuGNrPBPXFlLYaPmqSyk/nafnkmY4S3xvt6Zdrp2YWGOhHvERmtiZD\r
33         eCjkxG2yw53SZchF0CQmyNGZO++ibWYV49MXb+czsZezIHSeOFU2YjZQt/n0fqlLHuuw\r
34         gbVZStihYX2/14Oo0mCVZC6kwUKj2tGHarXwj0Uj7l4OZljGO0tV2uGW0gkYupLFKn5B\r
35         O+tQ==\r
36 X-Gm-Message-State:\r
37  ALoCoQnysQMx5s32H4YjQA6y0vwlMzFr7LOcWvfwPPTkJROheu48cDR3/CrEc18KPHbeBpJBw0Lv\r
38 X-Received: by 10.112.137.5 with SMTP id qe5mr20039501lbb.16.1394401252404;\r
39         Sun, 09 Mar 2014 14:40:52 -0700 (PDT)\r
40 Received: from localhost (dsl-hkibrasgw2-58c36f-91.dhcp.inet.fi.\r
41         [88.195.111.91])\r
42         by mx.google.com with ESMTPSA id i7sm26898996lai.5.2014.03.09.14.40.50\r
43         for <multiple recipients>\r
44         (version=TLSv1.2 cipher=RC4-SHA bits=128/128);\r
45         Sun, 09 Mar 2014 14:40:51 -0700 (PDT)\r
46 From: Jani Nikula <jani@nikula.org>\r
47 To: notmuch@notmuchmail.org\r
48 Subject: [PATCH v4 06/13] lib: make folder: prefix literal\r
49 Date: Sun,  9 Mar 2014 23:40:27 +0200\r
50 Message-Id:\r
51  <9640c403666ae291e71c22f252eb05f606b14a5e.1394400503.git.jani@nikula.org>\r
52 X-Mailer: git-send-email 1.9.0\r
53 In-Reply-To: <cover.1394400503.git.jani@nikula.org>\r
54 References: <cover.1394400503.git.jani@nikula.org>\r
55 In-Reply-To: <cover.1394400503.git.jani@nikula.org>\r
56 References: <cover.1394400503.git.jani@nikula.org>\r
57 X-BeenThere: notmuch@notmuchmail.org\r
58 X-Mailman-Version: 2.1.13\r
59 Precedence: list\r
60 List-Id: "Use and development of the notmuch mail system."\r
61         <notmuch.notmuchmail.org>\r
62 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
63         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
64 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
65 List-Post: <mailto:notmuch@notmuchmail.org>\r
66 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
67 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
68         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
69 X-List-Received-Date: Sun, 09 Mar 2014 21:41:24 -0000\r
70 \r
71 In xapian terms, convert folder: prefix from probabilistic to boolean\r
72 prefix, matching the paths, relative from the maildir root, of the\r
73 message files, ignoring the maildir new and cur leaf directories.\r
74 \r
75 folder:foo matches all message files in foo, foo/new, and foo/cur.\r
76 \r
77 folder:foo/new does *not* match message files in foo/new.\r
78 \r
79 folder:"" matches all message files in the top level maildir and its\r
80 new and cur subdirectories.\r
81 \r
82 This change constitutes a database change: bump the database version\r
83 and add database upgrade support for folder: terms. The upgrade also\r
84 adds path: terms.\r
85 \r
86 Finally, fix the folder search test for literal folder: search, as\r
87 some of the folder: matching capabilities are lost in the\r
88 probabilistic to boolean prefix change.\r
89 ---\r
90  lib/database.cc               | 44 ++++++++++++++++++++++--\r
91  lib/message.cc                | 80 +++++++++++++++++++++++++++++++++++++------\r
92  lib/notmuch-private.h         |  3 ++\r
93  test/T100-search-by-folder.sh | 24 +++++++++++--\r
94  4 files changed, 135 insertions(+), 16 deletions(-)\r
95 \r
96 diff --git a/lib/database.cc b/lib/database.cc\r
97 index 93cc7f57e9db..aef748f75b5d 100644\r
98 --- a/lib/database.cc\r
99 +++ b/lib/database.cc\r
100 @@ -42,7 +42,7 @@ typedef struct {\r
101      const char *prefix;\r
102  } prefix_t;\r
103  \r
104 -#define NOTMUCH_DATABASE_VERSION 1\r
105 +#define NOTMUCH_DATABASE_VERSION 2\r
106  \r
107  #define STRINGIFY(s) _SUB_STRINGIFY(s)\r
108  #define _SUB_STRINGIFY(s) #s\r
109 @@ -210,6 +210,13 @@ static prefix_t BOOLEAN_PREFIX_EXTERNAL[] = {\r
110      { "is",                    "K" },\r
111      { "id",                    "Q" },\r
112      { "path",                  "P" },\r
113 +    /*\r
114 +     * Without the ":", since this is a multi-letter prefix, Xapian\r
115 +     * will add a colon itself if the first letter of the path is\r
116 +     * upper-case ASCII. Including the ":" forces there to always be a\r
117 +     * colon, which keeps our own logic simpler.\r
118 +     */\r
119 +    { "folder",                        "XFOLDER:" },\r
120  };\r
121  \r
122  static prefix_t PROBABILISTIC_PREFIX[]= {\r
123 @@ -217,7 +224,6 @@ static prefix_t PROBABILISTIC_PREFIX[]= {\r
124      { "to",                    "XTO" },\r
125      { "attachment",            "XATTACHMENT" },\r
126      { "subject",               "XSUBJECT"},\r
127 -    { "folder",                        "XFOLDER"}\r
128  };\r
129  \r
130  const char *\r
131 @@ -1168,6 +1174,40 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,\r
132         }\r
133      }\r
134  \r
135 +    /*\r
136 +     * Prior to version 2, the "folder:" prefix was probabilistic and\r
137 +     * stemmed. Change it to the current boolean prefix. Add "path:"\r
138 +     * prefixes while at it.\r
139 +     */\r
140 +    if (version < 2) {\r
141 +       notmuch_query_t *query = notmuch_query_create (notmuch, "");\r
142 +       notmuch_messages_t *messages;\r
143 +       notmuch_message_t *message;\r
144 +\r
145 +       count = 0;\r
146 +       total = notmuch_query_count_messages (query);\r
147 +\r
148 +       for (messages = notmuch_query_search_messages (query);\r
149 +            notmuch_messages_valid (messages);\r
150 +            notmuch_messages_move_to_next (messages)) {\r
151 +           if (do_progress_notify) {\r
152 +               progress_notify (closure, (double) count / total);\r
153 +               do_progress_notify = 0;\r
154 +           }\r
155 +\r
156 +           message = notmuch_messages_get (messages);\r
157 +\r
158 +           _notmuch_message_upgrade_folder (message);\r
159 +           _notmuch_message_sync (message);\r
160 +\r
161 +           notmuch_message_destroy (message);\r
162 +\r
163 +           count++;\r
164 +       }\r
165 +\r
166 +       notmuch_query_destroy (query);\r
167 +    }\r
168 +\r
169      db->set_metadata ("version", STRINGIFY (NOTMUCH_DATABASE_VERSION));\r
170      db->flush ();\r
171  \r
172 diff --git a/lib/message.cc b/lib/message.cc\r
173 index 21abe8e12b9d..9243b769d2b0 100644\r
174 --- a/lib/message.cc\r
175 +++ b/lib/message.cc\r
176 @@ -504,6 +504,56 @@ _notmuch_message_remove_terms (notmuch_message_t *message, const char *prefix)\r
177      }\r
178  }\r
179  \r
180 +/* Return true if p points at "new" or "cur". */\r
181 +static bool is_maildir (const char *p)\r
182 +{\r
183 +    return strcmp (p, "cur") == 0 || strcmp (p, "new") == 0;\r
184 +}\r
185 +\r
186 +/* Add "folder:" term for directory. */\r
187 +static notmuch_status_t\r
188 +_notmuch_message_add_folder_terms (notmuch_message_t *message,\r
189 +                                  const char *directory)\r
190 +{\r
191 +    char *folder, *last;\r
192 +\r
193 +    folder = talloc_strdup (NULL, directory);\r
194 +    if (! folder)\r
195 +       return NOTMUCH_STATUS_OUT_OF_MEMORY;\r
196 +\r
197 +    /*\r
198 +     * If the message file is in a leaf directory named "new" or\r
199 +     * "cur", presume maildir and index the parent directory. Thus a\r
200 +     * "folder:" prefix search matches messages in the specified\r
201 +     * maildir folder, i.e. in the specified directory and its "new"\r
202 +     * and "cur" subdirectories.\r
203 +     *\r
204 +     * Note that this means the "folder:" prefix can't be used for\r
205 +     * distinguishing between message files in "new" or "cur". The\r
206 +     * "path:" prefix needs to be used for that.\r
207 +     *\r
208 +     * Note the deliberate difference to _filename_is_in_maildir(). We\r
209 +     * don't want to index different things depending on the existence\r
210 +     * or non-existence of all maildir sibling directories "new",\r
211 +     * "cur", and "tmp". Doing so would be surprising, and difficult\r
212 +     * for the user to fix in case all subdirectories were not in\r
213 +     * place during indexing.\r
214 +     */\r
215 +    last = strrchr (folder, '/');\r
216 +    if (last) {\r
217 +       if (is_maildir (last + 1))\r
218 +           *last = '\0';\r
219 +    } else if (is_maildir (folder)) {\r
220 +       *folder = '\0';\r
221 +    }\r
222 +\r
223 +    _notmuch_message_add_term (message, "folder", folder);\r
224 +\r
225 +    talloc_free (folder);\r
226 +\r
227 +    return NOTMUCH_STATUS_SUCCESS;\r
228 +}\r
229 +\r
230  #define RECURSIVE_SUFFIX "/**"\r
231  \r
232  /* Add "path:" terms for directory. */\r
233 @@ -570,9 +620,8 @@ _notmuch_message_add_directory_terms (void *ctx, notmuch_message_t *message)\r
234         directory = _notmuch_database_get_directory_path (ctx,\r
235                                                           message->notmuch,\r
236                                                           directory_id);\r
237 -       if (strlen (directory))\r
238 -           _notmuch_message_gen_terms (message, "folder", directory);\r
239  \r
240 +       _notmuch_message_add_folder_terms (message, directory);\r
241         _notmuch_message_add_path_terms (message, directory);\r
242      }\r
243  \r
244 @@ -610,9 +659,7 @@ _notmuch_message_add_filename (notmuch_message_t *message,\r
245       * notmuch_directory_get_child_files() . */\r
246      _notmuch_message_add_term (message, "file-direntry", direntry);\r
247  \r
248 -    /* New terms allow user to search with folder: specification. */\r
249 -    _notmuch_message_gen_terms (message, "folder", directory);\r
250 -\r
251 +    _notmuch_message_add_folder_terms (message, directory);\r
252      _notmuch_message_add_path_terms (message, directory);\r
253  \r
254      talloc_free (local);\r
255 @@ -637,8 +684,6 @@ _notmuch_message_remove_filename (notmuch_message_t *message,\r
256                                   const char *filename)\r
257  {\r
258      void *local = talloc_new (message);\r
259 -    const char *folder_prefix = _find_prefix ("folder");\r
260 -    char *zfolder_prefix = talloc_asprintf(local, "Z%s", folder_prefix);\r
261      char *direntry;\r
262      notmuch_private_status_t private_status;\r
263      notmuch_status_t status;\r
264 @@ -659,10 +704,7 @@ _notmuch_message_remove_filename (notmuch_message_t *message,\r
265      /* Re-synchronize "folder:" and "path:" terms for this message. */\r
266  \r
267      /* Remove all "folder:" terms. */\r
268 -    _notmuch_message_remove_terms (message, folder_prefix);\r
269 -\r
270 -    /* Remove all "folder:" stemmed terms. */\r
271 -    _notmuch_message_remove_terms (message, zfolder_prefix);\r
272 +    _notmuch_message_remove_terms (message, _find_prefix ("folder"));\r
273  \r
274      /* Remove all "path:" terms. */\r
275      _notmuch_message_remove_terms (message, _find_prefix ("path"));\r
276 @@ -675,6 +717,22 @@ _notmuch_message_remove_filename (notmuch_message_t *message,\r
277      return status;\r
278  }\r
279  \r
280 +/* Upgrade the "folder:" prefix from V1 to V2. */\r
281 +#define FOLDER_PREFIX_V1       "XFOLDER"\r
282 +#define ZFOLDER_PREFIX_V1      "Z" FOLDER_PREFIX_V1\r
283 +void\r
284 +_notmuch_message_upgrade_folder (notmuch_message_t *message)\r
285 +{\r
286 +    /* Remove all old "folder:" terms. */\r
287 +    _notmuch_message_remove_terms (message, FOLDER_PREFIX_V1);\r
288 +\r
289 +    /* Remove all old "folder:" stemmed terms. */\r
290 +    _notmuch_message_remove_terms (message, ZFOLDER_PREFIX_V1);\r
291 +\r
292 +    /* Add new boolean "folder:" and "path:" terms. */\r
293 +    _notmuch_message_add_directory_terms (message, message);\r
294 +}\r
295 +\r
296  char *\r
297  _notmuch_message_talloc_copy_data (notmuch_message_t *message)\r
298  {\r
299 diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h\r
300 index af185c7c5ba8..59eb2bc285a5 100644\r
301 --- a/lib/notmuch-private.h\r
302 +++ b/lib/notmuch-private.h\r
303 @@ -263,6 +263,9 @@ _notmuch_message_gen_terms (notmuch_message_t *message,\r
304  void\r
305  _notmuch_message_upgrade_filename_storage (notmuch_message_t *message);\r
306  \r
307 +void\r
308 +_notmuch_message_upgrade_folder (notmuch_message_t *message);\r
309 +\r
310  notmuch_status_t\r
311  _notmuch_message_add_filename (notmuch_message_t *message,\r
312                                const char *filename);\r
313 diff --git a/test/T100-search-by-folder.sh b/test/T100-search-by-folder.sh\r
314 index 5cc2ca8d388a..a7f63dd1750c 100755\r
315 --- a/test/T100-search-by-folder.sh\r
316 +++ b/test/T100-search-by-folder.sh\r
317 @@ -3,6 +3,7 @@ test_description='"notmuch search" by folder: (with variations)'\r
318  . ./test-lib.sh\r
319  \r
320  add_message '[dir]=bad' '[subject]="To the bone"'\r
321 +add_message '[dir]=.' '[subject]="Top level"'\r
322  add_message '[dir]=bad/news' '[subject]="Bears"'\r
323  mkdir -p "${MAIL_DIR}/duplicate/bad/news"\r
324  cp "$gen_msg_filename" "${MAIL_DIR}/duplicate/bad/news"\r
325 @@ -12,29 +13,46 @@ add_message '[dir]=things/favorite' '[subject]="Raindrops, whiskers, kettles"'\r
326  add_message '[dir]=things/bad' '[subject]="Bites, stings, sad feelings"'\r
327  \r
328  test_begin_subtest "Single-world folder: specification (multiple results)"\r
329 -output=$(notmuch search folder:bad | notmuch_search_sanitize)\r
330 +output=$(notmuch search folder:bad folder:bad/news folder:things/bad | notmuch_search_sanitize)\r
331  test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; To the bone (inbox unread)\r
332  thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Bears (inbox unread)\r
333  thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Bites, stings, sad feelings (inbox unread)"\r
334  \r
335 +test_begin_subtest "Top level folder"\r
336 +output=$(notmuch search folder:'""' | notmuch_search_sanitize)\r
337 +test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Top level (inbox unread)"\r
338 +\r
339  test_begin_subtest "Two-word path to narrow results to one"\r
340  output=$(notmuch search folder:bad/news | notmuch_search_sanitize)\r
341  test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Bears (inbox unread)"\r
342  \r
343 +test_begin_subtest "Folder search with --output=files"\r
344 +output=$(notmuch search --output=files folder:bad/news | notmuch_search_files_sanitize)\r
345 +test_expect_equal "$output" "MAIL_DIR/bad/news/msg-003\r
346 +MAIL_DIR/duplicate/bad/news/msg-003"\r
347 +\r
348  test_begin_subtest "After removing duplicate instance of matching path"\r
349  rm -r "${MAIL_DIR}/bad/news"\r
350  notmuch new\r
351  output=$(notmuch search folder:bad/news | notmuch_search_sanitize)\r
352 +test_expect_equal "$output" ""\r
353 +\r
354 +test_begin_subtest "Folder search with --output=files part #2"\r
355 +output=$(notmuch search --output=files folder:duplicate/bad/news | notmuch_search_files_sanitize)\r
356 +test_expect_equal "$output" "MAIL_DIR/duplicate/bad/news/msg-003"\r
357 +\r
358 +test_begin_subtest "After removing duplicate instance of matching path part #2"\r
359 +output=$(notmuch search folder:duplicate/bad/news | notmuch_search_sanitize)\r
360  test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Bears (inbox unread)"\r
361  \r
362  test_begin_subtest "After rename, old path returns nothing"\r
363  mv "${MAIL_DIR}/duplicate/bad/news" "${MAIL_DIR}/duplicate/bad/olds"\r
364  notmuch new\r
365 -output=$(notmuch search folder:bad/news | notmuch_search_sanitize)\r
366 +output=$(notmuch search folder:duplicate/bad/news | notmuch_search_sanitize)\r
367  test_expect_equal "$output" ""\r
368  \r
369  test_begin_subtest "After rename, new path returns result"\r
370 -output=$(notmuch search folder:bad/olds | notmuch_search_sanitize)\r
371 +output=$(notmuch search folder:duplicate/bad/olds | notmuch_search_sanitize)\r
372  test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Bears (inbox unread)"\r
373  \r
374  test_done\r
375 -- \r
376 1.9.0\r
377 \r