diff options
Diffstat (limited to 'pigeonhole/src/lib-sieve/plugins/include/ext-include-binary.c')
-rw-r--r-- | pigeonhole/src/lib-sieve/plugins/include/ext-include-binary.c | 492 |
1 files changed, 492 insertions, 0 deletions
diff --git a/pigeonhole/src/lib-sieve/plugins/include/ext-include-binary.c b/pigeonhole/src/lib-sieve/plugins/include/ext-include-binary.c new file mode 100644 index 0000000..3711c30 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/include/ext-include-binary.c @@ -0,0 +1,492 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" + +#include "sieve-common.h" +#include "sieve-error.h" +#include "sieve-script.h" +#include "sieve-storage.h" +#include "sieve-binary.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "sieve-ext-variables.h" + +#include "ext-include-common.h" +#include "ext-include-limits.h" +#include "ext-include-variables.h" +#include "ext-include-binary.h" + +/* + * Forward declarations + */ + +static bool ext_include_binary_pre_save + (const struct sieve_extension *ext, struct sieve_binary *sbin, + void *context, enum sieve_error *error_r); +static bool ext_include_binary_open + (const struct sieve_extension *ext, struct sieve_binary *sbin, + void *context); +static bool ext_include_binary_up_to_date + (const struct sieve_extension *ext, struct sieve_binary *sbin, + void *context, enum sieve_compile_flags cpflags); +static void ext_include_binary_free + (const struct sieve_extension *ext, struct sieve_binary *sbin, + void *context); + +/* + * Binary include extension + */ + +const struct sieve_binary_extension include_binary_ext = { + .extension = &include_extension, + .binary_pre_save = ext_include_binary_pre_save, + .binary_open = ext_include_binary_open, + .binary_free = ext_include_binary_free, + .binary_up_to_date = ext_include_binary_up_to_date +}; + +/* + * Binary context management + */ + +struct ext_include_binary_context { + struct sieve_binary *binary; + struct sieve_binary_block *dependency_block; + + HASH_TABLE(struct sieve_script *, + struct ext_include_script_info *) included_scripts; + ARRAY(struct ext_include_script_info *) include_index; + + struct sieve_variable_scope_binary *global_vars; + + bool outdated:1; +}; + +static struct ext_include_binary_context *ext_include_binary_create_context +(const struct sieve_extension *this_ext, struct sieve_binary *sbin) +{ + pool_t pool = sieve_binary_pool(sbin); + + struct ext_include_binary_context *ctx = + p_new(pool, struct ext_include_binary_context, 1); + + ctx->binary = sbin; + hash_table_create(&ctx->included_scripts, pool, 0, + sieve_script_hash, sieve_script_cmp); + p_array_init(&ctx->include_index, pool, 128); + + sieve_binary_extension_set(sbin, this_ext, &include_binary_ext, ctx); + + return ctx; +} + +struct ext_include_binary_context *ext_include_binary_get_context +(const struct sieve_extension *this_ext, struct sieve_binary *sbin) +{ + struct ext_include_binary_context *ctx = (struct ext_include_binary_context *) + sieve_binary_extension_get_context(sbin, this_ext); + + if ( ctx == NULL ) + ctx = ext_include_binary_create_context(this_ext, sbin); + + return ctx; +} + +struct ext_include_binary_context *ext_include_binary_init +(const struct sieve_extension *this_ext, struct sieve_binary *sbin, + struct sieve_ast *ast) +{ + struct ext_include_ast_context *ast_ctx = + ext_include_get_ast_context(this_ext, ast); + struct ext_include_binary_context *ctx; + + /* Get/create our context from the binary we are working on */ + ctx = ext_include_binary_get_context(this_ext, sbin); + + /* Create dependency block */ + if ( ctx->dependency_block == 0 ) + ctx->dependency_block = + sieve_binary_extension_create_block(sbin, this_ext); + + if ( ctx->global_vars == NULL ) { + ctx->global_vars = + sieve_variable_scope_binary_create(ast_ctx->global_vars); + sieve_variable_scope_binary_ref(ctx->global_vars); + } + + return ctx; +} + +/* + * Script inclusion + */ + +struct ext_include_script_info *ext_include_binary_script_include +(struct ext_include_binary_context *binctx, + enum ext_include_script_location location, enum ext_include_flags flags, + struct sieve_script *script, struct sieve_binary_block *inc_block) +{ + pool_t pool = sieve_binary_pool(binctx->binary); + struct ext_include_script_info *incscript; + + incscript = p_new(pool, struct ext_include_script_info, 1); + incscript->id = array_count(&binctx->include_index)+1; + incscript->location = location; + incscript->flags = flags; + incscript->script = script; + incscript->block = inc_block; + + /* Unreferenced on binary_free */ + sieve_script_ref(script); + + hash_table_insert(binctx->included_scripts, script, incscript); + array_append(&binctx->include_index, &incscript, 1); + + return incscript; +} + +struct ext_include_script_info *ext_include_binary_script_get_include_info +(struct ext_include_binary_context *binctx, struct sieve_script *script) +{ + struct ext_include_script_info *incscript = + hash_table_lookup(binctx->included_scripts, script); + + return incscript; +} + +const struct ext_include_script_info *ext_include_binary_script_get_included +(struct ext_include_binary_context *binctx, unsigned int include_id) +{ + if ( include_id > 0 && + (include_id - 1) < array_count(&binctx->include_index) ) { + struct ext_include_script_info *const *sinfo = + array_idx(&binctx->include_index, include_id - 1); + + return *sinfo; + } + + return NULL; +} + +const struct ext_include_script_info *ext_include_binary_script_get +(struct ext_include_binary_context *binctx, struct sieve_script *script) +{ + return hash_table_lookup(binctx->included_scripts, script); +} + +unsigned int ext_include_binary_script_get_count +(struct ext_include_binary_context *binctx) +{ + return array_count(&binctx->include_index); +} + +/* + * Variables + */ + +struct sieve_variable_scope_binary *ext_include_binary_get_global_scope +(const struct sieve_extension *this_ext, struct sieve_binary *sbin) +{ + struct ext_include_binary_context *binctx = + ext_include_binary_get_context(this_ext, sbin); + + return binctx->global_vars; +} + +/* + * Binary extension + */ + +static bool ext_include_binary_pre_save +(const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_binary *sbin ATTR_UNUSED, void *context, + enum sieve_error *error_r) +{ + struct ext_include_binary_context *binctx = + (struct ext_include_binary_context *) context; + struct ext_include_script_info *const *scripts; + struct sieve_binary_block *sblock = binctx->dependency_block; + unsigned int script_count, i; + bool result = TRUE; + + sieve_binary_block_clear(sblock); + + scripts = array_get(&binctx->include_index, &script_count); + + sieve_binary_emit_unsigned(sblock, script_count); + + for ( i = 0; i < script_count; i++ ) { + struct ext_include_script_info *incscript = scripts[i]; + + if ( incscript->block != NULL ) { + sieve_binary_emit_unsigned + (sblock, sieve_binary_block_get_id(incscript->block)); + } else { + sieve_binary_emit_unsigned(sblock, 0); + } + sieve_binary_emit_byte(sblock, incscript->location); + sieve_binary_emit_cstring(sblock, sieve_script_name(incscript->script)); + sieve_binary_emit_byte(sblock, incscript->flags); + sieve_script_binary_write_metadata(incscript->script, sblock); + } + + result = ext_include_variables_save(sblock, binctx->global_vars, error_r); + + return result; +} + +static bool ext_include_binary_open +(const struct sieve_extension *ext, struct sieve_binary *sbin, void *context) +{ + struct sieve_instance *svinst = ext->svinst; + struct ext_include_context *ext_ctx = + (struct ext_include_context *)ext->context; + struct ext_include_binary_context *binctx = + (struct ext_include_binary_context *) context; + struct sieve_binary_block *sblock; + unsigned int depcount, i, block_id; + sieve_size_t offset; + + sblock = sieve_binary_extension_get_block(sbin, ext); + block_id = sieve_binary_block_get_id(sblock); + + offset = 0; + + if ( !sieve_binary_read_unsigned(sblock, &offset, &depcount) ) { + e_error(svinst->event, + "include: failed to read include count " + "for dependency block %d of binary %s", block_id, + sieve_binary_path(sbin)); + return FALSE; + } + + /* Check include limit */ + if ( depcount > ext_ctx->max_includes ) { + e_error(svinst->event, + "include: binary %s includes too many scripts (%u > %u)", + sieve_binary_path(sbin), depcount, ext_ctx->max_includes); + return FALSE; + } + + /* Read dependencies */ + for ( i = 0; i < depcount; i++ ) { + unsigned int inc_block_id; + struct sieve_binary_block *inc_block = NULL; + unsigned int location, flags; + string_t *script_name; + struct sieve_storage *storage; + struct sieve_script *script; + enum sieve_error error; + int ret; + + if ( + !sieve_binary_read_unsigned(sblock, &offset, &inc_block_id) || + !sieve_binary_read_byte(sblock, &offset, &location) || + !sieve_binary_read_string(sblock, &offset, &script_name) || + !sieve_binary_read_byte(sblock, &offset, &flags) ) { + /* Binary is corrupt, recompile */ + e_error(svinst->event, + "include: failed to read included script " + "from dependency block %d of binary %s", + block_id, sieve_binary_path(sbin)); + return FALSE; + } + + if ( inc_block_id != 0 && + (inc_block=sieve_binary_block_get(sbin, inc_block_id)) == NULL ) { + e_error(svinst->event, + "include: failed to find block %d for included script " + "from dependency block %d of binary %s", + inc_block_id, block_id, + sieve_binary_path(sbin)); + return FALSE; + } + + if ( location >= EXT_INCLUDE_LOCATION_INVALID ) { + /* Binary is corrupt, recompile */ + e_error(svinst->event, + "include: dependency block %d of binary %s " + "uses invalid script location (id %d)", + block_id, sieve_binary_path(sbin), location); + return FALSE; + } + + /* Can we find the script dependency ? */ + storage = ext_include_get_script_storage + (ext, location, str_c(script_name), &error); + if ( storage == NULL ) { + /* No, recompile */ + // FIXME: handle ':optional' in this case + return FALSE; + } + + /* Can we open the script dependency ? */ + script = sieve_storage_get_script + (storage, str_c(script_name), &error); + if ( script == NULL ) { + /* No, recompile */ + return FALSE; + } + if ( sieve_script_open(script, &error) < 0 ) { + if ( error != SIEVE_ERROR_NOT_FOUND ) { + /* No, recompile */ + return FALSE; + } + + if ( (flags & EXT_INCLUDE_FLAG_OPTIONAL) == 0 ) { + /* Not supposed to be missing, recompile */ + if ( svinst->debug ) { + e_debug(svinst->event, "include: " + "script '%s' included in binary %s is missing, " + "so recompile", + str_c(script_name), + sieve_binary_path(sbin)); + } + return FALSE; + } + + } else if (inc_block == NULL) { + /* Script exists, but it is missing from the binary, recompile no matter + * what. + */ + if ( svinst->debug ) { + e_debug(svinst->event, "include: " + "script '%s' is missing in binary %s, but is now available, " + "so recompile", str_c(script_name), sieve_binary_path(sbin)); + } + sieve_script_unref(&script); + return FALSE; + } + + /* Can we read script metadata ? */ + if ( (ret=sieve_script_binary_read_metadata + (script, sblock, &offset)) < 0 ) { + /* Binary is corrupt, recompile */ + e_error(svinst->event, "include: " + "dependency block %d of binary %s " + "contains invalid script metadata for script %s", + block_id, sieve_binary_path(sbin), + sieve_script_location(script)); + sieve_script_unref(&script); + return FALSE; + } + + if ( ret == 0 ) + binctx->outdated = TRUE; + + (void)ext_include_binary_script_include + (binctx, location, flags, script, inc_block); + + sieve_script_unref(&script); + } + + if ( !ext_include_variables_load + (ext, sblock, &offset, &binctx->global_vars) ) + return FALSE; + + return TRUE; +} + +static bool ext_include_binary_up_to_date +(const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_binary *sbin ATTR_UNUSED, void *context, + enum sieve_compile_flags cpflags ATTR_UNUSED) +{ + struct ext_include_binary_context *binctx = + (struct ext_include_binary_context *) context; + + return !binctx->outdated; +} + +static void ext_include_binary_free +(const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_binary *sbin ATTR_UNUSED, void *context) +{ + struct ext_include_binary_context *binctx = + (struct ext_include_binary_context *) context; + struct hash_iterate_context *hctx; + struct sieve_script *script; + struct ext_include_script_info *incscript; + + /* Release references to all included script objects */ + hctx = hash_table_iterate_init(binctx->included_scripts); + while ( hash_table_iterate + (hctx, binctx->included_scripts, &script, &incscript) ) + sieve_script_unref(&incscript->script); + hash_table_iterate_deinit(&hctx); + + hash_table_destroy(&binctx->included_scripts); + + if ( binctx->global_vars != NULL ) + sieve_variable_scope_binary_unref(&binctx->global_vars); +} + +/* + * Dumping the binary + */ + +bool ext_include_binary_dump +(const struct sieve_extension *ext, struct sieve_dumptime_env *denv) +{ + struct sieve_binary *sbin = denv->sbin; + struct ext_include_binary_context *binctx = + ext_include_binary_get_context(ext, sbin); + struct hash_iterate_context *hctx; + struct sieve_script *script; + struct ext_include_script_info *incscript; + + if ( !ext_include_variables_dump(denv, binctx->global_vars) ) + return FALSE; + + hctx = hash_table_iterate_init(binctx->included_scripts); + while ( hash_table_iterate + (hctx, binctx->included_scripts, &script, &incscript) ) { + + if ( incscript->block == NULL ) { + sieve_binary_dump_sectionf(denv, "Included %s script '%s' (MISSING)", + ext_include_script_location_name(incscript->location), + sieve_script_name(incscript->script)); + + } else { + unsigned int block_id = sieve_binary_block_get_id(incscript->block); + + sieve_binary_dump_sectionf(denv, "Included %s script '%s' (block: %d)", + ext_include_script_location_name(incscript->location), + sieve_script_name(incscript->script), block_id); + + denv->sblock = incscript->block; + denv->cdumper = sieve_code_dumper_create(denv); + + if ( denv->cdumper == NULL ) + return FALSE; + + sieve_code_dumper_run(denv->cdumper); + sieve_code_dumper_free(&(denv->cdumper)); + } + } + hash_table_iterate_deinit(&hctx); + + return TRUE; +} + +bool ext_include_code_dump +(const struct sieve_extension *ext, const struct sieve_dumptime_env *denv, + sieve_size_t *address ATTR_UNUSED) +{ + struct sieve_binary *sbin = denv->sbin; + struct ext_include_binary_context *binctx = + ext_include_binary_get_context(ext, sbin); + struct ext_include_context *ectx = ext_include_get_context(ext); + + sieve_ext_variables_dump_set_scope + (ectx->var_ext, denv, ext, + sieve_variable_scope_binary_get(binctx->global_vars)); + + return TRUE; +} + + |