1 /**
2 * xrdp: A Remote Desktop Protocol server.
3 *
4 * Copyright (C) Jay Sorg 2004-2020
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * generic string handling calls
19 */
20
21 #if defined(HAVE_CONFIG_H)
22 #include "config_ac.h"
23 #endif
24 #include <string.h>
25 #include <strings.h>
26 #include <stdlib.h>
27 #include <ctype.h>
28
29
30 #include "log.h"
31 #include "os_calls.h"
32 #include "string_calls.h"
33 #include "defines.h"
34
35 unsigned int
g_format_info_string(char * dest,unsigned int len,const char * format,const struct info_string_tag map[])36 g_format_info_string(char *dest, unsigned int len,
37 const char *format,
38 const struct info_string_tag map[])
39 {
40 unsigned int result = 0;
41 const char *copy_from; /* Data to add to output */
42 unsigned int copy_len; /* Length of above */
43 unsigned int skip; /* Date to skip over in format string */
44 const char *p;
45 const struct info_string_tag *m;
46
47 for ( ; *format != '\0'; format += skip)
48 {
49 if (*format == '%')
50 {
51 char ch = *(format + 1);
52 if (ch == '%')
53 {
54 /* '%%' in format - replace with single '%' */
55 copy_from = format;
56 copy_len = 1;
57 skip = 2;
58 }
59 else if (ch == '\0')
60 {
61 /* Percent at end of string - ignore */
62 copy_from = NULL;
63 copy_len = 0;
64 skip = 1;
65 }
66 else
67 {
68 /* Look up the character in the map, assuming failure */
69 copy_from = NULL;
70 copy_len = 0;
71 skip = 2;
72
73 for (m = map ; m->ch != '\0' ; ++m)
74 {
75 if (ch == m->ch)
76 {
77 copy_from = m->val;
78 copy_len = strlen(copy_from);
79 break;
80 }
81 }
82 }
83 }
84 else if ((p = strchr(format, '%')) != NULL)
85 {
86 /* Copy up to the next '%' */
87 copy_from = format;
88 copy_len = p - format;
89 skip = copy_len;
90 }
91 else
92 {
93 /* Copy the rest of the format string */
94 copy_from = format;
95 copy_len = strlen(format);
96 skip = copy_len;
97 }
98
99 /* Update the result before any truncation */
100 result += copy_len;
101
102 /* Do we have room in the output buffer for any more data? We
103 * must always write a terminator if possible */
104 if (len > 1)
105 {
106 if (copy_len > (len - 1))
107 {
108 copy_len = len - 1;
109 }
110 memcpy(dest, copy_from, copy_len);
111 dest += copy_len;
112 len -= copy_len;
113 }
114 }
115
116 /* Room for a terminator? */
117 if (len > 0)
118 {
119 *dest = '\0';
120 }
121
122 return result;
123 }
124
125 /******************************************************************************/
126 const char *
g_bool2text(int value)127 g_bool2text(int value)
128 {
129 return value ? "true" : "false";
130 }
131
132 /*****************************************************************************/
133 int
g_text2bool(const char * s)134 g_text2bool(const char *s)
135 {
136 if ( (g_atoi(s) != 0) ||
137 (0 == g_strcasecmp(s, "true")) ||
138 (0 == g_strcasecmp(s, "on")) ||
139 (0 == g_strcasecmp(s, "yes")))
140 {
141 return 1;
142 }
143 return 0;
144 }
145
146 /*****************************************************************************/
147 int
g_get_display_num_from_display(const char * display_text)148 g_get_display_num_from_display(const char *display_text)
149 {
150 int rv = -1;
151 const char *p;
152
153 /* Skip over the hostname part of the DISPLAY */
154 if (display_text != NULL && (p = strchr(display_text, ':')) != NULL)
155 {
156 ++p; /* Skip the ':' */
157
158 /* Cater for the (still supported) double-colon. See
159 * https://www.x.org/releases/X11R7.7/doc/libX11/libX11/libX11.html */
160 if (*p == ':')
161 {
162 ++p;
163 }
164
165 /* Check it starts with a digit, to avoid oddities like DISPLAY=":zz.0"
166 * being parsed successfully */
167 if (isdigit(*p))
168 {
169 rv = g_atoi(p);
170 }
171 }
172
173 return rv;
174 }
175
176 /*****************************************************************************/
177 /* returns length of text */
178 int
g_strlen(const char * text)179 g_strlen(const char *text)
180 {
181 if (text == NULL)
182 {
183 return 0;
184 }
185
186 return strlen(text);
187 }
188
189 /*****************************************************************************/
190 /* locates char in text */
191 const char *
g_strchr(const char * text,int c)192 g_strchr(const char *text, int c)
193 {
194 if (text == NULL)
195 {
196 return 0;
197 }
198
199 return strchr(text, c);
200 }
201
202 /*****************************************************************************/
203 /* returns dest */
204 char *
g_strcpy(char * dest,const char * src)205 g_strcpy(char *dest, const char *src)
206 {
207 if (src == 0 && dest != 0)
208 {
209 dest[0] = 0;
210 return dest;
211 }
212
213 if (dest == 0 || src == 0)
214 {
215 return 0;
216 }
217
218 return strcpy(dest, src);
219 }
220
221 /*****************************************************************************/
222 /* returns dest */
223 char *
g_strncpy(char * dest,const char * src,int len)224 g_strncpy(char *dest, const char *src, int len)
225 {
226 char *rv;
227
228 if (src == 0 && dest != 0)
229 {
230 dest[0] = 0;
231 return dest;
232 }
233
234 if (dest == 0 || src == 0)
235 {
236 return 0;
237 }
238
239 rv = strncpy(dest, src, len);
240 dest[len] = 0;
241 return rv;
242 }
243
244 /*****************************************************************************/
245 /* returns dest */
246 char *
g_strcat(char * dest,const char * src)247 g_strcat(char *dest, const char *src)
248 {
249 if (dest == 0 || src == 0)
250 {
251 return dest;
252 }
253
254 return strcat(dest, src);
255 }
256
257 /*****************************************************************************/
258 /* returns dest */
259 char *
g_strncat(char * dest,const char * src,int len)260 g_strncat(char *dest, const char *src, int len)
261 {
262 if (dest == 0 || src == 0)
263 {
264 return dest;
265 }
266
267 return strncat(dest, src, len);
268 }
269
270 /*****************************************************************************/
271 /* if in = 0, return 0 else return newly alloced copy of in */
272 char *
g_strdup(const char * in)273 g_strdup(const char *in)
274 {
275 int len;
276 char *p;
277
278 if (in == 0)
279 {
280 return 0;
281 }
282
283 len = g_strlen(in);
284 p = (char *)g_malloc(len + 1, 0);
285
286 if (p != NULL)
287 {
288 g_strcpy(p, in);
289 }
290
291 return p;
292 }
293
294 /*****************************************************************************/
295 /* if in = 0, return 0 else return newly alloced copy of input string
296 * if the input string is larger than maxlen the returned string will be
297 * truncated. All strings returned will include null termination*/
298 char *
g_strndup(const char * in,const unsigned int maxlen)299 g_strndup(const char *in, const unsigned int maxlen)
300 {
301 unsigned int len;
302 char *p;
303
304 if (in == 0)
305 {
306 return 0;
307 }
308
309 len = g_strlen(in);
310
311 if (len > maxlen)
312 {
313 len = maxlen - 1;
314 }
315
316 p = (char *)g_malloc(len + 2, 0);
317
318 if (p != NULL)
319 {
320 g_strncpy(p, in, len + 1);
321 }
322
323 return p;
324 }
325
326 /*****************************************************************************/
327 int
g_strcmp(const char * c1,const char * c2)328 g_strcmp(const char *c1, const char *c2)
329 {
330 return strcmp(c1, c2);
331 }
332
333 /*****************************************************************************/
334 int
g_strncmp(const char * c1,const char * c2,int len)335 g_strncmp(const char *c1, const char *c2, int len)
336 {
337 return strncmp(c1, c2, len);
338 }
339
340 /*****************************************************************************/
341 /* compare up to delim */
342 int
g_strncmp_d(const char * s1,const char * s2,const char delim,int n)343 g_strncmp_d(const char *s1, const char *s2, const char delim, int n)
344 {
345 char c1;
346 char c2;
347
348 c1 = 0;
349 c2 = 0;
350 while (n > 0)
351 {
352 c1 = *(s1++);
353 c2 = *(s2++);
354 if ((c1 == 0) || (c1 != c2) || (c1 == delim) || (c2 == delim))
355 {
356 return c1 - c2;
357 }
358 n--;
359 }
360 return c1 - c2;
361 }
362
363 /*****************************************************************************/
364 int
g_strcasecmp(const char * c1,const char * c2)365 g_strcasecmp(const char *c1, const char *c2)
366 {
367 #if defined(_WIN32)
368 return stricmp(c1, c2);
369 #else
370 return strcasecmp(c1, c2);
371 #endif
372 }
373
374 /*****************************************************************************/
375 int
g_strncasecmp(const char * c1,const char * c2,int len)376 g_strncasecmp(const char *c1, const char *c2, int len)
377 {
378 #if defined(_WIN32)
379 return strnicmp(c1, c2, len);
380 #else
381 return strncasecmp(c1, c2, len);
382 #endif
383 }
384
385 /*****************************************************************************/
386 int
g_atoi(const char * str)387 g_atoi(const char *str)
388 {
389 if (str == 0)
390 {
391 return 0;
392 }
393
394 return atoi(str);
395 }
396
397 /*****************************************************************************/
398 int
g_htoi(char * str)399 g_htoi(char *str)
400 {
401 int len;
402 int index;
403 int rv;
404 int val;
405 int shift;
406
407 rv = 0;
408 len = strlen(str);
409 index = len - 1;
410 shift = 0;
411
412 while (index >= 0)
413 {
414 val = 0;
415
416 switch (str[index])
417 {
418 case '1':
419 val = 1;
420 break;
421 case '2':
422 val = 2;
423 break;
424 case '3':
425 val = 3;
426 break;
427 case '4':
428 val = 4;
429 break;
430 case '5':
431 val = 5;
432 break;
433 case '6':
434 val = 6;
435 break;
436 case '7':
437 val = 7;
438 break;
439 case '8':
440 val = 8;
441 break;
442 case '9':
443 val = 9;
444 break;
445 case 'a':
446 case 'A':
447 val = 10;
448 break;
449 case 'b':
450 case 'B':
451 val = 11;
452 break;
453 case 'c':
454 case 'C':
455 val = 12;
456 break;
457 case 'd':
458 case 'D':
459 val = 13;
460 break;
461 case 'e':
462 case 'E':
463 val = 14;
464 break;
465 case 'f':
466 case 'F':
467 val = 15;
468 break;
469 }
470
471 rv = rv | (val << shift);
472 index--;
473 shift += 4;
474 }
475
476 return rv;
477 }
478
479 /*****************************************************************************/
480 /* returns number of bytes copied into out_str */
481 int
g_bytes_to_hexstr(const void * bytes,int num_bytes,char * out_str,int bytes_out_str)482 g_bytes_to_hexstr(const void *bytes, int num_bytes, char *out_str,
483 int bytes_out_str)
484 {
485 int rv;
486 int index;
487 char *lout_str;
488 const tui8 *lbytes;
489
490 rv = 0;
491 lbytes = (const tui8 *) bytes;
492 lout_str = out_str;
493 for (index = 0; index < num_bytes; index++)
494 {
495 if (bytes_out_str < 3)
496 {
497 break;
498 }
499 g_snprintf(lout_str, bytes_out_str, "%2.2x", lbytes[index]);
500 lout_str += 2;
501 bytes_out_str -= 2;
502 rv += 2;
503 }
504 return rv;
505 }
506
507 /*****************************************************************************/
508 /* convert a byte array into a hex dump */
509 char *
g_bytes_to_hexdump(const char * src,int len)510 g_bytes_to_hexdump(const char *src, int len)
511 {
512 unsigned char *line;
513 int i;
514 int dump_number_lines;
515 int dump_line_length;
516 int dump_length;
517 int dump_offset;
518 int thisline;
519 int offset;
520 char *dump_buffer;
521
522 #define HEX_DUMP_SOURCE_BYTES_PER_LINE (16)
523 #ifdef _WIN32
524 #define HEX_DUMP_NEWLINE_SIZE (2)
525 #else
526 #ifdef _MACOS
527 #define HEX_DUMP_NEWLINE_SIZE (1)
528 #else
529 #define HEX_DUMP_NEWLINE_SIZE (1)
530 #endif
531 #endif
532
533 dump_line_length = (4 + 3 /* = 4 offset + 3 space */
534 + ((2 + 1) * HEX_DUMP_SOURCE_BYTES_PER_LINE) /* + (2 hex char + 1 space) per source byte */
535 + 2 /* + 2 space */
536 + HEX_DUMP_SOURCE_BYTES_PER_LINE
537 + HEX_DUMP_NEWLINE_SIZE);
538
539 dump_number_lines = (len / HEX_DUMP_SOURCE_BYTES_PER_LINE) + 1; /* +1 to round up */
540 dump_length = (dump_number_lines *dump_line_length /* hex dump lines */
541 + 1); /* terminating NULL */
542 dump_buffer = (char *)g_malloc(dump_length, 1);
543 if (dump_buffer == NULL)
544 {
545 LOG_DEVEL(LOG_LEVEL_WARNING,
546 "Failed to allocate buffer for hex dump of size %d",
547 dump_length);
548 return NULL;
549 }
550
551 line = (unsigned char *)src;
552 offset = 0;
553 dump_offset = 0;
554
555 while (offset < len)
556 {
557 g_sprintf(dump_buffer + dump_offset, "%04x ", offset);
558 dump_offset += 7;
559 thisline = len - offset;
560
561 if (thisline > HEX_DUMP_SOURCE_BYTES_PER_LINE)
562 {
563 thisline = HEX_DUMP_SOURCE_BYTES_PER_LINE;
564 }
565
566 for (i = 0; i < thisline; i++)
567 {
568 g_sprintf(dump_buffer + dump_offset, "%02x ", line[i]);
569 dump_offset += 3;
570 }
571
572 for (; i < HEX_DUMP_SOURCE_BYTES_PER_LINE; i++)
573 {
574 dump_buffer[dump_offset++] = ' ';
575 dump_buffer[dump_offset++] = ' ';
576 dump_buffer[dump_offset++] = ' ';
577 }
578
579 dump_buffer[dump_offset++] = ' ';
580 dump_buffer[dump_offset++] = ' ';
581
582 for (i = 0; i < thisline; i++)
583 {
584 dump_buffer[dump_offset++] = (line[i] >= 0x20 && line[i] < 0x7f) ? line[i] : '.';
585 }
586
587 for (; i < HEX_DUMP_SOURCE_BYTES_PER_LINE; i++)
588 {
589 dump_buffer[dump_offset++] = ' ';
590 }
591
592 #ifdef _WIN32
593 dump_buffer[dump_offset++] = '\r';
594 dump_buffer[dump_offset++] = '\n';
595 #else
596 #ifdef _MACOS
597 dump_buffer[dump_offset++] = '\r';
598 #else
599 dump_buffer[dump_offset++] = '\n';
600 #endif
601 #endif
602 offset += thisline;
603 line += thisline;
604
605
606 if (dump_offset % dump_line_length != 0)
607 {
608 LOG_DEVEL(LOG_LEVEL_WARNING,
609 "BUG: dump_offset (%d) at the end of a line is not a "
610 "multiple of the line length (%d)",
611 dump_offset, dump_line_length);
612 }
613
614 }
615 if (dump_offset > dump_length)
616 {
617 LOG_DEVEL(LOG_LEVEL_WARNING,
618 "BUG: dump_offset (%d) is larger than the dump_buffer length (%d)",
619 dump_offset, dump_length);
620 dump_buffer[0] = '\0';
621 return dump_buffer;
622 }
623
624 /* replace the last new line with the end of the string since log_message
625 will add a new line */
626 dump_buffer[dump_offset - HEX_DUMP_NEWLINE_SIZE] = '\0';
627 return dump_buffer;
628 }
629
630 /*****************************************************************************/
631 int
g_pos(const char * str,const char * to_find)632 g_pos(const char *str, const char *to_find)
633 {
634 const char *pp;
635
636 pp = strstr(str, to_find);
637
638 if (pp == 0)
639 {
640 return -1;
641 }
642
643 return (pp - str);
644 }
645
646 /*****************************************************************************/
647 int
g_mbstowcs(twchar * dest,const char * src,int n)648 g_mbstowcs(twchar *dest, const char *src, int n)
649 {
650 wchar_t *ldest;
651 int rv;
652
653 ldest = (wchar_t *)dest;
654 rv = mbstowcs(ldest, src, n);
655 return rv;
656 }
657
658 /*****************************************************************************/
659 int
g_wcstombs(char * dest,const twchar * src,int n)660 g_wcstombs(char *dest, const twchar *src, int n)
661 {
662 const wchar_t *lsrc;
663 int rv;
664
665 lsrc = (const wchar_t *)src;
666 rv = wcstombs(dest, lsrc, n);
667 return rv;
668 }
669
670 /*****************************************************************************/
671 /* returns error */
672 /* trim spaces and tabs, anything <= space */
673 /* trim_flags 1 trim left, 2 trim right, 3 trim both, 4 trim through */
674 /* this will always shorten the string or not change it */
675 int
g_strtrim(char * str,int trim_flags)676 g_strtrim(char *str, int trim_flags)
677 {
678 int index;
679 int len;
680 int text1_index;
681 int got_char;
682 wchar_t *text;
683 wchar_t *text1;
684
685 len = mbstowcs(0, str, 0);
686
687 if (len < 1)
688 {
689 return 0;
690 }
691
692 if ((trim_flags < 1) || (trim_flags > 4))
693 {
694 return 1;
695 }
696
697 text = (wchar_t *)malloc(len * sizeof(wchar_t) + 8);
698 text1 = (wchar_t *)malloc(len * sizeof(wchar_t) + 8);
699 if (text == NULL || text1 == NULL)
700 {
701 free(text);
702 free(text1);
703 return 1;
704 }
705 text1_index = 0;
706 mbstowcs(text, str, len + 1);
707
708 switch (trim_flags)
709 {
710 case 4: /* trim through */
711
712 for (index = 0; index < len; index++)
713 {
714 if (text[index] > 32)
715 {
716 text1[text1_index] = text[index];
717 text1_index++;
718 }
719 }
720
721 text1[text1_index] = 0;
722 break;
723 case 3: /* trim both */
724 got_char = 0;
725
726 for (index = 0; index < len; index++)
727 {
728 if (got_char)
729 {
730 text1[text1_index] = text[index];
731 text1_index++;
732 }
733 else
734 {
735 if (text[index] > 32)
736 {
737 text1[text1_index] = text[index];
738 text1_index++;
739 got_char = 1;
740 }
741 }
742 }
743
744 text1[text1_index] = 0;
745 len = text1_index;
746
747 /* trim right */
748 for (index = len - 1; index >= 0; index--)
749 {
750 if (text1[index] > 32)
751 {
752 break;
753 }
754 }
755
756 text1_index = index + 1;
757 text1[text1_index] = 0;
758 break;
759 case 2: /* trim right */
760
761 /* copy it */
762 for (index = 0; index < len; index++)
763 {
764 text1[text1_index] = text[index];
765 text1_index++;
766 }
767
768 /* trim right */
769 for (index = len - 1; index >= 0; index--)
770 {
771 if (text1[index] > 32)
772 {
773 break;
774 }
775 }
776
777 text1_index = index + 1;
778 text1[text1_index] = 0;
779 break;
780 case 1: /* trim left */
781 got_char = 0;
782
783 for (index = 0; index < len; index++)
784 {
785 if (got_char)
786 {
787 text1[text1_index] = text[index];
788 text1_index++;
789 }
790 else
791 {
792 if (text[index] > 32)
793 {
794 text1[text1_index] = text[index];
795 text1_index++;
796 got_char = 1;
797 }
798 }
799 }
800
801 text1[text1_index] = 0;
802 break;
803 }
804
805 wcstombs(str, text1, text1_index + 1);
806 free(text);
807 free(text1);
808 return 0;
809 }
810
811 /*****************************************************************************/
812 char *
g_strnjoin(char * dest,int dest_len,const char * joiner,const char * src[],int src_len)813 g_strnjoin(char *dest, int dest_len, const char *joiner, const char *src[], int src_len)
814 {
815 int len = 0;
816 int joiner_len;
817 int i = 0;
818 int dest_remaining;
819 char *dest_pos = dest;
820 char *dest_end;
821
822 if (dest == NULL || dest_len < 1)
823 {
824 return dest;
825 }
826 if (src == NULL || src_len < 1)
827 {
828 dest[0] = '\0';
829 return dest;
830 }
831
832 dest[0] = '\0';
833 dest_end = dest + dest_len - 1;
834 joiner_len = g_strlen(joiner);
835 for (i = 0; i < src_len - 1 && dest_pos < dest_end; i++)
836 {
837 len = g_strlen(src[i]);
838 dest_remaining = dest_end - dest_pos;
839 g_strncat(dest_pos, src[i], dest_remaining);
840 dest_pos += MIN(len, dest_remaining);
841
842 if (dest_pos < dest_end)
843 {
844 dest_remaining = dest_end - dest_pos;
845 g_strncat(dest_pos, joiner, dest_remaining);
846 dest_pos += MIN(joiner_len, dest_remaining);
847 }
848 }
849
850 if (i == src_len - 1 && dest_pos < dest_end)
851 {
852 g_strncat(dest_pos, src[i], dest_end - dest_pos);
853 }
854
855 return dest;
856 }
857