1 Return-Path: <dkg@fifthhorseman.net>
\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 arlo.cworth.org (Postfix) with ESMTP id 733586DE1BB0
\r
6 for <notmuch@notmuchmail.org>; Sun, 31 Jan 2016 12:40:25 -0800 (PST)
\r
7 X-Virus-Scanned: Debian amavisd-new at cworth.org
\r
11 X-Spam-Status: No, score=0 tagged_above=-999 required=5 tests=[none]
\r
13 Received: from arlo.cworth.org ([127.0.0.1])
\r
14 by localhost (arlo.cworth.org [127.0.0.1]) (amavisd-new, port 10024)
\r
15 with ESMTP id tShFG0lZnqD3 for <notmuch@notmuchmail.org>;
\r
16 Sun, 31 Jan 2016 12:40:23 -0800 (PST)
\r
17 Received: from che.mayfirst.org (che.mayfirst.org [209.234.253.108])
\r
18 by arlo.cworth.org (Postfix) with ESMTP id E71716DE1ADE
\r
19 for <notmuch@notmuchmail.org>; Sun, 31 Jan 2016 12:40:09 -0800 (PST)
\r
20 Received: from fifthhorseman.net (ip-64-134-185-108.public.wayport.net
\r
22 by che.mayfirst.org (Postfix) with ESMTPSA id 09222F9A6
\r
23 for <notmuch@notmuchmail.org>; Sun, 31 Jan 2016 15:40:07 -0500 (EST)
\r
24 Received: by fifthhorseman.net (Postfix, from userid 1000)
\r
25 id 683E0211EE; Sun, 31 Jan 2016 15:40:06 -0500 (EST)
\r
26 From: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
\r
27 To: Notmuch Mail <notmuch@notmuchmail.org>
\r
28 Subject: [PATCH v3 16/16] add "notmuch reindex" subcommand
\r
29 Date: Sun, 31 Jan 2016 15:40:01 -0500
\r
30 Message-Id: <1454272801-23623-17-git-send-email-dkg@fifthhorseman.net>
\r
31 X-Mailer: git-send-email 2.7.0.rc3
\r
32 In-Reply-To: <1454272801-23623-1-git-send-email-dkg@fifthhorseman.net>
\r
33 References: <1454272801-23623-1-git-send-email-dkg@fifthhorseman.net>
\r
35 Content-Type: text/plain; charset=UTF-8
\r
36 Content-Transfer-Encoding: 8bit
\r
37 X-BeenThere: notmuch@notmuchmail.org
\r
38 X-Mailman-Version: 2.1.20
\r
40 List-Id: "Use and development of the notmuch mail system."
\r
41 <notmuch.notmuchmail.org>
\r
42 List-Unsubscribe: <https://notmuchmail.org/mailman/options/notmuch>,
\r
43 <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>
\r
44 List-Archive: <http://notmuchmail.org/pipermail/notmuch/>
\r
45 List-Post: <mailto:notmuch@notmuchmail.org>
\r
46 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>
\r
47 List-Subscribe: <https://notmuchmail.org/mailman/listinfo/notmuch>,
\r
48 <mailto:notmuch-request@notmuchmail.org?subject=subscribe>
\r
49 X-List-Received-Date: Sun, 31 Jan 2016 20:40:25 -0000
\r
51 This new subcommand takes a set of search terms, and re-indexes the
\r
52 list of matching messages using the supplied options.
\r
54 This can be used to index the cleartext of encrypted messages with
\r
57 notmuch reindex --try-decrypt \
\r
58 tag:encrypted and not tag:index-decrypted
\r
60 Makefile.local | 1 +
\r
62 doc/man1/notmuch-reindex.rst | 41 ++++++++++
\r
63 doc/man1/notmuch.rst | 1 +
\r
64 doc/man7/notmuch-search-terms.rst | 7 +-
\r
65 notmuch-client.h | 3 +
\r
66 notmuch-reindex.c | 152 ++++++++++++++++++++++++++++++++++++++
\r
68 test/T357-index-decryption.sh | 53 +++++++++++++
\r
69 9 files changed, 265 insertions(+), 2 deletions(-)
\r
70 create mode 100644 doc/man1/notmuch-reindex.rst
\r
71 create mode 100644 notmuch-reindex.c
\r
73 diff --git a/Makefile.local b/Makefile.local
\r
74 index 6206771..e03a83d 100644
\r
75 --- a/Makefile.local
\r
76 +++ b/Makefile.local
\r
77 @@ -281,6 +281,7 @@ notmuch_client_srcs = \
\r
81 + notmuch-reindex.c \
\r
85 diff --git a/doc/conf.py b/doc/conf.py
\r
86 index 65adafe..f98d67a 100644
\r
89 @@ -94,6 +94,10 @@ man_pages = [
\r
90 u'incorporate new mail into the notmuch database',
\r
91 [u'Carl Worth and many others'], 1),
\r
93 +('man1/notmuch-reindex','notmuch-reindex',
\r
94 + u're-index matching messages',
\r
95 + [u'Carl Worth and many others'], 1),
\r
97 ('man1/notmuch-reply','notmuch-reply',
\r
98 u'constructs a reply template for a set of messages',
\r
99 [u'Carl Worth and many others'], 1),
\r
100 @@ -162,6 +166,9 @@ texinfo_documents = [
\r
101 ('man1/notmuch-new','notmuch-new',u'notmuch Documentation',
\r
102 u'Carl Worth and many others', 'notmuch-new',
\r
103 'incorporate new mail into the notmuch database','Miscellaneous'),
\r
104 +('man1/notmuch-reindex','notmuch-reindex',u'notmuch Documentation',
\r
105 + u'Carl Worth and many others', 'notmuch-reindex',
\r
106 + 're-index matching messages','Miscellaneous'),
\r
107 ('man1/notmuch-reply','notmuch-reply',u'notmuch Documentation',
\r
108 u'Carl Worth and many others', 'notmuch-reply',
\r
109 'constructs a reply template for a set of messages','Miscellaneous'),
\r
110 diff --git a/doc/man1/notmuch-reindex.rst b/doc/man1/notmuch-reindex.rst
\r
111 new file mode 100644
\r
112 index 0000000..7ccc947
\r
114 +++ b/doc/man1/notmuch-reindex.rst
\r
123 +**notmuch** **reindex** [*option* ...] <*search-term*> ...
\r
128 +Re-index all messages matching the search terms.
\r
130 +See **notmuch-search-terms(7)** for details of the supported syntax for
\r
131 +<*search-term*\ >.
\r
133 +The **reindex** command searches for all messages matching the
\r
134 +supplied search terms, and re-creates the full-text index on these
\r
135 +messages using the supplied options.
\r
137 +Supported options for **reindex** include
\r
139 + ``--try-decrypt``
\r
141 + For each message, if it is encrypted, try to decrypt it while
\r
142 + indexing. If decryption is successful, index the cleartext
\r
143 + itself. Be aware that the index is likely sufficient to
\r
144 + reconstruct the cleartext of the message itself, so please
\r
145 + ensure that the notmuch message index is adequately
\r
146 + protected. DO NOT USE THIS FLAG without considering the
\r
147 + security of your index.
\r
152 +**notmuch(1)**, **notmuch-config(1)**, **notmuch-count(1)**,
\r
153 +**notmuch-dump(1)**, **notmuch-hooks(5)**, **notmuch-insert(1)**,
\r
154 +**notmuch-new(1)**,
\r
155 +**notmuch-reply(1)**, **notmuch-restore(1)**, **notmuch-search(1)**,
\r
156 +**notmuch-search-terms(7)**, **notmuch-show(1)**, **notmuch-tag(1)**
\r
157 diff --git a/doc/man1/notmuch.rst b/doc/man1/notmuch.rst
\r
158 index 3acfbdb..d9ba146 100644
\r
159 --- a/doc/man1/notmuch.rst
\r
160 +++ b/doc/man1/notmuch.rst
\r
161 @@ -140,6 +140,7 @@ SEE ALSO
\r
163 **notmuch-config(1)**, **notmuch-count(1)**, **notmuch-dump(1)**,
\r
164 **notmuch-hooks(5)**, **notmuch-insert(1)**, **notmuch-new(1)**,
\r
165 +**notmuch-reindex(1)**,
\r
166 **notmuch-reply(1)**, **notmuch-restore(1)**, **notmuch-search(1)**,
\r
167 **notmuch-search-terms(7)**, **notmuch-show(1)**, **notmuch-tag(1)**,
\r
168 **notmuch-address(1)**
\r
169 diff --git a/doc/man7/notmuch-search-terms.rst b/doc/man7/notmuch-search-terms.rst
\r
170 index 2fbc16d..f9c4676 100644
\r
171 --- a/doc/man7/notmuch-search-terms.rst
\r
172 +++ b/doc/man7/notmuch-search-terms.rst
\r
173 @@ -9,6 +9,8 @@ SYNOPSIS
\r
175 **notmuch** **dump** [--format=(batch-tag|sup)] [--] [--output=<*file*>] [--] [<*search-term*> ...]
\r
177 +**notmuch** **reindex** [option ...] <*search-term*> ...
\r
179 **notmuch** **search** [option ...] <*search-term*> ...
\r
181 **notmuch** **show** [option ...] <*search-term*> ...
\r
182 @@ -375,5 +377,6 @@ SEE ALSO
\r
184 **notmuch(1)**, **notmuch-config(1)**, **notmuch-count(1)**,
\r
185 **notmuch-dump(1)**, **notmuch-hooks(5)**, **notmuch-insert(1)**,
\r
186 -**notmuch-new(1)**, **notmuch-reply(1)**, **notmuch-restore(1)**,
\r
187 -**notmuch-search(1)**, **notmuch-show(1)**, **notmuch-tag(1)**
\r
188 +**notmuch-new(1)**, **notmuch-reindex(1)**, **notmuch-reply(1)**,
\r
189 +**notmuch-restore(1)**, **notmuch-search(1)**, **notmuch-show(1)**,
\r
190 +**notmuch-tag(1)**
\r
191 diff --git a/notmuch-client.h b/notmuch-client.h
\r
192 index a41e90a..89b1180 100644
\r
193 --- a/notmuch-client.h
\r
194 +++ b/notmuch-client.h
\r
195 @@ -169,6 +169,9 @@ int
\r
196 notmuch_insert_command (notmuch_config_t *config, int argc, char *argv[]);
\r
199 +notmuch_reindex_command (notmuch_config_t *config, int argc, char *argv[]);
\r
202 notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[]);
\r
205 diff --git a/notmuch-reindex.c b/notmuch-reindex.c
\r
206 new file mode 100644
\r
207 index 0000000..6fc88c5
\r
209 +++ b/notmuch-reindex.c
\r
211 +/* notmuch - Not much of an email program, (just index and search)
\r
213 + * Copyright © 2016 Daniel Kahn Gillmor
\r
215 + * This program is free software: you can redistribute it and/or modify
\r
216 + * it under the terms of the GNU General Public License as published by
\r
217 + * the Free Software Foundation, either version 3 of the License, or
\r
218 + * (at your option) any later version.
\r
220 + * This program is distributed in the hope that it will be useful,
\r
221 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
222 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
223 + * GNU General Public License for more details.
\r
225 + * You should have received a copy of the GNU General Public License
\r
226 + * along with this program. If not, see http://www.gnu.org/licenses/ .
\r
228 + * Author: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
\r
231 +#include "notmuch-client.h"
\r
232 +#include "string-util.h"
\r
234 +static volatile sig_atomic_t interrupted;
\r
237 +handle_sigint (unused (int sig))
\r
239 + static char msg[] = "Stopping... \n";
\r
241 + /* This write is "opportunistic", so it's okay to ignore the
\r
242 + * result. It is not required for correctness, and if it does
\r
243 + * fail or produce a short write, we want to get out of the signal
\r
244 + * handler as quickly as possible, not retry it. */
\r
245 + IGNORE_RESULT (write (2, msg, sizeof (msg) - 1));
\r
249 +/* reindex all messages matching 'query_string' using the passed-in indexopts
\r
252 +reindex_query (notmuch_database_t *notmuch, const char *query_string,
\r
253 + notmuch_indexopts_t *indexopts)
\r
255 + notmuch_query_t *query;
\r
256 + notmuch_messages_t *messages;
\r
257 + notmuch_message_t *message;
\r
258 + notmuch_status_t status;
\r
260 + int ret = NOTMUCH_STATUS_SUCCESS;
\r
262 + query = notmuch_query_create (notmuch, query_string);
\r
263 + if (query == NULL) {
\r
264 + fprintf (stderr, "Out of memory.\n");
\r
268 + /* reindexing is not interested in any special sort order */
\r
269 + notmuch_query_set_sort (query, NOTMUCH_SORT_UNSORTED);
\r
271 + status = notmuch_query_search_messages_st (query, &messages);
\r
272 + if (print_status_query ("notmuch reindex", query, status))
\r
276 + notmuch_messages_valid (messages) && ! interrupted;
\r
277 + notmuch_messages_move_to_next (messages)) {
\r
278 + message = notmuch_messages_get (messages);
\r
280 + notmuch_message_reindex(message, indexopts);
\r
281 + notmuch_message_destroy (message);
\r
282 + if (ret != NOTMUCH_STATUS_SUCCESS)
\r
286 + notmuch_query_destroy (query);
\r
288 + return ret || interrupted;
\r
292 +notmuch_reindex_command (notmuch_config_t *config, int argc, char *argv[])
\r
294 + char *query_string = NULL;
\r
295 + notmuch_database_t *notmuch;
\r
296 + struct sigaction action;
\r
297 + notmuch_bool_t try_decrypt = FALSE;
\r
300 + notmuch_status_t status;
\r
301 + notmuch_indexopts_t *indexopts = NULL;
\r
303 + /* Set up our handler for SIGINT */
\r
304 + memset (&action, 0, sizeof (struct sigaction));
\r
305 + action.sa_handler = handle_sigint;
\r
306 + sigemptyset (&action.sa_mask);
\r
307 + action.sa_flags = SA_RESTART;
\r
308 + sigaction (SIGINT, &action, NULL);
\r
310 + notmuch_opt_desc_t options[] = {
\r
311 + { NOTMUCH_OPT_BOOLEAN, &try_decrypt, "try-decrypt", 0, 0 },
\r
312 + { NOTMUCH_OPT_INHERIT, (void *) ¬much_shared_options, NULL, 0, 0 },
\r
313 + { 0, 0, 0, 0, 0 }
\r
316 + opt_index = parse_arguments (argc, argv, options, 1);
\r
317 + if (opt_index < 0)
\r
318 + return EXIT_FAILURE;
\r
320 + notmuch_process_shared_options (argv[0]);
\r
322 + if (notmuch_database_open (notmuch_config_get_database_path (config),
\r
323 + NOTMUCH_DATABASE_MODE_READ_WRITE, ¬much))
\r
324 + return EXIT_FAILURE;
\r
326 + notmuch_exit_if_unmatched_db_uuid (notmuch);
\r
328 + indexopts = notmuch_indexopts_create();
\r
330 + return EXIT_FAILURE;
\r
332 + status = notmuch_indexopts_set_try_decrypt (indexopts, try_decrypt);
\r
334 + fprintf (stderr, "Warning: failed to set --try-decrypt to %d (%s)\n",
\r
335 + try_decrypt, notmuch_status_to_string (status));
\r
337 + if (try_decrypt) {
\r
338 + const char* gpg_path = notmuch_config_get_crypto_gpg_path (config);
\r
339 + status = notmuch_indexopts_set_gpg_path (indexopts, gpg_path);
\r
341 + fprintf (stderr, "Warning: failed to set gpg_path for reindexing to '%s' (%s)\n",
\r
342 + gpg_path ? gpg_path : "(NULL)",
\r
343 + notmuch_status_to_string (status));
\r
346 + query_string = query_string_from_args (config, argc-opt_index, argv+opt_index);
\r
347 + if (query_string == NULL) {
\r
348 + fprintf (stderr, "Out of memory\n");
\r
349 + return EXIT_FAILURE;
\r
352 + if (*query_string == '\0') {
\r
353 + fprintf (stderr, "Error: notmuch reindex requires at least one search term.\n");
\r
354 + return EXIT_FAILURE;
\r
357 + ret = reindex_query (notmuch, query_string, indexopts);
\r
359 + notmuch_database_destroy (notmuch);
\r
361 + return ret || interrupted ? EXIT_FAILURE : EXIT_SUCCESS;
\r
363 diff --git a/notmuch.c b/notmuch.c
\r
364 index ce6c575..df9cf4e 100644
\r
367 @@ -123,6 +123,8 @@ static command_t commands[] = {
\r
368 "Restore the tags from the given dump file (see 'dump')." },
\r
369 { "compact", notmuch_compact_command, FALSE,
\r
370 "Compact the notmuch database." },
\r
371 + { "reindex", notmuch_reindex_command, FALSE,
\r
372 + "Re-index all messages matching the search terms." },
\r
373 { "config", notmuch_config_command, FALSE,
\r
374 "Get or set settings in the notmuch configuration file." },
\r
375 { "help", notmuch_help_command, TRUE, /* create but don't save config */
\r
376 diff --git a/test/T357-index-decryption.sh b/test/T357-index-decryption.sh
\r
377 index 03e49cc..c60e152 100755
\r
378 --- a/test/T357-index-decryption.sh
\r
379 +++ b/test/T357-index-decryption.sh
\r
380 @@ -39,4 +39,57 @@ test_expect_equal \
\r
384 +# add a tag to all messages to ensure that it stays after reindexing
\r
385 +test_expect_success 'tagging all messages' \
\r
386 + 'notmuch tag +blarney "encrypted message"'
\r
387 +test_begin_subtest "verify that tags are all present"
\r
388 +output=$(notmuch search tag:blarney)
\r
389 +expected='thread:0000000000000001 2000-01-01 [1/1] Notmuch Test Suite; test encrypted message for cleartext index 001 (blarney encrypted inbox)
\r
390 +thread:0000000000000002 2000-01-01 [1/1] Notmuch Test Suite; test encrypted message for cleartext index 002 (blarney encrypted inbox index-decrypted)'
\r
391 +test_expect_equal \
\r
395 +# see if first message shows up after reindexing with --try-decrypt
\r
396 +test_expect_success 'reindex old messages' \
\r
397 + 'notmuch reindex --try-decrypt tag:encrypted and not tag:index-decrypted'
\r
398 +test_begin_subtest "reindexed encrypted message, including cleartext"
\r
399 +output=$(notmuch search wumpus)
\r
400 +expected='thread:0000000000000002 2000-01-01 [1/1] Notmuch Test Suite; test encrypted message for cleartext index 002 (blarney encrypted inbox index-decrypted)
\r
401 +thread:0000000000000003 2000-01-01 [1/1] Notmuch Test Suite; test encrypted message for cleartext index 001 (blarney encrypted inbox index-decrypted)'
\r
402 +test_expect_equal \
\r
407 +# try to remove cleartext indexing
\r
408 +test_expect_success 'reindex without cleartext' \
\r
409 + 'notmuch reindex tag:encrypted and tag:index-decrypted'
\r
410 +test_begin_subtest "reindexed encrypted messages, without cleartext"
\r
411 +output=$(notmuch search wumpus)
\r
413 +test_expect_equal \
\r
417 +# ensure that the tags remain even when we are dropping the cleartext.
\r
418 +test_begin_subtest "verify that tags remain without cleartext"
\r
419 +output=$(notmuch search tag:blarney)
\r
420 +expected='thread:0000000000000004 2000-01-01 [1/1] Notmuch Test Suite; test encrypted message for cleartext index 002 (blarney encrypted inbox)
\r
421 +thread:0000000000000005 2000-01-01 [1/1] Notmuch Test Suite; test encrypted message for cleartext index 001 (blarney encrypted inbox)'
\r
422 +test_expect_equal \
\r
427 +# TODO: test removal of a message from the message store between
\r
428 +# indexing and reindexing.
\r
430 +# TODO: insert the same message into the message store twice, index,
\r
431 +# remove one of them from the message store, and then reindex.
\r
432 +# reindexing should return a failure but the message should still be
\r
433 +# present? -- or what should the semantics be if you ask to reindex a
\r
434 +# message whose underlying files have been renamed or moved or
\r