[PATCH 3/8] lib: basic message-property API
David Bremner
david at tethera.net
Sun Jun 12 18:05:50 PDT 2016
Initially, support get, set and removal of single key/value pair, as
well as removing all properties.
---
lib/Makefile.local | 1 +
lib/message-private.h | 16 +++++++
lib/message-property.cc | 101 ++++++++++++++++++++++++++++++++++++++++++
lib/message.cc | 52 +++++++++++++++++++++-
lib/notmuch.h | 69 +++++++++++++++++++++++++++++
test/T610-message-property.sh | 84 +++++++++++++++++++++++++++++++++++
6 files changed, 321 insertions(+), 2 deletions(-)
create mode 100644 lib/message-private.h
create mode 100644 lib/message-property.cc
create mode 100755 test/T610-message-property.sh
diff --git a/lib/Makefile.local b/lib/Makefile.local
index 9280880..c012ed1 100644
--- a/lib/Makefile.local
+++ b/lib/Makefile.local
@@ -49,6 +49,7 @@ libnotmuch_cxx_srcs = \
$(dir)/directory.cc \
$(dir)/index.cc \
$(dir)/message.cc \
+ $(dir)/message-property.cc \
$(dir)/query.cc \
$(dir)/query-fp.cc \
$(dir)/config.cc \
diff --git a/lib/message-private.h b/lib/message-private.h
new file mode 100644
index 0000000..7419925
--- /dev/null
+++ b/lib/message-private.h
@@ -0,0 +1,16 @@
+#ifndef MESSAGE_PRIVATE_H
+#define MESSAGE_PRIVATE_H
+
+notmuch_string_map_t *
+_notmuch_message_property_map (notmuch_message_t *message);
+
+notmuch_bool_t
+_notmuch_message_frozen (notmuch_message_t *message);
+
+void
+_notmuch_message_remove_terms (notmuch_message_t *message, const char *prefix);
+
+void
+_notmuch_message_invalidate_metadata (notmuch_message_t *message, const char *prefix_name);
+
+#endif
diff --git a/lib/message-property.cc b/lib/message-property.cc
new file mode 100644
index 0000000..ad2250f
--- /dev/null
+++ b/lib/message-property.cc
@@ -0,0 +1,101 @@
+/* message-property.cc - Properties are like tags, but (key,value) pairs.
+ * keys are allowed to repeat.
+ *
+ * This file is part of notmuch.
+ *
+ * Copyright © 2016 David Bremner
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/ .
+ *
+ * Author: David Bremner <david at tethera.net>
+ */
+
+#include "notmuch-private.h"
+#include "database-private.h"
+#include "message-private.h"
+
+notmuch_status_t
+notmuch_message_get_property (notmuch_message_t *message, const char *key, const char **value)
+{
+ if (! value)
+ return NOTMUCH_STATUS_NULL_POINTER;
+
+ *value = _notmuch_string_map_get (_notmuch_message_property_map (message), key);
+
+ return NOTMUCH_STATUS_SUCCESS;
+}
+
+static notmuch_status_t
+_notmuch_message_modify_property (notmuch_message_t *message, const char *key, const char *value,
+ notmuch_bool_t delete_it)
+{
+ notmuch_private_status_t private_status;
+ notmuch_status_t status;
+ char *term = NULL;
+
+ status = _notmuch_database_ensure_writable (_notmuch_message_database (message));
+ if (status)
+ return status;
+
+ if (key == NULL || value == NULL)
+ return NOTMUCH_STATUS_NULL_POINTER;
+
+ if (index (key, '='))
+ return NOTMUCH_STATUS_ILLEGAL_ARGUMENT;
+
+ term = talloc_asprintf (message, "%s=%s", key, value);
+
+ if (delete_it)
+ private_status = _notmuch_message_remove_term (message, "property", term);
+ else
+ private_status = _notmuch_message_add_term (message, "property", term);
+
+ if (private_status)
+ return COERCE_STATUS (private_status,
+ "Unhandled error modifying message property");
+ if (! _notmuch_message_frozen (message))
+ _notmuch_message_sync (message);
+
+ if (term)
+ talloc_free (term);
+
+ return NOTMUCH_STATUS_SUCCESS;
+}
+
+notmuch_status_t
+notmuch_message_add_property (notmuch_message_t *message, const char *key, const char *value)
+{
+ return _notmuch_message_modify_property (message, key, value, FALSE);
+}
+
+notmuch_status_t
+notmuch_message_remove_property (notmuch_message_t *message, const char *key, const char *value)
+{
+ return _notmuch_message_modify_property (message, key, value, TRUE);
+}
+
+notmuch_status_t
+notmuch_message_remove_all_properties (notmuch_message_t *message)
+{
+ notmuch_status_t status;
+ status = _notmuch_database_ensure_writable (_notmuch_message_database (message));
+ if (status)
+ return status;
+
+ _notmuch_message_invalidate_metadata (message, "property");
+ /* XXX better error reporting ? */
+ _notmuch_message_remove_terms (message, _find_prefix ("property"));
+
+ return NOTMUCH_STATUS_SUCCESS;
+}
diff --git a/lib/message.cc b/lib/message.cc
index 63a8da5..9d3e807 100644
--- a/lib/message.cc
+++ b/lib/message.cc
@@ -20,6 +20,7 @@
#include "notmuch-private.h"
#include "database-private.h"
+#include "message-private.h"
#include <stdint.h>
@@ -395,7 +396,7 @@ _notmuch_message_ensure_metadata (notmuch_message_t *message)
message->in_reply_to = talloc_strdup (message, "");
}
-static void
+void
_notmuch_message_invalidate_metadata (notmuch_message_t *message,
const char *prefix_name)
{
@@ -552,7 +553,7 @@ notmuch_message_get_replies (notmuch_message_t *message)
return _notmuch_messages_create (message->replies);
}
-static void
+void
_notmuch_message_remove_terms (notmuch_message_t *message, const char *prefix)
{
Xapian::TermIterator i;
@@ -1799,3 +1800,50 @@ _notmuch_message_database (notmuch_message_t *message)
{
return message->notmuch;
}
+
+void
+_notmuch_message_ensure_property_map (notmuch_message_t *message)
+{
+ notmuch_string_node_t *node;
+
+ if (message->property_map)
+ return;
+
+ if (!message->property_term_list)
+ _notmuch_message_ensure_metadata (message);
+
+ message->property_map = _notmuch_string_map_create (message);
+
+ for (node = message->property_term_list->head; node; node = node->next) {
+ const char *key;
+ char *value;
+
+ value = index(node->string, '=');
+ if (!value)
+ INTERNAL_ERROR ("malformed property term");
+
+ *value = '\0';
+ value++;
+ key = node->string;
+
+ _notmuch_string_map_append (message->property_map, key, value);
+
+ }
+
+ talloc_free (message->property_term_list);
+ message->property_term_list = NULL;
+}
+
+notmuch_string_map_t *
+_notmuch_message_property_map (notmuch_message_t *message)
+{
+ _notmuch_message_ensure_property_map (message);
+
+ return message->property_map;
+}
+
+notmuch_bool_t
+_notmuch_message_frozen (notmuch_message_t *message)
+{
+ return message->frozen;
+}
diff --git a/lib/notmuch.h b/lib/notmuch.h
index d4a97cb..afc48c6 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -180,6 +180,11 @@ typedef enum _notmuch_status {
*/
NOTMUCH_STATUS_PATH_ERROR,
/**
+ * One of the arguments violates the preconditions for the
+ * function, in a way not covered by a more specific argument.
+ */
+ NOTMUCH_STATUS_ILLEGAL_ARGUMENT,
+ /**
* Not an actual status value. Just a way to find out how many
* valid status values there are.
*/
@@ -1652,6 +1657,70 @@ void
notmuch_message_destroy (notmuch_message_t *message);
/**
+ * @name Message Properties
+ *
+ * This interface provides the ability to attach arbitrary (key,value)
+ * string pairs to a message, to remove such pairs, and to iterate
+ * over them. The caller should take some care as to what keys they
+ * add or delete values for, as other subsystems or extensions may
+ * depend on these properties.
+ *
+ */
+/**@{*/
+/**
+ * Retrieve the value for a single property key
+ *
+ * *value* is set to a string owned by the message or NULL if there is
+ * no such key. In the case of multiple values for the given key, the
+ * first one is retrieved.
+ *
+ * @returns
+ * - NOTMUCH_STATUS_NULL_POINTER: *value* may not be NULL.
+ * - NOTMUCH_STATUS_SUCCESS: No error occured.
+
+ */
+notmuch_status_t
+notmuch_message_get_property (notmuch_message_t *message, const char *key, const char **value);
+
+/**
+ * Add a (key,value) pair to a message
+ *
+ * @returns
+ * - NOTMUCH_STATUS_ILLEGAL_ARGUMENT: *key* may not contain an '=' character.
+ * - NOTMUCH_STATUS_NULL_POINTER: Neither *key* nor *value* may be NULL.
+ * - NOTMUCH_STATUS_SUCCESS: No error occured.
+ */
+notmuch_status_t
+notmuch_message_add_property (notmuch_message_t *message, const char *key, const char *value);
+
+/**
+ * Remove a (key,value) pair from a message.
+ *
+ * It is not an error to remove a non-existant (key,value) pair
+ *
+ * @returns
+ * - NOTMUCH_STATUS_ILLEGAL_ARGUMENT: *key* may not contain an '=' character.
+ * - NOTMUCH_STATUS_NULL_POINTER: Neither *key* nor *value* may be NULL.
+ * - NOTMUCH_STATUS_SUCCESS: No error occured.
+ */
+notmuch_status_t
+notmuch_message_remove_property (notmuch_message_t *message, const char *key, const char *value);
+
+/**
+ * Remove all (key,value) pairs from the given message.
+ *
+ * @returns
+ * - NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in
+ * read-only mode so message cannot be modified.
+ * - NOTMUCH_STATUS_SUCCESS: No error occured.
+ *
+ */
+notmuch_status_t
+notmuch_message_remove_all_properties (notmuch_message_t *message);
+
+/**@}*/
+
+/**
* Is the given 'tags' iterator pointing at a valid tag.
*
* When this function returns TRUE, notmuch_tags_get will return a
diff --git a/test/T610-message-property.sh b/test/T610-message-property.sh
new file mode 100755
index 0000000..0217950
--- /dev/null
+++ b/test/T610-message-property.sh
@@ -0,0 +1,84 @@
+#!/usr/bin/env bash
+test_description="message property API"
+
+. ./test-lib.sh || exit 1
+
+add_email_corpus
+
+cat <<EOF > c_head
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <notmuch-test.h>
+
+int main (int argc, char** argv)
+{
+ notmuch_database_t *db;
+ notmuch_message_t *message = NULL;
+ const char *val;
+ notmuch_status_t stat;
+
+ EXPECT0(notmuch_database_open (argv[1], NOTMUCH_DATABASE_MODE_READ_WRITE, &db));
+ EXPECT0(notmuch_database_find_message(db, "4EFC743A.3060609 at april.org", &message));
+ if (message == NULL) {
+ fprintf (stderr, "unable to find message");
+ exit (1);
+ }
+EOF
+
+cat <<EOF > c_tail
+ EXPECT0(notmuch_database_destroy(db));
+}
+EOF
+
+test_begin_subtest "notmuch_message_{add,get,remove}_property"
+cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
+{
+ EXPECT0(notmuch_message_add_property (message, "testkey1", "testvalue1"));
+ EXPECT0(notmuch_message_get_property (message, "testkey1", &val));
+ printf("testkey1[1] = %s\n", val);
+ EXPECT0(notmuch_message_add_property (message, "testkey2", "this value has spaces and = sign"));
+ EXPECT0(notmuch_message_get_property (message, "testkey1", &val));
+ printf("testkey1[2] = %s\n", val);
+ EXPECT0(notmuch_message_get_property (message, "testkey1", &val));
+
+ EXPECT0(notmuch_message_get_property (message, "testkey2", &val));
+ printf("testkey2 = %s\n", val);
+
+ /* Add second value for key */
+ EXPECT0(notmuch_message_add_property (message, "testkey2", "zztestvalue3"));
+ EXPECT0(notmuch_message_get_property (message, "testkey2", &val));
+ printf("testkey2 = %s\n", val);
+
+ /* remove first value for key */
+ EXPECT0(notmuch_message_remove_property (message, "testkey2", "this value has spaces and = sign"));
+ EXPECT0(notmuch_message_get_property (message, "testkey2", &val));
+ printf("testkey2 = %s\n", val);
+
+ /* remove non-existant value for key */
+ EXPECT0(notmuch_message_remove_property (message, "testkey2", "this value has spaces and = sign"));
+ EXPECT0(notmuch_message_get_property (message, "testkey2", &val));
+ printf("testkey2 = %s\n", val);
+
+ /* remove only value for key */
+ EXPECT0(notmuch_message_remove_property (message, "testkey2", "zztestvalue3"));
+ EXPECT0(notmuch_message_get_property (message, "testkey2", &val));
+ printf("testkey2 = %s\n", val == NULL ? "NULL" : val);
+}
+EOF
+cat <<'EOF' >EXPECTED
+== stdout ==
+testkey1[1] = testvalue1
+testkey1[2] = testvalue1
+testkey2 = this value has spaces and = sign
+testkey2 = this value has spaces and = sign
+testkey2 = zztestvalue3
+testkey2 = zztestvalue3
+testkey2 = NULL
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+
+
+test_done
--
2.8.1
More information about the notmuch
mailing list