/src/pigeonhole/src/lib-sieve/sieve-extensions.c
Line | Count | Source |
1 | | /* Copyright (c) Pigeonhole authors, see top-level COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "str.h" |
5 | | #include "mempool.h" |
6 | | #include "hash.h" |
7 | | #include "array.h" |
8 | | |
9 | | #include "sieve-common.h" |
10 | | #include "sieve-error.h" |
11 | | #include "sieve-extensions.h" |
12 | | |
13 | | /* |
14 | | * Forward declarations |
15 | | */ |
16 | | |
17 | | static void sieve_extension_registry_init(struct sieve_instance *svinst); |
18 | | static void sieve_extension_registry_deinit(struct sieve_instance *svinst); |
19 | | |
20 | | static void sieve_capability_registry_init(struct sieve_instance *svinst); |
21 | | static void sieve_capability_registry_deinit(struct sieve_instance *svinst); |
22 | | |
23 | | static int |
24 | | _sieve_extension_register(struct sieve_instance *svinst, |
25 | | const struct sieve_extension_def *extdef, |
26 | | bool load, bool required, |
27 | | struct sieve_extension **ext_r); |
28 | | |
29 | | static int |
30 | | sieve_extensions_set_array(struct sieve_instance *svinst, |
31 | | const ARRAY_TYPE(const_string) *ext_names, |
32 | | bool global, bool implicit); |
33 | | |
34 | | /* |
35 | | * Instance global context |
36 | | */ |
37 | | |
38 | | struct sieve_extension_registry { |
39 | | ARRAY(struct sieve_extension *) extensions; |
40 | | HASH_TABLE(const char *, struct sieve_extension *) extension_index; |
41 | | HASH_TABLE(const char *, |
42 | | struct sieve_capability_registration *) capabilities_index; |
43 | | |
44 | | /* Core language 'extensions' */ |
45 | | const struct sieve_extension *comparator_extension; |
46 | | const struct sieve_extension *match_type_extension; |
47 | | const struct sieve_extension *address_part_extension; |
48 | | |
49 | | /* Preloaded extensions */ |
50 | | ARRAY(const struct sieve_extension *) preloaded_extensions; |
51 | | }; |
52 | | |
53 | | /* |
54 | | * Pre-loaded 'extensions' |
55 | | */ |
56 | | |
57 | | extern const struct sieve_extension_def comparator_extension; |
58 | | extern const struct sieve_extension_def match_type_extension; |
59 | | extern const struct sieve_extension_def address_part_extension; |
60 | | |
61 | | /* |
62 | | * Dummy extensions |
63 | | */ |
64 | | |
65 | | /* FIXME: This is stupid. Define a comparator-* extension and be done with it */ |
66 | | |
67 | | const struct sieve_extension_def comparator_i_octet_extension = { |
68 | | .name = "comparator-i;octet", |
69 | | }; |
70 | | |
71 | | const struct sieve_extension_def comparator_i_ascii_casemap_extension = { |
72 | | .name = "comparator-i;ascii-casemap", |
73 | | }; |
74 | | |
75 | | const struct sieve_extension_def comparator_i_unicode_casemap_extension = { |
76 | | .name = "comparator-i;unicode-casemap", |
77 | | }; |
78 | | |
79 | | /* |
80 | | * List of native extensions |
81 | | */ |
82 | | |
83 | | /* Dummy extensions */ |
84 | | |
85 | | extern const struct sieve_extension_def comparator_i_octet_extension; |
86 | | extern const struct sieve_extension_def comparator_i_ascii_casemap_extension; |
87 | | extern const struct sieve_extension_def comparator_i_unicode_casemap_extension; |
88 | | |
89 | | const struct sieve_extension_def *sieve_dummy_extensions[] = { |
90 | | &comparator_i_octet_extension, &comparator_i_ascii_casemap_extension, |
91 | | &comparator_i_unicode_casemap_extension, |
92 | | }; |
93 | | |
94 | | const unsigned int sieve_dummy_extensions_count = |
95 | | N_ELEMENTS(sieve_dummy_extensions); |
96 | | |
97 | | /* Core */ |
98 | | |
99 | | extern const struct sieve_extension_def fileinto_extension; |
100 | | extern const struct sieve_extension_def reject_extension; |
101 | | extern const struct sieve_extension_def envelope_extension; |
102 | | extern const struct sieve_extension_def encoded_character_extension; |
103 | | |
104 | | extern const struct sieve_extension_def vacation_extension; |
105 | | extern const struct sieve_extension_def subaddress_extension; |
106 | | extern const struct sieve_extension_def comparator_i_ascii_numeric_extension; |
107 | | extern const struct sieve_extension_def relational_extension; |
108 | | extern const struct sieve_extension_def regex_extension; |
109 | | extern const struct sieve_extension_def imap4flags_extension; |
110 | | extern const struct sieve_extension_def copy_extension; |
111 | | extern const struct sieve_extension_def include_extension; |
112 | | extern const struct sieve_extension_def body_extension; |
113 | | extern const struct sieve_extension_def variables_extension; |
114 | | extern const struct sieve_extension_def enotify_extension; |
115 | | extern const struct sieve_extension_def environment_extension; |
116 | | extern const struct sieve_extension_def mailbox_extension; |
117 | | extern const struct sieve_extension_def date_extension; |
118 | | extern const struct sieve_extension_def index_extension; |
119 | | extern const struct sieve_extension_def ihave_extension; |
120 | | extern const struct sieve_extension_def duplicate_extension; |
121 | | extern const struct sieve_extension_def mime_extension; |
122 | | extern const struct sieve_extension_def foreverypart_extension; |
123 | | extern const struct sieve_extension_def extracttext_extension; |
124 | | extern const struct sieve_extension_def mboxmetadata_extension; |
125 | | extern const struct sieve_extension_def servermetadata_extension; |
126 | | |
127 | | extern const struct sieve_extension_def vacation_seconds_extension; |
128 | | extern const struct sieve_extension_def spamtest_extension; |
129 | | extern const struct sieve_extension_def spamtestplus_extension; |
130 | | extern const struct sieve_extension_def virustest_extension; |
131 | | extern const struct sieve_extension_def editheader_extension; |
132 | | extern const struct sieve_extension_def special_use_extension; |
133 | | extern const struct sieve_extension_def extlists_extension; |
134 | | |
135 | | extern const struct sieve_extension_def vnd_debug_extension; |
136 | | extern const struct sieve_extension_def vnd_environment_extension; |
137 | | extern const struct sieve_extension_def vnd_report_extension; |
138 | | |
139 | | #ifdef HAVE_SIEVE_UNFINISHED |
140 | | extern const struct sieve_extension_def ereject_extension; |
141 | | #endif |
142 | | |
143 | | const struct sieve_extension_def *sieve_extensions[] = { |
144 | | /* Core extensions */ |
145 | | &fileinto_extension, &reject_extension, &envelope_extension, |
146 | | &encoded_character_extension, |
147 | | |
148 | | /* 'Plugins' */ |
149 | | &vacation_extension, &subaddress_extension, |
150 | | &comparator_i_ascii_numeric_extension, |
151 | | &relational_extension, ®ex_extension, &imap4flags_extension, |
152 | | ©_extension, &include_extension, &body_extension, |
153 | | &variables_extension, &enotify_extension, &environment_extension, |
154 | | &mailbox_extension, &date_extension, &index_extension, &ihave_extension, |
155 | | &duplicate_extension, &mime_extension, &foreverypart_extension, |
156 | | &extracttext_extension, |
157 | | |
158 | | /* Extra; |
159 | | These are not enabled by default, e.g. because explicit configuration |
160 | | is necessary to make these useful. |
161 | | */ |
162 | | &vacation_seconds_extension, &spamtest_extension, |
163 | | &spamtestplus_extension, &virustest_extension, &editheader_extension, |
164 | | &mboxmetadata_extension, &servermetadata_extension, |
165 | | &special_use_extension, &extlists_extension, |
166 | | |
167 | | /* vnd.dovecot. */ |
168 | | &vnd_debug_extension, &vnd_environment_extension, &vnd_report_extension, |
169 | | |
170 | | #ifdef HAVE_SIEVE_UNFINISHED |
171 | | /* Unfinished extensions */ |
172 | | &ereject_extension, |
173 | | #endif |
174 | | }; |
175 | | |
176 | | const unsigned int sieve_extensions_count = N_ELEMENTS(sieve_extensions); |
177 | | |
178 | | /* |
179 | | * Extensions init/deinit |
180 | | */ |
181 | | |
182 | | int sieve_extensions_init(struct sieve_instance *svinst) |
183 | 0 | { |
184 | 0 | unsigned int i; |
185 | 0 | struct sieve_extension_registry *ext_reg = |
186 | 0 | p_new(svinst->pool, struct sieve_extension_registry, 1); |
187 | 0 | int ret; |
188 | |
|
189 | 0 | svinst->ext_reg = ext_reg; |
190 | |
|
191 | 0 | sieve_extension_registry_init(svinst); |
192 | 0 | sieve_capability_registry_init(svinst); |
193 | | |
194 | | /* Preloaded 'extensions' */ |
195 | 0 | ret = sieve_extension_register(svinst, &comparator_extension, TRUE, |
196 | 0 | &ext_reg->comparator_extension); |
197 | 0 | i_assert(ret == 0); |
198 | 0 | ret = sieve_extension_register(svinst, &match_type_extension, TRUE, |
199 | 0 | &ext_reg->match_type_extension); |
200 | 0 | i_assert(ret == 0); |
201 | 0 | ret = sieve_extension_register(svinst, &address_part_extension, TRUE, |
202 | 0 | &ext_reg->address_part_extension); |
203 | 0 | i_assert(ret == 0); |
204 | | |
205 | 0 | p_array_init(&ext_reg->preloaded_extensions, svinst->pool, 5); |
206 | 0 | array_append(&ext_reg->preloaded_extensions, |
207 | 0 | &ext_reg->comparator_extension, 1); |
208 | 0 | array_append(&ext_reg->preloaded_extensions, |
209 | 0 | &ext_reg->match_type_extension, 1); |
210 | 0 | array_append(&ext_reg->preloaded_extensions, |
211 | 0 | &ext_reg->address_part_extension, 1); |
212 | | |
213 | | /* Pre-load dummy extensions */ |
214 | 0 | for (i = 0; i < sieve_dummy_extensions_count; i++) { |
215 | 0 | struct sieve_extension *ext; |
216 | |
|
217 | 0 | if (_sieve_extension_register(svinst, sieve_dummy_extensions[i], |
218 | 0 | TRUE, FALSE, &ext) < 0) |
219 | 0 | return -1; |
220 | 0 | ext->dummy = TRUE; |
221 | 0 | } |
222 | | |
223 | | /* Pre-load core extensions */ |
224 | 0 | for (i = 0; i < sieve_extensions_count; i++) { |
225 | 0 | if (sieve_extension_register(svinst, sieve_extensions[i], |
226 | 0 | FALSE, NULL) < 0) |
227 | 0 | return -1; |
228 | 0 | } |
229 | | |
230 | | /* More extensions can be added through plugins */ |
231 | 0 | return 0; |
232 | 0 | } |
233 | | |
234 | | int sieve_extensions_load(struct sieve_instance *svinst) |
235 | 0 | { |
236 | | /* Apply sieve_extensions configuration */ |
237 | 0 | if (sieve_extensions_set_array(svinst, &svinst->set->extensions, |
238 | 0 | FALSE, FALSE) < 0) |
239 | 0 | return -1; |
240 | | |
241 | | /* Apply sieve_global_extensions configuration */ |
242 | 0 | if (sieve_extensions_set_array(svinst, &svinst->set->global_extensions, |
243 | 0 | TRUE, FALSE) < 0) |
244 | 0 | return -1; |
245 | | |
246 | | /* Apply sieve_implicit_extensions configuration */ |
247 | 0 | if (sieve_extensions_set_array(svinst, |
248 | 0 | &svinst->set->implicit_extensions, |
249 | 0 | FALSE, TRUE) < 0) |
250 | 0 | return -1; |
251 | | |
252 | 0 | return 0; |
253 | 0 | } |
254 | | |
255 | | void sieve_extensions_deinit(struct sieve_instance *svinst) |
256 | 0 | { |
257 | 0 | sieve_extension_registry_deinit(svinst); |
258 | 0 | sieve_capability_registry_deinit(svinst); |
259 | 0 | } |
260 | | |
261 | | /* |
262 | | * Pre-loaded extensions |
263 | | */ |
264 | | |
265 | | const struct sieve_extension *const * |
266 | | sieve_extensions_get_preloaded(struct sieve_instance *svinst, |
267 | | unsigned int *count_r) |
268 | 0 | { |
269 | 0 | struct sieve_extension_registry *ext_reg = svinst->ext_reg; |
270 | |
|
271 | 0 | return array_get(&ext_reg->preloaded_extensions, count_r); |
272 | 0 | } |
273 | | |
274 | | /* |
275 | | * Extension registry |
276 | | */ |
277 | | |
278 | | static void _sieve_extension_unload(struct sieve_extension *ext); |
279 | | |
280 | | static int _sieve_extension_load(struct sieve_extension *ext) |
281 | 0 | { |
282 | 0 | int ret; |
283 | | |
284 | | /* Drop context if this is a reload */ |
285 | 0 | if (ext->context != NULL) |
286 | 0 | _sieve_extension_unload(ext); |
287 | | |
288 | | /* Call load handler */ |
289 | 0 | if (ext->def == NULL || ext->def->load == NULL) |
290 | 0 | return 0; |
291 | | |
292 | 0 | ret = ext->def->load(ext, &ext->context); |
293 | 0 | i_assert(ret <= 0); |
294 | 0 | i_assert(ret == 0 || ext->context == NULL); |
295 | | |
296 | 0 | if (ret < 0) { |
297 | 0 | e_error(ext->svinst->event, |
298 | 0 | "failed to load '%s' extension support.", |
299 | 0 | ext->def->name); |
300 | 0 | return -1; |
301 | 0 | } |
302 | 0 | return 0; |
303 | 0 | } |
304 | | |
305 | | static void _sieve_extension_unload(struct sieve_extension *ext) |
306 | 0 | { |
307 | | /* Call unload handler */ |
308 | 0 | if (ext->def != NULL && ext->def->unload != NULL) |
309 | 0 | ext->def->unload(ext); |
310 | 0 | ext->context = NULL; |
311 | 0 | } |
312 | | |
313 | | static void sieve_extension_registry_init(struct sieve_instance *svinst) |
314 | 0 | { |
315 | 0 | struct sieve_extension_registry *ext_reg = svinst->ext_reg; |
316 | |
|
317 | 0 | p_array_init(&ext_reg->extensions, svinst->pool, 50); |
318 | 0 | hash_table_create(&ext_reg->extension_index, default_pool, 0, |
319 | 0 | str_hash, strcmp); |
320 | 0 | } |
321 | | |
322 | | static void sieve_extension_registry_deinit(struct sieve_instance *svinst) |
323 | 0 | { |
324 | 0 | struct sieve_extension_registry *ext_reg = svinst->ext_reg; |
325 | 0 | struct sieve_extension *const *exts; |
326 | 0 | unsigned int i, ext_count; |
327 | |
|
328 | 0 | if (!hash_table_is_created(ext_reg->extension_index)) |
329 | 0 | return; |
330 | | |
331 | 0 | exts = array_get_modifiable(&ext_reg->extensions, &ext_count); |
332 | 0 | for (i = 0; i < ext_count; i++) |
333 | 0 | _sieve_extension_unload(exts[i]); |
334 | |
|
335 | 0 | hash_table_destroy(&ext_reg->extension_index); |
336 | 0 | } |
337 | | |
338 | | static struct sieve_extension * |
339 | | sieve_extension_lookup(struct sieve_instance *svinst, const char *name) |
340 | 0 | { |
341 | 0 | struct sieve_extension_registry *ext_reg = svinst->ext_reg; |
342 | |
|
343 | 0 | return hash_table_lookup(ext_reg->extension_index, name); |
344 | 0 | } |
345 | | |
346 | | static struct sieve_extension * |
347 | | sieve_extension_alloc(struct sieve_instance *svinst, |
348 | | const struct sieve_extension_def *extdef) |
349 | 0 | { |
350 | 0 | struct sieve_extension_registry *ext_reg = svinst->ext_reg; |
351 | 0 | struct sieve_extension *ext, **extr; |
352 | 0 | int ext_id; |
353 | |
|
354 | 0 | ext_id = (int)array_count(&ext_reg->extensions); |
355 | | |
356 | | /* Add extension to the registry */ |
357 | 0 | extr = array_append_space(&ext_reg->extensions); |
358 | 0 | *extr = ext = p_new(svinst->pool, struct sieve_extension, 1); |
359 | 0 | ext->id = ext_id; |
360 | 0 | ext->def = extdef; |
361 | 0 | ext->svinst = svinst; |
362 | 0 | return ext; |
363 | 0 | } |
364 | | |
365 | | static int |
366 | | _sieve_extension_register(struct sieve_instance *svinst, |
367 | | const struct sieve_extension_def *extdef, |
368 | | bool load, bool required, |
369 | | struct sieve_extension **ext_r) |
370 | 0 | { |
371 | 0 | struct sieve_extension *ext; |
372 | |
|
373 | 0 | if (ext_r != NULL) |
374 | 0 | *ext_r = NULL; |
375 | 0 | ext = sieve_extension_lookup(svinst, extdef->name); |
376 | | |
377 | | /* Register extension if it is not registered already */ |
378 | 0 | if (ext == NULL) { |
379 | 0 | ext = sieve_extension_alloc(svinst, extdef); |
380 | 0 | hash_table_insert(svinst->ext_reg->extension_index, |
381 | 0 | extdef->name, ext); |
382 | 0 | } else if (ext->overridden) { |
383 | | /* Create a dummy */ |
384 | 0 | ext = sieve_extension_alloc(svinst, extdef); |
385 | 0 | } else { |
386 | | /* Re-register it if it were previously unregistered |
387 | | * (not going to happen) |
388 | | */ |
389 | 0 | i_assert(ext->def == NULL || ext->def == extdef); |
390 | 0 | ext->def = extdef; |
391 | 0 | } |
392 | | |
393 | | /* Enable extension */ |
394 | 0 | if (load || required) { |
395 | 0 | ext->enabled = (ext->enabled || load); |
396 | | |
397 | | /* Call load handler if extension was not loaded already */ |
398 | 0 | if (!ext->loaded) { |
399 | 0 | if (_sieve_extension_load(ext) < 0) |
400 | 0 | return -1; |
401 | 0 | } |
402 | 0 | ext->loaded = TRUE; |
403 | 0 | } |
404 | | |
405 | 0 | ext->required = (ext->required || required); |
406 | |
|
407 | 0 | if (ext_r != NULL) |
408 | 0 | *ext_r = ext; |
409 | 0 | return 0; |
410 | 0 | } |
411 | | |
412 | | static inline int |
413 | | _sieve_extension_register_const(struct sieve_instance *svinst, |
414 | | const struct sieve_extension_def *extdef, |
415 | | bool load, bool required, |
416 | | const struct sieve_extension **ext_r) |
417 | 0 | { |
418 | 0 | struct sieve_extension *ext; |
419 | |
|
420 | 0 | if (_sieve_extension_register(svinst, extdef, load, required, |
421 | 0 | &ext) < 0) { |
422 | 0 | if (ext_r != NULL) |
423 | 0 | *ext_r = NULL; |
424 | 0 | return -1; |
425 | 0 | } |
426 | 0 | if (ext_r != NULL) |
427 | 0 | *ext_r = ext; |
428 | 0 | return 0; |
429 | 0 | } |
430 | | |
431 | | int sieve_extension_register(struct sieve_instance *svinst, |
432 | | const struct sieve_extension_def *extdef, |
433 | | bool load, const struct sieve_extension **ext_r) |
434 | 0 | { |
435 | 0 | return _sieve_extension_register_const(svinst, extdef, load, FALSE, |
436 | 0 | ext_r); |
437 | 0 | } |
438 | | |
439 | | void sieve_extension_unregister(const struct sieve_extension *ext) |
440 | 0 | { |
441 | 0 | if (ext == NULL) |
442 | 0 | return; |
443 | | |
444 | 0 | struct sieve_extension_registry *ext_reg = ext->svinst->ext_reg; |
445 | 0 | struct sieve_extension *const *mod_ext; |
446 | 0 | int ext_id = ext->id; |
447 | |
|
448 | 0 | if (ext_id >= 0 && ext_id < (int) array_count(&ext_reg->extensions)) { |
449 | 0 | mod_ext = array_idx(&ext_reg->extensions, ext_id); |
450 | |
|
451 | 0 | sieve_extension_capabilities_unregister(*mod_ext); |
452 | 0 | _sieve_extension_unload(*mod_ext); |
453 | 0 | (*mod_ext)->loaded = FALSE; |
454 | 0 | (*mod_ext)->enabled = FALSE; |
455 | 0 | (*mod_ext)->def = NULL; |
456 | 0 | } |
457 | 0 | } |
458 | | |
459 | | int sieve_extension_require(struct sieve_instance *svinst, |
460 | | const struct sieve_extension_def *extdef, bool load, |
461 | | const struct sieve_extension **ext_r) |
462 | 0 | { |
463 | 0 | return _sieve_extension_register_const(svinst, extdef, load, TRUE, |
464 | 0 | ext_r); |
465 | 0 | } |
466 | | |
467 | | int sieve_extension_reload(const struct sieve_extension *ext) |
468 | 0 | { |
469 | 0 | struct sieve_extension_registry *ext_reg = ext->svinst->ext_reg; |
470 | 0 | struct sieve_extension *const *mod_ext; |
471 | 0 | int ext_id = ext->id; |
472 | | |
473 | | /* Let's not just cast the 'const' away */ |
474 | 0 | i_assert(ext_id >= 0 && |
475 | 0 | ext_id < (int) array_count(&ext_reg->extensions)); |
476 | | |
477 | 0 | mod_ext = array_idx(&ext_reg->extensions, ext_id); |
478 | 0 | return _sieve_extension_load(*mod_ext); |
479 | 0 | } |
480 | | |
481 | | int sieve_extension_replace(struct sieve_instance *svinst, |
482 | | const struct sieve_extension_def *extdef, bool load, |
483 | | const struct sieve_extension **ext_r) |
484 | 0 | { |
485 | 0 | struct sieve_extension *ext; |
486 | |
|
487 | 0 | ext = sieve_extension_lookup(svinst, extdef->name); |
488 | 0 | if (ext != NULL) |
489 | 0 | sieve_extension_unregister(ext); |
490 | |
|
491 | 0 | return sieve_extension_register(svinst, extdef, load, ext_r); |
492 | 0 | } |
493 | | |
494 | | void sieve_extension_override(struct sieve_instance *svinst, const char *name, |
495 | | const struct sieve_extension *ext) |
496 | 0 | { |
497 | 0 | struct sieve_extension_registry *ext_reg = ext->svinst->ext_reg; |
498 | 0 | struct sieve_extension *const *mod_ext; |
499 | 0 | struct sieve_extension *old_ext; |
500 | |
|
501 | 0 | old_ext = sieve_extension_lookup(svinst, name); |
502 | 0 | if (old_ext == ext) |
503 | 0 | return; |
504 | 0 | i_assert(old_ext == NULL || !old_ext->overridden); |
505 | | |
506 | 0 | i_assert(ext->id >= 0 && |
507 | 0 | ext->id < (int)array_count(&ext_reg->extensions)); |
508 | 0 | mod_ext = array_idx(&ext_reg->extensions, ext->id); |
509 | |
|
510 | 0 | hash_table_update(ext_reg->extension_index, name, *mod_ext); |
511 | 0 | if (old_ext != NULL) |
512 | 0 | old_ext->overridden = TRUE; |
513 | 0 | } |
514 | | |
515 | | unsigned int sieve_extensions_get_count(struct sieve_instance *svinst) |
516 | 0 | { |
517 | 0 | struct sieve_extension_registry *ext_reg = svinst->ext_reg; |
518 | |
|
519 | 0 | return array_count(&ext_reg->extensions); |
520 | 0 | } |
521 | | |
522 | | const struct sieve_extension *const * |
523 | | sieve_extensions_get_all(struct sieve_instance *svinst, unsigned int *count_r) |
524 | 0 | { |
525 | 0 | struct sieve_extension_registry *ext_reg = svinst->ext_reg; |
526 | |
|
527 | 0 | return (const struct sieve_extension *const *) |
528 | 0 | array_get(&ext_reg->extensions, count_r); |
529 | 0 | } |
530 | | |
531 | | const struct sieve_extension * |
532 | | sieve_extension_get_by_id(struct sieve_instance *svinst, unsigned int ext_id) |
533 | 0 | { |
534 | 0 | struct sieve_extension_registry *ext_reg = svinst->ext_reg; |
535 | 0 | struct sieve_extension *const *ext; |
536 | |
|
537 | 0 | if (ext_id < array_count(&ext_reg->extensions)) { |
538 | 0 | ext = array_idx(&ext_reg->extensions, ext_id); |
539 | |
|
540 | 0 | if ((*ext)->def != NULL && |
541 | 0 | ((*ext)->enabled || (*ext)->required)) |
542 | 0 | return *ext; |
543 | 0 | } |
544 | 0 | return NULL; |
545 | 0 | } |
546 | | |
547 | | const struct sieve_extension * |
548 | | sieve_extension_get_by_name(struct sieve_instance *svinst, const char *name) |
549 | 0 | { |
550 | 0 | const struct sieve_extension *ext; |
551 | |
|
552 | 0 | if (*name == '@') |
553 | 0 | return NULL; |
554 | 0 | if (strlen(name) > 128) |
555 | 0 | return NULL; |
556 | | |
557 | 0 | ext = sieve_extension_lookup(svinst, name); |
558 | 0 | if (ext == NULL || ext->def == NULL || |
559 | 0 | (!ext->enabled && !ext->required)) |
560 | 0 | return NULL; |
561 | 0 | return ext; |
562 | 0 | } |
563 | | |
564 | | static inline bool _sieve_extension_listable(const struct sieve_extension *ext) |
565 | 0 | { |
566 | 0 | return (ext->enabled && ext->def != NULL && |
567 | 0 | *(ext->def->name) != '@' && !ext->dummy && |
568 | 0 | !ext->global && !ext->overridden); |
569 | 0 | } |
570 | | |
571 | | const char *sieve_extensions_get_string(struct sieve_instance *svinst) |
572 | 0 | { |
573 | 0 | struct sieve_extension_registry *ext_reg = svinst->ext_reg; |
574 | 0 | string_t *extstr = t_str_new(256); |
575 | 0 | struct sieve_extension *const *exts; |
576 | 0 | unsigned int i, ext_count; |
577 | |
|
578 | 0 | exts = array_get(&ext_reg->extensions, &ext_count); |
579 | 0 | if (ext_count > 0) { |
580 | 0 | i = 0; |
581 | | |
582 | | /* Find first listable extension */ |
583 | 0 | while (i < ext_count && !_sieve_extension_listable(exts[i])) |
584 | 0 | i++; |
585 | |
|
586 | 0 | if (i < ext_count) { |
587 | | /* Add first to string */ |
588 | 0 | str_append(extstr, exts[i]->def->name); |
589 | 0 | i++; |
590 | | |
591 | | /* Add others */ |
592 | 0 | for (; i < ext_count; i++) { |
593 | 0 | if (_sieve_extension_listable(exts[i])) { |
594 | 0 | str_append_c(extstr, ' '); |
595 | 0 | str_append(extstr, exts[i]->def->name); |
596 | 0 | } |
597 | 0 | } |
598 | 0 | } |
599 | 0 | } |
600 | 0 | return str_c(extstr); |
601 | 0 | } |
602 | | |
603 | | static int |
604 | | sieve_extension_set_enabled(struct sieve_extension *ext, bool enabled) |
605 | 0 | { |
606 | 0 | int ret = 0; |
607 | |
|
608 | 0 | if (enabled) { |
609 | 0 | ext->enabled = TRUE; |
610 | |
|
611 | 0 | if (!ext->loaded) |
612 | 0 | ret = _sieve_extension_load(ext); |
613 | |
|
614 | 0 | ext->loaded = TRUE; |
615 | 0 | } else { |
616 | 0 | ext->enabled = FALSE; |
617 | 0 | } |
618 | |
|
619 | 0 | return ret; |
620 | 0 | } |
621 | | |
622 | | static int |
623 | | sieve_extension_set_global(struct sieve_extension *ext, bool enabled) |
624 | 0 | { |
625 | 0 | int ret = 0; |
626 | |
|
627 | 0 | if (enabled) { |
628 | 0 | ret = sieve_extension_set_enabled(ext, TRUE); |
629 | 0 | ext->global = TRUE; |
630 | 0 | } else { |
631 | 0 | ext->global = FALSE; |
632 | 0 | } |
633 | |
|
634 | 0 | return ret; |
635 | 0 | } |
636 | | |
637 | | static int |
638 | | sieve_extension_set_implicit(struct sieve_extension *ext, bool enabled) |
639 | 0 | { |
640 | 0 | int ret = 0; |
641 | |
|
642 | 0 | if (enabled) { |
643 | 0 | ret = sieve_extension_set_enabled(ext, TRUE); |
644 | 0 | ext->implicit = TRUE; |
645 | 0 | } else { |
646 | 0 | ext->implicit = FALSE; |
647 | 0 | } |
648 | |
|
649 | 0 | return ret; |
650 | 0 | } |
651 | | |
652 | | static int |
653 | | sieve_extensions_set_array(struct sieve_instance *svinst, |
654 | | const ARRAY_TYPE(const_string) *ext_names, |
655 | | bool global, bool implicit) |
656 | 0 | { |
657 | 0 | struct sieve_extension_registry *ext_reg = svinst->ext_reg; |
658 | 0 | const char *name; |
659 | |
|
660 | 0 | if (array_is_empty(ext_names)) |
661 | 0 | return 0; |
662 | | |
663 | 0 | int ret = 0; |
664 | 0 | array_foreach_elem(ext_names, name) { |
665 | 0 | struct sieve_extension *ext; |
666 | |
|
667 | 0 | ext = hash_table_lookup(ext_reg->extension_index, name); |
668 | 0 | if (ext == NULL || ext->def == NULL || |
669 | 0 | *(ext->def->name) == '@') { |
670 | 0 | e_warning(svinst->event, |
671 | 0 | "ignored unknown extension '%s' " |
672 | 0 | "while configuring available extensions", |
673 | 0 | name); |
674 | 0 | continue; |
675 | 0 | } |
676 | 0 | if (ext->id < 0) |
677 | 0 | continue; |
678 | | |
679 | | /* Perform actual activation/deactivation */ |
680 | 0 | if (global) { |
681 | 0 | if (sieve_extension_set_global(ext, TRUE) < 0) { |
682 | 0 | ret = -1; |
683 | 0 | break; |
684 | 0 | } |
685 | 0 | } else if (implicit) { |
686 | 0 | if (sieve_extension_set_implicit(ext, TRUE) < 0) { |
687 | 0 | ret = -1; |
688 | 0 | break; |
689 | 0 | } |
690 | 0 | } else { |
691 | 0 | if (sieve_extension_set_enabled(ext, TRUE) < 0) { |
692 | 0 | ret = -1; |
693 | 0 | break; |
694 | 0 | } |
695 | 0 | } |
696 | 0 | } |
697 | |
|
698 | 0 | return ret; |
699 | 0 | } |
700 | | |
701 | | int sieve_extensions_set_string(struct sieve_instance *svinst, |
702 | | const char *ext_string, bool global, |
703 | | bool implicit) |
704 | 0 | { |
705 | 0 | struct sieve_extension_registry *ext_reg = svinst->ext_reg; |
706 | 0 | ARRAY(const struct sieve_extension *) enabled_extensions; |
707 | 0 | ARRAY(const struct sieve_extension *) disabled_extensions; |
708 | 0 | const struct sieve_extension *const *ext_enabled; |
709 | 0 | const struct sieve_extension *const *ext_disabled; |
710 | 0 | struct sieve_extension **exts; |
711 | 0 | const char **ext_names; |
712 | 0 | unsigned int i, ext_count, ena_count, dis_count; |
713 | 0 | bool relative = FALSE; |
714 | |
|
715 | 0 | if (ext_string == NULL) { |
716 | 0 | if (global || implicit) |
717 | 0 | return 0; |
718 | | |
719 | | /* Enable all */ |
720 | 0 | exts = array_get_modifiable(&ext_reg->extensions, &ext_count); |
721 | |
|
722 | 0 | for (i = 0; i < ext_count; i++) { |
723 | 0 | if (sieve_extension_set_enabled(exts[i], TRUE) < 0) |
724 | 0 | return -1; |
725 | 0 | } |
726 | 0 | return 0; |
727 | 0 | } |
728 | | |
729 | 0 | int ret = 0; |
730 | 0 | T_BEGIN { |
731 | 0 | t_array_init(&enabled_extensions, |
732 | 0 | array_count(&ext_reg->extensions)); |
733 | 0 | t_array_init(&disabled_extensions, |
734 | 0 | array_count(&ext_reg->extensions)); |
735 | |
|
736 | 0 | ext_names = t_strsplit_spaces(ext_string, " \t"); |
737 | |
|
738 | 0 | while (*ext_names != NULL) { |
739 | 0 | const char *name = *ext_names; |
740 | |
|
741 | 0 | ext_names++; |
742 | |
|
743 | 0 | if (*name != '\0') { |
744 | 0 | const struct sieve_extension *ext; |
745 | 0 | char op = '\0'; /* No add/remove operation */ |
746 | |
|
747 | 0 | if (*name == '+' || /* Add to existing config */ |
748 | 0 | *name == '-') { /* Remove from existing config */ |
749 | 0 | op = *name++; |
750 | 0 | relative = TRUE; |
751 | 0 | } |
752 | |
|
753 | 0 | if (*name == '@') |
754 | 0 | ext = NULL; |
755 | 0 | else { |
756 | 0 | ext = hash_table_lookup(ext_reg->extension_index, |
757 | 0 | name); |
758 | 0 | } |
759 | |
|
760 | 0 | if (ext == NULL || ext->def == NULL) { |
761 | 0 | e_warning(svinst->event, |
762 | 0 | "ignored unknown extension '%s' while configuring " |
763 | 0 | "available extensions", name); |
764 | 0 | continue; |
765 | 0 | } |
766 | | |
767 | 0 | if (op == '-') { |
768 | 0 | array_append(&disabled_extensions, |
769 | 0 | &ext, 1); |
770 | 0 | } else { |
771 | 0 | array_append(&enabled_extensions, |
772 | 0 | &ext, 1); |
773 | 0 | } |
774 | 0 | } |
775 | 0 | } |
776 | |
|
777 | 0 | exts = array_get_modifiable(&ext_reg->extensions, &ext_count); |
778 | 0 | ext_enabled = array_get(&enabled_extensions, &ena_count); |
779 | 0 | ext_disabled = array_get(&disabled_extensions, &dis_count); |
780 | | |
781 | | /* Set new extension status */ |
782 | |
|
783 | 0 | for (i = 0; i < ext_count; i++) { |
784 | 0 | unsigned int j; |
785 | 0 | bool enabled = FALSE; |
786 | |
|
787 | 0 | if (exts[i]->id < 0 || exts[i]->def == NULL || |
788 | 0 | *(exts[i]->def->name) == '@') |
789 | 0 | continue; |
790 | | |
791 | | /* If extensions are specified relative to the default |
792 | | set, we first need to check which ones are disabled |
793 | | */ |
794 | 0 | if (relative) { |
795 | 0 | if (global) |
796 | 0 | enabled = exts[i]->global; |
797 | 0 | else if (implicit) |
798 | 0 | enabled = exts[i]->implicit; |
799 | 0 | else |
800 | 0 | enabled = exts[i]->enabled; |
801 | |
|
802 | 0 | if (enabled) { |
803 | | /* Disable if explicitly disabled */ |
804 | 0 | for (j = 0; j < dis_count; j++) { |
805 | 0 | if (ext_disabled[j]->def == exts[i]->def) { |
806 | 0 | enabled = FALSE; |
807 | 0 | break; |
808 | 0 | } |
809 | 0 | } |
810 | 0 | } |
811 | 0 | } |
812 | | |
813 | | /* Enable if listed with '+' or no prefix */ |
814 | 0 | for (j = 0; j < ena_count; j++) { |
815 | 0 | if (ext_enabled[j]->def == exts[i]->def) { |
816 | 0 | enabled = TRUE; |
817 | 0 | break; |
818 | 0 | } |
819 | 0 | } |
820 | | |
821 | | /* Perform actual activation/deactivation */ |
822 | 0 | if (global) { |
823 | 0 | if (sieve_extension_set_global(exts[i], |
824 | 0 | enabled) < 0) { |
825 | 0 | ret = -1; |
826 | 0 | break; |
827 | 0 | } |
828 | 0 | } else if (implicit) { |
829 | 0 | if (sieve_extension_set_implicit(exts[i], |
830 | 0 | enabled) < 0) { |
831 | 0 | ret = -1; |
832 | 0 | break; |
833 | 0 | } |
834 | 0 | } else { |
835 | 0 | if (sieve_extension_set_enabled(exts[i], |
836 | 0 | enabled) < 0) { |
837 | 0 | ret = -1; |
838 | 0 | break; |
839 | 0 | } |
840 | 0 | } |
841 | 0 | } |
842 | 0 | } T_END; |
843 | | |
844 | 0 | return ret; |
845 | 0 | } |
846 | | |
847 | | const struct sieve_extension * |
848 | | sieve_get_match_type_extension(struct sieve_instance *svinst) |
849 | 0 | { |
850 | 0 | return svinst->ext_reg->match_type_extension; |
851 | 0 | } |
852 | | |
853 | | const struct sieve_extension * |
854 | | sieve_get_comparator_extension(struct sieve_instance *svinst) |
855 | 0 | { |
856 | 0 | return svinst->ext_reg->comparator_extension; |
857 | 0 | } |
858 | | |
859 | | const struct sieve_extension * |
860 | | sieve_get_address_part_extension(struct sieve_instance *svinst) |
861 | 0 | { |
862 | 0 | return svinst->ext_reg->address_part_extension; |
863 | 0 | } |
864 | | |
865 | | void sieve_enable_debug_extension(struct sieve_instance *svinst) |
866 | 0 | { |
867 | 0 | const struct sieve_extension *ext; |
868 | 0 | int ret; |
869 | |
|
870 | 0 | ret = sieve_extension_register(svinst, &vnd_debug_extension, TRUE, |
871 | 0 | &ext); |
872 | 0 | i_assert(ret == 0); |
873 | 0 | } |
874 | | |
875 | | /* |
876 | | * Extension capabilities |
877 | | */ |
878 | | |
879 | | struct sieve_capability_registration { |
880 | | const struct sieve_extension *ext; |
881 | | const struct sieve_extension_capabilities *capabilities; |
882 | | }; |
883 | | |
884 | | void sieve_capability_registry_init(struct sieve_instance *svinst) |
885 | 0 | { |
886 | 0 | struct sieve_extension_registry *ext_reg = svinst->ext_reg; |
887 | |
|
888 | 0 | hash_table_create(&ext_reg->capabilities_index, default_pool, 0, |
889 | 0 | str_hash, strcmp); |
890 | 0 | } |
891 | | |
892 | | void sieve_capability_registry_deinit(struct sieve_instance *svinst) |
893 | 0 | { |
894 | 0 | struct sieve_extension_registry *ext_reg = svinst->ext_reg; |
895 | |
|
896 | 0 | if (!hash_table_is_created(ext_reg->capabilities_index)) |
897 | 0 | return; |
898 | | |
899 | 0 | hash_table_destroy(&svinst->ext_reg->capabilities_index); |
900 | 0 | } |
901 | | |
902 | | void sieve_extension_capabilities_register( |
903 | | const struct sieve_extension *ext, |
904 | | const struct sieve_extension_capabilities *cap) |
905 | 0 | { |
906 | 0 | struct sieve_instance *svinst = ext->svinst; |
907 | 0 | struct sieve_extension_registry *ext_reg = svinst->ext_reg; |
908 | 0 | struct sieve_capability_registration *reg; |
909 | |
|
910 | 0 | reg = hash_table_lookup(ext_reg->capabilities_index, cap->name); |
911 | 0 | if (reg != NULL) { |
912 | | /* Already registered */ |
913 | 0 | return; |
914 | 0 | } |
915 | | |
916 | 0 | reg = p_new(svinst->pool, struct sieve_capability_registration, 1); |
917 | 0 | reg->ext = ext; |
918 | 0 | reg->capabilities = cap; |
919 | |
|
920 | 0 | hash_table_insert(ext_reg->capabilities_index, cap->name, reg); |
921 | 0 | } |
922 | | |
923 | | void sieve_extension_capabilities_unregister(const struct sieve_extension *ext) |
924 | 0 | { |
925 | 0 | struct sieve_extension_registry *ext_reg = ext->svinst->ext_reg; |
926 | 0 | struct hash_iterate_context *hictx; |
927 | 0 | const char *name; |
928 | 0 | struct sieve_capability_registration *reg; |
929 | |
|
930 | 0 | hictx = hash_table_iterate_init(ext_reg->capabilities_index); |
931 | 0 | while (hash_table_iterate(hictx, ext_reg->capabilities_index, |
932 | 0 | &name, ®)) { |
933 | 0 | if (reg->ext == ext) |
934 | 0 | hash_table_remove(ext_reg->capabilities_index, name); |
935 | 0 | } |
936 | 0 | hash_table_iterate_deinit(&hictx); |
937 | 0 | } |
938 | | |
939 | | const char * |
940 | | sieve_extension_capabilities_get_string(struct sieve_instance *svinst, |
941 | | const char *cap_name) |
942 | 0 | { |
943 | 0 | struct sieve_extension_registry *ext_reg = svinst->ext_reg; |
944 | 0 | const struct sieve_capability_registration *cap_reg = |
945 | 0 | hash_table_lookup(ext_reg->capabilities_index, cap_name); |
946 | 0 | const struct sieve_extension_capabilities *cap; |
947 | |
|
948 | 0 | if (cap_reg == NULL || cap_reg->capabilities == NULL) |
949 | 0 | return NULL; |
950 | | |
951 | 0 | cap = cap_reg->capabilities; |
952 | 0 | if (cap->get_string == NULL || !cap_reg->ext->enabled) |
953 | 0 | return NULL; |
954 | | |
955 | 0 | return cap->get_string(cap_reg->ext); |
956 | 0 | } |