1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 **/
19
20 #include "common.h"
21 #include "log.h"
22 #include "zbxalgo.h"
23 #include "zbxregexp.h"
24 #include "zbxjson.h"
25 #include "json.h"
26 #include "json_parser.h"
27 #include "jsonpath.h"
28
29 #include "../zbxalgo/vectorimpl.h"
30
31 ZBX_VECTOR_DECL(var, zbx_variant_t)
32 ZBX_VECTOR_IMPL(var, zbx_variant_t)
33
34 typedef struct
35 {
36 char *name;
37 const char *value;
38 }
39 zbx_json_element_t;
40
41 ZBX_VECTOR_DECL(json, zbx_json_element_t)
42 ZBX_VECTOR_IMPL(json, zbx_json_element_t)
43
44 static int jsonpath_query_object(const struct zbx_json_parse *jp_root, const struct zbx_json_parse *jp,
45 const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_json_t *objects);
46 static int jsonpath_query_array(const struct zbx_json_parse *jp_root, const struct zbx_json_parse *jp,
47 const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_json_t *objects);
48
49 typedef struct
50 {
51 zbx_jsonpath_token_group_t group;
52 int precedence;
53 }
54 zbx_jsonpath_token_def_t;
55
56 /* define token groups and precedence */
57 static zbx_jsonpath_token_def_t jsonpath_tokens[] = {
58 {0, 0},
59 {ZBX_JSONPATH_TOKEN_GROUP_OPERAND, 0}, /* ZBX_JSONPATH_TOKEN_PATH_ABSOLUTE */
60 {ZBX_JSONPATH_TOKEN_GROUP_OPERAND, 0}, /* ZBX_JSONPATH_TOKEN_PATH_RELATIVE */
61 {ZBX_JSONPATH_TOKEN_GROUP_OPERAND, 0}, /* ZBX_JSONPATH_TOKEN_CONST_STR */
62 {ZBX_JSONPATH_TOKEN_GROUP_OPERAND, 0}, /* ZBX_JSONPATH_TOKEN_CONST_NUM */
63 {ZBX_JSONPATH_TOKEN_GROUP_NONE, 0}, /* ZBX_JSONPATH_TOKEN_PAREN_LEFT */
64 {ZBX_JSONPATH_TOKEN_GROUP_NONE, 0}, /* ZBX_JSONPATH_TOKEN_PAREN_RIGHT */
65 {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 4}, /* ZBX_JSONPATH_TOKEN_OP_PLUS */
66 {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 4}, /* ZBX_JSONPATH_TOKEN_OP_MINUS */
67 {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 3}, /* ZBX_JSONPATH_TOKEN_OP_MULT */
68 {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 3}, /* ZBX_JSONPATH_TOKEN_OP_DIV */
69 {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 7}, /* ZBX_JSONPATH_TOKEN_OP_EQ */
70 {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 7}, /* ZBX_JSONPATH_TOKEN_OP_NE */
71 {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 6}, /* ZBX_JSONPATH_TOKEN_OP_GT */
72 {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 6}, /* ZBX_JSONPATH_TOKEN_OP_GE */
73 {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 6}, /* ZBX_JSONPATH_TOKEN_OP_LT */
74 {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 6}, /* ZBX_JSONPATH_TOKEN_OP_LE */
75 {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR1, 2}, /* ZBX_JSONPATH_TOKEN_OP_NOT */
76 {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 11}, /* ZBX_JSONPATH_TOKEN_OP_AND */
77 {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 12}, /* ZBX_JSONPATH_TOKEN_OP_OR */
78 {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 7} /* ZBX_JSONPATH_TOKEN_OP_REGEXP */
79 };
80
jsonpath_token_precedence(int type)81 static int jsonpath_token_precedence(int type)
82 {
83 return jsonpath_tokens[type].precedence;
84 }
85
jsonpath_token_group(int type)86 static int jsonpath_token_group(int type)
87 {
88 return jsonpath_tokens[type].group;
89 }
90
91 /* json element vector support */
zbx_vector_json_add_element(zbx_vector_json_t * elements,const char * name,const char * value)92 static void zbx_vector_json_add_element(zbx_vector_json_t *elements, const char *name, const char *value)
93 {
94 zbx_json_element_t el;
95
96 el.name = zbx_strdup(NULL, name);
97 el.value = value;
98 zbx_vector_json_append(elements, el);
99 }
100
zbx_vector_json_copy(zbx_vector_json_t * dst,const zbx_vector_json_t * src)101 static void zbx_vector_json_copy(zbx_vector_json_t *dst, const zbx_vector_json_t *src)
102 {
103 int i;
104
105 for (i = 0; i < src->values_num; i++)
106 zbx_vector_json_add_element(dst, src->values[i].name, src->values[i].value);
107 }
108
zbx_vector_json_clear_ext(zbx_vector_json_t * elements)109 static void zbx_vector_json_clear_ext(zbx_vector_json_t *elements)
110 {
111 int i;
112
113 for (i = 0; i < elements->values_num; i++)
114 zbx_free(elements->values[i].name);
115 zbx_vector_json_clear(elements);
116 }
117
118 /******************************************************************************
119 * *
120 * Function: zbx_jsonpath_error *
121 * *
122 * Purpose: set json error message and return FAIL *
123 * *
124 * Comments: This function is used to return from json path parsing functions *
125 * in the case of failure. *
126 * *
127 ******************************************************************************/
zbx_jsonpath_error(const char * path)128 static int zbx_jsonpath_error(const char *path)
129 {
130 if ('\0' != *path)
131 zbx_set_json_strerror("unsupported construct in jsonpath starting with: \"%s\"", path);
132 else
133 zbx_set_json_strerror("jsonpath was unexpectedly terminated");
134
135 return FAIL;
136 }
137
138 /******************************************************************************
139 * *
140 * Function: jsonpath_strndup *
141 * *
142 ******************************************************************************/
jsonpath_strndup(const char * source,size_t len)143 static char *jsonpath_strndup(const char *source, size_t len)
144 {
145 char *str;
146
147 str = (char *)zbx_malloc(NULL, len + 1);
148 memcpy(str, source, len);
149 str[len] = '\0';
150
151 return str;
152 }
153
154 /******************************************************************************
155 * *
156 * Function: jsonpath_unquote *
157 * *
158 * Purpose: unquote single or double quoted string by stripping *
159 * leading/trailing quotes and unescaping backslash sequences *
160 * *
161 * Parameters: value - [OUT] the output value, must have at least len bytes *
162 * start - [IN] a single or double quoted string to unquote *
163 * len - [IN] the length of the input string *
164 * *
165 ******************************************************************************/
jsonpath_unquote(char * value,const char * start,size_t len)166 static void jsonpath_unquote(char *value, const char *start, size_t len)
167 {
168 const char *end = start + len - 1;
169
170 for (start++; start != end; start++)
171 {
172 if ('\\' == *start)
173 start++;
174
175 *value++ = *start;
176 }
177
178 *value = '\0';
179 }
180
181 /******************************************************************************
182 * *
183 * Function: jsonpath_unquote_dyn *
184 * *
185 * Purpose: unquote string stripping leading/trailing quotes and unescaping *
186 * backspace sequences *
187 * *
188 * Parameters: start - [IN] the string to unquote including leading and *
189 * trailing quotes *
190 * len - [IN] the length of the input string *
191 * *
192 * Return value: The unescaped string (must be freed by the caller). *
193 * *
194 ******************************************************************************/
jsonpath_unquote_dyn(const char * start,size_t len)195 static char *jsonpath_unquote_dyn(const char *start, size_t len)
196 {
197 char *value;
198
199 value = (char *)zbx_malloc(NULL, len + 1);
200 jsonpath_unquote(value, start, len);
201
202 return value;
203 }
204
205 /******************************************************************************
206 * *
207 * Function: jsonpath_list_create_item *
208 * *
209 * Purpose: create jsonpath list item of the specified size *
210 * *
211 ******************************************************************************/
jsonpath_list_create_node(size_t size)212 static zbx_jsonpath_list_node_t *jsonpath_list_create_node(size_t size)
213 {
214 return (zbx_jsonpath_list_node_t *)zbx_malloc(NULL, offsetof(zbx_jsonpath_list_node_t, data) + size);
215 }
216
217 /******************************************************************************
218 * *
219 * Function: jsonpath_list_free *
220 * *
221 * Purpose: free jsonpath list *
222 * *
223 ******************************************************************************/
jsonpath_list_free(zbx_jsonpath_list_node_t * list)224 static void jsonpath_list_free(zbx_jsonpath_list_node_t *list)
225 {
226 while (NULL != list)
227 {
228 zbx_jsonpath_list_node_t *item = list;
229
230 list = list->next;
231 zbx_free(item);
232 }
233 }
234
235 /******************************************************************************
236 * *
237 * Function: jsonpath_create_token *
238 * *
239 * Purpose: create jsonpath expression token *
240 * *
241 * Parameters: type - [IN] the token type *
242 * expression - [IN] the expression *
243 * loc - [IN] the token location in the expression *
244 * *
245 * Return value: The created token (must be freed by the caller). *
246 * *
247 ******************************************************************************/
jsonpath_create_token(int type,const char * expression,const zbx_strloc_t * loc)248 static zbx_jsonpath_token_t *jsonpath_create_token(int type, const char *expression, const zbx_strloc_t *loc)
249 {
250 zbx_jsonpath_token_t *token;
251
252 token = (zbx_jsonpath_token_t *)zbx_malloc(NULL, sizeof(zbx_jsonpath_token_t));
253 token->type = type;
254
255 switch (token->type)
256 {
257 case ZBX_JSONPATH_TOKEN_CONST_STR:
258 token->data = jsonpath_unquote_dyn(expression + loc->l, loc->r - loc->l + 1);
259 break;
260 case ZBX_JSONPATH_TOKEN_PATH_ABSOLUTE:
261 case ZBX_JSONPATH_TOKEN_PATH_RELATIVE:
262 case ZBX_JSONPATH_TOKEN_CONST_NUM:
263 token->data = jsonpath_strndup(expression + loc->l, loc->r - loc->l + 1);
264 break;
265 default:
266 token->data = NULL;
267 }
268
269 return token;
270 }
271
272 /******************************************************************************
273 * *
274 * Function: jsonpath_token_free *
275 * *
276 ******************************************************************************/
jsonpath_token_free(zbx_jsonpath_token_t * token)277 static void jsonpath_token_free(zbx_jsonpath_token_t *token)
278 {
279 zbx_free(token->data);
280 zbx_free(token);
281 }
282
283 /******************************************************************************
284 * *
285 * Function: jsonpath_reserve *
286 * *
287 * Purpose: reserve space in jsonpath segments array for more segments *
288 * *
289 * Parameters: jsonpath - [IN] the jsonpath data *
290 * num - [IN] the number of segments to reserve *
291 * *
292 ******************************************************************************/
jsonpath_reserve(zbx_jsonpath_t * jsonpath,int num)293 static void jsonpath_reserve(zbx_jsonpath_t *jsonpath, int num)
294 {
295 if (jsonpath->segments_num + num > jsonpath->segments_alloc)
296 {
297 int old_alloc = jsonpath->segments_alloc;
298
299 if (jsonpath->segments_alloc < num)
300 jsonpath->segments_alloc = jsonpath->segments_num + num;
301 else
302 jsonpath->segments_alloc *= 2;
303
304 jsonpath->segments = (zbx_jsonpath_segment_t *)zbx_realloc(jsonpath->segments,
305 sizeof(zbx_jsonpath_segment_t) * jsonpath->segments_alloc);
306
307 /* Initialize the memory allocated for new segments, as parser can set */
308 /* detached flag for the next segment, so the memory cannot be initialized */
309 /* when creating a segment. */
310 memset(jsonpath->segments + old_alloc, 0,
311 (jsonpath->segments_alloc - old_alloc) * sizeof(zbx_jsonpath_segment_t));
312 }
313 }
314
315 /******************************************************************************
316 * *
317 * Function: jsonpath_segment_clear *
318 * *
319 ******************************************************************************/
jsonpath_segment_clear(zbx_jsonpath_segment_t * segment)320 static void jsonpath_segment_clear(zbx_jsonpath_segment_t *segment)
321 {
322 switch (segment->type)
323 {
324 case ZBX_JSONPATH_SEGMENT_MATCH_LIST:
325 jsonpath_list_free(segment->data.list.values);
326 break;
327 case ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION:
328 zbx_vector_ptr_clear_ext(&segment->data.expression.tokens,
329 (zbx_clean_func_t)jsonpath_token_free);
330 zbx_vector_ptr_destroy(&segment->data.expression.tokens);
331 break;
332 default:
333 break;
334 }
335 }
336
337 /******************************************************************************
338 * *
339 * Function: jsonpath_next *
340 * *
341 * Purpose: find next component of json path *
342 * *
343 * Parameters: pnext - [IN/OUT] the reference to the next path component *
344 * *
345 * Return value: SUCCEED - the json path component was parsed successfully *
346 * FAIL - json path parsing error *
347 * *
348 ******************************************************************************/
jsonpath_next(const char ** pnext)349 static int jsonpath_next(const char **pnext)
350 {
351 const char *next = *pnext, *start;
352
353 /* process dot notation component */
354 if ('.' == *next)
355 {
356 if ('\0' == *(++next))
357 return zbx_jsonpath_error(*pnext);
358
359 if ('[' != *next)
360 {
361 start = next;
362
363 while (0 != isalnum((unsigned char)*next) || '_' == *next)
364 next++;
365
366 if (start == next)
367 return zbx_jsonpath_error(*pnext);
368
369 *pnext = next;
370 return SUCCEED;
371 }
372 }
373
374 if ('[' != *next)
375 return zbx_jsonpath_error(*pnext);
376
377 SKIP_WHITESPACE_NEXT(next);
378
379 /* process array index component */
380 if (0 != isdigit((unsigned char)*next))
381 {
382 size_t pos;
383
384 for (pos = 1; 0 != isdigit((unsigned char)next[pos]); pos++)
385 ;
386
387 next += pos;
388 SKIP_WHITESPACE(next);
389 }
390 else
391 {
392 char quotes;
393
394 if ('\'' != *next && '"' != *next)
395 return zbx_jsonpath_error(*pnext);
396
397 start = next;
398
399 for (quotes = *next++; quotes != *next; next++)
400 {
401 if ('\0' == *next)
402 return zbx_jsonpath_error(*pnext);
403 }
404
405 if (start == next)
406 return zbx_jsonpath_error(*pnext);
407
408 SKIP_WHITESPACE_NEXT(next);
409 }
410
411 if (']' != *next++)
412 return zbx_jsonpath_error(*pnext);
413
414 *pnext = next;
415 return SUCCEED;
416 }
417
418 /******************************************************************************
419 * *
420 * Function: jsonpath_parse_substring *
421 * *
422 * Purpose: parse single or double quoted substring *
423 * *
424 * Parameters: start - [IN] the substring start *
425 * len - [OUT] the substring length *
426 * *
427 * Return value: SUCCEED - the substring was parsed successfully *
428 * FAIL - otherwise *
429 * *
430 ******************************************************************************/
jsonpath_parse_substring(const char * start,int * len)431 static int jsonpath_parse_substring(const char *start, int *len)
432 {
433 const char *ptr;
434 char quotes;
435
436 for (quotes = *start, ptr = start + 1; '\0' != *ptr; ptr++)
437 {
438 if (*ptr == quotes)
439 {
440 *len = ptr - start + 1;
441 return SUCCEED;
442 }
443
444 if ('\\' == *ptr)
445 {
446 if (quotes != ptr[1] && '\\' != ptr[1] )
447 return FAIL;
448 ptr++;
449 }
450 }
451
452 return FAIL;
453 }
454
455 /******************************************************************************
456 * *
457 * Function: jsonpath_parse_path *
458 * *
459 * Purpose: parse jsonpath reference *
460 * *
461 * Parameters: start - [IN] the jsonpath start *
462 * len - [OUT] the jsonpath length *
463 * *
464 * Return value: SUCCEED - the jsonpath was parsed successfully *
465 * FAIL - otherwise *
466 * *
467 * Comments: This function is used to parse jsonpath references used in *
468 * jsonpath filter expressions. *
469 * *
470 ******************************************************************************/
jsonpath_parse_path(const char * start,int * len)471 static int jsonpath_parse_path(const char *start, int *len)
472 {
473 const char *ptr = start + 1;
474
475 while ('[' == *ptr || '.' == *ptr)
476 {
477 if (FAIL == jsonpath_next(&ptr))
478 return FAIL;
479 }
480
481 *len = ptr - start;
482 return SUCCEED;
483 }
484
485 /******************************************************************************
486 * *
487 * Function: jsonpath_parse_number *
488 * *
489 * Purpose: parse number value *
490 * *
491 * Parameters: start - [IN] the number start *
492 * len - [OUT] the number length *
493 * *
494 * Return value: SUCCEED - the number was parsed successfully *
495 * FAIL - otherwise *
496 * *
497 ******************************************************************************/
jsonpath_parse_number(const char * start,int * len)498 static int jsonpath_parse_number(const char *start, int *len)
499 {
500 const char *ptr = start;
501 char *end;
502 int size;
503 double tmp;
504
505 if ('-' == *ptr || '+' == *ptr)
506 ptr++;
507
508 if (FAIL == zbx_number_parse(ptr, &size))
509 return FAIL;
510
511 ptr += size;
512 errno = 0;
513 tmp = strtod(start, &end);
514
515 if (ptr != end || HUGE_VAL == tmp || -HUGE_VAL == tmp || EDOM == errno)
516 return FAIL;
517
518 *len = (int)(ptr - start);
519
520 return SUCCEED;
521 }
522
523 /******************************************************************************
524 * *
525 * Function: jsonpath_expression_next_token *
526 * *
527 * Purpose: get next token in jsonpath expression *
528 * *
529 * Parameters: exprsesion - [IN] the jsonpath expression *
530 * pos - [IN] the position of token in the expression *
531 * prev_group - [IN] the preceding token group, used to determine *
532 * token type based on context if necessary *
533 * type - [OUT] the token type *
534 * loc - [OUT] the token location in the expression *
535 * *
536 * Return value: SUCCEED - the token was parsed successfully *
537 * FAIL - otherwise *
538 * *
539 ******************************************************************************/
jsonpath_expression_next_token(const char * expression,int pos,int prev_group,zbx_jsonpath_token_type_t * type,zbx_strloc_t * loc)540 static int jsonpath_expression_next_token(const char *expression, int pos, int prev_group,
541 zbx_jsonpath_token_type_t *type, zbx_strloc_t *loc)
542 {
543 int len;
544 const char *ptr = expression + pos;
545
546 SKIP_WHITESPACE(ptr);
547 loc->l = ptr - expression;
548
549 switch (*ptr)
550 {
551 case '(':
552 *type = ZBX_JSONPATH_TOKEN_PAREN_LEFT;
553 loc->r = loc->l;
554 return SUCCEED;
555 case ')':
556 *type = ZBX_JSONPATH_TOKEN_PAREN_RIGHT;
557 loc->r = loc->l;
558 return SUCCEED;
559 case '+':
560 *type = ZBX_JSONPATH_TOKEN_OP_PLUS;
561 loc->r = loc->l;
562 return SUCCEED;
563 case '-':
564 if (ZBX_JSONPATH_TOKEN_GROUP_OPERAND == prev_group)
565 {
566 *type = ZBX_JSONPATH_TOKEN_OP_MINUS;
567 loc->r = loc->l;
568 return SUCCEED;
569 }
570 break;
571 case '/':
572 *type = ZBX_JSONPATH_TOKEN_OP_DIV;
573 loc->r = loc->l;
574 return SUCCEED;
575 case '*':
576 *type = ZBX_JSONPATH_TOKEN_OP_MULT;
577 loc->r = loc->l;
578 return SUCCEED;
579 case '!':
580 if ('=' == ptr[1])
581 {
582 *type = ZBX_JSONPATH_TOKEN_OP_NE;
583 loc->r = loc->l + 1;
584 return SUCCEED;
585 }
586 *type = ZBX_JSONPATH_TOKEN_OP_NOT;
587 loc->r = loc->l;
588 return SUCCEED;
589 case '=':
590 switch (ptr[1])
591 {
592 case '=':
593 *type = ZBX_JSONPATH_TOKEN_OP_EQ;
594 loc->r = loc->l + 1;
595 return SUCCEED;
596 case '~':
597 *type = ZBX_JSONPATH_TOKEN_OP_REGEXP;
598 loc->r = loc->l + 1;
599 return SUCCEED;
600 }
601 goto out;
602 case '<':
603 if ('=' == ptr[1])
604 {
605 *type = ZBX_JSONPATH_TOKEN_OP_LE;
606 loc->r = loc->l + 1;
607 return SUCCEED;
608 }
609 *type = ZBX_JSONPATH_TOKEN_OP_LT;
610 loc->r = loc->l;
611 return SUCCEED;
612 case '>':
613 if ('=' == ptr[1])
614 {
615 *type = ZBX_JSONPATH_TOKEN_OP_GE;
616 loc->r = loc->l + 1;
617 return SUCCEED;
618 }
619 *type = ZBX_JSONPATH_TOKEN_OP_GT;
620 loc->r = loc->l;
621 return SUCCEED;
622 case '|':
623 if ('|' == ptr[1])
624 {
625 *type = ZBX_JSONPATH_TOKEN_OP_OR;
626 loc->r = loc->l + 1;
627 return SUCCEED;
628 }
629 goto out;
630 case '&':
631 if ('&' == ptr[1])
632 {
633 *type = ZBX_JSONPATH_TOKEN_OP_AND;
634 loc->r = loc->l + 1;
635 return SUCCEED;
636 }
637 goto out;
638 case '@':
639 if (SUCCEED == jsonpath_parse_path(ptr, &len))
640 {
641 *type = ZBX_JSONPATH_TOKEN_PATH_RELATIVE;
642 loc->r = loc->l + len - 1;
643 return SUCCEED;
644 }
645 goto out;
646
647 case '$':
648 if (SUCCEED == jsonpath_parse_path(ptr, &len))
649 {
650 *type = ZBX_JSONPATH_TOKEN_PATH_ABSOLUTE;
651 loc->r = loc->l + len - 1;
652 return SUCCEED;
653 }
654 goto out;
655 case '\'':
656 case '"':
657 if (SUCCEED == jsonpath_parse_substring(ptr, &len))
658 {
659 *type = ZBX_JSONPATH_TOKEN_CONST_STR;
660 loc->r = loc->l + len - 1;
661 return SUCCEED;
662 }
663 goto out;
664 }
665
666 if ('-' == *ptr || 0 != isdigit((unsigned char)*ptr))
667 {
668 if (SUCCEED == jsonpath_parse_number(ptr, &len))
669 {
670 *type = ZBX_JSONPATH_TOKEN_CONST_NUM;
671 loc->r = loc->l + len - 1;
672 return SUCCEED;
673 }
674 }
675 out:
676 return zbx_jsonpath_error(ptr);
677 }
678
679 /******************************************************************************
680 * *
681 * Function: jsonpath_parse_expression *
682 * *
683 * Purpose: parse jsonpath filter expression in format *
684 * *
685 * Parameters: expression - [IN] the expression, including opening and *
686 * closing parenthesis *
687 * jsonpath - [IN/OUT] the jsonpath *
688 * next - [OUT] a pointer to the next character after *
689 * parsed expression *
690 * *
691 * Return value: SUCCEED - the expression was parsed successfully *
692 * FAIL - otherwise *
693 * *
694 * Comments: This function uses shunting-yard algorithm to store parsed *
695 * tokens in postfix notation for evaluation. *
696 * *
697 * The following token precedence rules are enforced: *
698 * 1) binary operator must follow an operand *
699 * 2) operand must follow an operator *
700 * 3) unary operator must follow an operator *
701 * 4) ')' must follow an operand *
702 * *
703 ******************************************************************************/
jsonpath_parse_expression(const char * expression,zbx_jsonpath_t * jsonpath,const char ** next)704 static int jsonpath_parse_expression(const char *expression, zbx_jsonpath_t *jsonpath, const char **next)
705 {
706 int nesting = 1, ret = FAIL;
707 zbx_jsonpath_token_t *optoken;
708 zbx_vector_ptr_t output, operators;
709 zbx_strloc_t loc = {0, 0};
710 zbx_jsonpath_token_type_t token_type;
711 zbx_jsonpath_token_group_t prev_group = ZBX_JSONPATH_TOKEN_GROUP_NONE;
712
713 if ('(' != *expression)
714 return zbx_jsonpath_error(expression);
715
716 zbx_vector_ptr_create(&output);
717 zbx_vector_ptr_create(&operators);
718
719 while (SUCCEED == jsonpath_expression_next_token(expression, loc.r + 1, prev_group, &token_type, &loc))
720 {
721 switch (token_type)
722 {
723 case ZBX_JSONPATH_TOKEN_PAREN_LEFT:
724 nesting++;
725 break;
726
727 case ZBX_JSONPATH_TOKEN_PAREN_RIGHT:
728 if (ZBX_JSONPATH_TOKEN_GROUP_OPERAND != prev_group)
729 {
730 zbx_jsonpath_error(expression + loc.l);
731 goto out;
732 }
733
734 if (0 == --nesting)
735 {
736 *next = expression + loc.r + 1;
737 ret = SUCCEED;
738 goto out;
739 }
740 break;
741 default:
742 break;
743 }
744
745 if (ZBX_JSONPATH_TOKEN_GROUP_OPERAND == jsonpath_token_group(token_type))
746 {
747 /* expression cannot have two consequent operands */
748 if (ZBX_JSONPATH_TOKEN_GROUP_OPERAND == prev_group)
749 {
750 zbx_jsonpath_error(expression + loc.l);
751 goto out;
752 }
753
754 zbx_vector_ptr_append(&output, jsonpath_create_token(token_type, expression, &loc));
755 prev_group = jsonpath_token_group(token_type);
756 continue;
757 }
758
759 if (ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2 == jsonpath_token_group(token_type) ||
760 ZBX_JSONPATH_TOKEN_GROUP_OPERATOR1 == jsonpath_token_group(token_type))
761 {
762 /* binary operator must follow an operand */
763 if (ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2 == jsonpath_token_group(token_type) &&
764 ZBX_JSONPATH_TOKEN_GROUP_OPERAND != prev_group)
765 {
766 zbx_jsonpath_error(expression + loc.l);
767 goto cleanup;
768 }
769
770 /* negation ! operator cannot follow an operand */
771 if (ZBX_JSONPATH_TOKEN_OP_NOT == token_type &&
772 ZBX_JSONPATH_TOKEN_GROUP_OPERAND == prev_group)
773 {
774 zbx_jsonpath_error(expression + loc.l);
775 goto cleanup;
776 }
777
778 for (; 0 < operators.values_num; operators.values_num--)
779 {
780 optoken = operators.values[operators.values_num - 1];
781
782 if (jsonpath_token_precedence(optoken->type) >
783 jsonpath_token_precedence(token_type))
784 {
785 break;
786 }
787
788 if (ZBX_JSONPATH_TOKEN_PAREN_LEFT == optoken->type)
789 break;
790
791 zbx_vector_ptr_append(&output, optoken);
792 }
793
794 zbx_vector_ptr_append(&operators, jsonpath_create_token(token_type, expression, &loc));
795 prev_group = jsonpath_token_group(token_type);
796 continue;
797 }
798
799 if (ZBX_JSONPATH_TOKEN_PAREN_LEFT == token_type)
800 {
801 zbx_vector_ptr_append(&operators, jsonpath_create_token(token_type, expression, &loc));
802 prev_group = ZBX_JSONPATH_TOKEN_GROUP_NONE;
803 continue;
804 }
805
806 if (ZBX_JSONPATH_TOKEN_PAREN_RIGHT == token_type)
807 {
808 /* right parenthesis must follow and operand or right parenthesis */
809 if (ZBX_JSONPATH_TOKEN_GROUP_OPERAND != prev_group)
810 {
811 zbx_jsonpath_error(expression + loc.l);
812 goto cleanup;
813 }
814
815 for (optoken = 0; 0 < operators.values_num; operators.values_num--)
816 {
817 optoken = operators.values[operators.values_num - 1];
818
819 if (ZBX_JSONPATH_TOKEN_PAREN_LEFT == optoken->type)
820 {
821 operators.values_num--;
822 break;
823 }
824
825 zbx_vector_ptr_append(&output, optoken);
826 }
827
828 if (NULL == optoken)
829 {
830 zbx_jsonpath_error(expression + loc.l);
831 goto cleanup;
832 }
833 jsonpath_token_free(optoken);
834
835 prev_group = ZBX_JSONPATH_TOKEN_GROUP_OPERAND;
836 continue;
837 }
838 }
839 out:
840 if (SUCCEED == ret)
841 {
842 zbx_jsonpath_segment_t *segment;
843
844 for (optoken = 0; 0 < operators.values_num; operators.values_num--)
845 {
846 optoken = operators.values[operators.values_num - 1];
847
848 if (ZBX_JSONPATH_TOKEN_PAREN_LEFT == optoken->type)
849 {
850 zbx_set_json_strerror("mismatched () brackets in expression: %s", expression);
851 ret = FAIL;
852 goto cleanup;
853 }
854
855 zbx_vector_ptr_append(&output, optoken);
856 }
857
858 jsonpath_reserve(jsonpath, 1);
859 segment = &jsonpath->segments[jsonpath->segments_num++];
860 segment->type = ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION;
861 zbx_vector_ptr_create(&segment->data.expression.tokens);
862 zbx_vector_ptr_append_array(&segment->data.expression.tokens, output.values, output.values_num);
863
864 jsonpath->definite = 0;
865 }
866 cleanup:
867 if (SUCCEED != ret)
868 {
869 zbx_vector_ptr_clear_ext(&operators, (zbx_clean_func_t)jsonpath_token_free);
870 zbx_vector_ptr_clear_ext(&output, (zbx_clean_func_t)jsonpath_token_free);
871 }
872
873 zbx_vector_ptr_destroy(&operators);
874 zbx_vector_ptr_destroy(&output);
875
876 return ret;
877 }
878
879 /******************************************************************************
880 * *
881 * Function: jsonpath_parse_names *
882 * *
883 * Purpose: parse a list of single or double quoted names, including trivial *
884 * case when a single name is used *
885 * *
886 * Parameters: list - [IN] the name list *
887 * jsonpath - [IN/OUT] the jsonpath *
888 * next - [OUT] a pointer to the next character after parsed *
889 * list *
890 * *
891 * Return value: SUCCEED - the list was parsed successfully *
892 * FAIL - otherwise *
893 * *
894 * Comments: In the trivial case (when list contains one name) the name is *
895 * stored into zbx_jsonpath_list_t:value field and later its *
896 * address is stored into zbx_jsonpath_list_t:values to reduce *
897 * allocations in trivial cases. *
898 * *
899 ******************************************************************************/
jsonpath_parse_names(const char * list,zbx_jsonpath_t * jsonpath,const char ** next)900 static int jsonpath_parse_names(const char *list, zbx_jsonpath_t *jsonpath, const char **next)
901 {
902 zbx_jsonpath_segment_t *segment;
903 int ret = FAIL, parsed_name = 0;
904 const char *end, *start = NULL;
905 zbx_jsonpath_list_node_t *head = NULL;
906
907 for (end = list; ']' != *end || NULL != start; end++)
908 {
909 switch (*end)
910 {
911 case '\'':
912 case '"':
913 if (NULL == start)
914 {
915 start = end;
916 }
917 else if (*start == *end)
918 {
919 zbx_jsonpath_list_node_t *node;
920
921 if (start + 1 == end)
922 {
923 ret = zbx_jsonpath_error(start);
924 goto out;
925 }
926
927 node = jsonpath_list_create_node(end - start + 1);
928 jsonpath_unquote(node->data, start, end - start + 1);
929 node->next = head;
930 head = node;
931 parsed_name = 1;
932 start = NULL;
933 }
934 break;
935 case '\\':
936 if (NULL == start || ('\\' != end[1] && *start != end[1]))
937 {
938 ret = zbx_jsonpath_error(end);
939 goto out;
940 }
941 end++;
942 break;
943 case ' ':
944 case '\t':
945 break;
946 case ',':
947 if (NULL != start)
948 break;
949
950 if (0 == parsed_name)
951 {
952 ret = zbx_jsonpath_error(end);
953 goto out;
954 }
955 parsed_name = 0;
956 break;
957 case '\0':
958 ret = zbx_jsonpath_error(end);
959 goto out;
960 default:
961 if (NULL == start)
962 {
963 ret = zbx_jsonpath_error(end);
964 goto out;
965 }
966 }
967 }
968
969 if (0 == parsed_name)
970 {
971 ret = zbx_jsonpath_error(end);
972 goto out;
973 }
974
975 segment = &jsonpath->segments[jsonpath->segments_num++];
976 segment->type = ZBX_JSONPATH_SEGMENT_MATCH_LIST;
977 segment->data.list.type = ZBX_JSONPATH_LIST_NAME;
978 segment->data.list.values = head;
979
980 if (NULL != head->next)
981 jsonpath->definite = 0;
982
983 head = NULL;
984 *next = end;
985 ret = SUCCEED;
986 out:
987 if (NULL != head)
988 jsonpath_list_free(head);
989
990 return ret;
991 }
992
993 /******************************************************************************
994 * *
995 * Function: jsonpath_parse_indexes *
996 * *
997 * Purpose: parse a list of array indexes or range start:end values *
998 * case when a single name is used *
999 * *
1000 * Parameters: list - [IN] the index list *
1001 * jsonpath - [IN/OUT] the jsonpath *
1002 * next - [OUT] a pointer to the next character after parsed *
1003 * list *
1004 * *
1005 * Return value: SUCCEED - the list was parsed successfully *
1006 * FAIL - otherwise *
1007 * *
1008 ******************************************************************************/
jsonpath_parse_indexes(const char * list,zbx_jsonpath_t * jsonpath,const char ** next)1009 static int jsonpath_parse_indexes(const char *list, zbx_jsonpath_t *jsonpath, const char **next)
1010 {
1011 zbx_jsonpath_segment_t *segment;
1012 const char *end, *start = NULL;
1013 int ret = FAIL, type = ZBX_JSONPATH_SEGMENT_UNKNOWN;
1014 unsigned int flags = 0, parsed_index = 0;
1015 zbx_jsonpath_list_node_t *head = NULL, *node;
1016
1017 for (end = list; ; end++)
1018 {
1019 if (0 != isdigit((unsigned char)*end))
1020 {
1021 if (NULL == start)
1022 start = end;
1023 continue;
1024 }
1025
1026 if ('-' == *end)
1027 {
1028 if (NULL != start)
1029 {
1030 ret = zbx_jsonpath_error(end);
1031 goto out;
1032 }
1033 start = end;
1034 continue;
1035 }
1036
1037 if (NULL != start)
1038 {
1039 int value;
1040
1041 if ('-' == *start && end == start + 1)
1042 {
1043 ret = zbx_jsonpath_error(start);
1044 goto out;
1045 }
1046
1047 node = jsonpath_list_create_node(sizeof(int));
1048 node->next = head;
1049 head = node;
1050 value = atoi(start);
1051 memcpy(node->data, &value, sizeof(int));
1052 start = NULL;
1053 parsed_index = 1;
1054 }
1055
1056 if (']' == *end)
1057 {
1058 if (ZBX_JSONPATH_SEGMENT_MATCH_RANGE != type)
1059 {
1060 if (0 == parsed_index)
1061 {
1062 ret = zbx_jsonpath_error(end);
1063 goto out;
1064 }
1065 }
1066 else
1067 flags |= (parsed_index << 1);
1068 break;
1069 }
1070
1071 if (':' == *end)
1072 {
1073 if (ZBX_JSONPATH_SEGMENT_UNKNOWN != type)
1074 {
1075 ret = zbx_jsonpath_error(end);
1076 goto out;
1077 }
1078 type = ZBX_JSONPATH_SEGMENT_MATCH_RANGE;
1079 flags |= parsed_index;
1080 parsed_index = 0;
1081 }
1082 else if (',' == *end)
1083 {
1084 if (ZBX_JSONPATH_SEGMENT_MATCH_RANGE == type || 0 == parsed_index)
1085 {
1086 ret = zbx_jsonpath_error(end);
1087 goto out;
1088 }
1089 type = ZBX_JSONPATH_SEGMENT_MATCH_LIST;
1090 parsed_index = 0;
1091 }
1092 else if (' ' != *end && '\t' != *end)
1093 {
1094 ret = zbx_jsonpath_error(end);
1095 goto out;
1096 }
1097 }
1098
1099 segment = &jsonpath->segments[jsonpath->segments_num++];
1100
1101 if (ZBX_JSONPATH_SEGMENT_MATCH_RANGE == type)
1102 {
1103 node = head;
1104
1105 segment->type = ZBX_JSONPATH_SEGMENT_MATCH_RANGE;
1106 segment->data.range.flags = flags;
1107 if (0 != (flags & 0x02))
1108 {
1109 memcpy(&segment->data.range.end, node->data, sizeof(int));
1110 node = node->next;
1111 }
1112 else
1113 segment->data.range.end = 0;
1114
1115 if (0 != (flags & 0x01))
1116 memcpy(&segment->data.range.start, node->data, sizeof(int));
1117 else
1118 segment->data.range.start = 0;
1119
1120 jsonpath->definite = 0;
1121 }
1122 else
1123 {
1124 segment->type = ZBX_JSONPATH_SEGMENT_MATCH_LIST;
1125 segment->data.list.type = ZBX_JSONPATH_LIST_INDEX;
1126 segment->data.list.values = head;
1127
1128 if (NULL != head->next)
1129 jsonpath->definite = 0;
1130
1131 head = NULL;
1132 }
1133
1134 *next = end;
1135 ret = SUCCEED;
1136 out:
1137 jsonpath_list_free(head);
1138
1139 return ret;
1140 }
1141
1142 /******************************************************************************
1143 * *
1144 * Function: jsonpath_parse_bracket_segment *
1145 * *
1146 * Purpose: parse jsonpath bracket notation segment *
1147 * *
1148 * Parameters: start - [IN] the segment start *
1149 * jsonpath - [IN/OUT] the jsonpath *
1150 * next - [OUT] a pointer to the next character after parsed *
1151 * segment *
1152 * *
1153 * Return value: SUCCEED - the segment was parsed successfully *
1154 * FAIL - otherwise *
1155 * *
1156 ******************************************************************************/
jsonpath_parse_bracket_segment(const char * start,zbx_jsonpath_t * jsonpath,const char ** next)1157 static int jsonpath_parse_bracket_segment(const char *start, zbx_jsonpath_t *jsonpath, const char **next)
1158 {
1159 const char *ptr = start;
1160 int ret;
1161
1162 SKIP_WHITESPACE(ptr);
1163
1164 if ('?' == *ptr)
1165 {
1166 ret = jsonpath_parse_expression(ptr + 1, jsonpath, next);
1167 }
1168 else if ('*' == *ptr)
1169 {
1170 jsonpath->segments[jsonpath->segments_num++].type = ZBX_JSONPATH_SEGMENT_MATCH_ALL;
1171 jsonpath->definite = 0;
1172 *next = ptr + 1;
1173 ret = SUCCEED;
1174 }
1175 else if ('\'' == *ptr || '"' == *ptr)
1176 {
1177 ret = jsonpath_parse_names(ptr, jsonpath, next);
1178 }
1179 else if (0 != isdigit((unsigned char)*ptr) || ':' == *ptr || '-' == *ptr)
1180 {
1181 ret = jsonpath_parse_indexes(ptr, jsonpath, next);
1182 }
1183 else
1184 ret = zbx_jsonpath_error(ptr);
1185
1186 if (SUCCEED == ret)
1187 {
1188 ptr = *next;
1189 SKIP_WHITESPACE(ptr);
1190
1191 if (']' != *ptr)
1192 return zbx_jsonpath_error(ptr);
1193
1194 *next = ptr + 1;
1195 }
1196
1197 return ret;
1198 }
1199
1200 /******************************************************************************
1201 * *
1202 * Function: jsonpath_parse_dot_segment *
1203 * *
1204 * Purpose: parse jsonpath dot notation segment *
1205 * *
1206 * Parameters: start - [IN] the segment start *
1207 * jsonpath - [IN/OUT] the jsonpath *
1208 * next - [OUT] a pointer to the next character after parsed *
1209 * segment *
1210 * *
1211 * Return value: SUCCEED - the segment was parsed successfully *
1212 * FAIL - otherwise *
1213 * *
1214 ******************************************************************************/
jsonpath_parse_dot_segment(const char * start,zbx_jsonpath_t * jsonpath,const char ** next)1215 static int jsonpath_parse_dot_segment(const char *start, zbx_jsonpath_t *jsonpath, const char **next)
1216 {
1217 zbx_jsonpath_segment_t *segment;
1218 const char *ptr;
1219 int len;
1220
1221 segment = &jsonpath->segments[jsonpath->segments_num];
1222 jsonpath->segments_num++;
1223
1224 if ('*' == *start)
1225 {
1226 jsonpath->definite = 0;
1227 segment->type = ZBX_JSONPATH_SEGMENT_MATCH_ALL;
1228 *next = start + 1;
1229 return SUCCEED;
1230 }
1231
1232 for (ptr = start; 0 != isalnum((unsigned char)*ptr) || '_' == *ptr;)
1233 ptr++;
1234
1235 if ('(' == *ptr)
1236 {
1237 const char *end = ptr + 1;
1238
1239 SKIP_WHITESPACE(end);
1240 if (')' == *end)
1241 {
1242 if (ZBX_CONST_STRLEN("min") == ptr - start && 0 == strncmp(start, "min", ptr - start))
1243 segment->data.function.type = ZBX_JSONPATH_FUNCTION_MIN;
1244 else if (ZBX_CONST_STRLEN("max") == ptr - start && 0 == strncmp(start, "max", ptr - start))
1245 segment->data.function.type = ZBX_JSONPATH_FUNCTION_MAX;
1246 else if (ZBX_CONST_STRLEN("avg") == ptr - start && 0 == strncmp(start, "avg", ptr - start))
1247 segment->data.function.type = ZBX_JSONPATH_FUNCTION_AVG;
1248 else if (ZBX_CONST_STRLEN("length") == ptr - start && 0 == strncmp(start, "length", ptr - start))
1249 segment->data.function.type = ZBX_JSONPATH_FUNCTION_LENGTH;
1250 else if (ZBX_CONST_STRLEN("first") == ptr - start && 0 == strncmp(start, "first", ptr - start))
1251 segment->data.function.type = ZBX_JSONPATH_FUNCTION_FIRST;
1252 else if (ZBX_CONST_STRLEN("sum") == ptr - start && 0 == strncmp(start, "sum", ptr - start))
1253 segment->data.function.type = ZBX_JSONPATH_FUNCTION_SUM;
1254 else
1255 return zbx_jsonpath_error(start);
1256
1257 segment->type = ZBX_JSONPATH_SEGMENT_FUNCTION;
1258 *next = end + 1;
1259 return SUCCEED;
1260 }
1261 }
1262
1263 if (0 < (len = ptr - start))
1264 {
1265 segment->type = ZBX_JSONPATH_SEGMENT_MATCH_LIST;
1266 segment->data.list.type = ZBX_JSONPATH_LIST_NAME;
1267 segment->data.list.values = jsonpath_list_create_node(len + 1);
1268 zbx_strlcpy(segment->data.list.values->data, start, len + 1);
1269 segment->data.list.values->next = NULL;
1270 *next = start + len;
1271 return SUCCEED;
1272 }
1273
1274 return zbx_jsonpath_error(start);
1275 }
1276
1277 /******************************************************************************
1278 * *
1279 * Function: jsonpath_parse_name_reference *
1280 * *
1281 * Purpose: parse jsonpath name reference ~ *
1282 * *
1283 * Parameters: start - [IN] the segment start *
1284 * jsonpath - [IN/OUT] the jsonpath *
1285 * next - [OUT] a pointer to the next character after parsed *
1286 * segment *
1287 * *
1288 * Return value: SUCCEED - the name reference was parsed *
1289 * *
1290 ******************************************************************************/
jsonpath_parse_name_reference(const char * start,zbx_jsonpath_t * jsonpath,const char ** next)1291 static int jsonpath_parse_name_reference(const char *start, zbx_jsonpath_t *jsonpath, const char **next)
1292 {
1293 zbx_jsonpath_segment_t *segment;
1294
1295 segment = &jsonpath->segments[jsonpath->segments_num];
1296 jsonpath->segments_num++;
1297 segment->type = ZBX_JSONPATH_SEGMENT_FUNCTION;
1298 segment->data.function.type = ZBX_JSONPATH_FUNCTION_NAME;
1299 *next = start + 1;
1300 return SUCCEED;
1301 }
1302
1303 /******************************************************************************
1304 * *
1305 * Function: jsonpath_pointer_to_jp *
1306 * *
1307 * Purpose: convert a pointer to an object/array/value in json data to *
1308 * json parse structure *
1309 * *
1310 * Parameters: pnext - [IN] a pointer to object/array/value data *
1311 * jp - [OUT] json parse data with start/end set *
1312 * *
1313 * Return value: SUCCEED - pointer was converted successfully *
1314 * FAIL - otherwise *
1315 * *
1316 ******************************************************************************/
jsonpath_pointer_to_jp(const char * pnext,struct zbx_json_parse * jp)1317 static int jsonpath_pointer_to_jp(const char *pnext, struct zbx_json_parse *jp)
1318 {
1319 if ('[' == *pnext || '{' == *pnext)
1320 {
1321 return zbx_json_brackets_open(pnext, jp);
1322 }
1323 else
1324 {
1325 jp->start = pnext;
1326 jp->end = pnext + json_parse_value(pnext, NULL) - 1;
1327 return SUCCEED;
1328 }
1329 }
1330
1331 /******************************************************************************
1332 * *
1333 * Function: jsonpath_query_contents *
1334 * *
1335 * Purpose: perform the rest of jsonpath query on json data *
1336 * *
1337 * Parameters: jp_root - [IN] the document root *
1338 * pnext - [IN] a pointer to object/array/value in json data *
1339 * jsonpath - [IN] the jsonpath *
1340 * path_depth - [IN] the jsonpath segment to match *
1341 * objects - [OUT] the matched json elements (name, value) *
1342 * *
1343 * Return value: SUCCEED - the data were queried successfully *
1344 * FAIL - otherwise *
1345 * *
1346 ******************************************************************************/
jsonpath_query_contents(const struct zbx_json_parse * jp_root,const char * pnext,const zbx_jsonpath_t * jsonpath,int path_depth,zbx_vector_json_t * objects)1347 static int jsonpath_query_contents(const struct zbx_json_parse *jp_root, const char *pnext,
1348 const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_json_t *objects)
1349 {
1350 struct zbx_json_parse jp_child;
1351
1352 switch (*pnext)
1353 {
1354 case '{':
1355 if (FAIL == zbx_json_brackets_open(pnext, &jp_child))
1356 return FAIL;
1357
1358 return jsonpath_query_object(jp_root, &jp_child, jsonpath, path_depth, objects);
1359 case '[':
1360 if (FAIL == zbx_json_brackets_open(pnext, &jp_child))
1361 return FAIL;
1362
1363 return jsonpath_query_array(jp_root, &jp_child, jsonpath, path_depth, objects);
1364 }
1365 return SUCCEED;
1366 }
1367
1368 /******************************************************************************
1369 * *
1370 * Function: jsonpath_query_next_segment *
1371 * *
1372 * Purpose: query next segment *
1373 * *
1374 * Parameters: jp_root - [IN] the document root *
1375 * name - [IN] name or index of the next json element *
1376 * pnext - [IN] a pointer to object/array/value in json data *
1377 * jsonpath - [IN] the jsonpath *
1378 * path_depth - [IN] the jsonpath segment to match *
1379 * objects - [OUT] the matched json elements (name, value) *
1380 * *
1381 * Return value: SUCCEED - the segment was queried successfully *
1382 * FAIL - otherwise *
1383 * *
1384 ******************************************************************************/
jsonpath_query_next_segment(const struct zbx_json_parse * jp_root,const char * name,const char * pnext,const zbx_jsonpath_t * jsonpath,int path_depth,zbx_vector_json_t * objects)1385 static int jsonpath_query_next_segment(const struct zbx_json_parse *jp_root, const char *name, const char *pnext,
1386 const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_json_t *objects)
1387 {
1388 /* check if jsonpath end has been reached, so we have found matching data */
1389 /* (functions are processed afterwards) */
1390 if (++path_depth == jsonpath->segments_num ||
1391 ZBX_JSONPATH_SEGMENT_FUNCTION == jsonpath->segments[path_depth].type)
1392 {
1393 zbx_vector_json_add_element(objects, name, pnext);
1394 return SUCCEED;
1395 }
1396
1397 /* continue by matching found data against the rest of jsonpath segments */
1398 return jsonpath_query_contents(jp_root, pnext, jsonpath, path_depth, objects);
1399 }
1400
1401 /******************************************************************************
1402 * *
1403 * Function: jsonpath_match_name *
1404 * *
1405 * Purpose: match object value name against jsonpath segment name list *
1406 * *
1407 * Parameters: jp_root - [IN] the document root *
1408 * name - [IN] name or index of the next json element *
1409 * pnext - [IN] a pointer to object value with the specified *
1410 * name *
1411 * jsonpath - [IN] the jsonpath *
1412 * path_depth - [IN] the jsonpath segment to match *
1413 * objects - [OUT] the matched json elements (name, value) *
1414 * *
1415 * Return value: SUCCEED - no errors, failed match is not an error *
1416 * FAIL - otherwise *
1417 * *
1418 ******************************************************************************/
jsonpath_match_name(const struct zbx_json_parse * jp_root,const char * name,const char * pnext,const zbx_jsonpath_t * jsonpath,int path_depth,zbx_vector_json_t * objects)1419 static int jsonpath_match_name(const struct zbx_json_parse *jp_root, const char *name, const char *pnext,
1420 const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_json_t *objects)
1421 {
1422 const zbx_jsonpath_segment_t *segment = &jsonpath->segments[path_depth];
1423 const zbx_jsonpath_list_node_t *node;
1424
1425 /* object contents can match only name list */
1426 if (ZBX_JSONPATH_LIST_NAME != segment->data.list.type)
1427 return SUCCEED;
1428
1429 for (node = segment->data.list.values; NULL != node; node = node->next)
1430 {
1431 if (0 == strcmp(name, node->data))
1432 {
1433 if (FAIL == jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, objects))
1434 return FAIL;
1435 break;
1436 }
1437 }
1438
1439 return SUCCEED;
1440 }
1441
1442 /******************************************************************************
1443 * *
1444 * Function: jsonpath_extract_value *
1445 * *
1446 * Purpose: extract value from json data by the specified path *
1447 * *
1448 * Parameters: jp - [IN] the parent object *
1449 * path - [IN] the jsonpath (definite) *
1450 * value - [OUT] the extracted value *
1451 * *
1452 * Return value: SUCCEED - the value was extracted successfully *
1453 * FAIL - in the case of errors or if there was no value to *
1454 * extract *
1455 * *
1456 ******************************************************************************/
jsonpath_extract_value(const struct zbx_json_parse * jp,const char * path,zbx_variant_t * value)1457 static int jsonpath_extract_value(const struct zbx_json_parse *jp, const char *path, zbx_variant_t *value)
1458 {
1459 struct zbx_json_parse jp_child;
1460 char *data = NULL, *tmp_path = NULL;
1461 size_t data_alloc = 0;
1462 int ret = FAIL;
1463
1464 if ('@' == *path)
1465 {
1466 tmp_path = zbx_strdup(NULL, path);
1467 *tmp_path = '$';
1468 path = tmp_path;
1469 }
1470
1471 if (FAIL == zbx_json_open_path(jp, path, &jp_child))
1472 goto out;
1473
1474 if (NULL == zbx_json_decodevalue_dyn(jp_child.start, &data, &data_alloc, NULL))
1475 {
1476 size_t len = jp_child.end - jp_child.start + 2;
1477
1478 data = (char *)zbx_malloc(NULL, len);
1479 zbx_strlcpy(data, jp_child.start, len);
1480 }
1481
1482 zbx_variant_set_str(value, data);
1483 ret = SUCCEED;
1484 out:
1485 zbx_free(tmp_path);
1486
1487 return ret;
1488 }
1489
1490 /******************************************************************************
1491 * *
1492 * Function: jsonpath_expression_to_str *
1493 * *
1494 * Purpose: convert jsonpath expression to text format *
1495 * *
1496 * Parameters: expression - [IN] the jsonpath exprssion *
1497 * *
1498 * Return value: The converted expression, must be freed by the caller. *
1499 * *
1500 * Comments: This function is used to include expression in error message. *
1501 * *
1502 ******************************************************************************/
jsonpath_expression_to_str(zbx_jsonpath_expression_t * expression)1503 static char *jsonpath_expression_to_str(zbx_jsonpath_expression_t *expression)
1504 {
1505 int i;
1506 char *str = NULL;
1507 size_t str_alloc = 0, str_offset = 0;
1508
1509 for (i = 0; i < expression->tokens.values_num; i++)
1510 {
1511 zbx_jsonpath_token_t *token = (zbx_jsonpath_token_t *)expression->tokens.values[i];
1512
1513 if (0 != i)
1514 zbx_strcpy_alloc(&str, &str_alloc, &str_offset, ",");
1515
1516 switch (token->type)
1517 {
1518 case ZBX_JSONPATH_TOKEN_PATH_ABSOLUTE:
1519 case ZBX_JSONPATH_TOKEN_PATH_RELATIVE:
1520 case ZBX_JSONPATH_TOKEN_CONST_STR:
1521 case ZBX_JSONPATH_TOKEN_CONST_NUM:
1522 zbx_strcpy_alloc(&str, &str_alloc, &str_offset, token->data);
1523 break;
1524 case ZBX_JSONPATH_TOKEN_PAREN_LEFT:
1525 zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "(");
1526 break;
1527 case ZBX_JSONPATH_TOKEN_PAREN_RIGHT:
1528 zbx_strcpy_alloc(&str, &str_alloc, &str_offset, ")");
1529 break;
1530 case ZBX_JSONPATH_TOKEN_OP_PLUS:
1531 zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "+");
1532 break;
1533 case ZBX_JSONPATH_TOKEN_OP_MINUS:
1534 zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "-");
1535 break;
1536 case ZBX_JSONPATH_TOKEN_OP_MULT:
1537 zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "*");
1538 break;
1539 case ZBX_JSONPATH_TOKEN_OP_DIV:
1540 zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "/");
1541 break;
1542 case ZBX_JSONPATH_TOKEN_OP_EQ:
1543 zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "==");
1544 break;
1545 case ZBX_JSONPATH_TOKEN_OP_NE:
1546 zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "!=");
1547 break;
1548 case ZBX_JSONPATH_TOKEN_OP_GT:
1549 zbx_strcpy_alloc(&str, &str_alloc, &str_offset, ">");
1550 break;
1551 case ZBX_JSONPATH_TOKEN_OP_GE:
1552 zbx_strcpy_alloc(&str, &str_alloc, &str_offset, ">=");
1553 break;
1554 case ZBX_JSONPATH_TOKEN_OP_LT:
1555 zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "<");
1556 break;
1557 case ZBX_JSONPATH_TOKEN_OP_LE:
1558 zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "<=");
1559 break;
1560 case ZBX_JSONPATH_TOKEN_OP_NOT:
1561 zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "!");
1562 break;
1563 case ZBX_JSONPATH_TOKEN_OP_AND:
1564 zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "&&");
1565 break;
1566 case ZBX_JSONPATH_TOKEN_OP_OR:
1567 zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "||");
1568 break;
1569 case ZBX_JSONPATH_TOKEN_OP_REGEXP:
1570 zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "=~");
1571 break;
1572 default:
1573 zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "?");
1574 break;
1575 }
1576 }
1577
1578 return str;
1579 }
1580
1581 /******************************************************************************
1582 * *
1583 * Function: jsonpath_set_expression_error *
1584 * *
1585 * Purpose: set jsonpath expression error message *
1586 * *
1587 * Parameters: expression - [IN] the jsonpath exprssion *
1588 * *
1589 * Comments: This function is used to set error message when expression *
1590 * evaluation fails *
1591 * *
1592 ******************************************************************************/
jsonpath_set_expression_error(zbx_jsonpath_expression_t * expression)1593 static void jsonpath_set_expression_error(zbx_jsonpath_expression_t *expression)
1594 {
1595 char *text;
1596
1597 text = jsonpath_expression_to_str(expression);
1598 zbx_set_json_strerror("invalid compiled expression: %s", text);
1599 zbx_free(text);
1600 }
1601
1602 /******************************************************************************
1603 * *
1604 * Function: jsonpath_variant_to_boolean *
1605 * *
1606 * Purpose: convert variant value to 'boolean' (1, 0) *
1607 * *
1608 * Parameters: value - [IN/OUT] the value *
1609 * *
1610 * Comments: This function is used to cast operand to boolean value for *
1611 * boolean functions (and, or, negation). *
1612 * *
1613 ******************************************************************************/
jsonpath_variant_to_boolean(zbx_variant_t * value)1614 static void jsonpath_variant_to_boolean(zbx_variant_t *value)
1615 {
1616 double res;
1617
1618 switch (value->type)
1619 {
1620 case ZBX_VARIANT_UI64:
1621 res = (0 != value->data.ui64 ? 1 : 0);
1622 break;
1623 case ZBX_VARIANT_DBL:
1624 res = (SUCCEED != zbx_double_compare(value->data.dbl, 0.0) ? 1 : 0);
1625 break;
1626 case ZBX_VARIANT_STR:
1627 res = ('\0' != *value->data.str ? 1 : 0);
1628 break;
1629 case ZBX_VARIANT_NONE:
1630 res = 0;
1631 break;
1632 default:
1633 THIS_SHOULD_NEVER_HAPPEN;
1634 res = 0;
1635 break;
1636 }
1637
1638 zbx_variant_clear(value);
1639 zbx_variant_set_dbl(value, res);
1640 }
1641
1642 /******************************************************************************
1643 * *
1644 * Function: jsonpath_regexp_match *
1645 * *
1646 * Purpose: match text against regular expression *
1647 * *
1648 * Parameters: text - [IN] the text to match *
1649 * pattern - [IN] the regular expression *
1650 * result - [OUT] 1.0 if match succeeded, 0.0 otherwise *
1651 * *
1652 * Return value: SUCCEED - regular expression match was performed *
1653 * FAIL - regular expression error *
1654 * *
1655 ******************************************************************************/
jsonpath_regexp_match(const char * text,const char * pattern,double * result)1656 static int jsonpath_regexp_match(const char *text, const char *pattern, double *result)
1657 {
1658 zbx_regexp_t *rxp;
1659 const char *error = NULL;
1660
1661 if (FAIL == zbx_regexp_compile(pattern, &rxp, &error))
1662 {
1663 zbx_set_json_strerror("invalid regular expression in JSON path: %s", error);
1664 return FAIL;
1665 }
1666 *result = (0 == zbx_regexp_match_precompiled(text, rxp) ? 1.0 : 0.0);
1667 zbx_regexp_free(rxp);
1668
1669 return SUCCEED;
1670 }
1671
1672 /******************************************************************************
1673 * *
1674 * Function: jsonpath_match_expression *
1675 * *
1676 * Purpose: match json array element/object value against jsonpath expression *
1677 * *
1678 * Parameters: jp_root - [IN] the document root *
1679 * name - [IN] name or index of the next json element *
1680 * pnext - [IN] a pointer to array element/object value *
1681 * jsonpath - [IN] the jsonpath *
1682 * path_depth - [IN] the jsonpath segment to match *
1683 * objects - [OUT] the matched json elements (name, value) *
1684 * *
1685 * Return value: SUCCEED - no errors, failed match is not an error *
1686 * FAIL - otherwise *
1687 * *
1688 ******************************************************************************/
jsonpath_match_expression(const struct zbx_json_parse * jp_root,const char * name,const char * pnext,const zbx_jsonpath_t * jsonpath,int path_depth,zbx_vector_json_t * objects)1689 static int jsonpath_match_expression(const struct zbx_json_parse *jp_root, const char *name, const char *pnext,
1690 const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_json_t *objects)
1691 {
1692 struct zbx_json_parse jp;
1693 zbx_vector_var_t stack;
1694 int i, ret = SUCCEED;
1695 zbx_jsonpath_segment_t *segment;
1696 zbx_variant_t value, *right;
1697 double res;
1698
1699 if (SUCCEED != jsonpath_pointer_to_jp(pnext, &jp))
1700 return FAIL;
1701
1702 zbx_vector_var_create(&stack);
1703
1704 segment = &jsonpath->segments[path_depth];
1705
1706 for (i = 0; i < segment->data.expression.tokens.values_num; i++)
1707 {
1708 zbx_variant_t *left;
1709 zbx_jsonpath_token_t *token = (zbx_jsonpath_token_t *)segment->data.expression.tokens.values[i];
1710
1711 if (ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2 == jsonpath_token_group(token->type))
1712 {
1713 if (2 > stack.values_num)
1714 {
1715 jsonpath_set_expression_error(&segment->data.expression);
1716 ret = FAIL;
1717 goto out;
1718 }
1719
1720 left = &stack.values[stack.values_num - 2];
1721 right = &stack.values[stack.values_num - 1];
1722
1723 switch (token->type)
1724 {
1725 case ZBX_JSONPATH_TOKEN_OP_PLUS:
1726 zbx_variant_convert(left, ZBX_VARIANT_DBL);
1727 zbx_variant_convert(right, ZBX_VARIANT_DBL);
1728 left->data.dbl += right->data.dbl;
1729 stack.values_num--;
1730 break;
1731 case ZBX_JSONPATH_TOKEN_OP_MINUS:
1732 zbx_variant_convert(left, ZBX_VARIANT_DBL);
1733 zbx_variant_convert(right, ZBX_VARIANT_DBL);
1734 left->data.dbl -= right->data.dbl;
1735 stack.values_num--;
1736 break;
1737 case ZBX_JSONPATH_TOKEN_OP_MULT:
1738 zbx_variant_convert(left, ZBX_VARIANT_DBL);
1739 zbx_variant_convert(right, ZBX_VARIANT_DBL);
1740 left->data.dbl *= right->data.dbl;
1741 stack.values_num--;
1742 break;
1743 case ZBX_JSONPATH_TOKEN_OP_DIV:
1744 zbx_variant_convert(left, ZBX_VARIANT_DBL);
1745 zbx_variant_convert(right, ZBX_VARIANT_DBL);
1746 left->data.dbl /= right->data.dbl;
1747 stack.values_num--;
1748 break;
1749 case ZBX_JSONPATH_TOKEN_OP_EQ:
1750 res = (0 == zbx_variant_compare(left, right) ? 1.0 : 0.0);
1751 zbx_variant_clear(left);
1752 zbx_variant_clear(right);
1753 zbx_variant_set_dbl(left, res);
1754 stack.values_num--;
1755 break;
1756 case ZBX_JSONPATH_TOKEN_OP_NE:
1757 res = (0 != zbx_variant_compare(left, right) ? 1.0 : 0.0);
1758 zbx_variant_clear(left);
1759 zbx_variant_clear(right);
1760 zbx_variant_set_dbl(left, res);
1761 stack.values_num--;
1762 break;
1763 case ZBX_JSONPATH_TOKEN_OP_GT:
1764 res = (0 < zbx_variant_compare(left, right) ? 1.0 : 0.0);
1765 zbx_variant_clear(left);
1766 zbx_variant_clear(right);
1767 zbx_variant_set_dbl(left, res);
1768 stack.values_num--;
1769 break;
1770 case ZBX_JSONPATH_TOKEN_OP_GE:
1771 res = (0 <= zbx_variant_compare(left, right) ? 1.0 : 0.0);
1772 zbx_variant_clear(left);
1773 zbx_variant_clear(right);
1774 zbx_variant_set_dbl(left, res);
1775 stack.values_num--;
1776 break;
1777 case ZBX_JSONPATH_TOKEN_OP_LT:
1778 res = (0 > zbx_variant_compare(left, right) ? 1.0 : 0.0);
1779 zbx_variant_clear(left);
1780 zbx_variant_clear(right);
1781 zbx_variant_set_dbl(left, res);
1782 stack.values_num--;
1783 break;
1784 case ZBX_JSONPATH_TOKEN_OP_LE:
1785 res = (0 >= zbx_variant_compare(left, right) ? 1.0 : 0.0);
1786 zbx_variant_clear(left);
1787 zbx_variant_clear(right);
1788 zbx_variant_set_dbl(left, res);
1789 stack.values_num--;
1790 break;
1791 case ZBX_JSONPATH_TOKEN_OP_AND:
1792 jsonpath_variant_to_boolean(left);
1793 jsonpath_variant_to_boolean(right);
1794 if (SUCCEED != zbx_double_compare(left->data.dbl, 0.0) &&
1795 SUCCEED != zbx_double_compare(right->data.dbl, 0.0))
1796 {
1797 res = 1.0;
1798 }
1799 else
1800 res = 0.0;
1801 zbx_variant_set_dbl(left, res);
1802 zbx_variant_clear(right);
1803 stack.values_num--;
1804 break;
1805 case ZBX_JSONPATH_TOKEN_OP_OR:
1806 jsonpath_variant_to_boolean(left);
1807 jsonpath_variant_to_boolean(right);
1808 if (SUCCEED != zbx_double_compare(left->data.dbl, 0.0) ||
1809 SUCCEED != zbx_double_compare(right->data.dbl, 0.0))
1810 {
1811 res = 1.0;
1812 }
1813 else
1814 {
1815 res = 0.0;
1816 }
1817 zbx_variant_set_dbl(left, res);
1818 zbx_variant_clear(right);
1819 stack.values_num--;
1820 break;
1821 case ZBX_JSONPATH_TOKEN_OP_REGEXP:
1822 if (FAIL == zbx_variant_convert(left, ZBX_VARIANT_STR) ||
1823 FAIL == zbx_variant_convert(right, ZBX_VARIANT_STR))
1824 {
1825 res = 0.0;
1826 ret = SUCCEED;
1827 }
1828 else
1829 {
1830 ret = jsonpath_regexp_match(left->data.str, right->data.str, &res);
1831 }
1832
1833 zbx_variant_clear(left);
1834 zbx_variant_clear(right);
1835
1836 if (FAIL == ret)
1837 goto out;
1838
1839 zbx_variant_set_dbl(left, res);
1840 stack.values_num--;
1841 break;
1842 default:
1843 break;
1844 }
1845 continue;
1846 }
1847
1848 switch (token->type)
1849 {
1850 case ZBX_JSONPATH_TOKEN_PATH_ABSOLUTE:
1851 if (FAIL == jsonpath_extract_value(jp_root, token->data, &value))
1852 zbx_variant_set_none(&value);
1853 zbx_vector_var_append_ptr(&stack, &value);
1854 break;
1855 case ZBX_JSONPATH_TOKEN_PATH_RELATIVE:
1856 /* relative path can be applied only to array or object */
1857 if ('[' != *jp.start && '{' != *jp.start)
1858 goto out;
1859
1860 if (FAIL == jsonpath_extract_value(&jp, token->data, &value))
1861 zbx_variant_set_none(&value);
1862 zbx_vector_var_append_ptr(&stack, &value);
1863 break;
1864 case ZBX_JSONPATH_TOKEN_CONST_STR:
1865 zbx_variant_set_str(&value, zbx_strdup(NULL, token->data));
1866 zbx_vector_var_append_ptr(&stack, &value);
1867 break;
1868 case ZBX_JSONPATH_TOKEN_CONST_NUM:
1869 zbx_variant_set_dbl(&value, atof(token->data));
1870 zbx_vector_var_append_ptr(&stack, &value);
1871 break;
1872 case ZBX_JSONPATH_TOKEN_OP_NOT:
1873 if (1 > stack.values_num)
1874 {
1875 jsonpath_set_expression_error(&segment->data.expression);
1876 ret = FAIL;
1877 goto out;
1878 }
1879 right = &stack.values[stack.values_num - 1];
1880 jsonpath_variant_to_boolean(right);
1881 right->data.dbl = 1 - right->data.dbl;
1882 break;
1883 default:
1884 break;
1885 }
1886 }
1887
1888 if (1 != stack.values_num)
1889 {
1890 jsonpath_set_expression_error(&segment->data.expression);
1891 goto out;
1892 }
1893
1894 jsonpath_variant_to_boolean(&stack.values[0]);
1895 if (SUCCEED != zbx_double_compare(stack.values[0].data.dbl, 0.0))
1896 ret = jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, objects);
1897 out:
1898 for (i = 0; i < stack.values_num; i++)
1899 zbx_variant_clear(&stack.values[i]);
1900 zbx_vector_var_destroy(&stack);
1901
1902 return ret;
1903 }
1904
1905 /******************************************************************************
1906 * *
1907 * Function: jsonpath_query_object *
1908 * *
1909 * Purpose: query object fields for jsonpath segment match *
1910 * *
1911 * Parameters: jp_root - [IN] the document root *
1912 * jp - [IN] the json object to query *
1913 * jsonpath - [IN] the jsonpath *
1914 * path_depth - [IN] the jsonpath segment to match *
1915 * objects - [OUT] the matched json elements (name, value) *
1916 * *
1917 * Return value: SUCCEED - the object was queried successfully *
1918 * FAIL - otherwise *
1919 * *
1920 ******************************************************************************/
jsonpath_query_object(const struct zbx_json_parse * jp_root,const struct zbx_json_parse * jp,const zbx_jsonpath_t * jsonpath,int path_depth,zbx_vector_json_t * objects)1921 static int jsonpath_query_object(const struct zbx_json_parse *jp_root, const struct zbx_json_parse *jp,
1922 const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_json_t *objects)
1923 {
1924 const char *pnext = NULL;
1925 char name[MAX_STRING_LEN];
1926 const zbx_jsonpath_segment_t *segment;
1927 int ret = SUCCEED;
1928
1929 segment = &jsonpath->segments[path_depth];
1930
1931 while (NULL != (pnext = zbx_json_pair_next(jp, pnext, name, sizeof(name))) && SUCCEED == ret)
1932 {
1933 switch (segment->type)
1934 {
1935 case ZBX_JSONPATH_SEGMENT_MATCH_ALL:
1936 ret = jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, objects);
1937 break;
1938 case ZBX_JSONPATH_SEGMENT_MATCH_LIST:
1939 ret = jsonpath_match_name(jp_root, name, pnext, jsonpath, path_depth, objects);
1940 break;
1941 case ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION:
1942 ret = jsonpath_match_expression(jp_root, name, pnext, jsonpath, path_depth, objects);
1943 break;
1944 default:
1945 break;
1946 }
1947
1948 if (1 == segment->detached)
1949 ret = jsonpath_query_contents(jp_root, pnext, jsonpath, path_depth, objects);
1950 }
1951
1952 return ret;
1953 }
1954
1955 /******************************************************************************
1956 * *
1957 * Function: jsonpath_match_index *
1958 * *
1959 * Purpose: match array element against segment index list *
1960 * *
1961 * Parameters: jp_root - [IN] the document root *
1962 * name - [IN] the json element name (index) *
1963 * pnext - [IN] a pointer to an array element *
1964 * jsonpath - [IN] the jsonpath *
1965 * path_depth - [IN] the jsonpath segment to match *
1966 * index - [IN] the array element index *
1967 * elements_num - [IN] the total number of elements in array *
1968 * objects - [OUT] the matched json elements (name, value) *
1969 * *
1970 * Return value: SUCCEED - no errors, failed match is not an error *
1971 * FAIL - otherwise *
1972 * *
1973 ******************************************************************************/
jsonpath_match_index(const struct zbx_json_parse * jp_root,const char * name,const char * pnext,const zbx_jsonpath_t * jsonpath,int path_depth,int index,int elements_num,zbx_vector_json_t * objects)1974 static int jsonpath_match_index(const struct zbx_json_parse *jp_root, const char *name, const char *pnext,
1975 const zbx_jsonpath_t *jsonpath, int path_depth, int index, int elements_num, zbx_vector_json_t *objects)
1976 {
1977 const zbx_jsonpath_segment_t *segment = &jsonpath->segments[path_depth];
1978 const zbx_jsonpath_list_node_t *node;
1979
1980 /* array contents can match only index list */
1981 if (ZBX_JSONPATH_LIST_INDEX != segment->data.list.type)
1982 return SUCCEED;
1983
1984 for (node = segment->data.list.values; NULL != node; node = node->next)
1985 {
1986 int query_index;
1987
1988 memcpy(&query_index, node->data, sizeof(query_index));
1989
1990 if ((query_index >= 0 && index == query_index) || index == elements_num + query_index)
1991 {
1992 if (FAIL == jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, objects))
1993 return FAIL;
1994 break;
1995 }
1996 }
1997
1998 return SUCCEED;
1999 }
2000
2001 /******************************************************************************
2002 * *
2003 * Function: jsonpath_match_range *
2004 * *
2005 * Purpose: match array element against segment index range *
2006 * *
2007 * Parameters: jp_root - [IN] the document root *
2008 * name - [IN] the json element name (index) *
2009 * pnext - [IN] a pointer to an array element *
2010 * jsonpath - [IN] the jsonpath *
2011 * path_depth - [IN] the jsonpath segment to match *
2012 * index - [IN] the array element index *
2013 * elements_num - [IN] the total number of elements in array *
2014 * objects - [OUT] the matched json elements (name, value) *
2015 * *
2016 * Return value: SUCCEED - no errors, failed match is not an error *
2017 * FAIL - otherwise *
2018 * *
2019 ******************************************************************************/
jsonpath_match_range(const struct zbx_json_parse * jp_root,const char * name,const char * pnext,const zbx_jsonpath_t * jsonpath,int path_depth,int index,int elements_num,zbx_vector_json_t * objects)2020 static int jsonpath_match_range(const struct zbx_json_parse *jp_root, const char *name, const char *pnext,
2021 const zbx_jsonpath_t *jsonpath, int path_depth, int index, int elements_num, zbx_vector_json_t *objects)
2022 {
2023 int start_index, end_index;
2024 const zbx_jsonpath_segment_t *segment = &jsonpath->segments[path_depth];
2025
2026 start_index = (0 != (segment->data.range.flags & 0x01) ? segment->data.range.start : 0);
2027 end_index = (0 != (segment->data.range.flags & 0x02) ? segment->data.range.end : elements_num);
2028
2029 if (0 > start_index)
2030 start_index += elements_num;
2031 if (0 > end_index)
2032 end_index += elements_num;
2033
2034 if (start_index <= index && end_index > index)
2035 {
2036 if (FAIL == jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, objects))
2037 return FAIL;
2038 }
2039
2040 return SUCCEED;
2041 }
2042
2043 /******************************************************************************
2044 * *
2045 * Function: jsonpath_query_array *
2046 * *
2047 * Purpose: query array elements for jsonpath segment match *
2048 * *
2049 * Parameters: jp_root - [IN] the document root *
2050 * jp - [IN] the json array to query *
2051 * jsonpath - [IN] the jsonpath *
2052 * path_depth - [IN] the jsonpath segment to match *
2053 * objects - [OUT] the matched json elements (name, value) *
2054 * *
2055 * Return value: SUCCEED - the array was queried successfully *
2056 * FAIL - otherwise *
2057 * *
2058 ******************************************************************************/
jsonpath_query_array(const struct zbx_json_parse * jp_root,const struct zbx_json_parse * jp,const zbx_jsonpath_t * jsonpath,int path_depth,zbx_vector_json_t * objects)2059 static int jsonpath_query_array(const struct zbx_json_parse *jp_root, const struct zbx_json_parse *jp,
2060 const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_json_t *objects)
2061 {
2062 const char *pnext = NULL;
2063 int index = 0, elements_num = 0, ret = SUCCEED;
2064 zbx_jsonpath_segment_t *segment;
2065
2066 segment = &jsonpath->segments[path_depth];
2067
2068 while (NULL != (pnext = zbx_json_next(jp, pnext)))
2069 elements_num++;
2070
2071 while (NULL != (pnext = zbx_json_next(jp, pnext)) && SUCCEED == ret)
2072 {
2073 char name[MAX_ID_LEN + 1];
2074
2075 zbx_snprintf(name, sizeof(name), "%d", index);
2076 switch (segment->type)
2077 {
2078 case ZBX_JSONPATH_SEGMENT_MATCH_ALL:
2079 ret = jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, objects);
2080 break;
2081 case ZBX_JSONPATH_SEGMENT_MATCH_LIST:
2082 ret = jsonpath_match_index(jp_root, name, pnext, jsonpath, path_depth, index,
2083 elements_num, objects);
2084 break;
2085 case ZBX_JSONPATH_SEGMENT_MATCH_RANGE:
2086 ret = jsonpath_match_range(jp_root, name, pnext, jsonpath, path_depth, index,
2087 elements_num, objects);
2088 break;
2089 case ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION:
2090 ret = jsonpath_match_expression(jp_root, name, pnext, jsonpath, path_depth, objects);
2091 break;
2092 default:
2093 break;
2094 }
2095
2096 if (1 == segment->detached)
2097 ret = jsonpath_query_contents(jp_root, pnext, jsonpath, path_depth, objects);
2098
2099 index++;
2100 }
2101
2102 return ret;
2103 }
2104
2105 /******************************************************************************
2106 * *
2107 * Function: jsonpath_extract_element *
2108 * *
2109 * Purpose: extract JSON element value from data *
2110 * *
2111 * Parameters: ptr - [IN] pointer to the element to extract *
2112 * element - [OUT] the extracted element *
2113 * *
2114 * Return value: SUCCEED - the element was extracted successfully *
2115 * FAIL - the pointer was not pointing to a JSON element *
2116 * *
2117 * Comments: String value element is unquoted, other elements are copied as *
2118 * is. *
2119 * *
2120 ******************************************************************************/
jsonpath_extract_element(const char * ptr,char ** element)2121 static int jsonpath_extract_element(const char *ptr, char **element)
2122 {
2123 size_t element_size = 0;
2124
2125 if (NULL == zbx_json_decodevalue_dyn(ptr, element, &element_size, NULL))
2126 {
2127 struct zbx_json_parse jp;
2128
2129 if (SUCCEED != zbx_json_brackets_open(ptr, &jp))
2130 return FAIL;
2131
2132 *element = jsonpath_strndup(jp.start, jp.end - jp.start + 1);
2133 }
2134
2135 return SUCCEED;
2136 }
2137
2138 /******************************************************************************
2139 * *
2140 * Function: jsonpath_extract_numeric_value *
2141 * *
2142 * Purpose: extract numeric value from json data *
2143 * *
2144 * Parameters: ptr - [IN] pointer to the value to extract *
2145 * value - [OUT] the extracted value *
2146 * *
2147 * Return value: SUCCEED - the value was extracted successfully *
2148 * FAIL - the pointer was not pointing at numeric value *
2149 * *
2150 ******************************************************************************/
jsonpath_extract_numeric_value(const char * ptr,double * value)2151 static int jsonpath_extract_numeric_value(const char *ptr, double *value)
2152 {
2153 char buffer[MAX_STRING_LEN];
2154
2155 if (NULL == zbx_json_decodevalue(ptr, buffer, sizeof(buffer), NULL) ||
2156 SUCCEED != is_double(buffer, value))
2157 {
2158 zbx_set_json_strerror("array value is not a number or out of range starting with: %s", ptr);
2159 return FAIL;
2160 }
2161
2162 return SUCCEED;
2163 }
2164
2165 /******************************************************************************
2166 * *
2167 * Function: jsonpath_apply_function *
2168 * *
2169 * Purpose: apply jsonpath function to the extracted object list *
2170 * *
2171 * Parameters: objects - [IN] the matched json elements (name, value) *
2172 * type - [IN] the function type *
2173 * definite_path - [IN] 1 - if the path is definite (pointing at *
2174 * single object) *
2175 * 0 - otherwise *
2176 * output - [OUT] the output value *
2177 * *
2178 * Return value: SUCCEED - the function was applied successfully *
2179 * FAIL - invalid input data for the function or internal *
2180 * json error *
2181 * *
2182 ******************************************************************************/
jsonpath_apply_function(const zbx_vector_json_t * objects,zbx_jsonpath_function_type_t type,int definite_path,char ** output)2183 static int jsonpath_apply_function(const zbx_vector_json_t *objects, zbx_jsonpath_function_type_t type,
2184 int definite_path, char **output)
2185 {
2186 int i, ret = FAIL;
2187 zbx_vector_json_t objects_tmp;
2188 double result;
2189
2190 zbx_vector_json_create(&objects_tmp);
2191
2192 if (ZBX_JSONPATH_FUNCTION_NAME == type)
2193 {
2194 if (0 == objects->values_num)
2195 {
2196 zbx_set_json_strerror("cannot extract name from empty result");
2197 goto out;
2198 }
2199
2200 /* For definite paths we have single output value, so return its name. */
2201 /* Otherwise return array of all output element names. */
2202 if (0 == definite_path)
2203 {
2204 struct zbx_json j;
2205
2206 /* reserve some space for output json, 1k being large enough to satisfy most queries */
2207 zbx_json_initarray(&j, 1024);
2208 for (i = 0; i < objects->values_num; i++)
2209 zbx_json_addstring(&j, NULL, objects->values[i].name, ZBX_JSON_TYPE_STRING);
2210
2211 zbx_json_close(&j);
2212 *output = zbx_strdup(NULL, j.buffer);
2213 zbx_json_clean(&j);
2214 }
2215 else
2216 *output = zbx_strdup(NULL, objects->values[0].name);
2217
2218 ret = SUCCEED;
2219 goto out;
2220 }
2221
2222 /* convert definite path result to object array if possible */
2223 if (0 != definite_path)
2224 {
2225 const char *pnext;
2226 struct zbx_json_parse jp;
2227 int index = 0;
2228
2229 if (0 == objects->values_num || '[' != *objects->values[0].value)
2230 {
2231 /* all functions can be applied only to arrays */
2232 /* attempt to apply a function to non-array will fail */
2233 zbx_set_json_strerror("cannot apply function to non-array JSON element");
2234 goto out;
2235 }
2236
2237 if (FAIL == zbx_json_brackets_open(objects->values[0].value, &jp))
2238 goto out;
2239
2240 for (pnext = NULL; NULL != (pnext = zbx_json_next(&jp, pnext));)
2241 {
2242 char name[MAX_ID_LEN + 1];
2243
2244 zbx_snprintf(name, sizeof(name), "%d", index++);
2245 zbx_vector_json_add_element(&objects_tmp, name, pnext);
2246 }
2247
2248 objects = &objects_tmp;
2249 }
2250
2251 if (ZBX_JSONPATH_FUNCTION_LENGTH == type)
2252 {
2253 *output = zbx_dsprintf(NULL, "%d", objects->values_num);
2254 ret = SUCCEED;
2255 goto out;
2256 }
2257
2258 if (ZBX_JSONPATH_FUNCTION_FIRST == type)
2259 {
2260 if (0 < objects->values_num)
2261 ret = jsonpath_extract_element(objects->values[0].value, output);
2262 else
2263 ret = SUCCEED;
2264
2265 goto out;
2266 }
2267
2268 if (0 == objects->values_num)
2269 {
2270 zbx_set_json_strerror("cannot apply aggregation function to empty array");
2271 goto out;
2272 }
2273
2274 if (FAIL == jsonpath_extract_numeric_value(objects->values[0].value, &result))
2275 goto out;
2276
2277 for (i = 1; i < objects->values_num; i++)
2278 {
2279 double value;
2280
2281 if (FAIL == jsonpath_extract_numeric_value(objects->values[i].value, &value))
2282 goto out;
2283
2284 switch (type)
2285 {
2286 case ZBX_JSONPATH_FUNCTION_MIN:
2287 if (value < result)
2288 result = value;
2289 break;
2290 case ZBX_JSONPATH_FUNCTION_MAX:
2291 if (value > result)
2292 result = value;
2293 break;
2294 case ZBX_JSONPATH_FUNCTION_AVG:
2295 case ZBX_JSONPATH_FUNCTION_SUM:
2296 result += value;
2297 break;
2298 default:
2299 break;
2300 }
2301 }
2302
2303 if (ZBX_JSONPATH_FUNCTION_AVG == type)
2304 result /= objects->values_num;
2305
2306 *output = zbx_dsprintf(NULL, ZBX_FS_DBL, result);
2307 if (SUCCEED != is_double(*output, NULL))
2308 {
2309 zbx_set_json_strerror("invalid function result: %s", *output);
2310 goto out;
2311 }
2312 del_zeros(*output);
2313 ret = SUCCEED;
2314 out:
2315 zbx_vector_json_clear_ext(&objects_tmp);
2316 zbx_vector_json_destroy(&objects_tmp);
2317
2318 return ret;
2319 }
2320
2321 /******************************************************************************
2322 * *
2323 * Function: jsonpath_apply_functions *
2324 * *
2325 * Purpose: apply jsonpath function to the extracted object list *
2326 * *
2327 * Parameters: jp_root - [IN] the document root *
2328 * objects - [IN] the matched json elements (name, value) *
2329 * jsonpath - [IN] the jsonpath *
2330 * path_depth - [IN] the jsonpath segment to match *
2331 * output - [OUT] the output value *
2332 * *
2333 * Return value: SUCCEED - the function was applied successfully *
2334 * FAIL - invalid input data for the function or internal *
2335 * json error *
2336 * *
2337 ******************************************************************************/
jsonpath_apply_functions(const struct zbx_json_parse * jp_root,const zbx_vector_json_t * objects,const zbx_jsonpath_t * jsonpath,int path_depth,char ** output)2338 static int jsonpath_apply_functions(const struct zbx_json_parse *jp_root, const zbx_vector_json_t *objects,
2339 const zbx_jsonpath_t *jsonpath, int path_depth, char **output)
2340 {
2341 int ret, definite_path;
2342 zbx_vector_json_t input;
2343 char *input_json = NULL;
2344
2345 zbx_vector_json_create(&input);
2346
2347 /* when functions are applied directly to the json document (at the start of the jsonpath ) */
2348 /* it makes all document as input object */
2349 if (0 == path_depth)
2350 zbx_vector_json_add_element(&input, "", jp_root->start);
2351 else
2352 zbx_vector_json_copy(&input, objects);
2353
2354 definite_path = jsonpath->definite;
2355
2356 for (;;)
2357 {
2358 ret = jsonpath_apply_function(&input, jsonpath->segments[path_depth++].data.function.type,
2359 definite_path, output);
2360
2361 zbx_vector_json_clear_ext(&input);
2362 zbx_free(input_json);
2363
2364 if (SUCCEED != ret || path_depth == jsonpath->segments_num)
2365 break;
2366
2367 if (NULL != *output)
2368 {
2369 zbx_vector_json_add_element(&input, "", *output);
2370 input_json = *output;
2371 *output = NULL;
2372 }
2373
2374 /* functions return single value, so for the next functions path becomes definite */
2375 definite_path = 1;
2376 }
2377
2378 zbx_vector_json_destroy(&input);
2379
2380 return ret;
2381 }
2382
2383 /******************************************************************************
2384 * *
2385 * Function: jsonpath_format_query_result *
2386 * *
2387 * Purpose: format query result, depending on jsonpath type *
2388 * *
2389 * Parameters: objects - [IN] the matched json elements (name, value) *
2390 * jsonpath - [IN] the jsonpath used to acquire result *
2391 * output - [OUT] the output value *
2392 * *
2393 * Return value: SUCCEED - the result was formatted successfully *
2394 * FAIL - invalid result data (internal json error) *
2395 * *
2396 ******************************************************************************/
jsonpath_format_query_result(const zbx_vector_json_t * objects,zbx_jsonpath_t * jsonpath,char ** output)2397 static int jsonpath_format_query_result(const zbx_vector_json_t *objects, zbx_jsonpath_t *jsonpath, char **output)
2398 {
2399 size_t output_offset = 0, output_alloc;
2400 int i;
2401
2402 if (0 == objects->values_num)
2403 return SUCCEED;
2404
2405 if (1 == jsonpath->definite)
2406 {
2407 return jsonpath_extract_element(objects->values[0].value, output);
2408 }
2409
2410 /* reserve 32 bytes per returned object plus array start/end [] and terminating zero */
2411 output_alloc = objects->values_num * 32 + 3;
2412 *output = (char *)zbx_malloc(NULL, output_alloc);
2413
2414 zbx_chrcpy_alloc(output, &output_alloc, &output_offset, '[');
2415
2416 for (i = 0; i < objects->values_num; i++)
2417 {
2418 struct zbx_json_parse jp;
2419
2420 if (FAIL == jsonpath_pointer_to_jp(objects->values[i].value, &jp))
2421 {
2422 zbx_set_json_strerror("cannot format query result, unrecognized json part starting with: %s",
2423 objects->values[i].value);
2424 zbx_free(*output);
2425 return FAIL;
2426 }
2427
2428 if (0 != i)
2429 zbx_chrcpy_alloc(output, &output_alloc, &output_offset, ',');
2430
2431 zbx_strncpy_alloc(output, &output_alloc, &output_offset, jp.start, jp.end - jp.start + 1);
2432 }
2433
2434 zbx_chrcpy_alloc(output, &output_alloc, &output_offset, ']');
2435
2436 return SUCCEED;
2437 }
2438
2439 /******************************************************************************
2440 * *
2441 * Function: zbx_jsonpath_clear *
2442 * *
2443 ******************************************************************************/
zbx_jsonpath_clear(zbx_jsonpath_t * jsonpath)2444 void zbx_jsonpath_clear(zbx_jsonpath_t *jsonpath)
2445 {
2446 int i;
2447
2448 for (i = 0; i < jsonpath->segments_num; i++)
2449 jsonpath_segment_clear(&jsonpath->segments[i]);
2450
2451 zbx_free(jsonpath->segments);
2452 }
2453
2454 /******************************************************************************
2455 * *
2456 * Function: zbx_jsonpath_compile *
2457 * *
2458 * Purpose: compile jsonpath to be used in queries *
2459 * *
2460 * Parameters: path - [IN] the path to parse *
2461 * jsonpath - [IN/OUT] the compiled jsonpath *
2462 * *
2463 * Return value: SUCCEED - the segment was parsed successfully *
2464 * FAIL - otherwise *
2465 * *
2466 ******************************************************************************/
zbx_jsonpath_compile(const char * path,zbx_jsonpath_t * jsonpath)2467 int zbx_jsonpath_compile(const char *path, zbx_jsonpath_t *jsonpath)
2468 {
2469 int ret = FAIL;
2470 const char *ptr = path, *next;
2471 zbx_jsonpath_segment_type_t segment_type, last_segment_type = ZBX_JSONPATH_SEGMENT_UNKNOWN;
2472 zbx_jsonpath_t jpquery;
2473
2474 if ('$' != *ptr || '\0' == ptr[1])
2475 {
2476 zbx_set_json_strerror("JSONPath query must start with the root object/element $.");
2477 return FAIL;
2478 }
2479
2480 memset(&jpquery, 0, sizeof(zbx_jsonpath_t));
2481 jsonpath_reserve(&jpquery, 4);
2482 jpquery.definite = 1;
2483
2484 for (ptr++; '\0' != *ptr; ptr = next)
2485 {
2486 char prefix;
2487
2488 jsonpath_reserve(&jpquery, 1);
2489
2490 if ('.' == (prefix = *ptr))
2491 {
2492 if ('.' == *(++ptr))
2493 {
2494 /* mark next segment as detached */
2495 zbx_jsonpath_segment_t *segment = &jpquery.segments[jpquery.segments_num];
2496
2497 if (1 != segment->detached)
2498 {
2499 segment->detached = 1;
2500 jpquery.definite = 0;
2501 ptr++;
2502 }
2503 }
2504
2505 switch (*ptr)
2506 {
2507 case '[':
2508 prefix = *ptr;
2509 break;
2510 case '\0':
2511 case '.':
2512 prefix = 0;
2513 break;
2514 }
2515 }
2516
2517 switch (prefix)
2518 {
2519 case '.':
2520 ret = jsonpath_parse_dot_segment(ptr, &jpquery, &next);
2521 break;
2522 case '[':
2523 ret = jsonpath_parse_bracket_segment(ptr + 1, &jpquery, &next);
2524 break;
2525 case '~':
2526 ret = jsonpath_parse_name_reference(ptr, &jpquery, &next);
2527 break;
2528 default:
2529 ret = zbx_jsonpath_error(ptr);
2530 break;
2531 }
2532
2533 if (SUCCEED != ret)
2534 break;
2535
2536 /* function segments can followed only by function segments */
2537 segment_type = jpquery.segments[jpquery.segments_num - 1].type;
2538 if (ZBX_JSONPATH_SEGMENT_FUNCTION == last_segment_type && ZBX_JSONPATH_SEGMENT_FUNCTION != segment_type)
2539 {
2540 ret = zbx_jsonpath_error(ptr);
2541 break;
2542 }
2543 last_segment_type = segment_type;
2544 }
2545
2546 if (SUCCEED == ret && 0 == jpquery.segments_num)
2547 ret = zbx_jsonpath_error(ptr);
2548
2549 if (SUCCEED == ret)
2550 *jsonpath = jpquery;
2551 else
2552 zbx_jsonpath_clear(&jpquery);
2553
2554 return ret;
2555 }
2556
2557 /******************************************************************************
2558 * *
2559 * Function: zbx_jsonpath_query *
2560 * *
2561 * Purpose: perform jsonpath query on the specified json data *
2562 * *
2563 * Parameters: jp - [IN] the json data *
2564 * path - [IN] the jsonpath *
2565 * output - [OUT] the output value *
2566 * *
2567 * Return value: SUCCEED - the query was performed successfully (empty result *
2568 * being counted as successful query) *
2569 * FAIL - otherwise *
2570 * *
2571 ******************************************************************************/
zbx_jsonpath_query(const struct zbx_json_parse * jp,const char * path,char ** output)2572 int zbx_jsonpath_query(const struct zbx_json_parse *jp, const char *path, char **output)
2573 {
2574 zbx_jsonpath_t jsonpath;
2575 int path_depth = 0, ret = SUCCEED;
2576 zbx_vector_json_t objects;
2577
2578 if (FAIL == zbx_jsonpath_compile(path, &jsonpath))
2579 return FAIL;
2580
2581 zbx_vector_json_create(&objects);
2582
2583 if ('{' == *jp->start)
2584 ret = jsonpath_query_object(jp, jp, &jsonpath, path_depth, &objects);
2585 else if ('[' == *jp->start)
2586 ret = jsonpath_query_array(jp, jp, &jsonpath, path_depth, &objects);
2587
2588 if (SUCCEED == ret)
2589 {
2590 path_depth = jsonpath.segments_num;
2591 while (0 < path_depth && ZBX_JSONPATH_SEGMENT_FUNCTION == jsonpath.segments[path_depth - 1].type)
2592 path_depth--;
2593
2594 if (path_depth < jsonpath.segments_num)
2595 ret = jsonpath_apply_functions(jp, &objects, &jsonpath, path_depth, output);
2596 else
2597 ret = jsonpath_format_query_result(&objects, &jsonpath, output);
2598 }
2599
2600 zbx_vector_json_clear_ext(&objects);
2601 zbx_vector_json_destroy(&objects);
2602 zbx_jsonpath_clear(&jsonpath);
2603
2604 return ret;
2605 }
2606