1 /**
2  * @file parser_xml.c
3  * @author Radek Krejci <rkrejci@cesnet.cz>
4  * @brief XML data parser for libyang
5  *
6  * Copyright (c) 2015 - 2017 CESNET, z.s.p.o.
7  *
8  * This source code is licensed under BSD 3-Clause License (the "License").
9  * You may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     https://opensource.org/licenses/BSD-3-Clause
13  */
14 
15 #include <assert.h>
16 #include <ctype.h>
17 #include <errno.h>
18 #include <stdarg.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <limits.h>
22 
23 #include "libyang.h"
24 #include "common.h"
25 #include "context.h"
26 #include "parser.h"
27 #include "tree_internal.h"
28 #include "validation.h"
29 #include "xml_internal.h"
30 
31 /* does not log */
32 static struct lys_node *
xml_data_search_schemanode(struct lyxml_elem * xml,struct lys_node * start,int options)33 xml_data_search_schemanode(struct lyxml_elem *xml, struct lys_node *start, int options)
34 {
35     struct lys_node *result, *aux;
36 
37     LY_TREE_FOR(start, result) {
38         /* skip groupings ... */
39         if (result->nodetype == LYS_GROUPING) {
40             continue;
41         /* ... and output in case of RPC ... */
42         } else if (result->nodetype == LYS_OUTPUT && (options & LYD_OPT_RPC)) {
43             continue;
44         /* ... and input in case of RPC reply */
45         } else if (result->nodetype == LYS_INPUT && (options & LYD_OPT_RPCREPLY)) {
46             continue;
47         }
48 
49         /* go into cases, choices, uses and in RPCs into input and output */
50         if (result->nodetype & (LYS_CHOICE | LYS_CASE | LYS_USES | LYS_INPUT | LYS_OUTPUT)) {
51             aux = xml_data_search_schemanode(xml, result->child, options);
52             if (aux) {
53                 /* we have matching result */
54                 return aux;
55             }
56             /* else, continue with next schema node */
57             continue;
58         }
59 
60         /* match data nodes */
61         if (ly_strequal(result->name, xml->name, 1)) {
62             /* names matches, what about namespaces? */
63             if (ly_strequal(lys_main_module(result->module)->ns, xml->ns->value, 1)) {
64                 /* we have matching result */
65                 return result;
66             }
67             /* else, continue with next schema node */
68             continue;
69         }
70     }
71 
72     /* no match */
73     return NULL;
74 }
75 
76 /* logs directly */
77 static int
xml_get_value(struct lyd_node * node,struct lyxml_elem * xml,int editbits)78 xml_get_value(struct lyd_node *node, struct lyxml_elem *xml, int editbits)
79 {
80     struct lyd_node_leaf_list *leaf = (struct lyd_node_leaf_list *)node;
81 
82     assert(node && (node->schema->nodetype & (LYS_LEAFLIST | LYS_LEAF)) && xml);
83 
84     leaf->value_str = lydict_insert(node->schema->module->ctx, xml->content, 0);
85 
86     if ((editbits & 0x20) && (node->schema->nodetype & LYS_LEAF) && (!leaf->value_str || !leaf->value_str[0])) {
87         /* we have edit-config leaf/leaf-list with delete operation and no (empty) value,
88          * this is not a bug since the node is just used as a kind of selection node */
89         leaf->value_type = LY_TYPE_UNKNOWN;
90         return EXIT_SUCCESS;
91     }
92 
93     /* the value is here converted to a JSON format if needed in case of LY_TYPE_IDENT and LY_TYPE_INST or to a
94      * canonical form of the value */
95     if (!lyp_parse_value(&((struct lys_node_leaf *)leaf->schema)->type, &leaf->value_str, xml, leaf, NULL, NULL, 1, 0)) {
96         return EXIT_FAILURE;
97     }
98 
99     return EXIT_SUCCESS;
100 }
101 
102 /* logs directly */
103 static int
xml_parse_data(struct ly_ctx * ctx,struct lyxml_elem * xml,struct lyd_node * parent,struct lyd_node * first_sibling,struct lyd_node * prev,int options,struct unres_data * unres,struct lyd_node ** result,struct lyd_node ** act_notif,const char * yang_data_name)104 xml_parse_data(struct ly_ctx *ctx, struct lyxml_elem *xml, struct lyd_node *parent, struct lyd_node *first_sibling,
105                struct lyd_node *prev, int options, struct unres_data *unres, struct lyd_node **result,
106                struct lyd_node **act_notif, const char *yang_data_name)
107 {
108     const struct lys_module *mod = NULL;
109     struct lyd_node *diter, *dlast;
110     struct lys_node *schema = NULL, *target;
111     const struct lys_node *ext_node;
112     struct lys_node_augment *aug;
113     struct lyd_attr *dattr, *dattr_iter;
114     struct lyxml_attr *attr;
115     struct lyxml_elem *child, *next;
116     int i, j, havechildren, r, editbits = 0, filterflag = 0, found;
117     uint8_t pos;
118     int ret = 0;
119     const char *str = NULL;
120     char *msg;
121 
122     assert(xml);
123     assert(result);
124     *result = NULL;
125 
126     if (xml->flags & LYXML_ELEM_MIXED) {
127         if (options & LYD_OPT_STRICT) {
128             LOGVAL(ctx, LYE_XML_INVAL, LY_VLOG_XML, xml, "XML element with mixed content");
129             return -1;
130         } else {
131             return 0;
132         }
133     }
134 
135     if (!xml->ns || !xml->ns->value) {
136         if (options & LYD_OPT_STRICT) {
137             LOGVAL(ctx, LYE_XML_MISS, LY_VLOG_XML, xml, "element's", "namespace");
138             return -1;
139         } else {
140             return 0;
141         }
142     }
143 
144     /* find schema node */
145     if (!parent) {
146         mod = ly_ctx_get_module_by_ns(ctx, xml->ns->value, NULL, 0);
147         if (ctx->data_clb) {
148             if (!mod) {
149                 mod = ctx->data_clb(ctx, NULL, xml->ns->value, 0, ctx->data_clb_data);
150             } else if (!mod->implemented) {
151                 mod = ctx->data_clb(ctx, mod->name, mod->ns, LY_MODCLB_NOT_IMPLEMENTED, ctx->data_clb_data);
152             }
153         }
154 
155         /* get the proper schema node */
156         if (mod && mod->implemented && !mod->disabled) {
157             if (options & LYD_OPT_DATA_TEMPLATE) {
158                 if (yang_data_name) {
159                     ext_node = lyp_get_yang_data_template(mod, yang_data_name, strlen(yang_data_name));
160                     if (ext_node) {
161                         schema = *((struct lys_node **) lys_ext_complex_get_substmt(LY_STMT_CONTAINER, (struct lys_ext_instance_complex *)ext_node, NULL));
162                         schema = xml_data_search_schemanode(xml, schema, options);
163                     }
164                 }
165             } else {
166                 schema = xml_data_search_schemanode(xml, mod->data, options);
167                 if (!schema) {
168                     /* it still can be the specific case of this module containing an augment of another module
169                     * top-level choice or top-level choice's case, bleh */
170                     for (j = 0; j < mod->augment_size; ++j) {
171                         aug = &mod->augment[j];
172                         target = aug->target;
173                         if (target->nodetype & (LYS_CHOICE | LYS_CASE)) {
174                             /* 1) okay, the target is choice or case */
175                             while (target && (target->nodetype & (LYS_CHOICE | LYS_CASE | LYS_USES))) {
176                                 target = lys_parent(target);
177                             }
178                             /* 2) now, the data node will be top-level, there are only non-data schema nodes */
179                             if (!target) {
180                                 while ((schema = (struct lys_node *) lys_getnext(schema, (struct lys_node *) aug, NULL, 0))) {
181                                     /* 3) alright, even the name matches, we found our schema node */
182                                     if (ly_strequal(schema->name, xml->name, 1)) {
183                                         break;
184                                     }
185                                 }
186                             }
187                         }
188 
189                         if (schema) {
190                             break;
191                         }
192                     }
193                 }
194             }
195         }
196     } else {
197         /* parsing some internal node, we start with parent's schema pointer */
198         schema = xml_data_search_schemanode(xml, parent->schema->child, options);
199 
200         if (ctx->data_clb) {
201             if (schema && !lys_node_module(schema)->implemented) {
202                 ctx->data_clb(ctx, lys_node_module(schema)->name, lys_node_module(schema)->ns,
203                               LY_MODCLB_NOT_IMPLEMENTED, ctx->data_clb_data);
204             } else if (!schema) {
205                 if (ctx->data_clb(ctx, NULL, xml->ns->value, 0, ctx->data_clb_data)) {
206                     /* context was updated, so try to find the schema node again */
207                     schema = xml_data_search_schemanode(xml, parent->schema->child, options);
208                 }
209             }
210         }
211     }
212 
213     mod = lys_node_module(schema);
214     if (!mod || !mod->implemented || mod->disabled) {
215         if (options & LYD_OPT_STRICT) {
216             LOGVAL(ctx, LYE_INELEM, (parent ? LY_VLOG_LYD : LY_VLOG_STR), (parent ? (void *)parent : (void *)"/") , xml->name);
217             return -1;
218         } else {
219             return 0;
220         }
221     }
222 
223     /* create the element structure */
224     switch (schema->nodetype) {
225     case LYS_CONTAINER:
226     case LYS_LIST:
227     case LYS_NOTIF:
228     case LYS_RPC:
229     case LYS_ACTION:
230         for (i = 0; xml->content && xml->content[i]; ++i) {
231             if (!is_xmlws(xml->content[i])) {
232                 msg = malloc(22 + strlen(xml->content) + 1);
233                 LY_CHECK_ERR_RETURN(!msg, LOGMEM(ctx), -1);
234                 sprintf(msg, "node with text data \"%s\"", xml->content);
235                 LOGVAL(ctx, LYE_XML_INVAL, LY_VLOG_XML, xml, msg);
236                 free(msg);
237                 return -1;
238             }
239         }
240         *result = calloc(1, sizeof **result);
241         havechildren = 1;
242         break;
243     case LYS_LEAF:
244     case LYS_LEAFLIST:
245         *result = calloc(1, sizeof(struct lyd_node_leaf_list));
246         havechildren = 0;
247         break;
248     case LYS_ANYXML:
249     case LYS_ANYDATA:
250         *result = calloc(1, sizeof(struct lyd_node_anydata));
251         havechildren = 0;
252         break;
253     default:
254         LOGINT(ctx);
255         return -1;
256     }
257     LY_CHECK_ERR_RETURN(!(*result), LOGMEM(ctx), -1);
258 
259     (*result)->prev = *result;
260     (*result)->schema = schema;
261     (*result)->parent = parent;
262     diter = NULL;
263     if (schema->nodetype == LYS_LEAF && lys_is_key((struct lys_node_leaf *)schema, &pos)) {
264         /* it is key and we need to insert it into a correct place (a key must have a parent list) */
265         assert(parent);
266         for (i = 0, diter = parent->child;
267                 diter && i < pos && diter->schema->nodetype == LYS_LEAF && lys_is_key((struct lys_node_leaf *)diter->schema, NULL);
268                 i++, diter = diter->next);
269         if (diter) {
270             /* out of order insertion - insert list's key to the correct position, before the diter */
271             if (options & LYD_OPT_STRICT) {
272                 LOGVAL(ctx, LYE_INORDER, LY_VLOG_LYD, *result, schema->name, diter->schema->name);
273                 LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Invalid position of the key \"%s\" in a list \"%s\".",
274                        schema->name, parent->schema->name);
275                 free(*result);
276                 *result = NULL;
277                 return -1;
278             } else {
279                 LOGWRN(ctx, "Invalid position of the key \"%s\" in a list \"%s\".", schema->name, parent->schema->name)
280             }
281             if (parent->child == diter) {
282                 parent->child = *result;
283                 /* update first_sibling */
284                 first_sibling = *result;
285             }
286             if (diter->prev->next) {
287                 diter->prev->next = *result;
288             }
289             (*result)->prev = diter->prev;
290             diter->prev = *result;
291             (*result)->next = diter;
292         }
293     }
294     if (!diter) {
295         /* simplified (faster) insert as the last node */
296         if (parent && !parent->child) {
297             parent->child = *result;
298         }
299         if (prev) {
300             (*result)->prev = prev;
301             prev->next = *result;
302 
303             /* fix the "last" pointer */
304             first_sibling->prev = *result;
305         } else {
306             (*result)->prev = *result;
307             first_sibling = *result;
308         }
309     }
310     (*result)->validity = ly_new_node_validity((*result)->schema);
311     if (resolve_applies_when(schema, 0, NULL)) {
312         (*result)->when_status = LYD_WHEN;
313     }
314 
315     /* process attributes */
316     for (attr = xml->attr; attr; attr = attr->next) {
317         if (attr->type != LYXML_ATTR_STD) {
318             continue;
319         } else if (!attr->ns) {
320             if ((*result)->schema->nodetype == LYS_ANYXML &&
321                     ly_strequal((*result)->schema->name, "filter", 0) &&
322                     (ly_strequal((*result)->schema->module->name, "ietf-netconf", 0) ||
323                     ly_strequal((*result)->schema->module->name, "notifications", 0))) {
324                 /* NETCONF filter's attributes, which we implement as non-standard annotations,
325                  * they are unqualified (no namespace), but we know that we have internally defined
326                  * them in the ietf-netconf module */
327                 str = LY_NSNC;
328                 filterflag = 1;
329             } else {
330                 /* garbage */
331                 goto attr_error;
332             }
333         } else {
334             str = attr->ns->value;
335         }
336 
337         r = lyp_fill_attr(ctx, *result, str, NULL, attr->name, attr->value, xml, &dattr);
338         if (r == -1) {
339             goto unlink_node_error;
340         } else if (r == 1) {
341 attr_error:
342             if (!strcmp(attr->name, "default") && !strcmp(attr->ns->value, "urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults")) {
343                 /* we do not need to parse this attribute, just skip it */
344                 continue;
345             }
346 
347             if (options & LYD_OPT_STRICT) {
348                 LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, *result, attr->name);
349                 goto unlink_node_error;
350             }
351 
352             LOGWRN(ctx, "Unknown \"%s:%s\" metadata with value \"%s\", ignoring.",
353                    (attr->ns ? attr->ns->prefix : "<none>"), attr->name, attr->value);
354             continue;
355         }
356 
357         /* special case of xpath in the value, we want to convert it to JSON */
358         if (filterflag && !strcmp(attr->name, "select")) {
359             dattr->value.string = transform_xml2json(ctx, dattr->value_str, xml, 0, 0);
360             if (!dattr->value.string) {
361                 /* problem with resolving value as xpath */
362                 dattr->value.string = dattr->value_str;
363                 lyd_free_attr(ctx, NULL, dattr, 0);
364                 goto unlink_node_error;
365             }
366             lydict_remove(ctx, dattr->value_str);
367             dattr->value_str = dattr->value.string;
368         }
369 
370         /* insert into the data node */
371         if (!(*result)->attr) {
372             (*result)->attr = dattr;
373         } else {
374             for (dattr_iter = (*result)->attr; dattr_iter->next; dattr_iter = dattr_iter->next);
375             dattr_iter->next = dattr;
376         }
377         continue;
378     }
379 
380     /* check insert attribute and its values */
381     if (options & LYD_OPT_EDIT) {
382         if (lyp_check_edit_attr(ctx, (*result)->attr, *result, &editbits)) {
383             goto unlink_node_error;
384         }
385 
386     /* check correct filter extension attributes */
387     } else if (filterflag) {
388         found = 0; /* 0 - nothing, 1 - type subtree, 2 - type xpath, 3 - select, 4 - type xpath + select */
389         LY_TREE_FOR((*result)->attr, dattr_iter) {
390             if (!strcmp(dattr_iter->name, "type")) {
391                 if ((found == 1) || (found == 2) || (found == 4)) {
392                     LOGVAL(ctx, LYE_TOOMANY, LY_VLOG_LYD, (*result), "type", xml->name);
393                     goto unlink_node_error;
394                 }
395                 switch (dattr_iter->value.enm->value) {
396                 case 0:
397                     /* subtree */
398                     if (found == 3) {
399                         LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, (*result), dattr_iter->name);
400                         goto unlink_node_error;
401                     }
402 
403                     assert(!found);
404                     found = 1;
405                     break;
406                 case 1:
407                     /* xpath */
408                     if (found == 3) {
409                         found = 4;
410                     } else {
411                         assert(!found);
412                         found = 2;
413                     }
414                     break;
415                 default:
416                     LOGINT(ctx);
417                     goto unlink_node_error;
418                 }
419             } else if (!strcmp(dattr_iter->name, "select")) {
420                 switch (found) {
421                 case 0:
422                     found = 3;
423                     break;
424                 case 1:
425                     LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, (*result), dattr_iter->name);
426                     goto unlink_node_error;
427                 case 2:
428                     found = 4;
429                     break;
430                 case 3:
431                 case 4:
432                     LOGVAL(ctx, LYE_TOOMANY, LY_VLOG_LYD, (*result), "select", xml->name);
433                     goto unlink_node_error;
434                 default:
435                     LOGINT(ctx);
436                     goto unlink_node_error;
437                 }
438             }
439         }
440 
441         /* check if what we found is correct */
442         switch (found) {
443         case 1:
444         case 4:
445             /* ok */
446             break;
447         case 2:
448             LOGVAL(ctx, LYE_MISSATTR, LY_VLOG_LYD, (*result), "select", xml->name);
449             goto unlink_node_error;
450         case 3:
451             LOGVAL(ctx, LYE_MISSATTR, LY_VLOG_LYD, (*result), "type", xml->name);
452             goto unlink_node_error;
453         default:
454             LOGINT(ctx);
455             goto unlink_node_error;
456         }
457     }
458 
459     /* type specific processing */
460     if (schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
461         /* type detection and assigning the value */
462         if (xml_get_value(*result, xml, editbits)) {
463             goto unlink_node_error;
464         }
465     } else if (schema->nodetype & LYS_ANYDATA) {
466         /* store children values */
467         if (xml->child) {
468             child = xml->child;
469             /* manually unlink all siblings and correct namespaces */
470             xml->child = NULL;
471             LY_TREE_FOR(child, next) {
472                 next->parent = NULL;
473                 lyxml_correct_elem_ns(ctx, next, xml, 1, 1);
474             }
475 
476             ((struct lyd_node_anydata *)*result)->value_type = LYD_ANYDATA_XML;
477             ((struct lyd_node_anydata *)*result)->value.xml = child;
478         } else {
479             ((struct lyd_node_anydata *)*result)->value_type = LYD_ANYDATA_CONSTSTRING;
480             ((struct lyd_node_anydata *)*result)->value.str = lydict_insert(ctx, xml->content, 0);
481         }
482     } else if (schema->nodetype & (LYS_RPC | LYS_ACTION)) {
483         if (!(options & LYD_OPT_RPC) || *act_notif) {
484             LOGVAL(ctx, LYE_INELEM, LY_VLOG_LYD, (*result), schema->name);
485             LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Unexpected %s node \"%s\".",
486                    (schema->nodetype == LYS_RPC ? "rpc" : "action"), schema->name);
487             goto unlink_node_error;
488         }
489         *act_notif = *result;
490     } else if (schema->nodetype == LYS_NOTIF) {
491         if (!(options & (LYD_OPT_NOTIF | LYD_OPT_NOTIF_FILTER)) || *act_notif) {
492             LOGVAL(ctx, LYE_INELEM, LY_VLOG_LYD, (*result), schema->name);
493             LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Unexpected notification node \"%s\".", schema->name);
494             goto unlink_node_error;
495         }
496         *act_notif = *result;
497     }
498 
499 #ifdef LY_ENABLED_CACHE
500     /* calculate the hash and insert it into parent (list with keys is handled when its keys are inserted) */
501     if (((*result)->schema->nodetype != LYS_LIST) || !((struct lys_node_list *)(*result)->schema)->keys_size) {
502         lyd_hash(*result);
503         lyd_insert_hash(*result);
504     }
505 #endif
506 
507     /* first part of validation checks */
508     if (lyv_data_context(*result, options, 1, unres)) {
509         goto error;
510     }
511 
512     /* process children */
513     if (havechildren && xml->child) {
514         diter = dlast = NULL;
515         LY_TREE_FOR_SAFE(xml->child, next, child) {
516             r = xml_parse_data(ctx, child, *result, (*result)->child, dlast, options, unres, &diter, act_notif, yang_data_name);
517             if (r) {
518                 goto error;
519             } else if (options & LYD_OPT_DESTRUCT) {
520                 lyxml_free(ctx, child);
521             }
522             if (diter && !diter->next) {
523                 /* the child was parsed/created and it was placed as the last child. The child can be inserted
524                  * out of order (not as the last one) in case it is a list's key present out of the correct order */
525                 dlast = diter;
526             }
527         }
528     }
529 
530     /* if we have empty non-presence container, we keep it, but mark it as default */
531     if (schema->nodetype == LYS_CONTAINER && !(*result)->child &&
532             !(*result)->attr && !((struct lys_node_container *)schema)->presence) {
533         (*result)->dflt = 1;
534     }
535 
536     /* rest of validation checks */
537     if (lyv_data_content(*result, options, unres) ||
538             lyv_multicases(*result, NULL, prev ? &first_sibling : NULL, 0, NULL)) {
539         goto error;
540     }
541 
542     /* validation successful */
543     if ((*result)->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
544         /* postpone checking when there will be all list/leaflist instances */
545         (*result)->validity |= LYD_VAL_DUP;
546     }
547 
548     return ret;
549 
550 unlink_node_error:
551     lyd_unlink_internal(*result, 2);
552 error:
553     /* cleanup */
554     for (i = unres->count - 1; i >= 0; i--) {
555         /* remove unres items connected with the node being removed */
556         if (unres->node[i] == *result) {
557             unres_data_del(unres, i);
558         }
559     }
560     lyd_free(*result);
561     *result = NULL;
562     return -1;
563 }
564 
565 API struct lyd_node *
lyd_parse_xml(struct ly_ctx * ctx,struct lyxml_elem ** root,int options,...)566 lyd_parse_xml(struct ly_ctx *ctx, struct lyxml_elem **root, int options, ...)
567 {
568     FUN_IN;
569 
570     va_list ap;
571     int r;
572     struct unres_data *unres = NULL;
573     const struct lyd_node *rpc_act = NULL, *data_tree = NULL;
574     struct lyd_node *result = NULL, *iter, *last, *reply_parent = NULL, *reply_top = NULL, *act_notif = NULL;
575     struct lyxml_elem *xmlstart, *xmlelem, *xmlaux, *xmlfree = NULL;
576     const char *yang_data_name = NULL;
577 
578     if (!ctx || !root) {
579         LOGARG;
580         return NULL;
581     }
582 
583     if (lyp_data_check_options(ctx, options, __func__)) {
584         return NULL;
585     }
586 
587     if (!(*root) && !(options & LYD_OPT_RPCREPLY)) {
588         /* empty tree */
589         if (options & (LYD_OPT_RPC | LYD_OPT_NOTIF)) {
590             /* error, top level node identify RPC and Notification */
591             LOGERR(ctx, LY_EINVAL, "%s: *root identifies RPC/Notification so it cannot be NULL.", __func__);
592             return NULL;
593         } else if (!(options & LYD_OPT_RPCREPLY)) {
594             /* others - no work is needed, just check for missing mandatory nodes */
595             lyd_validate(&result, options, ctx);
596             return result;
597         }
598         /* continue with empty RPC reply, for which we need RPC */
599     }
600 
601     unres = calloc(1, sizeof *unres);
602     LY_CHECK_ERR_RETURN(!unres, LOGMEM(ctx), NULL);
603 
604     va_start(ap, options);
605     if (options & LYD_OPT_RPCREPLY) {
606         rpc_act = va_arg(ap, const struct lyd_node *);
607         if (!rpc_act || rpc_act->parent || !(rpc_act->schema->nodetype & (LYS_RPC | LYS_LIST | LYS_CONTAINER))) {
608             LOGERR(ctx, LY_EINVAL, "%s: invalid variable parameter (const struct lyd_node *rpc_act).", __func__);
609             goto error;
610         }
611         if (rpc_act->schema->nodetype == LYS_RPC) {
612             /* RPC request */
613             reply_top = reply_parent = _lyd_new(NULL, rpc_act->schema, 0);
614         } else {
615             /* action request */
616             reply_top = lyd_dup(rpc_act, 1);
617             LY_TREE_DFS_BEGIN(reply_top, iter, reply_parent) {
618                 if (reply_parent->schema->nodetype == LYS_ACTION) {
619                     break;
620                 }
621                 LY_TREE_DFS_END(reply_top, iter, reply_parent);
622             }
623             if (!reply_parent) {
624                 LOGERR(ctx, LY_EINVAL, "%s: invalid variable parameter (const struct lyd_node *rpc_act).", __func__);
625                 lyd_free_withsiblings(reply_top);
626                 goto error;
627             }
628             lyd_free_withsiblings(reply_parent->child);
629         }
630     }
631     if (options & (LYD_OPT_RPC | LYD_OPT_NOTIF | LYD_OPT_RPCREPLY)) {
632         data_tree = va_arg(ap, const struct lyd_node *);
633         if (data_tree) {
634             if (options & LYD_OPT_NOEXTDEPS) {
635                 LOGERR(ctx, LY_EINVAL, "%s: invalid parameter (variable arg const struct lyd_node *data_tree and LYD_OPT_NOEXTDEPS set).",
636                        __func__);
637                 goto error;
638             }
639 
640             LY_TREE_FOR((struct lyd_node *)data_tree, iter) {
641                 if (iter->parent) {
642                     /* a sibling is not top-level */
643                     LOGERR(ctx, LY_EINVAL, "%s: invalid variable parameter (const struct lyd_node *data_tree).", __func__);
644                     goto error;
645                 }
646             }
647 
648             /* move it to the beginning */
649             for (; data_tree->prev->next; data_tree = data_tree->prev);
650 
651             /* LYD_OPT_NOSIBLINGS cannot be set in this case */
652             if (options & LYD_OPT_NOSIBLINGS) {
653                 LOGERR(ctx, LY_EINVAL, "%s: invalid parameter (variable arg const struct lyd_node *data_tree with LYD_OPT_NOSIBLINGS).", __func__);
654                 goto error;
655             }
656         }
657     }
658     if (options & LYD_OPT_DATA_TEMPLATE) {
659         yang_data_name = va_arg(ap, const char *);
660     }
661 
662     if ((*root) && !(options & LYD_OPT_NOSIBLINGS)) {
663         /* locate the first root to process */
664         if ((*root)->parent) {
665             xmlstart = (*root)->parent->child;
666         } else {
667             xmlstart = *root;
668             while(xmlstart->prev->next) {
669                 xmlstart = xmlstart->prev;
670             }
671         }
672     } else {
673         xmlstart = *root;
674     }
675 
676     if ((options & LYD_OPT_RPC)
677             && !strcmp(xmlstart->name, "action")
678             && xmlstart->ns && !strcmp(xmlstart->ns->value, LY_NSYANG)) {
679         /* it's an action, not a simple RPC */
680         xmlstart = xmlstart->child;
681         if (options & LYD_OPT_DESTRUCT) {
682             /* free it later */
683             xmlfree = xmlstart->parent;
684         }
685     }
686 
687     iter = last = NULL;
688     LY_TREE_FOR_SAFE(xmlstart, xmlaux, xmlelem) {
689         r = xml_parse_data(ctx, xmlelem, reply_parent, result, last, options, unres, &iter, &act_notif, yang_data_name);
690         if (r) {
691             if (reply_top) {
692                 result = reply_top;
693             }
694             goto error;
695         } else if (options & LYD_OPT_DESTRUCT) {
696             lyxml_free(ctx, xmlelem);
697             *root = xmlaux;
698         }
699         if (iter) {
700             last = iter;
701             if ((options & LYD_OPT_DATA_ADD_YANGLIB) && iter->schema->module == ctx->models.list[ctx->internal_module_count - 1]) {
702                 /* ietf-yang-library data present, so ignore the option to add them */
703                 options &= ~LYD_OPT_DATA_ADD_YANGLIB;
704             }
705         }
706         if (!result) {
707             result = iter;
708         }
709 
710         if (options & LYD_OPT_NOSIBLINGS) {
711             /* stop after the first processed root */
712             break;
713         }
714     }
715 
716     if (reply_top) {
717         result = reply_top;
718     }
719 
720     if ((options & LYD_OPT_RPCREPLY) && (rpc_act->schema->nodetype != LYS_RPC)) {
721         /* action reply */
722         act_notif = reply_parent;
723     } else if ((options & (LYD_OPT_RPC | LYD_OPT_NOTIF)) && !act_notif) {
724         LOGVAL(ctx, LYE_INELEM, (result ? LY_VLOG_LYD : LY_VLOG_NONE), result, (options & LYD_OPT_RPC ? "action" : "notification"));
725         goto error;
726     }
727 
728     /* add missing ietf-yang-library if requested */
729     if (options & LYD_OPT_DATA_ADD_YANGLIB) {
730         if (!result) {
731             result = ly_ctx_info(ctx);
732         } else if (lyd_merge(result, ly_ctx_info(ctx), LYD_OPT_DESTRUCT | LYD_OPT_EXPLICIT)) {
733             LOGERR(ctx, LY_EINT, "Adding ietf-yang-library data failed.");
734             goto error;
735         }
736     }
737 
738     /* check for uniqueness of top-level lists/leaflists because
739      * only the inner instances were tested in lyv_data_content() */
740     LY_TREE_FOR(result, iter) {
741         if (!(iter->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) || !(iter->validity & LYD_VAL_DUP)) {
742             continue;
743         }
744 
745         if (lyv_data_dup(iter, result)) {
746             goto error;
747         }
748     }
749 
750     /* add default values, resolve unres and check for mandatory nodes in final tree */
751     if (lyd_defaults_add_unres(&result, options, ctx, NULL, 0, data_tree, act_notif, unres, 1)) {
752         goto error;
753     }
754     if (!(options & (LYD_OPT_TRUSTED | LYD_OPT_NOTIF_FILTER))
755             && lyd_check_mandatory_tree((act_notif ? act_notif : result), ctx, NULL, 0, options)) {
756         goto error;
757     }
758 
759     if (xmlfree) {
760         lyxml_free(ctx, xmlfree);
761     }
762     free(unres->node);
763     free(unres->type);
764     free(unres);
765     va_end(ap);
766     return result;
767 
768 error:
769     lyd_free_withsiblings(result);
770     if (xmlfree) {
771         lyxml_free(ctx, xmlfree);
772     }
773     free(unres->node);
774     free(unres->type);
775     free(unres);
776     va_end(ap);
777     return NULL;
778 }
779