xml parser NEW opaque parsing of invalid values/lists
diff --git a/src/parser_xml.c b/src/parser_xml.c
index 34e3161..e05f170 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -259,6 +259,91 @@
return ret;
}
+static LY_ERR
+lydxml_check_list(struct lyxml_context *xmlctx, const struct lysc_node *list, const char **data)
+{
+ LY_ERR ret;
+ struct ly_set key_set = {0};
+ const struct lysc_node *snode;
+ const char *name;
+ char *buffer = NULL, *value;
+ size_t name_len, buffer_size = 0, value_len;
+ int dynamic = 0;
+ uint32_t i, parents_count = xmlctx->elements.count;
+
+ assert(list && (list->nodetype == LYS_LIST));
+
+ /* get all keys into a set (keys do not have if-features or anything) */
+ snode = NULL;
+ while ((snode = lys_getnext(snode, list, NULL, LYS_GETNEXT_NOSTATECHECK)) && (snode->flags & LYS_KEY)) {
+ ly_set_add(&key_set, (void *)snode, LY_SET_OPT_USEASLIST);
+ }
+
+ while ((xmlctx->status == LYXML_ELEMENT) && key_set.count) {
+ /* keys must be from the same module */
+ LY_CHECK_GOTO(ret = lyxml_get_element(xmlctx, data, NULL, NULL, &name, &name_len), cleanup);
+ if (!name) {
+ /* closing previous element */
+ if (xmlctx->elements.count < parents_count) {
+ /* all siblings parsed */
+ break;
+ } else {
+ continue;
+ }
+ }
+
+ /* find key definition */
+ for (i = 0; i < key_set.count; ++i) {
+ snode = (const struct lysc_node *)key_set.objs[i];
+ if (!ly_strncmp(snode->name, name, name_len)) {
+ break;
+ }
+ }
+ if (i == key_set.count) {
+ /* uninteresting, skip it */
+ LY_CHECK_GOTO(ret = lyxml_skip_element(xmlctx, data), cleanup);
+ continue;
+ }
+
+ /* skip attributes */
+ while (xmlctx->status == LYXML_ATTRIBUTE) {
+ LY_CHECK_GOTO(ret = lyxml_get_attribute(xmlctx, data, NULL, NULL, NULL, NULL), cleanup);
+ }
+
+ /* get the value, if any */
+ if (dynamic) {
+ free(value);
+ }
+ value = "";
+ value_len = 0;
+ dynamic = 0;
+ if (xmlctx->status == LYXML_ELEM_CONTENT) {
+ ret = lyxml_get_string(xmlctx, data, &buffer, &buffer_size, &value, &value_len, &dynamic);
+ if (ret && (ret != LY_EINVAL)) {
+ goto cleanup;
+ }
+ }
+
+ /* validate it */
+ LY_CHECK_GOTO(ret = lys_value_validate(NULL, snode, value, value_len, lydxml_resolve_prefix, xmlctx->ctx, LYD_XML), cleanup);
+
+ /* key with a valid value, remove from the set */
+ ly_set_rm_index(&key_set, i, NULL);
+ }
+
+ if (key_set.count) {
+ /* some keys missing */
+ ret = LY_ENOT;
+ } else {
+ /* all keys found and validated */
+ ret = LY_SUCCESS;
+ }
+
+cleanup:
+ ly_set_erase(&key_set, NULL);
+ return ret;
+}
+
/**
* @brief Parse XML elements as YANG data node children the specified parent node.
*
@@ -271,22 +356,21 @@
static LY_ERR
lydxml_data_r(struct lyd_xml_ctx *ctx, struct lyd_node_inner *parent, const char **data, struct lyd_node **first)
{
- LY_ERR ret = LY_SUCCESS;
- const char *prefix, *name;
- size_t prefix_len, name_len;
+ LY_ERR ret = LY_SUCCESS, content_ret;
+ const char *prefix, *name, *backup_data;
+ char *buffer = NULL, *value;
+ size_t prefix_len, name_len, buffer_size = 0, value_len;
struct ly_set attrs_data = {0};
+ struct lyxml_context backup_ctx;
const struct lyxml_ns *ns;
struct lyd_meta *meta = NULL, *meta2, *prev_meta;
struct ly_attr *attr = NULL;
const struct lysc_node *snode;
struct lys_module *mod;
- unsigned int parents_count = ctx->elements.count;
- uint32_t prev_opts;
+ uint32_t prev_opts, parents_count = ctx->elements.count;
struct lyd_node *cur = NULL, *anchor;
struct ly_prefix *val_prefs;
int dynamic = 0;
- char *buffer = NULL, *value;
- size_t buffer_size = 0, value_len;
while (ctx->status == LYXML_ELEMENT) {
ret = lyxml_get_element((struct lyxml_context *)ctx, data, &prefix, &prefix_len, &name, &name_len);
@@ -347,6 +431,50 @@
}
}
+ /* get the value, if any */
+ value = "";
+ value_len = 0;
+ dynamic = 0;
+ content_ret = LY_SUCCESS;
+ if (ctx->status == LYXML_ELEM_CONTENT) {
+ content_ret = lyxml_get_string((struct lyxml_context *)ctx, data, &buffer, &buffer_size, &value, &value_len, &dynamic);
+ if (content_ret && (content_ret != LY_EINVAL)) {
+ LOGINT(ctx->ctx);
+ ret = LY_EINT;
+ goto cleanup;
+ }
+ }
+
+ if (snode && (ctx->options & LYD_OPT_OPAQ)) {
+ if (snode->nodetype & LYD_NODE_TERM) {
+ /* value may not be valid in which case we parse it as an opaque node */
+ if (lys_value_validate(NULL, snode, value, value_len, lydxml_resolve_prefix, ctx, LYD_XML)) {
+ snode = NULL;
+ }
+ } else if (snode->nodetype == LYS_LIST) {
+ /* use backup context and data pointer */
+ backup_ctx.ctx = ctx->ctx;
+ backup_ctx.line = ctx->line;
+ backup_ctx.status = ctx->status;
+ memset(&backup_ctx.elements, 0, sizeof backup_ctx.elements);
+ for (uint32_t i = 0; i < ctx->elements.count; ++i) {
+ ly_set_add(&backup_ctx.elements, lyxml_elem_dup(ctx->elements.objs[i]), LY_SET_OPT_USEASLIST);
+ }
+ memset(&backup_ctx.ns, 0, sizeof backup_ctx.ns);
+ for (uint32_t i = 0; i < ctx->ns.count; ++i) {
+ ly_set_add(&backup_ctx.ns, lyxml_ns_dup(ctx->ns.objs[i]), LY_SET_OPT_USEASLIST);
+ }
+ backup_data = *data;
+
+ /* list may be missing some keys, parse as opaque if it does */
+ if (lydxml_check_list(&backup_ctx, snode, &backup_data)) {
+ snode = NULL;
+ }
+
+ lyxml_context_clear(&backup_ctx);
+ }
+ }
+
/* create actual metadata so that prefixes are available in the context */
if (attrs_data.count) {
if (snode) {
@@ -369,23 +497,6 @@
if (!snode) {
if (ctx->options & LYD_OPT_OPAQ) {
- /* get the value */
- LY_ERR r = LY_EINVAL;
- if (ctx->status == LYXML_ELEM_CONTENT) {
- r = lyxml_get_string((struct lyxml_context *)ctx, data, &buffer, &buffer_size, &value, &value_len, &dynamic);
- if (r && (r != LY_EINVAL)) {
- LOGINT(ctx->ctx);
- ret = LY_EINT;
- goto cleanup;
- }
- }
- if (r == LY_EINVAL) {
- /* empty value */
- value = "";
- value_len = 0;
- dynamic = 0;
- }
-
/* get value prefixes */
ret = lyxml_get_prefixes((struct lyxml_context *)ctx, value, value_len, &val_prefs);
LY_CHECK_GOTO(ret, cleanup);
@@ -407,22 +518,12 @@
break;
}
} else if (snode->nodetype & LYD_NODE_TERM) {
- if (ctx->status == LYXML_ELEM_CONTENT) {
- /* get the value */
- ret = lyxml_get_string((struct lyxml_context *)ctx, data, &buffer, &buffer_size, &value, &value_len, &dynamic);
- if (ret != LY_SUCCESS) {
- if (ret == LY_EINVAL) {
- /* just indentation of a child element found */
- LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX, "Child element inside terminal node \"%s\" found.",
- snode->name);
- }
- goto cleanup;
- }
- } else {
- /* no content - validate empty value */
- value = "";
- value_len = 0;
- dynamic = 0;
+ if (content_ret == LY_EINVAL) {
+ /* just indentation of a child element found */
+ LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX, "Child element inside a terminal node \"%s\" found.",
+ snode->name);
+ ret = LY_EVALID;
+ goto cleanup;
}
/* create node */
@@ -452,13 +553,13 @@
}
}
} else if (snode->nodetype & LYD_NODE_INNER) {
- if (ctx->status == LYXML_ELEM_CONTENT) {
- LY_ERR r = lyxml_get_string((struct lyxml_context *)ctx, data, &buffer, &buffer_size, &value, &value_len, &dynamic);
- if (r != LY_EINVAL && (r != LY_SUCCESS || value_len != 0)) {
- LOGINT(ctx->ctx);
- ret = LY_EINT;
- goto cleanup;
- }
+ if (value_len) {
+ /* value in inner node */
+ LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX, "Text value inside an inner node \"%s\" found.",
+ snode->name);
+ ret = LY_EVALID;
+ goto cleanup;
+
}
/* create node */
@@ -473,8 +574,7 @@
if (snode->nodetype == LYS_LIST) {
/* check all keys exist */
- ret = lyd_parse_check_keys(cur);
- LY_CHECK_GOTO(ret, cleanup);
+ LY_CHECK_GOTO(ret = lyd_parse_check_keys(cur), cleanup);
}
if (!(ctx->options & LYD_OPT_PARSE_ONLY)) {
@@ -578,8 +678,8 @@
ly_free_attr(ctx->ctx, attr, 1);
lyd_free_tree(cur);
for (uint32_t u = 0; u < attrs_data.count; ++u) {
- if (((struct attr_data_s*)attrs_data.objs[u])->dynamic) {
- free(((struct attr_data_s*)attrs_data.objs[u])->value);
+ if (((struct attr_data_s *)attrs_data.objs[u])->dynamic) {
+ free(((struct attr_data_s *)attrs_data.objs[u])->value);
}
}
ly_set_erase(&attrs_data, free);