[PATCH 7/9] CLI: add properties to dump output
authorDavid Bremner <david@tethera.net>
Sat, 6 Aug 2016 13:52:37 +0000 (22:52 +0900)
committerW. Trevor King <wking@tremily.us>
Sat, 20 Aug 2016 23:22:20 +0000 (16:22 -0700)
73/f83b8238efb1dbd4460ca05b6412a74976e14a [new file with mode: 0644]

diff --git a/73/f83b8238efb1dbd4460ca05b6412a74976e14a b/73/f83b8238efb1dbd4460ca05b6412a74976e14a
new file mode 100644 (file)
index 0000000..08bd0c0
--- /dev/null
@@ -0,0 +1,295 @@
+Return-Path: <bremner@tesseract.cs.unb.ca>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+ by arlo.cworth.org (Postfix) with ESMTP id 3E88D6DE091B\r
+ for <notmuch@notmuchmail.org>; Sat,  6 Aug 2016 06:53:07 -0700 (PDT)\r
+X-Virus-Scanned: Debian amavisd-new at cworth.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: -0.004\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=-0.004 tagged_above=-999 required=5\r
+ tests=[AWL=-0.005, HEADER_FROM_DIFFERENT_DOMAINS=0.001]\r
+ autolearn=disabled\r
+Received: from arlo.cworth.org ([127.0.0.1])\r
+ by localhost (arlo.cworth.org [127.0.0.1]) (amavisd-new, port 10024)\r
+ with ESMTP id tC0-fn3Idznt for <notmuch@notmuchmail.org>;\r
+ Sat,  6 Aug 2016 06:52:59 -0700 (PDT)\r
+Received: from fethera.tethera.net (fethera.tethera.net [198.245.60.197])\r
+ by arlo.cworth.org (Postfix) with ESMTPS id 816EE6DE02AF\r
+ for <notmuch@notmuchmail.org>; Sat,  6 Aug 2016 06:52:55 -0700 (PDT)\r
+Received: from remotemail by fethera.tethera.net with local (Exim 4.84_2)\r
+ (envelope-from <bremner@tesseract.cs.unb.ca>)\r
+ id 1bW22I-0007Du-Fu; Sat, 06 Aug 2016 09:53:10 -0400\r
+Received: (nullmailer pid 4137 invoked by uid 1000);\r
+ Sat, 06 Aug 2016 13:52:44 -0000\r
+From: David Bremner <david@tethera.net>\r
+To: notmuch@notmuchmail.org\r
+Subject: [PATCH 7/9] CLI: add properties to dump output\r
+Date: Sat,  6 Aug 2016 22:52:37 +0900\r
+Message-Id: <1470491559-3946-8-git-send-email-david@tethera.net>\r
+X-Mailer: git-send-email 2.8.1\r
+In-Reply-To: <1470491559-3946-1-git-send-email-david@tethera.net>\r
+References: <1470491559-3946-1-git-send-email-david@tethera.net>\r
+MIME-Version: 1.0\r
+Content-Type: text/plain; charset=UTF-8\r
+Content-Transfer-Encoding: 8bit\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.20\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+ <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <https://notmuchmail.org/mailman/options/notmuch>,\r
+ <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch/>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <https://notmuchmail.org/mailman/listinfo/notmuch>,\r
+ <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Sat, 06 Aug 2016 13:53:07 -0000\r
+\r
+Part of providing extensibility via properties is to make sure that user\r
+data is not lost. Thus we need to be able to dump and restore\r
+properties.\r
+---\r
+ doc/man1/notmuch-dump.rst     | 18 ++++++---\r
+ notmuch-client.h              |  3 ++\r
+ notmuch-dump.c                | 85 +++++++++++++++++++++++++++++++++++++++----\r
+ notmuch-new.c                 |  2 +-\r
+ test/T610-message-property.sh | 21 +++++++++++\r
+ 5 files changed, 116 insertions(+), 13 deletions(-)\r
+\r
+diff --git a/doc/man1/notmuch-dump.rst b/doc/man1/notmuch-dump.rst\r
+index 94986a8..751850b 100644\r
+--- a/doc/man1/notmuch-dump.rst\r
++++ b/doc/man1/notmuch-dump.rst\r
+@@ -71,7 +71,7 @@ Supported options for **dump** include\r
+             characters. Note also that tags with spaces will not be\r
+             correctly restored with this format.\r
\r
+-    ``--include=(config|tags)``\r
++    ``--include=(config|properties|tags)``\r
\r
+     Control what kind of metadata is included in the output.\r
\r
+@@ -81,14 +81,22 @@ Supported options for **dump** include\r
+       starts with "#@ ", followed by a space seperated key-value\r
+       pair.  Both key and value are hex encoded if needed.\r
\r
++      **properties**\r
++\r
++      Output per-message (key,value) metadata.  Each line starts\r
++      with "#= ", followed by a message id, and a space seperated\r
++      list of key=value pairs.  pair.  Ids, keys and values are hex\r
++      encoded if needed.\r
++\r
+       **tags**\r
\r
+-      Output per-message metadata, namely tags. See *format* above\r
++      Output per-message boolean metadata, namely tags. See *format* above\r
+       for description of the output.\r
\r
+-      The default is to include both tags and configuration\r
+-      information. As of version 2 of the dump format, there is a\r
+-      header line of the following form\r
++      The default is to include all available types of data.  The\r
++      option can be specified multiple times to select some subset. As\r
++      of version 2 of the dump format, there is a header line of the\r
++      following form\r
\r
+       |\r
+       |  #notmuch-dump <*format*>:<*version*> <*included*>\r
+diff --git a/notmuch-client.h b/notmuch-client.h\r
+index ebc092b..9ce2aef 100644\r
+--- a/notmuch-client.h\r
++++ b/notmuch-client.h\r
+@@ -449,8 +449,11 @@ typedef enum dump_formats {\r
+ typedef enum dump_includes {\r
+     DUMP_INCLUDE_TAGS = 1,\r
+     DUMP_INCLUDE_CONFIG = 2,\r
++    DUMP_INCLUDE_PROPERTIES = 4\r
+ } dump_include_t;\r
\r
++#define DUMP_INCLUDE_DEFAULT (DUMP_INCLUDE_TAGS | DUMP_INCLUDE_CONFIG | DUMP_INCLUDE_PROPERTIES)\r
++\r
+ #define NOTMUCH_DUMP_VERSION 2\r
\r
+ int\r
+diff --git a/notmuch-dump.c b/notmuch-dump.c\r
+index d80ed8b8..e7965ce 100644\r
+--- a/notmuch-dump.c\r
++++ b/notmuch-dump.c\r
+@@ -69,12 +69,77 @@ database_dump_config (notmuch_database_t *notmuch, gzFile output)\r
+ static void\r
+ print_dump_header (gzFile output, int output_format, int include)\r
+ {\r
+-    gzprintf (output, "#notmuch-dump %s:%d %s%s%s\n",\r
++    const char *sep = "";\r
++\r
++    gzprintf (output, "#notmuch-dump %s:%d ",\r
+             (output_format == DUMP_FORMAT_SUP) ? "sup" : "batch-tag",\r
+-            NOTMUCH_DUMP_VERSION,\r
+-            (include & DUMP_INCLUDE_CONFIG) ? "config" : "",\r
+-            (include & DUMP_INCLUDE_TAGS) && (include & DUMP_INCLUDE_CONFIG) ? "," : "",\r
+-            (include & DUMP_INCLUDE_TAGS) ? "tags" : "");\r
++            NOTMUCH_DUMP_VERSION);\r
++\r
++    if (include & DUMP_INCLUDE_CONFIG) {\r
++      gzputs (output, "config");\r
++      sep = ",";\r
++    }\r
++    if (include & DUMP_INCLUDE_PROPERTIES) {\r
++      gzprintf (output, "%sproperties", sep);\r
++      sep = ",";\r
++    }\r
++    if (include & DUMP_INCLUDE_TAGS) {\r
++      gzprintf (output, "%sproperties", sep);\r
++    }\r
++    gzputs (output, "\n");\r
++}\r
++\r
++static int\r
++dump_properties_message (void *ctx,\r
++                       notmuch_message_t *message,\r
++                       gzFile output,\r
++                       char **buffer_p, size_t *size_p)\r
++{\r
++    const char *message_id;\r
++    notmuch_message_properties_t *list;\r
++    notmuch_bool_t first = TRUE;\r
++\r
++    message_id = notmuch_message_get_message_id (message);\r
++\r
++    if (strchr (message_id, '\n')) {\r
++      fprintf (stderr, "Warning: skipping message id containing line break: \"%s\"\n", message_id);\r
++      return 0;\r
++    }\r
++\r
++    for (list = notmuch_message_get_properties (message, "", FALSE);\r
++       notmuch_message_properties_valid (list); notmuch_message_properties_move_to_next (list)) {\r
++      const char *key, *val;\r
++\r
++      if (first) {\r
++          if (hex_encode (ctx, message_id, buffer_p, size_p) != HEX_SUCCESS) {\r
++              fprintf (stderr, "Error: failed to hex-encode message-id %s\n", message_id);\r
++              return 1;\r
++          }\r
++          gzprintf (output, "#= %s", *buffer_p);\r
++          first = FALSE;\r
++      }\r
++\r
++      key = notmuch_message_properties_key (list);\r
++      val = notmuch_message_properties_value (list);\r
++\r
++      if (hex_encode (ctx, key, buffer_p, size_p) != HEX_SUCCESS) {\r
++          fprintf (stderr, "Error: failed to hex-encode key %s\n", key);\r
++          return 1;\r
++      }\r
++      gzprintf (output, " %s", *buffer_p);\r
++\r
++      if (hex_encode (ctx, val, buffer_p, size_p) != HEX_SUCCESS) {\r
++          fprintf (stderr, "Error: failed to hex-encode value %s\n", val);\r
++          return 1;\r
++      }\r
++      gzprintf (output, "=%s", *buffer_p);\r
++    }\r
++    notmuch_message_properties_destroy (list);\r
++\r
++    if (! first)\r
++      gzprintf (output, "\n", *buffer_p);\r
++\r
++    return 0;\r
+ }\r
\r
+ static int\r
+@@ -159,7 +224,7 @@ database_dump_file (notmuch_database_t *notmuch, gzFile output,\r
+           return EXIT_FAILURE;\r
+     }\r
\r
+-    if (! (include & DUMP_INCLUDE_TAGS))\r
++    if (! (include & (DUMP_INCLUDE_TAGS | DUMP_INCLUDE_PROPERTIES)))\r
+       return EXIT_SUCCESS;\r
\r
+     if (! query_str)\r
+@@ -189,6 +254,11 @@ database_dump_file (notmuch_database_t *notmuch, gzFile output,\r
+                              &buffer, &buffer_size))\r
+           return EXIT_FAILURE;\r
\r
++      if ((include & DUMP_INCLUDE_PROPERTIES) &&\r
++          dump_properties_message (notmuch, message, output,\r
++                                   &buffer, &buffer_size))\r
++          return EXIT_FAILURE;\r
++\r
+       notmuch_message_destroy (message);\r
+     }\r
\r
+@@ -312,6 +382,7 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[])\r
+                                 { 0, 0 } } },\r
+       { NOTMUCH_OPT_KEYWORD_FLAGS, &include, "include", 'I',\r
+         (notmuch_keyword_t []){ { "config", DUMP_INCLUDE_CONFIG },\r
++                                { "properties", DUMP_INCLUDE_PROPERTIES },\r
+                                 { "tags", DUMP_INCLUDE_TAGS} } },\r
+       { NOTMUCH_OPT_STRING, &output_file_name, "output", 'o', 0  },\r
+       { NOTMUCH_OPT_BOOLEAN, &gzip_output, "gzip", 'z', 0 },\r
+@@ -326,7 +397,7 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[])\r
+     notmuch_process_shared_options (argv[0]);\r
\r
+     if (include == 0)\r
+-      include = DUMP_INCLUDE_CONFIG | DUMP_INCLUDE_TAGS;\r
++      include = DUMP_INCLUDE_CONFIG | DUMP_INCLUDE_TAGS | DUMP_INCLUDE_PROPERTIES;\r
\r
+     if (opt_index < argc) {\r
+       query_str = query_string_from_args (notmuch, argc - opt_index, argv + opt_index);\r
+diff --git a/notmuch-new.c b/notmuch-new.c\r
+index 799fec2..c55dea7 100644\r
+--- a/notmuch-new.c\r
++++ b/notmuch-new.c\r
+@@ -1042,7 +1042,7 @@ notmuch_new_command (notmuch_config_t *config, int argc, char *argv[])\r
+           }\r
\r
+           if (notmuch_database_dump (notmuch, backup_name, "",\r
+-                                     DUMP_FORMAT_BATCH_TAG, DUMP_INCLUDE_CONFIG | DUMP_INCLUDE_TAGS, TRUE)) {\r
++                                     DUMP_FORMAT_BATCH_TAG, DUMP_INCLUDE_DEFAULT, TRUE)) {\r
+               fprintf (stderr, "Backup failed. Aborting upgrade.");\r
+               return EXIT_FAILURE;\r
+           }\r
+diff --git a/test/T610-message-property.sh b/test/T610-message-property.sh\r
+index b5ddb7a..a9b76de 100755\r
+--- a/test/T610-message-property.sh\r
++++ b/test/T610-message-property.sh\r
+@@ -89,6 +89,17 @@ testkey2 = NULL\r
+ EOF\r
+ test_expect_equal_file EXPECTED OUTPUT\r
\r
++test_begin_subtest "notmuch_message_remove_all_properties"\r
++cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}\r
++EXPECT0(notmuch_message_remove_all_properties (message, NULL));\r
++print_properties (message, "", FALSE);\r
++EOF\r
++cat <<'EOF' >EXPECTED\r
++== stdout ==\r
++== stderr ==\r
++EOF\r
++test_expect_equal_file EXPECTED OUTPUT\r
++\r
+ test_begin_subtest "notmuch_message_get_properties: empty list"\r
+ cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}\r
+ {\r
+@@ -188,4 +199,14 @@ cat <<'EOF' >EXPECTED\r
+ EOF\r
+ test_expect_equal_file EXPECTED OUTPUT\r
\r
++test_begin_subtest "dump message properties"\r
++cat <<EOF > PROPERTIES\r
++#= 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\r
++EOF\r
++cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}\r
++EXPECT0(notmuch_message_add_property (message, "fancy key with áccènts", "import value with ="));\r
++EOF\r
++notmuch dump | grep '^#=' > OUTPUT\r
++test_expect_equal_file PROPERTIES OUTPUT\r
++\r
+ test_done\r
+-- \r
+2.8.1\r
+\r