summaryrefslogtreecommitdiffstats
path: root/pigeonhole/src/lib-sieve/plugins/include/ext-include-binary.c
diff options
context:
space:
mode:
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.c492
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;
+}
+
+