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