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, ¯o_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, ¶m_pos, ¶m_len, &sep_pos);
3059
3060 if (idx == Nparam)
3061 out = zbx_function_param_unquote_dyn(ptr + param_pos, param_len, "ed);
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, ¯o_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, ¶m);
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