return "Something went wrong trying to read or write a file";
case NOTMUCH_STATUS_FILE_NOT_EMAIL:
return "File is not an email";
+ case NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID:
+ return "Message ID is identical to a message in database";
case NOTMUCH_STATUS_NULL_POINTER:
return "Erroneous NULL pointer";
case NOTMUCH_STATUS_TAG_TOO_LONG:
- return "Tag value is too long";
+ return "Tag value is too long (exceeds NOTMUCH_TAG_MAX)";
default:
case NOTMUCH_STATUS_LAST_STATUS:
return "Unknown error status value";
free (term);
}
+static notmuch_private_status_t
+find_unique_doc_id (notmuch_database_t *notmuch,
+ const char *prefix_name,
+ const char *value,
+ unsigned int *doc_id)
+{
+ Xapian::PostingIterator i, end;
+
+ find_doc_ids (notmuch, prefix_name, value, &i, &end);
+
+ if (i == end) {
+ *doc_id = 0;
+ return NOTMUCH_PRIVATE_STATUS_NO_DOCUMENT_FOUND;
+ } else {
+ *doc_id = *i;
+ return NOTMUCH_PRIVATE_STATUS_SUCCESS;
+ }
+}
+
static Xapian::Document
find_document_for_doc_id (notmuch_database_t *notmuch, unsigned doc_id)
{
return notmuch->xapian_db->get_document (doc_id);
}
+static notmuch_private_status_t
+find_unique_document (notmuch_database_t *notmuch,
+ const char *prefix_name,
+ const char *value,
+ Xapian::Document *document,
+ unsigned int *doc_id)
+{
+ notmuch_private_status_t status;
+
+ status = find_unique_doc_id (notmuch, prefix_name, value, doc_id);
+
+ if (status) {
+ *document = Xapian::Document ();
+ return status;
+ }
+
+ *document = find_document_for_doc_id (notmuch, *doc_id);
+ return NOTMUCH_PRIVATE_STATUS_SUCCESS;
+}
+
static void
insert_thread_id (GHashTable *thread_ids, Xapian::Document doc)
{
notmuch_database_find_message (notmuch_database_t *notmuch,
const char *message_id)
{
- Xapian::PostingIterator i, end;
+ notmuch_private_status_t status;
+ unsigned int doc_id;
- find_doc_ids (notmuch, "msgid", message_id, &i, &end);
+ status = find_unique_doc_id (notmuch, "msgid", message_id, &doc_id);
- if (i == end)
+ if (status == NOTMUCH_PRIVATE_STATUS_NO_DOCUMENT_FOUND)
return NULL;
- return _notmuch_message_create (notmuch, notmuch, *i);
+ return _notmuch_message_create (notmuch, notmuch, doc_id);
}
/* Return one or more thread_ids, (as a GPtrArray of strings), for the
return notmuch->path;
}
+notmuch_private_status_t
+find_timestamp_document (notmuch_database_t *notmuch, const char *db_key,
+ Xapian::Document *doc, unsigned int *doc_id)
+{
+ return find_unique_document (notmuch, "timestamp", db_key, doc, doc_id);
+}
+
+/* We allow the user to use arbitrarily long keys for timestamps,
+ * (they're for filesystem paths after all, which have no limit we
+ * know about). But we have a term-length limit. So if we exceed that,
+ * we'll use the SHA-1 of the user's key as the actual key for
+ * constructing a database term.
+ *
+ * Caution: This function returns a newly allocated string which the
+ * caller should free() when finished.
+ */
+static char *
+timestamp_db_key (const char *key)
+{
+ if (strlen (key) + 1 > NOTMUCH_TERM_MAX) {
+ return notmuch_sha1_of_string (key);
+ } else {
+ return strdup (key);
+ }
+}
+
+notmuch_status_t
+notmuch_database_set_timestamp (notmuch_database_t *notmuch,
+ const char *key, time_t timestamp)
+{
+ Xapian::Document doc;
+ unsigned int doc_id;
+ notmuch_private_status_t status;
+ notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS;
+ char *db_key = NULL;
+
+ db_key = timestamp_db_key (key);
+
+ try {
+ status = find_timestamp_document (notmuch, db_key, &doc, &doc_id);
+
+ doc.add_value (0, Xapian::sortable_serialise (timestamp));
+
+ if (status == NOTMUCH_PRIVATE_STATUS_NO_DOCUMENT_FOUND) {
+ char *term = talloc_asprintf (NULL, "%s%s",
+ _find_prefix ("timestamp"), db_key);
+ doc.add_term (term);
+ talloc_free (term);
+
+ notmuch->xapian_db->add_document (doc);
+ } else {
+ notmuch->xapian_db->replace_document (doc_id, doc);
+ }
+
+ } catch (Xapian::Error &error) {
+ fprintf (stderr, "A Xapian exception occurred: %s.\n",
+ error.get_msg().c_str());
+ ret = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
+ }
+
+ if (db_key)
+ free (db_key);
+
+ return ret;
+}
+
+time_t
+notmuch_database_get_timestamp (notmuch_database_t *notmuch, const char *key)
+{
+ Xapian::Document doc;
+ unsigned int doc_id;
+ notmuch_private_status_t status;
+ char *db_key = NULL;
+ time_t ret = 0;
+
+ db_key = timestamp_db_key (key);
+
+ try {
+ status = find_timestamp_document (notmuch, db_key, &doc, &doc_id);
+
+ if (status == NOTMUCH_PRIVATE_STATUS_NO_DOCUMENT_FOUND)
+ goto DONE;
+
+ ret = Xapian::sortable_unserialise (doc.get_value (0));
+ } catch (Xapian::Error &error) {
+ goto DONE;
+ }
+
+ DONE:
+ if (db_key)
+ free (db_key);
+
+ return ret;
+}
+
notmuch_status_t
notmuch_database_add_message (notmuch_database_t *notmuch,
const char *filename)
/* Has a message previously been added with the same ID? */
old_filename = notmuch_message_get_filename (message);
if (old_filename && strlen (old_filename)) {
- /* XXX: This is too noisy to actually print, and what do we
- * really expect the user to do? Go manually delete a
- * redundant message or merge two similar messages?
- * Instead we should handle this transparently.
- *
- * What we likely want to move to is adding both filenames
- * to the database so that subsequent indexing will pick up
- * terms from both files.
- */
-#if 0
- fprintf (stderr,
- "Note: Attempting to add a message with a duplicate message ID:\n"
- "Old: %s\n" "New: %s\n",
- old_filename, filename);
- fprintf (stderr, "The old filename will be used, but any new terms\n"
- "from the new message will added to the database.\n");
-#endif
+ ret = NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID;
+ goto DONE;
} else {
_notmuch_message_set_filename (message, filename);
_notmuch_message_add_term (message, "type", "mail");