summaryrefslogtreecommitdiffstats
path: root/pigeonhole/src/lib-sieve/sieve-binary.c
diff options
context:
space:
mode:
Diffstat (limited to 'pigeonhole/src/lib-sieve/sieve-binary.c')
-rw-r--r--pigeonhole/src/lib-sieve/sieve-binary.c606
1 files changed, 606 insertions, 0 deletions
diff --git a/pigeonhole/src/lib-sieve/sieve-binary.c b/pigeonhole/src/lib-sieve/sieve-binary.c
new file mode 100644
index 0000000..06cf598
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/sieve-binary.c
@@ -0,0 +1,606 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str.h"
+#include "str-sanitize.h"
+#include "mempool.h"
+#include "buffer.h"
+#include "hash.h"
+#include "array.h"
+#include "ostream.h"
+#include "eacces-error.h"
+#include "safe-mkstemp.h"
+
+#include "sieve-error.h"
+#include "sieve-extensions.h"
+#include "sieve-code.h"
+#include "sieve-script.h"
+
+#include "sieve-binary-private.h"
+
+/*
+ * Forward declarations
+ */
+
+static inline struct sieve_binary_extension_reg *
+sieve_binary_extension_get_reg(struct sieve_binary *sbin,
+ const struct sieve_extension *ext, bool create);
+
+static inline int
+sieve_binary_extension_register(struct sieve_binary *sbin,
+ const struct sieve_extension *ext,
+ struct sieve_binary_extension_reg **reg);
+
+/*
+ * Binary object
+ */
+
+void sieve_binary_update_event(struct sieve_binary *sbin, const char *new_path)
+{
+ if (new_path != NULL) {
+ event_set_append_log_prefix(
+ sbin->event, t_strdup_printf("binary %s: ", new_path));
+ } else if (sbin->path != NULL) {
+ event_set_append_log_prefix(
+ sbin->event,
+ t_strdup_printf("binary %s: ", sbin->path));
+ } else if (sbin->script != NULL) {
+ event_set_append_log_prefix(
+ sbin->event,
+ t_strdup_printf("binary %s: ",
+ sieve_script_name(sbin->script)));
+ } else {
+ event_set_append_log_prefix(sbin->event, "binary: ");
+ }
+}
+
+struct sieve_binary *
+sieve_binary_create(struct sieve_instance *svinst, struct sieve_script *script)
+{
+ pool_t pool;
+ struct sieve_binary *sbin;
+ const struct sieve_extension *const *ext_preloaded;
+ unsigned int i, ext_count;
+
+ pool = pool_alloconly_create("sieve_binary", 16384);
+ sbin = p_new(pool, struct sieve_binary, 1);
+ sbin->pool = pool;
+ sbin->refcount = 1;
+ sbin->svinst = svinst;
+
+ sbin->header.version_major = SIEVE_BINARY_VERSION_MAJOR;
+ sbin->header.version_minor = SIEVE_BINARY_VERSION_MINOR;
+
+ sbin->script = script;
+ if (script != NULL)
+ sieve_script_ref(script);
+
+ sbin->event = event_create(svinst->event);
+
+ ext_count = sieve_extensions_get_count(svinst);
+
+ p_array_init(&sbin->linked_extensions, pool, ext_count);
+ p_array_init(&sbin->extensions, pool, ext_count);
+ p_array_init(&sbin->extension_index, pool, ext_count);
+
+ p_array_init(&sbin->blocks, pool, 16);
+
+ /* Pre-load core language features implemented as 'extensions' */
+ ext_preloaded = sieve_extensions_get_preloaded(svinst, &ext_count);
+ for (i = 0; i < ext_count; i++) {
+ const struct sieve_extension_def *ext_def = ext_preloaded[i]->def;
+
+ if (ext_def != NULL && ext_def->binary_load != NULL)
+ (void)ext_def->binary_load(ext_preloaded[i], sbin);
+ }
+
+ return sbin;
+}
+
+struct sieve_binary *sieve_binary_create_new(struct sieve_script *script)
+{
+ struct sieve_binary *sbin =
+ sieve_binary_create(sieve_script_svinst(script), script);
+ struct sieve_binary_block *sblock;
+ unsigned int i;
+
+ sieve_binary_update_event(sbin, NULL);
+
+ /* Create script metadata block */
+ sblock = sieve_binary_block_create(sbin);
+ sieve_script_binary_write_metadata(script, sblock);
+
+ /* Create other system blocks */
+ for (i = 1; i < SBIN_SYSBLOCK_LAST; i++)
+ (void)sieve_binary_block_create(sbin);
+
+ return sbin;
+}
+
+void sieve_binary_ref(struct sieve_binary *sbin)
+{
+ sbin->refcount++;
+}
+
+static inline void sieve_binary_extensions_free(struct sieve_binary *sbin)
+{
+ struct sieve_binary_extension_reg *const *regs;
+ unsigned int ext_count, i;
+
+ /* Cleanup binary extensions */
+ regs = array_get(&sbin->extensions, &ext_count);
+ for (i = 0; i < ext_count; i++) {
+ const struct sieve_binary_extension *binext = regs[i]->binext;
+
+ if (binext != NULL && binext->binary_free != NULL) {
+ binext->binary_free(regs[i]->extension, sbin,
+ regs[i]->context);
+ }
+ }
+}
+
+static void sieve_binary_update_resource_usage(struct sieve_binary *sbin)
+{
+ enum sieve_error error;
+
+ if (sbin->rusage_updated)
+ (void)sieve_binary_file_update_resource_usage(sbin, &error);
+ sbin->rusage_updated = FALSE;
+}
+
+void sieve_binary_unref(struct sieve_binary **_sbin)
+{
+ struct sieve_binary *sbin = *_sbin;
+
+ *_sbin = NULL;
+ if (sbin == NULL)
+ return;
+
+ i_assert(sbin->refcount > 0);
+ if (--sbin->refcount != 0)
+ return;
+
+ sieve_binary_file_close(&sbin->file);
+ sieve_binary_update_resource_usage(sbin);
+ sieve_binary_extensions_free(sbin);
+
+ if (sbin->script != NULL)
+ sieve_script_unref(&sbin->script);
+
+ event_unref(&sbin->event);
+ pool_unref(&sbin->pool);
+}
+
+void sieve_binary_close(struct sieve_binary **_sbin)
+{
+ struct sieve_binary *sbin = *_sbin;
+
+ *_sbin = NULL;
+ if (sbin == NULL)
+ return;
+
+ sieve_binary_file_close(&sbin->file);
+ sieve_binary_update_resource_usage(sbin);
+ sieve_binary_unref(&sbin);
+}
+
+/*
+ * Resource usage
+ */
+
+void sieve_binary_get_resource_usage(struct sieve_binary *sbin,
+ struct sieve_resource_usage *rusage_r)
+{
+ struct sieve_binary_header *header = &sbin->header;
+ time_t update_time = header->resource_usage.update_time;
+ unsigned int timeout = sbin->svinst->resource_usage_timeout_secs;
+
+ if (update_time != 0 && (ioloop_time - update_time) > timeout)
+ i_zero(&header->resource_usage);
+
+ sieve_resource_usage_init(rusage_r);
+ rusage_r->cpu_time_msecs = header->resource_usage.cpu_time_msecs;
+ sieve_resource_usage_add(rusage_r, &sbin->rusage);
+}
+
+bool sieve_binary_check_resource_usage(struct sieve_binary *sbin)
+{
+ struct sieve_binary_header *header = &sbin->header;
+ struct sieve_resource_usage rusage;
+
+ sieve_binary_get_resource_usage(sbin, &rusage);
+
+ if (sieve_resource_usage_is_excessive(sbin->svinst, &rusage)) {
+ header->flags |= SIEVE_BINARY_FLAG_RESOURCE_LIMIT;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool sieve_binary_record_resource_usage(
+ struct sieve_binary *sbin, const struct sieve_resource_usage *rusage)
+{
+ struct sieve_resource_usage rusage_total;
+
+ if (sbin == NULL)
+ return TRUE;
+ if (!sieve_resource_usage_is_high(sbin->svinst, rusage))
+ return TRUE;
+
+ sieve_resource_usage_add(&sbin->rusage, rusage);
+ sbin->rusage_updated = TRUE;
+
+ sieve_binary_get_resource_usage(sbin, &rusage_total);
+
+ e_debug(sbin->event, "Updated cumulative resource usage: %s",
+ sieve_resource_usage_get_summary(&rusage_total));
+
+ return sieve_binary_check_resource_usage(sbin);
+}
+
+void sieve_binary_set_resource_usage(struct sieve_binary *sbin,
+ const struct sieve_resource_usage *rusage)
+{
+ struct sieve_binary_header *header = &sbin->header;
+
+ i_zero(&header->resource_usage);
+ sbin->rusage = *rusage;
+ sbin->rusage_updated = TRUE;
+
+ (void)sieve_binary_check_resource_usage(sbin);
+}
+
+/*
+ * Accessors
+ */
+
+pool_t sieve_binary_pool(struct sieve_binary *sbin)
+{
+ return sbin->pool;
+}
+
+struct sieve_script *sieve_binary_script(struct sieve_binary *sbin)
+{
+ return sbin->script;
+}
+
+const char *sieve_binary_path(struct sieve_binary *sbin)
+{
+ return sbin->path;
+}
+
+bool sieve_binary_saved(struct sieve_binary *sbin)
+{
+ return (sbin->path != NULL);
+}
+
+bool sieve_binary_loaded(struct sieve_binary *sbin)
+{
+ return (sbin->file != NULL);
+}
+
+const char *sieve_binary_source(struct sieve_binary *sbin)
+{
+ if (sbin->script != NULL && (sbin->path == NULL || sbin->file == NULL))
+ return sieve_script_location(sbin->script);
+
+ return sbin->path;
+}
+
+struct sieve_instance *sieve_binary_svinst(struct sieve_binary *sbin)
+{
+ return sbin->svinst;
+}
+
+time_t sieve_binary_mtime(struct sieve_binary *sbin)
+{
+ i_assert(sbin->file != NULL);
+ return sbin->file->st.st_mtime;
+}
+
+const struct stat *sieve_binary_stat(struct sieve_binary *sbin)
+{
+ i_assert(sbin->file != NULL);
+ return &sbin->file->st;
+}
+
+const char *sieve_binary_script_name(struct sieve_binary *sbin)
+{
+ return (sbin->script == NULL ?
+ NULL : sieve_script_name(sbin->script));
+}
+
+const char *sieve_binary_script_location(struct sieve_binary *sbin)
+{
+ return (sbin->script == NULL ?
+ NULL : sieve_script_location(sbin->script));
+}
+
+/*
+ * Utility
+ */
+
+const char *sieve_binfile_from_name(const char *name)
+{
+ return t_strconcat(name, "."SIEVE_BINARY_FILEEXT, NULL);
+}
+
+/*
+ * Block management
+ */
+
+unsigned int sieve_binary_block_count(struct sieve_binary *sbin)
+{
+ return array_count(&sbin->blocks);
+}
+
+struct sieve_binary_block *sieve_binary_block_create(struct sieve_binary *sbin)
+{
+ unsigned int id = sieve_binary_block_count(sbin);
+ struct sieve_binary_block *sblock;
+
+ sblock = p_new(sbin->pool, struct sieve_binary_block, 1);
+ sblock->data = buffer_create_dynamic(sbin->pool, 64);
+ sblock->sbin = sbin;
+ sblock->id = id;
+
+ array_append(&sbin->blocks, &sblock, 1);
+
+ return sblock;
+}
+
+struct sieve_binary_block *
+sieve_binary_block_create_id(struct sieve_binary *sbin, unsigned int id)
+{
+ struct sieve_binary_block *sblock;
+
+ sblock = p_new(sbin->pool, struct sieve_binary_block, 1);
+
+ array_idx_set(&sbin->blocks, id, &sblock);
+ sblock->data = NULL;
+ sblock->sbin = sbin;
+ sblock->id = id;
+
+ return sblock;
+}
+
+static bool sieve_binary_block_fetch(struct sieve_binary_block *sblock)
+{
+ struct sieve_binary *sbin = sblock->sbin;
+
+ if (sbin->file != NULL) {
+ /* Try to acces the block in the binary on disk (apparently we
+ were lazy)
+ */
+ if (!sieve_binary_load_block(sblock) || sblock->data == NULL)
+ return FALSE;
+ } else {
+ sblock->data = buffer_create_dynamic(sbin->pool, 64);
+ return TRUE;
+ }
+
+ return TRUE;
+}
+
+struct sieve_binary_block *
+sieve_binary_block_get(struct sieve_binary *sbin, unsigned int id)
+{
+ struct sieve_binary_block *sblock = sieve_binary_block_index(sbin, id);
+
+ if (sblock == NULL)
+ return NULL;
+
+ if (sblock->data == NULL && !sieve_binary_block_fetch(sblock))
+ return NULL;
+
+ return sblock;
+}
+
+void sieve_binary_block_clear(struct sieve_binary_block *sblock)
+{
+ buffer_set_used_size(sblock->data, 0);
+}
+
+buffer_t *sieve_binary_block_get_buffer(struct sieve_binary_block *sblock)
+{
+ if (sblock->data == NULL && !sieve_binary_block_fetch(sblock))
+ return NULL;
+
+ return sblock->data;
+}
+
+struct sieve_binary *
+sieve_binary_block_get_binary(const struct sieve_binary_block *sblock)
+{
+ return sblock->sbin;
+}
+
+unsigned int sieve_binary_block_get_id(const struct sieve_binary_block *sblock)
+{
+ return sblock->id;
+}
+
+size_t sieve_binary_block_get_size(const struct sieve_binary_block *sblock)
+{
+ return _sieve_binary_block_get_size(sblock);
+}
+
+/*
+ * Up-to-date checking
+ */
+
+bool sieve_binary_up_to_date(struct sieve_binary *sbin,
+ enum sieve_compile_flags cpflags)
+{
+ struct sieve_binary_extension_reg *const *regs;
+ struct sieve_binary_block *sblock;
+ sieve_size_t offset = 0;
+ unsigned int ext_count, i;
+ int ret;
+
+ i_assert(sbin->file != NULL);
+
+ sblock = sieve_binary_block_get(sbin, SBIN_SYSBLOCK_SCRIPT_DATA);
+ if (sblock == NULL || sbin->script == NULL)
+ return FALSE;
+
+ if ((ret = sieve_script_binary_read_metadata(sbin->script, sblock,
+ &offset)) <= 0) {
+ if (ret < 0) {
+ e_debug(sbin->event, "up-to-date: "
+ "failed to read script metadata from binary");
+ } else {
+ e_debug(sbin->event, "up-to-date: "
+ "script metadata indicates that binary is not up-to-date");
+ }
+ return FALSE;
+ }
+
+ regs = array_get(&sbin->extensions, &ext_count);
+ for (i = 0; i < ext_count; i++) {
+ const struct sieve_binary_extension *binext = regs[i]->binext;
+
+ if (binext != NULL && binext->binary_up_to_date != NULL &&
+ !binext->binary_up_to_date(regs[i]->extension, sbin,
+ regs[i]->context, cpflags)) {
+ e_debug(sbin->event, "up-to-date: "
+ "the %s extension indicates binary is not up-to-date",
+ sieve_extension_name(regs[i]->extension));
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/*
+ * Activate the binary (after code generation)
+ */
+
+void sieve_binary_activate(struct sieve_binary *sbin)
+{
+ struct sieve_binary_extension_reg *const *regs;
+ unsigned int i, ext_count;
+
+ /* Load other extensions into binary */
+ regs = array_get(&sbin->linked_extensions, &ext_count);
+ for (i = 0; i < ext_count; i++) {
+ const struct sieve_extension *ext = regs[i]->extension;
+
+ if (ext != NULL && ext->def != NULL &&
+ ext->def->binary_load != NULL)
+ ext->def->binary_load(ext, sbin);
+ }
+}
+
+/*
+ * Extension handling
+ */
+
+void sieve_binary_extension_set_context(struct sieve_binary *sbin,
+ const struct sieve_extension *ext,
+ void *context)
+{
+ struct sieve_binary_extension_reg *ereg =
+ sieve_binary_extension_get_reg(sbin, ext, TRUE);
+
+ if (ereg != NULL)
+ ereg->context = context;
+}
+
+const void *
+sieve_binary_extension_get_context(struct sieve_binary *sbin,
+ const struct sieve_extension *ext)
+{
+ struct sieve_binary_extension_reg *ereg =
+ sieve_binary_extension_get_reg(sbin, ext, TRUE);
+
+ if (ereg != NULL)
+ return ereg->context;
+
+ return NULL;
+}
+
+void sieve_binary_extension_set(struct sieve_binary *sbin,
+ const struct sieve_extension *ext,
+ const struct sieve_binary_extension *bext,
+ void *context)
+{
+ struct sieve_binary_extension_reg *ereg =
+ sieve_binary_extension_get_reg(sbin, ext, TRUE);
+
+ if (ereg != NULL) {
+ ereg->binext = bext;
+
+ if (context != NULL)
+ ereg->context = context;
+ }
+}
+
+struct sieve_binary_block *
+sieve_binary_extension_create_block(struct sieve_binary *sbin,
+ const struct sieve_extension *ext)
+{
+ struct sieve_binary_block *sblock;
+ struct sieve_binary_extension_reg *ereg =
+ sieve_binary_extension_get_reg(sbin, ext, TRUE);
+
+ i_assert(ereg != NULL);
+
+ sblock = sieve_binary_block_create(sbin);
+
+ if (ereg->block_id < SBIN_SYSBLOCK_LAST)
+ ereg->block_id = sblock->id;
+ sblock->ext_index = ereg->index;
+
+ return sblock;
+}
+
+struct sieve_binary_block *
+sieve_binary_extension_get_block(struct sieve_binary *sbin,
+ const struct sieve_extension *ext)
+{
+ struct sieve_binary_extension_reg *ereg =
+ sieve_binary_extension_get_reg(sbin, ext, TRUE);
+
+ i_assert(ereg != NULL);
+
+ if (ereg->block_id < SBIN_SYSBLOCK_LAST)
+ return NULL;
+
+ return sieve_binary_block_get(sbin, ereg->block_id);
+}
+
+int sieve_binary_extension_link(struct sieve_binary *sbin,
+ const struct sieve_extension *ext)
+{
+ return sieve_binary_extension_register(sbin, ext, NULL);
+}
+
+const struct sieve_extension *
+sieve_binary_extension_get_by_index(struct sieve_binary *sbin, int index)
+{
+ struct sieve_binary_extension_reg * const *ereg;
+
+ if (index < (int)array_count(&sbin->extensions)) {
+ ereg = array_idx(&sbin->extensions, (unsigned int)index);
+
+ return (*ereg)->extension;
+ }
+
+ return NULL;
+}
+
+int sieve_binary_extension_get_index(struct sieve_binary *sbin,
+ const struct sieve_extension *ext)
+{
+ struct sieve_binary_extension_reg *ereg =
+ sieve_binary_extension_get_reg(sbin, ext, FALSE);
+
+ return (ereg == NULL ? -1 : ereg->index);
+}
+
+int sieve_binary_extensions_count(struct sieve_binary *sbin)
+{
+ return (int)array_count(&sbin->extensions);
+}