diff --git a/src/lib-sieve/cmd-require.c b/src/lib-sieve/cmd-require.c index e048e6ea4f1ee4ad88782719c2c079b519a0dc4b..1f3e129412f14e226c5a5dca6ab1bc89c484c589 100644 --- a/src/lib-sieve/cmd-require.c +++ b/src/lib-sieve/cmd-require.c @@ -57,7 +57,7 @@ static bool cmd_require_validate if ( sieve_ast_argument_type(arg) == SAAT_STRING ) { /* Single string */ const struct sieve_extension *ext = sieve_validator_extension_load - (validator, cmd, sieve_ast_argument_str(arg)); + (validator, arg, sieve_ast_argument_str(arg)); if ( ext == NULL ) result = FALSE; @@ -67,7 +67,7 @@ static bool cmd_require_validate while ( stritem != NULL ) { const struct sieve_extension *ext = sieve_validator_extension_load - (validator, cmd, sieve_ast_strlist_str(stritem)); + (validator, stritem, sieve_ast_strlist_str(stritem)); if ( ext == NULL ) result = FALSE; diff --git a/src/lib-sieve/sieve-extensions.c b/src/lib-sieve/sieve-extensions.c index f58281e05d8b20007c26eeaf25f98af8616f0629..d5441ea3ce4b9be412e62eaa55dee00bcf893ce6 100644 --- a/src/lib-sieve/sieve-extensions.c +++ b/src/lib-sieve/sieve-extensions.c @@ -148,6 +148,7 @@ void sieve_extensions_deinit(void) struct sieve_extension_registration { const struct sieve_extension *extension; int id; + bool required; }; static ARRAY_DEFINE(extensions, struct sieve_extension_registration); @@ -155,37 +156,74 @@ static struct hash_table *extension_index; static void sieve_extensions_init_registry(void) { - p_array_init(&extensions, default_pool, 30); + i_array_init(&extensions, 30); extension_index = hash_table_create (default_pool, default_pool, 0, str_hash, (hash_cmp_callback_t *)strcmp); } +static struct sieve_extension_registration *_sieve_extension_register +(const struct sieve_extension *extension) +{ + struct sieve_extension_registration *ereg = (struct sieve_extension_registration *) + hash_table_lookup(extension_index, extension->name); + + /* Register extension if it is not registered already */ + if ( ereg == NULL ) { + int ext_id = array_count(&extensions); + + /* Add extension to the registry */ + + ereg = array_append_space(&extensions); + ereg->id = ext_id; + + hash_table_insert(extension_index, (void *) extension->name, (void *) ereg); + } + + /* Enable extension */ + if ( ereg->extension == NULL && extension != NULL ) { + if ( extension->_id != NULL ) { + int ext_id = *(extension->_id); + + /* Make sure extension is enabled */ + *(extension->_id) = ereg->id; + + /* Call load handler if extension was not enabled */ + if ( ext_id == -1 && extension->load != NULL && !extension->load() ) { + sieve_sys_error("failed to load '%s' extension support.", + extension->name); + return NULL; + } + } + + ereg->extension = extension; + } + + return ereg; +} + int sieve_extension_register(const struct sieve_extension *extension) { - int ext_id = array_count(&extensions); struct sieve_extension_registration *ereg; - - ereg = array_append_space(&extensions); - - ereg->extension = extension; - ereg->id = ext_id; - - hash_table_insert(extension_index, (void *) extension->name, (void *) ereg); - - /* Assign ID */ - - if ( extension->_id != NULL ) { - i_assert( *(extension->_id) == -1 ); - *(extension->_id) = ext_id; - - if ( extension->load != NULL && !extension->load() ) { - sieve_sys_error("failed to load '%s' extension support.", - extension->name); - return -1; - } + + /* Register the extension */ + if ( (ereg=_sieve_extension_register(extension)) == NULL ) { + return -1; } - return ext_id; + return ereg->id; +} + +int sieve_extension_require(const struct sieve_extension *extension) +{ + struct sieve_extension_registration *ereg; + + /* Register (possibly unknown) extension */ + if ( (ereg=_sieve_extension_register(extension)) == NULL ) { + return -1; + } + + ereg->required = TRUE; + return ereg->id; } int sieve_extensions_get_count(void) @@ -209,7 +247,7 @@ const struct sieve_extension *sieve_extension_get_by_id(unsigned int ext_id) const struct sieve_extension *sieve_extension_get_by_name(const char *name) { - struct sieve_extension_registration *ereg; + struct sieve_extension_registration *ereg; if ( *name == '@' ) return NULL; @@ -335,7 +373,7 @@ void sieve_extensions_set_string(const char *ext_string) if ( eregs[i].extension->_id != NULL && *(eregs[i].extension->name) != '@' ) { - if ( disabled ) { + if ( disabled && !eregs[i].required ) { *(eregs[i].extension->_id) = -1; } else { *(eregs[i].extension->_id) = eregs[i].id; @@ -349,11 +387,12 @@ static void sieve_extensions_deinit_registry(void) struct hash_iterate_context *itx = hash_table_iterate_init(extension_index); void *key; - void *ereg; + void *value; - while ( hash_table_iterate(itx, &key, &ereg) ) { - const struct sieve_extension *ext = - ((struct sieve_extension_registration *) ereg)->extension; + while ( hash_table_iterate(itx, &key, &value) ) { + struct sieve_extension_registration *ereg = + (struct sieve_extension_registration *) value; + const struct sieve_extension *ext = ereg->extension; if ( ext->unload != NULL ) ext->unload(); diff --git a/src/lib-sieve/sieve-extensions.h b/src/lib-sieve/sieve-extensions.h index 18ebbcdb3a72a66a03ba866437141ecf060ee8ce..7affa072e247d631667527ea1feae64750f469aa 100644 --- a/src/lib-sieve/sieve-extensions.h +++ b/src/lib-sieve/sieve-extensions.h @@ -90,6 +90,7 @@ void sieve_extensions_deinit(void); */ int sieve_extension_register(const struct sieve_extension *extension); +int sieve_extension_require(const struct sieve_extension *extension); int sieve_extensions_get_count(void); const struct sieve_extension *sieve_extension_get_by_id(unsigned int ext_id); const struct sieve_extension *sieve_extension_get_by_name(const char *name); diff --git a/src/lib-sieve/sieve-validator.c b/src/lib-sieve/sieve-validator.c index a5b053b369e5d2816004fcc4122e3a9dd53c250f..f155cde6decf0a83bb37e266756572b91356d8ab 100644 --- a/src/lib-sieve/sieve-validator.c +++ b/src/lib-sieve/sieve-validator.c @@ -63,6 +63,7 @@ struct sieve_default_argument { struct sieve_validator_extension_reg { const struct sieve_validator_extension *val_ext; + struct sieve_ast_argument *arg; void *context; }; @@ -506,14 +507,16 @@ static const struct sieve_argument *sieve_validator_find_tag_by_identifier */ const struct sieve_extension *sieve_validator_extension_load -(struct sieve_validator *validator, struct sieve_command_context *cmd, +(struct sieve_validator *valdtr, struct sieve_ast_argument *ext_arg, string_t *ext_name) { + int ext_id; + struct sieve_validator_extension_reg *reg; const struct sieve_extension *ext; const char *name = str_c(ext_name); if ( str_len(ext_name) > 128 ) { - sieve_command_validate_error(validator, cmd, + sieve_argument_validate_error(valdtr, ext_arg, "unsupported sieve capability '%s' (name is impossibly long)", str_sanitize(name, 128)); return NULL; @@ -522,19 +525,26 @@ const struct sieve_extension *sieve_validator_extension_load ext = sieve_extension_get_by_name(name); if ( ext == NULL ) { - sieve_command_validate_error(validator, cmd, + sieve_argument_validate_error(valdtr, ext_arg, "unsupported sieve capability '%s'", name); return NULL; } - sieve_ast_extension_link(validator->ast, ext); + sieve_ast_extension_link(valdtr->ast, ext); - if ( ext->validator_load != NULL && !ext->validator_load(validator) ) { - sieve_command_validate_error(validator, cmd, + if ( ext->validator_load != NULL && !ext->validator_load(valdtr) ) { + sieve_argument_validate_error(valdtr, ext_arg, "failed to load sieve capability '%s'", ext->name); return NULL; } - + + /* Register extension no matter what and store the AST argument registering it */ + ext_id = SIEVE_EXT_ID(ext); + if ( ext_id >= 0 ) { + reg = array_idx_modifiable(&valdtr->extensions, (unsigned int) ext_id); + reg->arg = ext_arg; + } + return ext; } @@ -542,24 +552,27 @@ void sieve_validator_extension_register (struct sieve_validator *valdtr, const struct sieve_validator_extension *val_ext, void *context) { - struct sieve_validator_extension_reg reg = { val_ext, context }; + struct sieve_validator_extension_reg *reg; int ext_id = SIEVE_EXT_ID(val_ext->ext); if ( ext_id < 0 ) return; - array_idx_set(&valdtr->extensions, (unsigned int) ext_id, ®); + reg = array_idx_modifiable(&valdtr->extensions, (unsigned int) ext_id); + reg->val_ext = val_ext; + reg->context = context; } void sieve_validator_extension_set_context (struct sieve_validator *valdtr, const struct sieve_extension *ext, void *context) { - struct sieve_validator_extension_reg reg = { NULL, context }; + struct sieve_validator_extension_reg *reg; int ext_id = SIEVE_EXT_ID(ext); if ( ext_id < 0 ) return; - array_idx_set(&valdtr->extensions, (unsigned int) ext_id, ®); + reg = array_idx_modifiable(&valdtr->extensions, (unsigned int) ext_id); + reg->context = context; } void *sieve_validator_extension_get_context @@ -1138,6 +1151,18 @@ static bool sieve_validate_block } bool sieve_validator_run(struct sieve_validator *validator) { + const struct sieve_validator_extension_reg *extrs; + unsigned int ext_count, i; + + /* Validate registered extensions */ + extrs = array_get(&validator->extensions, &ext_count); + for ( i = 0; i < ext_count; i++ ) { + if ( extrs[i].val_ext != NULL && extrs[i].val_ext->validate != NULL ) { + if ( !extrs[i].val_ext->validate(validator, extrs[i].context, extrs[i].arg) ) + return FALSE; + } + } + return sieve_validate_block(validator, sieve_ast_root(validator->ast)); } diff --git a/src/lib-sieve/sieve-validator.h b/src/lib-sieve/sieve-validator.h index c16070664351a9e1da99d03c812626e449e194bf..2b553be680b77adbbdebb36b2935e6d69a18f6e2 100644 --- a/src/lib-sieve/sieve-validator.h +++ b/src/lib-sieve/sieve-validator.h @@ -121,11 +121,14 @@ bool sieve_validate_tag_parameter struct sieve_validator_extension { const struct sieve_extension *ext; + bool (*validate)(struct sieve_validator *valdtr, void *context, + struct sieve_ast_argument *require_arg); + void (*free)(struct sieve_validator *valdtr, void *context); }; const struct sieve_extension *sieve_validator_extension_load - (struct sieve_validator *validator, struct sieve_command_context *cmd, + (struct sieve_validator *validator, struct sieve_ast_argument *ext_arg, string_t *ext_name); void sieve_validator_extension_register