using namespace std;
+#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr[0]))
+
+typedef struct {
+ const char *name;
+ const char *prefix;
+} prefix_t;
+
+/* With these prefix values we follow the conventions published here:
+ *
+ * http://xapian.org/docs/omega/termprefixes.html
+ *
+ * as much as makes sense. Note that I took some liberty in matching
+ * the reserved prefix values to notmuch concepts, (for example, 'G'
+ * is documented as "newsGroup (or similar entity - e.g. a web forum
+ * name)", for which I think the thread is the closest analogue in
+ * notmuch. This in spite of the fact that we will eventually be
+ * storing mailing-list messages where 'G' for "mailing list name"
+ * might be even a closer analogue. I'm treating the single-character
+ * prefixes preferentially for core notmuch concepts (which will be
+ * nearly universal to all mail messages).
+ */
+
+prefix_t BOOLEAN_PREFIX_INTERNAL[] = {
+ { "type", "T" },
+ { "thread", "G" },
+ { "ref", "XREFERENCE" },
+ { "timestamp", "XTIMESTAMP" },
+};
+
+prefix_t BOOLEAN_PREFIX_EXTERNAL[] = {
+ { "tag", "K" },
+ { "id", "Q" }
+};
+
+const char *
+_find_prefix (const char *name)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE (BOOLEAN_PREFIX_INTERNAL); i++)
+ if (strcmp (name, BOOLEAN_PREFIX_INTERNAL[i].name) == 0)
+ return BOOLEAN_PREFIX_INTERNAL[i].prefix;
+
+ for (i = 0; i < ARRAY_SIZE (BOOLEAN_PREFIX_EXTERNAL); i++)
+ if (strcmp (name, BOOLEAN_PREFIX_EXTERNAL[i].name) == 0)
+ return BOOLEAN_PREFIX_EXTERNAL[i].prefix;
+
+ fprintf (stderr, "Internal error: No prefix exists for '%s'\n", name);
+ exit (1);
+
+ return "";
+}
+
const char *
notmuch_status_to_string (notmuch_status_t status)
{
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";
notmuch_private_status_t status;
unsigned int doc_id;
- status = find_unique_doc_id (notmuch, "msgid", message_id, &doc_id);
+ status = find_unique_doc_id (notmuch, "id", message_id, &doc_id);
if (status == NOTMUCH_PRIVATE_STATUS_NO_DOCUMENT_FOUND)
return NULL;
struct stat st;
int err;
char *local_path = NULL;
+ unsigned int i;
if (path == NULL)
path = local_path = notmuch_database_default_path ();
notmuch->query_parser = new Xapian::QueryParser;
notmuch->query_parser->set_default_op (Xapian::Query::OP_AND);
notmuch->query_parser->set_database (*notmuch->xapian_db);
+
+ for (i = 0; i < ARRAY_SIZE (BOOLEAN_PREFIX_EXTERNAL); i++) {
+ prefix_t *prefix = &BOOLEAN_PREFIX_EXTERNAL[i];
+ notmuch->query_parser->add_boolean_prefix (prefix->name,
+ prefix->prefix);
+ }
} catch (const Xapian::Error &error) {
fprintf (stderr, "A Xapian exception occurred: %s\n",
error.get_msg().c_str());
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)
+{
+ int term_len = strlen (_find_prefix ("timestamp")) + strlen (key);
+
+ if (term_len > 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 (NOTMUCH_VALUE_TIMESTAMP,
+ 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 (NOTMUCH_VALUE_TIMESTAMP));
+ } 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");