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

proftpd / proftpd / 28259826111

26 Jun 2026 07:20PM UTC coverage: 93.032% (+0.6%) from 92.469%
28259826111

push

github

51363 of 55210 relevant lines covered (93.03%)

200.05 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-2026 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, see <https://www.gnu.org/licenses/>.
17
 *
18
 * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu
19
 * and other respective copyright holders give permission to link this program
20
 * with OpenSSL, and distribute the resulting executable, without including
21
 * the source code for OpenSSL in the source distribution.
22
 */
23

24
/* Configuration database implementation. */
25

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

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

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

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

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

41
static config_rec *last_param_ptr = NULL;
42

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

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

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

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

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

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

66
  return c;
67
}
68

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

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

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

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

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

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

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

44✔
100
  return c;
101
}
44✔
102

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

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

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

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

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

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

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

43✔
134
  return c;
135
}
43✔
136

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

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

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

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

158
  c = pr_parser_config_ctxt_get();
159

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

×
165
  } else {
166
    parent = NULL;
167

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

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

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

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

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

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

6✔
188
  return c;
189
}
6✔
190

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

296
  return type;
297
}
298

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

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

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

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

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

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

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

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

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

364
  return 0;
365
}
4✔
366

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3✔
472
            if (merge) {
473
              (void) copy_config_from(c, dst);
474
            }
475

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

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

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

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

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

86✔
526
  if (prev == NULL) {
527
    prev = top;
528
  }
96✔
529

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

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

539
      pr_signals_handle();
540

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

642
  } while (TRUE);
40✔
643

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

739
    pr_signals_handle();
16✔
740

741
    found++;
16✔
742

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

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

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

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

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

766
  return found;
767
}
16✔
768

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

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

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

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

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

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

795
  va_end(ap);
31✔
796

797
  return c;
798
}
2✔
799

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

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

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

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

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

825
    va_end(ap);
826
  }
2✔
827

828
  return c;
829
}
2✔
830

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

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

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

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

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

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

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

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

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

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

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

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

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

888
    va_end(ap);
889
  }
890

891
  return c;
892
}
93✔
893

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

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

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

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

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

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

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

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

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

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

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

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

949
      return 0;
950
    }
951

31✔
952
  } else {
953
    id = *ptr;
954
  }
955

956
  return id;
957
}
42✔
958

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

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

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

975
    config_tab = pr_table_alloc(config_tab_pool, 0);
976

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

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

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

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

© 2026 Coveralls, Inc