]> git.notmuchmail.org Git - notmuch/commitdiff
CLI: add properties to dump output
authorDavid Bremner <david@tethera.net>
Mon, 13 Jun 2016 01:05:54 +0000 (22:05 -0300)
committerDavid Bremner <david@tethera.net>
Wed, 21 Sep 2016 21:14:25 +0000 (18:14 -0300)
Part of providing extensibility via properties is to make sure that user
data is not lost. Thus we need to be able to dump and restore
properties.

doc/man1/notmuch-dump.rst
notmuch-client.h
notmuch-dump.c
notmuch-new.c
test/T610-message-property.sh

index d56974ed0ba01aea71b34014e9a081aa52ad46da..585702726bf155d9e8ecd569775565289ac26f12 100644 (file)
@@ -71,7 +71,7 @@ Supported options for **dump** include
             characters. Note also that tags with spaces will not be
             correctly restored with this format.
 
             characters. Note also that tags with spaces will not be
             correctly restored with this format.
 
-    ``--include=(config|tags)``
+    ``--include=(config|properties|tags)``
 
     Control what kind of metadata is included in the output.
 
 
     Control what kind of metadata is included in the output.
 
@@ -81,14 +81,22 @@ Supported options for **dump** include
        starts with "#@ ", followed by a space separated key-value
        pair.  Both key and value are hex encoded if needed.
 
        starts with "#@ ", followed by a space separated key-value
        pair.  Both key and value are hex encoded if needed.
 
+      **properties**
+
+       Output per-message (key,value) metadata.  Each line starts
+       with "#= ", followed by a message id, and a space separated
+       list of key=value pairs.  pair.  Ids, keys and values are hex
+       encoded if needed.
+
       **tags**
 
       **tags**
 
-       Output per-message metadata, namely tags. See *format* above
+       Output per-message boolean metadata, namely tags. See *format* above
        for description of the output.
 
        for description of the output.
 
-      The default is to include both tags and configuration
-      information. As of version 2 of the dump format, there is a
-      header line of the following form
+      The default is to include all available types of data.  The
+      option can be specified multiple times to select some subset. As
+      of version 2 of the dump format, there is a header line of the
+      following form
 
       |
       |  #notmuch-dump <*format*>:<*version*> <*included*>
 
       |
       |  #notmuch-dump <*format*>:<*version*> <*included*>
index ebc092b844c91f5f488163cfb78f6eb77e6d453d..9ce2aef17431b3cd9792cc08477fcb630f434eea 100644 (file)
@@ -449,8 +449,11 @@ typedef enum dump_formats {
 typedef enum dump_includes {
     DUMP_INCLUDE_TAGS = 1,
     DUMP_INCLUDE_CONFIG = 2,
 typedef enum dump_includes {
     DUMP_INCLUDE_TAGS = 1,
     DUMP_INCLUDE_CONFIG = 2,
+    DUMP_INCLUDE_PROPERTIES = 4
 } dump_include_t;
 
 } dump_include_t;
 
+#define DUMP_INCLUDE_DEFAULT (DUMP_INCLUDE_TAGS | DUMP_INCLUDE_CONFIG | DUMP_INCLUDE_PROPERTIES)
+
 #define NOTMUCH_DUMP_VERSION 2
 
 int
 #define NOTMUCH_DUMP_VERSION 2
 
 int
index d80ed8b8bc6d94a94afb237cee305617cbea4578..e7965ceab1c55fb9347b6657df45fa62330b4839 100644 (file)
@@ -69,12 +69,77 @@ database_dump_config (notmuch_database_t *notmuch, gzFile output)
 static void
 print_dump_header (gzFile output, int output_format, int include)
 {
 static void
 print_dump_header (gzFile output, int output_format, int include)
 {
-    gzprintf (output, "#notmuch-dump %s:%d %s%s%s\n",
+    const char *sep = "";
+
+    gzprintf (output, "#notmuch-dump %s:%d ",
              (output_format == DUMP_FORMAT_SUP) ? "sup" : "batch-tag",
              (output_format == DUMP_FORMAT_SUP) ? "sup" : "batch-tag",
-             NOTMUCH_DUMP_VERSION,
-             (include & DUMP_INCLUDE_CONFIG) ? "config" : "",
-             (include & DUMP_INCLUDE_TAGS) && (include & DUMP_INCLUDE_CONFIG) ? "," : "",
-             (include & DUMP_INCLUDE_TAGS) ? "tags" : "");
+             NOTMUCH_DUMP_VERSION);
+
+    if (include & DUMP_INCLUDE_CONFIG) {
+       gzputs (output, "config");
+       sep = ",";
+    }
+    if (include & DUMP_INCLUDE_PROPERTIES) {
+       gzprintf (output, "%sproperties", sep);
+       sep = ",";
+    }
+    if (include & DUMP_INCLUDE_TAGS) {
+       gzprintf (output, "%sproperties", sep);
+    }
+    gzputs (output, "\n");
+}
+
+static int
+dump_properties_message (void *ctx,
+                        notmuch_message_t *message,
+                        gzFile output,
+                        char **buffer_p, size_t *size_p)
+{
+    const char *message_id;
+    notmuch_message_properties_t *list;
+    notmuch_bool_t first = TRUE;
+
+    message_id = notmuch_message_get_message_id (message);
+
+    if (strchr (message_id, '\n')) {
+       fprintf (stderr, "Warning: skipping message id containing line break: \"%s\"\n", message_id);
+       return 0;
+    }
+
+    for (list = notmuch_message_get_properties (message, "", FALSE);
+        notmuch_message_properties_valid (list); notmuch_message_properties_move_to_next (list)) {
+       const char *key, *val;
+
+       if (first) {
+           if (hex_encode (ctx, message_id, buffer_p, size_p) != HEX_SUCCESS) {
+               fprintf (stderr, "Error: failed to hex-encode message-id %s\n", message_id);
+               return 1;
+           }
+           gzprintf (output, "#= %s", *buffer_p);
+           first = FALSE;
+       }
+
+       key = notmuch_message_properties_key (list);
+       val = notmuch_message_properties_value (list);
+
+       if (hex_encode (ctx, key, buffer_p, size_p) != HEX_SUCCESS) {
+           fprintf (stderr, "Error: failed to hex-encode key %s\n", key);
+           return 1;
+       }
+       gzprintf (output, " %s", *buffer_p);
+
+       if (hex_encode (ctx, val, buffer_p, size_p) != HEX_SUCCESS) {
+           fprintf (stderr, "Error: failed to hex-encode value %s\n", val);
+           return 1;
+       }
+       gzprintf (output, "=%s", *buffer_p);
+    }
+    notmuch_message_properties_destroy (list);
+
+    if (! first)
+       gzprintf (output, "\n", *buffer_p);
+
+    return 0;
 }
 
 static int
 }
 
 static int
@@ -159,7 +224,7 @@ database_dump_file (notmuch_database_t *notmuch, gzFile output,
            return EXIT_FAILURE;
     }
 
            return EXIT_FAILURE;
     }
 
-    if (! (include & DUMP_INCLUDE_TAGS))
+    if (! (include & (DUMP_INCLUDE_TAGS | DUMP_INCLUDE_PROPERTIES)))
        return EXIT_SUCCESS;
 
     if (! query_str)
        return EXIT_SUCCESS;
 
     if (! query_str)
@@ -189,6 +254,11 @@ database_dump_file (notmuch_database_t *notmuch, gzFile output,
                               &buffer, &buffer_size))
            return EXIT_FAILURE;
 
                               &buffer, &buffer_size))
            return EXIT_FAILURE;
 
+       if ((include & DUMP_INCLUDE_PROPERTIES) &&
+           dump_properties_message (notmuch, message, output,
+                                    &buffer, &buffer_size))
+           return EXIT_FAILURE;
+
        notmuch_message_destroy (message);
     }
 
        notmuch_message_destroy (message);
     }
 
@@ -312,6 +382,7 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[])
                                  { 0, 0 } } },
        { NOTMUCH_OPT_KEYWORD_FLAGS, &include, "include", 'I',
          (notmuch_keyword_t []){ { "config", DUMP_INCLUDE_CONFIG },
                                  { 0, 0 } } },
        { NOTMUCH_OPT_KEYWORD_FLAGS, &include, "include", 'I',
          (notmuch_keyword_t []){ { "config", DUMP_INCLUDE_CONFIG },
+                                 { "properties", DUMP_INCLUDE_PROPERTIES },
                                  { "tags", DUMP_INCLUDE_TAGS} } },
        { NOTMUCH_OPT_STRING, &output_file_name, "output", 'o', 0  },
        { NOTMUCH_OPT_BOOLEAN, &gzip_output, "gzip", 'z', 0 },
                                  { "tags", DUMP_INCLUDE_TAGS} } },
        { NOTMUCH_OPT_STRING, &output_file_name, "output", 'o', 0  },
        { NOTMUCH_OPT_BOOLEAN, &gzip_output, "gzip", 'z', 0 },
@@ -326,7 +397,7 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[])
     notmuch_process_shared_options (argv[0]);
 
     if (include == 0)
     notmuch_process_shared_options (argv[0]);
 
     if (include == 0)
-       include = DUMP_INCLUDE_CONFIG | DUMP_INCLUDE_TAGS;
+       include = DUMP_INCLUDE_CONFIG | DUMP_INCLUDE_TAGS | DUMP_INCLUDE_PROPERTIES;
 
     if (opt_index < argc) {
        query_str = query_string_from_args (notmuch, argc - opt_index, argv + opt_index);
 
     if (opt_index < argc) {
        query_str = query_string_from_args (notmuch, argc - opt_index, argv + opt_index);
index 799fec2088489ee62882769a97a4097d22f08ae5..c55dea7bc1b7188522b2f2805fdf6a93ca12bf68 100644 (file)
@@ -1042,7 +1042,7 @@ notmuch_new_command (notmuch_config_t *config, int argc, char *argv[])
            }
 
            if (notmuch_database_dump (notmuch, backup_name, "",
            }
 
            if (notmuch_database_dump (notmuch, backup_name, "",
-                                      DUMP_FORMAT_BATCH_TAG, DUMP_INCLUDE_CONFIG | DUMP_INCLUDE_TAGS, TRUE)) {
+                                      DUMP_FORMAT_BATCH_TAG, DUMP_INCLUDE_DEFAULT, TRUE)) {
                fprintf (stderr, "Backup failed. Aborting upgrade.");
                return EXIT_FAILURE;
            }
                fprintf (stderr, "Backup failed. Aborting upgrade.");
                return EXIT_FAILURE;
            }
index b5ddb7a4d8aa3def2c258665c69614551da965fc..a9b76de18051b5dc315ac85ab9ab3775c3342ace 100755 (executable)
@@ -89,6 +89,17 @@ testkey2 = NULL
 EOF
 test_expect_equal_file EXPECTED OUTPUT
 
 EOF
 test_expect_equal_file EXPECTED OUTPUT
 
+test_begin_subtest "notmuch_message_remove_all_properties"
+cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
+EXPECT0(notmuch_message_remove_all_properties (message, NULL));
+print_properties (message, "", FALSE);
+EOF
+cat <<'EOF' >EXPECTED
+== stdout ==
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
 test_begin_subtest "notmuch_message_get_properties: empty list"
 cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
 {
 test_begin_subtest "notmuch_message_get_properties: empty list"
 cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
 {
@@ -188,4 +199,14 @@ cat <<'EOF' >EXPECTED
 EOF
 test_expect_equal_file EXPECTED OUTPUT
 
 EOF
 test_expect_equal_file EXPECTED OUTPUT
 
+test_begin_subtest "dump message properties"
+cat <<EOF > PROPERTIES
+#= 4EFC743A.3060609@april.org fancy%20key%20with%20%c3%a1cc%c3%a8nts=import%20value%20with%20= testkey1=alice testkey1=bob testkey1=testvalue1 testkey1=testvalue2 testkey3=alice3 testkey3=bob3 testkey3=testvalue3
+EOF
+cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
+EXPECT0(notmuch_message_add_property (message, "fancy key with áccènts", "import value with ="));
+EOF
+notmuch dump | grep '^#=' > OUTPUT
+test_expect_equal_file PROPERTIES OUTPUT
+
 test_done
 test_done