summaryrefslogtreecommitdiffstats
path: root/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-arguments.c
diff options
context:
space:
mode:
Diffstat (limited to 'pigeonhole/src/lib-sieve/plugins/variables/ext-variables-arguments.c')
-rw-r--r--pigeonhole/src/lib-sieve/plugins/variables/ext-variables-arguments.c420
1 files changed, 420 insertions, 0 deletions
diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-arguments.c b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-arguments.c
new file mode 100644
index 0000000..2ac773c
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-arguments.c
@@ -0,0 +1,420 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str.h"
+#include "str-sanitize.h"
+#include "array.h"
+
+#include "sieve-common.h"
+#include "sieve-ast.h"
+#include "sieve-commands.h"
+#include "sieve-code.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-dump.h"
+
+#include "ext-variables-common.h"
+#include "ext-variables-limits.h"
+#include "ext-variables-name.h"
+#include "ext-variables-operands.h"
+#include "ext-variables-namespaces.h"
+#include "ext-variables-arguments.h"
+
+/*
+ * Variable argument implementation
+ */
+
+static bool arg_variable_generate
+ (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg,
+ struct sieve_command *context);
+
+const struct sieve_argument_def variable_argument = {
+ .identifier = "@variable",
+ .generate = arg_variable_generate
+};
+
+static bool ext_variables_variable_argument_activate
+(const struct sieve_extension *var_ext,
+ const struct sieve_extension *this_ext,
+ struct sieve_validator *valdtr, struct sieve_ast_argument *arg,
+ const char *variable)
+{
+ struct sieve_ast *ast = arg->ast;
+ struct sieve_variable *var;
+
+ var = ext_variables_validator_declare_variable(this_ext, valdtr, variable);
+
+ if ( var == NULL ) {
+ sieve_argument_validate_error(valdtr, arg,
+ "(implicit) declaration of new variable '%s' exceeds the limit "
+ "(max variables: %u)", variable,
+ sieve_variables_get_max_scope_size(var_ext));
+ return FALSE;
+ }
+
+ arg->argument = sieve_argument_create(ast, &variable_argument, this_ext, 0);
+ arg->argument->data = (void *) var;
+ return TRUE;
+}
+
+static struct sieve_ast_argument *ext_variables_variable_argument_create
+(const struct sieve_extension *this_ext, struct sieve_validator *valdtr,
+ struct sieve_ast_argument *parent_arg, const char *variable)
+{
+ struct sieve_ast *ast = parent_arg->ast;
+ struct sieve_ast_argument *new_arg;
+
+ new_arg = sieve_ast_argument_create(ast, sieve_ast_argument_line(parent_arg));
+ new_arg->type = SAAT_STRING;
+
+ if ( !ext_variables_variable_argument_activate
+ (this_ext, this_ext, valdtr, new_arg, variable) )
+ return NULL;
+
+ return new_arg;
+}
+
+static bool arg_variable_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg,
+ struct sieve_command *context ATTR_UNUSED)
+{
+ struct sieve_argument *argument = arg->argument;
+ struct sieve_variable *var = (struct sieve_variable *) argument->data;
+
+ sieve_variables_opr_variable_emit(cgenv->sblock, argument->ext, var);
+
+ return TRUE;
+}
+
+/*
+ * Match value argument implementation
+ */
+
+static bool arg_match_value_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg,
+ struct sieve_command *context ATTR_UNUSED);
+
+const struct sieve_argument_def match_value_argument = {
+ .identifier = "@match_value",
+ .generate = arg_match_value_generate
+};
+
+static bool ext_variables_match_value_argument_activate
+(const struct sieve_extension *this_ext,
+ struct sieve_validator *valdtr, struct sieve_ast_argument *arg,
+ unsigned int index, bool assignment)
+{
+ struct sieve_ast *ast = arg->ast;
+
+ if ( assignment ) {
+ sieve_argument_validate_error(valdtr, arg,
+ "cannot assign to match variable");
+ return FALSE;
+ }
+
+ if ( index > EXT_VARIABLES_MAX_MATCH_INDEX ) {
+ sieve_argument_validate_error(valdtr, arg,
+ "match value index %u out of range (max: %u)", index,
+ EXT_VARIABLES_MAX_MATCH_INDEX);
+ return FALSE;
+ }
+
+ arg->argument = sieve_argument_create
+ (ast, &match_value_argument, this_ext, 0);
+ arg->argument->data = (void *) POINTER_CAST(index);
+ return TRUE;
+}
+
+static struct sieve_ast_argument *ext_variables_match_value_argument_create
+(const struct sieve_extension *this_ext, struct sieve_validator *valdtr,
+ struct sieve_ast_argument *parent_arg, unsigned int index)
+{
+ struct sieve_ast *ast = parent_arg->ast;
+ struct sieve_ast_argument *new_arg;
+
+ new_arg = sieve_ast_argument_create(ast, sieve_ast_argument_line(parent_arg));
+ new_arg->type = SAAT_STRING;
+
+ if ( !ext_variables_match_value_argument_activate
+ (this_ext, valdtr, new_arg, index, FALSE) ) {
+ return NULL;
+ }
+
+ return new_arg;
+}
+
+static bool arg_match_value_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg,
+ struct sieve_command *context ATTR_UNUSED)
+{
+ struct sieve_argument *argument = arg->argument;
+ unsigned int index = POINTER_CAST_TO(argument->data, unsigned int);
+
+ sieve_variables_opr_match_value_emit(cgenv->sblock, argument->ext, index);
+
+ return TRUE;
+}
+
+/*
+ * Variable string argument implementation
+ */
+
+static bool arg_variable_string_validate
+ (struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+ struct sieve_command *cmd);
+
+const struct sieve_argument_def variable_string_argument = {
+ .identifier = "@variable-string",
+ .validate = arg_variable_string_validate,
+ .generate = sieve_arg_catenated_string_generate,
+};
+
+static bool arg_variable_string_validate
+(struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
+ struct sieve_command *cmd)
+{
+ const struct sieve_extension *this_ext = (*arg)->argument->ext;
+ enum { ST_NONE, ST_OPEN, ST_VARIABLE, ST_CLOSE } state = ST_NONE;
+ pool_t pool = sieve_ast_pool((*arg)->ast);
+ struct sieve_arg_catenated_string *catstr = NULL;
+ string_t *str = sieve_ast_argument_str(*arg);
+ const char *p, *strstart, *substart = NULL;
+ const char *strval = (const char *) str_data(str);
+ const char *strend = strval + str_len(str);
+ bool result = TRUE;
+ ARRAY_TYPE(sieve_variable_name) substitution;
+ int nelements = 0;
+
+ T_BEGIN {
+ /* Initialize substitution structure */
+ t_array_init(&substitution, 2);
+
+ p = strval;
+ strstart = p;
+ while ( result && p < strend ) {
+ switch ( state ) {
+
+ /* Nothing found yet */
+ case ST_NONE:
+ if ( *p == '$' ) {
+ substart = p;
+ state = ST_OPEN;
+ }
+ p++;
+ break;
+
+ /* Got '$' */
+ case ST_OPEN:
+ if ( *p == '{' ) {
+ state = ST_VARIABLE;
+ p++;
+ } else
+ state = ST_NONE;
+ break;
+
+ /* Got '${' */
+ case ST_VARIABLE:
+ nelements = ext_variable_name_parse(&substitution, &p, strend);
+
+ if ( nelements < 0 )
+ state = ST_NONE;
+ else
+ state = ST_CLOSE;
+
+ break;
+
+ /* Finished parsing name, expecting '}' */
+ case ST_CLOSE:
+ if ( *p == '}' ) {
+ struct sieve_ast_argument *strarg;
+
+ /* We now know that the substitution is valid */
+
+ if ( catstr == NULL ) {
+ catstr = sieve_arg_catenated_string_create(*arg);
+ }
+
+ /* 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 ) {
+ string_t *newstr = str_new(pool, substart - strstart);
+ str_append_data(newstr, strstart, substart - strstart);
+
+ strarg = sieve_ast_argument_string_create_raw
+ ((*arg)->ast, newstr, (*arg)->source_line);
+ sieve_arg_catenated_string_add_element(catstr, strarg);
+
+ /* Give other substitution extensions a chance to do their work */
+ if ( !sieve_validator_argument_activate_super
+ (valdtr, cmd, strarg, FALSE) ) {
+ result = FALSE;
+ break;
+ }
+ }
+
+ /* Find the variable */
+ if ( nelements == 1 ) {
+ const struct sieve_variable_name *cur_element =
+ array_idx(&substitution, 0);
+
+ if ( cur_element->num_variable == -1 ) {
+ /* Add variable argument '${identifier}' */
+
+ strarg = ext_variables_variable_argument_create
+ (this_ext, valdtr, *arg, str_c(cur_element->identifier));
+
+ } else {
+ /* Add match value argument '${000}' */
+
+ strarg = ext_variables_match_value_argument_create
+ (this_ext, valdtr, *arg, cur_element->num_variable);
+ }
+ } else {
+ strarg = ext_variables_namespace_argument_create
+ (this_ext, valdtr, *arg, cmd, &substitution);
+ }
+
+ if ( strarg != NULL )
+ sieve_arg_catenated_string_add_element(catstr, strarg);
+
+ strstart = p + 1;
+ substart = strstart;
+
+ p++;
+ }
+
+ /* Finished, reset for the next substitution */
+ state = ST_NONE;
+ }
+ }
+ } T_END;
+
+ /* Bail out early if substitution is invalid */
+ if ( !result ) return FALSE;
+
+ /* Check whether any substitutions were found */
+ if ( catstr == NULL ) {
+ /* No substitutions in this string, pass it on to any other substution
+ * extension.
+ */
+ return sieve_validator_argument_activate_super(valdtr, 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;
+ string_t *newstr = str_new(pool, strend - strstart);
+ str_append_data(newstr, strstart, strend - strstart);
+
+ strarg = sieve_ast_argument_string_create_raw
+ ((*arg)->ast, newstr, (*arg)->source_line);
+ sieve_arg_catenated_string_add_element(catstr, strarg);
+
+ /* Give other substitution extensions a chance to do their work */
+ if ( !sieve_validator_argument_activate_super
+ (valdtr, cmd, strarg, FALSE) )
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Variable argument interface
+ */
+
+static bool _sieve_variable_argument_activate
+(const struct sieve_extension *var_ext,
+ const struct sieve_extension *this_ext,
+ struct sieve_validator *valdtr, struct sieve_command *cmd,
+ struct sieve_ast_argument *arg, bool assignment)
+{
+ bool result = FALSE;
+ string_t *variable;
+ const char *varstr, *varend;
+ ARRAY_TYPE(sieve_variable_name) vname;
+ int nelements = 0;
+
+ T_BEGIN {
+ t_array_init(&vname, 2);
+
+ variable = sieve_ast_argument_str(arg);
+ varstr = str_c(variable);
+ varend = PTR_OFFSET(varstr, str_len(variable));
+ nelements = ext_variable_name_parse(&vname, &varstr, varend);
+
+ /* Check whether name parsing succeeded */
+ if ( nelements <= 0 || varstr != varend ) {
+ /* Parse failed */
+ sieve_argument_validate_error(valdtr, arg,
+ "invalid variable name '%s'", str_sanitize(str_c(variable),80));
+ } else if ( nelements == 1 ) {
+ /* Normal (match) variable */
+
+ const struct sieve_variable_name *cur_element =
+ array_idx(&vname, 0);
+
+ if ( cur_element->num_variable < 0 ) {
+ /* Variable */
+ result = ext_variables_variable_argument_activate(var_ext,
+ this_ext, valdtr, arg, str_c(cur_element->identifier));
+
+ } else {
+ /* Match value */
+ result = ext_variables_match_value_argument_activate
+ (this_ext, valdtr, arg, cur_element->num_variable, assignment);
+ }
+
+ } else {
+ /* Namespace variable */
+ result = ext_variables_namespace_argument_activate
+ (this_ext, valdtr, arg, cmd, &vname, assignment);
+ }
+ } T_END;
+
+ return result;
+}
+
+bool sieve_variable_argument_activate
+(const struct sieve_extension *var_ext,
+ const struct sieve_extension *this_ext,
+ struct sieve_validator *valdtr, struct sieve_command *cmd,
+ struct sieve_ast_argument *arg, bool assignment)
+{
+ if ( sieve_ast_argument_type(arg) == SAAT_STRING ) {
+ /* Single string */
+ return _sieve_variable_argument_activate(var_ext,
+ this_ext, valdtr, cmd, arg, assignment);
+
+ } else if ( sieve_ast_argument_type(arg) == SAAT_STRING_LIST ) {
+ /* String list */
+ struct sieve_ast_argument *stritem;
+
+ i_assert ( !assignment );
+
+ stritem = sieve_ast_strlist_first(arg);
+ while ( stritem != NULL ) {
+ if ( !_sieve_variable_argument_activate(var_ext,
+ this_ext, valdtr, cmd, stritem, assignment) )
+ return FALSE;
+
+ stritem = sieve_ast_strlist_next(stritem);
+ }
+
+ arg->argument = sieve_argument_create
+ (arg->ast, &string_list_argument, NULL, 0);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+