1 /*
2  *
3   ***** BEGIN LICENSE BLOCK *****
4 
5   Copyright (C) 2009-2019 Olof Hagsand
6   Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
7 
8   This file is part of CLIXON.
9 
10   Licensed under the Apache License, Version 2.0 (the "License");
11   you may not use this file except in compliance with the License.
12   You may obtain a copy of the License at
13 
14     http://www.apache.org/licenses/LICENSE-2.0
15 
16   Unless required by applicable law or agreed to in writing, software
17   distributed under the License is distributed on an "AS IS" BASIS,
18   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19   See the License for the specific language governing permissions and
20   limitations under the License.
21 
22   Alternatively, the contents of this file may be used under the terms of
23   the GNU General Public License Version 3 or later (the "GPL"),
24   in which case the provisions of the GPL are applicable instead
25   of those above. If you wish to allow use of your version of this file only
26   under the terms of the GPL, and not to allow others to
27   use your version of this file under the terms of Apache License version 2, indicate
28   your decision by deleting the provisions above and replace them with the
29   notice and other provisions required by the GPL. If you do not delete
30   the provisions above, a recipient may use your version of this file under
31   the terms of any one of the Apache License version 2 or the GPL.
32 
33   ***** END LICENSE BLOCK *****
34 
35  * Clixon XML XPATH 1.0 according to https://www.w3.org/TR/xpath-10
36  *
37  * Some notes on namespace extensions in Netconf/Yang
38  * RFC6241 8.9.1
39  * The set of namespace declarations are those in scope on the <filter> element.
40  * <rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
41  *    <get-config>
42  *       <filter xmlns:t="http://example.com/schema/1.2/config"
43  *               type="xpath"
44  *               select="/t:top/t:users/t:user[t:name='fred']"/>
45  *       </get-config>
46  * We need to add namespace context to the cpath tree, typically in eval. How do
47  * we do that?
48  * One observation is that the namespace context is static, so it can not be a part
49  * of the xpath-tree, which is context-dependent.
50  * Best is to send it as a (read-only) parameter to the xp_eval family of functions
51  * as an exlicit namespace context.
52  * For that you need an API to get/set namespaces: clixon_xml_nscache.c?
53  * Then you need to fix API functions and this is the real work:
54  * - Replace all existing functions or create new?
55  * - Expose explicit namespace parameter, or xml object, or default namespace?
56  */
57 #ifdef HAVE_CONFIG_H
58 #include "clixon_config.h" /* generated by config & autoconf */
59 #endif
60 
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <unistd.h>
64 #include <errno.h>
65 #include <string.h>
66 #include <limits.h>
67 #include <stdint.h>
68 #include <assert.h>
69 #include <syslog.h>
70 #include <fcntl.h>
71 #include <math.h> /* NaN */
72 
73 /* cligen */
74 #include <cligen/cligen.h>
75 
76 /* clicon */
77 #include "clixon_err.h"
78 #include "clixon_log.h"
79 #include "clixon_string.h"
80 #include "clixon_queue.h"
81 #include "clixon_hash.h"
82 #include "clixon_handle.h"
83 #include "clixon_yang.h"
84 #include "clixon_xml.h"
85 #include "clixon_xml_sort.h"
86 #include "clixon_xml_nsctx.h"
87 #include "clixon_xpath_ctx.h"
88 #include "clixon_xpath.h"
89 #include "clixon_xpath_optimize.h"
90 #include "clixon_xpath_function.h"
91 #include "clixon_xpath_eval.h"
92 
93 /* Mapping between XPATH operator string <--> int  */
94 const map_str2int xpopmap[] = {
95     {"and",              XO_AND},
96     {"or",               XO_OR},
97     {"div",              XO_DIV},
98     {"mod",              XO_MOD},
99     {"+",                XO_ADD},
100     {"*",                XO_MULT},
101     {"-",                XO_SUB},
102     {"=",                XO_EQ},
103     {"!=",               XO_NE},
104     {">=",               XO_GE},
105     {"<=",               XO_LE},
106     {"<",                XO_LT},
107     {">",                XO_GT},
108     {"|",                XO_UNION},
109     {NULL,               -1}
110 };
111 
112 /*! Eval an XPATH nodetest
113  * @retval   -1     Error  XXX: retval -1 not properly handled
114  * @retval    0     No match
115  * @retval    1     Match
116  */
117 static int
nodetest_eval_node(cxobj * x,xpath_tree * xs,cvec * nsc)118 nodetest_eval_node(cxobj      *x,
119 		   xpath_tree *xs,
120 		   cvec       *nsc)
121 {
122     int  retval = -1;
123     char *name1 = xml_name(x);
124     char *prefix1 = xml_prefix(x);
125     char *nsxml = NULL;     /* xml body namespace */
126     char *nsxpath = NULL; /* xpath context namespace */
127     char *prefix2 = NULL;
128     char *name2 = NULL;
129 
130     /* Namespaces is s0, name is s1 */
131     if (strcmp(xs->xs_s1, "*")==0)
132 	return 1;
133     /* get namespace of xml tree */
134     if (xml2ns(x, prefix1, &nsxml) < 0)
135 	goto done;
136     prefix2 = xs->xs_s0;
137     name2 = xs->xs_s1;
138     /* Before going into namespaces, check name equality and filter out noteq  */
139     if (strcmp(name1, name2) != 0){
140 	retval = 0; /* no match */
141 	goto done;
142     }
143     /* here names are equal
144      * Now look for namespaces
145      * 1) prefix1 and prefix2 point to same namespace <<-- try this first
146      * 2) prefix1 is equal to prefix2 <<-- then try this
147      * (1) is strict yang xml
148      * (2) without yang
149      */
150     if (nsc != NULL) { /* solution (1) */
151 	nsxpath = xml_nsctx_get(nsc, prefix2);
152 	if (nsxml != NULL && nsxpath != NULL)
153 	    retval = (strcmp(nsxml, nsxpath) == 0);
154 	else
155 	    retval = (nsxml == nsxpath); /* True only if both are NULL */
156     }
157     else{ /* solution (2) */
158 	if (prefix1 == NULL && prefix2 == NULL)
159 	    retval = 1;
160 	else if (prefix1 == NULL || prefix2 == NULL)
161 	    retval = 0;
162 	else
163 	    retval = strcmp(prefix1, prefix2) == 0;
164     }
165 #if 0 /* debugging */
166     /* If retval == 0 here, then there is name match, but not ns match */
167     if (retval == 0){
168 	fprintf(stderr, "%s NOMATCH xml: (%s)%s\n\t\t xpath: (%s)%s\n", __FUNCTION__,
169 		name1, nsxml,
170 		name2, nsxpath);
171     }
172 #endif
173  done:	/* retval set in preceding statement */
174     return retval;
175 }
176 
177 /*! Eval an XPATH nodetest but skip prefix and namespace tests
178  * This is NOT according to standard
179  */
180 static int
nodetest_eval_node_localonly(cxobj * x,xpath_tree * xs,cvec * nsc)181 nodetest_eval_node_localonly(cxobj      *x,
182 			     xpath_tree *xs,
183 			     cvec       *nsc)
184 {
185     int  retval = -1;
186     char *name1 = xml_name(x);
187     char *name2 = NULL;
188 
189     /* Namespaces is s0, name is s1 */
190     if (strcmp(xs->xs_s1, "*")==0)
191 	return 1;
192     name2 = xs->xs_s1;
193     /* Before going into namespaces, check name equality and filter out noteq  */
194     if (strcmp(name1, name2) != 0){
195 	retval = 0; /* no match */
196 	goto done;
197     }
198  done:	/* retval set in preceding statement */
199     return retval;
200 }
201 
202 /*! Make a nodetest
203  * @param[in] x     XML node
204  * @param[in] xs    XPATH stack of type XP_NODE or XP_NODE_FN
205  * @param[in] nsc   XML Namespace context
206  * @param[in] localonly  Skip prefix and namespace tests (non-standard)
207  * @retval   -1     Error
208  * @retval    0     No match
209  * @retval    1     Match
210  * - node() is true for any node of any type whatsoever.
211  * - text() is true for any text node.
212  */
213 static int
nodetest_eval(cxobj * x,xpath_tree * xs,cvec * nsc,int localonly)214 nodetest_eval(cxobj      *x,
215 	      xpath_tree *xs,
216 	      cvec       *nsc,
217 	      int         localonly)
218 {
219     int   retval = 0; /* NB: no match is default (not error) */
220 
221     if (xs->xs_type == XP_NODE){
222 	if (localonly)
223 	    retval = nodetest_eval_node_localonly(x, xs, nsc);
224 	else
225 	    retval = nodetest_eval_node(x, xs, nsc);
226     }
227     else if (xs->xs_type == XP_NODE_FN){
228 	switch (xs->xs_int){
229 	case XPATHFN_NODE:
230 	case XPATHFN_TEXT:
231 	    retval = 1;
232 	    break;
233 	default:
234 	    break;
235 	}
236     }
237     /* note, retval set by previous statement */
238     return retval;
239 }
240 
241 /*!
242  * @param[in]  xn
243  * @param[in]  nodetest   XPATH stack
244  * @param[in]  node_type
245  * @param[in]  flags
246  * @param[in]  nsc        XML Namespace context
247  * @param[in]  localonly  Skip prefix and namespace tests (non-standard)
248  * @param[out] vec0
249  * @param[out] vec0len
250  */
251 int
nodetest_recursive(cxobj * xn,xpath_tree * nodetest,int node_type,uint16_t flags,cvec * nsc,int localonly,cxobj *** vec0,int * vec0len)252 nodetest_recursive(cxobj      *xn,
253 		   xpath_tree *nodetest,
254 		   int         node_type,
255 		   uint16_t    flags,
256 		   cvec       *nsc,
257 		   int         localonly,
258 		   cxobj    ***vec0,
259 		   int        *vec0len)
260 {
261     int     retval = -1;
262     cxobj  *xsub;
263     cxobj **vec = *vec0;
264     int     veclen = *vec0len;
265 
266     xsub = NULL;
267     while ((xsub = xml_child_each(xn, xsub, node_type)) != NULL) {
268 	if (nodetest_eval(xsub, nodetest, nsc, localonly) == 1){
269 	    clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(xsub, flags));
270 	    if (flags==0x0 || xml_flag(xsub, flags))
271 		if (cxvec_append(xsub, &vec, &veclen) < 0)
272 		    goto done;
273 	    //	    continue; /* Dont go deeper */
274 	}
275 	if (nodetest_recursive(xsub, nodetest, node_type, flags, nsc, localonly, &vec, &veclen) < 0)
276 	    goto done;
277     }
278     retval = 0;
279     *vec0 = vec;
280     *vec0len = veclen;
281   done:
282     return retval;
283 }
284 
285 /*! Evaluate xpath step rule of an XML tree
286  *
287  * @param[in]  xc0  Incoming context
288  * @param[in]  xs   XPATH node tree
289  * @param[in]  nsc  XML Namespace context
290  * @param[in]  localonly Skip prefix and namespace tests (non-standard)
291  * @param[out] xrp  Resulting context
292  *
293  * - A node test that is a QName is true if and only if the type of the node (see [5 Data Model])
294  * is the principal node type and has an expanded-name equal to the expanded-name specified by the QName.
295  * - A node test * is true for any node of the principal node type.
296  * - node() is true for any node of any type whatsoever.
297  * - text() is true for any text node.
298  */
299 static int
xp_eval_step(xp_ctx * xc0,xpath_tree * xs,cvec * nsc,int localonly,xp_ctx ** xrp)300 xp_eval_step(xp_ctx     *xc0,
301 	     xpath_tree *xs,
302 	     cvec       *nsc,
303 	     int         localonly,
304 	     xp_ctx    **xrp)
305 {
306     int         retval = -1;
307     int         i;
308     cxobj      *x;
309     cxobj      *xv;
310     cxobj      *xp;
311     cxobj     **vec = NULL;
312     int         veclen = 0;
313     xpath_tree *nodetest = xs->xs_c0;
314     xp_ctx     *xc = NULL;
315     int         ret;
316 
317     /* Create new xc */
318     if ((xc = ctx_dup(xc0)) == NULL)
319 	goto done;
320     switch (xs->xs_int){
321     case A_ANCESTOR:
322 	break;
323     case A_ANCESTOR_OR_SELF:
324 	break;
325     case A_ATTRIBUTE: /* principal node type is attribute */
326 	break;
327     case A_CHILD:
328 	if (xc->xc_descendant){
329 	    for (i=0; i<xc->xc_size; i++){
330 		xv = xc->xc_nodeset[i];
331 		if (nodetest_recursive(xv, nodetest, CX_ELMNT, 0x0, nsc, localonly, &vec, &veclen) < 0)
332 		    goto done;
333 	    }
334 	    xc->xc_descendant = 0;
335 	}
336 	else{
337 	    for (i=0; i<xc->xc_size; i++){
338 		xv = xc->xc_nodeset[i];
339 		x = NULL;
340 		if ((ret = xpath_optimize_check(xs, xv, &vec, &veclen)) < 0)
341 		    goto done;
342 		if (ret == 0){/* regular code, no optimization made */
343 		    while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) {
344 			/* xs->xs_c0 is nodetest */
345 			if (nodetest == NULL || nodetest_eval(x, nodetest, nsc, localonly) == 1){
346 			    if (cxvec_append(x, &vec, &veclen) < 0)
347 				goto done;
348 			}
349 		    }
350 		}
351 	    }
352 	}
353 	ctx_nodeset_replace(xc, vec, veclen);
354 	break;
355     case A_DESCENDANT_OR_SELF:
356 	for (i=0; i<xc->xc_size; i++){
357 	    xv = xc->xc_nodeset[i];
358 	    if (nodetest_recursive(xv, xs->xs_c0, CX_ELMNT, 0x0, nsc, localonly, &vec, &veclen) < 0)
359 		goto done;
360 	}
361 	for (i=0; i<veclen; i++){
362 	    x = vec[i];
363 	    if (cxvec_append(x, &xc->xc_nodeset, &xc->xc_size) < 0)
364 		goto done;
365 	}
366 	if (vec){
367 	    free(vec);
368 	    vec = NULL;
369 	}
370 	break;
371     case A_DESCENDANT:
372 	for (i=0; i<xc->xc_size; i++){
373 	    xv = xc->xc_nodeset[i];
374 	    if (nodetest_recursive(xv, xs->xs_c0, CX_ELMNT, 0x0, nsc, localonly, &vec, &veclen) < 0)
375 		goto done;
376 	}
377 	ctx_nodeset_replace(xc, vec, veclen);
378 	break;
379     case A_FOLLOWING:
380 	break;
381     case A_FOLLOWING_SIBLING:
382 	break;
383     case A_NAMESPACE: /* principal node type is namespace */
384 	break;
385     case A_PARENT:
386 	veclen = xc->xc_size;
387 	vec = xc->xc_nodeset;
388 	xc->xc_size = 0;
389 	xc->xc_nodeset = NULL;
390 	for (i=0; i<veclen; i++){
391 	    x = vec[i];
392 	    if ((xp = xml_parent(x)) != NULL)
393 		if (cxvec_append(xp, &xc->xc_nodeset, &xc->xc_size) < 0)
394 		    goto done;
395 	}
396 	if (vec){
397 	    free(vec);
398 	    vec = NULL;
399 	}
400 	break;
401     case A_PRECEDING:
402 	break;
403     case A_PRECEDING_SIBLING:
404 	break;
405     case A_SELF:
406 	break;
407     default:
408 	clicon_err(OE_XML, 0, "No such axisname: %d", xs->xs_int);
409 	goto done;
410 	break;
411     }
412     if (xs->xs_c1){
413 	if (xp_eval(xc, xs->xs_c1, nsc, localonly, xrp) < 0)
414 	    goto done;
415     }
416     else{
417 	*xrp = xc;
418 	xc = NULL;
419     }
420     assert(*xrp);
421     retval = 0;
422  done:
423     if (xc)
424 	ctx_free(xc);
425     return retval;
426 }
427 
428 /*! Evaluate xpath predicates rule
429  *
430  * pred -> pred expr
431  * @param[in]  xc   Incoming context
432  * @param[in]  xs   XPATH node tree
433  * @param[in]  nsc  XML Namespace context
434  * @param[in]  localonly Skip prefix and namespace tests (non-standard)
435  * @param[out] xrp  Resulting context
436  *
437  * A predicate filters a node-set with respect to an axis to produce a new
438  * node-set. For each node in the node-set to be filtered, the PredicateExpr is
439  * evaluated with that node as the context node, with the number of nodes in
440  * the node-set as the context size, and with the proximity position of the node
441  * in the node-set with respect to the axis as the context position; if
442  * PredicateExpr evaluates to true for that node, the node is included in the
443  * new node-set; otherwise, it is not included.
444  * A PredicateExpr is evaluated by evaluating the Expr and converting the result
445  * to a boolean. If the result is a
446  * - number, the result will be converted to true if the number is equal to the
447  *   context position and will be converted to false otherwise;
448  * - if the result is not a number, then the result will be converted as if by a
449  *   call to the boolean function.
450  * Thus a location path para[3] is equivalent to para[position()=3].
451  */
452 static int
xp_eval_predicate(xp_ctx * xc,xpath_tree * xs,cvec * nsc,int localonly,xp_ctx ** xrp)453 xp_eval_predicate(xp_ctx     *xc,
454 		  xpath_tree *xs,
455 		  cvec       *nsc,
456 		  int         localonly,
457 		  xp_ctx    **xrp)
458 {
459     int      retval = -1;
460     xp_ctx  *xr0 = NULL;
461     xp_ctx  *xr1 = NULL;
462     xp_ctx  *xrc = NULL;
463     int      i;
464     cxobj   *x;
465     xp_ctx  *xcc;
466 
467     if (xs->xs_c0 == NULL){ /* empty */
468 	if ((xr0 = ctx_dup(xc)) == NULL)
469 	    goto done;
470     }
471     else{ /* eval previous predicates */
472 	if (xp_eval(xc, xs->xs_c0, nsc, localonly, &xr0) < 0)
473 	    goto done;
474     }
475     if (xs->xs_c1){
476 	/* Loop over each node in the nodeset */
477 	assert (xr0->xc_type == XT_NODESET);
478 	if ((xr1 = malloc(sizeof(*xr1))) == NULL){
479 	    clicon_err(OE_UNIX, errno, "malloc");
480 	    goto done;
481 	}
482 	memset(xr1, 0, sizeof(*xr1));
483 	xr1->xc_type = XT_NODESET;
484 	xr1->xc_node = xc->xc_node;
485 	xr1->xc_initial = xc->xc_initial;
486 	for (i=0; i<xr0->xc_size; i++){
487 	    x = xr0->xc_nodeset[i];
488 	    /* Create new context */
489 	    if ((xcc = malloc(sizeof(*xcc))) == NULL){
490 		clicon_err(OE_XML, errno, "malloc");
491 		goto done;
492 	    }
493 	    memset(xcc, 0, sizeof(*xcc));
494 	    xcc->xc_type = XT_NODESET;
495 	    xcc->xc_initial = xc->xc_initial;
496 	    xcc->xc_node = x;
497 	    /* For each node in the node-set to be filtered, the PredicateExpr is
498 	     * evaluated with that node as the context node */
499 	    if (cxvec_append(x, &xcc->xc_nodeset, &xcc->xc_size) < 0)
500 		goto done;
501 	    if (xp_eval(xcc, xs->xs_c1, nsc, localonly, &xrc) < 0)
502 		goto done;
503 	    if (xcc)
504 		ctx_free(xcc);
505 	    if (xrc->xc_type == XT_NUMBER){
506 		/* If the result is a number, the result will be converted to true
507 		   if the number is equal to the context position */
508 		if ((int)xrc->xc_number == i)
509 		    if (cxvec_append(x, &xr1->xc_nodeset, &xr1->xc_size) < 0)
510 			goto done;
511 	    }
512 	    else {
513 		/* if PredicateExpr evaluates to true for that node, the node is
514 		   included in the new node-set */
515 		if (ctx2boolean(xrc))
516 		    if (cxvec_append(x, &xr1->xc_nodeset, &xr1->xc_size) < 0)
517 			goto done;
518 	    }
519 	    if (xrc)
520 		ctx_free(xrc);
521 	}
522     }
523     assert(xr0||xr1);
524     if (xr1){
525 	*xrp = xr1;
526 	xr1 = NULL;
527     }
528     else
529 	if (xr0){
530 	    *xrp = xr0;
531 	    xr0 = NULL;
532 	}
533     retval = 0;
534  done:
535     if (xr0)
536 	ctx_free(xr0);
537     if (xr1)
538 	ctx_free(xr1);
539     return retval;
540 }
541 
542 /*! Given two XPATH contexts, eval logical  operations: or,and
543  * The logical operators convert their operands to booleans
544  * @param[in]  xc1  Context of operand1
545  * @param[in]  xc2  Context of operand2
546  * @param[in]  op   Relational operator
547  * @param[out] xrp  Result context
548  * @retval     0    OK
549  * @retval    -1    Error
550  */
551 static int
xp_logop(xp_ctx * xc1,xp_ctx * xc2,enum xp_op op,xp_ctx ** xrp)552 xp_logop(xp_ctx    *xc1,
553 	 xp_ctx    *xc2,
554 	 enum xp_op op,
555 	 xp_ctx   **xrp)
556 {
557     int     retval = -1;
558     xp_ctx *xr = NULL;
559     int     b1;
560     int     b2;
561 
562     if ((xr = malloc(sizeof(*xr))) == NULL){
563 	clicon_err(OE_UNIX, errno, "malloc");
564 	goto done;
565     }
566     memset(xr, 0, sizeof(*xr));
567     xr->xc_initial = xc1->xc_initial;
568     xr->xc_type = XT_BOOL;
569     if ((b1 = ctx2boolean(xc1)) < 0)
570 	goto done;
571     if ((b2 = ctx2boolean(xc2)) < 0)
572 	goto done;
573     switch (op){
574     case XO_AND:
575 	xr->xc_bool = b1 && b2;
576 	break;
577     case XO_OR:
578 	xr->xc_bool = b1 || b2;
579 	break;
580     default:
581 	clicon_err(OE_UNIX, errno, "%s:Invalid operator %s in this context",
582 		   __FUNCTION__, clicon_int2str(xpopmap,op));
583 	goto done;
584     }
585     *xrp = xr;
586     retval = 0;
587  done:
588     return retval;
589 }
590 
591 /*! Given two XPATH contexts, eval numeric operations: +-*,div,mod
592  * The numeric operators convert their operands to numbers as if by
593  * calling the number function.
594  * @param[in]  xc1  Context of operand1
595  * @param[in]  xc2  Context of operand2
596  * @param[in]  op   Relational operator
597  * @param[out] xrp  Result context
598  * @retval     0    OK
599  * @retval    -1    Error
600  */
601 static int
xp_numop(xp_ctx * xc1,xp_ctx * xc2,enum xp_op op,xp_ctx ** xrp)602 xp_numop(xp_ctx    *xc1,
603 	 xp_ctx    *xc2,
604 	 enum xp_op op,
605 	 xp_ctx   **xrp)
606 {
607     int     retval = -1;
608     xp_ctx *xr = NULL;
609     double  n1;
610     double  n2;
611 
612     if ((xr = malloc(sizeof(*xr))) == NULL){
613 	clicon_err(OE_UNIX, errno, "malloc");
614 	goto done;
615     }
616     memset(xr, 0, sizeof(*xr));
617     xr->xc_initial = xc1->xc_initial;
618     xr->xc_type = XT_NUMBER;
619     if (ctx2number(xc1, &n1) < 0)
620 	goto done;
621     if (ctx2number(xc2, &n2) < 0)
622 	goto done;
623     if (isnan(n1) || isnan(n2))
624 	xr->xc_number = NAN;
625     else
626 	switch (op){
627 	case XO_DIV:
628 	    xr->xc_number = n1/n2;
629 	    break;
630 	case XO_MOD:
631 	    xr->xc_number = ((int)n1)%((int)n2);
632 	    break;
633 	case XO_ADD:
634 	    xr->xc_number = n1+n2;
635 	    break;
636 	case XO_MULT:
637 	    xr->xc_number = n1*n2;
638 	    break;
639 	case XO_SUB:
640 	    xr->xc_number = n1-n2;
641 	    break;
642 	default:
643 	    clicon_err(OE_UNIX, errno, "Invalid operator %s in this context",
644 		       clicon_int2str(xpopmap,op));
645 	    goto done;
646 	}
647     *xrp = xr;
648     retval = 0;
649  done:
650     return retval;
651 }
652 
653 /*! Given two XPATH contexts, eval relational operations: <>=
654  * A RelationalExpr is evaluated by comparing the objects that result from
655  * evaluating the two operands.
656  * This is covered:
657  * (a) Both are INTs, BOOLs, STRINGs. Result type is boolean
658  * (b) Both are nodesets and one is empty. Result type is boolean.
659  * (c) One is nodeset and other is INT or STRING. Result type is nodeset
660  * (d) All others (eg two nodesets, BOOL+STRING) are not supported.
661  * Op is = EQ
662  * From XPATH 1.0 standard, the evaluation has three variants:
663  * (1) comparisons that involve node-sets are defined in terms of comparisons that
664  * do not involve node-sets; this is defined uniformly for =, !=, <=, <, >= and >.
665  * (2) comparisons that do not involve node-sets are defined for = and !=.
666  * (3) comparisons that do not involve node-sets are defined for <=, <, >= and >.
667  * @param[in]  xc1  Context of operand1
668  * @param[in]  xc2  Context of operand2
669  * @param[in]  op   Relational operator
670  * @param[out] xrp  Result context
671  * @retval     0    OK
672  * @retval    -1    Error
673  */
674 static int
xp_relop(xp_ctx * xc1,xp_ctx * xc2,enum xp_op op,xp_ctx ** xrp)675 xp_relop(xp_ctx    *xc1,
676 	 xp_ctx    *xc2,
677 	 enum xp_op op,
678 	 xp_ctx   **xrp)
679 {
680     int     retval = -1;
681     xp_ctx *xr = NULL;
682     xp_ctx *xc;
683     cxobj  *x;
684     int     i;
685     int     j;
686     int     b;
687     char   *s1;
688     char   *s2;
689     int     reverse = 0;
690     double  n1, n2;
691 
692     if ((xr = malloc(sizeof(*xr))) == NULL){
693 	clicon_err(OE_UNIX, errno, "malloc");
694 	goto done;
695     }
696     memset(xr, 0, sizeof(*xr));
697     xr->xc_initial = xc1->xc_initial;
698     xr->xc_type = XT_BOOL;
699     if (xc1->xc_type == xc2->xc_type){ /* cases (2-3) above */
700 	switch (xc1->xc_type){
701 	case XT_NODESET:
702 	    /* If both are node-sets, then it is true iff the string value of one
703 	       node in the first node-set and one in the second node-set is true */
704 	    for (i=0; i<xc1->xc_size; i++){
705 		if ((s1 = xml_body(xc1->xc_nodeset[i])) == NULL){
706 		    xr->xc_bool = 0;
707 		    goto ok;
708 		}
709 		for (j=0; j<xc2->xc_size; j++){
710 		    if ((s2 = xml_body(xc2->xc_nodeset[j])) == NULL){
711 			xr->xc_bool = 0;
712 			goto ok;
713 		    }
714 		    switch(op){
715 		    case XO_EQ:
716 			xr->xc_bool = (strcmp(s1, s2)==0);
717 			break;
718 		    case XO_NE:
719 			xr->xc_bool = (strcmp(s1, s2)!=0);
720 			break;
721 		    case XO_GE:
722 			xr->xc_bool = (strcmp(s1, s2)>=0);
723 			break;
724 		    case XO_LE:
725 			xr->xc_bool = (strcmp(s1, s2)<=0);
726 			break;
727 		    case XO_LT:
728 			xr->xc_bool = (strcmp(s1, s2)<0);
729 			break;
730 		    case XO_GT:
731 			xr->xc_bool = (strcmp(s1, s2)>0);
732 			break;
733 		    default:
734 			clicon_err(OE_XML, 0, "Operator %s not supported for nodeset/nodeset comparison", clicon_int2str(xpopmap,op));
735 			goto done;
736 			break;
737 		    }
738 		    if (xr->xc_bool) /* enough to find a single node */
739 			break;
740 		}
741 		if (xr->xc_bool) /* enough to find a single node */
742 		    break;
743 	    }
744 	    break;
745 	case XT_BOOL:
746 	    xr->xc_bool = (xc1->xc_bool == xc2->xc_bool);
747 	    break;
748 	case XT_NUMBER:
749 	    xr->xc_bool = (xc1->xc_number == xc2->xc_number);
750 	    break;
751 	case XT_STRING:
752 	    xr->xc_bool = (strcmp(xc1->xc_string, xc2->xc_string)==0);
753 	    break;
754 	}
755     }
756     else if (xc1->xc_type != XT_NODESET &&
757 	     xc2->xc_type != XT_NODESET){
758 	clicon_err(OE_XML, 0, "Mixed types not supported, %d %d", xc1->xc_type, xc2->xc_type);
759 	goto done;
760     }
761     else{ /* one is nodeset, ie (1) above */
762 	if (xc2->xc_type == XT_NODESET){
763 	    xc = xc2;
764 	    xc2 = xc1;
765 	    xc1 = xc;
766 	    reverse++; /* reverse */
767 	}
768 	/* xc1 is nodeset
769 	 * xc2 is something else */
770 	switch (xc2->xc_type){
771 	case XT_BOOL:
772 	    /* comparison on the boolean and the result of converting the
773 	       node-set to a boolean using the boolean function is true. */
774 	    b = ctx2boolean(xc1);
775 	    switch(op){
776 	    case XO_EQ:
777 		xr->xc_bool = (b == xc2->xc_bool);
778 		break;
779 	    case XO_NE:
780 		xr->xc_bool = (b != xc2->xc_bool);
781 		break;
782 	    default:
783 		clicon_err(OE_XML, 0, "Operator %s not supported for nodeset and bool", clicon_int2str(xpopmap,op));
784 		goto done;
785 		break;
786 	    } /* switch op */
787 	    break;
788 	case XT_STRING:
789 	    /* If one object to be compared is a node-set and the
790 	       other is a string, then the comparison will be true if and only
791 	       if there is a node in the node-set such that the result of
792 	       performing the comparison on the string-value of the node and
793 	       the other string is true.*/
794 	    s2 = xc2->xc_string;
795 	    for (i=0; i<xc1->xc_size; i++){
796 		x = xc1->xc_nodeset[i]; /* node in nodeset */
797 		s1 = xml_body(x);
798 		switch(op){
799 		case XO_EQ:
800 		    if (s1 == NULL || s2 == NULL)
801 			xr->xc_bool = (s1==NULL && s2 == NULL);
802 		    else
803 			xr->xc_bool = (strcmp(s1, s2)==0);
804 		    break;
805 		case XO_NE:
806 		    if (s1 == NULL || s2 == NULL)
807 			xr->xc_bool = !(s1==NULL && s2 == NULL);
808 		    else
809 			xr->xc_bool = (strcmp(s1, s2));
810 		    break;
811 		default:
812 		    clicon_err(OE_XML, 0, "Operator %s not supported for nodeset and string", clicon_int2str(xpopmap,op));
813 		goto done;
814 		    break;
815 		}
816 		if (xr->xc_bool) /* enough to find a single node */
817 		    break;
818 	    }
819 	    break;
820 	case XT_NUMBER:
821 	    for (i=0; i<xc1->xc_size; i++){
822 		x = xc1->xc_nodeset[i]; /* node in nodeset */
823 		if (sscanf(xml_body(x), "%lf", &n1) != 1)
824 		    n1 = NAN;
825 		n2 = xc2->xc_number;
826 		switch(op){
827 		case XO_EQ:
828 		    xr->xc_bool = (n1 == n2);
829 		    break;
830 		case XO_NE:
831 		    xr->xc_bool = (n1 != n2);
832 		    break;
833 		case XO_GE:
834 		    xr->xc_bool = reverse?(n2 >= n1):(n1 >= n2);
835 		    break;
836 		case XO_LE:
837 		    xr->xc_bool = reverse?(n2 <= n1):(n1 <= n2);
838 		    break;
839 		case XO_LT:
840 		    xr->xc_bool = reverse?(n2 < n1):(n1 < n2);
841 		    break;
842 		case XO_GT:
843 		    xr->xc_bool = reverse?(n2 > n1):(n1 > n2);
844 		    break;
845 		default:
846 		    clicon_err(OE_XML, 0, "Operator %s not supported for nodeset and number", clicon_int2str(xpopmap,op));
847 		goto done;
848 		    break;
849 		}
850 		if (xr->xc_bool) /* enough to find a single node */
851 		    break;
852 	    }
853 	    break;
854 	default:
855 	    clicon_err(OE_XML, 0, "Type %d not supported", xc2->xc_type);
856 	} /* switch type */
857     }
858  ok:
859     /* Just ensure bool is 0 or 1 */
860     if (xr->xc_type == XT_BOOL && xr->xc_bool != 0)
861 	xr->xc_bool = 1;
862     *xrp = xr;
863     retval = 0;
864  done:
865     return retval;
866 }
867 
868 /*! Given two XPATH contexts, eval union operation
869  * Both operands must be nodesets, otherwise empty nodeset is returned
870  * @param[in]  xc1  Context of operand1
871  * @param[in]  xc2  Context of operand2
872  * @param[in]  op   Relational operator
873  * @param[out] xrp  Result context
874  * @retval     0    OK
875  * @retval    -1    Error
876  */
877 static int
xp_union(xp_ctx * xc1,xp_ctx * xc2,enum xp_op op,xp_ctx ** xrp)878 xp_union(xp_ctx    *xc1,
879 	 xp_ctx    *xc2,
880 	 enum xp_op op,
881 	 xp_ctx   **xrp)
882 {
883     int     retval = -1;
884     xp_ctx *xr = NULL;
885     int     i;
886 
887     if (op != XO_UNION){
888 	clicon_err(OE_UNIX, errno, "%s:Invalid operator %s in this context",
889 		   __FUNCTION__, clicon_int2str(xpopmap,op));
890 	goto done;
891     }
892     if ((xr = malloc(sizeof(*xr))) == NULL){
893 	clicon_err(OE_UNIX, errno, "malloc");
894 	goto done;
895     }
896     memset(xr, 0, sizeof(*xr));
897     xr->xc_initial = xc1->xc_initial;
898     xr->xc_type = XT_NODESET;
899 
900     for (i=0; i<xc1->xc_size; i++)
901 	if (cxvec_append(xc1->xc_nodeset[i], &xr->xc_nodeset, &xr->xc_size) < 0)
902 	    goto done;
903     for (i=0; i<xc2->xc_size; i++){
904 	if (cxvec_append(xc2->xc_nodeset[i], &xr->xc_nodeset, &xr->xc_size) < 0)
905 	    goto done;
906     }
907     *xrp = xr;
908     retval = 0;
909  done:
910     return retval;
911 }
912 
913 /*! Evaluate an XPATH on an XML tree
914 
915  * The initial sequence of steps selects a set of nodes relative to a context node.
916  * Each node in that set is used as a context node for the following step.
917  * @param[in]  xc   Incoming context
918  * @param[in]  xs   XPATH node tree
919  * @param[in]  nsc  XML Namespace context
920  * @param[in]  localonly Skip prefix and namespace tests (non-standard)
921  * @param[out] xrp  Resulting context
922  * @retval     0    OK
923  * @retval    -1    Error
924  */
925 int
xp_eval(xp_ctx * xc,xpath_tree * xs,cvec * nsc,int localonly,xp_ctx ** xrp)926 xp_eval(xp_ctx     *xc,
927 	xpath_tree *xs,
928 	cvec       *nsc,
929 	int         localonly,
930 	xp_ctx    **xrp)
931 {
932     int        retval = -1;
933     cxobj     *x;
934     xp_ctx    *xr0 = NULL;
935     xp_ctx    *xr1 = NULL;
936     xp_ctx    *xr2 = NULL;
937     int        use_xr0 = 0; /* In 2nd child use transitively result of 1st child */
938 
939     if (clicon_debug_get() > 1)
940 	ctx_print(stderr, xc, xpath_tree_int2str(xs->xs_type));
941     /* Pre-actions before check first child c0
942      */
943     switch (xs->xs_type){
944     case XP_RELLOCPATH:
945 	if (xs->xs_int == A_DESCENDANT_OR_SELF)
946 	    xc->xc_descendant = 1; /* XXX need to set to 0 in sub */
947 	break;
948     case XP_ABSPATH:
949 	/* Set context node to top node, and nodeset to that node only */
950 	x = xc->xc_node;
951 	while (xml_parent(x) != NULL)
952 	    x = xml_parent(x);
953 	xc->xc_node = x;
954 	xc->xc_nodeset[0] = x;
955 	xc->xc_size=1;
956 	/* // is short for /descendant-or-self::node()/ */
957 	if (xs->xs_int == A_DESCENDANT_OR_SELF)
958 	    xc->xc_descendant = 1; /* XXX need to set to 0 in sub */
959 
960 	break;
961     case XP_STEP:    /* XP_NODE is first argument -not called explicitly */
962 	if (xp_eval_step(xc, xs, nsc, localonly, xrp) < 0)
963 	    goto done;
964 	goto ok;
965 	break;
966     case XP_PRED:
967 	if (xp_eval_predicate(xc, xs, nsc, localonly, xrp) < 0)
968 	    goto done;
969 	goto ok;
970 	break;
971     case XP_PRIME_FN:
972 	if (xs->xs_s0){
973 	    switch (xs->xs_int){
974 	    case XPATHFN_CURRENT:
975 		if (xp_function_current(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
976 		    goto done;
977 		goto ok;
978 		break;
979 	    case XPATHFN_DEREF:
980 		if (xp_function_deref(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
981 		    goto done;
982 		goto ok;
983 		break;
984 	    case XPATHFN_DERIVED_FROM:
985 		if (xp_function_derived_from(xc, xs->xs_c0, nsc, localonly, 0, xrp) < 0)
986 		    goto done;
987 		goto ok;
988 		break;
989 	    case XPATHFN_DERIVED_FROM_OR_SELF:
990 		if (xp_function_derived_from(xc, xs->xs_c0, nsc, localonly, 1, xrp) < 0)
991 		    goto done;
992 		goto ok;
993 		break;
994 	    case XPATHFN_COUNT:
995 		if (xp_function_count(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
996 		    goto done;
997 		goto ok;
998 		break;
999 	    case XPATHFN_NAME:
1000 		if (xp_function_name(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
1001 		    goto done;
1002 		goto ok;
1003 		break;
1004 	    case XPATHFN_CONTAINS:
1005 		if (xp_function_contains(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
1006 		    goto done;
1007 		goto ok;
1008 		break;
1009 	    case XPATHFN_NOT:
1010 		if (xp_function_not(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
1011 		    goto done;
1012 		goto ok;
1013 		break;
1014 	    default:
1015 		clicon_err(OE_XML, EFAULT, "XPATH function not implemented: %s", xs->xs_s0);
1016 		goto done;
1017 		break;
1018 	    }
1019 	}
1020 	break;
1021     default:
1022 	break;
1023     }
1024     /* Eval first child c0
1025      */
1026     if (xs->xs_c0){
1027 	if (xp_eval(xc, xs->xs_c0, nsc, localonly, &xr0) < 0)
1028 	    goto done;
1029     }
1030     /* Actions between first and second child
1031      */
1032     switch (xs->xs_type){
1033     case XP_EXP:
1034 	break;
1035     case XP_AND:
1036 	break;
1037     case XP_RELEX: /* relexpr --> addexpr | relexpr relop addexpr */
1038 	break;
1039     case XP_ADD: /* combine mult and add ops */
1040 	break;
1041     case XP_UNION:
1042 	break;
1043     case XP_PATHEXPR:
1044 	if (xs->xs_c1)
1045 	    use_xr0++;
1046 	break;
1047     case XP_FILTEREXPR:
1048 	break;
1049     case XP_LOCPATH:
1050 	break;
1051     case XP_ABSPATH:
1052 	use_xr0++;
1053 	/* Special case, no c0 or c1, single "/" */
1054 	if (xs->xs_c0 == NULL){
1055 	    if ((xr0 = malloc(sizeof(*xr0))) == NULL){
1056 		clicon_err(OE_UNIX, errno, "malloc");
1057 		goto done;
1058 	    }
1059 	    memset(xr0, 0, sizeof(*xr0));
1060 	    xr0->xc_initial = xc->xc_initial;
1061 	    xr0->xc_type = XT_NODESET;
1062 	    x = NULL;
1063 	    while ((x = xml_child_each(xc->xc_node, x, CX_ELMNT)) != NULL) {
1064 		if (cxvec_append(x, &xr0->xc_nodeset, &xr0->xc_size) < 0)
1065 		    goto done;
1066 	    }
1067 	}
1068 	break;
1069     case XP_RELLOCPATH:
1070 	use_xr0++;
1071 	if (xs->xs_int == A_DESCENDANT_OR_SELF)
1072 	    xc->xc_descendant = 1; /* XXX need to set to 0 in sub */
1073 	break;
1074     case XP_NODE:
1075 	break;
1076     case XP_NODE_FN:
1077 	break;
1078     case XP_PRI0:
1079 	break;
1080     case XP_PRIME_NR: /* primaryexpr -> [<number>] */
1081 	if ((xr0 = malloc(sizeof(*xr0))) == NULL){
1082 	    clicon_err(OE_UNIX, errno, "malloc");
1083 	    goto done;
1084 	}
1085 	memset(xr0, 0, sizeof(*xr0));
1086 	xr0->xc_initial = xc->xc_initial;
1087 	xr0->xc_type = XT_NUMBER;
1088 	xr0->xc_number = xs->xs_double;
1089 	break;
1090     case XP_PRIME_STR:
1091 	if ((xr0 = malloc(sizeof(*xr0))) == NULL){
1092 	    clicon_err(OE_UNIX, errno, "malloc");
1093 	    goto done;
1094 	}
1095 	memset(xr0, 0, sizeof(*xr0));
1096 	xr0->xc_initial = xc->xc_initial;
1097 	xr0->xc_type = XT_STRING;
1098 	xr0->xc_string = xs->xs_s0?strdup(xs->xs_s0):NULL;
1099 	break;
1100     default:
1101 	break;
1102     }
1103     /* Eval second child c1
1104      * Note, some operators like locationpath, need transitive context (use_xr0)
1105      */
1106     if (xs->xs_c1)
1107 	if (xp_eval(use_xr0?xr0:xc, xs->xs_c1, nsc, localonly, &xr1) < 0)
1108 	    goto done;
1109     /* Actions after second child
1110      */
1111     if (xs->xs_c1)
1112 	switch (xs->xs_type){
1113 	case XP_AND: /* combine and and or ops */
1114 	    if (xp_logop(xr0, xr1, xs->xs_int, &xr2) < 0)
1115 		goto done;
1116 	    break;
1117 	case XP_RELEX: /* relexpr --> addexpr | relexpr relop addexpr */
1118 	    if (xp_relop(xr0, xr1, xs->xs_int, &xr2) < 0)
1119 		goto done;
1120 	    break;
1121 	case XP_ADD: /* combine mult and add ops */
1122 	    if (xp_numop(xr0, xr1, xs->xs_int, &xr2) < 0)
1123 		goto done;
1124 	    break;
1125 	case XP_UNION: /* combine and and or ops */
1126 	    if (xp_union(xr0, xr1, xs->xs_int, &xr2) < 0)
1127 		goto done;
1128 	default:
1129 	    break;
1130 	}
1131     xc->xc_descendant = 0;
1132 #if 0
1133     assert(xr0||xr1||xr2); /* for debugging */
1134 #else
1135     if (xr0 == NULL && xr1 == NULL && xr2 == NULL){
1136 	clicon_err(OE_XML, EFAULT, "Internal error: no result produced");
1137 	goto done;
1138     }
1139 #endif
1140     if (xr2){
1141 	*xrp = xr2;
1142 	xr2 = NULL;
1143     }
1144     else if (xr1){
1145 	*xrp = xr1;
1146 	xr1 = NULL;
1147     }
1148     else
1149 	if (xr0){
1150 	    *xrp = xr0;
1151 	    xr0 = NULL;
1152 	}
1153  ok:
1154     if (clicon_debug_get() > 1)
1155 	ctx_print(stderr, *xrp, xpath_tree_int2str(xs->xs_type));
1156     retval = 0;
1157  done:
1158     if (xr2)
1159 	ctx_free(xr2);
1160     if (xr1)
1161 	ctx_free(xr1);
1162     if (xr0)
1163 	ctx_free(xr0);
1164     return retval;
1165 } /* xp_eval */
1166 
1167 
1168