Coverage Report

Created: 2026-10-28 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/winpr/libwinpr/crt/string.c
Line
Count
Source
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * String Manipulation (CRT)
4
 *
5
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
20
#include <winpr/config.h>
21
#include <winpr/assert.h>
22
23
#include <string.h>
24
#include <errno.h>
25
#include <stdio.h>
26
#include <ctype.h>
27
#include <wctype.h>
28
#include <wchar.h>
29
30
#include <winpr/crt.h>
31
#include <winpr/endian.h>
32
33
#if defined(WITH_URIPARSER)
34
#include <uriparser/Uri.h>
35
#endif
36
37
#if defined(WINPR_HAVE_REGCOMP)
38
#include <regex.h>
39
#endif
40
41
/* String Manipulation (CRT): http://msdn.microsoft.com/en-us/library/f0151s4x.aspx */
42
43
#include "../log.h"
44
#define TAG WINPR_TAG("crt")
45
46
#if defined(WITH_URIPARSER)
47
BOOL winpr_str_is_valid_urlN(const char* str, size_t len)
48
{
49
  if (!str || (len == 0))
50
    return FALSE;
51
52
  const char* errorPos = nullptr;
53
  UriUriA uri = WINPR_C_ARRAY_INIT;
54
  const int rc = uriParseSingleUriExA(&uri, str, &str[len - 1], &errorPos);
55
  uriFreeUriMembersA(&uri);
56
  return rc == URI_SUCCESS;
57
}
58
59
BOOL winpr_str_is_valid_url(const char* str)
60
{
61
  if (!str)
62
    return FALSE;
63
64
  const char* errorPos = nullptr;
65
  UriUriA uri = WINPR_C_ARRAY_INIT;
66
  const int rc = uriParseSingleUriA(&uri, str, &errorPos);
67
  uriFreeUriMembersA(&uri);
68
  return rc == URI_SUCCESS;
69
}
70
71
char* winpr_str_url_decode(const char* str, size_t len)
72
{
73
  char* dst = strndup(str, len);
74
  if (!dst)
75
    return nullptr;
76
77
  if (!uriUnescapeInPlaceExA(dst, URI_FALSE, URI_BR_DONT_TOUCH))
78
  {
79
    free(dst);
80
    return nullptr;
81
  }
82
83
  return dst;
84
}
85
86
char* winpr_str_url_encode(const char* str, size_t len)
87
{
88
  char* dst = calloc(len + 1, sizeof(char) * 3);
89
  if (!dst)
90
    return nullptr;
91
92
  if (!uriEscapeA(str, dst, URI_FALSE, URI_FALSE))
93
  {
94
    free(dst);
95
    return nullptr;
96
  }
97
  return dst;
98
}
99
100
#else
101
static const char rfc3986[] = {
102
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
103
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
104
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x2e, 0x00,
105
  0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
106
  0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
107
  0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x5f,
108
  0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
109
  0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x00, 0x00, 0x00, 0x7e, 0x00,
110
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
111
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
112
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
113
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
114
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
115
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
116
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
117
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
118
};
119
120
static char hex2bin(char what)
121
1.03k
{
122
1.03k
  if (what >= 'a')
123
408
    what -= 'a' - 'A';
124
1.03k
  if (what >= 'A')
125
410
    what -= ('A' - 10);
126
628
  else
127
628
    what -= '0';
128
1.03k
  return what;
129
1.03k
}
130
131
static char unescape(const char* what, size_t* px)
132
21.0M
{
133
21.0M
  if ((*what == '%') && (isxdigit(what[1]) && isxdigit(what[2])))
134
519
  {
135
519
    *px += 2;
136
519
    return 16 * hex2bin(what[1]) + hex2bin(what[2]);
137
519
  }
138
139
21.0M
  return *what;
140
21.0M
}
141
142
char* winpr_str_url_decode(const char* str, size_t len)
143
9.26k
{
144
9.26k
  char* dst = calloc(len + 1, sizeof(char));
145
9.26k
  if (!dst)
146
0
    return nullptr;
147
148
9.26k
  size_t pos = 0;
149
21.0M
  for (size_t x = 0; x < strnlen(str, len); x++)
150
21.0M
  {
151
21.0M
    const char* cur = &str[x];
152
21.0M
    dst[pos++] = unescape(cur, &x);
153
21.0M
  }
154
9.26k
  return dst;
155
9.26k
}
156
157
WINPR_ATTR_NODISCARD
158
static char* escape(char* dst, char what)
159
0
{
160
0
  if (rfc3986[what & 0xff])
161
0
  {
162
0
    *dst = what;
163
0
    return dst + 1;
164
0
  }
165
166
0
  if (sprintf(dst, "%%%02" PRIX8, (BYTE)(what & 0xff)) < 0)
167
0
    return nullptr;
168
0
  return dst + 3;
169
0
}
170
171
char* winpr_str_url_encode(const char* str, size_t len)
172
0
{
173
0
  char* dst = calloc(len + 1, sizeof(char) * 3);
174
0
  if (!dst)
175
0
    return nullptr;
176
177
0
  char* ptr = dst;
178
0
  for (size_t x = 0; x < strnlen(str, len); x++)
179
0
  {
180
0
    const char cur = str[x];
181
0
    ptr = escape(ptr, cur);
182
0
  }
183
0
  return dst;
184
0
}
185
186
BOOL winpr_str_is_valid_urlN(const char* str, size_t len)
187
0
{
188
0
  if (!str || (len == 0))
189
0
    return FALSE;
190
191
0
  char* url = strndup(str, len);
192
0
  if (!url)
193
0
    return FALSE;
194
195
0
  const BOOL rc = winpr_str_is_valid_url(url);
196
0
  free(url);
197
0
  return rc;
198
0
}
199
200
#if defined(_WIN32)
201
#include <shlwapi.h>
202
203
BOOL winpr_str_is_valid_url(const char* str)
204
{
205
  if (!str)
206
    return FALSE;
207
  return PathIsURLA(str);
208
}
209
210
#elif defined(WINPR_HAVE_REGCOMP)
211
BOOL winpr_str_is_valid_url(const char* str)
212
0
{
213
0
  if (!str)
214
0
    return FALSE;
215
0
  regex_t regex = WINPR_C_ARRAY_INIT;
216
0
  const char pattern[] = "^([a-z]+://)?([a-zA-Z0-9.-]+)(:[0-9]+)?(/[a-zA-Z0-9./?=&%-]*)?$";
217
218
0
  int ret = regcomp(&regex, pattern, REG_EXTENDED);
219
0
  if (ret)
220
0
  {
221
0
    printf("Could not compile regex\n");
222
0
    return FALSE;
223
0
  }
224
225
0
  ret = regexec(&regex, str, 0, nullptr, 0);
226
0
  regfree(&regex);
227
0
  return ret == 0;
228
0
}
229
230
#else
231
BOOL winpr_str_is_valid_url(WINPR_ATTR_UNUSED const char* str)
232
{
233
  WLog_WARN(TAG, "URL validation not supported by your build, not performing check. Build with "
234
                 "-DWITH_URIPARSER=ON to fix.");
235
  return TRUE;
236
}
237
#endif
238
#endif
239
240
BOOL winpr_str_append(const char* what, char* buffer, size_t size, const char* separator)
241
529
{
242
529
  const size_t used = strnlen(buffer, size);
243
529
  const size_t add = strnlen(what, size);
244
529
  const size_t sep_len = separator ? strnlen(separator, size) : 0;
245
529
  const size_t sep = (used > 0) ? sep_len : 0;
246
247
529
  if (used + add + sep >= size)
248
16
    return FALSE;
249
250
513
  if ((used > 0) && (sep_len > 0))
251
0
    strncat(buffer, separator, sep_len);
252
253
513
  strncat(buffer, what, add);
254
513
  return TRUE;
255
529
}
256
257
WINPR_ATTR_FORMAT_ARG(3, 4)
258
int winpr_asprintf(char** s, size_t* slen, WINPR_FORMAT_ARG const char* templ, ...)
259
0
{
260
0
  va_list ap = WINPR_C_ARRAY_INIT;
261
262
0
  va_start(ap, templ);
263
0
  int rc = winpr_vasprintf(s, slen, templ, ap);
264
0
  va_end(ap);
265
0
  return rc;
266
0
}
267
268
WINPR_ATTR_FORMAT_ARG(3, 0)
269
int winpr_vasprintf(char** s, size_t* slen, WINPR_FORMAT_ARG const char* templ, va_list oap)
270
0
{
271
0
  va_list ap = WINPR_C_ARRAY_INIT;
272
273
0
  *s = nullptr;
274
0
  *slen = 0;
275
276
0
  va_copy(ap, oap);
277
0
  const int length = vsnprintf(nullptr, 0, templ, ap);
278
0
  va_end(ap);
279
0
  if (length < 0)
280
0
    return length;
281
282
0
  char* str = calloc((size_t)length + 1UL, sizeof(char));
283
0
  if (!str)
284
0
    return -1;
285
286
0
  va_copy(ap, oap);
287
0
  const int plen = vsnprintf(str, (size_t)length + 1UL, templ, ap);
288
0
  va_end(ap);
289
290
0
  if (length != plen)
291
0
  {
292
0
    free(str);
293
0
    return -1;
294
0
  }
295
0
  *s = str;
296
0
  *slen = (size_t)length;
297
0
  return length;
298
0
}
299
300
#ifndef _WIN32
301
302
char* _strdup(const char* strSource)
303
133k
{
304
133k
  if (strSource == nullptr)
305
0
    return nullptr;
306
307
133k
  char* strDestination = strdup(strSource);
308
309
133k
  if (strDestination == nullptr)
310
0
    WLog_ERR(TAG, "strdup");
311
312
133k
  return strDestination;
313
133k
}
314
315
WCHAR* _wcsdup(const WCHAR* strSource)
316
1.17M
{
317
1.17M
  if (!strSource)
318
0
    return nullptr;
319
320
1.17M
  size_t len = _wcslen(strSource);
321
1.17M
  WCHAR* strDestination = calloc(len + 1, sizeof(WCHAR));
322
323
1.17M
  if (strDestination != nullptr)
324
1.17M
    memcpy(strDestination, strSource, len * sizeof(WCHAR));
325
326
1.17M
  if (strDestination == nullptr)
327
0
    WLog_ERR(TAG, "wcsdup");
328
329
1.17M
  return strDestination;
330
1.17M
}
331
332
WCHAR* _wcsncat(WCHAR* dst, const WCHAR* src, size_t sz)
333
120k
{
334
120k
  WINPR_ASSERT(dst);
335
120k
  WINPR_ASSERT(src || (sz == 0));
336
337
120k
  const size_t dlen = _wcslen(dst);
338
120k
  const size_t slen = _wcsnlen(src, sz);
339
16.2M
  for (size_t x = 0; x < slen; x++)
340
16.1M
    dst[dlen + x] = src[x];
341
120k
  dst[dlen + slen] = '\0';
342
120k
  return dst;
343
120k
}
344
345
int _stricmp(const char* string1, const char* string2)
346
31.3M
{
347
31.3M
  return strcasecmp(string1, string2);
348
31.3M
}
349
350
int _strnicmp(const char* string1, const char* string2, size_t count)
351
0
{
352
0
  return strncasecmp(string1, string2, count);
353
0
}
354
355
/* _wcscmp -> wcscmp */
356
357
int _wcscmp(const WCHAR* string1, const WCHAR* string2)
358
943k
{
359
943k
  WINPR_ASSERT(string1);
360
943k
  WINPR_ASSERT(string2);
361
362
1.18M
  while (TRUE)
363
1.18M
  {
364
1.18M
    const WCHAR w1 = *string1++;
365
1.18M
    const WCHAR w2 = *string2++;
366
367
1.18M
    if (w1 != w2)
368
823k
      return (int)w1 - w2;
369
359k
    else if ((w1 == '\0') || (w2 == '\0'))
370
119k
      return (int)w1 - w2;
371
1.18M
  }
372
943k
}
373
374
int _wcsncmp(const WCHAR* string1, const WCHAR* string2, size_t count)
375
0
{
376
0
  WINPR_ASSERT(string1);
377
0
  WINPR_ASSERT(string2);
378
379
0
  for (size_t x = 0; x < count; x++)
380
0
  {
381
0
    const WCHAR a = string1[x];
382
0
    const WCHAR b = string2[x];
383
384
0
    if (a != b)
385
0
      return (int)a - b;
386
0
    else if ((a == '\0') || (b == '\0'))
387
0
      return (int)a - b;
388
0
  }
389
0
  return 0;
390
0
}
391
392
/* _wcslen -> wcslen */
393
394
size_t _wcslen(const WCHAR* str)
395
4.17M
{
396
4.17M
  const WCHAR* p = str;
397
398
4.17M
  WINPR_ASSERT(p);
399
400
305M
  while (*p)
401
301M
    p++;
402
403
4.17M
  return (size_t)(p - str);
404
4.17M
}
405
406
/* _wcsnlen -> wcsnlen */
407
408
size_t _wcsnlen(const WCHAR* str, size_t max)
409
1.33M
{
410
1.33M
  WINPR_ASSERT(str);
411
412
1.33M
  size_t x = 0;
413
186M
  for (; x < max; x++)
414
186M
  {
415
186M
    if (str[x] == 0)
416
1.26M
      return x;
417
186M
  }
418
419
75.1k
  return x;
420
1.33M
}
421
422
/* _wcsstr -> wcsstr */
423
424
WCHAR* _wcsstr(const WCHAR* str, const WCHAR* strSearch)
425
0
{
426
0
  WINPR_ASSERT(str);
427
0
  WINPR_ASSERT(strSearch);
428
429
0
  if (strSearch[0] == '\0')
430
0
    return WINPR_CAST_CONST_PTR_AWAY(str, WCHAR*);
431
432
0
  const size_t searchLen = _wcslen(strSearch);
433
0
  while (*str)
434
0
  {
435
0
    if (_wcsncmp(str, strSearch, searchLen) == 0)
436
0
      return WINPR_CAST_CONST_PTR_AWAY(str, WCHAR*);
437
0
    str++;
438
0
  }
439
0
  return nullptr;
440
0
}
441
442
/* _wcschr -> wcschr */
443
#if defined(WITHOUT_WINPR_3x_DEPRECATED)
444
static
445
#endif
446
    WCHAR* _wcschr(const WCHAR* str, WCHAR c)
447
0
{
448
0
  union
449
0
  {
450
0
    const WCHAR* cc;
451
0
    WCHAR* c;
452
0
  } cnv;
453
0
  const WCHAR* p = str;
454
455
0
  while (*p && (*p != c))
456
0
    p++;
457
458
0
  cnv.cc = (*p == c) ? p : nullptr;
459
0
  return cnv.c;
460
0
}
461
462
/* _wcsrchr -> wcsrchr */
463
#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
464
WCHAR* _wcsrchr(const WCHAR* str, WCHAR c)
465
0
{
466
0
  union
467
0
  {
468
0
    const WCHAR* cc;
469
0
    WCHAR* c;
470
0
  } cnv;
471
0
  const WCHAR* p = nullptr;
472
473
0
  if (!str)
474
0
    return nullptr;
475
476
0
  for (; *str != '\0'; str++)
477
0
  {
478
0
    const WCHAR ch = *str;
479
0
    if (ch == c)
480
0
      p = str;
481
0
  }
482
483
0
  cnv.cc = p;
484
0
  return cnv.c;
485
0
}
486
#endif
487
488
char* strtok_s(char* strToken, const char* strDelimit, char** context)
489
0
{
490
0
  return strtok_r(strToken, strDelimit, context);
491
0
}
492
493
WCHAR* wcstok_s(WCHAR* strToken, const WCHAR* strDelimit, WCHAR** context)
494
0
{
495
0
  WCHAR* nextToken = nullptr;
496
0
  WCHAR value = 0;
497
498
0
  if (!strToken)
499
0
    strToken = *context;
500
501
0
  value = *strToken;
502
503
0
  while (*strToken && _wcschr(strDelimit, value))
504
0
  {
505
0
    strToken++;
506
0
    value = *strToken;
507
0
  }
508
509
0
  if (!*strToken)
510
0
    return nullptr;
511
512
0
  nextToken = strToken++;
513
0
  value = *strToken;
514
515
0
  while (*strToken && !(_wcschr(strDelimit, value)))
516
0
  {
517
0
    strToken++;
518
0
    value = *strToken;
519
0
  }
520
521
0
  if (*strToken)
522
0
    *strToken++ = 0;
523
524
0
  *context = strToken;
525
0
  return nextToken;
526
0
}
527
528
#endif
529
530
#if !defined(_WIN32) || defined(_UWP)
531
532
/* Windows API Sets - api-ms-win-core-string-l2-1-0.dll
533
 * http://msdn.microsoft.com/en-us/library/hh802935/
534
 */
535
536
#include "casing.h"
537
538
LPSTR CharUpperA(LPSTR lpsz)
539
0
{
540
0
  size_t length = 0;
541
542
0
  if (!lpsz)
543
0
    return nullptr;
544
545
0
  length = strlen(lpsz);
546
547
0
  if (length < 1)
548
0
    return (LPSTR) nullptr;
549
550
0
  if (length == 1)
551
0
  {
552
0
    char c = *lpsz;
553
554
0
    if ((c >= 'a') && (c <= 'z'))
555
0
      c = (char)(c - 'a' + 'A');
556
557
0
    *lpsz = c;
558
0
    return lpsz;
559
0
  }
560
561
0
  for (size_t i = 0; i < length; i++)
562
0
  {
563
0
    if ((lpsz[i] >= 'a') && (lpsz[i] <= 'z'))
564
0
      lpsz[i] = (char)(lpsz[i] - 'a' + 'A');
565
0
  }
566
567
0
  return lpsz;
568
0
}
569
570
LPWSTR CharUpperW(LPWSTR lpsz)
571
0
{
572
0
  size_t length = 0;
573
574
0
  if (!lpsz)
575
0
    return nullptr;
576
577
0
  length = _wcslen(lpsz);
578
579
0
  if (length < 1)
580
0
    return (LPWSTR) nullptr;
581
582
0
  if (length == 1)
583
0
  {
584
0
    WCHAR c = *lpsz;
585
586
0
    if ((c >= L'a') && (c <= L'z'))
587
0
      c = c - L'a' + L'A';
588
589
0
    *lpsz = c;
590
0
    return lpsz;
591
0
  }
592
593
0
  for (size_t i = 0; i < length; i++)
594
0
  {
595
0
    if ((lpsz[i] >= L'a') && (lpsz[i] <= L'z'))
596
0
      lpsz[i] = lpsz[i] - L'a' + L'A';
597
0
  }
598
599
0
  return lpsz;
600
0
}
601
602
DWORD CharUpperBuffA(LPSTR lpsz, DWORD cchLength)
603
0
{
604
0
  if (cchLength < 1)
605
0
    return 0;
606
607
0
  for (DWORD i = 0; i < cchLength; i++)
608
0
  {
609
0
    if ((lpsz[i] >= 'a') && (lpsz[i] <= 'z'))
610
0
      lpsz[i] = (char)(lpsz[i] - 'a' + 'A');
611
0
  }
612
613
0
  return cchLength;
614
0
}
615
616
DWORD CharUpperBuffW(LPWSTR lpsz, DWORD cchLength)
617
0
{
618
0
  for (DWORD i = 0; i < cchLength; i++)
619
0
  {
620
0
    WCHAR value = winpr_Data_Get_UINT16(&lpsz[i]);
621
0
    value = WINPR_TOUPPERW(value);
622
0
    winpr_Data_Write_UINT16(&lpsz[i], value);
623
0
  }
624
625
0
  return cchLength;
626
0
}
627
628
LPSTR CharLowerA(LPSTR lpsz)
629
0
{
630
0
  size_t length = 0;
631
632
0
  if (!lpsz)
633
0
    return (LPSTR) nullptr;
634
635
0
  length = strlen(lpsz);
636
637
0
  if (length < 1)
638
0
    return (LPSTR) nullptr;
639
640
0
  if (length == 1)
641
0
  {
642
0
    char c = *lpsz;
643
644
0
    if ((c >= 'A') && (c <= 'Z'))
645
0
      c = (char)(c - 'A' + 'a');
646
647
0
    *lpsz = c;
648
0
    return lpsz;
649
0
  }
650
651
0
  for (size_t i = 0; i < length; i++)
652
0
  {
653
0
    if ((lpsz[i] >= 'A') && (lpsz[i] <= 'Z'))
654
0
      lpsz[i] = (char)(lpsz[i] - 'A' + 'a');
655
0
  }
656
657
0
  return lpsz;
658
0
}
659
660
LPWSTR CharLowerW(LPWSTR lpsz)
661
0
{
662
0
  const size_t len = _wcsnlen(lpsz, UINT32_MAX + 1);
663
0
  if (len > UINT32_MAX)
664
0
    return nullptr;
665
0
  CharLowerBuffW(lpsz, (UINT32)len);
666
0
  return lpsz;
667
0
}
668
669
DWORD CharLowerBuffA(LPSTR lpsz, DWORD cchLength)
670
0
{
671
0
  if (cchLength < 1)
672
0
    return 0;
673
674
0
  for (DWORD i = 0; i < cchLength; i++)
675
0
  {
676
0
    if ((lpsz[i] >= 'A') && (lpsz[i] <= 'Z'))
677
0
      lpsz[i] = (char)(lpsz[i] - 'A' + 'a');
678
0
  }
679
680
0
  return cchLength;
681
0
}
682
683
DWORD CharLowerBuffW(LPWSTR lpsz, DWORD cchLength)
684
0
{
685
0
  for (DWORD i = 0; i < cchLength; i++)
686
0
  {
687
0
    WCHAR value = winpr_Data_Get_UINT16(&lpsz[i]);
688
0
    value = WINPR_TOLOWERW(value);
689
0
    winpr_Data_Write_UINT16(&lpsz[i], value);
690
0
  }
691
692
0
  return cchLength;
693
0
}
694
695
BOOL IsCharAlphaA(CHAR ch)
696
0
{
697
0
  if (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')))
698
0
    return 1;
699
0
  else
700
0
    return 0;
701
0
}
702
703
BOOL IsCharAlphaW(WCHAR ch)
704
0
{
705
0
  if (((ch >= L'a') && (ch <= L'z')) || ((ch >= L'A') && (ch <= L'Z')))
706
0
    return 1;
707
0
  else
708
0
    return 0;
709
0
}
710
711
BOOL IsCharAlphaNumericA(CHAR ch)
712
0
{
713
0
  if (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')) ||
714
0
      ((ch >= '0') && (ch <= '9')))
715
0
    return 1;
716
0
  else
717
0
    return 0;
718
0
}
719
720
BOOL IsCharAlphaNumericW(WCHAR ch)
721
0
{
722
0
  if (((ch >= L'a') && (ch <= L'z')) || ((ch >= L'A') && (ch <= L'Z')) ||
723
0
      ((ch >= L'0') && (ch <= L'9')))
724
0
    return 1;
725
0
  else
726
0
    return 0;
727
0
}
728
729
BOOL IsCharUpperA(CHAR ch)
730
0
{
731
0
  if ((ch >= 'A') && (ch <= 'Z'))
732
0
    return 1;
733
0
  else
734
0
    return 0;
735
0
}
736
737
BOOL IsCharUpperW(WCHAR ch)
738
0
{
739
0
  if ((ch >= L'A') && (ch <= L'Z'))
740
0
    return 1;
741
0
  else
742
0
    return 0;
743
0
}
744
745
BOOL IsCharLowerA(CHAR ch)
746
0
{
747
0
  if ((ch >= 'a') && (ch <= 'z'))
748
0
    return 1;
749
0
  else
750
0
    return 0;
751
0
}
752
753
BOOL IsCharLowerW(WCHAR ch)
754
0
{
755
0
  if ((ch >= L'a') && (ch <= L'z'))
756
0
    return 1;
757
0
  else
758
0
    return 0;
759
0
}
760
761
#endif
762
763
size_t ConvertLineEndingToLF(char* str, size_t size)
764
3.72k
{
765
3.72k
  size_t skip = 0;
766
767
3.72k
  WINPR_ASSERT(str || (size == 0));
768
111M
  for (size_t x = 0; x < size; x++)
769
111M
  {
770
111M
    char c = str[x];
771
111M
    switch (c)
772
111M
    {
773
232k
      case '\r':
774
232k
        str[x - skip] = '\n';
775
232k
        if ((x + 1 < size) && (str[x + 1] == '\n'))
776
36.7k
          skip++;
777
232k
        break;
778
111M
      default:
779
111M
        str[x - skip] = c;
780
111M
        break;
781
111M
    }
782
111M
  }
783
3.72k
  if (skip > 0)
784
68
    str[size - skip] = '\0';
785
3.72k
  return size - skip;
786
3.72k
}
787
788
char* ConvertLineEndingToCRLF(const char* str, size_t* size)
789
402
{
790
402
  WINPR_ASSERT(size);
791
402
  const size_t s = *size;
792
402
  WINPR_ASSERT(str || (s == 0));
793
794
402
  *size = 0;
795
402
  if (s == 0)
796
2
    return nullptr;
797
798
400
  size_t linebreaks = 0;
799
9.17M
  for (size_t x = 0; x < s; x++)
800
9.17M
  {
801
9.17M
    char c = str[x];
802
9.17M
    switch (c)
803
9.17M
    {
804
1.66k
      case '\r':
805
2.90M
      case '\n':
806
2.90M
        linebreaks++;
807
2.90M
        break;
808
6.26M
      default:
809
6.26M
        break;
810
9.17M
    }
811
9.17M
  }
812
400
  char* cnv = calloc(s + linebreaks * 2ull + 1ull, sizeof(char));
813
400
  if (!cnv)
814
0
    return nullptr;
815
816
400
  size_t pos = 0;
817
9.17M
  for (size_t x = 0; x < s; x++)
818
9.17M
  {
819
9.17M
    const char c = str[x];
820
9.17M
    switch (c)
821
9.17M
    {
822
1.66k
      case '\r':
823
1.66k
        cnv[pos++] = '\r';
824
1.66k
        cnv[pos++] = '\n';
825
1.66k
        break;
826
2.90M
      case '\n':
827
        /* Do not duplicate existing \r\n sequences */
828
2.90M
        if ((x == 0) || (str[x - 1] != '\r'))
829
2.90M
        {
830
2.90M
          cnv[pos++] = '\r';
831
2.90M
          cnv[pos++] = '\n';
832
2.90M
        }
833
2.90M
        break;
834
6.26M
      default:
835
6.26M
        cnv[pos++] = c;
836
6.26M
        break;
837
9.17M
    }
838
9.17M
  }
839
400
  *size = pos;
840
400
  return cnv;
841
400
}
842
843
char* StrSep(char** stringp, const char* delim)
844
0
{
845
0
  char* start = *stringp;
846
0
  char* p = nullptr;
847
0
  p = (start != nullptr) ? strpbrk(start, delim) : nullptr;
848
849
0
  if (!p)
850
0
    *stringp = nullptr;
851
0
  else
852
0
  {
853
0
    *p = '\0';
854
0
    *stringp = p + 1;
855
0
  }
856
857
0
  return start;
858
0
}
859
860
INT64 GetLine(char** lineptr, size_t* size, FILE* stream)
861
0
{
862
#if defined(_WIN32)
863
  char c;
864
  char* n;
865
  size_t step = 32;
866
  size_t used = 0;
867
868
  if (!lineptr || !size)
869
  {
870
    errno = EINVAL;
871
    return -1;
872
  }
873
874
  do
875
  {
876
    if (used + 2 >= *size)
877
    {
878
      *size += step;
879
      n = realloc(*lineptr, *size);
880
881
      if (!n)
882
      {
883
        return -1;
884
      }
885
886
      *lineptr = n;
887
    }
888
889
    c = fgetc(stream);
890
891
    if (c != EOF)
892
      (*lineptr)[used++] = c;
893
  } while ((c != '\n') && (c != '\r') && (c != EOF));
894
895
  (*lineptr)[used] = '\0';
896
  return used;
897
#elif !defined(ANDROID) && !defined(IOS)
898
  return getline(lineptr, size, stream);
899
#else
900
  return -1;
901
#endif
902
0
}
903
904
#if !defined(WINPR_HAVE_STRNDUP)
905
char* strndup(const char* src, size_t n)
906
{
907
  char* dst = calloc(n + 1, sizeof(char));
908
  if (dst)
909
    strncpy(dst, src, n);
910
  return dst;
911
}
912
#endif
913
914
const WCHAR* InitializeConstWCharFromUtf8(const char* str, WCHAR* buffer, size_t len)
915
1.00M
{
916
1.00M
  WINPR_ASSERT(str);
917
1.00M
  WINPR_ASSERT(buffer || (len == 0));
918
1.00M
  (void)ConvertUtf8ToWChar(str, buffer, len);
919
1.00M
  return buffer;
920
1.00M
}
921
922
WCHAR* wcsndup(const WCHAR* s, size_t n)
923
0
{
924
0
  if (!s)
925
0
    return nullptr;
926
927
0
  WCHAR* copy = calloc(n + 1, sizeof(WCHAR));
928
0
  if (!copy)
929
0
    return nullptr;
930
0
  memcpy(copy, s, n * sizeof(WCHAR));
931
0
  return copy;
932
0
}
933
934
char* winpr_strnstr(const char* haystack, const char* needle, size_t hlen)
935
91.1k
{
936
91.1k
  WINPR_ASSERT(haystack || (hlen == 0));
937
91.1k
  WINPR_ASSERT(needle);
938
939
#if defined(WINPR_HAVE_STRNSTR)
940
  return strnstr(haystack, needle, hlen);
941
#else
942
  /* Match native strnstr semantics: search at most hlen characters and stop at
943
   * the haystack NUL terminator, whichever comes first. */
944
91.1k
  const size_t needle_len = strlen(needle);
945
946
91.1k
  if (0 == needle_len)
947
0
    return WINPR_CAST_CONST_PTR_AWAY(haystack, char*);
948
949
45.9M
  for (; (*haystack != '\0') && (hlen >= needle_len); haystack++, hlen--)
950
45.9M
  {
951
45.9M
    if ((haystack[0] == needle[0]) && (0 == strncmp(haystack, needle, needle_len)))
952
89.1k
      return WINPR_CAST_CONST_PTR_AWAY(haystack, char*);
953
45.9M
  }
954
2.01k
  return nullptr;
955
91.1k
#endif
956
91.1k
}
957
958
BOOL winpr_str_has_newlines(const char* str)
959
0
{
960
0
  if (!str)
961
0
    return FALSE;
962
0
  do
963
0
  {
964
0
    char c = *str++;
965
0
    switch (c)
966
0
    {
967
0
      case '\r':
968
0
      case '\n':
969
0
      case 0x0b:       /* VT vertical tab */
970
0
      case 0x0c:       /* FF form feed */
971
0
      case (char)0x85: /* NEL next line */
972
0
        return TRUE;
973
0
      case (char)0xE2:
974
0
      {
975
0
        if ((str[0] == (char)0x80) && ((str[1] == (char)0xA8) || (str[1] == (char)0xA9)))
976
0
          return TRUE;
977
0
      }
978
0
      break;
979
980
0
      case '\0':
981
0
        return FALSE;
982
0
      default:
983
0
        break;
984
0
    }
985
0
  } while (1);
986
0
}
987
988
WCHAR* winpr_wcsnchr(const WCHAR* str, size_t charLen, WCHAR c)
989
0
{
990
0
  if (!str || (charLen == 0))
991
0
    return nullptr;
992
993
0
  const WCHAR* p = str;
994
0
  const WCHAR* end = &str[charLen];
995
996
0
  while (p != end)
997
0
  {
998
0
    const WCHAR wc = *p;
999
0
    if (wc == c)
1000
0
    {
1001
0
      union
1002
0
      {
1003
0
        WCHAR* w;
1004
0
        const WCHAR* cw;
1005
0
      } cnv;
1006
0
      cnv.cw = p;
1007
0
      return cnv.w;
1008
0
    }
1009
0
    p++;
1010
0
  }
1011
0
  return nullptr;
1012
0
}
1013
1014
WCHAR* winpr_wcsnrchr(const WCHAR* str, size_t charLen, WCHAR c)
1015
0
{
1016
0
  if (!str || (charLen == 0))
1017
0
    return nullptr;
1018
1019
0
  const WCHAR* p = str;
1020
0
  const WCHAR* end = &str[charLen];
1021
1022
0
  union
1023
0
  {
1024
0
    WCHAR* w;
1025
0
    const WCHAR* cw;
1026
0
  } cnv;
1027
1028
0
  cnv.cw = nullptr;
1029
0
  while (p != end)
1030
0
  {
1031
0
    const WCHAR wc = *p;
1032
0
    if (wc == c)
1033
0
      cnv.cw = p;
1034
0
    p++;
1035
0
  }
1036
0
  return cnv.w;
1037
0
}
1038
1039
WINPR_ATTR_NODISCARD
1040
static BOOL validHostChar(char c)
1041
0
{
1042
0
  if ((c >= 'a') && (c <= 'z'))
1043
0
    return TRUE;
1044
0
  if ((c >= 'A') && (c <= 'Z'))
1045
0
    return TRUE;
1046
0
  if ((c >= '0') && (c <= '9'))
1047
0
    return TRUE;
1048
1049
0
  switch (c)
1050
0
  {
1051
0
    case '-':
1052
0
    case '.':
1053
0
      return TRUE;
1054
0
    default:
1055
0
      return FALSE;
1056
0
  }
1057
0
}
1058
1059
BOOL winpr_str_is_valid_hostname(const char* str)
1060
0
{
1061
0
  if (!str)
1062
0
    return FALSE;
1063
  /* do a limited strnlen with DNS max length + 1, so we can abort early.
1064
   */
1065
0
  const size_t len = strnlen(str, 256);
1066
0
  return winpr_str_is_valid_hostnameN(str, len);
1067
0
}
1068
1069
BOOL winpr_str_is_valid_hostnameN(const char* str, size_t len)
1070
0
{
1071
0
  if (!str || (len == 0))
1072
0
  {
1073
0
    WLog_VRB(TAG, "hostname %s [%" PRIuz "] is too short", str, len);
1074
0
    return FALSE;
1075
0
  }
1076
1077
0
  if (len > 255)
1078
0
  {
1079
0
    WLog_VRB(TAG, "hostname %s [%" PRIuz "] is too long", str, len);
1080
0
    return FALSE;
1081
0
  }
1082
1083
  // may not start or end with dot
1084
0
  if (str[0] == '.')
1085
0
  {
1086
0
    WLog_VRB(TAG, "hostname %s [%" PRIuz "] starts with a dot", str, len);
1087
0
    return FALSE;
1088
0
  }
1089
0
  if (str[len - 1] == '.')
1090
0
  {
1091
0
    WLog_VRB(TAG, "hostname %s [%" PRIuz "] starts ends a dot", str, len);
1092
0
    return FALSE;
1093
0
  }
1094
1095
0
  size_t segmentCount = 1;
1096
0
  size_t segment = 0;
1097
0
  for (size_t x = 0; x < len; x++)
1098
0
  {
1099
0
    char c = str[x];
1100
0
    if (!validHostChar(c))
1101
0
    {
1102
0
      WLog_VRB(TAG, "hostname %s [%" PRIuz "] invalid character %c at %" PRIuz, str, len, c,
1103
0
               x);
1104
0
      return FALSE;
1105
0
    }
1106
1107
0
    if (c == '.')
1108
0
    {
1109
0
      segmentCount++;
1110
0
      segment = 0;
1111
1112
      // may not start with dot
1113
0
      if (x == 0)
1114
0
      {
1115
0
        WLog_VRB(TAG, "hostname %s [%" PRIuz "] segment starts with a dot", str, len);
1116
0
        return FALSE;
1117
0
      }
1118
      // filter dotdot
1119
0
      else if (str[x - 1] == c)
1120
0
      {
1121
0
        WLog_VRB(TAG, "hostname %s [%" PRIuz "] segment contains dotdot", str, len);
1122
0
        return FALSE;
1123
0
      }
1124
      // segments may not end with a hyphen
1125
0
      else if (str[x - 1] == '-')
1126
0
      {
1127
0
        WLog_VRB(TAG, "hostname %s [%" PRIuz "] segment ends with a hyphen", str, len);
1128
0
        return FALSE;
1129
0
      }
1130
0
    }
1131
0
    else
1132
0
    {
1133
0
      segment++;
1134
0
      if (segment > 63)
1135
0
      {
1136
0
        WLog_VRB(TAG, "hostname %s [%" PRIuz "] segment longer than 63 characters", str,
1137
0
                 len);
1138
0
        return FALSE;
1139
0
      }
1140
1141
      // - may not occur as first in segment
1142
0
      if (c == '-')
1143
0
      {
1144
        // segments may not start with a hyphen
1145
0
        if ((x == 0) || (str[x - 1] == '.'))
1146
0
        {
1147
0
          WLog_VRB(TAG, "hostname %s [%" PRIuz "] segment starts with a hyphen", str,
1148
0
                   len);
1149
0
          return FALSE;
1150
0
        }
1151
0
      }
1152
0
    }
1153
0
  }
1154
1155
  // segments may not end with a hyphen
1156
0
  if (str[len - 1] == '-')
1157
0
  {
1158
0
    WLog_VRB(TAG, "hostname %s [%" PRIuz "] segment ends with a hyphen", str, len);
1159
0
    return FALSE;
1160
0
  }
1161
1162
  // last segment may not contain a hyphen
1163
0
  if (segmentCount > 1)
1164
0
  {
1165
0
    if (segment < 2)
1166
0
    {
1167
0
      WLog_VRB(TAG, "hostname %s [%" PRIuz "] TLD segment too short", str, len);
1168
0
      return FALSE;
1169
0
    }
1170
0
  }
1171
1172
0
  return TRUE;
1173
0
}