1 /*
2  *
3   ***** BEGIN LICENSE BLOCK *****
4 
5   Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
6   Copyright (C) 2017-2019 Olof Hagsand
7   Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
8 
9   This file is part of CLIXON.
10 
11   Licensed under the Apache License, Version 2.0 (the "License");
12   you may not use this file except in compliance with the License.
13   You may obtain a copy of the License at
14 
15     http://www.apache.org/licenses/LICENSE-2.0
16 
17   Unless required by applicable law or agreed to in writing, software
18   distributed under the License is distributed on an "AS IS" BASIS,
19   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   See the License for the specific language governing permissions and
21   limitations under the License.
22 
23   Alternatively, the contents of this file may be used under the terms of
24   the GNU General Public License Version 3 or later (the "GPL"),
25   in which case the provisions of the GPL are applicable instead
26   of those above. If you wish to allow use of your version of this file only
27   under the terms of the GPL, and not to allow others to
28   use your version of this file under the terms of Apache License version 2,
29   indicate your decision by deleting the provisions above and replace them with
30   the  notice and other provisions required by the GPL. If you do not delete
31   the provisions above, a recipient may use your version of this file under
32   the terms of any one of the Apache License version 2 or the GPL.
33 
34   ***** END LICENSE BLOCK *****
35 
36  *
37  * Translation / mapping code between formats
38  */
39 #ifdef HAVE_CONFIG_H
40 #include "clixon_config.h" /* generated by config & autoconf */
41 #endif
42 
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46 #include <errno.h>
47 #include <ctype.h>
48 #include <string.h>
49 #include <syslog.h>
50 #include <fcntl.h>
51 #include <assert.h>
52 #include <arpa/inet.h>
53 #include <sys/param.h>
54 #include <netinet/in.h>
55 
56 /* cligen */
57 #include <cligen/cligen.h>
58 
59 /* clicon */
60 
61 #include "clixon_string.h"
62 #include "clixon_queue.h"
63 #include "clixon_hash.h"
64 #include "clixon_handle.h"
65 #include "clixon_string.h"
66 #include "clixon_yang.h"
67 #include "clixon_xml.h"
68 #include "clixon_options.h"
69 #include "clixon_data.h"
70 #include "clixon_yang_module.h"
71 #include "clixon_plugin.h"
72 #include "clixon_xml_nsctx.h"
73 #include "clixon_xpath_ctx.h"
74 #include "clixon_xpath.h"
75 #include "clixon_log.h"
76 #include "clixon_err.h"
77 #include "clixon_netconf_lib.h"
78 #include "clixon_xml_sort.h"
79 #include "clixon_yang_type.h"
80 #include "clixon_xml_map.h"
81 
82 /* Local types
83  */
84 /* Merge code needs a two-phase pass where objects subject to merge are first checked for,
85  * the actually inserted.
86  * This is to mitigate a search problem where objects inserted are among the ones checked for
87  */
88 typedef struct  {
89     cxobj     *mt_x0c;
90     cxobj     *mt_x1c;
91     yang_stmt *mt_yc;
92 } merge_twophase;
93 
94 /*! Is attribute and is either of form xmlns="", or xmlns:x="" */
95 int
isxmlns(cxobj * x)96 isxmlns(cxobj *x)
97 {
98     char *prefix = NULL;
99 
100     if (xml_type(x) != CX_ATTR)
101 	return 0;
102     if (strcmp(xml_name(x), "xmlns")==0)
103 	return 1;
104     if ((prefix = xml_prefix(x)) != NULL
105 	&& strcmp(xml_prefix(x), "xmlns")==0)
106 	return 1;
107     return 0;
108 }
109 
110 /*! x is element and has eactly one child which in turn has none
111  * @see child_type in clixon_json.c
112  */
113 static int
tleaf(cxobj * x)114 tleaf(cxobj *x)
115 {
116     cxobj *xc;
117 
118     if (xml_type(x) != CX_ELMNT)
119 	return 0;
120     if (xml_child_nr_notype(x, CX_ATTR) != 1)
121 	return 0;
122     /* From here exactly one noattr child, get it */
123     xc = NULL;
124     while ((xc = xml_child_each(x, xc, -1)) != NULL)
125 	if (xml_type(xc) != CX_ATTR)
126 	    break;
127     if (xc == NULL)
128 	return -1; /* n/a */
129     return (xml_child_nr_notype(xc, CX_ATTR) == 0);
130 }
131 
132 /*! Translate XML to a "pseudo-code" textual format using a callback - internal function
133  * @param[in]  f      File to print to
134  * @param[in]  x      XML object to print
135  * @param[in]  fn     Callback to make print function
136  * @param[in]  level  print 4 spaces per level in front of each line
137  */
138 static int
xml2txt_recurse(FILE * f,cxobj * x,clicon_output_cb * fn,int level)139 xml2txt_recurse(FILE             *f,
140 		cxobj            *x,
141 		clicon_output_cb *fn,
142 		int               level)
143 {
144     cxobj *xc = NULL;
145     int    children=0;
146     int    retval = -1;
147 
148     if (f == NULL || x == NULL || fn == NULL){
149 	clicon_err(OE_XML, EINVAL, "f, x or fn is NULL");
150 	goto done;
151     }
152     xc = NULL;     /* count children (elements and bodies, not attributes) */
153     while ((xc = xml_child_each(x, xc, -1)) != NULL)
154 	if (xml_type(xc) == CX_ELMNT || xml_type(xc) == CX_BODY)
155 	    children++;
156     if (!children){ /* If no children print line */
157 	switch (xml_type(x)){
158 	case CX_BODY:
159 	    (*fn)(f, "%s;\n", xml_value(x));
160 	    break;
161 	case CX_ELMNT:
162 	    (*fn)(f, "%*s;\n", 4*level, xml_name(x));
163 	    break;
164 	default:
165 	    break;
166 	}
167 	goto ok;
168     }
169     (*fn)(f, "%*s", 4*level, "");
170     (*fn)(f, "%s ", xml_name(x));
171     if (!tleaf(x))
172 	(*fn)(f, "{\n");
173     xc = NULL;
174     while ((xc = xml_child_each(x, xc, -1)) != NULL){
175 	if (xml_type(xc) == CX_ELMNT || xml_type(xc) == CX_BODY)
176 	    if (xml2txt_recurse(f, xc, fn, level+1) < 0)
177 		break;
178     }
179     if (!tleaf(x))
180 	(*fn)(f, "%*s}\n", 4*level, "");
181  ok:
182     retval = 0;
183  done:
184     return retval;
185 }
186 
187 /*! Translate XML to a "pseudo-code" textual format using a callback
188  * @param[in]  f      File to print to
189  * @param[in]  x      XML object to print
190  * @param[in]  fn     Callback to make print function
191  */
192 int
xml2txt_cb(FILE * f,cxobj * x,clicon_output_cb * fn)193 xml2txt_cb(FILE             *f,
194 	   cxobj            *x,
195 	   clicon_output_cb *fn)
196 {
197     return xml2txt_recurse(f, x, fn, 0);
198 }
199 
200 /*! Translate XML to a "pseudo-code" textual format using stdio file
201  * @param[in]  f      File to print to
202  * @param[in]  x      XML object to print
203  * @param[in]  level  print 4 spaces per level in front of each line
204  * @see xml2txt_cb
205  */
206 int
xml2txt(FILE * f,cxobj * x,int level)207 xml2txt(FILE  *f,
208 	cxobj *x,
209 	int    level)
210 {
211     return xml2txt_recurse(f, x, fprintf, 0);
212 }
213 
214 /*! Translate from XML to CLI commands
215  * Howto: join strings and pass them down.
216  * Identify unique/index keywords for correct set syntax.
217  * @param[in] f        Where to print cli commands
218  * @param[in] x        XML Parse-tree (to translate)
219  * @param[in] prepend  Print this text in front of all commands.
220  * @param[in] gt       option to steer cli syntax
221  * @param[in] fn       Callback to make print function
222  */
223 int
xml2cli_recurse(FILE * f,cxobj * x,char * prepend,enum genmodel_type gt,clicon_output_cb * fn)224 xml2cli_recurse(FILE              *f,
225 		cxobj             *x,
226 		char              *prepend,
227 		enum genmodel_type gt,
228 		clicon_output_cb  *fn)
229 {
230     int              retval = -1;
231     cxobj           *xe = NULL;
232     cbuf            *cbpre = NULL;
233     yang_stmt       *ys;
234     int              match;
235     char            *body;
236 
237     if (xml_type(x)==CX_ATTR)
238 	goto ok;
239     if ((ys = xml_spec(x)) == NULL)
240 	goto ok;
241     if (yang_keyword_get(ys) == Y_LEAF || yang_keyword_get(ys) == Y_LEAF_LIST){
242 	if (prepend)
243 	    (*fn)(f, "%s", prepend);
244 	if (gt == GT_ALL || gt == GT_VARS || gt == GT_HIDE)
245 	    (*fn)(f, "%s ", xml_name(x));
246 	if ((body = xml_body(x)) != NULL){
247 	    if (index(body, ' '))
248 		(*fn)(f, "\"%s\"", body);
249 	    else
250 		(*fn)(f, "%s", body);
251 	}
252 	(*fn)(f, "\n");
253 	goto ok;
254     }
255     /* Create prepend variable string */
256     if ((cbpre = cbuf_new()) == NULL){
257 	clicon_err(OE_PLUGIN, errno, "cbuf_new");
258 	goto done;
259     }
260     if (prepend)
261 	cprintf(cbpre, "%s", prepend);
262 
263     /* If non-presence container && HIDE mode && only child is
264      * a list, then skip container keyword
265      * See also yang2cli_container */
266     if (yang_container_cli_hide(ys, gt) == 0)
267 	cprintf(cbpre, "%s ", xml_name(x));
268 
269     if (yang_keyword_get(ys) == Y_LIST){
270 	/* If list then first loop through keys */
271 	xe = NULL;
272 	while ((xe = xml_child_each(x, xe, -1)) != NULL){
273 	    if ((match = yang_key_match(ys, xml_name(xe))) < 0)
274 		goto done;
275 	    if (!match)
276 		continue;
277 	    if (gt == GT_ALL)
278 		cprintf(cbpre, "%s ", xml_name(xe));
279 	    cprintf(cbpre, "%s ", xml_body(xe));
280 	}
281     }
282     /* Then loop through all other (non-keys) */
283     xe = NULL;
284     while ((xe = xml_child_each(x, xe, -1)) != NULL){
285 	if (yang_keyword_get(ys) == Y_LIST){
286 	    if ((match = yang_key_match(ys, xml_name(xe))) < 0)
287 		goto done;
288 	    if (match){
289 		(*fn)(f, "%s\n", cbuf_get(cbpre));
290 		continue; /* Not key itself */
291 	    }
292 	}
293 	if (xml2cli_recurse(f, xe, cbuf_get(cbpre), gt, fn) < 0)
294 	    goto done;
295     }
296  ok:
297     retval = 0;
298   done:
299     if (cbpre)
300 	cbuf_free(cbpre);
301     return retval;
302 }
303 
304 /*! Translate from XML to CLI commands
305  * Howto: join strings and pass them down.
306  * Identify unique/index keywords for correct set syntax.
307  * @param[in] f        Where to print cli commands
308  * @param[in] x        XML Parse-tree (to translate)
309  * @param[in] prepend  Print this text in front of all commands.
310  * @param[in] gt       option to steer cli syntax
311  * @param[in] fn       Callback to make print function
312  */
313 int
xml2cli_cb(FILE * f,cxobj * x,char * prepend,enum genmodel_type gt,clicon_output_cb * fn)314 xml2cli_cb(FILE              *f,
315 	   cxobj             *x,
316 	   char              *prepend,
317 	   enum genmodel_type gt,
318 	   clicon_output_cb  *fn)
319 {
320     return xml2cli_recurse(f, x, prepend, gt, fn);
321 }
322 
323 /*! Translate from XML to CLI commands
324  * Howto: join strings and pass them down.
325  * Identify unique/index keywords for correct set syntax.
326  * Args:
327  *  @param[in] f        Where to print cli commands
328  *  @param[in] x        XML Parse-tree (to translate)
329  *  @param[in] prepend  Print this text in front of all commands.
330  *  @param[in] gt       option to steer cli syntax
331  */
332 int
xml2cli(FILE * f,cxobj * x,char * prepend,enum genmodel_type gt)333 xml2cli(FILE              *f,
334 	cxobj             *x,
335 	char              *prepend,
336 	enum genmodel_type gt)
337 {
338     return xml2cli_recurse(f, x, prepend, gt, fprintf);
339 }
340 
341 /*! Translate a single xml node to a cligen variable vector. Note not recursive
342  * @param[in]  xt   XML tree containing one top node
343  * @param[in]  ys   Yang spec containing type specification of top-node of xt
344  * @param[out] cvv  CLIgen variable vector. Should be freed by cvec_free()
345  * @retval     0    Everything OK, cvv allocated and set
346  * @retval    -1    Something wrong, clicon_err() called to set error. No cvv returned
347  * @note  cvv Should be freed by cvec_free() after use.
348  * 'Not recursive' means that only one level of XML bodies is translated to cvec:s.
349  * If range is wriong (eg 1000 for uint8) a warning is logged, the value is
350  * skipped, and continues.
351  * yang is needed to know which type an xml element has.
352  * Example:
353     <a>
354       <b>23</b>
355       <c>88</c>
356       <d>
357         <e>99</e>
358       </d>
359     </a>
360          --> b:23, c:88
361  * @see cvec2xml
362  */
363 int
xml2cvec(cxobj * xt,yang_stmt * yt,cvec ** cvv0)364 xml2cvec(cxobj      *xt,
365 	 yang_stmt  *yt,
366 	 cvec      **cvv0)
367 {
368     int               retval = -1;
369     cvec             *cvv = NULL;
370     cxobj            *xc;         /* xml iteration variable */
371     yang_stmt        *ys;         /* yang spec */
372     cg_var           *cv;
373     cg_var           *ycv;
374     char             *body;
375     char             *reason = NULL;
376     int               ret;
377     char             *name;
378 
379     xc = NULL;
380     /* Tried to allocate whole cvv here, but some cg_vars may be invalid */
381     if ((cvv = cvec_new(0)) == NULL){
382 	clicon_err(OE_UNIX, errno, "cvec_new");
383 	goto err;
384     }
385     xc = NULL;
386     /* Go through all children of the xml tree */
387     while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL){
388 	name = xml_name(xc);
389 	if ((ys = yang_find_datanode(yt, name)) == NULL){
390 	    clicon_debug(0, "%s: yang sanity problem: %s in xml but not present in yang under %s",
391 			 __FUNCTION__, name, yang_argument_get(yt));
392 	    if ((body = xml_body(xc)) != NULL){
393 		if ((cv = cv_new(CGV_STRING)) == NULL){
394 		    clicon_err(OE_PLUGIN, errno, "cv_new");
395 		    goto err;
396 		}
397 		cv_name_set(cv, name);
398 		if ((ret = cv_parse1(body, cv, &reason)) < 0){
399 		    clicon_err(OE_PLUGIN, errno, "cv_parse %s",name);
400 		    goto err;
401 		}
402 		/* If value is out-of-range, log and skip value, and continue */
403 		if (ret == 0){
404 		    clicon_log(LOG_WARNING, "cv_parse %s: %s", name, reason);
405 		    if (reason)
406 			free(reason);
407 		}
408 		else
409 		    cvec_append_var(cvv, cv); /* Add to variable vector */
410 		cv_free(cv);
411 	    }
412 	}
413 	else if ((ycv = yang_cv_get(ys)) != NULL){
414 	    if ((body = xml_body(xc)) != NULL){
415 		if ((cv = cv_new(CGV_STRING)) == NULL){
416 		    clicon_err(OE_PLUGIN, errno, "cv_new");
417 		    goto err;
418 		}
419 		if (cv_cp(cv, ycv) < 0){
420 		    clicon_err(OE_PLUGIN, errno, "cv_cp");
421 		    goto err;
422 		}
423 		if ((ret = cv_parse1(body, cv, &reason)) < 0){
424 		    clicon_err(OE_PLUGIN, errno, "cv_parse: %s", name);
425 		    goto err;
426 		}
427 		if (ret == 0){
428 		    clicon_log(LOG_WARNING, "cv_parse %s: %s", name, reason);
429 		    if (reason)
430 			free(reason);
431 		}
432 		else
433 		    cvec_append_var(cvv, cv); /* Add to variable vector */
434 		cv_free(cv);
435 	    }
436 	}
437     }
438     if (clicon_debug_get() > 1){
439 	clicon_debug(2, "%s cvv:\n", __FUNCTION__);
440 	cvec_print(stderr, cvv);
441     }
442     *cvv0 = cvv;
443     return 0;
444  err:
445     if (cvv)
446 	cvec_free(cvv);
447     return retval;
448 }
449 
450 /*! Translate a cligen variable vector to an XML tree with depth one
451  * @param[in]   cvv  CLIgen variable vector. Should be freed by cvec_free()
452  * @param[in]   toptag    The XML tree in xt will have this XML tag
453  * @param[in]   xt   Parent, or NULL
454  * @param[out]  xt   Pointer to XML tree containing one top node. Should be freed with xml_free
455  * @retval      0    Everything OK, cvv allocated and set
456  * @retval     -1    Something wrong, clicon_err() called to set error. No xt returned
457  * @see xml2cvec
458  * @see cvec2xml   This does more but has an internal xml2cvec translation
459 */
460 int
cvec2xml_1(cvec * cvv,char * toptag,cxobj * xp,cxobj ** xt0)461 cvec2xml_1(cvec   *cvv,
462 	   char   *toptag,
463 	   cxobj  *xp,
464 	   cxobj **xt0)
465 {
466     int               retval = -1;
467     cxobj            *xt = NULL;
468     cxobj            *xn;
469     cxobj            *xb;
470     cg_var           *cv;
471     char             *val;
472     int               len=0;
473     int               i;
474 
475     cv = NULL;
476     while ((cv = cvec_each(cvv, cv)) != NULL)
477 	len++;
478     if ((xt = xml_new(toptag, xp, CX_ELMNT)) == NULL)
479 	goto err;
480     if (xml_childvec_set(xt, len) < 0)
481 	goto err;
482     cv = NULL;
483     i = 0;
484     while ((cv = cvec_each(cvv, cv)) != NULL) {
485 	if (cv_type_get(cv)==CGV_ERR || cv_name_get(cv) == NULL)
486 	    continue;
487 	if ((xn = xml_new(cv_name_get(cv), NULL, CX_ELMNT)) == NULL) /* this leaks */
488 	    goto err;
489 	xml_parent_set(xn, xt);
490 	xml_child_i_set(xt, i++, xn);
491 	if ((xb = xml_new("body", xn, CX_BODY)) == NULL) /* this leaks */
492 	    goto err;
493 	val = cv2str_dup(cv);
494 	xml_value_set(xb, val); /* this leaks */
495 	if (val)
496 	    free(val);
497     }
498     *xt0 = xt;
499     return 0;
500  err:
501     if (xt)
502 	xml_free(xt);
503     return retval;
504 }
505 
506 /*! Recursive help function to compute differences between two xml trees
507  * @param[in]  x0         First XML tree
508  * @param[in]  x1         Second XML tree
509  * @param[out] x0vec      Pointervector to XML nodes existing in only first tree
510  * @param[out] x0veclen   Length of first vector
511  * @param[out] x1vec      Pointervector to XML nodes existing in only second tree
512  * @param[out] x1veclen   Length of x1vec vector
513  * @param[out] changed_x0 Pointervector to XML nodes changed orig value
514  * @param[out] changed_x1 Pointervector to XML nodes changed wanted value
515  * @param[out] changedlen Length of changed vector
516  * Algorithm to compare two sorted lists A, B:
517  *   A 0 1 2 3 5 6
518  *   B 0 2 4 5 6
519  * Let (a, b) be first elements of (A, B) respectively(*)
520  *   a = b :  EITHER leafs: a!=b : add a in changed_x0, b in changed_x1,
521  *            OR: Set (A,B) to children of (a,b) and call algorithm recursively
522  *         , get next (a,b)
523  *   a < b : add a in x0, get next a
524  *   a > b : add b in x1, get next b
525  * (*) "comparing" a&b here is made by xml_cmp() which judges equality from a structural
526  *     perspective, ie both have the same yang spec, if they are lists, they have the
527  *     the same keys. NOT that the values are equal!
528  * @see xml_diff  API function, this one is internal and recursive
529  */
530 static int
xml_diff1(cxobj * x0,cxobj * x1,cxobj *** x0vec,int * x0veclen,cxobj *** x1vec,int * x1veclen,cxobj *** changed_x0,cxobj *** changed_x1,int * changedlen)531 xml_diff1(cxobj     *x0,
532 	  cxobj     *x1,
533 	  cxobj   ***x0vec,
534 	  int       *x0veclen,
535 	  cxobj   ***x1vec,
536 	  int       *x1veclen,
537 	  cxobj   ***changed_x0,
538 	  cxobj   ***changed_x1,
539 	  int       *changedlen)
540 {
541     int        retval = -1;
542     cxobj     *x0c = NULL; /* x0 child */
543     cxobj     *x1c = NULL; /* x1 child */
544     yang_stmt *yc;
545     char      *b1;
546     char      *b2;
547     int        eq;
548 
549     /* Traverse x0 and x1 in lock-step */
550     x0c = x1c = NULL;
551     x0c = xml_child_each(x0, x0c, CX_ELMNT);
552     x1c = xml_child_each(x1, x1c, CX_ELMNT);
553     for (;;){
554 	if (x0c == NULL && x1c == NULL)
555 	    goto ok;
556 	else if (x0c == NULL){
557 	    if (cxvec_append(x1c, x1vec, x1veclen) < 0)
558 		goto done;
559 	    x1c = xml_child_each(x1, x1c, CX_ELMNT);
560 	    continue;
561 	}
562 	else if (x1c == NULL){
563 	    if (cxvec_append(x0c, x0vec, x0veclen) < 0)
564 		goto done;
565 	    x0c = xml_child_each(x0, x0c, CX_ELMNT);
566 	    continue;
567 	}
568 	/* Both x0c and x1c exists, check if they are equal. */
569 	eq = xml_cmp(x0c, x1c, 0, 0, NULL);
570 	if (eq < 0){
571 	    if (cxvec_append(x0c, x0vec, x0veclen) < 0)
572 		goto done;
573 	    x0c = xml_child_each(x0, x0c, CX_ELMNT);
574 	    continue;
575 	}
576 	else if (eq > 0){
577 	    if (cxvec_append(x1c, x1vec, x1veclen) < 0)
578 		goto done;
579 	    x1c = xml_child_each(x1, x1c, CX_ELMNT);
580 	    continue;
581 	}
582 	else{ /* equal */
583 	    /* xml-spec NULL could happen with anydata children for example,
584 	     * if so, continute compare children but without yang
585 	     */
586 	    yc = xml_spec(x0c);
587 	    if (yc && yang_keyword_get(yc) == Y_LEAF){
588 		/* if x0c and x1c are leafs w bodies, then they may be changed */
589 		if ((b1 = xml_body(x0c)) != NULL && /* skip empty type */
590 		    (b2 = xml_body(x1c)) != NULL && /* skip empty type */
591 		    strcmp(b1, b2) != 0){
592 		    if (cxvec_append(x0c, changed_x0, changedlen) < 0)
593 			goto done;
594 		    (*changedlen)--; /* append two vectors */
595 		    if (cxvec_append(x1c, changed_x1, changedlen) < 0)
596 			goto done;
597 		}
598 	    }
599 	    else if (xml_diff1(x0c, x1c,
600 			       x0vec, x0veclen,
601 			       x1vec, x1veclen,
602 			       changed_x0, changed_x1, changedlen)< 0)
603 		goto done;
604 	}
605 	x0c = xml_child_each(x0, x0c, CX_ELMNT);
606 	x1c = xml_child_each(x1, x1c, CX_ELMNT);
607     }
608  ok:
609     retval = 0;
610  done:
611     return retval;
612 }
613 
614 /*! Compute differences between two xml trees
615  * @param[in]  yspec      Yang specification
616  * @param[in]  x0         First XML tree
617  * @param[in]  x1         Second XML tree
618  * @param[out] first      Pointervector to XML nodes existing in only first tree
619  * @param[out] firstlen   Length of first vector
620  * @param[out] second     Pointervector to XML nodes existing in only second tree
621  * @param[out] secondlen  Length of second vector
622  * @param[out] changed_x0 Pointervector to XML nodes changed orig value
623  * @param[out] changed_x1 Pointervector to XML nodes changed wanted value
624  * @param[out] changedlen Length of changed vector
625  * All xml vectors should be freed after use.
626  */
627 int
xml_diff(yang_stmt * yspec,cxobj * x0,cxobj * x1,cxobj *** first,int * firstlen,cxobj *** second,int * secondlen,cxobj *** changed_x0,cxobj *** changed_x1,int * changedlen)628 xml_diff(yang_stmt *yspec,
629 	 cxobj     *x0,
630 	 cxobj     *x1,
631 	 cxobj   ***first,
632 	 int       *firstlen,
633 	 cxobj   ***second,
634 	 int       *secondlen,
635 	 cxobj   ***changed_x0,
636 	 cxobj   ***changed_x1,
637 	 int       *changedlen)
638 {
639     int retval = -1;
640 
641     *firstlen = 0;
642     *secondlen = 0;
643     *changedlen = 0;
644     if (x0 == NULL && x1 == NULL)
645 	return 0;
646     if (x1 == NULL){
647 	if (cxvec_append(x0, first, firstlen) < 0)
648 	    goto done;
649 	goto ok;
650     }
651     if (x0 == NULL){
652 	if (cxvec_append(x0, second, secondlen) < 0)
653 	    goto done;
654 	goto ok;
655     }
656     if (xml_diff1(x0, x1,
657 		  first, firstlen,
658 		  second, secondlen,
659 		  changed_x0, changed_x1, changedlen) < 0)
660 	goto done;
661  ok:
662     retval = 0;
663  done:
664     return retval;
665 }
666 
667 /*! Prune everything that does not pass test or have at least a child* does not
668  * @param[in]   xt      XML tree with some node marked
669  * @param[in]   flag    Which flag to test for
670  * @param[in]   test    1: test that flag is set, 0: test that flag is not set
671  * @param[out]  upmark  Set if a child (recursively) has marked set.
672  * The function removes all branches that does not pass the test
673  * Purge all nodes that dont have MARK flag set recursively.
674  * Save all nodes that is MARK:ed or have at least one (grand*)child that is MARKed
675  * @code
676  *    xml_tree_prune_flagged_sub(xt, XML_FLAG_MARK, 1, NULL);
677  * @endcode
678  * @note This function seems a little too complex semantics
679  * @see xml_tree_prune_flagged for a simpler variant
680  */
681 #if 1
682 int
xml_tree_prune_flagged_sub(cxobj * xt,int flag,int test,int * upmark)683 xml_tree_prune_flagged_sub(cxobj *xt,
684 			   int    flag,
685 			   int    test,
686 			   int   *upmark)
687 {
688     int        retval = -1;
689     int        submark;
690     int        mark;
691     cxobj     *x;
692     cxobj     *xprev;
693     int        iskey;
694     int        anykey=0;
695     yang_stmt *yt;
696 
697     mark = 0;
698     yt = xml_spec(xt); /* xan be null */
699     x = NULL;
700     xprev = x = NULL;
701     while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
702 	if (xml_flag(x, flag) == test?flag:0){
703 	    /* Pass test */
704 	    mark++;
705 	    xprev = x;
706 	    continue; /* mark and stop here */
707 	}
708 	/* If it is key dont remove it yet (see second round) */
709 	if (yt){
710 	    if ((iskey = yang_key_match(yt, xml_name(x))) < 0)
711 		goto done;
712 	    if (iskey){
713 		anykey++;
714 		xprev = x; /* skip if this is key */
715 		continue;
716 	    }
717 	}
718 	if (xml_tree_prune_flagged_sub(x, flag, test, &submark) < 0)
719 	    goto done;
720 	/* if xt is list and submark anywhere, then key subs are also marked
721 	 */
722 	if (submark)
723 	    mark++;
724 	else{ /* Safe with xml_child_each if last */
725 	    if (xml_purge(x) < 0)
726 		goto done;
727 	    x = xprev;
728 	}
729 	xprev = x;
730     }
731     /* Second round: if any keys were found, and no marks detected, purge now */
732     if (anykey && !mark){
733 	x = NULL;
734 	xprev = x = NULL;
735 	while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
736 	    /* If it is key remove it here */
737 	    if (yt){
738 		if ((iskey = yang_key_match(yt, xml_name(x))) < 0)
739 		    goto done;
740 		if (iskey && xml_purge(x) < 0)
741 		    goto done;
742 		x = xprev;
743 	    }
744 	    xprev = x;
745 	}
746     }
747     retval = 0;
748  done:
749     if (upmark)
750 	*upmark = mark;
751     return retval;
752 }
753 #else
754 /* This is optimized in the sense that xml_purge is replaced with xml_child_rm but it leaks memory,
755  * in poarticualr attributes and namespace caches
756  */
757 int
xml_tree_prune_flagged_sub(cxobj * xt,int flag,int test,int * upmark)758 xml_tree_prune_flagged_sub(cxobj *xt,
759 			   int    flag,
760 			   int    test,
761 			   int   *upmark)
762 {
763     int        retval = -1;
764     int        submark;
765     int        mark;
766     cxobj     *x;
767     cxobj     *xprev;
768     int        iskey;
769     int        anykey=0;
770     yang_stmt *yt;
771     int        i;
772 
773     mark = 0;
774     yt = xml_spec(xt); /* xan be null */
775     x = NULL;
776     xprev = x = NULL;
777     i = 0;
778     while ((x = xml_child_each(xt, x, -1)) != NULL) {
779 	i++;
780 	if (xml_type(x) != CX_ELMNT){
781 	    xprev = x;
782 	    continue;
783 	}
784 	if (xml_flag(x, flag) == test?flag:0){
785 	    /* Pass test */
786 	    mark++;
787 	    xprev = x;
788 	    continue; /* mark and stop here */
789 	}
790 	/* If it is key dont remove it yet (see second round) */
791 	if (yt){
792 	    if ((iskey = yang_key_match(yt, xml_name(x))) < 0)
793 		goto done;
794 	    if (iskey){
795 		anykey++;
796 		xprev = x; /* skip if this is key */
797 		continue;
798 	    }
799 	}
800 	if (xml_tree_prune_flagged_sub(x, flag, test, &submark) < 0)
801 	    goto done;
802 	/* if xt is list and submark anywhere, then key subs are also marked
803 	 */
804 	if (submark)
805 	    mark++;
806 	else{ /* Safe with xml_child_each if last */
807 	    if (xml_child_rm(xt, i-1) < 0)
808 		goto done;
809 	    i--;
810 	    x = xprev;
811 	}
812 	xprev = x;
813     }
814     /* Second round: if any keys were found, and no marks detected, purge now */
815     if (anykey && !mark){
816 	x = NULL;
817 	xprev = x = NULL;
818 	i = 0;
819 	while ((x = xml_child_each(xt, x, -1)) != NULL) {
820 	    i++;
821 	    if (xml_type(x) != CX_ELMNT){
822 		xprev = x;
823 		continue;
824 	    }
825 	    /* If it is key remove it here */
826 	    if (yt){
827 		if ((iskey = yang_key_match(yt, xml_name(x))) < 0)
828 		    goto done;
829 		if (xml_child_rm(xt, i-1) < 0)
830 		    goto done;
831 		i--;
832 		x = xprev;
833 	    }
834 	    xprev = x;
835 	}
836     }
837     retval = 0;
838  done:
839     if (upmark)
840 	*upmark = mark;
841     return retval;
842 }
843 #endif
844 
845 /*! Prune everything that passes test
846  * @param[in]   xt      XML tree with some node marked
847  * @param[in]   flag    Which flag to test for
848  * @param[in]   test    1: test that flag is set, 0: test that flag is not set
849  * The function removes all branches that does not pass test
850  * @code
851  *    xml_tree_prune_flagged(xt, XML_FLAG_MARK, 1);
852  * @endcode
853  */
854 int
xml_tree_prune_flagged(cxobj * xt,int flag,int test)855 xml_tree_prune_flagged(cxobj *xt,
856 		       int    flag,
857 		       int    test)
858 {
859     int        retval = -1;
860     cxobj     *x;
861     cxobj     *xprev;
862 
863     x = NULL;
864     xprev = NULL;
865     while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
866 	if (xml_flag(x, flag) == (test?flag:0)){ 	/* Pass test means purge */
867 	    if (xml_purge(x) < 0)
868 		goto done;
869 	    x = xprev;
870 	    continue;
871 	}
872 	if (xml_tree_prune_flagged(x, flag, test) < 0)
873 	    goto done;
874 	xprev = x;
875     }
876     retval = 0;
877  done:
878     return retval;
879 }
880 
881 /*! Add prefix:namespace pair to xml node, set cache, etc
882  * @param[in]  x         XML node whose namespace should change
883  * @param[in]  xp        XML node where namespace attribute should be declared (can be same)
884  * @param[in]  prefix1   Use this prefix
885  * @param[in]  namespace Use this namespace
886  * @note x and xp must be different if x is an attribute and may be different otherwise
887  */
888 static int
add_namespace(cxobj * x,cxobj * xp,char * prefix,char * namespace)889 add_namespace(cxobj *x,
890 	      cxobj *xp,
891 	      char  *prefix,
892 	      char  *namespace)
893 {
894     int    retval = -1;
895     cxobj *xa = NULL;
896 
897     /* Add binding to x1p. We add to parent due to heurestics, so we dont
898      * end up in adding it to large number of siblings
899      */
900     if (nscache_set(x, prefix, namespace) < 0)
901 	goto done;
902     /* Create xmlns attribute to x1p/x1 XXX same code v */
903     if (prefix){
904 	if ((xa = xml_new(prefix, xp, CX_ATTR)) == NULL)
905 	    goto done;
906 	if (xml_prefix_set(xa, "xmlns") < 0)
907 	    goto done;
908     }
909     else{
910 	if ((xa = xml_new("xmlns", xp, CX_ATTR)) == NULL)
911 	    goto done;
912     }
913     if (xml_value_set(xa, namespace) < 0)
914 	goto done;
915     xml_sort(xp); /* Ensure attr is first / XXX xml_insert? */
916 
917     retval = 0;
918  done:
919     return retval;
920 }
921 
922 /*! Change namespace of XML node
923  *
924  * @param[in]  x         XML node
925  * @param[in]  ns        Change to this namespace (if ns does not exist in tree)
926  * @param[in]  prefix    If change, use this prefix
927  * @param      0         OK
928  * @param     -1         Error
929  */
930 int
xml_namespace_change(cxobj * x,char * ns,char * prefix)931 xml_namespace_change(cxobj *x,
932                     char   *ns,
933                     char   *prefix)
934 {
935     int    retval = -1;
936     char  *ns0 = NULL;     /* existing namespace */
937     char  *prefix0 = NULL; /* existing prefix */
938     cxobj *xp;
939 
940     ns0 = NULL;
941     if (xml2ns(x, xml_prefix(x), &ns0) < 0)
942        goto done;
943     if (ns0 && strcmp(ns0, ns) == 0)
944 	goto ok; /* Already has right namespace */
945     /* Is namespace already declared? */
946     if (xml2prefix(x, ns, &prefix0) == 1){
947        /* Yes it is declared and the prefix is prefix0 */
948        if (xml_prefix_set(x, prefix0) < 0)
949            goto done;
950     }
951     else{ /* Namespace does not exist, add it */
952 	/* Clear old prefix if any */
953        if (xml_prefix_set(x, NULL) < 0)
954            goto done;
955        if (xml_type(x) == CX_ELMNT) /* If not element, do the namespace addition to the element */
956 	   xp = x;
957        else
958 	   xp = xml_parent(x);
959        if (add_namespace(x, xp, prefix, ns) < 0)
960 	   goto done;
961        /* Add prefix to x, if any */
962        if (prefix && xml_prefix_set(x, prefix) < 0)
963 	   goto done;
964 
965     }
966  ok:
967     retval = 0;
968  done:
969     return retval;
970 }
971 
972 int
xml_default_create1(yang_stmt * y,cxobj * xt,int top,cxobj ** xcp)973 xml_default_create1(yang_stmt *y,
974 		    cxobj     *xt,
975 		    int        top,
976 		    cxobj    **xcp)
977 {
978     int        retval = -1;
979     char      *namespace;
980     char      *prefix;
981     int        ret;
982     cxobj     *xc = NULL;
983 
984     if ((xc = xml_new(yang_argument_get(y), NULL, CX_ELMNT)) == NULL)
985 	goto done;
986     xml_spec_set(xc, y);
987 
988     /* assign right prefix */
989     if ((namespace = yang_find_mynamespace(y)) != NULL){
990 	prefix = NULL;
991 	if ((ret = xml2prefix(xt, namespace, &prefix)) < 0)
992 	    goto done;
993 	if (ret){ /* Namespace found, prefix returned in prefix */
994 	    if (xml_prefix_set(xc, prefix) < 0)
995 		goto done;
996 	}
997 	else{ /* Namespace does not exist in target, must add it w xmlns attr.
998 		 use source prefix */
999 	    if (!top){
1000 		if ((prefix = yang_find_myprefix(y)) == NULL){
1001 		    clicon_err(OE_UNIX, errno, "strdup");
1002 		    goto done;
1003 		}
1004 	    }
1005 	    if (add_namespace(xc, xc, prefix, namespace) < 0)
1006 		goto done;
1007 	    /* Add prefix to x, if any */
1008 	    if (prefix && xml_prefix_set(xc, prefix) < 0)
1009 		goto done;
1010 	}
1011     }
1012     if (xml_addsub(xt, xc) < 0)
1013 	goto done;
1014     *xcp = xc;
1015     retval = 0;
1016  done:
1017     return retval;
1018 }
1019 
1020 /*! Create leaf from default value
1021  *
1022  * @param[in]   yt      Yang spec
1023  * @param[in]   xt      XML tree
1024  * @param[in]   top     Use default namespace (if you create xmlns statement)
1025  * @retval      0       OK
1026  * @retval      -1      Error
1027  */
1028 static int
xml_default_create(yang_stmt * y,cxobj * xt,int top)1029 xml_default_create(yang_stmt *y,
1030 		   cxobj     *xt,
1031 		   int        top)
1032 {
1033     int        retval = -1;
1034     cxobj     *xc = NULL;
1035     cxobj     *xb;
1036     char      *str;
1037 
1038     if (xml_default_create1(y, xt, top, &xc) < 0)
1039 	goto done;
1040     xml_flag_set(xc, XML_FLAG_DEFAULT);
1041     if ((xb = xml_new("body", xc, CX_BODY)) == NULL)
1042 	goto done;
1043     if ((str = cv2str_dup(yang_cv_get(y))) == NULL){
1044 	clicon_err(OE_UNIX, errno, "cv2str_dup");
1045 	goto done;
1046     }
1047     if (xml_value_set(xb, str) < 0)
1048 	goto done;
1049     free(str);
1050     retval = 0;
1051  done:
1052     return retval;
1053 }
1054 
1055 /*! Try to see if intermediate nodes are necessary for default values, create if so
1056  *
1057  * @param[in]   yt      Yang container (no-presence)
1058  * @param[out]  createp Need to create XML container
1059  * @retval      0       OK
1060  * @retval      -1      Error
1061  */
1062 static int
xml_nopresence_try(yang_stmt * yt,int * createp)1063 xml_nopresence_try(yang_stmt *yt,
1064 		   int       *createp)
1065 {
1066     int        retval = -1;
1067     yang_stmt *y;
1068 
1069     if (yt == NULL || yang_keyword_get(yt) != Y_CONTAINER){
1070 	clicon_err(OE_XML, EINVAL, "yt argument is not container");
1071 	goto done;
1072     }
1073     *createp = 0;
1074     y = NULL;
1075     while ((y = yn_each(yt, y)) != NULL) {
1076 	switch (yang_keyword_get(y)){
1077 	case Y_LEAF:
1078 	    if (!cv_flag(yang_cv_get(y), V_UNSET)){  /* Default value exists */
1079 		/* Need to create container */
1080 		*createp = 1;
1081 		goto ok;
1082 	    }
1083 	    break;
1084 	case Y_CONTAINER:
1085 	    if (yang_find(y, Y_PRESENCE, NULL) == NULL){
1086 		/* If this is non-presence, (and it does not exist in xt) call recursively
1087 		 * and create nodes if any default value exist first. Then continue and populate?
1088 		 */
1089 		if (xml_nopresence_try(y, createp) < 0)
1090 		    goto done;
1091 		if (*createp)
1092 		    goto ok;
1093 	    }
1094 	    break;
1095 	default:
1096 	    break;
1097 	} /* switch */
1098     }
1099  ok:
1100     retval = 0;
1101  done:
1102     return retval;
1103 }
1104 
1105 /*! Ensure default values are set on (children of) one single xml node
1106  *
1107  * Not recursive, except in one case with one or several non-presence containers, in which case
1108  * XML containers may be created to host default values. That code may be a little too recursive.
1109  * @param[in]   yt      Yang spec
1110  * @param[in]   xt      XML tree (with yt as spec of xt, informally)
1111  * @param[in]   state   Set if global state, otherwise config
1112  * @retval      0       OK
1113  * @retval      -1      Error
1114  */
1115 static int
xml_default1(yang_stmt * yt,cxobj * xt,int state)1116 xml_default1(yang_stmt *yt,
1117 	     cxobj     *xt,
1118 	     int        state)
1119 {
1120     int        retval = -1;
1121     yang_stmt *yc;
1122     cxobj     *xc;
1123     int        top=0; /* Top symbol (set default namespace) */
1124     int        create = 0;
1125 
1126     if (xt == NULL){ /* No xml */
1127 	clicon_err(OE_XML, EINVAL, "No XML argument");
1128 	goto done;
1129     }
1130     switch (yang_keyword_get(yt)){
1131     case Y_MODULE:
1132     case Y_SUBMODULE:
1133 	top++;
1134     case Y_CONTAINER: /* XXX maybe check for non-presence here as well */
1135     case Y_LIST:
1136     case Y_INPUT:
1137     case Y_OUTPUT:
1138 	yc = NULL;
1139 	while ((yc = yn_each(yt, yc)) != NULL) {
1140 	    if (!state && !yang_config(yc))
1141 		continue;
1142 	    switch (yang_keyword_get(yc)){
1143 	    case Y_LEAF:
1144 		if (!cv_flag(yang_cv_get(yc), V_UNSET)){  /* Default value exists */
1145 		    if (xml_find_type(xt, NULL, yang_argument_get(yc), CX_ELMNT) == NULL){
1146 			/* No such child exist, create this leaf */
1147 			if (xml_default_create(yc, xt, top) < 0)
1148 			    goto done;
1149 			xml_sort(xt);
1150 		    }
1151 		}
1152 		break;
1153 	    case Y_CONTAINER:
1154 		if (yang_find(yc, Y_PRESENCE, NULL) == NULL){
1155 		    /* If this is non-presence, (and it does not exist in xt) call
1156 		     * recursively and create nodes if any default value exist first.
1157 		     * Then continue and populate?
1158 		     */
1159 		    if (xml_find_type(xt, NULL, yang_argument_get(yc), CX_ELMNT) == NULL){
1160 			/* No such container exist, recursively try if needed */
1161 			if (xml_nopresence_try(yc, &create) < 0)
1162 			    goto done;
1163 			if (create){
1164 			    /* Retval shows there is a default value need to create the
1165 			     * container */
1166 			    if (xml_default_create1(yc, xt, top, &xc) < 0)
1167 				goto done;
1168 			    xml_sort(xt);
1169 			    /* Then call it recursively */
1170 			    if (xml_default1(yc, xc, state) < 0)
1171 				goto done;
1172 			}
1173 		    }
1174 		}
1175 		break;
1176 	    default:
1177 		break;
1178 	    }
1179 	}
1180 	break;
1181     default:
1182 	break;
1183     } /* switch */
1184     retval = 0;
1185  done:
1186     return retval;
1187 }
1188 
1189 /*! Ensure default values are set on existing leaf children of this node
1190  *
1191  * Assume yang is bound to the tree
1192  * @param[in]   xt      XML tree
1193  * @param[in]   state   If set expand defaults also for state data, otherwise only config
1194  * @retval      0       OK
1195  * @retval      -1      Error
1196  */
1197 static int
xml_default(cxobj * xt,int state)1198 xml_default(cxobj *xt,
1199 	    int    state)
1200 {
1201     int        retval = -1;
1202     yang_stmt *ys;
1203 
1204     if ((ys = (yang_stmt*)xml_spec(xt)) == NULL){
1205 	retval = 0;
1206 	goto done;
1207     }
1208     if (xml_default1(ys, xt, state) < 0)
1209 	goto done;
1210     retval = 0;
1211  done:
1212     return retval;
1213 }
1214 
1215 /*! Recursively fill in default values in an XML tree
1216  * @param[in]   xt      XML tree
1217  * @param[in]   state   If set expand defaults also for state data, otherwise only config
1218  * @retval      0       OK
1219  * @retval      -1      Error
1220  */
1221 int
xml_default_recurse(cxobj * xn,int state)1222 xml_default_recurse(cxobj *xn,
1223 		    int    state)
1224 {
1225     int        retval = -1;
1226     cxobj     *x;
1227     yang_stmt *y;
1228 
1229     if (xml_default(xn, state) < 0)
1230 	goto done;
1231     x = NULL;
1232     while ((x = xml_child_each(xn, x, CX_ELMNT)) != NULL) {
1233 	if ((y = (yang_stmt*)xml_spec(x)) != NULL){
1234 	    if (!state && !yang_config(y))
1235 		continue;
1236 	}
1237 	if (xml_default_recurse(x, state) < 0)
1238 	    goto done;
1239     }
1240     retval = 0;
1241  done:
1242     return retval;
1243 }
1244 
1245 /*! Expand and set default values of global top-level on XML tree
1246  *
1247  * Not recursive, except in one case with one or several non-presence containers
1248  * @param[in]   xt      XML tree
1249  * @param[in]   yspec   Top-level YANG specification tree, all modules
1250  * @param[in]   state   Set if global state, otherwise config
1251  * @retval      0       OK
1252  * @retval      -1      Error
1253  */
1254 static int
xml_global_defaults_create(cxobj * xt,yang_stmt * yspec,int state)1255 xml_global_defaults_create(cxobj     *xt,
1256 			   yang_stmt *yspec,
1257 			   int        state)
1258 
1259 {
1260     int        retval = -1;
1261     yang_stmt *ymod = NULL;
1262 
1263     if (yspec == NULL || yang_keyword_get(yspec) != Y_SPEC){
1264 	clicon_err(OE_XML, EINVAL, "yspec argument is not yang spec");
1265 	goto done;
1266     }
1267     while ((ymod = yn_each(yspec, ymod)) != NULL)
1268 	if (xml_default1(ymod, xt, state) < 0)
1269 	    goto done;
1270     retval = 0;
1271  done:
1272     return retval;
1273 }
1274 
1275 /*! Expand and set default values of global top-level on XML tree
1276  *
1277  * Not recursive, except in one case with one or several non-presence containers
1278  * @param[in]   h       Clixon handle
1279  * @param[in]   xt      XML tree, assume already filtered with xpath
1280  * @param[in]   xpath   Filter global defaults with this and merge with xt
1281  * @param[in]   yspec   Top-level YANG specification tree, all modules
1282  * @param[in]   state   Set if global state, otherwise config
1283  * @retval      0       OK
1284  * @retval      -1      Error
1285  * Uses cache?
1286  */
1287 int
xml_global_defaults(clicon_handle h,cxobj * xt,cvec * nsc,const char * xpath,yang_stmt * yspec,int state)1288 xml_global_defaults(clicon_handle h,
1289 		    cxobj        *xt,
1290 		    cvec         *nsc,
1291 		    const char   *xpath,
1292 		    yang_stmt    *yspec,
1293 		    int           state)
1294 {
1295     int        retval = -1;
1296     db_elmnt   de0 = {0,};
1297     db_elmnt  *de = NULL;
1298     cxobj     *xcache = NULL;
1299     cxobj     *xpart = NULL;
1300     cxobj    **xvec = NULL;
1301     size_t     xlen;
1302     int        i;
1303     cxobj     *x0;
1304     int        ret;
1305     char      *key;
1306 
1307     /* Use different keys for config and state */
1308     key = state ? "global-defaults-state" : "global-defaults-config";
1309     /* First get or compute global xml tree cache */
1310     if ((de = clicon_db_elmnt_get(h, key)) == NULL){
1311 	/* Create it */
1312 	if ((xcache = xml_new("config", NULL, CX_ELMNT)) == NULL)
1313 	    goto done;
1314 	if (xml_global_defaults_create(xcache, yspec, state) < 0)
1315 	    goto done;
1316 	de0.de_xml = xcache;
1317 	clicon_db_elmnt_set(h, key, &de0);
1318     }
1319     else
1320 	xcache = de->de_xml;
1321 
1322     /* Here xcache has all global defaults. Now find the matching nodes
1323      * XXX: nsc as 2nd argument
1324      */
1325     if (xpath_vec(xcache, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
1326 	goto done;
1327     /* Iterate through match vector
1328      * For every node found in x0, mark the tree up to t1
1329      */
1330     for (i=0; i<xlen; i++){
1331 	x0 = xvec[i];
1332 	xml_flag_set(x0, XML_FLAG_MARK);
1333 	xml_apply_ancestor(x0, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE);
1334     }
1335     /* Create a new tree and copy over the parts from the cache that matches xpath */
1336     if ((xpart = xml_new("config", NULL, CX_ELMNT)) == NULL)
1337 	goto done;
1338     if (xml_copy_marked(xcache, xpart) < 0) /* config */
1339 	goto done;
1340     if (xml_apply(xcache, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)(XML_FLAG_MARK|XML_FLAG_CHANGE)) < 0)
1341 	goto done;
1342     if (xml_apply(xpart, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)(XML_FLAG_MARK|XML_FLAG_CHANGE)) < 0)
1343 	goto done;
1344     /* Merge global pruned tree with xt */
1345     if ((ret = xml_merge(xt, xpart, yspec, NULL)) < 1) /* XXX reason */
1346 	goto done;
1347     retval = 0;
1348  done:
1349     if (xpart)
1350 	xml_free(xpart);
1351     if (xvec)
1352 	free(xvec);
1353     return retval;
1354 }
1355 
1356 /*! This node is a default set value or (recursively) a non-presence container
1357  * @retval 1  xt is a nopresence/default node (ie "virtual")
1358  * @retval 0  xt is not such a node
1359  */
1360 int
xml_nopresence_default(cxobj * xt)1361 xml_nopresence_default(cxobj *xt)
1362 {
1363     cxobj     *xc;
1364     yang_stmt *yt;
1365 
1366     if ((yt = xml_spec(xt)) == NULL)
1367 	return 0;
1368     switch (yang_keyword_get(yt)){
1369     case Y_CONTAINER:
1370 	if (yang_find(yt, Y_PRESENCE, NULL))
1371 	    return 0;
1372 	break;
1373     case Y_LEAF:
1374 	return xml_flag(xt, XML_FLAG_DEFAULT)?1:0;
1375 	break;
1376     default:
1377 	return 0;
1378     }
1379     xc = NULL;
1380     while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) {
1381 	if (xml_nopresence_default(xc) == 0)
1382 	    return 0;
1383     }
1384     return 1;
1385 }
1386 
1387 /*! Remove xml container if it is non-presence and only contains default leafs
1388  * Called from xml_apply. Reason for marking is to delete it afterwords.
1389  * @param[in] x
1390  * @param[in] arg  (flag value)
1391  * @code
1392  *    if (xml_apply(xt, CX_ELMNT, xml_nopresence_default_mark, (void*)XML_FLAG_DEFAULT) < 0)
1393  *	err;
1394  *    if (xml_tree_prune_flagged(xt, XML_FLAG_DEFAULT, 1) < 0)
1395  *	goto done;
1396  * @endcode
1397  */
1398 int
xml_nopresence_default_mark(cxobj * x,void * arg)1399 xml_nopresence_default_mark(cxobj *x,
1400 			    void  *arg)
1401 {
1402     if (xml_nopresence_default(x))
1403 	xml_flag_set(x, (intptr_t)arg);
1404     return 0;
1405 }
1406 
1407 /*! Sanitize an xml tree: xml node has matching yang_stmt pointer
1408  * @param[in]   xt      XML top of tree
1409  */
1410 int
xml_sanity(cxobj * xt,void * arg)1411 xml_sanity(cxobj *xt,
1412 	   void  *arg)
1413 {
1414     int        retval = -1;
1415     yang_stmt *ys;
1416     char      *name;
1417 
1418     if ((ys = (yang_stmt*)xml_spec(xt)) == NULL){
1419 	retval = 0;
1420 	goto done;
1421     }
1422     name = xml_name(xt);
1423     if (strstr(yang_argument_get(ys), name)==NULL){
1424 	clicon_err(OE_XML, 0, "xml node name '%s' does not match yang spec arg '%s'",
1425 		   name, yang_argument_get(ys));
1426 	goto done;
1427     }
1428     retval = 0;
1429  done:
1430     return retval;
1431 }
1432 
1433 /*! Detect state data: Either mark or break on first occurence and return xerror
1434  * @param[in]   xt      XML tree
1435  * @param[out]  xerr    If set return netconf error, abort and return if a state variable found
1436  * @retval      -1      Error
1437  * @retval      0       Status node found and return xerror
1438  * @retval      1       OK
1439  * Note that the behaviour is quite different if xerr is set or not,...
1440  */
1441 int
xml_non_config_data(cxobj * xt,cxobj ** xerr)1442 xml_non_config_data(cxobj  *xt,
1443 		    cxobj **xerr)
1444 {
1445     int        retval = -1;
1446     cxobj     *x;
1447     yang_stmt *y;
1448     int        ret;
1449     cbuf      *cb = NULL;
1450 
1451     x = NULL;
1452     while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
1453 	if ((y = (yang_stmt*)xml_spec(x)) == NULL)
1454 	    goto ok;
1455 	if (!yang_config(y)){ /* config == false means state data */
1456 	    if (xerr){        /* behaviour 1: return on error */
1457 		if ((cb = cbuf_new()) == NULL){
1458 		    clicon_err(OE_UNIX, errno, "cbuf_new");
1459 		    goto done;
1460 		}
1461 		cprintf(cb, "%s in module %s: state data node unexpected",
1462 			yang_argument_get(y), yang_argument_get(ys_module(y)));
1463 		if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cb)) < 0)
1464 		    goto done;
1465 		retval = 0;
1466 		goto done;
1467 	    }
1468 	    xml_flag_set(x, XML_FLAG_MARK); /* behaviour 2: mark and continue */
1469 	}
1470 	if ((ret = xml_non_config_data(x, xerr)) < 0)
1471 	    goto done;
1472 	if (ret == 0){
1473 	    retval = 0;
1474 	    goto done;
1475 	}
1476     }
1477  ok:
1478     retval = 1;
1479  done:
1480     if (cb)
1481 	cbuf_free(cb);
1482     return retval;
1483 }
1484 
1485 /*! Given an XML node, build an xpath to root, internal function
1486  * @retval     0      OK
1487  * @retval    -1      Error. eg XML malformed
1488  */
1489 static int
xml2xpath1(cxobj * x,cbuf * cb)1490 xml2xpath1(cxobj *x,
1491 	   cbuf  *cb)
1492 {
1493     int           retval = -1;
1494     cxobj        *xp;
1495     yang_stmt    *y = NULL;
1496     cvec         *cvk = NULL; /* vector of index keys */
1497     cg_var       *cvi;
1498     char         *keyname;
1499     cxobj        *xkey;
1500     cxobj        *xb;
1501     char         *b;
1502     enum rfc_6020 keyword;
1503 
1504     if ((xp = xml_parent(x)) != NULL &&
1505 	xml_spec(xp) != NULL)
1506 	xml2xpath1(xp, cb);
1507     /* XXX: sometimes there should be a /, sometimes not */
1508     cprintf(cb, "/%s", xml_name(x));
1509     if ((y = xml_spec(x)) != NULL){
1510 	keyword = yang_keyword_get(y);
1511 	switch (keyword){
1512 	case Y_LEAF_LIST:
1513 	    if ((b = xml_body(x)) != NULL)
1514 		cprintf(cb, "[.=\"%s\"]", b);
1515 	    else
1516 		cprintf(cb, "[.=\"\"]");
1517 	    break;
1518 	case Y_LIST:
1519 	    cvk = yang_cvec_get(y);
1520 	    cvi = NULL;
1521 	    while ((cvi = cvec_each(cvk, cvi)) != NULL) {
1522 		keyname = cv_string_get(cvi);
1523 		if ((xkey = xml_find(x, keyname)) == NULL)
1524 		    goto done; /* No key in xml */
1525 		if ((xb = xml_find(x, keyname)) == NULL)
1526 		    goto done;
1527 		b = xml_body(xb);
1528 		cprintf(cb, "[%s=\"%s\"]", keyname, b?b:"");
1529 	    }
1530 	    break;
1531 	default:
1532 	    break;
1533 	}
1534     }
1535     retval = 0;
1536  done:
1537     return retval;
1538 }
1539 
1540 /*! Given an XML node, build an xpath to root
1541  * Builds only unqualified xpaths, ie no predicates []
1542  * @param[in]  x      XML object
1543  * @param[out] xpath  Malloced xpath string. Need to free() after use
1544  * @retval     0      OK
1545  * @retval    -1      Error. (eg XML malformed)
1546  * @note x needs to be bound to YANG, see eg xml_bind_yang()
1547  */
1548 int
xml2xpath(cxobj * x,char ** xpathp)1549 xml2xpath(cxobj *x,
1550 	  char **xpathp)
1551 {
1552     int   retval = -1;
1553     cbuf *cb;
1554     char *xpath = NULL;
1555 
1556     if ((cb = cbuf_new()) == NULL){
1557 	clicon_err(OE_XML, errno, "cbuf_new");
1558 	goto done;
1559     }
1560     if (xml2xpath1(x, cb) < 0)
1561 	goto done;
1562     /* XXX: see xpath in test statement,.. */
1563     xpath = cbuf_get(cb);
1564     if (xpathp){
1565 	if ((*xpathp = strdup(xpath)) == NULL){
1566 	    clicon_err(OE_UNIX, errno, "strdup");
1567 	    goto done;
1568 	}
1569 	xpath = NULL;
1570     }
1571     retval = 0;
1572  done:
1573     if (cb)
1574 	cbuf_free(cb);
1575     return retval;
1576 }
1577 
1578 /*! Check if the module tree x is in is assigned right XML namespace, assign if not
1579  * @param[in]  x   XML node
1580  *(0. You should probably find the XML root and apply this function to that.)
1581  * 1. Check which namespace x should have (via yang). This is correct namespace.
1582  * 2. Check which namespace x has via its XML tree
1583  * 3. If equal, OK
1584  * 4. Assign the correct namespace to the XML node
1585  * Assign default namespace to x. Create an "xmlns"
1586  * @code
1587  *    xml_yang_root(x, &xroot);
1588  *    xmlns_assign(xroot);
1589  * @endcode
1590  */
1591 int
xmlns_assign(cxobj * x)1592 xmlns_assign(cxobj *x)
1593 {
1594     int        retval = -1;
1595     yang_stmt *y;
1596     char      *ns_correct; /* correct uri */
1597     char      *ns_xml;     /* may be null or incorrect */
1598 
1599     if ((y = xml_spec(x)) == NULL){
1600 	clicon_err(OE_YANG, ENOENT, "XML %s does not have yang spec", xml_name(x));
1601 	goto done;
1602     }
1603     /* 1. Check which namespace x should have (via yang). This is correct namespace. */
1604     if ((ns_correct = yang_find_mynamespace(y)) == NULL){
1605 	clicon_err(OE_YANG, ENOENT, "yang node %s does not have namespace", yang_argument_get(y));
1606 	goto done;
1607     }
1608     /* 2. Check which namespace x has via its XML tree */
1609     if (xml2ns(x, NULL, &ns_xml) < 0)
1610 	goto done;
1611     /* 3. If equal, OK, 4. Else, find root of XML tree */
1612     if (ns_xml && strcmp(ns_xml, ns_correct)==0)
1613 	goto ok;
1614     /* 4. Assign the correct namespace */
1615     if (xmlns_set(x, NULL, ns_correct) < 0)
1616 	goto done;
1617  ok:
1618     retval = 0;
1619  done:
1620     return retval;
1621 }
1622 
1623 /*! Given a src element node x0 and a target node x1, assign (optional) prefix and namespace
1624  * @see assign_namespace_element  this is a subroutine
1625  */
1626 static int
assign_namespace(cxobj * x0,cxobj * x1,cxobj * x1p,int isroot,char * ns,char * prefix0)1627 assign_namespace(cxobj *x0, /* source */
1628 		 cxobj *x1, /* target */
1629 		 cxobj *x1p,
1630 		 int    isroot,
1631 		 char  *ns,
1632 		 char  *prefix0)
1633 {
1634     int        retval = -1;
1635     char      *prefix1 = NULL;
1636     char      *pexist = NULL;
1637     cvec      *nsc0 = NULL;
1638     cvec      *nsc = NULL;
1639     yang_stmt *y;
1640 
1641     /* 2a. Detect if namespace is declared in x1 target parent */
1642     if (xml2prefix(x1p, ns, &pexist) == 1){
1643 	/* Yes, and it has prefix pexist */
1644 	if (pexist){
1645 	    if ((prefix1 = strdup(pexist)) == NULL){
1646 		clicon_err(OE_UNIX, errno, "strdup");
1647 		goto done;
1648 	    }
1649 	}
1650 	else
1651 	    prefix1 = NULL;
1652 	/* 3. If yes, assign prefix to x1 */
1653 	if (prefix1 && xml_prefix_set(x1, prefix1) < 0)
1654 	    goto done;
1655 	/* And copy namespace context from parent to child */
1656 	if ((nsc0 = nscache_get_all(x1p)) != NULL){
1657 	    if ((nsc = cvec_dup(nsc0)) == NULL){
1658 		clicon_err(OE_UNIX, errno, "cvec_dup");
1659 		goto done;
1660 	    }
1661 	    nscache_replace(x1, nsc);
1662 	}
1663 	/* Just in case */
1664 	if (nscache_set(x1, prefix1, ns) < 0)
1665 	    goto done;
1666     }
1667     else{ /* No, namespace does not exist in x1 _parent_
1668 	   * Check if it is exists in x1 itself */
1669 	if (xml2prefix(x1, ns, &pexist) == 1){
1670 	    /* Yes it exists, but is it equal? */
1671 	    if (clicon_strcmp(pexist, prefix0) == 0)
1672 		; /* Equal, reuse */
1673 	    else{ /* namespace exist, but not equal, use existing */
1674 		/* Add prefix to x1, if any */
1675 		if (pexist && xml_prefix_set(x1, pexist) < 0)
1676 		    goto done;
1677 	    }
1678 	    goto ok; /* skip */
1679 	}
1680 	else { /* namespace does not exist in target x1, use source prefix
1681 		* use the prefix defined in the module
1682 		*/
1683 	    if (isroot){
1684 		if (prefix0 && (prefix1 = strdup(prefix0)) == NULL){
1685 		    clicon_err(OE_UNIX, errno, "strdup");
1686 		    goto done;
1687 		}
1688 	    }
1689 	    else{
1690 		char *ptmp;
1691 		if ((y = xml_spec(x0)) == NULL){
1692 		    clicon_err(OE_YANG, ENOENT, "XML %s does not have yang spec",
1693 			       xml_name(x0));
1694 		    goto done;
1695 		}
1696 		/* Find local (imported) prefix for that module namespace */
1697 		if (yang_find_prefix_by_namespace(y, ns, &ptmp) < 0)
1698 		    goto done;
1699 		if ((prefix1 = strdup(ptmp)) == NULL){
1700 		    clicon_err(OE_UNIX, errno, "strdup");
1701 		    goto done;
1702 		}
1703 
1704 	    }
1705 	}
1706 	if (add_namespace(x1, x1, prefix1, ns) < 0)
1707 	    goto done;
1708 	if (prefix1 && xml_prefix_set(x1, prefix1) < 0)
1709 	    goto done;
1710     }
1711  ok:
1712     /* 6. Ensure x1 cache is updated (I think it is done w xmlns_set above) */
1713     retval = 0;
1714  done:
1715     if (prefix1)
1716 	free(prefix1);
1717     return retval;
1718 }
1719 
1720 /*! Given a src element node x0 and a target node x1, assign (optional) prefix and namespace
1721  * @param[in]  x0       Source XML tree
1722  * @param[in]  x1       Target XML tree
1723  * @param[in]  x1p      Target XML tree parent
1724  * @retval     0        OK
1725  * @retval     -1       OK
1726  * 1. Find N=namespace(x0)
1727  * 2. Detect if N is declared in x1 parent
1728  * 3. If yes, assign prefix to x1
1729  * 4. If no, create new prefix/namespace binding and assign that to x1p (x1 if x1p is root)
1730  * 5. Add prefix to x1, if any
1731  * 6. Ensure x1 cache is updated
1732  * @note switch use of x0 and x1 compared to datastore text_modify
1733  * @see xml2ns
1734 */
1735 int
assign_namespace_element(cxobj * x0,cxobj * x1,cxobj * x1p)1736 assign_namespace_element(cxobj *x0, /* source */
1737 			 cxobj *x1, /* target */
1738 			 cxobj *x1p)
1739 {
1740     int        retval = -1;
1741     char      *namespace = NULL;
1742     char      *prefix0 = NULL;;
1743     int        isroot;
1744 
1745     /* XXX: need to identify root better than hiereustics and strcmp,... */
1746     isroot = xml_parent(x1p)==NULL &&
1747 	(strcmp(xml_name(x1p), "config") == 0 || strcmp(xml_name(x1p), "top") == 0)&&
1748 	xml_prefix(x1p)==NULL;
1749 
1750     /* 1. Find N=namespace(x0) in x0 element */
1751     prefix0 = xml_prefix(x0);
1752     if (xml2ns(x0, prefix0, &namespace) < 0)
1753 	goto done;
1754     if (namespace == NULL){
1755 	clicon_err(OE_XML, ENOENT, "No namespace found for prefix:%s",
1756 		   prefix0?prefix0:"NULL");
1757 	goto done;
1758     }
1759     if (assign_namespace(x0, x1, x1p, isroot, namespace, prefix0) < 0)
1760 	goto done;
1761     /* 6. Ensure x1 cache is updated (I think it is done w xmlns_set above) */
1762     retval = 0;
1763  done:
1764     return retval;
1765 }
1766 
1767 /* If origin body has namespace definitions, copy them. The reason is that
1768  * some bodies rely on namespace prefixes, such as NACM path, but there is
1769  * no way we can now this here.
1770  * However, this may lead to namespace collisions if these prefixes are not
1771  * canonical, and may collide with the assign_namespace_element() above (but that
1772  * is for element sysmbols)
1773  */
1774 int
assign_namespace_body(cxobj * x0,char * x0bstr,cxobj * x1)1775 assign_namespace_body(cxobj *x0, /* source */
1776 		      char  *x0bstr,
1777 		      cxobj *x1) /* target */
1778 {
1779     int    retval = -1;
1780     char  *namespace = NULL;
1781     char  *name;
1782     char  *prefix;
1783     char  *prefix0 = NULL;;
1784     char  *pexisting = NULL;;
1785     cxobj *xa;
1786 
1787     xa = NULL;
1788     while ((xa = xml_child_each(x0, xa, CX_ATTR)) != NULL) {
1789 	prefix = xml_prefix(xa);
1790 	name = xml_name(xa);
1791 	namespace = xml_value(xa);
1792 	if ((strcmp(name, "xmlns")==0 && prefix==NULL) ||
1793 	    (prefix != NULL && strcmp(prefix, "xmlns")==0)){
1794 	    if (prefix == NULL)
1795 		prefix0 = NULL;
1796 	    else
1797 		prefix0 = name;
1798 	    if (strcmp(namespace, NETCONF_BASE_NAMESPACE) ==0 ||
1799 		strcmp(namespace, YANG_XML_NAMESPACE) ==0)
1800 		continue;
1801 	    /* Detect if prefix:namespace is declared already? */
1802 	    if (xml2prefix(x1, namespace, &pexisting) == 1){
1803 		/* Yes, and it has prefix pexist */
1804 		if (clicon_strcmp(pexisting, prefix0) ==0)
1805 		    continue;
1806 	    }
1807 	    if (add_namespace(x1, x1, prefix0, namespace) < 0)
1808 		goto done;
1809 	}
1810     }
1811     /* 6. Ensure x1 cache is updated (I think it is done w xmlns_set above) */
1812     retval = 0;
1813  done:
1814     return retval;
1815 }
1816 
1817 /*! Merge a base tree x0 with x1 with yang spec y
1818  * @param[in]  x0  Base xml tree (can be NULL in add scenarios)
1819  * @param[in]  y0  Yang spec corresponding to xml-node x0. NULL if x0 is NULL
1820  * @param[in]  x0p Parent of x0
1821  * @param[in]  x1  xml tree which modifies base
1822  * @param[out] reason If retval=0 a malloced string
1823  * @retval     1      OK
1824  * @retval     0      Yang error, reason is set
1825  * @retval    -1      Error
1826  * Assume x0 and x1 are same on entry and that y is the spec
1827  */
1828 static int
xml_merge1(cxobj * x0,yang_stmt * y0,cxobj * x0p,cxobj * x1,char ** reason)1829 xml_merge1(cxobj              *x0,  /* the target */
1830 	   yang_stmt          *y0,
1831 	   cxobj              *x0p,
1832 	   cxobj              *x1,  /* the source */
1833 	   char              **reason)
1834 {
1835     int        retval = -1;
1836     char      *x1cname; /* child name */
1837     cxobj     *x0c; /* base child */
1838     cxobj     *x0b; /* base body */
1839     cxobj     *x1c; /* mod child */
1840     char      *x1bstr; /* mod body string */
1841     yang_stmt *yc;  /* yang child */
1842     cbuf      *cbr = NULL; /* Reason buffer */
1843     int        ret;
1844     int        i;
1845     merge_twophase *twophase = NULL;
1846     int twophase_len;
1847 
1848     assert(x1 && xml_type(x1) == CX_ELMNT);
1849     assert(y0);
1850 
1851     if (x0 == NULL){
1852 	cvec   *nsc = NULL;
1853 	cg_var *cv;
1854 	char   *ns;
1855 	char   *px;
1856 	nsc = cvec_dup(nscache_get_all(x1));
1857 	if (xml_rm(x1) < 0)
1858 	    goto done;
1859 	/* This is to make the anydata case a little more robust, more could be done */
1860         if (xml_spec(x1) == NULL){
1861             if (xml_addsub(x0p, x1) < 0)
1862                 goto done;
1863         }
1864         else
1865 	    if (xml_insert(x0p, x1, INS_LAST, NULL, NULL) < 0)
1866 		goto done;
1867 	cv = NULL;
1868 	while ((cv = cvec_each(nsc, cv)) != NULL){
1869 	    px = cv_name_get(cv);
1870 	    ns = cv_string_get(cv);
1871 	    /* Check if it exists */
1872 	    if (xml2prefix(x1, ns, NULL) == 0)
1873 		if (xmlns_set(x1, px, ns) < 0)
1874 		    goto done;
1875 	}
1876 	if (nsc)
1877 	    cvec_free(nsc);
1878 	goto ok;
1879     }
1880     if (yang_keyword_get(y0) == Y_LEAF_LIST || yang_keyword_get(y0) == Y_LEAF){
1881 	x1bstr = xml_body(x1);
1882 	if (x1bstr){
1883 	    if ((x0b = xml_body_get(x0)) == NULL){
1884 		if ((x0b = xml_new("body", x0, CX_BODY)) == NULL)
1885 		    goto done;
1886 	    }
1887 	    if (xml_value_set(x0b, x1bstr) < 0)
1888 		goto done;
1889 	}
1890 	if (xml_parent(x0) == NULL &&
1891 	    xml_insert(x0p, x0, INS_LAST, NULL, NULL) < 0)
1892 	    goto done;
1893 	if (assign_namespace_element(x1, x0, x0p) < 0)
1894 	    goto done;
1895     } /* if LEAF|LEAF_LIST */
1896     else { /* eg Y_CONTAINER, Y_LIST  */
1897 	if (assign_namespace_element(x1, x0, x0p) < 0)
1898 	    goto done;
1899 	twophase_len = xml_child_nr(x1);
1900 	if ((twophase = calloc(twophase_len, sizeof(*twophase))) == NULL){
1901 	    clicon_err(OE_UNIX, errno, "calloc");
1902 	    goto done;
1903 	}
1904 	i = 0;
1905 	/* Loop through children of the modification tree */
1906 	x1c = NULL;
1907 	while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
1908 	    x1cname = xml_name(x1c);
1909 	    /* Get yang spec of the child */
1910 	    if ((yc = yang_find_datanode(y0, x1cname)) == NULL){
1911 		if (reason){
1912 		    if ((cbr = cbuf_new()) == NULL){
1913 			clicon_err(OE_XML, errno, "cbuf_new");
1914 			goto done;
1915 		    }
1916 		    cprintf(cbr, "XML node %s/%s has no corresponding yang specification (Invalid XML or wrong Yang spec?", xml_name(x1), x1cname);
1917 		    if ((*reason = strdup(cbuf_get(cbr))) == NULL){
1918 			clicon_err(OE_UNIX, errno, "strdup");
1919 			goto done;
1920 		    }
1921 		}
1922 		goto fail;
1923 	    }
1924 	    /* See if there is a corresponding node in the base tree */
1925 	    x0c = NULL;
1926 	    if (yc && match_base_child(x0, x1c, yc, &x0c) < 0)
1927 		goto done;
1928 	    /* If x0 already has a value, do not replace it with a default value in x1 */
1929 	    if (x0c && xml_flag(x1c, XML_FLAG_DEFAULT))
1930 		continue;
1931 	    /* Save x0c, x1c, yc and merge in second wave, so that x1c entries dont "interfer"
1932 	     * with itself, ie that later searches are among earlier objects already added
1933 	     * to x0 */
1934 	    twophase[i].mt_x0c = x0c;
1935 	    twophase[i].mt_x1c = x1c;
1936 	    twophase[i].mt_yc  = yc;
1937 	    i++;
1938 	} /* while */
1939 	twophase_len = i; /* Inital length included non-elements */
1940 	/* Second run where actual merging is done
1941 	 * Loop through children of the modification tree */
1942 	for (i=0; i<twophase_len; i++){
1943 	    assert(twophase[i].mt_x1c);
1944 	    if ((ret = xml_merge1(twophase[i].mt_x0c,
1945 			   twophase[i].mt_yc,
1946 			   x0,
1947 			   twophase[i].mt_x1c,
1948 				  reason)) < 0)
1949 		goto done;
1950 	    if (ret == 0)
1951 		goto fail;
1952 	}
1953 	if (xml_parent(x0) == NULL &&
1954 	    xml_insert(x0p, x0, INS_LAST, NULL, NULL) < 0)
1955 	    goto done;
1956     } /* else Y_CONTAINER  */
1957  ok:
1958     retval = 1;
1959  done:
1960     if (twophase)
1961 	free(twophase);
1962     if (cbr)
1963 	cbuf_free(cbr);
1964     return retval;
1965  fail:
1966     retval = 0;
1967     goto done;
1968 }
1969 
1970 /*! Merge XML trees x1 into x0 according to yang spec yspec
1971  * @param[in]  x0     Base xml tree (can be NULL in add scenarios)
1972  * @param[in]  x1     xml tree which modifies base
1973  * @param[in]  yspec  Yang spec
1974  * @param[out] reason If retval=0, reason is set. Malloced. Needs to be freed by caller
1975  * @retval     1      OK
1976  * @retval     0      Yang error, reason is set
1977  * @retval    -1      Error
1978  * @note both x0 and x1 need to be top-level trees
1979  */
1980 int
xml_merge(cxobj * x0,cxobj * x1,yang_stmt * yspec,char ** reason)1981 xml_merge(cxobj     *x0,
1982 	  cxobj     *x1,
1983 	  yang_stmt *yspec,
1984 	  char     **reason)
1985 {
1986     int        retval = -1;
1987     char      *x1cname; /* child name */
1988     cxobj     *x0c; /* base child */
1989     cxobj     *x1c; /* mod child */
1990     yang_stmt *yc;
1991     yang_stmt *ymod;
1992     cbuf      *cbr = NULL; /* Reason buffer */
1993     int        i;
1994     merge_twophase *twophase = NULL;
1995     int        twophase_len;
1996     int        ret;
1997 
1998     if (x0 == NULL || x1 == NULL){
1999 	clicon_err(OE_UNIX, EINVAL, "parameters x0 or x1 is NULL");
2000 	goto done;
2001     }
2002     twophase_len = xml_child_nr(x1);
2003     if ((twophase = calloc(twophase_len, sizeof(*twophase))) == NULL){
2004 	clicon_err(OE_UNIX, errno, "calloc");
2005 	goto done;
2006     }
2007     /* Loop through children of the modification tree */
2008     i = 0;
2009     x1c = NULL;
2010     while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
2011 	x1cname = xml_name(x1c);
2012 	if ((ys_module_by_xml(yspec, x1c, &ymod)) < 0)
2013 	    goto done;
2014 	if (ymod == NULL){
2015 	    if (reason &&
2016 		(*reason = strdup("Namespace not found or yang spec not loaded")) == NULL){
2017 		    clicon_err(OE_UNIX, errno, "strdup");
2018 		    goto done;
2019 	    }
2020 	    goto fail;
2021 	}
2022 	/* Get yang spec of the child */
2023 	if ((yc = yang_find_datanode(ymod, x1cname)) == NULL){
2024 	    if (reason){
2025 		if ((cbr = cbuf_new()) == NULL){
2026 		    clicon_err(OE_XML, errno, "cbuf_new");
2027 		    goto done;
2028 		}
2029 		cprintf(cbr, "XML node %s/%s has no corresponding yang specification (Invalid XML or wrong Yang spec?)", xml_name(x1), x1cname);
2030 		if ((*reason = strdup(cbuf_get(cbr))) == NULL){
2031 		    clicon_err(OE_UNIX, errno, "strdup");
2032 		    goto done;
2033 		}
2034 	    }
2035 	    goto fail;
2036 	}
2037 	x0c = NULL;
2038 	/* See if there is a corresponding node (x1c) in the base tree (x0) */
2039 	if (yc && match_base_child(x0, x1c, yc, &x0c) < 0)
2040 	    goto done;
2041 	/* If x0 already has a value, do not replace it with a default value in x1 */
2042 	if (x0c && xml_flag(x1c, XML_FLAG_DEFAULT))
2043 	    continue;
2044 	/* Save x0c, x1c, yc and merge in second wave, so that x1c entries don't "interfere"
2045 	 * with itself, ie that later searches are among earlier objects already added
2046 	 * to x0 */
2047 	twophase[i].mt_x0c = x0c;
2048 	twophase[i].mt_x1c = x1c;
2049 	twophase[i].mt_yc  = yc;
2050 	i++;
2051     }
2052     twophase_len = i; /* Inital length included non-elements */
2053     /* Second run where actual merging is done
2054      * Loop through children of the modification tree */
2055     for (i=0; i<twophase_len; i++){
2056 	assert(twophase[i].mt_x1c);
2057 	if ((ret = xml_merge1(twophase[i].mt_x0c,
2058 			      twophase[i].mt_yc,
2059 			      x0,
2060 			      twophase[i].mt_x1c,
2061 			      reason)) < 0)
2062 	    goto done;
2063 	if (ret == 0)
2064 	    goto fail;
2065     }
2066     retval = 1; /* OK */
2067  done:
2068     if (twophase)
2069 	free(twophase);
2070     if (cbr)
2071 	cbuf_free(cbr);
2072     return retval;
2073  fail:
2074     retval = 0;
2075     goto done;
2076 }
2077 
2078 /*! Get integer value from xml node from yang enumeration
2079  * @param[in]  node XML node in a tree
2080  * @param[out] val  Integer value returned
2081  * @retval     0    OK, value parsed
2082  * @retval    -1    Error, value not obtained or parsed, no reason given
2083  * @note assume yang spec set
2084  * @note The function only returns assigned values, but according to RFC:
2085    If a value is not specified, then one will be automatically assigned.
2086    If the "enum" substatement is the first one defined, the assigned
2087    value is zero (0); otherwise, the assigned value is one greater than
2088    the current highest enum value (i.e., the highest enum value,
2089    * Thanks: Matthew Smith
2090  */
2091 int
yang_enum_int_value(cxobj * node,int32_t * val)2092 yang_enum_int_value(cxobj   *node,
2093 		    int32_t *val)
2094 {
2095     int retval = -1;
2096     yang_stmt *yspec;
2097     yang_stmt *ys;
2098     yang_stmt *ytype;
2099     yang_stmt *yrestype;  /* resolved type */
2100     yang_stmt *yenum;
2101     yang_stmt *yval;
2102     char      *reason = NULL;
2103 
2104     if (node == NULL)
2105 	goto done;
2106     if ((ys = (yang_stmt *) xml_spec(node)) == NULL)
2107 	goto done;
2108     if ((yspec = ys_spec(ys)) == NULL)
2109 	goto done;
2110     if ((ytype = yang_find(ys, Y_TYPE, NULL)) == NULL)
2111 	goto done;
2112     if (yang_type_resolve(ys, ys, ytype, &yrestype,
2113 			  NULL, NULL, NULL, NULL, NULL) < 0)
2114 	goto done;
2115     if (yrestype==NULL || strcmp(yang_argument_get(yrestype), "enumeration"))
2116 	goto done;
2117     if ((yenum = yang_find(yrestype, Y_ENUM, xml_body(node))) == NULL)
2118 	goto done;
2119     /* Should assign value if yval not found */
2120     if ((yval = yang_find(yenum, Y_VALUE, NULL)) == NULL)
2121 	goto done;
2122     /* reason is string containing why int could not be parsed */
2123     if (parse_int32(yang_argument_get(yval), val, &reason) < 0)
2124 	goto done;
2125     retval = 0;
2126 done:
2127     return retval;
2128 }
2129 
2130 
2131 /*! Given XML tree x0 with marked nodes, copy marked nodes to new tree x1
2132  * Two marks are used: XML_FLAG_MARK and XML_FLAG_CHANGE
2133  *
2134  * The algorithm works as following:
2135  * (1) Copy individual nodes marked with XML_FLAG_CHANGE
2136  * until nodes marked with XML_FLAG_MARK are reached, where
2137  * (2) the complete subtree of that node is copied.
2138  * (3) Special case: key nodes in lists are copied if any node in list is marked
2139  *  @note you may want to check:!yang_config(ys)
2140  */
2141 int
xml_copy_marked(cxobj * x0,cxobj * x1)2142 xml_copy_marked(cxobj *x0,
2143 		cxobj *x1)
2144 {
2145     int        retval = -1;
2146     int        mark;
2147     cxobj     *x;
2148     cxobj     *xcopy;
2149     int        iskey;
2150     yang_stmt *yt;
2151     char      *name;
2152     char      *prefix;
2153 
2154     assert(x0 && x1);
2155     yt = xml_spec(x0); /* can be null */
2156     xml_spec_set(x1, yt);
2157    /* Copy prefix*/
2158     if ((prefix = xml_prefix(x0)) != NULL)
2159 	if (xml_prefix_set(x1, prefix) < 0)
2160 	    goto done;
2161 
2162     /* Copy all attributes */
2163     x = NULL;
2164     while ((x = xml_child_each(x0, x, CX_ATTR)) != NULL) {
2165 	name = xml_name(x);
2166 	if ((xcopy = xml_new(name, x1, CX_ATTR)) == NULL)
2167 	    goto done;
2168 	if (xml_copy(x, xcopy) < 0)
2169 	    goto done;
2170     }
2171 
2172     /* Go through children to detect any marked nodes:
2173      * (3) Special case: key nodes in lists are copied if any
2174      * node in list is marked
2175      */
2176     mark = 0;
2177     x = NULL;
2178     while ((x = xml_child_each(x0, x, CX_ELMNT)) != NULL) {
2179 	if (xml_flag(x, XML_FLAG_MARK|XML_FLAG_CHANGE)){
2180 	    mark++;
2181 	    break;
2182 	}
2183     }
2184     x = NULL;
2185     while ((x = xml_child_each(x0, x, CX_ELMNT)) != NULL) {
2186 	name = xml_name(x);
2187 	if (xml_flag(x, XML_FLAG_MARK)){
2188 	    /* (2) the complete subtree of that node is copied. */
2189 	    if ((xcopy = xml_new(name, x1, CX_ELMNT)) == NULL)
2190 		goto done;
2191 	    if (xml_copy(x, xcopy) < 0)
2192 		goto done;
2193 	    continue;
2194 	}
2195 	if (xml_flag(x, XML_FLAG_CHANGE)){
2196 	    /*  Copy individual nodes marked with XML_FLAG_CHANGE */
2197 	    if ((xcopy = xml_new(name, x1, CX_ELMNT)) == NULL)
2198 		goto done;
2199 	    if (xml_copy_marked(x, xcopy) < 0) /*  */
2200 		goto done;
2201 	}
2202 	/* (3) Special case: key nodes in lists are copied if any
2203 	 * node in list is marked */
2204 	if (mark && yt && yang_keyword_get(yt) == Y_LIST){
2205 	    /* XXX: I think yang_key_match is suboptimal here */
2206 	    if ((iskey = yang_key_match(yt, name)) < 0)
2207 		goto done;
2208 	    if (iskey){
2209 		if ((xcopy = xml_new(name, x1, CX_ELMNT)) == NULL)
2210 		    goto done;
2211 		if (xml_copy(x, xcopy) < 0)
2212 		    goto done;
2213 	    }
2214 	}
2215     }
2216     retval = 0;
2217  done:
2218     return retval;
2219 }
2220 
2221