Blame SOURCES/liblognorm-2.0.0-rhbz1565219-add-skipempty.patch

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