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