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
23 #include "../zbxcrypto/tls.h"
24
25 #ifdef HAVE_ICONV
26 # include <iconv.h>
27 #endif
28
29 static const char copyright_message[] =
30 "Copyright (C) 2021 Zabbix SIA\n"
31 "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>.\n"
32 "This is free software: you are free to change and redistribute it according to\n"
33 "the license. There is NO WARRANTY, to the extent permitted by law.";
34
35 static const char help_message_footer[] =
36 "Report bugs to: <https://support.zabbix.com>\n"
37 "Zabbix home page: <http://www.zabbix.com>\n"
38 "Documentation: <https://www.zabbix.com/documentation>";
39
40 /******************************************************************************
41 * *
42 * Function: version *
43 * *
44 * Purpose: print version and compilation time of application on stdout *
45 * by application request with parameter '-V' *
46 * *
47 * Author: Eugene Grigorjev *
48 * *
49 * Comments: title_message - is global variable which must be initialized *
50 * in each zabbix application *
51 * *
52 ******************************************************************************/
version(void)53 void version(void)
54 {
55 printf("%s (Zabbix) %s\n", title_message, ZABBIX_VERSION);
56 printf("Revision %s %s, compilation time: %s %s\n\n", ZABBIX_REVISION, ZABBIX_REVDATE, __DATE__, __TIME__);
57 puts(copyright_message);
58 #if defined(HAVE_POLARSSL) || defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL)
59 printf("\n");
60 zbx_tls_version();
61 #endif
62 }
63
64 /******************************************************************************
65 * *
66 * Function: usage *
67 * *
68 * Purpose: print application parameters on stdout with layout suitable for *
69 * 80-column terminal *
70 * *
71 * Author: Eugene Grigorjev *
72 * *
73 * Comments: usage_message - is global variable which must be initialized *
74 * in each zabbix application *
75 * *
76 ******************************************************************************/
usage(void)77 void usage(void)
78 {
79 #define ZBX_MAXCOL 79
80 #define ZBX_SPACE1 " " /* left margin for the first line */
81 #define ZBX_SPACE2 " " /* left margin for subsequent lines */
82 const char **p = usage_message;
83
84 if (NULL != *p)
85 printf("usage:\n");
86
87 while (NULL != *p)
88 {
89 size_t pos;
90
91 printf("%s%s", ZBX_SPACE1, progname);
92 pos = ZBX_CONST_STRLEN(ZBX_SPACE1) + strlen(progname);
93
94 while (NULL != *p)
95 {
96 size_t len;
97
98 len = strlen(*p);
99
100 if (ZBX_MAXCOL > pos + len)
101 {
102 pos += len + 1;
103 printf(" %s", *p);
104 }
105 else
106 {
107 pos = ZBX_CONST_STRLEN(ZBX_SPACE2) + len + 1;
108 printf("\n%s %s", ZBX_SPACE2, *p);
109 }
110
111 p++;
112 }
113
114 printf("\n");
115 p++;
116 }
117 #undef ZBX_MAXCOL
118 #undef ZBX_SPACE1
119 #undef ZBX_SPACE2
120 }
121
122 /******************************************************************************
123 * *
124 * Function: help *
125 * *
126 * Purpose: print help of application parameters on stdout by application *
127 * request with parameter '-h' *
128 * *
129 * Author: Eugene Grigorjev *
130 * *
131 * Comments: help_message - is global variable which must be initialized *
132 * in each zabbix application *
133 * *
134 ******************************************************************************/
help(void)135 void help(void)
136 {
137 const char **p = help_message;
138
139 usage();
140 printf("\n");
141
142 while (NULL != *p)
143 printf("%s\n", *p++);
144
145 printf("\n");
146 puts(help_message_footer);
147 }
148
149 /******************************************************************************
150 * *
151 * Function: zbx_error *
152 * *
153 * Purpose: Print error text to the stderr *
154 * *
155 * Parameters: fmt - format of message *
156 * *
157 * Return value: *
158 * *
159 * Author: Eugene Grigorjev *
160 * *
161 ******************************************************************************/
__zbx_zbx_error(const char * fmt,...)162 void __zbx_zbx_error(const char *fmt, ...)
163 {
164 va_list args;
165
166 va_start(args, fmt);
167
168 fprintf(stderr, "%s [%li]: ", progname, zbx_get_thread_id());
169 vfprintf(stderr, fmt, args);
170 fprintf(stderr, "\n");
171 fflush(stderr);
172
173 va_end(args);
174 }
175
176 /******************************************************************************
177 * *
178 * Function: zbx_snprintf *
179 * *
180 * Purpose: Secure version of snprintf function. *
181 * Add zero character at the end of string. *
182 * *
183 * Parameters: str - destination buffer pointer *
184 * count - size of destination buffer *
185 * fmt - format *
186 * *
187 * Return value: *
188 * *
189 * Author: Eugene Grigorjev *
190 * *
191 ******************************************************************************/
__zbx_zbx_snprintf(char * str,size_t count,const char * fmt,...)192 size_t __zbx_zbx_snprintf(char *str, size_t count, const char *fmt, ...)
193 {
194 size_t written_len;
195 va_list args;
196
197 va_start(args, fmt);
198 written_len = zbx_vsnprintf(str, count, fmt, args);
199 va_end(args);
200
201 return written_len;
202 }
203
204 /******************************************************************************
205 * *
206 * Function: zbx_snprintf_alloc *
207 * *
208 * Purpose: Secure version of snprintf function. *
209 * Add zero character at the end of string. *
210 * Reallocs memory if not enough. *
211 * *
212 * Parameters: str - [IN/OUT] destination buffer pointer *
213 * alloc_len - [IN/OUT] already allocated memory *
214 * offset - [IN/OUT] offset for writing *
215 * fmt - [IN] format *
216 * *
217 * Return value: *
218 * *
219 * Author: Alexei Vladishev, Alexander Vladishev *
220 * *
221 ******************************************************************************/
__zbx_zbx_snprintf_alloc(char ** str,size_t * alloc_len,size_t * offset,const char * fmt,...)222 void __zbx_zbx_snprintf_alloc(char **str, size_t *alloc_len, size_t *offset, const char *fmt, ...)
223 {
224 va_list args;
225 size_t avail_len, written_len;
226 retry:
227 if (NULL == *str)
228 {
229 /* zbx_vsnprintf() returns bytes actually written instead of bytes to write, */
230 /* so we have to use the standard function */
231 va_start(args, fmt);
232 *alloc_len = vsnprintf(NULL, 0, fmt, args) + 2; /* '\0' + one byte to prevent the operation retry */
233 va_end(args);
234 *offset = 0;
235 *str = zbx_malloc(*str, *alloc_len);
236 }
237
238 avail_len = *alloc_len - *offset;
239 va_start(args, fmt);
240 written_len = zbx_vsnprintf(*str + *offset, avail_len, fmt, args);
241 va_end(args);
242
243 if (written_len == avail_len - 1)
244 {
245 *alloc_len *= 2;
246 *str = zbx_realloc(*str, *alloc_len);
247
248 goto retry;
249 }
250
251 *offset += written_len;
252 }
253
254 /******************************************************************************
255 * *
256 * Function: zbx_vsnprintf *
257 * *
258 * Purpose: Secure version of vsnprintf function. *
259 * Add zero character at the end of string. *
260 * *
261 * Parameters: str - [IN/OUT] destination buffer pointer *
262 * count - [IN] size of destination buffer *
263 * fmt - [IN] format *
264 * *
265 * Return value: the number of characters in the output buffer *
266 * (not including the trailing '\0') *
267 * *
268 * Author: Alexei Vladishev (see also zbx_snprintf) *
269 * *
270 ******************************************************************************/
zbx_vsnprintf(char * str,size_t count,const char * fmt,va_list args)271 size_t zbx_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
272 {
273 int written_len = 0;
274
275 if (0 < count)
276 {
277 if (0 > (written_len = vsnprintf(str, count, fmt, args)))
278 written_len = (int)count - 1; /* count an output error as a full buffer */
279 else
280 written_len = MIN(written_len, (int)count - 1); /* result could be truncated */
281 }
282 str[written_len] = '\0'; /* always write '\0', even if buffer size is 0 or vsnprintf() error */
283
284 return (size_t)written_len;
285 }
286
287 /******************************************************************************
288 * *
289 * Function: zbx_strncpy_alloc, zbx_strcpy_alloc, zbx_chrcpy_alloc *
290 * *
291 * Purpose: If there is no '\0' byte among the first n bytes of src, *
292 * then all n bytes will be placed into the dest buffer. *
293 * In other case only strlen() bytes will be placed there. *
294 * Add zero character at the end of string. *
295 * Reallocs memory if not enough. *
296 * *
297 * Parameters: str - [IN/OUT] destination buffer pointer *
298 * alloc_len - [IN/OUT] already allocated memory *
299 * offset - [IN/OUT] offset for writing *
300 * src - [IN] copied string *
301 * n - [IN] maximum number of bytes to copy *
302 * *
303 * Author: Alexander Vladishev *
304 * *
305 ******************************************************************************/
zbx_strncpy_alloc(char ** str,size_t * alloc_len,size_t * offset,const char * src,size_t n)306 void zbx_strncpy_alloc(char **str, size_t *alloc_len, size_t *offset, const char *src, size_t n)
307 {
308 if (NULL == *str)
309 {
310 *alloc_len = n + 1;
311 *offset = 0;
312 *str = zbx_malloc(*str, *alloc_len);
313 }
314 else if (*offset + n >= *alloc_len)
315 {
316 while (*offset + n >= *alloc_len)
317 *alloc_len *= 2;
318 *str = zbx_realloc(*str, *alloc_len);
319 }
320
321 while (0 != n && '\0' != *src)
322 {
323 (*str)[(*offset)++] = *src++;
324 n--;
325 }
326
327 (*str)[*offset] = '\0';
328 }
329
zbx_strcpy_alloc(char ** str,size_t * alloc_len,size_t * offset,const char * src)330 void zbx_strcpy_alloc(char **str, size_t *alloc_len, size_t *offset, const char *src)
331 {
332 zbx_strncpy_alloc(str, alloc_len, offset, src, strlen(src));
333 }
334
zbx_chrcpy_alloc(char ** str,size_t * alloc_len,size_t * offset,char c)335 void zbx_chrcpy_alloc(char **str, size_t *alloc_len, size_t *offset, char c)
336 {
337 zbx_strncpy_alloc(str, alloc_len, offset, &c, 1);
338 }
339
340 /* Has to be rewritten to avoid malloc */
string_replace(const char * str,const char * sub_str1,const char * sub_str2)341 char *string_replace(const char *str, const char *sub_str1, const char *sub_str2)
342 {
343 char *new_str = NULL;
344 const char *p;
345 const char *q;
346 const char *r;
347 char *t;
348 long len;
349 long diff;
350 unsigned long count = 0;
351
352 assert(str);
353 assert(sub_str1);
354 assert(sub_str2);
355
356 len = (long)strlen(sub_str1);
357
358 /* count the number of occurrences of sub_str1 */
359 for ( p=str; (p = strstr(p, sub_str1)); p+=len, count++ );
360
361 if ( 0 == count ) return strdup(str);
362
363 diff = (long)strlen(sub_str2) - len;
364
365 /* allocate new memory */
366 new_str = zbx_malloc(new_str, (size_t)(strlen(str) + count*diff + 1)*sizeof(char));
367
368 for (q=str,t=new_str,p=str; (p = strstr(p, sub_str1)); )
369 {
370 /* copy until next occurrence of sub_str1 */
371 for ( ; q < p; *t++ = *q++);
372 q += len;
373 p = q;
374 for ( r = sub_str2; (*t++ = *r++); );
375 --t;
376 }
377 /* copy the tail of str */
378 for( ; *q ; *t++ = *q++ );
379
380 *t = '\0';
381
382 return new_str;
383 }
384
385 /******************************************************************************
386 * *
387 * Function: del_zeroes *
388 * *
389 * Purpose: delete all right '0' and '.' for the string *
390 * *
391 * Parameters: s - string to trim '0' *
392 * *
393 * Return value: string without right '0' *
394 * *
395 * Author: Alexei Vladishev *
396 * *
397 * Comments: 10.0100 => 10.01, 10. => 10 *
398 * *
399 ******************************************************************************/
del_zeroes(char * s)400 void del_zeroes(char *s)
401 {
402 int trim = 0;
403 size_t len = 0;
404
405 while ('\0' != s[len])
406 {
407 if ('e' == s[len] || 'E' == s[len])
408 {
409 /* don't touch numbers that are written in scientific notation */
410 return;
411 }
412
413 if ('.' == s[len])
414 {
415 /* number has decimal part */
416 trim = 1;
417 }
418
419 len++;
420 }
421
422 if (1 == trim)
423 {
424 size_t i;
425
426 for (i = len - 1; ; i--)
427 {
428 if ('0' == s[i])
429 {
430 s[i] = '\0';
431 }
432 else if ('.' == s[i])
433 {
434 s[i] = '\0';
435 break;
436 }
437 else
438 {
439 break;
440 }
441 }
442 }
443 }
444
445 /******************************************************************************
446 * *
447 * Function: zbx_rtrim *
448 * *
449 * Purpose: Strip characters from the end of a string *
450 * *
451 * Parameters: str - string for processing *
452 * charlist - null terminated list of characters *
453 * *
454 * Return value: number of trimmed characters *
455 * *
456 * Author: Eugene Grigorjev, Aleksandrs Saveljevs *
457 * *
458 ******************************************************************************/
zbx_rtrim(char * str,const char * charlist)459 int zbx_rtrim(char *str, const char *charlist)
460 {
461 char *p;
462 int count = 0;
463
464 if (NULL == str || '\0' == *str)
465 return count;
466
467 for (p = str + strlen(str) - 1; p >= str && NULL != strchr(charlist, *p); p--)
468 {
469 *p = '\0';
470 count++;
471 }
472
473 return count;
474 }
475
476 /******************************************************************************
477 * *
478 * Function: zbx_ltrim *
479 * *
480 * Purpose: Strip characters from the beginning of a string *
481 * *
482 * Parameters: str - string for processing *
483 * charlist - null terminated list of characters *
484 * *
485 * Return value: *
486 * *
487 * Author: Eugene Grigorjev *
488 * *
489 ******************************************************************************/
zbx_ltrim(char * str,const char * charlist)490 void zbx_ltrim(char *str, const char *charlist)
491 {
492 char *p;
493
494 if (NULL == str || '\0' == *str)
495 return;
496
497 for (p = str; '\0' != *p && NULL != strchr(charlist, *p); p++)
498 ;
499
500 if (p == str)
501 return;
502
503 while ('\0' != *p)
504 *str++ = *p++;
505
506 *str = '\0';
507 }
508
509 /******************************************************************************
510 * *
511 * Function: zbx_lrtrim *
512 * *
513 * Purpose: Removes leading and trailing characters from the specified *
514 * character string *
515 * *
516 * Parameters: str - [IN/OUT] string for processing *
517 * charlist - [IN] null terminated list of characters *
518 * *
519 ******************************************************************************/
zbx_lrtrim(char * str,const char * charlist)520 void zbx_lrtrim(char *str, const char *charlist)
521 {
522 zbx_rtrim(str, charlist);
523 zbx_ltrim(str, charlist);
524 }
525
526 /******************************************************************************
527 * *
528 * Function: zbx_remove_chars *
529 * *
530 * Purpose: Remove characters 'charlist' from the whole string *
531 * *
532 * Parameters: str - string for processing *
533 * charlist - null terminated list of characters *
534 * *
535 * Return value: *
536 * *
537 * Author: Alexander Vladishev *
538 * *
539 ******************************************************************************/
zbx_remove_chars(register char * str,const char * charlist)540 void zbx_remove_chars(register char *str, const char *charlist)
541 {
542 register char *p;
543
544 if (NULL == str || NULL == charlist || '\0' == *str || '\0' == *charlist)
545 return;
546
547 for (p = str; '\0' != *p; p++)
548 {
549 if (NULL == strchr(charlist, *p))
550 *str++ = *p;
551 }
552
553 *str = '\0';
554 }
555
556 /******************************************************************************
557 * *
558 * Function: zbx_strlcpy *
559 * *
560 * Purpose: Copy src to string dst of size siz. At most siz - 1 characters *
561 * will be copied. Always null terminates (unless siz == 0). *
562 * *
563 * Return value: the number of characters copied (excluding the null byte) *
564 * *
565 ******************************************************************************/
zbx_strlcpy(char * dst,const char * src,size_t siz)566 size_t zbx_strlcpy(char *dst, const char *src, size_t siz)
567 {
568 const char *s = src;
569
570 if (0 != siz)
571 {
572 while (0 != --siz && '\0' != *s)
573 *dst++ = *s++;
574
575 *dst = '\0';
576 }
577
578 return s - src; /* count does not include null */
579 }
580
581 /******************************************************************************
582 * *
583 * Function: zbx_strlcat *
584 * *
585 * Purpose: Appends src to string dst of size siz (unlike strncat, size is *
586 * the full size of dst, not space left). At most siz - 1 characters *
587 * will be copied. Always null terminates (unless *
588 * siz <= strlen(dst)). *
589 * *
590 ******************************************************************************/
zbx_strlcat(char * dst,const char * src,size_t siz)591 void zbx_strlcat(char *dst, const char *src, size_t siz)
592 {
593 while ('\0' != *dst)
594 {
595 dst++;
596 siz--;
597 }
598
599 zbx_strlcpy(dst, src, siz);
600 }
601
602 /******************************************************************************
603 * *
604 * Function: zbx_strlcpy_utf8 *
605 * *
606 * Purpose: copies utf-8 string + terminating zero character into specified *
607 * buffer *
608 * *
609 * Return value: the number of copied bytes excluding terminating zero *
610 * character. *
611 * *
612 * Comments: If the source string is larger than destination buffer then the *
613 * string is truncated after last valid utf-8 character rather than *
614 * byte. *
615 * *
616 ******************************************************************************/
zbx_strlcpy_utf8(char * dst,const char * src,size_t size)617 size_t zbx_strlcpy_utf8(char *dst, const char *src, size_t size)
618 {
619 size = zbx_strlen_utf8_nbytes(src, size - 1);
620 memcpy(dst, src, size);
621 dst[size] = '\0';
622
623 return size;
624 }
625
626 /******************************************************************************
627 * *
628 * Function: zbx_dvsprintf *
629 * *
630 * Purpose: dynamical formatted output conversion *
631 * *
632 * Return value: formatted string *
633 * *
634 * Author: Eugene Grigorjev *
635 * *
636 * Comments: returns a pointer to allocated memory *
637 * *
638 ******************************************************************************/
zbx_dvsprintf(char * dest,const char * f,va_list args)639 char *zbx_dvsprintf(char *dest, const char *f, va_list args)
640 {
641 char *string = NULL;
642 int n, size = MAX_STRING_LEN >> 1;
643
644 va_list curr;
645
646 while (1)
647 {
648 string = zbx_malloc(string, size);
649
650 va_copy(curr, args);
651 n = vsnprintf(string, size, f, curr);
652 va_end(curr);
653
654 if (0 <= n && n < size)
655 break;
656
657 /* result was truncated */
658 if (-1 == n)
659 size = size * 3 / 2 + 1; /* the length is unknown */
660 else
661 size = n + 1; /* n bytes + trailing '\0' */
662
663 zbx_free(string);
664 }
665
666 zbx_free(dest);
667
668 return string;
669 }
670
671 /******************************************************************************
672 * *
673 * Function: zbx_dsprintf *
674 * *
675 * Purpose: dynamical formatted output conversion *
676 * *
677 * Return value: formatted string *
678 * *
679 * Author: Eugene Grigorjev *
680 * *
681 * Comments: returns a pointer to allocated memory *
682 * *
683 ******************************************************************************/
__zbx_zbx_dsprintf(char * dest,const char * f,...)684 char *__zbx_zbx_dsprintf(char *dest, const char *f, ...)
685 {
686 char *string;
687 va_list args;
688
689 va_start(args, f);
690
691 string = zbx_dvsprintf(dest, f, args);
692
693 va_end(args);
694
695 return string;
696 }
697
698 /******************************************************************************
699 * *
700 * Function: zbx_strdcat *
701 * *
702 * Purpose: dynamical cating of strings *
703 * *
704 * Return value: new pointer of string *
705 * *
706 * Author: Eugene Grigorjev *
707 * *
708 * Comments: returns a pointer to allocated memory *
709 * zbx_strdcat(NULL, "") will return "", not NULL! *
710 * *
711 ******************************************************************************/
zbx_strdcat(char * dest,const char * src)712 char *zbx_strdcat(char *dest, const char *src)
713 {
714 size_t len_dest, len_src;
715
716 if (NULL == src)
717 return dest;
718
719 if (NULL == dest)
720 return zbx_strdup(NULL, src);
721
722 len_dest = strlen(dest);
723 len_src = strlen(src);
724
725 dest = zbx_realloc(dest, len_dest + len_src + 1);
726
727 zbx_strlcpy(dest + len_dest, src, len_src + 1);
728
729 return dest;
730 }
731
732 /******************************************************************************
733 * *
734 * Function: zbx_strdcatf *
735 * *
736 * Purpose: dynamical cating of formated strings *
737 * *
738 * Return value: new pointer of string *
739 * *
740 * Author: Eugene Grigorjev *
741 * *
742 * Comments: returns a pointer to allocated memory *
743 * *
744 ******************************************************************************/
__zbx_zbx_strdcatf(char * dest,const char * f,...)745 char *__zbx_zbx_strdcatf(char *dest, const char *f, ...)
746 {
747 char *string, *result;
748 va_list args;
749
750 va_start(args, f);
751 string = zbx_dvsprintf(NULL, f, args);
752 va_end(args);
753
754 result = zbx_strdcat(dest, string);
755
756 zbx_free(string);
757
758 return result;
759 }
760
761 /******************************************************************************
762 * *
763 * Function: zbx_check_hostname *
764 * *
765 * Purpose: check a byte stream for a valid hostname *
766 * *
767 * Parameters: hostname - pointer to the first char of hostname *
768 * error - pointer to the error message (can be NULL) *
769 * *
770 * Return value: return SUCCEED if hostname is valid *
771 * or FAIL if hostname contains invalid chars, is empty *
772 * or is longer than MAX_ZBX_HOSTNAME_LEN *
773 * *
774 * Author: Alexander Vladishev *
775 * *
776 ******************************************************************************/
zbx_check_hostname(const char * hostname,char ** error)777 int zbx_check_hostname(const char *hostname, char **error)
778 {
779 int len = 0;
780
781 while ('\0' != hostname[len])
782 {
783 if (FAIL == is_hostname_char(hostname[len]))
784 {
785 if (NULL != error)
786 *error = zbx_dsprintf(NULL, "name contains invalid character '%c'", hostname[len]);
787 return FAIL;
788 }
789
790 len++;
791 }
792
793 if (0 == len)
794 {
795 if (NULL != error)
796 *error = zbx_strdup(NULL, "name is empty");
797 return FAIL;
798 }
799
800 if (MAX_ZBX_HOSTNAME_LEN < len)
801 {
802 if (NULL != error)
803 *error = zbx_dsprintf(NULL, "name is too long (max %d characters)", MAX_ZBX_HOSTNAME_LEN);
804 return FAIL;
805 }
806
807 return SUCCEED;
808 }
809
810 /******************************************************************************
811 * *
812 * Function: get_item_key *
813 * *
814 * Purpose: return key with parameters (if present) *
815 * *
816 * e.g., system.run[cat /etc/passwd | awk -F: '{ print $1 }'] *
817 * *
818 * Parameters: exp - [IN] pointer to the first char of key *
819 * key - [OUT] pointer to the resulted key *
820 * *
821 * e.g., {host:system.run[cat /etc/passwd | awk -F: '{ print $1 }'].last(0)} *
822 * ^ *
823 * *
824 * Return value: return SUCCEED and move exp to the next character after key *
825 * or FAIL and move exp to incorrect character *
826 * *
827 * Notes: implements the functionality of old parse_key() in a safe manner. *
828 * input pointer is NOT advanced. *
829 * *
830 ******************************************************************************/
get_item_key(char ** exp,char ** key)831 int get_item_key(char **exp, char **key)
832 {
833 char *p = *exp, c;
834
835 if (SUCCEED != parse_key(&p))
836 return FAIL;
837
838 if ('(' == *p)
839 {
840 for (p--; *exp < p && '.' != *p; p--)
841 ;
842
843 if (*exp == p) /* the key is empty */
844 return FAIL;
845 }
846
847 c = *p;
848 *p = '\0';
849 *key = zbx_strdup(NULL, *exp);
850 *p = c;
851
852 *exp = p;
853
854 return SUCCEED;
855 }
856
857 /******************************************************************************
858 * *
859 * Function: parse_host *
860 * *
861 * Purpose: parse hostname *
862 * *
863 * e.g., Zabbix server *
864 * *
865 * Parameters: exp - pointer to the first char of hostname *
866 * host - optional pointer to resulted hostname *
867 * *
868 * e.g., {Zabbix server:agent.ping.last(0)} *
869 * ^ *
870 * *
871 * Return value: return SUCCEED and move exp to the next char after hostname *
872 * or FAIL and move exp at the failed character *
873 * *
874 * Author: Aleksandrs Saveljevs *
875 * *
876 ******************************************************************************/
parse_host(char ** exp,char ** host)877 int parse_host(char **exp, char **host)
878 {
879 char *p, *s;
880
881 p = *exp;
882
883 for (s = *exp; SUCCEED == is_hostname_char(*s); s++)
884 ;
885
886 *exp = s;
887
888 if (p == s)
889 return FAIL;
890
891 if (NULL != host)
892 {
893 char c;
894
895 c = *s;
896 *s = '\0';
897 *host = strdup(p);
898 *s = c;
899 }
900
901 return SUCCEED;
902 }
903
904 /******************************************************************************
905 * *
906 * Function: parse_key *
907 * *
908 * Purpose: advances pointer to first invalid character in string *
909 * ensuring that everything before it is a valid key *
910 * *
911 * e.g., system.run[cat /etc/passwd | awk -F: '{ print $1 }'] *
912 * *
913 * Parameters: exp - [IN/OUT] pointer to the first char of key *
914 * *
915 * e.g., {host:system.run[cat /etc/passwd | awk -F: '{ print $1 }'].last(0)} *
916 * ^ *
917 * Return value: returns FAIL only if no key is present (length 0), *
918 * or the whole string is invalid. SUCCEED otherwise. *
919 * *
920 * Author: Aleksandrs Saveljevs *
921 * *
922 ******************************************************************************/
parse_key(char ** exp)923 int parse_key(char **exp)
924 {
925 char *s;
926
927 for (s = *exp; SUCCEED == is_key_char(*s); s++)
928 ;
929
930 if (*exp == s) /* the key is empty */
931 return FAIL;
932
933 if ('[' == *s) /* for instance, net.tcp.port[,80] */
934 {
935 int state = 0; /* 0 - init, 1 - inside quoted param, 2 - inside unquoted param */
936 int array = 0; /* array nest level */
937
938 for (s++; '\0' != *s; s++)
939 {
940 switch (state)
941 {
942 /* init state */
943 case 0:
944 if (',' == *s)
945 ;
946 else if (']' == *s && '[' == s[1] && 0 == array) /* Zapcat */
947 s++;
948 else if ('"' == *s)
949 state = 1;
950 else if ('[' == *s)
951 array++;
952 else if (']' == *s && 0 != array)
953 {
954 array--;
955
956 /* skip spaces */
957 while (' ' == s[1])
958 s++;
959
960 if (0 == array && ']' == s[1])
961 {
962 s++;
963 goto succeed;
964 }
965
966 if (',' != s[1] && !(0 != array && ']' == s[1]))
967 {
968 s++;
969 goto fail; /* incorrect syntax */
970 }
971 }
972 else if (']' == *s && 0 == array)
973 goto succeed;
974 else if (' ' != *s)
975 state = 2;
976 break;
977 /* quoted */
978 case 1:
979 if ('"' == *s)
980 {
981 /* skip spaces */
982 while (' ' == s[1])
983 s++;
984
985 if (0 == array && ']' == s[1] && '[' == s[2]) /* Zapcat */
986 {
987 state = 0;
988 break;
989 }
990
991 if (0 == array && ']' == s[1])
992 {
993 s++;
994 goto succeed;
995 }
996
997 if (',' != s[1] && !(0 != array && ']' == s[1]))
998 {
999 s++;
1000 goto fail; /* incorrect syntax */
1001 }
1002
1003 state = 0;
1004 }
1005 else if ('\\' == *s && '"' == s[1])
1006 s++;
1007 break;
1008 /* unquoted */
1009 case 2:
1010 if (0 == array && ']' == *s && '[' == s[1]) /* Zapcat */
1011 {
1012 s--;
1013 state = 0;
1014 }
1015 else if (',' == *s || (']' == *s && 0 != array))
1016 {
1017 s--;
1018 state = 0;
1019 }
1020 else if (']' == *s && 0 == array)
1021 goto succeed;
1022 break;
1023 }
1024 }
1025 fail:
1026 *exp = s;
1027 return FAIL;
1028 succeed:
1029 s++;
1030 }
1031
1032 *exp = s;
1033 return SUCCEED;
1034 }
1035
1036 /******************************************************************************
1037 * *
1038 * Function: parse_host_key *
1039 * *
1040 * Purpose: return hostname and key *
1041 * <hostname:>key *
1042 * *
1043 * Parameters: *
1044 * exp - pointer to the first char of hostname *
1045 * host:key[key params] *
1046 * ^ *
1047 * *
1048 * Return value: return SUCCEED or FAIL *
1049 * *
1050 * Author: Alexander Vladishev *
1051 * *
1052 ******************************************************************************/
parse_host_key(char * exp,char ** host,char ** key)1053 int parse_host_key(char *exp, char **host, char **key)
1054 {
1055 char *p, *s;
1056
1057 if (NULL == exp || '\0' == *exp)
1058 return FAIL;
1059
1060 for (p = exp, s = exp; '\0' != *p; p++) /* check for optional hostname */
1061 {
1062 if (':' == *p) /* hostname:vfs.fs.size[/,total]
1063 * --------^
1064 */
1065 {
1066 *p = '\0';
1067 *host = zbx_strdup(NULL, s);
1068 *p++ = ':';
1069
1070 s = p;
1071 break;
1072 }
1073
1074 if (SUCCEED != is_hostname_char(*p))
1075 break;
1076 }
1077
1078 *key = zbx_strdup(NULL, s);
1079
1080 return SUCCEED;
1081 }
1082
1083 /******************************************************************************
1084 * *
1085 * Function: num_param *
1086 * *
1087 * Purpose: calculate count of parameters from parameter list (param) *
1088 * *
1089 * Parameters: *
1090 * param - parameter list *
1091 * *
1092 * Return value: count of parameters *
1093 * *
1094 * Author: Alexei Vladishev *
1095 * *
1096 * Comments: delimeter for parameters is ',' *
1097 * *
1098 ******************************************************************************/
num_param(const char * p)1099 int num_param(const char *p)
1100 {
1101 /* 0 - init, 1 - inside quoted param, 2 - inside unquoted param */
1102 int ret = 1, state, array;
1103
1104 if (p == NULL)
1105 return 0;
1106
1107 for (state = 0, array = 0; '\0' != *p; p++)
1108 {
1109 switch (state) {
1110 /* Init state */
1111 case 0:
1112 if (',' == *p)
1113 {
1114 if (0 == array)
1115 ret++;
1116 }
1117 else if ('"' == *p)
1118 state = 1;
1119 else if ('[' == *p)
1120 array++;
1121 else if (']' == *p && 0 != array)
1122 {
1123 array--;
1124
1125 /* skip spaces */
1126 while (' ' == p[1])
1127 p++;
1128
1129 if (',' != p[1] && '\0' != p[1] && (0 == array || ']' != p[1]))
1130 return 0; /* incorrect syntax */
1131 }
1132 else if (' ' != *p)
1133 state = 2;
1134 break;
1135 /* Quoted */
1136 case 1:
1137 if ('"' == *p)
1138 {
1139 /* skip spaces */
1140 while (' ' == p[1])
1141 p++;
1142
1143 if (',' != p[1] && '\0' != p[1] && (0 == array || ']' != p[1]))
1144 return 0; /* incorrect syntax */
1145
1146 state = 0;
1147 }
1148 else if ('\\' == *p && '"' == p[1])
1149 p++;
1150 break;
1151 /* Unquoted */
1152 case 2:
1153 if (',' == *p || (']' == *p && 0 != array))
1154 {
1155 p--;
1156 state = 0;
1157 }
1158 break;
1159 }
1160 }
1161
1162 /* missing terminating '"' character */
1163 if (state == 1)
1164 return 0;
1165
1166 /* missing terminating ']' character */
1167 if (array != 0)
1168 return 0;
1169
1170 return ret;
1171 }
1172
1173 /******************************************************************************
1174 * *
1175 * Function: get_param *
1176 * *
1177 * Purpose: return parameter by index (num) from parameter list (param) *
1178 * *
1179 * Parameters: *
1180 * p - parameter list *
1181 * num - requested parameter index *
1182 * buf - pointer of output buffer *
1183 * max_len - size of output buffer *
1184 * *
1185 * Return value: *
1186 * 1 - requested parameter missing or buffer overflow *
1187 * 0 - requested parameter found (value - 'buf' can be empty string) *
1188 * *
1189 * Author: Eugene Grigorjev, rewritten by Alexei Vladishev *
1190 * *
1191 * Comments: delimeter for parameters is ',' *
1192 * *
1193 ******************************************************************************/
get_param(const char * p,int num,char * buf,size_t max_len)1194 int get_param(const char *p, int num, char *buf, size_t max_len)
1195 {
1196 #define ZBX_ASSIGN_PARAM \
1197 { \
1198 if (buf_i == max_len) \
1199 return 1; /* buffer overflow */ \
1200 buf[buf_i++] = *p; \
1201 }
1202
1203 int state; /* 0 - init, 1 - inside quoted param, 2 - inside unquoted param */
1204 int array, idx = 1;
1205 size_t buf_i = 0;
1206
1207 if (0 == max_len)
1208 return 1; /* buffer overflow */
1209
1210 max_len--; /* '\0' */
1211
1212 for (state = 0, array = 0; '\0' != *p && idx <= num; p++)
1213 {
1214 switch (state)
1215 {
1216 /* init state */
1217 case 0:
1218 if (',' == *p)
1219 {
1220 if (0 == array)
1221 idx++;
1222 else if (idx == num)
1223 ZBX_ASSIGN_PARAM;
1224 }
1225 else if ('"' == *p)
1226 {
1227 state = 1;
1228 if (0 != array && idx == num)
1229 ZBX_ASSIGN_PARAM;
1230 }
1231 else if ('[' == *p)
1232 {
1233 if (0 != array && idx == num)
1234 ZBX_ASSIGN_PARAM;
1235 array++;
1236 }
1237 else if (']' == *p && 0 != array)
1238 {
1239 array--;
1240 if (0 != array && idx == num)
1241 ZBX_ASSIGN_PARAM;
1242
1243 /* skip spaces */
1244 while (' ' == p[1])
1245 p++;
1246
1247 if (',' != p[1] && '\0' != p[1] && (0 == array || ']' != p[1]))
1248 return 1; /* incorrect syntax */
1249 }
1250 else if (' ' != *p)
1251 {
1252 if (idx == num)
1253 ZBX_ASSIGN_PARAM;
1254 state = 2;
1255 }
1256 break;
1257 case 1:
1258 /* quoted */
1259
1260 if ('"' == *p)
1261 {
1262 if (0 != array && idx == num)
1263 ZBX_ASSIGN_PARAM;
1264
1265 /* skip spaces */
1266 while (' ' == p[1])
1267 p++;
1268
1269 if (',' != p[1] && '\0' != p[1] && (0 == array || ']' != p[1]))
1270 return 1; /* incorrect syntax */
1271
1272 state = 0;
1273 }
1274 else if ('\\' == *p && '"' == p[1])
1275 {
1276 if (idx == num && 0 != array)
1277 ZBX_ASSIGN_PARAM;
1278
1279 p++;
1280
1281 if (idx == num)
1282 ZBX_ASSIGN_PARAM;
1283 }
1284 else if (idx == num)
1285 ZBX_ASSIGN_PARAM;
1286 break;
1287 case 2:
1288 /* unquoted */
1289
1290 if (',' == *p || (']' == *p && 0 != array))
1291 {
1292 p--;
1293 state = 0;
1294 }
1295 else if (idx == num)
1296 ZBX_ASSIGN_PARAM;
1297 break;
1298 }
1299
1300 if (idx > num)
1301 break;
1302 }
1303 #undef ZBX_ASSIGN_PARAM
1304
1305 /* missing terminating '"' character */
1306 if (1 == state)
1307 return 1;
1308
1309 /* missing terminating ']' character */
1310 if (0 != array)
1311 return 1;
1312
1313 buf[buf_i] = '\0';
1314
1315 if (idx >= num)
1316 return 0;
1317
1318 return 1;
1319 }
1320
1321 /******************************************************************************
1322 * *
1323 * Function: get_param_len *
1324 * *
1325 * Purpose: return length of the parameter by index (num) *
1326 * from parameter list (param) *
1327 * *
1328 * Parameters: *
1329 * p - [IN] parameter list *
1330 * num - [IN] requested parameter index *
1331 * sz - [OUT] length of requested parameter *
1332 * *
1333 * Return value: *
1334 * 1 - requested parameter missing *
1335 * 0 - requested parameter found *
1336 * (for first parameter result is always 0) *
1337 * *
1338 * Author: Alexander Vladishev *
1339 * *
1340 * Comments: delimeter for parameters is ',' *
1341 * *
1342 ******************************************************************************/
get_param_len(const char * p,int num,size_t * sz)1343 static int get_param_len(const char *p, int num, size_t *sz)
1344 {
1345 /* 0 - init, 1 - inside quoted param, 2 - inside unquoted param */
1346 int state, array, idx = 1;
1347
1348 *sz = 0;
1349
1350 for (state = 0, array = 0; '\0' != *p && idx <= num; p++)
1351 {
1352 switch (state) {
1353 /* Init state */
1354 case 0:
1355 if (',' == *p)
1356 {
1357 if (0 == array)
1358 idx++;
1359 else if (idx == num)
1360 (*sz)++;
1361 }
1362 else if ('"' == *p)
1363 {
1364 state = 1;
1365 if (0 != array && idx == num)
1366 (*sz)++;
1367 }
1368 else if ('[' == *p)
1369 {
1370 if (0 != array && idx == num)
1371 (*sz)++;
1372 array++;
1373 }
1374 else if (']' == *p && 0 != array)
1375 {
1376 array--;
1377 if (0 != array && idx == num)
1378 (*sz)++;
1379
1380 /* skip spaces */
1381 while (' ' == p[1])
1382 p++;
1383
1384 if (',' != p[1] && '\0' != p[1] && (0 == array || ']' != p[1]))
1385 return 1; /* incorrect syntax */
1386 }
1387 else if (' ' != *p)
1388 {
1389 if (idx == num)
1390 (*sz)++;
1391 state = 2;
1392 }
1393 break;
1394 /* Quoted */
1395 case 1:
1396 if ('"' == *p)
1397 {
1398 if (0 != array && idx == num)
1399 (*sz)++;
1400
1401 /* skip spaces */
1402 while (' ' == p[1])
1403 p++;
1404
1405 if (',' != p[1] && '\0' != p[1] && (0 == array || ']' != p[1]))
1406 return 1; /* incorrect syntax */
1407
1408 state = 0;
1409 }
1410 else if ('\\' == *p && '"' == p[1])
1411 {
1412 if (idx == num && 0 != array)
1413 (*sz)++;
1414
1415 p++;
1416
1417 if (idx == num)
1418 (*sz)++;
1419 }
1420 else if (idx == num)
1421 (*sz)++;
1422 break;
1423 /* Unquoted */
1424 case 2:
1425 if (',' == *p || (']' == *p && 0 != array))
1426 {
1427 p--;
1428 state = 0;
1429 }
1430 else if (idx == num)
1431 (*sz)++;
1432 break;
1433 }
1434
1435 if (idx > num)
1436 break;
1437 }
1438
1439 /* missing terminating '"' character */
1440 if (state == 1)
1441 return 1;
1442
1443 /* missing terminating ']' character */
1444 if (array != 0)
1445 return 1;
1446
1447 if (idx >= num)
1448 return 0;
1449
1450 return 1;
1451 }
1452
1453 /******************************************************************************
1454 * *
1455 * Function: get_param_dyn *
1456 * *
1457 * Purpose: return parameter by index (num) from parameter list (param) *
1458 * *
1459 * Parameters: *
1460 * p - [IN] parameter list *
1461 * num - [IN] requested parameter index *
1462 * *
1463 * Return value: *
1464 * NULL - requested parameter missing *
1465 * otherwise - requested parameter *
1466 * (for first parameter result is not NULL) *
1467 * *
1468 * Author: Alexander Vladishev *
1469 * *
1470 * Comments: delimeter for parameters is ',' *
1471 * *
1472 ******************************************************************************/
get_param_dyn(const char * p,int num)1473 char *get_param_dyn(const char *p, int num)
1474 {
1475 char *buf = NULL;
1476 size_t sz;
1477
1478 if (0 != get_param_len(p, num, &sz))
1479 return buf;
1480
1481 buf = zbx_malloc(buf, sz + 1);
1482
1483 if (0 != get_param(p, num, buf, sz + 1))
1484 zbx_free(buf);
1485
1486 return buf;
1487 }
1488
1489 /******************************************************************************
1490 * *
1491 * Function: replace_key_param *
1492 * *
1493 * Purpose: replaces an item key, SNMP OID or their parameters when callback *
1494 * function returns a new string *
1495 * *
1496 * Comments: auxiliary function for replace_key_params_dyn() *
1497 * *
1498 ******************************************************************************/
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)1499 static int replace_key_param(char **data, int key_type, size_t l, size_t *r, int level, int num, int quoted,
1500 replace_key_param_f cb, void *cb_data)
1501 {
1502 char c = (*data)[*r], *param = NULL;
1503 int ret;
1504
1505 (*data)[*r] = '\0';
1506 ret = cb(*data + l, key_type, level, num, quoted, cb_data, ¶m);
1507 (*data)[*r] = c;
1508
1509 if (NULL != param)
1510 {
1511 (*r)--;
1512 zbx_replace_string(data, l, r, param);
1513 (*r)++;
1514
1515 zbx_free(param);
1516 }
1517
1518 return ret;
1519 }
1520
1521 /******************************************************************************
1522 * *
1523 * Function: replace_key_params_dyn *
1524 * *
1525 * Purpose: replaces an item key, SNMP OID or their parameters by using *
1526 * callback function *
1527 * *
1528 * Parameters: *
1529 * data - [IN/OUT] item key or SNMP OID *
1530 * key_type - [IN] ZBX_KEY_TYPE_* *
1531 * cb - [IN] callback function *
1532 * cb_data - [IN] callback function custom data *
1533 * error - [OUT] error messsage *
1534 * maxerrlen - [IN] error size *
1535 * *
1536 * Return value: SUCCEED - function executed successfully *
1537 * FAIL - otherwise, error will contain error message *
1538 * *
1539 ******************************************************************************/
replace_key_params_dyn(char ** data,int key_type,replace_key_param_f cb,void * cb_data,char * error,size_t maxerrlen)1540 int replace_key_params_dyn(char **data, int key_type, replace_key_param_f cb, void *cb_data, char *error,
1541 size_t maxerrlen)
1542 {
1543 typedef enum
1544 {
1545 ZBX_STATE_NEW,
1546 ZBX_STATE_END,
1547 ZBX_STATE_UNQUOTED,
1548 ZBX_STATE_QUOTED
1549 }
1550 zbx_parser_state_t;
1551
1552 size_t i, l = 0;
1553 int level = 0, num = 0, ret = SUCCEED;
1554 zbx_parser_state_t state = ZBX_STATE_END;
1555
1556 if (ZBX_KEY_TYPE_ITEM == key_type)
1557 {
1558 for (i = 0; SUCCEED == is_key_char((*data)[i]) && '\0' != (*data)[i]; i++)
1559 ;
1560
1561 if (0 == i)
1562 goto clean;
1563
1564 if ('[' != (*data)[i] && '\0' != (*data)[i])
1565 goto clean;
1566 }
1567 else
1568 {
1569 for (i = 0; '[' != (*data)[i] && '\0' != (*data)[i]; i++)
1570 ;
1571 }
1572
1573 ret = replace_key_param(data, key_type, 0, &i, level, num, 0, cb, cb_data);
1574
1575 for (; '\0' != (*data)[i] && FAIL != ret; i++)
1576 {
1577 if (0 == level)
1578 {
1579 /* first square bracket + Zapcat compatibility */
1580 if (ZBX_STATE_END == state && '[' == (*data)[i])
1581 state = ZBX_STATE_NEW;
1582 else
1583 break;
1584 }
1585
1586 switch (state)
1587 {
1588 case ZBX_STATE_NEW: /* a new parameter started */
1589 switch ((*data)[i])
1590 {
1591 case ' ':
1592 break;
1593 case ',':
1594 ret = replace_key_param(data, key_type, i, &i, level, num, 0, cb,
1595 cb_data);
1596 if (1 == level)
1597 num++;
1598 break;
1599 case '[':
1600 level++;
1601 if (1 == level)
1602 num++;
1603 break;
1604 case ']':
1605 ret = replace_key_param(data, key_type, i, &i, level, num, 0, cb,
1606 cb_data);
1607 level--;
1608 state = ZBX_STATE_END;
1609 break;
1610 case '"':
1611 state = ZBX_STATE_QUOTED;
1612 l = i;
1613 break;
1614 default:
1615 state = ZBX_STATE_UNQUOTED;
1616 l = i;
1617 }
1618 break;
1619 case ZBX_STATE_END: /* end of parameter */
1620 switch ((*data)[i])
1621 {
1622 case ' ':
1623 break;
1624 case ',':
1625 state = ZBX_STATE_NEW;
1626 if (1 == level)
1627 num++;
1628 break;
1629 case ']':
1630 level--;
1631 break;
1632 default:
1633 goto clean;
1634 }
1635 break;
1636 case ZBX_STATE_UNQUOTED: /* an unquoted parameter */
1637 if (']' == (*data)[i] || ',' == (*data)[i])
1638 {
1639 ret = replace_key_param(data, key_type, l, &i, level, num, 0, cb, cb_data);
1640
1641 i--;
1642 state = ZBX_STATE_END;
1643 }
1644 break;
1645 case ZBX_STATE_QUOTED: /* a quoted parameter */
1646 if ('"' == (*data)[i] && '\\' != (*data)[i - 1])
1647 {
1648 i++;
1649 ret = replace_key_param(data, key_type, l, &i, level, num, 1, cb, cb_data);
1650 i--;
1651
1652 state = ZBX_STATE_END;
1653 }
1654 break;
1655 }
1656 }
1657 clean:
1658 if (0 == i || '\0' != (*data)[i] || 0 != level)
1659 {
1660 if (NULL != error)
1661 {
1662 zbx_snprintf(error, maxerrlen, "Invalid %s at position " ZBX_FS_SIZE_T,
1663 (ZBX_KEY_TYPE_ITEM == key_type ? "item key" : "SNMP OID"), (zbx_fs_size_t)i);
1664 }
1665 ret = FAIL;
1666 }
1667
1668 return ret;
1669 }
1670
1671 /******************************************************************************
1672 * *
1673 * Function: remove_param *
1674 * *
1675 * Purpose: remove parameter by index (num) from parameter list (param) *
1676 * *
1677 * Parameters: *
1678 * param - parameter list *
1679 * num - requested parameter index *
1680 * *
1681 * Return value: *
1682 * *
1683 * Comments: delimiter for parameters is ',' *
1684 * *
1685 ******************************************************************************/
remove_param(char * param,int num)1686 void remove_param(char *param, int num)
1687 {
1688 int state = 0; /* 0 - unquoted parameter, 1 - quoted parameter */
1689 int idx = 1, skip_char = 0;
1690 char *p;
1691
1692 for (p = param; '\0' != *p; p++)
1693 {
1694 switch (state)
1695 {
1696 case 0: /* in unquoted parameter */
1697 if (',' == *p)
1698 {
1699 if (1 == idx && 1 == num)
1700 skip_char = 1;
1701 idx++;
1702 }
1703 else if ('"' == *p)
1704 state = 1;
1705 break;
1706 case 1: /* in quoted parameter */
1707 if ('"' == *p && '\\' != *(p - 1))
1708 state = 0;
1709 break;
1710 }
1711 if (idx != num && 0 == skip_char)
1712 *param++ = *p;
1713
1714 skip_char = 0;
1715 }
1716
1717 *param = '\0';
1718 }
1719
1720 /******************************************************************************
1721 * *
1722 * Function: zbx_num2hex *
1723 * *
1724 * Purpose: convert parameter c (0-15) to hexadecimal value ('0'-'f') *
1725 * *
1726 * Parameters: *
1727 * c - number 0-15 *
1728 * *
1729 * Return value: *
1730 * '0'-'f' *
1731 * *
1732 * Author: Alexander Vladishev *
1733 * *
1734 ******************************************************************************/
zbx_num2hex(u_char c)1735 char zbx_num2hex(u_char c)
1736 {
1737 if (c >= 10)
1738 return c + 0x57; /* a-f */
1739 else
1740 return c + 0x30; /* 0-9 */
1741 }
1742
1743 /******************************************************************************
1744 * *
1745 * Function: zbx_hex2num *
1746 * *
1747 * Purpose: convert hexit c ('0'-'9''a'-'f') to number (0-15) *
1748 * *
1749 * Parameters: *
1750 * c - char ('0'-'9''a'-'f') *
1751 * *
1752 * Return value: *
1753 * 0-15 *
1754 * *
1755 * Author: Alexander Vladishev *
1756 * *
1757 ******************************************************************************/
zbx_hex2num(char c)1758 u_char zbx_hex2num(char c)
1759 {
1760 if (c >= 'a')
1761 return c - 0x57; /* a-f */
1762 else
1763 return c - 0x30; /* 0-9 */
1764 }
1765
1766 /******************************************************************************
1767 * *
1768 * Function: str_in_list *
1769 * *
1770 * Purpose: check if string is contained in a list of delimited strings *
1771 * *
1772 * Parameters: list - strings a,b,ccc,ddd *
1773 * value - value *
1774 * delimiter - delimiter *
1775 * *
1776 * Return value: SUCCEED - string is in the list, FAIL - otherwise *
1777 * *
1778 * Author: Alexei Vladishev, Aleksandrs Saveljevs *
1779 * *
1780 ******************************************************************************/
str_in_list(const char * list,const char * value,char delimiter)1781 int str_in_list(const char *list, const char *value, char delimiter)
1782 {
1783 const char *end;
1784 int ret = FAIL;
1785 size_t len;
1786
1787 len = strlen(value);
1788
1789 while (SUCCEED != ret)
1790 {
1791 if (NULL != (end = strchr(list, delimiter)))
1792 {
1793 ret = (len == (size_t)(end - list) && 0 == strncmp(list, value, len) ? SUCCEED : FAIL);
1794 list = end + 1;
1795 }
1796 else
1797 {
1798 ret = (0 == strcmp(list, value) ? SUCCEED : FAIL);
1799 break;
1800 }
1801 }
1802
1803 return ret;
1804 }
1805
1806 /******************************************************************************
1807 * *
1808 * Function: get_key_param *
1809 * *
1810 * Purpose: return parameter by index (num) from parameter list (param) *
1811 * to be used for keys: key[param1,param2] *
1812 * *
1813 * Parameters: *
1814 * param - parameter list *
1815 * num - requested parameter index *
1816 * buf - pointer of output buffer *
1817 * max_len - size of output buffer *
1818 * *
1819 * Return value: *
1820 * 1 - requested parameter missing *
1821 * 0 - requested parameter found (value - 'buf' can be empty string) *
1822 * *
1823 * Author: Alexei Vladishev *
1824 * *
1825 * Comments: delimeter for parameters is ',' *
1826 * *
1827 ******************************************************************************/
get_key_param(char * param,int num,char * buf,size_t max_len)1828 int get_key_param(char *param, int num, char *buf, size_t max_len)
1829 {
1830 int ret;
1831 char *pl, *pr;
1832
1833 pl = strchr(param, '[');
1834 pr = strrchr(param, ']');
1835
1836 if (NULL == pl || NULL == pr || pl > pr)
1837 return 1;
1838
1839 *pr = '\0';
1840 ret = get_param(pl + 1, num, buf, max_len);
1841 *pr = ']';
1842
1843 return ret;
1844 }
1845
1846 /******************************************************************************
1847 * *
1848 * Function: num_key_param *
1849 * *
1850 * Purpose: calculate count of parameters from parameter list (param) *
1851 * to be used for keys: key[param1,param2] *
1852 * *
1853 * Parameters: *
1854 * param - parameter list *
1855 * *
1856 * Return value: count of parameters *
1857 * *
1858 * Author: Alexei Vladishev *
1859 * *
1860 * Comments: delimeter for parameters is ',' *
1861 * *
1862 ******************************************************************************/
num_key_param(char * param)1863 int num_key_param(char *param)
1864 {
1865 int ret;
1866 char *pl, *pr;
1867
1868 if (NULL == param)
1869 return 0;
1870
1871 pl = strchr(param, '[');
1872 pr = strrchr(param, ']');
1873
1874 if (NULL == pl || NULL == pr || pl > pr)
1875 return 0;
1876
1877 *pr = '\0';
1878 ret = num_param(pl + 1);
1879 *pr = ']';
1880
1881 return ret;
1882 }
1883
1884 /******************************************************************************
1885 * *
1886 * Function: zbx_get_escape_string_len *
1887 * *
1888 * Purpose: calculate the required size for the escaped string *
1889 * *
1890 * Parameters: src - [IN] null terminated source string *
1891 * charlist - [IN] null terminated to-be-escaped character list *
1892 * *
1893 * Return value: size of the escaped string *
1894 * *
1895 * Author: Alexander Vladishev *
1896 * *
1897 ******************************************************************************/
zbx_get_escape_string_len(const char * src,const char * charlist)1898 size_t zbx_get_escape_string_len(const char *src, const char *charlist)
1899 {
1900 size_t sz = 0;
1901
1902 for (; '\0' != *src; src++, sz++)
1903 {
1904 if (NULL != strchr(charlist, *src))
1905 sz++;
1906 }
1907
1908 return sz;
1909 }
1910
1911 /******************************************************************************
1912 * *
1913 * Function: zbx_dyn_escape_string *
1914 * *
1915 * Purpose: escape characters in the source string *
1916 * *
1917 * Parameters: src - [IN] null terminated source string *
1918 * charlist - [IN] null terminated to-be-escaped character list *
1919 * *
1920 * Return value: the escaped string *
1921 * *
1922 * Author: Alexander Vladishev *
1923 * *
1924 ******************************************************************************/
zbx_dyn_escape_string(const char * src,const char * charlist)1925 char *zbx_dyn_escape_string(const char *src, const char *charlist)
1926 {
1927 size_t sz;
1928 char *d, *dst = NULL;
1929
1930 sz = zbx_get_escape_string_len(src, charlist) + 1;
1931
1932 dst = zbx_malloc(dst, sz);
1933
1934 for (d = dst; '\0' != *src; src++)
1935 {
1936 if (NULL != strchr(charlist, *src))
1937 *d++ = '\\';
1938
1939 *d++ = *src;
1940 }
1941
1942 *d = '\0';
1943
1944 return dst;
1945 }
1946
zbx_age2str(int age)1947 char *zbx_age2str(int age)
1948 {
1949 size_t offset = 0;
1950 int days, hours, minutes;
1951 static char buffer[32];
1952
1953 days = (int)((double)age / SEC_PER_DAY);
1954 hours = (int)((double)(age - days * SEC_PER_DAY) / SEC_PER_HOUR);
1955 minutes = (int)((double)(age - days * SEC_PER_DAY - hours * SEC_PER_HOUR) / SEC_PER_MIN);
1956
1957 if (0 != days)
1958 offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, "%dd ", days);
1959 if (0 != days || 0 != hours)
1960 offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, "%dh ", hours);
1961 offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, "%dm", minutes);
1962
1963 return buffer;
1964 }
1965
zbx_date2str(time_t date)1966 char *zbx_date2str(time_t date)
1967 {
1968 static char buffer[11];
1969 struct tm *tm;
1970
1971 tm = localtime(&date);
1972 zbx_snprintf(buffer, sizeof(buffer), "%.4d.%.2d.%.2d",
1973 tm->tm_year + 1900,
1974 tm->tm_mon + 1,
1975 tm->tm_mday);
1976
1977 return buffer;
1978 }
1979
zbx_time2str(time_t time)1980 char *zbx_time2str(time_t time)
1981 {
1982 static char buffer[9];
1983 struct tm *tm;
1984
1985 tm = localtime(&time);
1986 zbx_snprintf(buffer, sizeof(buffer), "%.2d:%.2d:%.2d",
1987 tm->tm_hour,
1988 tm->tm_min,
1989 tm->tm_sec);
1990 return buffer;
1991 }
1992
zbx_strncasecmp(const char * s1,const char * s2,size_t n)1993 int zbx_strncasecmp(const char *s1, const char *s2, size_t n)
1994 {
1995 if (NULL == s1 && NULL == s2)
1996 return 0;
1997
1998 if (NULL == s1)
1999 return 1;
2000
2001 if (NULL == s2)
2002 return -1;
2003
2004 while (0 != n && '\0' != *s1 && '\0' != *s2 &&
2005 tolower((unsigned char)*s1) == tolower((unsigned char)*s2))
2006 {
2007 s1++;
2008 s2++;
2009 n--;
2010 }
2011
2012 return 0 == n ? 0 : tolower((unsigned char)*s1) - tolower((unsigned char)*s2);
2013 }
2014
zbx_strcasestr(const char * haystack,const char * needle)2015 char *zbx_strcasestr(const char *haystack, const char *needle)
2016 {
2017 size_t sz_h, sz_n;
2018 const char *p;
2019
2020 if (NULL == needle || '\0' == *needle)
2021 return (char *)haystack;
2022
2023 if (NULL == haystack || '\0' == *haystack)
2024 return NULL;
2025
2026 sz_h = strlen(haystack);
2027 sz_n = strlen(needle);
2028 if (sz_h < sz_n)
2029 return NULL;
2030
2031 for (p = haystack; p <= &haystack[sz_h - sz_n]; p++)
2032 {
2033 if (0 == zbx_strncasecmp(p, needle, sz_n))
2034 return (char *)p;
2035 }
2036
2037 return NULL;
2038 }
2039
zbx_mismatch(const char * s1,const char * s2)2040 int zbx_mismatch(const char *s1, const char *s2)
2041 {
2042 int i = 0;
2043
2044 while (s1[i] == s2[i])
2045 {
2046 if ('\0' == s1[i++])
2047 return FAIL;
2048 }
2049
2050 return i;
2051 }
2052
cmp_key_id(const char * key_1,const char * key_2)2053 int cmp_key_id(const char *key_1, const char *key_2)
2054 {
2055 const char *p, *q;
2056
2057 for (p = key_1, q = key_2; *p == *q && '\0' != *q && '[' != *q; p++, q++)
2058 ;
2059
2060 return ('\0' == *p || '[' == *p) && ('\0' == *q || '[' == *q) ? SUCCEED : FAIL;
2061 }
2062
get_program_type_string(unsigned char program_type)2063 const char *get_program_type_string(unsigned char program_type)
2064 {
2065 switch (program_type)
2066 {
2067 case ZBX_PROGRAM_TYPE_SERVER:
2068 return "server";
2069 case ZBX_PROGRAM_TYPE_PROXY_ACTIVE:
2070 case ZBX_PROGRAM_TYPE_PROXY_PASSIVE:
2071 return "proxy";
2072 case ZBX_PROGRAM_TYPE_AGENTD:
2073 return "agent";
2074 case ZBX_PROGRAM_TYPE_SENDER:
2075 return "sender";
2076 case ZBX_PROGRAM_TYPE_GET:
2077 return "get";
2078 default:
2079 return "unknown";
2080 }
2081 }
2082
zbx_permission_string(int perm)2083 const char *zbx_permission_string(int perm)
2084 {
2085 switch (perm)
2086 {
2087 case PERM_DENY:
2088 return "dn";
2089 case PERM_READ:
2090 return "r";
2091 case PERM_READ_WRITE:
2092 return "rw";
2093 default:
2094 return "unknown";
2095 }
2096 }
2097
zbx_agent_type_string(zbx_item_type_t item_type)2098 const char *zbx_agent_type_string(zbx_item_type_t item_type)
2099 {
2100 switch (item_type)
2101 {
2102 case ITEM_TYPE_ZABBIX:
2103 return "Zabbix agent";
2104 case ITEM_TYPE_SNMPv1:
2105 case ITEM_TYPE_SNMPv2c:
2106 case ITEM_TYPE_SNMPv3:
2107 return "SNMP agent";
2108 case ITEM_TYPE_IPMI:
2109 return "IPMI agent";
2110 case ITEM_TYPE_JMX:
2111 return "JMX agent";
2112 default:
2113 return "generic";
2114 }
2115 }
2116
zbx_item_value_type_string(zbx_item_value_type_t value_type)2117 const char *zbx_item_value_type_string(zbx_item_value_type_t value_type)
2118 {
2119 switch (value_type)
2120 {
2121 case ITEM_VALUE_TYPE_FLOAT:
2122 return "Numeric (float)";
2123 case ITEM_VALUE_TYPE_STR:
2124 return "Character";
2125 case ITEM_VALUE_TYPE_LOG:
2126 return "Log";
2127 case ITEM_VALUE_TYPE_UINT64:
2128 return "Numeric (unsigned)";
2129 case ITEM_VALUE_TYPE_TEXT:
2130 return "Text";
2131 default:
2132 return "unknown";
2133 }
2134 }
2135
zbx_item_data_type_string(zbx_item_data_type_t data_type)2136 const char *zbx_item_data_type_string(zbx_item_data_type_t data_type)
2137 {
2138 switch (data_type)
2139 {
2140 case ITEM_DATA_TYPE_DECIMAL:
2141 return "Decimal";
2142 case ITEM_DATA_TYPE_OCTAL:
2143 return "Octal";
2144 case ITEM_DATA_TYPE_HEXADECIMAL:
2145 return "Hexadecimal";
2146 case ITEM_DATA_TYPE_BOOLEAN:
2147 return "Boolean";
2148 default:
2149 return "unknown";
2150 }
2151 }
2152
zbx_interface_type_string(zbx_interface_type_t type)2153 const char *zbx_interface_type_string(zbx_interface_type_t type)
2154 {
2155 switch (type)
2156 {
2157 case INTERFACE_TYPE_AGENT:
2158 return "Zabbix agent";
2159 case INTERFACE_TYPE_SNMP:
2160 return "SNMP";
2161 case INTERFACE_TYPE_IPMI:
2162 return "IPMI";
2163 case INTERFACE_TYPE_JMX:
2164 return "JMX";
2165 case INTERFACE_TYPE_ANY:
2166 return "any";
2167 case INTERFACE_TYPE_UNKNOWN:
2168 default:
2169 return "unknown";
2170 }
2171 }
2172
2173 const int INTERFACE_TYPE_PRIORITY[INTERFACE_TYPE_COUNT] =
2174 {
2175 INTERFACE_TYPE_AGENT,
2176 INTERFACE_TYPE_SNMP,
2177 INTERFACE_TYPE_JMX,
2178 INTERFACE_TYPE_IPMI
2179 };
2180
zbx_result_string(int result)2181 const char *zbx_result_string(int result)
2182 {
2183 switch (result)
2184 {
2185 case SUCCEED:
2186 return "SUCCEED";
2187 case FAIL:
2188 return "FAIL";
2189 case CONFIG_ERROR:
2190 return "CONFIG_ERROR";
2191 case NOTSUPPORTED:
2192 return "NOTSUPPORTED";
2193 case NETWORK_ERROR:
2194 return "NETWORK_ERROR";
2195 case TIMEOUT_ERROR:
2196 return "TIMEOUT_ERROR";
2197 case AGENT_ERROR:
2198 return "AGENT_ERROR";
2199 case GATEWAY_ERROR:
2200 return "GATEWAY_ERROR";
2201 default:
2202 return "unknown";
2203 }
2204 }
2205
zbx_item_logtype_string(unsigned char logtype)2206 const char *zbx_item_logtype_string(unsigned char logtype)
2207 {
2208 switch (logtype)
2209 {
2210 case ITEM_LOGTYPE_INFORMATION:
2211 return "Information";
2212 case ITEM_LOGTYPE_WARNING:
2213 return "Warning";
2214 case ITEM_LOGTYPE_ERROR:
2215 return "Error";
2216 case ITEM_LOGTYPE_FAILURE_AUDIT:
2217 return "Failure Audit";
2218 case ITEM_LOGTYPE_SUCCESS_AUDIT:
2219 return "Success Audit";
2220 case ITEM_LOGTYPE_CRITICAL:
2221 return "Critical";
2222 case ITEM_LOGTYPE_VERBOSE:
2223 return "Verbose";
2224 default:
2225 return "unknown";
2226 }
2227 }
2228
zbx_dservice_type_string(zbx_dservice_type_t service)2229 const char *zbx_dservice_type_string(zbx_dservice_type_t service)
2230 {
2231 switch (service)
2232 {
2233 case SVC_SSH:
2234 return "SSH";
2235 case SVC_LDAP:
2236 return "LDAP";
2237 case SVC_SMTP:
2238 return "SMTP";
2239 case SVC_FTP:
2240 return "FTP";
2241 case SVC_HTTP:
2242 return "HTTP";
2243 case SVC_POP:
2244 return "POP";
2245 case SVC_NNTP:
2246 return "NNTP";
2247 case SVC_IMAP:
2248 return "IMAP";
2249 case SVC_TCP:
2250 return "TCP";
2251 case SVC_AGENT:
2252 return "Zabbix agent";
2253 case SVC_SNMPv1:
2254 return "SNMPv1 agent";
2255 case SVC_SNMPv2c:
2256 return "SNMPv2c agent";
2257 case SVC_SNMPv3:
2258 return "SNMPv3 agent";
2259 case SVC_ICMPPING:
2260 return "ICMP ping";
2261 case SVC_HTTPS:
2262 return "HTTPS";
2263 case SVC_TELNET:
2264 return "Telnet";
2265 default:
2266 return "unknown";
2267 }
2268 }
2269
zbx_alert_type_string(unsigned char type)2270 const char *zbx_alert_type_string(unsigned char type)
2271 {
2272 switch (type)
2273 {
2274 case ALERT_TYPE_MESSAGE:
2275 return "message";
2276 default:
2277 return "script";
2278 }
2279 }
2280
zbx_alert_status_string(unsigned char type,unsigned char status)2281 const char *zbx_alert_status_string(unsigned char type, unsigned char status)
2282 {
2283 switch (status)
2284 {
2285 case ALERT_STATUS_SENT:
2286 return (ALERT_TYPE_MESSAGE == type ? "sent" : "executed");
2287 case ALERT_STATUS_NOT_SENT:
2288 return "in progress";
2289 default:
2290 return "failed";
2291 }
2292 }
2293
zbx_escalation_status_string(unsigned char status)2294 const char *zbx_escalation_status_string(unsigned char status)
2295 {
2296 switch (status)
2297 {
2298 case ESCALATION_STATUS_ACTIVE:
2299 return "active";
2300 case ESCALATION_STATUS_RECOVERY:
2301 return "recovery";
2302 case ESCALATION_STATUS_SLEEP:
2303 return "sleep";
2304 case ESCALATION_STATUS_COMPLETED:
2305 return "completed";
2306 default:
2307 return "unknown";
2308 }
2309 }
2310
zbx_trigger_value_string(unsigned char value)2311 const char *zbx_trigger_value_string(unsigned char value)
2312 {
2313 switch (value)
2314 {
2315 case TRIGGER_VALUE_PROBLEM:
2316 return "PROBLEM";
2317 case TRIGGER_VALUE_OK:
2318 return "OK";
2319 default:
2320 return "unknown";
2321 }
2322 }
2323
zbx_trigger_state_string(unsigned char state)2324 const char *zbx_trigger_state_string(unsigned char state)
2325 {
2326 switch (state)
2327 {
2328 case TRIGGER_STATE_NORMAL:
2329 return "Normal";
2330 case TRIGGER_STATE_UNKNOWN:
2331 return "Unknown";
2332 default:
2333 return "unknown";
2334 }
2335 }
2336
zbx_item_state_string(unsigned char state)2337 const char *zbx_item_state_string(unsigned char state)
2338 {
2339 switch (state)
2340 {
2341 case ITEM_STATE_NORMAL:
2342 return "Normal";
2343 case ITEM_STATE_NOTSUPPORTED:
2344 return "Not supported";
2345 default:
2346 return "unknown";
2347 }
2348 }
2349
zbx_event_value_string(unsigned char source,unsigned char object,unsigned char value)2350 const char *zbx_event_value_string(unsigned char source, unsigned char object, unsigned char value)
2351 {
2352 if (EVENT_SOURCE_TRIGGERS == source)
2353 return zbx_trigger_value_string(value);
2354
2355 if (EVENT_SOURCE_INTERNAL == source)
2356 {
2357 switch (object)
2358 {
2359 case EVENT_OBJECT_TRIGGER:
2360 return zbx_trigger_state_string(value);
2361 case EVENT_OBJECT_ITEM:
2362 case EVENT_OBJECT_LLDRULE:
2363 return zbx_item_state_string(value);
2364 }
2365 }
2366
2367 return "unknown";
2368 }
2369
2370 #ifdef _WINDOWS
get_codepage(const char * encoding,unsigned int * codepage)2371 static int get_codepage(const char *encoding, unsigned int *codepage)
2372 {
2373 typedef struct
2374 {
2375 unsigned int codepage;
2376 const char *name;
2377 }
2378 codepage_t;
2379
2380 int i;
2381 char buf[16];
2382 codepage_t cp[] = {{0, "ANSI"}, {37, "IBM037"}, {437, "IBM437"}, {500, "IBM500"}, {708, "ASMO-708"},
2383 {709, NULL}, {710, NULL}, {720, "DOS-720"}, {737, "IBM737"}, {775, "IBM775"}, {850, "IBM850"},
2384 {852, "IBM852"}, {855, "IBM855"}, {857, "IBM857"}, {858, "IBM00858"}, {860, "IBM860"},
2385 {861, "IBM861"}, {862, "DOS-862"}, {863, "IBM863"}, {864, "IBM864"}, {865, "IBM865"},
2386 {866, "CP866"}, {869, "IBM869"}, {870, "IBM870"}, {874, "WINDOWS-874"}, {875, "CP875"},
2387 {932, "SHIFT_JIS"}, {936, "GB2312"}, {949, "KS_C_5601-1987"}, {950, "BIG5"}, {1026, "IBM1026"},
2388 {1047, "IBM01047"}, {1140, "IBM01140"}, {1141, "IBM01141"}, {1142, "IBM01142"},
2389 {1143, "IBM01143"}, {1144, "IBM01144"}, {1145, "IBM01145"}, {1146, "IBM01146"},
2390 {1147, "IBM01147"}, {1148, "IBM01148"}, {1149, "IBM01149"}, {1200, "UTF-16"},
2391 {1201, "UNICODEFFFE"}, {1250, "WINDOWS-1250"}, {1251, "WINDOWS-1251"}, {1252, "WINDOWS-1252"},
2392 {1253, "WINDOWS-1253"}, {1254, "WINDOWS-1254"}, {1255, "WINDOWS-1255"}, {1256, "WINDOWS-1256"},
2393 {1257, "WINDOWS-1257"}, {1258, "WINDOWS-1258"}, {1361, "JOHAB"}, {10000, "MACINTOSH"},
2394 {10001, "X-MAC-JAPANESE"}, {10002, "X-MAC-CHINESETRAD"}, {10003, "X-MAC-KOREAN"},
2395 {10004, "X-MAC-ARABIC"}, {10005, "X-MAC-HEBREW"}, {10006, "X-MAC-GREEK"},
2396 {10007, "X-MAC-CYRILLIC"}, {10008, "X-MAC-CHINESESIMP"}, {10010, "X-MAC-ROMANIAN"},
2397 {10017, "X-MAC-UKRAINIAN"}, {10021, "X-MAC-THAI"}, {10029, "X-MAC-CE"},
2398 {10079, "X-MAC-ICELANDIC"}, {10081, "X-MAC-TURKISH"}, {10082, "X-MAC-CROATIAN"},
2399 {12000, "UTF-32"}, {12001, "UTF-32BE"}, {20000, "X-CHINESE_CNS"}, {20001, "X-CP20001"},
2400 {20002, "X_CHINESE-ETEN"}, {20003, "X-CP20003"}, {20004, "X-CP20004"}, {20005, "X-CP20005"},
2401 {20105, "X-IA5"}, {20106, "X-IA5-GERMAN"}, {20107, "X-IA5-SWEDISH"}, {20108, "X-IA5-NORWEGIAN"},
2402 {20127, "US-ASCII"}, {20261, "X-CP20261"}, {20269, "X-CP20269"}, {20273, "IBM273"},
2403 {20277, "IBM277"}, {20278, "IBM278"}, {20280, "IBM280"}, {20284, "IBM284"}, {20285, "IBM285"},
2404 {20290, "IBM290"}, {20297, "IBM297"}, {20420, "IBM420"}, {20423, "IBM423"}, {20424, "IBM424"},
2405 {20833, "X-EBCDIC-KOREANEXTENDED"}, {20838, "IBM-THAI"}, {20866, "KOI8-R"}, {20871, "IBM871"},
2406 {20880, "IBM880"}, {20905, "IBM905"}, {20924, "IBM00924"}, {20932, "EUC-JP"},
2407 {20936, "X-CP20936"}, {20949, "X-CP20949"}, {21025, "CP1025"}, {21027, NULL}, {21866, "KOI8-U"},
2408 {28591, "ISO-8859-1"}, {28592, "ISO-8859-2"}, {28593, "ISO-8859-3"}, {28594, "ISO-8859-4"},
2409 {28595, "ISO-8859-5"}, {28596, "ISO-8859-6"}, {28597, "ISO-8859-7"}, {28598, "ISO-8859-8"},
2410 {28599, "ISO-8859-9"}, {28603, "ISO-8859-13"}, {28605, "ISO-8859-15"}, {29001, "X-EUROPA"},
2411 {38598, "ISO-8859-8-I"}, {50220, "ISO-2022-JP"}, {50221, "CSISO2022JP"}, {50222, "ISO-2022-JP"},
2412 {50225, "ISO-2022-KR"}, {50227, "X-CP50227"}, {50229, NULL}, {50930, NULL}, {50931, NULL},
2413 {50933, NULL}, {50935, NULL}, {50936, NULL}, {50937, NULL}, {50939, NULL}, {51932, "EUC-JP"},
2414 {51936, "EUC-CN"}, {51949, "EUC-KR"}, {51950, NULL}, {52936, "HZ-GB-2312"}, {54936, "GB18030"},
2415 {57002, "X-ISCII-DE"}, {57003, "X-ISCII-BE"}, {57004, "X-ISCII-TA"}, {57005, "X-ISCII-TE"},
2416 {57006, "X-ISCII-AS"}, {57007, "X-ISCII-OR"}, {57008, "X-ISCII-KA"}, {57009, "X-ISCII-MA"},
2417 {57010, "X-ISCII-GU"}, {57011, "X-ISCII-PA"}, {65000, "UTF-7"}, {65001, "UTF-8"}, {0, NULL}};
2418
2419 if ('\0' == *encoding)
2420 {
2421 *codepage = 0; /* ANSI */
2422 return SUCCEED;
2423 }
2424
2425 /* by name */
2426 for (i = 0; 0 != cp[i].codepage || NULL != cp[i].name; i++)
2427 {
2428 if (NULL == cp[i].name)
2429 continue;
2430
2431 if (0 == strcmp(encoding, cp[i].name))
2432 {
2433 *codepage = cp[i].codepage;
2434 return SUCCEED;
2435 }
2436 }
2437
2438 /* by number */
2439 for (i = 0; 0 != cp[i].codepage || NULL != cp[i].name; i++)
2440 {
2441 _itoa_s(cp[i].codepage, buf, sizeof(buf), 10);
2442 if (0 == strcmp(encoding, buf))
2443 {
2444 *codepage = cp[i].codepage;
2445 return SUCCEED;
2446 }
2447 }
2448
2449 /* by 'cp' + number */
2450 for (i = 0; 0 != cp[i].codepage || NULL != cp[i].name; i++)
2451 {
2452 zbx_snprintf(buf, sizeof(buf), "cp%li", cp[i].codepage);
2453 if (0 == strcmp(encoding, buf))
2454 {
2455 *codepage = cp[i].codepage;
2456 return SUCCEED;
2457 }
2458 }
2459
2460 return FAIL;
2461 }
2462
2463 /* convert from selected code page to unicode */
zbx_to_unicode(unsigned int codepage,const char * cp_string)2464 static wchar_t *zbx_to_unicode(unsigned int codepage, const char *cp_string)
2465 {
2466 wchar_t *wide_string = NULL;
2467 int wide_size;
2468
2469 wide_size = MultiByteToWideChar(codepage, 0, cp_string, -1, NULL, 0);
2470 wide_string = (wchar_t *)zbx_malloc(wide_string, (size_t)wide_size * sizeof(wchar_t));
2471
2472 /* convert from cp_string to wide_string */
2473 MultiByteToWideChar(codepage, 0, cp_string, -1, wide_string, wide_size);
2474
2475 return wide_string;
2476 }
2477
2478 /* convert from Windows ANSI code page to unicode */
zbx_acp_to_unicode(const char * acp_string)2479 wchar_t *zbx_acp_to_unicode(const char *acp_string)
2480 {
2481 return zbx_to_unicode(CP_ACP, acp_string);
2482 }
2483
2484 /* convert from Windows OEM code page to unicode */
zbx_oemcp_to_unicode(const char * oemcp_string)2485 wchar_t *zbx_oemcp_to_unicode(const char *oemcp_string)
2486 {
2487 return zbx_to_unicode(CP_OEMCP, oemcp_string);
2488 }
2489
zbx_acp_to_unicode_static(const char * acp_string,wchar_t * wide_string,int wide_size)2490 int zbx_acp_to_unicode_static(const char *acp_string, wchar_t *wide_string, int wide_size)
2491 {
2492 /* convert from acp_string to wide_string */
2493 if (0 == MultiByteToWideChar(CP_ACP, 0, acp_string, -1, wide_string, wide_size))
2494 return FAIL;
2495
2496 return SUCCEED;
2497 }
2498
2499 /* convert from UTF-8 to unicode */
zbx_utf8_to_unicode(const char * utf8_string)2500 wchar_t *zbx_utf8_to_unicode(const char *utf8_string)
2501 {
2502 return zbx_to_unicode(CP_UTF8, utf8_string);
2503 }
2504
2505 /* convert from unicode to utf8 */
zbx_unicode_to_utf8(const wchar_t * wide_string)2506 char *zbx_unicode_to_utf8(const wchar_t *wide_string)
2507 {
2508 char *utf8_string = NULL;
2509 int utf8_size;
2510
2511 utf8_size = WideCharToMultiByte(CP_UTF8, 0, wide_string, -1, NULL, 0, NULL, NULL);
2512 utf8_string = (char *)zbx_malloc(utf8_string, (size_t)utf8_size);
2513
2514 /* convert from wide_string to utf8_string */
2515 WideCharToMultiByte(CP_UTF8, 0, wide_string, -1, utf8_string, utf8_size, NULL, NULL);
2516
2517 return utf8_string;
2518 }
2519
2520 /* convert from unicode to utf8 */
zbx_unicode_to_utf8_static(const wchar_t * wide_string,char * utf8_string,int utf8_size)2521 char *zbx_unicode_to_utf8_static(const wchar_t *wide_string, char *utf8_string, int utf8_size)
2522 {
2523 /* convert from wide_string to utf8_string */
2524 if (0 == WideCharToMultiByte(CP_UTF8, 0, wide_string, -1, utf8_string, utf8_size, NULL, NULL))
2525 *utf8_string = '\0';
2526
2527 return utf8_string;
2528 }
2529 #endif
2530
zbx_strlower(char * str)2531 void zbx_strlower(char *str)
2532 {
2533 for (; '\0' != *str; str++)
2534 *str = tolower(*str);
2535 }
2536
zbx_strupper(char * str)2537 void zbx_strupper(char *str)
2538 {
2539 for (; '\0' != *str; str++)
2540 *str = toupper(*str);
2541 }
2542
2543 #ifdef _WINDOWS
2544 #include "log.h"
convert_to_utf8(char * in,size_t in_size,const char * encoding)2545 char *convert_to_utf8(char *in, size_t in_size, const char *encoding)
2546 {
2547 #define STATIC_SIZE 1024
2548 wchar_t wide_string_static[STATIC_SIZE], *wide_string = NULL;
2549 int wide_size;
2550 char *utf8_string = NULL;
2551 int utf8_size;
2552 unsigned int codepage;
2553
2554 if ('\0' == *encoding || FAIL == get_codepage(encoding, &codepage))
2555 {
2556 utf8_size = (int)in_size + 1;
2557 utf8_string = zbx_malloc(utf8_string, utf8_size);
2558 memcpy(utf8_string, in, in_size);
2559 utf8_string[in_size] = '\0';
2560 return utf8_string;
2561 }
2562
2563 zabbix_log(LOG_LEVEL_DEBUG, "convert_to_utf8() in_size:%d encoding:'%s' codepage:%u", in_size, encoding,
2564 codepage);
2565
2566 if (1200 == codepage) /* Unicode UTF-16, little-endian byte order */
2567 {
2568 wide_string = (wchar_t *)in;
2569 wide_size = (int)in_size / 2;
2570 }
2571 else if (1201 == codepage) /* unicodeFFFE UTF-16, big-endian byte order */
2572 {
2573 wchar_t *wide_string_be = (wchar_t *)in;
2574 int i;
2575
2576 wide_size = (int)in_size / 2;
2577
2578 if (wide_size > STATIC_SIZE)
2579 wide_string = (wchar_t *)zbx_malloc(wide_string, (size_t)wide_size * sizeof(wchar_t));
2580 else
2581 wide_string = wide_string_static;
2582
2583 /* convert from big-endian 'in' to little-endian 'wide_string' */
2584 for (i = 0; i < wide_size; i++)
2585 wide_string[i] = ((wide_string_be[i] << 8) & 0xff00) | ((wide_string_be[i] >> 8) & 0xff);
2586 }
2587 else
2588 {
2589 wide_size = MultiByteToWideChar(codepage, 0, in, (int)in_size, NULL, 0);
2590
2591 if (wide_size > STATIC_SIZE)
2592 wide_string = (wchar_t *)zbx_malloc(wide_string, (size_t)wide_size * sizeof(wchar_t));
2593 else
2594 wide_string = wide_string_static;
2595
2596 /* convert from 'in' to 'wide_string' */
2597 MultiByteToWideChar(codepage, 0, in, (int)in_size, wide_string, wide_size);
2598 }
2599
2600 utf8_size = WideCharToMultiByte(CP_UTF8, 0, wide_string, wide_size, NULL, 0, NULL, NULL);
2601 utf8_string = (char *)zbx_malloc(utf8_string, (size_t)utf8_size + 1/* '\0' */);
2602
2603 /* convert from 'wide_string' to 'utf8_string' */
2604 WideCharToMultiByte(CP_UTF8, 0, wide_string, wide_size, utf8_string, utf8_size, NULL, NULL);
2605 utf8_string[utf8_size] = '\0';
2606 zabbix_log(LOG_LEVEL_DEBUG, "convert_to_utf8() utf8_size:%d utf8_string:'%s'", utf8_size, utf8_string);
2607
2608 if (wide_string != wide_string_static && wide_string != (wchar_t *)in)
2609 zbx_free(wide_string);
2610
2611 return utf8_string;
2612 }
2613 #elif defined(HAVE_ICONV)
convert_to_utf8(char * in,size_t in_size,const char * encoding)2614 char *convert_to_utf8(char *in, size_t in_size, const char *encoding)
2615 {
2616 iconv_t cd;
2617 size_t in_size_left, out_size_left, sz, out_alloc = 0;
2618 const char to_code[] = "UTF-8";
2619 char *out = NULL, *p;
2620
2621 out_alloc = in_size + 1;
2622 p = out = zbx_malloc(out, out_alloc);
2623
2624 if ('\0' == *encoding || (iconv_t)-1 == (cd = iconv_open(to_code, encoding)))
2625 {
2626 memcpy(out, in, in_size);
2627 out[in_size] = '\0';
2628 return out;
2629 }
2630
2631 in_size_left = in_size;
2632 out_size_left = out_alloc - 1;
2633
2634 while ((size_t)(-1) == iconv(cd, &in, &in_size_left, &p, &out_size_left))
2635 {
2636 if (E2BIG != errno)
2637 break;
2638
2639 sz = (size_t)(p - out);
2640 out_alloc += in_size;
2641 out_size_left += in_size;
2642 p = out = zbx_realloc(out, out_alloc);
2643 p += sz;
2644 }
2645
2646 *p = '\0';
2647
2648 iconv_close(cd);
2649
2650 return out;
2651 }
2652 #endif /* HAVE_ICONV */
2653
zbx_strlen_utf8(const char * text)2654 size_t zbx_strlen_utf8(const char *text)
2655 {
2656 size_t n = 0;
2657
2658 while ('\0' != *text)
2659 {
2660 if (0x80 != (0xc0 & *text++))
2661 n++;
2662 }
2663
2664 return n;
2665 }
2666
2667 /******************************************************************************
2668 * *
2669 * Function: zbx_utf8_char_len *
2670 * *
2671 * Purpose: Returns the size (in bytes) of an UTF-8 encoded character or 0 *
2672 * if the character is not a valid UTF-8. *
2673 * *
2674 * Parameters: text - [IN] pointer to the 1st byte of UTF-8 character *
2675 * *
2676 ******************************************************************************/
zbx_utf8_char_len(const char * text)2677 size_t zbx_utf8_char_len(const char *text)
2678 {
2679 if (0 == (*text & 0x80)) /* ASCII */
2680 return 1;
2681 else if (0xc0 == (*text & 0xe0)) /* 11000010-11011111 starts a 2-byte sequence */
2682 return 2;
2683 else if (0xe0 == (*text & 0xf0)) /* 11100000-11101111 starts a 3-byte sequence */
2684 return 3;
2685 else if (0xf0 == (*text & 0xf8)) /* 11110000-11110100 starts a 4-byte sequence */
2686 return 4;
2687 #if ZBX_MAX_BYTES_IN_UTF8_CHAR != 4
2688 # error "zbx_utf8_char_len() is not synchronized with ZBX_MAX_BYTES_IN_UTF8_CHAR"
2689 #endif
2690 return 0; /* not a valid UTF-8 character */
2691 }
2692
2693 /******************************************************************************
2694 * *
2695 * Function: zbx_strlen_utf8_nchars *
2696 * *
2697 * Purpose: calculates number of bytes in utf8 text limited by utf8_maxlen *
2698 * characters *
2699 * *
2700 ******************************************************************************/
zbx_strlen_utf8_nchars(const char * text,size_t utf8_maxlen)2701 size_t zbx_strlen_utf8_nchars(const char *text, size_t utf8_maxlen)
2702 {
2703 size_t sz = 0, csz = 0;
2704 const char *next;
2705
2706 while ('\0' != *text && 0 < utf8_maxlen && 0 != (csz = zbx_utf8_char_len(text)))
2707 {
2708 next = text + csz;
2709 while (next > text)
2710 {
2711 if ('\0' == *text++)
2712 return sz;
2713 }
2714 sz += csz;
2715 utf8_maxlen--;
2716 }
2717
2718 return sz;
2719 }
2720
2721 /******************************************************************************
2722 * *
2723 * Function: zbx_strlen_utf8_nbytes *
2724 * *
2725 * Purpose: calculates number of bytes in utf8 text limited by maxlen bytes *
2726 * *
2727 ******************************************************************************/
zbx_strlen_utf8_nbytes(const char * text,size_t maxlen)2728 size_t zbx_strlen_utf8_nbytes(const char *text, size_t maxlen)
2729 {
2730 size_t sz;
2731
2732 sz = strlen(text);
2733
2734 if (sz > maxlen)
2735 {
2736 sz = maxlen;
2737
2738 /* ensure that the string is not cut in the middle of UTF-8 sequence */
2739 while (0x80 == (0xc0 & text[sz]) && 0 < sz)
2740 sz--;
2741 }
2742
2743 return sz;
2744 }
2745
2746 /******************************************************************************
2747 * *
2748 * Function: zbx_replace_utf8 *
2749 * *
2750 * Purpose: replace non-ASCII UTF-8 characters with '?' character *
2751 * *
2752 * Parameters: text - [IN] pointer to the first char *
2753 * *
2754 * Author: Aleksandrs Saveljevs *
2755 * *
2756 ******************************************************************************/
zbx_replace_utf8(const char * text)2757 char *zbx_replace_utf8(const char *text)
2758 {
2759 int n;
2760 char *out, *p;
2761
2762 out = p = zbx_malloc(NULL, strlen(text) + 1);
2763
2764 while ('\0' != *text)
2765 {
2766 if (0 == (*text & 0x80)) /* ASCII */
2767 n = 1;
2768 else if (0xc0 == (*text & 0xe0)) /* 11000010-11011111 is a start of 2-byte sequence */
2769 n = 2;
2770 else if (0xe0 == (*text & 0xf0)) /* 11100000-11101111 is a start of 3-byte sequence */
2771 n = 3;
2772 else if (0xf0 == (*text & 0xf8)) /* 11110000-11110100 is a start of 4-byte sequence */
2773 n = 4;
2774 else
2775 goto bad;
2776
2777 if (1 == n)
2778 *p++ = *text++;
2779 else
2780 {
2781 *p++ = ZBX_UTF8_REPLACE_CHAR;
2782
2783 while (0 != n)
2784 {
2785 if ('\0' == *text)
2786 goto bad;
2787 n--;
2788 text++;
2789 }
2790 }
2791 }
2792
2793 *p = '\0';
2794 return out;
2795 bad:
2796 zbx_free(out);
2797 return NULL;
2798 }
2799
2800 /******************************************************************************
2801 * *
2802 * Function: zbx_is_utf8 *
2803 * *
2804 * Purpose: check UTF-8 sequences *
2805 * *
2806 * Parameters: text - [IN] pointer to the string *
2807 * *
2808 * Return value: SUCCEED if string is valid or FAIL otherwise *
2809 * *
2810 ******************************************************************************/
zbx_is_utf8(const char * text)2811 int zbx_is_utf8(const char *text)
2812 {
2813 unsigned int utf32;
2814 unsigned char *utf8;
2815 size_t i, mb_len, expecting_bytes = 0;
2816
2817 while ('\0' != *text)
2818 {
2819 /* single ASCII character */
2820 if (0 == (*text & 0x80))
2821 {
2822 text++;
2823 continue;
2824 }
2825
2826 /* unexpected continuation byte or invalid UTF-8 bytes '\xfe' & '\xff' */
2827 if (0x80 == (*text & 0xc0) || 0xfe == (*text & 0xfe))
2828 return FAIL;
2829
2830 /* multibyte sequence */
2831
2832 utf8 = (unsigned char *)text;
2833
2834 if (0xc0 == (*text & 0xe0)) /* 2-bytes multibyte sequence */
2835 expecting_bytes = 1;
2836 else if (0xe0 == (*text & 0xf0)) /* 3-bytes multibyte sequence */
2837 expecting_bytes = 2;
2838 else if (0xf0 == (*text & 0xf8)) /* 4-bytes multibyte sequence */
2839 expecting_bytes = 3;
2840 else if (0xf8 == (*text & 0xfc)) /* 5-bytes multibyte sequence */
2841 expecting_bytes = 4;
2842 else if (0xfc == (*text & 0xfe)) /* 6-bytes multibyte sequence */
2843 expecting_bytes = 5;
2844
2845 mb_len = expecting_bytes + 1;
2846 text++;
2847
2848 for (; 0 != expecting_bytes; expecting_bytes--)
2849 {
2850 /* not a continuation byte */
2851 if (0x80 != (*text++ & 0xc0))
2852 return FAIL;
2853 }
2854
2855 /* overlong sequence */
2856 if (0xc0 == (utf8[0] & 0xfe) ||
2857 (0xe0 == utf8[0] && 0x00 == (utf8[1] & 0x20)) ||
2858 (0xf0 == utf8[0] && 0x00 == (utf8[1] & 0x30)) ||
2859 (0xf8 == utf8[0] && 0x00 == (utf8[1] & 0x38)) ||
2860 (0xfc == utf8[0] && 0x00 == (utf8[1] & 0x3c)))
2861 {
2862 return FAIL;
2863 }
2864
2865 utf32 = 0;
2866
2867 if (0xc0 == (utf8[0] & 0xe0))
2868 utf32 = utf8[0] & 0x1f;
2869 else if (0xe0 == (utf8[0] & 0xf0))
2870 utf32 = utf8[0] & 0x0f;
2871 else if (0xf0 == (utf8[0] & 0xf8))
2872 utf32 = utf8[0] & 0x07;
2873 else if (0xf8 == (utf8[0] & 0xfc))
2874 utf32 = utf8[0] & 0x03;
2875 else if (0xfc == (utf8[0] & 0xfe))
2876 utf32 = utf8[0] & 0x01;
2877
2878 for (i = 1; i < mb_len; i++)
2879 {
2880 utf32 <<= 6;
2881 utf32 += utf8[i] & 0x3f;
2882 }
2883
2884 /* according to the Unicode standard the high and low
2885 * surrogate halves used by UTF-16 (U+D800 through U+DFFF)
2886 * and values above U+10FFFF are not legal
2887 */
2888 if (utf32 > 0x10ffff || 0xd800 == (utf32 & 0xf800))
2889 return FAIL;
2890 }
2891
2892 return SUCCEED;
2893 }
2894
2895 /******************************************************************************
2896 * *
2897 * Function: zbx_replace_invalid_utf8 *
2898 * *
2899 * Purpose: replace invalid UTF-8 sequences of bytes with '?' character *
2900 * *
2901 * Parameters: text - [IN/OUT] pointer to the first char *
2902 * *
2903 ******************************************************************************/
zbx_replace_invalid_utf8(char * text)2904 void zbx_replace_invalid_utf8(char *text)
2905 {
2906 char *out = text;
2907
2908 while ('\0' != *text)
2909 {
2910 if (0 == (*text & 0x80)) /* single ASCII character */
2911 *out++ = *text++;
2912 else if (0x80 == (*text & 0xc0) || /* unexpected continuation byte */
2913 0xfe == (*text & 0xfe)) /* invalid UTF-8 bytes '\xfe' & '\xff' */
2914 {
2915 *out++ = ZBX_UTF8_REPLACE_CHAR;
2916 text++;
2917 }
2918 else /* multibyte sequence */
2919 {
2920 unsigned int utf32;
2921 unsigned char *utf8 = (unsigned char *)out;
2922 size_t i, mb_len, expecting_bytes = 0;
2923 int ret = SUCCEED;
2924
2925 if (0xc0 == (*text & 0xe0)) /* 2-bytes multibyte sequence */
2926 expecting_bytes = 1;
2927 else if (0xe0 == (*text & 0xf0)) /* 3-bytes multibyte sequence */
2928 expecting_bytes = 2;
2929 else if (0xf0 == (*text & 0xf8)) /* 4-bytes multibyte sequence */
2930 expecting_bytes = 3;
2931 else if (0xf8 == (*text & 0xfc)) /* 5-bytes multibyte sequence */
2932 expecting_bytes = 4;
2933 else if (0xfc == (*text & 0xfe)) /* 6-bytes multibyte sequence */
2934 expecting_bytes = 5;
2935
2936 *out++ = *text++;
2937
2938 for (; 0 != expecting_bytes; expecting_bytes--)
2939 {
2940 if (0x80 != (*text & 0xc0)) /* not a continuation byte */
2941 {
2942 ret = FAIL;
2943 break;
2944 }
2945
2946 *out++ = *text++;
2947 }
2948
2949 mb_len = out - (char *)utf8;
2950
2951 if (SUCCEED == ret)
2952 {
2953 if (0xc0 == (utf8[0] & 0xfe) || /* overlong sequence */
2954 (0xe0 == utf8[0] && 0x00 == (utf8[1] & 0x20)) ||
2955 (0xf0 == utf8[0] && 0x00 == (utf8[1] & 0x30)) ||
2956 (0xf8 == utf8[0] && 0x00 == (utf8[1] & 0x38)) ||
2957 (0xfc == utf8[0] && 0x00 == (utf8[1] & 0x3c)))
2958 {
2959 ret = FAIL;
2960 }
2961 }
2962
2963 if (SUCCEED == ret)
2964 {
2965 utf32 = 0;
2966
2967 if (0xc0 == (utf8[0] & 0xe0))
2968 utf32 = utf8[0] & 0x1f;
2969 else if (0xe0 == (utf8[0] & 0xf0))
2970 utf32 = utf8[0] & 0x0f;
2971 else if (0xf0 == (utf8[0] & 0xf8))
2972 utf32 = utf8[0] & 0x07;
2973 else if (0xf8 == (utf8[0] & 0xfc))
2974 utf32 = utf8[0] & 0x03;
2975 else if (0xfc == (utf8[0] & 0xfe))
2976 utf32 = utf8[0] & 0x01;
2977
2978 for (i = 1; i < mb_len; i++)
2979 {
2980 utf32 <<= 6;
2981 utf32 += utf8[i] & 0x3f;
2982 }
2983
2984 /* according to the Unicode standard the high and low
2985 * surrogate halves used by UTF-16 (U+D800 through U+DFFF)
2986 * and values above U+10FFFF are not legal
2987 */
2988 if (utf32 > 0x10ffff || 0xd800 == (utf32 & 0xf800))
2989 ret = FAIL;
2990 }
2991
2992 if (SUCCEED != ret)
2993 {
2994 out -= mb_len;
2995 *out++ = ZBX_UTF8_REPLACE_CHAR;
2996 }
2997 }
2998 }
2999
3000 *out = '\0';
3001 }
3002
dos2unix(char * str)3003 void dos2unix(char *str)
3004 {
3005 char *o = str;
3006
3007 while ('\0' != *str)
3008 {
3009 if ('\r' == str[0] && '\n' == str[1]) /* CR+LF (Windows) */
3010 str++;
3011 *o++ = *str++;
3012 }
3013 *o = '\0';
3014 }
3015
is_ascii_string(const char * str)3016 int is_ascii_string(const char *str)
3017 {
3018 while ('\0' != *str)
3019 {
3020 if (0 != ((1 << 7) & *str)) /* check for range 0..127 */
3021 return FAIL;
3022
3023 str++;
3024 }
3025
3026 return SUCCEED;
3027 }
3028
3029 /******************************************************************************
3030 * *
3031 * Function: str_linefeed *
3032 * *
3033 * Purpose: wrap long string at specified position with linefeeds *
3034 * *
3035 * Parameters: src - input string *
3036 * maxline - maximum length of a line *
3037 * delim - delimiter to use as linefeed (default "\n" if NULL) *
3038 * *
3039 * Return value: newly allocated copy of input string with linefeeds *
3040 * *
3041 * Author: Vladimir Levijev *
3042 * *
3043 * Comments: allocates memory *
3044 * *
3045 ******************************************************************************/
str_linefeed(const char * src,size_t maxline,const char * delim)3046 char *str_linefeed(const char *src, size_t maxline, const char *delim)
3047 {
3048 size_t src_size, dst_size, delim_size, left;
3049 int feeds; /* number of feeds */
3050 char *dst = NULL; /* output with linefeeds */
3051 const char *p_src;
3052 char *p_dst;
3053
3054 assert(NULL != src);
3055 assert(0 < maxline);
3056
3057 /* default delimiter */
3058 if (NULL == delim)
3059 delim = "\n";
3060
3061 src_size = strlen(src);
3062 delim_size = strlen(delim);
3063
3064 /* make sure we don't feed the last line */
3065 feeds = (int)(src_size / maxline - (0 != src_size % maxline || 0 == src_size ? 0 : 1));
3066
3067 left = src_size - feeds * maxline;
3068 dst_size = src_size + feeds * delim_size + 1;
3069
3070 /* allocate memory for output */
3071 dst = zbx_malloc(dst, dst_size);
3072
3073 p_src = src;
3074 p_dst = dst;
3075
3076 /* copy chunks appending linefeeds */
3077 while (0 < feeds--)
3078 {
3079 memcpy(p_dst, p_src, maxline);
3080 p_src += maxline;
3081 p_dst += maxline;
3082
3083 memcpy(p_dst, delim, delim_size);
3084 p_dst += delim_size;
3085 }
3086
3087 if (0 < left)
3088 {
3089 /* copy what's left */
3090 memcpy(p_dst, p_src, left);
3091 p_dst += left;
3092 }
3093
3094 *p_dst = '\0';
3095
3096 return dst;
3097 }
3098
3099 /******************************************************************************
3100 * *
3101 * Function: zbx_strarr_init *
3102 * *
3103 * Purpose: initialize dynamic string array *
3104 * *
3105 * Parameters: arr - a pointer to array of strings *
3106 * *
3107 * Return value: *
3108 * *
3109 * Author: Vladimir Levijev *
3110 * *
3111 * Comments: allocates memory, calls assert() if that fails *
3112 * *
3113 ******************************************************************************/
zbx_strarr_init(char *** arr)3114 void zbx_strarr_init(char ***arr)
3115 {
3116 *arr = zbx_malloc(*arr, sizeof(char *));
3117 **arr = NULL;
3118 }
3119
3120 /******************************************************************************
3121 * *
3122 * Function: zbx_strarr_add *
3123 * *
3124 * Purpose: add a string to dynamic string array *
3125 * *
3126 * Parameters: arr - a pointer to array of strings *
3127 * entry - string to add *
3128 * *
3129 * Return value: *
3130 * *
3131 * Author: Vladimir Levijev *
3132 * *
3133 * Comments: allocates memory, calls assert() if that fails *
3134 * *
3135 ******************************************************************************/
zbx_strarr_add(char *** arr,const char * entry)3136 void zbx_strarr_add(char ***arr, const char *entry)
3137 {
3138 int i;
3139
3140 assert(entry);
3141
3142 for (i = 0; NULL != (*arr)[i]; i++)
3143 ;
3144
3145 *arr = zbx_realloc(*arr, sizeof(char *) * (i + 2));
3146
3147 (*arr)[i] = zbx_strdup((*arr)[i], entry);
3148 (*arr)[++i] = NULL;
3149 }
3150
3151 /******************************************************************************
3152 * *
3153 * Function: zbx_strarr_free *
3154 * *
3155 * Purpose: free dynamic string array memory *
3156 * *
3157 * Parameters: arr - array of strings *
3158 * *
3159 * Return value: *
3160 * *
3161 * Author: Vladimir Levijev *
3162 * *
3163 ******************************************************************************/
zbx_strarr_free(char ** arr)3164 void zbx_strarr_free(char **arr)
3165 {
3166 char **p;
3167
3168 for (p = arr; NULL != *p; p++)
3169 zbx_free(*p);
3170 zbx_free(arr);
3171 }
3172
3173 /******************************************************************************
3174 * *
3175 * Function: zbx_replace_string *
3176 * *
3177 * Purpose: replace data block with 'value' *
3178 * *
3179 * Parameters: data - [IN/OUT] pointer to the string *
3180 * l - [IN] left position of the block *
3181 * r - [IN/OUT] right position of the block *
3182 * value - [IN] the string to replace the block with *
3183 * *
3184 * Author: Alexander Vladishev *
3185 * *
3186 ******************************************************************************/
zbx_replace_string(char ** data,size_t l,size_t * r,const char * value)3187 void zbx_replace_string(char **data, size_t l, size_t *r, const char *value)
3188 {
3189 size_t sz_data, sz_block, sz_value;
3190 char *src, *dst;
3191
3192 sz_value = strlen(value);
3193 sz_block = *r - l + 1;
3194
3195 if (sz_value != sz_block)
3196 {
3197 sz_data = *r + strlen(*data + *r);
3198 sz_data += sz_value - sz_block;
3199
3200 if (sz_value > sz_block)
3201 *data = zbx_realloc(*data, sz_data + 1);
3202
3203 src = *data + l + sz_block;
3204 dst = *data + l + sz_value;
3205
3206 memmove(dst, src, sz_data - l - sz_value + 1);
3207
3208 *r = l + sz_value - 1;
3209 }
3210
3211 memcpy(&(*data)[l], value, sz_value);
3212 }
3213
3214 /******************************************************************************
3215 * *
3216 * Function: zbx_trim_str_list *
3217 * *
3218 * Purpose: remove whitespace surrounding a string list item delimiters *
3219 * *
3220 * Parameters: list - the list (a string containing items separated by *
3221 * delimiter) *
3222 * delimiter - the list delimiter *
3223 * *
3224 * Author: Andris Zeila *
3225 * *
3226 ******************************************************************************/
zbx_trim_str_list(char * list,char delimiter)3227 void zbx_trim_str_list(char *list, char delimiter)
3228 {
3229 /* NB! strchr(3): "terminating null byte is considered part of the string" */
3230 const char *whitespace = " \t";
3231 char *out, *in;
3232
3233 out = in = list;
3234
3235 while ('\0' != *in)
3236 {
3237 /* trim leading spaces from list item */
3238 while ('\0' != *in && NULL != strchr(whitespace, *in))
3239 in++;
3240
3241 /* copy list item */
3242 while (delimiter != *in && '\0' != *in)
3243 *out++ = *in++;
3244
3245 /* trim trailing spaces from list item */
3246 if (out > list)
3247 {
3248 while (NULL != strchr(whitespace, *(--out)))
3249 ;
3250 out++;
3251 }
3252 if (delimiter == *in)
3253 *out++ = *in++;
3254 }
3255 *out = '\0';
3256 }
3257
3258 /******************************************************************************
3259 * *
3260 * Function: zbx_strcmp_null *
3261 * *
3262 * Purpose: *
3263 * compares two strings where any of them can be a NULL pointer *
3264 * *
3265 * Parameters: same as strcmp() except NULL values are allowed *
3266 * *
3267 * Return value: same as strcmp() *
3268 * *
3269 * Comments: NULL is less than any string *
3270 * *
3271 ******************************************************************************/
zbx_strcmp_null(const char * s1,const char * s2)3272 int zbx_strcmp_null(const char *s1, const char *s2)
3273 {
3274 if (NULL == s1)
3275 return NULL == s2 ? 0 : -1;
3276
3277 if (NULL == s2)
3278 return 1;
3279
3280 return strcmp(s1, s2);
3281 }
3282
3283 /******************************************************************************
3284 * *
3285 * Function: zbx_user_macro_parse *
3286 * *
3287 * Purpose: *
3288 * parses user macro and finds its end position and context location *
3289 * *
3290 * Parameters: *
3291 * macro - [IN] the macro to parse *
3292 * macro_r - [OUT] the position of ending '}' character *
3293 * context_l - [OUT] the position of context start character (first non *
3294 * space character after context separator ':') *
3295 * 0 if macro does not have context specified. *
3296 * context_r - [OUT] the position of context end character (either the *
3297 * ending '"' for quoted context values or the last *
3298 * character before the ending '}' character) *
3299 * 0 if macro does not have context specified. *
3300 * *
3301 * Return value: *
3302 * SUCCEED - the macro was parsed successfully. *
3303 * FAIL - the macro parsing failed, the content of output variables *
3304 * is not defined. *
3305 * *
3306 ******************************************************************************/
zbx_user_macro_parse(const char * macro,int * macro_r,int * context_l,int * context_r)3307 int zbx_user_macro_parse(const char *macro, int *macro_r, int *context_l, int *context_r)
3308 {
3309 int i;
3310
3311 /* find the end of macro name by skipping {$ characters and iterating through */
3312 /* valid macro name characters */
3313 for (i = 2; SUCCEED == is_macro_char(macro[i]); i++)
3314 ;
3315
3316 /* check for empty macro name */
3317 if (2 == i)
3318 return FAIL;
3319
3320 if ('}' == macro[i])
3321 {
3322 /* no macro context specified, parsing done */
3323 *macro_r = i;
3324 *context_l = 0;
3325 *context_r = 0;
3326 return SUCCEED;
3327 }
3328
3329 /* fail if the next character is not a macro context separator */
3330 if (':' != macro[i])
3331 return FAIL;
3332
3333 /* skip the whitespace after macro context separator */
3334 while (' ' == macro[++i])
3335 ;
3336
3337 *context_l = i;
3338
3339 if ('"' == macro[i])
3340 {
3341 i++;
3342
3343 /* process quoted context */
3344 for (; '"' != macro[i]; i++)
3345 {
3346 if ('\0' == macro[i])
3347 return FAIL;
3348
3349 if ('\\' == macro[i] && '"' == macro[i + 1])
3350 i++;
3351 }
3352
3353 *context_r = i;
3354
3355 while (' ' == macro[++i])
3356 ;
3357 }
3358 else
3359 {
3360 /* process unquoted context */
3361 for (; '}' != macro[i]; i++)
3362 {
3363 if ('\0' == macro[i])
3364 return FAIL;
3365 }
3366
3367 *context_r = i - 1;
3368 }
3369
3370 if ('}' != macro[i])
3371 return FAIL;
3372
3373 *macro_r = i;
3374
3375 return SUCCEED;
3376 }
3377
3378 /******************************************************************************
3379 * *
3380 * Function: zbx_user_macro_parse_dyn *
3381 * *
3382 * Purpose: *
3383 * parses user macro {$MACRO:<context>} into {$MACRO} and <context> *
3384 * strings *
3385 * *
3386 * Parameters: *
3387 * macro - [IN] the macro to parse *
3388 * name - [OUT] the macro name without context *
3389 * context - [OUT] the unquoted macro context, NULL for macros without *
3390 * context *
3391 * length - [OUT] the length of parsed macro (optional) *
3392 * *
3393 * Return value: *
3394 * SUCCEED - the macro was parsed successfully *
3395 * FAIL - the macro parsing failed, invalid parameter syntax *
3396 * *
3397 ******************************************************************************/
zbx_user_macro_parse_dyn(const char * macro,char ** name,char ** context,int * length)3398 int zbx_user_macro_parse_dyn(const char *macro, char **name, char **context, int *length)
3399 {
3400 const char *ptr;
3401 int macro_r, context_l, context_r, len;
3402
3403 if (SUCCEED != zbx_user_macro_parse(macro, ¯o_r, &context_l, &context_r))
3404 return FAIL;
3405
3406 zbx_free(*context);
3407
3408 if (0 != context_l)
3409 {
3410 ptr = macro + context_l;
3411
3412 /* find the context separator ':' by stripping spaces before context */
3413 while (' ' == *(--ptr))
3414 ;
3415
3416 /* extract the macro name and close with '}' character */
3417 len = ptr - macro + 1;
3418 *name = zbx_realloc(*name, len + 1);
3419 memcpy(*name, macro, len - 1);
3420 (*name)[len - 1] = '}';
3421 (*name)[len] = '\0';
3422
3423 *context = zbx_user_macro_unquote_context_dyn(macro + context_l, context_r - context_l + 1);
3424 }
3425 else
3426 {
3427 *name = zbx_realloc(*name, macro_r + 2);
3428 zbx_strlcpy(*name, macro, macro_r + 2);
3429 }
3430
3431 if (NULL != length)
3432 *length = macro_r + 1;
3433
3434 return SUCCEED;
3435 }
3436
3437 /******************************************************************************
3438 * *
3439 * Function: zbx_user_macro_unquote_context_dyn *
3440 * *
3441 * Purpose: *
3442 * extracts the macro context unquoting if necessary *
3443 * *
3444 * Parameters: *
3445 * context - [IN] the macro context inside a user macro *
3446 * len - [IN] the macro context length (including quotes for quoted *
3447 * contexts) *
3448 * *
3449 * Return value: *
3450 * A string containing extracted macro context. This string must be freed *
3451 * by the caller. *
3452 * *
3453 ******************************************************************************/
zbx_user_macro_unquote_context_dyn(const char * context,int len)3454 char *zbx_user_macro_unquote_context_dyn(const char *context, int len)
3455 {
3456 int quoted = 0;
3457 char *buffer, *ptr;
3458
3459 ptr = buffer = zbx_malloc(NULL, len + 1);
3460
3461 if ('"' == *context)
3462 {
3463 quoted = 1;
3464 context++;
3465 len--;
3466 }
3467
3468 while (0 < len)
3469 {
3470 if (1 == quoted && '\\' == *context && '"' == context[1])
3471 {
3472 context++;
3473 len--;
3474 }
3475
3476 *ptr++ = *context++;
3477 len--;
3478 }
3479
3480 if (1 == quoted)
3481 ptr--;
3482
3483 *ptr = '\0';
3484
3485 return buffer;
3486 }
3487
3488 /******************************************************************************
3489 * *
3490 * Function: zbx_user_macro_quote_context_dyn *
3491 * *
3492 * Purpose: *
3493 * quotes user macro context if necessary *
3494 * *
3495 * Parameters: *
3496 * context - [IN] the macro context *
3497 * force_quote - [IN] if non zero then context quoting is enforced *
3498 * *
3499 * Return value: *
3500 * A string containing quoted macro context. This string must be freed by *
3501 * the caller. *
3502 * *
3503 ******************************************************************************/
zbx_user_macro_quote_context_dyn(const char * context,int force_quote)3504 char *zbx_user_macro_quote_context_dyn(const char *context, int force_quote)
3505 {
3506 int len, quotes = 0;
3507 char *buffer, *ptr_buffer;
3508 const char *ptr_context = context;
3509
3510 if ('"' == *ptr_context || ' ' == *ptr_context)
3511 force_quote = 1;
3512
3513 for (; '\0' != *ptr_context; ptr_context++)
3514 {
3515 if ('}' == *ptr_context)
3516 force_quote = 1;
3517
3518 if ('"' == *ptr_context)
3519 quotes++;
3520 }
3521
3522 if (0 == force_quote)
3523 return zbx_strdup(NULL, context);
3524
3525 len = strlen(context) + 2 + quotes;
3526 ptr_buffer = buffer = zbx_malloc(NULL, len + 1);
3527
3528 *ptr_buffer++ = '"';
3529
3530 while ('\0' != *context)
3531 {
3532 if ('"' == *context)
3533 *ptr_buffer++ = '\\';
3534
3535 *ptr_buffer++ = *context++;
3536 }
3537
3538 *ptr_buffer++ = '"';
3539 *ptr_buffer++ = '\0';
3540
3541 return buffer;
3542 }
3543
3544 /******************************************************************************
3545 * *
3546 * Function: zbx_dyn_escape_shell_single_quote *
3547 * *
3548 * Purpose: escape single quote in shell command arguments *
3549 * *
3550 * Parameters: arg - [IN] the argument to escape *
3551 * *
3552 * Return value: The escaped argument. *
3553 * *
3554 ******************************************************************************/
zbx_dyn_escape_shell_single_quote(const char * arg)3555 char *zbx_dyn_escape_shell_single_quote(const char *arg)
3556 {
3557 int len = 1; /* include terminating zero character */
3558 const char *pin;
3559 char *arg_esc, *pout;
3560
3561 for (pin = arg; '\0' != *pin; pin++)
3562 {
3563 if ('\'' == *pin)
3564 len += 3;
3565 len++;
3566 }
3567
3568 pout = arg_esc = zbx_malloc(NULL, len);
3569
3570 for (pin = arg; '\0' != *pin; pin++)
3571 {
3572 if ('\'' == *pin)
3573 {
3574 *pout++ = '\'';
3575 *pout++ = '\\';
3576 *pout++ = '\'';
3577 *pout++ = '\'';
3578 }
3579 else
3580 *pout++ = *pin;
3581 }
3582
3583 *pout = '\0';
3584
3585 return arg_esc;
3586 }
3587
3588 /******************************************************************************
3589 * *
3590 * Function: function_parse_name *
3591 * *
3592 * Purpose: parses function name *
3593 * *
3594 * Parameters: expr - [IN] the function expression: func(p1, p2,...) *
3595 * length - [OUT] the function name length or the amount of *
3596 * characters that can be safely skipped *
3597 * *
3598 * Return value: SUCCEED - the function name was successfully parsed *
3599 * FAIL - failed to parse function name *
3600 * *
3601 ******************************************************************************/
function_parse_name(const char * expr,size_t * length)3602 static int function_parse_name(const char *expr, size_t *length)
3603 {
3604 const char *ptr;
3605
3606 for (ptr = expr; SUCCEED == is_function_char(*ptr); ptr++)
3607 ;
3608
3609 *length = ptr - expr;
3610
3611 return ptr != expr && '(' == *ptr ? SUCCEED : FAIL;
3612 }
3613
3614 /******************************************************************************
3615 * *
3616 * Function: zbx_function_param_parse *
3617 * *
3618 * Purpose: parses function parameter *
3619 * *
3620 * Parameters: expr - [IN] pre-validated function parameter list *
3621 * param_pos - [OUT] the parameter position, excluding leading *
3622 * whitespace *
3623 * length - [OUT] the parameter length including trailing *
3624 * whitespace for unquoted parameter *
3625 * sep_pos - [OUT] the parameter separator character *
3626 * (',' or '\0') position *
3627 * *
3628 ******************************************************************************/
zbx_function_param_parse(const char * expr,size_t * param_pos,size_t * length,size_t * sep_pos)3629 void zbx_function_param_parse(const char *expr, size_t *param_pos, size_t *length, size_t *sep_pos)
3630 {
3631 const char *ptr = expr;
3632
3633 /* skip the leading whitespace */
3634 while (' ' == *ptr)
3635 ptr++;
3636
3637 *param_pos = ptr - expr;
3638
3639 if ('"' == *ptr) /* quoted parameter */
3640 {
3641 for (ptr++; '"' != *ptr || '\\' == *(ptr - 1); ptr++)
3642 ;
3643
3644 *length = ++ptr - expr - *param_pos;
3645
3646 /* skip trailing whitespace to find the next parameter */
3647 while (' ' == *ptr)
3648 ptr++;
3649 }
3650 else /* unquoted parameter */
3651 {
3652 for (ptr = expr; '\0' != *ptr && ',' != *ptr; ptr++)
3653 ;
3654
3655 *length = ptr - expr - *param_pos;
3656 }
3657
3658 *sep_pos = ptr - expr;
3659 }
3660
3661 /******************************************************************************
3662 * *
3663 * Function: zbx_function_param_unquote_dyn *
3664 * *
3665 * Purpose: unquotes function parameter *
3666 * *
3667 * Parameters: param - [IN] the parameter to unquote *
3668 * len - [IN] the parameter length *
3669 * quoted - [OUT] the flag that specifies whether parameter was *
3670 * quoted before extraction *
3671 * *
3672 * Return value: The unquoted parameter. This value must be freed by the *
3673 * caller. *
3674 * *
3675 ******************************************************************************/
zbx_function_param_unquote_dyn(const char * param,size_t len,int * quoted)3676 char *zbx_function_param_unquote_dyn(const char *param, size_t len, int *quoted)
3677 {
3678 char *out;
3679
3680 out = (char *)zbx_malloc(NULL, len + 1);
3681
3682 if (0 == (*quoted = (0 != len && '"' == *param)))
3683 {
3684 /* unquoted parameter - simply copy it */
3685 memcpy(out, param, len);
3686 out[len] = '\0';
3687 }
3688 else
3689 {
3690 /* quoted parameter - remove enclosing " and replace \" with " */
3691 const char *pin;
3692 char *pout = out;
3693
3694 for (pin = param + 1; (size_t)(pin - param) < len - 1; pin++)
3695 {
3696 if ('\\' == pin[0] && '"' == pin[1])
3697 pin++;
3698
3699 *pout++ = *pin;
3700 }
3701
3702 *pout = '\0';
3703 }
3704
3705 return out;
3706 }
3707
3708 /******************************************************************************
3709 * *
3710 * Function: zbx_function_param_quote *
3711 * *
3712 * Purpose: quotes function parameter *
3713 * *
3714 * Parameters: param - [IN/OUT] function parameter *
3715 * forced - [IN] 1 - enclose parameter in " even if it does not *
3716 * contain any special characters *
3717 * 0 - do nothing if the parameter does not *
3718 * contain any special characters *
3719 * *
3720 * Return value: SUCCEED - if parameter was successfully quoted or quoting *
3721 * was not necessary *
3722 * FAIL - if parameter needs to but cannot be quoted due to *
3723 * backslash in the end *
3724 * *
3725 ******************************************************************************/
zbx_function_param_quote(char ** param,int forced)3726 int zbx_function_param_quote(char **param, int forced)
3727 {
3728 size_t sz_src, sz_dst;
3729
3730 if (0 == forced && '"' != **param && ' ' != **param && NULL == strchr(*param, ',') &&
3731 NULL == strchr(*param, ')'))
3732 {
3733 return SUCCEED;
3734 }
3735
3736 if (0 != (sz_src = strlen(*param)) && '\\' == (*param)[sz_src - 1])
3737 return FAIL;
3738
3739 sz_dst = zbx_get_escape_string_len(*param, "\"") + 3;
3740
3741 *param = zbx_realloc(*param, sz_dst);
3742
3743 (*param)[--sz_dst] = '\0';
3744 (*param)[--sz_dst] = '"';
3745
3746 while (0 < sz_src)
3747 {
3748 (*param)[--sz_dst] = (*param)[--sz_src];
3749 if ('"' == (*param)[sz_src])
3750 (*param)[--sz_dst] = '\\';
3751 }
3752 (*param)[--sz_dst] = '"';
3753
3754 return SUCCEED;
3755 }
3756
3757 /******************************************************************************
3758 * *
3759 * Function: zbx_function_get_param_dyn *
3760 * *
3761 * Purpose: return parameter by index (Nparam) from parameter list (params) *
3762 * *
3763 * Parameters: *
3764 * params - [IN] parameter list *
3765 * Nparam - [IN] requested parameter index (from 1) *
3766 * *
3767 * Return value: *
3768 * NULL - requested parameter missing *
3769 * otherwise - requested parameter *
3770 * *
3771 ******************************************************************************/
zbx_function_get_param_dyn(const char * params,int Nparam)3772 char *zbx_function_get_param_dyn(const char *params, int Nparam)
3773 {
3774 const char *ptr;
3775 size_t sep_pos, params_len;
3776 char *out = NULL;
3777 int idx = 0;
3778
3779 params_len = strlen(params) + 1;
3780
3781 for (ptr = params; ++idx <= Nparam && ptr < params + params_len; ptr += sep_pos + 1)
3782 {
3783 size_t param_pos, param_len;
3784 int quoted;
3785
3786 zbx_function_param_parse(ptr, ¶m_pos, ¶m_len, &sep_pos);
3787
3788 if (idx == Nparam)
3789 out = zbx_function_param_unquote_dyn(ptr + param_pos, param_len, "ed);
3790 }
3791
3792 return out;
3793 }
3794
3795 /******************************************************************************
3796 * *
3797 * Function: zbx_no_function *
3798 * *
3799 * Purpose: count calculated item (prototype) formula characters that can be *
3800 * skipped without the risk of missing a function *
3801 * *
3802 ******************************************************************************/
zbx_no_function(const char * expr)3803 static size_t zbx_no_function(const char *expr)
3804 {
3805 const char *ptr = expr;
3806 int len, c_l, c_r;
3807
3808 while ('\0' != *ptr)
3809 {
3810 if ('{' == *ptr && '$' == *(ptr + 1) && SUCCEED == zbx_user_macro_parse(ptr, &len, &c_l, &c_r))
3811 {
3812 ptr += len + 1; /* skip to the position after user macro */
3813 }
3814 else if (SUCCEED != is_function_char(*ptr))
3815 {
3816 ptr++; /* skip one character which cannot belong to function name */
3817 }
3818 else if ((0 == strncmp("and", ptr, len = ZBX_CONST_STRLEN("and")) ||
3819 0 == strncmp("not", ptr, len = ZBX_CONST_STRLEN("not")) ||
3820 0 == strncmp("or", ptr, len = ZBX_CONST_STRLEN("or"))) &&
3821 NULL != strchr("()" ZBX_WHITESPACE, ptr[len]))
3822 {
3823 ptr += len; /* skip to the position after and/or/not operator */
3824 }
3825 else if (ptr > expr && 0 != isdigit(*(ptr - 1)) && NULL != strchr("KMGTsmhdw", *ptr))
3826 {
3827 ptr++; /* skip unit suffix symbol if it's preceded by a digit */
3828 }
3829 else
3830 break;
3831 }
3832
3833 return ptr - expr;
3834 }
3835
3836 /******************************************************************************
3837 * *
3838 * Function: function_match_parenthesis *
3839 * *
3840 * Purpose: given the position of opening function parenthesis find the *
3841 * position of a closing one *
3842 * *
3843 * Parameters: expr - [IN] string to parse that contains parameters *
3844 * *
3845 * terminator - [IN] use ')' if parameters end with *
3846 * parenthesis or '\0' if ends with NULL *
3847 * terminator *
3848 * par_r - [OUT] position of the terminator if found *
3849 * lpp_offset - [OUT] offset of the last parsed parameter *
3850 * lpp_len - [OUT] length of the last parsed parameter *
3851 * *
3852 * Return value: SUCCEED - closing parenthesis was found *
3853 * FAIL - string after par_l does not look like a valid *
3854 * function parameter list *
3855 * *
3856 ******************************************************************************/
function_validate_parameters(const char * expr,char terminator,size_t * par_r,size_t * lpp_offset,size_t * lpp_len)3857 static int function_validate_parameters(const char *expr, char terminator, size_t *par_r, size_t *lpp_offset,
3858 size_t *lpp_len)
3859 {
3860 #define ZBX_FUNC_PARAM_NEXT 0
3861 #define ZBX_FUNC_PARAM_QUOTED 1
3862 #define ZBX_FUNC_PARAM_UNQUOTED 2
3863 #define ZBX_FUNC_PARAM_POSTQUOTED 3
3864
3865 const char *ptr;
3866 int state = ZBX_FUNC_PARAM_NEXT;
3867
3868 for (ptr = expr; '\0' != *ptr; ptr++)
3869 {
3870 if (')' == *ptr && ZBX_FUNC_PARAM_QUOTED != state)
3871 {
3872 *par_r = ptr - expr;
3873 return SUCCEED;
3874 }
3875
3876 switch (state)
3877 {
3878 case ZBX_FUNC_PARAM_NEXT:
3879 *lpp_offset = ptr - expr;
3880 if ('"' == *ptr)
3881 state = ZBX_FUNC_PARAM_QUOTED;
3882 else if (' ' != *ptr && ',' != *ptr)
3883 state = ZBX_FUNC_PARAM_UNQUOTED;
3884 break;
3885 case ZBX_FUNC_PARAM_QUOTED:
3886 if ('"' == *ptr && '\\' != *(ptr - 1))
3887 state = ZBX_FUNC_PARAM_POSTQUOTED;
3888 break;
3889 case ZBX_FUNC_PARAM_UNQUOTED:
3890 if (',' == *ptr)
3891 state = ZBX_FUNC_PARAM_NEXT;
3892 break;
3893 case ZBX_FUNC_PARAM_POSTQUOTED:
3894 if (',' == *ptr)
3895 {
3896 state = ZBX_FUNC_PARAM_NEXT;
3897 }
3898 else if (' ' != *ptr)
3899 {
3900 *lpp_len = ptr - (expr + *lpp_offset);
3901 return FAIL;
3902 }
3903 break;
3904 default:
3905 THIS_SHOULD_NEVER_HAPPEN;
3906 }
3907 }
3908
3909 *lpp_len = ptr - (expr + *lpp_offset);
3910
3911 if (terminator == *ptr && ZBX_FUNC_PARAM_QUOTED != state)
3912 {
3913 *par_r = ptr - expr;
3914 return SUCCEED;
3915 }
3916
3917 return FAIL;
3918
3919 #undef ZBX_FUNC_PARAM_NEXT
3920 #undef ZBX_FUNC_PARAM_QUOTED
3921 #undef ZBX_FUNC_PARAM_UNQUOTED
3922 #undef ZBX_FUNC_PARAM_POSTQUOTED
3923 }
3924
3925 /******************************************************************************
3926 * *
3927 * Function: function_match_parenthesis *
3928 * *
3929 * Purpose: given the position of opening function parenthesis find the *
3930 * position of a closing one *
3931 * *
3932 * Parameters: expr - [IN] string to parse *
3933 * par_l - [IN] position of the opening parenthesis *
3934 * par_r - [OUT] position of the closing parenthesis *
3935 * lpp_offset - [OUT] offset of the last parsed parameter *
3936 * lpp_len - [OUT] length of the last parsed parameter *
3937 * *
3938 * Return value: SUCCEED - closing parenthesis was found *
3939 * FAIL - string after par_l does not look like a valid *
3940 * function parameter list *
3941 * *
3942 ******************************************************************************/
function_match_parenthesis(const char * expr,size_t par_l,size_t * par_r,size_t * lpp_offset,size_t * lpp_len)3943 static int function_match_parenthesis(const char *expr, size_t par_l, size_t *par_r, size_t *lpp_offset,
3944 size_t *lpp_len)
3945 {
3946 if (SUCCEED == function_validate_parameters(expr + par_l + 1, ')', par_r, lpp_offset, lpp_len))
3947 {
3948 *par_r += par_l + 1;
3949 return SUCCEED;
3950 }
3951
3952 *lpp_offset += par_l + 1;
3953 return FAIL;
3954 }
3955
3956 /******************************************************************************
3957 * *
3958 * Function: zbx_function_validate *
3959 * *
3960 * Purpose: check whether expression starts with a valid function *
3961 * *
3962 * Parameters: expr - [IN] string to parse *
3963 * par_l - [OUT] position of the opening parenthesis *
3964 * or the amount of characters to skip *
3965 * par_r - [OUT] position of the closing parenthesis *
3966 * error - [OUT] error message *
3967 * max_error_len - [IN] error size *
3968 * *
3969 * Return value: SUCCEED - string starts with a valid function *
3970 * FAIL - string does not start with a function and par_l *
3971 * characters can be safely skipped *
3972 * *
3973 ******************************************************************************/
zbx_function_validate(const char * expr,size_t * par_l,size_t * par_r,char * error,int max_error_len)3974 int zbx_function_validate(const char *expr, size_t *par_l, size_t *par_r, char *error, int max_error_len)
3975 {
3976 size_t lpp_offset = 0, lpp_len;
3977
3978 /* try to validate function name */
3979 if (SUCCEED == function_parse_name(expr, par_l))
3980 {
3981 /* now we know the position of '(', try to find ')' */
3982 if (SUCCEED == function_match_parenthesis(expr, *par_l, par_r, &lpp_offset, &lpp_len))
3983 return SUCCEED;
3984
3985 if (NULL != error && *par_l > *par_r)
3986 {
3987 zbx_snprintf(error, max_error_len, "Incorrect function '%.*s' expression. "
3988 "Check expression part starting from: %.*s",
3989 *par_l, expr, lpp_len, expr + lpp_offset);
3990
3991 return FAIL;
3992 }
3993 }
3994
3995 if (NULL != error)
3996 zbx_snprintf(error, max_error_len, "Incorrect function expression: %s", expr);
3997
3998 return FAIL;
3999 }
4000
4001 /******************************************************************************
4002 * *
4003 * Function: zbx_function_find *
4004 * *
4005 * Purpose: find the location of the next function and its parameters in *
4006 * calculated item (prototype) formula *
4007 * *
4008 * Parameters: expr - [IN] string to parse *
4009 * func_pos - [OUT] function position in the string *
4010 * par_l - [OUT] position of the opening parenthesis *
4011 * par_r - [OUT] position of the closing parenthesis *
4012 * error - [OUT] error message *
4013 * max_error_len - [IN] error size *
4014 * *
4015 * *
4016 * Return value: SUCCEED - function was found at func_pos *
4017 * FAIL - there are no functions in the expression *
4018 * *
4019 ******************************************************************************/
zbx_function_find(const char * expr,size_t * func_pos,size_t * par_l,size_t * par_r,char * error,int max_error_len)4020 int zbx_function_find(const char *expr, size_t *func_pos, size_t *par_l, size_t *par_r, char *error,
4021 int max_error_len)
4022 {
4023 const char *ptr;
4024
4025 for (ptr = expr; '\0' != *ptr; ptr += *par_l)
4026 {
4027 /* skip the part of expression that is definitely not a function */
4028 ptr += zbx_no_function(ptr);
4029 *par_r = 0;
4030
4031 /* try to validate function candidate */
4032 if (SUCCEED != zbx_function_validate(ptr, par_l, par_r, error, max_error_len))
4033 {
4034 if (*par_l > *par_r)
4035 return FAIL;
4036
4037 continue;
4038 }
4039
4040 *func_pos = ptr - expr;
4041 *par_l += *func_pos;
4042 *par_r += *func_pos;
4043 return SUCCEED;
4044 }
4045
4046 zbx_snprintf(error, max_error_len, "Incorrect function expression: %s", expr);
4047
4048 return FAIL;
4049 }
4050