Coverage Report

Created: 2025-10-31 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/proftpd/src/var.c
Line
Count
Source
1
/*
2
 * ProFTPD - FTP server daemon
3
 * Copyright (c) 2004-2016 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, The ProFTPD Project team and other respective
20
 * copyright holders give permission to link this program with OpenSSL, and
21
 * distribute the resulting executable, without including the source code for
22
 * OpenSSL in the source distribution.
23
 */
24
25
/* Variables API implementation */
26
27
#include "conf.h"
28
29
struct var {
30
  int v_type;
31
  const char *v_desc;
32
  void *v_val;
33
  void *v_data;
34
  size_t v_datasz;
35
};
36
37
static pool *var_pool = NULL;
38
static pr_table_t *var_tab = NULL;
39
40
typedef const char *(*var_vstr_cb)(void *, size_t);
41
42
static const char *trace_channel = "var";
43
44
/* Public API
45
 */
46
47
0
int pr_var_delete(const char *name) {
48
0
  if (var_tab == NULL) {
49
0
    errno = EPERM;
50
0
    return -1;
51
0
  }
52
53
0
  if (name == NULL) {
54
0
    errno = EINVAL;
55
0
    return -1;
56
0
  }
57
58
0
  return pr_table_remove(var_tab, name, NULL) ? 0 : -1;
59
0
}
60
61
0
int pr_var_exists(const char *name) {
62
0
  if (var_tab == NULL) {
63
0
    errno = EPERM;
64
0
    return -1;
65
0
  }
66
67
0
  if (name == NULL) {
68
0
    errno = EINVAL;
69
0
    return -1;
70
0
  }
71
72
0
  return pr_table_exists(var_tab, name) > 0 ? TRUE : FALSE;
73
0
}
74
75
0
const char *pr_var_get(const char *name) {
76
0
  const struct var *v = NULL;
77
78
0
  if (var_tab == NULL) {
79
0
    errno = EPERM;
80
0
    return NULL;
81
0
  }
82
83
0
  if (name == NULL) {
84
0
    errno = EINVAL;
85
0
    return NULL;
86
0
  }
87
88
0
  v = pr_table_get(var_tab, name, NULL);
89
0
  if (v == NULL) {
90
0
    int xerrno = errno;
91
92
0
    pr_trace_msg(trace_channel, 8, "no name '%s' found in registry: %s", name,
93
0
      strerror(xerrno));
94
95
0
    errno = xerrno;
96
0
    return NULL;
97
0
  }
98
99
0
  switch (v->v_type) {
100
0
    case PR_VAR_TYPE_STR:
101
0
      pr_trace_msg(trace_channel, 19, "found STR for '%s'", name);
102
0
      return (const char *) v->v_val;
103
104
0
    case PR_VAR_TYPE_FUNC:
105
0
      pr_trace_msg(trace_channel, 19, "found FUNC for '%s'", name);
106
0
      return ((var_vstr_cb) v->v_val)(v->v_data, v->v_datasz);
107
108
0
    default:
109
      /* Pass through to the error case. */
110
0
      pr_trace_msg(trace_channel, 9,
111
0
        "unknown var type (%d) found for name '%s'", v->v_type, name);
112
0
  }
113
114
0
  errno = EINVAL;
115
0
  return NULL;
116
0
}
117
118
0
const char *pr_var_next(const char **desc) {
119
0
  const char *name;
120
0
  const struct var *v;
121
122
0
  if (var_tab == NULL) {
123
0
    errno = EPERM;
124
0
    return NULL;
125
0
  }
126
127
0
  name = pr_table_next(var_tab);
128
0
  if (name == NULL) {
129
0
    return NULL;
130
0
  }
131
132
0
  v = pr_table_get(var_tab, name, NULL);
133
0
  if (v != NULL &&
134
0
      desc != NULL) {
135
0
    *desc = v->v_desc;
136
0
  }
137
138
0
  return name;
139
0
}
140
141
0
void pr_var_rewind(void) {
142
0
  if (var_tab != NULL) {
143
0
    pr_table_rewind(var_tab);
144
0
  }
145
0
}
146
147
int pr_var_set(pool *p, const char *name, const char *desc, int vtype,
148
0
    void *val, void *data, size_t datasz) {
149
0
  struct var *v;
150
0
  size_t namelen = 0;
151
152
0
  if (var_tab == NULL) {
153
0
    errno = EPERM;
154
0
    return -1;
155
0
  }
156
157
0
  if (p == NULL ||
158
0
      name == NULL ||
159
0
      val == NULL) {
160
0
    errno = EINVAL;
161
0
    return -1;
162
0
  }
163
164
  /* The length of the key must be greater than 3 characters (for "%{}"). */
165
0
  namelen = strlen(name);
166
0
  if (namelen < 4) {
167
0
    errno = EINVAL;
168
0
    return -1;
169
0
  }
170
171
  /* If the given variable type is not recognized, reject. */
172
0
  if (vtype != PR_VAR_TYPE_STR &&
173
0
      vtype != PR_VAR_TYPE_FUNC) {
174
0
    errno = EINVAL;
175
0
    return -1;
176
0
  }
177
178
  /* Specifying data, but no length for that data, is an error. */
179
0
  if (data != NULL &&
180
0
      datasz == 0) {
181
0
    errno = EINVAL;
182
0
    return -1;
183
0
  }
184
185
  /* Specifying no data, but providing a non-zero length for that data, is an
186
   * error.
187
   */
188
0
  if (data == NULL &&
189
0
      datasz > 0) {
190
0
    errno = EINVAL;
191
0
    return -1;
192
0
  }
193
194
  /* Variable names MUST start with '%{', and end in '}'. */
195
0
  if (strncmp(name, "%{", 2) != 0 ||
196
0
      name[namelen-1] != '}') {
197
0
    errno = EINVAL;
198
0
    return -1;
199
0
  }
200
201
  /* Remove any previously registered value for this name.  For names whose
202
   * values change rapidly (e.g. session.xfer.total_bytes), a callback
203
   * function should be used, rather than always setting the same name as an
204
   * update; using a callback avoids the memory consumption that setting does
205
   * (set always allocates a new struct var *).
206
   */
207
0
  (void) pr_var_delete(name);
208
209
  /* Note: if var_pool was used for allocating the struct var *, rather
210
   * than the given pool, then deleting an entry would not necessarily
211
   * lead to such memory consumption (assuming it would even be a problem).
212
   * However, if this was the case, then a churn counter would be needed,
213
   * and var_pool would need to be churned occasionally to limit memory
214
   * growth.
215
   */
216
217
0
  switch (vtype) {
218
0
    case PR_VAR_TYPE_STR:
219
0
      v = pcalloc(p, sizeof(struct var));
220
221
0
      if (desc) {
222
0
        v->v_desc = (const char *) pstrdup(p, desc);
223
0
      }
224
225
0
      v->v_type = PR_VAR_TYPE_STR;
226
0
      v->v_val = pstrdup(p, (char *) val);
227
0
      v->v_datasz = strlen((char *) val);
228
0
      break;
229
230
0
    case PR_VAR_TYPE_FUNC:
231
0
      v = pcalloc(p, sizeof(struct var));
232
233
0
      if (desc) {
234
0
        v->v_desc = (const char *) pstrdup(p, desc);
235
0
      }
236
237
0
      v->v_type = PR_VAR_TYPE_FUNC;
238
0
      v->v_val = val;
239
240
0
      if (data) {
241
0
        v->v_data = data;
242
0
        v->v_datasz = datasz;
243
0
      }
244
245
0
      break;
246
247
0
    default:
248
0
      errno = EINVAL;
249
0
      return -1;
250
0
  }
251
252
0
  return pr_table_add(var_tab, name, v, sizeof(struct var));
253
0
}
254
255
0
int var_init(void) {
256
257
0
  if (var_pool == NULL) {
258
0
    var_pool = make_sub_pool(permanent_pool);
259
0
    pr_pool_tag(var_pool, "Variables Pool");
260
0
  }
261
262
0
  if (var_tab == NULL) {
263
0
    var_tab = pr_table_alloc(var_pool, 0);
264
0
  }
265
266
0
  return 0;
267
0
}
268
269
0
int var_free(void) {
270
0
  if (var_pool) {
271
0
    if (var_tab) {
272
0
      pr_table_empty(var_tab);
273
0
      pr_table_free(var_tab);
274
0
    }
275
276
0
    destroy_pool(var_pool);
277
0
    var_pool = NULL;
278
0
    var_tab = NULL;
279
0
  }
280
281
0
  return 0;
282
0
}