[PATCH 07/12] util/string-map: add _notmuch_string_map_set

2018-06-22 Thread David Bremner
In contrast to the existing _append, this is intended for interleaved
read and write operations.
---
 test/T710-string-map.sh | 30 ++
 util/string-map.c   | 19 +++
 util/string-map.h   |  5 +
 3 files changed, 54 insertions(+)

diff --git a/test/T710-string-map.sh b/test/T710-string-map.sh
index 8fd69a53..6f07f363 100755
--- a/test/T710-string-map.sh
+++ b/test/T710-string-map.sh
@@ -97,6 +97,36 @@ val[2]=testval2
 EOF
 test_expect_equal_file EXPECTED OUTPUT
 
+test_begin_subtest "set (replace)"
+cat c_head - c_tail <<'EOF' | test_C $
+{
+notmuch_string_map_t *map = _notmuch_string_map_create(ctx);
+_notmuch_string_map_append (map, "testkey1", "testval1");
+_notmuch_string_map_append (map, "testkey2", "testval2");
+_notmuch_string_map_append (map, "testkey1", "testval3");
+dump_map (map);
+_notmuch_string_map_set (map, "testkey1", "newval");
+dump_map (map);
+}
+EOF
+cat< EXPECTED
+== stdout ==
+key[0]=testkey1
+val[0]=testval1
+key[1]=testkey1
+val[1]=testval3
+key[2]=testkey2
+val[2]=testval2
+key[0]=testkey1
+val[0]=newval
+key[1]=testkey1
+val[1]=testval3
+key[2]=testkey2
+val[2]=testval2
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
 test_begin_subtest "get first"
 cat c_head - c_tail <<'EOF' | test_C $
 {
diff --git a/util/string-map.c b/util/string-map.c
index b29a9ba0..41a6881d 100644
--- a/util/string-map.c
+++ b/util/string-map.c
@@ -143,6 +143,25 @@ bsearch_first (notmuch_string_pair_t *array, size_t len, 
const char *key, bool e
 
 }
 
+void
+_notmuch_string_map_set (notmuch_string_map_t *map,
+const char *key,
+const char *value)
+{
+
+notmuch_string_pair_t *pair;
+
+/* this means that calling append invalidates iterators */
+_notmuch_string_map_sort (map);
+
+pair = bsearch_first (map->pairs, map->length, key, true);
+if (! pair)
+   _notmuch_string_map_append (map, key, value);
+
+pair->value = talloc_strdup (map, value);
+
+}
+
 const char *
 _notmuch_string_map_get (notmuch_string_map_t *map, const char *key)
 {
diff --git a/util/string-map.h b/util/string-map.h
index 22aa487c..ff648b5c 100644
--- a/util/string-map.h
+++ b/util/string-map.h
@@ -12,6 +12,11 @@ _notmuch_string_map_append (notmuch_string_map_t *map,
const char *key,
const char *value);
 
+void
+_notmuch_string_map_set (notmuch_string_map_t *map,
+const char *key,
+const char *value);
+
 const char *
 _notmuch_string_map_get (notmuch_string_map_t *map, const char *key);
 
-- 
2.17.1

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 12/12] lib/message: add notmuch_message_set_header

2018-06-22 Thread David Bremner
This saves a header value to the message document data area, where it
can be retrieved later by notmuch_message_get_header.
---
 lib/message.cc| 24 +++
 lib/notmuch.h |  6 +++-
 test/T720-message-data.sh | 64 +++
 util/string-map.c |  4 +--
 4 files changed, 95 insertions(+), 3 deletions(-)
 create mode 100755 test/T720-message-data.sh

diff --git a/lib/message.cc b/lib/message.cc
index c3c71fd4..cfd51b2e 100644
--- a/lib/message.cc
+++ b/lib/message.cc
@@ -597,6 +597,30 @@ notmuch_message_get_header (notmuch_message_t *message, 
const char *header)
 return _notmuch_message_file_get_header (message->message_file, header);
 }
 
+notmuch_status_t
+notmuch_message_set_header (notmuch_message_t *message,
+   const char *header,
+   const char *value)
+{
+/* We don't want to accept changes which will be silently lost on
+ * sync */
+notmuch_status_t status = _notmuch_database_ensure_writable 
(message->notmuch);
+if (status)
+   return status;
+
+const char *key = talloc_asprintf (message->data_map, "header.%s", header);
+
+_notmuch_message_ensure_data_map (message);
+
+_notmuch_string_map_set (message->data_map, key, value);
+message->modified = true;
+
+if (! message->frozen)
+   _notmuch_message_sync (message);
+
+return NOTMUCH_STATUS_SUCCESS;
+}
+
 /* Return the message ID from the In-Reply-To header of 'message'.
  *
  * Returns an empty string ("") if 'message' has no In-Reply-To
diff --git a/lib/notmuch.h b/lib/notmuch.h
index 247f6ad7..ca809016 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -58,7 +58,7 @@ NOTMUCH_BEGIN_DECLS
  * version in Makefile.local.
  */
 #define LIBNOTMUCH_MAJOR_VERSION   5
-#define LIBNOTMUCH_MINOR_VERSION   2
+#define LIBNOTMUCH_MINOR_VERSION   3
 #define LIBNOTMUCH_MICRO_VERSION   0
 
 
@@ -1516,6 +1516,10 @@ notmuch_message_get_date  (notmuch_message_t *message);
 const char *
 notmuch_message_get_header (notmuch_message_t *message, const char *header);
 
+notmuch_status_t
+notmuch_message_set_header (notmuch_message_t *message,
+   const char *header,
+   const  char *value );
 /**
  * Get the tags for 'message', returning a notmuch_tags_t object which
  * can be used to iterate over all tags.
diff --git a/test/T720-message-data.sh b/test/T720-message-data.sh
new file mode 100755
index ..3b74d0b8
--- /dev/null
+++ b/test/T720-message-data.sh
@@ -0,0 +1,64 @@
+#!/usr/bin/env bash
+test_description="message data API"
+
+. $(dirname "$0")/test-lib.sh || exit 1
+
+add_email_corpus
+
+cat < c_head
+#include 
+#include 
+#include 
+#include 
+
+int main (int argc, char** argv)
+{
+   notmuch_database_t *db;
+   char *val;
+   notmuch_status_t stat;
+
+   EXPECT0(notmuch_database_open (argv[1], NOTMUCH_DATABASE_MODE_READ_WRITE, 
));
+
+EOF
+
+cat < c_tail
+   EXPECT0(notmuch_database_destroy(db));
+}
+EOF
+
+add_message '[subject]=initial' '[id]=subject-test-id'
+
+test_begin_subtest "find initial subject"
+notmuch search id:subject-test-id | notmuch_search_sanitize > OUTPUT
+cat EXPECTED
+== stdout ==
+subject = modified
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "modified subject persists"
+notmuch search id:subject-test-id | notmuch_search_sanitize > OUTPUT
+cat pairs, map->length, key, true);
 if (! pair)
_notmuch_string_map_append (map, key, value);
-
-pair->value = talloc_strdup (map, value);
+else
+   pair->value = talloc_strdup (map, value);
 
 }
 
-- 
2.17.1

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 03/12] test: add initial tests for string-map

2018-06-22 Thread David Bremner
These test every non-destroy function, albeit lightly.
---
 test/T710-string-map.sh | 117 
 1 file changed, 117 insertions(+)
 create mode 100755 test/T710-string-map.sh

diff --git a/test/T710-string-map.sh b/test/T710-string-map.sh
new file mode 100755
index ..b2f65381
--- /dev/null
+++ b/test/T710-string-map.sh
@@ -0,0 +1,117 @@
+#!/usr/bin/env bash
+test_description='string-map unit tests'
+. $(dirname "$0")/test-lib.sh || exit 1
+
+cat <<'EOF' > c_head
+#include 
+#include 
+#include 
+#include "string-map.h"
+static void
+dump_map(notmuch_string_map_t *map)
+{
+int count=0;
+for (notmuch_string_map_iterator_t *i=_notmuch_string_map_iterator_create 
(map, "", false);
+ _notmuch_string_map_iterator_valid (i);
+ _notmuch_string_map_iterator_move_to_next (i), count++) {
+printf("key[%d]=%s\nval[%d]=%s\n", count, 
_notmuch_string_map_iterator_key(i),
+ count, 
_notmuch_string_map_iterator_value(i));
+}
+}
+int main (int argc, char** argv)
+{
+void *ctx = talloc_new (NULL);
+EOF
+
+cat < c_tail
+}
+EOF
+
+test_begin_subtest "empty map"
+cat c_head - c_tail <<'EOF' | test_C $
+{
+notmuch_string_map_t *map = _notmuch_string_map_create(ctx);
+dump_map (map);
+}
+EOF
+cat< EXPECTED
+== stdout ==
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "single pair"
+cat c_head - c_tail <<'EOF' | test_C $
+{
+notmuch_string_map_t *map = _notmuch_string_map_create(ctx);
+_notmuch_string_map_append (map, "testkey1", "testval1");
+dump_map (map);
+}
+EOF
+cat< EXPECTED
+== stdout ==
+key[0]=testkey1
+val[0]=testval1
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "two pairs"
+cat c_head - c_tail <<'EOF' | test_C $
+{
+notmuch_string_map_t *map = _notmuch_string_map_create(ctx);
+_notmuch_string_map_append (map, "testkey1", "testval1");
+_notmuch_string_map_append (map, "testkey2", "testval2");
+dump_map (map);
+}
+EOF
+cat< EXPECTED
+== stdout ==
+key[0]=testkey1
+val[0]=testval1
+key[1]=testkey2
+val[1]=testval2
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "duplicate key, sorting"
+cat c_head - c_tail <<'EOF' | test_C $
+{
+notmuch_string_map_t *map = _notmuch_string_map_create(ctx);
+_notmuch_string_map_append (map, "testkey1", "testval1");
+_notmuch_string_map_append (map, "testkey2", "testval2");
+_notmuch_string_map_append (map, "testkey1", "testval3");
+dump_map (map);
+}
+EOF
+cat< EXPECTED
+== stdout ==
+key[0]=testkey1
+val[0]=testval1
+key[1]=testkey1
+val[1]=testval3
+key[2]=testkey2
+val[2]=testval2
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "get first"
+cat c_head - c_tail <<'EOF' | test_C $
+{
+notmuch_string_map_t *map = _notmuch_string_map_create(ctx);
+_notmuch_string_map_append (map, "testkey1", "testval1");
+_notmuch_string_map_append (map, "testkey2", "testval2");
+_notmuch_string_map_append (map, "testkey1", "testval3");
+printf ("%s\n",_notmuch_string_map_get (map, "testkey1"));
+}
+EOF
+cat< EXPECTED
+== stdout ==
+testval1
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_done
-- 
2.17.1

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 09/12] lib: add data_map field to message structs

2018-06-22 Thread David Bremner
This string-map stores the unserialized key-value map from the message
document data area. It is lazily read on the first read, and lazily
written by _notmuch_message_sync. Note that other than naming this is
independent from the other metadata, which is stored in document
terms (i.e. things you can search for).
---
 lib/message.cc | 53 +-
 1 file changed, 52 insertions(+), 1 deletion(-)

diff --git a/lib/message.cc b/lib/message.cc
index 153e4bed..bf597bc5 100644
--- a/lib/message.cc
+++ b/lib/message.cc
@@ -41,6 +41,7 @@ struct _notmuch_message {
 notmuch_message_file_t *message_file;
 notmuch_string_list_t *property_term_list;
 notmuch_string_map_t *property_map;
+notmuch_string_map_t *data_map;
 notmuch_message_list_t *replies;
 unsigned long flags;
 /* For flags that are initialized on-demand, lazy_flags indicates
@@ -129,6 +130,7 @@ _notmuch_message_create_for_document (const void 
*talloc_owner,
 message->author = NULL;
 message->property_term_list = NULL;
 message->property_map = NULL;
+message->data_map = NULL;
 
 message->replies = _notmuch_message_list_create (message);
 if (unlikely (message->replies == NULL)) {
@@ -334,7 +336,6 @@ _notmuch_message_get_thread_id_only (notmuch_message_t 
*message)
 return message->thread_id;
 }
 
-
 static void
 _notmuch_message_ensure_metadata (notmuch_message_t *message, void *field)
 {
@@ -483,6 +484,19 @@ _notmuch_message_invalidate_metadata (notmuch_message_t 
*message,
 }
 }
 
+static void
+_notmuch_message_ensure_data_map (notmuch_message_t *message)
+{
+if (message->data_map)
+   return;
+
+char *blob = talloc_strdup(message, message->doc.get_data().c_str ());
+
+message->data_map = _notmuch_string_map_deserialize (message, blob);
+
+talloc_free (blob);
+}
+
 unsigned int
 _notmuch_message_get_doc_id (notmuch_message_t *message)
 {
@@ -1169,6 +1183,13 @@ _notmuch_message_sync (notmuch_message_t *message)
_notmuch_database_new_revision (
message->notmuch)));
 
+if (message->notmuch->features & NOTMUCH_FEATURE_MESSAGE_DATA
+   && message->data_map) {
+   const char *blob = _notmuch_string_map_serialize (message,
+ message->data_map);
+   message->doc.set_data (blob);
+}
+
 db = static_cast  
(message->notmuch->xapian_db);
 db->replace_document (message->doc_id, message->doc);
 message->modified = false;
@@ -1930,6 +1951,36 @@ notmuch_message_get_database (const notmuch_message_t 
*message)
 return message->notmuch;
 }
 
+notmuch_status_t
+_notmuch_message_data_get (notmuch_message_t *message, const char *key, const 
char **value)
+{
+if (! message || !key || !value)
+   return NOTMUCH_STATUS_NULL_POINTER;
+
+_notmuch_message_ensure_data_map (message);
+
+*value = _notmuch_string_map_get (message->data_map, key);
+
+return NOTMUCH_STATUS_SUCCESS;
+}
+
+notmuch_status_t
+_notmuch_message_data_set (notmuch_message_t *message, const char *key, const 
char *value)
+{
+if (! message || !key || !value)
+   return NOTMUCH_STATUS_NULL_POINTER;
+
+notmuch_status_t status = _notmuch_database_ensure_writable 
(message->notmuch);
+if (status)
+   return status;
+
+_notmuch_message_ensure_data_map (message);
+
+_notmuch_string_map_set (message->data_map, key, value);
+
+return NOTMUCH_STATUS_SUCCESS;
+}
+
 static void
 _notmuch_message_ensure_property_map (notmuch_message_t *message)
 {
-- 
2.17.1

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 01/12] test: link test_C snippets to libnotmuch_util.a

2018-06-22 Thread David Bremner
This will allow testing (some) private APIs.  I couldn't understand
the trailing / on -L${NOTMUCH_BUILDDIR}/lib/, but it seems harmless so
I left it.
---
 test/test-lib.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test/test-lib.sh b/test/test-lib.sh
index fca5277d..e15cde45 100644
--- a/test/test-lib.sh
+++ b/test/test-lib.sh
@@ -1015,7 +1015,7 @@ test_C () {
 exec_file="test${test_count}"
 test_file="${exec_file}.c"
 cat > ${test_file}
-${TEST_CC} ${TEST_CFLAGS} -I${NOTMUCH_SRCDIR}/test -I${NOTMUCH_SRCDIR}/lib 
-o ${exec_file} ${test_file} -L${NOTMUCH_BUILDDIR}/lib/ -lnotmuch -ltalloc
+${TEST_CC} ${TEST_CFLAGS} -I${NOTMUCH_SRCDIR}/test -I${NOTMUCH_SRCDIR}/lib 
-I${NOTMUCH_SRCDIR}/util -o ${exec_file} ${test_file} 
-L${NOTMUCH_BUILDDIR}/lib/ -L${NOTMUCH_BUILDDIR}/util/ -lnotmuch -lnotmuch_util 
-ltalloc
 echo "== stdout ==" > OUTPUT.stdout
 echo "== stderr ==" > OUTPUT.stderr
 ./${exec_file} "$@" 1>>OUTPUT.stdout 2>>OUTPUT.stderr
-- 
2.17.1

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 05/12] util/string-map: add _notmuch_string_map_deserialize

2018-06-22 Thread David Bremner
The anticipated use case is loading the document data area from a
message Xapian document into a usable data structure.
---
 util/string-map.c | 49 +++
 util/string-map.h |  8 
 2 files changed, 57 insertions(+)

diff --git a/util/string-map.c b/util/string-map.c
index ab0c42ab..b29a9ba0 100644
--- a/util/string-map.c
+++ b/util/string-map.c
@@ -268,3 +268,52 @@ _notmuch_string_map_serialize (void* ctx, 
notmuch_string_map_t *map)
 
 return ret;
 }
+
+static char *
+unescape_newlines (void *ctx, const char *in, size_t len) {
+size_t i,j;
+/* removing escapes only makes things shorter */
+char *out = talloc_zero_size (ctx, len+1);
+for (i=0, j=0; ihttps://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 08/12] lib/database: add NOTMUCH_FEATURE_MESSAGE_DATA

2018-06-22 Thread David Bremner
This feature is intended to mark the database as supporting a
key-value store in the document data area. The actual key-value store
is implemented in a subsequent commit.
---
 lib/database-private.h |  8 +++-
 lib/database.cc| 12 
 2 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/lib/database-private.h b/lib/database-private.h
index a499b259..3a15fd16 100644
--- a/lib/database-private.h
+++ b/lib/database-private.h
@@ -108,6 +108,12 @@ enum _notmuch_features {
  *
  * Introduced: version 3. */
 NOTMUCH_FEATURE_LAST_MOD = 1 << 6,
+
+/* If set, messages store a serialized string-map in their data
+ * area
+ *
+ * Introduced: version 3. */
+NOTMUCH_FEATURE_MESSAGE_DATA = 1 << 7,
 };
 
 /* In C++, a named enum is its own type, so define bitwise operators
@@ -233,7 +239,7 @@ struct _notmuch_database {
 #define NOTMUCH_FEATURES_CURRENT \
 (NOTMUCH_FEATURE_FILE_TERMS | NOTMUCH_FEATURE_DIRECTORY_DOCS | \
  NOTMUCH_FEATURE_BOOL_FOLDER | NOTMUCH_FEATURE_GHOSTS | \
- NOTMUCH_FEATURE_LAST_MOD)
+ NOTMUCH_FEATURE_LAST_MOD | NOTMUCH_FEATURE_MESSAGE_DATA )
 
 /* Return the list of terms from the given iterator matching a prefix.
  * The prefix will be stripped from the strings in the returned list.
diff --git a/lib/database.cc b/lib/database.cc
index 9cf8062c..47e903d5 100644
--- a/lib/database.cc
+++ b/lib/database.cc
@@ -383,6 +383,8 @@ static const struct {
   "indexed MIME types", "w"},
 { NOTMUCH_FEATURE_LAST_MOD,
   "modification tracking", "w"},
+{ NOTMUCH_FEATURE_MESSAGE_DATA,
+  "per message data", "rw"},
 };
 
 const char *
@@ -1342,6 +1344,16 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,
 if (! notmuch_database_needs_upgrade (notmuch))
return NOTMUCH_STATUS_SUCCESS;
 
+/*
+ * the upgrade for NOTMUCH_FEATURE_MESSAGE_DATA is actually done
+ * by NOTMUCH_FEATURE_FILE_TERMS
+ */
+if ((new_features & NOTMUCH_FEATURE_MESSAGE_DATA) &&
+   !(target_features & NOTMUCH_FEATURE_FILE_TERMS)) {
+   _notmuch_database_log (notmuch, "inconsistent feature set");
+   return NOTMUCH_STATUS_UNSUPPORTED_OPERATION;
+}
+
 if (progress_notify) {
/* Set up our handler for SIGALRM */
memset (, 0, sizeof (struct sigaction));
-- 
2.17.1

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 06/12] util/string-map: add round trip test

2018-06-22 Thread David Bremner
In particular this tests that we are not escaping more than necessary.
---
 test/T710-string-map.sh | 58 +
 1 file changed, 58 insertions(+)

diff --git a/test/T710-string-map.sh b/test/T710-string-map.sh
index 9c5c1d8e..8fd69a53 100755
--- a/test/T710-string-map.sh
+++ b/test/T710-string-map.sh
@@ -426,4 +426,62 @@ val[3]=testval4
 EOF
 test_expect_equal_file EXPECTED OUTPUT
 
+test_begin_subtest "Round trip from string"
+cat c_head - c_tail <<'EOF' | test_C $
+{
+const char * str =
+ "testkey1\n"
+ "testval1\\b\n"
+ "testkey1\n"
+ "testval3\\\n"
+ "testkey1\\nkey continues\n"
+ "testval3\\nvalue continues\n"
+ "testkey2\n"
+ "testval2\n"
+ "testkey2not\n"
+ "testval2\n"
+ "testkey2\\toto\n"
+ "testval4\n";
+
+
+notmuch_string_map_t *map = _notmuch_string_map_deserialize (ctx, str);
+const char * str2 = _notmuch_string_map_serialize (ctx, map);
+fputs (str, stdout);
+fputs ("--\n", stdout);
+fputs (str2, stdout);
+printf ("%d\n", strcmp (str, str2));
+}
+EOF
+cat<<'EOF' > EXPECTED
+== stdout ==
+testkey1
+testval1\b
+testkey1
+testval3\
+testkey1\nkey continues
+testval3\nvalue continues
+testkey2
+testval2
+testkey2\\not
+testval2
+testkey2\toto
+testval4
+--
+testkey1
+testval1\b
+testkey1
+testval3\
+testkey1\nkey continues
+testval3\nvalue continues
+testkey2
+testval2
+testkey2\\not
+testval2
+testkey2\toto
+testval4
+0
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
 test_done
-- 
2.17.1

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 02/12] lib: move string-map functions to libnotmuch_util

2018-06-22 Thread David Bremner
Although they are not yet needed for the CLI, this will facilitate
writing unit tests.
---
 lib/Makefile.local |  1 -
 lib/notmuch-private.h  | 33 +
 util/Makefile.local|  3 ++-
 {lib => util}/string-map.c |  0
 util/string-map.h  | 36 
 5 files changed, 39 insertions(+), 34 deletions(-)
 rename {lib => util}/string-map.c (100%)
 create mode 100644 util/string-map.h

diff --git a/lib/Makefile.local b/lib/Makefile.local
index 5dc057c0..a9a310b4 100644
--- a/lib/Makefile.local
+++ b/lib/Makefile.local
@@ -42,7 +42,6 @@ libnotmuch_c_srcs =   \
$(dir)/messages.c   \
$(dir)/sha1.c   \
$(dir)/built-with.c \
-   $(dir)/string-map.c \
$(dir)/indexopts.c  \
$(dir)/tags.c
 
diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h
index 3764a6a9..063df5cd 100644
--- a/lib/notmuch-private.h
+++ b/lib/notmuch-private.h
@@ -580,38 +580,7 @@ _notmuch_string_list_append (notmuch_string_list_t *list,
 void
 _notmuch_string_list_sort (notmuch_string_list_t *list);
 
-/* string-map.c */
-typedef struct _notmuch_string_map  notmuch_string_map_t;
-typedef struct _notmuch_string_map_iterator notmuch_string_map_iterator_t;
-notmuch_string_map_t *
-_notmuch_string_map_create (const void *ctx);
-
-void
-_notmuch_string_map_append (notmuch_string_map_t *map,
-   const char *key,
-   const char *value);
-
-const char *
-_notmuch_string_map_get (notmuch_string_map_t *map, const char *key);
-
-notmuch_string_map_iterator_t *
-_notmuch_string_map_iterator_create (notmuch_string_map_t *map, const char 
*key,
-bool exact);
-
-bool
-_notmuch_string_map_iterator_valid (notmuch_string_map_iterator_t *iter);
-
-void
-_notmuch_string_map_iterator_move_to_next (notmuch_string_map_iterator_t 
*iter);
-
-const char *
-_notmuch_string_map_iterator_key (notmuch_string_map_iterator_t *iterator);
-
-const char *
-_notmuch_string_map_iterator_value (notmuch_string_map_iterator_t *iterator);
-
-void
-_notmuch_string_map_iterator_destroy (notmuch_string_map_iterator_t *iterator);
+#include "string-map.h"
 
 /* tags.c */
 
diff --git a/util/Makefile.local b/util/Makefile.local
index ba03230e..71a66158 100644
--- a/util/Makefile.local
+++ b/util/Makefile.local
@@ -5,7 +5,8 @@ extra_cflags += -I$(srcdir)/$(dir)
 
 libnotmuch_util_c_srcs := $(dir)/xutil.c $(dir)/error_util.c 
$(dir)/hex-escape.c \
  $(dir)/string-util.c $(dir)/talloc-extra.c 
$(dir)/zlib-extra.c \
-   $(dir)/util.c $(dir)/gmime-extra.c $(dir)/crypto.c
+   $(dir)/util.c $(dir)/gmime-extra.c $(dir)/crypto.c \
+   $(dir)/string-map.c
 
 libnotmuch_util_modules := $(libnotmuch_util_c_srcs:.c=.o)
 
diff --git a/lib/string-map.c b/util/string-map.c
similarity index 100%
rename from lib/string-map.c
rename to util/string-map.c
diff --git a/util/string-map.h b/util/string-map.h
new file mode 100644
index ..42d16da4
--- /dev/null
+++ b/util/string-map.h
@@ -0,0 +1,36 @@
+#ifndef STRING_MAP_H
+#define STRING_MAP_H
+
+#include 
+typedef struct _notmuch_string_map  notmuch_string_map_t;
+typedef struct _notmuch_string_map_iterator notmuch_string_map_iterator_t;
+notmuch_string_map_t *
+_notmuch_string_map_create (const void *ctx);
+
+void
+_notmuch_string_map_append (notmuch_string_map_t *map,
+   const char *key,
+   const char *value);
+
+const char *
+_notmuch_string_map_get (notmuch_string_map_t *map, const char *key);
+
+notmuch_string_map_iterator_t *
+_notmuch_string_map_iterator_create (notmuch_string_map_t *map, const char 
*key,
+bool exact);
+
+bool
+_notmuch_string_map_iterator_valid (notmuch_string_map_iterator_t *iter);
+
+void
+_notmuch_string_map_iterator_move_to_next (notmuch_string_map_iterator_t 
*iter);
+
+const char *
+_notmuch_string_map_iterator_key (notmuch_string_map_iterator_t *iterator);
+
+const char *
+_notmuch_string_map_iterator_value (notmuch_string_map_iterator_t *iterator);
+
+void
+_notmuch_string_map_iterator_destroy (notmuch_string_map_iterator_t *iterator);
+#endif
-- 
2.17.1

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 11/12] lib/message: check message data_map for header

2018-06-22 Thread David Bremner
The will allow the indexing process to override the values in the
files. This is potentially useful for encrypted headers, or in
resolving duplicate files for the same message(-id).
---
 lib/message.cc | 21 -
 1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/lib/message.cc b/lib/message.cc
index f59f0a5c..c3c71fd4 100644
--- a/lib/message.cc
+++ b/lib/message.cc
@@ -552,11 +552,30 @@ _notmuch_message_get_header_from_xapian 
(notmuch_message_t *message,
 return NULL;
 }
 
+static const char *
+_notmuch_message_get_header_from_data (notmuch_message_t *message, const char 
*header) {
+_notmuch_message_ensure_data_map (message);
+
+if (! (message->notmuch->features & NOTMUCH_FEATURE_MESSAGE_DATA))
+   return NULL;
+
+const char *key = talloc_asprintf (message->data_map, "header.%s", header);
+
+if (! key)
+   return NULL;
+
+return _notmuch_string_map_get (message->data_map, key);
+}
+
 const char *
 notmuch_message_get_header (notmuch_message_t *message, const char *header)
 {
-Xapian::valueno slot = Xapian::BAD_VALUENO;
+/* Have we explicitly chosen / set this header */
+const char *value = _notmuch_message_get_header_from_data (message, 
header);
+if (value)
+   return value;
 
+Xapian::valueno slot = Xapian::BAD_VALUENO;
 /* Fetch header from the appropriate xapian value field if
  * available */
 if (strcasecmp (header, "from") == 0)
-- 
2.17.1

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 10/12] lib: factor out xapian access from notmuch_message_get_header

2018-06-22 Thread David Bremner
In a later commit, we will introduce another layer of data structure
for headers. To keep the resulting function size down, factor out the
database access.
---
 lib/message.cc | 41 -
 1 file changed, 24 insertions(+), 17 deletions(-)

diff --git a/lib/message.cc b/lib/message.cc
index bf597bc5..f59f0a5c 100644
--- a/lib/message.cc
+++ b/lib/message.cc
@@ -529,6 +529,29 @@ _notmuch_message_ensure_message_file (notmuch_message_t 
*message)
notmuch_message_get_database (message), message, filename);
 }
 
+static const char *
+_notmuch_message_get_header_from_xapian (notmuch_message_t *message,
+Xapian::valueno slot)
+{
+try {
+   std::string value = message->doc.get_value (slot);
+
+   /* If we have NOTMUCH_FEATURE_FROM_SUBJECT_ID_VALUES, then
+* empty values indicate empty headers.  If we don't, then
+* it could just mean we didn't record the header. */
+   if ((message->notmuch->features &
+NOTMUCH_FEATURE_FROM_SUBJECT_ID_VALUES) ||
+   ! value.empty())
+   return talloc_strdup (message, value.c_str ());
+
+} catch (Xapian::Error ) {
+   _notmuch_database_log(notmuch_message_get_database (message), "A Xapian 
exception occurred when reading header: %s\n",
+ error.get_msg().c_str());
+   message->notmuch->exception_reported = true;
+}
+return NULL;
+}
+
 const char *
 notmuch_message_get_header (notmuch_message_t *message, const char *header)
 {
@@ -544,23 +567,7 @@ notmuch_message_get_header (notmuch_message_t *message, 
const char *header)
slot = NOTMUCH_VALUE_MESSAGE_ID;
 
 if (slot != Xapian::BAD_VALUENO) {
-   try {
-   std::string value = message->doc.get_value (slot);
-
-   /* If we have NOTMUCH_FEATURE_FROM_SUBJECT_ID_VALUES, then
-* empty values indicate empty headers.  If we don't, then
-* it could just mean we didn't record the header. */
-   if ((message->notmuch->features &
-NOTMUCH_FEATURE_FROM_SUBJECT_ID_VALUES) ||
-   ! value.empty())
-   return talloc_strdup (message, value.c_str ());
-
-   } catch (Xapian::Error ) {
-   _notmuch_database_log(notmuch_message_get_database (message), "A 
Xapian exception occurred when reading header: %s\n",
-error.get_msg().c_str());
-   message->notmuch->exception_reported = true;
-   return NULL;
-   }
+   return _notmuch_message_get_header_from_xapian (message, slot);
 }
 
 /* Otherwise fall back to parsing the file */
-- 
2.17.1

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


WIP: store message headers in document data area

2018-06-22 Thread David Bremner
Every Xapian document (thing in the database) has a data area that
stores an arbitrary string. That string is not usable for searching
(unlike terms or values), but can be used e.g. for display. Currently
we don't use this part of the Xapian document at all.  While I was
working adding all of the subjects of multi-file messages for regex
search, it seemed like it might be useful to fix the default subject
displayed to the user independent of how search terms/values are
added. I also have the feeling this might be useful for encrypted
headers. Before I suggested that I wanted to get a clearer idea of how
hard that would be to do.

There's really not that much new code here, it's mainly tests, and
some code movement. Unlike message properties, these are not currently
backed up. I guess that could be done, although I'd like to have a
clearer idea of the use cases before I do that.

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 04/12] util/string-map: add _notmuch_string_map_serialize

2018-06-22 Thread David Bremner
The anticipated usage of this is to serialize a string map into the
data area of a xapian document
---
 test/T710-string-map.sh | 312 
 util/string-map.c   |  42 ++
 util/string-map.h   |   7 +
 3 files changed, 361 insertions(+)

diff --git a/test/T710-string-map.sh b/test/T710-string-map.sh
index b2f65381..9c5c1d8e 100755
--- a/test/T710-string-map.sh
+++ b/test/T710-string-map.sh
@@ -114,4 +114,316 @@ testval1
 EOF
 test_expect_equal_file EXPECTED OUTPUT
 
+test_begin_subtest "serialize empty"
+cat c_head - c_tail <<'EOF' | test_C $
+{
+notmuch_string_map_t *map = _notmuch_string_map_create(ctx);
+dump_map (map);
+printf ("%s",_notmuch_string_map_serialize (ctx, map));
+}
+EOF
+cat< EXPECTED
+== stdout ==
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "serialize"
+cat c_head - c_tail <<'EOF' | test_C $
+{
+notmuch_string_map_t *map = _notmuch_string_map_create(ctx);
+_notmuch_string_map_append (map, "testkey1", "testval1");
+_notmuch_string_map_append (map, "testkey2", "testval2");
+_notmuch_string_map_append (map, "testkey1", "testval3");
+dump_map (map);
+printf ("%s",_notmuch_string_map_serialize (ctx, map));
+}
+EOF
+cat< EXPECTED
+== stdout ==
+key[0]=testkey1
+val[0]=testval1
+key[1]=testkey1
+val[1]=testval3
+key[2]=testkey2
+val[2]=testval2
+testkey1
+testval1
+testkey1
+testval3
+testkey2
+testval2
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "serialize, key with embedded newline"
+cat c_head - c_tail <<'EOF' | test_C $
+{
+notmuch_string_map_t *map = _notmuch_string_map_create(ctx);
+_notmuch_string_map_append (map, "testkey1", "testval1");
+_notmuch_string_map_append (map, "testkey2", "testval2");
+_notmuch_string_map_append (map, "testkey1", "testval3");
+_notmuch_string_map_append (map, "testkey2\nreallynot", "testval4");
+dump_map (map);
+printf ("%s",_notmuch_string_map_serialize (ctx, map));
+}
+EOF
+cat< EXPECTED
+== stdout ==
+key[0]=testkey1
+val[0]=testval1
+key[1]=testkey1
+val[1]=testval3
+key[2]=testkey2
+val[2]=testval2
+key[3]=testkey2
+reallynot
+val[3]=testval4
+testkey1
+testval1
+testkey1
+testval3
+testkey2
+testval2
+testkey2\nreallynot
+testval4
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "serialize, key with embedded backslash"
+cat c_head - c_tail <<'EOF' | test_C $
+{
+notmuch_string_map_t *map = _notmuch_string_map_create(ctx);
+_notmuch_string_map_append (map, "testkey1", "testval1");
+_notmuch_string_map_append (map, "testkey2", "testval2");
+_notmuch_string_map_append (map, "testkey1", "testval3");
+_notmuch_string_map_append (map, "testkey2\\not", "testval4");
+dump_map (map);
+printf ("%s",_notmuch_string_map_serialize (ctx, map));
+}
+EOF
+cat<<'EOF' > EXPECTED
+== stdout ==
+key[0]=testkey1
+val[0]=testval1
+key[1]=testkey1
+val[1]=testval3
+key[2]=testkey2
+val[2]=testval2
+key[3]=testkey2\not
+val[3]=testval4
+testkey1
+testval1
+testkey1
+testval3
+testkey2
+testval2
+testkey2\\not
+testval4
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "serialize, value with embedded newline"
+cat c_head - c_tail <<'EOF' | test_C $
+{
+notmuch_string_map_t *map = _notmuch_string_map_create(ctx);
+_notmuch_string_map_append (map, "testkey1", "testval1");
+_notmuch_string_map_append (map, "testkey2", "testval2");
+_notmuch_string_map_append (map, "testkey1", "testval3");
+_notmuch_string_map_append (map, "testkey2", "testval4\nvalue continues");
+dump_map (map);
+printf ("%s",_notmuch_string_map_serialize (ctx, map));
+}
+EOF
+cat< EXPECTED
+== stdout ==
+key[0]=testkey1
+val[0]=testval1
+key[1]=testkey1
+val[1]=testval3
+key[2]=testkey2
+val[2]=testval2
+key[3]=testkey2
+val[3]=testval4
+value continues
+testkey1
+testval1
+testkey1
+testval3
+testkey2
+testval2
+testkey2
+testval4\nvalue continues
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "serialize, key and value with embedded newline"
+cat c_head - c_tail <<'EOF' | test_C $
+{
+notmuch_string_map_t *map = _notmuch_string_map_create(ctx);
+_notmuch_string_map_append (map, "testkey1", "testval1");
+_notmuch_string_map_append (map, "testkey2", "testval2");
+_notmuch_string_map_append (map, "testkey1\nkey continues", 
"testval3\nvalue continues");
+dump_map (map);
+printf ("%s",_notmuch_string_map_serialize (ctx, map));
+}
+EOF
+cat< EXPECTED
+== stdout ==
+key[0]=testkey1
+val[0]=testval1
+key[1]=testkey1
+key continues
+val[1]=testval3
+value continues
+key[2]=testkey2
+val[2]=testval2
+testkey1
+testval1
+testkey1\nkey continues
+testval3\nvalue continues
+testkey2
+testval2
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "serialize, key and value with embedded literal \n"
+cat c_head - c_tail <<'EOF' | test_C $
+{
+

Re: notmuch-emacs: Fcc to top-level directory given by database.path

2018-06-22 Thread Arun Isaac

>> notmuch insert --folder= or --folder="" does not work:
>>
>> String argument for option "folder" must be non-empty.
>> Unrecognized option: --folder=
>>
> This should be fixed in 733ccfabc, as part of 0.26

I have verified this, and it works.
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


wish: notmuch-emacs: "Good signature by:" shows only email address, should show fingerprint also

2018-06-22 Thread Gregor Zattler
Dear notmuch developers,

it would be great if notmuch-emacs (optionally?) showed the fingerprint of the 
key an email was signed with, in order to aide the user in selecting the right 
key to encrypt a reply to.

My correspondents email showed successful signature verification but when I 
want to reply I do not know which of several keys with the respective user id I 
should select for encryption.

It was also helpful if the key selection interface showed more info regarding 
the keys, especially date of creation and expiration , but I assume this is 
outside your area of development!?

Ciao; Gregor
-- 
 -... --- .-. . -.. ..--.. ...-.-

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch