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