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);
diff --git a/src/printer_data.c b/src/printer_data.c
index b74eaee..790b19d 100644
--- a/src/printer_data.c
+++ b/src/printer_data.c
@@ -72,7 +72,7 @@
out.method.f = f;
if (root) {
- out.ctx = root->schema->module->ctx;
+ out.ctx = LYD_NODE_CTX(root);
}
ret = lyd_print_(&out, root, format, options);
@@ -95,7 +95,7 @@
f = fopen(path, "w");
if (!f) {
- LOGERR(root ? root->schema->module->ctx : NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", path, strerror(errno));
+ LOGERR(root ? LYD_NODE_CTX(root) : NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", path, strerror(errno));
return LY_ESYS;
}
@@ -117,7 +117,7 @@
out.method.fd = fd;
if (root) {
- out.ctx = root->schema->module->ctx;
+ out.ctx = LYD_NODE_CTX(root);
}
ret = lyd_print_(&out, root, format, options);
@@ -150,7 +150,7 @@
out.type = LYOUT_MEMORY;
if (root) {
- out.ctx = root->schema->module->ctx;
+ out.ctx = LYD_NODE_CTX(root);
}
ret = lyd_print_(&out, root, format, options);
@@ -180,7 +180,7 @@
out.method.clb.arg = arg;
if (root) {
- out.ctx = root->schema->module->ctx;
+ out.ctx = LYD_NODE_CTX(root);
}
ret = lyd_print_(&out, root, format, options);
diff --git a/src/printer_internal.h b/src/printer_internal.h
index 25b0afb..139a5bc 100644
--- a/src/printer_internal.h
+++ b/src/printer_internal.h
@@ -55,7 +55,7 @@
size_t printed; /**< Number of printed bytes */
- struct ly_ctx *ctx; /**< libyang context for error logging */
+ const struct ly_ctx *ctx; /**< libyang context for error logging */
LY_ERR status; /**< current status of the printer */
};
diff --git a/src/tree_data.c b/src/tree_data.c
index 5026d4e..939cfcb 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -148,7 +148,7 @@
}
API LY_ERR
-lys_value_validate(struct ly_ctx *ctx, const struct lysc_node *node, const char *value, size_t value_len,
+lys_value_validate(const struct ly_ctx *ctx, const struct lysc_node *node, const char *value, size_t value_len,
ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format)
{
LY_ERR rc = LY_SUCCESS;
@@ -183,7 +183,7 @@
}
API LY_ERR
-lyd_value_validate(struct ly_ctx *ctx, const struct lyd_node_term *node, const char *value, size_t value_len,
+lyd_value_validate(const struct ly_ctx *ctx, const struct lyd_node_term *node, const char *value, size_t value_len,
ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format, const struct lyd_node *tree)
{
LY_ERR rc;
diff --git a/src/tree_data.h b/src/tree_data.h
index 80ecca5..c9a0a9d 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -817,7 +817,7 @@
* @return LY_EINCOMPLETE in case the @p trees is not provided and it was needed to finish the validation (e.g. due to require-instance).
* @return LY_ERR value if an error occurred.
*/
-LY_ERR lyd_value_validate(struct ly_ctx *ctx, const struct lyd_node_term *node, const char *value, size_t value_len,
+LY_ERR lyd_value_validate(const struct ly_ctx *ctx, const struct lyd_node_term *node, const char *value, size_t value_len,
ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format, const struct lyd_node *tree);
/**
diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h
index a942f77..5873306 100644
--- a/src/tree_data_internal.h
+++ b/src/tree_data_internal.h
@@ -384,7 +384,8 @@
* @brief Check that a list has all its keys.
*
* @param[in] node List to check.
- * @return LY_ERR value.
+ * @return LY_SUCCESS on success.
+ * @return LY_ENOT on a missing key.
*/
LY_ERR lyd_parse_check_keys(struct lyd_node *node);
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 0d23f0c..0efc2e8 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -1974,7 +1974,7 @@
* @return LY_SUCCESS on success
* @return LY_ERR value if an error occurred.
*/
-LY_ERR lys_value_validate(struct ly_ctx *ctx, const struct lysc_node *node, const char *value, size_t value_len,
+LY_ERR lys_value_validate(const struct ly_ctx *ctx, const struct lysc_node *node, const char *value, size_t value_len,
ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format);
/**
diff --git a/src/xml.c b/src/xml.c
index ff76c31..d4b8a9e 100644
--- a/src/xml.c
+++ b/src/xml.c
@@ -240,6 +240,31 @@
}
}
+void *
+lyxml_elem_dup(void *item)
+{
+ struct lyxml_elem *dup;
+
+ dup = malloc(sizeof *dup);
+ memcpy(dup, item, sizeof *dup);
+
+ return dup;
+}
+
+void *
+lyxml_ns_dup(void *item)
+{
+ struct lyxml_ns *dup, *orig;
+
+ orig = (struct lyxml_ns *)item;
+ dup = malloc(sizeof *dup);
+ dup->prefix = orig->prefix ? strdup(orig->prefix) : NULL;
+ dup->uri = strdup(orig->uri);
+ dup->depth = orig->depth;
+
+ return dup;
+}
+
const struct lyxml_ns *
lyxml_ns_get(struct lyxml_context *context, const char *prefix, size_t prefix_len)
{
@@ -383,22 +408,18 @@
}
LY_ERR
-lyxml_get_element(struct lyxml_context *context, const char **input,
- const char **prefix, size_t *prefix_len, const char **name, size_t *name_len)
+lyxml_get_element(struct lyxml_context *context, const char **input, const char **prefix_p, size_t *prefix_len_p,
+ const char **name_p, size_t *name_len_p)
{
struct ly_ctx *ctx = context->ctx; /* shortcut */
- const char *in = (*input);
- size_t endtag_len;
+ const char *in = (*input), *prefix = NULL, *name = NULL;
+ size_t endtag_len, prefix_len = 0, name_len = 0;
bool loop = true;
int closing = 0;
unsigned int c;
LY_ERR rc;
struct lyxml_elem *e;
- /* initialize output variables */
- (*prefix) = (*name) = NULL;
- (*prefix_len) = (*name_len) = 0;
-
while (loop) {
rc = lyxml_parse_element_start(context, &in, &closing);
if (rc) {
@@ -407,20 +428,20 @@
goto success;
}
/* we are at the begining of the element name, remember the identifier start before checking its format */
- LY_CHECK_RET(rc = lyxml_parse_element_name(context, &in, &endtag_len, &c, prefix, prefix_len, name, name_len));
+ LY_CHECK_RET(rc = lyxml_parse_element_name(context, &in, &endtag_len, &c, &prefix, &prefix_len, &name, &name_len));
if (closing) {
/* match opening and closing element tags */
LY_CHECK_ERR_RET(
!context->elements.count,
LOGVAL(ctx, LY_VLOG_LINE, &context->line, LYVE_SYNTAX,
- "Opening and closing elements tag missmatch (\"%.*s\").", *name_len, *name),
+ "Opening and closing elements tag missmatch (\"%.*s\").", name_len, name),
LY_EVALID);
e = (struct lyxml_elem*)context->elements.objs[context->elements.count - 1];
- if (e->prefix_len != *prefix_len || e->name_len != *name_len
- || (*prefix_len && strncmp(*prefix, e->prefix, e->prefix_len)) || strncmp(*name, e->name, e->name_len)) {
+ if (e->prefix_len != prefix_len || e->name_len != name_len
+ || (prefix_len && strncmp(prefix, e->prefix, e->prefix_len)) || strncmp(name, e->name, e->name_len)) {
LOGVAL(ctx, LY_VLOG_LINE, &context->line, LYVE_SYNTAX,
- "Opening and closing elements tag missmatch (\"%.*s\").", *name_len, *name);
+ "Opening and closing elements tag missmatch (\"%.*s\").", name_len, name);
return LY_EVALID;
}
/* opening and closing element tags matches, remove record from the opening tags list */
@@ -430,9 +451,9 @@
/* remove also the namespaces connected with the element */
lyxml_ns_rm(context);
- /* do not return element information to announce closing element being currently processed */
- *name = *prefix = NULL;
- *name_len = *prefix_len = 0;
+ /* clear closing element */
+ name = prefix = NULL;
+ name_len = prefix_len = 0;
if (c == '>') {
/* end of closing element */
@@ -461,10 +482,10 @@
/* store element opening tag information */
e = malloc(sizeof *e);
LY_CHECK_ERR_RET(!e, LOGMEM(ctx), LY_EMEM);
- e->name = *name;
- e->prefix = *prefix;
- e->name_len = *name_len;
- e->prefix_len = *prefix_len;
+ e->name = name;
+ e->prefix = prefix;
+ e->name_len = name_len;
+ e->prefix_len = prefix_len;
ly_set_add(&context->elements, e, LY_SET_OPT_USEASLIST);
}
}
@@ -483,6 +504,15 @@
}
/* move caller's input */
(*input) = in;
+ /* return values */
+ if (prefix_p) {
+ *prefix_p = prefix;
+ *prefix_len_p = prefix_len;
+ }
+ if (name_p) {
+ *name_p = name;
+ *name_len_p = name_len;
+ }
return LY_SUCCESS;
}
diff --git a/src/xml.h b/src/xml.h
index f81c49b..8e30478 100644
--- a/src/xml.h
+++ b/src/xml.h
@@ -20,6 +20,7 @@
#include "log.h"
#include "set.h"
+#include "tree_schema.h"
struct lyout;
struct ly_prefix;
@@ -94,16 +95,16 @@
* @param[in] context XML context to track lines or store errors into libyang context.
* @param[in,out] input Input string to process, updated according to the processed/read data.
* @param[in] options Currently unused options to modify input processing.
- * @param[out] prefix Pointer to prefix if present in the element name, NULL otherwise.
- * @param[out] prefix_len Length of the prefix if any.
- * @param[out] name Element name. When LY_SUCCESS is returned but name is NULL, check context's status field:
+ * @param[out] prefix_p Pointer to prefix if present in the element name, NULL otherwise.
+ * @param[out] prefix_len_p Length of the prefix if any.
+ * @param[out] name_p Element name. When LY_SUCCESS is returned but name is NULL, check context's status field:
* - LYXML_END - end of input was reached
* - LYXML_ELEMENT - closing element found, expecting now a sibling element so call lyxml_get_element() again
- * @param[out] name_len Length of the element name.
+ * @param[out] name_len_p Length of the element name.
* @return LY_ERR values.
*/
-LY_ERR lyxml_get_element(struct lyxml_context *context, const char **input,
- const char **prefix, size_t *prefix_len, const char **name, size_t *name_len);
+LY_ERR lyxml_get_element(struct lyxml_context *context, const char **input, const char **prefix_p, size_t *prefix_len_p,
+ const char **name_p, size_t *name_len_p);
/**
* @brief Skip an element after its opening tag was parsed.
@@ -228,4 +229,8 @@
LY_ERR lyxml_value_compare(const char *value1, const struct ly_prefix *prefs1, const char *value2,
const struct ly_prefix *prefs2);
+void *lyxml_elem_dup(void *item);
+
+void *lyxml_ns_dup(void *item);
+
#endif /* LY_XML_H_ */