All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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: b38cf59775c4234b4730c6f0103b91baacc74e0f $
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 SARL <info@networkradius.com>
26  * @copyright 2015 The FreeRADIUS Server Project
27  */
28 #include <freeradius-devel/rad_assert.h>
29 #include "json.h"
30 
31 #define SELECTOR_INDEX_UNSET INT32_MAX
32 
33 typedef enum {
34  JPATH_OPERAND_LITERAL = 0, //!< Operand is a literal
35  JPATH_OPERAND_JPATH //!< Operand is a jpath sequence
37 
38 typedef 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 indicies, 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.
50 } jpath_type_t;
51 
52 /** Operand in a jpath expression
53  *
54  */
55 typedef 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  */
63 typedef struct jpath_expr {
64  jpath_operand_t lhs; //!< LHS value.
65  jpath_operand_type lhs_type; //!< LHS type.
66 
67  jpath_operand_t rhs; //!< RHS value.
68  jpath_operand_type rhs_type; //!< RHS type.
69 
70  FR_TOKEN op; //!< Comparison operator
71 } jpath_expr_t;
72 
73 /** Selects a subset of JSON child nodes
74  *
75  */
78  union {
79  char const *field; //!< JSON object name
80  int32_t slice[3]; //!< Array index, or slice, or step
81 
82  jpath_expr_t expr; //!< Expression
83  };
84  jpath_type_t type; //!< Type of the Jpath node.
86 };
87 
88 /** Node in a jpath selector sequence
89  *
90  */
91 struct fr_jpath_node {
92  jpath_selector_t *selector; //!< Jpath selector head (there may be multiple).
93  fr_jpath_node_t *next; //!< Next in the jpath chain.
94 };
95 
96 static char const escape_chars[] = "[],*.:\\()?";
97 
98 
99 /** Escapes special chars
100  *
101  * Escapes any characters that may have special meaning within a jpath expression
102  *
103  * @param request Current request (unused may be NULL).
104  * @param out Where to write the escaped string.
105  * @param outlen Length of the output buffer.
106  * @param in data to escape.
107  * @param arg uctx data, not used.
108  * @return the number of chars written to out.
109  */
110 size_t fr_jpath_escape_func(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
111 {
112  char const *p = in;
113  char *q = out, *end = out + outlen;
114 
115  if (outlen == 0) return 0;
116 
117  while (*p && (q < end)) {
118  if (memchr(escape_chars, *p, sizeof(escape_chars))) {
119  if ((q + 1) >= end) break;
120  q[0] = '\\';
121  q[1] = *p++;
122  q += 2;
123  continue;
124  }
125  *q++ = *p++;
126  }
127  *q = '\0';
128 
129  return q - end;
130 }
131 
132 /** Recursive function for jpath_expr_evaluate
133  *
134  * @param[in,out] ctx to allocate value_data_t in.
135  * @param[out] tail Where to write value_data_t (**).
136  * @param[in] dst_type FreeRADIUS type to convert to.
137  * @param[in] dst_enumv Enumeration values to allow string to integer conversions.
138  * @param[in] object current node in the json tree.
139  * @param[in] jpath to evaluate.
140  * @return
141  * - 1 on match.
142  * - 0 on no match.
143  * - -1 on error.
144  */
145 static int jpath_evaluate(TALLOC_CTX *ctx, value_data_t ***tail,
146  PW_TYPE dst_type, fr_dict_attr_t const *dst_enumv,
147  json_object *object, fr_jpath_node_t const *jpath)
148 {
149  value_data_t *value;
150  fr_jpath_node_t const *node;
151  jpath_selector_t const *selector;
152  bool child_matched = false;
153  int ret = 0;
154 
155  /*
156  * Iterate over the nodes, we only recurse for
157  * more complex operations.
158  */
159  for (node = jpath; node; node = node->next) switch (node->selector->type) {
161  if (!fr_json_object_is_type(object, json_type_object)) return 0;
162  if (!json_object_object_get_ex(object, node->selector->field, &object)) return 0;
163  continue;
164 
167  /*
168  * There may be multiple selectors per node
169  */
170  for (selector = node->selector; selector; selector = selector->next) switch (selector->type) {
172  {
173  struct array_list *array_obj; /* Because array_list is a global... */
174 
175  rad_assert(selector->slice[0] != SELECTOR_INDEX_UNSET);
176 
177  if (!fr_json_object_is_type(object, json_type_array)) return 0;
178  array_obj = json_object_get_array(object);
179  if (selector->slice[0] >= array_obj->length) continue;
180 
181  ret = jpath_evaluate(ctx, tail, dst_type, dst_enumv,
182  array_obj->array[selector->slice[0]], node->next);
183  if (ret < 0) return ret;
184  if (ret == 1) child_matched = true;
185  }
186  break;
187 
189  {
190  struct array_list *array_obj;
191  int32_t start, end, step, i;
192 
193  if (!fr_json_object_is_type(object, json_type_array)) return 0;
194  array_obj = json_object_get_array(object);
195 
196  /*
197  * This logic may seem slightly odd, but it perfectly
198  * emulates python array slicing behaviour AFAICT
199  */
200  step = selector->slice[2];
201  if (step == SELECTOR_INDEX_UNSET) step = 1;
202 
203  start = selector->slice[0];
204  if (start == SELECTOR_INDEX_UNSET) start = (step < 0) ? array_obj->length - 1 : 0;
205  else if (start < 0) start = array_obj->length + start;
206 
207  end = selector->slice[1];
208  if (end == SELECTOR_INDEX_UNSET) end = (step < 0) ? -1 : array_obj->length - 1;
209  else if (end < 0) end = array_obj->length + end;
210 
211  /*
212  * Descending
213  */
214  if (step < 0) for (i = start; (i > end) && (i >= 0); i += step) {
215  rad_assert((i >= 0) && (i < array_obj->length));
216  ret = jpath_evaluate(ctx, tail, dst_type, dst_enumv,
217  array_obj->array[i], node->next);
218  if (ret < 0) return ret;
219  if (ret == 1) child_matched = true;
220  /*
221  * Ascending
222  */
223  } else for (i = start; (i < end) && (i < array_obj->length); i += step) {
224  rad_assert((i >= 0) && (i < array_obj->length));
225  ret = jpath_evaluate(ctx, tail, dst_type, dst_enumv,
226  array_obj->array[i], node->next);
227  if (ret < 0) return ret;
228  if (ret == 1) child_matched = true;
229  }
230  }
231  break;
232 
233  default:
234  rad_assert(0);
235  return -1;
236  }
237  return child_matched ? 1 : 0;
238 
239  /*
240  * Iterate over fields or array indices
241  */
243  {
244  int i;
245 
246  if (fr_json_object_is_type(object, json_type_array)) {
247  struct array_list *array_obj;
248 
249  array_obj = json_object_get_array(object);
250  for (i = 0; i < array_obj->length; i++) {
251  ret = jpath_evaluate(ctx, tail, dst_type, dst_enumv,
252  array_obj->array[i], node->next);
253  if (ret < 0) return ret;
254  if (ret == 1) child_matched = true;
255  }
256  return child_matched ? 1 : 0;
257  } else if (fr_json_object_is_type(object, json_type_object)) {
258  json_object_object_foreach(object, field_name, field_value) {
259 #ifndef NDEBUG
260  rad_assert(field_name);
261 #else
262  UNUSED_VAR(field_name);
263 #endif
264  ret = jpath_evaluate(ctx, tail, dst_type, dst_enumv,
265  field_value, node->next);
266  if (ret < 0) return ret;
267  if (ret == 1) child_matched = true;
268  }
269  return child_matched ? 1 : 0;
270  } else return 0;
271  }
272 
273  /*
274  * @todo Brute force it more efficiently.
275  */
277  {
278  int i;
279 
280  if (fr_json_object_is_type(object, json_type_array)) {
281  struct array_list *array_obj;
282 
283  /*
284  * Descend into each element of the array
285  */
286  array_obj = json_object_get_array(object);
287  for (i = 0; i < array_obj->length; i++) {
288  ret = jpath_evaluate(ctx, tail, dst_type, dst_enumv,
289  array_obj->array[i], node);
290  if (ret < 0) return ret;
291  if (ret == 1) child_matched = true;
292  }
293 
294  /*
295  * On the way back up, evaluate the object's fields
296  */
297  ret = jpath_evaluate(ctx, tail, dst_type, dst_enumv,
298  object, node->next);
299  if (ret < 0) return ret;
300  if (ret == 1) child_matched = true;
301 
302  return child_matched ? 1 : 0;
303  } else if (fr_json_object_is_type(object, json_type_object)) {
304  /*
305  * Descend into each field of the object
306  */
307  json_object_object_foreach(object, field_name, field_value) {
308 #ifndef NDEBUG
309  rad_assert(field_name);
310 #else
311  UNUSED_VAR(field_name);
312 #endif
313  ret = jpath_evaluate(ctx, tail, dst_type, dst_enumv,
314  field_value, node);
315  if (ret < 0) return ret;
316  if (ret == 1) child_matched = true;
317  }
318 
319  /*
320  * On the way back up, evaluate the object's fields
321  */
322  ret = jpath_evaluate(ctx, tail, dst_type, dst_enumv,
323  object, node->next);
324  if (ret < 0) return ret;
325  if (ret == 1) child_matched = true;
326 
327  return child_matched ? 1 : 0;
328  }
329 
330  /*
331  * Descend down to the level of the leaf
332  *
333  * Parser guarantees that the recursive descent operator
334  * is never the last in a jpath sequence.
335  */
336  return jpath_evaluate(ctx, tail, dst_type, dst_enumv, object, node->next);
337  }
338 
342  case JPATH_SELECTOR_ROOT:
344  rad_assert(0);
345  return -1; /* Not yet implemented */
346  }
347 
348  /*
349  * We've reached the end of the jpath sequence
350  * we now attempt conversion of the leaf to
351  * the specified value.
352  */
353  value = talloc_zero(ctx, value_data_t);
354  if (fr_json_object_to_value_data(value, value, object, dst_type, dst_enumv) < 0) {
355  talloc_free(value);
356  return -1;
357  }
358  **tail = value;
359  *tail = &(**tail)->next;
360  return 1;
361 }
362 
363 /** Evaluate a parsed jpath expression against a json-c tree
364  *
365  * Will produce one or more value_data_t structures of the desired type,
366  * or error out if the conversion between types fails.
367  *
368  * @param[in,out] ctx to allocate value_data_t in.
369  * @param[out] out Where to write value_data_t.
370  * @param[in] dst_type FreeRADIUS type to convert to.
371  * @param[in] dst_enumv Enumeration values to allow string to integer conversions.
372  * @param[in] root of the json-c tree.
373  * @param[in] jpath to evaluate.
374  * @return
375  * - 1 on match.
376  * - 0 on no match.
377  * - -1 on error.
378  */
379 int fr_jpath_evaluate_leaf(TALLOC_CTX *ctx, value_data_t **out,
380  PW_TYPE dst_type, fr_dict_attr_t const *dst_enumv,
381  json_object *root, fr_jpath_node_t const *jpath)
382 {
383  value_data_t **tail = out;
384 
385  *tail = NULL;
386 
387  if (!root) return -1;
388 
389  switch (jpath->selector->type) {
390  case JPATH_SELECTOR_ROOT:
392  break;
393 
394  default:
395  rad_assert(0);
396  return -1;
397  }
398 
399  return jpath_evaluate(ctx, &tail, 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  */
411 char *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) {
420  case JPATH_SELECTOR_ROOT:
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  rad_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  rad_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  */
506 static ssize_t jpath_filter_expr_parse(jpath_selector_t *selector, UNUSED char const *in, UNUSED size_t inlen)
507 {
508  rad_assert(selector);
509 
510  /* selector->type = JPATH_SELECTOR_FILTER_EXPRESSION; */
511 
512  fr_strerror_printf("Filter expressions not yet implemented");
513  return 0;
514 }
515 
516 /** Parse a jpath expression
517  *
518  * @note this requires reworking some of the condition code to accept
519  * callbacks to retrieve virtual attributes, so is not yet implemented.
520  */
521 static ssize_t jpath_expr_parse(jpath_selector_t *selector, UNUSED char const *in, UNUSED size_t inlen)
522 {
523  rad_assert(selector);
524 
525  /* selector->type = JPATH_SELECTOR_EXPRESSION; */
526 
527  fr_strerror_printf("Expressions not yet implemented");
528  return 0;
529 }
530 
531 /** Parse index/slice notation
532  *
533  * Expects in to point to a buffer containing:
534  *
535  @verbatim
536  [<int0>:<int1>:<int2>]
537  @endverbatim
538  *
539  * Where each of the integers and its accompanying delimiter is optional.
540  *
541  * @param selector to populate with index/slice info.
542  * @param in input.
543  * @param inlen length of in.
544  * @return
545  * - > 0 on success.
546  * - <= 0 on error (* -1 to get offset error ocurred at).
547  */
548 static ssize_t jpath_array_parse(jpath_selector_t *selector, char const *in, size_t inlen)
549 {
550  int idx = 0;
551  int32_t num;
552  char const *p, *end = in + inlen;
553  char *q;
554  ssize_t ret;
555 
556  char buffer[33]; /* Max (uin32_t * 3) + 2 + 1 */
557 
558  selector->type = JPATH_SELECTOR_INDEX;
559  selector->slice[0] = SELECTOR_INDEX_UNSET;
560  selector->slice[1] = SELECTOR_INDEX_UNSET;
561  selector->slice[2] = SELECTOR_INDEX_UNSET;
562 
563  /*
564  * Scan forward until we find a delimiter or terminator
565  */
566  for (p = in; p < end; p++) if ((p[0] == ',') || (p[0] == ']')) break;
567  if (p == end) {
568  fr_strerror_printf("Missing selector delimiter ',' or terminator ']'");
569  return -inlen;
570  }
571 
572  ret = (p - in);
573  if (ret == 0) {
574  fr_strerror_printf("Empty selector");
575  return 0;
576  }
577 
578  if (inlen > sizeof(buffer)) { /* - 1 for ] */
579  fr_strerror_printf("Selector too long");
580  return -inlen;
581  }
582 
583  /*
584  * Have to use an intermediary buffer because strtol
585  * doesn't accept a terminating address via endptr.
586  */
587  memcpy(&buffer, in, p - in);
588  buffer[p - in] = '\0';
589  p = buffer;
590 
591  /*
592  * Index or start
593  */
594  num = (int32_t)strtol(p, &q, 10);
595  if (q > p) switch (q[0]) {
596  default:
597  no_term:
598  fr_strerror_printf("Expected num, ':' or ']'");
599  return buffer - q;
600 
601  case ':': /* More integers to parse */
602  selector->slice[idx] = num;
603  break;
604 
605  case '\0': /* Array index */
606  selector->slice[idx] = num;
607  return ret;
608  }
609  if (q[0] != ':') goto no_term;
610  idx++;
611  p = q + 1;
612 
613  selector->type = JPATH_SELECTOR_SLICE;
614 
615  /*
616  * End
617  */
618  num = (int32_t)strtol(p, &q, 10);
619  if (q > p) switch (q[0]) {
620  default:
621  goto no_term;
622 
623  case ':': /* More integers to parse */
624  selector->slice[idx] = num;
625  break;
626 
627  case '\0': /* Array End */
628  selector->slice[idx] = num;
629  return ret;
630  }
631  if (q[0] != ':') goto no_term;
632  idx++;
633  p = q + 1;
634 
635  /*
636  * Step
637  */
638  num = (int32_t)strtol(p, &q, 10);
639  if (q[0] != '\0') {
640  fr_strerror_printf("Expected num or ']'");
641  return buffer - q;
642  }
643  if (q > p) {
644  if (num == 0) {
645  fr_strerror_printf("Step cannot be 0");
646  return buffer - p;
647  }
648  selector->slice[idx] = num;
649  }
650  return ret;
651 }
652 
653 /** Parse a jpath field
654  *
655  */
656 static size_t jpath_field_parse(fr_jpath_node_t *node, char const *in, size_t inlen)
657 {
658  char buffer[128];
659 
660  char const *p = in, *end = p + inlen;
661  char *buff_p = buffer, *buff_end = buff_p + sizeof(buffer);
662 
663  /*
664  * Field name with optional selector
665  */
666  while (p < end) {
667  int clen;
668 
669  if (buff_p == buff_end) {
670  name_too_big:
671  fr_strerror_printf("Exceeded maximum field name length");
672  return in - p;
673  }
674 
675  clen = fr_utf8_char((uint8_t const *)p, end - p);
676  if (clen == 0) {
677  fr_strerror_printf("Bad UTF8 char");
678  return in - p;
679  }
680 
681  /*
682  * Multibyte
683  */
684  if (clen > 1) {
685  if ((buff_p + clen) >= buff_end) goto name_too_big;
686  memcpy(buff_p, p, clen);
687  buff_p += clen;
688  p += clen;
689  continue;
690  }
691 
692  switch (p[0]) { /* Normal char */
693  default:
694  *buff_p++ = *p++;
695  continue;
696 
697  /*
698  * Escape sequence
699  */
700  case '\\':
701  if (++p == end) return p - in;
702 
703  if (memchr(escape_chars, p[0], sizeof(escape_chars))) {
704  *buff_p++ = *p++;
705  continue;
706  }
707  *buff_p++ = '\\';
708  continue;
709 
710  /*
711  * Things that mark the end of a field
712  */
713  case '[':
714  case ']': /* Not really, but it's probably not right */
715  case '.':
716  break;
717  }
718  break;
719  }
720 
721  if (buff_p == buffer) {
722  fr_strerror_printf("Empty field specifier");
723  return 0;
724  }
725  node->selector->field = talloc_bstrndup(node, buffer, buff_p - buffer);
727 
728  return p - in;
729 }
730 
731 /** parse a jpath selector
732  *
733  */
734 static size_t jpath_selector_parse(fr_jpath_node_t *node, char const *in, size_t inlen)
735 {
736  ssize_t slen;
737  char const *p = in, *end = p + inlen;
738 
739  jpath_selector_t **stail;
740  jpath_selector_t *selector;
741 
742  stail = &node->selector->next;
743  selector = node->selector;
744 
745  if (++p == end) { /* Skip past [ */
746  missing_terminator:
747  fr_strerror_printf("Missing selector terminator ']'");
748  return in - p;
749  }
750 
751  while (p < end) {
752  /*
753  * What kind of selector is it?
754  */
755  switch (p[0]) {
756  case '?': /* Filter expression */
757  slen = jpath_filter_expr_parse(selector, p, end - p);
758  break;
759 
760  case '(': /* Expression */
761  slen = jpath_expr_parse(selector, p, end - p);
762  break;
763 
764  default: /* Index or slice */
765  slen = jpath_array_parse(selector, p, end - p);
766  break;
767  }
768  if (slen <= 0) {
769  p += -slen;
770  return in - p; /* Error */
771  }
772  p += slen;
773  if (p == end) goto missing_terminator;
774  rad_assert(p < end);
775 
776  /*
777  * Things that terminate a selector
778  *
779  * - ']' a selector terminator.
780  * - ',' another selector.
781  */
782  if (p[0] == ']') break; /* We're done */
783  if (p[0] != ',') { /* There's more... */
784  fr_strerror_printf("Expected selector delimiter ','"
785  "or selector terminator ']'");
786  return in - p; /* Error */
787  }
788  if (++p == end) goto missing_terminator;
789 
790  /*
791  * Link in an additional selector
792  */
793  *stail = selector = talloc_zero(node, jpath_selector_t);
794  if (!selector) {
795  fr_strerror_printf("Failed allocating selector");
796  return in - p;
797  }
798  stail = &selector->next;
799  }
800  if (p[0] != ']') goto missing_terminator;
801 
802  p++; /* Skip past ] */
803 
804  return p - in;
805 }
806 
807 /** Parse a jpath string
808  *
809  * Based on the syntax described here http://goessner.net/articles/JsonPath/
810  *
811  * Implements parser for everything except unions and expressions
812  *
813  * @return
814  * - > 0 on success.
815  * - <= 0 on error (* -1 to get offset error ocurred at).
816  */
817 ssize_t fr_jpath_parse(TALLOC_CTX *ctx, fr_jpath_node_t **head, char const *in, size_t inlen)
818 {
819  TALLOC_CTX *tail_ctx = ctx;
820 
821  ssize_t slen;
822  fr_jpath_node_t *node, **tail = head;
823 
824  char const *p = in, *end = p + inlen;
825 
826  *head = NULL;
827 
828 #define NODE_NEW(_node) \
829 do { \
830  tail_ctx = *tail = (_node) = talloc_zero(tail_ctx, fr_jpath_node_t); \
831  (_node)->selector = talloc_zero((_node), jpath_selector_t); \
832  tail = &(_node)->next; \
833 } while (0)
834 
835  if (inlen < 1) {
836  bad_start:
837  fr_strerror_printf("Expected root specifier '$', or current node specifier '@'");
838  return 0;
839  }
840 
841  /*
842  * Start of the jpath expression
843  *
844  * - '$' JSON root node specifier
845  * - '@' Current node specifier
846  */
847  switch (p[0]) {
848  case '$':
849  NODE_NEW(node);
851  p++;
852  break;
853 
854  case '@':
855  NODE_NEW(node);
857  p++;
858  break;
859 
860  default:
861  goto bad_start;
862  }
863 
864  /*
865  * Valid successions of '$' or '@'
866  *
867  * - '[' Start of a selector
868  * - '.' Start of a field specifier
869  */
870  while (p < end) {
871  NODE_NEW(node);
872 
873  switch (p[0]) {
874  case '.':
875  if (++p == end) {
876  fr_strerror_printf("Expected recursive descent '..' "
877  "wildcard '*' or field specifier");
878  error:
879  TALLOC_FREE(*head);
880  return in - p;
881  }
882 
883  /*
884  * Valid successions of '.'
885  *
886  * - '.' recursive descent.
887  * - '*' wildcard.
888  * - <name> (fieldname).
889  *
890  * This should probably be in its own function, but never mind...
891  */
892  switch (p[0]) {
893  case '.':
894  if ((p != end) && (p[1] == '.')) {
895  fr_strerror_printf("Recursive descent must not be "
896  "followed by child delimiter '.'");
897  p++;
898  goto error;
899  }
901 
902  if ((p + 1) == end) {
903  fr_strerror_printf("Path may not end in recursive descent");
904  goto error;
905  }
906 
907  /*
908  * If and only if, the next char is the beginning
909  * of a selector, advance the pointer.
910  *
911  * Otherwise we leave it pointing to the second '.'
912  * allowing .* and .<field>
913  */
914  if (p[1] == '[') p++;
915  continue;
916 
917  case '*':
919  p++;
920  continue;
921 
922  default:
923  /*
924  * Field specifier is the only other valid possibility
925  */
926  slen = jpath_field_parse(node, p, (end - p));
927  if (slen <= 0) {
928  p += -(slen);
929  goto error;
930  }
931  p += slen;
932  if (p == end) return p - in; /* The end of string! */
933  rad_assert(p < end);
934  }
935  break;
936 
937  case '[':
938  slen = jpath_selector_parse(node, p, (end - p));
939  if (slen <= 0) {
940  p += -(slen);
941  goto error;
942  }
943  p += slen;
944  if (p == end) return p - in; /* The end of string! */
945  rad_assert(p < end);
946  break;
947 
948  default:
949  fr_strerror_printf("Expected field specifier '.' or selector '['");
950  goto error;
951  }
952  }
953 
954  return p - in;
955 }
956 
Complex filter expression (NYI).
Definition: jpath.c:46
Operand is a literal.
Definition: jpath.c:34
int fr_jpath_evaluate_leaf(TALLOC_CTX *ctx, value_data_t **out, PW_TYPE 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:379
#define json_object_object_foreach(obj, key, val)
Definition: json_missing.h:92
int fr_json_object_to_value_data(TALLOC_CTX *ctx, value_data_t *out, json_object *object, PW_TYPE dst_type, fr_dict_attr_t const *dst_enumv)
Convert json object to value_data_t.
Definition: json.c:42
#define NODE_NEW(_node)
static size_t jpath_field_parse(fr_jpath_node_t *node, char const *in, size_t inlen)
Parse a jpath field.
Definition: jpath.c:656
Dictionary attribute.
Definition: dict.h:77
jpath_operand_t rhs
RHS value.
Definition: jpath.c:67
Wildcard, operate over all array indicies, or fields.
Definition: jpath.c:42
#define UNUSED
Definition: libradius.h:134
Array index, current JSON node must be an array.
Definition: jpath.c:44
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:817
static ssize_t jpath_array_parse(jpath_selector_t *selector, char const *in, size_t inlen)
Parse index/slice notation.
Definition: jpath.c:548
uint8_t length
Definition: proto_bfd.c:203
A jpath expression for performing complex comparisons against field values.
Definition: jpath.c:63
jpath_type_t type
Type of the Jpath node.
Definition: jpath.c:84
#define rad_assert(expr)
Definition: rad_assert.h:38
jpath_operand_type lhs_type
LHS type.
Definition: jpath.c:65
Array slice, current JSON node must be an array.
Definition: jpath.c:45
FR_TOKEN op
Comparison operator.
Definition: jpath.c:70
Jump to the root of the document.
Definition: jpath.c:40
int json_object_object_get_ex(struct json_object *jso, const char *key, struct json_object **value)
Definition: json_missing.c:40
static ssize_t jpath_expr_parse(jpath_selector_t *selector, UNUSED char const *in, UNUSED size_t inlen)
Parse a jpath expression.
Definition: jpath.c:521
char const * literal
Operand is a literal (value)
Definition: jpath.c:56
static char const escape_chars[]
Definition: jpath.c:96
Descend through the JSON tree, looking for a node which matches the next one in the jpath sequence...
Definition: jpath.c:48
value_data_t * next
Next in a series of value_data.
Definition: pair.h:88
fr_jpath_node_t * next
Next in the jpath chain.
Definition: jpath.c:93
#define SELECTOR_INDEX_UNSET
Definition: jpath.c:31
jpath_selector_t * next
Definition: jpath.c:85
fr_jpath_node_t * jpath
Operand is a jpath expression.
Definition: jpath.c:57
Operand in a jpath expression.
Definition: jpath.c:55
#define UNUSED_VAR(_x)
Definition: build.h:57
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
Node in a jpath selector sequence.
Definition: jpath.c:91
Expression (NYI).
Definition: jpath.c:47
Operand is a jpath sequence.
Definition: jpath.c:35
char * talloc_bstrndup(void const *t, char const *in, size_t inlen)
Binary safe strndup function.
Definition: missing.c:632
static size_t jpath_selector_parse(fr_jpath_node_t *node, char const *in, size_t inlen)
parse a jpath selector
Definition: jpath.c:734
Continue at the current node in the document.
Definition: jpath.c:41
jpath_selector_t * selector
Jpath selector head (there may be multiple).
Definition: jpath.c:92
size_t fr_jpath_escape_func(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
Escapes special chars.
Definition: jpath.c:110
jpath_operand_type
Definition: jpath.c:33
static int jpath_evaluate(TALLOC_CTX *ctx, value_data_t ***tail, PW_TYPE 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:145
void fr_strerror_printf(char const *,...) CC_HINT(format(printf
jpath_operand_t lhs
LHS value.
Definition: jpath.c:64
static ssize_t jpath_filter_expr_parse(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
Definition: jpath.c:38
struct jpath_expr jpath_expr_t
A jpath expression for performing complex comparisons against field values.
jpath_operand_type rhs_type
RHS type.
Definition: jpath.c:68
enum fr_token FR_TOKEN
int 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:34
#define fr_json_object_is_type(_obj, _type)
Definition: json_missing.h:57
PW_TYPE
Internal data types used within libfreeradius.
Definition: radius.h:31
A field, current JSON node must be an object.
Definition: jpath.c:43