aboutsummaryrefslogtreecommitdiff
path: root/lib/message.cc
diff options
context:
space:
mode:
authorDaniel Kahn Gillmor <dkg@fifthhorseman.net>2018-02-04 15:33:34 -0500
committerDaniel Kahn Gillmor <dkg@fifthhorseman.net>2018-02-04 15:33:34 -0500
commitd9be1028d47cb7e98b474df420858a690798810b (patch)
treefb37f83ca098129a5301ef141dc6a5007a0972a9 /lib/message.cc
parenta8fb877ad7e960d69ec10887ff79e24bb99c587c (diff)
parent3c4e64d976eb561ac5157df1bbe5882e3e65b583 (diff)
Merge tag 'debian/0.26-1' into debian/stretch-backports
notmuch Debian 0.26-1 upload (same as 0.26)
Diffstat (limited to 'lib/message.cc')
-rw-r--r--lib/message.cc269
1 files changed, 235 insertions, 34 deletions
diff --git a/lib/message.cc b/lib/message.cc
index f78e5a9d..d5db89b6 100644
--- a/lib/message.cc
+++ b/lib/message.cc
@@ -36,6 +36,7 @@ struct _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 _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 _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,11 +596,64 @@ _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". */
@@ -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;
}
@@ -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 <Xapian::WritableDatabase *> (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;
}
}
@@ -1867,8 +1953,123 @@ _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? */
+ 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)) {
+
+ 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;
+}