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 "threads.h"
22 #include "module.h"
23 
24 #include "zbxcrypto.h"
25 
26 #ifdef HAVE_ICONV
27 #	include <iconv.h>
28 #endif
29 
30 static const char	copyright_message[] =
31 	"Copyright (C) 2021 Zabbix SIA\n"
32 	"License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>.\n"
33 	"This is free software: you are free to change and redistribute it according to\n"
34 	"the license. There is NO WARRANTY, to the extent permitted by law.";
35 
36 static const char	help_message_footer[] =
37 	"Report bugs to: <https://support.zabbix.com>\n"
38 	"Zabbix home page: <http://www.zabbix.com>\n"
39 	"Documentation: <https://www.zabbix.com/documentation>";
40 
41 /******************************************************************************
42  *                                                                            *
43  * Function: version                                                          *
44  *                                                                            *
45  * Purpose: print version and compilation time of application on stdout       *
46  *          by application request with parameter '-V'                        *
47  *                                                                            *
48  * Author: Eugene Grigorjev                                                   *
49  *                                                                            *
50  * Comments:  title_message - is global variable which must be initialized    *
51  *                            in each zabbix application                      *
52  *                                                                            *
53  ******************************************************************************/
version(void)54 void	version(void)
55 {
56 	printf("%s (Zabbix) %s\n", title_message, ZABBIX_VERSION);
57 	printf("Revision %s %s, compilation time: %s %s\n\n", ZABBIX_REVISION, ZABBIX_REVDATE, __DATE__, __TIME__);
58 	puts(copyright_message);
59 #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL)
60 	printf("\n");
61 	zbx_tls_version();
62 #endif
63 }
64 
65 /******************************************************************************
66  *                                                                            *
67  * Function: usage                                                            *
68  *                                                                            *
69  * Purpose: print application parameters on stdout with layout suitable for   *
70  *          80-column terminal                                                *
71  *                                                                            *
72  * Author: Eugene Grigorjev                                                   *
73  *                                                                            *
74  * Comments:  usage_message - is global variable which must be initialized    *
75  *                            in each zabbix application                      *
76  *                                                                            *
77  ******************************************************************************/
usage(void)78 void	usage(void)
79 {
80 #define ZBX_MAXCOL	79
81 #define ZBX_SPACE1	"  "			/* left margin for the first line */
82 #define ZBX_SPACE2	"               "	/* left margin for subsequent lines */
83 	const char	**p = usage_message;
84 
85 	if (NULL != *p)
86 		printf("usage:\n");
87 
88 	while (NULL != *p)
89 	{
90 		size_t	pos;
91 
92 		printf("%s%s", ZBX_SPACE1, progname);
93 		pos = ZBX_CONST_STRLEN(ZBX_SPACE1) + strlen(progname);
94 
95 		while (NULL != *p)
96 		{
97 			size_t	len;
98 
99 			len = strlen(*p);
100 
101 			if (ZBX_MAXCOL > pos + len)
102 			{
103 				pos += len + 1;
104 				printf(" %s", *p);
105 			}
106 			else
107 			{
108 				pos = ZBX_CONST_STRLEN(ZBX_SPACE2) + len + 1;
109 				printf("\n%s %s", ZBX_SPACE2, *p);
110 			}
111 
112 			p++;
113 		}
114 
115 		printf("\n");
116 		p++;
117 	}
118 #undef ZBX_MAXCOL
119 #undef ZBX_SPACE1
120 #undef ZBX_SPACE2
121 }
122 
123 /******************************************************************************
124  *                                                                            *
125  * Function: help                                                             *
126  *                                                                            *
127  * Purpose: print help of application parameters on stdout by application     *
128  *          request with parameter '-h'                                       *
129  *                                                                            *
130  * Author: Eugene Grigorjev                                                   *
131  *                                                                            *
132  * Comments:  help_message - is global variable which must be initialized     *
133  *                            in each zabbix application                      *
134  *                                                                            *
135  ******************************************************************************/
help(void)136 void	help(void)
137 {
138 	const char	**p = help_message;
139 
140 	usage();
141 	printf("\n");
142 
143 	while (NULL != *p)
144 		printf("%s\n", *p++);
145 
146 	printf("\n");
147 	puts(help_message_footer);
148 }
149 
150 /******************************************************************************
151  *                                                                            *
152  * Function: zbx_error                                                        *
153  *                                                                            *
154  * Purpose: Print error text to the stderr                                    *
155  *                                                                            *
156  * Parameters: fmt - format of message                                        *
157  *                                                                            *
158  * Return value:                                                              *
159  *                                                                            *
160  * Author: Eugene Grigorjev                                                   *
161  *                                                                            *
162  ******************************************************************************/
zbx_error(const char * fmt,...)163 void	zbx_error(const char *fmt, ...)
164 {
165 	va_list	args;
166 
167 	va_start(args, fmt);
168 
169 	fprintf(stderr, "%s [%li]: ", progname, zbx_get_thread_id());
170 	vfprintf(stderr, fmt, args);
171 	fprintf(stderr, "\n");
172 	fflush(stderr);
173 
174 	va_end(args);
175 }
176 
177 /******************************************************************************
178  *                                                                            *
179  * Function: zbx_snprintf                                                     *
180  *                                                                            *
181  * Purpose: Secure version of snprintf function.                              *
182  *          Add zero character at the end of string.                          *
183  *                                                                            *
184  * Parameters: str - destination buffer pointer                               *
185  *             count - size of destination buffer                             *
186  *             fmt - format                                                   *
187  *                                                                            *
188  * Return value:                                                              *
189  *                                                                            *
190  * Author: Eugene Grigorjev                                                   *
191  *                                                                            *
192  ******************************************************************************/
zbx_snprintf(char * str,size_t count,const char * fmt,...)193 size_t	zbx_snprintf(char *str, size_t count, const char *fmt, ...)
194 {
195 	size_t	written_len;
196 	va_list	args;
197 
198 	va_start(args, fmt);
199 	written_len = zbx_vsnprintf(str, count, fmt, args);
200 	va_end(args);
201 
202 	return written_len;
203 }
204 
205 /******************************************************************************
206  *                                                                            *
207  * Function: zbx_snprintf_alloc                                               *
208  *                                                                            *
209  * Purpose: Secure version of snprintf function.                              *
210  *          Add zero character at the end of string.                          *
211  *          Reallocs memory if not enough.                                    *
212  *                                                                            *
213  * Parameters: str       - [IN/OUT] destination buffer pointer                *
214  *             alloc_len - [IN/OUT] already allocated memory                  *
215  *             offset    - [IN/OUT] offset for writing                        *
216  *             fmt       - [IN] format                                        *
217  *                                                                            *
218  * Return value:                                                              *
219  *                                                                            *
220  * Author: Alexei Vladishev, Alexander Vladishev                              *
221  *                                                                            *
222  ******************************************************************************/
zbx_snprintf_alloc(char ** str,size_t * alloc_len,size_t * offset,const char * fmt,...)223 void	zbx_snprintf_alloc(char **str, size_t *alloc_len, size_t *offset, const char *fmt, ...)
224 {
225 	va_list	args;
226 	size_t	avail_len, written_len;
227 retry:
228 	if (NULL == *str)
229 	{
230 		/* zbx_vsnprintf() returns bytes actually written instead of bytes to write, */
231 		/* so we have to use the standard function                                   */
232 		va_start(args, fmt);
233 		*alloc_len = vsnprintf(NULL, 0, fmt, args) + 2;	/* '\0' + one byte to prevent the operation retry */
234 		va_end(args);
235 		*offset = 0;
236 		*str = (char *)zbx_malloc(*str, *alloc_len);
237 	}
238 
239 	avail_len = *alloc_len - *offset;
240 	va_start(args, fmt);
241 	written_len = zbx_vsnprintf(*str + *offset, avail_len, fmt, args);
242 	va_end(args);
243 
244 	if (written_len == avail_len - 1)
245 	{
246 		*alloc_len *= 2;
247 		*str = (char *)zbx_realloc(*str, *alloc_len);
248 
249 		goto retry;
250 	}
251 
252 	*offset += written_len;
253 }
254 
255 /******************************************************************************
256  *                                                                            *
257  * Function: zbx_vsnprintf                                                    *
258  *                                                                            *
259  * Purpose: Secure version of vsnprintf function.                             *
260  *          Add zero character at the end of string.                          *
261  *                                                                            *
262  * Parameters: str   - [IN/OUT] destination buffer pointer                    *
263  *             count - [IN] size of destination buffer                        *
264  *             fmt   - [IN] format                                            *
265  *                                                                            *
266  * Return value: the number of characters in the output buffer                *
267  *               (not including the trailing '\0')                            *
268  *                                                                            *
269  * Author: Alexei Vladishev (see also zbx_snprintf)                           *
270  *                                                                            *
271  ******************************************************************************/
zbx_vsnprintf(char * str,size_t count,const char * fmt,va_list args)272 size_t	zbx_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
273 {
274 	int	written_len = 0;
275 
276 	if (0 < count)
277 	{
278 		if (0 > (written_len = vsnprintf(str, count, fmt, args)))
279 			written_len = (int)count - 1;		/* count an output error as a full buffer */
280 		else
281 			written_len = MIN(written_len, (int)count - 1);		/* result could be truncated */
282 	}
283 	str[written_len] = '\0';	/* always write '\0', even if buffer size is 0 or vsnprintf() error */
284 
285 	return (size_t)written_len;
286 }
287 
288 /******************************************************************************
289  *                                                                            *
290  * Function: zbx_strncpy_alloc, zbx_strcpy_alloc, zbx_chrcpy_alloc            *
291  *                                                                            *
292  * Purpose: If there is no '\0' byte among the first n bytes of src,          *
293  *          then all n bytes will be placed into the dest buffer.             *
294  *          In other case only strlen() bytes will be placed there.           *
295  *          Add zero character at the end of string.                          *
296  *          Reallocs memory if not enough.                                    *
297  *                                                                            *
298  * Parameters: str       - [IN/OUT] destination buffer pointer                *
299  *             alloc_len - [IN/OUT] already allocated memory                  *
300  *             offset    - [IN/OUT] offset for writing                        *
301  *             src       - [IN] copied string                                 *
302  *             n         - [IN] maximum number of bytes to copy               *
303  *                                                                            *
304  * Author: Alexander Vladishev                                                *
305  *                                                                            *
306  ******************************************************************************/
zbx_strncpy_alloc(char ** str,size_t * alloc_len,size_t * offset,const char * src,size_t n)307 void	zbx_strncpy_alloc(char **str, size_t *alloc_len, size_t *offset, const char *src, size_t n)
308 {
309 	if (NULL == *str)
310 	{
311 		*alloc_len = n + 1;
312 		*offset = 0;
313 		*str = (char *)zbx_malloc(*str, *alloc_len);
314 	}
315 	else if (*offset + n >= *alloc_len)
316 	{
317 		while (*offset + n >= *alloc_len)
318 			*alloc_len *= 2;
319 		*str = (char *)zbx_realloc(*str, *alloc_len);
320 	}
321 
322 	while (0 != n && '\0' != *src)
323 	{
324 		(*str)[(*offset)++] = *src++;
325 		n--;
326 	}
327 
328 	(*str)[*offset] = '\0';
329 }
330 
zbx_str_memcpy_alloc(char ** str,size_t * alloc_len,size_t * offset,const char * src,size_t n)331 void	zbx_str_memcpy_alloc(char **str, size_t *alloc_len, size_t *offset, const char *src, size_t n)
332 {
333 	if (NULL == *str)
334 	{
335 		*alloc_len = n + 1;
336 		*offset = 0;
337 		*str = (char *)zbx_malloc(*str, *alloc_len);
338 	}
339 	else if (*offset + n >= *alloc_len)
340 	{
341 		while (*offset + n >= *alloc_len)
342 			*alloc_len *= 2;
343 		*str = (char *)zbx_realloc(*str, *alloc_len);
344 	}
345 
346 	memcpy(*str + *offset, src, n);
347 	*offset += n;
348 	(*str)[*offset] = '\0';
349 }
350 
zbx_strcpy_alloc(char ** str,size_t * alloc_len,size_t * offset,const char * src)351 void	zbx_strcpy_alloc(char **str, size_t *alloc_len, size_t *offset, const char *src)
352 {
353 	zbx_strncpy_alloc(str, alloc_len, offset, src, strlen(src));
354 }
355 
zbx_chrcpy_alloc(char ** str,size_t * alloc_len,size_t * offset,char c)356 void	zbx_chrcpy_alloc(char **str, size_t *alloc_len, size_t *offset, char c)
357 {
358 	zbx_strncpy_alloc(str, alloc_len, offset, &c, 1);
359 }
360 
361 /* Has to be rewritten to avoid malloc */
string_replace(const char * str,const char * sub_str1,const char * sub_str2)362 char	*string_replace(const char *str, const char *sub_str1, const char *sub_str2)
363 {
364 	char *new_str = NULL;
365 	const char *p;
366 	const char *q;
367 	const char *r;
368 	char *t;
369 	long len, diff, count = 0;
370 
371 	assert(str);
372 	assert(sub_str1);
373 	assert(sub_str2);
374 
375 	len = (long)strlen(sub_str1);
376 
377 	/* count the number of occurrences of sub_str1 */
378 	for ( p=str; (p = strstr(p, sub_str1)); p+=len, count++ );
379 
380 	if (0 == count)
381 		return zbx_strdup(NULL, str);
382 
383 	diff = (long)strlen(sub_str2) - len;
384 
385         /* allocate new memory */
386 	new_str = (char *)zbx_malloc(new_str, (size_t)(strlen(str) + count*diff + 1)*sizeof(char));
387 
388         for (q=str,t=new_str,p=str; (p = strstr(p, sub_str1)); )
389         {
390                 /* copy until next occurrence of sub_str1 */
391                 for ( ; q < p; *t++ = *q++);
392                 q += len;
393                 p = q;
394                 for ( r = sub_str2; (*t++ = *r++); );
395                 --t;
396         }
397         /* copy the tail of str */
398         for( ; *q ; *t++ = *q++ );
399 
400 	*t = '\0';
401 
402 	return new_str;
403 }
404 
405 /******************************************************************************
406  *                                                                            *
407  * Function: del_zeros                                                       *
408  *                                                                            *
409  * Purpose: delete all right '0' and '.' for the string                       *
410  *                                                                            *
411  * Parameters: s - string to trim '0'                                         *
412  *                                                                            *
413  * Return value: string without right '0'                                     *
414  *                                                                            *
415  * Author: Alexei Vladishev                                                   *
416  *                                                                            *
417  * Comments: 10.0100 => 10.01, 10. => 10                                      *
418  *                                                                            *
419  ******************************************************************************/
del_zeros(char * s)420 void	del_zeros(char *s)
421 {
422 	int	trim = 0;
423 	size_t	len = 0;
424 
425 	while ('\0' != s[len])
426 	{
427 		if ('e' == s[len] || 'E' == s[len])
428 		{
429 			/* don't touch numbers that are written in scientific notation */
430 			return;
431 		}
432 
433 		if ('.' == s[len])
434 		{
435 			/* number has decimal part */
436 
437 			if (1 == trim)
438 			{
439 				/* don't touch invalid numbers with more than one decimal separator */
440 				return;
441 			}
442 
443 			trim = 1;
444 		}
445 
446 		len++;
447 	}
448 
449 	if (1 == trim)
450 	{
451 		size_t	i;
452 
453 		for (i = len - 1; ; i--)
454 		{
455 			if ('0' == s[i])
456 			{
457 				s[i] = '\0';
458 			}
459 			else if ('.' == s[i])
460 			{
461 				s[i] = '\0';
462 				break;
463 			}
464 			else
465 			{
466 				break;
467 			}
468 		}
469 	}
470 }
471 
472 /******************************************************************************
473  *                                                                            *
474  * Function: zbx_rtrim                                                        *
475  *                                                                            *
476  * Purpose: Strip characters from the end of a string                         *
477  *                                                                            *
478  * Parameters: str - string for processing                                    *
479  *             charlist - null terminated list of characters                  *
480  *                                                                            *
481  * Return value: number of trimmed characters                                 *
482  *                                                                            *
483  * Author: Eugene Grigorjev, Aleksandrs Saveljevs                             *
484  *                                                                            *
485  ******************************************************************************/
zbx_rtrim(char * str,const char * charlist)486 int	zbx_rtrim(char *str, const char *charlist)
487 {
488 	char	*p;
489 	int	count = 0;
490 
491 	if (NULL == str || '\0' == *str)
492 		return count;
493 
494 	for (p = str + strlen(str) - 1; p >= str && NULL != strchr(charlist, *p); p--)
495 	{
496 		*p = '\0';
497 		count++;
498 	}
499 
500 	return count;
501 }
502 
503 /******************************************************************************
504  *                                                                            *
505  * Function: zbx_ltrim                                                        *
506  *                                                                            *
507  * Purpose: Strip characters from the beginning of a string                   *
508  *                                                                            *
509  * Parameters: str - string for processing                                    *
510  *             charlist - null terminated list of characters                  *
511  *                                                                            *
512  * Return value:                                                              *
513  *                                                                            *
514  * Author: Eugene Grigorjev                                                   *
515  *                                                                            *
516  ******************************************************************************/
zbx_ltrim(char * str,const char * charlist)517 void	zbx_ltrim(char *str, const char *charlist)
518 {
519 	char	*p;
520 
521 	if (NULL == str || '\0' == *str)
522 		return;
523 
524 	for (p = str; '\0' != *p && NULL != strchr(charlist, *p); p++)
525 		;
526 
527 	if (p == str)
528 		return;
529 
530 	while ('\0' != *p)
531 		*str++ = *p++;
532 
533 	*str = '\0';
534 }
535 
536 /******************************************************************************
537  *                                                                            *
538  * Function: zbx_lrtrim                                                       *
539  *                                                                            *
540  * Purpose: Removes leading and trailing characters from the specified        *
541  *          character string                                                  *
542  *                                                                            *
543  * Parameters: str      - [IN/OUT] string for processing                      *
544  *             charlist - [IN] null terminated list of characters             *
545  *                                                                            *
546  ******************************************************************************/
zbx_lrtrim(char * str,const char * charlist)547 void	zbx_lrtrim(char *str, const char *charlist)
548 {
549 	zbx_rtrim(str, charlist);
550 	zbx_ltrim(str, charlist);
551 }
552 
553 /******************************************************************************
554  *                                                                            *
555  * Function: zbx_remove_chars                                                 *
556  *                                                                            *
557  * Purpose: Remove characters 'charlist' from the whole string                *
558  *                                                                            *
559  * Parameters: str - string for processing                                    *
560  *             charlist - null terminated list of characters                  *
561  *                                                                            *
562  * Return value:                                                              *
563  *                                                                            *
564  * Author: Alexander Vladishev                                                *
565  *                                                                            *
566  ******************************************************************************/
zbx_remove_chars(char * str,const char * charlist)567 void	zbx_remove_chars(char *str, const char *charlist)
568 {
569 	char	*p;
570 
571 	if (NULL == str || NULL == charlist || '\0' == *str || '\0' == *charlist)
572 		return;
573 
574 	for (p = str; '\0' != *p; p++)
575 	{
576 		if (NULL == strchr(charlist, *p))
577 			*str++ = *p;
578 	}
579 
580 	*str = '\0';
581 }
582 
583 /******************************************************************************
584  *                                                                            *
585  * Function: zbx_str_printable_dyn                                            *
586  *                                                                            *
587  * Purpose: converts text to printable string by converting special           *
588  *          characters to escape sequences                                    *
589  *                                                                            *
590  * Parameters: text - [IN] the text to convert                                *
591  *                                                                            *
592  * Return value: The text converted in printable format                       *
593  *                                                                            *
594  ******************************************************************************/
zbx_str_printable_dyn(const char * text)595 char	*zbx_str_printable_dyn(const char *text)
596 {
597 	size_t		out_alloc = 0;
598 	const char	*pin;
599 	char		*out, *pout;
600 
601 	for (pin = text; '\0' != *pin; pin++)
602 	{
603 		switch (*pin)
604 		{
605 			case '\n':
606 			case '\t':
607 			case '\r':
608 				out_alloc += 2;
609 				break;
610 			default:
611 				out_alloc++;
612 				break;
613 		}
614 	}
615 
616 	out = zbx_malloc(NULL, ++out_alloc);
617 
618 	for (pin = text, pout = out; '\0' != *pin; pin++)
619 	{
620 		switch (*pin)
621 		{
622 			case '\n':
623 				*pout++ = '\\';
624 				*pout++ = 'n';
625 				break;
626 			case '\t':
627 				*pout++ = '\\';
628 				*pout++ = 't';
629 				break;
630 			case '\r':
631 				*pout++ = '\\';
632 				*pout++ = 'r';
633 				break;
634 			default:
635 				*pout++ = *pin;
636 				break;
637 		}
638 	}
639 	*pout = '\0';
640 
641 	return out;
642 }
643 
644 /******************************************************************************
645  *                                                                            *
646  * Function: zbx_strlcpy                                                      *
647  *                                                                            *
648  * Purpose: Copy src to string dst of size siz. At most siz - 1 characters    *
649  *          will be copied. Always null terminates (unless siz == 0).         *
650  *                                                                            *
651  * Return value: the number of characters copied (excluding the null byte)    *
652  *                                                                            *
653  ******************************************************************************/
zbx_strlcpy(char * dst,const char * src,size_t siz)654 size_t	zbx_strlcpy(char *dst, const char *src, size_t siz)
655 {
656 	const char	*s = src;
657 
658 	if (0 != siz)
659 	{
660 		while (0 != --siz && '\0' != *s)
661 			*dst++ = *s++;
662 
663 		*dst = '\0';
664 	}
665 
666 	return s - src;	/* count does not include null */
667 }
668 
669 /******************************************************************************
670  *                                                                            *
671  * Function: zbx_strlcat                                                      *
672  *                                                                            *
673  * Purpose: Appends src to string dst of size siz (unlike strncat, size is    *
674  *          the full size of dst, not space left). At most siz - 1 characters *
675  *          will be copied. Always null terminates (unless                    *
676  *          siz <= strlen(dst)).                                              *
677  *                                                                            *
678  ******************************************************************************/
zbx_strlcat(char * dst,const char * src,size_t siz)679 void	zbx_strlcat(char *dst, const char *src, size_t siz)
680 {
681 	while ('\0' != *dst)
682 	{
683 		dst++;
684 		siz--;
685 	}
686 
687 	zbx_strlcpy(dst, src, siz);
688 }
689 
690 /******************************************************************************
691  *                                                                            *
692  * Function: zbx_strlcpy_utf8                                                 *
693  *                                                                            *
694  * Purpose: copies utf-8 string + terminating zero character into specified   *
695  *          buffer                                                            *
696  *                                                                            *
697  * Return value: the number of copied bytes excluding terminating zero        *
698  *               character.                                                   *
699  *                                                                            *
700  * Comments: If the source string is larger than destination buffer then the  *
701  *           string is truncated after last valid utf-8 character rather than *
702  *           byte.                                                            *
703  *                                                                            *
704  ******************************************************************************/
zbx_strlcpy_utf8(char * dst,const char * src,size_t size)705 size_t	zbx_strlcpy_utf8(char *dst, const char *src, size_t size)
706 {
707 	size = zbx_strlen_utf8_nbytes(src, size - 1);
708 	memcpy(dst, src, size);
709 	dst[size] = '\0';
710 
711 	return size;
712 }
713 
714 /******************************************************************************
715  *                                                                            *
716  * Function: zbx_dvsprintf                                                    *
717  *                                                                            *
718  * Purpose: dynamical formatted output conversion                             *
719  *                                                                            *
720  * Return value: formatted string                                             *
721  *                                                                            *
722  * Author: Eugene Grigorjev                                                   *
723  *                                                                            *
724  * Comments: returns a pointer to allocated memory                            *
725  *                                                                            *
726  ******************************************************************************/
zbx_dvsprintf(char * dest,const char * f,va_list args)727 char	*zbx_dvsprintf(char *dest, const char *f, va_list args)
728 {
729 	char	*string = NULL;
730 	int	n, size = MAX_STRING_LEN >> 1;
731 
732 	va_list curr;
733 
734 	while (1)
735 	{
736 		string = (char *)zbx_malloc(string, size);
737 
738 		va_copy(curr, args);
739 		n = vsnprintf(string, size, f, curr);
740 		va_end(curr);
741 
742 		if (0 <= n && n < size)
743 			break;
744 
745 		/* result was truncated */
746 		if (-1 == n)
747 			size = size * 3 / 2 + 1;	/* the length is unknown */
748 		else
749 			size = n + 1;	/* n bytes + trailing '\0' */
750 
751 		zbx_free(string);
752 	}
753 
754 	zbx_free(dest);
755 
756 	return string;
757 }
758 
759 /******************************************************************************
760  *                                                                            *
761  * Function: zbx_dsprintf                                                     *
762  *                                                                            *
763  * Purpose: dynamical formatted output conversion                             *
764  *                                                                            *
765  * Return value: formatted string                                             *
766  *                                                                            *
767  * Author: Eugene Grigorjev                                                   *
768  *                                                                            *
769  * Comments: returns a pointer to allocated memory                            *
770  *                                                                            *
771  ******************************************************************************/
zbx_dsprintf(char * dest,const char * f,...)772 char	*zbx_dsprintf(char *dest, const char *f, ...)
773 {
774 	char	*string;
775 	va_list args;
776 
777 	va_start(args, f);
778 
779 	string = zbx_dvsprintf(dest, f, args);
780 
781 	va_end(args);
782 
783 	return string;
784 }
785 
786 /******************************************************************************
787  *                                                                            *
788  * Function: zbx_strdcat                                                      *
789  *                                                                            *
790  * Purpose: dynamical cating of strings                                       *
791  *                                                                            *
792  * Return value: new pointer of string                                        *
793  *                                                                            *
794  * Author: Eugene Grigorjev                                                   *
795  *                                                                            *
796  * Comments: returns a pointer to allocated memory                            *
797  *           zbx_strdcat(NULL, "") will return "", not NULL!                  *
798  *                                                                            *
799  ******************************************************************************/
zbx_strdcat(char * dest,const char * src)800 char	*zbx_strdcat(char *dest, const char *src)
801 {
802 	size_t	len_dest, len_src;
803 
804 	if (NULL == src)
805 		return dest;
806 
807 	if (NULL == dest)
808 		return zbx_strdup(NULL, src);
809 
810 	len_dest = strlen(dest);
811 	len_src = strlen(src);
812 
813 	dest = (char *)zbx_realloc(dest, len_dest + len_src + 1);
814 
815 	zbx_strlcpy(dest + len_dest, src, len_src + 1);
816 
817 	return dest;
818 }
819 
820 /******************************************************************************
821  *                                                                            *
822  * Function: zbx_strdcatf                                                     *
823  *                                                                            *
824  * Purpose: dynamical cating of formatted strings                             *
825  *                                                                            *
826  * Return value: new pointer of string                                        *
827  *                                                                            *
828  * Author: Eugene Grigorjev                                                   *
829  *                                                                            *
830  * Comments: returns a pointer to allocated memory                            *
831  *                                                                            *
832  ******************************************************************************/
zbx_strdcatf(char * dest,const char * f,...)833 char	*zbx_strdcatf(char *dest, const char *f, ...)
834 {
835 	char	*string, *result;
836 	va_list	args;
837 
838 	va_start(args, f);
839 	string = zbx_dvsprintf(NULL, f, args);
840 	va_end(args);
841 
842 	result = zbx_strdcat(dest, string);
843 
844 	zbx_free(string);
845 
846 	return result;
847 }
848 
849 /******************************************************************************
850  *                                                                            *
851  * Function: zbx_check_hostname                                               *
852  *                                                                            *
853  * Purpose: check a byte stream for a valid hostname                          *
854  *                                                                            *
855  * Parameters: hostname - pointer to the first char of hostname               *
856  *             error - pointer to the error message (can be NULL)             *
857  *                                                                            *
858  * Return value: return SUCCEED if hostname is valid                          *
859  *               or FAIL if hostname contains invalid chars, is empty         *
860  *               or is longer than MAX_ZBX_HOSTNAME_LEN                       *
861  *                                                                            *
862  * Author: Alexander Vladishev                                                *
863  *                                                                            *
864  ******************************************************************************/
zbx_check_hostname(const char * hostname,char ** error)865 int	zbx_check_hostname(const char *hostname, char **error)
866 {
867 	int	len = 0;
868 
869 	while ('\0' != hostname[len])
870 	{
871 		if (FAIL == is_hostname_char(hostname[len]))
872 		{
873 			if (NULL != error)
874 				*error = zbx_dsprintf(NULL, "name contains invalid character '%c'", hostname[len]);
875 			return FAIL;
876 		}
877 
878 		len++;
879 	}
880 
881 	if (0 == len)
882 	{
883 		if (NULL != error)
884 			*error = zbx_strdup(NULL, "name is empty");
885 		return FAIL;
886 	}
887 
888 	if (MAX_ZBX_HOSTNAME_LEN < len)
889 	{
890 		if (NULL != error)
891 			*error = zbx_dsprintf(NULL, "name is too long (max %d characters)", MAX_ZBX_HOSTNAME_LEN);
892 		return FAIL;
893 	}
894 
895 	return SUCCEED;
896 }
897 
898 /******************************************************************************
899  *                                                                            *
900  * Function: parse_key                                                        *
901  *                                                                            *
902  * Purpose: advances pointer to first invalid character in string             *
903  *          ensuring that everything before it is a valid key                 *
904  *                                                                            *
905  *  e.g., system.run[cat /etc/passwd | awk -F: '{ print $1 }']                *
906  *                                                                            *
907  * Parameters: exp - [IN/OUT] pointer to the first char of key                *
908  *                                                                            *
909  *  e.g., {host:system.run[cat /etc/passwd | awk -F: '{ print $1 }'].last(0)} *
910  *              ^                                                             *
911  * Return value: returns FAIL only if no key is present (length 0),           *
912  *               or the whole string is invalid. SUCCEED otherwise.           *
913  *                                                                            *
914  * Author: Aleksandrs Saveljevs                                               *
915  *                                                                            *
916  * Comments: the pointer is advanced to the first invalid character even if   *
917  *           FAIL is returned (meaning there is a syntax error in item key).  *
918  *           If necessary, the caller must keep a copy of pointer original    *
919  *           value.                                                           *
920  *                                                                            *
921  ******************************************************************************/
parse_key(const char ** exp)922 int	parse_key(const char **exp)
923 {
924 	const char	*s;
925 
926 	for (s = *exp; SUCCEED == is_key_char(*s); s++)
927 		;
928 
929 	if (*exp == s)	/* the key is empty */
930 		return FAIL;
931 
932 	if ('[' == *s)	/* for instance, net.tcp.port[,80] */
933 	{
934 		int	state = 0;	/* 0 - init, 1 - inside quoted param, 2 - inside unquoted param */
935 		int	array = 0;	/* array nest level */
936 
937 		for (s++; '\0' != *s; s++)
938 		{
939 			switch (state)
940 			{
941 				/* init state */
942 				case 0:
943 					if (',' == *s)
944 						;
945 					else if ('"' == *s)
946 						state = 1;
947 					else if ('[' == *s)
948 					{
949 						if (0 == array)
950 							array = 1;
951 						else
952 							goto fail;	/* incorrect syntax: multi-level array */
953 					}
954 					else if (']' == *s && 0 != array)
955 					{
956 						array = 0;
957 						s++;
958 
959 						while (' ' == *s)	/* skip trailing spaces after closing ']' */
960 							s++;
961 
962 						if (']' == *s)
963 							goto succeed;
964 
965 						if (',' != *s)
966 							goto fail;	/* incorrect syntax */
967 					}
968 					else if (']' == *s && 0 == array)
969 						goto succeed;
970 					else if (' ' != *s)
971 						state = 2;
972 					break;
973 				/* quoted */
974 				case 1:
975 					if ('"' == *s)
976 					{
977 						while (' ' == s[1])	/* skip trailing spaces after closing quotes */
978 							s++;
979 
980 						if (0 == array && ']' == s[1])
981 						{
982 							s++;
983 							goto succeed;
984 						}
985 
986 						if (',' != s[1] && !(0 != array && ']' == s[1]))
987 						{
988 							s++;
989 							goto fail;	/* incorrect syntax */
990 						}
991 
992 						state = 0;
993 					}
994 					else if ('\\' == *s && '"' == s[1])
995 						s++;
996 					break;
997 				/* unquoted */
998 				case 2:
999 					if (',' == *s || (']' == *s && 0 != array))
1000 					{
1001 						s--;
1002 						state = 0;
1003 					}
1004 					else if (']' == *s && 0 == array)
1005 						goto succeed;
1006 					break;
1007 			}
1008 		}
1009 fail:
1010 		*exp = s;
1011 		return FAIL;
1012 succeed:
1013 		s++;
1014 	}
1015 
1016 	*exp = s;
1017 	return SUCCEED;
1018 }
1019 
1020 /******************************************************************************
1021  *                                                                            *
1022  * Function: parse_host_key                                                   *
1023  *                                                                            *
1024  * Purpose: return hostname and key                                           *
1025  *          <hostname:>key                                                    *
1026  *                                                                            *
1027  * Parameters:                                                                *
1028  *         exp - pointer to the first char of hostname                        *
1029  *                host:key[key params]                                        *
1030  *                ^                                                           *
1031  *                                                                            *
1032  * Return value: return SUCCEED or FAIL                                       *
1033  *                                                                            *
1034  * Author: Alexander Vladishev                                                *
1035  *                                                                            *
1036  ******************************************************************************/
parse_host_key(char * exp,char ** host,char ** key)1037 int	parse_host_key(char *exp, char **host, char **key)
1038 {
1039 	char	*p, *s;
1040 
1041 	if (NULL == exp || '\0' == *exp)
1042 		return FAIL;
1043 
1044 	for (p = exp, s = exp; '\0' != *p; p++)	/* check for optional hostname */
1045 	{
1046 		if (':' == *p)	/* hostname:vfs.fs.size[/,total]
1047 				 * --------^
1048 				 */
1049 		{
1050 			*p = '\0';
1051 			*host = zbx_strdup(NULL, s);
1052 			*p++ = ':';
1053 
1054 			s = p;
1055 			break;
1056 		}
1057 
1058 		if (SUCCEED != is_hostname_char(*p))
1059 			break;
1060 	}
1061 
1062 	*key = zbx_strdup(NULL, s);
1063 
1064 	return SUCCEED;
1065 }
1066 
1067 /******************************************************************************
1068  *                                                                            *
1069  * Function: zbx_get_escape_string_len                                        *
1070  *                                                                            *
1071  * Purpose: calculate the required size for the escaped string                *
1072  *                                                                            *
1073  * Parameters: src - [IN] null terminated source string                       *
1074  *             charlist - [IN] null terminated to-be-escaped character list   *
1075  *                                                                            *
1076  * Return value: size of the escaped string                                   *
1077  *                                                                            *
1078  * Author: Alexander Vladishev                                                *
1079  *                                                                            *
1080  ******************************************************************************/
zbx_get_escape_string_len(const char * src,const char * charlist)1081 size_t	zbx_get_escape_string_len(const char *src, const char *charlist)
1082 {
1083 	size_t	sz = 0;
1084 
1085 	for (; '\0' != *src; src++, sz++)
1086 	{
1087 		if (NULL != strchr(charlist, *src))
1088 			sz++;
1089 	}
1090 
1091 	return sz;
1092 }
1093 
1094 /******************************************************************************
1095  *                                                                            *
1096  * Function: zbx_dyn_escape_string                                            *
1097  *                                                                            *
1098  * Purpose: escape characters in the source string                            *
1099  *                                                                            *
1100  * Parameters: src - [IN] null terminated source string                       *
1101  *             charlist - [IN] null terminated to-be-escaped character list   *
1102  *                                                                            *
1103  * Return value: the escaped string                                           *
1104  *                                                                            *
1105  * Author: Alexander Vladishev                                                *
1106  *                                                                            *
1107  ******************************************************************************/
zbx_dyn_escape_string(const char * src,const char * charlist)1108 char	*zbx_dyn_escape_string(const char *src, const char *charlist)
1109 {
1110 	size_t	sz;
1111 	char	*d, *dst = NULL;
1112 
1113 	sz = zbx_get_escape_string_len(src, charlist) + 1;
1114 
1115 	dst = (char *)zbx_malloc(dst, sz);
1116 
1117 	for (d = dst; '\0' != *src; src++)
1118 	{
1119 		if (NULL != strchr(charlist, *src))
1120 			*d++ = '\\';
1121 
1122 		*d++ = *src;
1123 	}
1124 
1125 	*d = '\0';
1126 
1127 	return dst;
1128 }
1129 
1130 /******************************************************************************
1131  *                                                                            *
1132  * Function: zbx_escape_string                                                *
1133  *                                                                            *
1134  * Purpose: escape characters in the source string to fixed output buffer     *
1135  *                                                                            *
1136  * Parameters: dst      - [OUT] the output buffer                             *
1137  *             len      - [IN] the output buffer size                         *
1138  *             src      - [IN] null terminated source string                  *
1139  *             charlist - [IN] null terminated to-be-escaped character list   *
1140  *                                                                            *
1141  * Return value: SUCCEED - the string was escaped successfully.               *
1142  *               FAIL    - output buffer is too small.                        *
1143  *                                                                            *
1144  ******************************************************************************/
zbx_escape_string(char * dst,size_t len,const char * src,const char * charlist)1145 int	zbx_escape_string(char *dst, size_t len, const char *src, const char *charlist)
1146 {
1147 	for (; '\0' != *src; src++)
1148 	{
1149 		if (NULL != strchr(charlist, *src))
1150 		{
1151 			if (0 == --len)
1152 				return FAIL;
1153 			*dst++ = '\\';
1154 		}
1155 		else
1156 		{
1157 			if (0 == --len)
1158 				return FAIL;
1159 		}
1160 
1161 		*dst++ = *src;
1162 	}
1163 
1164 	*dst = '\0';
1165 
1166 	return SUCCEED;
1167 }
1168 
zbx_age2str(int age)1169 char	*zbx_age2str(int age)
1170 {
1171 	size_t		offset = 0;
1172 	int		days, hours, minutes, seconds;
1173 	static char	buffer[32];
1174 
1175 	days = (int)((double)age / SEC_PER_DAY);
1176 	hours = (int)((double)(age - days * SEC_PER_DAY) / SEC_PER_HOUR);
1177 	minutes = (int)((double)(age - days * SEC_PER_DAY - hours * SEC_PER_HOUR) / SEC_PER_MIN);
1178 	seconds = (int)((double)(age - days * SEC_PER_DAY - hours * SEC_PER_HOUR - minutes * SEC_PER_MIN));
1179 
1180 	if (0 != days)
1181 		offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, "%dd ", days);
1182 	if (0 != days || 0 != hours)
1183 		offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, "%dh ", hours);
1184 	if (0 != days || 0 != hours || 0 != minutes)
1185 		offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, "%dm ", minutes);
1186 
1187 	zbx_snprintf(buffer + offset, sizeof(buffer) - offset, "%ds", seconds);
1188 
1189 	return buffer;
1190 }
1191 
zbx_date2str(time_t date)1192 char	*zbx_date2str(time_t date)
1193 {
1194 	static char	buffer[11];
1195 	struct tm	*tm;
1196 
1197 	tm = localtime(&date);
1198 	zbx_snprintf(buffer, sizeof(buffer), "%.4d.%.2d.%.2d",
1199 			tm->tm_year + 1900,
1200 			tm->tm_mon + 1,
1201 			tm->tm_mday);
1202 
1203 	return buffer;
1204 }
1205 
zbx_time2str(time_t time)1206 char	*zbx_time2str(time_t time)
1207 {
1208 	static char	buffer[9];
1209 	struct tm	*tm;
1210 
1211 	tm = localtime(&time);
1212 	zbx_snprintf(buffer, sizeof(buffer), "%.2d:%.2d:%.2d",
1213 			tm->tm_hour,
1214 			tm->tm_min,
1215 			tm->tm_sec);
1216 	return buffer;
1217 }
1218 
zbx_strncasecmp(const char * s1,const char * s2,size_t n)1219 int	zbx_strncasecmp(const char *s1, const char *s2, size_t n)
1220 {
1221 	if (NULL == s1 && NULL == s2)
1222 		return 0;
1223 
1224 	if (NULL == s1)
1225 		return 1;
1226 
1227 	if (NULL == s2)
1228 		return -1;
1229 
1230 	while (0 != n && '\0' != *s1 && '\0' != *s2 &&
1231 			tolower((unsigned char)*s1) == tolower((unsigned char)*s2))
1232 	{
1233 		s1++;
1234 		s2++;
1235 		n--;
1236 	}
1237 
1238 	return 0 == n ? 0 : tolower((unsigned char)*s1) - tolower((unsigned char)*s2);
1239 }
1240 
zbx_strcasestr(const char * haystack,const char * needle)1241 char	*zbx_strcasestr(const char *haystack, const char *needle)
1242 {
1243 	size_t		sz_h, sz_n;
1244 	const char	*p;
1245 
1246 	if (NULL == needle || '\0' == *needle)
1247 		return (char *)haystack;
1248 
1249 	if (NULL == haystack || '\0' == *haystack)
1250 		return NULL;
1251 
1252 	sz_h = strlen(haystack);
1253 	sz_n = strlen(needle);
1254 	if (sz_h < sz_n)
1255 		return NULL;
1256 
1257 	for (p = haystack; p <= &haystack[sz_h - sz_n]; p++)
1258 	{
1259 		if (0 == zbx_strncasecmp(p, needle, sz_n))
1260 			return (char *)p;
1261 	}
1262 
1263 	return NULL;
1264 }
1265 
cmp_key_id(const char * key_1,const char * key_2)1266 int	cmp_key_id(const char *key_1, const char *key_2)
1267 {
1268 	const char	*p, *q;
1269 
1270 	for (p = key_1, q = key_2; *p == *q && '\0' != *q && '[' != *q; p++, q++)
1271 		;
1272 
1273 	return ('\0' == *p || '[' == *p) && ('\0' == *q || '[' == *q) ? SUCCEED : FAIL;
1274 }
1275 
1276 /******************************************************************************
1277  *                                                                            *
1278  * Function: get_process_type_string                                          *
1279  *                                                                            *
1280  * Purpose: Returns process name                                              *
1281  *                                                                            *
1282  * Parameters: proc_type - [IN] process type; ZBX_PROCESS_TYPE_*              *
1283  *                                                                            *
1284  * Author: Alexander Vladishev                                                *
1285  *                                                                            *
1286  * Comments: used in internals checks zabbix["process",...], process titles   *
1287  *           and log files                                                    *
1288  *                                                                            *
1289  ******************************************************************************/
get_process_type_string(unsigned char proc_type)1290 const char	*get_process_type_string(unsigned char proc_type)
1291 {
1292 	switch (proc_type)
1293 	{
1294 		case ZBX_PROCESS_TYPE_POLLER:
1295 			return "poller";
1296 		case ZBX_PROCESS_TYPE_UNREACHABLE:
1297 			return "unreachable poller";
1298 		case ZBX_PROCESS_TYPE_IPMIPOLLER:
1299 			return "ipmi poller";
1300 		case ZBX_PROCESS_TYPE_PINGER:
1301 			return "icmp pinger";
1302 		case ZBX_PROCESS_TYPE_JAVAPOLLER:
1303 			return "java poller";
1304 		case ZBX_PROCESS_TYPE_HTTPPOLLER:
1305 			return "http poller";
1306 		case ZBX_PROCESS_TYPE_TRAPPER:
1307 			return "trapper";
1308 		case ZBX_PROCESS_TYPE_SNMPTRAPPER:
1309 			return "snmp trapper";
1310 		case ZBX_PROCESS_TYPE_PROXYPOLLER:
1311 			return "proxy poller";
1312 		case ZBX_PROCESS_TYPE_ESCALATOR:
1313 			return "escalator";
1314 		case ZBX_PROCESS_TYPE_HISTSYNCER:
1315 			return "history syncer";
1316 		case ZBX_PROCESS_TYPE_DISCOVERER:
1317 			return "discoverer";
1318 		case ZBX_PROCESS_TYPE_ALERTER:
1319 			return "alerter";
1320 		case ZBX_PROCESS_TYPE_TIMER:
1321 			return "timer";
1322 		case ZBX_PROCESS_TYPE_HOUSEKEEPER:
1323 			return "housekeeper";
1324 		case ZBX_PROCESS_TYPE_DATASENDER:
1325 			return "data sender";
1326 		case ZBX_PROCESS_TYPE_CONFSYNCER:
1327 			return "configuration syncer";
1328 		case ZBX_PROCESS_TYPE_HEARTBEAT:
1329 			return "heartbeat sender";
1330 		case ZBX_PROCESS_TYPE_SELFMON:
1331 			return "self-monitoring";
1332 		case ZBX_PROCESS_TYPE_VMWARE:
1333 			return "vmware collector";
1334 		case ZBX_PROCESS_TYPE_COLLECTOR:
1335 			return "collector";
1336 		case ZBX_PROCESS_TYPE_LISTENER:
1337 			return "listener";
1338 		case ZBX_PROCESS_TYPE_ACTIVE_CHECKS:
1339 			return "active checks";
1340 		case ZBX_PROCESS_TYPE_TASKMANAGER:
1341 			return "task manager";
1342 		case ZBX_PROCESS_TYPE_IPMIMANAGER:
1343 			return "ipmi manager";
1344 		case ZBX_PROCESS_TYPE_ALERTMANAGER:
1345 			return "alert manager";
1346 		case ZBX_PROCESS_TYPE_PREPROCMAN:
1347 			return "preprocessing manager";
1348 		case ZBX_PROCESS_TYPE_PREPROCESSOR:
1349 			return "preprocessing worker";
1350 		case ZBX_PROCESS_TYPE_LLDMANAGER:
1351 			return "lld manager";
1352 		case ZBX_PROCESS_TYPE_LLDWORKER:
1353 			return "lld worker";
1354 		case ZBX_PROCESS_TYPE_ALERTSYNCER:
1355 			return "alert syncer";
1356 	}
1357 
1358 	THIS_SHOULD_NEVER_HAPPEN;
1359 	exit(EXIT_FAILURE);
1360 }
1361 
get_process_type_by_name(const char * proc_type_str)1362 int	get_process_type_by_name(const char *proc_type_str)
1363 {
1364 	int	i;
1365 
1366 	for (i = 0; i < ZBX_PROCESS_TYPE_COUNT; i++)
1367 	{
1368 		if (0 == strcmp(proc_type_str, get_process_type_string(i)))
1369 			return i;
1370 	}
1371 
1372 	return ZBX_PROCESS_TYPE_UNKNOWN;
1373 }
1374 
get_program_type_string(unsigned char program_type)1375 const char	*get_program_type_string(unsigned char program_type)
1376 {
1377 	switch (program_type)
1378 	{
1379 		case ZBX_PROGRAM_TYPE_SERVER:
1380 			return "server";
1381 		case ZBX_PROGRAM_TYPE_PROXY_ACTIVE:
1382 		case ZBX_PROGRAM_TYPE_PROXY_PASSIVE:
1383 			return "proxy";
1384 		case ZBX_PROGRAM_TYPE_AGENTD:
1385 			return "agent";
1386 		case ZBX_PROGRAM_TYPE_SENDER:
1387 			return "sender";
1388 		case ZBX_PROGRAM_TYPE_GET:
1389 			return "get";
1390 		default:
1391 			return "unknown";
1392 	}
1393 }
1394 
zbx_permission_string(int perm)1395 const char	*zbx_permission_string(int perm)
1396 {
1397 	switch (perm)
1398 	{
1399 		case PERM_DENY:
1400 			return "dn";
1401 		case PERM_READ:
1402 			return "r";
1403 		case PERM_READ_WRITE:
1404 			return "rw";
1405 		default:
1406 			return "unknown";
1407 	}
1408 }
1409 
zbx_agent_type_string(zbx_item_type_t item_type)1410 const char	*zbx_agent_type_string(zbx_item_type_t item_type)
1411 {
1412 	switch (item_type)
1413 	{
1414 		case ITEM_TYPE_ZABBIX:
1415 			return "Zabbix agent";
1416 		case ITEM_TYPE_SNMP:
1417 			return "SNMP agent";
1418 		case ITEM_TYPE_IPMI:
1419 			return "IPMI agent";
1420 		case ITEM_TYPE_JMX:
1421 			return "JMX agent";
1422 		default:
1423 			return "generic";
1424 	}
1425 }
1426 
zbx_item_value_type_string(zbx_item_value_type_t value_type)1427 const char	*zbx_item_value_type_string(zbx_item_value_type_t value_type)
1428 {
1429 	switch (value_type)
1430 	{
1431 		case ITEM_VALUE_TYPE_FLOAT:
1432 			return "Numeric (float)";
1433 		case ITEM_VALUE_TYPE_STR:
1434 			return "Character";
1435 		case ITEM_VALUE_TYPE_LOG:
1436 			return "Log";
1437 		case ITEM_VALUE_TYPE_UINT64:
1438 			return "Numeric (unsigned)";
1439 		case ITEM_VALUE_TYPE_TEXT:
1440 			return "Text";
1441 		default:
1442 			return "unknown";
1443 	}
1444 }
1445 
zbx_interface_type_string(zbx_interface_type_t type)1446 const char	*zbx_interface_type_string(zbx_interface_type_t type)
1447 {
1448 	switch (type)
1449 	{
1450 		case INTERFACE_TYPE_AGENT:
1451 			return "Zabbix agent";
1452 		case INTERFACE_TYPE_SNMP:
1453 			return "SNMP";
1454 		case INTERFACE_TYPE_IPMI:
1455 			return "IPMI";
1456 		case INTERFACE_TYPE_JMX:
1457 			return "JMX";
1458 		case INTERFACE_TYPE_ANY:
1459 			return "any";
1460 		case INTERFACE_TYPE_UNKNOWN:
1461 		default:
1462 			return "unknown";
1463 	}
1464 }
1465 
zbx_sysinfo_ret_string(int ret)1466 const char	*zbx_sysinfo_ret_string(int ret)
1467 {
1468 	switch (ret)
1469 	{
1470 		case SYSINFO_RET_OK:
1471 			return "SYSINFO_SUCCEED";
1472 		case SYSINFO_RET_FAIL:
1473 			return "SYSINFO_FAIL";
1474 		default:
1475 			return "SYSINFO_UNKNOWN";
1476 	}
1477 }
1478 
zbx_result_string(int result)1479 const char	*zbx_result_string(int result)
1480 {
1481 	switch (result)
1482 	{
1483 		case SUCCEED:
1484 			return "SUCCEED";
1485 		case FAIL:
1486 			return "FAIL";
1487 		case CONFIG_ERROR:
1488 			return "CONFIG_ERROR";
1489 		case NOTSUPPORTED:
1490 			return "NOTSUPPORTED";
1491 		case NETWORK_ERROR:
1492 			return "NETWORK_ERROR";
1493 		case TIMEOUT_ERROR:
1494 			return "TIMEOUT_ERROR";
1495 		case AGENT_ERROR:
1496 			return "AGENT_ERROR";
1497 		case GATEWAY_ERROR:
1498 			return "GATEWAY_ERROR";
1499 		default:
1500 			return "unknown";
1501 	}
1502 }
1503 
zbx_item_logtype_string(unsigned char logtype)1504 const char	*zbx_item_logtype_string(unsigned char logtype)
1505 {
1506 	switch (logtype)
1507 	{
1508 		case ITEM_LOGTYPE_INFORMATION:
1509 			return "Information";
1510 		case ITEM_LOGTYPE_WARNING:
1511 			return "Warning";
1512 		case ITEM_LOGTYPE_ERROR:
1513 			return "Error";
1514 		case ITEM_LOGTYPE_FAILURE_AUDIT:
1515 			return "Failure Audit";
1516 		case ITEM_LOGTYPE_SUCCESS_AUDIT:
1517 			return "Success Audit";
1518 		case ITEM_LOGTYPE_CRITICAL:
1519 			return "Critical";
1520 		case ITEM_LOGTYPE_VERBOSE:
1521 			return "Verbose";
1522 		default:
1523 			return "unknown";
1524 	}
1525 }
1526 
zbx_dservice_type_string(zbx_dservice_type_t service)1527 const char	*zbx_dservice_type_string(zbx_dservice_type_t service)
1528 {
1529 	switch (service)
1530 	{
1531 		case SVC_SSH:
1532 			return "SSH";
1533 		case SVC_LDAP:
1534 			return "LDAP";
1535 		case SVC_SMTP:
1536 			return "SMTP";
1537 		case SVC_FTP:
1538 			return "FTP";
1539 		case SVC_HTTP:
1540 			return "HTTP";
1541 		case SVC_POP:
1542 			return "POP";
1543 		case SVC_NNTP:
1544 			return "NNTP";
1545 		case SVC_IMAP:
1546 			return "IMAP";
1547 		case SVC_TCP:
1548 			return "TCP";
1549 		case SVC_AGENT:
1550 			return "Zabbix agent";
1551 		case SVC_SNMPv1:
1552 			return "SNMPv1 agent";
1553 		case SVC_SNMPv2c:
1554 			return "SNMPv2c agent";
1555 		case SVC_SNMPv3:
1556 			return "SNMPv3 agent";
1557 		case SVC_ICMPPING:
1558 			return "ICMP ping";
1559 		case SVC_HTTPS:
1560 			return "HTTPS";
1561 		case SVC_TELNET:
1562 			return "Telnet";
1563 		default:
1564 			return "unknown";
1565 	}
1566 }
1567 
zbx_alert_type_string(unsigned char type)1568 const char	*zbx_alert_type_string(unsigned char type)
1569 {
1570 	switch (type)
1571 	{
1572 		case ALERT_TYPE_MESSAGE:
1573 			return "message";
1574 		default:
1575 			return "script";
1576 	}
1577 }
1578 
zbx_alert_status_string(unsigned char type,unsigned char status)1579 const char	*zbx_alert_status_string(unsigned char type, unsigned char status)
1580 {
1581 	switch (status)
1582 	{
1583 		case ALERT_STATUS_SENT:
1584 			return (ALERT_TYPE_MESSAGE == type ? "sent" : "executed");
1585 		case ALERT_STATUS_NOT_SENT:
1586 			return "in progress";
1587 		default:
1588 			return "failed";
1589 	}
1590 }
1591 
zbx_escalation_status_string(unsigned char status)1592 const char	*zbx_escalation_status_string(unsigned char status)
1593 {
1594 	switch (status)
1595 	{
1596 		case ESCALATION_STATUS_ACTIVE:
1597 			return "active";
1598 		case ESCALATION_STATUS_SLEEP:
1599 			return "sleep";
1600 		case ESCALATION_STATUS_COMPLETED:
1601 			return "completed";
1602 		default:
1603 			return "unknown";
1604 	}
1605 }
1606 
zbx_trigger_value_string(unsigned char value)1607 const char	*zbx_trigger_value_string(unsigned char value)
1608 {
1609 	switch (value)
1610 	{
1611 		case TRIGGER_VALUE_PROBLEM:
1612 			return "PROBLEM";
1613 		case TRIGGER_VALUE_OK:
1614 			return "OK";
1615 		default:
1616 			return "unknown";
1617 	}
1618 }
1619 
zbx_trigger_state_string(unsigned char state)1620 const char	*zbx_trigger_state_string(unsigned char state)
1621 {
1622 	switch (state)
1623 	{
1624 		case TRIGGER_STATE_NORMAL:
1625 			return "Normal";
1626 		case TRIGGER_STATE_UNKNOWN:
1627 			return "Unknown";
1628 		default:
1629 			return "unknown";
1630 	}
1631 }
1632 
zbx_item_state_string(unsigned char state)1633 const char	*zbx_item_state_string(unsigned char state)
1634 {
1635 	switch (state)
1636 	{
1637 		case ITEM_STATE_NORMAL:
1638 			return "Normal";
1639 		case ITEM_STATE_NOTSUPPORTED:
1640 			return "Not supported";
1641 		default:
1642 			return "unknown";
1643 	}
1644 }
1645 
zbx_event_value_string(unsigned char source,unsigned char object,unsigned char value)1646 const char	*zbx_event_value_string(unsigned char source, unsigned char object, unsigned char value)
1647 {
1648 	if (EVENT_SOURCE_TRIGGERS == source)
1649 	{
1650 		switch (value)
1651 		{
1652 			case EVENT_STATUS_PROBLEM:
1653 				return "PROBLEM";
1654 			case EVENT_STATUS_RESOLVED:
1655 				return "RESOLVED";
1656 			default:
1657 				return "unknown";
1658 		}
1659 	}
1660 
1661 	if (EVENT_SOURCE_INTERNAL == source)
1662 	{
1663 		switch (object)
1664 		{
1665 			case EVENT_OBJECT_TRIGGER:
1666 				return zbx_trigger_state_string(value);
1667 			case EVENT_OBJECT_ITEM:
1668 			case EVENT_OBJECT_LLDRULE:
1669 				return zbx_item_state_string(value);
1670 		}
1671 	}
1672 
1673 	return "unknown";
1674 }
1675 
1676 #if defined(_WINDOWS) || defined(__MINGW32__)
get_codepage(const char * encoding,unsigned int * codepage)1677 static int	get_codepage(const char *encoding, unsigned int *codepage)
1678 {
1679 	typedef struct
1680 	{
1681 		unsigned int	codepage;
1682 		const char	*name;
1683 	}
1684 	codepage_t;
1685 
1686 	int		i;
1687 	char		buf[16];
1688 	codepage_t	cp[] = {{0, "ANSI"}, {37, "IBM037"}, {437, "IBM437"}, {500, "IBM500"}, {708, "ASMO-708"},
1689 			{709, NULL}, {710, NULL}, {720, "DOS-720"}, {737, "IBM737"}, {775, "IBM775"}, {850, "IBM850"},
1690 			{852, "IBM852"}, {855, "IBM855"}, {857, "IBM857"}, {858, "IBM00858"}, {860, "IBM860"},
1691 			{861, "IBM861"}, {862, "DOS-862"}, {863, "IBM863"}, {864, "IBM864"}, {865, "IBM865"},
1692 			{866, "CP866"}, {869, "IBM869"}, {870, "IBM870"}, {874, "WINDOWS-874"}, {875, "CP875"},
1693 			{932, "SHIFT_JIS"}, {936, "GB2312"}, {949, "KS_C_5601-1987"}, {950, "BIG5"}, {1026, "IBM1026"},
1694 			{1047, "IBM01047"}, {1140, "IBM01140"}, {1141, "IBM01141"}, {1142, "IBM01142"},
1695 			{1143, "IBM01143"}, {1144, "IBM01144"}, {1145, "IBM01145"}, {1146, "IBM01146"},
1696 			{1147, "IBM01147"}, {1148, "IBM01148"}, {1149, "IBM01149"}, {1200, "UTF-16"},
1697 			{1201, "UNICODEFFFE"}, {1250, "WINDOWS-1250"}, {1251, "WINDOWS-1251"}, {1252, "WINDOWS-1252"},
1698 			{1253, "WINDOWS-1253"}, {1254, "WINDOWS-1254"}, {1255, "WINDOWS-1255"}, {1256, "WINDOWS-1256"},
1699 			{1257, "WINDOWS-1257"}, {1258, "WINDOWS-1258"}, {1361, "JOHAB"}, {10000, "MACINTOSH"},
1700 			{10001, "X-MAC-JAPANESE"}, {10002, "X-MAC-CHINESETRAD"}, {10003, "X-MAC-KOREAN"},
1701 			{10004, "X-MAC-ARABIC"}, {10005, "X-MAC-HEBREW"}, {10006, "X-MAC-GREEK"},
1702 			{10007, "X-MAC-CYRILLIC"}, {10008, "X-MAC-CHINESESIMP"}, {10010, "X-MAC-ROMANIAN"},
1703 			{10017, "X-MAC-UKRAINIAN"}, {10021, "X-MAC-THAI"}, {10029, "X-MAC-CE"},
1704 			{10079, "X-MAC-ICELANDIC"}, {10081, "X-MAC-TURKISH"}, {10082, "X-MAC-CROATIAN"},
1705 			{12000, "UTF-32"}, {12001, "UTF-32BE"}, {20000, "X-CHINESE_CNS"}, {20001, "X-CP20001"},
1706 			{20002, "X_CHINESE-ETEN"}, {20003, "X-CP20003"}, {20004, "X-CP20004"}, {20005, "X-CP20005"},
1707 			{20105, "X-IA5"}, {20106, "X-IA5-GERMAN"}, {20107, "X-IA5-SWEDISH"}, {20108, "X-IA5-NORWEGIAN"},
1708 			{20127, "US-ASCII"}, {20261, "X-CP20261"}, {20269, "X-CP20269"}, {20273, "IBM273"},
1709 			{20277, "IBM277"}, {20278, "IBM278"}, {20280, "IBM280"}, {20284, "IBM284"}, {20285, "IBM285"},
1710 			{20290, "IBM290"}, {20297, "IBM297"}, {20420, "IBM420"}, {20423, "IBM423"}, {20424, "IBM424"},
1711 			{20833, "X-EBCDIC-KOREANEXTENDED"}, {20838, "IBM-THAI"}, {20866, "KOI8-R"}, {20871, "IBM871"},
1712 			{20880, "IBM880"}, {20905, "IBM905"}, {20924, "IBM00924"}, {20932, "EUC-JP"},
1713 			{20936, "X-CP20936"}, {20949, "X-CP20949"}, {21025, "CP1025"}, {21027, NULL}, {21866, "KOI8-U"},
1714 			{28591, "ISO-8859-1"}, {28592, "ISO-8859-2"}, {28593, "ISO-8859-3"}, {28594, "ISO-8859-4"},
1715 			{28595, "ISO-8859-5"}, {28596, "ISO-8859-6"}, {28597, "ISO-8859-7"}, {28598, "ISO-8859-8"},
1716 			{28599, "ISO-8859-9"}, {28603, "ISO-8859-13"}, {28605, "ISO-8859-15"}, {29001, "X-EUROPA"},
1717 			{38598, "ISO-8859-8-I"}, {50220, "ISO-2022-JP"}, {50221, "CSISO2022JP"}, {50222, "ISO-2022-JP"},
1718 			{50225, "ISO-2022-KR"}, {50227, "X-CP50227"}, {50229, NULL}, {50930, NULL}, {50931, NULL},
1719 			{50933, NULL}, {50935, NULL}, {50936, NULL}, {50937, NULL}, {50939, NULL}, {51932, "EUC-JP"},
1720 			{51936, "EUC-CN"}, {51949, "EUC-KR"}, {51950, NULL}, {52936, "HZ-GB-2312"}, {54936, "GB18030"},
1721 			{57002, "X-ISCII-DE"}, {57003, "X-ISCII-BE"}, {57004, "X-ISCII-TA"}, {57005, "X-ISCII-TE"},
1722 			{57006, "X-ISCII-AS"}, {57007, "X-ISCII-OR"}, {57008, "X-ISCII-KA"}, {57009, "X-ISCII-MA"},
1723 			{57010, "X-ISCII-GU"}, {57011, "X-ISCII-PA"}, {65000, "UTF-7"}, {65001, "UTF-8"}, {0, NULL}};
1724 
1725 	if ('\0' == *encoding)
1726 	{
1727 		*codepage = 0;	/* ANSI */
1728 		return SUCCEED;
1729 	}
1730 
1731 	/* by name */
1732 	for (i = 0; 0 != cp[i].codepage || NULL != cp[i].name; i++)
1733 	{
1734 		if (NULL == cp[i].name)
1735 			continue;
1736 
1737 		if (0 == strcmp(encoding, cp[i].name))
1738 		{
1739 			*codepage = cp[i].codepage;
1740 			return SUCCEED;
1741 		}
1742 	}
1743 
1744 	/* by number */
1745 	for (i = 0; 0 != cp[i].codepage || NULL != cp[i].name; i++)
1746 	{
1747 		_itoa_s(cp[i].codepage, buf, sizeof(buf), 10);
1748 		if (0 == strcmp(encoding, buf))
1749 		{
1750 			*codepage = cp[i].codepage;
1751 			return SUCCEED;
1752 		}
1753 	}
1754 
1755 	/* by 'cp' + number */
1756 	for (i = 0; 0 != cp[i].codepage || NULL != cp[i].name; i++)
1757 	{
1758 		zbx_snprintf(buf, sizeof(buf), "cp%li", cp[i].codepage);
1759 		if (0 == strcmp(encoding, buf))
1760 		{
1761 			*codepage = cp[i].codepage;
1762 			return SUCCEED;
1763 		}
1764 	}
1765 
1766 	return FAIL;
1767 }
1768 
1769 /* convert from selected code page to unicode */
zbx_to_unicode(unsigned int codepage,const char * cp_string)1770 static wchar_t	*zbx_to_unicode(unsigned int codepage, const char *cp_string)
1771 {
1772 	wchar_t	*wide_string = NULL;
1773 	int	wide_size;
1774 
1775 	wide_size = MultiByteToWideChar(codepage, 0, cp_string, -1, NULL, 0);
1776 	wide_string = (wchar_t *)zbx_malloc(wide_string, (size_t)wide_size * sizeof(wchar_t));
1777 
1778 	/* convert from cp_string to wide_string */
1779 	MultiByteToWideChar(codepage, 0, cp_string, -1, wide_string, wide_size);
1780 
1781 	return wide_string;
1782 }
1783 
1784 /* convert from Windows ANSI code page to unicode */
zbx_acp_to_unicode(const char * acp_string)1785 wchar_t	*zbx_acp_to_unicode(const char *acp_string)
1786 {
1787 	return zbx_to_unicode(CP_ACP, acp_string);
1788 }
1789 
1790 /* convert from Windows OEM code page to unicode */
zbx_oemcp_to_unicode(const char * oemcp_string)1791 wchar_t	*zbx_oemcp_to_unicode(const char *oemcp_string)
1792 {
1793 	return zbx_to_unicode(CP_OEMCP, oemcp_string);
1794 }
1795 
zbx_acp_to_unicode_static(const char * acp_string,wchar_t * wide_string,int wide_size)1796 int	zbx_acp_to_unicode_static(const char *acp_string, wchar_t *wide_string, int wide_size)
1797 {
1798 	/* convert from acp_string to wide_string */
1799 	if (0 == MultiByteToWideChar(CP_ACP, 0, acp_string, -1, wide_string, wide_size))
1800 		return FAIL;
1801 
1802 	return SUCCEED;
1803 }
1804 
1805 /* convert from UTF-8 to unicode */
zbx_utf8_to_unicode(const char * utf8_string)1806 wchar_t	*zbx_utf8_to_unicode(const char *utf8_string)
1807 {
1808 	return zbx_to_unicode(CP_UTF8, utf8_string);
1809 }
1810 
1811 /* convert from unicode to utf8 */
zbx_unicode_to_utf8(const wchar_t * wide_string)1812 char	*zbx_unicode_to_utf8(const wchar_t *wide_string)
1813 {
1814 	char	*utf8_string = NULL;
1815 	int	utf8_size;
1816 
1817 	utf8_size = WideCharToMultiByte(CP_UTF8, 0, wide_string, -1, NULL, 0, NULL, NULL);
1818 	utf8_string = (char *)zbx_malloc(utf8_string, (size_t)utf8_size);
1819 
1820 	/* convert from wide_string to utf8_string */
1821 	WideCharToMultiByte(CP_UTF8, 0, wide_string, -1, utf8_string, utf8_size, NULL, NULL);
1822 
1823 	return utf8_string;
1824 }
1825 
1826 /* convert from unicode to utf8 */
zbx_unicode_to_utf8_static(const wchar_t * wide_string,char * utf8_string,int utf8_size)1827 char	*zbx_unicode_to_utf8_static(const wchar_t *wide_string, char *utf8_string, int utf8_size)
1828 {
1829 	/* convert from wide_string to utf8_string */
1830 	if (0 == WideCharToMultiByte(CP_UTF8, 0, wide_string, -1, utf8_string, utf8_size, NULL, NULL))
1831 		*utf8_string = '\0';
1832 
1833 	return utf8_string;
1834 }
1835 #endif
1836 
zbx_strlower(char * str)1837 void	zbx_strlower(char *str)
1838 {
1839 	for (; '\0' != *str; str++)
1840 		*str = tolower(*str);
1841 }
1842 
zbx_strupper(char * str)1843 void	zbx_strupper(char *str)
1844 {
1845 	for (; '\0' != *str; str++)
1846 		*str = toupper(*str);
1847 }
1848 
1849 #if defined(_WINDOWS) || defined(__MINGW32__)
1850 #include "log.h"
convert_to_utf8(char * in,size_t in_size,const char * encoding)1851 char	*convert_to_utf8(char *in, size_t in_size, const char *encoding)
1852 {
1853 #define STATIC_SIZE	1024
1854 	wchar_t		wide_string_static[STATIC_SIZE], *wide_string = NULL;
1855 	int		wide_size;
1856 	char		*utf8_string = NULL;
1857 	int		utf8_size;
1858 	unsigned int	codepage;
1859 	int		bom_detected = 0;
1860 
1861 	/* try to guess encoding using BOM if it exists */
1862 	if (3 <= in_size && 0 == strncmp("\xef\xbb\xbf", in, 3))
1863 	{
1864 		bom_detected = 1;
1865 
1866 		if ('\0' == *encoding)
1867 			encoding = "UTF-8";
1868 	}
1869 	else if (2 <= in_size && 0 == strncmp("\xff\xfe", in, 2))
1870 	{
1871 		bom_detected = 1;
1872 
1873 		if ('\0' == *encoding)
1874 			encoding = "UTF-16";
1875 	}
1876 	else if (2 <= in_size && 0 == strncmp("\xfe\xff", in, 2))
1877 	{
1878 		bom_detected = 1;
1879 
1880 		if ('\0' == *encoding)
1881 			encoding = "UNICODEFFFE";
1882 	}
1883 
1884 	if ('\0' == *encoding || FAIL == get_codepage(encoding, &codepage))
1885 	{
1886 		utf8_size = (int)in_size + 1;
1887 		utf8_string = zbx_malloc(utf8_string, utf8_size);
1888 		memcpy(utf8_string, in, in_size);
1889 		utf8_string[in_size] = '\0';
1890 		return utf8_string;
1891 	}
1892 
1893 	zabbix_log(LOG_LEVEL_DEBUG, "convert_to_utf8() in_size:%d encoding:'%s' codepage:%u", in_size, encoding,
1894 			codepage);
1895 
1896 	if (65001 == codepage)
1897 	{
1898 		/* remove BOM */
1899 		if (bom_detected)
1900 			in += 3;
1901 	}
1902 
1903 	if (1200 == codepage)		/* Unicode UTF-16, little-endian byte order */
1904 	{
1905 		wide_size = (int)in_size / 2;
1906 
1907 		/* remove BOM */
1908 		if (bom_detected)
1909 		{
1910 			in += 2;
1911 			wide_size--;
1912 		}
1913 
1914 		wide_string = (wchar_t *)in;
1915 
1916 	}
1917 	else if (1201 == codepage)	/* unicodeFFFE UTF-16, big-endian byte order */
1918 	{
1919 		wchar_t *wide_string_be;
1920 		int	i;
1921 
1922 		wide_size = (int)in_size / 2;
1923 
1924 		/* remove BOM */
1925 		if (bom_detected)
1926 		{
1927 			in += 2;
1928 			wide_size--;
1929 		}
1930 
1931 		wide_string_be = (wchar_t *)in;
1932 
1933 		if (wide_size > STATIC_SIZE)
1934 			wide_string = (wchar_t *)zbx_malloc(wide_string, (size_t)wide_size * sizeof(wchar_t));
1935 		else
1936 			wide_string = wide_string_static;
1937 
1938 		/* convert from big-endian 'in' to little-endian 'wide_string' */
1939 		for (i = 0; i < wide_size; i++)
1940 			wide_string[i] = ((wide_string_be[i] << 8) & 0xff00) | ((wide_string_be[i] >> 8) & 0xff);
1941 	}
1942 	else
1943 	{
1944 		wide_size = MultiByteToWideChar(codepage, 0, in, (int)in_size, NULL, 0);
1945 
1946 		if (wide_size > STATIC_SIZE)
1947 			wide_string = (wchar_t *)zbx_malloc(wide_string, (size_t)wide_size * sizeof(wchar_t));
1948 		else
1949 			wide_string = wide_string_static;
1950 
1951 		/* convert from 'in' to 'wide_string' */
1952 		MultiByteToWideChar(codepage, 0, in, (int)in_size, wide_string, wide_size);
1953 	}
1954 
1955 	utf8_size = WideCharToMultiByte(CP_UTF8, 0, wide_string, wide_size, NULL, 0, NULL, NULL);
1956 	utf8_string = (char *)zbx_malloc(utf8_string, (size_t)utf8_size + 1/* '\0' */);
1957 
1958 	/* convert from 'wide_string' to 'utf8_string' */
1959 	WideCharToMultiByte(CP_UTF8, 0, wide_string, wide_size, utf8_string, utf8_size, NULL, NULL);
1960 	utf8_string[utf8_size] = '\0';
1961 
1962 	if (wide_string != wide_string_static && wide_string != (wchar_t *)in)
1963 		zbx_free(wide_string);
1964 
1965 	return utf8_string;
1966 }
1967 #elif defined(HAVE_ICONV)
convert_to_utf8(char * in,size_t in_size,const char * encoding)1968 char	*convert_to_utf8(char *in, size_t in_size, const char *encoding)
1969 {
1970 	iconv_t		cd;
1971 	size_t		in_size_left, out_size_left, sz, out_alloc = 0;
1972 	const char	to_code[] = "UTF-8";
1973 	char		*out = NULL, *p;
1974 
1975 	out_alloc = in_size + 1;
1976 	p = out = (char *)zbx_malloc(out, out_alloc);
1977 
1978 	/* try to guess encoding using BOM if it exists */
1979 	if ('\0' == *encoding)
1980 	{
1981 		if (3 <= in_size && 0 == strncmp("\xef\xbb\xbf", in, 3))
1982 		{
1983 			encoding = "UTF-8";
1984 		}
1985 		else if (2 <= in_size && 0 == strncmp("\xff\xfe", in, 2))
1986 		{
1987 			encoding = "UTF-16LE";
1988 		}
1989 		else if (2 <= in_size && 0 == strncmp("\xfe\xff", in, 2))
1990 		{
1991 			encoding = "UTF-16BE";
1992 		}
1993 	}
1994 
1995 	if ('\0' == *encoding || (iconv_t)-1 == (cd = iconv_open(to_code, encoding)))
1996 	{
1997 		memcpy(out, in, in_size);
1998 		out[in_size] = '\0';
1999 		return out;
2000 	}
2001 
2002 	in_size_left = in_size;
2003 	out_size_left = out_alloc - 1;
2004 
2005 	while ((size_t)(-1) == iconv(cd, &in, &in_size_left, &p, &out_size_left))
2006 	{
2007 		if (E2BIG != errno)
2008 			break;
2009 
2010 		sz = (size_t)(p - out);
2011 		out_alloc += in_size;
2012 		out_size_left += in_size;
2013 		p = out = (char *)zbx_realloc(out, out_alloc);
2014 		p += sz;
2015 	}
2016 
2017 	*p = '\0';
2018 
2019 	iconv_close(cd);
2020 
2021 	/* remove BOM */
2022 	if (3 <= p - out && 0 == strncmp("\xef\xbb\xbf", out, 3))
2023 		memmove(out, out + 3, (size_t)(p - out - 2));
2024 
2025 	return out;
2026 }
2027 #endif	/* HAVE_ICONV */
2028 
zbx_strlen_utf8(const char * text)2029 size_t	zbx_strlen_utf8(const char *text)
2030 {
2031 	size_t	n = 0;
2032 
2033 	while ('\0' != *text)
2034 	{
2035 		if (0x80 != (0xc0 & *text++))
2036 			n++;
2037 	}
2038 
2039 	return n;
2040 }
2041 
2042 /******************************************************************************
2043  *                                                                            *
2044  * Function: zbx_utf8_char_len                                                *
2045  *                                                                            *
2046  * Purpose: Returns the size (in bytes) of an UTF-8 encoded character or 0    *
2047  *          if the character is not a valid UTF-8.                            *
2048  *                                                                            *
2049  * Parameters: text - [IN] pointer to the 1st byte of UTF-8 character         *
2050  *                                                                            *
2051  ******************************************************************************/
zbx_utf8_char_len(const char * text)2052 size_t	zbx_utf8_char_len(const char *text)
2053 {
2054 	if (0 == (*text & 0x80))		/* ASCII */
2055 		return 1;
2056 	else if (0xc0 == (*text & 0xe0))	/* 11000010-11011111 starts a 2-byte sequence */
2057 		return 2;
2058 	else if (0xe0 == (*text & 0xf0))	/* 11100000-11101111 starts a 3-byte sequence */
2059 		return 3;
2060 	else if (0xf0 == (*text & 0xf8))	/* 11110000-11110100 starts a 4-byte sequence */
2061 		return 4;
2062 #if ZBX_MAX_BYTES_IN_UTF8_CHAR != 4
2063 #	error "zbx_utf8_char_len() is not synchronized with ZBX_MAX_BYTES_IN_UTF8_CHAR"
2064 #endif
2065 	return 0;				/* not a valid UTF-8 character */
2066 }
2067 
2068 /******************************************************************************
2069  *                                                                            *
2070  * Function: zbx_strlen_utf8_nchars                                           *
2071  *                                                                            *
2072  * Purpose: calculates number of bytes in utf8 text limited by utf8_maxlen    *
2073  *          characters                                                        *
2074  *                                                                            *
2075  ******************************************************************************/
zbx_strlen_utf8_nchars(const char * text,size_t utf8_maxlen)2076 size_t	zbx_strlen_utf8_nchars(const char *text, size_t utf8_maxlen)
2077 {
2078 	size_t		sz = 0, csz = 0;
2079 	const char	*next;
2080 
2081 	while ('\0' != *text && 0 < utf8_maxlen && 0 != (csz = zbx_utf8_char_len(text)))
2082 	{
2083 		next = text + csz;
2084 		while (next > text)
2085 		{
2086 			if ('\0' == *text++)
2087 				return sz;
2088 		}
2089 		sz += csz;
2090 		utf8_maxlen--;
2091 	}
2092 
2093 	return sz;
2094 }
2095 
2096 /******************************************************************************
2097  *                                                                            *
2098  * Function: zbx_strlen_utf8_nbytes                                           *
2099  *                                                                            *
2100  * Purpose: calculates number of bytes in utf8 text limited by maxlen bytes   *
2101  *                                                                            *
2102  ******************************************************************************/
zbx_strlen_utf8_nbytes(const char * text,size_t maxlen)2103 size_t	zbx_strlen_utf8_nbytes(const char *text, size_t maxlen)
2104 {
2105 	size_t	sz;
2106 
2107 	sz = strlen(text);
2108 
2109 	if (sz > maxlen)
2110 	{
2111 		sz = maxlen;
2112 
2113 		/* ensure that the string is not cut in the middle of UTF-8 sequence */
2114 		while (0x80 == (0xc0 & text[sz]) && 0 < sz)
2115 			sz--;
2116 	}
2117 
2118 	return sz;
2119 }
2120 
2121 /******************************************************************************
2122  *                                                                            *
2123  * Function: zbx_charcount_utf8_nbytes                                        *
2124  *                                                                            *
2125  * Purpose: calculates number of chars in utf8 text limited by maxlen bytes   *
2126  *                                                                            *
2127  ******************************************************************************/
zbx_charcount_utf8_nbytes(const char * text,size_t maxlen)2128 size_t	zbx_charcount_utf8_nbytes(const char *text, size_t maxlen)
2129 {
2130 	size_t	n = 0;
2131 
2132 	maxlen = zbx_strlen_utf8_nbytes(text, maxlen);
2133 
2134 	while ('\0' != *text && maxlen > 0)
2135 	{
2136 		if (0x80 != (0xc0 & *text++))
2137 			n++;
2138 
2139 		maxlen--;
2140 	}
2141 
2142 	return n;
2143 }
2144 
2145 /******************************************************************************
2146  *                                                                            *
2147  * Function: zbx_is_utf8                                                      *
2148  *                                                                            *
2149  * Purpose: check UTF-8 sequences                                             *
2150  *                                                                            *
2151  * Parameters: text - [IN] pointer to the string                              *
2152  *                                                                            *
2153  * Return value: SUCCEED if string is valid or FAIL otherwise                 *
2154  *                                                                            *
2155  ******************************************************************************/
zbx_is_utf8(const char * text)2156 int	zbx_is_utf8(const char *text)
2157 {
2158 	unsigned int	utf32;
2159 	unsigned char	*utf8;
2160 	size_t		i, mb_len, expecting_bytes = 0;
2161 
2162 	while ('\0' != *text)
2163 	{
2164 		/* single ASCII character */
2165 		if (0 == (*text & 0x80))
2166 		{
2167 			text++;
2168 			continue;
2169 		}
2170 
2171 		/* unexpected continuation byte or invalid UTF-8 bytes '\xfe' & '\xff' */
2172 		if (0x80 == (*text & 0xc0) || 0xfe == (*text & 0xfe))
2173 			return FAIL;
2174 
2175 		/* multibyte sequence */
2176 
2177 		utf8 = (unsigned char *)text;
2178 
2179 		if (0xc0 == (*text & 0xe0))		/* 2-bytes multibyte sequence */
2180 			expecting_bytes = 1;
2181 		else if (0xe0 == (*text & 0xf0))	/* 3-bytes multibyte sequence */
2182 			expecting_bytes = 2;
2183 		else if (0xf0 == (*text & 0xf8))	/* 4-bytes multibyte sequence */
2184 			expecting_bytes = 3;
2185 		else if (0xf8 == (*text & 0xfc))	/* 5-bytes multibyte sequence */
2186 			expecting_bytes = 4;
2187 		else if (0xfc == (*text & 0xfe))	/* 6-bytes multibyte sequence */
2188 			expecting_bytes = 5;
2189 
2190 		mb_len = expecting_bytes + 1;
2191 		text++;
2192 
2193 		for (; 0 != expecting_bytes; expecting_bytes--)
2194 		{
2195 			/* not a continuation byte */
2196 			if (0x80 != (*text++ & 0xc0))
2197 				return FAIL;
2198 		}
2199 
2200 		/* overlong sequence */
2201 		if (0xc0 == (utf8[0] & 0xfe) ||
2202 				(0xe0 == utf8[0] && 0x00 == (utf8[1] & 0x20)) ||
2203 				(0xf0 == utf8[0] && 0x00 == (utf8[1] & 0x30)) ||
2204 				(0xf8 == utf8[0] && 0x00 == (utf8[1] & 0x38)) ||
2205 				(0xfc == utf8[0] && 0x00 == (utf8[1] & 0x3c)))
2206 		{
2207 			return FAIL;
2208 		}
2209 
2210 		utf32 = 0;
2211 
2212 		if (0xc0 == (utf8[0] & 0xe0))
2213 			utf32 = utf8[0] & 0x1f;
2214 		else if (0xe0 == (utf8[0] & 0xf0))
2215 			utf32 = utf8[0] & 0x0f;
2216 		else if (0xf0 == (utf8[0] & 0xf8))
2217 			utf32 = utf8[0] & 0x07;
2218 		else if (0xf8 == (utf8[0] & 0xfc))
2219 			utf32 = utf8[0] & 0x03;
2220 		else if (0xfc == (utf8[0] & 0xfe))
2221 			utf32 = utf8[0] & 0x01;
2222 
2223 		for (i = 1; i < mb_len; i++)
2224 		{
2225 			utf32 <<= 6;
2226 			utf32 += utf8[i] & 0x3f;
2227 		}
2228 
2229 		/* according to the Unicode standard the high and low
2230 		 * surrogate halves used by UTF-16 (U+D800 through U+DFFF)
2231 		 * and values above U+10FFFF are not legal
2232 		 */
2233 		if (utf32 > 0x10ffff || 0xd800 == (utf32 & 0xf800))
2234 			return FAIL;
2235 	}
2236 
2237 	return SUCCEED;
2238 }
2239 
2240 /******************************************************************************
2241  *                                                                            *
2242  * Function: zbx_replace_invalid_utf8                                         *
2243  *                                                                            *
2244  * Purpose: replace invalid UTF-8 sequences of bytes with '?' character       *
2245  *                                                                            *
2246  * Parameters: text - [IN/OUT] pointer to the first char                      *
2247  *                                                                            *
2248  ******************************************************************************/
zbx_replace_invalid_utf8(char * text)2249 void	zbx_replace_invalid_utf8(char *text)
2250 {
2251 	char	*out = text;
2252 
2253 	while ('\0' != *text)
2254 	{
2255 		if (0 == (*text & 0x80))			/* single ASCII character */
2256 			*out++ = *text++;
2257 		else if (0x80 == (*text & 0xc0) ||		/* unexpected continuation byte */
2258 				0xfe == (*text & 0xfe))		/* invalid UTF-8 bytes '\xfe' & '\xff' */
2259 		{
2260 			*out++ = ZBX_UTF8_REPLACE_CHAR;
2261 			text++;
2262 		}
2263 		else						/* multibyte sequence */
2264 		{
2265 			unsigned int	utf32;
2266 			unsigned char	*utf8 = (unsigned char *)out;
2267 			size_t		i, mb_len, expecting_bytes = 0;
2268 			int		ret = SUCCEED;
2269 
2270 			if (0xc0 == (*text & 0xe0))		/* 2-bytes multibyte sequence */
2271 				expecting_bytes = 1;
2272 			else if (0xe0 == (*text & 0xf0))	/* 3-bytes multibyte sequence */
2273 				expecting_bytes = 2;
2274 			else if (0xf0 == (*text & 0xf8))	/* 4-bytes multibyte sequence */
2275 				expecting_bytes = 3;
2276 			else if (0xf8 == (*text & 0xfc))	/* 5-bytes multibyte sequence */
2277 				expecting_bytes = 4;
2278 			else if (0xfc == (*text & 0xfe))	/* 6-bytes multibyte sequence */
2279 				expecting_bytes = 5;
2280 
2281 			*out++ = *text++;
2282 
2283 			for (; 0 != expecting_bytes; expecting_bytes--)
2284 			{
2285 				if (0x80 != (*text & 0xc0))	/* not a continuation byte */
2286 				{
2287 					ret = FAIL;
2288 					break;
2289 				}
2290 
2291 				*out++ = *text++;
2292 			}
2293 
2294 			mb_len = out - (char *)utf8;
2295 
2296 			if (SUCCEED == ret)
2297 			{
2298 				if (0xc0 == (utf8[0] & 0xfe) ||	/* overlong sequence */
2299 						(0xe0 == utf8[0] && 0x00 == (utf8[1] & 0x20)) ||
2300 						(0xf0 == utf8[0] && 0x00 == (utf8[1] & 0x30)) ||
2301 						(0xf8 == utf8[0] && 0x00 == (utf8[1] & 0x38)) ||
2302 						(0xfc == utf8[0] && 0x00 == (utf8[1] & 0x3c)))
2303 				{
2304 					ret = FAIL;
2305 				}
2306 			}
2307 
2308 			if (SUCCEED == ret)
2309 			{
2310 				utf32 = 0;
2311 
2312 				if (0xc0 == (utf8[0] & 0xe0))
2313 					utf32 = utf8[0] & 0x1f;
2314 				else if (0xe0 == (utf8[0] & 0xf0))
2315 					utf32 = utf8[0] & 0x0f;
2316 				else if (0xf0 == (utf8[0] & 0xf8))
2317 					utf32 = utf8[0] & 0x07;
2318 				else if (0xf8 == (utf8[0] & 0xfc))
2319 					utf32 = utf8[0] & 0x03;
2320 				else if (0xfc == (utf8[0] & 0xfe))
2321 					utf32 = utf8[0] & 0x01;
2322 
2323 				for (i = 1; i < mb_len; i++)
2324 				{
2325 					utf32 <<= 6;
2326 					utf32 += utf8[i] & 0x3f;
2327 				}
2328 
2329 				/* according to the Unicode standard the high and low
2330 				 * surrogate halves used by UTF-16 (U+D800 through U+DFFF)
2331 				 * and values above U+10FFFF are not legal
2332 				 */
2333 				if (utf32 > 0x10ffff || 0xd800 == (utf32 & 0xf800))
2334 					ret = FAIL;
2335 			}
2336 
2337 			if (SUCCEED != ret)
2338 			{
2339 				out -= mb_len;
2340 				*out++ = ZBX_UTF8_REPLACE_CHAR;
2341 			}
2342 		}
2343 	}
2344 
2345 	*out = '\0';
2346 }
2347 
2348 /******************************************************************************
2349  *                                                                            *
2350  * Function: utf8_decode_3byte_sequence                                       *
2351  *                                                                            *
2352  * Purpose: decodes 3-byte utf-8 sequence                                     *
2353  *                                                                            *
2354  * Parameters: ptr - [IN] pointer to the 3 byte sequence                      *
2355  *             out - [OUT] the decoded value                                  *
2356  *                                                                            *
2357  * Return value: SUCCEED on success                                           *
2358  *               FAIL on failure                                              *
2359  *                                                                            *
2360  ******************************************************************************/
utf8_decode_3byte_sequence(const char * ptr,zbx_uint32_t * out)2361 static int	utf8_decode_3byte_sequence(const char *ptr, zbx_uint32_t *out)
2362 {
2363 	*out = ((unsigned char)*ptr++ & 0xF) << 12;
2364 	if (0x80 != (*ptr & 0xC0))
2365 		return FAIL;
2366 
2367 	*out |= ((unsigned char)*ptr++ & 0x3F) << 6;
2368 	if (0x80 != (*ptr & 0xC0))
2369 		return FAIL;
2370 
2371 	*out |= ((unsigned char)*ptr & 0x3F);
2372 	return SUCCEED;
2373 }
2374 
2375 /******************************************************************************
2376  *                                                                            *
2377  * Function: zbx_cesu8_to_utf8                                                *
2378  *                                                                            *
2379  * Purpose: convert cesu8 encoded string to utf8                              *
2380  *                                                                            *
2381  * Parameters: cesu8 - [IN] pointer to the first char of NULL terminated CESU8*
2382  *                     string                                                 *
2383  *             utf8  - [OUT] on success, pointer to pointer to the first char *
2384  *                     of allocated NULL terminated UTF8 string               *
2385  *                                                                            *
2386  * Return value: SUCCEED on success                                           *
2387  *               FAIL on failure                                              *
2388  *                                                                            *
2389  ******************************************************************************/
zbx_cesu8_to_utf8(const char * cesu8,char ** utf8)2390 int	zbx_cesu8_to_utf8(const char *cesu8, char **utf8)
2391 {
2392 	const char	*in, *end;
2393 	char		*out;
2394 	size_t		len;
2395 
2396 	len = strlen(cesu8);
2397 	out = *utf8 = zbx_malloc(*utf8, len + 1);
2398 	end = cesu8 + len;
2399 
2400 	for (in = cesu8; in < end;)
2401 	{
2402 		if (0x7f >= (unsigned char)*in)
2403 		{
2404 			*out++ = *in++;
2405 			continue;
2406 		}
2407 
2408 		if (0xdf >= (unsigned char)*in)
2409 		{
2410 			if (2 > end - in)
2411 				goto fail;
2412 
2413 			*out++ = *in++;
2414 			*out++ = *in++;
2415 			continue;
2416 		}
2417 
2418 		if (0xef >= (unsigned char)*in)
2419 		{
2420 			zbx_uint32_t	c1, c2, u;
2421 
2422 			if (3 > end - in || FAIL == utf8_decode_3byte_sequence(in, &c1))
2423 				goto fail;
2424 
2425 			if (0xd800 > c1 || 0xdbff < c1)
2426 			{
2427 				/* normal 3-byte sequence */
2428 				*out++ = *in++;
2429 				*out++ = *in++;
2430 				*out++ = *in++;
2431 				continue;
2432 			}
2433 
2434 			/* decode unicode supplementary character represented as surrogate pair */
2435 			in += 3;
2436 			if (3 > end - in || FAIL == utf8_decode_3byte_sequence(in, &c2) || 0xdc00 > c2 || 0xdfff < c2)
2437 				goto fail;
2438 
2439 			u = 0x10000 + ((((zbx_uint32_t)c1 & 0x3ff) << 10) | (c2 & 0x3ff));
2440 			*out++ = 0xf0 |  u >> 18;
2441 			*out++ = 0x80 | (u >> 12 & 0x3f);
2442 			*out++ = 0x80 | (u >> 6 & 0x3f);
2443 			*out++ = 0x80 | (u & 0x3f);
2444 			in += 3;
2445 			continue;
2446 		}
2447 
2448 		/* the four-byte UTF-8 style supplementary character sequence is not supported by CESU-8 */
2449 		goto fail;
2450 	}
2451 	*out = '\0';
2452 	return SUCCEED;
2453 fail:
2454 	zbx_free(*utf8);
2455 	return FAIL;
2456 }
2457 
dos2unix(char * str)2458 void	dos2unix(char *str)
2459 {
2460 	char	*o = str;
2461 
2462 	while ('\0' != *str)
2463 	{
2464 		if ('\r' == str[0] && '\n' == str[1])	/* CR+LF (Windows) */
2465 			str++;
2466 		*o++ = *str++;
2467 	}
2468 	*o = '\0';
2469 }
2470 
is_ascii_string(const char * str)2471 int	is_ascii_string(const char *str)
2472 {
2473 	while ('\0' != *str)
2474 	{
2475 		if (0 != ((1 << 7) & *str))	/* check for range 0..127 */
2476 			return FAIL;
2477 
2478 		str++;
2479 	}
2480 
2481 	return SUCCEED;
2482 }
2483 
2484 /******************************************************************************
2485  *                                                                            *
2486  * Function: str_linefeed                                                     *
2487  *                                                                            *
2488  * Purpose: wrap long string at specified position with linefeeds             *
2489  *                                                                            *
2490  * Parameters: src     - input string                                         *
2491  *             maxline - maximum length of a line                             *
2492  *             delim   - delimiter to use as linefeed (default "\n" if NULL)  *
2493  *                                                                            *
2494  * Return value: newly allocated copy of input string with linefeeds          *
2495  *                                                                            *
2496  * Author: Vladimir Levijev                                                   *
2497  *                                                                            *
2498  * Comments: allocates memory                                                 *
2499  *                                                                            *
2500  ******************************************************************************/
str_linefeed(const char * src,size_t maxline,const char * delim)2501 char	*str_linefeed(const char *src, size_t maxline, const char *delim)
2502 {
2503 	size_t		src_size, dst_size, delim_size, left;
2504 	int		feeds;		/* number of feeds */
2505 	char		*dst = NULL;	/* output with linefeeds */
2506 	const char	*p_src;
2507 	char		*p_dst;
2508 
2509 	assert(NULL != src);
2510 	assert(0 < maxline);
2511 
2512 	/* default delimiter */
2513 	if (NULL == delim)
2514 		delim = "\n";
2515 
2516 	src_size = strlen(src);
2517 	delim_size = strlen(delim);
2518 
2519 	/* make sure we don't feed the last line */
2520 	feeds = (int)(src_size / maxline - (0 != src_size % maxline || 0 == src_size ? 0 : 1));
2521 
2522 	left = src_size - feeds * maxline;
2523 	dst_size = src_size + feeds * delim_size + 1;
2524 
2525 	/* allocate memory for output */
2526 	dst = (char *)zbx_malloc(dst, dst_size);
2527 
2528 	p_src = src;
2529 	p_dst = dst;
2530 
2531 	/* copy chunks appending linefeeds */
2532 	while (0 < feeds--)
2533 	{
2534 		memcpy(p_dst, p_src, maxline);
2535 		p_src += maxline;
2536 		p_dst += maxline;
2537 
2538 		memcpy(p_dst, delim, delim_size);
2539 		p_dst += delim_size;
2540 	}
2541 
2542 	if (0 < left)
2543 	{
2544 		/* copy what's left */
2545 		memcpy(p_dst, p_src, left);
2546 		p_dst += left;
2547 	}
2548 
2549 	*p_dst = '\0';
2550 
2551 	return dst;
2552 }
2553 
2554 /******************************************************************************
2555  *                                                                            *
2556  * Function: zbx_strarr_init                                                  *
2557  *                                                                            *
2558  * Purpose: initialize dynamic string array                                   *
2559  *                                                                            *
2560  * Parameters: arr - a pointer to array of strings                            *
2561  *                                                                            *
2562  * Return value:                                                              *
2563  *                                                                            *
2564  * Author: Vladimir Levijev                                                   *
2565  *                                                                            *
2566  * Comments: allocates memory, calls assert() if that fails                   *
2567  *                                                                            *
2568  ******************************************************************************/
zbx_strarr_init(char *** arr)2569 void	zbx_strarr_init(char ***arr)
2570 {
2571 	*arr = (char **)zbx_malloc(*arr, sizeof(char *));
2572 	**arr = NULL;
2573 }
2574 
2575 /******************************************************************************
2576  *                                                                            *
2577  * Function: zbx_strarr_add                                                   *
2578  *                                                                            *
2579  * Purpose: add a string to dynamic string array                              *
2580  *                                                                            *
2581  * Parameters: arr - a pointer to array of strings                            *
2582  *             entry - string to add                                          *
2583  *                                                                            *
2584  * Return value:                                                              *
2585  *                                                                            *
2586  * Author: Vladimir Levijev                                                   *
2587  *                                                                            *
2588  * Comments: allocates memory, calls assert() if that fails                   *
2589  *                                                                            *
2590  ******************************************************************************/
zbx_strarr_add(char *** arr,const char * entry)2591 void	zbx_strarr_add(char ***arr, const char *entry)
2592 {
2593 	int	i;
2594 
2595 	assert(entry);
2596 
2597 	for (i = 0; NULL != (*arr)[i]; i++)
2598 		;
2599 
2600 	*arr = (char **)zbx_realloc(*arr, sizeof(char *) * (i + 2));
2601 
2602 	(*arr)[i] = zbx_strdup((*arr)[i], entry);
2603 	(*arr)[++i] = NULL;
2604 }
2605 
2606 /******************************************************************************
2607  *                                                                            *
2608  * Function: zbx_strarr_free                                                  *
2609  *                                                                            *
2610  * Purpose: free dynamic string array memory                                  *
2611  *                                                                            *
2612  * Parameters: arr - array of strings                                         *
2613  *                                                                            *
2614  * Return value:                                                              *
2615  *                                                                            *
2616  * Author: Vladimir Levijev                                                   *
2617  *                                                                            *
2618  ******************************************************************************/
zbx_strarr_free(char ** arr)2619 void	zbx_strarr_free(char **arr)
2620 {
2621 	char	**p;
2622 
2623 	for (p = arr; NULL != *p; p++)
2624 		zbx_free(*p);
2625 	zbx_free(arr);
2626 }
2627 
2628 /******************************************************************************
2629  *                                                                            *
2630  * Function: zbx_replace_string                                               *
2631  *                                                                            *
2632  * Purpose: replace data block with 'value'                                   *
2633  *                                                                            *
2634  * Parameters: data  - [IN/OUT] pointer to the string                         *
2635  *             l     - [IN] left position of the block                        *
2636  *             r     - [IN/OUT] right position of the block                   *
2637  *             value - [IN] the string to replace the block with              *
2638  *                                                                            *
2639  * Author: Alexander Vladishev                                                *
2640  *                                                                            *
2641  ******************************************************************************/
zbx_replace_string(char ** data,size_t l,size_t * r,const char * value)2642 void	zbx_replace_string(char **data, size_t l, size_t *r, const char *value)
2643 {
2644 	size_t	sz_data, sz_block, sz_value;
2645 	char	*src, *dst;
2646 
2647 	sz_value = strlen(value);
2648 	sz_block = *r - l + 1;
2649 
2650 	if (sz_value != sz_block)
2651 	{
2652 		sz_data = *r + strlen(*data + *r);
2653 		sz_data += sz_value - sz_block;
2654 
2655 		if (sz_value > sz_block)
2656 			*data = (char *)zbx_realloc(*data, sz_data + 1);
2657 
2658 		src = *data + l + sz_block;
2659 		dst = *data + l + sz_value;
2660 
2661 		memmove(dst, src, sz_data - l - sz_value + 1);
2662 
2663 		*r = l + sz_value - 1;
2664 	}
2665 
2666 	memcpy(&(*data)[l], value, sz_value);
2667 }
2668 
2669 /******************************************************************************
2670  *                                                                            *
2671  * Function: zbx_trim_str_list                                                *
2672  *                                                                            *
2673  * Purpose: remove whitespace surrounding a string list item delimiters       *
2674  *                                                                            *
2675  * Parameters: list      - the list (a string containing items separated by   *
2676  *                         delimiter)                                         *
2677  *             delimiter - the list delimiter                                 *
2678  *                                                                            *
2679  * Author: Andris Zeila                                                       *
2680  *                                                                            *
2681  ******************************************************************************/
zbx_trim_str_list(char * list,char delimiter)2682 void	zbx_trim_str_list(char *list, char delimiter)
2683 {
2684 	/* NB! strchr(3): "terminating null byte is considered part of the string" */
2685 	const char	*whitespace = " \t";
2686 	char		*out, *in;
2687 
2688 	out = in = list;
2689 
2690 	while ('\0' != *in)
2691 	{
2692 		/* trim leading spaces from list item */
2693 		while ('\0' != *in && NULL != strchr(whitespace, *in))
2694 			in++;
2695 
2696 		/* copy list item */
2697 		while (delimiter != *in && '\0' != *in)
2698 			*out++ = *in++;
2699 
2700 		/* trim trailing spaces from list item */
2701 		if (out > list)
2702 		{
2703 			while (NULL != strchr(whitespace, *(--out)))
2704 				;
2705 			out++;
2706 		}
2707 		if (delimiter == *in)
2708 			*out++ = *in++;
2709 	}
2710 	*out = '\0';
2711 }
2712 
2713 /******************************************************************************
2714  *                                                                            *
2715  * Function: zbx_strcmp_null                                                  *
2716  *                                                                            *
2717  * Purpose:                                                                   *
2718  *     compares two strings where any of them can be a NULL pointer           *
2719  *                                                                            *
2720  * Parameters: same as strcmp() except NULL values are allowed                *
2721  *                                                                            *
2722  * Return value: same as strcmp()                                             *
2723  *                                                                            *
2724  * Comments: NULL is less than any string                                     *
2725  *                                                                            *
2726  ******************************************************************************/
zbx_strcmp_null(const char * s1,const char * s2)2727 int	zbx_strcmp_null(const char *s1, const char *s2)
2728 {
2729 	if (NULL == s1)
2730 		return NULL == s2 ? 0 : -1;
2731 
2732 	if (NULL == s2)
2733 		return 1;
2734 
2735 	return strcmp(s1, s2);
2736 }
2737 
2738 /******************************************************************************
2739  *                                                                            *
2740  * Function: zbx_user_macro_parse                                             *
2741  *                                                                            *
2742  * Purpose:                                                                   *
2743  *     parses user macro and finds its end position and context location      *
2744  *                                                                            *
2745  * Parameters:                                                                *
2746  *     macro     - [IN] the macro to parse                                    *
2747  *     macro_r   - [OUT] the position of ending '}' character                 *
2748  *     context_l - [OUT] the position of context start character (first non   *
2749  *                       space character after context separator ':')         *
2750  *                       0 if macro does not have context specified.          *
2751  *     context_r - [OUT] the position of context end character (either the    *
2752  *                       ending '"' for quoted context values or the last     *
2753  *                       character before the ending '}' character)           *
2754  *                       0 if macro does not have context specified.          *
2755  *     context_op - [OUT] the context matching operator (optional):           *
2756  *                          CONDITION_OPERATOR_EQUAL                          *
2757  *                          CONDITION_OPERATOR_REGEXP                         *
2758  *                                                                            *
2759  * Return value:                                                              *
2760  *     SUCCEED - the macro was parsed successfully.                           *
2761  *     FAIL    - the macro parsing failed, the content of output variables    *
2762  *               is not defined.                                              *
2763  *                                                                            *
2764  ******************************************************************************/
zbx_user_macro_parse(const char * macro,int * macro_r,int * context_l,int * context_r,unsigned char * context_op)2765 int	zbx_user_macro_parse(const char *macro, int *macro_r, int *context_l, int *context_r, unsigned char *context_op)
2766 {
2767 	int	i;
2768 
2769 	/* find the end of macro name by skipping {$ characters and iterating through */
2770 	/* valid macro name characters                                                */
2771 	for (i = 2; SUCCEED == is_macro_char(macro[i]); i++)
2772 		;
2773 
2774 	/* check for empty macro name */
2775 	if (2 == i)
2776 		return FAIL;
2777 
2778 	if ('}' == macro[i])
2779 	{
2780 		/* no macro context specified, parsing done */
2781 		*macro_r = i;
2782 		*context_l = 0;
2783 		*context_r = 0;
2784 
2785 		if (NULL != context_op)
2786 			*context_op = CONDITION_OPERATOR_EQUAL;
2787 
2788 		return SUCCEED;
2789 	}
2790 
2791 	/* fail if the next character is not a macro context separator */
2792 	if  (':' != macro[i])
2793 		return FAIL;
2794 
2795 	i++;
2796 	if (NULL != context_op)
2797 	{
2798 		if (0 == strncmp(macro + i, ZBX_MACRO_REGEX_PREFIX, ZBX_CONST_STRLEN(ZBX_MACRO_REGEX_PREFIX)))
2799 		{
2800 			*context_op = CONDITION_OPERATOR_REGEXP;
2801 			i += ZBX_CONST_STRLEN(ZBX_MACRO_REGEX_PREFIX);
2802 		}
2803 		else
2804 			*context_op = CONDITION_OPERATOR_EQUAL;
2805 	}
2806 
2807 	/* skip the whitespace after macro context separator */
2808 	while (' ' == macro[i])
2809 		i++;
2810 
2811 	*context_l = i;
2812 
2813 	if ('"' == macro[i])
2814 	{
2815 		i++;
2816 
2817 		/* process quoted context */
2818 		for (; '"' != macro[i]; i++)
2819 		{
2820 			if ('\0' == macro[i])
2821 				return FAIL;
2822 
2823 			if ('\\' == macro[i] && '"' == macro[i + 1])
2824 				i++;
2825 		}
2826 
2827 		*context_r = i;
2828 
2829 		while (' ' == macro[++i])
2830 			;
2831 	}
2832 	else
2833 	{
2834 		/* process unquoted context */
2835 		for (; '}' != macro[i]; i++)
2836 		{
2837 			if ('\0' == macro[i])
2838 				return FAIL;
2839 		}
2840 
2841 		*context_r = i - 1;
2842 	}
2843 
2844 	if ('}' != macro[i])
2845 		return FAIL;
2846 
2847 	*macro_r = i;
2848 
2849 	return SUCCEED;
2850 }
2851 
2852 /******************************************************************************
2853  *                                                                            *
2854  * Function: zbx_user_macro_parse_dyn                                         *
2855  *                                                                            *
2856  * Purpose:                                                                   *
2857  *     parses user macro {$MACRO:<context>} into {$MACRO} and <context>       *
2858  *     strings                                                                *
2859  *                                                                            *
2860  * Parameters:                                                                *
2861  *     macro   - [IN] the macro to parse                                      *
2862  *     name    - [OUT] the macro name without context                         *
2863  *     context - [OUT] the unquoted macro context, NULL for macros without    *
2864  *                     context                                                *
2865  *     length  - [OUT] the length of parsed macro (optional)                  *
2866  *     context_op - [OUT] the context matching operator (optional):           *
2867  *                          CONDITION_OPERATOR_EQUAL                          *
2868  *                          CONDITION_OPERATOR_REGEXP                         *
2869  *                                                                            *
2870  * Return value:                                                              *
2871  *     SUCCEED - the macro was parsed successfully                            *
2872  *     FAIL    - the macro parsing failed, invalid parameter syntax           *
2873  *                                                                            *
2874  ******************************************************************************/
zbx_user_macro_parse_dyn(const char * macro,char ** name,char ** context,int * length,unsigned char * context_op)2875 int	zbx_user_macro_parse_dyn(const char *macro, char **name, char **context, int *length, unsigned char *context_op)
2876 {
2877 	const char	*ptr;
2878 	int		macro_r, context_l, context_r;
2879 	size_t		len;
2880 
2881 	if (SUCCEED != zbx_user_macro_parse(macro, &macro_r, &context_l, &context_r, context_op))
2882 		return FAIL;
2883 
2884 	zbx_free(*context);
2885 
2886 	if (0 != context_l)
2887 	{
2888 		ptr = macro + context_l;
2889 
2890 		/* find the context separator ':' by stripping spaces before context */
2891 		while (' ' == *(--ptr))
2892 			;
2893 
2894 		/* remove regex: prefix from macro name for regex contexts */
2895 		if (NULL != context_op && CONDITION_OPERATOR_REGEXP == *context_op)
2896 			ptr -= ZBX_CONST_STRLEN(ZBX_MACRO_REGEX_PREFIX);
2897 
2898 		/* extract the macro name and close with '}' character */
2899 		len = ptr - macro + 1;
2900 		*name = (char *)zbx_realloc(*name, len + 1);
2901 		memcpy(*name, macro, len - 1);
2902 		(*name)[len - 1] = '}';
2903 		(*name)[len] = '\0';
2904 
2905 		*context = zbx_user_macro_unquote_context_dyn(macro + context_l, context_r - context_l + 1);
2906 	}
2907 	else
2908 	{
2909 		*name = (char *)zbx_realloc(*name, macro_r + 2);
2910 		zbx_strlcpy(*name, macro, macro_r + 2);
2911 	}
2912 
2913 	if (NULL != length)
2914 		*length = macro_r + 1;
2915 
2916 	return SUCCEED;
2917 }
2918 
2919 /******************************************************************************
2920  *                                                                            *
2921  * Function: zbx_user_macro_unquote_context_dyn                               *
2922  *                                                                            *
2923  * Purpose:                                                                   *
2924  *     extracts the macro context unquoting if necessary                      *
2925  *                                                                            *
2926  * Parameters:                                                                *
2927  *     context - [IN] the macro context inside a user macro                   *
2928  *     len     - [IN] the macro context length (including quotes for quoted   *
2929  *                    contexts)                                               *
2930  *                                                                            *
2931  * Return value:                                                              *
2932  *     A string containing extracted macro context. This string must be freed *
2933  *     by the caller.                                                         *
2934  *                                                                            *
2935  ******************************************************************************/
zbx_user_macro_unquote_context_dyn(const char * context,int len)2936 char	*zbx_user_macro_unquote_context_dyn(const char *context, int len)
2937 {
2938 	int	quoted = 0;
2939 	char	*buffer, *ptr;
2940 
2941 	ptr = buffer = (char *)zbx_malloc(NULL, len + 1);
2942 
2943 	if ('"' == *context)
2944 	{
2945 		quoted = 1;
2946 		context++;
2947 		len--;
2948 	}
2949 
2950 	while (0 < len)
2951 	{
2952 		if (1 == quoted && '\\' == *context && '"' == context[1])
2953 		{
2954 			context++;
2955 			len--;
2956 		}
2957 
2958 		*ptr++ = *context++;
2959 		len--;
2960 	}
2961 
2962 	if (1 == quoted)
2963 		ptr--;
2964 
2965 	*ptr = '\0';
2966 
2967 	return buffer;
2968 }
2969 
2970 /******************************************************************************
2971  *                                                                            *
2972  * Function: zbx_user_macro_quote_context_dyn                                 *
2973  *                                                                            *
2974  * Purpose:                                                                   *
2975  *     quotes user macro context if necessary                                 *
2976  *                                                                            *
2977  * Parameters:                                                                *
2978  *     context     - [IN] the macro context                                   *
2979  *     force_quote - [IN] if non zero then context quoting is enforced        *
2980  *                                                                            *
2981  * Return value:                                                              *
2982  *     A string containing quoted macro context. This string must be freed by *
2983  *     the caller.                                                            *
2984  *                                                                            *
2985  ******************************************************************************/
zbx_user_macro_quote_context_dyn(const char * context,int force_quote)2986 char	*zbx_user_macro_quote_context_dyn(const char *context, int force_quote)
2987 {
2988 	int		len, quotes = 0;
2989 	char		*buffer, *ptr_buffer;
2990 	const char	*ptr_context = context;
2991 
2992 	if ('"' == *ptr_context || ' ' == *ptr_context)
2993 		force_quote = 1;
2994 
2995 	for (; '\0' != *ptr_context; ptr_context++)
2996 	{
2997 		if ('}' == *ptr_context)
2998 			force_quote = 1;
2999 
3000 		if ('"' == *ptr_context)
3001 			quotes++;
3002 	}
3003 
3004 	if (0 == force_quote)
3005 		return zbx_strdup(NULL, context);
3006 
3007 	len = (int)strlen(context) + 2 + quotes;
3008 	ptr_buffer = buffer = (char *)zbx_malloc(NULL, len + 1);
3009 
3010 	*ptr_buffer++ = '"';
3011 
3012 	while ('\0' != *context)
3013 	{
3014 		if ('"' == *context)
3015 			*ptr_buffer++ = '\\';
3016 
3017 		*ptr_buffer++ = *context++;
3018 	}
3019 
3020 	*ptr_buffer++ = '"';
3021 	*ptr_buffer++ = '\0';
3022 
3023 	return buffer;
3024 }
3025 
3026 /******************************************************************************
3027  *                                                                            *
3028  * Function: zbx_dyn_escape_shell_single_quote                                *
3029  *                                                                            *
3030  * Purpose: escape single quote in shell command arguments                    *
3031  *                                                                            *
3032  * Parameters: arg - [IN] the argument to escape                              *
3033  *                                                                            *
3034  * Return value: The escaped argument.                                        *
3035  *                                                                            *
3036  ******************************************************************************/
zbx_dyn_escape_shell_single_quote(const char * arg)3037 char	*zbx_dyn_escape_shell_single_quote(const char *arg)
3038 {
3039 	int		len = 1; /* include terminating zero character */
3040 	const char	*pin;
3041 	char		*arg_esc, *pout;
3042 
3043 	for (pin = arg; '\0' != *pin; pin++)
3044 	{
3045 		if ('\'' == *pin)
3046 			len += 3;
3047 		len++;
3048 	}
3049 
3050 	pout = arg_esc = (char *)zbx_malloc(NULL, len);
3051 
3052 	for (pin = arg; '\0' != *pin; pin++)
3053 	{
3054 		if ('\'' == *pin)
3055 		{
3056 			*pout++ = '\'';
3057 			*pout++ = '\\';
3058 			*pout++ = '\'';
3059 			*pout++ = '\'';
3060 		}
3061 		else
3062 			*pout++ = *pin;
3063 	}
3064 
3065 	*pout = '\0';
3066 
3067 	return arg_esc;
3068 }
3069 
3070 /******************************************************************************
3071  *                                                                            *
3072  * Function: function_parse_name                                              *
3073  *                                                                            *
3074  * Purpose: parses function name                                              *
3075  *                                                                            *
3076  * Parameters: expr     - [IN] the function expression: func(p1, p2,...)      *
3077  *             length   - [OUT] the function name length or the amount of     *
3078  *                              characters that can be safely skipped         *
3079  *                                                                            *
3080  * Return value: SUCCEED - the function name was successfully parsed          *
3081  *               FAIL    - failed to parse function name                      *
3082  *                                                                            *
3083  ******************************************************************************/
function_parse_name(const char * expr,size_t * length)3084 static int	function_parse_name(const char *expr, size_t *length)
3085 {
3086 	const char	*ptr;
3087 
3088 	for (ptr = expr; SUCCEED == is_function_char(*ptr); ptr++)
3089 		;
3090 
3091 	*length = ptr - expr;
3092 
3093 	return ptr != expr && '(' == *ptr ? SUCCEED : FAIL;
3094 }
3095 
3096 /******************************************************************************
3097  *                                                                            *
3098  * Function: zbx_function_param_parse                                         *
3099  *                                                                            *
3100  * Purpose: parses function parameter                                         *
3101  *                                                                            *
3102  * Parameters: expr      - [IN] pre-validated function parameter list         *
3103  *             param_pos - [OUT] the parameter position, excluding leading    *
3104  *                               whitespace                                   *
3105  *             length    - [OUT] the parameter length including trailing      *
3106  *                               whitespace for unquoted parameter            *
3107  *             sep_pos   - [OUT] the parameter separator character            *
3108  *                               (',' or '\0' or ')') position                *
3109  *                                                                            *
3110  ******************************************************************************/
zbx_function_param_parse(const char * expr,size_t * param_pos,size_t * length,size_t * sep_pos)3111 void	zbx_function_param_parse(const char *expr, size_t *param_pos, size_t *length, size_t *sep_pos)
3112 {
3113 	const char	*ptr = expr;
3114 
3115 	/* skip the leading whitespace */
3116 	while (' ' == *ptr)
3117 		ptr++;
3118 
3119 	*param_pos = ptr - expr;
3120 
3121 	if ('"' == *ptr)	/* quoted parameter */
3122 	{
3123 		for (ptr++; '"' != *ptr || '\\' == *(ptr - 1); ptr++)
3124 			;
3125 
3126 		*length = ++ptr - expr - *param_pos;
3127 
3128 		/* skip trailing whitespace to find the next parameter */
3129 		while (' ' == *ptr)
3130 			ptr++;
3131 	}
3132 	else	/* unquoted parameter */
3133 	{
3134 		for (ptr = expr; '\0' != *ptr && ')' != *ptr && ',' != *ptr; ptr++)
3135 			;
3136 
3137 		*length = ptr - expr - *param_pos;
3138 	}
3139 
3140 	*sep_pos = ptr - expr;
3141 }
3142 
3143 /******************************************************************************
3144  *                                                                            *
3145  * Function: zbx_function_param_unquote_dyn                                   *
3146  *                                                                            *
3147  * Purpose: unquotes function parameter                                       *
3148  *                                                                            *
3149  * Parameters: param -  [IN] the parameter to unquote                         *
3150  *             len   -  [IN] the parameter length                             *
3151  *             quoted - [OUT] the flag that specifies whether parameter was   *
3152  *                            quoted before extraction                        *
3153  *                                                                            *
3154  * Return value: The unquoted parameter. This value must be freed by the      *
3155  *               caller.                                                      *
3156  *                                                                            *
3157  ******************************************************************************/
zbx_function_param_unquote_dyn(const char * param,size_t len,int * quoted)3158 char	*zbx_function_param_unquote_dyn(const char *param, size_t len, int *quoted)
3159 {
3160 	char	*out;
3161 
3162 	out = (char *)zbx_malloc(NULL, len + 1);
3163 
3164 	if (0 == (*quoted = (0 != len && '"' == *param)))
3165 	{
3166 		/* unquoted parameter - simply copy it */
3167 		memcpy(out, param, len);
3168 		out[len] = '\0';
3169 	}
3170 	else
3171 	{
3172 		/* quoted parameter - remove enclosing " and replace \" with " */
3173 		const char	*pin;
3174 		char		*pout = out;
3175 
3176 		for (pin = param + 1; (size_t)(pin - param) < len - 1; pin++)
3177 		{
3178 			if ('\\' == pin[0] && '"' == pin[1])
3179 				pin++;
3180 
3181 			*pout++ = *pin;
3182 		}
3183 
3184 		*pout = '\0';
3185 	}
3186 
3187 	return out;
3188 }
3189 
3190 /******************************************************************************
3191  *                                                                            *
3192  * Function: zbx_function_param_quote                                         *
3193  *                                                                            *
3194  * Purpose: quotes function parameter                                         *
3195  *                                                                            *
3196  * Parameters: param   - [IN/OUT] function parameter                          *
3197  *             forced  - [IN] 1 - enclose parameter in " even if it does not  *
3198  *                                contain any special characters              *
3199  *                            0 - do nothing if the parameter does not        *
3200  *                                contain any special characters              *
3201  *                                                                            *
3202  * Return value: SUCCEED - if parameter was successfully quoted or quoting    *
3203  *                         was not necessary                                  *
3204  *               FAIL    - if parameter needs to but cannot be quoted due to  *
3205  *                         backslash in the end                               *
3206  *                                                                            *
3207  ******************************************************************************/
zbx_function_param_quote(char ** param,int forced)3208 int	zbx_function_param_quote(char **param, int forced)
3209 {
3210 	size_t	sz_src, sz_dst;
3211 
3212 	if (0 == forced && '"' != **param && ' ' != **param && NULL == strchr(*param, ',') &&
3213 			NULL == strchr(*param, ')'))
3214 	{
3215 		return SUCCEED;
3216 	}
3217 
3218 	if (0 != (sz_src = strlen(*param)) && '\\' == (*param)[sz_src - 1])
3219 		return FAIL;
3220 
3221 	sz_dst = zbx_get_escape_string_len(*param, "\"") + 3;
3222 
3223 	*param = (char *)zbx_realloc(*param, sz_dst);
3224 
3225 	(*param)[--sz_dst] = '\0';
3226 	(*param)[--sz_dst] = '"';
3227 
3228 	while (0 < sz_src)
3229 	{
3230 		(*param)[--sz_dst] = (*param)[--sz_src];
3231 		if ('"' == (*param)[sz_src])
3232 			(*param)[--sz_dst] = '\\';
3233 	}
3234 	(*param)[--sz_dst] = '"';
3235 
3236 	return SUCCEED;
3237 }
3238 
3239 /******************************************************************************
3240  *                                                                            *
3241  * Function: zbx_function_get_param_dyn                                       *
3242  *                                                                            *
3243  * Purpose: return parameter by index (Nparam) from parameter list (params)   *
3244  *                                                                            *
3245  * Parameters:                                                                *
3246  *      params - [IN] parameter list                                          *
3247  *      Nparam - [IN] requested parameter index (from 1)                      *
3248  *                                                                            *
3249  * Return value:                                                              *
3250  *      NULL - requested parameter missing                                    *
3251  *      otherwise - requested parameter                                       *
3252  *                                                                            *
3253  ******************************************************************************/
zbx_function_get_param_dyn(const char * params,int Nparam)3254 char	*zbx_function_get_param_dyn(const char *params, int Nparam)
3255 {
3256 	const char	*ptr;
3257 	size_t		sep_pos, params_len;
3258 	char		*out = NULL;
3259 	int		idx = 0;
3260 
3261 	params_len = strlen(params) + 1;
3262 
3263 	for (ptr = params; ++idx <= Nparam && ptr < params + params_len; ptr += sep_pos + 1)
3264 	{
3265 		size_t	param_pos, param_len;
3266 		int	quoted;
3267 
3268 		zbx_function_param_parse(ptr, &param_pos, &param_len, &sep_pos);
3269 
3270 		if (idx == Nparam)
3271 			out = zbx_function_param_unquote_dyn(ptr + param_pos, param_len, &quoted);
3272 	}
3273 
3274 	return out;
3275 }
3276 
3277 /******************************************************************************
3278  *                                                                            *
3279  * Function: function_validate_parameters                                     *
3280  *                                                                            *
3281  * Purpose: validate parameters and give position of terminator if found and  *
3282  *          not quoted                                                        *
3283  *                                                                            *
3284  * Parameters: expr       - [IN] string to parse that contains parameters     *
3285  *                                                                            *
3286  *             terminator - [IN] use ')' if parameters end with               *
3287  *                               parenthesis or '\0' if ends with NULL        *
3288  *                               terminator                                   *
3289  *             par_r      - [OUT] position of the terminator if found         *
3290  *             lpp_offset - [OUT] offset of the last parsed parameter         *
3291  *             lpp_len    - [OUT] length of the last parsed parameter         *
3292  *                                                                            *
3293  * Return value: SUCCEED -  closing parenthesis was found or other custom     *
3294  *                          terminator and not quoted and return info about a *
3295  *                          last processed parameter.                         *
3296  *               FAIL    -  does not look like a valid function parameter     *
3297  *                          list and return info about a last processed       *
3298  *                          parameter.                                        *
3299  *                                                                            *
3300  ******************************************************************************/
function_validate_parameters(const char * expr,char terminator,size_t * par_r,size_t * lpp_offset,size_t * lpp_len)3301 static int	function_validate_parameters(const char *expr, char terminator, size_t *par_r, size_t *lpp_offset,
3302 		size_t *lpp_len)
3303 {
3304 #define ZBX_FUNC_PARAM_NEXT		0
3305 #define ZBX_FUNC_PARAM_QUOTED		1
3306 #define ZBX_FUNC_PARAM_UNQUOTED		2
3307 #define ZBX_FUNC_PARAM_POSTQUOTED	3
3308 
3309 	const char	*ptr;
3310 	int		state = ZBX_FUNC_PARAM_NEXT;
3311 
3312 	*lpp_offset = 0;
3313 
3314 	for (ptr = expr; '\0' != *ptr; ptr++)
3315 	{
3316 		if (terminator == *ptr && ZBX_FUNC_PARAM_QUOTED != state)
3317 		{
3318 			*par_r = ptr - expr;
3319 			return SUCCEED;
3320 		}
3321 
3322 		switch (state)
3323 		{
3324 			case ZBX_FUNC_PARAM_NEXT:
3325 				*lpp_offset = ptr - expr;
3326 				if ('"' == *ptr)
3327 					state = ZBX_FUNC_PARAM_QUOTED;
3328 				else if (' ' != *ptr && ',' != *ptr)
3329 					state = ZBX_FUNC_PARAM_UNQUOTED;
3330 				break;
3331 			case ZBX_FUNC_PARAM_QUOTED:
3332 				if ('"' == *ptr && '\\' != *(ptr - 1))
3333 					state = ZBX_FUNC_PARAM_POSTQUOTED;
3334 				break;
3335 			case ZBX_FUNC_PARAM_UNQUOTED:
3336 				if (',' == *ptr)
3337 					state = ZBX_FUNC_PARAM_NEXT;
3338 				break;
3339 			case ZBX_FUNC_PARAM_POSTQUOTED:
3340 				if (',' == *ptr)
3341 				{
3342 					state = ZBX_FUNC_PARAM_NEXT;
3343 				}
3344 				else if (' ' != *ptr)
3345 				{
3346 					*lpp_len = ptr - (expr + *lpp_offset);
3347 					return FAIL;
3348 				}
3349 				break;
3350 			default:
3351 				THIS_SHOULD_NEVER_HAPPEN;
3352 		}
3353 	}
3354 
3355 	*lpp_len = ptr - (expr + *lpp_offset);
3356 
3357 	if (terminator == *ptr && ZBX_FUNC_PARAM_QUOTED != state)
3358 	{
3359 		*par_r = ptr - expr;
3360 		return SUCCEED;
3361 	}
3362 
3363 	return FAIL;
3364 
3365 #undef ZBX_FUNC_PARAM_NEXT
3366 #undef ZBX_FUNC_PARAM_QUOTED
3367 #undef ZBX_FUNC_PARAM_UNQUOTED
3368 #undef ZBX_FUNC_PARAM_POSTQUOTED
3369 }
3370 
3371 /******************************************************************************
3372  *                                                                            *
3373  * Function: function_match_parenthesis                                       *
3374  *                                                                            *
3375  * Purpose: given the position of opening function parenthesis find the       *
3376  *          position of a closing one                                         *
3377  *                                                                            *
3378  * Parameters: expr       - [IN] string to parse                              *
3379  *             par_l      - [IN] position of the opening parenthesis          *
3380  *             par_r      - [OUT] position of the closing parenthesis         *
3381  *             lpp_offset - [OUT] offset of the last parsed parameter         *
3382  *             lpp_len    - [OUT] length of the last parsed parameter         *
3383  *                                                                            *
3384  * Return value: SUCCEED - closing parenthesis was found                      *
3385  *               FAIL    - string after par_l does not look like a valid      *
3386  *                         function parameter list                            *
3387  *                                                                            *
3388  ******************************************************************************/
function_match_parenthesis(const char * expr,size_t par_l,size_t * par_r,size_t * lpp_offset,size_t * lpp_len)3389 static int	function_match_parenthesis(const char *expr, size_t par_l, size_t *par_r, size_t *lpp_offset,
3390 		size_t *lpp_len)
3391 {
3392 	if (SUCCEED == function_validate_parameters(expr + par_l + 1, ')', par_r, lpp_offset, lpp_len))
3393 	{
3394 		*par_r += par_l + 1;
3395 		return SUCCEED;
3396 	}
3397 
3398 	*lpp_offset += par_l + 1;
3399 	return FAIL;
3400 }
3401 
3402 /******************************************************************************
3403  *                                                                            *
3404  * Function: zbx_function_validate_parameters                                 *
3405  *                                                                            *
3406  * Purpose: validate parameters that end with '\0'                            *
3407  *                                                                            *
3408  * Parameters: expr       - [IN] string to parse that contains parameters     *
3409  *             length     - [OUT] length of parameters                        *
3410  *                                                                            *
3411  * Return value: SUCCEED -  null termination encountered when quotes are      *
3412  *                          closed and no other error                         *
3413  *               FAIL    -  does not look like a valid                        *
3414  *                          function parameter list                           *
3415  *                                                                            *
3416  ******************************************************************************/
zbx_function_validate_parameters(const char * expr,size_t * length)3417 int	zbx_function_validate_parameters(const char *expr, size_t *length)
3418 {
3419 	size_t offset, len;
3420 
3421 	return function_validate_parameters(expr, '\0', length, &offset, &len);
3422 }
3423 
3424 /******************************************************************************
3425  *                                                                            *
3426  * Function: zbx_function_validate                                            *
3427  *                                                                            *
3428  * Purpose: check whether expression starts with a valid function             *
3429  *                                                                            *
3430  * Parameters: expr          - [IN] string to parse                           *
3431  *             par_l         - [OUT] position of the opening parenthesis      *
3432  *                                   or the amount of characters to skip      *
3433  *             par_r         - [OUT] position of the closing parenthesis      *
3434  *             error         - [OUT] error message                            *
3435  *             max_error_len - [IN] error size                                *
3436  *                                                                            *
3437  * Return value: SUCCEED - string starts with a valid function                *
3438  *               FAIL    - string does not start with a function and par_l    *
3439  *                         characters can be safely skipped                   *
3440  *                                                                            *
3441  ******************************************************************************/
zbx_function_validate(const char * expr,size_t * par_l,size_t * par_r,char * error,int max_error_len)3442 static int	zbx_function_validate(const char *expr, size_t *par_l, size_t *par_r, char *error, int max_error_len)
3443 {
3444 	size_t	lpp_offset, lpp_len;
3445 
3446 	/* try to validate function name */
3447 	if (SUCCEED == function_parse_name(expr, par_l))
3448 	{
3449 		/* now we know the position of '(', try to find ')' */
3450 		if (SUCCEED == function_match_parenthesis(expr, *par_l, par_r, &lpp_offset, &lpp_len))
3451 			return SUCCEED;
3452 
3453 		if (NULL != error && *par_l > *par_r)
3454 		{
3455 			zbx_snprintf(error, max_error_len, "Incorrect function '%.*s' expression. "
3456 				"Check expression part starting from: %.*s",
3457 				(int)*par_l, expr, (int)lpp_len, expr + lpp_offset);
3458 
3459 			return FAIL;
3460 		}
3461 	}
3462 
3463 	if (NULL != error)
3464 		zbx_snprintf(error, max_error_len, "Incorrect function expression: %s", expr);
3465 
3466 	return FAIL;
3467 }
3468 
3469 /******************************************************************************
3470  *                                                                            *
3471  * Function: zbx_strcmp_natural                                               *
3472  *                                                                            *
3473  * Purpose: performs natural comparison of two strings                        *
3474  *                                                                            *
3475  * Parameters: s1 - [IN] the first string                                     *
3476  *             s2 - [IN] the second string                                    *
3477  *                                                                            *
3478  * Return value:  0: the strings are equal                                    *
3479  *               <0: s1 < s2                                                  *
3480  *               >0: s1 > s2                                                  *
3481  *                                                                            *
3482  ******************************************************************************/
zbx_strcmp_natural(const char * s1,const char * s2)3483 int	zbx_strcmp_natural(const char *s1, const char *s2)
3484 {
3485 	int	ret, value1, value2;
3486 
3487 	for (;'\0' != *s1 && '\0' != *s2; s1++, s2++)
3488 	{
3489 		if (0 == isdigit(*s1) || 0 == isdigit(*s2))
3490 		{
3491 			if (0 != (ret = *s1 - *s2))
3492 				return ret;
3493 
3494 			continue;
3495 		}
3496 
3497 		value1 = 0;
3498 		while (0 != isdigit(*s1))
3499 			value1 = value1 * 10 + *s1++ - '0';
3500 
3501 		value2 = 0;
3502 		while (0 != isdigit(*s2))
3503 			value2 = value2 * 10 + *s2++ - '0';
3504 
3505 		if (0 != (ret = value1 - value2))
3506 			return ret;
3507 
3508 		if ('\0' == *s1 || '\0' == *s2)
3509 			break;
3510 	}
3511 
3512 	return *s1 - *s2;
3513 }
3514 
3515 /******************************************************************************
3516  *                                                                            *
3517  * Function: zbx_token_parse_user_macro                                       *
3518  *                                                                            *
3519  * Purpose: parses user macro token                                           *
3520  *                                                                            *
3521  * Parameters: expression - [IN] the expression                               *
3522  *             macro      - [IN] the beginning of the token                   *
3523  *             token      - [OUT] the token data                              *
3524  *                                                                            *
3525  * Return value: SUCCEED - the user macro was parsed successfully             *
3526  *               FAIL    - macro does not point at valid user macro           *
3527  *                                                                            *
3528  * Comments: If the macro points at valid user macro in the expression then   *
3529  *           the generic token fields are set and the token->data.user_macro  *
3530  *           structure is filled with user macro specific data.               *
3531  *                                                                            *
3532  ******************************************************************************/
zbx_token_parse_user_macro(const char * expression,const char * macro,zbx_token_t * token)3533 static int	zbx_token_parse_user_macro(const char *expression, const char *macro, zbx_token_t *token)
3534 {
3535 	size_t			offset;
3536 	int			macro_r, context_l, context_r;
3537 	zbx_token_user_macro_t	*data;
3538 
3539 	if (SUCCEED != zbx_user_macro_parse(macro, &macro_r, &context_l, &context_r, NULL))
3540 		return FAIL;
3541 
3542 	offset = macro - expression;
3543 
3544 	/* initialize token */
3545 	token->type = ZBX_TOKEN_USER_MACRO;
3546 	token->loc.l = offset;
3547 	token->loc.r = offset + macro_r;
3548 
3549 	/* initialize token data */
3550 	data = &token->data.user_macro;
3551 	data->name.l = offset + 2;
3552 
3553 	if (0 != context_l)
3554 	{
3555 		const char *ptr = macro + context_l;
3556 
3557 		/* find the context separator ':' by stripping spaces before context */
3558 		while (' ' == *(--ptr))
3559 			;
3560 
3561 		data->name.r = offset + (ptr - macro) - 1;
3562 
3563 		data->context.l = offset + context_l;
3564 		data->context.r = offset + context_r;
3565 	}
3566 	else
3567 	{
3568 		data->name.r = token->loc.r - 1;
3569 		data->context.l = 0;
3570 		data->context.r = 0;
3571 	}
3572 
3573 	return SUCCEED;
3574 }
3575 
3576 /******************************************************************************
3577  *                                                                            *
3578  * Function: zbx_token_parse_lld_macro                                        *
3579  *                                                                            *
3580  * Purpose: parses lld macro token                                            *
3581  *                                                                            *
3582  * Parameters: expression - [IN] the expression                               *
3583  *             macro      - [IN] the beginning of the token                   *
3584  *             token      - [OUT] the token data                              *
3585  *                                                                            *
3586  * Return value: SUCCEED - the lld macro was parsed successfully              *
3587  *               FAIL    - macro does not point at valid lld macro            *
3588  *                                                                            *
3589  * Comments: If the macro points at valid lld macro in the expression then    *
3590  *           the generic token fields are set and the token->data.lld_macro   *
3591  *           structure is filled with lld macro specific data.                *
3592  *                                                                            *
3593  ******************************************************************************/
zbx_token_parse_lld_macro(const char * expression,const char * macro,zbx_token_t * token)3594 static int	zbx_token_parse_lld_macro(const char *expression, const char *macro, zbx_token_t *token)
3595 {
3596 	const char		*ptr;
3597 	size_t			offset;
3598 	zbx_token_macro_t	*data;
3599 
3600 	/* find the end of lld macro by validating its name until the closing bracket } */
3601 	for (ptr = macro + 2; '}' != *ptr; ptr++)
3602 	{
3603 		if ('\0' == *ptr)
3604 			return FAIL;
3605 
3606 		if (SUCCEED != is_macro_char(*ptr))
3607 			return FAIL;
3608 	}
3609 
3610 	/* empty macro name */
3611 	if (2 == ptr - macro)
3612 		return FAIL;
3613 
3614 	offset = macro - expression;
3615 
3616 	/* initialize token */
3617 	token->type = ZBX_TOKEN_LLD_MACRO;
3618 	token->loc.l = offset;
3619 	token->loc.r = offset + (ptr - macro);
3620 
3621 	/* initialize token data */
3622 	data = &token->data.lld_macro;
3623 	data->name.l = offset + 2;
3624 	data->name.r = token->loc.r - 1;
3625 
3626 	return SUCCEED;
3627 }
3628 
3629 /******************************************************************************
3630  *                                                                            *
3631  * Function: zbx_token_parse_objectid                                         *
3632  *                                                                            *
3633  * Purpose: parses object id token                                            *
3634  *                                                                            *
3635  * Parameters: expression - [IN] the expression                               *
3636  *             macro      - [IN] the beginning of the token                   *
3637  *             token      - [OUT] the token data                              *
3638  *                                                                            *
3639  * Return value: SUCCEED - the object id was parsed successfully              *
3640  *               FAIL    - macro does not point at valid object id            *
3641  *                                                                            *
3642  * Comments: If the macro points at valid object id in the expression then    *
3643  *           the generic token fields are set and the token->data.objectid    *
3644  *           structure is filled with object id specific data.                *
3645  *                                                                            *
3646  ******************************************************************************/
zbx_token_parse_objectid(const char * expression,const char * macro,zbx_token_t * token)3647 static int	zbx_token_parse_objectid(const char *expression, const char *macro, zbx_token_t *token)
3648 {
3649 	const char		*ptr;
3650 	size_t			offset;
3651 	zbx_token_macro_t	*data;
3652 
3653 	/* find the end of object id by checking if it contains digits until the closing bracket } */
3654 	for (ptr = macro + 1; '}' != *ptr; ptr++)
3655 	{
3656 		if ('\0' == *ptr)
3657 			return FAIL;
3658 
3659 		if (0 == isdigit(*ptr))
3660 			return FAIL;
3661 	}
3662 
3663 	/* empty object id */
3664 	if (1 == ptr - macro)
3665 		return FAIL;
3666 
3667 
3668 	offset = macro - expression;
3669 
3670 	/* initialize token */
3671 	token->type = ZBX_TOKEN_OBJECTID;
3672 	token->loc.l = offset;
3673 	token->loc.r = offset + (ptr - macro);
3674 
3675 	/* initialize token data */
3676 	data = &token->data.objectid;
3677 	data->name.l = offset + 1;
3678 	data->name.r = token->loc.r - 1;
3679 
3680 	return SUCCEED;
3681 }
3682 
3683 /******************************************************************************
3684  *                                                                            *
3685  * Function: zbx_token_parse_macro_segment                                    *
3686  *                                                                            *
3687  * Purpose: parses macro name segment                                         *
3688  *                                                                            *
3689  * Parameters: expression - [IN] the expression                               *
3690  *             segment    - [IN] the segment start                            *
3691  *             strict     - [OUT] 1 - macro contains only standard characters *
3692  *                                    (upper case alphanumeric characters,    *
3693  *                                     dots and underscores)                  *
3694  *                                0 - the last segment contains lowercase or  *
3695  *                                    quoted characters                       *
3696  *             next       - [OUT] offset of the next character after the      *
3697  *                                segment                                     *
3698  *                                                                            *
3699  * Return value: SUCCEED - the segment was parsed successfully                *
3700  *               FAIL    - otherwise                                          *
3701  *                                                                            *
3702  ******************************************************************************/
zbx_token_parse_macro_segment(const char * expression,const char * segment,int * strict,int * next)3703 static int	zbx_token_parse_macro_segment(const char *expression, const char *segment, int *strict, int *next)
3704 {
3705 	const char	*ptr = segment;
3706 
3707 	if ('"' != *ptr)
3708 	{
3709 		for (*strict = 1; '\0' != *ptr; ptr++)
3710 		{
3711 			if (0 != isalpha((unsigned char)*ptr))
3712 			{
3713 				if (0 == isupper((unsigned char)*ptr))
3714 					*strict = 0;
3715 				continue;
3716 			}
3717 
3718 			if (0 != isdigit((unsigned char)*ptr))
3719 				continue;
3720 
3721 			if ('_' == *ptr)
3722 				continue;
3723 
3724 			break;
3725 		}
3726 
3727 		/* check for empty segment */
3728 		if (ptr == segment)
3729 			return FAIL;
3730 
3731 		*next = ptr - expression;
3732 	}
3733 	else
3734 	{
3735 		for (*strict = 0, ptr++; '"' != *ptr; ptr++)
3736 		{
3737 			if ('\0' == *ptr)
3738 				return FAIL;
3739 
3740 			if ('\\' == *ptr)
3741 			{
3742 				ptr++;
3743 				if ('\\' != *ptr && '"' != *ptr)
3744 					return FAIL;
3745 			}
3746 		}
3747 
3748 		/* check for empty segment */
3749 		if (1 == ptr - segment)
3750 			return FAIL;
3751 
3752 		*next = ptr - expression + 1;
3753 	}
3754 
3755 	return SUCCEED;
3756 }
3757 
3758 /******************************************************************************
3759  *                                                                            *
3760  * Function: zbx_token_parse_macro_name                                       *
3761  *                                                                            *
3762  * Purpose: parses macro name                                                 *
3763  *                                                                            *
3764  * Parameters: expression - [IN] the expression                               *
3765  *             ptr        - [IN] the beginning of macro name                  *
3766  *             loc        - [OUT] the macro name location                     *
3767  *                                                                            *
3768  * Return value: SUCCEED - the simple macro was parsed successfully           *
3769  *               FAIL    - macro does not point at valid macro                *
3770  *                                                                            *
3771  * Comments: Note that the character following macro name must be inspected   *
3772  *           to draw any conclusions. For example for normal macros it must   *
3773  *           be '}' or it's not a valid macro.                                *
3774  *                                                                            *
3775  ******************************************************************************/
zbx_token_parse_macro_name(const char * expression,const char * ptr,zbx_strloc_t * loc)3776 static int	zbx_token_parse_macro_name(const char *expression, const char *ptr, zbx_strloc_t *loc)
3777 {
3778 	int	strict, offset, ret;
3779 
3780 	loc->l = ptr - expression;
3781 
3782 	while (SUCCEED == (ret = zbx_token_parse_macro_segment(expression, ptr, &strict, &offset)))
3783 	{
3784 		if (0 == strict && expression + loc->l == ptr)
3785 			return FAIL;
3786 
3787 		ptr = expression + offset;
3788 
3789 		if ('.' != *ptr || 0 == strict)
3790 		{
3791 			loc->r = ptr - expression - 1;
3792 			break;
3793 		}
3794 		ptr++;
3795 	}
3796 	return ret;
3797 }
3798 
3799 /******************************************************************************
3800  *                                                                            *
3801  * Function: zbx_token_parse_macro                                            *
3802  *                                                                            *
3803  * Purpose: parses normal macro token                                         *
3804  *                                                                            *
3805  * Parameters: expression - [IN] the expression                               *
3806  *             macro      - [IN] the beginning of the token                   *
3807  *             token      - [OUT] the token data                              *
3808  *                                                                            *
3809  * Return value: SUCCEED - the simple macro was parsed successfully           *
3810  *               FAIL    - macro does not point at valid macro                *
3811  *                                                                            *
3812  * Comments: If the macro points at valid macro in the expression then        *
3813  *           the generic token fields are set and the token->data.macro       *
3814  *           structure is filled with simple macro specific data.             *
3815  *                                                                            *
3816  ******************************************************************************/
zbx_token_parse_macro(const char * expression,const char * macro,zbx_token_t * token)3817 static int	zbx_token_parse_macro(const char *expression, const char *macro, zbx_token_t *token)
3818 {
3819 	zbx_strloc_t		loc;
3820 	zbx_token_macro_t	*data;
3821 
3822 	if (SUCCEED != zbx_token_parse_macro_name(expression, macro + 1, &loc))
3823 		return FAIL;
3824 
3825 	if ('}' != expression[loc.r + 1])
3826 		return FAIL;
3827 
3828 	/* initialize token */
3829 	token->type = ZBX_TOKEN_MACRO;
3830 	token->loc.l = loc.l - 1;
3831 	token->loc.r = loc.r + 1;
3832 
3833 	/* initialize token data */
3834 	data = &token->data.macro;
3835 	data->name = loc;
3836 
3837 	return SUCCEED;
3838 }
3839 
3840 /******************************************************************************
3841  *                                                                            *
3842  * Function: zbx_token_parse_function                                         *
3843  *                                                                            *
3844  * Purpose: parses function inside token                                      *
3845  *                                                                            *
3846  * Parameters: expression - [IN] the expression                               *
3847  *             func       - [IN] the beginning of the function                *
3848  *             func_loc   - [OUT] the function location relative to the       *
3849  *                                expression (including parameters)           *
3850  *                                                                            *
3851  * Return value: SUCCEED - the function was parsed successfully               *
3852  *               FAIL    - func does not point at valid function              *
3853  *                                                                            *
3854  ******************************************************************************/
zbx_token_parse_function(const char * expression,const char * func,zbx_strloc_t * func_loc,zbx_strloc_t * func_param)3855 static int	zbx_token_parse_function(const char *expression, const char *func,
3856 		zbx_strloc_t *func_loc, zbx_strloc_t *func_param)
3857 {
3858 	size_t	par_l, par_r;
3859 
3860 	if (SUCCEED != zbx_function_validate(func, &par_l, &par_r, NULL, 0))
3861 		return FAIL;
3862 
3863 	func_loc->l = func - expression;
3864 	func_loc->r = func_loc->l + par_r;
3865 
3866 	func_param->l = func_loc->l + par_l;
3867 	func_param->r = func_loc->l + par_r;
3868 
3869 	return SUCCEED;
3870 }
3871 
3872 /******************************************************************************
3873  *                                                                            *
3874  * Function: zbx_token_parse_func_macro                                       *
3875  *                                                                            *
3876  * Purpose: parses function macro token                                       *
3877  *                                                                            *
3878  * Parameters: expression - [IN] the expression                               *
3879  *             macro      - [IN] the beginning of the token                   *
3880  *             func       - [IN] the beginning of the macro function in the   *
3881  *                               token                                        *
3882  *             token      - [OUT] the token data                              *
3883  *             token_type - [IN] type flag ZBX_TOKEN_FUNC_MACRO or            *
3884  *                               ZBX_TOKEN_LLD_FUNC_MACRO                     *
3885  *                                                                            *
3886  * Return value: SUCCEED - the function macro was parsed successfully         *
3887  *               FAIL    - macro does not point at valid function macro       *
3888  *                                                                            *
3889  * Comments: If the macro points at valid function macro in the expression    *
3890  *           then the generic token fields are set and the                    *
3891  *           token->data.func_macro or token->data.lld_func_macro structures  *
3892  *           depending on token type flag are filled with function macro      *
3893  *           specific data.                                                   *
3894  *                                                                            *
3895  ******************************************************************************/
zbx_token_parse_func_macro(const char * expression,const char * macro,const char * func,zbx_token_t * token,int token_type)3896 static int	zbx_token_parse_func_macro(const char *expression, const char *macro, const char *func,
3897 		zbx_token_t *token, int token_type)
3898 {
3899 	zbx_strloc_t		func_loc, func_param;
3900 	zbx_token_func_macro_t	*data;
3901 	const char		*ptr;
3902 	size_t			offset;
3903 
3904 	if ('\0' == *func)
3905 		return FAIL;
3906 
3907 	if (SUCCEED != zbx_token_parse_function(expression, func, &func_loc, &func_param))
3908 		return FAIL;
3909 
3910 	ptr = expression + func_loc.r + 1;
3911 
3912 	/* skip trailing whitespace and verify that token ends with } */
3913 
3914 	while (' ' == *ptr)
3915 		ptr++;
3916 
3917 	if ('}' != *ptr)
3918 		return FAIL;
3919 
3920 	offset = macro - expression;
3921 
3922 	/* initialize token */
3923 	token->type = token_type;
3924 	token->loc.l = offset;
3925 	token->loc.r = ptr - expression;
3926 
3927 	/* initialize token data */
3928 	data = ZBX_TOKEN_FUNC_MACRO == token_type ? &token->data.func_macro : &token->data.lld_func_macro;
3929 	data->macro.l = offset + 1;
3930 	data->macro.r = func_loc.l - 2;
3931 
3932 	data->func = func_loc;
3933 	data->func_param = func_param;
3934 
3935 	return SUCCEED;
3936 }
3937 
3938 /******************************************************************************
3939  *                                                                            *
3940  * Function: zbx_token_parse_simple_macro_key                                 *
3941  *                                                                            *
3942  * Purpose: parses simple macro token with given key                          *
3943  *                                                                            *
3944  * Parameters: expression - [IN] the expression                               *
3945  *             macro      - [IN] the beginning of the token                   *
3946  *             key        - [IN] the beginning of host key inside the token   *
3947  *             token      - [OUT] the token data                              *
3948  *                                                                            *
3949  * Return value: SUCCEED - the function macro was parsed successfully         *
3950  *               FAIL    - macro does not point at valid simple macro         *
3951  *                                                                            *
3952  * Comments: Simple macros have format {<host>:<key>.<func>(<params>)}        *
3953  *           {HOST.HOSTn} macro can be used for host name and {ITEM.KEYn}     *
3954  *           macro can be used for item key.                                  *
3955  *                                                                            *
3956  *           If the macro points at valid simple macro in the expression      *
3957  *           then the generic token fields are set and the                    *
3958  *           token->data.simple_macro structure is filled with simple macro   *
3959  *           specific data.                                                   *
3960  *                                                                            *
3961  ******************************************************************************/
zbx_token_parse_simple_macro_key(const char * expression,const char * macro,const char * key,zbx_token_t * token)3962 static int	zbx_token_parse_simple_macro_key(const char *expression, const char *macro, const char *key,
3963 		zbx_token_t *token)
3964 {
3965 	size_t				offset;
3966 	zbx_token_simple_macro_t	*data;
3967 	const char			*ptr = key;
3968 	zbx_strloc_t			key_loc, func_loc, func_param;
3969 
3970 	if (SUCCEED != parse_key(&ptr))
3971 	{
3972 		zbx_token_t	key_token;
3973 
3974 		if (SUCCEED != zbx_token_parse_macro(expression, key, &key_token))
3975 			return FAIL;
3976 
3977 		ptr = expression + key_token.loc.r + 1;
3978 	}
3979 
3980 	/* If the key is without parameters, then parse_key() will move cursor past function name - */
3981 	/* at the start of its parameters. In this case move cursor back before function.           */
3982 	if ('(' == *ptr)
3983 	{
3984 		while ('.' != *(--ptr))
3985 			;
3986 	}
3987 
3988 	/* check for empty key */
3989 	if (0 == ptr - key)
3990 		return FAIL;
3991 
3992 	if (SUCCEED != zbx_token_parse_function(expression, ptr + 1, &func_loc, &func_param))
3993 		return FAIL;
3994 
3995 	key_loc.l = key - expression;
3996 	key_loc.r = ptr - expression - 1;
3997 
3998 	ptr = expression + func_loc.r + 1;
3999 
4000 	/* skip trailing whitespace and verify that token ends with } */
4001 
4002 	while (' ' == *ptr)
4003 		ptr++;
4004 
4005 	if ('}' != *ptr)
4006 		return FAIL;
4007 
4008 	offset = macro - expression;
4009 
4010 	/* initialize token */
4011 	token->type = ZBX_TOKEN_SIMPLE_MACRO;
4012 	token->loc.l = offset;
4013 	token->loc.r = ptr - expression;
4014 
4015 	/* initialize token data */
4016 	data = &token->data.simple_macro;
4017 	data->host.l = offset + 1;
4018 	data->host.r = offset + (key - macro) - 2;
4019 
4020 	data->key = key_loc;
4021 	data->func = func_loc;
4022 	data->func_param = func_param;
4023 
4024 	return SUCCEED;
4025 }
4026 
4027 /******************************************************************************
4028  *                                                                            *
4029  * Function: zbx_token_parse_simple_macro                                     *
4030  *                                                                            *
4031  * Purpose: parses simple macro token                                         *
4032  *                                                                            *
4033  * Parameters: expression - [IN] the expression                               *
4034  *             macro      - [IN] the beginning of the token                   *
4035  *             token      - [OUT] the token data                              *
4036  *                                                                            *
4037  * Return value: SUCCEED - the simple macro was parsed successfully           *
4038  *               FAIL    - macro does not point at valid simple macro         *
4039  *                                                                            *
4040  * Comments: Simple macros have format {<host>:<key>.<func>(<params>)}        *
4041  *           {HOST.HOSTn} macro can be used for host name and {ITEM.KEYn}     *
4042  *           macro can be used for item key.                                  *
4043  *                                                                            *
4044  *           If the macro points at valid simple macro in the expression      *
4045  *           then the generic token fields are set and the                    *
4046  *           token->data.simple_macro structure is filled with simple macro   *
4047  *           specific data.                                                   *
4048  *                                                                            *
4049  ******************************************************************************/
zbx_token_parse_simple_macro(const char * expression,const char * macro,zbx_token_t * token)4050 static int	zbx_token_parse_simple_macro(const char *expression, const char *macro, zbx_token_t *token)
4051 {
4052 	const char	*ptr;
4053 
4054 	/* Find the end of host name by validating its name until the closing bracket }.          */
4055 	/* {HOST.HOSTn} macro usage in the place of host name is handled by nested macro parsing. */
4056 	for (ptr = macro + 1; ':' != *ptr; ptr++)
4057 	{
4058 		if ('\0' == *ptr)
4059 			return FAIL;
4060 
4061 		if (SUCCEED != is_hostname_char(*ptr))
4062 			return FAIL;
4063 	}
4064 
4065 	/* check for empty host name */
4066 	if (1 == ptr - macro)
4067 		return FAIL;
4068 
4069 	return zbx_token_parse_simple_macro_key(expression, macro, ptr + 1, token);
4070 }
4071 
4072 /******************************************************************************
4073  *                                                                            *
4074  * Function: zbx_token_parse_nested_macro                                     *
4075  *                                                                            *
4076  * Purpose: parses token with nested macros                                   *
4077  *                                                                            *
4078  * Parameters: expression - [IN] the expression                               *
4079  *             macro      - [IN] the beginning of the token                   *
4080  *             token      - [OUT] the token data                              *
4081  *                                                                            *
4082  * Return value: SUCCEED - the token was parsed successfully                  *
4083  *               FAIL    - macro does not point at valid function or simple   *
4084  *                         macro                                              *
4085  *                                                                            *
4086  * Comments: This function parses token with a macro inside it. There are     *
4087  *           three types of nested macros - low-level discovery function      *
4088  *           macros, function macros and a specific case of simple macros     *
4089  *           where {HOST.HOSTn} macro is used as host name.                   *
4090  *                                                                            *
4091  *           If the macro points at valid macro in the expression then        *
4092  *           the generic token fields are set and either the                  *
4093  *           token->data.lld_func_macro, token->data.func_macro or            *
4094  *           token->data.simple_macro (depending on token type) structure is  *
4095  *           filled with macro specific data.                                 *
4096  *                                                                            *
4097  ******************************************************************************/
zbx_token_parse_nested_macro(const char * expression,const char * macro,zbx_token_t * token)4098 static int	zbx_token_parse_nested_macro(const char *expression, const char *macro, zbx_token_t *token)
4099 {
4100 	const char	*ptr;
4101 
4102 	if ('#' == macro[2])
4103 	{
4104 		/* find the end of the nested macro by validating its name until the closing bracket '}' */
4105 		for (ptr = macro + 3; '}' != *ptr; ptr++)
4106 		{
4107 			if ('\0' == *ptr)
4108 				return FAIL;
4109 
4110 			if (SUCCEED != is_macro_char(*ptr))
4111 				return FAIL;
4112 		}
4113 
4114 		/* empty macro name */
4115 		if (3 == ptr - macro)
4116 			return FAIL;
4117 	}
4118 	else
4119 	{
4120 		zbx_strloc_t	loc;
4121 
4122 		if (SUCCEED != zbx_token_parse_macro_name(expression, macro + 2, &loc))
4123 			return FAIL;
4124 
4125 		if ('}' != expression[loc.r + 1])
4126 			return FAIL;
4127 
4128 		ptr = expression + loc.r + 1;
4129 	}
4130 
4131 	/* Determine the token type.                                                   */
4132 	/* Nested macros formats:                                                      */
4133 	/*               low-level discovery function macros  {{#MACRO}.function()}    */
4134 	/*               function macros                      {{MACRO}.function()}     */
4135 	/*               simple macros                        {{MACRO}:key.function()} */
4136 	if ('.' == ptr[1])
4137 	{
4138 		return zbx_token_parse_func_macro(expression, macro, ptr + 2, token, '#' == macro[2] ?
4139 				ZBX_TOKEN_LLD_FUNC_MACRO : ZBX_TOKEN_FUNC_MACRO);
4140 	}
4141 	else if ('#' != macro[2] && ':' == ptr[1])
4142 		return zbx_token_parse_simple_macro_key(expression, macro, ptr + 2, token);
4143 
4144 	return FAIL;
4145 }
4146 
4147 /******************************************************************************
4148  *                                                                            *
4149  * Function: zbx_token_find                                                   *
4150  *                                                                            *
4151  * Purpose: finds token {} inside expression starting at specified position   *
4152  *          also searches for reference if requested                          *
4153  *                                                                            *
4154  * Parameters: expression   - [IN] the expression                             *
4155  *             pos          - [IN] the starting position                      *
4156  *             token        - [OUT] the token data                            *
4157  *             token_search - [IN] specify if references will be searched     *
4158  *                                                                            *
4159  * Return value: SUCCEED - the token was parsed successfully                  *
4160  *               FAIL    - expression does not contain valid token.           *
4161  *                                                                            *
4162  * Comments: The token field locations are specified as offsets from the      *
4163  *           beginning of the expression.                                     *
4164  *                                                                            *
4165  *           Simply iterating through tokens can be done with:                *
4166  *                                                                            *
4167  *           zbx_token_t token = {0};                                         *
4168  *                                                                            *
4169  *           while (SUCCEED == zbx_token_find(expression, token.loc.r + 1,    *
4170  *                       &token))                                             *
4171  *           {                                                                *
4172  *                   process_token(expression, &token);                       *
4173  *           }                                                                *
4174  *                                                                            *
4175  ******************************************************************************/
zbx_token_find(const char * expression,int pos,zbx_token_t * token,zbx_token_search_t token_search)4176 int	zbx_token_find(const char *expression, int pos, zbx_token_t *token, zbx_token_search_t token_search)
4177 {
4178 	int		ret = FAIL;
4179 	const char	*ptr = expression + pos, *dollar = ptr;
4180 
4181 	while (SUCCEED != ret)
4182 	{
4183 		ptr = strchr(ptr, '{');
4184 
4185 		switch (token_search)
4186 		{
4187 			case ZBX_TOKEN_SEARCH_BASIC:
4188 				break;
4189 			case ZBX_TOKEN_SEARCH_REFERENCES:
4190 				while (NULL != (dollar = strchr(dollar, '$')) && (NULL == ptr || ptr > dollar))
4191 				{
4192 					if (0 == isdigit(dollar[1]))
4193 					{
4194 						dollar++;
4195 						continue;
4196 					}
4197 
4198 					token->data.reference.index = dollar[1] - '0';
4199 					token->type = ZBX_TOKEN_REFERENCE;
4200 					token->loc.l = dollar - expression;
4201 					token->loc.r = token->loc.l + 1;
4202 					return SUCCEED;
4203 				}
4204 
4205 				if (NULL == dollar)
4206 					token_search = ZBX_TOKEN_SEARCH_BASIC;
4207 
4208 				break;
4209 			default:
4210 				THIS_SHOULD_NEVER_HAPPEN;
4211 		}
4212 
4213 		if (NULL == ptr)
4214 			return FAIL;
4215 
4216 		if ('\0' == ptr[1])
4217 			return FAIL;
4218 
4219 		switch (ptr[1])
4220 		{
4221 			case '$':
4222 				ret = zbx_token_parse_user_macro(expression, ptr, token);
4223 				break;
4224 			case '#':
4225 				ret = zbx_token_parse_lld_macro(expression, ptr, token);
4226 				break;
4227 
4228 			case '{':
4229 				ret = zbx_token_parse_nested_macro(expression, ptr, token);
4230 				break;
4231 			case '0':
4232 			case '1':
4233 			case '2':
4234 			case '3':
4235 			case '4':
4236 			case '5':
4237 			case '6':
4238 			case '7':
4239 			case '8':
4240 			case '9':
4241 				if (SUCCEED == (ret = zbx_token_parse_objectid(expression, ptr, token)))
4242 					break;
4243 				ZBX_FALLTHROUGH;
4244 			default:
4245 				if (SUCCEED != (ret = zbx_token_parse_macro(expression, ptr, token)))
4246 					ret = zbx_token_parse_simple_macro(expression, ptr, token);
4247 		}
4248 
4249 		ptr++;
4250 	}
4251 
4252 	return ret;
4253 }
4254 
4255 /******************************************************************************
4256  *                                                                            *
4257  * Function: zbx_no_function                                                  *
4258  *                                                                            *
4259  * Purpose: count calculated item (prototype) formula characters that can be  *
4260  *          skipped without the risk of missing a function                    *
4261  *                                                                            *
4262  ******************************************************************************/
zbx_no_function(const char * expr)4263 static size_t	zbx_no_function(const char *expr)
4264 {
4265 	const char	*ptr = expr;
4266 	int		inside_quote = 0, len, c_l, c_r;
4267 	zbx_token_t	token;
4268 
4269 	while ('\0' != *ptr)
4270 	{
4271 		switch  (*ptr)
4272 		{
4273 			case '\\':
4274 				if (0 != inside_quote)
4275 					ptr++;
4276 				break;
4277 			case '"':
4278 				inside_quote = !inside_quote;
4279 				ptr++;
4280 				continue;
4281 		}
4282 
4283 		if (inside_quote)
4284 		{
4285 			if ('\0' == *ptr)
4286 				break;
4287 			ptr++;
4288 			continue;
4289 		}
4290 
4291 		if ('{' == *ptr && '$' == *(ptr + 1) && SUCCEED == zbx_user_macro_parse(ptr, &len, &c_l, &c_r, NULL))
4292 		{
4293 			ptr += len + 1;	/* skip to the position after user macro */
4294 		}
4295 		else if ('{' == *ptr && '{' == *(ptr + 1) && '#' == *(ptr + 2) &&
4296 				SUCCEED == zbx_token_parse_nested_macro(ptr, ptr, &token))
4297 		{
4298 			ptr += token.loc.r - token.loc.l + 1;
4299 		}
4300 		else if (SUCCEED != is_function_char(*ptr))
4301 		{
4302 			ptr++;	/* skip one character which cannot belong to function name */
4303 		}
4304 		else if ((0 == strncmp("and", ptr, len = ZBX_CONST_STRLEN("and")) ||
4305 				0 == strncmp("not", ptr, len = ZBX_CONST_STRLEN("not")) ||
4306 				0 == strncmp("or", ptr, len = ZBX_CONST_STRLEN("or"))) &&
4307 				NULL != strchr("()" ZBX_WHITESPACE, ptr[len]))
4308 		{
4309 			ptr += len;	/* skip to the position after and/or/not operator */
4310 		}
4311 		else if (ptr > expr && 0 != isdigit(*(ptr - 1)) && NULL != strchr(ZBX_UNIT_SYMBOLS, *ptr))
4312 		{
4313 			ptr++;	/* skip unit suffix symbol if it's preceded by a digit */
4314 		}
4315 		else
4316 			break;
4317 	}
4318 
4319 	return ptr - expr;
4320 }
4321 
4322 /******************************************************************************
4323  *                                                                            *
4324  * Function: zbx_function_find                                                *
4325  *                                                                            *
4326  * Purpose: find the location of the next function and its parameters in      *
4327  *          calculated item (prototype) formula                               *
4328  *                                                                            *
4329  * Parameters: expr          - [IN] string to parse                           *
4330  *             func_pos      - [OUT] function position in the string          *
4331  *             par_l         - [OUT] position of the opening parenthesis      *
4332  *             par_r         - [OUT] position of the closing parenthesis      *
4333  *             error         - [OUT] error message                            *
4334  *             max_error_len - [IN] error size                                *
4335  *                                                                            *
4336  *                                                                            *
4337  * Return value: SUCCEED - function was found at func_pos                     *
4338  *               FAIL    - there are no functions in the expression           *
4339  *                                                                            *
4340  ******************************************************************************/
zbx_function_find(const char * expr,size_t * func_pos,size_t * par_l,size_t * par_r,char * error,int max_error_len)4341 int	zbx_function_find(const char *expr, size_t *func_pos, size_t *par_l, size_t *par_r, char *error,
4342 		int max_error_len)
4343 {
4344 	const char	*ptr;
4345 
4346 	for (ptr = expr; '\0' != *ptr; ptr += *par_l)
4347 	{
4348 		/* skip the part of expression that is definitely not a function */
4349 		ptr += zbx_no_function(ptr);
4350 		*par_r = 0;
4351 
4352 		/* try to validate function candidate */
4353 		if (SUCCEED != zbx_function_validate(ptr, par_l, par_r, error, max_error_len))
4354 		{
4355 			if (*par_l > *par_r)
4356 				return FAIL;
4357 
4358 			continue;
4359 		}
4360 
4361 		*func_pos = ptr - expr;
4362 		*par_l += *func_pos;
4363 		*par_r += *func_pos;
4364 		return SUCCEED;
4365 	}
4366 
4367 	zbx_snprintf(error, max_error_len, "Incorrect function expression: %s", expr);
4368 
4369 	return FAIL;
4370 }
4371 
4372 /******************************************************************************
4373  *                                                                            *
4374  * Function: zbx_strmatch_condition                                           *
4375  *                                                                            *
4376  * Purpose: check if pattern matches the specified value                      *
4377  *                                                                            *
4378  * Parameters: value    - [IN] the value to match                             *
4379  *             pattern  - [IN] the pattern to match                           *
4380  *             op       - [IN] the matching operator                          *
4381  *                                                                            *
4382  * Return value: SUCCEED - matches, FAIL - otherwise                          *
4383  *                                                                            *
4384  ******************************************************************************/
zbx_strmatch_condition(const char * value,const char * pattern,unsigned char op)4385 int	zbx_strmatch_condition(const char *value, const char *pattern, unsigned char op)
4386 {
4387 	int	ret = FAIL;
4388 
4389 	switch (op)
4390 	{
4391 		case CONDITION_OPERATOR_EQUAL:
4392 			if (0 == strcmp(value, pattern))
4393 				ret = SUCCEED;
4394 			break;
4395 		case CONDITION_OPERATOR_NOT_EQUAL:
4396 			if (0 != strcmp(value, pattern))
4397 				ret = SUCCEED;
4398 			break;
4399 		case CONDITION_OPERATOR_LIKE:
4400 			if (NULL != strstr(value, pattern))
4401 				ret = SUCCEED;
4402 			break;
4403 		case CONDITION_OPERATOR_NOT_LIKE:
4404 			if (NULL == strstr(value, pattern))
4405 				ret = SUCCEED;
4406 			break;
4407 	}
4408 
4409 	return ret;
4410 }
4411 
4412 /******************************************************************************
4413  *                                                                            *
4414  * Function: zbx_number_parse                                                 *
4415  *                                                                            *
4416  * Purpose: parse a number like "12.345"                                      *
4417  *                                                                            *
4418  * Parameters: number - [IN] start of number                                  *
4419  *             len    - [OUT] length of parsed number                         *
4420  *                                                                            *
4421  * Return value: SUCCEED - the number was parsed successfully                 *
4422  *               FAIL    - invalid number                                     *
4423  *                                                                            *
4424  * Comments: !!! Don't forget to sync the code with PHP !!!                   *
4425  *           The token field locations are specified as offsets from the      *
4426  *           beginning of the expression.                                     *
4427  *                                                                            *
4428  ******************************************************************************/
zbx_number_parse(const char * number,int * len)4429 int	zbx_number_parse(const char *number, int *len)
4430 {
4431 	int	digits = 0, dots = 0;
4432 
4433 	*len = 0;
4434 
4435 	while (1)
4436 	{
4437 		if (0 != isdigit(number[*len]))
4438 		{
4439 			(*len)++;
4440 			digits++;
4441 			continue;
4442 		}
4443 
4444 		if ('.' == number[*len])
4445 		{
4446 			(*len)++;
4447 			dots++;
4448 			continue;
4449 		}
4450 
4451 		if ('e' == number[*len] || 'E' == number[*len])
4452 		{
4453 			(*len)++;
4454 
4455 			if ('-' == number[*len] || '+' == number[*len])
4456 				(*len)++;
4457 
4458 			if (0 == isdigit(number[*len]))
4459 				return FAIL;
4460 
4461 			while (0 != isdigit(number[++(*len)]));
4462 
4463 			if ('.' == number[*len] ||'e' == number[*len] || 'E' == number[*len])
4464 				return FAIL;
4465 		}
4466 
4467 		if (1 > digits || 1 < dots)
4468 			return FAIL;
4469 
4470 		return SUCCEED;
4471 	}
4472 }
4473 
4474 /******************************************************************************
4475  *                                                                            *
4476  * Function: zbx_suffixed_number_parse                                        *
4477  *                                                                            *
4478  * Purpose: parse a suffixed number like "12.345K"                            *
4479  *                                                                            *
4480  * Parameters: number - [IN] start of number                                  *
4481  *             len    - [OUT] length of parsed number                         *
4482  *                                                                            *
4483  * Return value: SUCCEED - the number was parsed successfully                 *
4484  *               FAIL    - invalid number                                     *
4485  *                                                                            *
4486  * Comments: !!! Don't forget to sync the code with PHP !!!                   *
4487  *           The token field locations are specified as offsets from the      *
4488  *           beginning of the expression.                                     *
4489  *                                                                            *
4490  ******************************************************************************/
zbx_suffixed_number_parse(const char * number,int * len)4491 int	zbx_suffixed_number_parse(const char *number, int *len)
4492 {
4493 	if (FAIL == zbx_number_parse(number, len))
4494 		return FAIL;
4495 
4496 	if (0 != isalpha(number[*len]) && NULL != strchr(ZBX_UNIT_SYMBOLS, number[*len]))
4497 		(*len)++;
4498 
4499 	return SUCCEED;
4500 }
4501 
4502 /******************************************************************************
4503  *                                                                            *
4504  * Function: zbx_expression_next_constant                                     *
4505  *                                                                            *
4506  * Purpose: gets next constant (numeric/string value or unresolved user       *
4507  *          macro) from trigger expression.                                   *
4508  *                                                                            *
4509  * Parameters: src - [IN] the expression                                      *
4510  *             pos - [IN] the starting position                               *
4511  *             loc - [IN] the substring location                              *
4512  *                                                                            *
4513  * Return value: SUCCEED - the next constant was located.                     *
4514  *               FAIL    - otherwise.                                         *
4515  *                                                                            *
4516  ******************************************************************************/
zbx_expression_next_constant(const char * str,size_t pos,zbx_strloc_t * loc)4517 int	zbx_expression_next_constant(const char *str, size_t pos, zbx_strloc_t *loc)
4518 {
4519 	const char	*s;
4520 	zbx_token_t	token;
4521 	int		offset = 0, len;
4522 
4523 	for (s = str + pos; '\0' != *s; s++)
4524 	{
4525 		switch (*s)
4526 		{
4527 			case '"':
4528 				loc->l = s - str;
4529 
4530 				for (++s;'\0' != *s; s++)
4531 				{
4532 					if ('"' == *s)
4533 					{
4534 						loc->r = s - str;
4535 						return SUCCEED;
4536 					}
4537 					if ('\\' == *s)
4538 					{
4539 						if ('\\' != s[1] && '"' != s[1])
4540 							return FAIL;
4541 						s++;
4542 					}
4543 				}
4544 				return FAIL;
4545 			case '{':
4546 				if (SUCCEED == zbx_token_find(str, s - str, &token, ZBX_TOKEN_SEARCH_BASIC))
4547 				{
4548 					if (ZBX_TOKEN_USER_MACRO == token.type)
4549 					{
4550 						*loc = token.loc;
4551 						return SUCCEED;
4552 					}
4553 					/* Skip all other tokens. Currently it can be only {TRIGGER.VALUE} macro. */
4554 					s = str + token.loc.r;
4555 				}
4556 				continue;
4557 			case '-':
4558 				offset = 1;
4559 				continue;
4560 			case '0':
4561 			case '1':
4562 			case '2':
4563 			case '3':
4564 			case '4':
4565 			case '5':
4566 			case '6':
4567 			case '7':
4568 			case '8':
4569 			case '9':
4570 			case '.':
4571 				if (SUCCEED != zbx_suffixed_number_parse(s, &len))
4572 					return FAIL;
4573 
4574 				loc->l = s - str - offset;
4575 				loc->r = s - str + len - 1;
4576 				return SUCCEED;
4577 			default:
4578 				offset = 0;
4579 		}
4580 	}
4581 	return FAIL;
4582 }
4583 
4584 /******************************************************************************
4585  *                                                                            *
4586  * Function: zbx_expression_extract_constant                                  *
4587  *                                                                            *
4588  * Purpose: extracts constant from trigger expression unquoting/escaping if   *
4589  *          necessary                                                         *
4590  *                                                                            *
4591  * Parameters: src - [IN] the source string                                   *
4592  *             loc - [IN] the substring location                              *
4593  *                                                                            *
4594  * Return value: The constant.                                                *
4595  *                                                                            *
4596  ******************************************************************************/
zbx_expression_extract_constant(const char * src,const zbx_strloc_t * loc)4597 char	*zbx_expression_extract_constant(const char *src, const zbx_strloc_t *loc)
4598 {
4599 	char		*str, *pout;
4600 	const char	*pin;
4601 	size_t		len;
4602 
4603 	len = loc->r - loc->l + 1;
4604 	str = zbx_malloc(NULL, len + 1);
4605 
4606 	if ('"' == src[loc->l])
4607 	{
4608 		for (pout = str, pin = src + loc->l + 1; pin <= src + loc->r - 1; pin++)
4609 		{
4610 			if ('\\' == *pin)
4611 			{
4612 				pin++;
4613 				switch (*pin)
4614 				{
4615 					case '\\':
4616 						*pout++ = '\\';
4617 						break;
4618 					case '"':
4619 						*pout++ = '"';
4620 						break;
4621 					default:
4622 						THIS_SHOULD_NEVER_HAPPEN;
4623 						*pout++ = '?';
4624 				}
4625 			}
4626 			else
4627 				*pout++ = *pin;
4628 		}
4629 		*pout++  ='\0';
4630 	}
4631 	else
4632 	{
4633 		memcpy(str, src + loc->l, len);
4634 		str[len] = '\0';
4635 	}
4636 
4637 	return str;
4638 }
4639 
4640 /******************************************************************************
4641  *                                                                            *
4642  * Function: num_param                                                        *
4643  *                                                                            *
4644  * Purpose: find number of parameters in parameter list                       *
4645  *                                                                            *
4646  * Parameters:                                                                *
4647  *      p - [IN] parameter list                                               *
4648  *                                                                            *
4649  * Return value: number of parameters (starting from 1) or                    *
4650  *               0 if syntax error                                            *
4651  *                                                                            *
4652  * Author: Alexei Vladishev                                                   *
4653  *                                                                            *
4654  * Comments:  delimiter for parameters is ','. Empty parameter list or a list *
4655  *            containing only spaces is handled as having one empty parameter *
4656  *            and 1 is returned.                                              *
4657  *                                                                            *
4658  ******************************************************************************/
num_param(const char * p)4659 int	num_param(const char *p)
4660 {
4661 /* 0 - init, 1 - inside quoted param, 2 - inside unquoted param */
4662 	int	ret = 1, state, array;
4663 
4664 	if (p == NULL)
4665 		return 0;
4666 
4667 	for (state = 0, array = 0; '\0' != *p; p++)
4668 	{
4669 		switch (state) {
4670 		/* Init state */
4671 		case 0:
4672 			if (',' == *p)
4673 			{
4674 				if (0 == array)
4675 					ret++;
4676 			}
4677 			else if ('"' == *p)
4678 				state = 1;
4679 			else if ('[' == *p)
4680 			{
4681 				if (0 == array)
4682 					array = 1;
4683 				else
4684 					return 0;	/* incorrect syntax: multi-level array */
4685 			}
4686 			else if (']' == *p && 0 != array)
4687 			{
4688 				array = 0;
4689 
4690 				while (' ' == p[1])	/* skip trailing spaces after closing ']' */
4691 					p++;
4692 
4693 				if (',' != p[1] && '\0' != p[1])
4694 					return 0;	/* incorrect syntax */
4695 			}
4696 			else if (']' == *p && 0 == array)
4697 				return 0;		/* incorrect syntax */
4698 			else if (' ' != *p)
4699 				state = 2;
4700 			break;
4701 		/* Quoted */
4702 		case 1:
4703 			if ('"' == *p)
4704 			{
4705 				while (' ' == p[1])	/* skip trailing spaces after closing quotes */
4706 					p++;
4707 
4708 				if (',' != p[1] && '\0' != p[1] && (0 == array || ']' != p[1]))
4709 					return 0;	/* incorrect syntax */
4710 
4711 				state = 0;
4712 			}
4713 			else if ('\\' == *p && '"' == p[1])
4714 				p++;
4715 			break;
4716 		/* Unquoted */
4717 		case 2:
4718 			if (',' == *p || (']' == *p && 0 != array))
4719 			{
4720 				p--;
4721 				state = 0;
4722 			}
4723 			else if (']' == *p && 0 == array)
4724 				return 0;		/* incorrect syntax */
4725 			break;
4726 		}
4727 	}
4728 
4729 	/* missing terminating '"' character */
4730 	if (state == 1)
4731 		return 0;
4732 
4733 	/* missing terminating ']' character */
4734 	if (array != 0)
4735 		return 0;
4736 
4737 	return ret;
4738 }
4739 
4740 /******************************************************************************
4741  *                                                                            *
4742  * Function: get_param                                                        *
4743  *                                                                            *
4744  * Purpose: return parameter by index (num) from parameter list (param)       *
4745  *                                                                            *
4746  * Parameters:                                                                *
4747  *      p       - [IN]  parameter list                                        *
4748  *      num     - [IN]  requested parameter index                             *
4749  *      buf     - [OUT] pointer of output buffer                              *
4750  *      max_len - [IN]  size of output buffer                                 *
4751  *      type    - [OUT] parameter type (may be NULL)                          *
4752  *                                                                            *
4753  * Return value:                                                              *
4754  *      1 - requested parameter missing or buffer overflow                    *
4755  *      0 - requested parameter found (value - 'buf' can be empty string)     *
4756  *                                                                            *
4757  * Author: Eugene Grigorjev, rewritten by Alexei Vladishev                    *
4758  *                                                                            *
4759  * Comments:  delimiter for parameters is ','                                 *
4760  *                                                                            *
4761  ******************************************************************************/
get_param(const char * p,int num,char * buf,size_t max_len,zbx_request_parameter_type_t * type)4762 int	get_param(const char *p, int num, char *buf, size_t max_len, zbx_request_parameter_type_t *type)
4763 {
4764 #define ZBX_ASSIGN_PARAM				\
4765 {							\
4766 	if (buf_i == max_len)				\
4767 		return 1;	/* buffer overflow */	\
4768 	buf[buf_i++] = *p;				\
4769 }
4770 
4771 	int	state;	/* 0 - init, 1 - inside quoted param, 2 - inside unquoted param */
4772 	int	array, idx = 1;
4773 	size_t	buf_i = 0;
4774 
4775 	if (NULL != type)
4776 		*type = REQUEST_PARAMETER_TYPE_UNDEFINED;
4777 
4778 	if (0 == max_len)
4779 		return 1;	/* buffer overflow */
4780 
4781 	max_len--;	/* '\0' */
4782 
4783 	for (state = 0, array = 0; '\0' != *p && idx <= num; p++)
4784 	{
4785 		switch (state)
4786 		{
4787 			/* init state */
4788 			case 0:
4789 				if (',' == *p)
4790 				{
4791 					if (0 == array)
4792 						idx++;
4793 					else if (idx == num)
4794 						ZBX_ASSIGN_PARAM;
4795 				}
4796 				else if ('"' == *p)
4797 				{
4798 					state = 1;
4799 
4800 					if (idx == num)
4801 					{
4802 						if (NULL != type && REQUEST_PARAMETER_TYPE_UNDEFINED == *type)
4803 							*type = REQUEST_PARAMETER_TYPE_STRING;
4804 
4805 						if (0 != array)
4806 							ZBX_ASSIGN_PARAM;
4807 					}
4808 				}
4809 				else if ('[' == *p)
4810 				{
4811 					if (idx == num)
4812 					{
4813 						if (NULL != type && REQUEST_PARAMETER_TYPE_UNDEFINED == *type)
4814 							*type = REQUEST_PARAMETER_TYPE_ARRAY;
4815 
4816 						if (0 != array)
4817 							ZBX_ASSIGN_PARAM;
4818 					}
4819 					array++;
4820 				}
4821 				else if (']' == *p && 0 != array)
4822 				{
4823 					array--;
4824 					if (0 != array && idx == num)
4825 						ZBX_ASSIGN_PARAM;
4826 
4827 					/* skip spaces */
4828 					while (' ' == p[1])
4829 						p++;
4830 
4831 					if (',' != p[1] && '\0' != p[1] && (0 == array || ']' != p[1]))
4832 						return 1;	/* incorrect syntax */
4833 				}
4834 				else if (' ' != *p)
4835 				{
4836 					if (idx == num)
4837 					{
4838 						if (NULL != type && REQUEST_PARAMETER_TYPE_UNDEFINED == *type)
4839 							*type = REQUEST_PARAMETER_TYPE_STRING;
4840 
4841 						ZBX_ASSIGN_PARAM;
4842 					}
4843 
4844 					state = 2;
4845 				}
4846 				break;
4847 			case 1:
4848 				/* quoted */
4849 
4850 				if ('"' == *p)
4851 				{
4852 					if (0 != array && idx == num)
4853 						ZBX_ASSIGN_PARAM;
4854 
4855 					/* skip spaces */
4856 					while (' ' == p[1])
4857 						p++;
4858 
4859 					if (',' != p[1] && '\0' != p[1] && (0 == array || ']' != p[1]))
4860 						return 1;	/* incorrect syntax */
4861 
4862 					state = 0;
4863 				}
4864 				else if ('\\' == *p && '"' == p[1])
4865 				{
4866 					if (idx == num && 0 != array)
4867 						ZBX_ASSIGN_PARAM;
4868 
4869 					p++;
4870 
4871 					if (idx == num)
4872 						ZBX_ASSIGN_PARAM;
4873 				}
4874 				else if (idx == num)
4875 					ZBX_ASSIGN_PARAM;
4876 				break;
4877 			case 2:
4878 				/* unquoted */
4879 
4880 				if (',' == *p || (']' == *p && 0 != array))
4881 				{
4882 					p--;
4883 					state = 0;
4884 				}
4885 				else if (idx == num)
4886 					ZBX_ASSIGN_PARAM;
4887 				break;
4888 		}
4889 
4890 		if (idx > num)
4891 			break;
4892 	}
4893 #undef ZBX_ASSIGN_PARAM
4894 
4895 	/* missing terminating '"' character */
4896 	if (1 == state)
4897 		return 1;
4898 
4899 	/* missing terminating ']' character */
4900 	if (0 != array)
4901 		return 1;
4902 
4903 	buf[buf_i] = '\0';
4904 
4905 	if (idx >= num)
4906 		return 0;
4907 
4908 	return 1;
4909 }
4910 
4911 /******************************************************************************
4912  *                                                                            *
4913  * Function: get_param_len                                                    *
4914  *                                                                            *
4915  * Purpose: return length of the parameter by index (num)                     *
4916  *          from parameter list (param)                                       *
4917  *                                                                            *
4918  * Parameters:                                                                *
4919  *      p   - [IN]  parameter list                                            *
4920  *      num - [IN]  requested parameter index                                 *
4921  *      sz  - [OUT] length of requested parameter                             *
4922  *                                                                            *
4923  * Return value:                                                              *
4924  *      1 - requested parameter missing                                       *
4925  *      0 - requested parameter found                                         *
4926  *          (for first parameter result is always 0)                          *
4927  *                                                                            *
4928  * Author: Alexander Vladishev                                                *
4929  *                                                                            *
4930  * Comments: delimiter for parameters is ','                                  *
4931  *                                                                            *
4932  ******************************************************************************/
get_param_len(const char * p,int num,size_t * sz)4933 static int	get_param_len(const char *p, int num, size_t *sz)
4934 {
4935 /* 0 - init, 1 - inside quoted param, 2 - inside unquoted param */
4936 	int	state, array, idx = 1;
4937 
4938 	*sz = 0;
4939 
4940 	for (state = 0, array = 0; '\0' != *p && idx <= num; p++)
4941 	{
4942 		switch (state) {
4943 		/* Init state */
4944 		case 0:
4945 			if (',' == *p)
4946 			{
4947 				if (0 == array)
4948 					idx++;
4949 				else if (idx == num)
4950 					(*sz)++;
4951 			}
4952 			else if ('"' == *p)
4953 			{
4954 				state = 1;
4955 				if (0 != array && idx == num)
4956 					(*sz)++;
4957 			}
4958 			else if ('[' == *p)
4959 			{
4960 				if (0 != array && idx == num)
4961 					(*sz)++;
4962 				array++;
4963 			}
4964 			else if (']' == *p && 0 != array)
4965 			{
4966 				array--;
4967 				if (0 != array && idx == num)
4968 					(*sz)++;
4969 
4970 				/* skip spaces */
4971 				while (' ' == p[1])
4972 					p++;
4973 
4974 				if (',' != p[1] && '\0' != p[1] && (0 == array || ']' != p[1]))
4975 					return 1;	/* incorrect syntax */
4976 			}
4977 			else if (' ' != *p)
4978 			{
4979 				if (idx == num)
4980 					(*sz)++;
4981 				state = 2;
4982 			}
4983 			break;
4984 		/* Quoted */
4985 		case 1:
4986 			if ('"' == *p)
4987 			{
4988 				if (0 != array && idx == num)
4989 					(*sz)++;
4990 
4991 				/* skip spaces */
4992 				while (' ' == p[1])
4993 					p++;
4994 
4995 				if (',' != p[1] && '\0' != p[1] && (0 == array || ']' != p[1]))
4996 					return 1;	/* incorrect syntax */
4997 
4998 				state = 0;
4999 			}
5000 			else if ('\\' == *p && '"' == p[1])
5001 			{
5002 				if (idx == num && 0 != array)
5003 					(*sz)++;
5004 
5005 				p++;
5006 
5007 				if (idx == num)
5008 					(*sz)++;
5009 			}
5010 			else if (idx == num)
5011 				(*sz)++;
5012 			break;
5013 		/* Unquoted */
5014 		case 2:
5015 			if (',' == *p || (']' == *p && 0 != array))
5016 			{
5017 				p--;
5018 				state = 0;
5019 			}
5020 			else if (idx == num)
5021 				(*sz)++;
5022 			break;
5023 		}
5024 
5025 		if (idx > num)
5026 			break;
5027 	}
5028 
5029 	/* missing terminating '"' character */
5030 	if (state == 1)
5031 		return 1;
5032 
5033 	/* missing terminating ']' character */
5034 	if (array != 0)
5035 		return 1;
5036 
5037 	if (idx >= num)
5038 		return 0;
5039 
5040 	return 1;
5041 }
5042 
5043 /******************************************************************************
5044  *                                                                            *
5045  * Function: get_param_dyn                                                    *
5046  *                                                                            *
5047  * Purpose: return parameter by index (num) from parameter list (param)       *
5048  *                                                                            *
5049  * Parameters:                                                                *
5050  *      p    - [IN] parameter list                                            *
5051  *      num  - [IN] requested parameter index                                 *
5052  *      type - [OUT] parameter type (may be NULL)                             *
5053  *                                                                            *
5054  * Return value:                                                              *
5055  *      NULL - requested parameter missing                                    *
5056  *      otherwise - requested parameter                                       *
5057  *          (for first parameter result is not NULL)                          *
5058  *                                                                            *
5059  * Author: Alexander Vladishev                                                *
5060  *                                                                            *
5061  * Comments:  delimiter for parameters is ','                                 *
5062  *                                                                            *
5063  ******************************************************************************/
get_param_dyn(const char * p,int num,zbx_request_parameter_type_t * type)5064 char	*get_param_dyn(const char *p, int num, zbx_request_parameter_type_t *type)
5065 {
5066 	char	*buf = NULL;
5067 	size_t	sz;
5068 
5069 	if (0 != get_param_len(p, num, &sz))
5070 		return buf;
5071 
5072 	buf = (char *)zbx_malloc(buf, sz + 1);
5073 
5074 	if (0 != get_param(p, num, buf, sz + 1, type))
5075 		zbx_free(buf);
5076 
5077 	return buf;
5078 }
5079 
5080 /******************************************************************************
5081  *                                                                            *
5082  * Function: replace_key_param                                                *
5083  *                                                                            *
5084  * Purpose: replaces an item key, SNMP OID or their parameters when callback  *
5085  *          function returns a new string                                     *
5086  *                                                                            *
5087  * Comments: auxiliary function for replace_key_params_dyn()                  *
5088  *                                                                            *
5089  ******************************************************************************/
replace_key_param(char ** data,int key_type,size_t l,size_t * r,int level,int num,int quoted,replace_key_param_f cb,void * cb_data)5090 static int	replace_key_param(char **data, int key_type, size_t l, size_t *r, int level, int num, int quoted,
5091 		replace_key_param_f cb, void *cb_data)
5092 {
5093 	char	c = (*data)[*r], *param = NULL;
5094 	int	ret;
5095 
5096 	(*data)[*r] = '\0';
5097 	ret = cb(*data + l, key_type, level, num, quoted, cb_data, &param);
5098 	(*data)[*r] = c;
5099 
5100 	if (NULL != param)
5101 	{
5102 		(*r)--;
5103 		zbx_replace_string(data, l, r, param);
5104 		(*r)++;
5105 
5106 		zbx_free(param);
5107 	}
5108 
5109 	return ret;
5110 }
5111 
5112 /******************************************************************************
5113  *                                                                            *
5114  * Function: replace_key_params_dyn                                           *
5115  *                                                                            *
5116  * Purpose: replaces an item key, SNMP OID or their parameters by using       *
5117  *          callback function                                                 *
5118  *                                                                            *
5119  * Parameters:                                                                *
5120  *      data      - [IN/OUT] item key or SNMP OID                             *
5121  *      key_type  - [IN] ZBX_KEY_TYPE_*                                       *
5122  *      cb        - [IN] callback function                                    *
5123  *      cb_data   - [IN] callback function custom data                        *
5124  *      error     - [OUT] error message                                       *
5125  *      maxerrlen - [IN] error size                                           *
5126  *                                                                            *
5127  * Return value: SUCCEED - function executed successfully                     *
5128  *               FAIL - otherwise, error will contain error message           *
5129  *                                                                            *
5130  ******************************************************************************/
replace_key_params_dyn(char ** data,int key_type,replace_key_param_f cb,void * cb_data,char * error,size_t maxerrlen)5131 int	replace_key_params_dyn(char **data, int key_type, replace_key_param_f cb, void *cb_data, char *error,
5132 		size_t maxerrlen)
5133 {
5134 	typedef enum
5135 	{
5136 		ZBX_STATE_NEW,
5137 		ZBX_STATE_END,
5138 		ZBX_STATE_UNQUOTED,
5139 		ZBX_STATE_QUOTED
5140 	}
5141 	zbx_parser_state_t;
5142 
5143 	size_t			i = 0, l = 0;
5144 	int			level = 0, num = 0, ret = SUCCEED;
5145 	zbx_parser_state_t	state = ZBX_STATE_NEW;
5146 
5147 	if (ZBX_KEY_TYPE_ITEM == key_type)
5148 	{
5149 		for (; SUCCEED == is_key_char((*data)[i]) && '\0' != (*data)[i]; i++)
5150 			;
5151 
5152 		if (0 == i)
5153 			goto clean;
5154 
5155 		if ('[' != (*data)[i] && '\0' != (*data)[i])
5156 			goto clean;
5157 	}
5158 	else
5159 	{
5160 		zbx_token_t	token;
5161 		int		len, c_l, c_r;
5162 
5163 		while ('\0' != (*data)[i])
5164 		{
5165 			if ('{' == (*data)[i] && '$' == (*data)[i + 1] &&
5166 					SUCCEED == zbx_user_macro_parse(&(*data)[i], &len, &c_l, &c_r, NULL))
5167 			{
5168 				i += len + 1;	/* skip to the position after user macro */
5169 			}
5170 			else if ('{' == (*data)[i] && '{' == (*data)[i + 1] && '#' == (*data)[i + 2] &&
5171 					SUCCEED == zbx_token_parse_nested_macro(&(*data)[i], &(*data)[i], &token))
5172 			{
5173 				i += token.loc.r - token.loc.l + 1;
5174 			}
5175 			else if ('[' != (*data)[i])
5176 			{
5177 				i++;
5178 			}
5179 			else
5180 				break;
5181 		}
5182 	}
5183 
5184 	ret = replace_key_param(data, key_type, 0, &i, level, num, 0, cb, cb_data);
5185 
5186 	for (; '\0' != (*data)[i] && FAIL != ret; i++)
5187 	{
5188 		switch (state)
5189 		{
5190 			case ZBX_STATE_NEW:	/* a new parameter started */
5191 				switch ((*data)[i])
5192 				{
5193 					case ' ':
5194 						break;
5195 					case ',':
5196 						ret = replace_key_param(data, key_type, i, &i, level, num, 0, cb,
5197 								cb_data);
5198 						if (1 == level)
5199 							num++;
5200 						break;
5201 					case '[':
5202 						if (2 == level)
5203 							goto clean;	/* incorrect syntax: multi-level array */
5204 						level++;
5205 						if (1 == level)
5206 							num++;
5207 						break;
5208 					case ']':
5209 						ret = replace_key_param(data, key_type, i, &i, level, num, 0, cb,
5210 								cb_data);
5211 						level--;
5212 						state = ZBX_STATE_END;
5213 						break;
5214 					case '"':
5215 						state = ZBX_STATE_QUOTED;
5216 						l = i;
5217 						break;
5218 					default:
5219 						state = ZBX_STATE_UNQUOTED;
5220 						l = i;
5221 				}
5222 				break;
5223 			case ZBX_STATE_END:	/* end of parameter */
5224 				switch ((*data)[i])
5225 				{
5226 					case ' ':
5227 						break;
5228 					case ',':
5229 						state = ZBX_STATE_NEW;
5230 						if (1 == level)
5231 							num++;
5232 						break;
5233 					case ']':
5234 						if (0 == level)
5235 							goto clean;	/* incorrect syntax: redundant ']' */
5236 						level--;
5237 						break;
5238 					default:
5239 						goto clean;
5240 				}
5241 				break;
5242 			case ZBX_STATE_UNQUOTED:	/* an unquoted parameter */
5243 				if (']' == (*data)[i] || ',' == (*data)[i])
5244 				{
5245 					ret = replace_key_param(data, key_type, l, &i, level, num, 0, cb, cb_data);
5246 
5247 					i--;
5248 					state = ZBX_STATE_END;
5249 				}
5250 				break;
5251 			case ZBX_STATE_QUOTED:	/* a quoted parameter */
5252 				if ('"' == (*data)[i] && '\\' != (*data)[i - 1])
5253 				{
5254 					i++;
5255 					ret = replace_key_param(data, key_type, l, &i, level, num, 1, cb, cb_data);
5256 					i--;
5257 
5258 					state = ZBX_STATE_END;
5259 				}
5260 				break;
5261 		}
5262 	}
5263 clean:
5264 	if (0 == i || '\0' != (*data)[i] || 0 != level)
5265 	{
5266 		if (NULL != error)
5267 		{
5268 			zbx_snprintf(error, maxerrlen, "Invalid %s at position " ZBX_FS_SIZE_T,
5269 					(ZBX_KEY_TYPE_ITEM == key_type ? "item key" : "SNMP OID"), (zbx_fs_size_t)i);
5270 		}
5271 		ret = FAIL;
5272 	}
5273 
5274 	return ret;
5275 }
5276 
5277 /******************************************************************************
5278  *                                                                            *
5279  * Function: remove_param                                                     *
5280  *                                                                            *
5281  * Purpose: remove parameter by index (num) from parameter list (param)       *
5282  *                                                                            *
5283  * Parameters:                                                                *
5284  *      param  - parameter list                                               *
5285  *      num    - requested parameter index                                    *
5286  *                                                                            *
5287  * Return value:                                                              *
5288  *                                                                            *
5289  * Comments: delimiter for parameters is ','                                  *
5290  *                                                                            *
5291  ******************************************************************************/
remove_param(char * param,int num)5292 void	remove_param(char *param, int num)
5293 {
5294 	int	state = 0;	/* 0 - unquoted parameter, 1 - quoted parameter */
5295 	int	idx = 1, skip_char = 0;
5296 	char	*p;
5297 
5298 	for (p = param; '\0' != *p; p++)
5299 	{
5300 		switch (state)
5301 		{
5302 			case 0:			/* in unquoted parameter */
5303 				if (',' == *p)
5304 				{
5305 					if (1 == idx && 1 == num)
5306 						skip_char = 1;
5307 					idx++;
5308 				}
5309 				else if ('"' == *p)
5310 					state = 1;
5311 				break;
5312 			case 1:			/* in quoted parameter */
5313 				if ('"' == *p && '\\' != *(p - 1))
5314 					state = 0;
5315 				break;
5316 		}
5317 		if (idx != num && 0 == skip_char)
5318 			*param++ = *p;
5319 
5320 		skip_char = 0;
5321 	}
5322 
5323 	*param = '\0';
5324 }
5325 
5326 /******************************************************************************
5327  *                                                                            *
5328  * Function: str_in_list                                                      *
5329  *                                                                            *
5330  * Purpose: check if string is contained in a list of delimited strings       *
5331  *                                                                            *
5332  * Parameters: list      - strings a,b,ccc,ddd                                *
5333  *             value     - value                                              *
5334  *             delimiter - delimiter                                          *
5335  *                                                                            *
5336  * Return value: SUCCEED - string is in the list, FAIL - otherwise            *
5337  *                                                                            *
5338  * Author: Alexei Vladishev, Aleksandrs Saveljevs                             *
5339  *                                                                            *
5340  ******************************************************************************/
str_in_list(const char * list,const char * value,char delimiter)5341 int	str_in_list(const char *list, const char *value, char delimiter)
5342 {
5343 	const char	*end;
5344 	int		ret = FAIL;
5345 	size_t		len;
5346 
5347 	len = strlen(value);
5348 
5349 	while (SUCCEED != ret)
5350 	{
5351 		if (NULL != (end = strchr(list, delimiter)))
5352 		{
5353 			ret = (len == (size_t)(end - list) && 0 == strncmp(list, value, len) ? SUCCEED : FAIL);
5354 			list = end + 1;
5355 		}
5356 		else
5357 		{
5358 			ret = (0 == strcmp(list, value) ? SUCCEED : FAIL);
5359 			break;
5360 		}
5361 	}
5362 
5363 	return ret;
5364 }
5365 
5366 /******************************************************************************
5367  *                                                                            *
5368  * Function: get_key_param                                                    *
5369  *                                                                            *
5370  * Purpose: return parameter by index (num) from parameter list (param)       *
5371  *          to be used for keys: key[param1,param2]                           *
5372  *                                                                            *
5373  * Parameters:                                                                *
5374  *      param   - parameter list                                              *
5375  *      num     - requested parameter index                                   *
5376  *      buf     - pointer of output buffer                                    *
5377  *      max_len - size of output buffer                                       *
5378  *                                                                            *
5379  * Return value:                                                              *
5380  *      1 - requested parameter missing                                       *
5381  *      0 - requested parameter found (value - 'buf' can be empty string)     *
5382  *                                                                            *
5383  * Author: Alexei Vladishev                                                   *
5384  *                                                                            *
5385  * Comments:  delimiter for parameters is ','                                 *
5386  *                                                                            *
5387  ******************************************************************************/
get_key_param(char * param,int num,char * buf,size_t max_len)5388 int	get_key_param(char *param, int num, char *buf, size_t max_len)
5389 {
5390 	int	ret;
5391 	char	*pl, *pr;
5392 
5393 	pl = strchr(param, '[');
5394 	pr = strrchr(param, ']');
5395 
5396 	if (NULL == pl || NULL == pr || pl > pr)
5397 		return 1;
5398 
5399 	*pr = '\0';
5400 	ret = get_param(pl + 1, num, buf, max_len, NULL);
5401 	*pr = ']';
5402 
5403 	return ret;
5404 }
5405 
5406 /******************************************************************************
5407  *                                                                            *
5408  * Function: num_key_param                                                    *
5409  *                                                                            *
5410  * Purpose: calculate count of parameters from parameter list (param)         *
5411  *          to be used for keys: key[param1,param2]                           *
5412  *                                                                            *
5413  * Parameters:                                                                *
5414  *      param  - parameter list                                               *
5415  *                                                                            *
5416  * Return value: count of parameters                                          *
5417  *                                                                            *
5418  * Author: Alexei Vladishev                                                   *
5419  *                                                                            *
5420  * Comments:  delimiter for parameters is ','                                 *
5421  *                                                                            *
5422  ******************************************************************************/
num_key_param(char * param)5423 int	num_key_param(char *param)
5424 {
5425 	int	ret;
5426 	char	*pl, *pr;
5427 
5428 	if (NULL == param)
5429 		return 0;
5430 
5431 	pl = strchr(param, '[');
5432 	pr = strrchr(param, ']');
5433 
5434 	if (NULL == pl || NULL == pr || pl > pr)
5435 		return 0;
5436 
5437 	*pr = '\0';
5438 	ret = num_param(pl + 1);
5439 	*pr = ']';
5440 
5441 	return ret;
5442 }
5443 
5444 /******************************************************************************
5445  *                                                                            *
5446  * Function: zbx_replace_mem_dyn                                              *
5447  *                                                                            *
5448  * Purpose: to replace memory block and allocate more memory if needed        *
5449  *                                                                            *
5450  * Parameters: data       - [IN/OUT] allocated memory                         *
5451  *             data_alloc - [IN/OUT] allocated memory size                    *
5452  *             data_len   - [IN/OUT] used memory size                         *
5453  *             offset     - [IN] offset of memory block to be replaced        *
5454  *             sz_to      - [IN] size of block that need to be replaced       *
5455  *             from       - [IN] what to replace with                         *
5456  *             sz_from    - [IN] size of new block                            *
5457  *                                                                            *
5458  * Return value: once data is replaced offset can become less, bigger or      *
5459  *               remain unchanged                                             *
5460  ******************************************************************************/
zbx_replace_mem_dyn(char ** data,size_t * data_alloc,size_t * data_len,size_t offset,size_t sz_to,const char * from,size_t sz_from)5461 int	zbx_replace_mem_dyn(char **data, size_t *data_alloc, size_t *data_len, size_t offset, size_t sz_to,
5462 		const char *from, size_t sz_from)
5463 {
5464 	size_t	sz_changed = sz_from - sz_to;
5465 
5466 	if (0 != sz_changed)
5467 	{
5468 		char	*to;
5469 
5470 		*data_len += sz_changed;
5471 
5472 		if (*data_len > *data_alloc)
5473 		{
5474 			while (*data_len > *data_alloc)
5475 				*data_alloc *= 2;
5476 
5477 			*data = (char *)zbx_realloc(*data, *data_alloc);
5478 		}
5479 
5480 		to = *data + offset;
5481 		memmove(to + sz_from, to + sz_to, *data_len - (to - *data) - sz_from);
5482 	}
5483 
5484 	memcpy(*data + offset, from, sz_from);
5485 
5486 	return (int)sz_changed;
5487 }
5488 
5489 /******************************************************************************
5490  *                                                                            *
5491  * Function: zbx_strsplit                                                     *
5492  *                                                                            *
5493  * Purpose: splits string                                                     *
5494  *                                                                            *
5495  * Parameters: src       - [IN] source string                                 *
5496  *             delimiter - [IN] delimiter                                     *
5497  *             left      - [IN/OUT] first part of the string                  *
5498  *             right     - [IN/OUT] second part of the string or NULL, if     *
5499  *                                  delimiter was not found                   *
5500  *                                                                            *
5501  ******************************************************************************/
zbx_strsplit(const char * src,char delimiter,char ** left,char ** right)5502 void	zbx_strsplit(const char *src, char delimiter, char **left, char **right)
5503 {
5504 	char	*delimiter_ptr;
5505 
5506 	if (NULL == (delimiter_ptr = strchr(src, delimiter)))
5507 	{
5508 		*left = zbx_strdup(NULL, src);
5509 		*right = NULL;
5510 	}
5511 	else
5512 	{
5513 		size_t	left_size;
5514 		size_t	right_size;
5515 
5516 		left_size = (size_t)(delimiter_ptr - src) + 1;
5517 		right_size = strlen(src) - (size_t)(delimiter_ptr - src);
5518 
5519 		*left = zbx_malloc(NULL, left_size);
5520 		*right = zbx_malloc(NULL, right_size);
5521 
5522 		memcpy(*left, src, left_size - 1);
5523 		(*left)[left_size - 1] = '\0';
5524 		memcpy(*right, delimiter_ptr + 1, right_size);
5525 	}
5526 }
5527 
5528 /******************************************************************************
5529  *                                                                            *
5530  * Function: zbx_trim_number                                                  *
5531  *                                                                            *
5532  * Purpose: Removes spaces from both ends of the string, then unquotes it if  *
5533  *          double quotation mark is present on both ends of the string. If   *
5534  *          strip_plus_sign is non-zero, then removes single "+" sign from    *
5535  *          the beginning of the trimmed and unquoted string.                 *
5536  *                                                                            *
5537  *          This function does not guarantee that the resulting string        *
5538  *          contains numeric value. It is meant to be used for removing       *
5539  *          "valid" characters from the value that is expected to be numeric  *
5540  *          before checking if value is numeric.                              *
5541  *                                                                            *
5542  * Parameters: str             - [IN/OUT] string for processing               *
5543  *             strip_plus_sign - [IN] non-zero if "+" should be stripped      *
5544  *                                                                            *
5545  ******************************************************************************/
zbx_trim_number(char * str,int strip_plus_sign)5546 static void	zbx_trim_number(char *str, int strip_plus_sign)
5547 {
5548 	char	*left = str;			/* pointer to the first character */
5549 	char	*right = strchr(str, '\0') - 1; /* pointer to the last character, not including terminating null-char */
5550 
5551 	if (left > right)
5552 	{
5553 		/* string is empty before any trimming */
5554 		return;
5555 	}
5556 
5557 	while (' ' == *left)
5558 	{
5559 		left++;
5560 	}
5561 
5562 	while (' ' == *right && left < right)
5563 	{
5564 		right--;
5565 	}
5566 
5567 	if ('"' == *left && '"' == *right && left < right)
5568 	{
5569 		left++;
5570 		right--;
5571 	}
5572 
5573 	if (0 != strip_plus_sign && '+' == *left)
5574 	{
5575 		left++;
5576 	}
5577 
5578 	if (left > right)
5579 	{
5580 		/* string is empty after trimming */
5581 		*str = '\0';
5582 		return;
5583 	}
5584 
5585 	if (str < left)
5586 	{
5587 		while (left <= right)
5588 		{
5589 			*str++ = *left++;
5590 		}
5591 		*str = '\0';
5592 	}
5593 	else
5594 	{
5595 		*(right + 1) = '\0';
5596 	}
5597 }
5598 
5599 /******************************************************************************
5600  *                                                                            *
5601  * Function: zbx_trim_integer                                                 *
5602  *                                                                            *
5603  * Purpose: Removes spaces from both ends of the string, then unquotes it if  *
5604  *          double quotation mark is present on both ends of the string, then *
5605  *          removes single "+" sign from the beginning of the trimmed and     *
5606  *          unquoted string.                                                  *
5607  *                                                                            *
5608  *          This function does not guarantee that the resulting string        *
5609  *          contains integer value. It is meant to be used for removing       *
5610  *          "valid" characters from the value that is expected to be numeric  *
5611  *          before checking if value is numeric.                              *
5612  *                                                                            *
5613  * Parameters: str - [IN/OUT] string for processing                           *
5614  *                                                                            *
5615  ******************************************************************************/
zbx_trim_integer(char * str)5616 void	zbx_trim_integer(char *str)
5617 {
5618 	zbx_trim_number(str, 1);
5619 }
5620 
5621 /******************************************************************************
5622  *                                                                            *
5623  * Function: zbx_trim_float                                                   *
5624  *                                                                            *
5625  * Purpose: Removes spaces from both ends of the string, then unquotes it if  *
5626  *          double quotation mark is present on both ends of the string.      *
5627  *                                                                            *
5628  *          This function does not guarantee that the resulting string        *
5629  *          contains floating-point number. It is meant to be used for        *
5630  *          removing "valid" characters from the value that is expected to be *
5631  *          numeric before checking if value is numeric.                      *
5632  *                                                                            *
5633  * Parameters: str - [IN/OUT] string for processing                           *
5634  *                                                                            *
5635  ******************************************************************************/
zbx_trim_float(char * str)5636 void	zbx_trim_float(char *str)
5637 {
5638 	zbx_trim_number(str, 0);
5639 }
5640 
5641 /******************************************************************************
5642  *                                                                            *
5643  * Function: zbx_get_component_version                                        *
5644  *                                                                            *
5645  * Purpose: extracts protocol version from value                              *
5646  *                                                                            *
5647  * Parameters:                                                                *
5648  *     value      - [IN] textual representation of version                    *
5649  *                                                                            *
5650  * Return value: The protocol version if it was successfully extracted,       *
5651  *               otherwise -1                                                 *
5652  *                                                                            *
5653  ******************************************************************************/
zbx_get_component_version(char * value)5654 int	zbx_get_component_version(char *value)
5655 {
5656 	char	*pminor, *ptr;
5657 
5658 	if (NULL == (pminor = strchr(value, '.')))
5659 		return FAIL;
5660 
5661 	*pminor++ = '\0';
5662 
5663 	if (NULL != (ptr = strchr(pminor, '.')))
5664 		*ptr = '\0';
5665 
5666 	return ZBX_COMPONENT_VERSION(atoi(value), atoi(pminor));
5667 }
5668 
5669 /******************************************************************************
5670  *                                                                            *
5671  * Function: zbx_str_extract                                                  *
5672  *                                                                            *
5673  * Purpose: extracts value from a string, unquoting if necessary              *
5674  *                                                                            *
5675  * Parameters:                                                                *
5676  *    text  - [IN] the text containing value to extract                       *
5677  *    len   - [IN] length (in bytes) of the value to extract.                 *
5678  *            It can be 0. It must not exceed length of 'text' string.        *
5679  *    value - [OUT] the extracted value                                       *
5680  *                                                                            *
5681  * Return value: SUCCEED - the value was extracted successfully               *
5682  *               FAIL    - otherwise                                          *
5683  *                                                                            *
5684  * Comments: When unquoting value only " and \ character escapes are accepted.*
5685  *                                                                            *
5686  ******************************************************************************/
zbx_str_extract(const char * text,size_t len,char ** value)5687 int	zbx_str_extract(const char *text, size_t len, char **value)
5688 {
5689 	char		*tmp, *out;
5690 	const char	*in;
5691 
5692 	tmp = zbx_malloc(NULL, len + 1);
5693 
5694 	if (0 == len)
5695 	{
5696 		*tmp = '\0';
5697 		*value = tmp;
5698 		return SUCCEED;
5699 	}
5700 
5701 	if ('"' != *text)
5702 	{
5703 		memcpy(tmp, text, len);
5704 		tmp[len] = '\0';
5705 		*value = tmp;
5706 		return SUCCEED;
5707 	}
5708 
5709 	if (2 > len)
5710 		goto fail;
5711 
5712 	for (out = tmp, in = text + 1; '"' != *in; in++)
5713 	{
5714 		if ((size_t)(in - text) >= len - 1)
5715 			goto fail;
5716 
5717 		if ('\\' == *in)
5718 		{
5719 			if ((size_t)(++in - text) >= len - 1)
5720 				goto fail;
5721 
5722 			if ('"' != *in && '\\' != *in)
5723 				goto fail;
5724 		}
5725 		*out++ = *in;
5726 	}
5727 
5728 	if ((size_t)(in - text) != len - 1)
5729 		goto fail;
5730 
5731 	*out = '\0';
5732 	*value = tmp;
5733 	return SUCCEED;
5734 fail:
5735 	zbx_free(tmp);
5736 	return FAIL;
5737 }
5738 
5739 /******************************************************************************
5740  *                                                                            *
5741  * Function: zbx_truncate_itemkey                                             *
5742  *                                                                            *
5743  * Purpose: check the item key characters length and, if the length exceeds   *
5744  *          max allowable characters length, truncate the item key, while     *
5745  *          maintaining the right square bracket                              *
5746  *                                                                            *
5747  * Parameters: key      - [IN] item key for processing                        *
5748  *             char_max - [IN] item key max characters length                 *
5749  *             buf      - [IN/OUT] buffer for short version of item key       *
5750  *             buf_len  - [IN] buffer size for short version of item key      *
5751  *                                                                            *
5752  * Return value: The item key that does not exceed passed length              *
5753  *                                                                            *
5754  ******************************************************************************/
zbx_truncate_itemkey(const char * key,const size_t char_max,char * buf,const size_t buf_len)5755 const char	*zbx_truncate_itemkey(const char *key, const size_t char_max, char *buf, const size_t buf_len)
5756 {
5757 #	define ZBX_SUFFIX	"..."
5758 #	define ZBX_BSUFFIX	"[...]"
5759 
5760 	size_t	key_byte_count, key_char_total;
5761 	int	is_bracket = 0;
5762 	char	*bracket_l;
5763 
5764 	if (char_max >= (key_char_total = zbx_strlen_utf8(key)))
5765 		return key;
5766 
5767 	if (NULL != (bracket_l = strchr(key, '[')))
5768 		is_bracket = 1;
5769 
5770 	if (char_max < ZBX_CONST_STRLEN(ZBX_SUFFIX) + 2 * is_bracket)	/* [...] or ... */
5771 		return key;
5772 
5773 	if (0 != is_bracket)
5774 	{
5775 		size_t	key_char_count, param_char_count, param_byte_count;
5776 
5777 		key_char_count = zbx_charcount_utf8_nbytes(key, bracket_l - key);
5778 		param_char_count = key_char_total - key_char_count;
5779 
5780 		if (param_char_count <= ZBX_CONST_STRLEN(ZBX_BSUFFIX))
5781 		{
5782 			if (char_max < param_char_count + ZBX_CONST_STRLEN(ZBX_SUFFIX))
5783 				return key;
5784 
5785 			key_byte_count = 1 + zbx_strlen_utf8_nchars(key, char_max - param_char_count -
5786 					ZBX_CONST_STRLEN(ZBX_SUFFIX));
5787 			param_byte_count = 1 + zbx_strlen_utf8_nchars(bracket_l, key_char_count);
5788 
5789 			if (buf_len < key_byte_count + ZBX_CONST_STRLEN(ZBX_SUFFIX) + param_byte_count - 1)
5790 				return key;
5791 
5792 			key_byte_count = zbx_strlcpy_utf8(buf, key, key_byte_count);
5793 			key_byte_count += zbx_strlcpy_utf8(&buf[key_byte_count], ZBX_SUFFIX, sizeof(ZBX_SUFFIX));
5794 			zbx_strlcpy_utf8(&buf[key_byte_count], bracket_l, param_byte_count);
5795 
5796 			return buf;
5797 		}
5798 
5799 		if (key_char_count + ZBX_CONST_STRLEN(ZBX_BSUFFIX) > char_max)
5800 		{
5801 			if (char_max <= ZBX_CONST_STRLEN(ZBX_SUFFIX) + ZBX_CONST_STRLEN(ZBX_BSUFFIX))
5802 				return key;
5803 
5804 			key_byte_count = 1 + zbx_strlen_utf8_nchars(key, char_max - ZBX_CONST_STRLEN(ZBX_SUFFIX) -
5805 					ZBX_CONST_STRLEN(ZBX_BSUFFIX));
5806 
5807 			if (buf_len < key_byte_count + ZBX_CONST_STRLEN(ZBX_SUFFIX) + ZBX_CONST_STRLEN(ZBX_BSUFFIX))
5808 				return key;
5809 
5810 			key_byte_count = zbx_strlcpy_utf8(buf, key, key_byte_count);
5811 			key_byte_count += zbx_strlcpy_utf8(&buf[key_byte_count], ZBX_SUFFIX, sizeof(ZBX_SUFFIX));
5812 			zbx_strlcpy_utf8(&buf[key_byte_count], ZBX_BSUFFIX, sizeof(ZBX_BSUFFIX));
5813 
5814 			return buf;
5815 		}
5816 	}
5817 
5818 	key_byte_count = 1 + zbx_strlen_utf8_nchars(key, char_max - (ZBX_CONST_STRLEN(ZBX_SUFFIX) + is_bracket));
5819 
5820 	if (buf_len < key_byte_count + ZBX_CONST_STRLEN(ZBX_SUFFIX) + is_bracket)
5821 		return key;
5822 
5823 	key_byte_count = zbx_strlcpy_utf8(buf, key, key_byte_count);
5824 	zbx_strlcpy_utf8(&buf[key_byte_count], ZBX_SUFFIX, sizeof(ZBX_SUFFIX));
5825 
5826 	if (0 != is_bracket)
5827 		zbx_strlcpy_utf8(&buf[key_byte_count + ZBX_CONST_STRLEN(ZBX_SUFFIX)], "]", sizeof("]"));
5828 
5829 	return buf;
5830 
5831 #	undef ZBX_SUFFIX
5832 #	undef ZBX_BSUFFIX
5833 }
5834 
5835 /******************************************************************************
5836  *                                                                            *
5837  * Function: zbx_truncate_value                                               *
5838  *                                                                            *
5839  * Purpose: check the value characters length and, if the length exceeds      *
5840  *          max allowable characters length, truncate the value               *
5841  *                                                                            *
5842  * Parameters: val      - [IN] value for processing                           *
5843  *             char_max - [IN] value max characters length                    *
5844  *             buf      - [IN/OUT] buffer for short version of value          *
5845  *             buf_len  - [IN] buffer size for short version of value         *
5846  *                                                                            *
5847  * Return value: The value that does not exceed passed length                 *
5848  *                                                                            *
5849  ******************************************************************************/
zbx_truncate_value(const char * val,const size_t char_max,char * buf,const size_t buf_len)5850 const char	*zbx_truncate_value(const char *val, const size_t char_max, char *buf, const size_t buf_len)
5851 {
5852 #	define ZBX_SUFFIX	"..."
5853 
5854 	size_t	key_byte_count;
5855 
5856 	if (char_max >= zbx_strlen_utf8(val))
5857 		return val;
5858 
5859 	key_byte_count = 1 + zbx_strlen_utf8_nchars(val, char_max - ZBX_CONST_STRLEN(ZBX_SUFFIX));
5860 
5861 	if (buf_len < key_byte_count + ZBX_CONST_STRLEN(ZBX_SUFFIX))
5862 		return val;
5863 
5864 	key_byte_count = zbx_strlcpy_utf8(buf, val, key_byte_count);
5865 	zbx_strlcpy_utf8(&buf[key_byte_count], ZBX_SUFFIX, sizeof(ZBX_SUFFIX));
5866 
5867 	return buf;
5868 
5869 #	undef ZBX_SUFFIX
5870 }
5871 
5872 /******************************************************************************
5873  *                                                                            *
5874  * Function: zbx_print_double                                                 *
5875  *                                                                            *
5876  * Purpose: converts double value to string and truncates insignificant       *
5877  *          precision                                                         *
5878  *                                                                            *
5879  * Parameters: buffer - [OUT] the output buffer                               *
5880  *             size   - [IN] the output buffer size                           *
5881  *             val    - [IN] double value to be converted                     *
5882  *                                                                            *
5883  * Return value: the oputput buffer with printed value                        *
5884  *                                                                            *
5885  ******************************************************************************/
zbx_print_double(char * buffer,size_t size,double val)5886 const char	*zbx_print_double(char *buffer, size_t size, double val)
5887 {
5888 	zbx_snprintf(buffer, size, "%.15G", val);
5889 
5890 	if (atof(buffer) != val)
5891 		zbx_snprintf(buffer, size, ZBX_FS_DBL64, val);
5892 
5893 	return buffer;
5894 }
5895