+ /* Add new boolean "folder:" and "path:" terms. */
+ _notmuch_message_add_directory_terms (message, message);
+}
+
+char *
+_notmuch_message_talloc_copy_data (notmuch_message_t *message)
+{
+ return talloc_strdup (message, message->doc.get_data ().c_str ());
+}
+
+void
+_notmuch_message_clear_data (notmuch_message_t *message)
+{
+ message->doc.set_data ("");
+ message->modified = true;
+}
+
+static void
+_notmuch_message_ensure_filename_list (notmuch_message_t *message)
+{
+ notmuch_string_node_t *node;
+
+ if (message->filename_list)
+ return;
+
+ _notmuch_message_ensure_metadata (message, message->filename_term_list);
+
+ message->filename_list = _notmuch_string_list_create (message);
+ node = message->filename_term_list->head;
+
+ if (! node) {
+ /* A message document created by an old version of notmuch
+ * (prior to rename support) will have the filename in the
+ * data of the document rather than as a file-direntry term.
+ *
+ * It would be nice to do the upgrade of the document directly
+ * here, but the database is likely open in read-only mode. */
+
+ std::string datastr = message->doc.get_data ();
+ const char *data = datastr.c_str ();
+
+ if (data == NULL)
+ INTERNAL_ERROR ("message with no filename");
+
+ _notmuch_string_list_append (message->filename_list, data);
+
+ return;
+ }
+
+ for (; node; node = node->next) {
+ void *local = talloc_new (message);
+ const char *db_path, *directory, *basename, *filename;
+ char *colon, *direntry = NULL;
+ unsigned int directory_id;
+
+ direntry = node->string;
+
+ directory_id = strtol (direntry, &colon, 10);
+
+ if (colon == NULL || *colon != ':')
+ INTERNAL_ERROR ("malformed direntry");
+
+ basename = colon + 1;
+
+ *colon = '\0';
+
+ db_path = notmuch_database_get_path (message->notmuch);
+
+ directory = _notmuch_database_get_directory_path (local,
+ message->notmuch,
+ directory_id);
+
+ if (strlen (directory))
+ filename = talloc_asprintf (message, "%s/%s/%s",
+ db_path, directory, basename);
+ else
+ filename = talloc_asprintf (message, "%s/%s",
+ db_path, basename);
+
+ _notmuch_string_list_append (message->filename_list, filename);
+
+ talloc_free (local);
+ }
+
+ talloc_free (message->filename_term_list);
+ message->filename_term_list = NULL;
+}
+
+const char *
+notmuch_message_get_filename (notmuch_message_t *message)
+{
+ try {
+ _notmuch_message_ensure_filename_list (message);
+ } catch (Xapian::Error &error) {
+ LOG_XAPIAN_EXCEPTION (message, error);
+ return NULL;
+ }
+
+ if (message->filename_list == NULL)
+ return NULL;
+
+ if (message->filename_list->head == NULL ||
+ message->filename_list->head->string == NULL) {
+ INTERNAL_ERROR ("message with no filename");
+ }
+
+ return message->filename_list->head->string;
+}
+
+notmuch_filenames_t *
+notmuch_message_get_filenames (notmuch_message_t *message)
+{
+ try {
+ _notmuch_message_ensure_filename_list (message);
+ } catch (Xapian::Error &error) {
+ LOG_XAPIAN_EXCEPTION (message, error);
+ return NULL;
+ }
+
+ return _notmuch_filenames_create (message, message->filename_list);
+}
+
+int
+notmuch_message_count_files (notmuch_message_t *message)
+{
+ try {
+ _notmuch_message_ensure_filename_list (message);
+ } catch (Xapian::Error &error) {
+ LOG_XAPIAN_EXCEPTION (message, error);
+ return -1;
+ }
+
+ return _notmuch_string_list_length (message->filename_list);
+}
+
+notmuch_status_t
+notmuch_message_get_flag_st (notmuch_message_t *message,
+ notmuch_message_flag_t flag,
+ notmuch_bool_t *is_set)
+{
+ if (! is_set)
+ return NOTMUCH_STATUS_NULL_POINTER;
+
+ try {
+ if (flag == NOTMUCH_MESSAGE_FLAG_GHOST &&
+ ! NOTMUCH_TEST_BIT (message->lazy_flags, flag))
+ _notmuch_message_ensure_metadata (message, NULL);
+ } catch (Xapian::Error &error) {
+ LOG_XAPIAN_EXCEPTION (message, error);
+ return NOTMUCH_STATUS_XAPIAN_EXCEPTION;
+ }
+
+ *is_set = NOTMUCH_TEST_BIT (message->flags, flag);
+ return NOTMUCH_STATUS_SUCCESS;
+}
+
+notmuch_bool_t
+notmuch_message_get_flag (notmuch_message_t *message,
+ notmuch_message_flag_t flag)
+{
+ notmuch_bool_t is_set;
+ notmuch_status_t status;
+
+ status = notmuch_message_get_flag_st (message, flag, &is_set);
+
+ if (status)
+ return FALSE;
+ else
+ return is_set;
+}
+
+void
+notmuch_message_set_flag (notmuch_message_t *message,
+ notmuch_message_flag_t flag, notmuch_bool_t enable)
+{
+ if (enable)
+ NOTMUCH_SET_BIT (&message->flags, flag);
+ else
+ NOTMUCH_CLEAR_BIT (&message->flags, flag);
+ NOTMUCH_SET_BIT (&message->lazy_flags, flag);
+}
+
+time_t
+notmuch_message_get_date (notmuch_message_t *message)
+{
+ std::string value;
+
+ try {
+ value = message->doc.get_value (NOTMUCH_VALUE_TIMESTAMP);
+ } catch (Xapian::Error &error) {
+ LOG_XAPIAN_EXCEPTION (message, error);
+ return 0;
+ }
+
+ if (value.empty ())
+ /* sortable_unserialise is undefined on empty string */
+ return 0;
+ return Xapian::sortable_unserialise (value);
+}
+
+notmuch_tags_t *
+notmuch_message_get_tags (notmuch_message_t *message)
+{
+ notmuch_tags_t *tags;
+
+ try {
+ _notmuch_message_ensure_metadata (message, message->tag_list);
+ } catch (Xapian::Error &error) {
+ LOG_XAPIAN_EXCEPTION (message, error);
+ return NULL;
+ }
+
+ tags = _notmuch_tags_create (message, message->tag_list);
+ /* _notmuch_tags_create steals the reference to the tag_list, but
+ * in this case it's still used by the message, so we add an
+ * *additional* talloc reference to the list. As a result, it's
+ * possible to modify the message tags (which talloc_unlink's the
+ * current list from the message) while still iterating because
+ * the iterator will keep the current list alive. */
+ if (! talloc_reference (message, message->tag_list))
+ return NULL;
+
+ return tags;
+}
+
+const char *
+_notmuch_message_get_author (notmuch_message_t *message)
+{
+ return message->author;
+}
+
+void
+_notmuch_message_set_author (notmuch_message_t *message,
+ const char *author)
+{
+ if (message->author)
+ talloc_free (message->author);
+ message->author = talloc_strdup (message, author);
+ return;
+}
+
+void
+_notmuch_message_set_header_values (notmuch_message_t *message,
+ const char *date,
+ const char *from,
+ const char *subject)
+{
+ time_t time_value;
+
+ /* GMime really doesn't want to see a NULL date, so protect its
+ * sensibilities. */
+ if (date == NULL || *date == '\0') {
+ time_value = 0;
+ } else {
+ time_value = g_mime_utils_header_decode_date_unix (date);
+ /*
+ * Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=779923
+ */
+ if (time_value < 0)
+ time_value = 0;
+ }
+
+ message->doc.add_value (NOTMUCH_VALUE_TIMESTAMP,
+ Xapian::sortable_serialise (time_value));
+ message->doc.add_value (NOTMUCH_VALUE_FROM, from);
+ message->doc.add_value (NOTMUCH_VALUE_SUBJECT, subject);
+ message->modified = true;
+}
+
+void
+_notmuch_message_update_subject (notmuch_message_t *message,
+ const char *subject)
+{
+ message->doc.add_value (NOTMUCH_VALUE_SUBJECT, subject);
+ message->modified = true;
+}
+
+/* Upgrade a message to support NOTMUCH_FEATURE_LAST_MOD. The caller
+ * must call _notmuch_message_sync. */
+void
+_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;