source: src/router/proftpd/src/str.c @ 17876

Last change on this file since 17876 was 17876, checked in by BrainSlayer, 19 months ago

update proftp

File size: 10.6 KB
Line 
1/*
2 * ProFTPD - FTP server daemon
3 * Copyright (c) 2008-2011 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 * $Id: str.c,v 1.11 2011/05/23 21:22:24 castaglia Exp $
27 */
28
29#include "conf.h"
30
31/* Maximum number of replacements that we will do in a given string. */
32#define PR_STR_MAX_REPLACEMENTS                 128
33
34char *sreplace(pool *p, char *s, ...) {
35  va_list args;
36  char *m, *r, *src, *cp;
37  char *matches[PR_STR_MAX_REPLACEMENTS+1], *replaces[PR_STR_MAX_REPLACEMENTS+1];
38  char buf[PR_TUNABLE_PATH_MAX] = {'\0'}, *pbuf = NULL;
39  size_t nmatches = 0, rlen = 0;
40  int blen = 0;
41
42  if (p == NULL ||
43      s == NULL) {
44    errno = EINVAL;
45    return NULL;
46  }
47
48  src = s;
49  cp = buf;
50  *cp = '\0';
51
52  memset(matches, 0, sizeof(matches));
53  memset(replaces, 0, sizeof(replaces));
54
55  blen = strlen(src) + 1;
56
57  va_start(args, s);
58
59  while ((m = va_arg(args, char *)) != NULL &&
60         nmatches < PR_STR_MAX_REPLACEMENTS) {
61    char *tmp = NULL;
62    int count = 0;
63
64    r = va_arg(args, char *);
65    if (r == NULL) {
66      break;
67    }
68
69    /* Increase the length of the needed buffer by the difference between
70     * the given match and replacement strings, multiplied by the number
71     * of times the match string occurs in the source string.
72     */
73    tmp = strstr(s, m);
74    while (tmp) {
75      pr_signals_handle();
76      count++;
77      if (count > 8) {
78        /* More than eight instances of the same escape on the same line?
79         * Give me a break.
80         */
81        return s;
82      }
83
84      /* Be sure to increment the pointer returned by strstr(3), to
85       * advance past the beginning of the substring for which we are
86       * looking.  Otherwise, we just loop endlessly, seeing the same
87       * value for tmp over and over.
88       */
89      tmp += strlen(m);
90      tmp = strstr(tmp, m);
91    }
92
93    /* We are only concerned about match/replacement strings that actually
94     * occur in the given string.
95     */
96    if (count) {
97      blen += count * (strlen(r) - strlen(m));
98      if (blen < 0) {
99        /* Integer overflow. In order to overflow this, somebody must be
100         * doing something very strange. The possibility still exists that
101         * we might not catch this overflow in extreme corner cases, but
102         * massive amounts of data (gigabytes) would need to be in s to
103         * trigger this, easily larger than any buffer we might use.
104         */
105        return s;
106      }
107      matches[nmatches] = m;
108      replaces[nmatches++] = r;
109    }
110  }
111
112  va_end(args);
113
114  /* If there are no matches, then there is nothing to replace. */
115  if (nmatches == 0) {
116    return s;
117  }
118
119  /* Try to handle large buffer situations (i.e. escaping of PR_TUNABLE_PATH_MAX
120   * (>2048) correctly, but do not allow very big buffer sizes, that may
121   * be dangerous (BUFSIZ may be defined in stdio.h) in some library
122   * functions.
123   */
124#ifndef BUFSIZ
125# define BUFSIZ 8192
126#endif
127
128  if (blen >= BUFSIZ) {
129    errno = ENOSPC;
130    return NULL;
131  }
132
133  cp = pbuf = (char *) pcalloc(p, ++blen);
134
135  while (*src) {
136    char **mptr, **rptr;
137
138    for (mptr = matches, rptr = replaces; *mptr; mptr++, rptr++) {
139      size_t mlen;
140
141      mlen = strlen(*mptr);
142      rlen = strlen(*rptr);
143
144      if (strncmp(src, *mptr, mlen) == 0) {
145        sstrncpy(cp, *rptr, blen - strlen(pbuf));
146
147        if (((cp + rlen) - pbuf + 1) > blen) {
148          pr_log_pri(PR_LOG_ERR,
149            "WARNING: attempt to overflow internal ProFTPD buffers");
150          cp = pbuf;
151
152          cp += (blen - 1);
153          goto done;
154
155        } else {
156          cp += rlen;
157        }
158       
159        src += mlen;
160        break;
161      }
162    }
163
164    if (!*mptr) {
165      if ((cp - pbuf + 1) >= blen) {
166        pr_log_pri(PR_LOG_ERR,
167          "WARNING: attempt to overflow internal ProFTPD buffers");
168        cp = pbuf;
169
170        cp += (blen - 1);
171        goto done;
172      }
173
174      *cp++ = *src++;
175    }
176  }
177
178 done:
179  *cp = '\0';
180
181  return pbuf;
182}
183
184/* "safe" strcat, saves room for NUL at end of dst, and refuses to copy more
185 * than "n" bytes.
186 */
187char *sstrcat(char *dst, const char *src, size_t n) {
188  register char *d;
189
190  if (!dst || !src || n == 0) {
191    errno = EINVAL;
192    return NULL;
193  }
194
195  for (d = dst; *d && n > 1; d++, n--) ;
196
197  while (n-- > 1 && *src)
198    *d++ = *src++;
199
200  *d = 0;
201  return dst;
202}
203
204char *pstrdup(pool *p, const char *str) {
205  char *res;
206  size_t len;
207
208  if (!p || !str) {
209    errno = EINVAL;
210    return NULL;
211  }
212
213  len = strlen(str) + 1;
214
215  res = palloc(p, len);
216  sstrncpy(res, str, len);
217  return res;
218}
219
220char *pstrndup(pool *p, const char *str, size_t n) {
221  char *res;
222
223  if (!p || !str) {
224    errno = EINVAL;
225    return NULL;
226  }
227
228  res = palloc(p, n + 1);
229  sstrncpy(res, str, n + 1);
230  return res;
231}
232
233char *pdircat(pool *p, ...) {
234  char *argp, *res;
235  char last;
236
237  int count = 0;
238  size_t len = 0;
239  va_list ap;
240
241  if (p == NULL) {
242    errno = EINVAL;
243    return NULL;
244  }
245
246  va_start(ap, p);
247
248  last = 0;
249
250  while ((res = va_arg(ap, char *)) != NULL) {
251    /* If the first argument is "", we have to account for a leading /
252     * which must be added.
253     */
254    if (!count++ && !*res)
255      len++;
256
257    else if (last && last != '/' && *res != '/')
258      len++;
259
260    else if (last && last == '/' && *res == '/')
261      len--;
262
263    len += strlen(res);
264    last = (*res ? res[strlen(res) - 1] : 0);
265  }
266
267  va_end(ap);
268  res = (char *) pcalloc(p, len + 1);
269
270  va_start(ap, p);
271
272  last = 0;
273
274  while ((argp = va_arg(ap, char *)) != NULL) {
275    if (last && last == '/' && *argp == '/')
276      argp++;
277
278    else if (last && last != '/' && *argp != '/')
279      sstrcat(res, "/", len + 1);
280
281    sstrcat(res, argp, len + 1);
282    last = (*res ? res[strlen(res) - 1] : 0);
283  }
284
285  va_end(ap);
286
287  return res;
288}
289
290char *pstrcat(pool *p, ...) {
291  char *argp, *res;
292
293  size_t len = 0;
294  va_list ap;
295
296  if (p == NULL) {
297    errno = EINVAL;
298    return NULL;
299  }
300
301  va_start(ap, p);
302
303  while ((res = va_arg(ap, char *)) != NULL)
304    len += strlen(res);
305
306  va_end(ap);
307
308  res = pcalloc(p, len + 1);
309
310  va_start(ap, p);
311
312  while ((argp = va_arg(ap, char *)) != NULL)
313    sstrcat(res, argp, len + 1);
314
315  va_end(ap);
316
317  return res;
318}
319
320char *pr_str_strip(pool *p, char *str) {
321  char c, *dupstr, *start, *finish;
322 
323  if (!p || !str) {
324    errno = EINVAL;
325    return NULL;
326  }
327 
328  /* First, find the non-whitespace start of the given string */
329  for (start = str; isspace((int) *start); start++);
330 
331  /* Now, find the non-whitespace end of the given string */
332  for (finish = &str[strlen(str)-1]; isspace((int) *finish); finish--);
333
334  /* finish is now pointing to a non-whitespace character.  So advance one
335   * character forward, and set that to NUL.
336   */
337  c = *++finish;
338  *finish = '\0';
339
340  /* The space-stripped string is, then, everything from start to finish. */
341  dupstr = pstrdup(p, start);
342
343  /* Restore the given string buffer contents. */
344  *finish = c;
345
346  return dupstr;
347}
348
349char *pr_str_strip_end(char *s, char *ch) {
350  size_t len;
351
352  if (s == NULL ||
353      ch == NULL) {
354    errno = EINVAL;
355    return NULL;
356  }
357
358  len = strlen(s);
359
360  while (len && strchr(ch, *(s+len - 1))) {
361    pr_signals_handle();
362
363    *(s+len - 1) = '\0';
364    len--;
365  }
366
367  return s;
368}
369
370char *pr_str_get_word(char **cp, int flags) {
371  char *res, *dst;
372  char quote_mode = 0;
373
374  if (cp == NULL ||
375     !*cp ||
376     !**cp) {
377    errno = EINVAL;
378    return NULL;
379  }
380
381  if (!(flags & PR_STR_FL_PRESERVE_WHITESPACE)) {
382    while (**cp && isspace((int) **cp))
383      (*cp)++;
384  }
385
386  if (!**cp)
387    return NULL;
388
389  res = dst = *cp;
390
391  if (!(flags & PR_STR_FL_PRESERVE_COMMENTS)) {
392    /* Stop processing at start of an inline comment. */
393    if (**cp == '#')
394      return NULL;
395  }
396
397  if (**cp == '\"') {
398    quote_mode++;
399    (*cp)++;
400  }
401
402  while (**cp && (quote_mode ? (**cp != '\"') : !isspace((int) **cp))) {
403    if (**cp == '\\' && quote_mode) {
404
405      /* Escaped char */
406      if (*((*cp)+1))
407        *dst = *(++(*cp));
408    }
409
410    *dst++ = **cp;
411    ++(*cp);
412  }
413
414  if (**cp)
415    (*cp)++;
416  *dst = '\0';
417
418  return res;
419}
420
421/* get_token tokenizes a string, increments the src pointer to the next
422 * non-separator in the string.  If the src string is empty or NULL, the next
423 * token returned is NULL.
424 */
425char *pr_str_get_token(char **s, char *sep) {
426  char *res;
427
428  if (s == NULL ||
429      *s == NULL ||
430      **s == '\0' ||
431      sep == NULL) {
432    errno = EINVAL;
433    return NULL;
434  }
435
436  res = *s;
437
438  while (**s && !strchr(sep, **s)) {
439    (*s)++;
440  }
441
442  if (**s)
443    *(*s)++ = '\0';
444
445  return res;
446}
447
448int pr_str_is_boolean(const char *str) {
449  if (str == NULL) {
450    errno = EINVAL;
451    return -1;
452  }
453
454  if (strncasecmp(str, "on", 3) == 0) {
455    return TRUE;
456  }
457
458  if (strncasecmp(str, "off", 4) == 0) {
459    return FALSE;
460  }
461
462  if (strncasecmp(str, "yes", 4) == 0) {
463    return TRUE;
464  }
465 
466  if (strncasecmp(str, "no", 3) == 0) {
467    return FALSE;
468  }
469
470  if (strncasecmp(str, "true", 5) == 0) {
471    return TRUE;
472  }
473
474  if (strncasecmp(str, "false", 6) == 0) {
475    return FALSE;
476  }
477
478  if (strncasecmp(str, "1", 2) == 0) {
479    return TRUE;
480  }
481
482  if (strncasecmp(str, "0", 2) == 0) {
483    return FALSE;
484  }
485
486  errno = EINVAL;
487  return -1;
488}
489
490/* Return true if str contains any of the glob(7) characters. */
491int pr_str_is_fnmatch(const char *str) {
492  int have_bracket = 0;
493
494  while (*str) {
495    switch (*str) {
496      case '?':
497      case '*':
498        return TRUE;
499
500      case '\\':
501        /* If the next character is NUL, we've reached the end of the string. */
502        if (*(str+1) == '\0')
503          return FALSE;
504
505        /* Skip past the escaped character, i.e. the next character. */
506        str++;
507        break;
508
509      case '[':
510        have_bracket++;
511        break;
512
513      case ']':
514        if (have_bracket)
515          return TRUE;
516        break;
517
518      default:
519        break;
520    }
521
522    str++;
523  }
524
525  return FALSE;
526}
527
Note: See TracBrowser for help on using the repository browser.