|
Packit |
8fb591 |
/**
|
|
Packit |
8fb591 |
* @file parser_xml.c
|
|
Packit |
8fb591 |
* @author Radek Krejci <rkrejci@cesnet.cz>
|
|
Packit |
8fb591 |
* @brief XML data parser for libyang
|
|
Packit |
8fb591 |
*
|
|
Packit |
8fb591 |
* Copyright (c) 2015 - 2017 CESNET, z.s.p.o.
|
|
Packit |
8fb591 |
*
|
|
Packit |
8fb591 |
* This source code is licensed under BSD 3-Clause License (the "License").
|
|
Packit |
8fb591 |
* You may not use this file except in compliance with the License.
|
|
Packit |
8fb591 |
* You may obtain a copy of the License at
|
|
Packit |
8fb591 |
*
|
|
Packit |
8fb591 |
* https://opensource.org/licenses/BSD-3-Clause
|
|
Packit |
8fb591 |
*/
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
#include <assert.h>
|
|
Packit |
8fb591 |
#include <ctype.h>
|
|
Packit |
8fb591 |
#include <errno.h>
|
|
Packit |
8fb591 |
#include <stdarg.h>
|
|
Packit |
8fb591 |
#include <stdlib.h>
|
|
Packit |
8fb591 |
#include <string.h>
|
|
Packit |
8fb591 |
#include <limits.h>
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
#include "libyang.h"
|
|
Packit |
8fb591 |
#include "common.h"
|
|
Packit |
8fb591 |
#include "context.h"
|
|
Packit |
8fb591 |
#include "parser.h"
|
|
Packit |
8fb591 |
#include "tree_internal.h"
|
|
Packit |
8fb591 |
#include "validation.h"
|
|
Packit |
8fb591 |
#include "xml_internal.h"
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* does not log */
|
|
Packit |
8fb591 |
static struct lys_node *
|
|
Packit |
8fb591 |
xml_data_search_schemanode(struct lyxml_elem *xml, struct lys_node *start, int options)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
struct lys_node *result, *aux;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
LY_TREE_FOR(start, result) {
|
|
Packit |
8fb591 |
/* skip groupings ... */
|
|
Packit |
8fb591 |
if (result->nodetype == LYS_GROUPING) {
|
|
Packit |
8fb591 |
continue;
|
|
Packit |
8fb591 |
/* ... and output in case of RPC ... */
|
|
Packit |
8fb591 |
} else if (result->nodetype == LYS_OUTPUT && (options & LYD_OPT_RPC)) {
|
|
Packit |
8fb591 |
continue;
|
|
Packit |
8fb591 |
/* ... and input in case of RPC reply */
|
|
Packit |
8fb591 |
} else if (result->nodetype == LYS_INPUT && (options & LYD_OPT_RPCREPLY)) {
|
|
Packit |
8fb591 |
continue;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* go into cases, choices, uses and in RPCs into input and output */
|
|
Packit |
8fb591 |
if (result->nodetype & (LYS_CHOICE | LYS_CASE | LYS_USES | LYS_INPUT | LYS_OUTPUT)) {
|
|
Packit |
8fb591 |
aux = xml_data_search_schemanode(xml, result->child, options);
|
|
Packit |
8fb591 |
if (aux) {
|
|
Packit |
8fb591 |
/* we have matching result */
|
|
Packit |
8fb591 |
return aux;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
/* else, continue with next schema node */
|
|
Packit |
8fb591 |
continue;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* match data nodes */
|
|
Packit |
8fb591 |
if (ly_strequal(result->name, xml->name, 1)) {
|
|
Packit |
8fb591 |
/* names matches, what about namespaces? */
|
|
Packit |
8fb591 |
if (ly_strequal(lys_main_module(result->module)->ns, xml->ns->value, 1)) {
|
|
Packit |
8fb591 |
/* we have matching result */
|
|
Packit |
8fb591 |
return result;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
/* else, continue with next schema node */
|
|
Packit |
8fb591 |
continue;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* no match */
|
|
Packit |
8fb591 |
return NULL;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* logs directly */
|
|
Packit |
8fb591 |
static int
|
|
Packit |
8fb591 |
xml_get_value(struct lyd_node *node, struct lyxml_elem *xml, int editbits, int trusted)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
struct lyd_node_leaf_list *leaf = (struct lyd_node_leaf_list *)node;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
assert(node && (node->schema->nodetype & (LYS_LEAFLIST | LYS_LEAF)) && xml);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
leaf->value_str = lydict_insert(node->schema->module->ctx, xml->content, 0);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if ((editbits & 0x20) && (node->schema->nodetype & LYS_LEAF) && (!leaf->value_str || !leaf->value_str[0])) {
|
|
Packit |
8fb591 |
/* we have edit-config leaf/leaf-list with delete operation and no (empty) value,
|
|
Packit |
8fb591 |
* this is not a bug since the node is just used as a kind of selection node */
|
|
Packit |
8fb591 |
leaf->value_type = LY_TYPE_UNKNOWN;
|
|
Packit |
8fb591 |
return EXIT_SUCCESS;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* the value is here converted to a JSON format if needed in case of LY_TYPE_IDENT and LY_TYPE_INST or to a
|
|
Packit |
8fb591 |
* canonical form of the value */
|
|
Packit |
8fb591 |
if (!lyp_parse_value(&((struct lys_node_leaf *)leaf->schema)->type, &leaf->value_str, xml, leaf, NULL, NULL, 1, 0, trusted)) {
|
|
Packit |
8fb591 |
return EXIT_FAILURE;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
return EXIT_SUCCESS;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* logs directly */
|
|
Packit |
8fb591 |
static int
|
|
Packit |
8fb591 |
xml_parse_data(struct ly_ctx *ctx, struct lyxml_elem *xml, struct lyd_node *parent, struct lyd_node *first_sibling,
|
|
Packit |
8fb591 |
struct lyd_node *prev, int options, struct unres_data *unres, struct lyd_node **result,
|
|
Packit |
8fb591 |
struct lyd_node **act_notif, const char *yang_data_name)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
const struct lys_module *mod = NULL;
|
|
Packit |
8fb591 |
struct lyd_node *diter, *dlast;
|
|
Packit |
8fb591 |
struct lys_node *schema = NULL, *target;
|
|
Packit |
8fb591 |
const struct lys_node *ext_node;
|
|
Packit |
8fb591 |
struct lys_node_augment *aug;
|
|
Packit |
8fb591 |
struct lyd_attr *dattr, *dattr_iter;
|
|
Packit |
8fb591 |
struct lyxml_attr *attr;
|
|
Packit |
8fb591 |
struct lyxml_elem *child, *next;
|
|
Packit |
8fb591 |
int i, j, havechildren, r, editbits = 0, filterflag = 0, found;
|
|
Packit |
8fb591 |
uint8_t pos;
|
|
Packit |
8fb591 |
int ret = 0;
|
|
Packit |
8fb591 |
const char *str = NULL;
|
|
Packit |
8fb591 |
char *msg;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
assert(xml);
|
|
Packit |
8fb591 |
assert(result);
|
|
Packit |
8fb591 |
*result = NULL;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (xml->flags & LYXML_ELEM_MIXED) {
|
|
Packit |
8fb591 |
if (options & LYD_OPT_STRICT) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_XML_INVAL, LY_VLOG_XML, xml, "XML element with mixed content");
|
|
Packit |
8fb591 |
return -1;
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
return 0;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (!xml->ns || !xml->ns->value) {
|
|
Packit |
8fb591 |
if (options & LYD_OPT_STRICT) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_XML_MISS, LY_VLOG_XML, xml, "element's", "namespace");
|
|
Packit |
8fb591 |
return -1;
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
return 0;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* find schema node */
|
|
Packit |
8fb591 |
if (!parent) {
|
|
Packit |
8fb591 |
mod = ly_ctx_get_module_by_ns(ctx, xml->ns->value, NULL, 0);
|
|
Packit |
8fb591 |
if (ctx->data_clb) {
|
|
Packit |
8fb591 |
if (!mod) {
|
|
Packit |
8fb591 |
mod = ctx->data_clb(ctx, NULL, xml->ns->value, 0, ctx->data_clb_data);
|
|
Packit |
8fb591 |
} else if (!mod->implemented) {
|
|
Packit |
8fb591 |
mod = ctx->data_clb(ctx, mod->name, mod->ns, LY_MODCLB_NOT_IMPLEMENTED, ctx->data_clb_data);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* get the proper schema node */
|
|
Packit |
8fb591 |
if (mod && mod->implemented && !mod->disabled) {
|
|
Packit |
8fb591 |
if (options & LYD_OPT_DATA_TEMPLATE) {
|
|
Packit |
8fb591 |
if (yang_data_name) {
|
|
Packit |
8fb591 |
ext_node = lyp_get_yang_data_template(mod, yang_data_name, strlen(yang_data_name));
|
|
Packit |
8fb591 |
if (ext_node) {
|
|
Packit |
8fb591 |
schema = *((struct lys_node **) lys_ext_complex_get_substmt(LY_STMT_CONTAINER, (struct lys_ext_instance_complex *)ext_node, NULL));
|
|
Packit |
8fb591 |
schema = xml_data_search_schemanode(xml, schema, options);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
schema = xml_data_search_schemanode(xml, mod->data, options);
|
|
Packit |
8fb591 |
if (!schema) {
|
|
Packit |
8fb591 |
/* it still can be the specific case of this module containing an augment of another module
|
|
Packit |
8fb591 |
* top-level choice or top-level choice's case, bleh */
|
|
Packit |
8fb591 |
for (j = 0; j < mod->augment_size; ++j) {
|
|
Packit |
8fb591 |
aug = &mod->augment[j];
|
|
Packit |
8fb591 |
target = aug->target;
|
|
Packit |
8fb591 |
if (target->nodetype & (LYS_CHOICE | LYS_CASE)) {
|
|
Packit |
8fb591 |
/* 1) okay, the target is choice or case */
|
|
Packit |
8fb591 |
while (target && (target->nodetype & (LYS_CHOICE | LYS_CASE | LYS_USES))) {
|
|
Packit |
8fb591 |
target = lys_parent(target);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
/* 2) now, the data node will be top-level, there are only non-data schema nodes */
|
|
Packit |
8fb591 |
if (!target) {
|
|
Packit |
8fb591 |
while ((schema = (struct lys_node *) lys_getnext(schema, (struct lys_node *) aug, NULL, 0))) {
|
|
Packit |
8fb591 |
/* 3) alright, even the name matches, we found our schema node */
|
|
Packit |
8fb591 |
if (ly_strequal(schema->name, xml->name, 1)) {
|
|
Packit |
8fb591 |
break;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (schema) {
|
|
Packit |
8fb591 |
break;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
/* parsing some internal node, we start with parent's schema pointer */
|
|
Packit |
8fb591 |
schema = xml_data_search_schemanode(xml, parent->schema->child, options);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (ctx->data_clb) {
|
|
Packit |
8fb591 |
if (schema && !lys_node_module(schema)->implemented) {
|
|
Packit |
8fb591 |
ctx->data_clb(ctx, lys_node_module(schema)->name, lys_node_module(schema)->ns,
|
|
Packit |
8fb591 |
LY_MODCLB_NOT_IMPLEMENTED, ctx->data_clb_data);
|
|
Packit |
8fb591 |
} else if (!schema) {
|
|
Packit |
8fb591 |
if (ctx->data_clb(ctx, NULL, xml->ns->value, 0, ctx->data_clb_data)) {
|
|
Packit |
8fb591 |
/* context was updated, so try to find the schema node again */
|
|
Packit |
8fb591 |
schema = xml_data_search_schemanode(xml, parent->schema->child, options);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
mod = lys_node_module(schema);
|
|
Packit |
8fb591 |
if (!mod || !mod->implemented || mod->disabled) {
|
|
Packit |
8fb591 |
if (options & LYD_OPT_STRICT) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_INELEM, (parent ? LY_VLOG_LYD : LY_VLOG_STR), (parent ? (void *)parent : (void *)"/") , xml->name);
|
|
Packit |
8fb591 |
return -1;
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
return 0;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* create the element structure */
|
|
Packit |
8fb591 |
switch (schema->nodetype) {
|
|
Packit |
8fb591 |
case LYS_CONTAINER:
|
|
Packit |
8fb591 |
case LYS_LIST:
|
|
Packit |
8fb591 |
case LYS_NOTIF:
|
|
Packit |
8fb591 |
case LYS_RPC:
|
|
Packit |
8fb591 |
case LYS_ACTION:
|
|
Packit |
8fb591 |
for (i = 0; xml->content && xml->content[i]; ++i) {
|
|
Packit |
8fb591 |
if (!is_xmlws(xml->content[i])) {
|
|
Packit |
8fb591 |
msg = malloc(22 + strlen(xml->content) + 1);
|
|
Packit |
8fb591 |
LY_CHECK_ERR_RETURN(!msg, LOGMEM(ctx), -1);
|
|
Packit |
8fb591 |
sprintf(msg, "node with text data \"%s\"", xml->content);
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_XML_INVAL, LY_VLOG_XML, xml, msg);
|
|
Packit |
8fb591 |
free(msg);
|
|
Packit |
8fb591 |
return -1;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
*result = calloc(1, sizeof **result);
|
|
Packit |
8fb591 |
havechildren = 1;
|
|
Packit |
8fb591 |
break;
|
|
Packit |
8fb591 |
case LYS_LEAF:
|
|
Packit |
8fb591 |
case LYS_LEAFLIST:
|
|
Packit |
8fb591 |
*result = calloc(1, sizeof(struct lyd_node_leaf_list));
|
|
Packit |
8fb591 |
havechildren = 0;
|
|
Packit |
8fb591 |
break;
|
|
Packit |
8fb591 |
case LYS_ANYXML:
|
|
Packit |
8fb591 |
case LYS_ANYDATA:
|
|
Packit |
8fb591 |
*result = calloc(1, sizeof(struct lyd_node_anydata));
|
|
Packit |
8fb591 |
havechildren = 0;
|
|
Packit |
8fb591 |
break;
|
|
Packit |
8fb591 |
default:
|
|
Packit |
8fb591 |
LOGINT(ctx);
|
|
Packit |
8fb591 |
return -1;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
LY_CHECK_ERR_RETURN(!(*result), LOGMEM(ctx), -1);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
(*result)->prev = *result;
|
|
Packit |
8fb591 |
(*result)->schema = schema;
|
|
Packit |
8fb591 |
(*result)->parent = parent;
|
|
Packit |
8fb591 |
diter = NULL;
|
|
Packit |
8fb591 |
if (schema->nodetype == LYS_LEAF && lys_is_key((struct lys_node_leaf *)schema, &pos)) {
|
|
Packit |
8fb591 |
/* it is key and we need to insert it into a correct place (a key must have a parent list) */
|
|
Packit |
8fb591 |
assert(parent);
|
|
Packit |
8fb591 |
for (i = 0, diter = parent->child;
|
|
Packit |
8fb591 |
diter && i < pos && diter->schema->nodetype == LYS_LEAF && lys_is_key((struct lys_node_leaf *)diter->schema, NULL);
|
|
Packit |
8fb591 |
i++, diter = diter->next);
|
|
Packit |
8fb591 |
if (diter) {
|
|
Packit |
8fb591 |
/* out of order insertion - insert list's key to the correct position, before the diter */
|
|
Packit |
8fb591 |
if (options & LYD_OPT_STRICT) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_INORDER, LY_VLOG_LYD, *result, schema->name, diter->schema->name);
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Invalid position of the key \"%s\" in a list \"%s\".",
|
|
Packit |
8fb591 |
schema->name, parent->schema->name);
|
|
Packit |
8fb591 |
free(*result);
|
|
Packit |
8fb591 |
*result = NULL;
|
|
Packit |
8fb591 |
return -1;
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
LOGWRN(ctx, "Invalid position of the key \"%s\" in a list \"%s\".", schema->name, parent->schema->name)
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
if (parent->child == diter) {
|
|
Packit |
8fb591 |
parent->child = *result;
|
|
Packit |
8fb591 |
/* update first_sibling */
|
|
Packit |
8fb591 |
first_sibling = *result;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
if (diter->prev->next) {
|
|
Packit |
8fb591 |
diter->prev->next = *result;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
(*result)->prev = diter->prev;
|
|
Packit |
8fb591 |
diter->prev = *result;
|
|
Packit |
8fb591 |
(*result)->next = diter;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
if (!diter) {
|
|
Packit |
8fb591 |
/* simplified (faster) insert as the last node */
|
|
Packit |
8fb591 |
if (parent && !parent->child) {
|
|
Packit |
8fb591 |
parent->child = *result;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
if (prev) {
|
|
Packit |
8fb591 |
(*result)->prev = prev;
|
|
Packit |
8fb591 |
prev->next = *result;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* fix the "last" pointer */
|
|
Packit |
8fb591 |
first_sibling->prev = *result;
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
(*result)->prev = *result;
|
|
Packit |
8fb591 |
first_sibling = *result;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
(*result)->validity = ly_new_node_validity((*result)->schema);
|
|
Packit |
8fb591 |
if (resolve_applies_when(schema, 0, NULL)) {
|
|
Packit |
8fb591 |
(*result)->when_status = LYD_WHEN;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* process attributes */
|
|
Packit |
8fb591 |
for (attr = xml->attr; attr; attr = attr->next) {
|
|
Packit |
8fb591 |
if (attr->type != LYXML_ATTR_STD) {
|
|
Packit |
8fb591 |
continue;
|
|
Packit |
8fb591 |
} else if (!attr->ns) {
|
|
Packit |
8fb591 |
if ((*result)->schema->nodetype == LYS_ANYXML &&
|
|
Packit |
8fb591 |
ly_strequal((*result)->schema->name, "filter", 0) &&
|
|
Packit |
8fb591 |
(ly_strequal((*result)->schema->module->name, "ietf-netconf", 0) ||
|
|
Packit |
8fb591 |
ly_strequal((*result)->schema->module->name, "notifications", 0))) {
|
|
Packit |
8fb591 |
/* NETCONF filter's attributes, which we implement as non-standard annotations,
|
|
Packit |
8fb591 |
* they are unqualified (no namespace), but we know that we have internally defined
|
|
Packit |
8fb591 |
* them in the ietf-netconf module */
|
|
Packit |
8fb591 |
str = "urn:ietf:params:xml:ns:netconf:base:1.0";
|
|
Packit |
8fb591 |
filterflag = 1;
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
/* garbage */
|
|
Packit |
8fb591 |
goto attr_error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
str = attr->ns->value;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
r = lyp_fill_attr(ctx, *result, str, NULL, attr->name, attr->value, xml, options, &dattr);
|
|
Packit |
8fb591 |
if (r == -1) {
|
|
Packit |
8fb591 |
goto unlink_node_error;
|
|
Packit |
8fb591 |
} else if (r == 1) {
|
|
Packit |
8fb591 |
attr_error:
|
|
Packit |
8fb591 |
if (options & LYD_OPT_STRICT) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, *result, attr->name);
|
|
Packit |
8fb591 |
goto unlink_node_error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
LOGWRN(ctx, "Unknown \"%s:%s\" metadata with value \"%s\", ignoring.",
|
|
Packit |
8fb591 |
(attr->ns ? attr->ns->prefix : "<none>"), attr->name, attr->value);
|
|
Packit |
8fb591 |
continue;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* special case of xpath in the value, we want to convert it to JSON */
|
|
Packit |
8fb591 |
if (filterflag && !strcmp(attr->name, "select")) {
|
|
Packit |
8fb591 |
dattr->value.string = transform_xml2json(ctx, dattr->value_str, xml, 0, 0);
|
|
Packit |
8fb591 |
if (!dattr->value.string) {
|
|
Packit |
8fb591 |
/* problem with resolving value as xpath */
|
|
Packit |
8fb591 |
dattr->value.string = dattr->value_str;
|
|
Packit |
8fb591 |
goto unlink_node_error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
lydict_remove(ctx, dattr->value_str);
|
|
Packit |
8fb591 |
dattr->value_str = dattr->value.string;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* insert into the data node */
|
|
Packit |
8fb591 |
if (!(*result)->attr) {
|
|
Packit |
8fb591 |
(*result)->attr = dattr;
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
for (dattr_iter = (*result)->attr; dattr_iter->next; dattr_iter = dattr_iter->next);
|
|
Packit |
8fb591 |
dattr_iter->next = dattr;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
continue;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* check insert attribute and its values */
|
|
Packit |
8fb591 |
if (options & LYD_OPT_EDIT) {
|
|
Packit |
8fb591 |
if (lyp_check_edit_attr(ctx, (*result)->attr, *result, &editbits)) {
|
|
Packit |
8fb591 |
goto unlink_node_error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* check correct filter extension attributes */
|
|
Packit |
8fb591 |
} else if (filterflag) {
|
|
Packit |
8fb591 |
found = 0; /* 0 - nothing, 1 - type subtree, 2 - type xpath, 3 - select, 4 - type xpath + select */
|
|
Packit |
8fb591 |
LY_TREE_FOR((*result)->attr, dattr_iter) {
|
|
Packit |
8fb591 |
if (!strcmp(dattr_iter->name, "type")) {
|
|
Packit |
8fb591 |
if ((found == 1) || (found == 2) || (found == 4)) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_TOOMANY, LY_VLOG_LYD, (*result), "type", xml->name);
|
|
Packit |
8fb591 |
goto unlink_node_error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
switch (dattr_iter->value.enm->value) {
|
|
Packit |
8fb591 |
case 0:
|
|
Packit |
8fb591 |
/* subtree */
|
|
Packit |
8fb591 |
if (found == 3) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, (*result), dattr_iter->name);
|
|
Packit |
8fb591 |
goto unlink_node_error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
assert(!found);
|
|
Packit |
8fb591 |
found = 1;
|
|
Packit |
8fb591 |
break;
|
|
Packit |
8fb591 |
case 1:
|
|
Packit |
8fb591 |
/* xpath */
|
|
Packit |
8fb591 |
if (found == 3) {
|
|
Packit |
8fb591 |
found = 4;
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
assert(!found);
|
|
Packit |
8fb591 |
found = 2;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
break;
|
|
Packit |
8fb591 |
default:
|
|
Packit |
8fb591 |
LOGINT(ctx);
|
|
Packit |
8fb591 |
goto unlink_node_error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
} else if (!strcmp(dattr_iter->name, "select")) {
|
|
Packit |
8fb591 |
switch (found) {
|
|
Packit |
8fb591 |
case 0:
|
|
Packit |
8fb591 |
found = 3;
|
|
Packit |
8fb591 |
break;
|
|
Packit |
8fb591 |
case 1:
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, (*result), dattr_iter->name);
|
|
Packit |
8fb591 |
goto unlink_node_error;
|
|
Packit |
8fb591 |
case 2:
|
|
Packit |
8fb591 |
found = 4;
|
|
Packit |
8fb591 |
break;
|
|
Packit |
8fb591 |
case 3:
|
|
Packit |
8fb591 |
case 4:
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_TOOMANY, LY_VLOG_LYD, (*result), "select", xml->name);
|
|
Packit |
8fb591 |
goto unlink_node_error;
|
|
Packit |
8fb591 |
default:
|
|
Packit |
8fb591 |
LOGINT(ctx);
|
|
Packit |
8fb591 |
goto unlink_node_error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* check if what we found is correct */
|
|
Packit |
8fb591 |
switch (found) {
|
|
Packit |
8fb591 |
case 1:
|
|
Packit |
8fb591 |
case 4:
|
|
Packit |
8fb591 |
/* ok */
|
|
Packit |
8fb591 |
break;
|
|
Packit |
8fb591 |
case 2:
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_MISSATTR, LY_VLOG_LYD, (*result), "select", xml->name);
|
|
Packit |
8fb591 |
goto unlink_node_error;
|
|
Packit |
8fb591 |
case 3:
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_MISSATTR, LY_VLOG_LYD, (*result), "type", xml->name);
|
|
Packit |
8fb591 |
goto unlink_node_error;
|
|
Packit |
8fb591 |
default:
|
|
Packit |
8fb591 |
LOGINT(ctx);
|
|
Packit |
8fb591 |
goto unlink_node_error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* type specific processing */
|
|
Packit |
8fb591 |
if (schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
|
|
Packit |
8fb591 |
/* type detection and assigning the value */
|
|
Packit |
8fb591 |
if (xml_get_value(*result, xml, editbits, options & LYD_OPT_TRUSTED)) {
|
|
Packit |
8fb591 |
goto unlink_node_error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
} else if (schema->nodetype & LYS_ANYDATA) {
|
|
Packit |
8fb591 |
/* store children values */
|
|
Packit |
8fb591 |
if (xml->child) {
|
|
Packit |
8fb591 |
child = xml->child;
|
|
Packit |
8fb591 |
/* manually unlink all siblings and correct namespaces */
|
|
Packit |
8fb591 |
xml->child = NULL;
|
|
Packit |
8fb591 |
LY_TREE_FOR(child, next) {
|
|
Packit |
8fb591 |
next->parent = NULL;
|
|
Packit |
8fb591 |
lyxml_correct_elem_ns(ctx, next, 1, 1);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
((struct lyd_node_anydata *)*result)->value_type = LYD_ANYDATA_XML;
|
|
Packit |
8fb591 |
((struct lyd_node_anydata *)*result)->value.xml = child;
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
((struct lyd_node_anydata *)*result)->value_type = LYD_ANYDATA_CONSTSTRING;
|
|
Packit |
8fb591 |
((struct lyd_node_anydata *)*result)->value.str = lydict_insert(ctx, xml->content, 0);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
} else if (schema->nodetype & (LYS_RPC | LYS_ACTION)) {
|
|
Packit |
8fb591 |
if (!(options & LYD_OPT_RPC) || *act_notif) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_INELEM, LY_VLOG_LYD, (*result), schema->name);
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Unexpected %s node \"%s\".",
|
|
Packit |
8fb591 |
(schema->nodetype == LYS_RPC ? "rpc" : "action"), schema->name);
|
|
Packit |
8fb591 |
goto unlink_node_error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
*act_notif = *result;
|
|
Packit |
8fb591 |
} else if (schema->nodetype == LYS_NOTIF) {
|
|
Packit |
8fb591 |
if (!(options & LYD_OPT_NOTIF) || *act_notif) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_INELEM, LY_VLOG_LYD, (*result), schema->name);
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Unexpected notification node \"%s\".", schema->name);
|
|
Packit |
8fb591 |
goto unlink_node_error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
*act_notif = *result;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
#ifdef LY_ENABLED_CACHE
|
|
Packit |
8fb591 |
/* calculate the hash and insert it into parent (list with keys is handled when its keys are inserted) */
|
|
Packit |
8fb591 |
if (((*result)->schema->nodetype != LYS_LIST) || !((struct lys_node_list *)(*result)->schema)->keys_size) {
|
|
Packit |
8fb591 |
lyd_hash(*result);
|
|
Packit |
8fb591 |
lyd_insert_hash(*result);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
#endif
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* first part of validation checks */
|
|
Packit |
8fb591 |
if (lyv_data_context(*result, options, unres)) {
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* process children */
|
|
Packit |
8fb591 |
if (havechildren && xml->child) {
|
|
Packit |
8fb591 |
diter = dlast = NULL;
|
|
Packit |
8fb591 |
LY_TREE_FOR_SAFE(xml->child, next, child) {
|
|
Packit |
8fb591 |
r = xml_parse_data(ctx, child, *result, (*result)->child, dlast, options, unres, &diter, act_notif, yang_data_name);
|
|
Packit |
8fb591 |
if (r) {
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
} else if (options & LYD_OPT_DESTRUCT) {
|
|
Packit |
8fb591 |
lyxml_free(ctx, child);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
if (diter && !diter->next) {
|
|
Packit |
8fb591 |
/* the child was parsed/created and it was placed as the last child. The child can be inserted
|
|
Packit |
8fb591 |
* out of order (not as the last one) in case it is a list's key present out of the correct order */
|
|
Packit |
8fb591 |
dlast = diter;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* if we have empty non-presence container, we keep it, but mark it as default */
|
|
Packit |
8fb591 |
if (schema->nodetype == LYS_CONTAINER && !(*result)->child &&
|
|
Packit |
8fb591 |
!(*result)->attr && !((struct lys_node_container *)schema)->presence) {
|
|
Packit |
8fb591 |
(*result)->dflt = 1;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* rest of validation checks */
|
|
Packit |
8fb591 |
if (lyv_data_content(*result, options, unres) ||
|
|
Packit |
8fb591 |
lyv_multicases(*result, NULL, prev ? &first_sibling : NULL, 0, NULL)) {
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* validation successful */
|
|
Packit |
8fb591 |
if ((*result)->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
|
|
Packit |
8fb591 |
/* postpone checking when there will be all list/leaflist instances */
|
|
Packit |
8fb591 |
(*result)->validity |= LYD_VAL_DUP;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
return ret;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
unlink_node_error:
|
|
Packit |
8fb591 |
lyd_unlink_internal(*result, 2);
|
|
Packit |
8fb591 |
error:
|
|
Packit |
8fb591 |
/* cleanup */
|
|
Packit |
8fb591 |
for (i = unres->count - 1; i >= 0; i--) {
|
|
Packit |
8fb591 |
/* remove unres items connected with the node being removed */
|
|
Packit |
8fb591 |
if (unres->node[i] == *result) {
|
|
Packit |
8fb591 |
unres_data_del(unres, i);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
lyd_free(*result);
|
|
Packit |
8fb591 |
*result = NULL;
|
|
Packit |
8fb591 |
return -1;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
API struct lyd_node *
|
|
Packit |
8fb591 |
lyd_parse_xml(struct ly_ctx *ctx, struct lyxml_elem **root, int options, ...)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
va_list ap;
|
|
Packit |
8fb591 |
int r;
|
|
Packit |
8fb591 |
struct unres_data *unres = NULL;
|
|
Packit |
8fb591 |
const struct lyd_node *rpc_act = NULL, *data_tree = NULL;
|
|
Packit |
8fb591 |
struct lyd_node *result = NULL, *iter, *last, *reply_parent = NULL, *reply_top = NULL, *act_notif = NULL;
|
|
Packit |
8fb591 |
struct lyxml_elem *xmlstart, *xmlelem, *xmlaux, *xmlfree = NULL;
|
|
Packit |
8fb591 |
const char *yang_data_name = NULL;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (!ctx || !root) {
|
|
Packit |
8fb591 |
LOGARG;
|
|
Packit |
8fb591 |
return NULL;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (lyp_data_check_options(ctx, options, __func__)) {
|
|
Packit |
8fb591 |
return NULL;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (!(*root) && !(options & LYD_OPT_RPCREPLY)) {
|
|
Packit |
8fb591 |
/* empty tree */
|
|
Packit |
8fb591 |
if (options & (LYD_OPT_RPC | LYD_OPT_NOTIF)) {
|
|
Packit |
8fb591 |
/* error, top level node identify RPC and Notification */
|
|
Packit |
8fb591 |
LOGERR(ctx, LY_EINVAL, "%s: *root identifies RPC/Notification so it cannot be NULL.", __func__);
|
|
Packit |
8fb591 |
return NULL;
|
|
Packit |
8fb591 |
} else if (!(options & LYD_OPT_RPCREPLY)) {
|
|
Packit |
8fb591 |
/* others - no work is needed, just check for missing mandatory nodes */
|
|
Packit |
8fb591 |
lyd_validate(&result, options, ctx);
|
|
Packit |
8fb591 |
return result;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
/* continue with empty RPC reply, for which we need RPC */
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
unres = calloc(1, sizeof *unres);
|
|
Packit |
8fb591 |
LY_CHECK_ERR_RETURN(!unres, LOGMEM(ctx), NULL);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
va_start(ap, options);
|
|
Packit |
8fb591 |
if (options & LYD_OPT_RPCREPLY) {
|
|
Packit |
8fb591 |
rpc_act = va_arg(ap, const struct lyd_node *);
|
|
Packit |
8fb591 |
if (!rpc_act || rpc_act->parent || !(rpc_act->schema->nodetype & (LYS_RPC | LYS_LIST | LYS_CONTAINER))) {
|
|
Packit |
8fb591 |
LOGERR(ctx, LY_EINVAL, "%s: invalid variable parameter (const struct lyd_node *rpc_act).", __func__);
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
if (rpc_act->schema->nodetype == LYS_RPC) {
|
|
Packit |
8fb591 |
/* RPC request */
|
|
Packit |
8fb591 |
reply_top = reply_parent = _lyd_new(NULL, rpc_act->schema, 0);
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
/* action request */
|
|
Packit |
8fb591 |
reply_top = lyd_dup(rpc_act, 1);
|
|
Packit |
8fb591 |
LY_TREE_DFS_BEGIN(reply_top, iter, reply_parent) {
|
|
Packit |
8fb591 |
if (reply_parent->schema->nodetype == LYS_ACTION) {
|
|
Packit |
8fb591 |
break;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
LY_TREE_DFS_END(reply_top, iter, reply_parent);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
if (!reply_parent) {
|
|
Packit |
8fb591 |
LOGERR(ctx, LY_EINVAL, "%s: invalid variable parameter (const struct lyd_node *rpc_act).", __func__);
|
|
Packit |
8fb591 |
lyd_free_withsiblings(reply_top);
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
lyd_free_withsiblings(reply_parent->child);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
if (options & (LYD_OPT_RPC | LYD_OPT_NOTIF | LYD_OPT_RPCREPLY)) {
|
|
Packit |
8fb591 |
data_tree = va_arg(ap, const struct lyd_node *);
|
|
Packit |
8fb591 |
if (data_tree) {
|
|
Packit |
8fb591 |
if (options & LYD_OPT_NOEXTDEPS) {
|
|
Packit |
8fb591 |
LOGERR(ctx, LY_EINVAL, "%s: invalid parameter (variable arg const struct lyd_node *data_tree and LYD_OPT_NOEXTDEPS set).",
|
|
Packit |
8fb591 |
__func__);
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
LY_TREE_FOR((struct lyd_node *)data_tree, iter) {
|
|
Packit |
8fb591 |
if (iter->parent) {
|
|
Packit |
8fb591 |
/* a sibling is not top-level */
|
|
Packit |
8fb591 |
LOGERR(ctx, LY_EINVAL, "%s: invalid variable parameter (const struct lyd_node *data_tree).", __func__);
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* move it to the beginning */
|
|
Packit |
8fb591 |
for (; data_tree->prev->next; data_tree = data_tree->prev);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* LYD_OPT_NOSIBLINGS cannot be set in this case */
|
|
Packit |
8fb591 |
if (options & LYD_OPT_NOSIBLINGS) {
|
|
Packit |
8fb591 |
LOGERR(ctx, LY_EINVAL, "%s: invalid parameter (variable arg const struct lyd_node *data_tree with LYD_OPT_NOSIBLINGS).", __func__);
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
if (options & LYD_OPT_DATA_TEMPLATE) {
|
|
Packit |
8fb591 |
yang_data_name = va_arg(ap, const char *);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if ((*root) && !(options & LYD_OPT_NOSIBLINGS)) {
|
|
Packit |
8fb591 |
/* locate the first root to process */
|
|
Packit |
8fb591 |
if ((*root)->parent) {
|
|
Packit |
8fb591 |
xmlstart = (*root)->parent->child;
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
xmlstart = *root;
|
|
Packit |
8fb591 |
while(xmlstart->prev->next) {
|
|
Packit |
8fb591 |
xmlstart = xmlstart->prev;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
xmlstart = *root;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if ((options & LYD_OPT_RPC)
|
|
Packit |
8fb591 |
&& !strcmp(xmlstart->name, "action") && !strcmp(xmlstart->ns->value, "urn:ietf:params:xml:ns:yang:1")) {
|
|
Packit |
8fb591 |
/* it's an action, not a simple RPC */
|
|
Packit |
8fb591 |
xmlstart = xmlstart->child;
|
|
Packit |
8fb591 |
if (options & LYD_OPT_DESTRUCT) {
|
|
Packit |
8fb591 |
/* free it later */
|
|
Packit |
8fb591 |
xmlfree = xmlstart->parent;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
iter = last = NULL;
|
|
Packit |
8fb591 |
LY_TREE_FOR_SAFE(xmlstart, xmlaux, xmlelem) {
|
|
Packit |
8fb591 |
r = xml_parse_data(ctx, xmlelem, reply_parent, result, last, options, unres, &iter, &act_notif, yang_data_name);
|
|
Packit |
8fb591 |
if (r) {
|
|
Packit |
8fb591 |
if (reply_top) {
|
|
Packit |
8fb591 |
result = reply_top;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
} else if (options & LYD_OPT_DESTRUCT) {
|
|
Packit |
8fb591 |
lyxml_free(ctx, xmlelem);
|
|
Packit |
8fb591 |
*root = xmlaux;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
if (iter) {
|
|
Packit |
8fb591 |
last = iter;
|
|
Packit |
8fb591 |
if ((options & LYD_OPT_DATA_ADD_YANGLIB) && iter->schema->module == ctx->models.list[ctx->internal_module_count - 1]) {
|
|
Packit |
8fb591 |
/* ietf-yang-library data present, so ignore the option to add them */
|
|
Packit |
8fb591 |
options &= ~LYD_OPT_DATA_ADD_YANGLIB;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
if (!result) {
|
|
Packit |
8fb591 |
result = iter;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (options & LYD_OPT_NOSIBLINGS) {
|
|
Packit |
8fb591 |
/* stop after the first processed root */
|
|
Packit |
8fb591 |
break;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (reply_top) {
|
|
Packit |
8fb591 |
result = reply_top;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if ((options & LYD_OPT_RPCREPLY) && (rpc_act->schema->nodetype != LYS_RPC)) {
|
|
Packit |
8fb591 |
/* action reply */
|
|
Packit |
8fb591 |
act_notif = reply_parent;
|
|
Packit |
8fb591 |
} else if ((options & (LYD_OPT_RPC | LYD_OPT_NOTIF)) && !act_notif) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_INELEM, (result ? LY_VLOG_LYD : LY_VLOG_NONE), result, (options & LYD_OPT_RPC ? "action" : "notification"));
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* add missing ietf-yang-library if requested */
|
|
Packit |
8fb591 |
if (options & LYD_OPT_DATA_ADD_YANGLIB) {
|
|
Packit |
8fb591 |
if (!result) {
|
|
Packit |
8fb591 |
result = ly_ctx_info(ctx);
|
|
Packit |
8fb591 |
} else if (lyd_merge(result, ly_ctx_info(ctx), LYD_OPT_DESTRUCT | LYD_OPT_EXPLICIT)) {
|
|
Packit |
8fb591 |
LOGERR(ctx, LY_EINT, "Adding ietf-yang-library data failed.");
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* check for uniqueness of top-level lists/leaflists because
|
|
Packit |
8fb591 |
* only the inner instances were tested in lyv_data_content() */
|
|
Packit |
8fb591 |
LY_TREE_FOR(result, iter) {
|
|
Packit |
8fb591 |
if (!(iter->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) || !(iter->validity & LYD_VAL_DUP)) {
|
|
Packit |
8fb591 |
continue;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (lyv_data_dup(iter, result)) {
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* add default values, resolve unres and check for mandatory nodes in final tree */
|
|
Packit |
8fb591 |
if (lyd_defaults_add_unres(&result, options, ctx, NULL, 0, data_tree, act_notif, unres, 1)) {
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
if (!(options & (LYD_OPT_TRUSTED | LYD_OPT_NOTIF_FILTER))
|
|
Packit |
8fb591 |
&& lyd_check_mandatory_tree((act_notif ? act_notif : result), ctx, NULL, 0, options)) {
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (xmlfree) {
|
|
Packit |
8fb591 |
lyxml_free(ctx, xmlfree);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
free(unres->node);
|
|
Packit |
8fb591 |
free(unres->type);
|
|
Packit |
8fb591 |
free(unres);
|
|
Packit |
8fb591 |
va_end(ap);
|
|
Packit |
8fb591 |
return result;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
error:
|
|
Packit |
8fb591 |
lyd_free_withsiblings(result);
|
|
Packit |
8fb591 |
if (xmlfree) {
|
|
Packit |
8fb591 |
lyxml_free(ctx, xmlfree);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
free(unres->node);
|
|
Packit |
8fb591 |
free(unres->type);
|
|
Packit |
8fb591 |
free(unres);
|
|
Packit |
8fb591 |
va_end(ap);
|
|
Packit |
8fb591 |
return NULL;
|
|
Packit |
8fb591 |
}
|