X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=blobdiff_plain;f=lib%2Fmessage.cc;h=4a5fae1e5901f584bdddcdd52bb9563aea585d5d;hp=28f19a88502dc79ff29210297abfbed4f5ef2562;hb=717279fbcf7e057957c7c6726cd4930393cd5fdf;hpb=146549321044615d9aef2b30cedccda9c49f3f38 diff --git a/lib/message.cc b/lib/message.cc index 28f19a88..4a5fae1e 100644 --- a/lib/message.cc +++ b/lib/message.cc @@ -21,6 +21,8 @@ #include "notmuch-private.h" #include "database-private.h" +#include + #include #include @@ -31,8 +33,11 @@ struct _notmuch_message { int frozen; char *message_id; char *thread_id; + char *in_reply_to; char *filename; notmuch_message_file_t *message_file; + notmuch_message_list_t *replies; + Xapian::Document doc; }; @@ -107,9 +112,17 @@ _notmuch_message_create (const void *talloc_owner, /* Each of these will be lazily created as needed. */ message->message_id = NULL; message->thread_id = NULL; + message->in_reply_to = NULL; message->filename = NULL; message->message_file = NULL; + message->replies = _notmuch_message_list_create (message); + if (unlikely (message->replies == NULL)) { + if (status) + *status = NOTMUCH_PRIVATE_STATUS_OUT_OF_MEMORY; + return NULL; + } + /* This is C++'s creepy "placement new", which is really just an * ugly way to call a constructor for a pre-allocated object. So * it's really not an error to not be checking for OUT_OF_MEMORY @@ -133,17 +146,10 @@ _notmuch_message_create (const void *talloc_owner, } /* Create a new notmuch_message_t object for a specific message ID, - * (which may or may not already exist in the databas). + * (which may or may not already exist in the database). * - * Here, 'talloc owner' is an optional talloc context to which the new - * message will belong. This allows for the caller to not bother - * calling notmuch_message_destroy on the message, and no that all - * memory will be reclaimed with 'talloc_owner' is free. The caller - * still can call notmuch_message_destroy when finished with the - * message if desired. - * - * The 'talloc_owner' argument can also be NULL, in which case the - * caller *is* responsible for calling notmuch_message_destroy. + * The 'notmuch' database will be the talloc owner of the returned + * message. * * If there is already a document with message ID 'message_id' in the * database, then the returned message can be used to query/modify the @@ -156,21 +162,26 @@ _notmuch_message_create (const void *talloc_owner, * not be NULL.) */ notmuch_message_t * -_notmuch_message_create_for_message_id (const void *talloc_owner, - notmuch_database_t *notmuch, +_notmuch_message_create_for_message_id (notmuch_database_t *notmuch, const char *message_id, notmuch_private_status_t *status_ret) { notmuch_message_t *message; Xapian::Document doc; + Xapian::WritableDatabase *db; unsigned int doc_id; char *term; + if (notmuch->mode == NOTMUCH_DATABASE_MODE_READ_ONLY) { + *status_ret = NOTMUCH_PRIVATE_STATUS_READONLY_DATABASE; + return NULL; + } + *status_ret = NOTMUCH_PRIVATE_STATUS_SUCCESS; message = notmuch_database_find_message (notmuch, message_id); if (message) - return talloc_steal (talloc_owner, message); + return talloc_steal (notmuch, message); term = talloc_asprintf (NULL, "%s%s", _find_prefix ("id"), message_id); @@ -179,19 +190,22 @@ _notmuch_message_create_for_message_id (const void *talloc_owner, return NULL; } + db = static_cast (notmuch->xapian_db); try { doc.add_term (term); talloc_free (term); doc.add_value (NOTMUCH_VALUE_MESSAGE_ID, message_id); - doc_id = notmuch->xapian_db->add_document (doc); + doc_id = db->add_document (doc); } catch (const Xapian::Error &error) { + fprintf (stderr, "A Xapian exception occurred creating message: %s\n", + error.get_msg().c_str()); *status_ret = NOTMUCH_PRIVATE_STATUS_XAPIAN_EXCEPTION; return NULL; } - message = _notmuch_message_create (talloc_owner, notmuch, + message = _notmuch_message_create (notmuch, notmuch, doc_id, status_ret); /* We want to inform the caller that we had to create a new @@ -259,40 +273,115 @@ notmuch_message_get_header (notmuch_message_t *message, const char *header) return notmuch_message_file_get_header (message->message_file, header); } +/* Return the message ID from the In-Reply-To header of 'message'. + * + * Returns an empty string ("") if 'message' has no In-Reply-To + * header. + * + * Returns NULL if any error occurs. + */ +const char * +_notmuch_message_get_in_reply_to (notmuch_message_t *message) +{ + const char *prefix = _find_prefix ("replyto"); + int prefix_len = strlen (prefix); + Xapian::TermIterator i; + std::string in_reply_to; + + if (message->in_reply_to) + return message->in_reply_to; + + i = message->doc.termlist_begin (); + i.skip_to (prefix); + + if (i != message->doc.termlist_end ()) + in_reply_to = *i; + + /* It's perfectly valid for a message to have no In-Reply-To + * header. For these cases, we return an empty string. */ + if (i == message->doc.termlist_end () || + strncmp (in_reply_to.c_str (), prefix, prefix_len)) + { + message->in_reply_to = talloc_strdup (message, ""); + return message->in_reply_to; + } + + message->in_reply_to = talloc_strdup (message, + in_reply_to.c_str () + prefix_len); + +#if DEBUG_DATABASE_SANITY + i++; + + in_reply_to = *i; + + if (i != message->doc.termlist_end () && + strncmp ((*i).c_str (), prefix, prefix_len)) + { + INTERNAL_ERROR ("Message %s has duplicate In-Reply-To IDs: %s and %s\n" + notmuch_message_get_message_id (message), + message->in_reply_to, + (*i).c_str () + prefix_len); + } +#endif + + return message->in_reply_to; +} + const char * notmuch_message_get_thread_id (notmuch_message_t *message) { + const char *prefix = _find_prefix ("thread"); Xapian::TermIterator i; + std::string id; + + /* This code is written with the assumption that "thread" has a + * single-character prefix. */ + assert (strlen (prefix) == 1); if (message->thread_id) return message->thread_id; i = message->doc.termlist_begin (); - i.skip_to (_find_prefix ("thread")); + i.skip_to (prefix); - if (i == message->doc.termlist_end ()) + if (i != message->doc.termlist_end ()) + id = *i; + + if (i == message->doc.termlist_end () || id[0] != *prefix) INTERNAL_ERROR ("Message with document ID of %d has no thread ID.\n", message->doc_id); - message->thread_id = talloc_strdup (message, (*i).c_str () + 1); + message->thread_id = talloc_strdup (message, id.c_str () + 1); #if DEBUG_DATABASE_SANITY i++; + id = *i; - if (i != message->doc.termlist_end () && - strncmp ((*i).c_str (), _find_prefix ("thread"), - strlen (_find_prefix ("thread"))) == 0) + if (i != message->doc.termlist_end () && id[0] == *prefix) { INTERNAL_ERROR ("Message %s has duplicate thread IDs: %s and %s\n", notmuch_message_get_message_id (message), message->thread_id, - (*i).c_str () + 1); + id.c_str () + 1); } #endif return message->thread_id; } +void +_notmuch_message_add_reply (notmuch_message_t *message, + notmuch_message_node_t *reply) +{ + _notmuch_message_list_append (message->replies, reply); +} + +notmuch_messages_t * +notmuch_message_get_replies (notmuch_message_t *message) +{ + return _notmuch_messages_create (message->replies); +} + /* Set the filename for 'message' to 'filename'. * * XXX: We should still figure out if we think it's important to store @@ -387,7 +476,7 @@ notmuch_message_get_tags (notmuch_message_t *message) i.skip_to (prefix); - while (1) { + while (i != end) { tag = *i; if (tag.empty () || tag[0] != *prefix) @@ -411,7 +500,7 @@ _notmuch_message_set_date (notmuch_message_t *message, /* GMime really doesn't want to see a NULL date, so protect its * sensibilities. */ - if (date == NULL) + if (date == NULL || *date == '\0') time_value = 0; else time_value = g_mime_utils_header_decode_date (date, NULL); @@ -463,11 +552,28 @@ _notmuch_message_ensure_thread_id (notmuch_message_t *message) void _notmuch_message_sync (notmuch_message_t *message) { - Xapian::WritableDatabase *db = message->notmuch->xapian_db; + Xapian::WritableDatabase *db; + if (message->notmuch->mode == NOTMUCH_DATABASE_MODE_READ_ONLY) + return; + + db = static_cast (message->notmuch->xapian_db); db->replace_document (message->doc_id, message->doc); } +/* Ensure that 'message' is not holding any file object open. Future + * calls to various functions will still automatically open the + * message file as needed. + */ +void +_notmuch_message_close (notmuch_message_t *message) +{ + if (message->message_file) { + notmuch_message_file_close (message->message_file); + message->message_file = NULL; + } +} + /* Add a name:value term to 'message', (the actual term will be * encoded by prefixing the value with a short prefix). See * NORMAL_PREFIX and BOOLEAN_PREFIX arrays for the mapping of term