From 1935269d70a8e71b9684fb6db9e588f42b1dd8e9 Mon Sep 17 00:00:00 2001 From: Stephan Bosch <stephan@rename-it.nl> Date: Sat, 8 Mar 2008 00:28:34 +0100 Subject: [PATCH] Variables: First work towards match value support. --- src/lib-sieve/ext-envelope.c | 2 +- src/lib-sieve/plugins/body/tst-body.c | 2 +- src/lib-sieve/plugins/imapflags/tst-hasflag.c | 2 +- .../plugins/variables/ext-variables-common.c | 109 +++++++++- .../plugins/variables/ext-variables-common.h | 3 + .../plugins/variables/ext-variables.c | 2 + src/lib-sieve/plugins/variables/tst-string.c | 2 +- .../plugins/variables/variables-match.sieve | 23 ++ src/lib-sieve/sieve-match-types.c | 198 ++++++++++++++++-- src/lib-sieve/sieve-match-types.h | 30 ++- src/lib-sieve/tst-address.c | 2 +- src/lib-sieve/tst-header.c | 2 +- 12 files changed, 349 insertions(+), 28 deletions(-) create mode 100644 src/lib-sieve/plugins/variables/variables-match.sieve diff --git a/src/lib-sieve/ext-envelope.c b/src/lib-sieve/ext-envelope.c index 818f26037..1e5bdf2ae 100644 --- a/src/lib-sieve/ext-envelope.c +++ b/src/lib-sieve/ext-envelope.c @@ -241,7 +241,7 @@ static bool ext_envelope_operation_execute } /* Initialize match context */ - mctx = sieve_match_begin(mtch, cmp, key_list); + mctx = sieve_match_begin(renv->interp, mtch, cmp, key_list); /* Iterate through all requested headers to match */ hdr_item = NULL; diff --git a/src/lib-sieve/plugins/body/tst-body.c b/src/lib-sieve/plugins/body/tst-body.c index b6f3409b7..03fa540d1 100644 --- a/src/lib-sieve/plugins/body/tst-body.c +++ b/src/lib-sieve/plugins/body/tst-body.c @@ -364,7 +364,7 @@ static bool ext_body_operation_execute return FALSE; } - mctx = sieve_match_begin(mtch, cmp, key_list); + mctx = sieve_match_begin(renv->interp, mtch, cmp, key_list); /* Iterate through all requested body parts to match */ matched = FALSE; diff --git a/src/lib-sieve/plugins/imapflags/tst-hasflag.c b/src/lib-sieve/plugins/imapflags/tst-hasflag.c index 2df3d6b3b..152e9ae74 100644 --- a/src/lib-sieve/plugins/imapflags/tst-hasflag.c +++ b/src/lib-sieve/plugins/imapflags/tst-hasflag.c @@ -246,7 +246,7 @@ static bool tst_hasflag_operation_execute } matched = FALSE; - mctx = sieve_match_begin(mtch, cmp, key_list); + mctx = sieve_match_begin(renv->interp, mtch, cmp, key_list); ext_imapflags_get_flags_init(&iter, renv); diff --git a/src/lib-sieve/plugins/variables/ext-variables-common.c b/src/lib-sieve/plugins/variables/ext-variables-common.c index 39704ccd0..99da7ad76 100644 --- a/src/lib-sieve/plugins/variables/ext-variables-common.c +++ b/src/lib-sieve/plugins/variables/ext-variables-common.c @@ -8,6 +8,7 @@ #include "sieve-ast.h" #include "sieve-binary.h" #include "sieve-code.h" +#include "sieve-match-types.h" #include "sieve-commands.h" #include "sieve-validator.h" @@ -230,6 +231,9 @@ void ext_variables_interpreter_initialize(struct sieve_interpreter *interp) /* Create our context */ ctx = ext_variables_interpreter_context_create(interp); + + /* Enable support for match values */ + (void) sieve_match_values_set_enabled(interp, TRUE); } static inline struct ext_variables_interpreter_context * @@ -347,6 +351,41 @@ static bool arg_variable_generate return TRUE; } +/* Match value argument */ + +static bool arg_match_value_generate +(struct sieve_generator *generator, struct sieve_ast_argument *arg, + struct sieve_command_context *context ATTR_UNUSED); + +const struct sieve_argument match_value_argument = + { "@match_value", NULL, NULL, NULL, arg_match_value_generate }; + +static struct sieve_ast_argument *ext_variables_match_value_argument_create +(struct sieve_validator *validator ATTR_UNUSED, struct sieve_ast *ast, + unsigned int source_line, unsigned int index) +{ + struct sieve_ast_argument *arg; + + arg = sieve_ast_argument_create(ast, source_line); + arg->type = SAAT_STRING; + arg->argument = &match_value_argument; + arg->context = (void *) index; + + return arg; +} + +static bool arg_match_value_generate +(struct sieve_generator *generator, struct sieve_ast_argument *arg, + struct sieve_command_context *context ATTR_UNUSED) +{ + unsigned int index = (unsigned int) arg->context; + + ext_variables_opr_match_value_emit + (sieve_generator_get_binary(generator), index); + + return TRUE; +} + /* Variable string argument */ static bool arg_variable_string_validate @@ -464,6 +503,7 @@ static bool arg_variable_string_validate array_idx(&substitution, 0); if ( cur_element->num_variable == -1 ) { + /* Add variable argument '${identifier}' */ string_t *cur_ident = cur_element->identifier; strarg = ext_variables_variable_argument_create @@ -471,7 +511,12 @@ static bool arg_variable_string_validate if ( strarg != NULL ) sieve_ast_arg_list_add(arglist, strarg); } else { - /* FIXME: Match substitutions are not supported */ + /* Add match value argument '${000}' */ + strarg = ext_variables_match_value_argument_create + (validator, (*arg)->ast, (*arg)->source_line, + cur_element->num_variable); + if ( strarg != NULL ) + sieve_ast_arg_list_add(arglist, strarg); } } else { int i; @@ -654,6 +699,68 @@ bool ext_variables_opr_variable_read return FALSE; } +/* Match value operand */ + +static bool opr_match_value_read + (const struct sieve_runtime_env *renv, sieve_size_t *address, string_t **str); +static bool opr_match_value_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); + +const struct sieve_opr_string_interface match_value_interface = { + opr_match_value_dump, + opr_match_value_read +}; + +const struct sieve_operand match_value_operand = { + "match-value", + &variables_extension, + EXT_VARIABLES_OPERAND_MATCH_VALUE, + &string_class, + &match_value_interface +}; + +void ext_variables_opr_match_value_emit + (struct sieve_binary *sbin, unsigned int index) +{ + (void) sieve_operand_emit_code + (sbin, &match_value_operand, ext_variables_my_id); + (void) sieve_binary_emit_integer(sbin, index); +} + +static bool opr_match_value_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + sieve_size_t index = 0; + + if (sieve_binary_read_integer(denv->sbin, address, &index) ) { + sieve_code_dumpf(denv, "MVALUE: %ld", (long) index); + + return TRUE; + } + + return FALSE; +} + +static bool opr_match_value_read + (const struct sieve_runtime_env *renv, sieve_size_t *address, string_t **str) +{ + sieve_size_t index = 0; + + if (sieve_binary_read_integer(renv->sbin, address, &index) ) { + /* Parameter str can be NULL if we are requested to only skip and not + * actually read the argument. + */ + if ( str != NULL ) { + sieve_match_values_get(renv->interp, (unsigned int) index, str); + + if ( *str == NULL ) *str = t_str_new(0); + } + return TRUE; + } + + return FALSE; +} + /* Variable string operand */ static bool opr_variable_string_read diff --git a/src/lib-sieve/plugins/variables/ext-variables-common.h b/src/lib-sieve/plugins/variables/ext-variables-common.h index c00a96104..83eb4c99f 100644 --- a/src/lib-sieve/plugins/variables/ext-variables-common.h +++ b/src/lib-sieve/plugins/variables/ext-variables-common.h @@ -16,6 +16,7 @@ enum ext_variables_opcode { enum ext_variables_operand { EXT_VARIABLES_OPERAND_VARIABLE, + EXT_VARIABLES_OPERAND_MATCH_VALUE, EXT_VARIABLES_OPERAND_VARIABLE_STRING }; @@ -57,6 +58,8 @@ extern const struct sieve_argument variable_string_argument; void ext_variables_opr_variable_emit (struct sieve_binary *sbin, struct sieve_variable *var); +void ext_variables_opr_match_value_emit + (struct sieve_binary *sbin, unsigned int index); bool ext_variables_opr_variable_read (const struct sieve_runtime_env *renv, sieve_size_t *address, struct sieve_variable_storage **storage, unsigned int *var_index); diff --git a/src/lib-sieve/plugins/variables/ext-variables.c b/src/lib-sieve/plugins/variables/ext-variables.c index 82884b4ad..6012e368c 100644 --- a/src/lib-sieve/plugins/variables/ext-variables.c +++ b/src/lib-sieve/plugins/variables/ext-variables.c @@ -53,10 +53,12 @@ const struct sieve_operation *ext_variables_operations[] = { /* Operands */ extern const struct sieve_operand variable_operand; +extern const struct sieve_operand match_value_operand; extern const struct sieve_operand variable_string_operand; const struct sieve_operand *ext_variables_operands[] = { &variable_operand, + &match_value_operand, &variable_string_operand }; diff --git a/src/lib-sieve/plugins/variables/tst-string.c b/src/lib-sieve/plugins/variables/tst-string.c index 425cab6b3..44f0020af 100644 --- a/src/lib-sieve/plugins/variables/tst-string.c +++ b/src/lib-sieve/plugins/variables/tst-string.c @@ -208,7 +208,7 @@ static bool tst_string_operation_execute return FALSE; } - mctx = sieve_match_begin(mtch, cmp, key_list); + mctx = sieve_match_begin(renv->interp, mtch, cmp, key_list); /* Iterate through all requested strings to match */ src_item = NULL; diff --git a/src/lib-sieve/plugins/variables/variables-match.sieve b/src/lib-sieve/plugins/variables/variables-match.sieve new file mode 100644 index 000000000..9920fa64f --- /dev/null +++ b/src/lib-sieve/plugins/variables/variables-match.sieve @@ -0,0 +1,23 @@ +require "variables"; +require "fileinto"; + +set "match1" "Test of general stupidity"; + +# Test 1 +if string :matches "${match1}" "Test of *" { + fileinto "TEST 1: ${1}"; +} + +# Test 2 +if string :matches "${match1}" "of *" { + fileinto "FAILED 2: ${1}"; +} else { + fileinto "TEST 2 NOT MATCHED: ${match1}"; +} + +set "match2" "toptoptop"; + +# Test 3 +if string :matches "${match2}" "*top" { + fileinto "TEST 3: ${1}"; +} diff --git a/src/lib-sieve/sieve-match-types.c b/src/lib-sieve/sieve-match-types.c index 71f8ad188..4d23ded4f 100644 --- a/src/lib-sieve/sieve-match-types.c +++ b/src/lib-sieve/sieve-match-types.c @@ -189,6 +189,143 @@ static bool mtch_binary_load(struct sieve_binary *sbin) return TRUE; } +/* + * Interpreter context + */ + +struct mtch_interpreter_context { + struct sieve_match_values *match_values; + bool match_values_enabled; +}; + +static inline struct mtch_interpreter_context * +get_interpreter_context(struct sieve_interpreter *interp) +{ + return (struct mtch_interpreter_context *) + sieve_interpreter_extension_get_context(interp, ext_my_id); +} + +static struct mtch_interpreter_context * +mtch_interpreter_context_init(struct sieve_interpreter *interp) +{ + pool_t pool = sieve_interpreter_pool(interp); + struct mtch_interpreter_context *ctx; + + ctx = p_new(pool, struct mtch_interpreter_context, 1); + + sieve_interpreter_extension_set_context + (interp, ext_my_id, (void *) ctx); + + return ctx; +} + +/* + * Match values + */ + +struct sieve_match_values { + pool_t pool; + ARRAY_DEFINE(values, string_t *); + unsigned count; +}; + +bool sieve_match_values_set_enabled +(struct sieve_interpreter *interp, bool enable) +{ + bool previous; + struct mtch_interpreter_context *ctx = get_interpreter_context(interp); + + if ( ctx == NULL && enable ) + ctx = mtch_interpreter_context_init(interp); + + previous = ctx->match_values_enabled; + ctx->match_values_enabled = enable; + + return previous; +} + +struct sieve_match_values *sieve_match_values_start(struct sieve_interpreter *interp) +{ + struct mtch_interpreter_context *ctx = get_interpreter_context(interp); + + if ( ctx == NULL || !ctx->match_values_enabled ) + return NULL; + + if ( ctx->match_values == NULL ) { + pool_t pool = sieve_interpreter_pool(interp); + + ctx->match_values = p_new(pool, struct sieve_match_values, 1); + ctx->match_values->pool = pool; + p_array_init(&ctx->match_values->values, pool, 4); + } + + ctx->match_values->count = 0; + + return ctx->match_values; +} + +void sieve_match_values_add + (struct sieve_match_values *mvalues, string_t *value) +{ + string_t *entry; + + if ( mvalues == NULL ) return; + + if ( mvalues->count >= array_count(&mvalues->values) ) { + entry = str_new(mvalues->pool, 64); + array_append(&mvalues->values, &entry, 1); + } else { + string_t * const *ep = array_idx(&mvalues->values, mvalues->count); + entry = *ep; + str_truncate(entry, 0); + } + + if ( value != NULL ) + str_append_str(entry, value); + + mvalues->count++; +} + +void sieve_match_values_set_first + (struct sieve_match_values *mvalues, string_t *value) +{ + string_t * const *entry; + + if ( mvalues == NULL ) return; + + if ( mvalues->count == 0 ) { + sieve_match_values_add(mvalues, value); + + return; + } + + entry = array_idx(&mvalues->values, mvalues->count); + str_truncate(*entry, 0); +} + +void sieve_match_values_get + (struct sieve_interpreter *interp, unsigned int index, string_t **value_r) +{ + struct mtch_interpreter_context *ctx = get_interpreter_context(interp); + struct sieve_match_values *mvalues; + + if ( ctx == NULL || ctx->match_values == NULL ) { + *value_r = NULL; + return; + } + + mvalues = ctx->match_values; + if ( index <= mvalues->count ) { + string_t * const *entry = array_idx(&mvalues->values, index); + + *value_r = *entry; + return; + } + + *value_r = NULL; +} + + /* * Match-type operand */ @@ -385,11 +522,12 @@ static bool tag_match_type_generate /* Match Utility */ struct sieve_match_context *sieve_match_begin -(const struct sieve_match_type *mtch, const struct sieve_comparator *cmp, - struct sieve_coded_stringlist *key_list) +(struct sieve_interpreter *interp, const struct sieve_match_type *mtch, + const struct sieve_comparator *cmp, struct sieve_coded_stringlist *key_list) { struct sieve_match_context *mctx = t_new(struct sieve_match_context, 1); + mctx->interp = interp; mctx->match_type = mtch; mctx->comparator = cmp; mctx->key_list = key_list; @@ -523,14 +661,14 @@ static bool mtch_contains_match /* :matches */ /* Quick 'n dirty debug */ -//#define MATCH_DEBUG +#define MATCH_DEBUG #ifdef MATCH_DEBUG #define debug_printf(...) printf (__VA_ARGS__) #else #define debug_printf(...) #endif -static bool _matches_section +static inline bool _matches_section (const struct sieve_comparator *cmp, const char **val, const char *vend, const char **key, const char *kend, bool must_end) @@ -585,12 +723,18 @@ static bool mtch_matches_match const char *key, size_t key_size, int key_index ATTR_UNUSED) { const struct sieve_comparator *cmp = mctx->comparator; + struct sieve_match_values *mvalues; + string_t *match = t_str_new(32); const char *vend = (const char *) val + val_size; const char *kend = (const char *) key + key_size; - const char *vp = val; - const char *kp = key; - const char *wp = key; + const char *vp = val; /* Value pointer */ + const char *kp = key; /* Key pointer */ + const char *wp = key; /* Wildcard (key) pointer */ + /* Reset match values list */ + mvalues = sieve_match_values_start(mctx->interp); + sieve_match_values_add(mvalues, NULL); + /* Match the pattern as a two-level structure: * <pattern> = <section>*<section>*<section>.... * <section> = [text]?[text]?[text].... @@ -601,6 +745,7 @@ static bool mtch_matches_match debug_printf("MATCH key: %s\n", key); debug_printf("MATCH val: %s\n", val); + /* Loop until either key or value ends */ while (kp < kend && vp < vend) { char wildcard; @@ -620,10 +765,21 @@ static bool mtch_matches_match wp++; } - wildcard = *wp; - debug_printf("MATCH found wildcard: %c %d\n", wildcard, (int) (wp-key)); - - /* Find this section */ + /* Record wildcard character or \0 */ + if ( wp < kend ) { + wildcard = *wp; + debug_printf("MATCH found wildcard: %c %d\n", wildcard, (int) (wp-key)); + } else + wildcard = '\0'; + + if ( kp > key+1 ) { + debug_printf("MATCH value (previous): %s %d\n", str_c(match), kp-key); + sieve_match_values_add(mvalues, match); + } + + str_truncate(match, 0); + + /* Find this section (starting at kp and ending at wp-1)*/ if ( wp > kp ) { while ( (vp < vend) && (kp < wp) ) { #ifdef MATCH_DEBUG @@ -637,8 +793,12 @@ static bool mtch_matches_match debug_printf("MATCH first section failed\n"); return FALSE; } + + /* Wildcard eats the character */ + str_append_c(match, *vp); /* Add it to the match value */ vp++; } else { + /* Section successfully matched */ debug_printf("MATCH matched section key: %s\n", t_strdup_until(skp, wp)); debug_printf("MATCH matched section val: %s\n", svp); } @@ -651,9 +811,9 @@ static bool mtch_matches_match return FALSE; } - + /* Break the loop if match was successful already */ - if (kp == kend && vp == vend) return TRUE; + if (kp == kend && vp == vend) break; /* Advance loop to next wildcard search */ wp++; @@ -662,15 +822,23 @@ static bool mtch_matches_match /* Check whether string ends in a wildcard * (avoid scnning the rest of the string) */ - if ( kp == kend && wildcard == '*' ) - return TRUE; + if ( kp == kend && wildcard == '*' ) { + str_append_n(match, vp, vend-vp); + vp = vend; + break; + } /* Current wp is escaped.. */ if ( wildcard == '\\' ) wp++; + + debug_printf("MATCH loop '*'\n"); } debug_printf("MATCH loop ended\n"); + + debug_printf("MATCH value (end loop): %s\n", str_c(match)); + sieve_match_values_add(mvalues, match); /* By definition, the match is only successful if both value and key pattern * are exhausted. diff --git a/src/lib-sieve/sieve-match-types.h b/src/lib-sieve/sieve-match-types.h index bf0265d87..92a5cc90f 100644 --- a/src/lib-sieve/sieve-match-types.h +++ b/src/lib-sieve/sieve-match-types.h @@ -2,6 +2,7 @@ #define __SIEVE_MATCH_TYPES_H #include "sieve-common.h" +#include "sieve-extensions.h" enum sieve_match_type_code { SIEVE_MATCH_TYPE_IS, @@ -11,11 +12,12 @@ enum sieve_match_type_code { }; struct sieve_match_context { - const struct sieve_match_type *match_type; - const struct sieve_comparator *comparator; - struct sieve_coded_stringlist *key_list; + struct sieve_interpreter *interp; + const struct sieve_match_type *match_type; + const struct sieve_comparator *comparator; + struct sieve_coded_stringlist *key_list; - void *data; + void *data; }; struct sieve_match_type; @@ -69,6 +71,21 @@ struct sieve_match_type_context { void *ctx_data; }; +/* Match values */ + +struct sieve_match_values; + +bool sieve_match_values_set_enabled + (struct sieve_interpreter *interp, bool enable); +struct sieve_match_values *sieve_match_values_start + (struct sieve_interpreter *interp); +void sieve_match_values_add + (struct sieve_match_values *mvalues, string_t *value); +void sieve_match_values_get + (struct sieve_interpreter *interp, unsigned int index, string_t **value_r); + +/* ... */ + void sieve_match_types_link_tags (struct sieve_validator *validator, struct sieve_command_registration *cmd_reg, int id_code); @@ -106,8 +123,9 @@ bool sieve_opr_match_type_dump /* Match Utility */ struct sieve_match_context *sieve_match_begin -(const struct sieve_match_type *mtch, const struct sieve_comparator *cmp, - struct sieve_coded_stringlist *key_list); +(struct sieve_interpreter *interp, + const struct sieve_match_type *mtch, const struct sieve_comparator *cmp, + struct sieve_coded_stringlist *key_list); bool sieve_match_value (struct sieve_match_context *mctx, const char *value, size_t val_size); bool sieve_match_end(struct sieve_match_context *mctx); diff --git a/src/lib-sieve/tst-address.c b/src/lib-sieve/tst-address.c index bc835bf29..cef90e8c3 100644 --- a/src/lib-sieve/tst-address.c +++ b/src/lib-sieve/tst-address.c @@ -166,7 +166,7 @@ static bool tst_address_operation_execute } /* Initialize match context */ - mctx = sieve_match_begin(mtch, cmp, key_list); + mctx = sieve_match_begin(renv->interp, mtch, cmp, key_list); /* Iterate through all requested headers to match */ hdr_item = NULL; diff --git a/src/lib-sieve/tst-header.c b/src/lib-sieve/tst-header.c index d3191f448..e2fd44644 100644 --- a/src/lib-sieve/tst-header.c +++ b/src/lib-sieve/tst-header.c @@ -205,7 +205,7 @@ static bool tst_header_operation_execute return FALSE; } - mctx = sieve_match_begin(mtch, cmp, key_list); + mctx = sieve_match_begin(renv->interp, mtch, cmp, key_list); /* Iterate through all requested headers to match */ hdr_item = NULL; -- GitLab