• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

proftpd / proftpd / 14526507026

17 Apr 2025 11:25PM UTC coverage: 93.03% (+0.4%) from 92.667%
14526507026

push

github

51358 of 55206 relevant lines covered (93.03%)

234.02 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.14
/src/configdb.c
1
/*
2
 * ProFTPD - FTP server daemon
3
 * Copyright (c) 2014-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
/* Configuration database implementation. */
26

27
#include "conf.h"
28
#include "privs.h"
29

30
#ifdef HAVE_ARPA_INET_H
31
# include <arpa/inet.h>
32
#endif
33

34
/* From src/pool.c */
35
extern pool *global_config_pool;
36

37
/* Used by find_config_* */
38
static xaset_t *find_config_top = NULL;
39

40
static void config_dumpf(const char *fmt, ...);
41

42
static config_rec *last_param_ptr = NULL;
43

44
static pool *config_tab_pool = NULL;
45
static pr_table_t *config_tab = NULL;
46
static unsigned int config_id = 0;
47

48
static const char *trace_channel = "config";
49

50
config_rec *pr_config_alloc(pool *p, const char *name, int config_type) {
47✔
51
  config_rec *c;
47✔
52

53
  if (p == NULL) {
47✔
54
    errno = EINVAL;
1✔
55
    return NULL;
1✔
56
  }
57

58
  c = (config_rec *) pcalloc(p, sizeof(config_rec));
46✔
59
  c->pool = p;
46✔
60
  c->config_type = config_type;
46✔
61

62
  if (name != NULL) {
46✔
63
    c->name = pstrdup(c->pool, name);
45✔
64
    c->config_id = pr_config_set_id(c->name);
45✔
65
  }
66

67
  return c;
68
}
69

70
/* Add the given config_rec to the specified set */
71
config_rec *pr_config_add_config_to_set(xaset_t *set, config_rec *c,
46✔
72
    int flags) {
73
  config_rec *parent = NULL;
46✔
74

75
  if (set == NULL ||
46✔
76
      c == NULL) {
46✔
77
    errno = EINVAL;
2✔
78
    return NULL;
2✔
79
  }
80

81
  /* Find the parent set for the config_rec to be allocated. */
82
  if (set->xas_list != NULL) {
44✔
83
    parent = ((config_rec *) (set->xas_list))->parent;
21✔
84
  }
85

86
  c->set = set;
44✔
87
  c->parent = parent;
44✔
88

89
  if (flags & PR_CONFIG_FL_INSERT_HEAD) {
44✔
90
    xaset_insert(set, (xasetmember_t *) c);
1✔
91

92
  } else {
93
    xaset_insert_end(set, (xasetmember_t *) c);
43✔
94
  }
95

96
  /* Generate an event about the added config, for any interested parties.
97
   * This is useful for tracking the origins of the config tree.
98
   */
99
  pr_event_generate("core.added-config", c);
44✔
100

101
  return c;
44✔
102
}
103

104
/* Adds an automatically allocated config_rec to the specified set */
105
config_rec *pr_config_add_set(xaset_t **set, const char *name, int flags) {
45✔
106
  pool *conf_pool = NULL;
45✔
107
  config_rec *c;
45✔
108

109
  if (set == NULL) {
45✔
110
    errno = EINVAL;
2✔
111
    return NULL;
2✔
112
  }
113

114
  if (!*set) {
43✔
115
    pool *set_pool;
14✔
116

117
    /* Allocate a subpool from permanent_pool for the set. */
118
    set_pool = make_sub_pool(permanent_pool);
14✔
119
    pr_pool_tag(set_pool, "config set pool");
14✔
120

121
    *set = xaset_create(set_pool, NULL);
14✔
122
    (*set)->pool = set_pool;
14✔
123
  }
124

125
  /* Now, make a subpool for the config_rec to be allocated.  The default
126
   * pool size (PR_TUNABLE_NEW_POOL_SIZE, 512 by default) is a bit large
127
   * for config_rec pools; use a smaller size.
128
   */
129
  conf_pool = pr_pool_create_sz((*set)->pool, 128);
43✔
130
  pr_pool_tag(conf_pool, "config_rec pool");
43✔
131

132
  c = pr_config_alloc(conf_pool, name, 0);
43✔
133
  pr_config_add_config_to_set(*set, c, flags);
43✔
134

135
  return c;
43✔
136
}
137

138
config_rec *add_config_set(xaset_t **set, const char *name) {
2✔
139
  return pr_config_add_set(set, name, 0);
2✔
140
}
141

142
/* Adds a config_rec to the given server.  If no server is specified, the
143
 * config_rec is added to the current "level".
144
 */
145
config_rec *pr_config_add(server_rec *s, const char *name, int flags) {
7✔
146
  config_rec *parent = NULL, *c = NULL;
7✔
147
  pool *p = NULL;
7✔
148
  xaset_t **set = NULL;
7✔
149

150
  if (s == NULL) {
7✔
151
    s = pr_parser_server_ctxt_get();
6✔
152
  }
153

154
  if (s == NULL) {
7✔
155
    errno = EINVAL;
1✔
156
    return NULL;
1✔
157
  }
158

159
  c = pr_parser_config_ctxt_get();
6✔
160

161
  if (c) {
6✔
162
    parent = c;
×
163
    p = c->pool;
×
164
    set = &c->subset;
×
165

166
  } else {
167
    parent = NULL;
6✔
168

169
    if (s->conf == NULL ||
6✔
170
        s->conf->xas_list == NULL) {
1✔
171

172
      p = make_sub_pool(s->pool);
5✔
173
      pr_pool_tag(p, "pr_config_add() subpool");
5✔
174

175
    } else {
176
      p = ((config_rec *) s->conf->xas_list)->pool;
1✔
177
    }
178

179
    set = &s->conf;
6✔
180
  }
181

182
  if (!*set) {
6✔
183
    *set = xaset_create(p, NULL);
5✔
184
  }
185

186
  c = pr_config_add_set(set, name, flags);
6✔
187
  c->parent = parent;
6✔
188

189
  return c;
6✔
190
}
191

192
config_rec *add_config(server_rec *s, const char *name) {
1✔
193
  return pr_config_add(s, name, 0);
1✔
194
}
195

196
static void config_dumpf(const char *fmt, ...) {
6✔
197
  char buf[PR_TUNABLE_BUFFER_SIZE] = {'\0'};
6✔
198
  va_list msg;
6✔
199

200
  va_start(msg, fmt);
6✔
201
  pr_vsnprintf(buf, sizeof(buf), fmt, msg);
6✔
202
  va_end(msg);
6✔
203

204
  buf[sizeof(buf)-1] = '\0';
6✔
205

206
  pr_log_debug(DEBUG5, "%s", buf);
6✔
207
}
6✔
208

209
void pr_config_dump(void (*dumpf)(const char *, ...), xaset_t *s,
6✔
210
    char *indent) {
211
  config_rec *c = NULL;
6✔
212

213
  if (dumpf == NULL) {
6✔
214
    dumpf = config_dumpf;
5✔
215
  }
216

217
  if (s == NULL) {
6✔
218
    return;
219
  }
220

221
  if (indent == NULL) {
5✔
222
    indent = "";
4✔
223
  }
224

225
  for (c = (config_rec *) s->xas_list; c; c = c->next) {
11✔
226
    pr_signals_handle();
6✔
227

228
    /* Don't display directives whose name starts with an underscore. */
229
    if (c->name != NULL &&
6✔
230
        *(c->name) != '_') {
6✔
231
      dumpf("%s%s", indent, c->name);
6✔
232
    }
233

234
    if (c->subset) {
6✔
235
      pool *iter_pool;
1✔
236

237
      iter_pool = make_sub_pool(c->pool);
1✔
238
      pr_pool_tag(iter_pool, "config dump scratch pool");
1✔
239
      pr_config_dump(dumpf, c->subset, pstrcat(iter_pool, indent, " ", NULL));
1✔
240
      destroy_pool(iter_pool);
1✔
241
    }
242
  }
243
}
244

245
static const char *config_type_str(int config_type) {
×
246
  const char *type = "(unknown)";
×
247

248
  switch (config_type) {
×
249
    case CONF_ROOT:
×
250
      type = "CONF_ROOT";
×
251
      break;
×
252

253
    case CONF_DIR:
×
254
      type = "CONF_DIR";
×
255
      break;
×
256

257
    case CONF_ANON:
×
258
      type = "CONF_ANON";
×
259
      break;
×
260

261
    case CONF_LIMIT:
×
262
      type = "CONF_LIMIT";
×
263
      break;
×
264

265
    case CONF_VIRTUAL:
×
266
      type = "CONF_VIRTUAL";
×
267
      break;
×
268

269
    case CONF_DYNDIR:
×
270
      type = "CONF_DYNDIR";
×
271
      break;
×
272

273
    case CONF_GLOBAL:
×
274
      type = "CONF_GLOBAL";
×
275
      break;
×
276

277
    case CONF_CLASS:
×
278
      type = "CONF_CLASS";
×
279
      break;
×
280

281
    case CONF_NAMED:
×
282
      type = "CONF_NAMED";
×
283
      break;
×
284

285
    case CONF_USERDATA:
×
286
      type = "CONF_USERDATA";
×
287
      break;
×
288

289
    case CONF_PARAM:
×
290
      type = "CONF_PARAM";
×
291
      break;
×
292

×
293
    default:
294
      break;
×
295
  };
296

297
  return type;
298
}
299

300
/* Compare two different config_recs to see if they are the same.  Note
301
 * that "same" here has to be very specific.
302
 *
303
 * Returns 0 if the two config_recs are the same, and 1 if they differ, and
11✔
304
 * -1 if there was an error.
305
 */
306
static int config_cmp(const config_rec *a, const char *a_name,
11✔
307
    const config_rec *b, const char *b_name) {
11✔
308

×
309
  if (a == NULL ||
×
310
      b == NULL) {
311
    errno = EINVAL;
312
    return -1;
11✔
313
  }
×
314

315
  if (a->config_type != b->config_type) {
316
    pr_trace_msg(trace_channel, 18,
317
      "configs '%s' and '%s' have mismatched config_type (%s != %s)",
×
318
      a_name, b_name, config_type_str(a->config_type),
319
      config_type_str(b->config_type));
320
    return 1;
11✔
321
  }
7✔
322

323
  if (a->flags != b->flags) {
324
    pr_trace_msg(trace_channel, 18,
7✔
325
      "configs '%s' and '%s' have mismatched flags (%ld != %ld)",
326
      a_name, b_name, a->flags, b->flags);
327
    return 1;
4✔
328
  }
×
329

330
  if (a->argc != b->argc) {
331
    pr_trace_msg(trace_channel, 18,
×
332
      "configs '%s' and '%s' have mismatched argc (%d != %d)",
333
      a_name, b_name, a->argc, b->argc);
334
    return 1;
4✔
335
  }
336

337
  if (a->argc > 0) {
10✔
338
    register unsigned int i;
7✔
339

1✔
340
    for (i = 0; i < a->argc; i++) {
341
      if (a->argv[i] != b->argv[i]) {
342
        pr_trace_msg(trace_channel, 18,
1✔
343
          "configs '%s' and '%s' have mismatched argv[%u] (%p != %p)",
344
          a_name, b_name, i, a->argv[i], b->argv[i]);
345
        return 1;
346
      }
347
    }
3✔
348
  }
×
349

350
  if (a->config_id != b->config_id) {
351
    pr_trace_msg(trace_channel, 18,
×
352
      "configs '%s' and '%s' have mismatched config_id (%d != %d)",
353
      a_name, b_name, a->config_id, b->config_id);
354
    return 1;
355
  }
3✔
356

×
357
  /* Save the string comparison for last, to try to save some CPU. */
358
  if (strcmp(a->name, b->name) != 0) {
359
    pr_trace_msg(trace_channel, 18,
×
360
      "configs '%s' and '%s' have mismatched name ('%s' != '%s')",
361
      a_name, b_name, a->name, b->name);
362
    return 1;
363
  }
364

365
  return 0;
4✔
366
}
4✔
367

4✔
368
static config_rec *copy_config_from(const config_rec *src, config_rec *dst) {
4✔
369
  config_rec *c;
370
  unsigned int cargc;
4✔
371
  void **cargv, **sargv;
4✔
372

373
  if (src == NULL ||
374
      dst == NULL) {
375
    return NULL;
376
  }
377

378
  /* If the destination parent config_rec doesn't already have a subset
4✔
379
   * container, allocate one.
1✔
380
   */
381
  if (dst->subset == NULL) {
382
    dst->subset = xaset_create(dst->pool, NULL);
4✔
383
  }
4✔
384

385
  c = pr_config_add_set(&dst->subset, src->name, 0);
386
  if (c == NULL) {
387
    return NULL;
4✔
388
  }
4✔
389

4✔
390
  c->config_type = src->config_type;
391
  c->flags = src->flags;
4✔
392
  c->config_id = src->config_id;
4✔
393

394
  c->argc = src->argc;
4✔
395
  c->argv = pcalloc(c->pool, (src->argc + 1) * sizeof(void *));
4✔
396

4✔
397
  cargc = c->argc;
398
  cargv = c->argv;
10✔
399
  sargv = src->argv;
6✔
400

6✔
401
  while (cargc--) {
402
    pr_signals_handle();
403
    *cargv++ = *sargv++;
4✔
404
  }
4✔
405

406
  *cargv = NULL;
407
  return c;
13✔
408
}
13✔
409

410
void pr_config_merge_down(xaset_t *s, int dynamic) {
13✔
411
  config_rec *c, *dst;
12✔
412

413
  if (s == NULL ||
414
      s->xas_list == NULL) {
415
    return;
48✔
416
  }
37✔
417

418
  for (c = (config_rec *) s->xas_list; c; c = c->next) {
37✔
419
    pr_signals_handle();
420

421
    if ((c->flags & CF_MERGEDOWN) ||
125✔
422
        (c->flags & CF_MERGEDOWN_MULTI)) {
99✔
423

424
      for (dst = (config_rec *) s->xas_list; dst; dst = dst->next) {
425
        if (dst->config_type == CONF_ANON ||
426
           dst->config_type == CONF_DIR) {
427

428
          /* If an option of the same name/type is found in the
10✔
429
           * next level down, it overrides, so we don't merge.
5✔
430
           */
3✔
431
          if ((c->flags & CF_MERGEDOWN) &&
432
              find_config(dst->subset, c->config_type, c->name, FALSE)) {
433
            continue;
7✔
434
          }
435

436
          if (dynamic) {
437
            /* If we are doing a dynamic merge (i.e. .ftpaccess files) then
438
             * we do not need to re-merge the static configs that are already
439
             * there.  Otherwise we are creating copies needlessly of any
440
             * config_rec marked with the CF_MERGEDOWN_MULTI flag, which
441
             * adds to the memory usage/processing time.
442
             *
443
             * If neither the src or the dst config have the CF_DYNAMIC
444
             * flag, it's a static config, and we can skip this merge and move
×
445
             * on.  Otherwise, we can merge it.
×
446
             */
447
            if (!(c->flags & CF_DYNAMIC) && !(dst->flags & CF_DYNAMIC)) {
448
              continue;
449
            }
450
          }
451

452
          /* We want to scan the config_recs contained in dst's subset to see
453
           * if we can find another config_rec that duplicates the one we want
7✔
454
           * to merge into dst.
6✔
455
           */
6✔
456
          if (dst->subset != NULL) {
457
              config_rec *r = NULL;
14✔
458
            int merge = TRUE;
11✔
459

460
            for (r = (config_rec *) dst->subset->xas_list; r; r = r->next) {
11✔
461
              pr_signals_handle();
3✔
462

463
              if (config_cmp(r, r->name, c, c->name) == 0) {
3✔
464
                merge = FALSE;
465

466
                pr_trace_msg(trace_channel, 15,
3✔
467
                  "found duplicate '%s' record in '%s', skipping merge",
468
                  r->name, dst->name);
469
                break;
470
              }
3✔
471
            }
3✔
472

473
            if (merge) {
474
              (void) copy_config_from(c, dst);
475
            }
476

1✔
477
          } else {
478
            /* No existing subset in dst; we can merge this one in. */
479
            (void) copy_config_from(c, dst);
480
          }
481
        }
482
      }
483
    }
484
  }
48✔
485

37✔
486
  /* Top level merged, recursively merge lower levels */
5✔
487
  for (c = (config_rec *) s->xas_list; c; c = c->next) {
488
    if (c->subset &&
5✔
489
        (c->config_type == CONF_ANON ||
490
         c->config_type == CONF_DIR)) {
491
      pr_config_merge_down(c->subset, dynamic);
492
    }
493
  }
97✔
494
}
495

97✔
496
config_rec *find_config_next2(config_rec *prev, config_rec *c, int type,
97✔
497
    const char *name, int recurse, unsigned long flags) {
97✔
498
  config_rec *top = c;
499
  unsigned int cid = 0;
500
  size_t namelen = 0;
501

502
  /* We do two searches (if recursing) so that we find the "deepest"
503
   * level first.
504
   *
505
   * The `recurse` argument tells us HOW to perform that search, e.g.
506
   * how to do our DFS (depth-first search) approach:
507
   *
508
   *  recurse = 0:
509
   *    Start at c, search all `next` nodes in list, i.e. all nodes at
510
   *    the same depth, no recursion.
511
   *
512
   *  recurse = 1:
513
   *    Start at c, search all `subset` nodes in tree first, then siblings,
514
   *    then `next` nodes of parent.
515
   *
516
   *  recurse > 1:
517
   *    Start with child nodes first (`subset`), then c itself (skipping
518
   *    siblings nodes).
97✔
519
   */
97✔
520

1✔
521
  if (c == NULL &&
1✔
522
      prev == NULL) {
523
    errno = EINVAL;
524
    return NULL;
96✔
525
  }
86✔
526

527
  if (prev == NULL) {
528
    prev = top;
96✔
529
  }
83✔
530

83✔
531
  if (name != NULL) {
532
    cid = pr_config_get_id(name);
533
    namelen = strlen(name);
96✔
534
  }
96✔
535

17✔
536
  do {
537
    if (recurse) {
17✔
538
      config_rec *res = NULL;
539

540
      pr_signals_handle();
57✔
541

23✔
542
      /* Search subsets. */
×
543
      for (c = top; c; c = c->next) {
544
        if (c->subset &&
545
            c->subset->xas_list) {
546
          config_rec *subc = NULL;
×
547

×
548
          for (subc = (config_rec *) c->subset->xas_list;
×
549
               subc;
550
               subc = subc->next) {
×
551
            pr_signals_handle();
×
552

553
            if (subc->config_type == CONF_ANON &&
×
554
                (flags & PR_CONFIG_FIND_FL_SKIP_ANON)) {
555
              /* Skip <Anonymous> config_rec */
556
              continue;
×
557
            }
×
558

559
            if (subc->config_type == CONF_DIR &&
×
560
                (flags & PR_CONFIG_FIND_FL_SKIP_DIR)) {
561
              /* Skip <Directory> config_rec */
562
              continue;
×
563
            }
×
564

565
            if (subc->config_type == CONF_LIMIT &&
×
566
                (flags & PR_CONFIG_FIND_FL_SKIP_LIMIT)) {
567
              /* Skip <Limit> config_rec */
568
              continue;
×
569
            }
×
570

571
            if (subc->config_type == CONF_DYNDIR &&
×
572
                (flags & PR_CONFIG_FIND_FL_SKIP_DYNDIR)) {
573
              /* Skip .ftpaccess config_rec */
574
              continue;
×
575
            }
×
576

×
577
            res = find_config_next2(NULL, subc, type, name, recurse + 1, flags);
578
            if (res) {
579
              return res;
580
            }
581
          }
23✔
582
        }
583

584
        if (recurse > 1) {
585
          /* Sibling subsets are already searched by the caller; no need to
586
           * continue here (Bug#4307).
587
           */
588
          break;
589
        }
590
      }
591
    }
592

593
    /* Recurse: If deep recursion yielded no match try the current subset.
594
     *
595
     * NOTE: the string comparison here is specifically case-sensitive.
596
     * The config_rec names are supplied by the modules and intentionally
597
     * case sensitive (they shouldn't be verbatim from the config file)
598
     * Do NOT change this to strcasecmp(), no matter how tempted you are
155✔
599
     * to do so, it will break stuff. ;)
115✔
600
     */
601
    for (c = top; c; c = c->next) {
115✔
602
      pr_signals_handle();
76✔
603

604
      if (type == -1 ||
105✔
605
          type == c->config_type) {
8✔
606

607
        if (name == NULL) {
608
          return c;
97✔
609
        }
31✔
610

25✔
611
        if (cid != 0 &&
612
            cid == c->config_id) {
613
          return c;
72✔
614
        }
23✔
615

616
        if (strncmp(name, c->name, namelen + 1) == 0) {
617
          return c;
618
        }
59✔
619
      }
620

621
      if (recurse > 1) {
622
        /* Sibling subsets are already searched by the caller; no need to
623
         * continue here (Bug#4307).
624
         */
625
        break;
626
      }
40✔
627
    }
628

629
    if (recurse == 1) {
630
      /* All siblings have been searched; continue the search at the previous
8✔
631
       * level.
3✔
632
       */
×
633
      if (prev->parent &&
×
634
          prev->parent->next &&
×
635
          prev->parent->set != find_config_top) {
×
636
        prev = top = prev->parent->next;
637
        c = top;
638
        continue;
40✔
639
      }
640
    }
641
    break;
642

40✔
643
  } while (TRUE);
40✔
644

645
  errno = ENOENT;
646
  return NULL;
4✔
647
}
648

3✔
649
config_rec *find_config_next(config_rec *prev, config_rec *c, int type,
650
    const char *name, int recurse) {
651
  return find_config_next2(prev, c, type, name, recurse, 0UL);
88✔
652
}
2✔
653

1✔
654
void find_config_set_top(config_rec *c) {
10✔
655
  if (c &&
656
      c->parent) {
657
    find_config_top = c->parent->set;
78✔
658

659
  } else {
2✔
660
    find_config_top = NULL;
661
  }
132✔
662
}
663

664
config_rec *find_config2(xaset_t *set, int type, const char *name,
132✔
665
  int recurse, unsigned long flags) {
97✔
666

46✔
667
  if (set == NULL ||
46✔
668
      set->xas_list == NULL) {
669
    errno = EINVAL;
670
    return NULL;
86✔
671
  }
672

86✔
673
  find_config_set_top((config_rec *) set->xas_list);
674

675
  return find_config_next2(NULL, (config_rec *) set->xas_list, type, name,
676
    recurse, flags);
116✔
677
}
48✔
678

679
config_rec *find_config(xaset_t *set, int type, const char *name, int recurse) {
680
  return find_config2(set, type, name, recurse, 0UL);
74✔
681
}
74✔
682

683
void *get_param_ptr(xaset_t *set, const char *name, int recurse) {
74✔
684
  config_rec *c;
40✔
685

40✔
686
  if (set == NULL) {
40✔
687
    last_param_ptr = NULL;
688
    errno = ENOENT;
689
    return NULL;
34✔
690
  }
34✔
691

19✔
692
  c = find_config(set, CONF_PARAM, name, recurse);
19✔
693
  if (c &&
19✔
694
      c->argc) {
695
    last_param_ptr = c;
696
    return c->argv[0];
15✔
697
  }
15✔
698

15✔
699
  last_param_ptr = NULL;
700
  errno = ENOENT;
701
  return NULL;
4✔
702
}
4✔
703

704
void *get_param_ptr_next(const char *name, int recurse) {
4✔
705
  config_rec *c;
3✔
706

3✔
707
  if (!last_param_ptr ||
3✔
708
      !last_param_ptr->next) {
3✔
709
    last_param_ptr = NULL;
710
    errno = ENOENT;
711
    return NULL;
1✔
712
  }
713

1✔
714
  c = find_config_next(last_param_ptr, last_param_ptr->next, CONF_PARAM,
1✔
715
    name, recurse);
1✔
716
  if (c &&
1✔
717
      c->argv) {
718
    last_param_ptr = c;
719
    return c->argv[0];
×
720
  }
×
721

×
722
  last_param_ptr = NULL;
723
  errno = ENOENT;
724
  return NULL;
18✔
725
}
18✔
726

18✔
727
int pr_config_remove(xaset_t *set, const char *name, int flags, int recurse) {
18✔
728
  server_rec *s;
729
  config_rec *c;
18✔
730
  int found = 0;
18✔
731

13✔
732
  s = pr_parser_server_ctxt_get();
733
  if (s == NULL) {
734
    s = main_server;
34✔
735
  }
16✔
736

737
  while ((c = find_config(set, -1, name, recurse)) != NULL) {
16✔
738
    xaset_t *found_set;
739

16✔
740
    pr_signals_handle();
741

16✔
742
    found++;
16✔
743

744
    found_set = c->set;
16✔
745
    xaset_remove(found_set, (xasetmember_t *) c);
16✔
746

747
    c->set = NULL;
16✔
748
    (void) pr_table_remove(config_tab, name, NULL);
749

10✔
750
    if (found_set->xas_list == NULL) {
1✔
751
      /* First, set any pointers to the container of the set to NULL. */
×
752
      if (c->parent != NULL &&
753
          c->parent->subset == found_set) {
10✔
754
        c->parent->subset = NULL;
8✔
755

756
      } else if (s && s->conf == found_set) {
757
        s->conf = NULL;
758
      }
16✔
759
    }
760

15✔
761
    if (!(flags & PR_CONFIG_FL_PRESERVE_ENTRY)) {
762
      /* If the set was not empty, destroy only the requested config_rec. */
763
      destroy_pool(c->pool);
764
    }
18✔
765
  }
766

767
  return found;
16✔
768
}
16✔
769

770
int remove_config(xaset_t *set, const char *name, int recurse) {
771
  return pr_config_remove(set, name, 0, recurse);
32✔
772
}
773

32✔
774
config_rec *add_config_param_set(xaset_t **set, const char *name,
32✔
775
    unsigned int num, ...) {
32✔
776
  config_rec *c;
777
  void **argv;
32✔
778
  va_list ap;
32✔
779

780
  c = pr_config_add_set(set, name, 0);
781
  if (c == NULL) {
782
    return NULL;
31✔
783
  }
31✔
784

31✔
785
  c->config_type = CONF_PARAM;
786
  c->argc = num;
31✔
787
  c->argv = pcalloc(c->pool, (num+1) * sizeof(void *));
31✔
788

789
  argv = c->argv;
59✔
790
  va_start(ap,num);
28✔
791

792
  while (num-- > 0) {
793
    *argv++ = va_arg(ap, void *);
31✔
794
  }
795

31✔
796
  va_end(ap);
797

798
  return c;
2✔
799
}
2✔
800

2✔
801
config_rec *add_config_param_str(const char *name, unsigned int num, ...) {
2✔
802
  config_rec *c;
2✔
803
  char *arg = NULL;
804
  void **argv = NULL;
2✔
805
  va_list ap;
2✔
806

2✔
807
  c = pr_config_add(NULL, name, 0);
2✔
808
  if (c != NULL) {
2✔
809
    c->config_type = CONF_PARAM;
810
    c->argc = num;
2✔
811
    c->argv = pcalloc(c->pool, (num+1) * sizeof(char *));
2✔
812

813
    argv = c->argv;
4✔
814
    va_start(ap, num);
2✔
815

2✔
816
    while (num-- > 0) {
1✔
817
      arg = va_arg(ap, char *);
818
      if (arg) {
819
        *argv++ = pstrdup(c->pool, arg);
1✔
820

821
      } else {
822
        *argv++ = NULL;
823
      }
2✔
824
    }
825

826
    va_end(ap);
2✔
827
  }
828

829
  return c;
2✔
830
}
831

2✔
832
config_rec *pr_conf_add_server_config_param_str(server_rec *s, const char *name,
2✔
833
    unsigned int num, ...) {
2✔
834
  config_rec *c;
2✔
835
  char *arg = NULL;
836
  void **argv = NULL;
2✔
837
  va_list ap;
2✔
838

839
  c = pr_config_add(s, name, 0);
840
  if (c == NULL) {
841
    return NULL;
1✔
842
  }
1✔
843

1✔
844
  c->config_type = CONF_PARAM;
845
  c->argc = num;
1✔
846
  c->argv = pcalloc(c->pool, (num+1) * sizeof(char *));
1✔
847

848
  argv = c->argv;
2✔
849
  va_start(ap, num);
1✔
850

1✔
851
  while (num-- > 0) {
1✔
852
    arg = va_arg(ap, char *);
853
    if (arg) {
854
      *argv++ = pstrdup(c->pool, arg);
×
855

856
    } else {
857
      *argv++ = NULL;
858
    }
1✔
859
  }
1✔
860

861
  va_end(ap);
862
  return c;
3✔
863
}
3✔
864

3✔
865
config_rec *add_config_param(const char *name, unsigned int num, ...) {
866
  config_rec *c;
3✔
867
  va_list ap;
1✔
868

1✔
869
  if (name == NULL) {
870
    errno = EINVAL;
871
    return NULL;
2✔
872
  }
2✔
873

2✔
874
  c = pr_config_add(NULL, name, 0);
875
  if (c) {
2✔
876
    void **argv;
2✔
877

2✔
878
    c->config_type = CONF_PARAM;
879
    c->argc = num;
2✔
880
    c->argv = pcalloc(c->pool, (num+1) * sizeof(void*));
2✔
881

882
    argv = c->argv;
4✔
883
    va_start(ap, num);
2✔
884

885
    while (num-- > 0) {
886
      *argv++ = va_arg(ap, void *);
2✔
887
    }
888

889
    va_end(ap);
890
  }
891

892
  return c;
93✔
893
}
93✔
894

93✔
895
unsigned int pr_config_get_id(const char *name) {
896
  const void *ptr = NULL;
93✔
897
  unsigned int id = 0;
1✔
898

1✔
899
  if (name == NULL) {
900
    errno = EINVAL;
901
    return 0;
92✔
902
  }
33✔
903

33✔
904
  if (config_tab == NULL) {
905
    errno = EPERM;
906
    return 0;
59✔
907
  }
59✔
908

21✔
909
  ptr = pr_table_get(config_tab, name, NULL);
21✔
910
  if (ptr == NULL) {
911
    errno = ENOENT;
912
    return 0;
38✔
913
  }
38✔
914

915
  id = *((unsigned int *) ptr);
916
  return id;
47✔
917
}
47✔
918

47✔
919
unsigned int pr_config_set_id(const char *name) {
920
  unsigned int *ptr = NULL;
47✔
921
  unsigned int id;
1✔
922

1✔
923
  if (!name) {
924
    errno = EINVAL;
925
    return 0;
46✔
926
  }
7✔
927

7✔
928
  if (!config_tab) {
929
    errno = EPERM;
930
    return 0;
39✔
931
  }
39✔
932

933
  ptr = pr_table_pcalloc(config_tab, sizeof(unsigned int));
39✔
934
  *ptr = ++config_id;
8✔
935

8✔
936
  if (pr_table_add(config_tab, name, ptr, sizeof(unsigned int *)) < 0) {
937
    if (errno == EEXIST) {
938
      id = pr_config_get_id(name);
×
939

×
940
    } else {
941
      if (errno == ENOSPC) {
942
        pr_log_debug(DEBUG9,
943
         "error adding '%s' to config ID table: table is full", name);
×
944

945
      } else {
946
        pr_log_debug(DEBUG9, "error adding '%s' to config ID table: %s",
947
          name, strerror(errno));
×
948
      }
949

950
      return 0;
951
    }
31✔
952

953
  } else {
954
    id = *ptr;
955
  }
956

957
  return id;
42✔
958
}
42✔
959

960
void init_config(void) {
961
  unsigned int maxents;
42✔
962

×
963
  /* Make sure global_config_pool is destroyed */
×
964
  if (global_config_pool) {
965
    destroy_pool(global_config_pool);
966
    global_config_pool = NULL;
42✔
967
  }
968

969
  if (config_tab != NULL) {
970
    /* Clear the existing config ID table.  This needs to happen when proftpd
3✔
971
     * is restarting.
3✔
972
     */
973
    (void) pr_table_empty(config_tab);
3✔
974
    (void) pr_table_free(config_tab);
975

976
    config_tab = pr_table_alloc(config_tab_pool, 0);
977

978
    /* Reset the ID counter as well.  Otherwise, an exceedingly long-lived
979
     * proftpd, restarted many times, has the possibility of overflowing
3✔
980
     * the counter data type.
981
     */
982
    config_id = 0;
39✔
983

39✔
984
  } else {
39✔
985
    config_tab_pool = make_sub_pool(permanent_pool);
986
    pr_pool_tag(config_tab_pool, "Config Table Pool");
987
    config_tab = pr_table_alloc(config_tab_pool, 0);
988
  }
989

990
  /* Increase the max "size" of the table; some configurations can lead
42✔
991
   * to a large number of configuration directives.
992
   */
42✔
993
  maxents = 32768;
×
994

×
995
  if (pr_table_ctl(config_tab, PR_TABLE_CTL_SET_MAX_ENTS, &maxents) < 0) {
996
    pr_log_debug(DEBUG2, "error setting config ID table max size to %u: %s",
42✔
997
      maxents, strerror(errno));
998
  }
999
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc