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