Coverage Report

Created: 2025-11-02 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/proftpd/src/str.c
Line
Count
Source
1
/*
2
 * ProFTPD - FTP server daemon
3
 * Copyright (c) 2008-2025 The ProFTPD Project team
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, Suite 500, Boston, MA 02110-1335, USA.
18
 *
19
 * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu
20
 * and other respective copyright holders give permission to link this program
21
 * with OpenSSL, and distribute the resulting executable, without including
22
 * the source code for OpenSSL in the source distribution.
23
 */
24
25
/* String manipulation functions. */
26
27
#include "conf.h"
28
29
/* Maximum number of matches that we will do in a given string. */
30
0
#define PR_STR_MAX_MATCHES      128
31
32
static const char *str_vreplace(pool *p, unsigned int max_replaces,
33
0
    const char *s, va_list args) {
34
0
  const char *src;
35
0
  char *m, *r, *cp;
36
0
  char *matches[PR_STR_MAX_MATCHES+1], *replaces[PR_STR_MAX_MATCHES+1];
37
0
  char buf[PR_TUNABLE_PATH_MAX] = {'\0'}, *pbuf = NULL;
38
0
  size_t nmatches = 0, rlen = 0;
39
0
  int blen = 0;
40
41
0
  src = s;
42
0
  cp = buf;
43
0
  *cp = '\0';
44
45
0
  memset(matches, 0, sizeof(matches));
46
0
  memset(replaces, 0, sizeof(replaces));
47
48
0
  blen = strlen(src) + 1;
49
50
0
  while ((m = va_arg(args, char *)) != NULL &&
51
0
         nmatches < PR_STR_MAX_MATCHES) {
52
0
    char *tmp = NULL;
53
0
    unsigned int count = 0;
54
55
0
    r = va_arg(args, char *);
56
0
    if (r == NULL) {
57
0
      break;
58
0
    }
59
60
    /* Increase the length of the needed buffer by the difference between
61
     * the given match and replacement strings, multiplied by the number
62
     * of times the match string occurs in the source string.
63
     */
64
0
    tmp = strstr(s, m);
65
0
    while (tmp) {
66
0
      pr_signals_handle();
67
0
      count++;
68
0
      if (count > max_replaces) {
69
0
        errno = E2BIG;
70
0
        return NULL;
71
0
      }
72
73
      /* Be sure to increment the pointer returned by strstr(3), to
74
       * advance past the beginning of the substring for which we are
75
       * looking.  Otherwise, we just loop endlessly, seeing the same
76
       * value for tmp over and over.
77
       */
78
0
      tmp += strlen(m);
79
0
      tmp = strstr(tmp, m);
80
0
    }
81
82
    /* We are only concerned about match/replacement strings that actually
83
     * occur in the given string.
84
     */
85
0
    if (count) {
86
0
      blen += count * (strlen(r) - strlen(m));
87
0
      if (blen < 0) {
88
        /* Integer overflow. In order to overflow this, somebody must be
89
         * doing something very strange. The possibility still exists that
90
         * we might not catch this overflow in extreme corner cases, but
91
         * massive amounts of data (gigabytes) would need to be in s to
92
         * trigger this, easily larger than any buffer we might use.
93
         */
94
0
        return s;
95
0
      }
96
0
      matches[nmatches] = m;
97
0
      replaces[nmatches++] = r;
98
0
    }
99
0
  }
100
101
  /* If there are no matches, then there is nothing to replace. */
102
0
  if (nmatches == 0) {
103
0
    return s;
104
0
  }
105
106
  /* Try to handle large buffer situations (i.e. escaping of PR_TUNABLE_PATH_MAX
107
   * (>2048) correctly, but do not allow very big buffer sizes, that may
108
   * be dangerous (BUFSIZ may be defined in stdio.h) in some library
109
   * functions.
110
   */
111
#ifndef BUFSIZ
112
# define BUFSIZ 8192
113
#endif
114
115
0
  if (blen >= BUFSIZ) {
116
0
    errno = ENOSPC;
117
0
    return NULL;
118
0
  }
119
120
0
  cp = pbuf = (char *) pcalloc(p, ++blen);
121
122
0
  while (*src) {
123
0
    char **mptr, **rptr;
124
125
0
    for (mptr = matches, rptr = replaces; *mptr; mptr++, rptr++) {
126
0
      size_t mlen;
127
128
0
      pr_signals_handle();
129
130
0
      mlen = strlen(*mptr);
131
0
      rlen = strlen(*rptr);
132
133
0
      if (strncmp(src, *mptr, mlen) == 0) {
134
0
        sstrncpy(cp, *rptr, blen - strlen(pbuf));
135
136
0
        if (((cp + rlen) - pbuf + 1) > blen) {
137
0
          pr_log_pri(PR_LOG_ERR,
138
0
            "WARNING: attempt to overflow internal ProFTPD buffers");
139
0
          cp = pbuf;
140
141
0
          cp += (blen - 1);
142
0
          goto done;
143
144
0
        } else {
145
0
          cp += rlen;
146
0
        }
147
  
148
0
        src += mlen;
149
0
        break;
150
0
      }
151
0
    }
152
153
0
    if (!*mptr) {
154
0
      if ((cp - pbuf + 1) >= blen) {
155
0
        pr_log_pri(PR_LOG_ERR,
156
0
          "WARNING: attempt to overflow internal ProFTPD buffers");
157
0
        cp = pbuf;
158
159
0
        cp += (blen - 1);
160
0
        goto done;
161
0
      }
162
163
0
      *cp++ = *src++;
164
0
    }
165
0
  }
166
167
0
 done:
168
0
  *cp = '\0';
169
170
0
  return pbuf;
171
0
}
172
173
0
const char *pr_str_quote(pool *p, const char *str) {
174
0
  if (p == NULL ||
175
0
      str == NULL) {
176
0
    errno = EINVAL;
177
0
    return NULL;
178
0
  }
179
180
0
  return sreplace(p, str, "\"", "\"\"", NULL);
181
0
}
182
183
0
const char *quote_dir(pool *p, char *path) {
184
0
  return pr_str_quote(p, path);
185
0
}
186
187
const char *pr_str_replace(pool *p, unsigned int max_replaces,
188
0
    const char *s, ...) {
189
0
  va_list args;
190
0
  const char *res = NULL;
191
192
0
  if (p == NULL ||
193
0
      s == NULL ||
194
0
      max_replaces == 0) {
195
0
    errno = EINVAL;
196
0
    return NULL;
197
0
  }
198
199
0
  va_start(args, s);
200
0
  res = str_vreplace(p, max_replaces, s, args);
201
0
  va_end(args);
202
203
0
  return res;
204
0
}
205
206
0
const char *sreplace(pool *p, const char *s, ...) {
207
0
  va_list args;
208
0
  const char *res = NULL;
209
210
0
  if (p == NULL ||
211
0
      s == NULL) {
212
0
    errno = EINVAL;
213
0
    return NULL;
214
0
  }
215
216
0
  va_start(args, s);
217
0
  res = str_vreplace(p, PR_STR_MAX_REPLACEMENTS, s, args);
218
0
  va_end(args);
219
220
0
  if (res == NULL &&
221
0
      errno == E2BIG) {
222
    /* For backward compatible behavior. */
223
0
    return s;
224
0
  }
225
226
0
  return res;
227
0
}
228
229
/* "safe" strcat, saves room for NUL at end of dst, and refuses to copy more
230
 * than "n" bytes.
231
 */
232
0
char *sstrcat(char *dst, const char *src, size_t n) {
233
0
  register char *d = dst;
234
235
0
  if (dst == NULL ||
236
0
      src == NULL ||
237
0
      n == 0) {
238
0
    errno = EINVAL;
239
0
    return NULL;
240
0
  }
241
242
  /* Edge case short circuit; strlcat(3) doesn't do what I think it should
243
   * do for this particular case.
244
   */
245
0
  if (n > 1) {
246
#if defined(HAVE_STRLCAT)
247
    strlcat(dst, src, n);
248
249
#else
250
0
    for (; *d && n > 1; d++, n--) {
251
0
    }
252
253
0
    while (n-- > 1 && *src) {
254
0
      *d++ = *src++;
255
0
    }
256
257
0
    *d = '\0';
258
0
#endif /* HAVE_STRLCAT */
259
260
0
  } else {
261
0
    *d = '\0';
262
0
  }
263
264
0
  return dst;
265
0
}
266
267
0
char *pstrdup(pool *p, const char *str) {
268
0
  char *res;
269
0
  size_t len;
270
271
0
  if (p == NULL ||
272
0
      str == NULL) {
273
0
    errno = EINVAL;
274
0
    return NULL;
275
0
  }
276
277
0
  len = strlen(str) + 1;
278
279
0
  res = palloc(p, len);
280
0
  if (res != NULL) {
281
0
    sstrncpy(res, str, len);
282
0
  }
283
284
0
  return res;
285
0
}
286
287
0
char *pstrndup(pool *p, const char *str, size_t n) {
288
0
  char *res;
289
290
0
  if (!p || !str) {
291
0
    errno = EINVAL;
292
0
    return NULL;
293
0
  }
294
295
0
  res = palloc(p, n + 1);
296
0
  sstrncpy(res, str, n + 1);
297
0
  return res;
298
0
}
299
300
0
char *pdircat(pool *p, ...) {
301
0
  char *argp, *ptr, *res;
302
0
  char last;
303
0
  int count = 0;
304
0
  size_t len = 0, res_len = 0;
305
0
  va_list ap;
306
307
0
  if (p == NULL) {
308
0
    errno = EINVAL;
309
0
    return NULL;
310
0
  }
311
312
0
  va_start(ap, p);
313
314
0
  last = 0;
315
316
0
  while ((res = va_arg(ap, char *)) != NULL) {
317
    /* If the first argument is "", we have to account for a leading /
318
     * which must be added.
319
     */
320
0
    if (!count++ && !*res) {
321
0
      len++;
322
323
0
    } else if (last && last != '/' && *res != '/') {
324
0
      len++;
325
326
0
    } else if (last && last == '/' && *res == '/') {
327
0
      len--;
328
0
    }
329
330
0
    res_len = strlen(res);
331
0
    len += res_len;
332
0
    last = (*res ? res[res_len-1] : 0);
333
0
  }
334
335
0
  va_end(ap);
336
0
  ptr = res = (char *) pcalloc(p, len + 1);
337
338
0
  va_start(ap, p);
339
340
0
  last = res_len = 0;
341
342
0
  while ((argp = va_arg(ap, char *)) != NULL) {
343
0
    size_t arglen;
344
345
0
    if (last && last == '/' && *argp == '/') {
346
0
      argp++;
347
348
0
    } else if (last && last != '/' && *argp != '/') {
349
0
      sstrcat(ptr, "/", len + 1);
350
0
      ptr += 1;
351
0
      res_len += 1;
352
0
    }
353
354
0
    arglen = strlen(argp);
355
0
    sstrcat(ptr, argp, len + 1);
356
0
    ptr += arglen;
357
0
    res_len += arglen;
358
359
0
    last = (*res ? res[res_len-1] : 0);
360
0
  }
361
362
0
  va_end(ap);
363
364
0
  return res;
365
0
}
366
367
0
char *pstrcat(pool *p, ...) {
368
0
  char *argp, *ptr, *res;
369
0
  size_t len = 0;
370
0
  va_list ap;
371
372
0
  if (p == NULL) {
373
0
    errno = EINVAL;
374
0
    return NULL;
375
0
  }
376
377
0
  va_start(ap, p);
378
379
0
  while ((res = va_arg(ap, char *)) != NULL) {
380
0
    len += strlen(res);
381
0
  }
382
383
0
  va_end(ap);
384
385
0
  ptr = res = pcalloc(p, len + 1);
386
387
0
  va_start(ap, p);
388
389
0
  while ((argp = va_arg(ap, char *)) != NULL) {
390
0
    size_t arglen;
391
392
0
    arglen = strlen(argp);
393
0
    sstrcat(ptr, argp, len + 1);
394
0
    ptr += arglen;
395
0
  }
396
397
0
  va_end(ap);
398
399
0
  return res;
400
0
}
401
402
int pr_strnrstr(const char *s, size_t slen, const char *suffix,
403
0
    size_t suffixlen, int flags) {
404
0
  int res = FALSE;
405
406
0
  if (s == NULL ||
407
0
      suffix == NULL) {
408
0
    errno = EINVAL;
409
0
    return -1;
410
0
  }
411
412
0
  if (slen == 0) {
413
0
    slen = strlen(s);
414
0
  }
415
416
0
  if (suffixlen == 0) {
417
0
    suffixlen = strlen(suffix);
418
0
  }
419
420
0
  if (slen == 0 &&
421
0
      suffixlen == 0) {
422
0
    return TRUE;
423
0
  }
424
425
0
  if (slen == 0 ||
426
0
      suffixlen == 0) {
427
0
    return FALSE;
428
0
  }
429
430
0
  if (suffixlen > slen) {
431
0
    return FALSE;
432
0
  }
433
434
0
  if (flags & PR_STR_FL_IGNORE_CASE) {
435
0
    if (strncasecmp(s + (slen - suffixlen), suffix, suffixlen) == 0) {
436
0
      res = TRUE;
437
0
    }
438
439
0
  } else {
440
0
    if (strncmp(s + (slen - suffixlen), suffix, suffixlen) == 0) {
441
0
      res = TRUE;
442
0
    }
443
0
  }
444
445
0
  return res;
446
0
}
447
448
0
const char *pr_str_strip(pool *p, const char *str) {
449
0
  const char *dup_str, *start, *finish;
450
0
  size_t len = 0;
451
452
0
  if (p == NULL ||
453
0
      str == NULL) {
454
0
    errno = EINVAL;
455
0
    return NULL;
456
0
  }
457
458
  /* First, find the non-whitespace start of the given string */
459
0
  for (start = str; PR_ISSPACE(*start); start++) {
460
0
  }
461
462
  /* Now, find the non-whitespace end of the given string */
463
0
  for (finish = &str[strlen(str)-1]; PR_ISSPACE(*finish); finish--) {
464
0
  }
465
466
  /* Include for the last byte, of course. */
467
0
  len = finish - start + 1;
468
469
  /* The space-stripped string is, then, everything from start to finish. */
470
0
  dup_str = pstrndup(p, start, len);
471
472
0
  return dup_str;
473
0
}
474
475
0
char *pr_str_strip_end(char *s, const char *ch) {
476
0
  size_t len;
477
478
0
  if (s == NULL ||
479
0
      ch == NULL) {
480
0
    errno = EINVAL;
481
0
    return NULL;
482
0
  }
483
484
0
  len = strlen(s);
485
486
0
  while (len && strchr(ch, *(s+len - 1))) {
487
0
    pr_signals_handle();
488
489
0
    *(s+len - 1) = '\0';
490
0
    len--;
491
0
  }
492
493
0
  return s;
494
0
}
495
496
#if defined(HAVE_STRTOULL) && \
497
  (SIZEOF_UID_T == SIZEOF_LONG_LONG && SIZEOF_GID_T == SIZEOF_LONG_LONG)
498
static int parse_ull(const char *val, unsigned long long *num) {
499
  char *endp = NULL;
500
  unsigned long long res;
501
502
  res = strtoull(val, &endp, 10);
503
  if (endp && *endp) {
504
    errno = EINVAL;
505
    return -1;
506
  }
507
508
  *num = res;
509
  return 0;
510
}
511
#endif /* HAVE_STRTOULL */
512
513
0
static int parse_ul(const char *val, unsigned long *num) {
514
0
  char *endp = NULL;
515
0
  unsigned long res;
516
517
0
  res = strtoul(val, &endp, 10);
518
0
  if (endp && *endp) {
519
0
    errno = EINVAL;
520
0
    return -1;
521
0
  }
522
523
0
  *num = res;
524
0
  return 0;
525
0
}
526
527
0
char *pr_str_bin2hex(pool *p, const unsigned char *buf, size_t len, int flags) {
528
0
  static const char *hex_lc = "0123456789abcdef", *hex_uc = "0123456789ABCDEF";
529
0
  register unsigned int i;
530
0
  const char *hex_vals;
531
0
  char *hex, *ptr;
532
0
  size_t hex_len;
533
534
0
  if (p == NULL ||
535
0
      buf == NULL) {
536
0
    errno = EINVAL;
537
0
    return NULL;
538
0
  }
539
540
0
  if (len == 0) {
541
0
    return pstrdup(p, "");
542
0
  }
543
544
  /* By default, we use lowercase hex values. */
545
0
  hex_vals = hex_lc;
546
0
  if (flags & PR_STR_FL_HEX_USE_UC) {
547
0
    hex_vals = hex_uc;
548
0
  }
549
550
0
  hex_len = (len * 2) + 1;
551
0
  hex = palloc(p, hex_len);
552
553
0
  ptr = hex;
554
0
  for (i = 0; i < len; i++) {
555
0
    *ptr++ = hex_vals[buf[i] >> 4];
556
0
    *ptr++ = hex_vals[buf[i] % 16];
557
0
  }
558
0
  *ptr = '\0';
559
560
0
  return hex;
561
0
}
562
563
0
static int c2h(char c, unsigned char *h) {
564
0
  if (c >= '0' &&
565
0
      c <= '9') {
566
0
    *h = c - '0';
567
0
    return TRUE;
568
0
  }
569
570
0
  if (c >= 'a' &&
571
0
      c <= 'f') {
572
0
    *h = c - 'a' + 10;
573
0
    return TRUE;
574
0
  }
575
576
0
  if (c >= 'A' &&
577
0
      c <= 'F') {
578
0
    *h = c - 'A' + 10;
579
0
    return TRUE;
580
0
  }
581
582
0
  return FALSE;
583
0
}
584
585
unsigned char *pr_str_hex2bin(pool *p, const unsigned char *hex, size_t hex_len,
586
0
    size_t *len) {
587
0
  register unsigned int i, j;
588
0
  unsigned char *data;
589
0
  size_t data_len;
590
591
0
  if (p == NULL ||
592
0
      hex == NULL) {
593
0
    errno = EINVAL;
594
0
    return NULL;
595
0
  }
596
597
0
  if (hex_len == 0) {
598
0
    hex_len = strlen((char *) hex);
599
0
  }
600
601
0
  if (hex_len == 0) {
602
0
    data = (unsigned char *) pstrdup(p, "");
603
0
    return data;
604
0
  }
605
606
0
  data_len = hex_len / 2;
607
0
  data = palloc(p, data_len);
608
609
0
  for (i = 0, j = 0; i < hex_len; i += 2) {
610
0
    unsigned char v1, v2;
611
612
0
    if (c2h(hex[i], &v1) == FALSE) {
613
0
      errno = ERANGE;
614
0
      return NULL;
615
0
    }
616
617
0
    if (c2h(hex[i+1], &v2) == FALSE) {
618
0
      errno = ERANGE;
619
0
      return NULL;
620
0
    }
621
622
0
    data[j++] = ((v1 << 4) | v2);
623
0
  }
624
625
0
  if (len != NULL) {
626
0
    *len = data_len;
627
0
  }
628
629
0
  return data;
630
0
}
631
632
/* Calculate the Damerau-Levenshtein distance between strings `a' and `b'.
633
 * This implementation borrows from the git implementation; see
634
 * git/src/levenshtein.c.
635
 */
636
int pr_str_levenshtein(pool *p, const char *a, const char *b, int swap_cost,
637
0
    int subst_cost, int insert_cost, int del_cost, int flags) {
638
0
  size_t alen, blen;
639
0
  unsigned int i, j;
640
0
  int *row0, *row1, *row2, res;
641
0
  pool *tmp_pool;
642
643
0
  if (p == NULL ||
644
0
      a == NULL ||
645
0
      b == NULL) {
646
0
    errno = EINVAL;
647
0
    return -1;
648
0
  }
649
650
0
  alen = strlen(a);
651
0
  blen = strlen(b);
652
653
0
  tmp_pool = make_sub_pool(p);
654
0
  pr_pool_tag(tmp_pool, "Levenshtein Distance pool");
655
656
0
  if (flags & PR_STR_FL_IGNORE_CASE) {
657
0
    char *a2, *b2;
658
659
0
    a2 = pstrdup(tmp_pool, a);
660
0
    for (i = 0; i < alen; i++) {
661
0
      a2[i] = tolower((int) a[i]);
662
0
    }
663
664
0
    b2 = pstrdup(tmp_pool, b);
665
0
    for (i = 0; i < blen; i++) {
666
0
      b2[i] = tolower((int) b[i]);
667
0
    }
668
669
0
    a = a2;
670
0
    b = b2;
671
0
  }
672
673
0
  row0 = pcalloc(tmp_pool, sizeof(int) * (blen + 1));
674
0
  row1 = pcalloc(tmp_pool, sizeof(int) * (blen + 1));
675
0
  row2 = pcalloc(tmp_pool, sizeof(int) * (blen + 1));
676
677
0
  for (j = 0; j <= blen; j++) {
678
0
    row1[j] = j * insert_cost;
679
0
  }
680
681
0
  for (i = 0; i < alen; i++) {
682
0
    int *ptr;
683
684
0
    row2[0] = (i + 1) * del_cost;
685
0
    for (j = 0; j < blen; j++) {
686
      /* Substitution */
687
0
      row2[j + 1] = row1[j] + (subst_cost * (a[i] != b[j]));
688
689
      /* Swap */
690
0
      if (i > 0 &&
691
0
          j > 0 &&
692
0
          a[i-1] == b[j] &&
693
0
          a[i] == b[j-1] &&
694
0
          row2[j+1] > (row0[j-1] + swap_cost)) {
695
0
        row2[j+1] = row0[j-1] + swap_cost;
696
0
      }
697
698
      /* Deletion */
699
0
      if (row2[j+1] > (row1[j+1] + del_cost)) {
700
0
        row2[j+1] = row1[j+1] + del_cost;
701
0
      }
702
703
      /* Insertion */
704
0
      if (row2[j+1] > (row2[j] + insert_cost)) {
705
0
        row2[j+1] = row2[j] + insert_cost;
706
0
      }
707
0
    }
708
709
0
    ptr = row0;
710
0
    row0 = row1;
711
0
    row1 = row2;
712
0
    row2 = ptr;
713
0
  }
714
715
0
  res = row2[blen];
716
717
0
  destroy_pool(tmp_pool);
718
0
  return res;
719
0
}
720
721
/* For tracking the Levenshtein distance for a string. */
722
struct candidate {
723
  const char *s;
724
  int distance;
725
  int flags;
726
};
727
728
0
static int distance_cmp(const void *a, const void *b) {
729
0
  const struct candidate *cand1, *cand2;
730
0
  const char *s1, *s2;
731
0
  int distance1, distance2;
732
733
0
  cand1 = *((const struct candidate **) a);
734
0
  s1 = cand1->s;
735
0
  distance1 = cand1->distance;
736
737
0
  cand2 = *((const struct candidate **) b);
738
0
  s2 = cand2->s;
739
0
  distance2 = cand2->distance;
740
741
0
  if (distance1 != distance2) {
742
0
    return distance1 - distance2;
743
0
  }
744
745
0
  if (cand1->flags & PR_STR_FL_IGNORE_CASE) {
746
0
    return strcasecmp(s1, s2);
747
0
  }
748
749
0
  return strcmp(s1, s2);
750
0
}
751
752
array_header *pr_str_get_similars(pool *p, const char *s,
753
0
    array_header *candidates, int max_distance, int flags) {
754
0
  register unsigned int i;
755
0
  size_t len;
756
0
  array_header *similars;
757
0
  struct candidate **distances;
758
0
  pool *tmp_pool;
759
760
0
  if (p == NULL ||
761
0
      s == NULL ||
762
0
      candidates == NULL) {
763
0
    errno = EINVAL;
764
0
    return NULL;
765
0
  }
766
767
0
  if (candidates->nelts == 0) {
768
0
    errno = ENOENT;
769
0
    return NULL;
770
0
  }
771
772
0
  if (max_distance <= 0) {
773
0
    max_distance = PR_STR_DEFAULT_MAX_EDIT_DISTANCE;
774
0
  }
775
776
0
  tmp_pool = make_sub_pool(p);
777
0
  pr_pool_tag(tmp_pool, "Similar Strings pool");
778
779
  /* In order to use qsort(3), we need a contiguous block of memory, not
780
   * one of our array_headers.
781
   */
782
783
0
  distances = pcalloc(tmp_pool, candidates->nelts * sizeof(struct candidate *));
784
785
0
  len = strlen(s);
786
0
  for (i = 0; i < candidates->nelts; i++) {
787
0
    const char *c;
788
0
    struct candidate *cand;
789
0
    int prefix_match = FALSE;
790
791
0
    c = ((const char **) candidates->elts)[i];
792
0
    cand = pcalloc(tmp_pool, sizeof(struct candidate));
793
0
    cand->s = c;
794
0
    cand->flags = flags;
795
796
    /* Give prefix matches a higher score */
797
0
    if (flags & PR_STR_FL_IGNORE_CASE) {
798
0
      if (strncasecmp(c, s, len) == 0) {
799
0
        prefix_match = TRUE;
800
0
      }
801
802
0
    } else {
803
0
      if (strncmp(c, s, len) == 0) {
804
0
        prefix_match = TRUE;
805
0
      }
806
0
    }
807
808
0
    if (prefix_match == TRUE) {
809
0
      cand->distance = 0;
810
811
0
    } else {
812
      /* Note: We arbitrarily add one to the edit distance, in order to
813
       * distinguish a distance of zero from our prefix match "distances" of
814
       * zero above.
815
       */
816
0
      cand->distance = pr_str_levenshtein(tmp_pool, s, c, 0, 2, 1, 3,
817
0
        flags) + 1;
818
0
    }
819
820
0
    distances[i] = cand;
821
0
  }
822
823
0
  qsort(distances, candidates->nelts, sizeof(struct candidate *), distance_cmp);
824
825
0
  similars = make_array(p, candidates->nelts, sizeof(const char *));
826
0
  for (i = 0; i < candidates->nelts; i++) {
827
0
    struct candidate *cand;
828
829
0
    cand = distances[i];
830
0
    if (cand->distance <= max_distance) {
831
0
      *((const char **) push_array(similars)) = cand->s;
832
0
    }
833
0
  }
834
835
0
  destroy_pool(tmp_pool);
836
0
  return similars;
837
0
}
838
839
0
array_header *pr_str_text_to_array(pool *p, const char *text, char delimiter) {
840
0
  char *ptr;
841
0
  array_header *items;
842
0
  size_t text_len;
843
844
0
  if (p == NULL ||
845
0
      text == NULL) {
846
0
    errno = EINVAL;
847
0
    return NULL;
848
0
  }
849
850
0
  text_len = strlen(text);
851
0
  items = make_array(p, 1, sizeof(char *));
852
853
0
  if (text_len == 0) {
854
0
    return items;
855
0
  }
856
857
0
  ptr = memchr(text, delimiter, text_len);
858
0
  while (ptr != NULL) {
859
0
    size_t item_len;
860
861
0
    pr_signals_handle();
862
863
0
    item_len = ptr - text;
864
0
    if (item_len > 0) {
865
0
      char *item;
866
867
0
      item = palloc(p, item_len + 1);
868
0
      memcpy(item, text, item_len);
869
0
      item[item_len] = '\0';
870
0
      *((char **) push_array(items)) = item;
871
0
    }
872
873
0
    text = ++ptr;
874
875
    /* Include one byte for the delimiter character being skipped over. */
876
0
    text_len = text_len - item_len - 1;
877
878
0
    if (text_len == 0) {
879
0
      break;
880
0
    }
881
882
0
    ptr = memchr(text, delimiter, text_len);
883
0
  }
884
885
0
  if (text_len > 0) {
886
0
    *((char **) push_array(items)) = pstrdup(p, text);
887
0
  }
888
889
0
  return items;
890
0
}
891
892
char *pr_str_array_to_text(pool *p, const array_header *items,
893
0
    const char *delimiter) {
894
0
  register unsigned int i;
895
0
  char **elts, *text = "";
896
897
0
  if (p == NULL ||
898
0
      items == NULL ||
899
0
      delimiter == NULL) {
900
0
    errno = EINVAL;
901
0
    return NULL;
902
0
  }
903
904
0
  if (items->nelts == 0) {
905
0
    return pstrdup(p, "");
906
0
  }
907
908
0
  elts = items->elts;
909
0
  for (i = 0; i < items->nelts; i++) {
910
0
    char *elt;
911
912
0
    elt = elts[i];
913
0
    text = pstrcat(p, text, *text ? delimiter : "", elt, NULL);
914
0
  }
915
916
0
  return text;
917
0
}
918
919
0
int pr_str2uid(const char *val, uid_t *uid) {
920
0
#ifdef HAVE_STRTOULL
921
0
  unsigned long long ull = 0ULL;
922
0
#endif /* HAVE_STRTOULL */
923
0
  unsigned long ul = 0UL;
924
925
0
  if (val == NULL ||
926
0
      uid == NULL) {
927
0
    errno = EINVAL;
928
0
    return -1;
929
0
  }
930
931
#if SIZEOF_UID_T == SIZEOF_LONG_LONG
932
# ifdef HAVE_STRTOULL
933
  if (parse_ull(val, &ull) < 0) {
934
    return -1;
935
  }
936
  *uid = ull;
937
938
# else
939
  if (parse_ul(val, &ul) < 0) {
940
    return -1;
941
  }
942
  *uid = ul;
943
# endif /* HAVE_STRTOULL */
944
#else
945
0
  (void) ull;
946
0
  if (parse_ul(val, &ul) < 0) {
947
0
    return -1;
948
0
  }
949
0
  *uid = ul;
950
0
#endif /* sizeof(uid_t) != sizeof(long long) */
951
952
0
  return 0;
953
0
}
954
955
0
int pr_str2gid(const char *val, gid_t *gid) {
956
0
#ifdef HAVE_STRTOULL
957
0
  unsigned long long ull = 0ULL;
958
0
#endif /* HAVE_STRTOULL */
959
0
  unsigned long ul = 0UL;
960
961
0
  if (val == NULL ||
962
0
      gid == NULL) {
963
0
    errno = EINVAL;
964
0
    return -1;
965
0
  }
966
967
#if SIZEOF_GID_T == SIZEOF_LONG_LONG
968
# ifdef HAVE_STRTOULL
969
  if (parse_ull(val, &ull) < 0) {
970
    return -1;
971
  }
972
  *gid = ull;
973
974
# else
975
  if (parse_ul(val, &ul) < 0) {
976
    return -1;
977
  }
978
  *gid = ul;
979
# endif /* HAVE_STRTOULL */
980
#else
981
0
  (void) ull;
982
0
  if (parse_ul(val, &ul) < 0) {
983
0
    return -1;
984
0
  }
985
0
  *gid = ul;
986
0
#endif /* sizeof(gid_t) != sizeof(long long) */
987
988
0
  return 0;
989
0
}
990
991
0
const char *pr_uid2str(pool *p, uid_t uid) {
992
0
  static char buf[64];
993
994
0
  memset(&buf, 0, sizeof(buf));
995
0
  if (uid != (uid_t) -1) {
996
#if SIZEOF_UID_T == SIZEOF_LONG_LONG
997
    pr_snprintf(buf, sizeof(buf)-1, "%llu", (unsigned long long) uid);
998
#else
999
0
    pr_snprintf(buf, sizeof(buf)-1, "%lu", (unsigned long) uid);
1000
0
#endif /* sizeof(uid_t) != sizeof(long long) */
1001
0
  } else {
1002
0
    pr_snprintf(buf, sizeof(buf)-1, "%d", -1);
1003
0
  }
1004
1005
0
  if (p != NULL) {
1006
0
    return pstrdup(p, buf);
1007
0
  }
1008
1009
0
  return buf;
1010
0
}
1011
1012
0
const char *pr_gid2str(pool *p, gid_t gid) {
1013
0
  static char buf[64];
1014
1015
0
  memset(&buf, 0, sizeof(buf));
1016
0
  if (gid != (gid_t) -1) {
1017
#if SIZEOF_GID_T == SIZEOF_LONG_LONG
1018
    pr_snprintf(buf, sizeof(buf)-1, "%llu", (unsigned long long) gid);
1019
#else
1020
0
    pr_snprintf(buf, sizeof(buf)-1, "%lu", (unsigned long) gid);
1021
0
#endif /* sizeof(gid_t) != sizeof(long long) */
1022
0
  } else {
1023
0
    pr_snprintf(buf, sizeof(buf)-1, "%d", -1);
1024
0
  }
1025
1026
0
  if (p != NULL) {
1027
0
    return pstrdup(p, buf);
1028
0
  }
1029
1030
0
  return buf;
1031
0
}
1032
1033
/* NOTE: Update mod_ban's ban_parse_timestr() to use this function. */
1034
0
int pr_str_get_duration(const char *str, int *duration) {
1035
0
  int hours, mins, secs;
1036
0
  int flags = PR_STR_FL_IGNORE_CASE, has_suffix = FALSE;
1037
0
  size_t len;
1038
0
  char *ptr = NULL;
1039
1040
0
  if (str == NULL) {
1041
0
    errno = EINVAL;
1042
0
    return -1;
1043
0
  }
1044
1045
0
  if (sscanf(str, "%2d:%2d:%2d", &hours, &mins, &secs) == 3) {
1046
0
    if (hours < 0 ||
1047
0
        mins < 0 ||
1048
0
        secs < 0) {
1049
0
      errno = ERANGE;
1050
0
      return -1;
1051
0
    }
1052
1053
0
    if (duration != NULL) {
1054
0
      *duration = (hours * 60 * 60) + (mins * 60) + secs;
1055
0
    }
1056
1057
0
    return 0;
1058
0
  }
1059
1060
0
  len = strlen(str);
1061
0
  if (len == 0) {
1062
0
    errno = EINVAL;
1063
0
    return -1;
1064
0
  }
1065
1066
  /* Handle the "single component" formats:
1067
   *
1068
   * If ends with "S", "s", or "sec": parse secs
1069
   * If ends with "M", "m", or "min": parse minutes
1070
   * If ends with "H", "h", or "hr": parse hours
1071
   *
1072
   * Otherwise, try to parse as just a number of seconds.
1073
   */
1074
1075
0
  has_suffix = pr_strnrstr(str, len, "s", 1, flags);
1076
0
  if (has_suffix == FALSE) {
1077
0
    has_suffix = pr_strnrstr(str, len, "sec", 3, flags);
1078
0
  }
1079
0
  if (has_suffix == TRUE) {
1080
    /* Parse seconds */
1081
1082
0
    if (sscanf(str, "%d", &secs) == 1) {
1083
0
      if (secs < 0) {
1084
0
        errno = ERANGE;
1085
0
        return -1;
1086
0
      }
1087
1088
0
      if (duration != NULL) {
1089
0
        *duration = secs;
1090
0
      }
1091
1092
0
      return 0;
1093
0
    }
1094
1095
0
    errno = EINVAL;
1096
0
    return -1;
1097
0
  }
1098
1099
0
  has_suffix = pr_strnrstr(str, len, "m", 1, flags);
1100
0
  if (has_suffix == FALSE) {
1101
0
    has_suffix = pr_strnrstr(str, len, "min", 3, flags);
1102
0
  }
1103
0
  if (has_suffix == TRUE) {
1104
    /* Parse minutes */
1105
1106
0
    if (sscanf(str, "%d", &mins) == 1) {
1107
0
      if (mins < 0) {
1108
0
        errno = ERANGE;
1109
0
        return -1;
1110
0
      }
1111
1112
0
      if (duration != NULL) {
1113
0
        *duration = (mins * 60);
1114
0
      }
1115
1116
0
      return 0;
1117
0
    }
1118
1119
0
    errno = EINVAL;
1120
0
    return -1;
1121
0
  }
1122
1123
0
  has_suffix = pr_strnrstr(str, len, "h", 1, flags);
1124
0
  if (has_suffix == FALSE) {
1125
0
    has_suffix = pr_strnrstr(str, len, "hr", 2, flags);
1126
0
  }
1127
0
  if (has_suffix == TRUE) {
1128
    /* Parse hours */
1129
1130
0
    if (sscanf(str, "%d", &hours) == 1) {
1131
0
      if (hours < 0) {
1132
0
        errno = ERANGE;
1133
0
        return -1;
1134
0
      }
1135
1136
0
      if (duration != NULL) {
1137
0
        *duration = (hours * 60 * 60);
1138
0
      }
1139
1140
0
      return 0;
1141
0
    }
1142
1143
0
    errno = EINVAL;
1144
0
    return -1;
1145
0
  }
1146
1147
  /* Use strtol(3) here, check for trailing garbage, etc. */
1148
0
  secs = (int) strtol(str, &ptr, 10);
1149
0
  if (ptr && *ptr) {
1150
    /* Not a bare number, but a string with non-numeric characters. */
1151
0
    errno = EINVAL;
1152
0
    return -1;
1153
0
  }
1154
1155
0
  if (secs < 0) {
1156
0
    errno = ERANGE;
1157
0
    return -1;
1158
0
  }
1159
1160
0
  if (duration != NULL) {
1161
0
    *duration = secs;
1162
0
  }
1163
1164
0
  return 0;
1165
0
}
1166
1167
0
int pr_str_get_nbytes(const char *str, const char *units, off_t *nbytes) {
1168
0
  off_t sz;
1169
0
  char *ptr = NULL;
1170
0
  float factor = 0.0;
1171
1172
0
  if (str == NULL) {
1173
0
    errno = EINVAL;
1174
0
    return -1;
1175
0
  }
1176
1177
  /* No negative numbers. */
1178
0
  if (*str == '-') {
1179
0
    errno = EINVAL;
1180
0
    return -1;
1181
0
  }
1182
1183
0
  if (units == NULL ||
1184
0
      *units == '\0') {
1185
0
    factor = 1.0;
1186
1187
0
  } else if (strncasecmp(units, "KB", 3) == 0) {
1188
0
    factor = 1024.0;
1189
1190
0
  } else if (strncasecmp(units, "MB", 3) == 0) {
1191
0
    factor = 1024.0 * 1024.0;
1192
1193
0
  } else if (strncasecmp(units, "GB", 3) == 0) {
1194
0
    factor = 1024.0 * 1024.0 * 1024.0;
1195
1196
0
  } else if (strncasecmp(units, "TB", 3) == 0) {
1197
0
    factor = 1024.0 * 1024.0 * 1024.0 * 1024.0;
1198
1199
0
  } else if (strncasecmp(units, "B", 2) == 0) {
1200
0
    factor = 1.0;
1201
1202
0
  } else {
1203
0
    errno = EINVAL;
1204
0
    return -1;
1205
0
  }
1206
1207
0
  errno = 0;
1208
1209
0
#ifdef HAVE_STRTOULL
1210
0
  sz = strtoull(str, &ptr, 10);
1211
#else
1212
  sz = strtoul(str, &ptr, 10);
1213
#endif /* !HAVE_STRTOULL */
1214
1215
0
  if (errno == ERANGE) {
1216
0
    return -1;
1217
0
  }
1218
1219
0
  if (ptr != NULL && *ptr) {
1220
    /* Error parsing the given string */
1221
0
    errno = EINVAL;
1222
0
    return -1;
1223
0
  }
1224
1225
  /* Don't bother applying the factor if the result will overflow the result. */
1226
0
#ifdef ULLONG_MAX
1227
0
  if (sz > (ULLONG_MAX / factor)) {
1228
#else
1229
  if (sz > (ULONG_MAX / factor)) {
1230
#endif /* !ULLONG_MAX */
1231
0
    errno = ERANGE;
1232
0
    return -1;
1233
0
  }
1234
1235
0
  if (nbytes != NULL) {
1236
0
    *nbytes = (off_t) (sz * factor);
1237
0
  }
1238
1239
0
  return 0;
1240
0
}
1241
1242
0
char *pr_str_get_word(char **cp, int flags) {
1243
0
  char *res, *dst;
1244
0
  int quote_mode = FALSE;
1245
1246
0
  if (cp == NULL ||
1247
0
     !*cp ||
1248
0
     !**cp) {
1249
0
    errno = EINVAL;
1250
0
    return NULL;
1251
0
  }
1252
1253
0
  if (!(flags & PR_STR_FL_PRESERVE_WHITESPACE)) {
1254
0
    while (**cp && PR_ISSPACE(**cp)) {
1255
0
      pr_signals_handle();
1256
0
      (*cp)++;
1257
0
    }
1258
0
  }
1259
1260
0
  if (!**cp) {
1261
0
    return NULL;
1262
0
  }
1263
1264
0
  res = dst = *cp;
1265
1266
0
  if (!(flags & PR_STR_FL_PRESERVE_COMMENTS)) {
1267
    /* Stop processing at start of an inline comment. */
1268
0
    if (**cp == '#') {
1269
0
      return NULL;
1270
0
    }
1271
0
  }
1272
1273
0
  if (!(flags & PR_STR_FL_IGNORE_QUOTES)) {
1274
0
    if (**cp == '\"') {
1275
0
      quote_mode = TRUE;
1276
0
      (*cp)++;
1277
0
    }
1278
0
  }
1279
1280
0
  while (**cp && (quote_mode ? (**cp != '\"') : !PR_ISSPACE(**cp))) {
1281
0
    pr_signals_handle();
1282
1283
0
    if (**cp == '\\' &&
1284
0
        quote_mode == TRUE) {
1285
      /* Escaped char */
1286
0
      if (*((*cp)+1)) {
1287
0
        *dst++ = *(++(*cp));
1288
0
        (*cp)++;
1289
0
        continue;
1290
0
      }
1291
0
    }
1292
1293
0
    *dst++ = **cp;
1294
0
    (*cp)++;
1295
0
  }
1296
1297
0
  if (**cp) {
1298
0
    (*cp)++;
1299
0
  }
1300
1301
0
  *dst = '\0';
1302
0
  return res;
1303
0
}
1304
1305
/* get_token tokenizes a string, increments the src pointer to the next
1306
 * non-separator in the string.  If the src string is empty or NULL, the next
1307
 * token returned is NULL.
1308
 */
1309
0
char *pr_str_get_token2(char **src, char *sep, size_t *token_len) {
1310
0
  char *token;
1311
0
  size_t len = 0;
1312
1313
0
  if (src == NULL ||
1314
0
      *src == NULL ||
1315
0
      **src == '\0' ||
1316
0
      sep == NULL) {
1317
1318
0
    if (token_len != NULL) {
1319
0
      *token_len = len;
1320
0
    }
1321
1322
0
    errno = EINVAL;
1323
0
    return NULL;
1324
0
  }
1325
1326
0
  token = *src;
1327
1328
0
  while (**src && !strchr(sep, **src)) {
1329
0
    (*src)++;
1330
0
    len++;
1331
0
  }
1332
1333
0
  if (**src) {
1334
0
    *(*src)++ = '\0';
1335
0
  }
1336
1337
0
  if (token_len != NULL) {
1338
0
    *token_len = len;
1339
0
  }
1340
1341
0
  return token;
1342
0
}
1343
1344
0
char *pr_str_get_token(char **src, char *sep) {
1345
0
  return pr_str_get_token2(src, sep, NULL);
1346
0
}
1347
1348
0
int pr_str_is_boolean(const char *str) {
1349
0
  if (str == NULL) {
1350
0
    errno = EINVAL;
1351
0
    return -1;
1352
0
  }
1353
1354
0
  if (strncasecmp(str, "on", 3) == 0) {
1355
0
    return TRUE;
1356
0
  }
1357
1358
0
  if (strncasecmp(str, "off", 4) == 0) {
1359
0
    return FALSE;
1360
0
  }
1361
1362
0
  if (strncasecmp(str, "yes", 4) == 0) {
1363
0
    return TRUE;
1364
0
  }
1365
1366
0
  if (strncasecmp(str, "no", 3) == 0) {
1367
0
    return FALSE;
1368
0
  }
1369
1370
0
  if (strncasecmp(str, "true", 5) == 0) {
1371
0
    return TRUE;
1372
0
  }
1373
1374
0
  if (strncasecmp(str, "false", 6) == 0) {
1375
0
    return FALSE;
1376
0
  }
1377
1378
0
  if (strncasecmp(str, "1", 2) == 0) {
1379
0
    return TRUE;
1380
0
  }
1381
1382
0
  if (strncasecmp(str, "0", 2) == 0) {
1383
0
    return FALSE;
1384
0
  }
1385
1386
0
  errno = EINVAL;
1387
0
  return -1;
1388
0
}
1389
1390
/* Return true if str contains any of the glob(7) characters. */
1391
0
int pr_str_is_fnmatch(const char *str) {
1392
0
  int have_bracket = 0;
1393
1394
0
  if (str == NULL) {
1395
0
    return FALSE;
1396
0
  }
1397
1398
0
  while (*str) {
1399
0
    switch (*str) {
1400
0
      case '?':
1401
0
      case '*':
1402
0
        return TRUE;
1403
1404
0
      case '\\':
1405
        /* If the next character is NUL, we've reached the end of the string. */
1406
0
        if (*(str+1) == '\0') {
1407
0
          return FALSE;
1408
0
        }
1409
1410
        /* Skip past the escaped character, i.e. the next character. */
1411
0
        str++;
1412
0
        break;
1413
1414
0
      case '[':
1415
0
        have_bracket++;
1416
0
        break;
1417
1418
0
      case ']':
1419
0
        if (have_bracket) {
1420
0
          return TRUE;
1421
0
        }
1422
0
        break;
1423
1424
0
      default:
1425
0
        break;
1426
0
    }
1427
1428
0
    str++;
1429
0
  }
1430
1431
0
  return FALSE;
1432
0
}