X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=blobdiff_plain;f=lib%2Fmessage.cc;h=12743460a4895e1acfc1849f6ac19ac2979ca698;hp=f8215a49f7dc93b482fb1d309b5ce8c68d6ba2f3;hb=4dfcc8c9b2e1dbb965f69283dca50c7581c88050;hpb=5ce8e0b11b40f733e6231d2067764e76717a341a diff --git a/lib/message.cc b/lib/message.cc index f8215a49..12743460 100644 --- a/lib/message.cc +++ b/lib/message.cc @@ -26,7 +26,7 @@ #include -struct visible _notmuch_message { +struct _notmuch_message { notmuch_database_t *notmuch; Xapian::docid doc_id; int frozen; @@ -36,6 +36,7 @@ struct visible _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 visible _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 visible _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; @@ -268,7 +270,7 @@ _notmuch_message_create_for_message_id (notmuch_database_t *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->exception_reported = TRUE; + notmuch->exception_reported = true; *status_ret = NOTMUCH_PRIVATE_STATUS_XAPIAN_EXCEPTION; return NULL; } @@ -525,7 +527,7 @@ notmuch_message_get_header (notmuch_message_t *message, const char *header) } 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()); - message->notmuch->exception_reported = TRUE; + message->notmuch->exception_reported = true; return NULL; } } @@ -579,7 +581,9 @@ void _notmuch_message_remove_terms (notmuch_message_t *message, const char *prefix) { Xapian::TermIterator i; - size_t prefix_len = strlen (prefix); + size_t prefix_len = 0; + + prefix_len = strlen (prefix); while (1) { i = message->doc.termlist_begin (); @@ -592,13 +596,66 @@ _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. */ } } } + +/* Remove all terms generated by indexing, i.e. not tags or + * properties, along with any automatic tags*/ +notmuch_private_status_t +_notmuch_message_remove_indexed_terms (notmuch_message_t *message) +{ + Xapian::TermIterator i; + + const std::string + id_prefix = _find_prefix ("id"), + property_prefix = _find_prefix ("property"), + tag_prefix = _find_prefix ("tag"), + type_prefix = _find_prefix ("type"); + + for (i = message->doc.termlist_begin (); + i != message->doc.termlist_end (); i++) { + + const std::string term = *i; + + if (term.compare (0, type_prefix.size (), type_prefix) == 0) + continue; + + if (term.compare (0, id_prefix.size (), id_prefix) == 0) + continue; + + if (term.compare (0, property_prefix.size (), property_prefix) == 0) + continue; + + 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; + + 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; + + 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; + } + } + return NOTMUCH_PRIVATE_STATUS_SUCCESS; +} + /* Return true if p points at "new" or "cur". */ static bool is_maildir (const char *p) { @@ -646,6 +703,7 @@ _notmuch_message_add_folder_terms (notmuch_message_t *message, talloc_free (folder); + message->modified = true; return NOTMUCH_STATUS_SUCCESS; } @@ -847,7 +905,7 @@ void _notmuch_message_clear_data (notmuch_message_t *message) { message->doc.set_data (""); - message->modified = TRUE; + message->modified = true; } static void @@ -946,6 +1004,14 @@ notmuch_message_get_filenames (notmuch_message_t *message) return _notmuch_filenames_create (message, message->filename_list); } +int +notmuch_message_count_files (notmuch_message_t *message) +{ + _notmuch_message_ensure_filename_list (message); + + return _notmuch_string_list_length (message->filename_list); +} + notmuch_bool_t notmuch_message_get_flag (notmuch_message_t *message, notmuch_message_flag_t flag) @@ -978,7 +1044,7 @@ notmuch_message_get_date (notmuch_message_t *message) } 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()); - message->notmuch->exception_reported = TRUE; + message->notmuch->exception_reported = true; return 0; } @@ -1037,7 +1103,7 @@ _notmuch_message_set_header_values (notmuch_message_t *message, if (date == NULL || *date == '\0') { time_value = 0; } else { - time_value = g_mime_utils_header_decode_date (date, NULL); + time_value = g_mime_utils_header_decode_date_unix (date); /* * Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=779923 */ @@ -1049,7 +1115,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 @@ -1059,7 +1125,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. */ @@ -1088,7 +1154,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 @@ -1104,7 +1170,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); @@ -1233,7 +1299,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); @@ -1302,7 +1368,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 @@ -1321,10 +1387,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) @@ -1342,7 +1408,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; } @@ -1449,17 +1515,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)) @@ -1485,11 +1556,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); @@ -1497,7 +1585,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) { @@ -1510,8 +1598,6 @@ notmuch_message_maildir_flags_to_tags (notmuch_message_t *message) } status = notmuch_message_thaw (message); - talloc_free (combined_flags); - return status; } @@ -1599,7 +1685,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; @@ -1640,7 +1726,7 @@ _new_maildir_filename (void *ctx, if (flag_map[flag] == 0) { flag_map[flag] = 1; flags_in_map++; - flags_changed = TRUE; + flags_changed = true; } } @@ -1649,7 +1735,7 @@ _new_maildir_filename (void *ctx, if (flag_map[flag]) { flag_map[flag] = 0; flags_in_map--; - flags_changed = TRUE; + flags_changed = true; } } @@ -1843,7 +1929,7 @@ _notmuch_message_ensure_property_map (notmuch_message_t *message) const char *key; char *value; - value = index(node->string, '='); + value = strchr(node->string, '='); if (!value) INTERNAL_ERROR ("malformed property term"); @@ -1867,8 +1953,118 @@ _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; } + +notmuch_status_t +notmuch_message_reindex (notmuch_message_t *message, + notmuch_indexopts_t *indexopts) +{ + notmuch_database_t *notmuch = NULL; + notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS; + notmuch_private_status_t private_status; + notmuch_filenames_t *orig_filenames = NULL; + const char *orig_thread_id = NULL; + notmuch_message_file_t *message_file = NULL; + + int found = 0; + + if (message == NULL) + return NOTMUCH_STATUS_NULL_POINTER; + + /* Save in case we need to delete message */ + orig_thread_id = notmuch_message_get_thread_id (message); + if (!orig_thread_id) { + /* XXX TODO: make up new error return? */ + INTERNAL_ERROR ("message without thread-id"); + } + + /* strdup it because the metadata may be invalidated */ + orig_thread_id = talloc_strdup (message, orig_thread_id); + + notmuch = _notmuch_message_database (message); + + ret = _notmuch_database_ensure_writable (notmuch); + if (ret) + return ret; + + orig_filenames = notmuch_message_get_filenames (message); + + private_status = _notmuch_message_remove_indexed_terms (message); + if (private_status) { + ret = COERCE_STATUS(private_status, "error removing terms"); + goto DONE; + } + + ret = notmuch_message_remove_all_properties_with_prefix (message, "index."); + if (ret) + goto DONE; /* XXX TODO: distinguish from other error returns above? */ + + /* re-add the filenames with the associated indexopts */ + for (; notmuch_filenames_valid (orig_filenames); + notmuch_filenames_move_to_next (orig_filenames)) { + + const char *date; + const char *from, *to, *subject; + char *message_id = NULL; + const char *thread_id = NULL; + + const char *filename = notmuch_filenames_get (orig_filenames); + + message_file = _notmuch_message_file_open (notmuch, filename); + if (message_file == NULL) + continue; + + ret = _notmuch_message_file_get_headers (message_file, + &from, &subject, &to, &date, + &message_id); + if (ret) + goto DONE; + + /* XXX TODO: deal with changing message id? */ + + _notmuch_message_add_filename (message, filename); + + ret = _notmuch_database_link_message_to_parents (notmuch, message, + message_file, + &thread_id); + if (ret) + goto DONE; + + if (thread_id == NULL) + thread_id = orig_thread_id; + + _notmuch_message_add_term (message, "thread", thread_id); + /* 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, indexopts, message_file); + + if (ret == NOTMUCH_STATUS_FILE_ERROR) + continue; + if (ret) + goto DONE; + + found++; + _notmuch_message_file_close (message_file); + message_file = NULL; + } + if (found == 0) { + /* put back thread id to help cleanup */ + _notmuch_message_add_term (message, "thread", orig_thread_id); + ret = _notmuch_message_delete (message); + } else { + _notmuch_message_sync (message); + } + + DONE: + if (message_file) + _notmuch_message_file_close (message_file); + + /* XXX TODO destroy orig_filenames? */ + return ret; +}