/src/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-common.c
Line | Count | Source |
1 | | /* Copyright (c) Pigeonhole authors, see top-level COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "hash.h" |
5 | | #include "str.h" |
6 | | #include "array.h" |
7 | | #include "settings.h" |
8 | | |
9 | | #include "sieve-common.h" |
10 | | |
11 | | #include "sieve-ast.h" |
12 | | #include "sieve-binary.h" |
13 | | #include "sieve-code.h" |
14 | | #include "sieve-objects.h" |
15 | | #include "sieve-match-types.h" |
16 | | |
17 | | #include "sieve-commands.h" |
18 | | #include "sieve-validator.h" |
19 | | #include "sieve-generator.h" |
20 | | #include "sieve-dump.h" |
21 | | #include "sieve-interpreter.h" |
22 | | |
23 | | #include "ext-variables-common.h" |
24 | | #include "ext-variables-name.h" |
25 | | #include "ext-variables-modifiers.h" |
26 | | |
27 | | /* |
28 | | * Limits |
29 | | */ |
30 | | |
31 | | unsigned int |
32 | | sieve_variables_get_max_scope_count(const struct sieve_extension *var_ext) |
33 | 0 | { |
34 | 0 | const struct ext_variables_context *extctx = |
35 | 0 | ext_variables_get_context(var_ext); |
36 | |
|
37 | 0 | return extctx->set->max_scope_count; |
38 | 0 | } |
39 | | |
40 | | size_t sieve_variables_get_max_value_size( |
41 | | const struct sieve_extension *var_ext) |
42 | 0 | { |
43 | 0 | const struct ext_variables_context *extctx = |
44 | 0 | ext_variables_get_context(var_ext); |
45 | |
|
46 | 0 | return extctx->set->max_value_size; |
47 | 0 | } |
48 | | |
49 | | /* |
50 | | * Extension configuration |
51 | | */ |
52 | | |
53 | | int ext_variables_load(const struct sieve_extension *ext, void **context_r) |
54 | 0 | { |
55 | 0 | struct sieve_instance *svinst = ext->svinst; |
56 | 0 | const struct ext_variables_settings *set; |
57 | 0 | struct ext_variables_context *extctx; |
58 | 0 | const char *error; |
59 | |
|
60 | 0 | if (settings_get(svinst->event, &ext_variables_setting_parser_info, 0, |
61 | 0 | &set, &error) < 0) { |
62 | 0 | e_error(svinst->event, "%s", error); |
63 | 0 | return -1; |
64 | 0 | } |
65 | | |
66 | 0 | extctx = i_new(struct ext_variables_context, 1); |
67 | 0 | extctx->set = set; |
68 | |
|
69 | 0 | *context_r = extctx; |
70 | 0 | return 0; |
71 | 0 | } |
72 | | |
73 | | void ext_variables_unload(const struct sieve_extension *ext) |
74 | 0 | { |
75 | 0 | struct ext_variables_context *extctx = ext->context; |
76 | |
|
77 | 0 | if (extctx == NULL) |
78 | 0 | return; |
79 | 0 | settings_free(extctx->set); |
80 | 0 | i_free(extctx); |
81 | 0 | } |
82 | | |
83 | | const struct ext_variables_context * |
84 | | ext_variables_get_context(const struct sieve_extension *var_ext) |
85 | 0 | { |
86 | 0 | const struct ext_variables_context *extctx = var_ext->context; |
87 | |
|
88 | 0 | i_assert(var_ext->def == &variables_extension); |
89 | 0 | return extctx; |
90 | 0 | } |
91 | | |
92 | | /* |
93 | | * Variable scope |
94 | | */ |
95 | | |
96 | | struct sieve_variable_scope { |
97 | | pool_t pool; |
98 | | int refcount; |
99 | | |
100 | | struct sieve_instance *svinst; |
101 | | const struct sieve_extension *var_ext; |
102 | | const struct sieve_extension *ext; |
103 | | |
104 | | struct sieve_variable *error_var; |
105 | | |
106 | | HASH_TABLE(const char *, struct sieve_variable *) variables; |
107 | | ARRAY(struct sieve_variable *) variable_index; |
108 | | }; |
109 | | |
110 | | struct sieve_variable_scope_binary { |
111 | | struct sieve_variable_scope *scope; |
112 | | |
113 | | unsigned int count; |
114 | | struct sieve_binary_block *sblock; |
115 | | sieve_size_t address; |
116 | | }; |
117 | | |
118 | | struct sieve_variable_scope_iter { |
119 | | struct sieve_variable_scope *scope; |
120 | | struct hash_iterate_context *hctx; |
121 | | }; |
122 | | |
123 | | struct sieve_variable_scope * |
124 | | sieve_variable_scope_create(struct sieve_instance *svinst, |
125 | | const struct sieve_extension *var_ext, |
126 | | const struct sieve_extension *ext) |
127 | 0 | { |
128 | 0 | struct sieve_variable_scope *scope; |
129 | 0 | pool_t pool; |
130 | |
|
131 | 0 | i_assert(var_ext->def == &variables_extension); |
132 | | |
133 | 0 | pool = pool_alloconly_create("sieve_variable_scope", 4096); |
134 | 0 | scope = p_new(pool, struct sieve_variable_scope, 1); |
135 | 0 | scope->pool = pool; |
136 | 0 | scope->refcount = 1; |
137 | |
|
138 | 0 | scope->svinst = svinst; |
139 | 0 | scope->var_ext = var_ext; |
140 | 0 | scope->ext = ext; |
141 | |
|
142 | 0 | hash_table_create(&scope->variables, pool, 0, strcase_hash, strcasecmp); |
143 | 0 | p_array_init(&scope->variable_index, pool, 128); |
144 | |
|
145 | 0 | return scope; |
146 | 0 | } |
147 | | |
148 | | void sieve_variable_scope_ref(struct sieve_variable_scope *scope) |
149 | 0 | { |
150 | 0 | scope->refcount++; |
151 | 0 | } |
152 | | |
153 | | void sieve_variable_scope_unref(struct sieve_variable_scope **_scope) |
154 | 0 | { |
155 | 0 | struct sieve_variable_scope *scope = *_scope; |
156 | |
|
157 | 0 | i_assert(scope->refcount > 0); |
158 | | |
159 | 0 | if (--scope->refcount != 0) |
160 | 0 | return; |
161 | | |
162 | 0 | hash_table_destroy(&scope->variables); |
163 | |
|
164 | 0 | *_scope = NULL; |
165 | 0 | pool_unref(&scope->pool); |
166 | 0 | } |
167 | | |
168 | | pool_t sieve_variable_scope_pool(struct sieve_variable_scope *scope) |
169 | 0 | { |
170 | 0 | return scope->pool; |
171 | 0 | } |
172 | | |
173 | | struct sieve_variable * |
174 | | sieve_variable_scope_declare(struct sieve_variable_scope *scope, |
175 | | const char *identifier) |
176 | 0 | { |
177 | 0 | unsigned int max_scope_count; |
178 | 0 | struct sieve_variable *var; |
179 | |
|
180 | 0 | var = hash_table_lookup(scope->variables, identifier); |
181 | 0 | if (var != NULL) |
182 | 0 | return var; |
183 | | |
184 | 0 | max_scope_count = sieve_variables_get_max_scope_count(scope->var_ext); |
185 | 0 | if (array_count(&scope->variable_index) >= max_scope_count) { |
186 | 0 | if (scope->error_var == NULL) { |
187 | 0 | var = p_new(scope->pool, struct sieve_variable, 1); |
188 | 0 | var->identifier = "@ERROR@"; |
189 | 0 | var->index = 0; |
190 | |
|
191 | 0 | scope->error_var = var; |
192 | 0 | return NULL; |
193 | 0 | } |
194 | | |
195 | 0 | return scope->error_var; |
196 | 0 | } |
197 | | |
198 | 0 | var = p_new(scope->pool, struct sieve_variable, 1); |
199 | 0 | var->ext = scope->ext; |
200 | 0 | var->identifier = p_strdup(scope->pool, identifier); |
201 | 0 | var->index = array_count(&scope->variable_index); |
202 | |
|
203 | 0 | hash_table_insert(scope->variables, var->identifier, var); |
204 | 0 | array_append(&scope->variable_index, &var, 1); |
205 | 0 | return var; |
206 | 0 | } |
207 | | |
208 | | struct sieve_variable * |
209 | | sieve_variable_scope_get_variable(struct sieve_variable_scope *scope, |
210 | | const char *identifier) |
211 | 0 | { |
212 | 0 | return hash_table_lookup(scope->variables, identifier); |
213 | 0 | } |
214 | | |
215 | | struct sieve_variable * |
216 | | sieve_variable_scope_import(struct sieve_variable_scope *scope, |
217 | | struct sieve_variable *var) |
218 | 0 | { |
219 | 0 | struct sieve_variable *old_var, *new_var; |
220 | |
|
221 | 0 | old_var = sieve_variable_scope_get_variable(scope, var->identifier); |
222 | 0 | if (old_var != NULL) { |
223 | 0 | i_assert(memcmp(old_var, var, sizeof(*var)) == 0); |
224 | 0 | return old_var; |
225 | 0 | } |
226 | | |
227 | 0 | new_var = p_new(scope->pool, struct sieve_variable, 1); |
228 | 0 | memcpy(new_var, var, sizeof(*new_var)); |
229 | |
|
230 | 0 | hash_table_insert(scope->variables, new_var->identifier, new_var); |
231 | | |
232 | | /* Not entered into the index because it is an external variable |
233 | | (This can be done unlimited; only limited by the size of the external |
234 | | scope) |
235 | | */ |
236 | 0 | return new_var; |
237 | 0 | } |
238 | | |
239 | | struct sieve_variable_scope_iter * |
240 | | sieve_variable_scope_iterate_init(struct sieve_variable_scope *scope) |
241 | 0 | { |
242 | 0 | struct sieve_variable_scope_iter *iter; |
243 | |
|
244 | 0 | iter = t_new(struct sieve_variable_scope_iter, 1); |
245 | 0 | iter->scope = scope; |
246 | 0 | iter->hctx = hash_table_iterate_init(scope->variables); |
247 | |
|
248 | 0 | return iter; |
249 | 0 | } |
250 | | |
251 | | bool sieve_variable_scope_iterate(struct sieve_variable_scope_iter *iter, |
252 | | struct sieve_variable **var_r) |
253 | 0 | { |
254 | 0 | const char *key; |
255 | |
|
256 | 0 | return hash_table_iterate(iter->hctx, iter->scope->variables, |
257 | 0 | &key, var_r); |
258 | 0 | } |
259 | | |
260 | | void sieve_variable_scope_iterate_deinit( |
261 | | struct sieve_variable_scope_iter **iter) |
262 | 0 | { |
263 | 0 | hash_table_iterate_deinit(&(*iter)->hctx); |
264 | 0 | *iter = NULL; |
265 | 0 | } |
266 | | |
267 | | unsigned int |
268 | | sieve_variable_scope_declarations(struct sieve_variable_scope *scope) |
269 | 0 | { |
270 | 0 | return hash_table_count(scope->variables); |
271 | 0 | } |
272 | | |
273 | | unsigned int sieve_variable_scope_size(struct sieve_variable_scope *scope) |
274 | 0 | { |
275 | 0 | return array_count(&scope->variable_index); |
276 | 0 | } |
277 | | |
278 | | struct sieve_variable *const * |
279 | | sieve_variable_scope_get_variables(struct sieve_variable_scope *scope, |
280 | | unsigned int *size_r) |
281 | 0 | { |
282 | 0 | return array_get(&scope->variable_index, size_r); |
283 | 0 | } |
284 | | |
285 | | struct sieve_variable * |
286 | | sieve_variable_scope_get_indexed(struct sieve_variable_scope *scope, |
287 | | unsigned int index) |
288 | 0 | { |
289 | 0 | struct sieve_variable *const *var; |
290 | |
|
291 | 0 | if (index >= array_count(&scope->variable_index)) |
292 | 0 | return NULL; |
293 | | |
294 | 0 | var = array_idx(&scope->variable_index, index); |
295 | 0 | return *var; |
296 | 0 | } |
297 | | |
298 | | /* Scope binary */ |
299 | | |
300 | | struct sieve_variable_scope * |
301 | | sieve_variable_scope_binary_dump(struct sieve_instance *svinst, |
302 | | const struct sieve_extension *var_ext, |
303 | | const struct sieve_extension *ext, |
304 | | const struct sieve_dumptime_env *denv, |
305 | | sieve_size_t *address) |
306 | 0 | { |
307 | 0 | struct sieve_variable_scope *local_scope; |
308 | 0 | unsigned int i, scope_size; |
309 | 0 | sieve_size_t pc; |
310 | 0 | sieve_offset_t end_offset; |
311 | | |
312 | | /* Read scope size */ |
313 | 0 | sieve_code_mark(denv); |
314 | 0 | if (!sieve_binary_read_unsigned(denv->sblock, address, &scope_size)) |
315 | 0 | return NULL; |
316 | | |
317 | | /* Read offset */ |
318 | 0 | pc = *address; |
319 | 0 | if (!sieve_binary_read_offset(denv->sblock, address, &end_offset)) |
320 | 0 | return NULL; |
321 | | |
322 | | /* Create scope */ |
323 | 0 | local_scope = sieve_variable_scope_create(svinst, var_ext, ext); |
324 | | |
325 | | /* Read and dump scope itself */ |
326 | |
|
327 | 0 | sieve_code_dumpf(denv, "VARIABLES SCOPE [%u] (end: %08x)", |
328 | 0 | scope_size, (unsigned int)(pc + end_offset)); |
329 | |
|
330 | 0 | for (i = 0; i < scope_size; i++) { |
331 | 0 | string_t *identifier; |
332 | |
|
333 | 0 | sieve_code_mark(denv); |
334 | 0 | if (!sieve_binary_read_string(denv->sblock, address, |
335 | 0 | &identifier)) |
336 | 0 | return NULL; |
337 | | |
338 | 0 | sieve_code_dumpf(denv, "%3d: '%s'", i, str_c(identifier)); |
339 | |
|
340 | 0 | (void)sieve_variable_scope_declare(local_scope, |
341 | 0 | str_c(identifier)); |
342 | 0 | } |
343 | | |
344 | 0 | return local_scope; |
345 | 0 | } |
346 | | |
347 | | struct sieve_variable_scope_binary * |
348 | | sieve_variable_scope_binary_create(struct sieve_variable_scope *scope) |
349 | 0 | { |
350 | 0 | struct sieve_variable_scope_binary *scpbin; |
351 | |
|
352 | 0 | scpbin = p_new(scope->pool, struct sieve_variable_scope_binary, 1); |
353 | 0 | scpbin->scope = scope; |
354 | |
|
355 | 0 | return scpbin; |
356 | 0 | } |
357 | | |
358 | | void sieve_variable_scope_binary_ref(struct sieve_variable_scope_binary *scpbin) |
359 | 0 | { |
360 | 0 | sieve_variable_scope_ref(scpbin->scope); |
361 | 0 | } |
362 | | |
363 | | void sieve_variable_scope_binary_unref( |
364 | | struct sieve_variable_scope_binary **scpbin) |
365 | 0 | { |
366 | 0 | sieve_variable_scope_unref(&(*scpbin)->scope); |
367 | 0 | *scpbin = NULL; |
368 | 0 | } |
369 | | |
370 | | struct sieve_variable_scope_binary * |
371 | | sieve_variable_scope_binary_read(struct sieve_instance *svinst, |
372 | | const struct sieve_extension *var_ext, |
373 | | const struct sieve_extension *ext, |
374 | | struct sieve_binary_block *sblock, |
375 | | sieve_size_t *address) |
376 | 0 | { |
377 | 0 | struct sieve_variable_scope *scope; |
378 | 0 | struct sieve_variable_scope_binary *scpbin; |
379 | 0 | unsigned int scope_count, max_scope_count; |
380 | 0 | const char *ext_name = (ext == NULL ? "variables" : |
381 | 0 | sieve_extension_name(ext)); |
382 | 0 | sieve_size_t pc; |
383 | 0 | sieve_offset_t end_offset; |
384 | | |
385 | | /* Read scope size */ |
386 | 0 | if (!sieve_binary_read_unsigned(sblock, address, &scope_count)) { |
387 | 0 | e_error(svinst->event, "%s: " |
388 | 0 | "variable scope: failed to read count", ext_name); |
389 | 0 | return NULL; |
390 | 0 | } |
391 | | |
392 | | /* Check size limit */ |
393 | 0 | max_scope_count = sieve_variables_get_max_scope_count(var_ext); |
394 | 0 | if (scope_count > max_scope_count) { |
395 | 0 | e_error(svinst->event, "%s: " |
396 | 0 | "variable scope: count exceeds the limit (%u > %u)", |
397 | 0 | ext_name, scope_count, max_scope_count); |
398 | 0 | return NULL; |
399 | 0 | } |
400 | | |
401 | | /* Read offset */ |
402 | 0 | pc = *address; |
403 | 0 | if (!sieve_binary_read_offset(sblock, address, &end_offset)) { |
404 | 0 | e_error(svinst->event, "%s: " |
405 | 0 | "variable scope: failed to read end offset", ext_name); |
406 | 0 | return NULL; |
407 | 0 | } |
408 | | |
409 | | /* Create scope */ |
410 | 0 | scope = sieve_variable_scope_create(svinst, var_ext, ext); |
411 | |
|
412 | 0 | scpbin = sieve_variable_scope_binary_create(scope); |
413 | 0 | scpbin->count = scope_count; |
414 | 0 | scpbin->sblock = sblock; |
415 | 0 | scpbin->address = *address; |
416 | |
|
417 | 0 | *address = pc + end_offset; |
418 | |
|
419 | 0 | return scpbin; |
420 | 0 | } |
421 | | |
422 | | struct sieve_variable_scope * |
423 | | sieve_variable_scope_binary_get(struct sieve_variable_scope_binary *scpbin) |
424 | 0 | { |
425 | 0 | const struct sieve_extension *ext = scpbin->scope->ext; |
426 | 0 | struct sieve_instance *svinst = scpbin->scope->svinst; |
427 | 0 | const char *ext_name = (ext == NULL ? "variables" : |
428 | 0 | sieve_extension_name(ext)); |
429 | 0 | unsigned int i; |
430 | |
|
431 | 0 | if (scpbin->sblock != NULL) { |
432 | 0 | sieve_size_t *address = &scpbin->address; |
433 | | |
434 | | /* Read scope itself */ |
435 | 0 | for (i = 0; i < scpbin->count; i++) { |
436 | 0 | struct sieve_variable *var; |
437 | 0 | string_t *identifier; |
438 | |
|
439 | 0 | if (!sieve_binary_read_string(scpbin->sblock, address, |
440 | 0 | &identifier)) { |
441 | 0 | e_error(svinst->event, "%s: variable scope: " |
442 | 0 | "failed to read variable name", |
443 | 0 | ext_name); |
444 | 0 | return NULL; |
445 | 0 | } |
446 | | |
447 | 0 | var = sieve_variable_scope_declare(scpbin->scope, |
448 | 0 | str_c(identifier)); |
449 | |
|
450 | 0 | i_assert(var != NULL); |
451 | 0 | i_assert(var->index == i); |
452 | 0 | } |
453 | | |
454 | 0 | scpbin->sblock = NULL; |
455 | 0 | } |
456 | | |
457 | 0 | return scpbin->scope; |
458 | 0 | } |
459 | | |
460 | | unsigned int |
461 | | sieve_variable_scope_binary_get_count( |
462 | | struct sieve_variable_scope_binary *scpbin) |
463 | 0 | { |
464 | 0 | if (scpbin->sblock != NULL) |
465 | 0 | return scpbin->count; |
466 | | |
467 | 0 | return array_count(&scpbin->scope->variable_index); |
468 | 0 | } |
469 | | |
470 | | /* |
471 | | * Variable storage |
472 | | */ |
473 | | |
474 | | struct sieve_variable_storage { |
475 | | pool_t pool; |
476 | | const struct sieve_extension *var_ext; |
477 | | struct sieve_variable_scope *scope; |
478 | | struct sieve_variable_scope_binary *scope_bin; |
479 | | unsigned int max_count; |
480 | | ARRAY(string_t *) var_values; |
481 | | }; |
482 | | |
483 | | struct sieve_variable_storage * |
484 | | sieve_variable_storage_create(const struct sieve_extension *var_ext, |
485 | | pool_t pool, |
486 | | struct sieve_variable_scope_binary *scpbin) |
487 | 0 | { |
488 | 0 | struct sieve_variable_storage *storage; |
489 | |
|
490 | 0 | storage = p_new(pool, struct sieve_variable_storage, 1); |
491 | 0 | storage->pool = pool; |
492 | 0 | storage->var_ext = var_ext; |
493 | 0 | storage->scope_bin = scpbin; |
494 | 0 | storage->scope = NULL; |
495 | |
|
496 | 0 | storage->max_count = sieve_variable_scope_binary_get_count(scpbin); |
497 | |
|
498 | 0 | p_array_init(&storage->var_values, pool, 4); |
499 | |
|
500 | 0 | return storage; |
501 | 0 | } |
502 | | |
503 | | static inline bool |
504 | | sieve_variable_valid(struct sieve_variable_storage *storage, |
505 | | unsigned int index) |
506 | 0 | { |
507 | 0 | if (storage->scope_bin == NULL) |
508 | 0 | return TRUE; |
509 | | |
510 | 0 | return (index < storage->max_count); |
511 | 0 | } |
512 | | |
513 | | bool sieve_variable_get_identifier(struct sieve_variable_storage *storage, |
514 | | unsigned int index, const char **identifier) |
515 | 0 | { |
516 | 0 | struct sieve_variable *const *var; |
517 | |
|
518 | 0 | *identifier = NULL; |
519 | |
|
520 | 0 | if (storage->scope_bin == NULL) |
521 | 0 | return TRUE; |
522 | | |
523 | 0 | if (storage->scope == NULL) { |
524 | 0 | storage->scope = |
525 | 0 | sieve_variable_scope_binary_get(storage->scope_bin); |
526 | 0 | if (storage->scope == NULL) |
527 | 0 | return FALSE; |
528 | 0 | } |
529 | | |
530 | | /* FIXME: direct invasion of the scope object is a bit ugly */ |
531 | 0 | if (index >= array_count(&storage->scope->variable_index)) |
532 | 0 | return FALSE; |
533 | | |
534 | 0 | var = array_idx(&storage->scope->variable_index, index); |
535 | 0 | if (*var != NULL) |
536 | 0 | *identifier = (*var)->identifier; |
537 | 0 | return TRUE; |
538 | 0 | } |
539 | | |
540 | | const char * |
541 | | sieve_variable_get_varid(struct sieve_variable_storage *storage, |
542 | | unsigned int index) |
543 | 0 | { |
544 | 0 | if (storage->scope_bin == NULL) |
545 | 0 | return t_strdup_printf("%ld", (long)index); |
546 | | |
547 | 0 | if (storage->scope == NULL) { |
548 | 0 | storage->scope = |
549 | 0 | sieve_variable_scope_binary_get(storage->scope_bin); |
550 | 0 | if (storage->scope == NULL) |
551 | 0 | return NULL; |
552 | 0 | } |
553 | | |
554 | 0 | return sieve_ext_variables_get_varid(storage->scope->ext, index); |
555 | 0 | } |
556 | | |
557 | | bool sieve_variable_get(struct sieve_variable_storage *storage, |
558 | | unsigned int index, string_t **value) |
559 | 0 | { |
560 | 0 | *value = NULL; |
561 | |
|
562 | 0 | if (index < array_count(&storage->var_values)) { |
563 | 0 | string_t *const *varent; |
564 | |
|
565 | 0 | varent = array_idx(&storage->var_values, index); |
566 | |
|
567 | 0 | *value = *varent; |
568 | 0 | } else if (!sieve_variable_valid(storage, index)) { |
569 | 0 | return FALSE; |
570 | 0 | } |
571 | | |
572 | 0 | return TRUE; |
573 | 0 | } |
574 | | |
575 | | bool sieve_variable_get_modifiable(struct sieve_variable_storage *storage, |
576 | | unsigned int index, string_t **value) |
577 | 0 | { |
578 | 0 | string_t *dummy; |
579 | |
|
580 | 0 | if (value == NULL) |
581 | 0 | value = &dummy; |
582 | |
|
583 | 0 | if (!sieve_variable_get(storage, index, value)) |
584 | 0 | return FALSE; |
585 | | |
586 | 0 | if (*value == NULL) { |
587 | 0 | *value = str_new(storage->pool, 256); |
588 | 0 | array_idx_set(&storage->var_values, index, value); |
589 | 0 | } |
590 | 0 | return TRUE; |
591 | 0 | } |
592 | | |
593 | | bool sieve_variable_assign(struct sieve_variable_storage *storage, |
594 | | unsigned int index, const string_t *value) |
595 | 0 | { |
596 | 0 | const struct ext_variables_context *extctx = |
597 | 0 | ext_variables_get_context(storage->var_ext); |
598 | 0 | string_t *varval; |
599 | |
|
600 | 0 | if (!sieve_variable_get_modifiable(storage, index, &varval)) |
601 | 0 | return FALSE; |
602 | | |
603 | 0 | str_truncate(varval, 0); |
604 | 0 | str_append_str(varval, value); |
605 | | |
606 | | /* Just a precaution, caller should prevent this in the first place */ |
607 | 0 | if (str_len(varval) > extctx->set->max_value_size) |
608 | 0 | str_truncate_utf8(varval, extctx->set->max_value_size); |
609 | |
|
610 | 0 | return TRUE; |
611 | 0 | } |
612 | | |
613 | | bool sieve_variable_assign_cstr(struct sieve_variable_storage *storage, |
614 | | unsigned int index, const char *value) |
615 | 0 | { |
616 | 0 | const struct ext_variables_context *extctx = |
617 | 0 | ext_variables_get_context(storage->var_ext); |
618 | 0 | string_t *varval; |
619 | |
|
620 | 0 | if (!sieve_variable_get_modifiable(storage, index, &varval)) |
621 | 0 | return FALSE; |
622 | | |
623 | 0 | str_truncate(varval, 0); |
624 | 0 | str_append(varval, value); |
625 | | |
626 | | /* Just a precaution, caller should prevent this in the first place */ |
627 | 0 | if (str_len(varval) > extctx->set->max_value_size) |
628 | 0 | str_truncate_utf8(varval, extctx->set->max_value_size); |
629 | |
|
630 | 0 | return TRUE; |
631 | 0 | } |
632 | | |
633 | | /* |
634 | | * AST Context |
635 | | */ |
636 | | |
637 | | static void |
638 | | ext_variables_ast_free(const struct sieve_extension *ext ATTR_UNUSED, |
639 | | struct sieve_ast *ast ATTR_UNUSED, void *context) |
640 | 0 | { |
641 | 0 | struct sieve_variable_scope *local_scope = |
642 | 0 | (struct sieve_variable_scope *)context; |
643 | | |
644 | | /* Unreference main variable scope */ |
645 | 0 | sieve_variable_scope_unref(&local_scope); |
646 | 0 | } |
647 | | |
648 | | static const struct sieve_ast_extension variables_ast_extension = { |
649 | | &variables_extension, |
650 | | ext_variables_ast_free, |
651 | | }; |
652 | | |
653 | | static struct sieve_variable_scope * |
654 | | ext_variables_create_local_scope(const struct sieve_extension *this_ext, |
655 | | struct sieve_ast *ast) |
656 | 0 | { |
657 | 0 | struct sieve_variable_scope *scope; |
658 | |
|
659 | 0 | scope = sieve_variable_scope_create(this_ext->svinst, this_ext, NULL); |
660 | |
|
661 | 0 | sieve_ast_extension_register(ast, this_ext, &variables_ast_extension, |
662 | 0 | scope); |
663 | 0 | return scope; |
664 | 0 | } |
665 | | |
666 | | static struct sieve_variable_scope * |
667 | | ext_variables_ast_get_local_scope(const struct sieve_extension *this_ext, |
668 | | struct sieve_ast *ast) |
669 | 0 | { |
670 | 0 | struct sieve_variable_scope *local_scope = |
671 | 0 | (struct sieve_variable_scope *) |
672 | 0 | sieve_ast_extension_get_context(ast, this_ext); |
673 | |
|
674 | 0 | return local_scope; |
675 | 0 | } |
676 | | |
677 | | /* |
678 | | * Validator context |
679 | | */ |
680 | | |
681 | | static struct ext_variables_validator_context * |
682 | | ext_variables_validator_context_create(const struct sieve_extension *this_ext, |
683 | | struct sieve_validator *valdtr) |
684 | 0 | { |
685 | 0 | pool_t pool = sieve_validator_pool(valdtr); |
686 | 0 | struct ext_variables_validator_context *ctx; |
687 | 0 | struct sieve_ast *ast = sieve_validator_ast(valdtr); |
688 | |
|
689 | 0 | ctx = p_new(pool, struct ext_variables_validator_context, 1); |
690 | 0 | ctx->modifiers = sieve_validator_object_registry_create(valdtr); |
691 | 0 | ctx->namespaces = sieve_validator_object_registry_create(valdtr); |
692 | 0 | ctx->local_scope = ext_variables_create_local_scope(this_ext, ast); |
693 | |
|
694 | 0 | sieve_validator_extension_set_context(valdtr, this_ext, ctx); |
695 | 0 | return ctx; |
696 | 0 | } |
697 | | |
698 | | struct ext_variables_validator_context * |
699 | | ext_variables_validator_context_get(const struct sieve_extension *this_ext, |
700 | | struct sieve_validator *valdtr) |
701 | 0 | { |
702 | 0 | struct ext_variables_validator_context *ctx; |
703 | |
|
704 | 0 | i_assert(sieve_extension_is(this_ext, variables_extension)); |
705 | 0 | ctx = (struct ext_variables_validator_context *) |
706 | 0 | sieve_validator_extension_get_context(valdtr, this_ext); |
707 | |
|
708 | 0 | if (ctx == NULL) |
709 | 0 | ctx = ext_variables_validator_context_create(this_ext, valdtr); |
710 | 0 | return ctx; |
711 | 0 | } |
712 | | |
713 | | void ext_variables_validator_initialize(const struct sieve_extension *this_ext, |
714 | | struct sieve_validator *valdtr) |
715 | 0 | { |
716 | 0 | struct ext_variables_validator_context *ctx; |
717 | | |
718 | | /* Create our context */ |
719 | 0 | ctx = ext_variables_validator_context_get(this_ext, valdtr); |
720 | |
|
721 | 0 | ext_variables_register_core_modifiers(this_ext, ctx); |
722 | |
|
723 | 0 | ctx->active = TRUE; |
724 | 0 | } |
725 | | |
726 | | struct sieve_variable *ext_variables_validator_get_variable( |
727 | | const struct sieve_extension *this_ext, |
728 | | struct sieve_validator *validator, const char *variable) |
729 | 0 | { |
730 | 0 | struct ext_variables_validator_context *ctx = |
731 | 0 | ext_variables_validator_context_get(this_ext, validator); |
732 | |
|
733 | 0 | return sieve_variable_scope_get_variable(ctx->local_scope, variable); |
734 | 0 | } |
735 | | |
736 | | struct sieve_variable * |
737 | | ext_variables_validator_declare_variable(const struct sieve_extension *this_ext, |
738 | | struct sieve_validator *validator, |
739 | | const char *variable) |
740 | 0 | { |
741 | 0 | struct ext_variables_validator_context *ctx = |
742 | 0 | ext_variables_validator_context_get(this_ext, validator); |
743 | |
|
744 | 0 | return sieve_variable_scope_declare(ctx->local_scope, variable); |
745 | 0 | } |
746 | | |
747 | | struct sieve_variable_scope * |
748 | | sieve_ext_variables_get_local_scope(const struct sieve_extension *var_ext, |
749 | | struct sieve_validator *validator) |
750 | 0 | { |
751 | 0 | struct ext_variables_validator_context *ctx = |
752 | 0 | ext_variables_validator_context_get(var_ext, validator); |
753 | |
|
754 | 0 | return ctx->local_scope; |
755 | 0 | } |
756 | | |
757 | | bool sieve_ext_variables_is_active(const struct sieve_extension *var_ext, |
758 | | struct sieve_validator *valdtr) |
759 | 0 | { |
760 | 0 | struct ext_variables_validator_context *ctx = |
761 | 0 | ext_variables_validator_context_get(var_ext, valdtr); |
762 | |
|
763 | 0 | return (ctx != NULL && ctx->active); |
764 | 0 | } |
765 | | |
766 | | /* |
767 | | * Code generation |
768 | | */ |
769 | | |
770 | | bool ext_variables_generator_load(const struct sieve_extension *ext, |
771 | | const struct sieve_codegen_env *cgenv) |
772 | 0 | { |
773 | 0 | struct sieve_variable_scope *local_scope = |
774 | 0 | ext_variables_ast_get_local_scope(ext, cgenv->ast); |
775 | 0 | unsigned int count = sieve_variable_scope_size(local_scope); |
776 | 0 | sieve_size_t jump; |
777 | |
|
778 | 0 | sieve_binary_emit_unsigned(cgenv->sblock, count); |
779 | |
|
780 | 0 | jump = sieve_binary_emit_offset(cgenv->sblock, 0); |
781 | |
|
782 | 0 | if (count > 0) { |
783 | 0 | unsigned int size, i; |
784 | 0 | struct sieve_variable *const *vars = |
785 | 0 | sieve_variable_scope_get_variables(local_scope, &size); |
786 | |
|
787 | 0 | for (i = 0; i < size; i++) { |
788 | 0 | sieve_binary_emit_cstring(cgenv->sblock, |
789 | 0 | vars[i]->identifier); |
790 | 0 | } |
791 | 0 | } |
792 | |
|
793 | 0 | sieve_binary_resolve_offset(cgenv->sblock, jump); |
794 | 0 | return TRUE; |
795 | 0 | } |
796 | | |
797 | | /* |
798 | | * Interpreter context |
799 | | */ |
800 | | |
801 | | struct ext_variables_interpreter_context { |
802 | | pool_t pool; |
803 | | |
804 | | struct sieve_variable_scope *local_scope; |
805 | | struct sieve_variable_scope_binary *local_scope_bin; |
806 | | |
807 | | struct sieve_variable_storage *local_storage; |
808 | | ARRAY(struct sieve_variable_storage *) ext_storages; |
809 | | }; |
810 | | |
811 | | static void |
812 | | ext_variables_interpreter_free(const struct sieve_extension *ext ATTR_UNUSED, |
813 | | struct sieve_interpreter *interp ATTR_UNUSED, |
814 | | void *context) |
815 | 0 | { |
816 | 0 | struct ext_variables_interpreter_context *ctx = |
817 | 0 | (struct ext_variables_interpreter_context *)context; |
818 | |
|
819 | 0 | sieve_variable_scope_binary_unref(&ctx->local_scope_bin); |
820 | 0 | } |
821 | | |
822 | | static struct sieve_interpreter_extension |
823 | | variables_interpreter_extension = { |
824 | | .ext_def = &variables_extension, |
825 | | .free = ext_variables_interpreter_free, |
826 | | }; |
827 | | |
828 | | static struct ext_variables_interpreter_context * |
829 | | ext_variables_interpreter_context_create( |
830 | | const struct sieve_extension *this_ext, |
831 | | struct sieve_interpreter *interp, |
832 | | struct sieve_variable_scope_binary *scpbin) |
833 | 0 | { |
834 | 0 | pool_t pool = sieve_interpreter_pool(interp); |
835 | 0 | struct ext_variables_interpreter_context *ctx; |
836 | |
|
837 | 0 | ctx = p_new(pool, struct ext_variables_interpreter_context, 1); |
838 | 0 | ctx->pool = pool; |
839 | 0 | ctx->local_scope = NULL; |
840 | 0 | ctx->local_scope_bin = scpbin; |
841 | 0 | ctx->local_storage = |
842 | 0 | sieve_variable_storage_create(this_ext, pool, scpbin); |
843 | 0 | p_array_init(&ctx->ext_storages, pool, |
844 | 0 | sieve_extensions_get_count(this_ext->svinst)); |
845 | |
|
846 | 0 | sieve_interpreter_extension_register(interp, this_ext, |
847 | 0 | &variables_interpreter_extension, |
848 | 0 | ctx); |
849 | 0 | return ctx; |
850 | 0 | } |
851 | | |
852 | | bool ext_variables_interpreter_load(const struct sieve_extension *ext, |
853 | | const struct sieve_runtime_env *renv, |
854 | | sieve_size_t *address) |
855 | 0 | { |
856 | 0 | const struct sieve_execute_env *eenv = renv->exec_env; |
857 | 0 | struct sieve_variable_scope_binary *scpbin; |
858 | |
|
859 | 0 | scpbin = sieve_variable_scope_binary_read(eenv->svinst, ext, NULL, |
860 | 0 | renv->sblock, address); |
861 | 0 | if (scpbin == NULL) |
862 | 0 | return FALSE; |
863 | | |
864 | | /* Create our context */ |
865 | 0 | (void)ext_variables_interpreter_context_create(ext, renv->interp, |
866 | 0 | scpbin); |
867 | | |
868 | | /* Enable support for match values */ |
869 | 0 | (void)sieve_match_values_set_enabled(renv, TRUE); |
870 | |
|
871 | 0 | return TRUE; |
872 | 0 | } |
873 | | |
874 | | static inline struct ext_variables_interpreter_context * |
875 | | ext_variables_interpreter_context_get(const struct sieve_extension *this_ext, |
876 | | struct sieve_interpreter *interp) |
877 | 0 | { |
878 | 0 | struct ext_variables_interpreter_context *ctx; |
879 | |
|
880 | 0 | i_assert(sieve_extension_is(this_ext, variables_extension)); |
881 | 0 | ctx = (struct ext_variables_interpreter_context *) |
882 | 0 | sieve_interpreter_extension_get_context(interp, this_ext); |
883 | 0 | return ctx; |
884 | 0 | } |
885 | | |
886 | | struct sieve_variable_storage * |
887 | | sieve_ext_variables_runtime_get_storage(const struct sieve_extension *var_ext, |
888 | | const struct sieve_runtime_env *renv, |
889 | | const struct sieve_extension *ext) |
890 | 0 | { |
891 | 0 | struct ext_variables_interpreter_context *ctx = |
892 | 0 | ext_variables_interpreter_context_get(var_ext, renv->interp); |
893 | 0 | struct sieve_variable_storage *const *storage; |
894 | |
|
895 | 0 | if (ext == NULL) |
896 | 0 | return ctx->local_storage; |
897 | | |
898 | 0 | if (ext->id >= (int)array_count(&ctx->ext_storages)) |
899 | 0 | storage = NULL; |
900 | 0 | else |
901 | 0 | storage = array_idx(&ctx->ext_storages, ext->id); |
902 | |
|
903 | 0 | if (storage == NULL) |
904 | 0 | return NULL; |
905 | 0 | return *storage; |
906 | 0 | } |
907 | | |
908 | | void sieve_ext_variables_runtime_set_storage( |
909 | | const struct sieve_extension *var_ext, |
910 | | const struct sieve_runtime_env *renv, const struct sieve_extension *ext, |
911 | | struct sieve_variable_storage *storage) |
912 | 0 | { |
913 | 0 | struct ext_variables_interpreter_context *ctx = |
914 | 0 | ext_variables_interpreter_context_get(var_ext, renv->interp); |
915 | |
|
916 | 0 | if (ctx == NULL || ext == NULL || storage == NULL) |
917 | 0 | return; |
918 | 0 | if (ext->id < 0) |
919 | 0 | return; |
920 | | |
921 | 0 | array_idx_set(&ctx->ext_storages, (unsigned int) ext->id, &storage); |
922 | 0 | } |