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