diff options
Diffstat (limited to 'pigeonhole/src/lib-sieve/sieve-extensions.c')
-rw-r--r-- | pigeonhole/src/lib-sieve/sieve-extensions.c | 879 |
1 files changed, 879 insertions, 0 deletions
diff --git a/pigeonhole/src/lib-sieve/sieve-extensions.c b/pigeonhole/src/lib-sieve/sieve-extensions.c new file mode 100644 index 0000000..a1cb810 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-extensions.c @@ -0,0 +1,879 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "mempool.h" +#include "hash.h" +#include "array.h" + +#include "sieve-common.h" +#include "sieve-error.h" +#include "sieve-settings.h" +#include "sieve-extensions.h" + +/* + * Forward declarations + */ + +static void sieve_extension_registry_init(struct sieve_instance *svinst); +static void sieve_extension_registry_deinit(struct sieve_instance *svinst); + +static void sieve_capability_registry_init(struct sieve_instance *svinst); +static void sieve_capability_registry_deinit(struct sieve_instance *svinst); + +static struct sieve_extension *_sieve_extension_register + (struct sieve_instance *svinst, const struct sieve_extension_def *extdef, + bool load, bool required); + +/* + * Instance global context + */ + +struct sieve_extension_registry { + ARRAY(struct sieve_extension *) extensions; + HASH_TABLE(const char *, struct sieve_extension *) extension_index; + HASH_TABLE(const char *, struct sieve_capability_registration *) capabilities_index; + + /* Core language 'extensions' */ + const struct sieve_extension *comparator_extension; + const struct sieve_extension *match_type_extension; + const struct sieve_extension *address_part_extension; + + /* Preloaded extensions */ + ARRAY(const struct sieve_extension *) preloaded_extensions; +}; + +/* + * Pre-loaded 'extensions' + */ + +extern const struct sieve_extension_def comparator_extension; +extern const struct sieve_extension_def match_type_extension; +extern const struct sieve_extension_def address_part_extension; + +/* + * Dummy extensions + */ + +/* FIXME: This is stupid. Define a comparator-* extension and be done with it */ + +const struct sieve_extension_def comparator_i_octet_extension = { + .name = "comparator-i;octet", +}; + +const struct sieve_extension_def comparator_i_ascii_casemap_extension = { + .name = "comparator-i;ascii-casemap", +}; + +/* + * List of native extensions + */ + +/* Dummy extensions */ + +extern const struct sieve_extension_def comparator_i_octet_extension; +extern const struct sieve_extension_def comparator_i_ascii_casemap_extension; + +const struct sieve_extension_def *sieve_dummy_extensions[] = { + &comparator_i_octet_extension, &comparator_i_ascii_casemap_extension +}; + +const unsigned int sieve_dummy_extensions_count = + N_ELEMENTS(sieve_dummy_extensions); + +/* Core */ + +extern const struct sieve_extension_def fileinto_extension; +extern const struct sieve_extension_def reject_extension; +extern const struct sieve_extension_def envelope_extension; +extern const struct sieve_extension_def encoded_character_extension; + +extern const struct sieve_extension_def vacation_extension; +extern const struct sieve_extension_def subaddress_extension; +extern const struct sieve_extension_def comparator_i_ascii_numeric_extension; +extern const struct sieve_extension_def relational_extension; +extern const struct sieve_extension_def regex_extension; +extern const struct sieve_extension_def imap4flags_extension; +extern const struct sieve_extension_def copy_extension; +extern const struct sieve_extension_def include_extension; +extern const struct sieve_extension_def body_extension; +extern const struct sieve_extension_def variables_extension; +extern const struct sieve_extension_def enotify_extension; +extern const struct sieve_extension_def environment_extension; +extern const struct sieve_extension_def mailbox_extension; +extern const struct sieve_extension_def date_extension; +extern const struct sieve_extension_def index_extension; +extern const struct sieve_extension_def ihave_extension; +extern const struct sieve_extension_def duplicate_extension; +extern const struct sieve_extension_def mime_extension; +extern const struct sieve_extension_def foreverypart_extension; +extern const struct sieve_extension_def extracttext_extension; +extern const struct sieve_extension_def mboxmetadata_extension; +extern const struct sieve_extension_def servermetadata_extension; + +const struct sieve_extension_def *sieve_core_extensions[] = { + /* Core extensions */ + &fileinto_extension, &reject_extension, &envelope_extension, + &encoded_character_extension, + + /* 'Plugins' */ + &vacation_extension, &subaddress_extension, + &comparator_i_ascii_numeric_extension, + &relational_extension, ®ex_extension, &imap4flags_extension, + ©_extension, &include_extension, &body_extension, + &variables_extension, &enotify_extension, &environment_extension, + &mailbox_extension, &date_extension, &index_extension, &ihave_extension, + &duplicate_extension, &mime_extension, &foreverypart_extension, + &extracttext_extension +}; + +const unsigned int sieve_core_extensions_count = + N_ELEMENTS(sieve_core_extensions); + +/* Extra; + * These are not enabled by default, e.g. because explicit configuration is + * necessary to make these useful. + */ + +extern const struct sieve_extension_def vacation_seconds_extension; +extern const struct sieve_extension_def spamtest_extension; +extern const struct sieve_extension_def spamtestplus_extension; +extern const struct sieve_extension_def virustest_extension; +extern const struct sieve_extension_def editheader_extension; +extern const struct sieve_extension_def special_use_extension; + +extern const struct sieve_extension_def vnd_debug_extension; +extern const struct sieve_extension_def vnd_environment_extension; +extern const struct sieve_extension_def vnd_report_extension; + +const struct sieve_extension_def *sieve_extra_extensions[] = { + &vacation_seconds_extension, &spamtest_extension, &spamtestplus_extension, + &virustest_extension, &editheader_extension, + &mboxmetadata_extension, &servermetadata_extension, + &special_use_extension, + + /* vnd.dovecot. */ + &vnd_debug_extension, &vnd_environment_extension, &vnd_report_extension +}; + +const unsigned int sieve_extra_extensions_count = + N_ELEMENTS(sieve_extra_extensions); + +/* + * Deprecated extensions + */ + +extern const struct sieve_extension_def imapflags_extension; +extern const struct sieve_extension_def notify_extension; +extern const struct sieve_extension_def vnd_duplicate_extension; + +const struct sieve_extension_def *sieve_deprecated_extensions[] = { + &imapflags_extension, + ¬ify_extension, + &vnd_duplicate_extension +}; + +const unsigned int sieve_deprecated_extensions_count = + N_ELEMENTS(sieve_deprecated_extensions); + +/* + * Unfinished extensions + */ + +#ifdef HAVE_SIEVE_UNFINISHED + +extern const struct sieve_extension_def ereject_extension; + +const struct sieve_extension_def *sieve_unfinished_extensions[] = { + &ereject_extension +}; + +const unsigned int sieve_unfinished_extensions_count = + N_ELEMENTS(sieve_unfinished_extensions); + +#endif /* HAVE_SIEVE_UNFINISHED */ + +/* + * Extensions init/deinit + */ + +bool sieve_extensions_init(struct sieve_instance *svinst) +{ + unsigned int i; + struct sieve_extension_registry *ext_reg = + p_new(svinst->pool, struct sieve_extension_registry, 1); + struct sieve_extension *ext; + + svinst->ext_reg = ext_reg; + + sieve_extension_registry_init(svinst); + sieve_capability_registry_init(svinst); + + /* Preloaded 'extensions' */ + ext_reg->comparator_extension = + sieve_extension_register(svinst, &comparator_extension, TRUE); + ext_reg->match_type_extension = + sieve_extension_register(svinst, &match_type_extension, TRUE); + ext_reg->address_part_extension = + sieve_extension_register(svinst, &address_part_extension, TRUE); + + p_array_init(&ext_reg->preloaded_extensions, svinst->pool, 5); + array_append(&ext_reg->preloaded_extensions, + &ext_reg->comparator_extension, 1); + array_append(&ext_reg->preloaded_extensions, + &ext_reg->match_type_extension, 1); + array_append(&ext_reg->preloaded_extensions, + &ext_reg->address_part_extension, 1); + + /* Pre-load dummy extensions */ + for ( i = 0; i < sieve_dummy_extensions_count; i++ ) { + if ( (ext=_sieve_extension_register + (svinst, sieve_dummy_extensions[i], TRUE, FALSE)) == NULL ) + return FALSE; + + ext->dummy = TRUE; + } + + /* Pre-load core extensions */ + for ( i = 0; i < sieve_core_extensions_count; i++ ) { + if ( sieve_extension_register + (svinst, sieve_core_extensions[i], TRUE) == NULL ) + return FALSE; + } + + /* Pre-load extra extensions */ + for ( i = 0; i < sieve_extra_extensions_count; i++ ) { + if ( sieve_extension_register + (svinst, sieve_extra_extensions[i], FALSE) == NULL ) + return FALSE; + } + + /* Register deprecated extensions */ + for ( i = 0; i < sieve_deprecated_extensions_count; i++ ) { + if ( sieve_extension_register + (svinst, sieve_deprecated_extensions[i], FALSE) == NULL ) + return FALSE; + } + +#ifdef HAVE_SIEVE_UNFINISHED + /* Register unfinished extensions */ + for ( i = 0; i < sieve_unfinished_extensions_count; i++ ) { + if ( sieve_extension_register + (svinst, sieve_unfinished_extensions[i], FALSE) == NULL ) + return FALSE; + } +#endif + + /* More extensions can be added through plugins */ + + return TRUE; +} + +void sieve_extensions_configure(struct sieve_instance *svinst) +{ + const char *extensions; + + /* Apply sieve_extensions configuration */ + + if ( (extensions=sieve_setting_get + (svinst, "sieve_extensions")) != NULL ) + sieve_extensions_set_string(svinst, extensions, FALSE, FALSE); + + /* Apply sieve_global_extensions configuration */ + + if ( (extensions=sieve_setting_get + (svinst, "sieve_global_extensions")) != NULL ) + sieve_extensions_set_string(svinst, extensions, TRUE, FALSE); + + /* Apply sieve_implicit_extensions configuration */ + + if ( (extensions=sieve_setting_get + (svinst, "sieve_implicit_extensions")) != NULL ) + sieve_extensions_set_string(svinst, extensions, FALSE, TRUE); +} + +void sieve_extensions_deinit(struct sieve_instance *svinst) +{ + sieve_extension_registry_deinit(svinst); + sieve_capability_registry_deinit(svinst); +} + +/* + * Pre-loaded extensions + */ + +const struct sieve_extension *const *sieve_extensions_get_preloaded +(struct sieve_instance *svinst, unsigned int *count_r) +{ + struct sieve_extension_registry *ext_reg = svinst->ext_reg; + + return array_get(&ext_reg->preloaded_extensions, count_r); +} + +/* + * Extension registry + */ + +static bool _sieve_extension_load(struct sieve_extension *ext) +{ + /* Call load handler */ + if ( ext->def != NULL && ext->def->load != NULL && + !ext->def->load(ext, &ext->context) ) { + e_error(ext->svinst->event, + "failed to load '%s' extension support.", + ext->def->name); + return FALSE; + } + + return TRUE; +} + +static void _sieve_extension_unload(struct sieve_extension *ext) +{ + /* Call unload handler */ + if ( ext->def != NULL && ext->def->unload != NULL ) + ext->def->unload(ext); + ext->context = NULL; +} + +static void sieve_extension_registry_init(struct sieve_instance *svinst) +{ + struct sieve_extension_registry *ext_reg = svinst->ext_reg; + + p_array_init(&ext_reg->extensions, svinst->pool, 50); + hash_table_create + (&ext_reg->extension_index, default_pool, 0, str_hash, strcmp); +} + +static void sieve_extension_registry_deinit(struct sieve_instance *svinst) +{ + struct sieve_extension_registry *ext_reg = svinst->ext_reg; + struct sieve_extension * const *exts; + unsigned int i, ext_count; + + if ( !hash_table_is_created(ext_reg->extension_index) ) return; + + exts = array_get_modifiable(&ext_reg->extensions, &ext_count); + for ( i = 0; i < ext_count; i++ ) { + _sieve_extension_unload(exts[i]); + } + + hash_table_destroy(&ext_reg->extension_index); +} + +bool sieve_extension_reload(const struct sieve_extension *ext) +{ + struct sieve_extension_registry *ext_reg = ext->svinst->ext_reg; + struct sieve_extension * const *mod_ext; + int ext_id = ext->id; + + /* Let's not just cast the 'const' away */ + if ( ext_id >= 0 && ext_id < (int) array_count(&ext_reg->extensions) ) { + mod_ext = array_idx(&ext_reg->extensions, ext_id); + + return _sieve_extension_load(*mod_ext); + } + + return FALSE; +} + +static struct sieve_extension *sieve_extension_lookup +(struct sieve_instance *svinst, const char *name) +{ + struct sieve_extension_registry *ext_reg = svinst->ext_reg; + + return hash_table_lookup(ext_reg->extension_index, name); +} + +static struct sieve_extension *sieve_extension_alloc +(struct sieve_instance *svinst, + const struct sieve_extension_def *extdef) +{ + struct sieve_extension_registry *ext_reg = svinst->ext_reg; + struct sieve_extension *ext, **extr; + int ext_id; + + ext_id = (int)array_count(&ext_reg->extensions); + + /* Add extension to the registry */ + extr = array_append_space(&ext_reg->extensions); + *extr = ext = p_new(svinst->pool, struct sieve_extension, 1); + ext->id = ext_id; + ext->def = extdef; + ext->svinst = svinst; + return ext; +} + +static struct sieve_extension *_sieve_extension_register +(struct sieve_instance *svinst, const struct sieve_extension_def *extdef, + bool load, bool required) +{ + struct sieve_extension *ext; + + ext = sieve_extension_lookup(svinst, extdef->name); + + /* Register extension if it is not registered already */ + if ( ext == NULL ) { + ext = sieve_extension_alloc(svinst, extdef); + hash_table_insert + (svinst->ext_reg->extension_index, extdef->name, ext); + + } else if ( ext->overridden ) { + /* Create a dummy */ + ext = sieve_extension_alloc(svinst, extdef); + + } else { + /* Re-register it if it were previously unregistered + * (not going to happen) + */ + i_assert( ext->def == NULL || ext->def == extdef ); + ext->def = extdef; + } + + /* Enable extension */ + if ( load || required ) { + ext->enabled = ( ext->enabled || load ); + + /* Call load handler if extension was not loaded already */ + if ( !ext->loaded ) { + if ( !_sieve_extension_load(ext) ) + return NULL; + } + + ext->loaded = TRUE; + } + + ext->required = ( ext->required || required ); + + return ext; +} + +const struct sieve_extension *sieve_extension_register +(struct sieve_instance *svinst, const struct sieve_extension_def *extdef, + bool load) +{ + return _sieve_extension_register(svinst, extdef, load, FALSE); +} + +void sieve_extension_unregister(const struct sieve_extension *ext) +{ + struct sieve_extension_registry *ext_reg = ext->svinst->ext_reg; + struct sieve_extension * const *mod_ext; + int ext_id = ext->id; + + if ( ext_id >= 0 && ext_id < (int) array_count(&ext_reg->extensions) ) { + mod_ext = array_idx(&ext_reg->extensions, ext_id); + + sieve_extension_capabilities_unregister(*mod_ext); + _sieve_extension_unload(*mod_ext); + (*mod_ext)->loaded = FALSE; + (*mod_ext)->enabled = FALSE; + (*mod_ext)->def = NULL; + } +} + +const struct sieve_extension *sieve_extension_replace +(struct sieve_instance *svinst, const struct sieve_extension_def *extdef, + bool load) +{ + struct sieve_extension *ext; + + ext = sieve_extension_lookup(svinst, extdef->name); + if (ext != NULL) + sieve_extension_unregister(ext); + return sieve_extension_register(svinst, extdef, load); +} + +const struct sieve_extension *sieve_extension_require +(struct sieve_instance *svinst, const struct sieve_extension_def *extdef, + bool load) +{ + return _sieve_extension_register(svinst, extdef, load, TRUE); +} + +void sieve_extension_override +(struct sieve_instance *svinst, const char *name, + const struct sieve_extension *ext) +{ + struct sieve_extension_registry *ext_reg = ext->svinst->ext_reg; + struct sieve_extension * const *mod_ext; + struct sieve_extension *old_ext; + + old_ext = sieve_extension_lookup(svinst, name); + if (old_ext == ext) + return; + i_assert( old_ext == NULL || !old_ext->overridden ); + + i_assert( ext->id >= 0 && + ext->id < (int) array_count(&ext_reg->extensions) ); + mod_ext = array_idx(&ext_reg->extensions, ext->id); + + hash_table_update + (ext_reg->extension_index, name, *mod_ext); + if ( old_ext != NULL ) + old_ext->overridden = TRUE; +} + +unsigned int sieve_extensions_get_count(struct sieve_instance *svinst) +{ + struct sieve_extension_registry *ext_reg = svinst->ext_reg; + + return array_count(&ext_reg->extensions); +} + +const struct sieve_extension *const * +sieve_extensions_get_all(struct sieve_instance *svinst, + unsigned int *count_r) +{ + struct sieve_extension_registry *ext_reg = svinst->ext_reg; + + return (const struct sieve_extension *const *) + array_get(&ext_reg->extensions, count_r); +} + +const struct sieve_extension *sieve_extension_get_by_id +(struct sieve_instance *svinst, unsigned int ext_id) +{ + struct sieve_extension_registry *ext_reg = svinst->ext_reg; + struct sieve_extension * const *ext; + + if ( ext_id < array_count(&ext_reg->extensions) ) { + ext = array_idx(&ext_reg->extensions, ext_id); + + if ( (*ext)->def != NULL && ((*ext)->enabled || (*ext)->required) ) + return *ext; + } + + return NULL; +} + +const struct sieve_extension *sieve_extension_get_by_name +(struct sieve_instance *svinst, const char *name) +{ + const struct sieve_extension *ext; + + if ( *name == '@' ) + return NULL; + + if ( strlen(name) > 128 ) + return NULL; + + ext = sieve_extension_lookup(svinst, name); + if ( ext == NULL || ext->def == NULL || (!ext->enabled && !ext->required)) + return NULL; + + return ext; +} + +static inline bool _sieve_extension_listable(const struct sieve_extension *ext) +{ + return ( ext->enabled && ext->def != NULL && *(ext->def->name) != '@' + && !ext->dummy && !ext->global && !ext->overridden); +} + +const char *sieve_extensions_get_string(struct sieve_instance *svinst) +{ + struct sieve_extension_registry *ext_reg = svinst->ext_reg; + string_t *extstr = t_str_new(256); + struct sieve_extension * const *exts; + unsigned int i, ext_count; + + exts = array_get(&ext_reg->extensions, &ext_count); + + if ( ext_count > 0 ) { + i = 0; + + /* Find first listable extension */ + while ( i < ext_count && !_sieve_extension_listable(exts[i]) ) + i++; + + if ( i < ext_count ) { + /* Add first to string */ + str_append(extstr, exts[i]->def->name); + i++; + + /* Add others */ + for ( ; i < ext_count; i++ ) { + if ( _sieve_extension_listable(exts[i]) ) { + str_append_c(extstr, ' '); + str_append(extstr, exts[i]->def->name); + } + } + } + } + + return str_c(extstr); +} + +static void sieve_extension_set_enabled +(struct sieve_extension *ext, bool enabled) +{ + if ( enabled ) { + ext->enabled = TRUE; + + if ( !ext->loaded ) { + (void)_sieve_extension_load(ext); + } + + ext->loaded = TRUE; + } else { + ext->enabled = FALSE; + } +} + +static void sieve_extension_set_global +(struct sieve_extension *ext, bool enabled) +{ + if ( enabled ) { + sieve_extension_set_enabled(ext, TRUE); + ext->global = TRUE; + } else { + ext->global = FALSE; + } +} + +static void sieve_extension_set_implicit +(struct sieve_extension *ext, bool enabled) +{ + if ( enabled ) { + sieve_extension_set_enabled(ext, TRUE); + ext->implicit = TRUE; + } else { + ext->implicit = FALSE; + } +} + +void sieve_extensions_set_string +(struct sieve_instance *svinst, const char *ext_string, + bool global, bool implicit) +{ + struct sieve_extension_registry *ext_reg = svinst->ext_reg; + ARRAY(const struct sieve_extension *) enabled_extensions; + ARRAY(const struct sieve_extension *) disabled_extensions; + const struct sieve_extension *const *ext_enabled; + const struct sieve_extension *const *ext_disabled; + struct sieve_extension **exts; + const char **ext_names; + unsigned int i, ext_count, ena_count, dis_count; + bool relative = FALSE; + + if ( ext_string == NULL ) { + if ( global || implicit ) return; + + /* Enable all */ + exts = array_get_modifiable(&ext_reg->extensions, &ext_count); + + for ( i = 0; i < ext_count; i++ ) + sieve_extension_set_enabled(exts[i], TRUE); + + return; + } + + T_BEGIN { + t_array_init(&enabled_extensions, array_count(&ext_reg->extensions)); + t_array_init(&disabled_extensions, array_count(&ext_reg->extensions)); + + ext_names = t_strsplit_spaces(ext_string, " \t"); + + while ( *ext_names != NULL ) { + const char *name = *ext_names; + + ext_names++; + + if ( *name != '\0' ) { + const struct sieve_extension *ext; + char op = '\0'; /* No add/remove operation */ + + if ( *name == '+' /* Add to existing config */ + || *name == '-' ) { /* Remove from existing config */ + op = *name++; + relative = TRUE; + } + + if ( *name == '@' ) + ext = NULL; + else + ext = hash_table_lookup(ext_reg->extension_index, name); + + if ( ext == NULL || ext->def == NULL ) { + e_warning(svinst->event, + "ignored unknown extension '%s' while configuring " + "available extensions", name); + continue; + } + + if ( op == '-' ) + array_append(&disabled_extensions, &ext, 1); + else + array_append(&enabled_extensions, &ext, 1); + } + } + + exts = array_get_modifiable(&ext_reg->extensions, &ext_count); + ext_enabled = array_get(&enabled_extensions, &ena_count); + ext_disabled = array_get(&disabled_extensions, &dis_count); + + /* Set new extension status */ + + for ( i = 0; i < ext_count; i++ ) { + unsigned int j; + bool enabled = FALSE; + + if ( exts[i]->id < 0 || exts[i]->def == NULL || + *(exts[i]->def->name) == '@' ) { + continue; + } + + /* If extensions are specified relative to the default set, + * we first need to check which ones are disabled + */ + + if ( relative ) { + if ( global ) + enabled = exts[i]->global; + else if ( implicit ) + enabled = exts[i]->implicit; + else + enabled = exts[i]->enabled; + + if ( enabled ) { + /* Disable if explicitly disabled */ + for ( j = 0; j < dis_count; j++ ) { + if ( ext_disabled[j]->def == exts[i]->def ) { + enabled = FALSE; + break; + } + } + } + } + + /* Enable if listed with '+' or no prefix */ + + for ( j = 0; j < ena_count; j++ ) { + if ( ext_enabled[j]->def == exts[i]->def ) { + enabled = TRUE; + break; + } + } + + /* Perform actual activation/deactivation */ + if ( global ) { + sieve_extension_set_global(exts[i], enabled); + } else if ( implicit ) { + sieve_extension_set_implicit(exts[i], enabled); + } else { + sieve_extension_set_enabled(exts[i], enabled); + } + } + } T_END; +} + +const struct sieve_extension *sieve_get_match_type_extension + (struct sieve_instance *svinst) +{ + return svinst->ext_reg->match_type_extension; +} + +const struct sieve_extension *sieve_get_comparator_extension + (struct sieve_instance *svinst) +{ + return svinst->ext_reg->comparator_extension; +} + +const struct sieve_extension *sieve_get_address_part_extension + (struct sieve_instance *svinst) +{ + return svinst->ext_reg->address_part_extension; +} + +void sieve_enable_debug_extension(struct sieve_instance *svinst) +{ + (void) sieve_extension_register(svinst, &vnd_debug_extension, TRUE); +} + +/* + * Extension capabilities + */ + +struct sieve_capability_registration { + const struct sieve_extension *ext; + const struct sieve_extension_capabilities *capabilities; +}; + +void sieve_capability_registry_init(struct sieve_instance *svinst) +{ + struct sieve_extension_registry *ext_reg = svinst->ext_reg; + + hash_table_create + (&ext_reg->capabilities_index, default_pool, 0, str_hash, strcmp); +} + +void sieve_capability_registry_deinit(struct sieve_instance *svinst) +{ + struct sieve_extension_registry *ext_reg = svinst->ext_reg; + + if ( !hash_table_is_created(ext_reg->capabilities_index) ) return; + + hash_table_destroy(&svinst->ext_reg->capabilities_index); +} + +void sieve_extension_capabilities_register +(const struct sieve_extension *ext, + const struct sieve_extension_capabilities *cap) +{ + struct sieve_instance *svinst = ext->svinst; + struct sieve_extension_registry *ext_reg = svinst->ext_reg; + struct sieve_capability_registration *reg; + + reg = hash_table_lookup(ext_reg->capabilities_index, cap->name); + if (reg != NULL) { + /* Already registered */ + return; + } + + reg = p_new(svinst->pool, struct sieve_capability_registration, 1); + reg->ext = ext; + reg->capabilities = cap; + + hash_table_insert(ext_reg->capabilities_index, cap->name, reg); +} + +void sieve_extension_capabilities_unregister +(const struct sieve_extension *ext) +{ + struct sieve_extension_registry *ext_reg = ext->svinst->ext_reg; + struct hash_iterate_context *hictx; + const char *name; + struct sieve_capability_registration *reg; + + hictx = hash_table_iterate_init(ext_reg->capabilities_index); + while ( hash_table_iterate(hictx, ext_reg->capabilities_index, &name, ®) ) { + if ( reg->ext == ext ) + hash_table_remove(ext_reg->capabilities_index, name); + } + hash_table_iterate_deinit(&hictx); +} + +const char *sieve_extension_capabilities_get_string +(struct sieve_instance *svinst, const char *cap_name) +{ + struct sieve_extension_registry *ext_reg = svinst->ext_reg; + const struct sieve_capability_registration *cap_reg = + hash_table_lookup(ext_reg->capabilities_index, cap_name); + const struct sieve_extension_capabilities *cap; + + if ( cap_reg == NULL || cap_reg->capabilities == NULL ) + return NULL; + + cap = cap_reg->capabilities; + + if ( cap->get_string == NULL || !cap_reg->ext->enabled ) + return NULL; + + return cap->get_string(cap_reg->ext); +} + + + + |