X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=blobdiff_plain;f=lib%2Fmessage.cc;h=a7a13cc26aa181ec94f336048385403b903d4d37;hp=e6a5a5af9651d207b01ca755e6adbba700fc0a85;hb=bc9c50602d20ce712188ea2cc3c5d30647d4d3f5;hpb=df8885f62c13f77b2b16cbb211e3a727945870b0 diff --git a/lib/message.cc b/lib/message.cc index e6a5a5af..a7a13cc2 100644 --- a/lib/message.cc +++ b/lib/message.cc @@ -39,6 +39,9 @@ struct visible _notmuch_message { notmuch_message_file_t *message_file; notmuch_message_list_t *replies; unsigned long flags; + /* For flags that are initialized on-demand, lazy_flags indicates + * if each flag has been initialized. */ + unsigned long lazy_flags; Xapian::Document doc; Xapian::termcount termpos; @@ -99,6 +102,7 @@ _notmuch_message_create_for_document (const void *talloc_owner, message->frozen = 0; message->flags = 0; + message->lazy_flags = 0; /* Each of these will be lazily created as needed. */ message->message_id = NULL; @@ -192,15 +196,17 @@ _notmuch_message_create (const void *talloc_owner, * * There is already a document with message ID 'message_id' in the * database. The returned message can be used to query/modify the - * document. + * document. The message may be a ghost message. + * * NOTMUCH_PRIVATE_STATUS_NO_DOCUMENT_FOUND: * * No document with 'message_id' exists in the database. The * returned message contains a newly created document (not yet * added to the database) and a document ID that is known not to - * exist in the database. The caller can modify the message, and a - * call to _notmuch_message_sync will add * the document to the - * database. + * exist in the database. This message is "blank"; that is, it + * contains only a message ID and no other metadata. The caller + * can modify the message, and a call to _notmuch_message_sync + * will add the document to the database. * * If an error occurs, this function will return NULL and *status * will be set as appropriate. (The status pointer argument must @@ -224,6 +230,10 @@ _notmuch_message_create_for_message_id (notmuch_database_t *notmuch, else if (*status_ret) return NULL; + /* If the message ID is too long, substitute its sha1 instead. */ + if (strlen (message_id) > NOTMUCH_MESSAGE_ID_MAX) + message_id = _notmuch_message_id_compressed (message, message_id); + term = talloc_asprintf (NULL, "%s%s", _find_prefix ("id"), message_id); if (term == NULL) { @@ -299,6 +309,7 @@ _notmuch_message_ensure_metadata (notmuch_message_t *message) 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"), *replyto_prefix = _find_prefix ("replyto"); @@ -331,10 +342,25 @@ _notmuch_message_ensure_metadata (notmuch_message_t *message) message->message_id = _notmuch_message_get_term (message, i, end, id_prefix); + /* Get document type */ + assert (strcmp (id_prefix, type_prefix) < 0); + if (! NOTMUCH_TEST_BIT (message->lazy_flags, NOTMUCH_MESSAGE_FLAG_GHOST)) { + i.skip_to (type_prefix); + /* "T" is the prefix "type" fields. See + * BOOLEAN_PREFIX_INTERNAL. */ + if (*i == "Tmail") + NOTMUCH_CLEAR_BIT (&message->flags, NOTMUCH_MESSAGE_FLAG_GHOST); + else if (*i == "Tghost") + NOTMUCH_SET_BIT (&message->flags, NOTMUCH_MESSAGE_FLAG_GHOST); + else + INTERNAL_ERROR ("Message without type term"); + NOTMUCH_SET_BIT (&message->lazy_flags, NOTMUCH_MESSAGE_FLAG_GHOST); + } + /* Get filename list. Here we get only the terms. We lazily * expand them to full file names when needed in * _notmuch_message_ensure_filename_list. */ - assert (strcmp (id_prefix, filename_prefix) < 0); + assert (strcmp (type_prefix, filename_prefix) < 0); if (!message->filename_term_list && !message->filename_list) message->filename_term_list = _notmuch_database_get_terms_with_prefix (message, i, end, @@ -365,6 +391,11 @@ _notmuch_message_invalidate_metadata (notmuch_message_t *message, message->tag_list = NULL; } + if (strcmp ("type", prefix_name) == 0) { + NOTMUCH_CLEAR_BIT (&message->flags, NOTMUCH_MESSAGE_FLAG_GHOST); + NOTMUCH_CLEAR_BIT (&message->lazy_flags, NOTMUCH_MESSAGE_FLAG_GHOST); + } + if (strcmp ("file-direntry", prefix_name) == 0) { talloc_free (message->filename_term_list); talloc_free (message->filename_list); @@ -412,26 +443,35 @@ _notmuch_message_ensure_message_file (notmuch_message_t *message) const char * notmuch_message_get_header (notmuch_message_t *message, const char *header) { - try { - std::string value; - - /* Fetch header from the appropriate xapian value field if - * available */ - if (strcasecmp (header, "from") == 0) - value = message->doc.get_value (NOTMUCH_VALUE_FROM); - else if (strcasecmp (header, "subject") == 0) - value = message->doc.get_value (NOTMUCH_VALUE_SUBJECT); - else if (strcasecmp (header, "message-id") == 0) - value = message->doc.get_value (NOTMUCH_VALUE_MESSAGE_ID); - - if (!value.empty()) + Xapian::valueno slot = Xapian::BAD_VALUENO; + + /* Fetch header from the appropriate xapian value field if + * available */ + if (strcasecmp (header, "from") == 0) + slot = NOTMUCH_VALUE_FROM; + else if (strcasecmp (header, "subject") == 0) + slot = NOTMUCH_VALUE_SUBJECT; + else if (strcasecmp (header, "message-id") == 0) + slot = NOTMUCH_VALUE_MESSAGE_ID; + + if (slot != Xapian::BAD_VALUENO) { + try { + std::string value = message->doc.get_value (slot); + + /* If we have NOTMUCH_FEATURE_FROM_SUBJECT_ID_VALUES, then + * empty values indicate empty headers. If we don't, then + * it could just mean we didn't record the header. */ + if ((message->notmuch->features & + NOTMUCH_FEATURE_FROM_SUBJECT_ID_VALUES) || + ! value.empty()) return talloc_strdup (message, value.c_str ()); - } catch (Xapian::Error &error) { - fprintf (stderr, "A Xapian exception occurred when reading header: %s\n", - error.get_msg().c_str()); - message->notmuch->exception_reported = TRUE; - return NULL; + } catch (Xapian::Error &error) { + fprintf (stderr, "A Xapian exception occurred when reading header: %s\n", + error.get_msg().c_str()); + message->notmuch->exception_reported = TRUE; + return NULL; + } } /* Otherwise fall back to parsing the file */ @@ -644,6 +684,10 @@ _notmuch_message_add_filename (notmuch_message_t *message, if (filename == NULL) INTERNAL_ERROR ("Message filename cannot be NULL."); + if (! (message->notmuch->features & NOTMUCH_FEATURE_FILE_TERMS) || + ! (message->notmuch->features & NOTMUCH_FEATURE_BOOL_FOLDER)) + return NOTMUCH_STATUS_UPGRADE_REQUIRED; + relative = _notmuch_database_relative_path (message->notmuch, filename); status = _notmuch_database_split_path (local, relative, &directory, NULL); @@ -688,6 +732,10 @@ _notmuch_message_remove_filename (notmuch_message_t *message, notmuch_private_status_t private_status; notmuch_status_t status; + if (! (message->notmuch->features & NOTMUCH_FEATURE_FILE_TERMS) || + ! (message->notmuch->features & NOTMUCH_FEATURE_BOOL_FOLDER)) + return NOTMUCH_STATUS_UPGRADE_REQUIRED; + status = _notmuch_database_filename_to_direntry ( local, message->notmuch, filename, NOTMUCH_FIND_LOOKUP, &direntry); if (status || !direntry) @@ -846,7 +894,11 @@ notmuch_bool_t notmuch_message_get_flag (notmuch_message_t *message, notmuch_message_flag_t flag) { - return message->flags & (1 << flag); + if (flag == NOTMUCH_MESSAGE_FLAG_GHOST && + ! NOTMUCH_TEST_BIT (message->lazy_flags, flag)) + _notmuch_message_ensure_metadata (message); + + return NOTMUCH_TEST_BIT (message->flags, flag); } void @@ -854,9 +906,10 @@ notmuch_message_set_flag (notmuch_message_t *message, notmuch_message_flag_t flag, notmuch_bool_t enable) { if (enable) - message->flags |= (1 << flag); + NOTMUCH_SET_BIT (&message->flags, flag); else - message->flags &= ~(1 << flag); + NOTMUCH_CLEAR_BIT (&message->flags, flag); + NOTMUCH_SET_BIT (&message->lazy_flags, flag); } time_t @@ -873,6 +926,9 @@ notmuch_message_get_date (notmuch_message_t *message) return 0; } + if (value.empty ()) + /* sortable_unserialise is undefined on empty string */ + return 0; return Xapian::sortable_unserialise (value); } @@ -963,6 +1019,24 @@ _notmuch_message_delete (notmuch_message_t *message) return NOTMUCH_STATUS_SUCCESS; } +/* Transform a blank message into a ghost message. The caller must + * _notmuch_message_sync the message. */ +notmuch_private_status_t +_notmuch_message_initialize_ghost (notmuch_message_t *message, + const char *thread_id) +{ + notmuch_private_status_t status; + + status = _notmuch_message_add_term (message, "type", "ghost"); + if (status) + return status; + status = _notmuch_message_add_term (message, "thread", thread_id); + if (status) + return status; + + return NOTMUCH_PRIVATE_STATUS_SUCCESS; +} + /* Ensure that 'message' is not holding any file object open. Future * calls to various functions will still automatically open the * message file as needed. @@ -1032,6 +1106,8 @@ _notmuch_message_gen_terms (notmuch_message_t *message, /* 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; + + _notmuch_message_invalidate_metadata (message, prefix_name); } term_gen->set_termpos (message->termpos); @@ -1476,7 +1552,7 @@ notmuch_message_tags_to_maildir_flags (notmuch_message_t *message) talloc_free (to_set); talloc_free (to_clear); - return NOTMUCH_STATUS_SUCCESS; + return status; } notmuch_status_t