/src/dovecot/src/lib-var-expand/expansion-program.c
Line | Count | Source |
1 | | /* Copyright (c) Dovecot authors, see top-level COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "array.h" |
5 | | #include "str.h" |
6 | | #include "strescape.h" |
7 | | #include "hex-binary.h" |
8 | | #include "var-expand-private.h" |
9 | | #include "var-expand-parser-private.h" |
10 | | #include "var-expand-parser.h" |
11 | | #include "expansion.h" |
12 | | |
13 | 0 | #define MAX_PROGRAM_SIZE 8192 |
14 | | |
15 | | extern void var_expand_parser_lex_init_extra(void*, void*); |
16 | | |
17 | | static const struct var_expand_params empty_params = { |
18 | | }; |
19 | | |
20 | | int var_expand_program_create(const char *str, |
21 | | struct var_expand_program **program_r, |
22 | | const char **error_r) |
23 | 0 | { |
24 | 0 | int ret; |
25 | 0 | struct var_expand_parser_state state; |
26 | 0 | i_zero(&state); |
27 | |
|
28 | 0 | if (strlen(str) > MAX_PROGRAM_SIZE) { |
29 | 0 | *error_r = t_strdup_printf("Program size exceeds maximum of %d bytes", |
30 | 0 | MAX_PROGRAM_SIZE); |
31 | 0 | return -1; |
32 | 0 | } |
33 | | |
34 | 0 | pool_t pool = |
35 | 0 | pool_alloconly_create(MEMPOOL_GROWING"var expand program", 1024); |
36 | 0 | state.p = state.plist = p_new(pool, struct var_expand_program, 1); |
37 | 0 | state.p->pool = pool; |
38 | 0 | p_array_init(&state.variables, pool, 1); |
39 | |
|
40 | 0 | T_BEGIN { |
41 | 0 | state.str = NULL; |
42 | 0 | state.pool = |
43 | 0 | pool_allocfree_create("var expand parser"); |
44 | 0 | p_array_init(&state.variables, pool, 1); |
45 | 0 | state.input = str; |
46 | 0 | state.left = strlen(str); |
47 | 0 | var_expand_parser_lex_init_extra(&state, &state.scanner); |
48 | | /* 0 = OK, everything else = something went wrong */ |
49 | 0 | ret = var_expand_parser_parse(&state); |
50 | 0 | state.error = t_strdup(state.error); |
51 | 0 | } T_END_PASS_STR_IF(ret != 0, &state.error); |
52 | | |
53 | 0 | array_append_space(&state.variables); |
54 | 0 | state.plist->variables = array_front(&state.variables); |
55 | 0 | i_assert(state.plist->variables != NULL); |
56 | 0 | pool_unref(&state.pool); |
57 | |
|
58 | 0 | if (ret != 0) { |
59 | 0 | *error_r = state.error; |
60 | 0 | var_expand_program_free(&state.plist); |
61 | 0 | } else { |
62 | 0 | *program_r = state.plist; |
63 | 0 | } |
64 | 0 | i_assert(ret == 0 || *error_r != NULL); |
65 | | |
66 | 0 | return ret == 0 ? 0 : -1; |
67 | 0 | } |
68 | | |
69 | | void var_expand_program_dump(const struct var_expand_program *prog, string_t *dest) |
70 | 0 | { |
71 | 0 | while (prog != NULL) { |
72 | 0 | struct var_expand_statement *stmt = prog->first; |
73 | 0 | while (stmt != NULL) { |
74 | 0 | const char *or_var = ""; |
75 | 0 | if (stmt == prog->first && !prog->only_literal) |
76 | 0 | or_var = " or variable"; |
77 | 0 | str_printfa(dest, "function%s %s\n", or_var, stmt->function); |
78 | 0 | struct var_expand_parameter_iter_context *ctx = |
79 | 0 | var_expand_parameter_iter_init(stmt); |
80 | 0 | while (var_expand_parameter_iter_more(ctx)) { |
81 | 0 | const struct var_expand_parameter *par = |
82 | 0 | var_expand_parameter_iter_next(ctx); |
83 | 0 | var_expand_parameter_dump(dest, par); |
84 | 0 | } |
85 | 0 | stmt = stmt->next; |
86 | 0 | } |
87 | 0 | prog = prog->next; |
88 | 0 | } |
89 | 0 | } |
90 | | |
91 | | static void prepare_state(const struct var_expand_params *params, |
92 | | struct var_expand_state *state_r) |
93 | 0 | { |
94 | 0 | i_zero(state_r); |
95 | 0 | i_assert(params != NULL); |
96 | | |
97 | 0 | i_assert((params->table == NULL && params->tables_arr == NULL) || |
98 | 0 | (params->table != NULL && params->tables_arr == NULL) || |
99 | 0 | (params->table == NULL && params->tables_arr != NULL)); |
100 | | |
101 | 0 | i_assert((params->providers == NULL && params->providers_arr == NULL) || |
102 | 0 | (params->providers != NULL && params->providers_arr == NULL) || |
103 | 0 | (params->providers == NULL && params->providers_arr != NULL)); |
104 | | |
105 | 0 | size_t num_tables = 0; |
106 | 0 | if (params->tables_arr != NULL) { |
107 | 0 | while (params->tables_arr[num_tables] != NULL) |
108 | 0 | num_tables++; |
109 | 0 | } |
110 | 0 | size_t num_providers = 0; |
111 | 0 | if (params->providers_arr != NULL) { |
112 | 0 | while (params->providers_arr[num_providers] != NULL) |
113 | 0 | num_providers++; |
114 | 0 | } |
115 | 0 | size_t num_contexts = I_MAX(num_tables, num_providers); |
116 | | |
117 | | /* ensure contexts are properly terminated. */ |
118 | 0 | i_assert(params->contexts == NULL || |
119 | 0 | params->contexts[num_contexts] == var_expand_contexts_end); |
120 | | |
121 | 0 | state_r->params = params; |
122 | 0 | state_r->result = str_new(default_pool, 32); |
123 | 0 | state_r->transfer = str_new(default_pool, 32); |
124 | 0 | } |
125 | | |
126 | | static int |
127 | | var_expand_program_execute_one_real(const struct var_expand_program *program, |
128 | | const struct var_expand_params *params, |
129 | | struct var_expand_state *state, |
130 | | const char **error_r) |
131 | 0 | { |
132 | 0 | int ret = 0; |
133 | 0 | const struct var_expand_statement *stmt = program->first; |
134 | 0 | if (stmt == NULL) { |
135 | | /* skip empty programs */ |
136 | 0 | return 0; |
137 | 0 | } |
138 | 0 | T_BEGIN { |
139 | 0 | while (stmt != NULL) { |
140 | 0 | bool first = stmt == program->first; |
141 | 0 | if (!var_expand_execute_stmt(state, stmt, |
142 | 0 | first, error_r)) { |
143 | 0 | ret = -1; |
144 | 0 | break; |
145 | 0 | } |
146 | 0 | stmt = stmt->next; |
147 | 0 | } |
148 | 0 | } T_END_PASS_STR_IF(ret < 0, error_r); |
149 | 0 | if (ret < 0) |
150 | 0 | return ret; |
151 | 0 | if (state->transfer_binary) |
152 | 0 | var_expand_state_set_transfer(state, |
153 | 0 | binary_to_hex(state->transfer->data, state->transfer->used)); |
154 | 0 | if (state->transfer_set) { |
155 | 0 | if (!program->only_literal && params->escape_func != NULL && |
156 | 0 | !state->transfer_safe) { |
157 | 0 | str_append(state->result, params->escape_func(str_c(state->transfer), |
158 | 0 | params->escape_context)); |
159 | 0 | } else |
160 | 0 | str_append_str(state->result, state->transfer); |
161 | 0 | } else { |
162 | 0 | *error_r = t_strdup(state->delayed_error); |
163 | 0 | ret = -1; |
164 | 0 | } |
165 | 0 | var_expand_state_unset_transfer(state); |
166 | 0 | return ret; |
167 | 0 | } |
168 | | |
169 | | int var_expand_program_execute_one(string_t *dest, const struct var_expand_program *program, |
170 | | const struct var_expand_params *params, const char **error_r) |
171 | 0 | { |
172 | 0 | int ret = 0; |
173 | 0 | struct var_expand_state state; |
174 | 0 | if (params == NULL) |
175 | 0 | params = &empty_params; |
176 | 0 | prepare_state(params, &state); |
177 | |
|
178 | 0 | *error_r = NULL; |
179 | |
|
180 | 0 | ret = var_expand_program_execute_one_real(program, params, &state, error_r); |
181 | |
|
182 | 0 | if (state.delayed_error != NULL) { |
183 | 0 | *error_r = t_strdup(state.delayed_error); |
184 | 0 | ret = -1; |
185 | 0 | } |
186 | 0 | str_free(&state.transfer); |
187 | 0 | i_free(state.delayed_error); |
188 | | /* only write to dest on success */ |
189 | 0 | if (ret == 0) |
190 | 0 | str_append_str(dest, state.result); |
191 | 0 | str_free(&state.result); |
192 | 0 | i_assert(ret == 0 || *error_r != NULL); |
193 | | |
194 | 0 | return ret; |
195 | 0 | } |
196 | | |
197 | | int var_expand_program_execute(string_t *dest, const struct var_expand_program *program, |
198 | | const struct var_expand_params *params, const char **error_r) |
199 | 0 | { |
200 | 0 | int ret = 0; |
201 | 0 | struct var_expand_state state; |
202 | 0 | if (params == NULL) |
203 | 0 | params = &empty_params; |
204 | 0 | prepare_state(params, &state); |
205 | |
|
206 | 0 | *error_r = NULL; |
207 | |
|
208 | 0 | while (program != NULL) { |
209 | 0 | ret = var_expand_program_execute_one_real(program, params, &state, error_r); |
210 | 0 | if (ret == -1) |
211 | 0 | break; |
212 | 0 | program = program->next; |
213 | 0 | }; |
214 | 0 | if (state.delayed_error != NULL) { |
215 | 0 | *error_r = t_strdup(state.delayed_error); |
216 | 0 | ret = -1; |
217 | 0 | } |
218 | 0 | str_free(&state.transfer); |
219 | 0 | i_free(state.delayed_error); |
220 | | /* only write to dest on success */ |
221 | 0 | if (ret == 0) |
222 | 0 | str_append_str(dest, state.result); |
223 | 0 | str_free(&state.result); |
224 | 0 | i_assert(ret == 0 || *error_r != NULL); |
225 | | |
226 | 0 | return ret; |
227 | 0 | } |
228 | | |
229 | | const char *const * |
230 | | var_expand_program_variables(const struct var_expand_program *program) |
231 | 0 | { |
232 | 0 | return program->variables; |
233 | 0 | } |
234 | | |
235 | | bool var_expand_program_has_variable(const struct var_expand_program *program, |
236 | | const char *variable, bool first_program_only) |
237 | 0 | { |
238 | 0 | if (!first_program_only) |
239 | 0 | return str_array_find(var_expand_program_variables(program), variable); |
240 | | |
241 | 0 | const struct var_expand_statement *stmt = program->first; |
242 | 0 | if (stmt->params == NULL && strcmp(stmt->function, variable) == 0) |
243 | 0 | return TRUE; |
244 | 0 | while (stmt != NULL) { |
245 | 0 | const struct var_expand_parameter *par = stmt->params; |
246 | 0 | while (par != NULL) { |
247 | 0 | if (par->value_type == VAR_EXPAND_PARAMETER_VALUE_TYPE_VARIABLE && |
248 | 0 | strcmp(par->value.str, variable) == 0) |
249 | 0 | return TRUE; |
250 | 0 | par = par->next; |
251 | 0 | } |
252 | 0 | stmt = stmt->next; |
253 | 0 | } |
254 | 0 | return FALSE; |
255 | 0 | } |
256 | | |
257 | | void var_expand_program_free(struct var_expand_program **_program) |
258 | 0 | { |
259 | 0 | struct var_expand_program *program = *_program; |
260 | 0 | if (program == NULL) |
261 | 0 | return; |
262 | 0 | *_program = NULL; |
263 | |
|
264 | 0 | pool_unref(&program->pool); |
265 | 0 | } |
266 | | |
267 | | /* Export code */ |
268 | | |
269 | | /* Encodes numbers in 7-bit bytes, using 8th to indicate |
270 | | that the number continues. Uses little endian encoding |
271 | | to allow this. */ |
272 | | static void export_number(string_t *dest, intmax_t number) |
273 | 0 | { |
274 | 0 | unsigned char b; |
275 | | |
276 | | /* fast path - store any non-negative number that's smaller than |
277 | | 127 as number + 1, including 0. */ |
278 | 0 | if (number >= 0 && number < 0x7f) { |
279 | 0 | b = number + 1; |
280 | 0 | str_append_c(dest, b); |
281 | 0 | return; |
282 | 0 | } |
283 | | |
284 | | /* Store sign with 0x80, so we can differentiate |
285 | | from fast path. */ |
286 | 0 | if (number < 0) { |
287 | 0 | str_append_c(dest, 0x80 | '-'); |
288 | 0 | number = -number; |
289 | 0 | } else |
290 | 0 | str_append_c(dest, 0x80 | '+'); |
291 | | |
292 | | /* Store the number in 7 byte chunks |
293 | | so we can use the 8th bit for indicating |
294 | | whether the number continues. */ |
295 | 0 | while (number > 0) { |
296 | 0 | if (number > 0x7f) |
297 | 0 | b = 0x80; |
298 | 0 | else |
299 | 0 | b = 0x0; |
300 | 0 | b |= number & 0x7f; |
301 | 0 | number >>= 7; |
302 | 0 | str_append_c(dest, b); |
303 | 0 | } |
304 | 0 | } |
305 | | |
306 | | static void var_expand_program_export_one(const struct var_expand_program *program, |
307 | | string_t *dest) |
308 | 0 | { |
309 | 0 | const struct var_expand_statement *stmt = program->first; |
310 | 0 | while (stmt != NULL) { |
311 | 0 | str_append(dest, stmt->function); |
312 | 0 | str_append_c(dest, '\1'); |
313 | 0 | const struct var_expand_parameter *param = stmt->params; |
314 | 0 | while (param != NULL) { |
315 | 0 | if (param->key != NULL) |
316 | 0 | str_append(dest, param->key); |
317 | 0 | str_append_c(dest, '\1'); |
318 | 0 | switch (param->value_type) { |
319 | 0 | case VAR_EXPAND_PARAMETER_VALUE_TYPE_STRING: |
320 | 0 | str_append_c(dest, 's'); |
321 | 0 | str_append_tabescaped(dest, param->value.str); |
322 | 0 | str_append_c(dest, '\r'); |
323 | 0 | break; |
324 | 0 | case VAR_EXPAND_PARAMETER_VALUE_TYPE_INT: |
325 | 0 | str_append_c(dest, 'i'); |
326 | 0 | export_number(dest, param->value.num); |
327 | 0 | break; |
328 | 0 | case VAR_EXPAND_PARAMETER_VALUE_TYPE_VARIABLE: |
329 | 0 | str_append_c(dest, 'v'); |
330 | 0 | str_append_tabescaped(dest, param->value.str); |
331 | 0 | str_append_c(dest, '\r'); |
332 | 0 | break; |
333 | 0 | default: |
334 | 0 | i_unreached(); |
335 | 0 | } |
336 | 0 | param = param->next; |
337 | 0 | if (param != NULL) |
338 | 0 | str_append_c(dest, '\1'); |
339 | 0 | } |
340 | 0 | str_append_c(dest, '\t'); |
341 | 0 | stmt = stmt->next; |
342 | 0 | if (stmt != NULL) |
343 | 0 | str_append_c(dest, '\1'); |
344 | 0 | else |
345 | 0 | str_append_c(dest, '\t'); |
346 | 0 | } |
347 | 0 | const char *const *vars = program->variables; |
348 | |
|
349 | 0 | for (; vars != NULL && *vars != NULL; vars++) { |
350 | | /* ensure variable has no \1 in name */ |
351 | 0 | i_assert(strchr(*vars, '\1') == NULL); |
352 | 0 | str_append(dest, *vars); |
353 | 0 | str_append_c(dest, '\1'); |
354 | 0 | } |
355 | 0 | str_append_c(dest, '\t'); |
356 | 0 | } |
357 | | |
358 | | void var_expand_program_export_append(string_t *dest, |
359 | | const struct var_expand_program *program) |
360 | 0 | { |
361 | 0 | i_assert(program != NULL); |
362 | 0 | i_assert(dest != NULL); |
363 | | |
364 | 0 | while (program != NULL) { |
365 | 0 | if (program->only_literal) { |
366 | 0 | i_assert(program->first->params->value_type == |
367 | 0 | VAR_EXPAND_PARAMETER_VALUE_TYPE_STRING); |
368 | 0 | str_append_c(dest, '\1'); |
369 | 0 | str_append_tabescaped(dest, program->first->params->value.str); |
370 | 0 | str_append_c(dest, '\r'); |
371 | 0 | } else { |
372 | 0 | str_append_c(dest, '\2'); |
373 | 0 | var_expand_program_export_one(program, dest); |
374 | 0 | } |
375 | | |
376 | 0 | program = program->next; |
377 | 0 | } |
378 | 0 | } |
379 | | |
380 | | const char *var_expand_program_export(const struct var_expand_program *program) |
381 | 0 | { |
382 | 0 | string_t *dest = t_str_new(64); |
383 | 0 | var_expand_program_export_append(dest, program); |
384 | 0 | return str_c(dest); |
385 | 0 | } |
386 | | |
387 | | static void |
388 | | var_expand_program_to_string_calculate(const struct var_expand_statement *stmt, |
389 | | string_t *dest) |
390 | 0 | { |
391 | 0 | if (str_len(dest) > 0) |
392 | 0 | str_append_c(dest, ' '); |
393 | 0 | switch (stmt->params->value.num) { |
394 | 0 | case VAR_EXPAND_STATEMENT_OPER_PLUS: |
395 | 0 | str_append_c(dest, '+'); |
396 | 0 | break; |
397 | 0 | case VAR_EXPAND_STATEMENT_OPER_MINUS: |
398 | 0 | str_append_c(dest, '-'); |
399 | 0 | break; |
400 | 0 | case VAR_EXPAND_STATEMENT_OPER_STAR: |
401 | 0 | str_append_c(dest, '*'); |
402 | 0 | break; |
403 | 0 | case VAR_EXPAND_STATEMENT_OPER_SLASH: |
404 | 0 | str_append_c(dest, '/'); |
405 | 0 | break; |
406 | 0 | case VAR_EXPAND_STATEMENT_OPER_MODULO: |
407 | 0 | str_append_c(dest, '%'); |
408 | 0 | break; |
409 | 0 | case VAR_EXPAND_STATEMENT_OPER_COUNT: |
410 | 0 | default: |
411 | 0 | i_unreached(); |
412 | 0 | } |
413 | 0 | str_append_c(dest, ' '); |
414 | 0 | str_printfa(dest, "%jd", stmt->params->next->value.num); |
415 | 0 | } |
416 | | |
417 | | void |
418 | | var_expand_program_to_string_append_one(string_t *dest, |
419 | | const struct var_expand_program *program) |
420 | 0 | { |
421 | 0 | if (program->only_literal) { |
422 | 0 | i_assert(program->first->params->value_type == |
423 | 0 | VAR_EXPAND_PARAMETER_VALUE_TYPE_STRING); |
424 | 0 | str_append(dest, program->first->params->value.str); |
425 | 0 | return; |
426 | 0 | } |
427 | 0 | str_append(dest, "%{"); |
428 | 0 | const struct var_expand_statement *stmt = program->first; |
429 | 0 | while (stmt != NULL) { |
430 | 0 | bool braces = FALSE; |
431 | 0 | if (strcmp(stmt->function, "calculate") == 0) { |
432 | 0 | var_expand_program_to_string_calculate(stmt, dest); |
433 | 0 | goto next; |
434 | 0 | } |
435 | 0 | str_append(dest, stmt->function); |
436 | 0 | const struct var_expand_parameter *param = stmt->params; |
437 | 0 | if (param != NULL) { |
438 | 0 | str_append_c(dest, '('); |
439 | 0 | braces = TRUE; |
440 | 0 | } |
441 | 0 | while (param != NULL) { |
442 | 0 | if (param->key != NULL) { |
443 | 0 | str_append(dest, param->key); |
444 | 0 | str_append_c(dest, '='); |
445 | 0 | } |
446 | 0 | switch (param->value_type) { |
447 | 0 | case VAR_EXPAND_PARAMETER_VALUE_TYPE_STRING: |
448 | 0 | str_append_c(dest, '\''); |
449 | 0 | str_append_escaped(dest, param->value.str, |
450 | 0 | strlen(param->value.str)); |
451 | 0 | str_append_c(dest, '\''); |
452 | 0 | break; |
453 | 0 | case VAR_EXPAND_PARAMETER_VALUE_TYPE_INT: |
454 | 0 | str_printfa(dest, "%jd", param->value.num); |
455 | 0 | break; |
456 | 0 | case VAR_EXPAND_PARAMETER_VALUE_TYPE_VARIABLE: |
457 | 0 | str_append(dest, param->value.str); |
458 | 0 | break; |
459 | 0 | default: |
460 | 0 | i_unreached(); |
461 | 0 | } |
462 | 0 | param = param->next; |
463 | 0 | if (param != NULL) |
464 | 0 | str_append(dest, ", "); |
465 | 0 | } |
466 | 0 | if (braces) |
467 | 0 | str_append_c(dest, ')'); |
468 | 0 | next: stmt = stmt->next; |
469 | 0 | if (stmt != NULL && strcmp(stmt->function, "calculate") != 0) |
470 | 0 | str_append(dest, " | "); |
471 | 0 | } |
472 | 0 | str_append_c(dest, '}'); |
473 | 0 | } |
474 | | |
475 | | void var_expand_program_to_string_append(string_t *dest, |
476 | | const struct var_expand_program *program) |
477 | 0 | { |
478 | 0 | i_assert(program != NULL); |
479 | 0 | i_assert(dest != NULL); |
480 | | |
481 | 0 | while (program != NULL) { |
482 | 0 | var_expand_program_to_string_append_one(dest, program); |
483 | 0 | program = program->next; |
484 | 0 | } |
485 | 0 | } |
486 | | |
487 | | const char *var_expand_program_to_string_one(const struct var_expand_program *program) |
488 | 0 | { |
489 | 0 | string_t *dest = t_str_new(64); |
490 | 0 | var_expand_program_to_string_append_one(dest, program); |
491 | 0 | return str_c(dest); |
492 | 0 | } |
493 | | |
494 | | const char *var_expand_program_to_string(const struct var_expand_program *program) |
495 | 0 | { |
496 | 0 | string_t *dest = t_str_new(64); |
497 | 0 | var_expand_program_to_string_append(dest, program); |
498 | 0 | return str_c(dest); |
499 | 0 | } |
500 | | |
501 | | /* Import code */ |
502 | | |
503 | | static int extract_name(pool_t pool, const char *data, size_t size, |
504 | | const char **value_r, const char **error_r) |
505 | 0 | { |
506 | 0 | if (size == 0) { |
507 | 0 | *error_r = "Missing end of name"; |
508 | 0 | return -1; |
509 | 0 | } |
510 | | |
511 | 0 | const char *ptr = memchr(data, '\1', size); |
512 | 0 | if (ptr == NULL) { |
513 | 0 | *error_r = "Missing end of name"; |
514 | 0 | return -1; |
515 | 0 | } |
516 | 0 | size_t len = ptr - data; |
517 | 0 | if (len == 0) { |
518 | 0 | *value_r = NULL; |
519 | 0 | return 1; |
520 | 0 | } |
521 | 0 | *value_r = p_strdup_until(pool, data, ptr); |
522 | 0 | return len + 1; |
523 | 0 | } |
524 | | |
525 | | static int extract_value(pool_t pool, const char *data, size_t size, |
526 | | const char **value_r, const char **error_r) |
527 | 0 | { |
528 | 0 | if (size == 0) { |
529 | 0 | *error_r = "Missing end of string"; |
530 | 0 | return -1; |
531 | 0 | } |
532 | | |
533 | 0 | const char *ptr = memchr(data, '\r', size); |
534 | 0 | if (ptr == NULL) { |
535 | 0 | *error_r = "Missing end of string"; |
536 | 0 | return -1; |
537 | 0 | } |
538 | 0 | size_t len = ptr - data; |
539 | 0 | string_t *unescaped = str_new(pool, len); |
540 | 0 | str_append_tabunescaped(unescaped, data, len); |
541 | 0 | *value_r = str_c(unescaped); |
542 | | /* make sure we end up in right place. */ |
543 | 0 | return len + 1; |
544 | 0 | } |
545 | | |
546 | | static int extract_number(const char *data, size_t size, intmax_t *value_r, |
547 | | const char **error_r) |
548 | 0 | { |
549 | 0 | if (size == 0) { |
550 | 0 | *error_r = "Too short number"; |
551 | 0 | return -1; |
552 | 0 | } |
553 | | |
554 | 0 | const unsigned char *ptr = (const unsigned char*)data; |
555 | 0 | bool negative; |
556 | 0 | size_t len = 1; |
557 | |
|
558 | 0 | if ((*ptr & 0x80) == 0) { |
559 | | /* fast path for small positive number */ |
560 | 0 | intmax_t number = *ptr; |
561 | 0 | *value_r = number - 1; |
562 | 0 | return 1; |
563 | 0 | } |
564 | | |
565 | 0 | if (size < 2) { |
566 | 0 | *error_r = "Too short number"; |
567 | 0 | return -1; |
568 | 0 | } |
569 | | |
570 | 0 | const char sign = *ptr - 0x80; |
571 | 0 | if (sign == '+') { |
572 | 0 | negative = FALSE; |
573 | 0 | } else if (sign == '-') { |
574 | 0 | negative = TRUE; |
575 | 0 | } else { |
576 | 0 | *error_r = "Unknown number"; |
577 | 0 | return -1; |
578 | 0 | } |
579 | 0 | size--; |
580 | 0 | ptr++; |
581 | |
|
582 | 0 | intmax_t value = 0; |
583 | 0 | intmax_t shift = 0; |
584 | 0 | size_t max_size = I_MIN(size, 9); |
585 | | |
586 | | /* a number can be at most 9 bytes */ |
587 | 0 | for (size_t i = 0; i < max_size; i++) { |
588 | 0 | len++; |
589 | 0 | value |= ((*(ptr) & 0x7fLL) << shift); |
590 | | /* if high byte is set, the number continues */ |
591 | 0 | if ((*ptr & 0x80) == 0) |
592 | 0 | break; |
593 | 0 | shift += 7; |
594 | 0 | ptr++; |
595 | 0 | size--; |
596 | 0 | } |
597 | |
|
598 | 0 | if (size > 0 && (*ptr & 0x80) != 0) { |
599 | 0 | *error_r = "Unfinished number"; |
600 | 0 | return -1; |
601 | 0 | } |
602 | | |
603 | 0 | if (negative) |
604 | 0 | value = -value; |
605 | |
|
606 | 0 | *value_r = value; |
607 | |
|
608 | 0 | return len; |
609 | 0 | } |
610 | | |
611 | | #define ADVANCE_INPUT(count) \ |
612 | 0 | if (unlikely(size < (size_t)count)) { \ |
613 | 0 | *error_r = "Premature end of data"; \ |
614 | 0 | return -1; \ |
615 | 0 | }\ |
616 | 0 | data = data + (count); \ |
617 | 0 | size = size - (size_t)(count); |
618 | | |
619 | | static int var_expand_program_import_stmt(const char *data, size_t size, |
620 | | struct var_expand_program *program, |
621 | | const char **error_r) |
622 | 0 | { |
623 | 0 | const char *name; |
624 | 0 | const char *value; |
625 | 0 | size_t orig_size = size; |
626 | | |
627 | | /* normal program, starts with filter name */ |
628 | 0 | int ret = extract_name(program->pool, data, size, &name, error_r); |
629 | 0 | if (ret < 0) |
630 | 0 | return -1; |
631 | 0 | if (name == NULL) { |
632 | 0 | *error_r = "missing function name"; |
633 | 0 | return -1; |
634 | 0 | } |
635 | 0 | ADVANCE_INPUT(ret); |
636 | |
|
637 | 0 | struct var_expand_statement *stmt = |
638 | 0 | p_new(program->pool, struct var_expand_statement, 1); |
639 | |
|
640 | 0 | if (program->first == NULL) |
641 | 0 | program->first = stmt; |
642 | 0 | else { |
643 | 0 | struct var_expand_statement *ptr = program->first; |
644 | 0 | while (ptr->next != NULL) ptr = ptr->next; |
645 | 0 | ptr->next = stmt; |
646 | 0 | } |
647 | |
|
648 | 0 | stmt->function = name; |
649 | 0 | struct var_expand_parameter *prev = NULL; |
650 | 0 | int idx = -1; |
651 | |
|
652 | 0 | while (size > 0 && *data != '\t') { |
653 | 0 | struct var_expand_parameter *param = |
654 | 0 | p_new(program->pool, struct var_expand_parameter, 1); |
655 | | /* check if it's named parameter */ |
656 | 0 | if (*data == '\1') { |
657 | 0 | param->idx = ++idx; |
658 | 0 | ADVANCE_INPUT(1); |
659 | 0 | } else { |
660 | 0 | ret = extract_name(program->pool, data, size, |
661 | 0 | &name, error_r); |
662 | 0 | if (ret < 0) |
663 | 0 | return -1; |
664 | 0 | ADVANCE_INPUT(ret); |
665 | 0 | param->key = name; |
666 | 0 | } |
667 | | |
668 | 0 | if (size < 1) { |
669 | 0 | *error_r = "Premature end of data"; |
670 | 0 | return -1; |
671 | 0 | } |
672 | | |
673 | | /* check the parameter type */ |
674 | 0 | switch (*data) { |
675 | 0 | case 's': |
676 | 0 | param->value_type = |
677 | 0 | VAR_EXPAND_PARAMETER_VALUE_TYPE_STRING; |
678 | 0 | break; |
679 | 0 | case 'i': |
680 | 0 | param->value_type = |
681 | 0 | VAR_EXPAND_PARAMETER_VALUE_TYPE_INT; |
682 | 0 | break; |
683 | 0 | case 'v': |
684 | 0 | param->value_type = |
685 | 0 | VAR_EXPAND_PARAMETER_VALUE_TYPE_VARIABLE; |
686 | 0 | break; |
687 | 0 | default: |
688 | 0 | *error_r = "Unsupported parameter type"; |
689 | 0 | return -1; |
690 | 0 | } |
691 | 0 | ADVANCE_INPUT(1); |
692 | |
|
693 | 0 | if (size == 0) { |
694 | 0 | *error_r = "Premature end of data"; |
695 | 0 | return -1; |
696 | 0 | } |
697 | | |
698 | 0 | if (param->value_type == VAR_EXPAND_PARAMETER_VALUE_TYPE_STRING || |
699 | 0 | param->value_type == VAR_EXPAND_PARAMETER_VALUE_TYPE_VARIABLE) { |
700 | 0 | ret = extract_value(program->pool, data, size, |
701 | 0 | &value, error_r); |
702 | 0 | if (ret < 0) |
703 | 0 | return -1; |
704 | 0 | ADVANCE_INPUT(ret); |
705 | 0 | param->value.str = value; |
706 | 0 | } else if (param->value_type == VAR_EXPAND_PARAMETER_VALUE_TYPE_INT) { |
707 | 0 | ret = extract_number(data, size, ¶m->value.num, |
708 | 0 | error_r); |
709 | 0 | if (ret < 0) |
710 | 0 | return -1; |
711 | | |
712 | 0 | ADVANCE_INPUT(ret); |
713 | 0 | } else { |
714 | 0 | *error_r = "Unsupported value type"; |
715 | 0 | return -1; |
716 | 0 | } |
717 | | |
718 | 0 | if (prev == NULL) |
719 | 0 | stmt->params = param; |
720 | 0 | else |
721 | 0 | prev->next = param; |
722 | 0 | prev = param; |
723 | |
|
724 | 0 | if (size > 0 && *data == '\t') { |
725 | 0 | break; |
726 | 0 | } else if (size > 0 && *data == '\1') { |
727 | 0 | ADVANCE_INPUT(1); |
728 | 0 | } else { |
729 | 0 | *error_r = "Missing parameter end"; |
730 | 0 | return -1; |
731 | 0 | } |
732 | 0 | } |
733 | | |
734 | 0 | if (size < 1 || *data != '\t') |
735 | 0 | *error_r = "Missing parameter statement end"; |
736 | |
|
737 | 0 | ADVANCE_INPUT(1); |
738 | |
|
739 | 0 | return orig_size - size; |
740 | 0 | } |
741 | | |
742 | | static int var_expand_program_import_one(const char **_data, size_t *_size, |
743 | | struct var_expand_program *program, |
744 | | const char **error_r) |
745 | 0 | { |
746 | 0 | const char *data = *_data; |
747 | 0 | size_t size = *_size; |
748 | 0 | const char *value; |
749 | 0 | int ret; |
750 | | |
751 | | /* Only literal */ |
752 | 0 | if (*data == '\1') { |
753 | 0 | ADVANCE_INPUT(1); |
754 | 0 | ret = extract_value(program->pool, data, size, &value, error_r); |
755 | 0 | if (ret < 0) |
756 | 0 | return -1; |
757 | 0 | ADVANCE_INPUT(ret); |
758 | | |
759 | | /* just literal data */ |
760 | 0 | struct var_expand_statement *stmt = |
761 | 0 | p_new(program->pool, struct var_expand_statement, 1); |
762 | 0 | struct var_expand_parameter *param = |
763 | 0 | p_new(program->pool, struct var_expand_parameter, 1); |
764 | 0 | param->idx = 0; |
765 | 0 | param->value_type = VAR_EXPAND_PARAMETER_VALUE_TYPE_STRING; |
766 | 0 | param->value.str = value; |
767 | 0 | stmt->params = param; |
768 | 0 | stmt->function = "literal"; |
769 | 0 | program->first = stmt; |
770 | 0 | program->only_literal = TRUE; |
771 | | /* A full program */ |
772 | 0 | } else if (*data == '\2') { |
773 | 0 | ADVANCE_INPUT(1); |
774 | 0 | while (size > 0 && *data != '\t') { |
775 | 0 | int ret = var_expand_program_import_stmt(data, size, program, error_r); |
776 | 0 | if (ret < 0) |
777 | 0 | return -1; |
778 | 0 | ADVANCE_INPUT(ret); |
779 | 0 | if (size > 0 && *data == '\t') { |
780 | 0 | ADVANCE_INPUT(1); |
781 | 0 | break; |
782 | 0 | } else if (size == 0 || *data != '\1') { |
783 | 0 | *error_r = "Missing statement end"; |
784 | 0 | return -1; |
785 | 0 | } |
786 | 0 | ADVANCE_INPUT(1); |
787 | 0 | } |
788 | | /* And finally there should be variables */ |
789 | 0 | if (size > 0 && *data != '\t') { |
790 | 0 | const char *ptr = memchr(data, '\t', size); |
791 | 0 | if (ptr == NULL) { |
792 | 0 | *error_r = "Missing variables end"; |
793 | 0 | return -1; |
794 | 0 | } |
795 | 0 | size_t len = ptr - data; |
796 | | /* ensure data is properly terminated, since |
797 | | p_strsplit() expects a NUL terminated string. */ |
798 | 0 | const char *variables = t_strdup_until(data, ptr); |
799 | 0 | program->variables = (const char *const *) |
800 | 0 | p_strsplit(program->pool, variables, "\1"); |
801 | 0 | ADVANCE_INPUT(len + 1); |
802 | 0 | } else { |
803 | 0 | ADVANCE_INPUT(1); |
804 | 0 | } |
805 | 0 | } else { |
806 | 0 | *error_r = "Unknown input"; |
807 | 0 | return -1; |
808 | 0 | } |
809 | 0 | *_data = data; |
810 | 0 | *_size = size; |
811 | |
|
812 | 0 | return 0; |
813 | 0 | } |
814 | | |
815 | | int var_expand_program_import_sized(const char *data, size_t size, |
816 | | struct var_expand_program **program_r, |
817 | | const char **error_r) |
818 | 0 | { |
819 | 0 | i_assert(data != NULL); |
820 | | |
821 | | /* The absolute minimum program is \2 \t or \1 \r. */ |
822 | 0 | if (size < 2) { |
823 | 0 | *error_r = "Too short"; |
824 | 0 | return -1; |
825 | 0 | } |
826 | | |
827 | 0 | pool_t pool = pool_alloconly_create(MEMPOOL_GROWING"var expand program", size); |
828 | 0 | struct var_expand_program *prev = NULL; |
829 | 0 | struct var_expand_program *first = NULL; |
830 | 0 | int ret; |
831 | |
|
832 | 0 | while (size > 0) { |
833 | 0 | struct var_expand_program *program = |
834 | 0 | p_new(pool, struct var_expand_program, 1); |
835 | 0 | program->pool = pool; |
836 | 0 | T_BEGIN { |
837 | 0 | ret = var_expand_program_import_one(&data, &size, |
838 | 0 | program, error_r); |
839 | 0 | } T_END; |
840 | 0 | if (ret < 0) |
841 | 0 | break; |
842 | 0 | if (first == NULL) |
843 | 0 | first = program; |
844 | 0 | if (prev != NULL) |
845 | 0 | prev->next = program; |
846 | 0 | prev = program; |
847 | 0 | } |
848 | | |
849 | 0 | if (ret < 0) |
850 | 0 | pool_unref(&pool); |
851 | 0 | else |
852 | 0 | *program_r = first; |
853 | |
|
854 | 0 | return ret; |
855 | 0 | } |
856 | | |
857 | | int var_expand_program_import(const char *data, |
858 | | struct var_expand_program **program_r, |
859 | | const char **error_r) |
860 | 0 | { |
861 | 0 | i_assert(data != NULL); |
862 | 0 | return var_expand_program_import_sized(data, strlen(data), program_r, |
863 | 0 | error_r); |
864 | 0 | } |