blob: a0cf1f552a41327c155a21a485962e36ab83c0b2 [file] [log] [blame]
Radek Krejcie7b95092019-05-15 11:03:07 +02001/**
2 * @file parser_xml.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
Michal Vasko8cc3f662022-03-29 11:25:51 +02004 * @author Michal Vasko <mvasko@cesnet.cz>
Radek Krejcie7b95092019-05-15 11:03:07 +02005 * @brief XML data parser for libyang
6 *
Michal Vasko8cc3f662022-03-29 11:25:51 +02007 * Copyright (c) 2019 - 2022 CESNET, z.s.p.o.
Radek Krejcie7b95092019-05-15 11:03:07 +02008 *
9 * This source code is licensed under BSD 3-Clause License (the "License").
10 * You may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * https://opensource.org/licenses/BSD-3-Clause
14 */
15
Michal Vasko742a5b12022-02-24 16:07:27 +010016#define _GNU_SOURCE
17
Michal Vasko69730152020-10-09 16:30:07 +020018#include <assert.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020019#include <stdint.h>
20#include <stdlib.h>
21#include <string.h>
22
Michal Vasko742a5b12022-02-24 16:07:27 +010023#include "compat.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020024#include "context.h"
Radek Krejci77114102021-03-10 15:21:57 +010025#include "dict.h"
Michal Vaskoafac7822020-10-20 14:22:26 +020026#include "in_internal.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020027#include "log.h"
Michal Vasko8f702ee2024-02-20 15:44:24 +010028#include "ly_common.h"
Radek Krejci7931b192020-06-25 17:05:03 +020029#include "parser_data.h"
30#include "parser_internal.h"
tadeas-vintrlik2aa36b42021-11-03 13:07:34 +010031#include "plugins_exts.h"
Michal Vasko51de7b72022-04-29 09:50:22 +020032#include "plugins_internal.h"
33#include "schema_compile_node.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020034#include "set.h"
Radek Krejci77114102021-03-10 15:21:57 +010035#include "tree.h"
Radek Krejci47fab892020-11-05 17:02:41 +010036#include "tree_data.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020037#include "tree_data_internal.h"
38#include "tree_schema.h"
Radek Krejci77114102021-03-10 15:21:57 +010039#include "tree_schema_internal.h"
Michal Vaskocde73ac2019-11-14 16:10:27 +010040#include "validation.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020041#include "xml.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020042
aPiecek18afb712024-06-10 14:47:10 +020043#define LYDXML_LOG_NAMESPACE_ERR(XMLCTX, PREFIX, PREFIX_LEN) \
44 if (PREFIX_LEN) { \
45 LOGVAL(XMLCTX->ctx, LYVE_REFERENCE, "Unknown XML prefix \"%.*s\".", (int)PREFIX_LEN, PREFIX); \
46 } else { \
47 LOGVAL(XMLCTX->ctx, LYVE_REFERENCE, "Missing XML namespace."); \
48 }
49
Michal Vaskod027f382023-02-10 09:13:25 +010050static LY_ERR lydxml_subtree_r(struct lyd_xml_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p,
51 struct ly_set *parsed);
52
Radek Krejci1798aae2020-07-14 13:26:06 +020053void
54lyd_xml_ctx_free(struct lyd_ctx *lydctx)
55{
56 struct lyd_xml_ctx *ctx = (struct lyd_xml_ctx *)lydctx;
57
58 lyd_ctx_free(lydctx);
59 lyxml_ctx_free(ctx->xmlctx);
60 free(ctx);
61}
62
Michal Vasko45791ad2021-06-17 08:45:03 +020063/**
64 * @brief Parse and create XML metadata.
65 *
66 * @param[in] lydctx XML data parser context.
Michal Vaskoddd76592022-01-17 13:34:48 +010067 * @param[in] sparent Schema node of the parent.
Michal Vasko45791ad2021-06-17 08:45:03 +020068 * @param[out] meta List of created metadata instances.
69 * @return LY_ERR value.
70 */
Radek Krejcie7b95092019-05-15 11:03:07 +020071static LY_ERR
Michal Vaskoddd76592022-01-17 13:34:48 +010072lydxml_metadata(struct lyd_xml_ctx *lydctx, const struct lysc_node *sparent, struct lyd_meta **meta)
Radek Krejcie7b95092019-05-15 11:03:07 +020073{
aPiecek1c4da362021-04-29 14:26:34 +020074 LY_ERR ret = LY_SUCCESS;
Radek Krejci28681fa2019-09-06 13:08:45 +020075 const struct lyxml_ns *ns;
76 struct lys_module *mod;
Michal Vaskob36053d2020-03-26 15:49:30 +010077 const char *name;
78 size_t name_len;
Michal Vasko45791ad2021-06-17 08:45:03 +020079 LY_ARRAY_COUNT_TYPE u;
80 ly_bool filter_attrs = 0;
Radek Krejci1798aae2020-07-14 13:26:06 +020081 struct lyxml_ctx *xmlctx = lydctx->xmlctx;
Radek Krejci28681fa2019-09-06 13:08:45 +020082
Michal Vaskob36053d2020-03-26 15:49:30 +010083 *meta = NULL;
Radek Krejci28681fa2019-09-06 13:08:45 +020084
Michal Vasko7a266772024-01-23 11:02:38 +010085 LOG_LOCSET(sparent, NULL);
Michal Vasko85be65e2023-06-13 09:44:17 +020086
Michal Vasko45791ad2021-06-17 08:45:03 +020087 /* check for NETCONF filter unqualified attributes */
Michal Vasko1b2a3f42022-12-20 09:38:28 +010088 if (!strcmp(sparent->module->name, "notifications")) {
89 /* ancient module that does not even use the extension */
90 filter_attrs = 1;
91 } else {
92 LY_ARRAY_FOR(sparent->exts, u) {
93 if (!strcmp(sparent->exts[u].def->name, "get-filter-element-attributes") &&
94 !strcmp(sparent->exts[u].def->module->name, "ietf-netconf")) {
95 filter_attrs = 1;
96 break;
97 }
Michal Vasko45791ad2021-06-17 08:45:03 +020098 }
99 }
100
Michal Vaskob36053d2020-03-26 15:49:30 +0100101 while (xmlctx->status == LYXML_ATTRIBUTE) {
102 if (!xmlctx->prefix_len) {
Michal Vasko45791ad2021-06-17 08:45:03 +0200103 /* in XML all attributes must be prefixed except NETCONF filter ones marked by an extension */
104 if (filter_attrs && (!ly_strncmp("type", xmlctx->name, xmlctx->name_len) ||
105 !ly_strncmp("select", xmlctx->name, xmlctx->name_len))) {
106 mod = ly_ctx_get_module_implemented(xmlctx->ctx, "ietf-netconf");
107 if (!mod) {
108 LOGVAL(xmlctx->ctx, LYVE_REFERENCE,
109 "Missing (or not implemented) YANG module \"ietf-netconf\" for special filter attributes.");
110 ret = LY_ENOTFOUND;
111 goto cleanup;
112 }
113 goto create_meta;
114 }
115
Michal Vaskoe0665742021-02-11 11:08:44 +0100116 if (lydctx->parse_opts & LYD_PARSE_STRICT) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100117 LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Missing mandatory prefix for XML metadata \"%.*s\".",
Radek Krejci422afb12021-03-04 16:38:16 +0100118 (int)xmlctx->name_len, xmlctx->name);
Michal Vasko45791ad2021-06-17 08:45:03 +0200119 ret = LY_EVALID;
Michal Vaskob36053d2020-03-26 15:49:30 +0100120 goto cleanup;
Radek Krejci28681fa2019-09-06 13:08:45 +0200121 }
Michal Vaskob36053d2020-03-26 15:49:30 +0100122
Michal Vasko45791ad2021-06-17 08:45:03 +0200123 /* skip attr */
Michal Vaskob36053d2020-03-26 15:49:30 +0100124 LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
125 assert(xmlctx->status == LYXML_ATTR_CONTENT);
126 LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
Radek Krejci28681fa2019-09-06 13:08:45 +0200127 continue;
128 }
129
130 /* get namespace of the attribute to find its annotation definition */
Michal Vaskoc8a230d2020-08-14 12:17:10 +0200131 ns = lyxml_ns_get(&xmlctx->ns, xmlctx->prefix, xmlctx->prefix_len);
Radek Krejci28681fa2019-09-06 13:08:45 +0200132 if (!ns) {
aPiecek18afb712024-06-10 14:47:10 +0200133 LYDXML_LOG_NAMESPACE_ERR(xmlctx, xmlctx->prefix, xmlctx->prefix_len);
Michal Vasko45791ad2021-06-17 08:45:03 +0200134 ret = LY_ENOTFOUND;
Radek Krejci28681fa2019-09-06 13:08:45 +0200135 goto cleanup;
136 }
Michal Vasko45791ad2021-06-17 08:45:03 +0200137
138 /* get the module with metadata definition */
Michal Vasko52927e22020-03-16 17:26:14 +0100139 mod = ly_ctx_get_module_implemented_ns(xmlctx->ctx, ns->uri);
Radek Krejci28681fa2019-09-06 13:08:45 +0200140 if (!mod) {
Michal Vaskoe0665742021-02-11 11:08:44 +0100141 if (lydctx->parse_opts & LYD_PARSE_STRICT) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100142 LOGVAL(xmlctx->ctx, LYVE_REFERENCE,
Michal Vasko69730152020-10-09 16:30:07 +0200143 "Unknown (or not implemented) YANG module with namespace \"%s\" for metadata \"%.*s%s%.*s\".",
Radek Krejci422afb12021-03-04 16:38:16 +0100144 ns->uri, (int)xmlctx->prefix_len, xmlctx->prefix, xmlctx->prefix_len ? ":" : "",
145 (int)xmlctx->name_len, xmlctx->name);
Michal Vasko45791ad2021-06-17 08:45:03 +0200146 ret = LY_ENOTFOUND;
Michal Vaskob36053d2020-03-26 15:49:30 +0100147 goto cleanup;
Radek Krejci28681fa2019-09-06 13:08:45 +0200148 }
Michal Vasko45791ad2021-06-17 08:45:03 +0200149
150 /* skip attr */
151 LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
152 assert(xmlctx->status == LYXML_ATTR_CONTENT);
153 LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
154 continue;
Radek Krejci28681fa2019-09-06 13:08:45 +0200155 }
156
Michal Vasko45791ad2021-06-17 08:45:03 +0200157create_meta:
Michal Vasko60ea6352020-06-29 13:39:39 +0200158 /* remember meta name and get its content */
Michal Vaskob36053d2020-03-26 15:49:30 +0100159 name = xmlctx->name;
160 name_len = xmlctx->name_len;
161 LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
162 assert(xmlctx->status == LYXML_ATTR_CONTENT);
163
164 /* create metadata */
Michal Vaskoc8a230d2020-08-14 12:17:10 +0200165 ret = lyd_parser_create_meta((struct lyd_ctx *)lydctx, NULL, meta, mod, name, name_len, xmlctx->value,
Michal Vaskoddd76592022-01-17 13:34:48 +0100166 xmlctx->value_len, &xmlctx->dynamic, LY_VALUE_XML, &xmlctx->ns, LYD_HINT_DATA, sparent);
Radek Krejci1798aae2020-07-14 13:26:06 +0200167 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskob36053d2020-03-26 15:49:30 +0100168
169 /* next attribute */
170 LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
Radek Krejcie7b95092019-05-15 11:03:07 +0200171 }
Michal Vasko52927e22020-03-16 17:26:14 +0100172
Radek Krejcie7b95092019-05-15 11:03:07 +0200173cleanup:
Michal Vasko7a266772024-01-23 11:02:38 +0100174 LOG_LOCBACK(1, 0);
Michal Vaskob36053d2020-03-26 15:49:30 +0100175 if (ret) {
Michal Vasko3a41dff2020-07-15 14:30:28 +0200176 lyd_free_meta_siblings(*meta);
Michal Vaskob36053d2020-03-26 15:49:30 +0100177 *meta = NULL;
Radek Krejci38d85362019-09-05 16:26:38 +0200178 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200179 return ret;
180}
181
Michal Vasko52927e22020-03-16 17:26:14 +0100182static LY_ERR
Radek Krejci1798aae2020-07-14 13:26:06 +0200183lydxml_attrs(struct lyxml_ctx *xmlctx, struct lyd_attr **attr)
Michal Vasko52927e22020-03-16 17:26:14 +0100184{
185 LY_ERR ret = LY_SUCCESS;
186 const struct lyxml_ns *ns;
Michal Vasko6b5cb2a2020-11-11 19:11:21 +0100187 void *val_prefix_data;
Radek Krejci8df109d2021-04-23 12:19:08 +0200188 LY_VALUE_FORMAT format;
Radek Krejci1798aae2020-07-14 13:26:06 +0200189 struct lyd_attr *attr2;
Michal Vaskob36053d2020-03-26 15:49:30 +0100190 const char *name, *prefix;
191 size_t name_len, prefix_len;
Michal Vasko52927e22020-03-16 17:26:14 +0100192
193 assert(attr);
Michal Vaskob36053d2020-03-26 15:49:30 +0100194 *attr = NULL;
Michal Vasko52927e22020-03-16 17:26:14 +0100195
Michal Vaskob36053d2020-03-26 15:49:30 +0100196 while (xmlctx->status == LYXML_ATTRIBUTE) {
Michal Vasko52927e22020-03-16 17:26:14 +0100197 if (*attr) {
198 attr2 = *attr;
199 } else {
200 attr2 = NULL;
201 }
202
Michal Vaskob36053d2020-03-26 15:49:30 +0100203 /* remember attr prefix, name, and get its content */
204 prefix = xmlctx->prefix;
205 prefix_len = xmlctx->prefix_len;
206 name = xmlctx->name;
207 name_len = xmlctx->name_len;
208 LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
209 assert(xmlctx->status == LYXML_ATTR_CONTENT);
210
Michal Vaskoe137fc42021-07-22 11:53:13 +0200211 /* handle special "xml" attribute prefix */
212 if ((prefix_len == 3) && !strncmp(prefix, "xml", 3)) {
213 name = prefix;
214 name_len += 1 + prefix_len;
215 prefix = NULL;
216 prefix_len = 0;
217 }
218
219 /* find namespace of the attribute, if any */
220 ns = NULL;
221 if (prefix_len) {
222 ns = lyxml_ns_get(&xmlctx->ns, prefix, prefix_len);
223 if (!ns) {
aPiecek18afb712024-06-10 14:47:10 +0200224 LYDXML_LOG_NAMESPACE_ERR(xmlctx, prefix, prefix_len);
Michal Vaskoe137fc42021-07-22 11:53:13 +0200225 ret = LY_EVALID;
226 goto cleanup;
227 }
228 }
229
Michal Vasko52927e22020-03-16 17:26:14 +0100230 /* get value prefixes */
Michal Vaskofc2cd072021-02-24 13:17:17 +0100231 val_prefix_data = NULL;
Radek Krejci8df109d2021-04-23 12:19:08 +0200232 LY_CHECK_GOTO(ret = ly_store_prefix_data(xmlctx->ctx, xmlctx->value, xmlctx->value_len, LY_VALUE_XML,
Michal Vasko6b5cb2a2020-11-11 19:11:21 +0100233 &xmlctx->ns, &format, &val_prefix_data), cleanup);
Michal Vasko52927e22020-03-16 17:26:14 +0100234
235 /* attr2 is always changed to the created attribute */
Michal Vasko501af032020-11-11 20:27:44 +0100236 ret = lyd_create_attr(NULL, &attr2, xmlctx->ctx, name, name_len, prefix, prefix_len, ns ? ns->uri : NULL,
Michal Vaskoe5e49e92022-02-01 13:15:08 +0100237 ns ? strlen(ns->uri) : 0, xmlctx->value, xmlctx->value_len, &xmlctx->dynamic, format, val_prefix_data,
238 LYD_HINT_DATA);
Michal Vasko52927e22020-03-16 17:26:14 +0100239 LY_CHECK_GOTO(ret, cleanup);
240
241 if (!*attr) {
242 *attr = attr2;
243 }
Michal Vaskob36053d2020-03-26 15:49:30 +0100244
245 /* next attribute */
246 LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
Michal Vasko52927e22020-03-16 17:26:14 +0100247 }
248
249cleanup:
Michal Vaskob36053d2020-03-26 15:49:30 +0100250 if (ret) {
Radek Krejci011e4aa2020-09-04 15:22:31 +0200251 lyd_free_attr_siblings(xmlctx->ctx, *attr);
Michal Vaskob36053d2020-03-26 15:49:30 +0100252 *attr = NULL;
Michal Vasko52927e22020-03-16 17:26:14 +0100253 }
Michal Vasko52927e22020-03-16 17:26:14 +0100254 return ret;
255}
256
Michal Vasko44685da2020-03-17 15:38:06 +0100257static LY_ERR
Michal Vaskob36053d2020-03-26 15:49:30 +0100258lydxml_check_list(struct lyxml_ctx *xmlctx, const struct lysc_node *list)
Michal Vasko44685da2020-03-17 15:38:06 +0100259{
Michal Vaskob36053d2020-03-26 15:49:30 +0100260 LY_ERR ret = LY_SUCCESS, r;
261 enum LYXML_PARSER_STATUS next;
Michal Vasko44685da2020-03-17 15:38:06 +0100262 struct ly_set key_set = {0};
263 const struct lysc_node *snode;
Michal Vaskob36053d2020-03-26 15:49:30 +0100264 uint32_t i, parents_count;
Michal Vasko44685da2020-03-17 15:38:06 +0100265
266 assert(list && (list->nodetype == LYS_LIST));
267
268 /* get all keys into a set (keys do not have if-features or anything) */
269 snode = NULL;
Michal Vasko7b1ad1a2020-11-02 15:41:27 +0100270 while ((snode = lys_getnext(snode, list, NULL, 0)) && (snode->flags & LYS_KEY)) {
Radek Krejci3d92e442020-10-12 12:48:13 +0200271 ret = ly_set_add(&key_set, (void *)snode, 1, NULL);
Radek Krejciba03a5a2020-08-27 14:40:41 +0200272 LY_CHECK_GOTO(ret, cleanup);
Michal Vasko44685da2020-03-17 15:38:06 +0100273 }
274
Michal Vasko12d809c2021-03-03 16:34:32 +0100275 /* remember parent count */
276 parents_count = xmlctx->elements.count;
277
Michal Vaskob36053d2020-03-26 15:49:30 +0100278 while (xmlctx->status == LYXML_ELEMENT) {
Michal Vasko44685da2020-03-17 15:38:06 +0100279 /* find key definition */
280 for (i = 0; i < key_set.count; ++i) {
281 snode = (const struct lysc_node *)key_set.objs[i];
Michal Vaskob36053d2020-03-26 15:49:30 +0100282 if (!ly_strncmp(snode->name, xmlctx->name, xmlctx->name_len)) {
Michal Vasko44685da2020-03-17 15:38:06 +0100283 break;
284 }
285 }
Michal Vaskob36053d2020-03-26 15:49:30 +0100286 LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
Michal Vasko44685da2020-03-17 15:38:06 +0100287
288 /* skip attributes */
289 while (xmlctx->status == LYXML_ATTRIBUTE) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100290 LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
291 assert(xmlctx->status == LYXML_ATTR_CONTENT);
292 LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
Michal Vasko44685da2020-03-17 15:38:06 +0100293 }
294
Michal Vaskob36053d2020-03-26 15:49:30 +0100295 assert(xmlctx->status == LYXML_ELEM_CONTENT);
296 if (i < key_set.count) {
297 /* validate the value */
Michal Vasko583b4642023-05-25 10:39:34 +0200298 r = ly_value_validate(NULL, snode, xmlctx->value, xmlctx->value_len, LY_VALUE_XML, &xmlctx->ns, LYD_HINT_DATA);
Michal Vaskob36053d2020-03-26 15:49:30 +0100299 if (!r) {
300 /* key with a valid value, remove from the set */
301 ly_set_rm_index(&key_set, i, NULL);
Michal Vasko44685da2020-03-17 15:38:06 +0100302 }
303 }
304
Michal Vaskob36053d2020-03-26 15:49:30 +0100305 /* parser next */
306 LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
Michal Vasko44685da2020-03-17 15:38:06 +0100307
Michal Vaskob36053d2020-03-26 15:49:30 +0100308 /* skip any children, resursively */
Michal Vasko12d809c2021-03-03 16:34:32 +0100309 while (xmlctx->status == LYXML_ELEMENT) {
310 while (parents_count < xmlctx->elements.count) {
311 LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
312 }
313 assert(xmlctx->status == LYXML_ELEM_CLOSE);
Michal Vaskob36053d2020-03-26 15:49:30 +0100314 LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
315 }
316
317 /* parser next, but do not parse closing element of the list because it would remove its namespaces */
318 assert(xmlctx->status == LYXML_ELEM_CLOSE);
319 LY_CHECK_GOTO(ret = lyxml_ctx_peek(xmlctx, &next), cleanup);
320 if (next != LYXML_ELEM_CLOSE) {
321 LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
322 }
Michal Vasko44685da2020-03-17 15:38:06 +0100323 }
324
325 if (key_set.count) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100326 /* some keys are missing/did not validate */
Michal Vasko44685da2020-03-17 15:38:06 +0100327 ret = LY_ENOT;
Michal Vasko44685da2020-03-17 15:38:06 +0100328 }
329
330cleanup:
331 ly_set_erase(&key_set, NULL);
332 return ret;
333}
334
Michal Vasko5c24ed12021-06-09 09:27:32 +0200335/**
336 * @brief Skip an element with all its descendants.
337 *
338 * @param[in] xmlctx XML parser context.
339 * @return LY_ERR value.
340 */
Michal Vasko1bf09392020-03-27 12:38:10 +0100341static LY_ERR
342lydxml_data_skip(struct lyxml_ctx *xmlctx)
343{
344 uint32_t parents_count;
345
346 /* remember current number of parents */
347 parents_count = xmlctx->elements.count;
aPiecek9cdb9e62021-05-18 09:46:20 +0200348 assert(parents_count);
Michal Vasko1bf09392020-03-27 12:38:10 +0100349
350 /* skip after the content */
351 while (xmlctx->status != LYXML_ELEM_CONTENT) {
352 LY_CHECK_RET(lyxml_ctx_next(xmlctx));
353 }
354 LY_CHECK_RET(lyxml_ctx_next(xmlctx));
355
356 /* skip all children elements, recursively, if any */
aPiecek9cdb9e62021-05-18 09:46:20 +0200357 while (parents_count <= xmlctx->elements.count) {
Michal Vasko1bf09392020-03-27 12:38:10 +0100358 LY_CHECK_RET(lyxml_ctx_next(xmlctx));
359 }
360
361 /* close element */
362 assert(xmlctx->status == LYXML_ELEM_CLOSE);
363 LY_CHECK_RET(lyxml_ctx_next(xmlctx));
364
365 return LY_SUCCESS;
366}
367
Michal Vasko5c24ed12021-06-09 09:27:32 +0200368/**
369 * @brief Check that the current element can be parsed as a data node.
370 *
371 * @param[in] lydctx XML data parser context.
372 * @param[in,out] snode Found schema node, set to NULL if data node cannot be created.
373 * @return LY_ERR value.
374 */
Michal Vasko1bf09392020-03-27 12:38:10 +0100375static LY_ERR
Radek Krejci1798aae2020-07-14 13:26:06 +0200376lydxml_data_check_opaq(struct lyd_xml_ctx *lydctx, const struct lysc_node **snode)
Michal Vasko1bf09392020-03-27 12:38:10 +0100377{
378 LY_ERR ret = LY_SUCCESS;
Michal Vaskoda8fbbf2021-06-16 11:44:44 +0200379 struct lyxml_ctx *xmlctx = lydctx->xmlctx, pxmlctx;
Michal Vasko1bf09392020-03-27 12:38:10 +0100380
Radek IÅ¡a3930fd72021-03-08 10:48:40 +0100381 if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
382 /* only checks specific to opaque nodes */
383 return LY_SUCCESS;
384 }
385
Michal Vasko13854662021-06-09 09:27:50 +0200386 if (!((*snode)->nodetype & (LYD_NODE_TERM | LYD_NODE_INNER))) {
387 /* nothing to check */
388 return LY_SUCCESS;
389 }
Michal Vasko1bf09392020-03-27 12:38:10 +0100390
Michal Vaskoda8fbbf2021-06-16 11:44:44 +0200391 assert(xmlctx->elements.count);
392
Michal Vasko13854662021-06-09 09:27:50 +0200393 /* backup parser */
Michal Vaskoda8fbbf2021-06-16 11:44:44 +0200394 LY_CHECK_RET(lyxml_ctx_backup(xmlctx, &pxmlctx));
Michal Vasko1bf09392020-03-27 12:38:10 +0100395
Michal Vasko13854662021-06-09 09:27:50 +0200396 /* skip attributes */
397 while (xmlctx->status == LYXML_ATTRIBUTE) {
398 LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), restore);
399 LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), restore);
400 }
Michal Vasko1bf09392020-03-27 12:38:10 +0100401
Michal Vasko13854662021-06-09 09:27:50 +0200402 if ((*snode)->nodetype & LYD_NODE_TERM) {
403 /* value may not be valid in which case we parse it as an opaque node */
Michal Vasko583b4642023-05-25 10:39:34 +0200404 if (ly_value_validate(NULL, *snode, xmlctx->value, xmlctx->value_len, LY_VALUE_XML, &xmlctx->ns, LYD_HINT_DATA)) {
Michal Vasko7b3a00e2023-08-09 11:58:03 +0200405 LOGVRB("Parsing opaque term node \"%s\" with invalid value \"%.*s\".", (*snode)->name, (int)xmlctx->value_len,
Michal Vaskod0237d42021-07-12 14:49:46 +0200406 xmlctx->value);
Michal Vasko13854662021-06-09 09:27:50 +0200407 *snode = NULL;
Michal Vasko1bf09392020-03-27 12:38:10 +0100408 }
Michal Vasko13854662021-06-09 09:27:50 +0200409 } else if ((*snode)->nodetype == LYS_LIST) {
410 /* skip content */
411 LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), restore);
Michal Vasko1bf09392020-03-27 12:38:10 +0100412
Michal Vasko13854662021-06-09 09:27:50 +0200413 if (lydxml_check_list(xmlctx, *snode)) {
414 /* invalid list, parse as opaque if it missing/has invalid some keys */
Michal Vaskod0237d42021-07-12 14:49:46 +0200415 LOGVRB("Parsing opaque list node \"%s\" with missing/invalid keys.", (*snode)->name);
Michal Vasko13854662021-06-09 09:27:50 +0200416 *snode = NULL;
Michal Vasko1bf09392020-03-27 12:38:10 +0100417 }
Michal Vasko13854662021-06-09 09:27:50 +0200418 } else {
Radek IÅ¡a3930fd72021-03-08 10:48:40 +0100419 /* if there is a non-WS value, it cannot be parsed as an inner node */
420 assert(xmlctx->status == LYXML_ELEM_CONTENT);
421 if (!xmlctx->ws_only) {
422 *snode = NULL;
423 }
Michal Vasko1bf09392020-03-27 12:38:10 +0100424 }
425
Michal Vasko13854662021-06-09 09:27:50 +0200426restore:
427 /* restore parser */
Michal Vaskoda8fbbf2021-06-16 11:44:44 +0200428 lyxml_ctx_restore(xmlctx, &pxmlctx);
Michal Vasko1bf09392020-03-27 12:38:10 +0100429 return ret;
430}
431
Radek Krejcie7b95092019-05-15 11:03:07 +0200432/**
Michal Vaskocea58712022-04-01 14:37:08 +0200433 * @brief Get sensible data hints for an opaque node.
434 *
435 * @param[in] name Node name.
436 * @param[in] name_len Length of @p name.
437 * @param[in] value Node value.
438 * @param[in] value_len Length of @p value.
439 * @param[in] first Node first sibling.
Michal Vaskoce2e07c2022-12-01 11:08:52 +0100440 * @param[in] ns Node module namespace, NULL for no namespace.
Michal Vaskocea58712022-04-01 14:37:08 +0200441 * @param[out] hints Data hints to use.
442 * @param[out] anchor Anchor to insert after in case of a list.
443 */
444static void
Michal Vaskod027f382023-02-10 09:13:25 +0100445lydxml_get_hints_opaq(const char *name, size_t name_len, const char *value, size_t value_len, const struct lyd_node *first,
Michal Vaskocea58712022-04-01 14:37:08 +0200446 const char *ns, uint32_t *hints, struct lyd_node **anchor)
447{
448 struct lyd_node_opaq *opaq;
449 char *ptr;
Jan Kundrát863b18b2024-07-06 20:55:23 +0200450 /* this needs to be at least 64bit, and it "should not" be an explicit int64_t
451 * because the code calls strtoll later on, which "might" return a bigger type */
452 long long num;
Michal Vaskocea58712022-04-01 14:37:08 +0200453
454 *hints = 0;
455 *anchor = NULL;
456
457 if (!value_len) {
Michal Vasko6235e152023-08-07 13:39:13 +0200458 /* no value but it may also be zero-length string */
459 *hints |= LYD_VALHINT_EMPTY | LYD_VALHINT_STRING;
Michal Vaskocea58712022-04-01 14:37:08 +0200460 } else if (!strncmp(value, "true", value_len) || !strncmp(value, "false", value_len)) {
461 /* boolean value */
462 *hints |= LYD_VALHINT_BOOLEAN;
463 } else {
Jan Kundrát863b18b2024-07-06 20:55:23 +0200464 num = strtoll(value, &ptr, 10);
Michal Vaskocea58712022-04-01 14:37:08 +0200465 if ((unsigned)(ptr - value) == value_len) {
466 /* number value */
467 *hints |= LYD_VALHINT_DECNUM;
Jan Kundrát863b18b2024-07-06 20:55:23 +0200468 if ((num < INT32_MIN) || (num > UINT32_MAX)) {
Michal Vaskocea58712022-04-01 14:37:08 +0200469 /* large number */
470 *hints |= LYD_VALHINT_NUM64;
471 }
472 } else {
473 /* string value */
474 *hints |= LYD_VALHINT_STRING;
475 }
476 }
477
478 if (!first) {
479 return;
480 }
481
482 /* search backwards to find the last instance */
483 do {
484 first = first->prev;
485 if (first->schema) {
486 continue;
487 }
488
489 opaq = (struct lyd_node_opaq *)first;
490 assert(opaq->format == LY_VALUE_XML);
Michal Vaskoce2e07c2022-12-01 11:08:52 +0100491 if (!ly_strncmp(opaq->name.name, name, name_len) &&
zhangtaogb348a8b2024-02-21 15:43:30 +0800492 ((ns && opaq->name.module_ns && !strcmp(opaq->name.module_ns, ns)) || (!ns && !opaq->name.module_ns))) {
Michal Vaskocea58712022-04-01 14:37:08 +0200493 if (opaq->value && opaq->value[0]) {
494 /* leaf-list nodes */
495 opaq->hints |= LYD_NODEHINT_LEAFLIST;
496 *hints |= LYD_NODEHINT_LEAFLIST;
497 } else {
498 /* list nodes */
499 opaq->hints |= LYD_NODEHINT_LIST;
500 *hints |= LYD_NODEHINT_LIST;
501 }
Michal Vaskod027f382023-02-10 09:13:25 +0100502 *anchor = (struct lyd_node *)first;
Michal Vaskocea58712022-04-01 14:37:08 +0200503 break;
504 }
505 } while (first->prev->next);
506}
507
508/**
Michal Vasko8cc3f662022-03-29 11:25:51 +0200509 * @brief Get schema node for the current element.
Michal Vaskoddd76592022-01-17 13:34:48 +0100510 *
511 * @param[in] lydctx XML data parser context.
Michal Vasko8cc3f662022-03-29 11:25:51 +0200512 * @param[in] parent Parsed parent data node, if any.
513 * @param[in] prefix Element prefix, if any.
514 * @param[in] prefix_len Length of @p prefix.
515 * @param[in] name Element name.
516 * @param[in] name_len Length of @p name.
517 * @param[out] snode Found schema node, NULL if no suitable was found.
518 * @param[out] ext Extension instance that provided @p snode, if any.
Michal Vaskoddd76592022-01-17 13:34:48 +0100519 * @return LY_SUCCESS on success;
Michal Vaskoddd76592022-01-17 13:34:48 +0100520 * @return LY_ERR on error.
521 */
522static LY_ERR
Michal Vaskod027f382023-02-10 09:13:25 +0100523lydxml_subtree_get_snode(struct lyd_xml_ctx *lydctx, const struct lyd_node *parent, const char *prefix, size_t prefix_len,
Michal Vasko8cc3f662022-03-29 11:25:51 +0200524 const char *name, size_t name_len, const struct lysc_node **snode, struct lysc_ext_instance **ext)
Michal Vaskoddd76592022-01-17 13:34:48 +0100525{
526 LY_ERR r;
Michal Vaskob36053d2020-03-26 15:49:30 +0100527 struct lyxml_ctx *xmlctx;
528 const struct ly_ctx *ctx;
Radek Krejcie7b95092019-05-15 11:03:07 +0200529 const struct lyxml_ns *ns;
Radek Krejcie7b95092019-05-15 11:03:07 +0200530 struct lys_module *mod;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200531 uint32_t getnext_opts;
Michal Vaskoe0665742021-02-11 11:08:44 +0100532
Michal Vaskob36053d2020-03-26 15:49:30 +0100533 xmlctx = lydctx->xmlctx;
534 ctx = xmlctx->ctx;
Michal Vasko7b1ad1a2020-11-02 15:41:27 +0100535 getnext_opts = lydctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0;
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100536
Michal Vasko8cc3f662022-03-29 11:25:51 +0200537 *snode = NULL;
538 *ext = NULL;
Michal Vaskoddd76592022-01-17 13:34:48 +0100539
Michal Vasko8cc3f662022-03-29 11:25:51 +0200540 /* get current namespace */
Michal Vaskoc8a230d2020-08-14 12:17:10 +0200541 ns = lyxml_ns_get(&xmlctx->ns, prefix, prefix_len);
Michal Vaskoa5da3292020-08-12 13:10:50 +0200542 if (!ns) {
Michal Vaskoce2e07c2022-12-01 11:08:52 +0100543 if (lydctx->int_opts & LYD_INTOPT_ANY) {
544 goto unknown_module;
545 }
aPiecek18afb712024-06-10 14:47:10 +0200546 LYDXML_LOG_NAMESPACE_ERR(xmlctx, prefix, prefix_len);
Michal Vasko8cc3f662022-03-29 11:25:51 +0200547 return LY_EVALID;
Michal Vaskoa5da3292020-08-12 13:10:50 +0200548 }
Michal Vasko8cc3f662022-03-29 11:25:51 +0200549
550 /* get the element module, use parent context if possible because of extensions */
551 mod = ly_ctx_get_module_implemented_ns(parent ? LYD_CTX(parent) : ctx, ns->uri);
Michal Vaskoa5da3292020-08-12 13:10:50 +0200552 if (!mod) {
Michal Vaskoddd76592022-01-17 13:34:48 +0100553 /* check for extension data */
Michal Vasko8cc3f662022-03-29 11:25:51 +0200554 r = ly_nested_ext_schema(parent, NULL, prefix, prefix_len, LY_VALUE_XML, &lydctx->xmlctx->ns, name, name_len,
555 snode, ext);
556 if (r != LY_ENOT) {
557 /* success or error */
Michal Vaskoddd76592022-01-17 13:34:48 +0100558 return r;
Michal Vaskoddd76592022-01-17 13:34:48 +0100559 }
560
Michal Vaskoce2e07c2022-12-01 11:08:52 +0100561unknown_module:
Michal Vaskoe0665742021-02-11 11:08:44 +0100562 if (lydctx->parse_opts & LYD_PARSE_STRICT) {
Michal Vasko7b3a00e2023-08-09 11:58:03 +0200563 if (ns) {
564 LOGVAL(ctx, LYVE_REFERENCE, "No module with namespace \"%s\" in the context.", ns->uri);
565 } else if (prefix_len) {
566 LOGVAL(ctx, LYVE_REFERENCE, "No module with namespace \"%.*s\" in the context.", (int)prefix_len, prefix);
567 } else {
568 LOGVAL(ctx, LYVE_REFERENCE, "No default namespace in the context.");
569 }
Michal Vasko8cc3f662022-03-29 11:25:51 +0200570 return LY_EVALID;
Radek Krejcie7b95092019-05-15 11:03:07 +0200571 }
Michal Vasko8cc3f662022-03-29 11:25:51 +0200572 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200573 }
574
Michal Vaskoa5da3292020-08-12 13:10:50 +0200575 /* get the schema node */
Michal Vaskoa878a892023-08-18 12:22:07 +0200576 if (!parent && lydctx->ext) {
577 *snode = lysc_ext_find_node(lydctx->ext, mod, name, name_len, 0, getnext_opts);
578 } else {
579 /* try to find parent schema node even if it is an opaque node (not connected to the parent) */
580 *snode = lys_find_child(lyd_parser_node_schema(parent), mod, name, name_len, 0, getnext_opts);
581 }
582 if (!*snode) {
583 /* check for extension data */
584 r = ly_nested_ext_schema(parent, NULL, prefix, prefix_len, LY_VALUE_XML, &lydctx->xmlctx->ns, name,
585 name_len, snode, ext);
586 if (r != LY_ENOT) {
587 /* success or error */
588 return r;
Radek Krejcif16e2542021-02-17 15:39:23 +0100589 }
Michal Vaskoddd76592022-01-17 13:34:48 +0100590
Michal Vaskoa878a892023-08-18 12:22:07 +0200591 /* unknown data node */
592 if (lydctx->parse_opts & LYD_PARSE_STRICT) {
593 if (parent) {
594 LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found as a child of \"%s\" node.",
595 (int)name_len, name, LYD_NAME(parent));
596 } else if (lydctx->ext) {
597 if (lydctx->ext->argument) {
598 LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" %s extension instance.",
599 (int)name_len, name, lydctx->ext->argument, lydctx->ext->def->name);
Michal Vaskoe0665742021-02-11 11:08:44 +0100600 } else {
Michal Vaskoa878a892023-08-18 12:22:07 +0200601 LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the %s extension instance.",
602 (int)name_len, name, lydctx->ext->def->name);
Michal Vaskoe0665742021-02-11 11:08:44 +0100603 }
Michal Vaskoa878a892023-08-18 12:22:07 +0200604 } else {
605 LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" module.",
606 (int)name_len, name, mod->name);
Michal Vaskoa5da3292020-08-12 13:10:50 +0200607 }
Michal Vaskoa878a892023-08-18 12:22:07 +0200608 return LY_EVALID;
Michal Vaskoa5da3292020-08-12 13:10:50 +0200609 }
Michal Vaskoa878a892023-08-18 12:22:07 +0200610 return LY_SUCCESS;
611 } else {
612 /* check that schema node is valid and can be used */
613 LY_CHECK_RET(lyd_parser_check_schema((struct lyd_ctx *)lydctx, *snode));
614 LY_CHECK_RET(lydxml_data_check_opaq(lydctx, snode));
Michal Vaskoa5da3292020-08-12 13:10:50 +0200615 }
616
Michal Vasko8cc3f662022-03-29 11:25:51 +0200617 return LY_SUCCESS;
618}
619
620/**
Michal Vaskod027f382023-02-10 09:13:25 +0100621 * @brief Parse an XML opque node.
622 *
623 * @param[in] lydctx XML YANG data parser context.
624 * @param[in] sibling Existing sibling node, if any.
625 * @param[in] prefix Parsed node prefix.
626 * @param[in] prefix_len Length of @p prefix.
627 * @param[in] name Parsed node name.
628 * @param[in] name_len Length of @p name.
629 * @param[out] insert_anchor Optional anchor node for inserting this node.
630 * @param[out] node Created node.
631 * @return LY_ERR value.
632 */
633static LY_ERR
634lydxml_subtree_opaq(struct lyd_xml_ctx *lydctx, const struct lyd_node *sibling, const char *prefix, uint32_t prefix_len,
635 const char *name, uint32_t name_len, struct lyd_node **insert_anchor, struct lyd_node **node)
636{
637 LY_ERR rc = LY_SUCCESS;
638 struct lyxml_ctx *xmlctx = lydctx->xmlctx;
Michal Vaskoa16dbb42023-08-15 15:20:25 +0200639 struct lyd_node_opaq *opaq;
640 const char *ns_uri, *value = NULL;
641 size_t value_len;
642 ly_bool ws_only, dynamic = 0;
Michal Vaskod027f382023-02-10 09:13:25 +0100643 const struct lyxml_ns *ns;
644 uint32_t hints;
645 void *val_prefix_data = NULL;
646 LY_VALUE_FORMAT format;
647
648 assert(lydctx->parse_opts & LYD_PARSE_OPAQ);
649
650 *node = NULL;
651
Michal Vaskoa16dbb42023-08-15 15:20:25 +0200652 /* remember the value */
653 value = xmlctx->value;
654 value_len = xmlctx->value_len;
655 ws_only = xmlctx->ws_only;
656 dynamic = xmlctx->dynamic;
657 if (dynamic) {
Michal Vaskod027f382023-02-10 09:13:25 +0100658 xmlctx->dynamic = 0;
Michal Vaskod027f382023-02-10 09:13:25 +0100659 }
660
Michal Vasko535d21c2023-08-09 10:41:44 +0200661 /* get value prefixes, if any */
Michal Vaskoa16dbb42023-08-15 15:20:25 +0200662 rc = ly_store_prefix_data(xmlctx->ctx, value, value_len, LY_VALUE_XML, &xmlctx->ns, &format, &val_prefix_data);
Michal Vasko535d21c2023-08-09 10:41:44 +0200663 LY_CHECK_GOTO(rc, cleanup);
664
Michal Vaskod027f382023-02-10 09:13:25 +0100665 /* get NS again, it may have been backed up and restored */
666 ns = lyxml_ns_get(&xmlctx->ns, prefix, prefix_len);
667 ns_uri = ns ? ns->uri : NULL;
668
669 /* get best-effort node hints */
670 lydxml_get_hints_opaq(name, name_len, xmlctx->value, xmlctx->value_len, sibling, ns_uri, &hints, insert_anchor);
671
Michal Vaskoa16dbb42023-08-15 15:20:25 +0200672 /* create the node without value */
673 rc = lyd_create_opaq(xmlctx->ctx, name, name_len, prefix, prefix_len, ns_uri, ns_uri ? strlen(ns_uri) : 0, NULL, 0,
674 NULL, format, NULL, hints, node);
Michal Vaskod027f382023-02-10 09:13:25 +0100675 LY_CHECK_GOTO(rc, cleanup);
Michal Vaskod027f382023-02-10 09:13:25 +0100676
Michal Vaskoa878a892023-08-18 12:22:07 +0200677 assert(*node);
Michal Vasko7a266772024-01-23 11:02:38 +0100678 LOG_LOCSET(NULL, *node);
Michal Vaskoa878a892023-08-18 12:22:07 +0200679
Michal Vaskod027f382023-02-10 09:13:25 +0100680 /* parser next */
681 rc = lyxml_ctx_next(xmlctx);
682 LY_CHECK_GOTO(rc, cleanup);
683
684 /* process children */
685 while (xmlctx->status == LYXML_ELEMENT) {
686 rc = lydxml_subtree_r(lydctx, *node, lyd_node_child_p(*node), NULL);
687 LY_CHECK_GOTO(rc, cleanup);
688 }
689
Michal Vaskoa16dbb42023-08-15 15:20:25 +0200690 /* update the value */
691 opaq = (struct lyd_node_opaq *)*node;
692 if (opaq->child) {
693 if (!ws_only) {
694 LOGVAL(xmlctx->ctx, LYVE_SYNTAX_XML, "Mixed XML content node \"%s\" found, not supported.", LYD_NAME(opaq));
695 rc = LY_EVALID;
696 goto cleanup;
697 }
698 } else if (value_len) {
699 lydict_remove(xmlctx->ctx, opaq->value);
700 if (dynamic) {
701 LY_CHECK_GOTO(rc = lydict_insert_zc(xmlctx->ctx, (char *)value, &opaq->value), cleanup);
702 dynamic = 0;
703 } else {
704 LY_CHECK_GOTO(rc = lydict_insert(xmlctx->ctx, value, value_len, &opaq->value), cleanup);
705 }
706 }
707
708 /* always store val_prefix_data because the format requires them */
709 assert(!opaq->val_prefix_data);
710 opaq->val_prefix_data = val_prefix_data;
711 val_prefix_data = NULL;
712
Michal Vaskod027f382023-02-10 09:13:25 +0100713cleanup:
Michal Vaskoa878a892023-08-18 12:22:07 +0200714 if (*node) {
Michal Vasko7a266772024-01-23 11:02:38 +0100715 LOG_LOCBACK(0, 1);
Michal Vaskoa878a892023-08-18 12:22:07 +0200716 }
Michal Vaskod027f382023-02-10 09:13:25 +0100717 ly_free_prefix_data(format, val_prefix_data);
Michal Vaskoa16dbb42023-08-15 15:20:25 +0200718 if (dynamic) {
719 free((char *)value);
720 }
Michal Vaskod027f382023-02-10 09:13:25 +0100721 if (rc) {
722 lyd_free_tree(*node);
723 *node = NULL;
724 }
725 return rc;
726}
727
728/**
729 * @brief Parse an XML leaf/leaf-list node.
730 *
731 * @param[in] lydctx XML YANG data parser context.
732 * @param[in] parent Parent node, if any.
733 * @param[in] snode Schema node of the new node.
734 * @param[out] node Created node.
735 * @return LY_ERR value.
736 */
737static LY_ERR
738lydxml_subtree_term(struct lyd_xml_ctx *lydctx, struct lyd_node *parent, const struct lysc_node *snode,
739 struct lyd_node **node)
740{
741 LY_ERR r, rc = LY_SUCCESS;
742 struct lyxml_ctx *xmlctx = lydctx->xmlctx;
743 struct lyd_node *anchor;
744
745 *node = NULL;
746
747 /* create node */
748 r = lyd_parser_create_term((struct lyd_ctx *)lydctx, snode, xmlctx->value, xmlctx->value_len, &xmlctx->dynamic,
749 LY_VALUE_XML, &xmlctx->ns, LYD_HINT_DATA, node);
750 LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
751
752 if (*node) {
Michal Vasko7a266772024-01-23 11:02:38 +0100753 LOG_LOCSET(NULL, *node);
Michal Vaskod027f382023-02-10 09:13:25 +0100754 }
755
Michal Vasko58a1a702023-03-01 13:45:38 +0100756 if (*node && parent && (snode->flags & LYS_KEY)) {
Michal Vaskod027f382023-02-10 09:13:25 +0100757 /* check the key order, the anchor must never be a key */
758 anchor = lyd_insert_get_next_anchor(lyd_child(parent), *node);
759 if (anchor && anchor->schema && (anchor->schema->flags & LYS_KEY)) {
760 if (lydctx->parse_opts & LYD_PARSE_STRICT) {
761 LOGVAL(xmlctx->ctx, LYVE_DATA, "Invalid position of the key \"%s\" in a list.", snode->name);
762 r = LY_EVALID;
763 LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
764 } else {
765 LOGWRN(xmlctx->ctx, "Invalid position of the key \"%s\" in a list.", snode->name);
766 }
767 }
768 }
769
770 /* parser next */
771 r = lyxml_ctx_next(xmlctx);
772 LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
773
774 /* no children expected */
775 if (xmlctx->status == LYXML_ELEMENT) {
776 LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Child element \"%.*s\" inside a terminal node \"%s\" found.",
777 (int)xmlctx->name_len, xmlctx->name, snode->name);
778 r = LY_EVALID;
779 LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
780 }
781
782cleanup:
783 if (*node) {
Michal Vasko7a266772024-01-23 11:02:38 +0100784 LOG_LOCBACK(0, 1);
Michal Vaskod027f382023-02-10 09:13:25 +0100785 }
786 if (rc && (!(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) {
787 lyd_free_tree(*node);
788 *node = NULL;
789 }
790 return rc;
791}
792
793/**
794 * @brief Parse an XML inner node.
795 *
796 * @param[in] lydctx XML YANG data parser context.
797 * @param[in] snode Schema node of the new node.
798 * @param[in] ext Extension instance of @p snode, if any.
799 * @param[out] node Created node.
800 * @return LY_ERR value.
801 */
802static LY_ERR
803lydxml_subtree_inner(struct lyd_xml_ctx *lydctx, const struct lysc_node *snode, const struct lysc_ext_instance *ext,
804 struct lyd_node **node)
805{
806 LY_ERR r, rc = LY_SUCCESS;
807 struct lyxml_ctx *xmlctx = lydctx->xmlctx;
808 uint32_t prev_parse_opts = lydctx->parse_opts;
809
810 *node = NULL;
811
812 if (!xmlctx->ws_only) {
813 /* value in inner node */
814 LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Text value \"%.*s\" inside an inner node \"%s\" found.",
815 (int)xmlctx->value_len, xmlctx->value, snode->name);
816 r = LY_EVALID;
817 LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
818 }
819
820 /* create node */
821 rc = lyd_create_inner(snode, node);
822 LY_CHECK_GOTO(rc, cleanup);
823
Michal Vasko58a1a702023-03-01 13:45:38 +0100824 assert(*node);
Michal Vasko7a266772024-01-23 11:02:38 +0100825 LOG_LOCSET(NULL, *node);
Michal Vaskod027f382023-02-10 09:13:25 +0100826
827 /* parser next */
828 rc = lyxml_ctx_next(xmlctx);
829 LY_CHECK_GOTO(rc, cleanup);
830
831 if (ext) {
832 /* only parse these extension data and validate afterwards */
833 lydctx->parse_opts |= LYD_PARSE_ONLY;
834 }
835
836 /* process children */
837 while (xmlctx->status == LYXML_ELEMENT) {
838 r = lydxml_subtree_r(lydctx, *node, lyd_node_child_p(*node), NULL);
839 LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
840 }
841
842 /* restore options */
843 lydctx->parse_opts = prev_parse_opts;
844
845 if (snode->nodetype == LYS_LIST) {
846 /* check all keys exist */
847 r = lyd_parse_check_keys(*node);
Michal Vasko202d8162023-03-01 14:42:19 +0100848 LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
Michal Vaskod027f382023-02-10 09:13:25 +0100849 }
850
Michal Vasko40015732023-11-20 13:48:15 +0100851 if (!(lydctx->parse_opts & LYD_PARSE_ONLY) && !rc) {
852 /* new node validation, autodelete CANNOT occur (it can if multi-error), all nodes are new */
Michal Vaskod027f382023-02-10 09:13:25 +0100853 r = lyd_validate_new(lyd_node_child_p(*node), snode, NULL, lydctx->val_opts, NULL);
854 LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
855
856 /* add any missing default children */
857 r = lyd_new_implicit_r(*node, lyd_node_child_p(*node), NULL, NULL, &lydctx->node_when, &lydctx->node_types,
858 &lydctx->ext_node, (lydctx->val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
859 LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
860 }
861
862 if (snode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) {
863 /* rememeber the RPC/action/notification */
864 lydctx->op_node = *node;
865 }
866
867cleanup:
868 if (*node) {
Michal Vasko7a266772024-01-23 11:02:38 +0100869 LOG_LOCBACK(0, 1);
Michal Vaskod027f382023-02-10 09:13:25 +0100870 }
871 lydctx->parse_opts = prev_parse_opts;
Michal Vasko202d8162023-03-01 14:42:19 +0100872 if (rc && ((*node && !(*node)->hash) || !(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) {
873 /* list without keys is unusable or an error */
Michal Vaskod027f382023-02-10 09:13:25 +0100874 lyd_free_tree(*node);
875 *node = NULL;
876 }
877 return rc;
878}
879
880/**
881 * @brief Parse an XML anyxml/anydata node.
882 *
883 * @param[in] lydctx XML YANG data parser context.
884 * @param[in] snode Schema node of the new node.
885 * @param[in] ext Extension instance of @p snode, if any.
886 * @param[out] node Created node.
887 * @return LY_ERR value.
888 */
889static LY_ERR
890lydxml_subtree_any(struct lyd_xml_ctx *lydctx, const struct lysc_node *snode, const struct lysc_ext_instance *ext,
891 struct lyd_node **node)
892{
893 LY_ERR r, rc = LY_SUCCESS;
894 struct lyxml_ctx *xmlctx = lydctx->xmlctx;
895 uint32_t prev_parse_opts = lydctx->parse_opts, prev_int_opts = lydctx->int_opts;
896 struct lyd_node *child = NULL;
897 char *val = NULL;
Michal Vasko85be65e2023-06-13 09:44:17 +0200898 ly_bool log_node = 0;
Michal Vaskod027f382023-02-10 09:13:25 +0100899
900 *node = NULL;
901
902 if ((snode->nodetype == LYS_ANYDATA) && !xmlctx->ws_only) {
903 /* value in anydata node, we expect a tree */
904 LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Text value \"%.*s\" inside an anydata node \"%s\" found.",
Michal Vasko7b3a00e2023-08-09 11:58:03 +0200905 xmlctx->value_len < 20 ? (int)xmlctx->value_len : 20, xmlctx->value, snode->name);
Michal Vaskod027f382023-02-10 09:13:25 +0100906 r = LY_EVALID;
907 LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
908 }
909
910 if (!xmlctx->ws_only) {
911 /* use an arbitrary text value for anyxml */
912 val = strndup(xmlctx->value, xmlctx->value_len);
913 LY_CHECK_ERR_GOTO(!val, LOGMEM(xmlctx->ctx); rc = LY_EMEM, cleanup);
914
915 /* parser next */
916 r = lyxml_ctx_next(xmlctx);
917 LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
918
919 /* create node */
920 r = lyd_create_any(snode, val, LYD_ANYDATA_STRING, 1, node);
921 LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
922 val = NULL;
923 } else {
Michal Vasko85be65e2023-06-13 09:44:17 +0200924 /* create node */
925 r = lyd_create_any(snode, NULL, LYD_ANYDATA_DATATREE, 1, node);
926 LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
927
928 assert(*node);
Michal Vasko7a266772024-01-23 11:02:38 +0100929 LOG_LOCSET(NULL, *node);
Michal Vasko85be65e2023-06-13 09:44:17 +0200930 log_node = 1;
931
Michal Vaskod027f382023-02-10 09:13:25 +0100932 /* parser next */
933 r = lyxml_ctx_next(xmlctx);
934 LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
935
936 /* update options so that generic data can be parsed */
937 lydctx->parse_opts &= ~LYD_PARSE_STRICT;
938 lydctx->parse_opts |= LYD_PARSE_OPAQ | (ext ? LYD_PARSE_ONLY : 0);
939 lydctx->int_opts |= LYD_INTOPT_ANY | LYD_INTOPT_WITH_SIBLINGS;
940
941 /* parse any data tree */
942 while (xmlctx->status == LYXML_ELEMENT) {
943 r = lydxml_subtree_r(lydctx, NULL, &child, NULL);
944 LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
945 }
946
Michal Vasko85be65e2023-06-13 09:44:17 +0200947 /* assign the data tree */
948 ((struct lyd_node_any *)*node)->value.tree = child;
Michal Vaskod027f382023-02-10 09:13:25 +0100949 child = NULL;
950 }
951
952cleanup:
Michal Vasko85be65e2023-06-13 09:44:17 +0200953 if (log_node) {
Michal Vasko7a266772024-01-23 11:02:38 +0100954 LOG_LOCBACK(0, 1);
Michal Vasko85be65e2023-06-13 09:44:17 +0200955 }
Michal Vaskod027f382023-02-10 09:13:25 +0100956 lydctx->parse_opts = prev_parse_opts;
957 lydctx->int_opts = prev_int_opts;
958 free(val);
959 lyd_free_tree(child);
960 if (rc && (!(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) {
961 lyd_free_tree(*node);
962 *node = NULL;
963 }
964 return rc;
965}
966
967/**
968 * @brief Parse an XML subtree, recursively.
Michal Vasko8cc3f662022-03-29 11:25:51 +0200969 *
970 * @param[in] lydctx XML YANG data parser context.
971 * @param[in,out] parent Parent node where the children are inserted. NULL in case of parsing top-level elements.
Michal Vaskod027f382023-02-10 09:13:25 +0100972 * @param[in,out] first_p Pointer to the first (@p parent or top-level) child.
Michal Vasko8cc3f662022-03-29 11:25:51 +0200973 * @param[in,out] parsed Optional set to add all the parsed siblings into.
974 * @return LY_ERR value.
975 */
976static LY_ERR
977lydxml_subtree_r(struct lyd_xml_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_set *parsed)
978{
Michal Vaskod027f382023-02-10 09:13:25 +0100979 LY_ERR r, rc = LY_SUCCESS;
980 const char *prefix, *name;
Michal Vasko8cc3f662022-03-29 11:25:51 +0200981 size_t prefix_len, name_len;
982 struct lyxml_ctx *xmlctx;
983 const struct ly_ctx *ctx;
Michal Vasko8cc3f662022-03-29 11:25:51 +0200984 struct lyd_meta *meta = NULL;
985 struct lyd_attr *attr = NULL;
Michal Vasko4a1e3e82023-09-05 08:45:01 +0200986 const struct lysc_node *snode = NULL;
987 struct lysc_ext_instance *ext = NULL;
Michal Vaskod027f382023-02-10 09:13:25 +0100988 uint32_t orig_parse_opts;
989 struct lyd_node *node = NULL, *insert_anchor = NULL;
Michal Vasko8cc3f662022-03-29 11:25:51 +0200990 ly_bool parse_subtree;
Michal Vasko8cc3f662022-03-29 11:25:51 +0200991
992 assert(parent || first_p);
993
994 xmlctx = lydctx->xmlctx;
995 ctx = xmlctx->ctx;
996
997 parse_subtree = lydctx->parse_opts & LYD_PARSE_SUBTREE ? 1 : 0;
998 /* all descendants should be parsed */
999 lydctx->parse_opts &= ~LYD_PARSE_SUBTREE;
1000 orig_parse_opts = lydctx->parse_opts;
1001
1002 assert(xmlctx->status == LYXML_ELEMENT);
1003
1004 /* remember element prefix and name */
1005 prefix = xmlctx->prefix;
1006 prefix_len = xmlctx->prefix_len;
1007 name = xmlctx->name;
1008 name_len = xmlctx->name_len;
1009
1010 /* parser next */
Michal Vaskod027f382023-02-10 09:13:25 +01001011 rc = lyxml_ctx_next(xmlctx);
1012 LY_CHECK_GOTO(rc, cleanup);
Michal Vasko8cc3f662022-03-29 11:25:51 +02001013
Michal Vasko4a1e3e82023-09-05 08:45:01 +02001014 if ((lydctx->int_opts & LYD_INTOPT_EVENTTIME) && !parent && name_len && !prefix_len &&
1015 !ly_strncmp("eventTime", name, name_len)) {
1016 /* parse eventTime, create node */
1017 assert(xmlctx->status == LYXML_ELEM_CONTENT);
1018 rc = lyd_create_opaq(xmlctx->ctx, name, name_len, prefix, prefix_len,
1019 "urn:ietf:params:xml:ns:netconf:notification:1.0", 47, xmlctx->value,
1020 xmlctx->ws_only ? 0 : xmlctx->value_len, NULL, LY_VALUE_XML, NULL, LYD_HINT_DATA, &node);
1021 LY_CHECK_GOTO(rc, cleanup);
1022
1023 /* validate the value */
1024 r = lyd_parser_notif_eventtime_validate(node);
1025 LY_CHECK_ERR_GOTO(r, rc = r; lyd_free_tree(node), cleanup);
1026
1027 /* parser next */
1028 r = lyxml_ctx_next(xmlctx);
1029 LY_CHECK_ERR_GOTO(r, rc = r; lyd_free_tree(node), cleanup);
1030 if (xmlctx->status != LYXML_ELEM_CLOSE) {
1031 LOGVAL(ctx, LYVE_DATA, "Unexpected notification \"eventTime\" node children.");
1032 rc = LY_EVALID;
1033 lyd_free_tree(node);
1034 goto cleanup;
1035 }
1036
1037 goto node_parsed;
1038 }
1039
Michal Vasko8cc3f662022-03-29 11:25:51 +02001040 /* get the schema node */
Michal Vasko202d8162023-03-01 14:42:19 +01001041 r = lydxml_subtree_get_snode(lydctx, parent, prefix, prefix_len, name, name_len, &snode, &ext);
1042 if (r) {
1043 rc = r;
1044 if ((r == LY_EVALID) && (lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR)) {
1045 /* skip the invalid data */
1046 if ((r = lydxml_data_skip(xmlctx))) {
1047 rc = r;
1048 }
1049 }
1050 goto cleanup;
1051 } else if (!snode && !(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
Michal Vasko7b3a00e2023-08-09 11:58:03 +02001052 LOGVRB("Skipping parsing of unknown node \"%.*s\".", (int)name_len, name);
Michal Vasko8cc3f662022-03-29 11:25:51 +02001053
1054 /* skip element with children */
Michal Vaskod027f382023-02-10 09:13:25 +01001055 rc = lydxml_data_skip(xmlctx);
1056 goto cleanup;
Michal Vasko8cc3f662022-03-29 11:25:51 +02001057 }
1058
Michal Vaskoa5da3292020-08-12 13:10:50 +02001059 /* create metadata/attributes */
1060 if (xmlctx->status == LYXML_ATTRIBUTE) {
1061 if (snode) {
Michal Vaskod027f382023-02-10 09:13:25 +01001062 rc = lydxml_metadata(lydctx, snode, &meta);
1063 LY_CHECK_GOTO(rc, cleanup);
Michal Vaskoa5da3292020-08-12 13:10:50 +02001064 } else {
Michal Vaskoe0665742021-02-11 11:08:44 +01001065 assert(lydctx->parse_opts & LYD_PARSE_OPAQ);
Michal Vaskod027f382023-02-10 09:13:25 +01001066 rc = lydxml_attrs(xmlctx, &attr);
1067 LY_CHECK_GOTO(rc, cleanup);
Michal Vaskoa5da3292020-08-12 13:10:50 +02001068 }
1069 }
1070
1071 assert(xmlctx->status == LYXML_ELEM_CONTENT);
1072 if (!snode) {
Michal Vaskod027f382023-02-10 09:13:25 +01001073 /* opaque */
1074 r = lydxml_subtree_opaq(lydctx, parent ? lyd_child(parent) : *first_p, prefix, prefix_len, name, name_len,
1075 &insert_anchor, &node);
Michal Vaskoa5da3292020-08-12 13:10:50 +02001076 } else if (snode->nodetype & LYD_NODE_TERM) {
Michal Vaskod027f382023-02-10 09:13:25 +01001077 /* term */
1078 r = lydxml_subtree_term(lydctx, parent, snode, &node);
Michal Vaskoa5da3292020-08-12 13:10:50 +02001079 } else if (snode->nodetype & LYD_NODE_INNER) {
Michal Vaskod027f382023-02-10 09:13:25 +01001080 /* inner */
1081 r = lydxml_subtree_inner(lydctx, snode, ext, &node);
Michal Vasko7c6f33f2023-02-10 09:40:11 +01001082 } else {
Michal Vaskod027f382023-02-10 09:13:25 +01001083 /* any */
Michal Vasko7c6f33f2023-02-10 09:40:11 +01001084 assert(snode->nodetype & LYD_NODE_ANY);
Michal Vaskod027f382023-02-10 09:13:25 +01001085 r = lydxml_subtree_any(lydctx, snode, ext, &node);
Michal Vaskoa5da3292020-08-12 13:10:50 +02001086 }
Michal Vaskod027f382023-02-10 09:13:25 +01001087 LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
Michal Vasko1524f1a2023-03-01 09:36:34 +01001088
Michal Vasko4a1e3e82023-09-05 08:45:01 +02001089node_parsed:
Michal Vasko58a1a702023-03-01 13:45:38 +01001090 if (node && snode) {
Michal Vasko135719f2022-08-25 12:18:17 +02001091 /* add/correct flags */
Michal Vaskod027f382023-02-10 09:13:25 +01001092 r = lyd_parse_set_data_flags(node, &meta, (struct lyd_ctx *)lydctx, ext);
Michal Vasko567d7032023-02-13 08:53:31 +01001093 LY_CHECK_ERR_GOTO(r, rc = r; lyd_free_tree(node), cleanup);
Michal Vasko135719f2022-08-25 12:18:17 +02001094
Michal Vaskoeba23112022-08-26 08:35:41 +02001095 if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) {
1096 /* store for ext instance node validation, if needed */
Michal Vaskod027f382023-02-10 09:13:25 +01001097 r = lyd_validate_node_ext(node, &lydctx->ext_node);
1098 LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
Michal Vaskoeba23112022-08-26 08:35:41 +02001099 }
Michal Vaskoa5da3292020-08-12 13:10:50 +02001100 }
1101
1102 /* parser next */
1103 assert(xmlctx->status == LYXML_ELEM_CLOSE);
Michal Vaskoddd76592022-01-17 13:34:48 +01001104 if (!parse_subtree) {
Michal Vaskod027f382023-02-10 09:13:25 +01001105 r = lyxml_ctx_next(xmlctx);
Michal Vasko567d7032023-02-13 08:53:31 +01001106 LY_CHECK_ERR_GOTO(r, rc = r; lyd_free_tree(node), cleanup);
Michal Vaskoddd76592022-01-17 13:34:48 +01001107 }
Michal Vaskoa5da3292020-08-12 13:10:50 +02001108
Michal Vasko58a1a702023-03-01 13:45:38 +01001109 LY_CHECK_GOTO(!node, cleanup);
1110
Michal Vaskoa5da3292020-08-12 13:10:50 +02001111 /* add metadata/attributes */
1112 if (snode) {
Michal Vasko871a0252020-11-11 18:35:24 +01001113 lyd_insert_meta(node, meta, 0);
Michal Vaskod027f382023-02-10 09:13:25 +01001114 meta = NULL;
Michal Vaskoa5da3292020-08-12 13:10:50 +02001115 } else {
1116 lyd_insert_attr(node, attr);
Michal Vaskod027f382023-02-10 09:13:25 +01001117 attr = NULL;
Michal Vaskoa5da3292020-08-12 13:10:50 +02001118 }
1119
1120 /* insert, keep first pointer correct */
Michal Vaskocea58712022-04-01 14:37:08 +02001121 if (insert_anchor) {
1122 lyd_insert_after(insert_anchor, node);
1123 } else if (ext) {
Michal Vaskod027f382023-02-10 09:13:25 +01001124 r = lyplg_ext_insert(parent, node);
Michal Vasko567d7032023-02-13 08:53:31 +01001125 LY_CHECK_ERR_GOTO(r, rc = r; lyd_free_tree(node), cleanup);
Michal Vasko8cc3f662022-03-29 11:25:51 +02001126 } else {
aPiecek1462ab12024-02-07 09:13:29 +01001127 lyd_insert_node(parent, first_p, node,
1128 lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT);
Michal Vasko8cc3f662022-03-29 11:25:51 +02001129 }
Michal Vaskoa5da3292020-08-12 13:10:50 +02001130 while (!parent && (*first_p)->prev->next) {
1131 *first_p = (*first_p)->prev;
1132 }
1133
Michal Vaskoe0665742021-02-11 11:08:44 +01001134 /* rememeber a successfully parsed node */
1135 if (parsed) {
1136 ly_set_add(parsed, node, 1, NULL);
1137 }
1138
Michal Vaskod027f382023-02-10 09:13:25 +01001139cleanup:
Michal Vasko8cc3f662022-03-29 11:25:51 +02001140 lydctx->parse_opts = orig_parse_opts;
Michal Vasko3a41dff2020-07-15 14:30:28 +02001141 lyd_free_meta_siblings(meta);
Radek Krejci011e4aa2020-09-04 15:22:31 +02001142 lyd_free_attr_siblings(ctx, attr);
Michal Vaskod027f382023-02-10 09:13:25 +01001143 return rc;
Radek Krejcie7b95092019-05-15 11:03:07 +02001144}
1145
Michal Vaskoe0665742021-02-11 11:08:44 +01001146/**
1147 * @brief Parse a specific XML element into an opaque node.
1148 *
1149 * @param[in] xmlctx XML parser context.
1150 * @param[in] name Name of the element.
1151 * @param[in] uri URI of the element.
1152 * @param[in] value Whether a value is expected in the element.
1153 * @param[out] evnp Parsed envelope (opaque node).
1154 * @return LY_SUCCESS on success.
1155 * @return LY_ENOT if the specified element did not match.
1156 * @return LY_ERR value on error.
1157 */
Michal Vasko1bf09392020-03-27 12:38:10 +01001158static LY_ERR
Michal Vaskoe0665742021-02-11 11:08:44 +01001159lydxml_envelope(struct lyxml_ctx *xmlctx, const char *name, const char *uri, ly_bool value, struct lyd_node **envp)
Michal Vasko1bf09392020-03-27 12:38:10 +01001160{
Michal Vaskoe0665742021-02-11 11:08:44 +01001161 LY_ERR rc = LY_SUCCESS;
1162 const struct lyxml_ns *ns;
Radek Krejci1798aae2020-07-14 13:26:06 +02001163 struct lyd_attr *attr = NULL;
Michal Vasko1bf09392020-03-27 12:38:10 +01001164 const char *prefix;
1165 size_t prefix_len;
1166
Michal Vaskof048ba52023-06-13 10:40:43 +02001167 if (xmlctx->status != LYXML_ELEMENT) {
1168 /* nothing to parse */
1169 return LY_ENOT;
1170 }
1171
Michal Vasko1bf09392020-03-27 12:38:10 +01001172 if (ly_strncmp(name, xmlctx->name, xmlctx->name_len)) {
1173 /* not the expected element */
Michal Vaskoe0665742021-02-11 11:08:44 +01001174 return LY_ENOT;
Michal Vaskoa8edff02020-03-27 14:47:01 +01001175 }
1176
1177 prefix = xmlctx->prefix;
1178 prefix_len = xmlctx->prefix_len;
Michal Vaskoc8a230d2020-08-14 12:17:10 +02001179 ns = lyxml_ns_get(&xmlctx->ns, prefix, prefix_len);
Michal Vaskoa8edff02020-03-27 14:47:01 +01001180 if (!ns) {
aPiecek18afb712024-06-10 14:47:10 +02001181 LYDXML_LOG_NAMESPACE_ERR(xmlctx, prefix, prefix_len);
Michal Vaskoe0665742021-02-11 11:08:44 +01001182 return LY_EVALID;
1183 } else if (strcmp(ns->uri, uri)) {
1184 /* different namespace */
1185 return LY_ENOT;
Michal Vaskoa8edff02020-03-27 14:47:01 +01001186 }
1187
1188 LY_CHECK_RET(lyxml_ctx_next(xmlctx));
1189
1190 /* create attributes */
1191 if (xmlctx->status == LYXML_ATTRIBUTE) {
1192 LY_CHECK_RET(lydxml_attrs(xmlctx, &attr));
1193 }
1194
Michal Vaskoe0665742021-02-11 11:08:44 +01001195 assert(xmlctx->status == LYXML_ELEM_CONTENT);
1196 if (!value && !xmlctx->ws_only) {
1197 LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Unexpected value \"%.*s\" in the \"%s\" element.",
Radek Krejci422afb12021-03-04 16:38:16 +01001198 (int)xmlctx->value_len, xmlctx->value, name);
Michal Vaskoe0665742021-02-11 11:08:44 +01001199 rc = LY_EVALID;
Michal Vaskoa8edff02020-03-27 14:47:01 +01001200 goto cleanup;
Michal Vaskoe0665742021-02-11 11:08:44 +01001201 }
Michal Vaskoa8edff02020-03-27 14:47:01 +01001202
1203 /* create node */
Michal Vaskoe0665742021-02-11 11:08:44 +01001204 rc = lyd_create_opaq(xmlctx->ctx, name, strlen(name), prefix, prefix_len, uri, strlen(uri), xmlctx->value,
Radek Krejci8df109d2021-04-23 12:19:08 +02001205 xmlctx->ws_only ? 0 : xmlctx->value_len, NULL, LY_VALUE_XML, NULL, 0, envp);
Michal Vaskoe0665742021-02-11 11:08:44 +01001206 LY_CHECK_GOTO(rc, cleanup);
Michal Vaskoa8edff02020-03-27 14:47:01 +01001207
1208 /* assign atributes */
Michal Vaskoe0665742021-02-11 11:08:44 +01001209 ((struct lyd_node_opaq *)(*envp))->attr = attr;
Michal Vaskoa8edff02020-03-27 14:47:01 +01001210 attr = NULL;
1211
Michal Vaskoe0665742021-02-11 11:08:44 +01001212 /* parser next element */
1213 LY_CHECK_GOTO(rc = lyxml_ctx_next(xmlctx), cleanup);
Michal Vaskoa8edff02020-03-27 14:47:01 +01001214
Michal Vaskoe0665742021-02-11 11:08:44 +01001215cleanup:
1216 lyd_free_attr_siblings(xmlctx->ctx, attr);
1217 if (rc) {
1218 lyd_free_tree(*envp);
1219 *envp = NULL;
1220 }
1221 return rc;
1222}
1223
Michal Vaskoe3ed7dc2022-11-30 11:39:44 +01001224LY_ERR
1225lyd_parse_xml(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent,
1226 struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts,
1227 struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p)
1228{
Michal Vaskod027f382023-02-10 09:13:25 +01001229 LY_ERR r, rc = LY_SUCCESS;
Michal Vaskoe3ed7dc2022-11-30 11:39:44 +01001230 struct lyd_xml_ctx *lydctx;
Michal Vasko64592692023-06-12 13:50:11 +02001231 ly_bool parsed_data_nodes = 0, close_elem = 0;
1232 struct lyd_node *act = NULL;
Michal Vaskoe3ed7dc2022-11-30 11:39:44 +01001233 enum LYXML_PARSER_STATUS status;
1234
1235 assert(ctx && in && lydctx_p);
1236 assert(!(parse_opts & ~LYD_PARSE_OPTS_MASK));
1237 assert(!(val_opts & ~LYD_VALIDATE_OPTS_MASK));
1238
1239 /* init context */
1240 lydctx = calloc(1, sizeof *lydctx);
1241 LY_CHECK_ERR_RET(!lydctx, LOGMEM(ctx), LY_EMEM);
1242 LY_CHECK_GOTO(rc = lyxml_ctx_new(ctx, in, &lydctx->xmlctx), cleanup);
1243 lydctx->parse_opts = parse_opts;
1244 lydctx->val_opts = val_opts;
1245 lydctx->int_opts = int_opts;
1246 lydctx->free = lyd_xml_ctx_free;
1247 lydctx->ext = ext;
1248
1249 /* find the operation node if it exists already */
1250 LY_CHECK_GOTO(rc = lyd_parser_find_operation(parent, int_opts, &lydctx->op_node), cleanup);
1251
Michal Vasko64592692023-06-12 13:50:11 +02001252 if ((int_opts & LYD_INTOPT_RPC) && (int_opts & LYD_INTOPT_ACTION)) {
1253 /* can be either, try to parse "action" */
1254 if (!lydxml_envelope(lydctx->xmlctx, "action", "urn:ietf:params:xml:ns:yang:1", 0, &act)) {
1255 close_elem = 1;
1256 int_opts &= ~LYD_INTOPT_RPC;
1257 }
1258 }
1259
Michal Vaskoe3ed7dc2022-11-30 11:39:44 +01001260 /* parse XML data */
1261 while (lydctx->xmlctx->status == LYXML_ELEMENT) {
Michal Vaskod027f382023-02-10 09:13:25 +01001262 r = lydxml_subtree_r(lydctx, parent, first_p, parsed);
1263 LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
1264
Michal Vaskoe3ed7dc2022-11-30 11:39:44 +01001265 parsed_data_nodes = 1;
1266
1267 if (!(int_opts & LYD_INTOPT_WITH_SIBLINGS)) {
1268 break;
1269 }
1270 }
1271
Michal Vasko64592692023-06-12 13:50:11 +02001272 /* close an opened element */
1273 if (close_elem) {
1274 if (lydctx->xmlctx->status != LYXML_ELEM_CLOSE) {
1275 assert(lydctx->xmlctx->status == LYXML_ELEMENT);
1276 LOGVAL(lydctx->xmlctx->ctx, LYVE_SYNTAX, "Unexpected child element \"%.*s\".",
1277 (int)lydctx->xmlctx->name_len, lydctx->xmlctx->name);
1278 rc = LY_EVALID;
1279 goto cleanup;
1280 }
1281
1282 LY_CHECK_GOTO(rc = lyxml_ctx_next(lydctx->xmlctx), cleanup);
1283 }
1284
Michal Vaskoe3ed7dc2022-11-30 11:39:44 +01001285 /* check final state */
1286 if ((int_opts & LYD_INTOPT_NO_SIBLINGS) && (lydctx->xmlctx->status == LYXML_ELEMENT)) {
1287 LOGVAL(ctx, LYVE_SYNTAX, "Unexpected sibling node.");
Michal Vaskod027f382023-02-10 09:13:25 +01001288 r = LY_EVALID;
1289 LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
Michal Vaskoe3ed7dc2022-11-30 11:39:44 +01001290 }
1291 if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) && !lydctx->op_node) {
1292 LOGVAL(ctx, LYVE_DATA, "Missing the operation node.");
Michal Vaskod027f382023-02-10 09:13:25 +01001293 r = LY_EVALID;
1294 LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
Michal Vaskoe3ed7dc2022-11-30 11:39:44 +01001295 }
1296
1297 if (!parsed_data_nodes) {
1298 /* no data nodes were parsed */
1299 lydctx->op_node = NULL;
1300 }
1301
1302 if (parse_opts & LYD_PARSE_SUBTREE) {
1303 /* check for a sibling element */
1304 assert(subtree_sibling);
1305 if (!lyxml_ctx_peek(lydctx->xmlctx, &status) && (status == LYXML_ELEMENT)) {
1306 *subtree_sibling = 1;
1307 } else {
1308 *subtree_sibling = 0;
1309 }
1310 }
1311
1312cleanup:
1313 /* there should be no unres stored if validation should be skipped */
1314 assert(!(parse_opts & LYD_PARSE_ONLY) || (!lydctx->node_types.count && !lydctx->meta_types.count &&
1315 !lydctx->node_when.count));
1316
Michal Vasko64592692023-06-12 13:50:11 +02001317 lyd_free_tree(act);
Michal Vaskod027f382023-02-10 09:13:25 +01001318 if (rc && (!(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) {
Michal Vaskoe3ed7dc2022-11-30 11:39:44 +01001319 lyd_xml_ctx_free((struct lyd_ctx *)lydctx);
1320 } else {
1321 *lydctx_p = (struct lyd_ctx *)lydctx;
1322
1323 /* the XML context is no more needed, freeing it also stops logging line numbers which would be confusing now */
1324 lyxml_ctx_free(lydctx->xmlctx);
1325 lydctx->xmlctx = NULL;
1326 }
1327 return rc;
1328}
1329
Michal Vaskoe0665742021-02-11 11:08:44 +01001330/**
1331 * @brief Parse all expected non-data XML elements of a NETCONF rpc message.
1332 *
1333 * @param[in] xmlctx XML parser context.
1334 * @param[out] evnp Parsed envelope(s) (opaque node).
1335 * @param[out] int_opts Internal options for parsing the rest of YANG data.
1336 * @param[out] close_elem Number of parsed opened elements that need to be closed.
1337 * @return LY_SUCCESS on success.
1338 * @return LY_ERR value on error.
1339 */
1340static LY_ERR
1341lydxml_env_netconf_rpc(struct lyxml_ctx *xmlctx, struct lyd_node **envp, uint32_t *int_opts, uint32_t *close_elem)
1342{
1343 LY_ERR rc = LY_SUCCESS, r;
1344 struct lyd_node *child;
1345
1346 assert(envp && !*envp);
1347
1348 /* parse "rpc" */
1349 r = lydxml_envelope(xmlctx, "rpc", "urn:ietf:params:xml:ns:netconf:base:1.0", 0, envp);
Michal Vaskoe0665742021-02-11 11:08:44 +01001350 LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
1351
1352 /* parse "action", if any */
1353 r = lydxml_envelope(xmlctx, "action", "urn:ietf:params:xml:ns:yang:1", 0, &child);
1354 if (r == LY_SUCCESS) {
1355 /* insert */
aPiecek1462ab12024-02-07 09:13:29 +01001356 lyd_insert_node(*envp, NULL, child, LYD_INSERT_NODE_DEFAULT);
Michal Vaskoe0665742021-02-11 11:08:44 +01001357
1358 /* NETCONF action */
1359 *int_opts = LYD_INTOPT_NO_SIBLINGS | LYD_INTOPT_ACTION;
1360 *close_elem = 2;
1361 } else if (r == LY_ENOT) {
1362 /* NETCONF RPC */
1363 *int_opts = LYD_INTOPT_NO_SIBLINGS | LYD_INTOPT_RPC;
1364 *close_elem = 1;
1365 } else {
1366 rc = r;
1367 goto cleanup;
1368 }
1369
1370cleanup:
1371 if (rc) {
1372 lyd_free_tree(*envp);
1373 *envp = NULL;
1374 }
1375 return rc;
1376}
1377
1378/**
Michal Vaskoe0665742021-02-11 11:08:44 +01001379 * @brief Parse an XML element as an opaque node subtree.
1380 *
1381 * @param[in] xmlctx XML parser context.
1382 * @param[in] parent Parent to append nodes to.
1383 * @return LY_ERR value.
1384 */
1385static LY_ERR
1386lydxml_opaq_r(struct lyxml_ctx *xmlctx, struct lyd_node *parent)
Michal Vaskoa8edff02020-03-27 14:47:01 +01001387{
Michal Vaskoe0665742021-02-11 11:08:44 +01001388 LY_ERR rc = LY_SUCCESS;
1389 const struct lyxml_ns *ns;
1390 struct lyd_attr *attr = NULL;
Michal Vaskoa16dbb42023-08-15 15:20:25 +02001391 struct lyd_node *node = NULL;
1392 struct lyd_node_opaq *opaq;
1393 const char *name, *prefix, *value = NULL;
1394 size_t name_len, prefix_len, value_len;
1395 ly_bool ws_only, dynamic = 0;
Michal Vaskoa8edff02020-03-27 14:47:01 +01001396
Michal Vaskoe0665742021-02-11 11:08:44 +01001397 assert(xmlctx->status == LYXML_ELEMENT);
Michal Vaskoa8edff02020-03-27 14:47:01 +01001398
Michal Vaskoe0665742021-02-11 11:08:44 +01001399 name = xmlctx->name;
1400 name_len = xmlctx->name_len;
1401 prefix = xmlctx->prefix;
1402 prefix_len = xmlctx->prefix_len;
1403 ns = lyxml_ns_get(&xmlctx->ns, prefix, prefix_len);
1404 if (!ns) {
aPiecek18afb712024-06-10 14:47:10 +02001405 LYDXML_LOG_NAMESPACE_ERR(xmlctx, prefix, prefix_len);
Michal Vaskoe0665742021-02-11 11:08:44 +01001406 return LY_EVALID;
1407 }
Michal Vaskoa8edff02020-03-27 14:47:01 +01001408
Michal Vaskoe0665742021-02-11 11:08:44 +01001409 LY_CHECK_RET(lyxml_ctx_next(xmlctx));
Michal Vaskoa8edff02020-03-27 14:47:01 +01001410
Michal Vaskoe0665742021-02-11 11:08:44 +01001411 /* create attributes */
1412 if (xmlctx->status == LYXML_ATTRIBUTE) {
1413 LY_CHECK_RET(lydxml_attrs(xmlctx, &attr));
1414 }
1415
Michal Vaskoa16dbb42023-08-15 15:20:25 +02001416 /* remember the value */
Michal Vaskoe0665742021-02-11 11:08:44 +01001417 assert(xmlctx->status == LYXML_ELEM_CONTENT);
Michal Vaskoa16dbb42023-08-15 15:20:25 +02001418 value = xmlctx->value;
1419 value_len = xmlctx->value_len;
1420 ws_only = xmlctx->ws_only;
1421 dynamic = xmlctx->dynamic;
1422 if (dynamic) {
1423 xmlctx->dynamic = 0;
1424 }
1425
1426 /* create the node without value */
1427 rc = lyd_create_opaq(xmlctx->ctx, name, name_len, prefix, prefix_len, ns->uri, strlen(ns->uri), NULL, 0, NULL,
1428 LY_VALUE_XML, NULL, 0, &node);
Michal Vaskoe0665742021-02-11 11:08:44 +01001429 LY_CHECK_GOTO(rc, cleanup);
1430
1431 /* assign atributes */
Michal Vaskoa16dbb42023-08-15 15:20:25 +02001432 ((struct lyd_node_opaq *)node)->attr = attr;
Michal Vaskoe0665742021-02-11 11:08:44 +01001433 attr = NULL;
1434
1435 /* parser next element */
1436 LY_CHECK_GOTO(rc = lyxml_ctx_next(xmlctx), cleanup);
1437
1438 /* parse all the descendants */
1439 while (xmlctx->status == LYXML_ELEMENT) {
Michal Vaskoa16dbb42023-08-15 15:20:25 +02001440 rc = lydxml_opaq_r(xmlctx, node);
Michal Vaskoe0665742021-02-11 11:08:44 +01001441 LY_CHECK_GOTO(rc, cleanup);
1442 }
1443
1444 /* insert */
aPiecek1462ab12024-02-07 09:13:29 +01001445 lyd_insert_node(parent, NULL, node, LYD_INSERT_NODE_LAST);
Michal Vaskoa16dbb42023-08-15 15:20:25 +02001446
1447 /* update the value */
1448 opaq = (struct lyd_node_opaq *)node;
1449 if (opaq->child) {
1450 if (!ws_only) {
1451 LOGVAL(xmlctx->ctx, LYVE_SYNTAX_XML, "Mixed XML content node \"%s\" found, not supported.", LYD_NAME(node));
1452 rc = LY_EVALID;
1453 goto cleanup;
1454 }
1455 } else if (value_len) {
1456 lydict_remove(xmlctx->ctx, opaq->value);
1457 if (dynamic) {
1458 LY_CHECK_GOTO(rc = lydict_insert_zc(xmlctx->ctx, (char *)value, &opaq->value), cleanup);
1459 dynamic = 0;
1460 } else {
1461 LY_CHECK_GOTO(rc = lydict_insert(xmlctx->ctx, value, value_len, &opaq->value), cleanup);
1462 }
1463 }
Michal Vaskoe0665742021-02-11 11:08:44 +01001464
1465cleanup:
1466 lyd_free_attr_siblings(xmlctx->ctx, attr);
Michal Vaskoa16dbb42023-08-15 15:20:25 +02001467 if (dynamic) {
1468 free((char *)value);
1469 }
Michal Vaskoe0665742021-02-11 11:08:44 +01001470 if (rc) {
Michal Vaskoa16dbb42023-08-15 15:20:25 +02001471 lyd_free_tree(node);
Michal Vaskoe0665742021-02-11 11:08:44 +01001472 }
1473 return rc;
1474}
1475
1476/**
1477 * @brief Parse all expected non-data XML elements of the error-info element in NETCONF rpc-reply message.
1478 *
1479 * @param[in] xmlctx XML parser context.
1480 * @param[in] parent Parent to append nodes to.
1481 * @return LY_ERR value.
1482 */
1483static LY_ERR
1484lydxml_env_netconf_rpc_reply_error_info(struct lyxml_ctx *xmlctx, struct lyd_node *parent)
1485{
1486 LY_ERR r;
1487 struct lyd_node *child, *iter;
Michal Vaskoe0665742021-02-11 11:08:44 +01001488 ly_bool no_dup;
1489
1490 /* there must be some child */
1491 if (xmlctx->status == LYXML_ELEM_CLOSE) {
1492 LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Missing child elements of \"error-info\".");
1493 return LY_EVALID;
1494 }
1495
1496 while (xmlctx->status == LYXML_ELEMENT) {
1497 child = NULL;
1498
1499 /*
1500 * session-id
1501 */
1502 r = lydxml_envelope(xmlctx, "session-id", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child);
1503 if (r == LY_SUCCESS) {
1504 no_dup = 1;
1505 goto check_child;
1506 } else if (r != LY_ENOT) {
1507 goto error;
1508 }
1509
1510 /*
1511 * bad-attribute
1512 */
1513 r = lydxml_envelope(xmlctx, "bad-attribute", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child);
1514 if (r == LY_SUCCESS) {
1515 no_dup = 1;
1516 goto check_child;
1517 } else if (r != LY_ENOT) {
1518 goto error;
1519 }
1520
1521 /*
1522 * bad-element
1523 */
1524 r = lydxml_envelope(xmlctx, "bad-element", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child);
1525 if (r == LY_SUCCESS) {
1526 no_dup = 1;
1527 goto check_child;
1528 } else if (r != LY_ENOT) {
1529 goto error;
1530 }
1531
1532 /*
1533 * bad-namespace
1534 */
1535 r = lydxml_envelope(xmlctx, "bad-namespace", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child);
1536 if (r == LY_SUCCESS) {
1537 no_dup = 1;
1538 goto check_child;
1539 } else if (r != LY_ENOT) {
1540 goto error;
1541 }
1542
1543 if (r == LY_ENOT) {
1544 assert(xmlctx->status == LYXML_ELEMENT);
1545
Michal Vasko845ba392022-06-16 07:52:13 +02001546 /* custom elements, parse all the siblings */
1547 while (xmlctx->status == LYXML_ELEMENT) {
1548 LY_CHECK_GOTO(r = lydxml_opaq_r(xmlctx, parent), error);
1549 LY_CHECK_GOTO(r = lyxml_ctx_next(xmlctx), error);
1550 }
Michal Vasko6398eaf2022-01-10 10:12:30 +01001551 continue;
Michal Vaskoe0665742021-02-11 11:08:44 +01001552 }
1553
1554check_child:
1555 /* check for duplicates */
1556 if (no_dup) {
1557 LY_LIST_FOR(lyd_child(parent), iter) {
1558 if ((((struct lyd_node_opaq *)iter)->name.name == ((struct lyd_node_opaq *)child)->name.name) &&
1559 (((struct lyd_node_opaq *)iter)->name.module_ns == ((struct lyd_node_opaq *)child)->name.module_ns)) {
1560 LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Duplicate element \"%s\" in \"error-info\".",
1561 ((struct lyd_node_opaq *)child)->name.name);
1562 r = LY_EVALID;
1563 goto error;
1564 }
1565 }
1566 }
1567
1568 /* finish child parsing */
1569 if (xmlctx->status != LYXML_ELEM_CLOSE) {
1570 assert(xmlctx->status == LYXML_ELEMENT);
1571 LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Unexpected child element \"%.*s\" of \"error-info\".",
Radek Krejci422afb12021-03-04 16:38:16 +01001572 (int)xmlctx->name_len, xmlctx->name);
Michal Vaskoe0665742021-02-11 11:08:44 +01001573 r = LY_EVALID;
1574 goto error;
1575 }
1576 LY_CHECK_GOTO(r = lyxml_ctx_next(xmlctx), error);
1577
1578 /* insert */
aPiecek1462ab12024-02-07 09:13:29 +01001579 lyd_insert_node(parent, NULL, child, LYD_INSERT_NODE_LAST);
Michal Vaskoe0665742021-02-11 11:08:44 +01001580 }
1581
1582 return LY_SUCCESS;
1583
1584error:
1585 lyd_free_tree(child);
1586 return r;
1587}
1588
1589/**
1590 * @brief Parse all expected non-data XML elements of the rpc-error element in NETCONF rpc-reply message.
1591 *
1592 * @param[in] xmlctx XML parser context.
1593 * @param[in] parent Parent to append nodes to.
1594 * @return LY_ERR value.
1595 */
1596static LY_ERR
1597lydxml_env_netconf_rpc_reply_error(struct lyxml_ctx *xmlctx, struct lyd_node *parent)
1598{
1599 LY_ERR r;
1600 struct lyd_node *child, *iter;
1601 const char *val;
1602 ly_bool no_dup;
1603
1604 /* there must be some child */
1605 if (xmlctx->status == LYXML_ELEM_CLOSE) {
1606 LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Missing child elements of \"rpc-error\".");
1607 return LY_EVALID;
1608 }
1609
1610 while (xmlctx->status == LYXML_ELEMENT) {
1611 child = NULL;
1612
1613 /*
1614 * error-type
1615 */
1616 r = lydxml_envelope(xmlctx, "error-type", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child);
1617 if (r == LY_SUCCESS) {
1618 val = ((struct lyd_node_opaq *)child)->value;
1619 if (strcmp(val, "transport") && strcmp(val, "rpc") && strcmp(val, "protocol") && strcmp(val, "application")) {
1620 LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Invalid value \"%s\" of element \"%s\".", val,
1621 ((struct lyd_node_opaq *)child)->name.name);
1622 r = LY_EVALID;
1623 goto error;
1624 }
1625
1626 no_dup = 1;
1627 goto check_child;
1628 } else if (r != LY_ENOT) {
1629 goto error;
1630 }
1631
1632 /*
1633 * error-tag
1634 */
1635 r = lydxml_envelope(xmlctx, "error-tag", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child);
1636 if (r == LY_SUCCESS) {
1637 val = ((struct lyd_node_opaq *)child)->value;
1638 if (strcmp(val, "in-use") && strcmp(val, "invalid-value") && strcmp(val, "too-big") &&
1639 strcmp(val, "missing-attribute") && strcmp(val, "bad-attribute") &&
1640 strcmp(val, "unknown-attribute") && strcmp(val, "missing-element") && strcmp(val, "bad-element") &&
1641 strcmp(val, "unknown-element") && strcmp(val, "unknown-namespace") && strcmp(val, "access-denied") &&
1642 strcmp(val, "lock-denied") && strcmp(val, "resource-denied") && strcmp(val, "rollback-failed") &&
1643 strcmp(val, "data-exists") && strcmp(val, "data-missing") && strcmp(val, "operation-not-supported") &&
1644 strcmp(val, "operation-failed") && strcmp(val, "malformed-message")) {
1645 LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Invalid value \"%s\" of element \"%s\".", val,
1646 ((struct lyd_node_opaq *)child)->name.name);
1647 r = LY_EVALID;
1648 goto error;
1649 }
1650
1651 no_dup = 1;
1652 goto check_child;
1653 } else if (r != LY_ENOT) {
1654 goto error;
1655 }
1656
1657 /*
1658 * error-severity
1659 */
1660 r = lydxml_envelope(xmlctx, "error-severity", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child);
1661 if (r == LY_SUCCESS) {
1662 val = ((struct lyd_node_opaq *)child)->value;
1663 if (strcmp(val, "error") && strcmp(val, "warning")) {
1664 LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Invalid value \"%s\" of element \"%s\".", val,
1665 ((struct lyd_node_opaq *)child)->name.name);
1666 r = LY_EVALID;
1667 goto error;
1668 }
1669
1670 no_dup = 1;
1671 goto check_child;
1672 } else if (r != LY_ENOT) {
1673 goto error;
1674 }
1675
1676 /*
1677 * error-app-tag
1678 */
1679 r = lydxml_envelope(xmlctx, "error-app-tag", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child);
1680 if (r == LY_SUCCESS) {
1681 no_dup = 1;
1682 goto check_child;
1683 } else if (r != LY_ENOT) {
1684 goto error;
1685 }
1686
1687 /*
1688 * error-path
1689 */
1690 r = lydxml_envelope(xmlctx, "error-path", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child);
1691 if (r == LY_SUCCESS) {
1692 no_dup = 1;
1693 goto check_child;
1694 } else if (r != LY_ENOT) {
1695 goto error;
1696 }
1697
1698 /*
1699 * error-message
1700 */
1701 r = lydxml_envelope(xmlctx, "error-message", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child);
1702 if (r == LY_SUCCESS) {
1703 no_dup = 1;
1704 goto check_child;
1705 } else if (r != LY_ENOT) {
1706 goto error;
1707 }
1708
1709 /* error-info */
1710 r = lydxml_envelope(xmlctx, "error-info", "urn:ietf:params:xml:ns:netconf:base:1.0", 0, &child);
1711 if (r == LY_SUCCESS) {
1712 /* parse all the descendants */
1713 LY_CHECK_GOTO(r = lydxml_env_netconf_rpc_reply_error_info(xmlctx, child), error);
1714
1715 no_dup = 0;
1716 goto check_child;
1717 } else if (r != LY_ENOT) {
1718 goto error;
1719 }
1720
1721 if (r == LY_ENOT) {
1722 assert(xmlctx->status == LYXML_ELEMENT);
1723 LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Unexpected child element \"%.*s\" of \"rpc-error\".",
Radek Krejci422afb12021-03-04 16:38:16 +01001724 (int)xmlctx->name_len, xmlctx->name);
Michal Vaskoe0665742021-02-11 11:08:44 +01001725 r = LY_EVALID;
1726 goto error;
1727 }
1728
1729check_child:
1730 /* check for duplicates */
1731 if (no_dup) {
1732 LY_LIST_FOR(lyd_child(parent), iter) {
1733 if ((((struct lyd_node_opaq *)iter)->name.name == ((struct lyd_node_opaq *)child)->name.name) &&
1734 (((struct lyd_node_opaq *)iter)->name.module_ns == ((struct lyd_node_opaq *)child)->name.module_ns)) {
1735 LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Duplicate element \"%s\" in \"rpc-error\".",
1736 ((struct lyd_node_opaq *)child)->name.name);
1737 r = LY_EVALID;
1738 goto error;
1739 }
1740 }
1741 }
1742
1743 /* finish child parsing */
1744 if (xmlctx->status != LYXML_ELEM_CLOSE) {
1745 assert(xmlctx->status == LYXML_ELEMENT);
1746 LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Unexpected child element \"%.*s\" of \"rpc-error\".",
Radek Krejci422afb12021-03-04 16:38:16 +01001747 (int)xmlctx->name_len, xmlctx->name);
Michal Vaskoe0665742021-02-11 11:08:44 +01001748 r = LY_EVALID;
1749 goto error;
1750 }
1751 LY_CHECK_GOTO(r = lyxml_ctx_next(xmlctx), error);
1752
1753 /* insert */
aPiecek1462ab12024-02-07 09:13:29 +01001754 lyd_insert_node(parent, NULL, child, LYD_INSERT_NODE_LAST);
Michal Vaskoe0665742021-02-11 11:08:44 +01001755 }
1756
1757 return LY_SUCCESS;
1758
1759error:
1760 lyd_free_tree(child);
1761 return r;
1762}
1763
1764/**
1765 * @brief Parse all expected non-data XML elements of a NETCONF rpc-reply message.
1766 *
1767 * @param[in] xmlctx XML parser context.
1768 * @param[out] evnp Parsed envelope(s) (opaque node).
1769 * @param[out] int_opts Internal options for parsing the rest of YANG data.
1770 * @param[out] close_elem Number of parsed opened elements that need to be closed.
1771 * @return LY_SUCCESS on success.
1772 * @return LY_ERR value on error.
1773 */
1774static LY_ERR
1775lydxml_env_netconf_reply(struct lyxml_ctx *xmlctx, struct lyd_node **envp, uint32_t *int_opts, uint32_t *close_elem)
1776{
1777 LY_ERR rc = LY_SUCCESS, r;
1778 struct lyd_node *child = NULL;
1779 const char *parsed_elem = NULL;
1780
1781 assert(envp && !*envp);
1782
1783 /* parse "rpc-reply" */
1784 r = lydxml_envelope(xmlctx, "rpc-reply", "urn:ietf:params:xml:ns:netconf:base:1.0", 0, envp);
Michal Vaskoe0665742021-02-11 11:08:44 +01001785 LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
1786
1787 /* there must be some child */
1788 if (xmlctx->status == LYXML_ELEM_CLOSE) {
1789 LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Missing child elements of \"rpc-reply\".");
1790 rc = LY_EVALID;
Michal Vaskocf770e22020-08-12 13:21:43 +02001791 goto cleanup;
Michal Vaskoa8edff02020-03-27 14:47:01 +01001792 }
1793
Michal Vaskoe0665742021-02-11 11:08:44 +01001794 /* try to parse "ok" */
1795 r = lydxml_envelope(xmlctx, "ok", "urn:ietf:params:xml:ns:netconf:base:1.0", 0, &child);
1796 if (r == LY_SUCCESS) {
1797 /* insert */
aPiecek1462ab12024-02-07 09:13:29 +01001798 lyd_insert_node(*envp, NULL, child, LYD_INSERT_NODE_LAST);
Michal Vaskoe0665742021-02-11 11:08:44 +01001799
1800 /* finish child parsing */
1801 if (xmlctx->status != LYXML_ELEM_CLOSE) {
1802 assert(xmlctx->status == LYXML_ELEMENT);
1803 LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Unexpected child element \"%.*s\" of \"ok\".",
Radek Krejci422afb12021-03-04 16:38:16 +01001804 (int)xmlctx->name_len, xmlctx->name);
Michal Vaskoe0665742021-02-11 11:08:44 +01001805 rc = LY_EVALID;
1806 goto cleanup;
1807 }
1808 LY_CHECK_GOTO(rc = lyxml_ctx_next(xmlctx), cleanup);
1809
1810 /* success */
1811 parsed_elem = "ok";
1812 goto finish;
1813 } else if (r != LY_ENOT) {
1814 rc = r;
1815 goto cleanup;
Michal Vasko2552ea32020-12-08 15:32:34 +01001816 }
1817
Michal Vaskoe0665742021-02-11 11:08:44 +01001818 /* try to parse all "rpc-error" elements */
1819 while (xmlctx->status == LYXML_ELEMENT) {
1820 r = lydxml_envelope(xmlctx, "rpc-error", "urn:ietf:params:xml:ns:netconf:base:1.0", 0, &child);
1821 if (r == LY_ENOT) {
1822 break;
1823 } else if (r) {
1824 rc = r;
1825 goto cleanup;
1826 }
1827
1828 /* insert */
aPiecek1462ab12024-02-07 09:13:29 +01001829 lyd_insert_node(*envp, NULL, child, LYD_INSERT_NODE_LAST);
Michal Vaskoe0665742021-02-11 11:08:44 +01001830
1831 /* parse all children of "rpc-error" */
1832 LY_CHECK_GOTO(rc = lydxml_env_netconf_rpc_reply_error(xmlctx, child), cleanup);
1833
1834 /* finish child parsing */
1835 assert(xmlctx->status == LYXML_ELEM_CLOSE);
1836 LY_CHECK_GOTO(rc = lyxml_ctx_next(xmlctx), cleanup);
1837
1838 parsed_elem = "rpc-error";
Michal Vasko2552ea32020-12-08 15:32:34 +01001839 }
Michal Vaskoe0665742021-02-11 11:08:44 +01001840
1841finish:
1842 if (parsed_elem) {
1843 /* NETCONF rpc-reply with no data */
1844 if (xmlctx->status != LYXML_ELEM_CLOSE) {
1845 assert(xmlctx->status == LYXML_ELEMENT);
1846 LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Unexpected sibling element \"%.*s\" of \"%s\".",
Radek Krejci422afb12021-03-04 16:38:16 +01001847 (int)xmlctx->name_len, xmlctx->name, parsed_elem);
Michal Vaskoe0665742021-02-11 11:08:44 +01001848 rc = LY_EVALID;
1849 goto cleanup;
1850 }
1851 }
1852
1853 /* NETCONF rpc-reply */
Michal Vasko1d991fd2021-07-09 13:14:40 +02001854 *int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_REPLY;
Michal Vaskoe0665742021-02-11 11:08:44 +01001855 *close_elem = 1;
1856
1857cleanup:
1858 if (rc) {
1859 lyd_free_tree(*envp);
1860 *envp = NULL;
1861 }
1862 return rc;
1863}
1864
Michal Vasko2552ea32020-12-08 15:32:34 +01001865LY_ERR
Michal Vaskoe3ed7dc2022-11-30 11:39:44 +01001866lyd_parse_xml_netconf(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent,
Radek Krejcif16e2542021-02-17 15:39:23 +01001867 struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, enum lyd_type data_type,
Michal Vaskoe3ed7dc2022-11-30 11:39:44 +01001868 struct lyd_node **envp, struct ly_set *parsed, struct lyd_ctx **lydctx_p)
Michal Vasko2552ea32020-12-08 15:32:34 +01001869{
Michal Vaskoe0665742021-02-11 11:08:44 +01001870 LY_ERR rc = LY_SUCCESS;
1871 struct lyd_xml_ctx *lydctx;
Michal Vasko4a1e3e82023-09-05 08:45:01 +02001872 struct lyd_node *node;
Michal Vasko2ca9f9e2021-07-02 09:21:36 +02001873 uint32_t i, int_opts = 0, close_elem = 0;
Michal Vaskoe0665742021-02-11 11:08:44 +01001874 ly_bool parsed_data_nodes = 0;
Michal Vasko2552ea32020-12-08 15:32:34 +01001875
Michal Vaskoe0665742021-02-11 11:08:44 +01001876 assert(ctx && in && lydctx_p);
1877 assert(!(parse_opts & ~LYD_PARSE_OPTS_MASK));
1878 assert(!(val_opts & ~LYD_VALIDATE_OPTS_MASK));
Michal Vaskoe3ed7dc2022-11-30 11:39:44 +01001879 assert(!(parse_opts & LYD_PARSE_SUBTREE));
1880
Michal Vaskoe0665742021-02-11 11:08:44 +01001881 /* init context */
1882 lydctx = calloc(1, sizeof *lydctx);
1883 LY_CHECK_ERR_RET(!lydctx, LOGMEM(ctx), LY_EMEM);
1884 LY_CHECK_GOTO(rc = lyxml_ctx_new(ctx, in, &lydctx->xmlctx), cleanup);
1885 lydctx->parse_opts = parse_opts;
1886 lydctx->val_opts = val_opts;
1887 lydctx->free = lyd_xml_ctx_free;
Radek Krejcif16e2542021-02-17 15:39:23 +01001888 lydctx->ext = ext;
Michal Vasko2552ea32020-12-08 15:32:34 +01001889
Michal Vaskoe0665742021-02-11 11:08:44 +01001890 switch (data_type) {
Michal Vasko1e4c68e2021-02-18 15:03:01 +01001891 case LYD_TYPE_RPC_NETCONF:
Michal Vaskoe0665742021-02-11 11:08:44 +01001892 assert(!parent);
Michal Vaskod71c2d42022-04-29 09:50:44 +02001893 rc = lydxml_env_netconf_rpc(lydctx->xmlctx, envp, &int_opts, &close_elem);
1894 if (rc == LY_ENOT) {
1895 LOGVAL(ctx, LYVE_DATA, "Missing NETCONF <rpc> envelope or in incorrect namespace.");
1896 }
1897 LY_CHECK_GOTO(rc, cleanup);
Michal Vaskoe0665742021-02-11 11:08:44 +01001898 break;
Michal Vasko1e4c68e2021-02-18 15:03:01 +01001899 case LYD_TYPE_NOTIF_NETCONF:
1900 assert(!parent);
Michal Vasko4a1e3e82023-09-05 08:45:01 +02001901
1902 /* parse "notification" */
1903 rc = lydxml_envelope(lydctx->xmlctx, "notification", "urn:ietf:params:xml:ns:netconf:notification:1.0", 0, envp);
Michal Vaskod71c2d42022-04-29 09:50:44 +02001904 if (rc == LY_ENOT) {
1905 LOGVAL(ctx, LYVE_DATA, "Missing NETCONF <notification> envelope or in incorrect namespace.");
1906 }
1907 LY_CHECK_GOTO(rc, cleanup);
Michal Vasko4a1e3e82023-09-05 08:45:01 +02001908
1909 /* NETCONF notification */
1910 int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_NOTIF | LYD_INTOPT_EVENTTIME;
1911 close_elem = 1;
Michal Vasko1e4c68e2021-02-18 15:03:01 +01001912 break;
1913 case LYD_TYPE_REPLY_NETCONF:
Michal Vaskoe0665742021-02-11 11:08:44 +01001914 assert(parent);
Michal Vaskod71c2d42022-04-29 09:50:44 +02001915 rc = lydxml_env_netconf_reply(lydctx->xmlctx, envp, &int_opts, &close_elem);
1916 if (rc == LY_ENOT) {
1917 LOGVAL(ctx, LYVE_DATA, "Missing NETCONF <rpc-reply> envelope or in incorrect namespace.");
1918 }
1919 LY_CHECK_GOTO(rc, cleanup);
Michal Vaskoe0665742021-02-11 11:08:44 +01001920 break;
Michal Vasko820efe82023-05-12 15:47:43 +02001921 case LYD_TYPE_RPC_RESTCONF:
1922 assert(parent);
1923
1924 /* parse "input" */
Michal Vasko420cc252023-08-24 08:14:24 +02001925 rc = lydxml_envelope(lydctx->xmlctx, "input", lyd_node_module(parent)->ns, 0, envp);
Michal Vasko820efe82023-05-12 15:47:43 +02001926 if (rc == LY_ENOT) {
1927 LOGVAL(ctx, LYVE_DATA, "Missing RESTCONF \"input\" object or in incorrect namespace.");
1928 }
1929 LY_CHECK_GOTO(rc, cleanup);
1930
1931 int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_RPC | LYD_INTOPT_ACTION;
1932 close_elem = 1;
1933 break;
1934 case LYD_TYPE_REPLY_RESTCONF:
1935 assert(parent);
1936
1937 /* parse "output" */
Michal Vasko420cc252023-08-24 08:14:24 +02001938 rc = lydxml_envelope(lydctx->xmlctx, "output", lyd_node_module(parent)->ns, 0, envp);
Michal Vasko820efe82023-05-12 15:47:43 +02001939 if (rc == LY_ENOT) {
1940 LOGVAL(ctx, LYVE_DATA, "Missing RESTCONF \"output\" object or in incorrect namespace.");
1941 }
1942 LY_CHECK_GOTO(rc, cleanup);
1943
1944 int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_REPLY;
1945 close_elem = 1;
1946 break;
Michal Vaskoe3ed7dc2022-11-30 11:39:44 +01001947 default:
1948 LOGINT(ctx);
1949 rc = LY_EINT;
1950 goto cleanup;
Michal Vaskoe0665742021-02-11 11:08:44 +01001951 }
Michal Vaskoe3ed7dc2022-11-30 11:39:44 +01001952
Michal Vaskoe0665742021-02-11 11:08:44 +01001953 lydctx->int_opts = int_opts;
1954
1955 /* find the operation node if it exists already */
1956 LY_CHECK_GOTO(rc = lyd_parser_find_operation(parent, int_opts, &lydctx->op_node), cleanup);
1957
1958 /* parse XML data */
1959 while (lydctx->xmlctx->status == LYXML_ELEMENT) {
1960 LY_CHECK_GOTO(rc = lydxml_subtree_r(lydctx, parent, first_p, parsed), cleanup);
1961 parsed_data_nodes = 1;
1962
1963 if (!(int_opts & LYD_INTOPT_WITH_SIBLINGS)) {
1964 break;
1965 }
Michal Vasko2552ea32020-12-08 15:32:34 +01001966 }
1967
Michal Vaskoe0665742021-02-11 11:08:44 +01001968 /* close all opened elements */
1969 for (i = 0; i < close_elem; ++i) {
1970 if (lydctx->xmlctx->status != LYXML_ELEM_CLOSE) {
1971 assert(lydctx->xmlctx->status == LYXML_ELEMENT);
Radek Krejci422afb12021-03-04 16:38:16 +01001972 LOGVAL(lydctx->xmlctx->ctx, LYVE_SYNTAX, "Unexpected child element \"%.*s\".",
1973 (int)lydctx->xmlctx->name_len, lydctx->xmlctx->name);
Michal Vaskoe0665742021-02-11 11:08:44 +01001974 rc = LY_EVALID;
1975 goto cleanup;
1976 }
1977
1978 LY_CHECK_GOTO(rc = lyxml_ctx_next(lydctx->xmlctx), cleanup);
Michal Vasko4189c0f2020-08-13 09:05:22 +02001979 }
Michal Vaskoe0665742021-02-11 11:08:44 +01001980
1981 /* check final state */
1982 if ((int_opts & LYD_INTOPT_NO_SIBLINGS) && (lydctx->xmlctx->status == LYXML_ELEMENT)) {
1983 LOGVAL(ctx, LYVE_SYNTAX, "Unexpected sibling node.");
1984 rc = LY_EVALID;
1985 goto cleanup;
1986 }
Michal Vaskoc939fdd2022-01-03 11:35:13 +01001987 if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) && !lydctx->op_node) {
Michal Vaskoe0665742021-02-11 11:08:44 +01001988 LOGVAL(ctx, LYVE_DATA, "Missing the operation node.");
1989 rc = LY_EVALID;
1990 goto cleanup;
1991 }
Michal Vasko4a1e3e82023-09-05 08:45:01 +02001992 if (int_opts & LYD_INTOPT_EVENTTIME) {
1993 /* parse as a child of the envelope */
1994 node = (*first_p)->prev;
1995 if (node->schema) {
1996 LOGVAL(ctx, LYVE_DATA, "Missing notification \"eventTime\" node.");
1997 rc = LY_EVALID;
1998 goto cleanup;
1999 } else {
2000 /* can be the only opaque node and an operation had to be parsed */
2001 assert(!strcmp(LYD_NAME(node), "eventTime") && (*first_p)->next);
Michal Vasko2e784f82024-01-11 09:51:22 +01002002 lyd_unlink(node);
Michal Vasko4a1e3e82023-09-05 08:45:01 +02002003 assert(*envp);
2004 lyd_insert_child(*envp, node);
2005 }
2006 }
Michal Vaskoe0665742021-02-11 11:08:44 +01002007
2008 if (!parsed_data_nodes) {
2009 /* no data nodes were parsed */
2010 lydctx->op_node = NULL;
Michal Vaskoa8edff02020-03-27 14:47:01 +01002011 }
2012
2013cleanup:
Michal Vaskoe0665742021-02-11 11:08:44 +01002014 /* there should be no unres stored if validation should be skipped */
2015 assert(!(parse_opts & LYD_PARSE_ONLY) || (!lydctx->node_types.count && !lydctx->meta_types.count &&
Michal Vaskoddd76592022-01-17 13:34:48 +01002016 !lydctx->node_when.count));
Michal Vaskoe0665742021-02-11 11:08:44 +01002017
2018 if (rc) {
2019 lyd_xml_ctx_free((struct lyd_ctx *)lydctx);
2020 } else {
2021 *lydctx_p = (struct lyd_ctx *)lydctx;
2022
2023 /* the XML context is no more needed, freeing it also stops logging line numbers which would be confusing now */
2024 lyxml_ctx_free(lydctx->xmlctx);
2025 lydctx->xmlctx = NULL;
Michal Vasko1ce933a2020-03-30 12:38:22 +02002026 }
Michal Vaskoe0665742021-02-11 11:08:44 +01002027 return rc;
Michal Vasko1ce933a2020-03-30 12:38:22 +02002028}