blob: 8bd959c9f7cbc187a8d7f9f730dfa5d86bc53d7d [file] [log] [blame]
Radek Krejci1721c012015-07-08 12:52:33 +02001/**
2 * @file xml.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @brief XML data parser for libyang
5 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of the Company nor the names of its contributors
18 * may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 */
21
22#include <assert.h>
Radek Krejci3e3affe2015-07-09 15:38:40 +020023#include <ctype.h>
Radek Krejci1721c012015-07-08 12:52:33 +020024#include <errno.h>
25#include <stdlib.h>
26#include <string.h>
Michal Vasko8bcdf292015-08-19 14:04:43 +020027#include <limits.h>
Radek Krejci1721c012015-07-08 12:52:33 +020028
Radek Krejci998a0b82015-08-17 13:14:36 +020029#include "libyang.h"
30#include "common.h"
31#include "context.h"
Michal Vaskob7982322015-10-16 13:56:12 +020032#include "parser.h"
Radek Krejci998a0b82015-08-17 13:14:36 +020033#include "tree_internal.h"
34#include "validation.h"
Michal Vaskofc5744d2015-10-22 12:09:34 +020035#include "xml_internal.h"
Radek Krejci1721c012015-07-08 12:52:33 +020036
Michal Vasko0d343d12015-08-24 14:57:36 +020037/* does not log */
Radek Krejci76512572015-08-04 09:47:08 +020038static struct lys_node *
39xml_data_search_schemanode(struct lyxml_elem *xml, struct lys_node *start)
Radek Krejci1721c012015-07-08 12:52:33 +020040{
Radek Krejci76512572015-08-04 09:47:08 +020041 struct lys_node *result, *aux;
Radek Krejci1721c012015-07-08 12:52:33 +020042
43 LY_TREE_FOR(start, result) {
44 /* skip groupings */
Radek Krejci76512572015-08-04 09:47:08 +020045 if (result->nodetype == LYS_GROUPING) {
Radek Krejci1721c012015-07-08 12:52:33 +020046 continue;
47 }
48
Radek Krejcifb54be42015-10-02 15:21:16 +020049 /* 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)) {
Radek Krejci1721c012015-07-08 12:52:33 +020051 aux = xml_data_search_schemanode(xml, result->child);
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 (result->name == xml->name) {
62 /* names matches, what about namespaces? */
63 if (result->module->ns == xml->ns->value) {
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
Michal Vasko0d343d12015-08-24 14:57:36 +020076/* logs directly */
Radek Krejcie4748472015-07-08 18:00:22 +020077static int
Michal Vasko8ea2b7f2015-09-29 14:30:53 +020078xml_get_value(struct lyd_node *node, struct lyxml_elem *xml, int options, struct unres_data *unres)
Michal Vasko07471a52015-07-16 11:18:48 +020079{
Michal Vasko4c183312015-09-25 10:41:47 +020080 struct lyd_node_leaf_list *leaf = (struct lyd_node_leaf_list *)node;
Michal Vasko8ea2b7f2015-09-29 14:30:53 +020081 struct lys_type *type, *stype;
82 int resolve, found;
Michal Vasko23b61ec2015-08-19 11:19:50 +020083
Michal Vasko8ea2b7f2015-09-29 14:30:53 +020084 assert(node && (node->schema->nodetype & (LYS_LEAFLIST | LYS_LEAF)) && xml && unres);
Radek Krejci5a988152015-07-15 11:16:26 +020085
Michal Vasko8ea2b7f2015-09-29 14:30:53 +020086 stype = &((struct lys_node_leaf *)node->schema)->type;
Radek Krejci5a988152015-07-15 11:16:26 +020087 leaf->value_str = xml->content;
88 xml->content = NULL;
Radek Krejcie4748472015-07-08 18:00:22 +020089
Michal Vasko8ea2b7f2015-09-29 14:30:53 +020090 /* will be changed in case of union */
91 leaf->value_type = stype->base;
Radek Krejcie3c33142015-08-10 15:04:36 +020092
Radek Krejcie2cf7c12015-08-12 10:24:05 +020093 if ((options & LYD_OPT_FILTER) && !leaf->value_str) {
94 /* no value in filter (selection) node -> nothing more is needed */
95 return EXIT_SUCCESS;
96 }
97
Michal Vasko8ea2b7f2015-09-29 14:30:53 +020098 if (options & (LYD_OPT_FILTER | LYD_OPT_EDIT)) {
99 resolve = 0;
100 } else {
101 resolve = 1;
102 }
Radek Krejci3e3affe2015-07-09 15:38:40 +0200103
Michal Vasko8ea2b7f2015-09-29 14:30:53 +0200104 if ((stype->base == LY_TYPE_IDENT) || (stype->base == LY_TYPE_INST)) {
Michal Vaskofb0873c2015-08-21 09:00:07 +0200105 /* convert the path from the XML form using XML namespaces into the JSON format
106 * using module names as namespaces
107 */
108 xml->content = leaf->value_str;
Michal Vaskofba15262015-10-21 12:10:28 +0200109 leaf->value_str = transform_xml2json(leaf->schema->module->ctx, xml->content, xml, 1);
Michal Vasko8ea2b7f2015-09-29 14:30:53 +0200110 lydict_remove(leaf->schema->module->ctx, xml->content);
Michal Vaskofb0873c2015-08-21 09:00:07 +0200111 xml->content = NULL;
112 if (!leaf->value_str) {
113 return EXIT_FAILURE;
Radek Krejciac8aac62015-07-10 15:36:35 +0200114 }
Michal Vasko8ea2b7f2015-09-29 14:30:53 +0200115 }
Radek Krejciac8aac62015-07-10 15:36:35 +0200116
Michal Vasko8ea2b7f2015-09-29 14:30:53 +0200117 if (stype->base == LY_TYPE_UNION) {
Michal Vasko07471a52015-07-16 11:18:48 +0200118 found = 0;
Michal Vasko8ea2b7f2015-09-29 14:30:53 +0200119 type = lyp_get_next_union_type(stype, NULL, &found);
120 while (type) {
121 leaf->value_type = type->base;
Michal Vasko9f9ea082015-10-16 13:41:25 +0200122
123 /* in these cases we use JSON format */
124 if ((type->base == LY_TYPE_IDENT) || (type->base == LY_TYPE_INST)) {
125 xml->content = leaf->value_str;
Michal Vaskofba15262015-10-21 12:10:28 +0200126 leaf->value_str = transform_xml2json(leaf->schema->module->ctx, xml->content, xml, 0);
Michal Vasko9f9ea082015-10-16 13:41:25 +0200127 if (!leaf->value_str) {
128 leaf->value_str = xml->content;
129 xml->content = NULL;
130
131 found = 0;
132 type = lyp_get_next_union_type(stype, type, &found);
133 continue;
134 }
135 }
136
Michal Vasko8ea2b7f2015-09-29 14:30:53 +0200137 if (!lyp_parse_value(leaf, type, resolve, unres, UINT_MAX)) {
Michal Vasko07471a52015-07-16 11:18:48 +0200138 break;
139 }
Michal Vasko8ea2b7f2015-09-29 14:30:53 +0200140
Michal Vasko9f9ea082015-10-16 13:41:25 +0200141 if ((type->base == LY_TYPE_IDENT) || (type->base == LY_TYPE_INST)) {
142 lydict_remove(leaf->schema->module->ctx, leaf->value_str);
143 leaf->value_str = xml->content;
144 xml->content = NULL;
145 }
146
Michal Vasko8ea2b7f2015-09-29 14:30:53 +0200147 found = 0;
148 type = lyp_get_next_union_type(stype, type, &found);
Michal Vasko07471a52015-07-16 11:18:48 +0200149 }
150
151 if (!type) {
Michal Vasko8ea2b7f2015-09-29 14:30:53 +0200152 LOGVAL(LYE_INVAL, LOGLINE(xml), (leaf->value_str ? leaf->value_str : ""), xml->name);
Michal Vasko07471a52015-07-16 11:18:48 +0200153 return EXIT_FAILURE;
154 }
Michal Vasko8ea2b7f2015-09-29 14:30:53 +0200155 } else if (lyp_parse_value(leaf, stype, resolve, unres, LOGLINE(xml))) {
Michal Vasko493bea72015-07-16 16:08:12 +0200156 return EXIT_FAILURE;
Radek Krejcie4748472015-07-08 18:00:22 +0200157 }
158
159 return EXIT_SUCCESS;
160}
161
Michal Vasko0d343d12015-08-24 14:57:36 +0200162/* logs directly */
Michal Vasko24437552015-10-01 16:00:00 +0200163static struct lyd_node *
Michal Vasko493bea72015-07-16 16:08:12 +0200164xml_parse_data(struct ly_ctx *ctx, struct lyxml_elem *xml, struct lyd_node *parent, struct lyd_node *prev,
Michal Vasko23b61ec2015-08-19 11:19:50 +0200165 int options, struct unres_data *unres)
Radek Krejci1721c012015-07-08 12:52:33 +0200166{
Radek Krejci7f40ce32015-08-12 20:38:46 +0200167 struct lyd_node *result = NULL, *diter;
Radek Krejcieab784a2015-08-27 09:56:53 +0200168 struct lys_node *schema = NULL;
Radek Krejcia5241e52015-08-19 15:09:31 +0200169 struct lyxml_attr *attr;
Michal Vasko9f1ef592015-09-29 14:57:51 +0200170 struct lyxml_elem *first_child, *last_child, *child;
Michal Vaskoab8e4402015-07-17 12:54:28 +0200171 int i, havechildren;
Radek Krejci1721c012015-07-08 12:52:33 +0200172
173 if (!xml) {
Michal Vasko0d343d12015-08-24 14:57:36 +0200174 LOGINT;
Radek Krejci1721c012015-07-08 12:52:33 +0200175 return NULL;
176 }
177 if (!xml->ns || !xml->ns->value) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200178 LOGVAL(LYE_XML_MISS, LOGLINE(xml), "element's", "namespace");
Radek Krejci1721c012015-07-08 12:52:33 +0200179 return NULL;
180 }
181
182 /* find schema node */
183 if (!parent) {
184 /* starting in root */
185 for (i = 0; i < ctx->models.used; i++) {
186 /* match data model based on namespace */
187 if (ctx->models.list[i]->ns == xml->ns->value) {
188 /* get the proper schema node */
189 LY_TREE_FOR(ctx->models.list[i]->data, schema) {
190 if (schema->name == xml->name) {
191 break;
192 }
193 }
194 break;
195 }
196 }
197 } else {
198 /* parsing some internal node, we start with parent's schema pointer */
199 schema = xml_data_search_schemanode(xml, parent->schema->child);
200 }
201 if (!schema) {
Radek Krejci25b9fd32015-08-10 15:06:07 +0200202 if ((options & LYD_OPT_STRICT) || ly_ctx_get_module_by_ns(ctx, xml->ns->value, NULL)) {
203 LOGVAL(LYE_INELEM, LOGLINE(xml), xml->name);
204 return NULL;
205 } else {
206 goto siblings;
207 }
Radek Krejci1721c012015-07-08 12:52:33 +0200208 }
209
Radek Krejcia5241e52015-08-19 15:09:31 +0200210 /* check insert attribute and its values */
211 if (options & LYD_OPT_EDIT) {
212 i = 0;
213 for (attr = xml->attr; attr; attr = attr->next) {
214 if (attr->type != LYXML_ATTR_STD || !attr->ns ||
215 strcmp(attr->name, "insert") || strcmp(attr->ns->value, LY_NSYANG)) {
216 continue;
217 }
218
219 /* insert attribute present */
220 if (!(schema->flags & LYS_USERORDERED)) {
221 /* ... but it is not expected */
222 LOGVAL(LYE_INATTR, LOGLINE(xml), "insert", schema->name);
223 return NULL;
224 }
225
226 if (i) {
227 LOGVAL(LYE_TOOMANY, LOGLINE(xml), "insert attributes", xml->name);
228 return NULL;
229 }
230 if (!strcmp(attr->value, "first") || !strcmp(attr->value, "last")) {
231 i = 1;
232 } else if (!strcmp(attr->value, "before") || !strcmp(attr->value, "after")) {
233 i = 2;
234 } else {
235 LOGVAL(LYE_INARG, LOGLINE(xml), attr->value, attr->name);
236 return NULL;
237 }
238 }
239
240 for (attr = xml->attr; attr; attr = attr->next) {
241 if (attr->type != LYXML_ATTR_STD || !attr->ns ||
242 strcmp(attr->name, "value") || strcmp(attr->ns->value, LY_NSYANG)) {
243 continue;
244 }
245
246 /* the value attribute is present */
247 if (i < 2) {
248 /* but it shouldn't */
249 LOGVAL(LYE_INATTR, LOGLINE(xml), "value", schema->name);
250 return NULL;
251 }
252 i++;
253 }
254 if (i == 2) {
255 /* missing value attribute for "before" or "after" */
256 LOGVAL(LYE_MISSATTR, LOGLINE(xml), "value", xml->name);
257 return NULL;
258 } else if (i > 3) {
259 /* more than one instance of the value attribute */
260 LOGVAL(LYE_TOOMANY, LOGLINE(xml), "value attributes", xml->name);
261 return NULL;
262 }
Radek Krejci074bf852015-08-19 14:22:16 +0200263 }
264
Radek Krejcib9930252015-07-08 15:47:45 +0200265 switch (schema->nodetype) {
Radek Krejci76512572015-08-04 09:47:08 +0200266 case LYS_CONTAINER:
Radek Krejci27aaa732015-09-04 15:24:04 +0200267 case LYS_LIST:
Michal Vasko2f30e572015-10-01 16:00:38 +0200268 case LYS_NOTIF:
269 case LYS_RPC:
Michal Vaskoab8e4402015-07-17 12:54:28 +0200270 result = calloc(1, sizeof *result);
271 havechildren = 1;
Radek Krejcib9930252015-07-08 15:47:45 +0200272 break;
Radek Krejci76512572015-08-04 09:47:08 +0200273 case LYS_LEAF:
Radek Krejci76512572015-08-04 09:47:08 +0200274 case LYS_LEAFLIST:
Michal Vasko4c183312015-09-25 10:41:47 +0200275 result = calloc(1, sizeof(struct lyd_node_leaf_list));
Radek Krejcie4748472015-07-08 18:00:22 +0200276 havechildren = 0;
277 break;
Radek Krejci76512572015-08-04 09:47:08 +0200278 case LYS_ANYXML:
Michal Vaskoab8e4402015-07-17 12:54:28 +0200279 result = calloc(1, sizeof(struct lyd_node_anyxml));
280 havechildren = 0;
281 break;
Radek Krejcib9930252015-07-08 15:47:45 +0200282 default:
Michal Vasko0c888fd2015-08-11 15:54:08 +0200283 LOGINT;
Michal Vaskoab8e4402015-07-17 12:54:28 +0200284 return NULL;
Radek Krejcib9930252015-07-08 15:47:45 +0200285 }
Radek Krejci1721c012015-07-08 12:52:33 +0200286 result->parent = parent;
Radek Krejcic41bf8a2015-08-21 10:28:48 +0200287 if (parent && !parent->child) {
288 parent->child = result;
289 }
290 if (prev) {
291 result->prev = prev;
292 prev->next = result;
293
294 /* fix the "last" pointer */
295 for (diter = prev; diter->prev != prev; diter = diter->prev);
296 diter->prev = result;
297 } else {
298 result->prev = result;
299 }
Radek Krejci1721c012015-07-08 12:52:33 +0200300 result->schema = schema;
301
Michal Vaskocf024702015-10-08 15:01:42 +0200302 if (lyv_data_context(result, options, LOGLINE(xml), unres)) {
303 goto error;
304 }
305
Radek Krejcib9930252015-07-08 15:47:45 +0200306 /* type specific processing */
Radek Krejci27aaa732015-09-04 15:24:04 +0200307 if (schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
Radek Krejcie4748472015-07-08 18:00:22 +0200308 /* type detection and assigning the value */
Radek Krejci25b9fd32015-08-10 15:06:07 +0200309 if (xml_get_value(result, xml, options, unres)) {
Radek Krejci3e3affe2015-07-09 15:38:40 +0200310 goto error;
311 }
Radek Krejci1b0d01a2015-08-19 17:00:35 +0200312 } else if (schema->nodetype == LYS_ANYXML && !(options & LYD_OPT_FILTER)) {
Michal Vasko9f1ef592015-09-29 14:57:51 +0200313 /* unlink xml children, they will be the anyxml value */
Radek Krejcie4e4d722015-10-05 16:53:50 +0200314 first_child = last_child = NULL;
Michal Vasko9f1ef592015-09-29 14:57:51 +0200315 LY_TREE_FOR(xml->child, child) {
316 lyxml_unlink_elem(ctx, child, 1);
317 if (!first_child) {
318 first_child = child;
319 last_child = child;
320 } else {
321 last_child->next = child;
322 child->prev = last_child;
323 last_child = child;
324 }
Michal Vasko60beecb2015-09-03 14:24:09 +0200325 }
Radek Krejcie4e4d722015-10-05 16:53:50 +0200326 if (first_child) {
327 first_child->prev = last_child;
328 }
Michal Vasko60beecb2015-09-03 14:24:09 +0200329
Michal Vasko9f1ef592015-09-29 14:57:51 +0200330 ((struct lyd_node_anyxml *)result)->value = first_child;
Michal Vasko60beecb2015-09-03 14:24:09 +0200331 /* we can safely continue with xml, it's like it was, only without children */
Radek Krejcib9930252015-07-08 15:47:45 +0200332 }
333
Radek Krejci1721c012015-07-08 12:52:33 +0200334 /* process children */
Radek Krejcie4748472015-07-08 18:00:22 +0200335 if (havechildren && xml->child) {
Michal Vasko2f30e572015-10-01 16:00:38 +0200336 if (schema->nodetype & (LYS_RPC | LYS_NOTIF)) {
337 xml_parse_data(ctx, xml->child, result, NULL, 0, unres);
338 } else {
339 xml_parse_data(ctx, xml->child, result, NULL, options, unres);
340 }
Radek Krejci25b9fd32015-08-10 15:06:07 +0200341 if (ly_errno) {
Radek Krejci3e3affe2015-07-09 15:38:40 +0200342 goto error;
Radek Krejci1721c012015-07-08 12:52:33 +0200343 }
Radek Krejci1721c012015-07-08 12:52:33 +0200344 }
345
Michal Vasko4ff7b072015-08-21 09:05:03 +0200346 result->attr = (struct lyd_attr *)xml->attr;
347 xml->attr = NULL;
348
Radek Krejcib1c12512015-08-11 11:22:04 +0200349 /* various validation checks */
Radek Krejcieab784a2015-08-27 09:56:53 +0200350 ly_errno = 0;
Michal Vaskocf024702015-10-08 15:01:42 +0200351 if (lyv_data_content(result, options, LOGLINE(xml), unres)) {
Radek Krejcieab784a2015-08-27 09:56:53 +0200352 if (ly_errno) {
Radek Krejcib1c12512015-08-11 11:22:04 +0200353 goto error;
Radek Krejci1b0d01a2015-08-19 17:00:35 +0200354 } else {
Radek Krejcieab784a2015-08-27 09:56:53 +0200355 goto cleargotosiblings;
Radek Krejcida374342015-08-19 13:33:22 +0200356 }
Radek Krejci78ce8612015-08-18 14:31:05 +0200357 }
358
Radek Krejci25b9fd32015-08-10 15:06:07 +0200359siblings:
Radek Krejci1721c012015-07-08 12:52:33 +0200360 /* process siblings */
361 if (xml->next) {
Radek Krejci25b9fd32015-08-10 15:06:07 +0200362 if (result) {
Radek Krejcic41bf8a2015-08-21 10:28:48 +0200363 xml_parse_data(ctx, xml->next, parent, result, options, unres);
Radek Krejci25b9fd32015-08-10 15:06:07 +0200364 } else {
Radek Krejcic41bf8a2015-08-21 10:28:48 +0200365 xml_parse_data(ctx, xml->next, parent, prev, options, unres);
Radek Krejci25b9fd32015-08-10 15:06:07 +0200366 }
367 if (ly_errno) {
Radek Krejci3e3affe2015-07-09 15:38:40 +0200368 goto error;
Radek Krejci1721c012015-07-08 12:52:33 +0200369 }
Radek Krejci1721c012015-07-08 12:52:33 +0200370 }
371
372 return result;
Radek Krejci3e3affe2015-07-09 15:38:40 +0200373
374error:
375
Radek Krejcieab784a2015-08-27 09:56:53 +0200376 /* cleanup */
377 lyd_free(result);
Radek Krejci3e3affe2015-07-09 15:38:40 +0200378
379 return NULL;
Radek Krejci1b0d01a2015-08-19 17:00:35 +0200380
381cleargotosiblings:
382
Radek Krejcieab784a2015-08-27 09:56:53 +0200383 /* remove the result ... */
Radek Krejci1b0d01a2015-08-19 17:00:35 +0200384 lyd_free(result);
385 result = NULL;
386
387 /* ... and then go to siblings label */
388 goto siblings;
Radek Krejci1721c012015-07-08 12:52:33 +0200389}
390
Radek Krejcic6704c82015-10-06 11:12:45 +0200391API struct lyd_node *
392lyd_parse_xml(struct ly_ctx *ctx, struct lyxml_elem *root, int options)
393{
394 struct lyd_node *result, *next, *iter;
395 struct unres_data *unres = NULL;
396
397 if (!ctx || !root) {
398 LOGERR(LY_EINVAL, "%s: Invalid parameter.", __func__);
399 return NULL;
400 }
401
402 unres = calloc(1, sizeof *unres);
403
404 ly_errno = 0;
405 result = xml_parse_data(ctx, root->child, NULL, NULL, options, unres);
406
407 /* check leafrefs and/or instids if any */
408 if (result && resolve_unres_data(unres)) {
409 /* leafref & instid checking failed */
410 LY_TREE_FOR_SAFE(result, next, iter) {
411 lyd_free(iter);
412 }
413 result = NULL;
414 }
415
Michal Vaskocf024702015-10-08 15:01:42 +0200416 free(unres->node);
417 free(unres->type);
Radek Krejcic6704c82015-10-06 11:12:45 +0200418#ifndef NDEBUG
419 free(unres->line);
420#endif
421 free(unres);
422
423 return result;
424}