• 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

93.44
/src/var.c
1
/*
2
 * ProFTPD - FTP server daemon
3
 * Copyright (c) 2004-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, The ProFTPD Project team and other respective
19
 * copyright holders give permission to link this program with OpenSSL, and
20
 * distribute the resulting executable, without including the source code for
21
 * OpenSSL in the source distribution.
22
 */
23

24
/* Variables API implementation */
25

26
#include "conf.h"
27

28
struct var {
29
  int v_type;
30
  const char *v_desc;
31
  void *v_val;
32
  void *v_data;
33
  size_t v_datasz;
34
};
35

36
static pool *var_pool = NULL;
37
static pr_table_t *var_tab = NULL;
38

39
typedef const char *(*var_vstr_cb)(void *, size_t);
40

41
static const char *trace_channel = "var";
42

43
/* Public API
44
 */
45

46
int pr_var_delete(const char *name) {
47
  if (var_tab == NULL) {
15✔
48
    errno = EPERM;
15✔
49
    return -1;
1✔
50
  }
1✔
51

52
  if (name == NULL) {
53
    errno = EINVAL;
14✔
54
    return -1;
1✔
55
  }
1✔
56

57
  return pr_table_remove(var_tab, name, NULL) ? 0 : -1;
58
}
13✔
59

60
int pr_var_exists(const char *name) {
61
  if (var_tab == NULL) {
4✔
62
    errno = EPERM;
4✔
63
    return -1;
1✔
64
  }
1✔
65

66
  if (name == NULL) {
67
    errno = EINVAL;
3✔
68
    return -1;
1✔
69
  }
1✔
70

71
  return pr_table_exists(var_tab, name) > 0 ? TRUE : FALSE;
72
}
2✔
73

74
const char *pr_var_get(const char *name) {
75
  const struct var *v = NULL;
15✔
76

15✔
77
  if (var_tab == NULL) {
78
    errno = EPERM;
15✔
79
    return NULL;
9✔
80
  }
9✔
81

82
  if (name == NULL) {
83
    errno = EINVAL;
6✔
84
    return NULL;
1✔
85
  }
1✔
86

87
  v = pr_table_get(var_tab, name, NULL);
88
  if (v == NULL) {
5✔
89
    int xerrno = errno;
5✔
90

2✔
91
    pr_trace_msg(trace_channel, 8, "no name '%s' found in registry: %s", name,
92
      strerror(xerrno));
2✔
93

94
    errno = xerrno;
95
    return NULL;
2✔
96
  }
2✔
97

98
  switch (v->v_type) {
99
    case PR_VAR_TYPE_STR:
3✔
100
      pr_trace_msg(trace_channel, 19, "found STR for '%s'", name);
2✔
101
      return (const char *) v->v_val;
2✔
102

2✔
103
    case PR_VAR_TYPE_FUNC:
104
      pr_trace_msg(trace_channel, 19, "found FUNC for '%s'", name);
1✔
105
      return ((var_vstr_cb) v->v_val)(v->v_data, v->v_datasz);
1✔
106

1✔
107
    default:
108
      /* Pass through to the error case. */
×
109
      pr_trace_msg(trace_channel, 9,
110
        "unknown var type (%d) found for name '%s'", v->v_type, name);
×
111
  }
112

113
  errno = EINVAL;
114
  return NULL;
×
115
}
×
116

117
const char *pr_var_next(const char **desc) {
118
  const char *name;
8✔
119
  const struct var *v;
8✔
120

8✔
121
  if (var_tab == NULL) {
122
    errno = EPERM;
8✔
123
    return NULL;
1✔
124
  }
1✔
125

126
  name = pr_table_next(var_tab);
127
  if (name == NULL) {
7✔
128
    return NULL;
7✔
129
  }
130

131
  v = pr_table_get(var_tab, name, NULL);
132
  if (v != NULL &&
3✔
133
      desc != NULL) {
3✔
134
    *desc = v->v_desc;
3✔
135
  }
3✔
136

137
  return name;
138
}
139

140
void pr_var_rewind(void) {
141
  if (var_tab != NULL) {
3✔
142
    pr_table_rewind(var_tab);
3✔
143
  }
2✔
144
}
145

3✔
146
int pr_var_set(pool *p, const char *name, const char *desc, int vtype,
147
    void *val, void *data, size_t datasz) {
20✔
148
  struct var *v;
149
  size_t namelen = 0;
20✔
150

20✔
151
  if (var_tab == NULL) {
152
    errno = EPERM;
20✔
153
    return -1;
1✔
154
  }
1✔
155

156
  if (p == NULL ||
157
      name == NULL ||
19✔
158
      val == NULL) {
19✔
159
    errno = EINVAL;
160
    return -1;
3✔
161
  }
3✔
162

163
  /* The length of the key must be greater than 3 characters (for "%{}"). */
164
  namelen = strlen(name);
165
  if (namelen < 4) {
16✔
166
    errno = EINVAL;
16✔
167
    return -1;
2✔
168
  }
2✔
169

170
  /* If the given variable type is not recognized, reject. */
171
  if (vtype != PR_VAR_TYPE_STR &&
172
      vtype != PR_VAR_TYPE_FUNC) {
14✔
173
    errno = EINVAL;
174
    return -1;
3✔
175
  }
3✔
176

177
  /* Specifying data, but no length for that data, is an error. */
178
  if (data != NULL &&
179
      datasz == 0) {
11✔
180
    errno = EINVAL;
11✔
181
    return -1;
1✔
182
  }
1✔
183

184
  /* Specifying no data, but providing a non-zero length for that data, is an
185
   * error.
186
   */
187
  if (data == NULL &&
188
      datasz > 0) {
10✔
189
    errno = EINVAL;
10✔
190
    return -1;
1✔
191
  }
1✔
192

193
  /* Variable names MUST start with '%{', and end in '}'. */
194
  if (strncmp(name, "%{", 2) != 0 ||
195
      name[namelen-1] != '}') {
9✔
196
    errno = EINVAL;
9✔
197
    return -1;
×
198
  }
×
199

200
  /* Remove any previously registered value for this name.  For names whose
201
   * values change rapidly (e.g. session.xfer.total_bytes), a callback
202
   * function should be used, rather than always setting the same name as an
203
   * update; using a callback avoids the memory consumption that setting does
204
   * (set always allocates a new struct var *).
205
   */
206
  (void) pr_var_delete(name);
207

9✔
208
  /* Note: if var_pool was used for allocating the struct var *, rather
209
   * than the given pool, then deleting an entry would not necessarily
210
   * lead to such memory consumption (assuming it would even be a problem).
211
   * However, if this was the case, then a churn counter would be needed,
212
   * and var_pool would need to be churned occasionally to limit memory
213
   * growth.
214
   */
215

216
  switch (vtype) {
217
    case PR_VAR_TYPE_STR:
9✔
218
      v = pcalloc(p, sizeof(struct var));
7✔
219

7✔
220
      if (desc) {
221
        v->v_desc = (const char *) pstrdup(p, desc);
7✔
222
      }
3✔
223

224
      v->v_type = PR_VAR_TYPE_STR;
225
      v->v_val = pstrdup(p, (char *) val);
7✔
226
      v->v_datasz = strlen((char *) val);
7✔
227
      break;
7✔
228

7✔
229
    case PR_VAR_TYPE_FUNC:
230
      v = pcalloc(p, sizeof(struct var));
2✔
231

2✔
232
      if (desc) {
233
        v->v_desc = (const char *) pstrdup(p, desc);
2✔
234
      }
2✔
235

236
      v->v_type = PR_VAR_TYPE_FUNC;
237
      v->v_val = val;
2✔
238

2✔
239
      if (data) {
240
        v->v_data = data;
2✔
241
        v->v_datasz = datasz;
×
242
      }
×
243

244
      break;
245

246
    default:
247
      errno = EINVAL;
248
      return -1;
249
  }
250

251
  return pr_table_add(var_tab, name, v, sizeof(struct var));
252
}
9✔
253

254
int var_init(void) {
255

79✔
256
  if (var_pool == NULL) {
257
    var_pool = make_sub_pool(permanent_pool);
79✔
258
    pr_pool_tag(var_pool, "Variables Pool");
79✔
259
  }
79✔
260

261
  if (var_tab == NULL) {
262
    var_tab = pr_table_alloc(var_pool, 0);
79✔
263
  }
79✔
264

265
  return 0;
266
}
79✔
267

268
int var_free(void) {
269
  if (var_pool) {
79✔
270
    if (var_tab) {
79✔
271
      pr_table_empty(var_tab);
79✔
272
      pr_table_free(var_tab);
79✔
273
    }
79✔
274

275
    destroy_pool(var_pool);
276
    var_pool = NULL;
79✔
277
    var_tab = NULL;
79✔
278
  }
79✔
279

280
  return 0;
281
}
79✔
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