diff --git a/src/lib-sieve/plugins/regex/Makefile.am b/src/lib-sieve/plugins/regex/Makefile.am index 3b4444768d544eb4522a94ed8bca26bd9d03cd05..d80bbedef641490c36f7f602d902a3319dadbb51 100644 --- a/src/lib-sieve/plugins/regex/Makefile.am +++ b/src/lib-sieve/plugins/regex/Makefile.am @@ -8,5 +8,8 @@ AM_CPPFLAGS = \ -I$(dovecot_incdir)/src/lib-storage libsieve_ext_regex_la_SOURCES = \ + mcht-regex.c \ ext-regex.c +noinst_HEADERS = \ + ext-regex-common.h diff --git a/src/lib-sieve/plugins/regex/ext-regex-common.h b/src/lib-sieve/plugins/regex/ext-regex-common.h new file mode 100644 index 0000000000000000000000000000000000000000..3c50ad1cf423cdda882af440c6ecb1cd37ded630 --- /dev/null +++ b/src/lib-sieve/plugins/regex/ext-regex-common.h @@ -0,0 +1,8 @@ +#ifndef __EXT_REGEX_COMMON_H +#define __EXT_REGEX_COMMON_H + +extern const struct sieve_match_type_extension regex_match_extension; + +#endif + + diff --git a/src/lib-sieve/plugins/regex/ext-regex.c b/src/lib-sieve/plugins/regex/ext-regex.c index a9a39b882cd009c35fad88ebea54f80240625be5..ca8b1999f998a5a2e9cd59afa5b0f0a044b10c44 100644 --- a/src/lib-sieve/plugins/regex/ext-regex.c +++ b/src/lib-sieve/plugins/regex/ext-regex.c @@ -31,6 +31,8 @@ #include "sieve-generator.h" #include "sieve-interpreter.h" +#include "ext-regex-common.h" + #include <sys/types.h> #include <ctype.h> #include <regex.h> @@ -41,12 +43,6 @@ static bool ext_regex_load(int ext_id); static bool ext_regex_validator_load(struct sieve_validator *validator); static bool ext_regex_binary_load(struct sieve_binary *sbin); -void mtch_regex_match_init(struct sieve_match_context *mctx); -static bool mtch_regex_match - (struct sieve_match_context *mctx, const char *val, size_t val_size, - const char *key, size_t key_size, int key_index); -bool mtch_regex_match_deinit(struct sieve_match_context *mctx); - /* Extension definitions */ static int ext_my_id; @@ -69,219 +65,20 @@ static bool ext_regex_load(int ext_id) return TRUE; } -/* Extension access structures */ - -extern const struct sieve_match_type_extension regex_match_extension; - -bool mtch_regex_validate_context -(struct sieve_validator *validator, struct sieve_ast_argument *arg, - struct sieve_match_type_context *ctx, struct sieve_ast_argument *key_arg); +/* + * Extension access structures + */ -const struct sieve_match_type regex_match_type = { - "regex", TRUE, - ®ex_match_extension, - 0, - NULL, - mtch_regex_validate_context, - mtch_regex_match_init, - mtch_regex_match, - mtch_regex_match_deinit -}; +extern const struct sieve_match_type regex_match_type; const struct sieve_match_type_extension regex_match_extension = { ®ex_extension, SIEVE_EXT_DEFINE_MATCH_TYPE(regex_match_type) }; -/* Validation */ - -/* Wrapper around the regerror function for easy access */ -static const char *_regexp_error(regex_t *regexp, int errorcode) -{ - size_t errsize = regerror(errorcode, regexp, NULL, 0); - - if ( errsize > 0 ) { - char *errbuf; - - buffer_t *error_buf = - buffer_create_dynamic(pool_datastack_create(), errsize); - errbuf = buffer_get_space_unsafe(error_buf, 0, errsize); - - errsize = regerror(errorcode, regexp, errbuf, errsize); - - /* We don't want the error to start with a capital letter */ - errbuf[0] = i_tolower(errbuf[0]); - - buffer_append_space_unsafe(error_buf, errsize); - - return str_c(error_buf); - } - - return ""; -} - -static bool mtch_regex_validate_regexp -(struct sieve_validator *validator, struct sieve_match_type_context *ctx, - struct sieve_ast_argument *key, int cflags) -{ - int ret; - regex_t regexp; - - if ( (ret=regcomp(®exp, sieve_ast_argument_strc(key), cflags)) != 0 ) { - sieve_command_validate_error(validator, ctx->command_ctx, - "invalid regular expression for regex match: %s", - _regexp_error(®exp, ret)); - - regfree(®exp); - return FALSE; - } - - regfree(®exp); - return TRUE; -} - -bool mtch_regex_validate_context -(struct sieve_validator *validator, struct sieve_ast_argument *arg, - struct sieve_match_type_context *ctx, struct sieve_ast_argument *key_arg) -{ - int cflags; - struct sieve_ast_argument *carg = - sieve_command_first_argument(ctx->command_ctx); - - cflags = REG_EXTENDED | REG_NOSUB; - while ( carg != NULL ) { - if ( carg != arg && carg->argument == &comparator_tag ) { - if ( sieve_comparator_tag_is(carg, &i_ascii_casemap_comparator) ) - cflags = REG_EXTENDED | REG_NOSUB | REG_ICASE; - else if ( sieve_comparator_tag_is(carg, &i_octet_comparator) ) - cflags = REG_EXTENDED | REG_NOSUB ; - else { - sieve_command_validate_error(validator, ctx->command_ctx, - "regex match type only supports " - "i;octet and i;ascii-casemap comparators" ); - return FALSE; - } - - return TRUE; - } - - carg = sieve_ast_argument_next(carg); - } - - /* Validate regular expression(s) */ - if ( sieve_ast_argument_type(key_arg) == SAAT_STRING ) { - /* Single string */ - if ( !mtch_regex_validate_regexp(validator, ctx, key_arg, cflags) ) - return FALSE; - - } else if ( sieve_ast_argument_type(key_arg) == SAAT_STRING_LIST ) { - /* String list */ - struct sieve_ast_argument *stritem = sieve_ast_strlist_first(key_arg); - - while ( stritem != NULL ) { - if ( !mtch_regex_validate_regexp(validator, ctx, stritem, cflags) ) - return FALSE; - - stritem = sieve_ast_strlist_next(stritem); - } - } else { - /* ??? */ - sieve_command_validate_error(validator, ctx->command_ctx, - "!!BUG!!: mtch_regex_validate_context: invalid ast argument type(%s)", - sieve_ast_argument_type_name(sieve_ast_argument_type(key_arg)) ); - return FALSE; - } - - return TRUE; -} - -/* Actual extension implementation */ - -struct mtch_regex_context { - ARRAY_DEFINE(reg_expressions, regex_t *); - int value_index; -}; - -void mtch_regex_match_init - (struct sieve_match_context *mctx) -{ - struct mtch_regex_context *ctx = - t_new(struct mtch_regex_context, 1); - - t_array_init(&ctx->reg_expressions, 4); - - ctx->value_index = -1; - - mctx->data = (void *) ctx; -} - -static regex_t *mtch_regex_get -(struct mtch_regex_context *ctx, - const struct sieve_comparator *cmp, - const char *key, unsigned int key_index) -{ - regex_t *regexp = NULL; - regex_t * const *rxp = ®exp; - int ret; - int cflags; - - if ( ctx->value_index <= 0 ) { - regexp = p_new(pool_datastack_create(), regex_t, 1); - - if ( cmp == &i_octet_comparator ) - cflags = REG_EXTENDED | REG_NOSUB; - else if ( cmp == &i_ascii_casemap_comparator ) - cflags = REG_EXTENDED | REG_NOSUB | REG_ICASE; - else - return NULL; - - if ( (ret=regcomp(regexp, key, cflags)) != 0 ) { - /* FIXME: Do something useful, i.e. report error somewhere */ - return NULL; - } - - array_idx_set(&ctx->reg_expressions, key_index, ®exp); - rxp = ®exp; - } else { - rxp = array_idx(&ctx->reg_expressions, key_index); - } - - return *rxp; -} - -static bool mtch_regex_match -(struct sieve_match_context *mctx, - const char *val, size_t val_size ATTR_UNUSED, - const char *key, size_t key_size ATTR_UNUSED, int key_index) -{ - struct mtch_regex_context *ctx = (struct mtch_regex_context *) mctx->data; - regex_t *regexp; - - if ( key_index < 0 ) return FALSE; - - if ( key_index == 0 ) ctx->value_index++; - - regexp = mtch_regex_get(ctx, mctx->comparator, key, key_index); - - return ( regexec(regexp, val, 0, NULL, 0) == 0 ); -} - -bool mtch_regex_match_deinit - (struct sieve_match_context *mctx) -{ - struct mtch_regex_context *ctx = (struct mtch_regex_context *) mctx->data; - unsigned int i; - - for ( i = 0; i < array_count(&ctx->reg_expressions); i++ ) { - regex_t * const *regexp = array_idx(&ctx->reg_expressions, i); - - regfree(*regexp); - } - - return FALSE; -} - -/* Load extension into validator */ +/* + * Load extension into validator + */ static bool ext_regex_validator_load(struct sieve_validator *validator) { @@ -291,7 +88,9 @@ static bool ext_regex_validator_load(struct sieve_validator *validator) return TRUE; } -/* Load extension into binary */ +/* + * Load extension into binary + */ static bool ext_regex_binary_load(struct sieve_binary *sbin) { diff --git a/src/lib-sieve/plugins/regex/mcht-regex.c b/src/lib-sieve/plugins/regex/mcht-regex.c new file mode 100644 index 0000000000000000000000000000000000000000..c3156f36ce1945f9fb27644ce7fb088b2170fc91 --- /dev/null +++ b/src/lib-sieve/plugins/regex/mcht-regex.c @@ -0,0 +1,239 @@ +/* Match-type ':regex' + */ + +#include "lib.h" +#include "mempool.h" +#include "buffer.h" +#include "array.h" +#include "str.h" + +#include "sieve-common.h" +#include "sieve-ast.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" + +#include "ext-regex-common.h" + +#include <sys/types.h> +#include <ctype.h> +#include <regex.h> + +/* + * Forward declarations + */ + +void mcht_regex_match_init(struct sieve_match_context *mctx); +static bool mcht_regex_match + (struct sieve_match_context *mctx, const char *val, size_t val_size, + const char *key, size_t key_size, int key_index); +bool mcht_regex_match_deinit(struct sieve_match_context *mctx); + +bool mcht_regex_validate_context +(struct sieve_validator *validator, struct sieve_ast_argument *arg, + struct sieve_match_type_context *ctx, struct sieve_ast_argument *key_arg); + +const struct sieve_match_type regex_match_type = { + "regex", TRUE, + ®ex_match_extension, + 0, + NULL, + mcht_regex_validate_context, + mcht_regex_match_init, + mcht_regex_match, + mcht_regex_match_deinit +}; + +/* + * Match-type validation + */ + +/* Wrapper around the regerror function for easy access */ +static const char *_regexp_error(regex_t *regexp, int errorcode) +{ + size_t errsize = regerror(errorcode, regexp, NULL, 0); + + if ( errsize > 0 ) { + char *errbuf; + + buffer_t *error_buf = + buffer_create_dynamic(pool_datastack_create(), errsize); + errbuf = buffer_get_space_unsafe(error_buf, 0, errsize); + + errsize = regerror(errorcode, regexp, errbuf, errsize); + + /* We don't want the error to start with a capital letter */ + errbuf[0] = i_tolower(errbuf[0]); + + buffer_append_space_unsafe(error_buf, errsize); + + return str_c(error_buf); + } + + return ""; +} + +static bool mcht_regex_validate_regexp +(struct sieve_validator *validator, struct sieve_match_type_context *ctx, + struct sieve_ast_argument *key, int cflags) +{ + int ret; + regex_t regexp; + + if ( (ret=regcomp(®exp, sieve_ast_argument_strc(key), cflags)) != 0 ) { + sieve_command_validate_error(validator, ctx->command_ctx, + "invalid regular expression for regex match: %s", + _regexp_error(®exp, ret)); + + regfree(®exp); + return FALSE; + } + + regfree(®exp); + return TRUE; +} + +bool mcht_regex_validate_context +(struct sieve_validator *validator, struct sieve_ast_argument *arg, + struct sieve_match_type_context *ctx, struct sieve_ast_argument *key_arg) +{ + int cflags; + struct sieve_ast_argument *carg = + sieve_command_first_argument(ctx->command_ctx); + + cflags = REG_EXTENDED | REG_NOSUB; + while ( carg != NULL ) { + if ( carg != arg && carg->argument == &comparator_tag ) { + if ( sieve_comparator_tag_is(carg, &i_ascii_casemap_comparator) ) + cflags = REG_EXTENDED | REG_NOSUB | REG_ICASE; + else if ( sieve_comparator_tag_is(carg, &i_octet_comparator) ) + cflags = REG_EXTENDED | REG_NOSUB ; + else { + sieve_command_validate_error(validator, ctx->command_ctx, + "regex match type only supports " + "i;octet and i;ascii-casemap comparators" ); + return FALSE; + } + + return TRUE; + } + + carg = sieve_ast_argument_next(carg); + } + + /* Validate regular expression(s) */ + if ( sieve_ast_argument_type(key_arg) == SAAT_STRING ) { + /* Single string */ + if ( !mcht_regex_validate_regexp(validator, ctx, key_arg, cflags) ) + return FALSE; + + } else if ( sieve_ast_argument_type(key_arg) == SAAT_STRING_LIST ) { + /* String list */ + struct sieve_ast_argument *stritem = sieve_ast_strlist_first(key_arg); + + while ( stritem != NULL ) { + if ( !mcht_regex_validate_regexp(validator, ctx, stritem, cflags) ) + return FALSE; + + stritem = sieve_ast_strlist_next(stritem); + } + } else { + /* ??? */ + sieve_command_validate_error(validator, ctx->command_ctx, + "!!BUG!!: mcht_regex_validate_context: invalid ast argument type(%s)", + sieve_ast_argument_type_name(sieve_ast_argument_type(key_arg)) ); + return FALSE; + } + + return TRUE; +} + +/* + * Match-type implementation + */ + +struct mcht_regex_context { + ARRAY_DEFINE(reg_expressions, regex_t *); + int value_index; +}; + +void mcht_regex_match_init + (struct sieve_match_context *mctx) +{ + struct mcht_regex_context *ctx = + t_new(struct mcht_regex_context, 1); + + t_array_init(&ctx->reg_expressions, 4); + + ctx->value_index = -1; + + mctx->data = (void *) ctx; +} + +static regex_t *mcht_regex_get +(struct mcht_regex_context *ctx, + const struct sieve_comparator *cmp, + const char *key, unsigned int key_index) +{ + regex_t *regexp = NULL; + regex_t * const *rxp = ®exp; + int ret; + int cflags; + + if ( ctx->value_index <= 0 ) { + regexp = p_new(pool_datastack_create(), regex_t, 1); + + if ( cmp == &i_octet_comparator ) + cflags = REG_EXTENDED | REG_NOSUB; + else if ( cmp == &i_ascii_casemap_comparator ) + cflags = REG_EXTENDED | REG_NOSUB | REG_ICASE; + else + return NULL; + + if ( (ret=regcomp(regexp, key, cflags)) != 0 ) { + /* FIXME: Do something useful, i.e. report error somewhere */ + return NULL; + } + + array_idx_set(&ctx->reg_expressions, key_index, ®exp); + rxp = ®exp; + } else { + rxp = array_idx(&ctx->reg_expressions, key_index); + } + + return *rxp; +} + +static bool mcht_regex_match +(struct sieve_match_context *mctx, + const char *val, size_t val_size ATTR_UNUSED, + const char *key, size_t key_size ATTR_UNUSED, int key_index) +{ + struct mcht_regex_context *ctx = (struct mcht_regex_context *) mctx->data; + regex_t *regexp; + + if ( key_index < 0 ) return FALSE; + + if ( key_index == 0 ) ctx->value_index++; + + regexp = mcht_regex_get(ctx, mctx->comparator, key, key_index); + + return ( regexec(regexp, val, 0, NULL, 0) == 0 ); +} + +bool mcht_regex_match_deinit + (struct sieve_match_context *mctx) +{ + struct mcht_regex_context *ctx = (struct mcht_regex_context *) mctx->data; + unsigned int i; + + for ( i = 0; i < array_count(&ctx->reg_expressions); i++ ) { + regex_t * const *regexp = array_idx(&ctx->reg_expressions, i); + + regfree(*regexp); + } + + return FALSE; +} +