blob: dd4f913bd26b618c53e31adb11f427eb848bb579 [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>
27
28#include "../libyang.h"
29#include "../common.h"
30#include "../context.h"
31#include "../xml.h"
32
33#define LY_NSNC "urn:ietf:params:xml:ns:netconf:base:1.0"
34
Radek Krejcie4748472015-07-08 18:00:22 +020035static struct ly_mnode *
Radek Krejci1721c012015-07-08 12:52:33 +020036xml_data_search_schemanode(struct lyxml_elem *xml, struct ly_mnode *start)
37{
38 struct ly_mnode *result, *aux;
39
40 LY_TREE_FOR(start, result) {
41 /* skip groupings */
42 if (result->nodetype == LY_NODE_GROUPING) {
43 continue;
44 }
45
46 /* go into cases, choices, uses */
47 if (result->nodetype & (LY_NODE_CHOICE | LY_NODE_CASE | LY_NODE_USES)) {
48 aux = xml_data_search_schemanode(xml, result->child);
49 if (aux) {
50 /* we have matching result */
51 return aux;
52 }
53 /* else, continue with next schema node */
54 continue;
55 }
56
57 /* match data nodes */
58 if (result->name == xml->name) {
59 /* names matches, what about namespaces? */
60 if (result->module->ns == xml->ns->value) {
61 /* we have matching result */
62 return result;
63 }
64 /* else, continue with next schema node */
65 continue;
66 }
67 }
68
69 /* no match */
70 return NULL;
71}
72
Radek Krejcie4748472015-07-08 18:00:22 +020073static int
74xml_get_value(struct lyd_node *node, struct lyxml_elem *xml)
75{
Radek Krejci7511f402015-07-10 09:56:30 +020076#define DECSIZE 21
Radek Krejci3e3affe2015-07-09 15:38:40 +020077 struct lyd_node_leaf *leaf = (struct lyd_node_leaf *)node;
78 struct ly_mnode_leaf *sleaf = (struct ly_mnode_leaf *)node->schema;
79 struct ly_type *type;
Radek Krejci7511f402015-07-10 09:56:30 +020080 char dec[DECSIZE];
81 char *endptr;
Radek Krejci3e3affe2015-07-09 15:38:40 +020082 int len;
Radek Krejci7511f402015-07-10 09:56:30 +020083 int c, i, j, d;
Radek Krejci3e3affe2015-07-09 15:38:40 +020084 int found;
Radek Krejcie4748472015-07-08 18:00:22 +020085
Radek Krejci3e3affe2015-07-09 15:38:40 +020086 switch (sleaf->type.base) {
87 case LY_TYPE_BINARY:
88 leaf->value.binary = xml->content;
89 xml->content = NULL;
90 leaf->value_type = LY_TYPE_BINARY;
91
92 if (sleaf->type.info.binary.length) {
Radek Krejcie4748472015-07-08 18:00:22 +020093 /* TODO: check length restriction */
94 }
95 break;
96
Radek Krejci3e3affe2015-07-09 15:38:40 +020097 case LY_TYPE_BITS:
98 leaf->value_type = LY_TYPE_BITS;
Radek Krejcie4748472015-07-08 18:00:22 +020099
Radek Krejci3e3affe2015-07-09 15:38:40 +0200100 /* locate bits structure with the bits definitions */
101 for (type = &sleaf->type; type->der->type.der; type = &type->der->type);
102
103 /* allocate the array of pointers to bits definition */
104 leaf->value.bit = calloc(type->info.bits.count, sizeof *leaf->value.bit);
105
106 if (!xml->content) {
107 /* no bits set */
108 break;
109 }
110
111 c = 0;
112 i = 0;
113 while (xml->content[c]) {
114 /* skip leading whitespaces */
115 while(isspace(xml->content[c])) {
116 c++;
117 }
118
119 /* get the length of the bit identifier */
Radek Krejci7511f402015-07-10 09:56:30 +0200120 for (len = 0; xml->content[c] && !isspace(xml->content[c]); c++, len++);
Radek Krejci3e3affe2015-07-09 15:38:40 +0200121
122 /* go back to the beginning of the identifier */
123 c = c - len;
124
125 /* find bit definition, identifiers appear ordered by their posititon */
126 for (found = 0; i < type->info.bits.count; i++) {
127 if (!strncmp(type->info.bits.bit[i].name, &xml->content[c], len)
128 && !type->info.bits.bit[i].name[len]) {
129 /* we have match, store the pointer */
130 leaf->value.bit[i] = &type->info.bits.bit[i];
131
132 /* stop searching */
133 i++;
134 found = 1;
135 break;
136 }
137 }
138
139 if (!found) {
140 /* referenced bit value does not exists */
141 LOGVAL(DE_INVAL, LOGLINE(xml), xml->content, xml->name);
142 return EXIT_FAILURE;
143 }
144
145 c = c + len;
146 }
147
148 break;
149
Radek Krejcib7384642015-07-09 16:08:45 +0200150 case LY_TYPE_BOOL:
151 if (!strcmp(xml->content, "true")) {
152 leaf->value.bool = 1;
153 } /* else false, so keep it zero */
154 break;
155
Radek Krejci7511f402015-07-10 09:56:30 +0200156 case LY_TYPE_DEC64:
157 /* locate dec64 structure with the fraction-digits value */
158 for (type = &sleaf->type; type->der->type.der; type = &type->der->type);
159
160 for (c = 0; isspace(xml->content[c]); c++);
161 for (len = 0; xml->content[c] && !isspace(xml->content[c]); c++, len++);
162 c = c - len;
163 if (len > DECSIZE) {
164 /* too long */
165 LOGVAL(DE_INVAL, LOGLINE(xml), xml->content, xml->name);
166 return EXIT_FAILURE;
167 }
168
169 /* normalize the number */
170 dec[0] = '\0';
171 for (i = j = d = found = 0; i < DECSIZE; i++) {
172 if (xml->content[c + i] == '.') {
173 found = 1;
174 j = type->info.dec64.dig;
175 i--;
176 c++;
177 continue;
178 }
179 if (xml->content[c + i] == '\0') {
180 c--;
181 if (!found) {
182 j = type->info.dec64.dig;
183 found = 1;
184 }
185 if (!j) {
186 dec[i] = '\0';
187 break;
188 }
189 d++;
190 if (d > DECSIZE - 2) {
191 LOGVAL(DE_OORVAL, LOGLINE(xml), xml->content, xml->name);
192 return EXIT_FAILURE;
193 }
194 dec[i] = '0';
195 } else {
196 if (!isdigit(xml->content[c + i])) {
197 if (i || xml->content[c] != '-') {
198 LOGVAL(DE_INVAL, LOGLINE(xml), xml->content, xml->name);
199 return EXIT_FAILURE;
200 }
201 } else {
202 d++;
203 }
204 if (d > DECSIZE - 2 || (found && !j)) {
205 LOGVAL(DE_OORVAL, LOGLINE(xml), xml->content, xml->name);
206 return EXIT_FAILURE;
207 }
208 dec[i] = xml->content[c + i];
209 }
210 if (j) {
211 j--;
212 }
213 }
214
215 /* convert string into 64b integer */
216 errno = 0;
217 endptr = NULL;
218 leaf->value.dec64 = strtoll(dec, &endptr, 10);
219 if (errno) {
220 LOGVAL(DE_OORVAL, LOGLINE(xml), xml->content, xml->name);
221 return EXIT_FAILURE;
222 } else if (endptr && *endptr) {
223 LOGVAL(DE_INVAL, LOGLINE(xml), xml->content, xml->name);
224 return EXIT_FAILURE;
225 }
226
227 break;
228
Radek Krejcibce73742015-07-10 12:46:06 +0200229 case LY_TYPE_EMPTY:
230 /* just check that it is empty */
231 if (xml->content && xml->content[0]) {
232 LOGVAL(DE_INVAL, LOGLINE(xml), xml->content, xml->name);
233 return EXIT_FAILURE;
234 }
235 break;
236
Radek Krejci5b315a92015-07-10 13:18:45 +0200237 case LY_TYPE_ENUM:
238 if (!xml->content) {
239 LOGVAL(DE_INVAL, LOGLINE(xml), "", xml->name);
240 return EXIT_FAILURE;
241 }
242
243 /* locate enums structure with the enumeration definitions */
244 for (type = &sleaf->type; type->der->type.der; type = &type->der->type);
245
246 /* find matching enumeration value */
247 for (i = 0; i < type->info.enums.count; i++) {
248 if (!strcmp(xml->content, type->info.enums.list[i].name)) {
249 /* we have match, store pointer to the definition */
250 leaf->value.enm = &type->info.enums.list[i];
251 break;
252 }
253 }
254
255 if (!leaf->value.enm) {
256 LOGVAL(DE_INVAL, LOGLINE(xml), xml->content, xml->name);
257 return EXIT_FAILURE;
258 }
259
260 break;
261
Radek Krejci3e3affe2015-07-09 15:38:40 +0200262 case LY_TYPE_STRING:
263 leaf->value.string = xml->content;
264 xml->content = NULL;
265 leaf->value_type = LY_TYPE_STRING;
266
267 if (sleaf->type.info.str.length) {
Radek Krejcie4748472015-07-08 18:00:22 +0200268 /* TODO: check length restriction */
269 }
270
Radek Krejci3e3affe2015-07-09 15:38:40 +0200271 if (sleaf->type.info.str.patterns) {
Radek Krejcie4748472015-07-08 18:00:22 +0200272 /* TODO: check pattern restriction */
273 }
274 break;
275
276 default:
277 /* TODO */
278 break;
279 }
280
281 return EXIT_SUCCESS;
282}
283
Radek Krejci1721c012015-07-08 12:52:33 +0200284struct lyd_node *
Radek Krejcib9930252015-07-08 15:47:45 +0200285xml_parse_data(struct ly_ctx *ctx, struct lyxml_elem *xml, struct lyd_node *parent, struct lyd_node *prev)
Radek Krejci1721c012015-07-08 12:52:33 +0200286{
287 struct lyd_node *result, *aux;
288 struct ly_mnode *schema = NULL;
289 int i;
Radek Krejcie4748472015-07-08 18:00:22 +0200290 int havechildren = 1;
Radek Krejci1721c012015-07-08 12:52:33 +0200291
292 if (!xml) {
293 return NULL;
294 }
295 if (!xml->ns || !xml->ns->value) {
296 LOGVAL(VE_XML_MISS, LOGLINE(xml), "element's", "namespace");
297 return NULL;
298 }
299
300 /* find schema node */
301 if (!parent) {
302 /* starting in root */
303 for (i = 0; i < ctx->models.used; i++) {
304 /* match data model based on namespace */
305 if (ctx->models.list[i]->ns == xml->ns->value) {
306 /* get the proper schema node */
307 LY_TREE_FOR(ctx->models.list[i]->data, schema) {
308 if (schema->name == xml->name) {
309 break;
310 }
311 }
312 break;
313 }
314 }
315 } else {
316 /* parsing some internal node, we start with parent's schema pointer */
317 schema = xml_data_search_schemanode(xml, parent->schema->child);
318 }
319 if (!schema) {
Radek Krejci3e3affe2015-07-09 15:38:40 +0200320 LOGVAL(DE_INELEM, LOGLINE(xml), xml->name);
Radek Krejci1721c012015-07-08 12:52:33 +0200321 return NULL;
322 }
323
324 /* TODO: fit this into different types of nodes */
Radek Krejcib9930252015-07-08 15:47:45 +0200325 switch (schema->nodetype) {
326 case LY_NODE_LIST:
327 result = calloc(1, sizeof(struct lyd_node_list));
328 break;
Radek Krejcie4748472015-07-08 18:00:22 +0200329 case LY_NODE_LEAF:
330 result = calloc(1, sizeof(struct lyd_node_leaf));
331 havechildren = 0;
332 break;
333 case LY_NODE_LEAFLIST:
334 result = calloc(1, sizeof(struct lyd_node_leaflist));
335 havechildren = 0;
336 break;
Radek Krejcib9930252015-07-08 15:47:45 +0200337 default:
338 result = calloc(1, sizeof *result);
339 }
Radek Krejci1721c012015-07-08 12:52:33 +0200340 result->parent = parent;
Radek Krejcib9930252015-07-08 15:47:45 +0200341 result->prev = prev;
Radek Krejci1721c012015-07-08 12:52:33 +0200342 result->schema = schema;
343
Radek Krejcib9930252015-07-08 15:47:45 +0200344 /* type specific processing */
345 if (schema->nodetype == LY_NODE_LIST) {
346 /* pointers to next and previous instances of the same list */
347 for (aux = result->prev; aux; aux = aux->prev) {
348 if (aux->schema == result->schema) {
349 /* instances of the same list */
350 ((struct lyd_node_list *)aux)->lnext = (struct lyd_node_list *)result;
351 ((struct lyd_node_list *)result)->lprev = (struct lyd_node_list *)aux;
352 break;
353 }
354 }
Radek Krejcie4748472015-07-08 18:00:22 +0200355 } else if (schema->nodetype == LY_NODE_LEAF) {
356 /* type detection and assigning the value */
Radek Krejci3e3affe2015-07-09 15:38:40 +0200357 if (xml_get_value(result, xml)) {
358 goto error;
359 }
Radek Krejcie4748472015-07-08 18:00:22 +0200360 } else if (schema->nodetype == LY_NODE_LEAFLIST) {
361 /* type detection and assigning the value */
Radek Krejci3e3affe2015-07-09 15:38:40 +0200362 if (xml_get_value(result, xml)) {
363 goto error;
364 }
Radek Krejcie4748472015-07-08 18:00:22 +0200365
366 /* pointers to next and previous instances of the same leaflist */
367 for (aux = result->prev; aux; aux = aux->prev) {
368 if (aux->schema == result->schema) {
369 /* instances of the same list */
370 ((struct lyd_node_leaflist *)aux)->lnext = (struct lyd_node_leaflist *)result;
371 ((struct lyd_node_leaflist *)result)->lprev = (struct lyd_node_leaflist *)aux;
372 break;
373 }
374 }
Radek Krejcib9930252015-07-08 15:47:45 +0200375 }
376
Radek Krejci1721c012015-07-08 12:52:33 +0200377 /* process children */
Radek Krejcie4748472015-07-08 18:00:22 +0200378 if (havechildren && xml->child) {
Radek Krejcib9930252015-07-08 15:47:45 +0200379 result->child = xml_parse_data(ctx, xml->child, result, NULL);
Radek Krejci1721c012015-07-08 12:52:33 +0200380 if (!result->child) {
Radek Krejci3e3affe2015-07-09 15:38:40 +0200381 goto error;
Radek Krejci1721c012015-07-08 12:52:33 +0200382 }
383
Radek Krejci1721c012015-07-08 12:52:33 +0200384 }
385
386 /* process siblings */
387 if (xml->next) {
Radek Krejcib9930252015-07-08 15:47:45 +0200388 result->next = xml_parse_data(ctx, xml->next, parent, result);
Radek Krejci1721c012015-07-08 12:52:33 +0200389 if (!result->next) {
Radek Krejci3e3affe2015-07-09 15:38:40 +0200390 goto error;
Radek Krejci1721c012015-07-08 12:52:33 +0200391 }
Radek Krejci1721c012015-07-08 12:52:33 +0200392 }
393
Radek Krejcib9930252015-07-08 15:47:45 +0200394 /* fix the "last" pointer */
395 if (!result->prev) {
396 for (aux = result; aux->next; aux = aux->next);
397 result->prev = aux;
398 }
Radek Krejci1721c012015-07-08 12:52:33 +0200399 return result;
Radek Krejci3e3affe2015-07-09 15:38:40 +0200400
401error:
402
Radek Krejci7511f402015-07-10 09:56:30 +0200403 if (havechildren) {
404 result->child = NULL;
405 }
Radek Krejci3e3affe2015-07-09 15:38:40 +0200406 result->next = NULL;
407 result->parent = NULL;
408 result->prev = result;
409 lyd_node_free(result);
410
411 return NULL;
Radek Krejci1721c012015-07-08 12:52:33 +0200412}
413
414struct lyd_node *
415xml_read_data(struct ly_ctx *ctx, const char *data)
416{
417 struct lyxml_elem *xml;
418 struct lyd_node *result;
419
420 xml = lyxml_read(ctx, data, 0);
421 if (!xml) {
422 return NULL;
423 }
424
425 /* check the returned data - the root must be config or data in NETCONF namespace */
426 if (!xml->ns || strcmp(xml->ns->value, LY_NSNC) || (strcmp(xml->name, "data") && strcmp(xml->name, "config"))) {
427 LOGERR(LY_EINVAL, "XML data parser expect <data> or <config> root in \"%s\" namespace.", LY_NSNC);
428 return NULL;
429 }
430
Radek Krejcib9930252015-07-08 15:47:45 +0200431 result = xml_parse_data(ctx, xml->child, NULL, NULL);
Radek Krejci1721c012015-07-08 12:52:33 +0200432 lyxml_free_elem(ctx, xml);
433
434 return result;
435}