]> git.notmuchmail.org Git - notmuch/blobdiff - lib/message.cc
lib/thread: sort sibling messages by date
[notmuch] / lib / message.cc
index 539d3320b24144b093db6332785fe23e2218a6d2..23266c1af90f5823d9adf95d959f7ad3d8bbd310 100644 (file)
@@ -36,6 +36,7 @@ struct _notmuch_message {
     notmuch_string_list_t *tag_list;
     notmuch_string_list_t *filename_term_list;
     notmuch_string_list_t *filename_list;
+    char *maildir_flags;
     char *author;
     notmuch_message_file_t *message_file;
     notmuch_string_list_t *property_term_list;
@@ -47,7 +48,7 @@ struct _notmuch_message {
     unsigned long lazy_flags;
 
     /* Message document modified since last sync */
-    notmuch_bool_t modified;
+    bool modified;
 
     /* last view of database the struct is synced with */
     unsigned long last_view;
@@ -61,16 +62,16 @@ struct _notmuch_message {
 struct maildir_flag_tag {
     char flag;
     const char *tag;
-    notmuch_bool_t inverse;
+    bool inverse;
 };
 
 /* 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},
-    { 'S', "unread",  TRUE }
+    { 'D', "draft",   false},
+    { 'F', "flagged", false},
+    { 'P', "passed",  false},
+    { 'R', "replied", false},
+    { 'S', "unread",  true }
 };
 
 /* We end up having to call the destructor explicitly because we had
@@ -123,6 +124,7 @@ _notmuch_message_create_for_document (const void *talloc_owner,
     message->tag_list = NULL;
     message->filename_term_list = NULL;
     message->filename_list = NULL;
+    message->maildir_flags = NULL;
     message->message_file = NULL;
     message->author = NULL;
     message->property_term_list = NULL;
@@ -266,9 +268,9 @@ _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",
+       _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;
+       notmuch->exception_reported = true;
        *status_ret = NOTMUCH_PRIVATE_STATUS_XAPIAN_EXCEPTION;
        return NULL;
     }
@@ -316,6 +318,23 @@ _notmuch_message_get_term (notmuch_message_t *message,
     return value;
 }
 
+/*
+ * For special applications where we only want the thread id, reading
+ * in all metadata is a heavy I/O penalty.
+ */
+const char *
+_notmuch_message_get_thread_id_only (notmuch_message_t *message)
+{
+
+    Xapian::TermIterator i = message->doc.termlist_begin ();
+    Xapian::TermIterator end = message->doc.termlist_end ();
+
+    message->thread_id = _notmuch_message_get_term (message, i, end,
+                                                   _find_prefix ("thread"));
+    return message->thread_id;
+}
+
+
 static void
 _notmuch_message_ensure_metadata (notmuch_message_t *message, void *field)
 {
@@ -493,7 +512,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 *
@@ -523,9 +542,9 @@ notmuch_message_get_header (notmuch_message_t *message, const char *header)
                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",
+           _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;
+           message->notmuch->exception_reported = true;
            return NULL;
        }
     }
@@ -569,6 +588,58 @@ _notmuch_message_add_reply (notmuch_message_t *message,
     _notmuch_message_list_add_message (message->replies, reply);
 }
 
+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)
 {
@@ -594,7 +665,7 @@ _notmuch_message_remove_terms (notmuch_message_t *message, const char *prefix)
 
        try {
            message->doc.remove_term ((*i));
-           message->modified = TRUE;
+           message->modified = true;
        } catch (const Xapian::InvalidArgumentError) {
            /* Ignore failure to remove non-existent term. */
        }
@@ -637,16 +708,16 @@ _notmuch_message_remove_indexed_terms (notmuch_message_t *message)
 
        try {
            message->doc.remove_term ((*i));
-           message->modified = TRUE;
+           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;
 
            if (!notmuch->exception_reported) {
-               _notmuch_database_log(_notmuch_message_database (message), "A Xapian exception occurred creating message: %s\n",
+               _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;
+               notmuch->exception_reported = true;
            }
            return NOTMUCH_PRIVATE_STATUS_XAPIAN_EXCEPTION;
        }
@@ -701,7 +772,7 @@ _notmuch_message_add_folder_terms (notmuch_message_t *message,
 
     talloc_free (folder);
 
-    message->modified = TRUE;
+    message->modified = true;
     return NOTMUCH_STATUS_SUCCESS;
 }
 
@@ -903,7 +974,7 @@ void
 _notmuch_message_clear_data (notmuch_message_t *message)
 {
     message->doc.set_data ("");
-    message->modified = TRUE;
+    message->modified = true;
 }
 
 static void
@@ -1040,9 +1111,9 @@ 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",
+       _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;
+       message->notmuch->exception_reported = true;
        return 0;
     }
 
@@ -1113,7 +1184,7 @@ _notmuch_message_set_header_values (notmuch_message_t *message,
                            Xapian::sortable_serialise (time_value));
     message->doc.add_value (NOTMUCH_VALUE_FROM, from);
     message->doc.add_value (NOTMUCH_VALUE_SUBJECT, subject);
-    message->modified = TRUE;
+    message->modified = true;
 }
 
 /* Upgrade a message to support NOTMUCH_FEATURE_LAST_MOD.  The caller
@@ -1123,7 +1194,7 @@ _notmuch_message_upgrade_last_mod (notmuch_message_t *message)
 {
     /* _notmuch_message_sync will update the last modification
      * revision; we just have to ask it to. */
-    message->modified = TRUE;
+    message->modified = true;
 }
 
 /* Synchronize changes made to message->doc out into the database. */
@@ -1152,7 +1223,7 @@ _notmuch_message_sync (notmuch_message_t *message)
 
     db = static_cast <Xapian::WritableDatabase *> (message->notmuch->xapian_db);
     db->replace_document (message->doc_id, message->doc);
-    message->modified = FALSE;
+    message->modified = false;
 }
 
 /* Delete a message document from the database, leaving a ghost
@@ -1168,7 +1239,7 @@ _notmuch_message_delete (notmuch_message_t *message)
     notmuch_database_t *notmuch;
     notmuch_query_t *query;
     unsigned int count = 0;
-    notmuch_bool_t is_ghost;
+    bool is_ghost;
 
     mid = notmuch_message_get_message_id (message);
     tid = notmuch_message_get_thread_id (message);
@@ -1297,7 +1368,7 @@ _notmuch_message_add_term (notmuch_message_t *message,
        return NOTMUCH_PRIVATE_STATUS_TERM_TOO_LONG;
 
     message->doc.add_term (term, 0);
-    message->modified = TRUE;
+    message->modified = true;
 
     talloc_free (term);
 
@@ -1366,7 +1437,7 @@ _notmuch_message_remove_term (notmuch_message_t *message,
 
     try {
        message->doc.remove_term (term);
-       message->modified = TRUE;
+       message->modified = true;
     } catch (const Xapian::InvalidArgumentError) {
        /* We'll let the philosophers try to wrestle with the
         * question of whether failing to remove that which was not
@@ -1385,10 +1456,10 @@ notmuch_private_status_t
 _notmuch_message_has_term (notmuch_message_t *message,
                           const char *prefix_name,
                           const char *value,
-                          notmuch_bool_t *result)
+                          bool *result)
 {
     char *term;
-    notmuch_bool_t out = FALSE;
+    bool out = false;
     notmuch_private_status_t status = NOTMUCH_PRIVATE_STATUS_SUCCESS;
 
     if (value == NULL)
@@ -1406,7 +1477,7 @@ _notmuch_message_has_term (notmuch_message_t *message,
        i.skip_to (term);
        if (i != message->doc.termlist_end () &&
            !strcmp ((*i).c_str (), term))
-           out = TRUE;
+           out = true;
     } catch (Xapian::Error &error) {
        status = NOTMUCH_PRIVATE_STATUS_XAPIAN_EXCEPTION;
     }
@@ -1513,17 +1584,22 @@ _filename_is_in_maildir (const char *filename)
     return NULL;
 }
 
-notmuch_status_t
-notmuch_message_maildir_flags_to_tags (notmuch_message_t *message)
+static void
+_ensure_maildir_flags (notmuch_message_t *message, bool force)
 {
     const char *flags;
-    notmuch_status_t status;
     notmuch_filenames_t *filenames;
     const char *filename, *dir;
     char *combined_flags = talloc_strdup (message, "");
-    unsigned i;
     int seen_maildir_info = 0;
 
+    if (message->maildir_flags) {
+       if (force) {
+           talloc_free (message->maildir_flags);
+           message->maildir_flags = NULL;
+       }
+    }
+
     for (filenames = notmuch_message_get_filenames (message);
         notmuch_filenames_valid (filenames);
         notmuch_filenames_move_to_next (filenames))
@@ -1549,11 +1625,28 @@ notmuch_message_maildir_flags_to_tags (notmuch_message_t *message)
            seen_maildir_info = 1;
        }
     }
+    if (seen_maildir_info)
+       message->maildir_flags = combined_flags;
+}
+
+notmuch_bool_t
+notmuch_message_has_maildir_flag (notmuch_message_t *message, char flag)
+{
+    _ensure_maildir_flags (message, false);
+    return message->maildir_flags && (strchr (message->maildir_flags, flag) != NULL);
+}
+
+notmuch_status_t
+notmuch_message_maildir_flags_to_tags (notmuch_message_t *message)
+{
+    notmuch_status_t status;
+    unsigned i;
 
+    _ensure_maildir_flags (message, true);
     /* If none of the filenames have any maildir info field (not even
      * an empty info with no flags set) then there's no information to
      * go on, so do nothing. */
-    if (! seen_maildir_info)
+    if (! message->maildir_flags)
        return NOTMUCH_STATUS_SUCCESS;
 
     status = notmuch_message_freeze (message);
@@ -1561,7 +1654,7 @@ notmuch_message_maildir_flags_to_tags (notmuch_message_t *message)
        return status;
 
     for (i = 0; i < ARRAY_SIZE(flag2tag); i++) {
-       if ((strchr (combined_flags, flag2tag[i].flag) != NULL)
+       if ((strchr (message->maildir_flags, flag2tag[i].flag) != NULL)
            ^
            flag2tag[i].inverse)
        {
@@ -1574,8 +1667,6 @@ notmuch_message_maildir_flags_to_tags (notmuch_message_t *message)
     }
     status = notmuch_message_thaw (message);
 
-    talloc_free (combined_flags);
-
     return status;
 }
 
@@ -1663,7 +1754,7 @@ _new_maildir_filename (void *ctx,
     char *filename_new, *dir;
     char flag_map[128];
     int flags_in_map = 0;
-    notmuch_bool_t flags_changed = FALSE;
+    bool flags_changed = false;
     unsigned int i;
     char *s;
 
@@ -1704,7 +1795,7 @@ _new_maildir_filename (void *ctx,
        if (flag_map[flag] == 0) {
            flag_map[flag] = 1;
            flags_in_map++;
-           flags_changed = TRUE;
+           flags_changed = true;
        }
     }
 
@@ -1713,7 +1804,7 @@ _new_maildir_filename (void *ctx,
        if (flag_map[flag]) {
            flag_map[flag] = 0;
            flags_in_map--;
-           flags_changed = TRUE;
+           flags_changed = true;
        }
     }
 
@@ -1886,7 +1977,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;
 }
@@ -1931,7 +2022,7 @@ _notmuch_message_property_map (notmuch_message_t *message)
     return message->property_map;
 }
 
-notmuch_bool_t
+bool
 _notmuch_message_frozen (notmuch_message_t *message)
 {
     return message->frozen;
@@ -1939,7 +2030,7 @@ _notmuch_message_frozen (notmuch_message_t *message)
 
 notmuch_status_t
 notmuch_message_reindex (notmuch_message_t *message,
-                        notmuch_param_t unused (*indexopts))
+                        notmuch_indexopts_t *indexopts)
 {
     notmuch_database_t *notmuch = NULL;
     notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS;
@@ -1963,7 +2054,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)
@@ -1977,6 +2068,15 @@ notmuch_message_reindex (notmuch_message_t *message,
        goto DONE;
     }
 
+    ret = notmuch_message_remove_all_properties_with_prefix (message, "index.");
+    if (ret)
+       goto DONE; /* XXX TODO: distinguish from other error returns above? */
+    if (indexopts && notmuch_indexopts_get_decrypt_policy (indexopts) == NOTMUCH_DECRYPT_FALSE) {
+       ret = notmuch_message_remove_all_properties (message, "session-key");
+       if (ret)
+           goto DONE;
+    }
+
     /* re-add the filenames with the associated indexopts */
     for (; notmuch_filenames_valid (orig_filenames);
         notmuch_filenames_move_to_next (orig_filenames)) {
@@ -2012,9 +2112,11 @@ notmuch_message_reindex (notmuch_message_t *message,
            thread_id = orig_thread_id;
 
        _notmuch_message_add_term (message, "thread", thread_id);
-       _notmuch_message_set_header_values (message, date, from, subject);
+       /* Take header values only from first filename */
+       if (found == 0)
+           _notmuch_message_set_header_values (message, date, from, subject);
 
-       ret = _notmuch_message_index_file (message, message_file);
+       ret = _notmuch_message_index_file (message, indexopts, message_file);
 
        if (ret == NOTMUCH_STATUS_FILE_ERROR)
            continue;