From 5577d50aa57c67d60043427d56263e50ef36817c Mon Sep 17 00:00:00 2001 From: Carl Smith Date: Wed, 13 Sep 2023 11:15:46 +1200 Subject: [PATCH] Properly parse restconf "fields" query format Previously we only parsed one set of parentheses --- models/test.xml | 6 ++ schema.c | 199 +++++++++++++++++++++++++----------------------- 2 files changed, 109 insertions(+), 96 deletions(-) diff --git a/models/test.xml b/models/test.xml index 7cdfc89..3bdced8 100644 --- a/models/test.xml +++ b/models/test.xml @@ -56,6 +56,12 @@ + + + + + + diff --git a/schema.c b/schema.c index be3c1e1..169c31d 100644 --- a/schema.c +++ b/schema.c @@ -1825,128 +1825,135 @@ sch_validate_pattern (sch_node * node, const char *value) /* Data translation/manipulation */ -static bool parse_query_fields (sch_node * schema, char *fields, GNode *parent, int flags, int depth); -static GNode * -parse_field (sch_node * schema, const char *path, int flags, int depth) +static GList* +q2n_split_params (const char *params, char separator) { - GNode *rnode = NULL; - GNode *child = NULL; - const char *next = NULL; - const char *sublist = NULL; - char *name; - - /* Find name */ - sublist = strchr (path, '('); - next = strchr (path, '/'); - if (sublist && (!next || sublist < next)) - name = g_strndup (path, sublist - path); - else if (next) - name = g_strndup (path, next - path); - else - name = g_strdup (path); - - /* Find schema node */ - schema = sch_node_child (schema, name); - if (schema == NULL) - { - ERROR (flags, SCH_E_NOSCHEMANODE, "No schema match for %s\n", name); - g_free (name); - return NULL; - } - if (!sch_is_readable (schema)) - { - ERROR (flags, SCH_E_NOTREADABLE, "Ignoring non-readable node %s\n", name); - g_free (name); - return NULL; + GList *list = NULL; + int depth = 0; + GString *result = g_string_new (NULL); + int i = 0; + for (i = 0; i < strlen (params); i++) + { + char c = params[i]; + if (c == '(' || c == '[' || c == '{') + depth += 1; + else if (c == ')' || c == ']' || c == '}') + depth -= 1; + else if (depth == 0 && c == separator) + { + list = g_list_append (list, g_string_free (result, false)); + result = g_string_new (NULL); + continue; + } + g_string_append_c (result, c); } + if (result) + list = g_list_append (list, g_string_free (result, false)); + return list; +} - /* Create the node */ - rnode = APTERYX_NODE (NULL, name); - DEBUG (flags, "%*s%s\n", depth * 2, " ", APTERYX_NAME (rnode)); - - /* Process subpath */ - if (next) +static GNode* +q2n_append_path (sch_node * schema, GNode *root, const char *path, int flags, int depth, sch_node **rschema) +{ + char **nodes = g_strsplit (path, "/", -1); + char **node = nodes; + while (*node) { - child = parse_field (schema, next + 1, flags, depth + 1); - if (!child) + char *name = *node; + + /* Find schema node */ + schema = sch_node_child (schema, name); + if (schema == NULL) { - free ((void *)rnode->data); - g_node_destroy (rnode); + ERROR (flags, SCH_E_NOSCHEMANODE, "No schema match for %s\n", name); return NULL; } - g_node_prepend (rnode, child); - } - else if (sublist) - { - char *fields = g_strndup (sublist + 1, strlen (sublist) - 2); - if (!parse_query_fields (schema, fields, rnode, flags, depth + 1)) + if (!sch_is_readable (schema)) { - free ((void *)rnode->data); - g_node_destroy (rnode); - free (fields); - return false; + ERROR (flags, SCH_E_NOTREADABLE, "Ignoring non-readable node %s\n", name); + return NULL; } - free (fields); - } - return rnode; + /* Create the node if it does not already exist */ + DEBUG (flags, "%*s%s\n", depth * 2, " ", name); + GNode *existing = apteryx_find_child (root, name); + if (existing) + root = existing; + else + root = g_node_append_data (root, g_strdup (name)); + node++; + depth++; + } + g_strfreev (nodes); + if (rschema) + *rschema = schema; + return root; } -static void -merge_node_into_parent (GNode *parent, GNode *node) +static bool +_field_query_to_node (sch_node * schema, const char *fields, GNode *parent, int flags, int depth, const char *tail) { - for (GNode *pchild = parent->children; pchild; pchild = pchild->next) + GList *params = q2n_split_params (fields, ';'); + bool rc = true; + + for (GList *iter = g_list_first (params); rc && iter; iter = g_list_next (iter)) { - if (g_strcmp0 (pchild->data, node->data) == 0) + sch_node *nschema = schema; + GNode *rroot = parent; + fields = iter->data; + char *left = g_strstr_len (fields, -1, "("); + char *right = g_strrstr_len (fields, -1, ")"); + if (left == NULL && right == NULL) { - /* Unlink all the children and add to the original parent */ - GList *children = NULL; - for (GNode *nchild = node->children; nchild; nchild = nchild->next) + rroot = q2n_append_path (schema, rroot, fields, flags, depth, &nschema); + if (!rroot) + rc = false; + if (rc && tail) { - children = g_list_append (children, nchild); + rroot = q2n_append_path (nschema, rroot, tail, flags, depth, NULL); + if (!rroot) + rc = false; } - for (GList *nchild = children; nchild; nchild = nchild->next) + continue; + } + if (left == NULL || right == NULL) + return false; + char *left_side = (left - fields) > 0 ? g_strndup (fields, left - fields) : NULL; + char *middle = g_strndup (left + 1, right - left - 1); + char *right_side = strlen (right + 1) > 0 ? g_strdup (right + 1) : NULL; + if (left_side) + { + rroot = q2n_append_path (nschema, rroot, left_side, flags, depth, &nschema); + if (!rroot) + rc = false; + } + if (rc && middle) + { + if (!_field_query_to_node (nschema, middle, rroot, flags, depth, right_side ?: tail)) { - g_node_unlink (nchild->data); - merge_node_into_parent (pchild, nchild->data); + rc = false; + goto exit; } - g_list_free (children); - node->children = NULL; - free ((void *)node->data); - g_node_destroy (node); - return; } + else if (rc && tail) + { + rroot = q2n_append_path (nschema, rroot, tail, flags, depth, NULL); + if (!rroot) + return false; + } + free (left_side); + free (middle); + free (right_side); } - g_node_prepend (parent, node); +exit: + g_list_free_full (params, g_free); + return rc; } static bool parse_query_fields (sch_node * schema, char *fields, GNode *parent, int flags, int depth) { - char *h, *t; - bool skip = false; - - h = t = fields; - while (*h) - { - if (*(h + 1) == '(') - skip = true; - else if (*(h + 1) == '\0' || (!skip && *(h + 1) == ';')) - { - char *field = g_strndup (t, (h - t + 1)); - GNode *node = parse_field (schema, field, flags, depth); - free (field); - if (!node) - return false; - merge_node_into_parent (parent, node); - t = h + 2; - } - else if (*(h + 1) == ')') - skip = false; - - h++; - } - return true; + return _field_query_to_node (schema, fields, parent, flags, depth, NULL); } static bool