summaryrefslogtreecommitdiffstats
path: root/pigeonhole/src/lib-sieve/sieve-result.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--pigeonhole/src/lib-sieve/sieve-result.c2445
1 files changed, 2445 insertions, 0 deletions
diff --git a/pigeonhole/src/lib-sieve/sieve-result.c b/pigeonhole/src/lib-sieve/sieve-result.c
new file mode 100644
index 0000000..89d249e
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/sieve-result.c
@@ -0,0 +1,2445 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "mempool.h"
+#include "ostream.h"
+#include "hash.h"
+#include "str.h"
+#include "llist.h"
+#include "strfuncs.h"
+#include "str-sanitize.h"
+#include "var-expand.h"
+#include "message-address.h"
+#include "mail-storage.h"
+
+#include "sieve-common.h"
+#include "sieve-limits.h"
+#include "sieve-script.h"
+#include "sieve-error.h"
+#include "sieve-interpreter.h"
+#include "sieve-actions.h"
+#include "sieve-message.h"
+
+#include "sieve-result.h"
+
+#include <stdio.h>
+
+struct event_category event_category_sieve_action = {
+ .parent = &event_category_sieve,
+ .name = "sieve-action",
+};
+
+/*
+ * Types
+ */
+
+enum sieve_action_execution_state {
+ SIEVE_ACTION_EXECUTION_STATE_INIT = 0,
+ SIEVE_ACTION_EXECUTION_STATE_STARTED,
+ SIEVE_ACTION_EXECUTION_STATE_EXECUTED,
+ SIEVE_ACTION_EXECUTION_STATE_FINALIZED,
+};
+
+struct sieve_result_action {
+ struct sieve_action action;
+
+ struct sieve_side_effects_list *seffects;
+
+ struct sieve_result_action *prev, *next;
+};
+
+struct sieve_side_effects_list {
+ struct sieve_result *result;
+
+ struct sieve_result_side_effect *first_effect;
+ struct sieve_result_side_effect *last_effect;
+};
+
+struct sieve_result_side_effect {
+ struct sieve_side_effect seffect;
+
+ struct sieve_result_side_effect *prev, *next;
+};
+
+struct sieve_result_action_context {
+ const struct sieve_action_def *action;
+ struct sieve_side_effects_list *seffects;
+};
+
+/*
+ * Result object
+ */
+
+struct sieve_result {
+ pool_t pool;
+ int refcount;
+
+ struct sieve_instance *svinst;
+ struct event *event;
+
+ /* Context data for extensions */
+ ARRAY(void *) ext_contexts;
+
+ const struct sieve_execute_env *exec_env;
+ struct sieve_error_handler *ehandler;
+ struct sieve_message_context *msgctx;
+
+ unsigned int exec_seq;
+ struct sieve_result_execution *exec;
+
+ struct sieve_action keep_action;
+ struct sieve_action failure_action;
+
+ unsigned int action_count;
+ struct sieve_result_action *actions_head, *actions_tail;
+
+ HASH_TABLE(const struct sieve_action_def *,
+ struct sieve_result_action_context *) action_contexts;
+};
+
+static const char *
+sieve_result_event_log_message(struct sieve_result *result,
+ enum log_type log_type, const char *message)
+{
+ const struct sieve_script_env *senv = result->exec_env->scriptenv;
+
+ i_assert(senv->result_amend_log_message != NULL);
+ return senv->result_amend_log_message(senv, log_type, message);
+}
+
+struct sieve_result *
+sieve_result_create(struct sieve_instance *svinst, pool_t pool,
+ const struct sieve_execute_env *eenv)
+{
+ const struct sieve_script_env *senv = eenv->scriptenv;
+ const struct sieve_message_data *msgdata = eenv->msgdata;
+ struct sieve_result *result;
+
+ pool_ref(pool);
+
+ result = p_new(pool, struct sieve_result, 1);
+ result->refcount = 1;
+ result->pool = pool;
+ result->svinst = svinst;
+
+ result->event = event_create(eenv->event);
+ event_add_category(result->event, &event_category_sieve_action);
+ if (senv->result_amend_log_message != NULL) {
+ event_set_log_message_callback(
+ result->event, sieve_result_event_log_message, result);
+ }
+
+ p_array_init(&result->ext_contexts, pool, 4);
+
+ result->exec_env = eenv;
+ result->msgctx =
+ sieve_message_context_create(svinst, senv->user, msgdata);
+
+ result->keep_action.def = &act_store;
+ result->keep_action.ext = NULL;
+ result->failure_action.def = &act_store;
+ result->failure_action.ext = NULL;
+
+ result->action_count = 0;
+ result->actions_head = NULL;
+ result->actions_tail = NULL;
+
+ return result;
+}
+
+void sieve_result_ref(struct sieve_result *result)
+{
+ result->refcount++;
+}
+
+static void
+sieve_result_action_deinit(struct sieve_result_action *ract)
+{
+ event_unref(&ract->action.event);
+}
+
+void sieve_result_unref(struct sieve_result **_result)
+{
+ struct sieve_result *result = *_result;
+ struct sieve_result_action *ract;
+
+ i_assert(result->refcount > 0);
+
+ if (--result->refcount != 0)
+ return;
+
+ sieve_message_context_unref(&result->msgctx);
+
+ hash_table_destroy(&result->action_contexts);
+
+ ract = result->actions_head;
+ while (ract != NULL) {
+ sieve_result_action_deinit(ract);
+ ract = ract->next;
+ }
+ event_unref(&result->event);
+
+ pool_unref(&result->pool);
+ *_result = NULL;
+}
+
+pool_t sieve_result_pool(struct sieve_result *result)
+{
+ return result->pool;
+}
+
+/*
+ * Getters/Setters
+ */
+
+const struct sieve_script_env *
+sieve_result_get_script_env(struct sieve_result *result)
+{
+ return result->exec_env->scriptenv;
+}
+
+const struct sieve_message_data *
+sieve_result_get_message_data(struct sieve_result *result)
+{
+ return result->exec_env->msgdata;
+}
+
+struct sieve_message_context *
+sieve_result_get_message_context(struct sieve_result *result)
+{
+ return result->msgctx;
+}
+
+unsigned int sieve_result_get_exec_seq(struct sieve_result *result)
+{
+ return result->exec_seq;
+}
+
+/*
+ * Extension support
+ */
+
+void sieve_result_extension_set_context(struct sieve_result *result,
+ const struct sieve_extension *ext,
+ void *context)
+{
+ if (ext->id < 0)
+ return;
+
+ array_idx_set(&result->ext_contexts, (unsigned int) ext->id, &context);
+}
+
+const void *
+sieve_result_extension_get_context(struct sieve_result *result,
+ const struct sieve_extension *ext)
+{
+ void * const *ctx;
+
+ if (ext->id < 0 || ext->id >= (int) array_count(&result->ext_contexts))
+ return NULL;
+
+ ctx = array_idx(&result->ext_contexts, (unsigned int) ext->id);
+
+ return *ctx;
+}
+
+/*
+ * Result composition
+ */
+
+static void
+sieve_result_init_action_event(struct sieve_result *result,
+ struct sieve_action *action, bool add_prefix)
+{
+ const char *name = sieve_action_name(action);
+
+ if (action->event != NULL)
+ return;
+
+ action->event = event_create(result->event);
+ if (add_prefix && name != NULL) {
+ event_set_append_log_prefix(
+ action->event, t_strconcat(name, " action: ", NULL));
+ }
+ event_add_str(action->event, "action_name", name);
+ event_add_str(action->event, "script_location", action->location);
+}
+
+void sieve_result_add_implicit_side_effect(
+ struct sieve_result *result, const struct sieve_action_def *to_action,
+ bool to_keep, const struct sieve_extension *ext,
+ const struct sieve_side_effect_def *seff_def, void *context)
+{
+ struct sieve_result_action_context *actctx = NULL;
+ struct sieve_side_effect seffect;
+
+ to_action = to_keep ? &act_store : to_action;
+
+ if (!hash_table_is_created(result->action_contexts)) {
+ hash_table_create_direct(&result->action_contexts,
+ result->pool, 0);
+ } else {
+ actctx = hash_table_lookup(result->action_contexts, to_action);
+ }
+
+ if (actctx == NULL) {
+ actctx = p_new(result->pool,
+ struct sieve_result_action_context, 1);
+ actctx->action = to_action;
+ actctx->seffects = sieve_side_effects_list_create(result);
+
+ hash_table_insert(result->action_contexts, to_action, actctx);
+ }
+
+ seffect.object.def = &seff_def->obj_def;
+ seffect.object.ext = ext;
+ seffect.def = seff_def;
+ seffect.context = context;
+
+ sieve_side_effects_list_add(actctx->seffects, &seffect);
+}
+
+static int
+sieve_result_side_effects_merge(const struct sieve_runtime_env *renv,
+ const struct sieve_action *action,
+ struct sieve_result_action *old_action,
+ struct sieve_side_effects_list *new_seffects)
+{
+ struct sieve_side_effects_list *old_seffects = old_action->seffects;
+ int ret;
+ struct sieve_result_side_effect *rsef, *nrsef;
+
+ /* Allow side-effects to merge with existing copy */
+
+ /* Merge existing side effects */
+ rsef = old_seffects != NULL ? old_seffects->first_effect : NULL;
+ while (rsef != NULL) {
+ struct sieve_side_effect *seffect = &rsef->seffect;
+ bool found = FALSE;
+
+ i_assert(seffect->def != NULL);
+ if (seffect->def->merge != NULL) {
+ /* Try to find it among the new */
+ nrsef = (new_seffects != NULL ?
+ new_seffects->first_effect : NULL);
+ while (nrsef != NULL) {
+ struct sieve_side_effect *nseffect = &nrsef->seffect;
+
+ if (nseffect->def == seffect->def) {
+ if (seffect->def->merge(
+ renv, action, seffect, nseffect,
+ &seffect->context) < 0)
+ return -1;
+
+ found = TRUE;
+ break;
+ }
+ nrsef = nrsef->next;
+ }
+
+ /* Not found? */
+ if (!found && seffect->def->merge(
+ renv, action, seffect, NULL,
+ &rsef->seffect.context) < 0)
+ return -1;
+ }
+ rsef = rsef->next;
+ }
+
+ /* Merge new Side effects */
+ nrsef = new_seffects != NULL ? new_seffects->first_effect : NULL;
+ while (nrsef != NULL) {
+ struct sieve_side_effect *nseffect = &nrsef->seffect;
+ bool found = FALSE;
+
+ i_assert(nseffect->def != NULL);
+ if (nseffect->def->merge != NULL) {
+ /* Try to find it among the exising */
+ rsef = (old_seffects != NULL ?
+ old_seffects->first_effect : NULL);
+ while (rsef != NULL) {
+ if (rsef->seffect.def == nseffect->def) {
+ found = TRUE;
+ break;
+ }
+ rsef = rsef->next;
+ }
+
+ /* Not found? */
+ if (!found) {
+ void *new_context = NULL;
+
+ if ((ret = nseffect->def->merge(
+ renv, action, nseffect, nseffect,
+ &new_context)) < 0)
+ return -1;
+
+ if (ret != 0) {
+ if (old_action->seffects == NULL) {
+ old_action->seffects = old_seffects =
+ sieve_side_effects_list_create(renv->result);
+ }
+
+ nseffect->context = new_context;
+
+ /* Add side effect */
+ sieve_side_effects_list_add(old_seffects,
+ nseffect);
+ }
+ }
+ }
+ nrsef = nrsef->next;
+ }
+
+ return 1;
+}
+
+static void
+sieve_result_action_detach(struct sieve_result *result,
+ struct sieve_result_action *raction)
+{
+ if (result->actions_head == raction)
+ result->actions_head = raction->next;
+
+ if (result->actions_tail == raction)
+ result->actions_tail = raction->prev;
+
+ if (raction->next != NULL)
+ raction->next->prev = raction->prev;
+ if (raction->prev != NULL)
+ raction->prev->next = raction->next;
+
+ raction->next = NULL;
+ raction->prev = NULL;
+
+ if (result->action_count > 0)
+ result->action_count--;
+}
+
+static int
+_sieve_result_add_action(const struct sieve_runtime_env *renv,
+ const struct sieve_extension *ext, const char *name,
+ const struct sieve_action_def *act_def,
+ struct sieve_side_effects_list *seffects,
+ void *context, unsigned int instance_limit,
+ bool preserve_mail, bool keep)
+{
+ int ret = 0;
+ unsigned int instance_count = 0;
+ struct sieve_instance *svinst = renv->exec_env->svinst;
+ struct sieve_result *result = renv->result;
+ struct sieve_result_action *raction = NULL, *kaction = NULL;
+ struct sieve_action action;
+
+ i_assert(name != NULL || act_def != NULL);
+ action.name = name;
+ action.def = act_def;
+ action.ext = ext;
+ action.location = sieve_runtime_get_full_command_location(renv);
+ action.context = context;
+ action.exec_seq = result->exec_seq;
+
+ /* First, check for duplicates or conflicts */
+ raction = result->actions_head;
+ while (raction != NULL) {
+ const struct sieve_action *oact = &raction->action;
+ bool oact_new = (oact->exec_seq == result->exec_seq);
+
+ if (keep && raction->action.keep) {
+ /* Duplicate keep */
+ if (oact->def == NULL || !oact_new) {
+ /* Keep action from preceeding execution */
+
+ /* Detach existing keep action */
+ sieve_result_action_detach(result, raction);
+
+ /* Merge existing side-effects with new keep action */
+ if (kaction == NULL)
+ kaction = raction;
+
+ if ((ret = sieve_result_side_effects_merge(
+ renv, &action, kaction, seffects)) <= 0)
+ return ret;
+ } else {
+ /* True duplicate */
+ return sieve_result_side_effects_merge(
+ renv, &action, raction, seffects);
+ }
+ } else if ( act_def != NULL && raction->action.def == act_def ) {
+ instance_count++;
+
+ /* Possible duplicate */
+ if (act_def->check_duplicate != NULL) {
+ if ((ret = act_def->check_duplicate(
+ renv, &action, &raction->action)) < 0)
+ return ret;
+
+ /* Duplicate */
+ if (ret == 1) {
+ if (keep && !oact->keep) {
+ /* New keep has higher precedence than
+ existing duplicate non-keep action.
+ So, take over the result action object
+ and transform it into a keep.
+ */
+ if ((ret = sieve_result_side_effects_merge(
+ renv, &action, raction, seffects)) < 0)
+ return ret;
+
+ if (kaction == NULL) {
+ raction->action.context = NULL;
+ raction->action.location =
+ p_strdup(result->pool, action.location);
+
+ /* Note that existing execution
+ status is retained, making sure
+ that keep is not executed
+ multiple times.
+ */
+ kaction = raction;
+ } else {
+ sieve_result_action_detach(result, raction);
+
+ if ((ret = sieve_result_side_effects_merge(
+ renv, &action, kaction,
+ raction->seffects)) < 0)
+ return ret;
+ }
+ } else {
+ /* Merge side-effects, but don't add new action
+ */
+ return sieve_result_side_effects_merge(
+ renv, &action, raction, seffects);
+ }
+ }
+ }
+ } else {
+ if (act_def != NULL && oact->def != NULL) {
+ /* Check conflict */
+ if (act_def->check_conflict != NULL &&
+ (ret = act_def->check_conflict(
+ renv, &action, &raction->action)) != 0)
+ return ret;
+
+ if (oact_new &&
+ oact->def->check_conflict != NULL &&
+ (ret = oact->def->check_conflict(
+ renv, &raction->action, &action)) != 0)
+ return ret;
+ }
+ }
+ raction = raction->next;
+ }
+
+ if (kaction != NULL) {
+ /* Use existing keep action to define new one */
+ raction = kaction;
+ } else {
+ /* Check policy limit on total number of actions */
+ if (svinst->max_actions > 0 &&
+ result->action_count >= svinst->max_actions)
+ {
+ sieve_runtime_error(
+ renv, action.location,
+ "total number of actions exceeds policy limit "
+ "(%u > %u)",
+ result->action_count+1, svinst->max_actions);
+ return -1;
+ }
+
+ /* Check policy limit on number of this class of actions */
+ if (instance_limit > 0 && instance_count >= instance_limit) {
+ sieve_runtime_error(
+ renv, action.location,
+ "number of %s actions exceeds policy limit "
+ "(%u > %u)",
+ act_def->name, instance_count+1,
+ instance_limit);
+ return -1;
+ }
+
+ /* Create new action object */
+ raction = p_new(result->pool, struct sieve_result_action, 1);
+ raction->seffects = seffects;
+ }
+
+ raction->action.name = (action.name == NULL ?
+ act_def->name :
+ p_strdup(result->pool, action.name));
+ raction->action.context = context;
+ raction->action.def = act_def;
+ raction->action.ext = ext;
+ raction->action.location = p_strdup(result->pool, action.location);
+ raction->action.keep = keep;
+ raction->action.exec_seq = result->exec_seq;
+
+ if (raction->prev == NULL && raction != result->actions_head) {
+ /* Add */
+ if (result->actions_head == NULL) {
+ result->actions_head = raction;
+ result->actions_tail = raction;
+ raction->prev = NULL;
+ raction->next = NULL;
+ } else {
+ result->actions_tail->next = raction;
+ raction->prev = result->actions_tail;
+ result->actions_tail = raction;
+ raction->next = NULL;
+ }
+ result->action_count++;
+
+ /* Apply any implicit side effects */
+ if (hash_table_is_created(result->action_contexts)) {
+ struct sieve_result_action_context *actctx;
+
+ /* Check for implicit side effects to this particular
+ action */
+ actctx = hash_table_lookup(
+ result->action_contexts,
+ (keep ? &act_store : act_def));
+
+ if (actctx != NULL) {
+ struct sieve_result_side_effect *iseff;
+
+ /* Iterate through all implicit side effects and
+ add those that are missing.
+ */
+ iseff = actctx->seffects->first_effect;
+ while (iseff != NULL) {
+ struct sieve_result_side_effect *seff;
+ bool exists = FALSE;
+
+ /* Scan for presence */
+ if (seffects != NULL) {
+ seff = seffects->first_effect;
+ while (seff != NULL) {
+ if (seff->seffect.def == iseff->seffect.def) {
+ exists = TRUE;
+ break;
+ }
+
+ seff = seff->next;
+ }
+ } else {
+ raction->seffects = seffects =
+ sieve_side_effects_list_create(result);
+ }
+
+ /* If not present, add it */
+ if (!exists) {
+ sieve_side_effects_list_add(seffects, &iseff->seffect);
+ }
+
+ iseff = iseff->next;
+ }
+ }
+ }
+ }
+
+ if (preserve_mail) {
+ raction->action.mail = sieve_message_get_mail(renv->msgctx);
+ sieve_message_snapshot(renv->msgctx);
+ } else {
+ raction->action.mail = NULL;
+ }
+
+ sieve_result_init_action_event(result, &raction->action, !keep);
+ return 0;
+}
+
+int sieve_result_add_action(const struct sieve_runtime_env *renv,
+ const struct sieve_extension *ext, const char *name,
+ const struct sieve_action_def *act_def,
+ struct sieve_side_effects_list *seffects,
+ void *context, unsigned int instance_limit,
+ bool preserve_mail)
+{
+ return _sieve_result_add_action(renv, ext, name, act_def, seffects,
+ context, instance_limit, preserve_mail,
+ FALSE);
+}
+
+int sieve_result_add_keep(const struct sieve_runtime_env *renv,
+ struct sieve_side_effects_list *seffects)
+{
+ return _sieve_result_add_action(renv, renv->result->keep_action.ext,
+ "keep", renv->result->keep_action.def,
+ seffects, NULL, 0, TRUE, TRUE);
+}
+
+void sieve_result_set_keep_action(struct sieve_result *result,
+ const struct sieve_extension *ext,
+ const struct sieve_action_def *act_def)
+{
+ result->keep_action.def = act_def;
+ result->keep_action.ext = ext;
+}
+
+void sieve_result_set_failure_action(struct sieve_result *result,
+ const struct sieve_extension *ext,
+ const struct sieve_action_def *act_def)
+{
+ result->failure_action.def = act_def;
+ result->failure_action.ext = ext;
+}
+
+/*
+ * Result printing
+ */
+
+void sieve_result_vprintf(const struct sieve_result_print_env *penv,
+ const char *fmt, va_list args)
+{
+ string_t *outbuf = t_str_new(128);
+
+ str_vprintfa(outbuf, fmt, args);
+
+ o_stream_nsend(penv->stream, str_data(outbuf), str_len(outbuf));
+}
+
+void sieve_result_printf(const struct sieve_result_print_env *penv,
+ const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ sieve_result_vprintf(penv, fmt, args);
+ va_end(args);
+}
+
+void sieve_result_action_printf(const struct sieve_result_print_env *penv,
+ const char *fmt, ...)
+{
+ string_t *outbuf = t_str_new(128);
+ va_list args;
+
+ va_start(args, fmt);
+ str_append(outbuf, " * ");
+ str_vprintfa(outbuf, fmt, args);
+ str_append_c(outbuf, '\n');
+ va_end(args);
+
+ o_stream_nsend(penv->stream, str_data(outbuf), str_len(outbuf));
+}
+
+void sieve_result_seffect_printf(const struct sieve_result_print_env *penv,
+ const char *fmt, ...)
+{
+ string_t *outbuf = t_str_new(128);
+ va_list args;
+
+ va_start(args, fmt);
+ str_append(outbuf, " + ");
+ str_vprintfa(outbuf, fmt, args);
+ str_append_c(outbuf, '\n');
+ va_end(args);
+
+ o_stream_nsend(penv->stream, str_data(outbuf), str_len(outbuf));
+}
+
+static void
+sieve_result_print_side_effects(struct sieve_result_print_env *rpenv,
+ const struct sieve_action *action,
+ struct sieve_side_effects_list *slist,
+ bool *implicit_keep)
+{
+ struct sieve_result_side_effect *rsef;
+
+ /* Print side effects */
+ rsef = (slist != NULL ? slist->first_effect : NULL);
+ while (rsef != NULL) {
+ const struct sieve_side_effect *sef = &rsef->seffect;
+
+ i_assert(sef->def != NULL);
+
+ if (sef->def->print != NULL) {
+ sef->def->print(sef, action, rpenv,
+ implicit_keep);
+ }
+ rsef = rsef->next;
+ }
+}
+
+static void
+sieve_result_print_implicit_side_effects(struct sieve_result_print_env *rpenv)
+{
+ struct sieve_result *result = rpenv->result;
+ bool dummy = TRUE;
+
+ /* Print any implicit side effects if applicable */
+ if (hash_table_is_created(result->action_contexts)) {
+ struct sieve_result_action_context *actctx;
+
+ /* Check for implicit side effects to keep action */
+ actctx = hash_table_lookup(rpenv->result->action_contexts,
+ &act_store);
+
+ if (actctx != NULL && actctx->seffects != NULL) {
+ sieve_result_print_side_effects(
+ rpenv, &result->keep_action,
+ actctx->seffects, &dummy);
+ }
+ }
+}
+
+bool sieve_result_print(struct sieve_result *result,
+ const struct sieve_script_env *senv,
+ struct ostream *stream, bool *keep)
+{
+ struct sieve_action act_keep = result->keep_action;
+ struct sieve_result_print_env penv;
+ bool implicit_keep = TRUE, printed_any = FALSE;
+ struct sieve_result_action *rac;
+
+ if (keep != NULL)
+ *keep = FALSE;
+
+ /* Prepare environment */
+
+ penv.result = result;
+ penv.stream = stream;
+ penv.scriptenv = senv;
+
+ sieve_result_printf(&penv, "\nPerformed actions:\n\n");
+
+ rac = result->actions_head;
+ while (rac != NULL) {
+ bool impl_keep = TRUE;
+ const struct sieve_action *act = &rac->action;
+
+ if (act->exec_seq < result->exec_seq) {
+ rac = rac->next;
+ continue;
+ }
+
+ if (rac->action.keep && keep != NULL)
+ *keep = TRUE;
+
+ if (act->def != NULL) {
+ if (act->def->print != NULL)
+ act->def->print(act, &penv, &impl_keep);
+ else {
+ sieve_result_action_printf(
+ &penv, "%s", act->def->name);
+ }
+ } else {
+ if (act->keep) {
+ sieve_result_action_printf(&penv, "keep");
+ impl_keep = FALSE;
+ } else {
+ sieve_result_action_printf(&penv, "[NULL]");
+ }
+ }
+ printed_any = TRUE;
+
+ /* Print side effects */
+ sieve_result_print_side_effects(
+ &penv, &rac->action, rac->seffects, &impl_keep);
+
+ implicit_keep = implicit_keep && impl_keep;
+
+ rac = rac->next;
+ }
+ if (!printed_any)
+ sieve_result_printf(&penv, " (none)\n");
+
+ if (implicit_keep && keep != NULL)
+ *keep = TRUE;
+
+ sieve_result_printf(&penv, "\nImplicit keep:\n\n");
+
+ if (implicit_keep) {
+ bool dummy = TRUE;
+
+ if (act_keep.def == NULL) {
+ sieve_result_action_printf(&penv, "keep");
+
+ sieve_result_print_implicit_side_effects(&penv);
+ } else {
+ /* Scan for execution of keep-equal actions */
+ rac = result->actions_head;
+ while (act_keep.def != NULL && rac != NULL) {
+ if (rac->action.def == act_keep.def &&
+ act_keep.def->equals != NULL &&
+ act_keep.def->equals(senv, NULL, &rac->action) &&
+ sieve_action_is_executed(&rac->action,
+ result))
+ act_keep.def = NULL;
+
+ rac = rac->next;
+ }
+
+ if (act_keep.def == NULL) {
+ sieve_result_printf(&penv,
+ " (none; keep or equivalent action executed earlier)\n");
+ } else {
+ act_keep.def->print(&act_keep, &penv, &dummy);
+
+ sieve_result_print_implicit_side_effects(&penv);
+ }
+ }
+ } else {
+ sieve_result_printf(&penv, " (none)\n");
+ }
+
+ sieve_result_printf(&penv, "\n");
+
+ return TRUE;
+}
+
+/*
+ * Result execution
+ */
+
+struct sieve_side_effect_execution {
+ struct sieve_result_side_effect *seffect;
+
+ void *tr_context;
+
+ struct sieve_side_effect_execution *prev, *next;
+};
+
+struct sieve_action_execution {
+ struct sieve_result_action *action;
+ unsigned int exec_seq;
+ struct sieve_action_execution *prev, *next;
+
+ struct sieve_side_effect_execution *seffects_head, *seffects_tail;
+
+ struct sieve_error_handler *ehandler;
+ void *tr_context;
+ enum sieve_action_execution_state state;
+ int status;
+
+ bool commit:1;
+};
+
+struct sieve_result_execution {
+ pool_t pool;
+ struct sieve_action_exec_env action_env;
+ struct sieve_error_handler *ehandler;
+ struct event *event;
+
+ int status;
+
+ struct sieve_action_execution *actions_head, *actions_tail;
+
+ struct sieve_result_action keep_action;
+ struct sieve_action_execution keep;
+ struct sieve_action_execution *keep_equiv_action;
+ int keep_status;
+
+ bool keep_success:1;
+ bool keep_explicit:1;
+ bool keep_implicit:1;
+ bool keep_finalizing:1;
+ bool seen_delivery:1;
+ bool executed:1;
+ bool executed_delivery:1;
+ bool committed:1;
+};
+
+void sieve_result_mark_executed(struct sieve_result *result)
+{
+ result->exec_seq++;
+}
+
+/* Side effect */
+
+static int
+sieve_result_side_effect_pre_execute(struct sieve_result_execution *rexec,
+ struct sieve_action_execution *aexec,
+ struct sieve_side_effect_execution *seexec)
+{
+ struct sieve_result_side_effect *rsef = seexec->seffect;
+ struct sieve_side_effect *sef = &rsef->seffect;
+
+ i_assert(sef->def != NULL);
+ if (sef->def->pre_execute == NULL)
+ return SIEVE_EXEC_OK;
+
+ return sef->def->pre_execute(sef, &rexec->action_env,
+ aexec->tr_context, &seexec->tr_context);
+}
+
+static int
+sieve_result_side_effect_post_execute(
+ struct sieve_result_execution *rexec,
+ struct sieve_action_execution *aexec,
+ struct sieve_side_effect_execution *seexec, bool *impl_keep)
+{
+ struct sieve_result_side_effect *rsef = seexec->seffect;
+ struct sieve_side_effect *sef = &rsef->seffect;
+
+ i_assert(sef->def != NULL);
+ if (sef->def->post_execute == NULL)
+ return SIEVE_EXEC_OK;
+
+ return sef->def->post_execute(sef, &rexec->action_env,
+ aexec->tr_context, seexec->tr_context,
+ impl_keep);
+}
+
+static void
+sieve_result_side_effect_post_commit(struct sieve_result_execution *rexec,
+ struct sieve_action_execution *aexec,
+ struct sieve_side_effect_execution *seexec,
+ int commit_status)
+{
+ struct sieve_result_side_effect *rsef = seexec->seffect;
+ struct sieve_side_effect *sef = &rsef->seffect;
+
+ i_assert(sef->def != NULL);
+ if (sef->def->post_commit == NULL)
+ return;
+
+ sef->def->post_commit(sef, &rexec->action_env,
+ aexec->tr_context, seexec->tr_context,
+ commit_status);
+}
+
+static void
+sieve_result_side_effect_rollback(struct sieve_result_execution *rexec,
+ struct sieve_action_execution *aexec,
+ struct sieve_side_effect_execution *seexec)
+{
+ struct sieve_result_side_effect *rsef = seexec->seffect;
+ struct sieve_side_effect *sef = &rsef->seffect;
+
+ i_assert(sef->def != NULL);
+ if (sef->def->rollback == NULL)
+ return;
+
+ sef->def->rollback(sef, &rexec->action_env,
+ aexec->tr_context, seexec->tr_context,
+ (aexec->status == SIEVE_EXEC_OK));
+}
+
+static void
+sieve_action_execution_add_side_effect(struct sieve_result_execution *rexec,
+ struct sieve_action_execution *aexec,
+ struct sieve_result_side_effect *seffect)
+{
+ struct sieve_side_effect_execution *seexec;
+
+ seexec = aexec->seffects_head;
+ while (seexec != NULL) {
+ if (seexec->seffect == seffect)
+ return;
+ seexec = seexec->next;
+ }
+
+ seexec = p_new(rexec->pool, struct sieve_side_effect_execution, 1);
+ seexec->seffect = seffect;
+
+ DLLIST2_APPEND(&aexec->seffects_head, &aexec->seffects_tail, seexec);
+}
+
+static void
+sieve_action_execution_add_side_effects(struct sieve_result_execution *rexec,
+ struct sieve_action_execution *aexec,
+ struct sieve_result_action *rac)
+{
+ struct sieve_result_side_effect *rsef;
+
+ rsef = (rac->seffects == NULL ? NULL : rac->seffects->first_effect);
+ while (rsef != NULL) {
+ sieve_action_execution_add_side_effect(rexec, aexec, rsef);
+ rsef = rsef->next;
+ }
+}
+
+/* Action */
+
+static void
+sieve_action_execution_pre(struct sieve_result_execution *rexec,
+ struct sieve_action_execution *aexec)
+{
+ if (aexec->ehandler == NULL)
+ aexec->ehandler = rexec->ehandler;
+ rexec->action_env.action = &aexec->action->action;
+ rexec->action_env.event = aexec->action->action.event;
+ rexec->action_env.ehandler = aexec->ehandler;
+}
+
+static void
+sieve_action_execution_post(struct sieve_result_execution *rexec)
+{
+ rexec->action_env.action = NULL;
+ rexec->action_env.event = rexec->action_env.result->event;
+ rexec->action_env.ehandler = NULL;
+}
+
+static int
+sieve_result_action_start(struct sieve_result_execution *rexec,
+ struct sieve_action_execution *aexec)
+{
+ struct sieve_result_action *rac = aexec->action;
+ struct sieve_action *act = &rac->action;
+ int status = SIEVE_EXEC_OK;
+
+ /* Skip actions that are already started. */
+ if (aexec->state >= SIEVE_ACTION_EXECUTION_STATE_STARTED)
+ return status;
+ aexec->state = SIEVE_ACTION_EXECUTION_STATE_STARTED;
+ aexec->status = status;
+
+ /* Skip non-actions (inactive keep). */
+ if (act->def == NULL)
+ return status;
+
+ if (act->def->start != NULL) {
+ sieve_action_execution_pre(rexec, aexec);
+ status = act->def->start(&rexec->action_env,
+ &aexec->tr_context);
+ aexec->status = status;
+ sieve_action_execution_post(rexec);
+ }
+ return status;
+}
+
+static int
+sieve_result_action_execute(struct sieve_result_execution *rexec,
+ struct sieve_action_execution *aexec,
+ int start_status)
+{
+ struct sieve_result_action *rac = aexec->action;
+ struct sieve_action *act = &rac->action;
+ struct sieve_side_effect_execution *seexec;
+ int status = start_status;
+ bool impl_keep = TRUE;
+
+ /* Skip actions that are already executed. */
+ if (aexec->state >= SIEVE_ACTION_EXECUTION_STATE_EXECUTED)
+ return status;
+ aexec->state = SIEVE_ACTION_EXECUTION_STATE_EXECUTED;
+
+ /* Record explicit keep when it is not the final implicit keep */
+ if (act->keep && aexec != &rexec->keep)
+ rexec->keep_explicit = TRUE;
+
+ /* Skip non-actions (inactive keep) */
+ if (act->def == NULL) {
+ i_assert(aexec != &rexec->keep);
+ if (act->keep)
+ e_debug(rexec->event, "Executed explicit keep");
+ return status;
+ }
+
+ /* Don't execute if others already failed */
+ if (status != SIEVE_EXEC_OK)
+ return status;
+
+ if (aexec == &rexec->keep)
+ e_debug(rexec->event, "Executing implicit keep action");
+ else {
+ e_debug(rexec->event, "Executing %s action%s",
+ sieve_action_name(act),
+ (act->keep ? " (explicit keep)" : ""));
+ }
+
+ sieve_action_execution_pre(rexec, aexec);
+
+ /* Execute pre-execute event of side effects */
+ seexec = aexec->seffects_head;
+ while (status == SIEVE_EXEC_OK && seexec != NULL) {
+ status = sieve_result_side_effect_pre_execute(
+ rexec, aexec, seexec);
+ seexec = seexec->next;
+ }
+
+ /* Execute the action itself */
+ if (status == SIEVE_EXEC_OK && act->def != NULL &&
+ act->def->execute != NULL) {
+ status = act->def->execute(&rexec->action_env,
+ aexec->tr_context,
+ &impl_keep);
+ if (status == SIEVE_EXEC_OK)
+ rexec->executed = TRUE;
+ }
+
+ /* Execute post-execute event of side effects */
+ seexec = aexec->seffects_head;
+ while (status == SIEVE_EXEC_OK && seexec != NULL) {
+ status = sieve_result_side_effect_post_execute(
+ rexec, aexec, seexec, &impl_keep);
+ seexec = seexec->next;
+ }
+
+ if (aexec == &rexec->keep) {
+ e_debug(rexec->event,
+ "Finished executing implicit keep action (status=%s)",
+ sieve_execution_exitcode_to_str(status));
+ } else {
+ e_debug(rexec->event, "Finished executing %s action "
+ "(status=%s, keep=%s)", sieve_action_name(act),
+ sieve_execution_exitcode_to_str(status),
+ (act->keep ? "explicit" :
+ (impl_keep && rexec->keep_implicit ?
+ "implicit" : "canceled")));
+ }
+
+ if (status == SIEVE_EXEC_OK &&
+ (act->def->flags & SIEVE_ACTFLAG_TRIES_DELIVER) != 0)
+ rexec->seen_delivery = TRUE;
+
+ /* Update implicit keep status (but only when we're not running the
+ implicit keep right now). */
+ if (aexec != &rexec->keep)
+ rexec->keep_implicit = rexec->keep_implicit && impl_keep;
+
+ sieve_action_execution_post(rexec);
+
+ aexec->status = status;
+ return status;
+}
+
+static int
+sieve_result_action_commit(struct sieve_result_execution *rexec,
+ struct sieve_action_execution *aexec)
+{
+ struct sieve_result_action *rac = aexec->action;
+ struct sieve_action *act = &rac->action;
+ struct sieve_side_effect_execution *seexec;
+ int cstatus = SIEVE_EXEC_OK;
+
+ if (aexec == &rexec->keep) {
+ e_debug(rexec->event, "Commit implicit keep action");
+ } else {
+ e_debug(rexec->event, "Commit %s action%s",
+ sieve_action_name(act),
+ (act->keep ? " (explicit keep)" : ""));
+ }
+
+ sieve_action_execution_pre(rexec, aexec);
+
+ if (act->def->commit != NULL) {
+ cstatus = act->def->commit(&rexec->action_env,
+ aexec->tr_context);
+ if (cstatus == SIEVE_EXEC_OK)
+ rexec->committed = TRUE;
+ }
+
+ /* Execute post_commit event of side effects */
+ seexec = aexec->seffects_head;
+ while (seexec != NULL) {
+ sieve_result_side_effect_post_commit(
+ rexec, aexec, seexec, cstatus);
+ seexec = seexec->next;
+ }
+
+ sieve_action_execution_post(rexec);
+
+ return cstatus;
+}
+
+static void
+sieve_result_action_rollback(struct sieve_result_execution *rexec,
+ struct sieve_action_execution *aexec)
+{
+ struct sieve_result_action *rac = aexec->action;
+ struct sieve_action *act = &rac->action;
+ struct sieve_side_effect_execution *seexec;
+
+ if (aexec == &rexec->keep) {
+ e_debug(rexec->event, "Roll back implicit keep action");
+ } else {
+ e_debug(rexec->event, "Roll back %s action%s",
+ sieve_action_name(act),
+ (act->keep ? " (explicit keep)" : ""));
+ }
+
+ sieve_action_execution_pre(rexec, aexec);
+
+ if (act->def->rollback != NULL) {
+ act->def->rollback(&rexec->action_env, aexec->tr_context,
+ (aexec->status == SIEVE_EXEC_OK));
+ }
+
+ /* Rollback side effects */
+ seexec = aexec->seffects_head;
+ while (seexec != NULL) {
+ sieve_result_side_effect_rollback(rexec, aexec, seexec);
+ seexec = seexec->next;
+ }
+
+ sieve_action_execution_post(rexec);
+}
+
+static int
+sieve_result_action_commit_or_rollback(struct sieve_result_execution *rexec,
+ struct sieve_action_execution *aexec,
+ int status, int *commit_status)
+{
+ struct sieve_result_action *rac = aexec->action;
+ struct sieve_action *act = &rac->action;
+
+ /* Skip actions that are already finalized. */
+ if (aexec->state >= SIEVE_ACTION_EXECUTION_STATE_FINALIZED)
+ return status;
+ aexec->state = SIEVE_ACTION_EXECUTION_STATE_FINALIZED;
+
+ if (aexec == &rexec->keep) {
+ e_debug(rexec->event, "Finalize implicit keep action"
+ "(status=%s, action_status=%s, commit_status=%s)",
+ sieve_execution_exitcode_to_str(status),
+ sieve_execution_exitcode_to_str(aexec->status),
+ sieve_execution_exitcode_to_str(*commit_status));
+ } else {
+ e_debug(rexec->event, "Finalize %s action "
+ "(%sstatus=%s, action_status=%s, commit_status=%s, "
+ "pre-commit=%s)",
+ sieve_action_name(act),
+ (act->keep ? "explicit keep, " : ""),
+ sieve_execution_exitcode_to_str(status),
+ sieve_execution_exitcode_to_str(aexec->status),
+ sieve_execution_exitcode_to_str(*commit_status),
+ (aexec->commit ? "yes" : "no"));
+ }
+
+ /* Skip non-actions (inactive keep) */
+ if (act->def == NULL)
+ return status;
+
+ if (aexec->status == SIEVE_EXEC_OK &&
+ (status == SIEVE_EXEC_OK ||
+ (aexec->commit && *commit_status == SIEVE_EXEC_OK))) {
+ int cstatus = SIEVE_EXEC_OK;
+
+ cstatus = sieve_result_action_commit(rexec, aexec);
+ if (cstatus != SIEVE_EXEC_OK) {
+ /* This is bad; try to salvage as much as possible */
+ if (*commit_status == SIEVE_EXEC_OK) {
+ *commit_status = cstatus;
+ if (!rexec->committed) {
+ /* We haven't executed anything yet;
+ continue as rollback */
+ status = cstatus;
+ }
+ }
+ }
+ } else {
+ sieve_result_action_rollback(rexec, aexec);
+ }
+
+ if (act->keep) {
+ if (status == SIEVE_EXEC_FAILURE)
+ status = SIEVE_EXEC_KEEP_FAILED;
+ if (*commit_status == SIEVE_EXEC_FAILURE)
+ *commit_status = SIEVE_EXEC_KEEP_FAILED;
+ }
+
+ return status;
+}
+
+static void
+sieve_result_action_finish(struct sieve_result_execution *rexec,
+ struct sieve_action_execution *aexec, int status)
+{
+ struct sieve_result_action *rac = aexec->action;
+ struct sieve_action *act = &rac->action;
+
+ /* Skip non-actions (inactive keep) */
+ if (act->def == NULL)
+ return;
+
+ if (aexec == &rexec->keep) {
+ e_debug(rexec->event, "Finish implicit keep action");
+ } else {
+ e_debug(rexec->event, "Finish %s action%s",
+ sieve_action_name(act),
+ (act->keep ? " (explicit keep)" : ""));
+ }
+
+ if (act->def->finish != NULL) {
+ sieve_action_execution_pre(rexec, aexec);
+ act->def->finish(&rexec->action_env, aexec->tr_context, status);
+ sieve_action_execution_post(rexec);
+ }
+}
+
+static void
+sieve_result_action_abort(struct sieve_result_execution *rexec,
+ struct sieve_action_execution *aexec)
+{
+ if (aexec->state > SIEVE_ACTION_EXECUTION_STATE_INIT &&
+ aexec->state < SIEVE_ACTION_EXECUTION_STATE_FINALIZED)
+ sieve_result_action_rollback(rexec, aexec);
+ DLLIST2_REMOVE(&rexec->actions_head, &rexec->actions_tail, aexec);
+}
+
+static void
+sieve_action_execution_update(struct sieve_result_execution *rexec,
+ struct sieve_action_execution *aexec)
+{
+ const struct sieve_action_exec_env *aenv = &rexec->action_env;
+ struct sieve_result *result = aenv->result;
+ struct sieve_result_action *rac;
+
+ rac = result->actions_head;
+ while (rac != NULL) {
+ if (aexec->action == rac)
+ break;
+ rac = rac->next;
+ }
+
+ if (rac == NULL) {
+ /* Action was removed; abort it. */
+ sieve_result_action_abort(rexec, aexec);
+ return;
+ }
+
+ if (aexec->exec_seq != rac->action.exec_seq) {
+ i_assert(rac->action.keep);
+
+ /* Recycled keep */
+ aexec->exec_seq = rac->action.exec_seq;
+ aexec->state = SIEVE_ACTION_EXECUTION_STATE_INIT;
+ }
+
+ sieve_action_execution_add_side_effects(rexec, aexec, rac);
+}
+
+static void
+sieve_result_execution_add_action(struct sieve_result_execution *rexec,
+ struct sieve_result_action *rac)
+{
+ struct sieve_action_execution *aexec;
+
+ aexec = rexec->actions_head;
+ while (aexec != NULL) {
+ if (aexec->action == rac)
+ return;
+ aexec = aexec->next;
+ }
+
+ aexec = p_new(rexec->pool, struct sieve_action_execution, 1);
+ aexec->action = rac;
+ aexec->exec_seq = rac->action.exec_seq;
+ aexec->ehandler = rexec->ehandler;
+
+ DLLIST2_APPEND(&rexec->actions_head, &rexec->actions_tail, aexec);
+
+ sieve_action_execution_add_side_effects(rexec, aexec, rac);
+}
+
+/* Result */
+
+struct sieve_result_execution *
+sieve_result_execution_create(struct sieve_result *result, pool_t pool)
+{
+ struct sieve_result_execution *rexec;
+
+ pool_ref(pool);
+ rexec = p_new(pool, struct sieve_result_execution, 1);
+ rexec->pool = pool;
+ rexec->event = result->event;
+ rexec->action_env.result = result;
+ rexec->action_env.event = result->event;
+ rexec->action_env.exec_env = result->exec_env;
+ rexec->action_env.msgctx = result->msgctx;
+ rexec->status = SIEVE_EXEC_OK;
+ rexec->keep_success = TRUE;
+ rexec->keep_status = SIEVE_EXEC_OK;
+ rexec->keep_explicit = FALSE;
+ rexec->keep_implicit = TRUE;
+
+ sieve_result_ref(result);
+ result->exec = rexec;
+
+ return rexec;
+}
+
+void sieve_result_execution_destroy(struct sieve_result_execution **_rexec)
+{
+ struct sieve_result_execution *rexec = *_rexec;
+
+ *_rexec = NULL;
+
+ if (rexec == NULL)
+ return;
+
+ rexec->action_env.result->exec = NULL;
+ sieve_result_unref(&rexec->action_env.result);
+ pool_unref(&rexec->pool);
+}
+
+static void
+sieve_result_implicit_keep_execute(struct sieve_result_execution *rexec)
+{
+ const struct sieve_action_exec_env *aenv = &rexec->action_env;
+ struct sieve_result *result = aenv->result;
+ const struct sieve_execute_env *eenv = aenv->exec_env;
+ struct sieve_action_execution *aexec;
+ int status = SIEVE_EXEC_OK;
+ struct sieve_action_execution *aexec_keep = &rexec->keep;
+ struct sieve_result_action *ract_keep = &rexec->keep_action;
+ struct sieve_action *act_keep = &ract_keep->action;
+ bool success = FALSE;
+
+ switch (rexec->status) {
+ case SIEVE_EXEC_OK:
+ success = TRUE;
+ break;
+ case SIEVE_EXEC_TEMP_FAILURE:
+ case SIEVE_EXEC_RESOURCE_LIMIT:
+ if (rexec->committed) {
+ e_debug(rexec->event,
+ "Temporary failure occurred (status=%s), "
+ "but other actions were already committed: "
+ "execute failure implicit keep",
+ sieve_execution_exitcode_to_str(rexec->status));
+ break;
+ }
+ if (rexec->keep_finalizing)
+ break;
+
+ e_debug(rexec->event,
+ "Skip implicit keep for temporary failure "
+ "(state=execute, status=%s)",
+ sieve_execution_exitcode_to_str(rexec->status));
+ return;
+ default:
+ break;
+ }
+
+ if (rexec->keep_equiv_action != NULL) {
+ e_debug(rexec->event, "No implicit keep needed "
+ "(equivalent action already executed)");
+ return;
+ }
+
+ rexec->keep.action = &rexec->keep_action;
+ rexec->keep.ehandler = rexec->ehandler;
+ rexec->keep_success = success;
+ rexec->keep_status = status;
+
+ if ((eenv->flags & SIEVE_EXECUTE_FLAG_DEFER_KEEP) != 0) {
+ e_debug(rexec->event, "Execution of implicit keep is deferred");
+ return;
+ }
+
+ if (!success)
+ *act_keep = result->failure_action;
+ else
+ *act_keep = result->keep_action;
+ act_keep->name = "keep";
+ act_keep->mail = NULL;
+ act_keep->keep = TRUE;
+
+ /* If keep is a non-action, return right away */
+ if (act_keep->def == NULL) {
+ e_debug(rexec->event, "Keep is not defined yet");
+ return;
+ }
+
+ /* Scan for execution of keep-equal actions */
+ aexec = rexec->actions_head;
+ while (aexec != NULL) {
+ struct sieve_result_action *rac = aexec->action;
+
+ if (rac->action.def == act_keep->def &&
+ act_keep->def->equals != NULL &&
+ act_keep->def->equals(eenv->scriptenv, NULL,
+ &rac->action) &&
+ aexec->state >= SIEVE_ACTION_EXECUTION_STATE_EXECUTED) {
+ e_debug(rexec->event, "No implicit keep needed "
+ "(equivalent %s action already executed)",
+ sieve_action_name(&rac->action));
+ rexec->keep_equiv_action = aexec;
+ return;
+ }
+
+ aexec = aexec->next;
+ }
+
+ /* Scan for deferred keep */
+ aexec = rexec->actions_tail;
+ while (aexec != NULL) {
+ struct sieve_result_action *rac = aexec->action;
+
+ if (aexec->state < SIEVE_ACTION_EXECUTION_STATE_EXECUTED) {
+ aexec = NULL;
+ break;
+ }
+ if (rac->action.keep && rac->action.def == NULL)
+ break;
+ aexec = aexec->prev;
+ }
+
+ if (aexec == NULL) {
+ if (success)
+ act_keep->mail = sieve_message_get_mail(aenv->msgctx);
+ } else {
+ e_debug(rexec->event, "Found deferred keep action");
+
+ if (success) {
+ act_keep->location = aexec->action->action.location;
+ act_keep->mail = aexec->action->action.mail;
+ ract_keep->seffects = aexec->action->seffects;
+ }
+ aexec->state = SIEVE_ACTION_EXECUTION_STATE_FINALIZED;
+ }
+
+ if (ract_keep->seffects == NULL) {
+ /* Apply any implicit side effects if applicable */
+ if (success && hash_table_is_created(result->action_contexts)) {
+ struct sieve_result_action_context *actctx;
+
+ /* Check for implicit side effects to keep action */
+ actctx = hash_table_lookup(result->action_contexts,
+ act_keep->def);
+
+ if (actctx != NULL)
+ ract_keep->seffects = actctx->seffects;
+ }
+ }
+
+ e_debug(rexec->event, "Execute implicit keep (status=%s)",
+ sieve_execution_exitcode_to_str(rexec->status));
+
+ /* Initialize side effects */
+ sieve_action_execution_add_side_effects(rexec, aexec_keep, ract_keep);
+
+ /* Initialize keep action event */
+ sieve_result_init_action_event(result, act_keep, FALSE);
+
+ /* Start keep action */
+ status = sieve_result_action_start(rexec, aexec_keep);
+
+ /* Execute keep action */
+ if (status == SIEVE_EXEC_OK)
+ status = sieve_result_action_execute(rexec, aexec_keep, status);
+ if (status == SIEVE_EXEC_OK)
+ aexec_keep->commit = TRUE;
+
+ rexec->executed_delivery = rexec->seen_delivery;
+ rexec->keep_status = status;
+ sieve_action_execution_post(rexec);
+}
+
+static int
+sieve_result_implicit_keep_finalize(struct sieve_result_execution *rexec)
+{
+ const struct sieve_action_exec_env *aenv = &rexec->action_env;
+ const struct sieve_execute_env *eenv = aenv->exec_env;
+ struct sieve_action_execution *aexec_keep = &rexec->keep;
+ struct sieve_result_action *ract_keep = &rexec->keep_action;
+ struct sieve_action *act_keep = &ract_keep->action;
+ int commit_status = SIEVE_EXEC_OK;
+ bool success = FALSE, temp_failure = FALSE;
+
+ switch (rexec->status) {
+ case SIEVE_EXEC_OK:
+ success = TRUE;
+ break;
+ case SIEVE_EXEC_TEMP_FAILURE:
+ case SIEVE_EXEC_RESOURCE_LIMIT:
+ if (rexec->committed) {
+ e_debug(rexec->event,
+ "Temporary failure occurred (status=%s), "
+ "but other actions were already committed: "
+ "commit failure implicit keep",
+ sieve_execution_exitcode_to_str(rexec->status));
+ break;
+ }
+
+ if (aexec_keep->state !=
+ SIEVE_ACTION_EXECUTION_STATE_EXECUTED) {
+ e_debug(rexec->event,
+ "Skip implicit keep for temporary failure "
+ "(state=commit, status=%s)",
+ sieve_execution_exitcode_to_str(rexec->status));
+ return rexec->status;
+ }
+ /* Roll back for temporary failure when no other action
+ is committed. */
+ commit_status = rexec->status;
+ temp_failure = TRUE;
+ break;
+ default:
+ break;
+ }
+
+ if ((eenv->flags & SIEVE_EXECUTE_FLAG_DEFER_KEEP) != 0) {
+ e_debug(rexec->event, "Execution of implicit keep is deferred");
+ return rexec->keep_status;
+ }
+
+ rexec->keep_finalizing = TRUE;
+
+ /* Start keep if necessary */
+ if (temp_failure) {
+ rexec->keep_status = rexec->status;
+ } else if (act_keep->def == NULL ||
+ aexec_keep->state != SIEVE_ACTION_EXECUTION_STATE_EXECUTED) {
+ sieve_result_implicit_keep_execute(rexec);
+ /* Switch to failure keep if necessary. */
+ } else if (rexec->keep_success && !success){
+ e_debug(rexec->event, "Switch to failure implicit keep");
+
+ /* Failed transaction, rollback success keep action. */
+ sieve_result_action_rollback(rexec, aexec_keep);
+
+ event_unref(&act_keep->event);
+ i_zero(aexec_keep);
+
+ /* Start failure keep action. */
+ sieve_result_implicit_keep_execute(rexec);
+ }
+ if (act_keep->def == NULL)
+ return rexec->keep_status;
+
+ if (rexec->keep_equiv_action != NULL) {
+ struct sieve_action_execution *ke_aexec =
+ rexec->keep_equiv_action;
+
+ i_assert(ke_aexec->state >=
+ SIEVE_ACTION_EXECUTION_STATE_FINALIZED);
+
+ e_debug(rexec->event, "No implicit keep needed "
+ "(equivalent %s action already finalized)",
+ sieve_action_name(&ke_aexec->action->action));
+ return ke_aexec->status;
+ }
+
+ e_debug(rexec->event, "Finalize implicit keep (status=%s)",
+ sieve_execution_exitcode_to_str(rexec->status));
+
+ i_assert(aexec_keep->state == SIEVE_ACTION_EXECUTION_STATE_EXECUTED);
+
+ /* Finalize keep action */
+ rexec->keep_status = sieve_result_action_commit_or_rollback(
+ rexec, aexec_keep, rexec->keep_status, &commit_status);
+
+ /* Finish keep action */
+ sieve_result_action_finish(rexec, aexec_keep,
+ rexec->keep_status);
+
+ sieve_action_execution_post(rexec);
+ event_unref(&act_keep->event);
+
+ if (rexec->keep_status == SIEVE_EXEC_FAILURE)
+ rexec->keep_status = SIEVE_EXEC_KEEP_FAILED;
+ return rexec->keep_status;
+}
+
+bool sieve_result_executed(struct sieve_result_execution *rexec)
+{
+ return rexec->executed;
+}
+
+bool sieve_result_committed(struct sieve_result_execution *rexec)
+{
+ return rexec->committed;
+}
+
+bool sieve_result_executed_delivery(struct sieve_result_execution *rexec)
+{
+ return rexec->executed_delivery;
+}
+
+static int sieve_result_transaction_start(struct sieve_result_execution *rexec)
+{
+ struct sieve_action_execution *aexec;
+ int status = SIEVE_EXEC_OK;
+
+ e_debug(rexec->event, "Starting execution of actions");
+
+ aexec = rexec->actions_head;
+ while (status == SIEVE_EXEC_OK && aexec != NULL) {
+ status = sieve_result_action_start(rexec, aexec);
+ aexec = aexec->next;
+ }
+ sieve_action_execution_post(rexec);
+
+ return status;
+}
+
+static int
+sieve_result_transaction_execute(struct sieve_result_execution *rexec,
+ int start_status)
+{
+ struct sieve_action_execution *aexec;
+ int status = SIEVE_EXEC_OK;
+
+ e_debug(rexec->event, "Executing actions");
+
+ rexec->seen_delivery = FALSE;
+ aexec = rexec->actions_head;
+ while (status == SIEVE_EXEC_OK && aexec != NULL) {
+ status = sieve_result_action_execute(rexec, aexec,
+ start_status);
+ aexec = aexec->next;
+ }
+ sieve_action_execution_post(rexec);
+
+ if (status == SIEVE_EXEC_OK) {
+ /* Since this execution series is successful so far, mark all
+ actions in it to be committed. */
+ aexec = rexec->actions_head;
+ while (aexec != NULL) {
+ aexec->commit = TRUE;
+ aexec = aexec->next;
+ }
+
+ rexec->executed_delivery =
+ rexec->executed_delivery || rexec->seen_delivery;
+ }
+
+ e_debug(rexec->event, "Finished executing actions "
+ "(status=%s, keep=%s, executed=%s)",
+ sieve_execution_exitcode_to_str(status),
+ (rexec->keep_explicit ? "explicit" :
+ (rexec->keep_implicit ? "implicit" : "none")),
+ (rexec->executed ? "yes" : "no"));
+ return status;
+}
+
+static int
+sieve_result_transaction_commit_or_rollback(
+ struct sieve_result_execution *rexec, int status)
+{
+ struct sieve_action_execution *aexec;
+ int commit_status = SIEVE_EXEC_OK;
+
+ switch (status) {
+ case SIEVE_EXEC_TEMP_FAILURE:
+ /* Roll back all actions */
+ commit_status = status;
+ break;
+ default:
+ break;
+ }
+
+ e_debug(rexec->event, "Finalizing actions");
+
+ /* First commit/rollback all storage actions */
+ aexec = rexec->actions_head;
+ while (aexec != NULL) {
+ struct sieve_result_action *rac = aexec->action;
+ struct sieve_action *act = &rac->action;
+
+ if (act->def == NULL ||
+ (act->def->flags & SIEVE_ACTFLAG_MAIL_STORAGE) == 0) {
+ aexec = aexec->next;
+ continue;
+ }
+
+ status = sieve_result_action_commit_or_rollback(
+ rexec, aexec, status, &commit_status);
+
+ aexec = aexec->next;
+ }
+
+ /* Then commit/rollback all other actions */
+ aexec = rexec->actions_head;
+ while (aexec != NULL) {
+ struct sieve_result_action *rac = aexec->action;
+ struct sieve_action *act = &rac->action;
+
+ if (act->def != NULL &&
+ (act->def->flags & SIEVE_ACTFLAG_MAIL_STORAGE) != 0) {
+ aexec = aexec->next;
+ continue;
+ }
+
+ status = sieve_result_action_commit_or_rollback(
+ rexec, aexec, status, &commit_status);
+
+ aexec = aexec->next;
+ }
+
+ e_debug(rexec->event, "Finished finalizing actions "
+ "(status=%s, keep=%s, committed=%s)",
+ sieve_execution_exitcode_to_str(status),
+ (rexec->keep_explicit ? "explicit" :
+ (rexec->keep_implicit ? "implicit" : "none")),
+ (rexec->committed ? "yes" : "no"));
+
+ return commit_status;
+}
+
+static void
+sieve_result_transaction_finish(struct sieve_result_execution *rexec,
+ int status)
+{
+ struct sieve_action_execution *aexec;
+
+ e_debug(rexec->event, "Finishing actions");
+
+ aexec = rexec->actions_head;
+ while (aexec != NULL) {
+ sieve_result_action_finish(rexec, aexec, status);
+ aexec = aexec->next;
+ }
+ sieve_action_execution_post(rexec);
+}
+
+static void
+sieve_result_execute_update_status(struct sieve_result_execution *rexec,
+ int status)
+{
+ switch (status) {
+ case SIEVE_EXEC_OK:
+ break;
+ case SIEVE_EXEC_TEMP_FAILURE:
+ rexec->status = status;
+ break;
+ case SIEVE_EXEC_BIN_CORRUPT:
+ i_unreached();
+ case SIEVE_EXEC_FAILURE:
+ case SIEVE_EXEC_KEEP_FAILED:
+ if (rexec->status == SIEVE_EXEC_OK)
+ rexec->status = status;
+ break;
+ case SIEVE_EXEC_RESOURCE_LIMIT:
+ if (rexec->status != SIEVE_EXEC_TEMP_FAILURE)
+ rexec->status = status;
+ break;
+ }
+}
+
+static void
+sieve_result_execution_update(struct sieve_result_execution *rexec)
+{
+ const struct sieve_action_exec_env *aenv = &rexec->action_env;
+ struct sieve_result *result = aenv->result;
+ struct sieve_action_execution *aexec;
+ struct sieve_result_action *rac;
+
+ aexec = rexec->actions_head;
+ while (aexec != NULL) {
+ struct sieve_action_execution *aexec_next = aexec->next;
+
+ sieve_action_execution_update(rexec, aexec);
+ aexec = aexec_next;
+ }
+
+ rac = result->actions_head;
+ while (rac != NULL) {
+ sieve_result_execution_add_action(rexec, rac);
+ rac = rac->next;
+ }
+}
+
+int sieve_result_execute(struct sieve_result_execution *rexec, int status,
+ bool commit, struct sieve_error_handler *ehandler,
+ bool *keep_r)
+{
+ const struct sieve_action_exec_env *aenv = &rexec->action_env;
+ struct sieve_result *result = aenv->result;
+ int result_status, ret;
+
+ e_debug(rexec->event, "Executing result (status=%s, commit=%s)",
+ sieve_execution_exitcode_to_str(status),
+ (commit ? "yes" : "no"));
+
+ if (keep_r != NULL)
+ *keep_r = FALSE;
+ sieve_result_mark_executed(result);
+
+ /* Prepare environment */
+
+ rexec->ehandler = ehandler;
+
+ /* Update actions in execution from result */
+
+ sieve_result_execution_update(rexec);
+
+ /* Transaction start and execute */
+
+ if (status != SIEVE_EXEC_OK) {
+ sieve_result_execute_update_status(rexec, status);
+ } else if (rexec->status == SIEVE_EXEC_OK) {
+ /* Transaction start */
+
+ status = sieve_result_transaction_start(rexec);
+
+ /* Transaction execute */
+
+ status = sieve_result_transaction_execute(rexec, status);
+ sieve_result_execute_update_status(rexec, status);
+ }
+
+ if (!commit) {
+ sieve_action_execution_post(rexec);
+ rexec->ehandler = NULL;
+
+ /* Merge explicit keep status into implicit keep for the next
+ execution round. */
+ rexec->keep_implicit = (rexec->keep_explicit ||
+ rexec->keep_implicit);
+ rexec->keep_explicit = FALSE;
+
+ e_debug(rexec->event, "Finished executing result "
+ "(no commit, status=%s, keep=%s)",
+ sieve_execution_exitcode_to_str(rexec->status),
+ (rexec->keep_implicit ? "yes" : "no"));
+
+ if (keep_r != NULL)
+ *keep_r = rexec->keep_implicit;
+ return rexec->status;
+ }
+
+ /* Execute implicit keep if the transaction failed or when the
+ implicit keep was not canceled during transaction.
+ */
+ if (rexec->status != SIEVE_EXEC_OK || rexec->keep_implicit)
+ sieve_result_implicit_keep_execute(rexec);
+
+ /* Transaction commit/rollback */
+
+ status = sieve_result_transaction_commit_or_rollback(rexec, status);
+ sieve_result_execute_update_status(rexec, status);
+
+ /* Commit implicit keep if necessary */
+
+ result_status = rexec->status;
+
+ /* Commit implicit keep if the transaction failed or when the
+ implicit keep was not canceled during transaction.
+ */
+ if (rexec->status != SIEVE_EXEC_OK || rexec->keep_implicit) {
+ ret = sieve_result_implicit_keep_finalize(rexec);
+ switch (ret) {
+ case SIEVE_EXEC_OK:
+ if (result_status == SIEVE_EXEC_TEMP_FAILURE)
+ result_status = SIEVE_EXEC_FAILURE;
+ break;
+ case SIEVE_EXEC_TEMP_FAILURE:
+ case SIEVE_EXEC_RESOURCE_LIMIT:
+ if (!rexec->committed) {
+ result_status = ret;
+ break;
+ }
+ /* fall through */
+ default:
+ result_status = SIEVE_EXEC_KEEP_FAILED;
+ }
+ }
+ if (rexec->status == SIEVE_EXEC_OK)
+ rexec->status = result_status;
+
+ /* Finish execution */
+
+ sieve_result_transaction_finish(rexec, rexec->status);
+
+ sieve_action_execution_post(rexec);
+ rexec->ehandler = NULL;
+
+ rexec->status = result_status;
+
+ /* Merge explicit keep status into implicit keep (in this case only for
+ completeness).
+ */
+ rexec->keep_implicit = (rexec->keep_explicit ||
+ rexec->keep_implicit);
+ rexec->keep_explicit = FALSE;
+
+ e_debug(rexec->event, "Finished executing result "
+ "(final, status=%s, keep=%s)",
+ sieve_execution_exitcode_to_str(result_status),
+ (rexec->keep_implicit ? "yes" : "no"));
+
+ if (keep_r != NULL)
+ *keep_r = rexec->keep_implicit;
+ return result_status;
+}
+
+/*
+ * Result evaluation
+ */
+
+struct sieve_result_iterate_context {
+ struct sieve_result *result;
+ struct sieve_result_action *current_action;
+ struct sieve_result_action *next_action;
+};
+
+struct sieve_result_iterate_context *
+sieve_result_iterate_init(struct sieve_result *result)
+{
+ struct sieve_result_iterate_context *rictx =
+ t_new(struct sieve_result_iterate_context, 1);
+
+ rictx->result = result;
+ rictx->current_action = NULL;
+ rictx->next_action = result->actions_head;
+
+ return rictx;
+}
+
+const struct sieve_action *
+sieve_result_iterate_next(struct sieve_result_iterate_context *rictx,
+ bool *keep)
+{
+ struct sieve_result_action *rac;
+
+ if (rictx == NULL)
+ return NULL;
+
+ rac = rictx->current_action = rictx->next_action;
+ if (rac != NULL) {
+ rictx->next_action = rac->next;
+
+ if (keep != NULL)
+ *keep = rac->action.keep;
+
+ return &rac->action;
+ }
+
+ return NULL;
+}
+
+void sieve_result_iterate_delete(struct sieve_result_iterate_context *rictx)
+{
+ struct sieve_result *result;
+ struct sieve_result_action *rac;
+
+ if (rictx == NULL || rictx->current_action == NULL)
+ return;
+
+ result = rictx->result;
+ rac = rictx->current_action;
+
+ /* Delete action */
+
+ if (rac->prev == NULL)
+ result->actions_head = rac->next;
+ else
+ rac->prev->next = rac->next;
+
+ if (rac->next == NULL)
+ result->actions_tail = rac->prev;
+ else
+ rac->next->prev = rac->prev;
+
+ sieve_result_action_deinit(rac);
+
+ /* Skip to next action in iteration */
+
+ rictx->current_action = NULL;
+}
+
+/*
+ * Side effects list
+ */
+
+struct sieve_side_effects_list *
+sieve_side_effects_list_create(struct sieve_result *result)
+{
+ struct sieve_side_effects_list *list =
+ p_new(result->pool, struct sieve_side_effects_list, 1);
+
+ list->result = result;
+ list->first_effect = NULL;
+ list->last_effect = NULL;
+
+ return list;
+}
+
+void sieve_side_effects_list_add(struct sieve_side_effects_list *list,
+ const struct sieve_side_effect *seffect)
+{
+ struct sieve_result_side_effect *reffect, *reffect_pos;
+
+ /* Prevent duplicates */
+ reffect = list->first_effect;
+ reffect_pos = NULL;
+ while (reffect != NULL) {
+ const struct sieve_side_effect_def *ref_def = reffect->seffect.def;
+ const struct sieve_side_effect_def *sef_def = seffect->def;
+
+ i_assert(ref_def != NULL);
+ i_assert(sef_def != NULL);
+
+ if (sef_def == ref_def) {
+ /* already listed */
+ i_assert(reffect_pos == NULL);
+ return;
+ }
+ if (sef_def->precedence > ref_def->precedence) {
+ /* insert it before this position */
+ reffect_pos = reffect;
+ }
+
+ reffect = reffect->next;
+ }
+
+ /* Create new side effect object */
+ reffect = p_new(list->result->pool, struct sieve_result_side_effect, 1);
+ reffect->seffect = *seffect;
+
+ if (reffect_pos != NULL) {
+ /* Insert */
+ reffect->next = reffect_pos;
+ reffect_pos->prev = reffect;
+ if (list->first_effect == reffect_pos)
+ list->first_effect = reffect;
+ } else {
+ /* Add */
+ if ( list->first_effect == NULL ) {
+ list->first_effect = reffect;
+ list->last_effect = reffect;
+ reffect->prev = NULL;
+ reffect->next = NULL;
+ } else {
+ list->last_effect->next = reffect;
+ reffect->prev = list->last_effect;
+ list->last_effect = reffect;
+ reffect->next = NULL;
+ }
+ }
+}
+
+/*
+ * Error handling
+ */
+
+#undef sieve_result_error
+void sieve_result_error(const struct sieve_action_exec_env *aenv,
+ const char *csrc_filename, unsigned int csrc_linenum,
+ const char *fmt, ...)
+{
+ struct sieve_error_params params = {
+ .log_type = LOG_TYPE_ERROR,
+ .event = aenv->event,
+ .csrc = {
+ .filename = csrc_filename,
+ .linenum = csrc_linenum,
+ },
+ };
+ va_list args;
+
+ va_start(args, fmt);
+ sieve_logv(aenv->ehandler, &params, fmt, args);
+ va_end(args);
+}
+
+#undef sieve_result_global_error
+void sieve_result_global_error(const struct sieve_action_exec_env *aenv,
+ const char *csrc_filename,
+ unsigned int csrc_linenum, const char *fmt, ...)
+{
+ const struct sieve_execute_env *eenv = aenv->exec_env;
+ struct sieve_error_params params = {
+ .log_type = LOG_TYPE_ERROR,
+ .event = aenv->event,
+ .csrc = {
+ .filename = csrc_filename,
+ .linenum = csrc_linenum,
+ },
+ };
+ va_list args;
+
+ va_start(args, fmt);
+ sieve_global_logv(eenv->svinst, aenv->ehandler, &params, fmt, args);
+ va_end(args);
+}
+
+#undef sieve_result_warning
+void sieve_result_warning(const struct sieve_action_exec_env *aenv,
+ const char *csrc_filename, unsigned int csrc_linenum,
+ const char *fmt, ...)
+{
+ struct sieve_error_params params = {
+ .log_type = LOG_TYPE_WARNING,
+ .event = aenv->event,
+ .csrc = {
+ .filename = csrc_filename,
+ .linenum = csrc_linenum,
+ },
+ };
+ va_list args;
+
+ va_start(args, fmt);
+ sieve_logv(aenv->ehandler, &params, fmt, args);
+ va_end(args);
+}
+
+#undef sieve_result_global_warning
+void sieve_result_global_warning(const struct sieve_action_exec_env *aenv,
+ const char *csrc_filename,
+ unsigned int csrc_linenum,
+ const char *fmt, ...)
+{
+ const struct sieve_execute_env *eenv = aenv->exec_env;
+ struct sieve_error_params params = {
+ .log_type = LOG_TYPE_WARNING,
+ .event = aenv->event,
+ .csrc = {
+ .filename = csrc_filename,
+ .linenum = csrc_linenum,
+ },
+ };
+ va_list args;
+
+ va_start(args, fmt);
+ sieve_global_logv(eenv->svinst, aenv->ehandler, &params, fmt, args);
+ va_end(args);
+}
+
+#undef sieve_result_log
+void sieve_result_log(const struct sieve_action_exec_env *aenv,
+ const char *csrc_filename, unsigned int csrc_linenum,
+ const char *fmt, ...)
+{
+ const struct sieve_execute_env *eenv = aenv->exec_env;
+ struct sieve_error_params params = {
+ .log_type = (HAS_ALL_BITS(eenv->flags,
+ SIEVE_EXECUTE_FLAG_LOG_RESULT) ?
+ LOG_TYPE_INFO : LOG_TYPE_DEBUG),
+ .event = aenv->event,
+ .csrc = {
+ .filename = csrc_filename,
+ .linenum = csrc_linenum,
+ },
+ };
+ va_list args;
+
+ va_start(args, fmt);
+ sieve_logv(aenv->ehandler, &params, fmt, args);
+ va_end(args);
+}
+
+#undef sieve_result_global_log
+void sieve_result_global_log(const struct sieve_action_exec_env *aenv,
+ const char *csrc_filename,
+ unsigned int csrc_linenum, const char *fmt, ...)
+{
+ const struct sieve_execute_env *eenv = aenv->exec_env;
+ struct sieve_error_params params = {
+ .log_type = (HAS_ALL_BITS(eenv->flags,
+ SIEVE_EXECUTE_FLAG_LOG_RESULT) ?
+ LOG_TYPE_INFO : LOG_TYPE_DEBUG),
+ .event = aenv->event,
+ .csrc = {
+ .filename = csrc_filename,
+ .linenum = csrc_linenum,
+ },
+ };
+ va_list args;
+
+ va_start(args, fmt);
+ sieve_global_logv(eenv->svinst, aenv->ehandler, &params, fmt, args);
+ va_end(args);
+}
+
+#undef sieve_result_global_log_error
+void sieve_result_global_log_error(const struct sieve_action_exec_env *aenv,
+ const char *csrc_filename,
+ unsigned int csrc_linenum,
+ const char *fmt, ...)
+{
+ const struct sieve_execute_env *eenv = aenv->exec_env;
+ struct sieve_error_params params = {
+ .log_type = LOG_TYPE_ERROR,
+ .event = aenv->event,
+ .csrc = {
+ .filename = csrc_filename,
+ .linenum = csrc_linenum,
+ },
+ };
+ va_list args;
+
+ va_start(args, fmt);
+ sieve_global_info_logv(eenv->svinst, aenv->ehandler, &params,
+ fmt, args);
+ va_end(args);
+}
+
+#undef sieve_result_global_log_warning
+void sieve_result_global_log_warning(const struct sieve_action_exec_env *aenv,
+ const char *csrc_filename,
+ unsigned int csrc_linenum,
+ const char *fmt, ...)
+{
+ const struct sieve_execute_env *eenv = aenv->exec_env;
+ struct sieve_error_params params = {
+ .log_type = LOG_TYPE_WARNING,
+ .event = aenv->event,
+ .csrc = {
+ .filename = csrc_filename,
+ .linenum = csrc_linenum,
+ },
+ };
+ va_list args;
+
+ va_start(args, fmt);
+ sieve_global_info_logv(eenv->svinst, aenv->ehandler, &params,
+ fmt, args);
+ va_end(args);
+}
+
+#undef sieve_result_event_log
+void sieve_result_event_log(const struct sieve_action_exec_env *aenv,
+ const char *csrc_filename,
+ unsigned int csrc_linenum, struct event *event,
+ const char *fmt, ...)
+{
+ const struct sieve_execute_env *eenv = aenv->exec_env;
+ struct sieve_error_params params = {
+ .log_type = (HAS_ALL_BITS(eenv->flags,
+ SIEVE_EXECUTE_FLAG_LOG_RESULT) ?
+ LOG_TYPE_INFO : LOG_TYPE_DEBUG),
+ .event = event,
+ .csrc = {
+ .filename = csrc_filename,
+ .linenum = csrc_linenum,
+ },
+ };
+ va_list args;
+
+ va_start(args, fmt);
+ sieve_global_logv(eenv->svinst, aenv->ehandler, &params, fmt, args);
+ va_end(args);
+}
+
+
+#undef sieve_result_critical
+void sieve_result_critical(const struct sieve_action_exec_env *aenv,
+ const char *csrc_filename, unsigned int csrc_linenum,
+ const char *user_prefix, const char *fmt, ...)
+{
+ const struct sieve_execute_env *eenv = aenv->exec_env;
+ struct sieve_error_params params = {
+ .log_type = LOG_TYPE_ERROR,
+ .event = aenv->event,
+ .csrc = {
+ .filename = csrc_filename,
+ .linenum = csrc_linenum,
+ },
+ };
+ va_list args;
+
+ va_start(args, fmt);
+
+ T_BEGIN {
+ sieve_criticalv(eenv->svinst, aenv->ehandler, &params,
+ user_prefix, fmt, args);
+ } T_END;
+
+ va_end(args);
+}
+
+#undef sieve_result_mail_error
+int sieve_result_mail_error(const struct sieve_action_exec_env *aenv,
+ struct mail *mail,
+ const char *csrc_filename,
+ unsigned int csrc_linenum, const char *fmt, ...)
+{
+ const char *error_msg, *user_prefix;
+ va_list args;
+
+ error_msg = mailbox_get_last_internal_error(mail->box, NULL);
+
+ va_start(args, fmt);
+ user_prefix = t_strdup_vprintf(fmt, args);
+ sieve_result_critical(aenv, csrc_filename, csrc_linenum,
+ user_prefix, "%s: %s", user_prefix, error_msg);
+ va_end(args);
+
+ return SIEVE_EXEC_TEMP_FAILURE;
+}