summaryrefslogtreecommitdiffstats
path: root/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-common.c
diff options
context:
space:
mode:
Diffstat (limited to 'pigeonhole/src/lib-sieve/plugins/variables/ext-variables-common.c')
-rw-r--r--pigeonhole/src/lib-sieve/plugins/variables/ext-variables-common.c950
1 files changed, 950 insertions, 0 deletions
diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-common.c b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-common.c
new file mode 100644
index 0000000..be9f677
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-common.c
@@ -0,0 +1,950 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "hash.h"
+#include "str.h"
+#include "array.h"
+
+#include "sieve-common.h"
+#include "sieve-settings.h"
+
+#include "sieve-ast.h"
+#include "sieve-binary.h"
+#include "sieve-code.h"
+#include "sieve-objects.h"
+#include "sieve-match-types.h"
+
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-dump.h"
+#include "sieve-interpreter.h"
+
+#include "ext-variables-common.h"
+#include "ext-variables-limits.h"
+#include "ext-variables-name.h"
+#include "ext-variables-modifiers.h"
+
+/*
+ * Limits
+ */
+
+unsigned int
+sieve_variables_get_max_scope_size(const struct sieve_extension *var_ext)
+{
+ const struct ext_variables_config *config =
+ ext_variables_get_config(var_ext);
+
+ return config->max_scope_size;
+}
+
+size_t
+sieve_variables_get_max_variable_size(const struct sieve_extension *var_ext)
+{
+ const struct ext_variables_config *config =
+ ext_variables_get_config(var_ext);
+
+ return config->max_variable_size;
+}
+
+/*
+ * Extension configuration
+ */
+
+bool
+ext_variables_load(const struct sieve_extension *ext, void **context)
+{
+ struct sieve_instance *svinst = ext->svinst;
+ struct ext_variables_config *config;
+ unsigned long long int uint_setting;
+ size_t size_setting;
+
+ if (*context != NULL)
+ ext_variables_unload(ext);
+
+ config = i_new(struct ext_variables_config, 1);
+
+ /* Get limits */
+ config->max_scope_size = EXT_VARIABLES_DEFAULT_MAX_SCOPE_SIZE;
+ config->max_variable_size = EXT_VARIABLES_DEFAULT_MAX_VARIABLE_SIZE;
+
+ if (sieve_setting_get_uint_value(
+ svinst, "sieve_variables_max_scope_size", &uint_setting)) {
+ if (uint_setting < EXT_VARIABLES_REQUIRED_MAX_SCOPE_SIZE) {
+ e_warning(svinst->event, "variables: "
+ "setting sieve_variables_max_scope_size "
+ "is lower than required by standards "
+ "(>= %llu items)",
+ (unsigned long long)EXT_VARIABLES_REQUIRED_MAX_SCOPE_SIZE);
+ } else {
+ config->max_scope_size = (unsigned int)uint_setting;
+ }
+ }
+
+ if (sieve_setting_get_size_value(
+ svinst, "sieve_variables_max_variable_size", &size_setting)) {
+ if (size_setting < EXT_VARIABLES_REQUIRED_MAX_VARIABLE_SIZE) {
+ e_warning(svinst->event, "variables: "
+ "setting sieve_variables_max_variable_size "
+ "is lower than required by standards "
+ "(>= %zu bytes)",
+ (size_t)EXT_VARIABLES_REQUIRED_MAX_VARIABLE_SIZE);
+ } else {
+ config->max_variable_size = size_setting;
+ }
+ }
+
+ *context = (void *)config;
+ return TRUE;
+}
+
+void ext_variables_unload(const struct sieve_extension *ext)
+{
+ struct ext_variables_config *config =
+ (struct ext_variables_config *)ext->context;
+
+ i_free(config);
+}
+
+const struct ext_variables_config *
+ext_variables_get_config(const struct sieve_extension *var_ext)
+{
+ const struct ext_variables_config *config =
+ (const struct ext_variables_config *)var_ext->context;
+
+ i_assert(var_ext->def == &variables_extension);
+ return config;
+}
+
+/*
+ * Variable scope
+ */
+
+struct sieve_variable_scope {
+ pool_t pool;
+ int refcount;
+
+ struct sieve_instance *svinst;
+ const struct sieve_extension *var_ext;
+ const struct sieve_extension *ext;
+
+ struct sieve_variable *error_var;
+
+ HASH_TABLE(const char *, struct sieve_variable *) variables;
+ ARRAY(struct sieve_variable *) variable_index;
+};
+
+struct sieve_variable_scope_binary {
+ struct sieve_variable_scope *scope;
+
+ unsigned int size;
+ struct sieve_binary_block *sblock;
+ sieve_size_t address;
+};
+
+struct sieve_variable_scope_iter {
+ struct sieve_variable_scope *scope;
+ struct hash_iterate_context *hctx;
+};
+
+struct sieve_variable_scope *
+sieve_variable_scope_create(struct sieve_instance *svinst,
+ const struct sieve_extension *var_ext,
+ const struct sieve_extension *ext)
+{
+ struct sieve_variable_scope *scope;
+ pool_t pool;
+
+ i_assert(var_ext->def == &variables_extension);
+
+ pool = pool_alloconly_create("sieve_variable_scope", 4096);
+ scope = p_new(pool, struct sieve_variable_scope, 1);
+ scope->pool = pool;
+ scope->refcount = 1;
+
+ scope->svinst = svinst;
+ scope->var_ext = var_ext;
+ scope->ext = ext;
+
+ hash_table_create(&scope->variables, pool, 0, strcase_hash, strcasecmp);
+ p_array_init(&scope->variable_index, pool, 128);
+
+ return scope;
+}
+
+void sieve_variable_scope_ref(struct sieve_variable_scope *scope)
+{
+ scope->refcount++;
+}
+
+void sieve_variable_scope_unref(struct sieve_variable_scope **_scope)
+{
+ struct sieve_variable_scope *scope = *_scope;
+
+ i_assert(scope->refcount > 0);
+
+ if (--scope->refcount != 0)
+ return;
+
+ hash_table_destroy(&scope->variables);
+
+ *_scope = NULL;
+ pool_unref(&scope->pool);
+}
+
+pool_t sieve_variable_scope_pool(struct sieve_variable_scope *scope)
+{
+ return scope->pool;
+}
+
+struct sieve_variable *
+sieve_variable_scope_declare(struct sieve_variable_scope *scope,
+ const char *identifier)
+{
+ unsigned int max_scope_size;
+ struct sieve_variable *var;
+
+ var = hash_table_lookup(scope->variables, identifier);
+ if (var != NULL)
+ return var;
+
+ max_scope_size = sieve_variables_get_max_scope_size(scope->var_ext);
+ if (array_count(&scope->variable_index) >= max_scope_size) {
+ if (scope->error_var == NULL) {
+ var = p_new(scope->pool, struct sieve_variable, 1);
+ var->identifier = "@ERROR@";
+ var->index = 0;
+
+ scope->error_var = var;
+ return NULL;
+ }
+
+ return scope->error_var;
+ }
+
+ var = p_new(scope->pool, struct sieve_variable, 1);
+ var->ext = scope->ext;
+ var->identifier = p_strdup(scope->pool, identifier);
+ var->index = array_count(&scope->variable_index);
+
+ hash_table_insert(scope->variables, var->identifier, var);
+ array_append(&scope->variable_index, &var, 1);
+ return var;
+}
+
+struct sieve_variable *
+sieve_variable_scope_get_variable(struct sieve_variable_scope *scope,
+ const char *identifier)
+{
+ return hash_table_lookup(scope->variables, identifier);
+}
+
+struct sieve_variable *
+sieve_variable_scope_import(struct sieve_variable_scope *scope,
+ struct sieve_variable *var)
+{
+ struct sieve_variable *old_var, *new_var;
+
+ old_var = sieve_variable_scope_get_variable(scope, var->identifier);
+ if (old_var != NULL) {
+ i_assert(memcmp(old_var, var, sizeof(*var)) == 0);
+ return old_var;
+ }
+
+ new_var = p_new(scope->pool, struct sieve_variable, 1);
+ memcpy(new_var, var, sizeof(*new_var));
+
+ hash_table_insert(scope->variables, new_var->identifier, new_var);
+
+ /* Not entered into the index because it is an external variable
+ (This can be done unlimited; only limited by the size of the external
+ scope)
+ */
+ return new_var;
+}
+
+struct sieve_variable_scope_iter *
+sieve_variable_scope_iterate_init(struct sieve_variable_scope *scope)
+{
+ struct sieve_variable_scope_iter *iter;
+
+ iter = t_new(struct sieve_variable_scope_iter, 1);
+ iter->scope = scope;
+ iter->hctx = hash_table_iterate_init(scope->variables);
+
+ return iter;
+}
+
+bool sieve_variable_scope_iterate(struct sieve_variable_scope_iter *iter,
+ struct sieve_variable **var_r)
+{
+ const char *key;
+
+ return hash_table_iterate(iter->hctx, iter->scope->variables,
+ &key, var_r);
+}
+
+void sieve_variable_scope_iterate_deinit(
+ struct sieve_variable_scope_iter **iter)
+{
+ hash_table_iterate_deinit(&(*iter)->hctx);
+ *iter = NULL;
+}
+
+unsigned int
+sieve_variable_scope_declarations(struct sieve_variable_scope *scope)
+{
+ return hash_table_count(scope->variables);
+}
+
+unsigned int sieve_variable_scope_size(struct sieve_variable_scope *scope)
+{
+ return array_count(&scope->variable_index);
+}
+
+struct sieve_variable * const *
+sieve_variable_scope_get_variables(struct sieve_variable_scope *scope,
+ unsigned int *size_r)
+{
+ return array_get(&scope->variable_index, size_r);
+}
+
+struct sieve_variable *
+sieve_variable_scope_get_indexed(struct sieve_variable_scope *scope,
+ unsigned int index)
+{
+ struct sieve_variable * const *var;
+
+ if (index >= array_count(&scope->variable_index))
+ return NULL;
+
+ var = array_idx(&scope->variable_index, index);
+ return *var;
+}
+
+/* Scope binary */
+
+struct sieve_variable_scope *
+sieve_variable_scope_binary_dump(struct sieve_instance *svinst,
+ const struct sieve_extension *var_ext,
+ const struct sieve_extension *ext,
+ const struct sieve_dumptime_env *denv,
+ sieve_size_t *address)
+{
+ struct sieve_variable_scope *local_scope;
+ unsigned int i, scope_size;
+ sieve_size_t pc;
+ sieve_offset_t end_offset;
+
+ /* Read scope size */
+ sieve_code_mark(denv);
+ if (!sieve_binary_read_unsigned(denv->sblock, address, &scope_size))
+ return NULL;
+
+ /* Read offset */
+ pc = *address;
+ if (!sieve_binary_read_offset(denv->sblock, address, &end_offset))
+ return NULL;
+
+ /* Create scope */
+ local_scope = sieve_variable_scope_create(svinst, var_ext, ext);
+
+ /* Read and dump scope itself */
+
+ sieve_code_dumpf(denv, "VARIABLES SCOPE [%u] (end: %08x)",
+ scope_size, (unsigned int)(pc + end_offset));
+
+ for (i = 0; i < scope_size; i++) {
+ string_t *identifier;
+
+ sieve_code_mark(denv);
+ if (!sieve_binary_read_string(denv->sblock, address,
+ &identifier))
+ return NULL;
+
+ sieve_code_dumpf(denv, "%3d: '%s'", i, str_c(identifier));
+
+ (void)sieve_variable_scope_declare(local_scope,
+ str_c(identifier));
+ }
+
+ return local_scope;
+}
+
+struct sieve_variable_scope_binary *
+sieve_variable_scope_binary_create(struct sieve_variable_scope *scope)
+{
+ struct sieve_variable_scope_binary *scpbin;
+
+ scpbin = p_new(scope->pool, struct sieve_variable_scope_binary, 1);
+ scpbin->scope = scope;
+
+ return scpbin;
+}
+
+void sieve_variable_scope_binary_ref(struct sieve_variable_scope_binary *scpbin)
+{
+ sieve_variable_scope_ref(scpbin->scope);
+}
+
+void sieve_variable_scope_binary_unref(
+ struct sieve_variable_scope_binary **scpbin)
+{
+ sieve_variable_scope_unref(&(*scpbin)->scope);
+ *scpbin = NULL;
+}
+
+struct sieve_variable_scope_binary *
+sieve_variable_scope_binary_read(struct sieve_instance *svinst,
+ const struct sieve_extension *var_ext,
+ const struct sieve_extension *ext,
+ struct sieve_binary_block *sblock,
+ sieve_size_t *address)
+{
+ struct sieve_variable_scope *scope;
+ struct sieve_variable_scope_binary *scpbin;
+ unsigned int scope_size, max_scope_size;
+ const char *ext_name = (ext == NULL ? "variables" :
+ sieve_extension_name(ext));
+ sieve_size_t pc;
+ sieve_offset_t end_offset;
+
+ /* Read scope size */
+ if (!sieve_binary_read_unsigned(sblock, address, &scope_size)) {
+ e_error(svinst->event, "%s: "
+ "variable scope: failed to read size", ext_name);
+ return NULL;
+ }
+
+ /* Check size limit */
+ max_scope_size = sieve_variables_get_max_scope_size(var_ext);
+ if (scope_size > max_scope_size) {
+ e_error(svinst->event, "%s: "
+ "variable scope: size exceeds the limit (%u > %u)",
+ ext_name, scope_size, max_scope_size);
+ return NULL;
+ }
+
+ /* Read offset */
+ pc = *address;
+ if (!sieve_binary_read_offset(sblock, address, &end_offset)) {
+ e_error(svinst->event, "%s: "
+ "variable scope: failed to read end offset", ext_name);
+ return NULL;
+ }
+
+ /* Create scope */
+ scope = sieve_variable_scope_create(svinst, var_ext, ext);
+
+ scpbin = sieve_variable_scope_binary_create(scope);
+ scpbin->size = scope_size;
+ scpbin->sblock = sblock;
+ scpbin->address = *address;
+
+ *address = pc + end_offset;
+
+ return scpbin;
+}
+
+struct sieve_variable_scope *
+sieve_variable_scope_binary_get(struct sieve_variable_scope_binary *scpbin)
+{
+ const struct sieve_extension *ext = scpbin->scope->ext;
+ struct sieve_instance *svinst = scpbin->scope->svinst;
+ const char *ext_name = (ext == NULL ? "variables" :
+ sieve_extension_name(ext));
+ unsigned int i;
+
+ if (scpbin->sblock != NULL) {
+ sieve_size_t *address = &scpbin->address;
+
+ /* Read scope itself */
+ for (i = 0; i < scpbin->size; i++) {
+ struct sieve_variable *var;
+ string_t *identifier;
+
+ if (!sieve_binary_read_string(scpbin->sblock, address,
+ &identifier)) {
+ e_error(svinst->event, "%s: variable scope: "
+ "failed to read variable name",
+ ext_name);
+ return NULL;
+ }
+
+ var = sieve_variable_scope_declare(scpbin->scope,
+ str_c(identifier));
+
+ i_assert(var != NULL);
+ i_assert(var->index == i);
+ }
+
+ scpbin->sblock = NULL;
+ }
+
+ return scpbin->scope;
+}
+
+unsigned int
+sieve_variable_scope_binary_get_size(
+ struct sieve_variable_scope_binary *scpbin)
+{
+ if (scpbin->sblock != NULL)
+ return scpbin->size;
+
+ return array_count(&scpbin->scope->variable_index);
+}
+
+/*
+ * Variable storage
+ */
+
+struct sieve_variable_storage {
+ pool_t pool;
+ const struct sieve_extension *var_ext;
+ struct sieve_variable_scope *scope;
+ struct sieve_variable_scope_binary *scope_bin;
+ unsigned int max_size;
+ ARRAY(string_t *) var_values;
+};
+
+struct sieve_variable_storage *
+sieve_variable_storage_create(const struct sieve_extension *var_ext,
+ pool_t pool,
+ struct sieve_variable_scope_binary *scpbin)
+{
+ struct sieve_variable_storage *storage;
+
+ storage = p_new(pool, struct sieve_variable_storage, 1);
+ storage->pool = pool;
+ storage->var_ext = var_ext;
+ storage->scope_bin = scpbin;
+ storage->scope = NULL;
+
+ storage->max_size = sieve_variable_scope_binary_get_size(scpbin);
+
+ p_array_init(&storage->var_values, pool, 4);
+
+ return storage;
+}
+
+static inline bool
+sieve_variable_valid(struct sieve_variable_storage *storage,
+ unsigned int index)
+{
+ if (storage->scope_bin == NULL)
+ return TRUE;
+
+ return (index < storage->max_size);
+}
+
+bool sieve_variable_get_identifier(struct sieve_variable_storage *storage,
+ unsigned int index, const char **identifier)
+{
+ struct sieve_variable * const *var;
+
+ *identifier = NULL;
+
+ if (storage->scope_bin == NULL)
+ return TRUE;
+
+ if (storage->scope == NULL) {
+ storage->scope =
+ sieve_variable_scope_binary_get(storage->scope_bin);
+ if (storage->scope == NULL)
+ return FALSE;
+ }
+
+ /* FIXME: direct invasion of the scope object is a bit ugly */
+ if (index >= array_count(&storage->scope->variable_index))
+ return FALSE;
+
+ var = array_idx(&storage->scope->variable_index, index);
+ if (*var != NULL)
+ *identifier = (*var)->identifier;
+ return TRUE;
+}
+
+const char *
+sieve_variable_get_varid(struct sieve_variable_storage *storage,
+ unsigned int index)
+{
+ if (storage->scope_bin == NULL)
+ return t_strdup_printf("%ld", (long)index);
+
+ if (storage->scope == NULL) {
+ storage->scope =
+ sieve_variable_scope_binary_get(storage->scope_bin);
+ if (storage->scope == NULL)
+ return NULL;
+ }
+
+ return sieve_ext_variables_get_varid(storage->scope->ext, index);
+}
+
+bool sieve_variable_get(struct sieve_variable_storage *storage,
+ unsigned int index, string_t **value)
+{
+ *value = NULL;
+
+ if (index < array_count(&storage->var_values)) {
+ string_t * const *varent;
+
+ varent = array_idx(&storage->var_values, index);
+
+ *value = *varent;
+ } else if (!sieve_variable_valid(storage, index)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+bool sieve_variable_get_modifiable(struct sieve_variable_storage *storage,
+ unsigned int index, string_t **value)
+{
+ string_t *dummy;
+
+ if (value == NULL)
+ value = &dummy;
+
+ if (!sieve_variable_get(storage, index, value))
+ return FALSE;
+
+ if (*value == NULL) {
+ *value = str_new(storage->pool, 256);
+ array_idx_set(&storage->var_values, index, value);
+ }
+ return TRUE;
+}
+
+bool sieve_variable_assign(struct sieve_variable_storage *storage,
+ unsigned int index, const string_t *value)
+{
+ const struct ext_variables_config *config =
+ ext_variables_get_config(storage->var_ext);
+ string_t *varval;
+
+ if (!sieve_variable_get_modifiable(storage, index, &varval))
+ return FALSE;
+
+ str_truncate(varval, 0);
+ str_append_str(varval, value);
+
+ /* Just a precaution, caller should prevent this in the first place */
+ if (str_len(varval) > config->max_variable_size)
+ str_truncate_utf8(varval, config->max_variable_size);
+
+ return TRUE;
+}
+
+bool sieve_variable_assign_cstr(struct sieve_variable_storage *storage,
+ unsigned int index, const char *value)
+{
+ const struct ext_variables_config *config =
+ ext_variables_get_config(storage->var_ext);
+ string_t *varval;
+
+ if (!sieve_variable_get_modifiable(storage, index, &varval))
+ return FALSE;
+
+ str_truncate(varval, 0);
+ str_append(varval, value);
+
+ /* Just a precaution, caller should prevent this in the first place */
+ if (str_len(varval) > config->max_variable_size)
+ str_truncate_utf8(varval, config->max_variable_size);
+
+ return TRUE;
+}
+
+/*
+ * AST Context
+ */
+
+static void
+ext_variables_ast_free(const struct sieve_extension *ext ATTR_UNUSED,
+ struct sieve_ast *ast ATTR_UNUSED, void *context)
+{
+ struct sieve_variable_scope *local_scope =
+ (struct sieve_variable_scope *)context;
+
+ /* Unreference main variable scope */
+ sieve_variable_scope_unref(&local_scope);
+}
+
+static const struct sieve_ast_extension variables_ast_extension = {
+ &variables_extension,
+ ext_variables_ast_free
+};
+
+static struct sieve_variable_scope *
+ext_variables_create_local_scope(const struct sieve_extension *this_ext,
+ struct sieve_ast *ast)
+{
+ struct sieve_variable_scope *scope;
+
+ scope = sieve_variable_scope_create(this_ext->svinst, this_ext, NULL);
+
+ sieve_ast_extension_register(ast, this_ext, &variables_ast_extension,
+ (void *)scope);
+ return scope;
+}
+
+static struct sieve_variable_scope *
+ext_variables_ast_get_local_scope(const struct sieve_extension *this_ext,
+ struct sieve_ast *ast)
+{
+ struct sieve_variable_scope *local_scope =
+ (struct sieve_variable_scope *)
+ sieve_ast_extension_get_context(ast, this_ext);
+
+ return local_scope;
+}
+
+/*
+ * Validator context
+ */
+
+static struct ext_variables_validator_context *
+ext_variables_validator_context_create(const struct sieve_extension *this_ext,
+ struct sieve_validator *valdtr)
+{
+ pool_t pool = sieve_validator_pool(valdtr);
+ struct ext_variables_validator_context *ctx;
+ struct sieve_ast *ast = sieve_validator_ast(valdtr);
+
+ ctx = p_new(pool, struct ext_variables_validator_context, 1);
+ ctx->modifiers = sieve_validator_object_registry_create(valdtr);
+ ctx->namespaces = sieve_validator_object_registry_create(valdtr);
+ ctx->local_scope = ext_variables_create_local_scope(this_ext, ast);
+
+ sieve_validator_extension_set_context(valdtr, this_ext, (void *)ctx);
+ return ctx;
+}
+
+struct ext_variables_validator_context *
+ext_variables_validator_context_get(const struct sieve_extension *this_ext,
+ struct sieve_validator *valdtr)
+{
+ struct ext_variables_validator_context *ctx;
+
+ i_assert(sieve_extension_is(this_ext, variables_extension));
+ ctx = (struct ext_variables_validator_context *)
+ sieve_validator_extension_get_context(valdtr, this_ext);
+
+ if (ctx == NULL)
+ ctx = ext_variables_validator_context_create(this_ext, valdtr);
+ return ctx;
+}
+
+void ext_variables_validator_initialize(const struct sieve_extension *this_ext,
+ struct sieve_validator *valdtr)
+{
+ struct ext_variables_validator_context *ctx;
+
+ /* Create our context */
+ ctx = ext_variables_validator_context_get(this_ext, valdtr);
+
+ ext_variables_register_core_modifiers(this_ext, ctx);
+
+ ctx->active = TRUE;
+}
+
+struct sieve_variable *ext_variables_validator_get_variable(
+ const struct sieve_extension *this_ext,
+ struct sieve_validator *validator, const char *variable)
+{
+ struct ext_variables_validator_context *ctx =
+ ext_variables_validator_context_get(this_ext, validator);
+
+ return sieve_variable_scope_get_variable(ctx->local_scope, variable);
+}
+
+struct sieve_variable *
+ext_variables_validator_declare_variable(const struct sieve_extension *this_ext,
+ struct sieve_validator *validator,
+ const char *variable)
+{
+ struct ext_variables_validator_context *ctx =
+ ext_variables_validator_context_get(this_ext, validator);
+
+ return sieve_variable_scope_declare(ctx->local_scope, variable);
+}
+
+struct sieve_variable_scope *
+sieve_ext_variables_get_local_scope(const struct sieve_extension *var_ext,
+ struct sieve_validator *validator)
+{
+ struct ext_variables_validator_context *ctx =
+ ext_variables_validator_context_get(var_ext, validator);
+
+ return ctx->local_scope;
+}
+
+bool sieve_ext_variables_is_active(const struct sieve_extension *var_ext,
+ struct sieve_validator *valdtr)
+{
+ struct ext_variables_validator_context *ctx =
+ ext_variables_validator_context_get(var_ext, valdtr);
+
+ return (ctx != NULL && ctx->active);
+}
+
+/*
+ * Code generation
+ */
+
+bool ext_variables_generator_load(const struct sieve_extension *ext,
+ const struct sieve_codegen_env *cgenv)
+{
+ struct sieve_variable_scope *local_scope =
+ ext_variables_ast_get_local_scope(ext, cgenv->ast);
+ unsigned int count = sieve_variable_scope_size(local_scope);
+ sieve_size_t jump;
+
+ sieve_binary_emit_unsigned(cgenv->sblock, count);
+
+ jump = sieve_binary_emit_offset(cgenv->sblock, 0);
+
+ if (count > 0) {
+ unsigned int size, i;
+ struct sieve_variable *const *vars =
+ sieve_variable_scope_get_variables(local_scope, &size);
+
+ for (i = 0; i < size; i++) {
+ sieve_binary_emit_cstring(cgenv->sblock,
+ vars[i]->identifier);
+ }
+ }
+
+ sieve_binary_resolve_offset(cgenv->sblock, jump);
+ return TRUE;
+}
+
+/*
+ * Interpreter context
+ */
+
+struct ext_variables_interpreter_context {
+ pool_t pool;
+
+ struct sieve_variable_scope *local_scope;
+ struct sieve_variable_scope_binary *local_scope_bin;
+
+ struct sieve_variable_storage *local_storage;
+ ARRAY(struct sieve_variable_storage *) ext_storages;
+};
+
+static void
+ext_variables_interpreter_free(const struct sieve_extension *ext ATTR_UNUSED,
+ struct sieve_interpreter *interp ATTR_UNUSED,
+ void *context)
+{
+ struct ext_variables_interpreter_context *ctx =
+ (struct ext_variables_interpreter_context *)context;
+
+ sieve_variable_scope_binary_unref(&ctx->local_scope_bin);
+}
+
+static struct sieve_interpreter_extension
+variables_interpreter_extension = {
+ .ext_def = &variables_extension,
+ .free = ext_variables_interpreter_free
+};
+
+static struct ext_variables_interpreter_context *
+ext_variables_interpreter_context_create(
+ const struct sieve_extension *this_ext,
+ struct sieve_interpreter *interp,
+ struct sieve_variable_scope_binary *scpbin)
+{
+ pool_t pool = sieve_interpreter_pool(interp);
+ struct ext_variables_interpreter_context *ctx;
+
+ ctx = p_new(pool, struct ext_variables_interpreter_context, 1);
+ ctx->pool = pool;
+ ctx->local_scope = NULL;
+ ctx->local_scope_bin = scpbin;
+ ctx->local_storage =
+ sieve_variable_storage_create(this_ext, pool, scpbin);
+ p_array_init(&ctx->ext_storages, pool,
+ sieve_extensions_get_count(this_ext->svinst));
+
+ sieve_interpreter_extension_register(interp, this_ext,
+ &variables_interpreter_extension,
+ (void *)ctx);
+ return ctx;
+}
+
+bool ext_variables_interpreter_load(const struct sieve_extension *ext,
+ const struct sieve_runtime_env *renv,
+ sieve_size_t *address)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+ struct sieve_variable_scope_binary *scpbin;
+
+ scpbin = sieve_variable_scope_binary_read(eenv->svinst, ext, NULL,
+ renv->sblock, address);
+ if (scpbin == NULL)
+ return FALSE;
+
+ /* Create our context */
+ (void)ext_variables_interpreter_context_create(ext, renv->interp,
+ scpbin);
+
+ /* Enable support for match values */
+ (void)sieve_match_values_set_enabled(renv, TRUE);
+
+ return TRUE;
+}
+
+static inline struct ext_variables_interpreter_context *
+ext_variables_interpreter_context_get(const struct sieve_extension *this_ext,
+ struct sieve_interpreter *interp)
+{
+ struct ext_variables_interpreter_context *ctx;
+
+ i_assert(sieve_extension_is(this_ext, variables_extension));
+ ctx = (struct ext_variables_interpreter_context *)
+ sieve_interpreter_extension_get_context(interp, this_ext);
+ return ctx;
+}
+
+struct sieve_variable_storage *
+sieve_ext_variables_runtime_get_storage(const struct sieve_extension *var_ext,
+ const struct sieve_runtime_env *renv,
+ const struct sieve_extension *ext)
+{
+ struct ext_variables_interpreter_context *ctx =
+ ext_variables_interpreter_context_get(var_ext, renv->interp);
+ struct sieve_variable_storage * const *storage;
+
+ if (ext == NULL)
+ return ctx->local_storage;
+
+ if (ext->id >= (int)array_count(&ctx->ext_storages))
+ storage = NULL;
+ else
+ storage = array_idx(&ctx->ext_storages, ext->id);
+
+ if (storage == NULL)
+ return NULL;
+ return *storage;
+}
+
+void sieve_ext_variables_runtime_set_storage(
+ const struct sieve_extension *var_ext,
+ const struct sieve_runtime_env *renv, const struct sieve_extension *ext,
+ struct sieve_variable_storage *storage)
+{
+ struct ext_variables_interpreter_context *ctx =
+ ext_variables_interpreter_context_get(var_ext, renv->interp);
+
+ if (ctx == NULL || ext == NULL || storage == NULL)
+ return;
+ if (ext->id < 0)
+ return;
+
+ array_idx_set(&ctx->ext_storages, (unsigned int) ext->id, &storage);
+}