The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
jpath.c
Go to the documentation of this file.
1/*
2 * This program is is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: a69f0df3752c77e8952ce2c514a98ecfc5961a4d $
19 * @file jpath.c
20 * @brief Implements the evaluation and parsing functions for the FreeRADIUS version of jpath.
21 *
22 * @author Arran Cudbard-Bell
23 *
24 * @copyright 2015 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
25 * @copyright 2015 Network RADIUS SAS (legal@networkradius.com)
26 * @copyright 2015 The FreeRADIUS Server Project
27 */
28#include <freeradius-devel/util/debug.h>
29#include "base.h"
30
31#define SELECTOR_INDEX_UNSET INT32_MAX
32
33typedef enum {
34 JPATH_OPERAND_LITERAL = 0, //!< Operand is a literal
35 JPATH_OPERAND_JPATH //!< Operand is a jpath sequence
37
38typedef enum {
40 JPATH_SELECTOR_ROOT, //!< Jump to the root of the document.
41 JPATH_SELECTOR_CURRENT, //!< Continue at the current node in the document.
42 JPATH_SELECTOR_WILDCARD, //!< Wildcard, operate over all array indices, or fields
43 JPATH_SELECTOR_FIELD, //!< A field, current JSON node must be an object.
44 JPATH_SELECTOR_INDEX, //!< Array index, current JSON node must be an array.
45 JPATH_SELECTOR_SLICE, //!< Array slice, current JSON node must be an array.
46 JPATH_SELECTOR_FILTER_EXPRESSION, //!< Complex filter expression (NYI).
47 JPATH_SELECTOR_EXPRESSION, //!< Expression (NYI).
48 JPATH_SELECTOR_RECURSIVE_DESCENT //!< Descend through the JSON tree, looking for a node which matches
49 //!< the next one in the jpath sequence.
51
52/** Operand in a jpath expression
53 *
54 */
55typedef union {
56 char const *literal; //!< Operand is a literal (value)
57 fr_jpath_node_t *jpath; //!< Operand is a jpath expression
59
60/** A jpath expression for performing complex comparisons against field values
61 *
62 */
63typedef struct {
64 jpath_operand_t lhs; //!< LHS value.
65
66 jpath_operand_t rhs; //!< RHS value.
68
69/** Selects a subset of JSON child nodes
70 *
71 */
74 union {
75 char const *field; //!< JSON object name
76 int32_t slice[3]; //!< Array index, or slice, or step
77
78 jpath_expr_t expr; //!< Expression
79 };
80 jpath_type_t type; //!< Type of the Jpath node.
82};
83
84/** Node in a jpath selector sequence
85 *
86 */
88 jpath_selector_t *selector; //!< Jpath selector head (there may be multiple).
89 fr_jpath_node_t *next; //!< Next in the jpath chain.
90};
91
92static char const escape_chars[] = "[],*.:()?";
93
94
95/** Escapes special chars
96 *
97 * Escapes any characters that may have special meaning within a jpath expression
98 *
99 * @param request Current request (unused may be NULL).
100 * @param out Where to write the escaped string.
101 * @param outlen Length of the output buffer.
102 * @param in data to escape.
103 * @param arg uctx data, not used.
104 * @return the number of chars written to out.
105 */
106ssize_t fr_jpath_escape_func(UNUSED request_t *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
107{
108 char const *p = in;
109 char *q = out, *end = out + outlen;
110
111 if (outlen == 0) return 0;
112
113 while (*p && (q < end)) {
114 if (memchr(escape_chars, *p, sizeof(escape_chars))) {
115 if ((q + 1) >= end) break;
116 q[0] = '\\';
117 q[1] = *p++;
118 q += 2;
119 continue;
120 }
121 *q++ = *p++;
122 }
123 *q = '\0';
124
125 return q - out;
126}
127
128/** Recursive function for jpath_expr_evaluate
129 *
130 * @param[in,out] ctx to allocate fr_value_box_t in.
131 * @param[out] tail Where to write fr_value_box_t.
132 * @param[in] dst_type FreeRADIUS type to convert to.
133 * @param[in] dst_enumv Enumeration values to allow string to integer conversions.
134 * @param[in] object current node in the json tree.
135 * @param[in] jpath to evaluate.
136 * @return
137 * - 1 on match.
138 * - 0 on no match.
139 * - -1 on error.
140 */
141static int jpath_evaluate(TALLOC_CTX *ctx, fr_value_box_list_t *tail,
142 fr_type_t dst_type, fr_dict_attr_t const *dst_enumv,
143 json_object *object, fr_jpath_node_t const *jpath)
144{
146 fr_jpath_node_t const *node;
147 jpath_selector_t const *selector;
148 bool child_matched = false;
149 int ret = 0;
150
151 /*
152 * Iterate over the nodes, we only recurse for
153 * more complex operations.
154 */
155 for (node = jpath; node; node = node->next) switch (node->selector->type) {
157 if (!json_object_is_type(object, json_type_object)) return 0;
158 if (!json_object_object_get_ex(object, node->selector->field, &object)) return 0;
159 continue;
160
163 /*
164 * There may be multiple selectors per node
165 */
166 for (selector = node->selector; selector; selector = selector->next) switch (selector->type) {
168 {
169 struct array_list *array_obj; /* Because array_list is a global... */
170
171 fr_assert(selector->slice[0] != SELECTOR_INDEX_UNSET);
172
173 if (!json_object_is_type(object, json_type_array)) return 0;
174 array_obj = json_object_get_array(object);
175 if ((selector->slice[0] < 0) ||
176 (selector->slice[0] >= (int32_t)(array_obj->length & INT32_MAX))) continue;
177
178 ret = jpath_evaluate(ctx, tail, dst_type, dst_enumv,
179 array_obj->array[selector->slice[0]], node->next);
180 if (ret < 0) return ret;
181 if (ret == 1) child_matched = true;
182 }
183 break;
184
186 {
187 struct array_list *array_obj;
188 int32_t start, end, step, i;
189
190 if (!json_object_is_type(object, json_type_array)) return 0;
191 array_obj = json_object_get_array(object);
192
193 /*
194 * This logic may seem slightly odd, but it perfectly
195 * emulates python array slicing behaviour AFAICT
196 */
197 step = selector->slice[2];
198 if (step == SELECTOR_INDEX_UNSET) step = 1;
199
200 start = selector->slice[0];
201 if (start == SELECTOR_INDEX_UNSET) start = (step < 0) ?
202 (int32_t)((array_obj->length - 1) & INT32_MAX) : 0;
203 else if (start < 0) start = array_obj->length + start;
204
205 end = selector->slice[1];
206 if (end == SELECTOR_INDEX_UNSET) end = (step < 0) ?
207 -1 : (int32_t)((array_obj->length - 1) & INT32_MAX);
208 else if (end < 0) end = array_obj->length + end;
209
210 /*
211 * Descending
212 */
213 if (step < 0) for (i = start; (i > end) && (i >= 0); i += step) {
214 fr_assert((i >= 0) && (i < (int32_t)(array_obj->length & INT32_MAX)));
215 ret = jpath_evaluate(ctx, tail, dst_type, dst_enumv,
216 array_obj->array[i], node->next);
217 if (ret < 0) return ret;
218 if (ret == 1) child_matched = true;
219 /*
220 * Ascending
221 */
222 } else for (i = start; (i < end) && (i < (int32_t)(array_obj->length & INT32_MAX)); i += step) {
223 fr_assert((i >= 0) && (i < (int32_t)(array_obj->length & INT32_MAX)));
224 ret = jpath_evaluate(ctx, tail, dst_type, dst_enumv,
225 array_obj->array[i], node->next);
226 if (ret < 0) return ret;
227 if (ret == 1) child_matched = true;
228 }
229 }
230 break;
231
232 default:
233 fr_assert(0);
234 return -1;
235 }
236 return child_matched ? 1 : 0;
237
238 /*
239 * Iterate over fields or array indices
240 */
242 {
243 int i;
244
245 if (json_object_is_type(object, json_type_array)) {
246 struct array_list *array_obj;
247
248 array_obj = json_object_get_array(object);
249 for (i = 0; i < (int32_t)(array_obj->length & INT32_MAX); i++) {
250 ret = jpath_evaluate(ctx, tail, dst_type, dst_enumv,
251 array_obj->array[i], node->next);
252 if (ret < 0) return ret;
253 if (ret == 1) child_matched = true;
254 }
255 return child_matched ? 1 : 0;
256 } else if (json_object_is_type(object, json_type_object)) {
257 json_object_object_foreach(object, field_name, field_value) {
258#ifndef NDEBUG
259 fr_assert(field_name);
260#else
261 UNUSED_VAR(field_name);
262#endif
263 ret = jpath_evaluate(ctx, tail, dst_type, dst_enumv,
264 field_value, node->next);
265 if (ret < 0) return ret;
266 if (ret == 1) child_matched = true;
267 }
268 return child_matched ? 1 : 0;
269 } else return 0;
270 }
271
272 /*
273 * @todo Brute force it more efficiently.
274 */
276 {
277 int i;
278
279 if (json_object_is_type(object, json_type_array)) {
280 struct array_list *array_obj;
281
282 /*
283 * Descend into each element of the array
284 */
285 array_obj = json_object_get_array(object);
286 for (i = 0; i < (int32_t)(array_obj->length & INT32_MAX); i++) {
287 ret = jpath_evaluate(ctx, tail, dst_type, dst_enumv,
288 array_obj->array[i], node);
289 if (ret < 0) return ret;
290 if (ret == 1) child_matched = true;
291 }
292
293 /*
294 * On the way back up, evaluate the object's fields
295 */
296 ret = jpath_evaluate(ctx, tail, dst_type, dst_enumv,
297 object, node->next);
298 if (ret < 0) return ret;
299 if (ret == 1) child_matched = true;
300
301 return child_matched ? 1 : 0;
302 } else if (json_object_is_type(object, json_type_object)) {
303 /*
304 * Descend into each field of the object
305 */
306 json_object_object_foreach(object, field_name, field_value) {
307#ifndef NDEBUG
308 fr_assert(field_name);
309#else
310 UNUSED_VAR(field_name);
311#endif
312 ret = jpath_evaluate(ctx, tail, dst_type, dst_enumv,
313 field_value, node);
314 if (ret < 0) return ret;
315 if (ret == 1) child_matched = true;
316 }
317
318 /*
319 * On the way back up, evaluate the object's fields
320 */
321 ret = jpath_evaluate(ctx, tail, dst_type, dst_enumv,
322 object, node->next);
323 if (ret < 0) return ret;
324 if (ret == 1) child_matched = true;
325
326 return child_matched ? 1 : 0;
327 }
328
329 /*
330 * Descend down to the level of the leaf
331 *
332 * Parser guarantees that the recursive descent operator
333 * is never the last in a jpath sequence.
334 */
335 return jpath_evaluate(ctx, tail, dst_type, dst_enumv, object, node->next);
336 }
337
343 fr_assert(0);
344 return -1; /* Not yet implemented */
345 }
346
347 /*
348 * We've reached the end of the jpath sequence
349 * we now attempt conversion of the leaf to
350 * the specified value.
351 */
353 if (fr_json_object_to_value_box(value, value, object, dst_enumv, true) < 0) {
355 return -1;
356 }
357
358 if (fr_value_box_cast_in_place(value, value, dst_type, dst_enumv) < 0) {
360 return -1;
361 }
362
363 fr_value_box_list_insert_tail(tail, value);
364 return 1;
365}
366
367/** Evaluate a parsed jpath expression against a json-c tree
368 *
369 * Will produce one or more fr_value_box_t structures of the desired type,
370 * or error out if the conversion between types fails.
371 *
372 * @param[in,out] ctx to allocate fr_value_box_t in.
373 * @param[out] out Where to write fr_value_box_t.
374 * @param[in] dst_type FreeRADIUS type to convert to.
375 * @param[in] dst_enumv Enumeration values to allow string to integer conversions.
376 * @param[in] root of the json-c tree.
377 * @param[in] jpath to evaluate.
378 * @return
379 * - 1 on match.
380 * - 0 on no match.
381 * - -1 on error.
382 */
383int fr_jpath_evaluate_leaf(TALLOC_CTX *ctx, fr_value_box_list_t *out,
384 fr_type_t dst_type, fr_dict_attr_t const *dst_enumv,
385 json_object *root, fr_jpath_node_t const *jpath)
386{
387 if (!root) return -1;
388
389 switch (jpath->selector->type) {
392 break;
393
394 default:
395 fr_assert(0);
396 return -1;
397 }
398
399 return jpath_evaluate(ctx, out, dst_type, dst_enumv, root, jpath->next);
400}
401
402/** Print a node list to a string for debugging
403 *
404 * Will not be identical to the original parsed string, but should be sufficient
405 * for testing purposes.
406 *
407 * @param ctx to allocate string in.
408 * @param head of the node list.
409 * @return the string representation of the node list.
410 */
411char *fr_jpath_asprint(TALLOC_CTX *ctx, fr_jpath_node_t const *head)
412{
413 fr_jpath_node_t const *node;
414 jpath_selector_t *selector;
415 char *p;
416
417 p = talloc_zero_array(ctx, char, 1);
418
419 for (node = head; node; node = node->next) switch (node->selector->type) {
421 p = talloc_strdup_append_buffer(p, "$");
422 break;
423
425 p = talloc_strdup_append_buffer(p, "@");
426 break;
427
429 p = talloc_strdup_append_buffer(p, ".*");
430 break;
431
433 {
434 char buffer[257];
435
436 fr_jpath_escape_func(NULL, buffer, sizeof(buffer), node->selector->field, NULL);
437 p = talloc_asprintf_append_buffer(p, ".%s", buffer);
438 }
439 break;
440
441 /*
442 * Bracketed selectors can contain a mix of types
443 */
448 p = talloc_strdup_append_buffer(p, "[");
449 for (selector = node->selector; selector; selector = selector->next) switch (selector->type) {
451 p = talloc_asprintf_append_buffer(p, "%i%s", selector->slice[0], selector->next ? "," : "");
452 break;
453
455 if (selector->slice[0] == SELECTOR_INDEX_UNSET) {
456 p = talloc_strdup_append_buffer(p, ":");
457 } else {
458 p = talloc_asprintf_append_buffer(p, "%i:", selector->slice[0]);
459 }
460 if (selector->slice[1] == SELECTOR_INDEX_UNSET) {
461 p = talloc_strdup_append_buffer(p, ":");
462 } else if (selector->slice[2] == SELECTOR_INDEX_UNSET) {
463 p = talloc_asprintf_append_buffer(p, "%i", selector->slice[1]);
464 } else {
465 p = talloc_asprintf_append_buffer(p, "%i:", selector->slice[1]);
466 }
467 if (selector->slice[2] != SELECTOR_INDEX_UNSET) {
468 p = talloc_asprintf_append_buffer(p, "%i", selector->slice[2]);
469 }
470 if (selector->next) p = talloc_strdup_append_buffer(p, ",");
471 break;
472
473 default:
474 fr_assert(0); /* Not yet implemented */
475 break;
476 }
477 p = talloc_strdup_append_buffer(p, "]");
478 break;
479
481 if (node->next) switch (node->next->selector->type) {
484 p = talloc_strdup_append_buffer(p, "..");
485 break;
486
487 default:
488 p = talloc_strdup_append_buffer(p, ".");
489 break;
490 }
491 break;
492
494 fr_assert(0);
495 return NULL;
496 }
497
498 return p;
499}
500
501/** Parse a jpath filter expression, which in our case, is a FreeRADIUS condition
502 *
503 * @note this requires reworking some of the condition code to accept
504 * callbacks to retrieve virtual attributes, so is not yet implemented.
505 */
507{
508 /* selector->type = JPATH_SELECTOR_FILTER_EXPRESSION; */
509
510 fr_strerror_const("Filter expressions not yet implemented");
511 return 0;
512}
513
514/** Parse a jpath expression
515 *
516 * @note this requires reworking some of the condition code to accept
517 * callbacks to retrieve virtual attributes, so is not yet implemented.
518 */
519static ssize_t jpath_expr_parse(UNUSED jpath_selector_t *selector, UNUSED char const *in, UNUSED size_t inlen)
520{
521 /* selector->type = JPATH_SELECTOR_EXPRESSION; */
522
523 fr_strerror_const("Expressions not yet implemented");
524 return 0;
525}
526
527/** Parse index/slice notation
528 *
529 * Expects in to point to a buffer containing:
530 *
531 @verbatim
532 [<int0>:<int1>:<int2>]
533 @endverbatim
534 *
535 * Where each of the integers and its accompanying delimiter is optional.
536 *
537 * @param selector to populate with index/slice info.
538 * @param in input.
539 * @param inlen length of in.
540 * @return
541 * - > 0 on success.
542 * - <= 0 on error (* -1 to get offset error occurred at).
543 */
544static ssize_t jpath_array_parse(jpath_selector_t *selector, char const *in, size_t inlen)
545{
546 int idx = 0;
547 int32_t num;
548 char const *p, *end = in + inlen;
549 char *q;
550 ssize_t ret;
551
552 char buffer[33]; /* Max (uin32_t * 3) + 2 + 1 */
553
554 selector->type = JPATH_SELECTOR_INDEX;
555 selector->slice[0] = SELECTOR_INDEX_UNSET;
556 selector->slice[1] = SELECTOR_INDEX_UNSET;
557 selector->slice[2] = SELECTOR_INDEX_UNSET;
558
559 /*
560 * Scan forward until we find a delimiter or terminator
561 */
562 for (p = in; p < end; p++) if ((p[0] == ',') || (p[0] == ']')) break;
563 if (p == end) {
564 fr_strerror_const("Missing selector delimiter ',' or terminator ']'");
565 return -inlen;
566 }
567
568 ret = (p - in);
569 if (ret == 0) {
570 fr_strerror_const("Empty selector");
571 return 0;
572 }
573
574 if (inlen > sizeof(buffer)) { /* - 1 for ] */
575 fr_strerror_const("Selector too long");
576 return -inlen;
577 }
578
579 /*
580 * Have to use an intermediary buffer because strtol
581 * doesn't accept a terminating address via endptr.
582 */
583 memcpy(&buffer, in, p - in);
584 buffer[p - in] = '\0';
585 p = buffer;
586
587 /*
588 * Index or start
589 */
590 num = (int32_t)strtol(p, &q, 10);
591 if (q > p) switch (q[0]) {
592 default:
593 no_term:
594 fr_strerror_const("Expected num, ':' or ']'");
595 return buffer - q;
596
597 case ':': /* More integers to parse */
598 selector->slice[idx] = num;
599 break;
600
601 case '\0': /* Array index */
602 selector->slice[idx] = num;
603 return ret;
604 }
605 if (q[0] != ':') goto no_term;
606 idx++;
607 p = q + 1;
608
609 selector->type = JPATH_SELECTOR_SLICE;
610
611 /*
612 * End
613 */
614 num = (int32_t)strtol(p, &q, 10);
615 if (q > p) switch (q[0]) {
616 default:
617 goto no_term;
618
619 case ':': /* More integers to parse */
620 selector->slice[idx] = num;
621 break;
622
623 case '\0': /* Array End */
624 selector->slice[idx] = num;
625 return ret;
626 }
627 if (q[0] != ':') goto no_term;
628 idx++;
629 p = q + 1;
630
631 /*
632 * Step
633 */
634 num = (int32_t)strtol(p, &q, 10);
635 if (q[0] != '\0') {
636 fr_strerror_const("Expected num or ']'");
637 return buffer - q;
638 }
639 if (q > p) {
640 if (num == 0) {
641 fr_strerror_const("Step cannot be 0");
642 return buffer - p;
643 }
644 selector->slice[idx] = num;
645 }
646 return ret;
647}
648
649/** Parse a jpath field
650 *
651 */
652static size_t jpath_field_parse(fr_jpath_node_t *node, char const *in, size_t inlen)
653{
654 char buffer[128];
655
656 char const *p = in, *end = p + inlen;
657 char *buff_p = buffer, *buff_end = buff_p + sizeof(buffer);
658
659 /*
660 * Field name with optional selector
661 */
662 while (p < end) {
663 int clen;
664
665 if (buff_p == buff_end) {
666 name_too_big:
667 fr_strerror_const("Exceeded maximum field name length");
668 return in - p;
669 }
670
671 clen = fr_utf8_char((uint8_t const *)p, end - p);
672 if (clen == 0) {
673 fr_strerror_const("Bad UTF8 char");
674 return in - p;
675 }
676
677 /*
678 * Multibyte
679 */
680 if (clen > 1) {
681 if ((buff_p + clen) >= buff_end) goto name_too_big;
682 memcpy(buff_p, p, clen);
683 buff_p += clen;
684 p += clen;
685 continue;
686 }
687
688 switch (p[0]) { /* Normal char */
689 default:
690 *buff_p++ = *p++;
691 continue;
692
693 /*
694 * Escape sequence
695 */
696 case '\\':
697 if (++p == end) return p - in;
698
699 if (memchr(escape_chars, p[0], sizeof(escape_chars))) {
700 *buff_p++ = *p++;
701 continue;
702 }
703 *buff_p++ = '\\';
704 continue;
705
706 /*
707 * Things that mark the end of a field
708 */
709 case '[':
710 case ']': /* Not really, but it's probably not right */
711 case '.':
712 break;
713 }
714 break;
715 }
716
717 if (buff_p == buffer) {
718 fr_strerror_const("Empty field specifier");
719 return 0;
720 }
721 node->selector->field = talloc_bstrndup(node, buffer, buff_p - buffer);
723
724 return p - in;
725}
726
727/** parse a jpath selector
728 *
729 */
730static size_t jpath_selector_parse(fr_jpath_node_t *node, char const *in, size_t inlen)
731{
732 ssize_t slen;
733 char const *p = in, *end = p + inlen;
734
735 jpath_selector_t **stail;
736 jpath_selector_t *selector;
737
738 stail = &node->selector->next;
739 selector = node->selector;
740
741 if (++p == end) { /* Skip past [ */
742 missing_terminator:
743 fr_strerror_const("Missing selector terminator ']'");
744 return in - p;
745 }
746
747 while (p < end) {
748 /*
749 * What kind of selector is it?
750 */
751 switch (p[0]) {
752 case '?': /* Filter expression */
753 slen = jpath_filter_expr_parse(selector, p, end - p);
754 break;
755
756 case '(': /* Expression */
757 slen = jpath_expr_parse(selector, p, end - p);
758 break;
759
760 default: /* Index or slice */
761 slen = jpath_array_parse(selector, p, end - p);
762 break;
763 }
764 if (slen <= 0) {
765 p += -slen;
766 return in - p; /* Error */
767 }
768 p += slen;
769 if (p == end) goto missing_terminator;
770 fr_assert(p < end);
771
772 /*
773 * Things that terminate a selector
774 *
775 * - ']' a selector terminator.
776 * - ',' another selector.
777 */
778 if (p[0] == ']') break; /* We're done */
779 if (p[0] != ',') { /* There's more... */
780 fr_strerror_printf("Expected selector delimiter ','"
781 "or selector terminator ']'");
782 return in - p; /* Error */
783 }
784 if (++p == end) goto missing_terminator;
785
786 /*
787 * Link in an additional selector
788 */
789 *stail = selector = talloc_zero(node, jpath_selector_t);
790 if (!selector) {
791 fr_strerror_const("Failed allocating selector");
792 return in - p;
793 }
794 stail = &selector->next;
795 }
796 if (p[0] != ']') goto missing_terminator;
797
798 p++; /* Skip past ] */
799
800 return p - in;
801}
802
803/** Parse a jpath string
804 *
805 * Based on the syntax described here http://goessner.net/articles/JsonPath/
806 *
807 * Implements parser for everything except unions and expressions
808 *
809 * @return
810 * - > 0 on success.
811 * - <= 0 on error (* -1 to get offset error occurred at).
812 */
813ssize_t fr_jpath_parse(TALLOC_CTX *ctx, fr_jpath_node_t **head, char const *in, size_t inlen)
814{
815 TALLOC_CTX *tail_ctx = ctx;
816
817 ssize_t slen;
818 fr_jpath_node_t *node, **tail = head;
819
820 char const *p = in, *end = p + inlen;
821
822 *head = NULL;
823
824#define NODE_NEW(_node) \
825do { \
826 tail_ctx = *tail = (_node) = talloc_zero(tail_ctx, fr_jpath_node_t); \
827 (_node)->selector = talloc_zero((_node), jpath_selector_t); \
828 tail = &(_node)->next; \
829} while (0)
830
831 if (inlen < 1) {
832 bad_start:
833 fr_strerror_const("Expected root specifier '$', or current node specifier '@'");
834 return 0;
835 }
836
837 /*
838 * Start of the jpath expression
839 *
840 * - '$' JSON root node specifier
841 * - '@' Current node specifier
842 */
843 switch (p[0]) {
844 case '$':
845 NODE_NEW(node);
847 p++;
848 break;
849
850 case '@':
851 NODE_NEW(node);
853 p++;
854 break;
855
856 default:
857 goto bad_start;
858 }
859
860 /*
861 * Valid successions of '$' or '@'
862 *
863 * - '[' Start of a selector
864 * - '.' Start of a field specifier
865 */
866 while (p < end) {
867 NODE_NEW(node);
868
869 switch (p[0]) {
870 case '.':
871 if (++p == end) {
872 fr_strerror_printf("Expected recursive descent '..' "
873 "wildcard '*' or field specifier");
874 error:
875 TALLOC_FREE(*head);
876 return in - p;
877 }
878
879 /*
880 * Valid successions of '.'
881 *
882 * - '.' recursive descent.
883 * - '*' wildcard.
884 * - <name> (fieldname).
885 *
886 * This should probably be in its own function, but never mind...
887 */
888 switch (p[0]) {
889 case '.':
890 if ((p != end) && (p[1] == '.')) {
891 fr_strerror_printf("Recursive descent must not be "
892 "followed by child delimiter '.'");
893 p++;
894 goto error;
895 }
897
898 if ((p + 1) == end) {
899 fr_strerror_const("Path may not end in recursive descent");
900 goto error;
901 }
902
903 /*
904 * If and only if, the next char is the beginning
905 * of a selector, advance the pointer.
906 *
907 * Otherwise we leave it pointing to the second '.'
908 * allowing .* and .<field>
909 */
910 if (p[1] == '[') p++;
911 continue;
912
913 case '*':
915 p++;
916 continue;
917
918 default:
919 /*
920 * Field specifier is the only other valid possibility
921 */
922 slen = jpath_field_parse(node, p, (end - p));
923 if (slen <= 0) {
924 p += -(slen);
925 goto error;
926 }
927 p += slen;
928 if (p == end) return p - in; /* The end of string! */
929 fr_assert(p < end);
930 }
931 break;
932
933 case '[':
934 slen = jpath_selector_parse(node, p, (end - p));
935 if (slen <= 0) {
936 p += -(slen);
937 goto error;
938 }
939 p += slen;
940 if (p == end) return p - in; /* The end of string! */
941 fr_assert(p < end);
942 break;
943
944 default:
945 fr_strerror_const("Expected field specifier '.' or selector '['");
946 goto error;
947 }
948 }
949
950 return p - in;
951}
952
static int const char char buffer[256]
Definition acutest.h:576
#define UNUSED_VAR(_x)
Definition build.h:303
#define UNUSED
Definition build.h:315
#define MEM(x)
Definition debug.h:36
static fr_slen_t in
Definition dict.h:824
Test enumeration values.
Definition dict_test.h:92
char const * literal
Operand is a literal (value)
Definition jpath.c:56
jpath_operand_type
Definition jpath.c:33
@ JPATH_OPERAND_JPATH
Operand is a jpath sequence.
Definition jpath.c:35
@ JPATH_OPERAND_LITERAL
Operand is a literal.
Definition jpath.c:34
jpath_operand_t rhs
RHS value.
Definition jpath.c:66
fr_jpath_node_t * next
Next in the jpath chain.
Definition jpath.c:89
jpath_operand_t lhs
LHS value.
Definition jpath.c:64
static char const escape_chars[]
Definition jpath.c:92
char * fr_jpath_asprint(TALLOC_CTX *ctx, fr_jpath_node_t const *head)
Print a node list to a string for debugging.
Definition jpath.c:411
fr_jpath_node_t * jpath
Operand is a jpath expression.
Definition jpath.c:57
#define SELECTOR_INDEX_UNSET
Definition jpath.c:31
jpath_selector_t * selector
Jpath selector head (there may be multiple).
Definition jpath.c:88
jpath_type_t
Definition jpath.c:38
@ JPATH_SELECTOR_EXPRESSION
Expression (NYI).
Definition jpath.c:47
@ JPATH_SELECTOR_CURRENT
Continue at the current node in the document.
Definition jpath.c:41
@ JPATH_SELECTOR_INDEX
Array index, current JSON node must be an array.
Definition jpath.c:44
@ JPATH_SELECTOR_SLICE
Array slice, current JSON node must be an array.
Definition jpath.c:45
@ JPATH_SELECTOR_FIELD
A field, current JSON node must be an object.
Definition jpath.c:43
@ JPATH_SELECTOR_INVALID
Definition jpath.c:39
@ JPATH_SELECTOR_RECURSIVE_DESCENT
Descend through the JSON tree, looking for a node which matches the next one in the jpath sequence.
Definition jpath.c:48
@ JPATH_SELECTOR_ROOT
Jump to the root of the document.
Definition jpath.c:40
@ JPATH_SELECTOR_FILTER_EXPRESSION
Complex filter expression (NYI).
Definition jpath.c:46
@ JPATH_SELECTOR_WILDCARD
Wildcard, operate over all array indices, or fields.
Definition jpath.c:42
int fr_jpath_evaluate_leaf(TALLOC_CTX *ctx, fr_value_box_list_t *out, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, json_object *root, fr_jpath_node_t const *jpath)
Evaluate a parsed jpath expression against a json-c tree.
Definition jpath.c:383
static size_t jpath_field_parse(fr_jpath_node_t *node, char const *in, size_t inlen)
Parse a jpath field.
Definition jpath.c:652
ssize_t fr_jpath_escape_func(UNUSED request_t *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
Escapes special chars.
Definition jpath.c:106
jpath_selector_t * next
Definition jpath.c:81
static ssize_t jpath_filter_expr_parse(UNUSED jpath_selector_t *selector, UNUSED char const *in, UNUSED size_t inlen)
Parse a jpath filter expression, which in our case, is a FreeRADIUS condition.
Definition jpath.c:506
jpath_type_t type
Type of the Jpath node.
Definition jpath.c:80
static size_t jpath_selector_parse(fr_jpath_node_t *node, char const *in, size_t inlen)
parse a jpath selector
Definition jpath.c:730
static ssize_t jpath_expr_parse(UNUSED jpath_selector_t *selector, UNUSED char const *in, UNUSED size_t inlen)
Parse a jpath expression.
Definition jpath.c:519
static ssize_t jpath_array_parse(jpath_selector_t *selector, char const *in, size_t inlen)
Parse index/slice notation.
Definition jpath.c:544
ssize_t fr_jpath_parse(TALLOC_CTX *ctx, fr_jpath_node_t **head, char const *in, size_t inlen)
Parse a jpath string.
Definition jpath.c:813
#define NODE_NEW(_node)
static int jpath_evaluate(TALLOC_CTX *ctx, fr_value_box_list_t *tail, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, json_object *object, fr_jpath_node_t const *jpath)
Recursive function for jpath_expr_evaluate.
Definition jpath.c:141
Node in a jpath selector sequence.
Definition jpath.c:87
A jpath expression for performing complex comparisons against field values.
Definition jpath.c:63
Operand in a jpath expression.
Definition jpath.c:55
int fr_json_object_to_value_box(TALLOC_CTX *ctx, fr_value_box_t *out, json_object *object, fr_dict_attr_t const *enumv, bool tainted)
Convert json object to fr_value_box_t.
Definition json.c:96
talloc_free(reap)
fr_type_t
long int ssize_t
unsigned char uint8_t
size_t fr_utf8_char(uint8_t const *str, ssize_t inlen)
Checks for utf-8, taken from http://www.w3.org/International/questions/qa-forms-utf-8.
Definition print.c:39
#define fr_assert(_expr)
Definition rad_assert.h:38
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition talloc.c:564
static fr_slen_t head
Definition xlat.h:422
Master include file to access all functions and structures in the library.
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223
int fr_value_box_cast_in_place(TALLOC_CTX *ctx, fr_value_box_t *vb, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv)
Convert one type of fr_value_box_t to another in place.
Definition value.c:3572
static size_t char fr_sbuff_t size_t inlen
Definition value.h:997
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.
Definition value.h:632
static size_t char ** out
Definition value.h:997