From eb2299a0897577048205e4d8a331168d82ce09d0 Mon Sep 17 00:00:00 2001 From: Noriko Hosoi Date: Thu, 26 Jul 2018 17:18:38 -0700 Subject: [PATCH] Add a parameter skipempty to the json field type. If skipempty is set as follows, empty json objects are dropped from the parsed result. %field_name:json:skipempty% If any parameter other than "skipempty" is given ("bogus" in this example), an error message "invalid flag for JSON parser: bogus" is issued. --- src/parser.c | 127 ++++++++++++++++++++++++++++++++++++++++++ src/parser.h | 2 +- src/pdag.c | 2 +- 3 files changed, 129 insertions(+), 2 deletions(-) diff --git a/src/parser.c b/src/parser.c index 77407c6..6736c6f 100644 --- a/src/parser.c +++ b/src/parser.c @@ -2325,6 +2325,85 @@ PARSER_Parse(v2IPTables) return r; } +/* + * Delete children of the given object if it has children and they are empty. + * + * return 0 if object is not empty + * return 1 if object is empty + * return < 0 if error + * + * Caller should do this: + * if (jsonSkipEmpty(obj) > 0) { + * json_object_put(obj); + * obj = NULL; + * } + * or otherwise not use obj if jsonSkipEmpty returns > 0. + */ +static int +jsonSkipEmpty(struct json_object *__restrict__ json) +{ + int rc = 0; + struct json_object *val = NULL; + + if(json == NULL) { + rc = 1; + goto finalize_it; + } + + switch (json_object_get_type(json)) { + case json_type_string: + rc = json_object_get_string_len(json) == 0; + break; + case json_type_array: + { + int i; + int arrayLen = json_object_array_length(json); + for (i = 0 ; i < arrayLen ; ++i) { + val = json_object_array_get_idx(json, i); + if ((rc = jsonSkipEmpty(val)) > 0) { + /* delete the empty item and reset the index and arrayLen */ + json_object_array_del_idx(json, i--); + arrayLen = json_object_array_length(json); + } else if (rc < 0) { + goto finalize_it; + } + } + rc = json_object_array_length(json) == 0; + break; + } + case json_type_object: + { + struct json_object_iterator it = json_object_iter_begin(json); + struct json_object_iterator itEnd = json_object_iter_end(json); + while (!json_object_iter_equal(&it, &itEnd)) { + val = json_object_iter_peek_value(&it); + if ((rc = jsonSkipEmpty(val)) > 0) { + json_object_object_del(json, json_object_iter_peek_name(&it)); + } else if (rc < 0) { + goto finalize_it; + } + json_object_iter_next(&it); + } + rc = json_object_object_length(json) == 0; + } + case json_type_null: + case json_type_boolean: + case json_type_double: + case json_type_int: + default: break; + } +finalize_it: + return rc; +} + +/* + * Parameters for field type json + * skipempty - skips empty json objects. + * - %field_name:json:skipempty% + */ +struct data_JSON { + int skipempty; +}; /** * Parse JSON. This parser tries to find JSON data inside a message. * If it finds valid JSON, it will extract it. Extra data after the @@ -2340,6 +2419,7 @@ PARSER_Parse(v2IPTables) PARSER_Parse(JSON) const size_t i = *offs; struct json_tokener *tokener = NULL; + struct data_JSON *const data = (struct data_JSON*) pdata; if(npb->str[i] != '{' && npb->str[i] != ']') { /* this can't be json, see RFC4627, Sect. 2 @@ -2368,6 +2448,20 @@ PARSER_Parse(JSON) if(value == NULL) { json_object_put(json); } else { + if (data && data->skipempty) { + int rc = jsonSkipEmpty(json); + if (rc < 0) { + json_object_put(json); + FAIL(LN_WRONGPARSER); + } else if (rc > 0) { + /* + * json value is empty. + * E.g., {"message":""}, {"message":[]}, {"message":{}} + */ + json_object_put(json); + FAIL(0); + } + } *value = json; } @@ -2376,7 +2470,40 @@ PARSER_Parse(JSON) json_tokener_free(tokener); return r; } +PARSER_Construct(JSON) +{ + int r = 0; + struct json_object *ed; + struct data_JSON *data = NULL; + char *flag; + if(json == NULL) + goto done; + + if(json_object_object_get_ex(json, "extradata", &ed) == 0) { + /* No JSON parameter */ + goto done; + } + data = (struct data_JSON*) calloc(1, sizeof(struct data_JSON)); + flag = json_object_get_string(ed); + if (strcasecmp(flag, "skipempty") == 0) { + data->skipempty = 1; + } else { + ln_errprintf(ctx, 0, "invalid flag for JSON parser: %s", flag); + r = LN_BADCONFIG; + goto done; + } + *pdata = data; +done: + if(r != 0) { + free(data); + } + return r; +} +PARSER_Destruct(JSON) +{ + free(pdata); +} /* check if a char is valid inside a name of a NameValue list * The set of valid characters may be extended if there is good diff --git a/src/parser.h b/src/parser.h index 38be62d..5b4a821 100644 --- a/src/parser.h +++ b/src/parser.h @@ -70,7 +70,7 @@ PARSERDEF_NO_DATA(Time24hr); PARSERDEF_NO_DATA(Duration); PARSERDEF_NO_DATA(IPv4); PARSERDEF_NO_DATA(IPv6); -PARSERDEF_NO_DATA(JSON); +PARSERDEF(JSON); PARSERDEF_NO_DATA(CEESyslog); PARSERDEF_NO_DATA(v2IPTables); PARSERDEF_NO_DATA(CiscoInterfaceSpec); diff --git a/src/pdag.c b/src/pdag.c index 0768e99..9feb755 100644 --- a/src/pdag.c +++ b/src/pdag.c @@ -90,7 +90,7 @@ static struct ln_parser_info parser_lookup_table[] = { PARSER_ENTRY_NO_DATA("duration", Duration, 16), PARSER_ENTRY_NO_DATA("cisco-interface-spec", CiscoInterfaceSpec, 4), PARSER_ENTRY_NO_DATA("name-value-list", NameValue, 8), - PARSER_ENTRY_NO_DATA("json", JSON, 4), + PARSER_ENTRY("json", JSON, 4), PARSER_ENTRY_NO_DATA("cee-syslog", CEESyslog, 4), PARSER_ENTRY_NO_DATA("mac48", MAC48, 16), PARSER_ENTRY_NO_DATA("cef", CEF, 4),