]> git.notmuchmail.org Git - notmuch/blobdiff - lib/message.cc
lib: catch error from closed db in n_m_get_message_id
[notmuch] / lib / message.cc
index b2067076effb9981c56a16ffdbd2ca4fdbe1c849..b7a64b1c3894c26224e78be165bf58ac445fa777 100644 (file)
@@ -32,6 +32,7 @@ struct _notmuch_message {
     int frozen;
     char *message_id;
     char *thread_id;
+    size_t thread_depth;
     char *in_reply_to;
     notmuch_string_list_t *tag_list;
     notmuch_string_list_t *filename_term_list;
@@ -41,6 +42,7 @@ struct _notmuch_message {
     notmuch_message_file_t *message_file;
     notmuch_string_list_t *property_term_list;
     notmuch_string_map_t *property_map;
+    notmuch_string_list_t *reference_list;
     notmuch_message_list_t *replies;
     unsigned long flags;
     /* For flags that are initialized on-demand, lazy_flags indicates
@@ -67,10 +69,10 @@ struct maildir_flag_tag {
 
 /* ASCII ordered table of Maildir flags and associated tags */
 static struct maildir_flag_tag flag2tag[] = {
-    { 'D', "draft",   false},
-    { 'F', "flagged", false},
-    { 'P', "passed",  false},
-    { 'R', "replied", false},
+    { 'D', "draft",   false },
+    { 'F', "flagged", false },
+    { 'P', "passed",  false },
+    { 'R', "replied", false },
     { 'S', "unread",  true }
 };
 
@@ -88,6 +90,18 @@ _notmuch_message_destructor (notmuch_message_t *message)
     return 0;
 }
 
+#define LOG_XAPIAN_EXCEPTION(message, error) _log_xapian_exception (__location__, message, error)
+
+static void
+_log_xapian_exception (const char *where, notmuch_message_t *message,  const Xapian::Error error) {
+    notmuch_database_t *notmuch = notmuch_message_get_database (message);
+    _notmuch_database_log (notmuch,
+                          "A Xapian exception occurred %s retrieving %s : %s\n",
+                          where,
+                          error.get_msg ().c_str ());
+    notmuch->exception_reported = true;
+}
+
 static notmuch_message_t *
 _notmuch_message_create_for_document (const void *talloc_owner,
                                      notmuch_database_t *notmuch,
@@ -117,6 +131,9 @@ _notmuch_message_create_for_document (const void *talloc_owner,
     /* the message is initially not synchronized with Xapian */
     message->last_view = 0;
 
+    /* Calculated after the thread structure is computed */
+    message->thread_depth = 0;
+
     /* Each of these will be lazily created as needed. */
     message->message_id = NULL;
     message->thread_id = NULL;
@@ -129,6 +146,7 @@ _notmuch_message_create_for_document (const void *talloc_owner,
     message->author = NULL;
     message->property_term_list = NULL;
     message->property_map = NULL;
+    message->reference_list = NULL;
 
     message->replies = _notmuch_message_list_create (message);
     if (unlikely (message->replies == NULL)) {
@@ -268,8 +286,8 @@ _notmuch_message_create_for_message_id (notmuch_database_t *notmuch,
 
        doc_id = _notmuch_database_generate_doc_id (notmuch);
     } catch (const Xapian::Error &error) {
-       _notmuch_database_log(_notmuch_message_database (message), "A Xapian exception occurred creating message: %s\n",
-                error.get_msg().c_str());
+       _notmuch_database_log (notmuch_message_get_database (message), "A Xapian exception occurred creating message: %s\n",
+                              error.get_msg ().c_str ());
        notmuch->exception_reported = true;
        *status_ret = NOTMUCH_PRIVATE_STATUS_XAPIAN_EXCEPTION;
        return NULL;
@@ -300,10 +318,10 @@ _notmuch_message_get_term (notmuch_message_t *message,
        return NULL;
 
     const std::string &term = *i;
-    if (strncmp (term.c_str(), prefix, prefix_len))
+    if (strncmp (term.c_str (), prefix, prefix_len))
        return NULL;
 
-    value = talloc_strdup (message, term.c_str() + prefix_len);
+    value = talloc_strdup (message, term.c_str () + prefix_len);
 
 #if DEBUG_DATABASE_SANITY
     i++;
@@ -344,31 +362,32 @@ _notmuch_message_ensure_metadata (notmuch_message_t *message, void *field)
        return;
 
     const char *thread_prefix = _find_prefix ("thread"),
-       *tag_prefix = _find_prefix ("tag"),
-       *id_prefix = _find_prefix ("id"),
-       *type_prefix = _find_prefix ("type"),
-       *filename_prefix = _find_prefix ("file-direntry"),
-       *property_prefix = _find_prefix ("property"),
-       *replyto_prefix = _find_prefix ("replyto");
+              *tag_prefix = _find_prefix ("tag"),
+              *id_prefix = _find_prefix ("id"),
+              *type_prefix = _find_prefix ("type"),
+              *filename_prefix = _find_prefix ("file-direntry"),
+              *property_prefix = _find_prefix ("property"),
+              *reference_prefix = _find_prefix ("reference"),
+              *replyto_prefix = _find_prefix ("replyto");
 
     /* We do this all in a single pass because Xapian decompresses the
      * term list every time you iterate over it.  Thus, while this is
      * slightly more costly than looking up individual fields if only
      * one field of the message object is actually used, it's a huge
      * win as more fields are used. */
-    for (int count=0; count < 3; count++) {
+    for (int count = 0; count < 3; count++) {
        try {
            i = message->doc.termlist_begin ();
            end = message->doc.termlist_end ();
 
            /* Get thread */
-           if (!message->thread_id)
+           if (! message->thread_id)
                message->thread_id =
                    _notmuch_message_get_term (message, i, end, thread_prefix);
 
            /* Get tags */
            assert (strcmp (thread_prefix, tag_prefix) < 0);
-           if (!message->tag_list) {
+           if (! message->tag_list) {
                message->tag_list =
                    _notmuch_database_get_terms_with_prefix (message, i, end,
                                                             tag_prefix);
@@ -377,7 +396,7 @@ _notmuch_message_ensure_metadata (notmuch_message_t *message, void *field)
 
            /* Get id */
            assert (strcmp (tag_prefix, id_prefix) < 0);
-           if (!message->message_id)
+           if (! message->message_id)
                message->message_id =
                    _notmuch_message_get_term (message, i, end, id_prefix);
 
@@ -400,7 +419,7 @@ _notmuch_message_ensure_metadata (notmuch_message_t *message, void *field)
             * expand them to full file names when needed in
             * _notmuch_message_ensure_filename_list. */
            assert (strcmp (type_prefix, filename_prefix) < 0);
-           if (!message->filename_term_list && !message->filename_list)
+           if (! message->filename_term_list && ! message->filename_list)
                message->filename_term_list =
                    _notmuch_database_get_terms_with_prefix (message, i, end,
                                                             filename_prefix);
@@ -408,21 +427,29 @@ _notmuch_message_ensure_metadata (notmuch_message_t *message, void *field)
 
            /* Get property terms. Mimic the setup with filenames above */
            assert (strcmp (filename_prefix, property_prefix) < 0);
-           if (!message->property_map && !message->property_term_list)
+           if (! message->property_map && ! message->property_term_list)
                message->property_term_list =
                    _notmuch_database_get_terms_with_prefix (message, i, end,
-                                                        property_prefix);
+                                                            property_prefix);
+
+           /* get references */
+           assert (strcmp (property_prefix, reference_prefix) < 0);
+           if (! message->reference_list) {
+               message->reference_list =
+                   _notmuch_database_get_terms_with_prefix (message, i, end,
+                                                            reference_prefix);
+           }
 
            /* Get reply to */
            assert (strcmp (property_prefix, replyto_prefix) < 0);
-           if (!message->in_reply_to)
+           if (! message->in_reply_to)
                message->in_reply_to =
                    _notmuch_message_get_term (message, i, end, replyto_prefix);
 
 
            /* It's perfectly valid for a message to have no In-Reply-To
             * header. For these cases, we return an empty string. */
-           if (!message->in_reply_to)
+           if (! message->in_reply_to)
                message->in_reply_to = talloc_strdup (message, "");
 
            /* all the way without an exception */
@@ -432,9 +459,6 @@ _notmuch_message_ensure_metadata (notmuch_message_t *message, void *field)
            if (status != NOTMUCH_STATUS_SUCCESS)
                INTERNAL_ERROR ("unhandled error from notmuch_database_reopen: %s\n",
                                notmuch_status_to_string (status));
-       } catch (const Xapian::Error &error) {
-           INTERNAL_ERROR ("A Xapian exception occurred fetching message metadata: %s\n",
-                           error.get_msg().c_str());
        }
     }
     message->last_view = message->notmuch->view;
@@ -492,8 +516,14 @@ _notmuch_message_get_doc_id (notmuch_message_t *message)
 const char *
 notmuch_message_get_message_id (notmuch_message_t *message)
 {
-    _notmuch_message_ensure_metadata (message, message->message_id);
-    if (!message->message_id)
+    try {
+       _notmuch_message_ensure_metadata (message, message->message_id);
+    } catch (const Xapian::Error &error) {
+       LOG_XAPIAN_EXCEPTION (message, error);
+       return NULL;
+    }
+
+    if (! message->message_id)
        INTERNAL_ERROR ("Message with document ID of %u has no message ID.\n",
                        message->doc_id);
     return message->message_id;
@@ -512,7 +542,7 @@ _notmuch_message_ensure_message_file (notmuch_message_t *message)
        return;
 
     message->message_file = _notmuch_message_file_open_ctx (
-       _notmuch_message_database (message), message, filename);
+       notmuch_message_get_database (message), message, filename);
 }
 
 const char *
@@ -538,12 +568,12 @@ notmuch_message_get_header (notmuch_message_t *message, const char *header)
             * it could just mean we didn't record the header. */
            if ((message->notmuch->features &
                 NOTMUCH_FEATURE_FROM_SUBJECT_ID_VALUES) ||
-               ! value.empty())
+               ! value.empty ())
                return talloc_strdup (message, value.c_str ());
 
        } catch (Xapian::Error &error) {
-           _notmuch_database_log(_notmuch_message_database (message), "A Xapian exception occurred when reading header: %s\n",
-                    error.get_msg().c_str());
+           _notmuch_database_log (notmuch_message_get_database (message), "A Xapian exception occurred when reading header: %s\n",
+                                  error.get_msg ().c_str ());
            message->notmuch->exception_reported = true;
            return NULL;
        }
@@ -575,7 +605,7 @@ const char *
 notmuch_message_get_thread_id (notmuch_message_t *message)
 {
     _notmuch_message_ensure_metadata (message, message->thread_id);
-    if (!message->thread_id)
+    if (! message->thread_id)
        INTERNAL_ERROR ("Message with document ID of %u has no thread ID.\n",
                        message->doc_id);
     return message->thread_id;
@@ -588,6 +618,85 @@ _notmuch_message_add_reply (notmuch_message_t *message,
     _notmuch_message_list_add_message (message->replies, reply);
 }
 
+size_t
+_notmuch_message_get_thread_depth (notmuch_message_t *message)
+{
+    return message->thread_depth;
+}
+
+void
+_notmuch_message_label_depths (notmuch_message_t *message,
+                              size_t depth)
+{
+    message->thread_depth = depth;
+
+    for (notmuch_messages_t *messages = _notmuch_messages_create (message->replies);
+        notmuch_messages_valid (messages);
+        notmuch_messages_move_to_next (messages)) {
+       notmuch_message_t *child = notmuch_messages_get (messages);
+       _notmuch_message_label_depths (child, depth + 1);
+    }
+}
+
+const notmuch_string_list_t *
+_notmuch_message_get_references (notmuch_message_t *message)
+{
+    _notmuch_message_ensure_metadata (message, message->reference_list);
+    return message->reference_list;
+}
+
+static int
+_cmpmsg (const void *pa, const void *pb)
+{
+    notmuch_message_t **a = (notmuch_message_t **) pa;
+    notmuch_message_t **b = (notmuch_message_t **) pb;
+    time_t time_a = notmuch_message_get_date (*a);
+    time_t time_b = notmuch_message_get_date (*b);
+
+    if (time_a == time_b)
+       return 0;
+    else if (time_a < time_b)
+       return -1;
+    else
+       return 1;
+}
+
+notmuch_message_list_t *
+_notmuch_message_sort_subtrees (void *ctx, notmuch_message_list_t *list)
+{
+
+    size_t count = 0;
+    size_t capacity = 16;
+
+    if (! list)
+       return list;
+
+    void *local = talloc_new (NULL);
+    notmuch_message_list_t *new_list = _notmuch_message_list_create (ctx);
+    notmuch_message_t **message_array = talloc_zero_array (local, notmuch_message_t *, capacity);
+
+    for (notmuch_messages_t *messages = _notmuch_messages_create (list);
+        notmuch_messages_valid (messages);
+        notmuch_messages_move_to_next (messages)) {
+       notmuch_message_t *root = notmuch_messages_get (messages);
+       if (count >= capacity) {
+           capacity *= 2;
+           message_array = talloc_realloc (local, message_array, notmuch_message_t *, capacity);
+       }
+       message_array[count++] = root;
+       root->replies = _notmuch_message_sort_subtrees (root, root->replies);
+    }
+
+    qsort (message_array, count, sizeof (notmuch_message_t *), _cmpmsg);
+    for (size_t i = 0; i < count; i++) {
+       _notmuch_message_list_add_message (new_list, message_array[i]);
+    }
+
+    talloc_free (local);
+    talloc_free (list);
+    return new_list;
+}
+
 notmuch_messages_t *
 notmuch_message_get_replies (notmuch_message_t *message)
 {
@@ -623,6 +732,8 @@ _notmuch_message_remove_terms (notmuch_message_t *message, const char *prefix)
 
 /* Remove all terms generated by indexing, i.e. not tags or
  * properties, along with any automatic tags*/
+/* According to Xapian API docs, none of these calls throw
+ * exceptions */
 notmuch_private_status_t
 _notmuch_message_remove_indexed_terms (notmuch_message_t *message)
 {
@@ -634,47 +745,56 @@ _notmuch_message_remove_indexed_terms (notmuch_message_t *message)
        tag_prefix = _find_prefix ("tag"),
        type_prefix = _find_prefix ("type");
 
-    for (i = message->doc.termlist_begin ();
-        i != message->doc.termlist_end (); i++) {
+    /* Make sure we have the data to restore to Xapian*/
+    _notmuch_message_ensure_metadata (message, NULL);
 
-       const std::string term = *i;
+    /* Empirically, it turns out to be faster to remove all the terms,
+     * and add back the ones we want. */
+    message->doc.clear_terms ();
+    message->modified = true;
 
-       if (term.compare (0, type_prefix.size (), type_prefix) == 0)
-           continue;
+    /* still a mail message */
+    message->doc.add_term (type_prefix + "mail");
 
-       if (term.compare (0, id_prefix.size (), id_prefix) == 0)
-           continue;
-
-       if (term.compare (0, property_prefix.size (), property_prefix) == 0)
-           continue;
+    /* Put back message-id */
+    message->doc.add_term (id_prefix + message->message_id);
 
-       if (term.compare (0, tag_prefix.size (), tag_prefix) == 0 &&
-           term.compare (1, strlen("encrypted"), "encrypted") != 0 &&
-           term.compare (1, strlen("signed"), "signed") != 0 &&
-           term.compare (1, strlen("attachment"), "attachment") != 0)
-           continue;
+    /* Put back non-automatic tags */
+    for (notmuch_tags_t *tags = notmuch_message_get_tags (message);
+        notmuch_tags_valid (tags);
+        notmuch_tags_move_to_next (tags)) {
 
-       try {
-           message->doc.remove_term ((*i));
-           message->modified = true;
-       } catch (const Xapian::InvalidArgumentError) {
-           /* Ignore failure to remove non-existent term. */
-       } catch (const Xapian::Error &error) {
-           notmuch_database_t *notmuch = message->notmuch;
+       const char *tag = notmuch_tags_get (tags);
 
-           if (!notmuch->exception_reported) {
-               _notmuch_database_log(_notmuch_message_database (message), "A Xapian exception occurred creating message: %s\n",
-                                     error.get_msg().c_str());
-               notmuch->exception_reported = true;
-           }
-           return NOTMUCH_PRIVATE_STATUS_XAPIAN_EXCEPTION;
+       if (strcmp (tag, "encrypted") != 0 &&
+           strcmp (tag, "signed") != 0 &&
+           strcmp (tag, "attachment") != 0) {
+           std::string term = tag_prefix + tag;
+           message->doc.add_term (term);
        }
     }
+
+    /* Put back properties */
+    notmuch_message_properties_t *list;
+
+    for (list = notmuch_message_get_properties (message, "", false);
+        notmuch_message_properties_valid (list); notmuch_message_properties_move_to_next (list)) {
+       std::string term = property_prefix +
+                          notmuch_message_properties_key (list) + "=" +
+                          notmuch_message_properties_value (list);
+
+       message->doc.add_term (term);
+    }
+
+    notmuch_message_properties_destroy (list);
+
     return NOTMUCH_PRIVATE_STATUS_SUCCESS;
 }
 
+
 /* Return true if p points at "new" or "cur". */
-static bool is_maildir (const char *p)
+static bool
+is_maildir (const char *p)
 {
     return strcmp (p, "cur") == 0 || strcmp (p, "new") == 0;
 }
@@ -869,7 +989,7 @@ _notmuch_message_remove_filename (notmuch_message_t *message,
 
     status = _notmuch_database_filename_to_direntry (
        local, message->notmuch, filename, NOTMUCH_FIND_LOOKUP, &direntry);
-    if (status || !direntry)
+    if (status || ! direntry)
        return status;
 
     /* Unlink this file from its parent directory. */
@@ -938,7 +1058,7 @@ _notmuch_message_ensure_filename_list (notmuch_message_t *message)
     message->filename_list = _notmuch_string_list_create (message);
     node = message->filename_term_list->head;
 
-    if (!node) {
+    if (! node) {
        /* A message document created by an old version of notmuch
         * (prior to rename support) will have the filename in the
         * data of the document rather than as a file-direntry term.
@@ -1005,8 +1125,7 @@ notmuch_message_get_filename (notmuch_message_t *message)
        return NULL;
 
     if (message->filename_list->head == NULL ||
-       message->filename_list->head->string == NULL)
-    {
+       message->filename_list->head->string == NULL) {
        INTERNAL_ERROR ("message with no filename");
     }
 
@@ -1059,8 +1178,8 @@ notmuch_message_get_date (notmuch_message_t *message)
     try {
        value = message->doc.get_value (NOTMUCH_VALUE_TIMESTAMP);
     } catch (Xapian::Error &error) {
-       _notmuch_database_log(_notmuch_message_database (message), "A Xapian exception occurred when reading date: %s\n",
-                error.get_msg().c_str());
+       _notmuch_database_log (notmuch_message_get_database (message), "A Xapian exception occurred when reading date: %s\n",
+                              error.get_msg ().c_str ());
        message->notmuch->exception_reported = true;
        return 0;
     }
@@ -1085,7 +1204,7 @@ notmuch_message_get_tags (notmuch_message_t *message)
      * possible to modify the message tags (which talloc_unlink's the
      * current list from the message) while still iterating because
      * the iterator will keep the current list alive. */
-    if (!talloc_reference (message, message->tag_list))
+    if (! talloc_reference (message, message->tag_list))
        return NULL;
 
     return tags;
@@ -1099,11 +1218,11 @@ _notmuch_message_get_author (notmuch_message_t *message)
 
 void
 _notmuch_message_set_author (notmuch_message_t *message,
-                           const char *author)
+                            const char *author)
 {
     if (message->author)
-       talloc_free(message->author);
-    message->author = talloc_strdup(message, author);
+       talloc_free (message->author);
+    message->author = talloc_strdup (message, author);
     return;
 }
 
@@ -1135,6 +1254,14 @@ _notmuch_message_set_header_values (notmuch_message_t *message,
     message->modified = true;
 }
 
+void
+_notmuch_message_update_subject (notmuch_message_t *message,
+                                const char *subject)
+{
+    message->doc.add_value (NOTMUCH_VALUE_SUBJECT, subject);
+    message->modified = true;
+}
+
 /* Upgrade a message to support NOTMUCH_FEATURE_LAST_MOD.  The caller
  * must call _notmuch_message_sync. */
 void
@@ -1228,8 +1355,8 @@ _notmuch_message_delete (notmuch_message_t *message)
                _notmuch_message_sync (ghost);
        } else if (private_status == NOTMUCH_PRIVATE_STATUS_SUCCESS) {
            /* this is deeply weird, and we should not have gotten
-              into this state.  is there a better error message to
-              return here? */
+            * into this state.  is there a better error message to
+            * return here? */
            status = NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID;
        }
 
@@ -1247,8 +1374,8 @@ _notmuch_message_delete (notmuch_message_t *message)
                message = notmuch_messages_get (messages);
                status = _notmuch_message_delete (message);
                if (status) /* we'll report the last failure we see;
-                            * if there is more than one failure, we
-                            * forget about previous ones */
+                                        * if there is more than one failure, we
+                                        * forget about previous ones */
                    last_error = status;
                notmuch_message_destroy (message);
                notmuch_messages_move_to_next (messages);
@@ -1326,8 +1453,9 @@ _notmuch_message_add_term (notmuch_message_t *message,
 }
 
 /* Parse 'text' and add a term to 'message' for each parsed word. Each
- * term will be added both prefixed (if prefix_name is not NULL) and
- * also non-prefixed). */
+ * term will be added with the appropriate prefix if prefix_name is
+ * non-NULL.
+ */
 notmuch_private_status_t
 _notmuch_message_gen_terms (notmuch_message_t *message,
                            const char *prefix_name,
@@ -1339,22 +1467,21 @@ _notmuch_message_gen_terms (notmuch_message_t *message,
        return NOTMUCH_PRIVATE_STATUS_NULL_POINTER;
 
     term_gen->set_document (message->doc);
+    term_gen->set_termpos (message->termpos);
 
     if (prefix_name) {
-       const char *prefix = _find_prefix (prefix_name);
-
-       term_gen->set_termpos (message->termpos);
-       term_gen->index_text (text, 1, prefix);
-       /* Create a gap between this an the next terms so they don't
-        * appear to be a phrase. */
-       message->termpos = term_gen->get_termpos () + 100;
+       const char *prefix = _notmuch_database_prefix (message->notmuch, prefix_name);
+       if (prefix == NULL)
+           return NOTMUCH_PRIVATE_STATUS_BAD_PREFIX;
 
        _notmuch_message_invalidate_metadata (message, prefix_name);
+       term_gen->index_text (text, 1, prefix);
+    } else {
+       term_gen->index_text (text);
     }
 
-    term_gen->set_termpos (message->termpos);
-    term_gen->index_text (text);
-    /* Create a term gap, as above. */
+    /* Create a gap between this an the next terms so they don't
+     * appear to be a phrase. */
     message->termpos = term_gen->get_termpos () + 100;
 
     return NOTMUCH_PRIVATE_STATUS_SUCCESS;
@@ -1424,7 +1551,7 @@ _notmuch_message_has_term (notmuch_message_t *message,
        Xapian::TermIterator i = message->doc.termlist_begin ();
        i.skip_to (term);
        if (i != message->doc.termlist_end () &&
-           !strcmp ((*i).c_str (), term))
+           ! strcmp ((*i).c_str (), term))
            out = true;
     } catch (Xapian::Error &error) {
        status = NOTMUCH_PRIVATE_STATUS_XAPIAN_EXCEPTION;
@@ -1524,8 +1651,7 @@ _filename_is_in_maildir (const char *filename)
     dir = slash + 1;
 
     if (STRNCMP_LITERAL (dir, "cur/") == 0 ||
-       STRNCMP_LITERAL (dir, "new/") == 0)
-    {
+       STRNCMP_LITERAL (dir, "new/") == 0) {
        return dir;
     }
 
@@ -1550,8 +1676,7 @@ _ensure_maildir_flags (notmuch_message_t *message, bool force)
 
     for (filenames = notmuch_message_get_filenames (message);
         notmuch_filenames_valid (filenames);
-        notmuch_filenames_move_to_next (filenames))
-    {
+        notmuch_filenames_move_to_next (filenames)) {
        filename = notmuch_filenames_get (filenames);
        dir = _filename_is_in_maildir (filename);
 
@@ -1601,11 +1726,10 @@ notmuch_message_maildir_flags_to_tags (notmuch_message_t *message)
     if (status)
        return status;
 
-    for (i = 0; i < ARRAY_SIZE(flag2tag); i++) {
+    for (i = 0; i < ARRAY_SIZE (flag2tag); i++) {
        if ((strchr (message->maildir_flags, flag2tag[i].flag) != NULL)
            ^
-           flag2tag[i].inverse)
-       {
+           flag2tag[i].inverse) {
            status = notmuch_message_add_tag (message, flag2tag[i].tag);
        } else {
            status = notmuch_message_remove_tag (message, flag2tag[i].tag);
@@ -1640,8 +1764,7 @@ _get_maildir_flag_actions (notmuch_message_t *message,
     /* First, find flags for all set tags. */
     for (tags = notmuch_message_get_tags (message);
         notmuch_tags_valid (tags);
-        notmuch_tags_move_to_next (tags))
-    {
+        notmuch_tags_move_to_next (tags)) {
        tag = notmuch_tags_get (tags);
 
        for (i = 0; i < ARRAY_SIZE (flag2tag); i++) {
@@ -1691,7 +1814,7 @@ _get_maildir_flag_actions (notmuch_message_t *message,
  * non-ASCII ordering of flags), this function will return NULL
  * (meaning that renaming would not be safe and should not occur).
  */
-static char*
+static char *
 _new_maildir_filename (void *ctx,
                       const char *filename,
                       const char *flags_to_set,
@@ -1711,13 +1834,12 @@ _new_maildir_filename (void *ctx,
     info = strstr (filename, ":2,");
 
     if (info == NULL) {
-       info = filename + strlen(filename);
+       info = filename + strlen (filename);
     } else {
        /* Loop through existing flags in filename. */
        for (flags = info + 3, last_flag = 0;
             *flags;
-            last_flag = flag, flags++)
-       {
+            last_flag = flag, flags++) {
            flag = *flags;
 
            /* Original flags not in ASCII order. Abort. */
@@ -1725,7 +1847,7 @@ _new_maildir_filename (void *ctx,
                return NULL;
 
            /* Non-ASCII flag. Abort. */
-           if (flag > sizeof(flag_map) - 1)
+           if (flag > sizeof (flag_map) - 1)
                return NULL;
 
            /* Repeated flag value. Abort. */
@@ -1759,7 +1881,7 @@ _new_maildir_filename (void *ctx,
     /* Messages in new/ without maildir info can be kept in new/ if no
      * flags have changed. */
     dir = (char *) _filename_is_in_maildir (filename);
-    if (dir && STRNCMP_LITERAL (dir, "new/") == 0 && !*info && !flags_changed)
+    if (dir && STRNCMP_LITERAL (dir, "new/") == 0 && ! *info && ! flags_changed)
        return talloc_strdup (ctx, filename);
 
     filename_new = (char *) talloc_size (ctx,
@@ -1774,8 +1896,7 @@ _new_maildir_filename (void *ctx,
     strcat (filename_new, ":2,");
 
     s = filename_new + strlen (filename_new);
-    for (i = 0; i < sizeof (flag_map); i++)
-    {
+    for (i = 0; i < sizeof (flag_map); i++) {
        if (flag_map[i]) {
            *s = i;
            s++;
@@ -1804,8 +1925,7 @@ notmuch_message_tags_to_maildir_flags (notmuch_message_t *message)
 
     for (filenames = notmuch_message_get_filenames (message);
         notmuch_filenames_valid (filenames);
-        notmuch_filenames_move_to_next (filenames))
-    {
+        notmuch_filenames_move_to_next (filenames)) {
        filename = notmuch_filenames_get (filenames);
 
        if (! _filename_is_in_maildir (filename))
@@ -1867,8 +1987,7 @@ notmuch_message_remove_all_tags (notmuch_message_t *message)
 
     for (tags = notmuch_message_get_tags (message);
         notmuch_tags_valid (tags);
-        notmuch_tags_move_to_next (tags))
-    {
+        notmuch_tags_move_to_next (tags)) {
        tag = notmuch_tags_get (tags);
 
        private_status = _notmuch_message_remove_term (message, "tag", tag);
@@ -1925,7 +2044,7 @@ notmuch_message_destroy (notmuch_message_t *message)
 }
 
 notmuch_database_t *
-_notmuch_message_database (notmuch_message_t *message)
+notmuch_message_get_database (const notmuch_message_t *message)
 {
     return message->notmuch;
 }
@@ -1946,8 +2065,8 @@ _notmuch_message_ensure_property_map (notmuch_message_t *message)
        const char *key;
        char *value;
 
-       value = strchr(node->string, '=');
-       if (!value)
+       value = strchr (node->string, '=');
+       if (! value)
            INTERNAL_ERROR ("malformed property term");
 
        *value = '\0';
@@ -1994,7 +2113,7 @@ notmuch_message_reindex (notmuch_message_t *message,
 
     /* Save in case we need to delete message */
     orig_thread_id = notmuch_message_get_thread_id (message);
-    if (!orig_thread_id) {
+    if (! orig_thread_id) {
        /* XXX TODO: make up new error return? */
        INTERNAL_ERROR ("message without thread-id");
     }
@@ -2002,7 +2121,7 @@ notmuch_message_reindex (notmuch_message_t *message,
     /* strdup it because the metadata may be invalidated */
     orig_thread_id = talloc_strdup (message, orig_thread_id);
 
-    notmuch = _notmuch_message_database (message);
+    notmuch = notmuch_message_get_database (message);
 
     ret = _notmuch_database_ensure_writable (notmuch);
     if (ret)
@@ -2012,7 +2131,7 @@ notmuch_message_reindex (notmuch_message_t *message,
 
     private_status = _notmuch_message_remove_indexed_terms (message);
     if (private_status) {
-       ret = COERCE_STATUS(private_status, "error removing terms");
+       ret = COERCE_STATUS (private_status, "error removing terms");
        goto DONE;
     }
 
@@ -2083,7 +2202,7 @@ notmuch_message_reindex (notmuch_message_t *message,
        _notmuch_message_sync (message);
     }
 
- DONE:
 DONE:
     if (message_file)
        _notmuch_message_file_close (message_file);