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