summaryrefslogtreecommitdiffstats
path: root/pigeonhole/src/lib-sieve/sieve-extensions.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:51:24 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:51:24 +0000
commitf7548d6d28c313cf80e6f3ef89aed16a19815df1 (patch)
treea3f6f2a3f247293bee59ecd28e8cd8ceb6ca064a /pigeonhole/src/lib-sieve/sieve-extensions.c
parentInitial commit. (diff)
downloaddovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.tar.xz
dovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.zip
Adding upstream version 1:2.3.19.1+dfsg1.upstream/1%2.3.19.1+dfsg1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'pigeonhole/src/lib-sieve/sieve-extensions.c')
-rw-r--r--pigeonhole/src/lib-sieve/sieve-extensions.c879
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, &regex_extension, &imap4flags_extension,
+ &copy_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,
+ &notify_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, &reg) ) {
+ 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);
+}
+
+
+
+