diff --git a/common/json.c b/common/json.c index 54290cfc8..6455ece61 100644 --- a/common/json.c +++ b/common/json.c @@ -493,6 +493,56 @@ void json_add_string(struct json_result *result, const char *fieldname, const ch result_append_fmt(result, "\"%s\"", escaped); } +void json_add_string_escape(struct json_result *result, const char *fieldname, + const char *value) +{ + /* Worst case: all \uXXXX */ + char *escaped = tal_arr(result, char, strlen(value) * 6 + 1); + size_t i, n; + + json_start_member(result, fieldname); + for (i = n = 0; value[i]; i++, n++) { + char esc = 0; + switch (value[i]) { + case '\n': + esc = 'n'; + break; + case '\b': + esc = 'b'; + break; + case '\f': + esc = 'f'; + break; + case '\t': + esc = 't'; + break; + case '\r': + esc = 'r'; + break; + case '\\': + case '"': + esc = value[i]; + break; + default: + if ((unsigned)value[i] < ' ' + || value[i] == 127) { + sprintf(escaped + n, "\\u%04X", value[i]); + n += 5; + continue; + } + } + if (esc) { + escaped[n++] = '\\'; + escaped[n] = esc; + } else + escaped[n] = value[i]; + } + + escaped[n] = '\0'; + result_append_fmt(result, "\"%s\"", escaped); + tal_free(escaped); +} + void json_add_bool(struct json_result *result, const char *fieldname, bool value) { json_start_member(result, fieldname); diff --git a/common/json.h b/common/json.h index 8e3100824..796f387fd 100644 --- a/common/json.h +++ b/common/json.h @@ -83,8 +83,15 @@ void json_object_end(struct json_result *ptr); struct json_result *new_json_result(const tal_t *ctx); -/* '"fieldname" : "value"' or '"value"' if fieldname is NULL*/ +/* '"fieldname" : "value"' or '"value"' if fieldname is NULL. Turns + * any unusual chars into ?. + */ void json_add_string(struct json_result *result, const char *fieldname, const char *value); + +/* Properly escapes any characters in @value */ +void json_add_string_escape(struct json_result *result, const char *fieldname, + const char *value); + /* '"fieldname" : literal' or 'literal' if fieldname is NULL*/ void json_add_literal(struct json_result *result, const char *fieldname, const char *literal, int len); diff --git a/common/test/run-json.c b/common/test/run-json.c index 8c21ed7f1..c9bb663e5 100644 --- a/common/test/run-json.c +++ b/common/test/run-json.c @@ -37,8 +37,7 @@ static int test_json_tok_bitcoin_amount(void) return 0; } - -static int test_json_escape(void) +static int test_json_filter(void) { struct json_result *result = new_json_result(NULL); jsmntok_t *toks; @@ -78,9 +77,41 @@ static int test_json_escape(void) return 0; } +static void test_json_escape(void) +{ + int i; + const char *str; + + for (i = 1; i < 256; i++) { + char badstr[2]; + struct json_result *result = new_json_result(NULL); + + badstr[0] = i; + badstr[1] = 0; + + json_object_start(result, NULL); + json_add_string_escape(result, "x", badstr); + json_object_end(result); + + str = json_result_string(result); + if (i == '\\' || i == '"' + || i == '\n' || i == '\r' || i == '\b' + || i == '\t' || i == '\f') + assert(strstarts(str, "{ \"x\" : \"\\")); + else if (i < 32 || i == 127) + assert(strstarts(str, "{ \"x\" : \"\\u00")); + else { + char expect[] = "{ \"x\" : \"?\" }"; + expect[9] = i; + assert(streq(str, expect)); + } + tal_free(result); + } +} int main(void) { test_json_tok_bitcoin_amount(); + test_json_filter(); test_json_escape(); }