X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=blobdiff_plain;f=lib%2Fmessage.cc;h=6f2f634512453770a358b374ff1e3a3073888d0e;hp=75f36bdda55fc193e71c91741c42c6d74fa47508;hb=71eaa19350883b30bafe885ea083c2e3f1621e10;hpb=8a8fb39b0c0199f224f4477cbcab2d7a578fee0f diff --git a/lib/message.cc b/lib/message.cc index 75f36bdd..6f2f6345 100644 --- a/lib/message.cc +++ b/lib/message.cc @@ -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 @@ -48,7 +50,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; @@ -62,16 +64,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 @@ -117,6 +119,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 +134,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,9 +274,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; } @@ -318,6 +324,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) { @@ -332,6 +355,7 @@ _notmuch_message_ensure_metadata (notmuch_message_t *message, void *field) *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 @@ -396,6 +420,14 @@ _notmuch_message_ensure_metadata (notmuch_message_t *message, void *field) _notmuch_database_get_terms_with_prefix (message, i, end, 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) @@ -495,7 +527,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 * @@ -525,9 +557,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; } } @@ -571,6 +603,84 @@ _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) { @@ -596,7 +706,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. */ } @@ -639,16 +749,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; } @@ -703,7 +813,7 @@ _notmuch_message_add_folder_terms (notmuch_message_t *message, talloc_free (folder); - message->modified = TRUE; + message->modified = true; return NOTMUCH_STATUS_SUCCESS; } @@ -905,7 +1015,7 @@ void _notmuch_message_clear_data (notmuch_message_t *message) { message->doc.set_data (""); - message->modified = TRUE; + message->modified = true; } static void @@ -1042,9 +1152,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; } @@ -1115,7 +1225,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 @@ -1125,7 +1235,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. */ @@ -1154,7 +1264,7 @@ _notmuch_message_sync (notmuch_message_t *message) db = static_cast (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 @@ -1170,7 +1280,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); @@ -1299,7 +1409,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); @@ -1368,7 +1478,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 @@ -1387,10 +1497,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) @@ -1408,7 +1518,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; } @@ -1516,7 +1626,7 @@ _filename_is_in_maildir (const char *filename) } static void -_ensure_maildir_flags (notmuch_message_t *message, notmuch_bool_t force) +_ensure_maildir_flags (notmuch_message_t *message, bool force) { const char *flags; notmuch_filenames_t *filenames; @@ -1560,13 +1670,20 @@ _ensure_maildir_flags (notmuch_message_t *message, notmuch_bool_t force) 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); + _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. */ @@ -1678,7 +1795,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; @@ -1719,7 +1836,7 @@ _new_maildir_filename (void *ctx, if (flag_map[flag] == 0) { flag_map[flag] = 1; flags_in_map++; - flags_changed = TRUE; + flags_changed = true; } } @@ -1728,7 +1845,7 @@ _new_maildir_filename (void *ctx, if (flag_map[flag]) { flag_map[flag] = 0; flags_in_map--; - flags_changed = TRUE; + flags_changed = true; } } @@ -1901,7 +2018,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,7 +2063,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; @@ -1954,7 +2071,7 @@ _notmuch_message_frozen (notmuch_message_t *message) notmuch_status_t notmuch_message_reindex (notmuch_message_t *message, - notmuch_indexopts_t unused (*indexopts)) + notmuch_indexopts_t *indexopts) { notmuch_database_t *notmuch = NULL; notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS; @@ -1978,7 +2095,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) @@ -1992,6 +2109,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)) { @@ -2027,9 +2153,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;