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 | } |