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