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.
This commit is contained in:
David Bremner 2016-06-12 22:05:55 -03:00
parent b7345d277e
commit 651da30c09
3 changed files with 120 additions and 6 deletions

View file

@ -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.

View file

@ -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 */

View file

@ -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