#include "lib.h"
#include "mempool.h"
#include "ostream.h"
#include "hash.h"
#include "str.h"
#include "strfuncs.h"
#include "str-sanitize.h"
#include "var-expand.h"
#include "message-address.h"
#include "mail-deliver.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>
#define DEFAULT_ACTION_LOG_FORMAT "msgid=%m: %$"
struct sieve_result_action {
struct sieve_action action;
void *tr_context;
bool success;
bool keep;
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;
};
struct sieve_result {
pool_t pool;
int refcount;
struct sieve_instance *svinst;
ARRAY_DEFINE(ext_contexts, void *);
struct sieve_error_handler *ehandler;
struct sieve_action_exec_env action_env;
struct sieve_action keep_action;
struct sieve_action failure_action;
unsigned int action_count;
struct sieve_result_action *first_action;
struct sieve_result_action *last_action;
struct sieve_result_action *last_attempted_action;
struct hash_table *action_contexts;
};
struct sieve_result *sieve_result_create
(struct sieve_instance *svinst, const struct sieve_message_data *msgdata,
const struct sieve_script_env *senv, struct sieve_error_handler *ehandler)
{
pool_t pool;
struct sieve_result *result;
pool = pool_alloconly_create("sieve_result", 4096);
result = p_new(pool, struct sieve_result, 1);
result->refcount = 1;
result->pool = pool;
result->svinst = svinst;
p_array_init(&result->ext_contexts, pool, 4);
if ( ehandler != NULL )
sieve_error_handler_ref(ehandler);
result->ehandler = ehandler;
result->action_env.svinst = svinst;
result->action_env.result = result;
result->action_env.scriptenv = senv;
result->action_env.msgdata = msgdata;
result->action_env.msgctx = sieve_message_context_create(svinst, 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->first_action = NULL;
result->last_action = NULL;
result->action_contexts = NULL;
return result;
}
void sieve_result_ref(struct sieve_result *result)
{
result->refcount++;
}
void sieve_result_unref(struct sieve_result **result)
{
i_assert((*result)->refcount > 0);
if (--(*result)->refcount != 0)
return;
sieve_message_context_unref(&(*result)->action_env.msgctx);
if ( (*result)->action_contexts != NULL )
hash_table_destroy(&(*result)->action_contexts);
if ( (*result)->ehandler != NULL )
sieve_error_handler_unref(&(*result)->ehandler);
if ( (*result)->action_env.ehandler != NULL )
sieve_error_handler_unref(&(*result)->action_env.ehandler);
pool_unref(&(*result)->pool);
*result = NULL;
}
pool_t sieve_result_pool(struct sieve_result *result)
{
return result->pool;
}
struct sieve_error_handler *sieve_result_get_error_handler
(struct sieve_result *result)
{
return result->ehandler;
}
const struct sieve_script_env *sieve_result_get_script_env
(struct sieve_result *result)
{
return result->action_env.scriptenv;
}
const struct sieve_message_data *sieve_result_get_message_data
(struct sieve_result *result)
{
return result->action_env.msgdata;
}
struct sieve_message_context *sieve_result_get_message_context
(struct sieve_result *result)
{
return result->action_env.msgctx;
}
void sieve_result_set_error_handler
(struct sieve_result *result, struct sieve_error_handler *ehandler)
{
if ( result->ehandler != ehandler ) {
sieve_error_handler_ref(ehandler);
sieve_error_handler_unref(&result->ehandler);
result->ehandler = ehandler;
}
}
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;
}
void sieve_result_error
(const struct sieve_action_exec_env *aenv, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
sieve_verror(aenv->ehandler, NULL, fmt, args);
va_end(args);
}
void sieve_result_global_error
(const struct sieve_action_exec_env *aenv, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
sieve_global_verror(aenv->svinst, aenv->ehandler, NULL, fmt, args);
va_end(args);
}
void sieve_result_warning
(const struct sieve_action_exec_env *aenv, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
sieve_vwarning(aenv->ehandler, NULL, fmt, args);
va_end(args);
}
void sieve_result_global_warning
(const struct sieve_action_exec_env *aenv, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
sieve_global_vwarning(aenv->svinst, aenv->ehandler, NULL, fmt, args);
va_end(args);
}
void sieve_result_log
(const struct sieve_action_exec_env *aenv, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
sieve_vinfo(aenv->ehandler, NULL, fmt, args);
va_end(args);
}
void sieve_result_global_log
(const struct sieve_action_exec_env *aenv, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
sieve_global_vinfo(aenv->svinst, aenv->ehandler, NULL, fmt, args);
va_end(args);
}
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 ( result->action_contexts == NULL ) {
result->action_contexts = hash_table_create
(default_pool, result->pool, 0, NULL, NULL);
} else {
actctx = (struct sieve_result_action_context *)
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, (void *) to_action,
(void *) 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;
rsef = old_seffects != NULL ? old_seffects->first_effect : NULL;
while ( rsef != NULL ) {
struct sieve_side_effect *seffect = &rsef->seffect;
bool found = FALSE;
if ( seffect->def != NULL && seffect->def->merge != NULL ) {
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;
}
if ( !found && seffect->def->merge
(renv, action, seffect, NULL, &rsef->seffect.context) < 0 )
return -1;
}
rsef = rsef->next;
}
nrsef = new_seffects != NULL ? new_seffects->first_effect : NULL;
while ( nrsef != NULL ) {
struct sieve_side_effect *nseffect = &nrsef->seffect;
bool found = FALSE;
if ( nseffect->def != NULL && nseffect->def->merge != NULL ) {
rsef = old_seffects != NULL ? old_seffects->first_effect : NULL;
while ( rsef != NULL ) {
if ( rsef->seffect.def == nseffect->def ) {
found = TRUE;
break;
}
rsef = rsef->next;
}
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;
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->first_action == raction )
result->first_action = raction->next;
if ( result->last_action == raction )
result->last_action = raction->prev;
if ( result->last_attempted_action == raction )
result->last_attempted_action = 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 struct sieve_action_def *act_def,
struct sieve_side_effects_list *seffects,
void *context, unsigned int instance_limit, bool keep)
{
int ret = 0;
unsigned int instance_count = 0;
struct sieve_instance *svinst = renv->svinst;
struct sieve_result *result = renv->result;
struct sieve_result_action *raction = NULL, *kaction = NULL;
struct sieve_action action;
action.def = act_def;
action.ext = ext;
action.location = sieve_runtime_get_full_command_location(renv);
action.context = context;
action.executed = FALSE;
raction = result->first_action;
while ( raction != NULL ) {
const struct sieve_action *oact = &raction->action;
if ( keep && raction->keep ) {
if ( raction->action.def == NULL || raction->action.executed ) {
sieve_result_action_detach(result, raction);
if ( kaction == NULL )
kaction = raction;
if ( (ret=sieve_result_side_effects_merge
(renv, &action, kaction, seffects)) <= 0 )
return ret;
} else {
return sieve_result_side_effects_merge
(renv, &action, raction, seffects);
}
} if ( act_def != NULL && raction->action.def == act_def ) {
instance_count++;
if ( act_def->check_duplicate != NULL ) {
if ( (ret=act_def->check_duplicate(renv, &action, &raction->action))
< 0 )
return ret;
if ( ret == 1 ) {
if ( keep && !raction->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);
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 {
return sieve_result_side_effects_merge
(renv, &action, raction, seffects);
}
}
}
} else {
if ( act_def != NULL && oact->def != NULL ) {
if ( act_def->check_conflict != NULL &&
(ret=act_def->check_conflict(renv, &action, &raction->action)) != 0 )
return ret;
if ( !raction->action.executed && oact->def->check_conflict != NULL &&
(ret=oact->def->check_conflict
(renv, &raction->action, &action)) != 0 )
return ret;
}
}
raction = raction->next;
}
if ( svinst->max_actions > 0 && result->action_count >= svinst->max_actions )
{
sieve_runtime_error(renv, action.location,
"total number of actions exceeds policy limit");
return -1;
}
if ( instance_limit > 0 && instance_count >= instance_limit ) {
sieve_runtime_error(renv, action.location,
"number of %s actions exceeds policy limit", act_def->name);
return -1;
}
if ( kaction != NULL ) {
raction = kaction;
} else {
raction = p_new(result->pool, struct sieve_result_action, 1);
raction->action.executed = FALSE;
raction->seffects = seffects;
raction->tr_context = NULL;
raction->success = FALSE;
}
raction->action.context = context;
raction->action.def = act_def;
raction->action.ext = ext;
raction->action.location = p_strdup(result->pool, action.location);
raction->keep = keep;
if ( raction->prev == NULL ) {
if ( result->first_action == NULL ) {
result->first_action = raction;
result->last_action = raction;
raction->prev = NULL;
raction->next = NULL;
} else {
result->last_action->next = raction;
raction->prev = result->last_action;
result->last_action = raction;
raction->next = NULL;
}
result->action_count++;
if ( result->action_contexts != NULL ) {
struct sieve_result_action_context *actctx;
actctx = (struct sieve_result_action_context *)
hash_table_lookup(result->action_contexts,
( keep ? &act_store : act_def ));
if ( actctx != NULL ) {
struct sieve_result_side_effect *iseff;
iseff = actctx->seffects->first_effect;
while ( iseff != NULL ) {
struct sieve_result_side_effect *seff;
bool exists = FALSE;
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 ( !exists ) {
sieve_side_effects_list_add(seffects, &iseff->seffect);
}
iseff = iseff->next;
}
}
}
}
return 0;
}
int sieve_result_add_action
(const struct sieve_runtime_env *renv, const struct sieve_extension *ext,
const struct sieve_action_def *act_def,
struct sieve_side_effects_list *seffects,
void *context, unsigned int instance_limit)
{
return _sieve_result_add_action
(renv, ext, act_def, seffects, context, instance_limit, 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, renv->result->keep_action.def,
seffects, NULL, 0, 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;
}
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_send(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_send(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_send(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;
rsef = slist != NULL ? slist->first_effect : NULL;
while ( rsef != NULL ) {
if ( rsef->seffect.def != NULL ) {
const struct sieve_side_effect *sef = &rsef->seffect;
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;
if ( result->action_contexts != NULL ) {
struct sieve_result_action_context *actctx;
actctx = (struct sieve_result_action_context *)
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;
struct sieve_result_action *rac, *first_action;
first_action = ( result->last_attempted_action == NULL ?
result->first_action : result->last_attempted_action->next );
if ( keep != NULL ) *keep = FALSE;
penv.result = result;
penv.stream = stream;
penv.scriptenv = senv;
sieve_result_printf(&penv, "\nPerformed actions:\n\n");
if ( first_action == NULL ) {
sieve_result_printf(&penv, " (none)\n");
} else {
rac = first_action;
while ( rac != NULL ) {
bool impl_keep = TRUE;
const struct sieve_action *act = &rac->action;
if ( rac->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 ( rac->keep ) {
sieve_result_action_printf(&penv, "keep");
impl_keep = FALSE;
} else {
sieve_result_action_printf(&penv, "[NULL]");
}
}
sieve_result_print_side_effects
(&penv, &rac->action, rac->seffects, &impl_keep);
implicit_keep = implicit_keep && impl_keep;
rac = rac->next;
}
}
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 {
rac = result->first_action;
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)
&& rac->action.executed ) {
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;
}
static void _sieve_result_prepare_execution(struct sieve_result *result)
{
const struct sieve_message_data *msgdata = result->action_env.msgdata;
const struct sieve_script_env *senv = result->action_env.scriptenv;
const struct var_expand_table *tab;
tab = mail_deliver_get_log_var_expand_table(msgdata->mail, NULL);
result->action_env.exec_status =
( senv->exec_status == NULL ?
t_new(struct sieve_exec_status, 1) : senv->exec_status );
if ( result->action_env.ehandler != NULL )
sieve_error_handler_unref(&result->action_env.ehandler);
if ( senv->action_log_format != NULL ) {
result->action_env.ehandler = sieve_varexpand_ehandler_create
(result->ehandler, senv->action_log_format, tab);
} else {
result->action_env.ehandler = sieve_varexpand_ehandler_create
(result->ehandler, DEFAULT_ACTION_LOG_FORMAT, tab);
}
}
static bool _sieve_result_implicit_keep
(struct sieve_result *result, bool rollback)
{
struct sieve_result_action *rac;
bool success = TRUE;
struct sieve_result_side_effect *rsef, *rsef_first = NULL;
void *tr_context = NULL;
struct sieve_action act_keep;
if ( rollback )
act_keep = result->failure_action;
else
act_keep = result->keep_action;
if ( act_keep.def == NULL ) return TRUE;
rac = result->first_action;
while ( rac != NULL ) {
if ( rac->action.def == act_keep.def && act_keep.def->equals != NULL &&
act_keep.def->equals
(result->action_env.scriptenv, NULL, &rac->action) &&
rac->action.executed )
return TRUE;
rac = rac->next;
}
if ( !rollback && result->action_contexts != NULL ) {
struct sieve_result_action_context *actctx;
actctx = (struct sieve_result_action_context *)
hash_table_lookup(result->action_contexts, act_keep.def);
if ( actctx != NULL && actctx->seffects != NULL )
rsef_first = actctx->seffects->first_effect;
}
if ( act_keep.def->start != NULL )
success = act_keep.def->start
(&act_keep, &result->action_env, &tr_context);
if ( success ) {
rsef = rsef_first;
while ( success && rsef != NULL ) {
struct sieve_side_effect *sef = &rsef->seffect;
if ( sef->def->pre_execute != NULL )
success = success && sef->def->pre_execute
(sef, &act_keep, &result->action_env, &sef->context, tr_context);
rsef = rsef->next;
}
if ( act_keep.def->execute != NULL )
success = success && act_keep.def->execute
(&act_keep, &result->action_env, tr_context);
rsef = rsef_first;
while ( success && rsef != NULL ) {
struct sieve_side_effect *sef = &rsef->seffect;
if ( sef->def->post_execute != NULL )
success = success && sef->def->post_execute
(sef, &act_keep, &result->action_env, tr_context);
rsef = rsef->next;
}
}
if ( success ) {
bool dummy = TRUE;
if ( act_keep.def->commit != NULL )
success = act_keep.def->commit
(&act_keep, &result->action_env, tr_context, &dummy);
rsef = rsef_first;
while ( rsef != NULL ) {
struct sieve_side_effect *sef = &rsef->seffect;
bool keep = TRUE;
if ( sef->def->post_commit != NULL )
sef->def->post_commit
(sef, &act_keep, &result->action_env, tr_context, &keep);
rsef = rsef->next;
}
return success;
}
if ( act_keep.def->rollback != NULL )
act_keep.def->rollback
(&act_keep, &result->action_env, tr_context, success);
return FALSE;
}
bool sieve_result_implicit_keep
(struct sieve_result *result)
{
_sieve_result_prepare_execution(result);
return _sieve_result_implicit_keep(result, TRUE);
}
void sieve_result_mark_executed(struct sieve_result *result)
{
struct sieve_result_action *first_action, *rac;
first_action = ( result->last_attempted_action == NULL ?
result->first_action : result->last_attempted_action->next );
result->last_attempted_action = result->last_action;
rac = first_action;
while ( rac != NULL ) {
if ( rac->action.def != NULL )
rac->action.executed = TRUE;
rac = rac->next;
}
}
int sieve_result_execute
(struct sieve_result *result, bool *keep)
{
bool implicit_keep = TRUE;
bool success = TRUE, commit_ok;
struct sieve_result_action *rac, *first_action;
struct sieve_result_action *last_attempted;
if ( keep != NULL ) *keep = FALSE;
_sieve_result_prepare_execution(result);
first_action = ( result->last_attempted_action == NULL ?
result->first_action : result->last_attempted_action->next );
result->last_attempted_action = result->last_action;
rac = first_action;
while ( success && rac != NULL ) {
struct sieve_action *act = &rac->action;
if ( act->def == NULL || act->executed ) {
rac = rac->next;
continue;
}
if ( act->def->start != NULL ) {
rac->success = act->def->start
(act, &result->action_env, &rac->tr_context);
success = success && rac->success;
}
rac = rac->next;
}
last_attempted = rac;
rac = first_action;
while ( success && rac != NULL ) {
struct sieve_action *act = &rac->action;
struct sieve_result_side_effect *rsef;
struct sieve_side_effect *sef;
if ( act->def == NULL || act->executed ) {
rac = rac->next;
continue;
}
rsef = rac->seffects != NULL ? rac->seffects->first_effect : NULL;
while ( success && rsef != NULL ) {
sef = &rsef->seffect;
if ( sef->def != NULL && sef->def->pre_execute != NULL )
success = success & sef->def->pre_execute
(sef, act, &result->action_env, &sef->context, rac->tr_context);
rsef = rsef->next;
}
if ( success && act->def != NULL && act->def->execute != NULL ) {
rac->success = act->def->execute
(act, &result->action_env, rac->tr_context);
success = success && rac->success;
}
rsef = rac->seffects != NULL ? rac->seffects->first_effect : NULL;
while ( success && rsef != NULL ) {
sef = &rsef->seffect;
if ( sef->def != NULL && sef->def->post_execute != NULL )
success = success && sef->def->post_execute
(sef, act, &result->action_env, rac->tr_context);
rsef = rsef->next;
}
rac = rac->next;
}
commit_ok = success;
rac = first_action;
while ( rac != NULL && rac != last_attempted ) {
struct sieve_action *act = &rac->action;
struct sieve_result_side_effect *rsef;
struct sieve_side_effect *sef;
if ( success ) {
bool impl_keep = TRUE;
if ( rac->keep && keep != NULL ) *keep = TRUE;
if ( act->def == NULL || act->executed ) {
rac = rac->next;
continue;
}
if ( act->def->commit != NULL ) {
act->executed = act->def->commit
(act, &result->action_env, rac->tr_context, &impl_keep);
commit_ok = act->executed && commit_ok;
}
rsef = rac->seffects != NULL ? rac->seffects->first_effect : NULL;
while ( rsef != NULL ) {
sef = &rsef->seffect;
if ( sef->def->post_commit != NULL )
sef->def->post_commit
(sef, act, &result->action_env, rac->tr_context, &impl_keep);
rsef = rsef->next;
}
implicit_keep = implicit_keep && impl_keep;
} else {
if ( act->def == NULL || act->executed ) {
rac = rac->next;
continue;
}
if ( act->def->rollback != NULL )
act->def->rollback
(act, &result->action_env, rac->tr_context, rac->success);
rsef = rac->seffects != NULL ? rac->seffects->first_effect : NULL;
while ( rsef != NULL ) {
sef = &rsef->seffect;
if ( sef->def && sef->def->rollback != NULL )
sef->def->rollback
(sef, act, &result->action_env, rac->tr_context, rac->success);
rsef = rsef->next;
}
}
rac = rac->next;
}
if ( implicit_keep && keep != NULL ) *keep = TRUE;
if ( !commit_ok || implicit_keep ) {
if ( !_sieve_result_implicit_keep(result, !commit_ok) )
return SIEVE_EXEC_KEEP_FAILED;
return ( commit_ok ?
SIEVE_EXEC_OK :
SIEVE_EXEC_FAILURE );
}
return SIEVE_EXEC_OK;
}
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->first_action;
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->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;
if ( rac->prev == NULL )
result->first_action = rac->next;
else
rac->prev->next = rac->next;
if ( rac->next == NULL )
result->last_action = rac->prev;
else
rac->next->prev = rac->prev;
rictx->current_action = NULL;
}
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 = list->first_effect;
while ( reffect != NULL ) {
if ( reffect->seffect.def == seffect->def ) return;
reffect = reffect->next;
}
reffect = p_new(list->result->pool, struct sieve_result_side_effect, 1);
reffect->seffect = *seffect;
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;
}
}