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  * and rfc 7950
37  *
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 <string.h>
48 #include <limits.h>
49 #include <stdint.h>
50 #include <assert.h>
51 #include <syslog.h>
52 #include <fcntl.h>
53 #include <math.h> /* NaN */
54 
55 /* cligen */
56 #include <cligen/cligen.h>
57 
58 /* clicon */
59 #include "clixon_err.h"
60 #include "clixon_log.h"
61 #include "clixon_string.h"
62 #include "clixon_queue.h"
63 #include "clixon_hash.h"
64 #include "clixon_handle.h"
65 #include "clixon_options.h"
66 #include "clixon_yang.h"
67 #include "clixon_yang_type.h"
68 #include "clixon_xml.h"
69 #include "clixon_xml_map.h"
70 #include "clixon_yang_module.h"
71 #include "clixon_validate.h"
72 #include "clixon_xpath_ctx.h"
73 #include "clixon_xpath.h"
74 #include "clixon_xpath_eval.h"
75 #include "clixon_xpath_function.h"
76 
77 static const map_str2int xpath_fnname_map[] = { /* alphabetic order */
78     {"bit-is-set",           XPATHFN_BIT_IS_SET},
79     {"boolean",              XPATHFN_BOOLEAN},
80     {"eiling",               XPATHFN_CEILING},
81     {"comment",              XPATHFN_COMMENT},
82     {"concat",               XPATHFN_CONCAT},
83     {"contains",             XPATHFN_CONTAINS},
84     {"count",                XPATHFN_COUNT},
85     {"current",              XPATHFN_CURRENT},
86     {"deref",                XPATHFN_DEREF},
87     {"derived-from",         XPATHFN_DERIVED_FROM},
88     {"derived-from-or-self", XPATHFN_DERIVED_FROM_OR_SELF},
89     {"enum-value",           XPATHFN_ENUM_VALUE},
90     {"false",                XPATHFN_FALSE},
91     {"floor",                XPATHFN_FLOOR},
92     {"id",                   XPATHFN_ID},
93     {"lang",                 XPATHFN_LANG},
94     {"last",                 XPATHFN_LAST},
95     {"local-name",           XPATHFN_LOCAL_NAME},
96     {"name",                 XPATHFN_NAME},
97     {"namespace-uri",        XPATHFN_NAMESPACE_URI},
98     {"normalize-space",      XPATHFN_NORMALIZE_SPACE},
99     {"node",                 XPATHFN_NODE},
100     {"not",                  XPATHFN_NOT},
101     {"number",               XPATHFN_NUMBER},
102     {"position",             XPATHFN_POSITION},
103     {"processing-instructions", XPATHFN_PROCESSING_INSTRUCTIONS},
104     {"re-match",             XPATHFN_RE_MATCH},
105     {"round",                XPATHFN_ROUND},
106     {"starts-with",          XPATHFN_STARTS_WITH},
107     {"string",               XPATHFN_STRING},
108     {"substring",            XPATHFN_SUBSTRING},
109     {"substring-after",      XPATHFN_SUBSTRING_AFTER},
110     {"substring-before",     XPATHFN_SUBSTRING_BEFORE},
111     {"sum",                  XPATHFN_SUM},
112     {"text",                 XPATHFN_TEXT},
113     {"translate",            XPATHFN_TRANSLATE},
114     {"true",                 XPATHFN_TRUE},
115     {NULL,                  -1}
116 };
117 
118 /*! Translate xpath function name to int code
119  */
120 int
xp_fnname_str2int(char * fnname)121 xp_fnname_str2int(char *fnname)
122 {
123     return clicon_str2int(xpath_fnname_map, fnname);
124 }
125 
126 /*! Translate xpath function code to string name
127  */
128 const char *
xp_fnname_int2str(enum clixon_xpath_function code)129 xp_fnname_int2str(enum clixon_xpath_function code)
130 {
131     return clicon_int2str(xpath_fnname_map, code);
132 }
133 
134 int
xp_function_current(xp_ctx * xc0,struct xpath_tree * xs,cvec * nsc,int localonly,xp_ctx ** xrp)135 xp_function_current(xp_ctx            *xc0,
136 		    struct xpath_tree *xs,
137 		    cvec              *nsc,
138 		    int                localonly,
139 		    xp_ctx           **xrp)
140 {
141     int         retval = -1;
142     cxobj     **vec = NULL;
143     int         veclen = 0;
144     xp_ctx     *xc = NULL;
145 
146     if ((xc = ctx_dup(xc0)) == NULL)
147 	goto done;
148     if (cxvec_append(xc->xc_initial, &vec, &veclen) < 0)
149 	goto done;
150     ctx_nodeset_replace(xc, vec, veclen);
151     *xrp = xc;
152     xc = NULL;
153     retval = 0;
154  done:
155     if (xc)
156 	ctx_free(xc);
157     return retval;
158 }
159 
160 int
xp_function_deref(xp_ctx * xc0,struct xpath_tree * xs,cvec * nsc,int localonly,xp_ctx ** xrp)161 xp_function_deref(xp_ctx            *xc0,
162 		  struct xpath_tree *xs,
163 		  cvec              *nsc,
164 		  int                localonly,
165 		  xp_ctx           **xrp)
166 {
167     int         retval = -1;
168     xp_ctx     *xc = NULL;
169     int         i;
170     cxobj     **vec = NULL;
171     int         veclen = 0;
172     cxobj      *xv;
173     cxobj      *xref;
174     yang_stmt  *ys;
175     yang_stmt  *yt;
176     yang_stmt  *ypath;
177     char       *path;
178 
179     /* Create new xc */
180     if ((xc = ctx_dup(xc0)) == NULL)
181 	goto done;
182     for (i=0; i<xc->xc_size; i++){
183 	xv = xc->xc_nodeset[i];
184 	if ((ys = xml_spec(xv)) == NULL)
185 	    continue;
186 	/* Get base type yc */
187 	if (yang_type_get(ys, NULL, &yt, NULL, NULL, NULL, NULL, NULL) < 0)
188 	    goto done;
189 	if (strcmp(yang_argument_get(yt), "leafref") == 0){
190 	    if ((ypath = yang_find(yt, Y_PATH, NULL)) != NULL){
191 		path = yang_argument_get(ypath);
192 		if ((xref = xpath_first(xv, nsc, "%s", path)) != NULL)
193 		    if (cxvec_append(xref, &vec, &veclen) < 0)
194 			goto done;
195 	    }
196 	    ctx_nodeset_replace(xc, vec, veclen);
197 	}
198 	else if (strcmp(yang_argument_get(yt), "identityref") == 0){
199 	}
200     }
201     *xrp = xc;
202     xc = NULL;
203     retval = 0;
204  done:
205     if (xc)
206 	ctx_free(xc);
207     return retval;
208 }
209 
210 /*! Helper function for derived-from(-and-self) - eval one node
211  * @param[in]  nsc  XML Namespace context
212  * @param[in]  self If set, implements derived_from_or_self
213  * @retval     1    OK and match
214  * @retval     0    OK but not match
215  * @retval    -1    Error
216  */
217 static int
derived_from_one(char * baseidentity,cvec * nsc,cxobj * xleaf,int self)218 derived_from_one(char  *baseidentity,
219 		 cvec  *nsc,
220 		 cxobj *xleaf,
221 		 int    self)
222 {
223     int        retval = -1;
224     yang_stmt *yleaf;
225     yang_stmt *ytype;
226     yang_stmt *ybaseid;
227     yang_stmt *ymod;
228     cvec      *idrefvec; /* Derived identityref list: (module:id)**/
229     char      *node = NULL;
230     char      *prefix = NULL;
231     char      *id = NULL;
232     cbuf      *cb = NULL;
233     char      *baseid = NULL;
234 
235     /* Split baseidentity to get its id (w/o prefix) */
236     if (nodeid_split(baseidentity, NULL, &baseid) < 0)
237 	goto done;
238     if ((yleaf = xml_spec(xleaf)) == NULL)
239 	goto nomatch;
240     if (yang_keyword_get(yleaf) != Y_LEAF && yang_keyword_get(yleaf) != Y_LEAF_LIST)
241     	goto nomatch;
242     /* Node is of type identityref */
243     if (yang_type_get(yleaf, NULL, &ytype, NULL, NULL, NULL, NULL, NULL) < 0)
244 	goto done;
245     if (ytype == NULL || strcmp(yang_argument_get(ytype), "identityref"))
246     	goto nomatch;
247     /* Find if the derivation chain is: identity ->...-> ytype
248      * Example:
249      * identity is ex:ethernet
250      * xleaf <type>fast-ethernet</type>
251      * yleaf type identityref{base interface-type;}
252      */
253     /* Just get the object corresponding to the base identity */
254     if ((ybaseid = yang_find_identity_nsc(ys_spec(yleaf), baseidentity, nsc)) == NULL)
255     	goto nomatch;
256     /* Get its list of derived identities  */
257     idrefvec = yang_cvec_get(ybaseid);
258     /* Get and split the leaf id reference */
259     if ((node = xml_body(xleaf)) == NULL) /* It may not be empty */
260     	goto nomatch;
261     if (nodeid_split(node, &prefix, &id) < 0)
262 	goto done;
263     /* Get its module (prefixes are not used here) */
264     if (prefix == NULL)
265 	ymod = ys_module(yleaf);
266     else{ /* from prefix to name */
267 #if 1 /* IDENTITYREF_KLUDGE  */
268 	ymod = yang_find_module_by_prefix_yspec(ys_spec(yleaf), prefix);
269 #endif
270     }
271     if (ymod == NULL)
272     	goto nomatch;
273     /* self special case, ie that the xleaf has a ref to itself */
274     if (self &&
275 	ymod == ys_module(ybaseid) &&
276 	strcmp(baseid, id) == 0){
277 	; /* match */
278     }
279     else {
280 	/* Allocate cbuf */
281 	if ((cb = cbuf_new()) == NULL){
282 	    clicon_err(OE_UNIX, errno, "cbuf_new");
283 	    goto done;
284 	}
285 	cprintf(cb, "%s:%s", yang_argument_get(ymod), id);
286 	if (cvec_find(idrefvec, cbuf_get(cb)) == NULL)
287 	    goto nomatch;
288     }
289     retval = 1;
290  done:
291     if (baseid)
292 	free(baseid);
293     if (cb)
294 	cbuf_free(cb);
295     if (id)
296 	free(id);
297     if (prefix)
298 	free(prefix);
299     return retval;
300  nomatch:
301     retval = 0;
302     goto done;
303 }
304 
305 /*! Eval xpath function derived-from(-and-self)
306  * @param[in]  xc   Incoming context
307  * @param[in]  xs   XPATH node tree
308  * @param[in]  nsc  XML Namespace context
309  * @param[in]  localonly Skip prefix and namespace tests (non-standard)
310  * @param[in]  self If set, implements derived_from_or_self
311  * @param[out] xrp  Resulting context
312  * @retval     0    OK
313  * @retval    -1    Error
314  * @see rfc7950 10.4.1
315  *  Returns "true" if any node in the argument "nodes" is a node of type "identityref" and its
316  *  value is an identity that is derived from (see Section 7.18.2) the identity "identity"
317  * boolean derived-from(node-set nodes, string identity)
318  * @see validate_identityref for similar code other usage
319  */
320 int
xp_function_derived_from(xp_ctx * xc,struct xpath_tree * xs,cvec * nsc,int localonly,int self,xp_ctx ** xrp)321 xp_function_derived_from(xp_ctx            *xc,
322 			 struct xpath_tree *xs,
323 			 cvec              *nsc,
324 			 int                localonly,
325 			 int                self,
326 			 xp_ctx           **xrp)
327 {
328     int        retval = -1;
329     xp_ctx    *xr0 = NULL;
330     xp_ctx    *xr1 = NULL;
331     xp_ctx    *xr = NULL;
332     char      *identity = NULL;
333     int        i;
334     int        ret = 0;
335 
336     if (xs == NULL || xs->xs_c0 == NULL || xs->xs_c1 == NULL){
337 	clicon_err(OE_XML, EINVAL, "derived-from expects but did not get two arguments");
338 	goto done;
339     }
340     /* contains two arguments in xs: boolean derived-from(node-set, string) */
341     /* This evolves to a set of (identityref) nodes */
342     if (xp_eval(xc, xs->xs_c0, nsc, localonly, &xr0) < 0)
343 	goto done;
344     if (xr0->xc_type != XT_NODESET)
345 	goto done;
346     /* This evolves to a string identity */
347     if (xp_eval(xc, xs->xs_c1, nsc, localonly, &xr1) < 0)
348 	goto done;
349     if (ctx2string(xr1, &identity) < 0)
350 	goto done;
351     /* Allocate a return struct of type boolean */
352     if ((xr = malloc(sizeof(*xr))) == NULL){
353 	clicon_err(OE_UNIX, errno, "malloc");
354 	goto done;
355     }
356     memset(xr, 0, sizeof(*xr));
357     xr->xc_type = XT_BOOL;
358     /* ANY node is an identityref and its value an identity that is derived ... */
359     for (i=0; i<xr0->xc_size; i++){
360 	if ((ret = derived_from_one(identity, nsc, xr0->xc_nodeset[i], self)) < 0)
361 	    goto done;
362 	if (ret == 1)
363 	    break;
364     }
365     xr->xc_bool = ret;
366     *xrp = xr;
367     xr = NULL;
368     retval = 0;
369  done:
370     if (xr0)
371 	ctx_free(xr0);
372     if (xr1)
373 	ctx_free(xr1);
374     if (identity)
375 	free(identity);
376     return retval;
377 }
378 
379 /*! The count function returns the number of nodes in the argument node-set.
380  *
381  * Signature: number count(node-set)
382  */
383 int
xp_function_count(xp_ctx * xc0,struct xpath_tree * xs,cvec * nsc,int localonly,xp_ctx ** xrp)384 xp_function_count(xp_ctx            *xc0,
385 		  struct xpath_tree *xs,
386 		  cvec              *nsc,
387 		  int                localonly,
388 		  xp_ctx           **xrp)
389 {
390     int         retval = -1;
391     xp_ctx     *xc = NULL;
392     xp_ctx     *xr = NULL;
393     xp_ctx     *xr0 = NULL;
394 
395     if (xs == NULL || xs->xs_c0 == NULL){
396 	clicon_err(OE_XML, EINVAL, "count expects but did not get one argument");
397 	goto done;
398     }
399     if (xp_eval(xc, xs->xs_c0, nsc, localonly, &xr0) < 0)
400 	goto done;
401     if ((xr = malloc(sizeof(*xr))) == NULL){
402 	clicon_err(OE_UNIX, errno, "malloc");
403 	goto done;
404     }
405     memset(xr, 0, sizeof(*xr));
406     xr->xc_type = XT_NUMBER;
407     xr->xc_number = xr0->xc_number;
408     *xrp = xc;
409     retval = 0;
410  done:
411     if (xr0)
412 	ctx_free(xr0);
413     return retval;
414 }
415 
416 /*! The name function returns a string of a QName
417  *
418  * The name function returns a string containing a QName representing the expanded-name
419  * of the node in the argument node-set that is first in document order.
420  * Signature: string name(node-set?)
421  * XXX: should return expanded-name, should namespace be included?
422  */
423 int
xp_function_name(xp_ctx * xc0,struct xpath_tree * xs,cvec * nsc,int localonly,xp_ctx ** xrp)424 xp_function_name(xp_ctx            *xc0,
425 		 struct xpath_tree *xs,
426 		 cvec              *nsc,
427 		 int                localonly,
428 		 xp_ctx           **xrp)
429 {
430     int         retval = -1;
431     xp_ctx     *xc = NULL;
432     xp_ctx     *xr = NULL;
433     xp_ctx     *xr0 = NULL;
434     char       *s0 = NULL;
435     int         i;
436     cxobj      *x;
437 
438     if (xs == NULL || xs->xs_c0 == NULL){
439 	clicon_err(OE_XML, EINVAL, "not expects but did not get one argument");
440 	goto done;
441     }
442     if (xp_eval(xc, xs->xs_c0, nsc, localonly, &xr0) < 0)
443 	goto done;
444     if ((xr = malloc(sizeof(*xr))) == NULL){
445 	clicon_err(OE_UNIX, errno, "malloc");
446 	goto done;
447     }
448     memset(xr, 0, sizeof(*xr));
449     xr->xc_type = XT_STRING;
450     for (i=0; i<xr0->xc_size; i++){
451 	if ((x = xr0->xc_nodeset[i]) == NULL)
452 	    continue;
453 	if ((xr->xc_string = strdup(xml_name(x))) == NULL){
454 	    clicon_err(OE_UNIX, errno, "strdup");
455 	    goto done;
456 	}
457 	break;
458     }
459     *xrp = xc;
460     retval = 0;
461  done:
462     if (xr0)
463 	ctx_free(xr0);
464     if (s0)
465 	free(s0);
466     return retval;
467 }
468 
469 /*! Eval xpath function contains
470  * @param[in]  xc   Incoming context
471  * @param[in]  xs   XPATH node tree
472  * @param[in]  nsc  XML Namespace context
473  * @param[in]  localonly Skip prefix and namespace tests (non-standard)
474  * @param[out] xrp  Resulting context
475  * @retval     0    OK
476  * @retval    -1    Error
477  * @see https://www.w3.org/TR/xpath-10/#NT-FunctionName 4.2 String Functions
478  */
479 int
xp_function_contains(xp_ctx * xc,struct xpath_tree * xs,cvec * nsc,int localonly,xp_ctx ** xrp)480 xp_function_contains(xp_ctx            *xc,
481 		     struct xpath_tree *xs,
482 		     cvec              *nsc,
483 		     int                localonly,
484 		     xp_ctx           **xrp)
485 {
486     int                retval = -1;
487     xp_ctx            *xr0 = NULL;
488     xp_ctx            *xr1 = NULL;
489     xp_ctx            *xr = NULL;
490     char              *s0 = NULL;
491     char              *s1 = NULL;
492 
493     if (xs == NULL || xs->xs_c0 == NULL || xs->xs_c1 == NULL){
494 	clicon_err(OE_XML, EINVAL, "contains expects but did not get two arguments");
495 	goto done;
496     }
497     /* contains two arguments in xs: boolean contains(string, string) */
498     if (xp_eval(xc, xs->xs_c0, nsc, localonly, &xr0) < 0)
499 	goto done;
500     if (ctx2string(xr0, &s0) < 0)
501 	goto done;
502     if (xp_eval(xc, xs->xs_c1, nsc, localonly, &xr1) < 0)
503 	goto done;
504     if (ctx2string(xr1, &s1) < 0)
505 	goto done;
506     if ((xr = malloc(sizeof(*xr))) == NULL){
507 	clicon_err(OE_UNIX, errno, "malloc");
508 	goto done;
509     }
510     memset(xr, 0, sizeof(*xr));
511     xr->xc_type = XT_BOOL;
512     xr->xc_bool = (strstr(s0, s1) != NULL);
513     *xrp = xr;
514     xr = NULL;
515     retval = 0;
516  done:
517     if (xr0)
518 	ctx_free(xr0);
519     if (xr1)
520 	ctx_free(xr1);
521     if (s0)
522 	free(s0);
523     if (s1)
524 	free(s1);
525     return retval;
526 }
527 
528 /*! The not function returns true if its argument is false, and false otherwise.
529  *
530  * Signatire: boolean contains(boolean)
531  */
532 int
xp_function_not(xp_ctx * xc0,struct xpath_tree * xs,cvec * nsc,int localonly,xp_ctx ** xrp)533 xp_function_not(xp_ctx            *xc0,
534 		struct xpath_tree *xs,
535 		cvec              *nsc,
536 		int                localonly,
537 		xp_ctx           **xrp)
538 {
539     int         retval = -1;
540     xp_ctx     *xc = NULL;
541     xp_ctx     *xr = NULL;
542     xp_ctx     *xr0 = NULL;
543     int         bool;
544 
545     if (xs == NULL || xs->xs_c0 == NULL){
546 	clicon_err(OE_XML, EINVAL, "not expects but did not get one argument");
547 	goto done;
548     }
549     if (xp_eval(xc, xs->xs_c0, nsc, localonly, &xr0) < 0)
550 	goto done;
551     bool = ctx2boolean(xr0);
552     if ((xr = malloc(sizeof(*xr))) == NULL){
553 	clicon_err(OE_UNIX, errno, "malloc");
554 	goto done;
555     }
556     memset(xr, 0, sizeof(*xr));
557     xr->xc_type = XT_BOOL;
558     xr->xc_bool = !bool;
559     *xrp = xc;
560     retval = 0;
561  done:
562     if (xr0)
563 	ctx_free(xr0);
564     return retval;
565 }
566