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