From 2ffba54fe67b0fd4b46e94a49406b6414bdab6eb Mon Sep 17 00:00:00 2001 From: Stephan Bosch <stephan@rename-it.nl> Date: Wed, 27 Feb 2008 23:55:24 +0100 Subject: [PATCH] Variables: added parsing support for namespaces. --- src/lib-sieve/ext-envelope.c | 6 +- .../plugins/variables/ext-variables-common.c | 163 +++++++++++++++--- 2 files changed, 145 insertions(+), 24 deletions(-) diff --git a/src/lib-sieve/ext-envelope.c b/src/lib-sieve/ext-envelope.c index a81ccb7a1..818f26037 100644 --- a/src/lib-sieve/ext-envelope.c +++ b/src/lib-sieve/ext-envelope.c @@ -93,7 +93,8 @@ const struct sieve_operation envelope_operation = { }; /* Command Registration */ -static bool tst_envelope_registered(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg) +static bool tst_envelope_registered +(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg) { /* The order of these is not significant */ sieve_comparators_link_tag(validator, cmd_reg, SIEVE_AM_OPT_COMPARATOR); @@ -107,7 +108,8 @@ static bool tst_envelope_registered(struct sieve_validator *validator, struct si * Validation */ -static bool tst_envelope_validate(struct sieve_validator *validator, struct sieve_command_context *tst) +static bool tst_envelope_validate +(struct sieve_validator *validator, struct sieve_command_context *tst) { struct sieve_ast_argument *arg = tst->first_positional; diff --git a/src/lib-sieve/plugins/variables/ext-variables-common.c b/src/lib-sieve/plugins/variables/ext-variables-common.c index b2e84b628..5a10582d1 100644 --- a/src/lib-sieve/plugins/variables/ext-variables-common.c +++ b/src/lib-sieve/plugins/variables/ext-variables-common.c @@ -338,29 +338,62 @@ inline static struct sieve_ast_argument *_add_string_element return strarg; } +/* Variable Substitution + * --------------------- + * + * The variable strings are preprocessed into an AST list consisting of variable + * substitutions and constant parts of the string. The variables to which + * the substitutions link are looked up and their index in their scope storage + * is what is added to the list and eventually emitted as byte code. So in byte + * code a variable string will look as a series of substrings interrupted by + * integer operands that refer to variables. During execution the strings and + * the looked-up variables are concatenated to obtain the desired result. The + * the variable references are simple indexes into an array of variables, so + * looking these up during execution is a trivial process. + * + * However (RFC 5229): + * Tests or actions in future extensions may need to access the + * unexpanded version of the string argument and, e.g., do the expansion + * after setting variables in its namespace. The design of the + * implementation should allow this. + * + * Various options exist to provide this feature. If the extension is entirely + * namespace-based there is actually not very much of a problem. The variable + * list can easily be extended with new argument-types that refer to a variable + * identifier in stead of an index in the variable's storage. + */ static bool arg_variable_string_validate (struct sieve_validator *validator, struct sieve_ast_argument **arg, struct sieve_command_context *cmd) { + struct _variable_name { + string_t *identifier; + int num_variable; + }; + enum { ST_NONE, ST_OPEN, ST_VARIABLE, ST_CLOSE } state = ST_NONE; pool_t pool = sieve_ast_pool((*arg)->ast); struct sieve_ast_arg_list *arglist = NULL; string_t *str = sieve_ast_argument_str(*arg); - string_t *ident; const char *p, *mark, *strstart, *substart = NULL; const char *strval = (const char *) str_data(str); const char *strend = strval + str_len(str); struct _variable_string_data *strdata; bool result = TRUE; + + ARRAY_DEFINE(substitution, struct _variable_name); + unsigned int nspace_used = 0; + + t_push(); + + /* Initialize substitution structure */ + t_array_init(&substitution, 2); - //t_push(); - - ident = t_str_new(32); - p = strval; strstart = p; while ( result && p < strend ) { switch ( state ) { + /* Nothing found yet */ case ST_NONE: if ( *p == '$' ) { substart = p; @@ -368,6 +401,7 @@ static bool arg_variable_string_validate } p++; break; + /* Got '$' */ case ST_OPEN: if ( *p == '{' ) { state = ST_VARIABLE; @@ -375,31 +409,74 @@ static bool arg_variable_string_validate } else state = ST_NONE; break; + /* Got '${' */ case ST_VARIABLE: mark = p; - if ( p < strend ) { - if (*p == '_' || isalpha(*p) ) { - str_append_c(ident, *p); + /* Reset */ + nspace_used = 0; + + for (;;) { + struct _variable_name *cur_element; + string_t *cur_ident; + + /* Acquire current position in the substitution structure or allocate + * a new one if this substitution consists of more elements than before. + */ + if ( nspace_used < array_count(&substitution) ) { + cur_element = array_idx_modifiable + (&substitution, (unsigned int) nspace_used); + cur_ident = cur_element->identifier; + } else { + cur_element = array_append_space(&substitution); + cur_ident = cur_element->identifier = t_str_new(32); + } + + /* Identifier */ + if ( *p == '_' || isalpha(*p) ) { + cur_element->num_variable = -1; + str_truncate(cur_ident, 0); + str_append_c(cur_ident, *p); p++; while ( p < strend && (*p == '_' || isalnum(*p)) ) { - str_append_c(ident, *p); + str_append_c(cur_ident, *p); p++; } + state = ST_CLOSE; + + /* Num-variable */ } else if ( isdigit(*p) ) { - unsigned int num_variable = *p - '0'; + cur_element->num_variable = *p - '0'; p++; while ( p < strend && isdigit(*p) ) { - num_variable = num_variable*10 + (*p - '0'); + cur_element->num_variable = cur_element->num_variable*10 + (*p - '0'); p++; } + state = ST_CLOSE; - } else + + /* If a num-variable is first, no more elements can follow because no + * namespace is specified. + */ + if ( nspace_used == 0 ) { + nspace_used = 1; + break; + } + } else { state = ST_NONE; - } + break; + } + + nspace_used++; + + if ( p < strend && *p == '.' ) + p++; + else + break; + } break; case ST_CLOSE: @@ -412,52 +489,94 @@ static bool arg_variable_string_validate arglist = sieve_ast_arg_list_create(pool); } + /* Add the substring that is before the substitution to the + * variable-string AST. + * + * FIXME: For efficiency, if the variable is not found we should + * coalesce this substring with the one after the substitution. + */ if ( substart > strstart ) { strarg = _add_string_element(arglist, *arg); strarg->_value.str = str_new(pool, substart - strstart); str_append_n(strarg->_value.str, strstart, substart - strstart); + /* Give other substitution extensions a chance to do their work */ if ( !sieve_validator_argument_activate_super (validator, cmd, strarg, FALSE) ) return FALSE; } /* Find the variable */ - strarg = ext_variables_variable_argument_create - (validator, (*arg)->ast, (*arg)->source_line, str_c(ident)); - if ( strarg != NULL ) - sieve_ast_arg_list_add(arglist, strarg); - - str_truncate(ident, 0); + if ( nspace_used == 1 ) { + const struct _variable_name *cur_element = + array_idx(&substitution, 0); + + if ( cur_element->num_variable == -1 ) { + string_t *cur_ident = cur_element->identifier; + + strarg = ext_variables_variable_argument_create + (validator, (*arg)->ast, (*arg)->source_line, str_c(cur_ident)); + if ( strarg != NULL ) + sieve_ast_arg_list_add(arglist, strarg); + } else { + /* FIXME: Match substitutions are not supported */ + } + } else { + unsigned int i; + /* FIXME: Namespaces are not supported. */ + /* DEBUG: Just print the variable substitution: */ + + printf("NS_VARIABLE: "); + for ( i = 0; i < nspace_used; i++ ) { + const struct _variable_name *cur_element = + array_idx(&substitution, i); + + if ( cur_element->num_variable == -1 ) { + printf("%s.", str_c(cur_element->identifier)); + } else { + printf("%d.", cur_element->num_variable); + } + } + printf("\n"); + } strstart = p + 1; substart = strstart; } + + /* Finished, reset for the next substitution */ state = ST_NONE; p++; } } - //t_pop(); + t_pop(); if ( arglist == NULL ) { + /* No substitutions in this string, pass it on to any other substution + * extension. + */ return sieve_validator_argument_activate_super (validator, cmd, *arg, TRUE); } + /* Add the final substring that comes after the last substitution to the + * variable-string AST. + */ if ( strend > strstart ) { struct sieve_ast_argument *strarg = _add_string_element(arglist, *arg); strarg->_value.str = str_new(pool, strend - strstart); str_append_n(strarg->_value.str, strstart, strend - strstart); - + + /* Give other substitution extensions a chance to do their work */ if ( !sieve_validator_argument_activate_super (validator, cmd, strarg, FALSE) ) return FALSE; } + /* Assign the constructed variable-string AST-branch to the actual AST */ strdata = p_new(pool, struct _variable_string_data, 1); strdata->str_parts = arglist; - (*arg)->context = (void *) strdata; return TRUE; -- GitLab