aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Bremner <david@tethera.net>2016-06-12 22:05:55 -0300
committerDavid Bremner <david@tethera.net>2016-09-21 18:14:25 -0300
commit651da30c0941081cf730930fc1a7cac34954ca0e (patch)
tree645886643d9ba06a17714c60fe5d1161156aad2c
parentb7345d277ec5f562000c0e740e6515c2a84f9c76 (diff)
cli: optionally restore message properties from dump file
This somewhat mimics the config line parsing, except there can be arbitrarily many key value pairs, so one more level of looping is required.
-rw-r--r--doc/man1/notmuch-restore.rst13
-rw-r--r--notmuch-restore.c85
-rwxr-xr-xtest/T610-message-property.sh28
3 files changed, 120 insertions, 6 deletions
diff --git a/doc/man1/notmuch-restore.rst b/doc/man1/notmuch-restore.rst
index 706f8912..c681fa2d 100644
--- a/doc/man1/notmuch-restore.rst
+++ b/doc/man1/notmuch-restore.rst
@@ -50,7 +50,7 @@ Supported options for **restore** include
format, this heuristic, based the fact that batch-tag format
contains no parentheses, should be accurate.
- ``--include=(config|tags)``
+ ``--include=(config|properties|tags)``
Control what kind of metadata is restored.
@@ -60,13 +60,20 @@ Supported options for **restore** include
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**
Output per-message metadata, namely tags. See *format* above
for more details.
- The default is to restore both tags and configuration
- information
+ The default is to restore all available types of data. The
+ option can be specified multiple times to select some subset.
``--input=``\ <filename>
Read input from given file instead of stdin.
diff --git a/notmuch-restore.c b/notmuch-restore.c
index 371237c5..d6429efb 100644
--- a/notmuch-restore.c
+++ b/notmuch-restore.c
@@ -57,6 +57,72 @@ process_config_line (notmuch_database_t *notmuch, const char* line)
return ret;
}
+static int
+process_properties_line (notmuch_database_t *notmuch, const char* line)
+
+{
+ const char *id_p, *tok;
+ size_t id_len = 0, tok_len = 0;
+ char *id;
+
+ notmuch_message_t *message = NULL;
+ const char *delim = " \t\n";
+ int ret = EXIT_FAILURE;
+
+ void *local = talloc_new (NULL);
+
+ id_p = strtok_len_c (line, delim, &id_len);
+ id = talloc_strndup (local, id_p, id_len);
+ if (hex_decode_inplace (id) != HEX_SUCCESS) {
+ fprintf (stderr, "hex decoding failure on line %s\n", line);
+ goto DONE;
+ }
+
+ if (print_status_database ("notmuch restore", notmuch,
+ notmuch_database_find_message (notmuch, id, &message)))
+ goto DONE;
+
+ if (print_status_database ("notmuch restore", notmuch,
+ notmuch_message_remove_all_properties (message, NULL)))
+ goto DONE;
+
+ tok = id_p + id_len;
+
+ while ((tok = strtok_len_c (tok + tok_len, delim, &tok_len)) != NULL) {
+ char *key, *value;
+ size_t off = strcspn (tok, "=");
+ if (off > tok_len) {
+ fprintf (stderr, "unparsable token %s\n", tok);
+ goto DONE;
+ }
+
+ key = talloc_strndup (local, tok, off);
+ value = talloc_strndup (local, tok + off + 1, tok_len - off - 1);
+
+ if (hex_decode_inplace (key) != HEX_SUCCESS) {
+ fprintf (stderr, "hex decoding failure on key %s\n", key);
+ goto DONE;
+ }
+
+ if (hex_decode_inplace (value) != HEX_SUCCESS) {
+ fprintf (stderr, "hex decoding failure on value %s\n", value);
+ goto DONE;
+ }
+
+ if (print_status_database ("notmuch restore", notmuch,
+ notmuch_message_add_property (message, key, value)))
+ goto DONE;
+
+ }
+
+ ret = EXIT_SUCCESS;
+
+ DONE:
+ talloc_free (local);
+ return ret;
+}
+
+
static regex_t regex;
/* Non-zero return indicates an error in retrieving the message,
@@ -188,6 +254,7 @@ notmuch_restore_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 },
+ { "properties", DUMP_INCLUDE_PROPERTIES },
{ "tags", DUMP_INCLUDE_TAGS} } },
{ NOTMUCH_OPT_STRING, &input_file_name, "input", 'i', 0 },
@@ -206,7 +273,7 @@ notmuch_restore_command (notmuch_config_t *config, int argc, char *argv[])
notmuch_exit_if_unmatched_db_uuid (notmuch);
if (include == 0) {
- include = DUMP_INCLUDE_CONFIG | DUMP_INCLUDE_TAGS;
+ include = DUMP_INCLUDE_CONFIG | DUMP_INCLUDE_PROPERTIES | DUMP_INCLUDE_TAGS;
}
name_for_error = input_file_name ? input_file_name : "stdin";
@@ -273,13 +340,18 @@ notmuch_restore_command (notmuch_config_t *config, int argc, char *argv[])
if (ret)
goto DONE;
}
+ if ((include & DUMP_INCLUDE_PROPERTIES) && line_len >= 2 && line[0] == '#' && line[1] == '=') {
+ ret = process_properties_line (notmuch, line + 2);
+ if (ret)
+ goto DONE;
+ }
} while ((line_len == 0) ||
(line[0] == '#') ||
/* the cast is safe because we checked about for line_len < 0 */
(strspn (line, " \t\n") == (unsigned)line_len));
- if (! (include & DUMP_INCLUDE_TAGS)) {
+ if (! ((include & DUMP_INCLUDE_TAGS) || (include & DUMP_INCLUDE_PROPERTIES))) {
ret = EXIT_SUCCESS;
goto DONE;
}
@@ -306,6 +378,13 @@ notmuch_restore_command (notmuch_config_t *config, int argc, char *argv[])
talloc_free (line_ctx);
line_ctx = talloc_new (config);
+
+ if ((include & DUMP_INCLUDE_PROPERTIES) && line_len >= 2 && line[0] == '#' && line[1] == '=') {
+ ret = process_properties_line (notmuch, line + 2);
+ if (ret)
+ goto DONE;
+ }
+
if (input_format == DUMP_FORMAT_SUP) {
ret = parse_sup_line (line_ctx, line, &query_string, tag_ops);
} else {
@@ -344,7 +423,7 @@ notmuch_restore_command (notmuch_config_t *config, int argc, char *argv[])
break;
} while (! (ret = gz_getline (line_ctx, &line, &line_len, input)));
-
+
/* EOF is normal loop termination condition, UTIL_SUCCESS is
* impossible here */
diff --git a/test/T610-message-property.sh b/test/T610-message-property.sh
index a9b76de1..c92b99ba 100755
--- a/test/T610-message-property.sh
+++ b/test/T610-message-property.sh
@@ -209,4 +209,32 @@ EOF
notmuch dump | grep '^#=' > OUTPUT
test_expect_equal_file PROPERTIES OUTPUT
+
+test_begin_subtest "restore missing message property (single line)"
+notmuch dump | grep '^#=' > BEFORE1
+cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
+EXPECT0(notmuch_message_remove_property (message, "testkey1", "bob"));
+EOF
+notmuch restore < BEFORE1
+notmuch dump | grep '^#=' > OUTPUT
+test_expect_equal_file PROPERTIES OUTPUT
+
+
+test_begin_subtest "restore missing message property (full dump)"
+notmuch dump > BEFORE2
+cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
+EXPECT0(notmuch_message_remove_property (message, "testkey1", "bob"));
+EOF
+notmuch restore < BEFORE2
+notmuch dump | grep '^#=' > OUTPUT
+test_expect_equal_file PROPERTIES OUTPUT
+
+test_begin_subtest "restore clear extra message property"
+cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
+EXPECT0(notmuch_message_add_property (message, "testkey1", "charles"));
+EOF
+notmuch restore < BEFORE2
+notmuch dump | grep '^#=' > OUTPUT
+test_expect_equal_file PROPERTIES OUTPUT
+
test_done