--- /dev/null
+Return-Path: <dkg@fifthhorseman.net>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+ by arlo.cworth.org (Postfix) with ESMTP id BF9286DE091F\r
+ for <notmuch@notmuchmail.org>; Fri, 8 Jul 2016 03:13:16 -0700 (PDT)\r
+X-Virus-Scanned: Debian amavisd-new at cworth.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: 0\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=0 tagged_above=-999 required=5 tests=[none]\r
+ autolearn=disabled\r
+Received: from arlo.cworth.org ([127.0.0.1])\r
+ by localhost (arlo.cworth.org [127.0.0.1]) (amavisd-new, port 10024)\r
+ with ESMTP id RsaQ-QlS0tUS for <notmuch@notmuchmail.org>;\r
+ Fri, 8 Jul 2016 03:13:08 -0700 (PDT)\r
+Received: from che.mayfirst.org (che.mayfirst.org [162.247.75.118])\r
+ by arlo.cworth.org (Postfix) with ESMTP id 121A16DE01BA\r
+ for <notmuch@notmuchmail.org>; Fri, 8 Jul 2016 03:13:07 -0700 (PDT)\r
+Received: from fifthhorseman.net (unknown [88.128.80.54])\r
+ by che.mayfirst.org (Postfix) with ESMTPSA id 8414CF99A\r
+ for <notmuch@notmuchmail.org>; Fri, 8 Jul 2016 06:13:06 -0400 (EDT)\r
+Received: by fifthhorseman.net (Postfix, from userid 1000)\r
+ id C9C402174C; Fri, 8 Jul 2016 11:27:34 +0200 (CEST)\r
+From: Daniel Kahn Gillmor <dkg@fifthhorseman.net>\r
+To: Notmuch Mail <notmuch@notmuchmail.org>\r
+Subject: [PATCH v4 15/16] added notmuch_message_reindex\r
+Date: Fri, 8 Jul 2016 11:27:26 +0200\r
+Message-Id: <1467970047-8013-16-git-send-email-dkg@fifthhorseman.net>\r
+X-Mailer: git-send-email 2.8.1\r
+In-Reply-To: <1467970047-8013-1-git-send-email-dkg@fifthhorseman.net>\r
+References: <1467970047-8013-1-git-send-email-dkg@fifthhorseman.net>\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.20\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+ <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <https://notmuchmail.org/mailman/options/notmuch>,\r
+ <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch/>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <https://notmuchmail.org/mailman/listinfo/notmuch>,\r
+ <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Fri, 08 Jul 2016 10:13:16 -0000\r
+\r
+This new function asks the database to reindex a given message, using\r
+the supplied indexopts.\r
+\r
+This can be used, for example, to index the cleartext of an encrypted\r
+message.\r
+\r
+My initial inclination for this implementation was to remove all the\r
+indexed terms for a given message's body, and then to add them back\r
+in.\r
+\r
+Unfortunately, that doesn't appear to be possible due to the way we're\r
+using xapian. I could find no way to distinguish terms which were\r
+added during indexing of the message body from other terms associated\r
+with the document. As a result, we just save the tags and properties,\r
+remove the message from the database entirely, and add it back into\r
+the database in full, re-adding tags and properties as needed.\r
+---\r
+ lib/message.cc | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-\r
+ lib/notmuch.h | 14 ++++++++\r
+ 2 files changed, 121 insertions(+), 1 deletion(-)\r
+\r
+diff --git a/lib/message.cc b/lib/message.cc\r
+index 9d3e807..ab807b7 100644\r
+--- a/lib/message.cc\r
++++ b/lib/message.cc\r
+@@ -557,7 +557,9 @@ void\r
+ _notmuch_message_remove_terms (notmuch_message_t *message, const char *prefix)\r
+ {\r
+ Xapian::TermIterator i;\r
+- size_t prefix_len = strlen (prefix);\r
++ size_t prefix_len = 0;\r
++\r
++ prefix_len = strlen (prefix);\r
+ \r
+ while (1) {\r
+ i = message->doc.termlist_begin ();\r
+@@ -1847,3 +1849,107 @@ _notmuch_message_frozen (notmuch_message_t *message)\r
+ {\r
+ return message->frozen;\r
+ }\r
++\r
++notmuch_status_t\r
++notmuch_message_reindex (notmuch_message_t *message,\r
++ notmuch_indexopts_t *indexopts)\r
++{\r
++ notmuch_database_t *notmuch = NULL;\r
++ notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS, status;\r
++ notmuch_tags_t *tags = NULL;\r
++ notmuch_message_properties_t *properties = NULL;\r
++ notmuch_filenames_t *filenames, *orig_filenames = NULL;\r
++ const char *filename = NULL, *tag = NULL, *propkey = NULL;\r
++ notmuch_message_t *newmsg = NULL;\r
++ notmuch_bool_t readded = FALSE, skip;\r
++ const char *autotags[] = {\r
++ "attachment",\r
++ "encrypted",\r
++ "signed" };\r
++ const char *autoproperties[] = { "index-decryption" };\r
++\r
++ if (message == NULL)\r
++ return NOTMUCH_STATUS_NULL_POINTER;\r
++ \r
++ notmuch = _notmuch_message_database (message);\r
++\r
++ /* cache tags, properties, and filenames */\r
++ tags = notmuch_message_get_tags (message);\r
++ properties = notmuch_message_get_properties (message, "", FALSE);\r
++ filenames = notmuch_message_get_filenames (message);\r
++ orig_filenames = notmuch_message_get_filenames (message);\r
++ \r
++ /* walk through filenames, removing them until the message is gone */\r
++ for ( ; notmuch_filenames_valid (filenames);\r
++ notmuch_filenames_move_to_next (filenames)) {\r
++ filename = notmuch_filenames_get (filenames);\r
++\r
++ ret = notmuch_database_remove_message (notmuch, filename);\r
++ if (ret != NOTMUCH_STATUS_SUCCESS &&\r
++ ret != NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID)\r
++ return ret;\r
++ }\r
++ if (ret != NOTMUCH_STATUS_SUCCESS)\r
++ return ret;\r
++ \r
++ /* re-add the filenames with the associated indexopts */\r
++ for (; notmuch_filenames_valid (orig_filenames);\r
++ notmuch_filenames_move_to_next (orig_filenames)) {\r
++ filename = notmuch_filenames_get (orig_filenames);\r
++\r
++ status = notmuch_database_add_message_with_indexopts(notmuch,\r
++ filename,\r
++ indexopts,\r
++ readded ? NULL : &newmsg);\r
++ if (status == NOTMUCH_STATUS_SUCCESS ||\r
++ status == NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID) {\r
++ if (!readded) {\r
++ /* re-add tags */\r
++ for (; notmuch_tags_valid (tags);\r
++ notmuch_tags_move_to_next (tags)) {\r
++ tag = notmuch_tags_get (tags);\r
++ skip = FALSE;\r
++ \r
++ for (size_t i = 0; i < ARRAY_SIZE (autotags); i++)\r
++ if (strcmp (tag, autotags[i]) == 0)\r
++ skip = TRUE;\r
++ \r
++ if (!skip) {\r
++ status = notmuch_message_add_tag (newmsg, tag);\r
++ if (status != NOTMUCH_STATUS_SUCCESS)\r
++ ret = status;\r
++ }\r
++ }\r
++ /* re-add properties */\r
++ for (; notmuch_message_properties_valid (properties);\r
++ notmuch_message_properties_move_to_next (properties)) {\r
++ propkey = notmuch_message_properties_key (properties);\r
++ skip = FALSE;\r
++\r
++ for (size_t i = 0; i < ARRAY_SIZE (autoproperties); i++)\r
++ if (strcmp (propkey, autoproperties[i]) == 0)\r
++ skip = TRUE;\r
++\r
++ if (!skip) {\r
++ status = notmuch_message_add_property (newmsg, propkey,\r
++ notmuch_message_properties_value (properties));\r
++ if (status != NOTMUCH_STATUS_SUCCESS)\r
++ ret = status;\r
++ }\r
++ }\r
++ readded = TRUE;\r
++ }\r
++ } else {\r
++ /* if we failed to add this filename, go ahead and try the\r
++ * next one as though it were first, but report the\r
++ * error... */\r
++ ret = status;\r
++ }\r
++ }\r
++ if (newmsg)\r
++ notmuch_message_destroy (newmsg);\r
++ \r
++ /* should we also destroy the incoming message object? at the\r
++ * moment, we leave that to the caller */\r
++ return ret;\r
++}\r
+diff --git a/lib/notmuch.h b/lib/notmuch.h\r
+index 66b3503..9076a9b 100644\r
+--- a/lib/notmuch.h\r
++++ b/lib/notmuch.h\r
+@@ -1394,6 +1394,20 @@ notmuch_filenames_t *\r
+ notmuch_message_get_filenames (notmuch_message_t *message);\r
+ \r
+ /**\r
++ * Re-index the e-mail corresponding to 'message' using the supplied index options\r
++ *\r
++ * Returns the status of the re-index operation. (see the return\r
++ * codes documented in notmuch_database_add_message)\r
++ *\r
++ * After reindexing, the user should discard the message object passed\r
++ * in here by calling notmuch_message_destroy, since it refers to the\r
++ * original message, not to the reindexed message.\r
++ */\r
++notmuch_status_t\r
++notmuch_message_reindex (notmuch_message_t *message,\r
++ notmuch_indexopts_t *indexopts);\r
++\r
++/**\r
+ * Message flags.\r
+ */\r
+ typedef enum _notmuch_message_flag {\r
+-- \r
+2.8.1\r
+\r