parser xml NEW rpc/action parsing
Also LYS_RPC added as a separate type.
diff --git a/src/parser_xml.c b/src/parser_xml.c
index 792d377..55aaf67 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -30,6 +30,9 @@
#include "xml.h"
#include "validation.h"
+#define LYD_INTOPT_RPC 0x01 /**< RPC/action invocation is being parsed */
+#define LYD_INTOPT_NOTIF 0x02 /**< notification is being parsed */
+
/**
* @brief Internal context for XML YANG data parser.
*/
@@ -37,12 +40,14 @@
struct lyxml_ctx *xmlctx; /**< XML context */
uint32_t options; /**< various @ref dataparseroptions. */
+ uint32_t int_opts; /**< internal data parser options */
uint32_t path_len; /**< used bytes in the path buffer */
#define LYD_PARSER_BUFSIZE 4078
char path[LYD_PARSER_BUFSIZE]; /**< buffer for the generated path */
struct ly_set unres_node_type; /**< set of nodes validated with LY_EINCOMPLETE result */
struct ly_set unres_meta_type; /**< set of metadata validated with LY_EINCOMPLETE result */
struct ly_set when_check; /**< set of nodes with "when" conditions */
+ struct lyd_node *op_ntf; /**< if an RPC/action/notification is being parsed, store the pointer to it */
};
/**
@@ -279,30 +284,186 @@
return ret;
}
+static LY_ERR
+lydxml_data_skip(struct lyxml_ctx *xmlctx)
+{
+ uint32_t parents_count;
+
+ /* remember current number of parents */
+ parents_count = xmlctx->elements.count;
+
+ /* skip after the content */
+ while (xmlctx->status != LYXML_ELEM_CONTENT) {
+ LY_CHECK_RET(lyxml_ctx_next(xmlctx));
+ }
+ LY_CHECK_RET(lyxml_ctx_next(xmlctx));
+
+ /* skip all children elements, recursively, if any */
+ while (parents_count < xmlctx->elements.count) {
+ LY_CHECK_RET(lyxml_ctx_next(xmlctx));
+ }
+
+ /* close element */
+ assert(xmlctx->status == LYXML_ELEM_CLOSE);
+ LY_CHECK_RET(lyxml_ctx_next(xmlctx));
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+lydxml_data_check_schema(struct lyd_xml_ctx *lydctx, const struct lysc_node **snode)
+{
+ LY_ERR ret = LY_SUCCESS;
+ enum LYXML_PARSER_STATUS prev_status;
+ const char *prev_input, *pname, *pprefix;
+ size_t pprefix_len, pname_len;
+ struct lyxml_ctx *xmlctx = lydctx->xmlctx;
+
+ if ((lydctx->options & LYD_OPT_NO_STATE) && ((*snode)->flags & LYS_CONFIG_R)) {
+ LOGVAL(xmlctx->ctx, LY_VLOG_LINE, &xmlctx->line, LY_VCODE_INSTATE, (*snode)->name);
+ return LY_EVALID;
+ }
+
+ if ((*snode)->nodetype & (LYS_RPC | LYS_ACTION)) {
+ if (lydctx->int_opts & LYD_INTOPT_RPC) {
+ if (lydctx->op_ntf) {
+ LOGVAL(xmlctx->ctx, LY_VLOG_LINE, &xmlctx->line, LYVE_DATA, "Unexpected %s element \"%s\", %s \"%s\" already parsed.",
+ lys_nodetype2str((*snode)->nodetype), (*snode)->name,
+ lys_nodetype2str(lydctx->op_ntf->schema->nodetype), lydctx->op_ntf->schema->name);
+ return LY_EVALID;
+ }
+ } else {
+ LOGVAL(xmlctx->ctx, LY_VLOG_LINE, &xmlctx->line, LYVE_DATA, "Unexpected %s element \"%s\".",
+ lys_nodetype2str((*snode)->nodetype), (*snode)->name);
+ return LY_EVALID;
+ }
+ } else if ((*snode)->nodetype == LYS_NOTIF) {
+ if (lydctx->int_opts & LYD_INTOPT_NOTIF) {
+ if (lydctx->op_ntf) {
+ LOGVAL(xmlctx->ctx, LY_VLOG_LINE, &xmlctx->line, LYVE_DATA, "Unexpected %s element \"%s\", %s \"%s\" already parsed.",
+ lys_nodetype2str((*snode)->nodetype), (*snode)->name,
+ lys_nodetype2str(lydctx->op_ntf->schema->nodetype), lydctx->op_ntf->schema->name);
+ return LY_EVALID;
+ }
+ } else {
+ LOGVAL(xmlctx->ctx, LY_VLOG_LINE, &xmlctx->line, LYVE_DATA, "Unexpected %s element \"%s\".",
+ lys_nodetype2str((*snode)->nodetype), (*snode)->name);
+ return LY_EVALID;
+ }
+ }
+
+ if ((lydctx->options & LYD_OPT_OPAQ) && ((*snode)->nodetype & (LYD_NODE_TERM | LYS_LIST))) {
+ /* backup parser */
+ prev_status = xmlctx->status;
+ pprefix = xmlctx->prefix;
+ pprefix_len = xmlctx->prefix_len;
+ pname = xmlctx->name;
+ pname_len = xmlctx->name_len;
+ prev_input = xmlctx->input;
+ if ((xmlctx->status == LYXML_ELEM_CONTENT) && xmlctx->dynamic) {
+ /* it was backed up, do not free */
+ xmlctx->dynamic = 0;
+ }
+
+ /* skip attributes */
+ while (xmlctx->status == LYXML_ATTRIBUTE) {
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), restore);
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), restore);
+ }
+
+ 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, xmlctx->value, xmlctx->value_len, lydxml_resolve_prefix, xmlctx, LYD_XML)) {
+ *snode = NULL;
+ }
+ } else {
+ /* skip content */
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), restore);
+
+ if (lydxml_check_list(xmlctx, *snode)) {
+ /* invalid list, parse as opaque if it missing/has invalid some keys */
+ *snode = NULL;
+ }
+ }
+
+restore:
+ /* restore parser */
+ if (xmlctx->dynamic) {
+ free((char *)xmlctx->value);
+ }
+ xmlctx->status = prev_status;
+ xmlctx->prefix = pprefix;
+ xmlctx->prefix_len = pprefix_len;
+ xmlctx->name = pname;
+ xmlctx->name_len = pname_len;
+ xmlctx->input = prev_input;
+ }
+
+ return ret;
+}
+
+static void
+lydxml_data_flags(struct lyd_xml_ctx *lydctx, struct lyd_node *node, struct lyd_meta **meta)
+{
+ struct lyd_meta *meta2, *prev_meta = NULL;
+
+ if (!(node->schema->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) && node->schema->when) {
+ if (lydctx->options & LYD_OPT_TRUSTED) {
+ /* just set it to true */
+ node->flags |= LYD_WHEN_TRUE;
+ } else {
+ /* remember we need to evaluate this node's when */
+ ly_set_add(&lydctx->when_check, node, LY_SET_OPT_USEASLIST);
+ }
+ }
+
+ if (lydctx->options & LYD_OPT_TRUSTED) {
+ /* node is valid */
+ node->flags &= ~LYD_NEW;
+ }
+
+ LY_LIST_FOR(*meta, meta2) {
+ if (!strcmp(meta2->name, "default") && !strcmp(meta2->annotation->module->name, "ietf-netconf-with-defaults")
+ && meta2->value.boolean) {
+ /* node is default according to the metadata */
+ node->flags |= LYD_DEFAULT;
+
+ /* delete the metadata */
+ if (prev_meta) {
+ prev_meta->next = meta2->next;
+ } else {
+ *meta = (*meta)->next;
+ }
+ lyd_free_meta(lydctx->xmlctx->ctx, meta2, 0);
+ break;
+ }
+
+ prev_meta = meta2;
+ }
+}
+
/**
* @brief Parse XML elements as YANG data node children the specified parent node.
*
* @param[in] lydctx XML YANG data parser context.
* @param[in] parent Parent node where the children are inserted. NULL in case of parsing top-level elements.
- * @param[in,out] data Pointer to the XML string representation of the YANG data to parse.
* @param[out] node Resulting list of the parsed nodes.
* @return LY_ERR value.
*/
static LY_ERR
-lydxml_data_r(struct lyd_xml_ctx *lydctx, struct lyd_node_inner *parent, const char **data, struct lyd_node **first)
+lydxml_data_r(struct lyd_xml_ctx *lydctx, struct lyd_node_inner *parent, struct lyd_node **first)
{
LY_ERR ret = LY_SUCCESS;
- enum LYXML_PARSER_STATUS prev_status;
- const char *prefix, *name, *prev_input, *pname, *pprefix;
- size_t prefix_len, name_len, pprefix_len, pname_len;
+ const char *prefix, *name;
+ size_t prefix_len, name_len;
struct lyxml_ctx *xmlctx;
const struct ly_ctx *ctx;
const struct lyxml_ns *ns;
- struct lyd_meta *meta = NULL, *meta2, *prev_meta;
+ struct lyd_meta *meta = NULL;
struct ly_attr *attr = NULL;
const struct lysc_node *snode;
struct lys_module *mod;
- uint32_t prev_opts, parents_count;
+ uint32_t prev_opts;
struct lyd_node *cur = NULL, *anchor;
struct ly_prefix *val_prefs;
@@ -331,6 +492,9 @@
goto cleanup;
}
+ /* parser next */
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
+
/* get the schema node */
snode = NULL;
if (mod && (!parent || parent->schema)) {
@@ -343,88 +507,14 @@
ret = LY_EVALID;
goto cleanup;
} else if (!(lydctx->options & LYD_OPT_OPAQ)) {
- /* remember current number of parents */
- parents_count = xmlctx->elements.count;
-
- /* skip after the content */
- while (xmlctx->status != LYXML_ELEM_CONTENT) {
- LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
- }
- LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
-
- /* skip all children elements, recursively, if any */
- while (parents_count < xmlctx->elements.count) {
- LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
- }
-
- /* close element */
- assert(xmlctx->status == LYXML_ELEM_CLOSE);
- LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
+ /* skip element with children */
+ LY_CHECK_GOTO(ret = lydxml_data_skip(xmlctx), cleanup);
continue;
}
- }
- if (snode) {
- if ((lydctx->options & LYD_OPT_NO_STATE) && (snode->flags & LYS_CONFIG_R)) {
- LOGVAL(ctx, LY_VLOG_LINE, &xmlctx->line, LY_VCODE_INSTATE, snode->name);
- ret = LY_EVALID;
- goto cleanup;
- }
- if (snode->nodetype & (LYS_ACTION | LYS_NOTIF)) {
- LOGVAL(ctx, LY_VLOG_LINE, &xmlctx->line, LYVE_DATA, "Unexpected %s element \"%.*s\".",
- snode->nodetype == LYS_ACTION ? "RPC/action" : "notification", name_len, name);
- ret = LY_EVALID;
- goto cleanup;
- }
- }
- }
-
- /* parser next */
- LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
-
- if (snode && (lydctx->options & LYD_OPT_OPAQ) && (snode->nodetype & (LYD_NODE_TERM | LYS_LIST))) {
- /* backup parser */
- prev_status = xmlctx->status;
- pprefix = xmlctx->prefix;
- pprefix_len = xmlctx->prefix_len;
- pname = xmlctx->name;
- pname_len = xmlctx->name_len;
- prev_input = xmlctx->input;
- if ((xmlctx->status == LYXML_ELEM_CONTENT) && xmlctx->dynamic) {
- /* it was backed up, do not free */
- xmlctx->dynamic = 0;
- }
-
- /* skip attributes */
- while (xmlctx->status == LYXML_ATTRIBUTE) {
- LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
- LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
- }
-
- 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, xmlctx->value, xmlctx->value_len, lydxml_resolve_prefix, xmlctx, LYD_XML)) {
- snode = NULL;
- }
} else {
- /* skip content */
- LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
-
- if (lydxml_check_list(xmlctx, snode)) {
- /* invalid list, parse as opaque if it does */
- snode = NULL;
- }
+ /* check that schema node is valid and can be used */
+ LY_CHECK_GOTO(ret = lydxml_data_check_schema(lydctx, &snode), cleanup);
}
-
- /* restore parser */
- if (xmlctx->dynamic) {
- free((char *)xmlctx->value);
- }
- xmlctx->status = prev_status;
- xmlctx->prefix = pprefix;
- xmlctx->prefix_len = pprefix_len;
- xmlctx->name = pname;
- xmlctx->name_len = pname_len;
- xmlctx->input = prev_input;
}
/* create metadata/attributes */
@@ -463,7 +553,7 @@
/* process children */
if (xmlctx->status == LYXML_ELEMENT) {
- ret = lydxml_data_r(lydctx, (struct lyd_node_inner *)cur, data, lyd_node_children_p(cur));
+ ret = lydxml_data_r(lydctx, (struct lyd_node_inner *)cur, lyd_node_children_p(cur));
LY_CHECK_GOTO(ret, cleanup);
}
} else if (snode->nodetype & LYD_NODE_TERM) {
@@ -498,16 +588,16 @@
/* no children expected */
if (xmlctx->status == LYXML_ELEMENT) {
- LOGVAL(ctx, LY_VLOG_LINE, &xmlctx->line, LYVE_SYNTAX, "Child element inside a terminal node \"%s\" found.",
- snode->name);
+ LOGVAL(ctx, LY_VLOG_LINE, &xmlctx->line, LYVE_SYNTAX, "Child element \"%.*s\" inside a terminal node \"%s\" found.",
+ xmlctx->name_len, xmlctx->name, snode->name);
ret = LY_EVALID;
goto cleanup;
}
} else if (snode->nodetype & LYD_NODE_INNER) {
if (!xmlctx->ws_only) {
/* value in inner node */
- LOGVAL(ctx, LY_VLOG_LINE, &xmlctx->line, LYVE_SYNTAX, "Text value inside an inner node \"%s\" found.",
- snode->name);
+ LOGVAL(ctx, LY_VLOG_LINE, &xmlctx->line, LYVE_SYNTAX, "Text value \"%.*s\" inside an inner node \"%s\" found.",
+ xmlctx->value_len, xmlctx->value, snode->name);
ret = LY_EVALID;
goto cleanup;
}
@@ -521,7 +611,7 @@
/* process children */
if (xmlctx->status == LYXML_ELEMENT) {
- ret = lydxml_data_r(lydctx, (struct lyd_node_inner *)cur, data, lyd_node_children_p(cur));
+ ret = lydxml_data_r(lydctx, (struct lyd_node_inner *)cur, lyd_node_children_p(cur));
LY_CHECK_GOTO(ret, cleanup);
}
@@ -541,15 +631,18 @@
LY_CHECK_GOTO(ret, cleanup);
}
- /* hash now that all keys should be parsed, rehash for key-less list */
if (snode->nodetype == LYS_LIST) {
+ /* hash now that all keys should be parsed, rehash for key-less list */
lyd_hash(cur);
+ } else if (snode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) {
+ /* rememeber the RPC/action/notification */
+ lydctx->op_ntf = cur;
}
} else if (snode->nodetype & LYD_NODE_ANY) {
if (!xmlctx->ws_only) {
/* value in inner node */
- LOGVAL(ctx, LY_VLOG_LINE, &xmlctx->line, LYVE_SYNTAX, "Text value inside an any node \"%s\" found.",
- snode->name);
+ LOGVAL(ctx, LY_VLOG_LINE, &xmlctx->line, LYVE_SYNTAX, "Text value \"%.*s\" inside an any node \"%s\" found.",
+ xmlctx->value_len, xmlctx->value, snode->name);
ret = LY_EVALID;
goto cleanup;
}
@@ -562,7 +655,7 @@
lydctx->options &= ~LYD_OPT_STRICT;
lydctx->options |= LYD_OPT_OPAQ;
anchor = NULL;
- ret = lydxml_data_r(lydctx, NULL, data, &anchor);
+ ret = lydxml_data_r(lydctx, NULL, &anchor);
lydctx->options = prev_opts;
LY_CHECK_GOTO(ret, cleanup);
@@ -570,41 +663,11 @@
ret = lyd_create_any(snode, anchor, LYD_ANYDATA_DATATREE, &cur);
LY_CHECK_GOTO(ret, cleanup);
}
+ assert(cur);
- /* correct flags */
+ /* add/correct flags */
if (snode) {
- if (!(snode->nodetype & (LYS_ACTION | LYS_NOTIF)) && snode->when) {
- if (lydctx->options & LYD_OPT_TRUSTED) {
- /* just set it to true */
- cur->flags |= LYD_WHEN_TRUE;
- } else {
- /* remember we need to evaluate this node's when */
- ly_set_add(&lydctx->when_check, cur, LY_SET_OPT_USEASLIST);
- }
- }
- if (lydctx->options & LYD_OPT_TRUSTED) {
- /* node is valid */
- cur->flags &= ~LYD_NEW;
- }
- prev_meta = NULL;
- LY_LIST_FOR(meta, meta2) {
- if (!strcmp(meta2->name, "default") && !strcmp(meta2->annotation->module->name, "ietf-netconf-with-defaults")
- && meta2->value.boolean) {
- /* node is default according to the metadata */
- cur->flags |= LYD_DEFAULT;
-
- /* delete the metadata */
- if (prev_meta) {
- prev_meta->next = meta2->next;
- } else {
- meta = meta->next;
- }
- lyd_free_meta(ctx, meta2, 0);
- break;
- }
-
- prev_meta = meta2;
- }
+ lydxml_data_flags(lydctx, cur, &meta);
}
/* add metadata/attributes */
@@ -641,7 +704,7 @@
}
LY_ERR
-lyd_parse_xml_data(struct ly_ctx *ctx, const char *data, int options, struct lyd_node **tree)
+lyd_parse_xml_data(const struct ly_ctx *ctx, const char *data, int options, struct lyd_node **tree)
{
LY_ERR ret = LY_SUCCESS;
struct lyd_xml_ctx lydctx = {0};
@@ -655,8 +718,7 @@
*tree = NULL;
/* parse XML data */
- ret = lydxml_data_r(&lydctx, NULL, &data, tree);
- LY_CHECK_GOTO(ret, cleanup);
+ LY_CHECK_GOTO(ret = lydxml_data_r(&lydctx, NULL, tree), cleanup);
if (!(options & LYD_OPT_PARSE_ONLY)) {
next = *tree;
@@ -677,8 +739,7 @@
}
/* validate new top-level nodes, autodelete CANNOT occur, all nodes are new */
- ret = lyd_validate_new(first2, NULL, mod);
- LY_CHECK_GOTO(ret, cleanup);
+ LY_CHECK_GOTO(ret = lyd_validate_new(first2, NULL, mod), cleanup);
/* add all top-level defaults for this module */
ret = lyd_validate_defaults_r(NULL, first2, NULL, mod, &lydctx.unres_node_type, &lydctx.when_check,
@@ -691,8 +752,7 @@
LY_CHECK_GOTO(ret, cleanup);
/* perform final validation that assumes the data tree is final */
- ret = lyd_validate_siblings_r(*first2, NULL, mod, options & LYD_VALOPT_MASK);
- LY_CHECK_GOTO(ret, cleanup);
+ LY_CHECK_GOTO(ret = lyd_validate_siblings_r(*first2, NULL, mod, options & LYD_VALOPT_MASK), cleanup);
}
}
@@ -711,3 +771,153 @@
}
return ret;
}
+
+static LY_ERR
+lydxml_envelope(struct lyxml_ctx *xmlctx, const char *name, const char *uri, struct lyd_node **envp)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const struct lyxml_ns *ns = NULL;
+ struct ly_attr *attr = NULL;
+ const char *prefix;
+ size_t prefix_len;
+
+ *envp = NULL;
+
+ assert(xmlctx->status == LYXML_ELEMENT);
+ if (ly_strncmp(name, xmlctx->name, xmlctx->name_len)) {
+ /* not the expected element */
+ return LY_SUCCESS;
+ }
+
+ prefix = xmlctx->prefix;
+ prefix_len = xmlctx->prefix_len;
+ ns = lyxml_ns_get(xmlctx, prefix, prefix_len);
+ if (!ns) {
+ LOGVAL(xmlctx->ctx, LY_VLOG_LINE, &xmlctx->line, LYVE_REFERENCE, "Unknown XML prefix \"%.*s\".",
+ prefix_len, prefix);
+ return LY_EVALID;
+ } else if (strcmp(ns->uri, uri)) {
+ /* different namespace */
+ return LY_SUCCESS;
+ }
+
+ LY_CHECK_RET(lyxml_ctx_next(xmlctx));
+
+ /* create attributes */
+ if (xmlctx->status == LYXML_ATTRIBUTE) {
+ LY_CHECK_RET(lydxml_attrs(xmlctx, &attr));
+ }
+
+ if (!xmlctx->ws_only) {
+ LOGVAL(xmlctx->ctx, LY_VLOG_LINE, &xmlctx->line, LYVE_SYNTAX, "Unexpected value \"%.*s\" in the \"%s\" element.",
+ xmlctx->value_len, xmlctx->value, name);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* parser next element */
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
+
+ /* create node */
+ ret = lyd_create_opaq(xmlctx->ctx, name, strlen(name), "", 0, NULL, LYD_XML, NULL, prefix, prefix_len, uri, envp);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* assign atributes */
+ ((struct lyd_node_opaq *)(*envp))->attr = attr;
+ attr = NULL;
+
+cleanup:
+ ly_free_attr(xmlctx->ctx, attr, 1);
+ return ret;
+}
+
+LY_ERR
+lyd_parse_xml_rpc(const struct ly_ctx *ctx, const char *data, struct lyd_node **tree, struct lyd_node **op)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_xml_ctx lydctx = {0};
+ struct lyd_node *rpc_e = NULL, *act_e = NULL;
+
+ /* init */
+ LY_CHECK_GOTO(ret = lyxml_ctx_new(ctx, data, &lydctx.xmlctx), cleanup);
+ lydctx.options = LYD_OPT_PARSE_ONLY | LYD_OPT_STRICT;
+ lydctx.int_opts = LYD_INTOPT_RPC;
+ *tree = NULL;
+ *op = NULL;
+
+ /* parse "rpc", if any */
+ LY_CHECK_GOTO(ret = lydxml_envelope(lydctx.xmlctx, "rpc", "urn:ietf:params:xml:ns:netconf:base:1.0", &rpc_e), cleanup);
+
+ if (rpc_e) {
+ /* parse "action", if any */
+ LY_CHECK_GOTO(ret = lydxml_envelope(lydctx.xmlctx, "action", "urn:ietf:params:xml:ns:yang:1", &act_e), cleanup);
+ }
+
+ /* parse the rest of data normally */
+ LY_CHECK_GOTO(ret = lydxml_data_r(&lydctx, NULL, tree), cleanup);
+
+ /* make sure we have parsed some operation */
+ if (!lydctx.op_ntf) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_DATA, "Missing the \"rpc\"/\"action\" node.");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* finish XML parsing and check operation type */
+ if (act_e) {
+ if (lydctx.xmlctx->status != LYXML_ELEM_CLOSE) {
+ assert(lydctx.xmlctx->status == LYXML_ELEMENT);
+ LOGVAL(ctx, LY_VLOG_LINE, &lydctx.xmlctx->line, LYVE_SYNTAX, "Unexpected sibling element \"%.*s\" of \"action\".",
+ lydctx.xmlctx->name_len, lydctx.xmlctx->name);
+ ret = LY_EVALID;
+ goto cleanup;
+ } else if (lydctx.op_ntf->schema->nodetype != LYS_ACTION) {
+ LOGVAL(ctx, LY_VLOG_LYD, lydctx.op_ntf, LYVE_DATA, "Unexpected %s element, an \"action\" expected.",
+ lys_nodetype2str(lydctx.op_ntf->schema->nodetype));
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(lydctx.xmlctx), cleanup);
+ }
+ if (rpc_e) {
+ if (lydctx.xmlctx->status != LYXML_ELEM_CLOSE) {
+ assert(lydctx.xmlctx->status == LYXML_ELEMENT);
+ LOGVAL(ctx, LY_VLOG_LINE, &lydctx.xmlctx->line, LYVE_SYNTAX, "Unexpected sibling element \"%.*s\" of \"rpc\".",
+ lydctx.xmlctx->name_len, lydctx.xmlctx->name);
+ ret = LY_EVALID;
+ goto cleanup;
+ } else if (!act_e && (lydctx.op_ntf->schema->nodetype != LYS_RPC)) {
+ LOGVAL(ctx, LY_VLOG_LYD, lydctx.op_ntf, LYVE_DATA, "Unexpected %s element, an \"rpc\" expected.",
+ lys_nodetype2str(lydctx.op_ntf->schema->nodetype));
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ LY_CHECK_GOTO(ret = lyxml_ctx_next(lydctx.xmlctx), cleanup);
+ }
+
+ *op = lydctx.op_ntf;
+ assert(*tree);
+ if (act_e) {
+ /* connect to the action */
+ lyd_insert_node(act_e, NULL, *tree);
+ *tree = act_e;
+ }
+ if (rpc_e) {
+ /* connect to the rpc */
+ lyd_insert_node(rpc_e, NULL, *tree);
+ *tree = rpc_e;
+ }
+
+cleanup:
+ /* we have used parse_only flag */
+ assert(!lydctx.unres_node_type.count && !lydctx.unres_meta_type.count && !lydctx.when_check.count);
+ lyxml_ctx_free(lydctx.xmlctx);
+ if (ret) {
+ lyd_free_all(*tree);
+ lyd_free_tree(act_e);
+ lyd_free_tree(rpc_e);
+ *tree = NULL;
+ *op = NULL;
+ }
+ return ret;
+}