diff --git a/src/lib-sieve/cmd-redirect.c b/src/lib-sieve/cmd-redirect.c index ca2b21b2c524772344b0d59dcf1cab307764de00..1faa3c888316abab0e266d331a05070f6c4c116d 100644 --- a/src/lib-sieve/cmd-redirect.c +++ b/src/lib-sieve/cmd-redirect.c @@ -74,6 +74,8 @@ const struct sieve_operation cmd_redirect_operation = { * Redirect action */ +static bool act_redirect_equals + (const struct sieve_script_env *senv, const void *ctx1, const void *ctx2); static int act_redirect_check_duplicate (const struct sieve_runtime_env *renv, const struct sieve_action_data *act, @@ -88,6 +90,7 @@ static bool act_redirect_commit const struct sieve_action act_redirect = { "redirect", SIEVE_ACTFLAG_TRIES_DELIVER, + act_redirect_equals, act_redirect_check_duplicate, NULL, act_redirect_print, @@ -237,22 +240,27 @@ static int cmd_redirect_operation_execute /* * Action implementation */ + +static bool act_redirect_equals +(const struct sieve_script_env *senv ATTR_UNUSED, + const void *ctx1, const void *ctx2) +{ + struct act_redirect_context *rd_ctx1 = + (struct act_redirect_context *) ctx1; + struct act_redirect_context *rd_ctx2 = + (struct act_redirect_context *) ctx2; + + /* Address is already normalized, strcmp suffices to assess duplicates */ + return ( strcmp(rd_ctx1->to_address, rd_ctx2->to_address) == 0 ); +} static int act_redirect_check_duplicate (const struct sieve_runtime_env *renv ATTR_UNUSED, const struct sieve_action_data *act, const struct sieve_action_data *act_other) { - struct act_redirect_context *ctx1 = - (struct act_redirect_context *) act->context; - struct act_redirect_context *ctx2 = - (struct act_redirect_context *) act_other->context; - - /* Address is already normalized, strcmp suffices to assess duplicates */ - if ( strcmp(ctx1->to_address, ctx2->to_address) == 0 ) - return 1; - - return 0; + return ( act_redirect_equals + (renv->scriptenv, act->context, act_other->context) ? 1 : 0 ); } static void act_redirect_print diff --git a/src/lib-sieve/ext-reject.c b/src/lib-sieve/ext-reject.c index 26ee60db24fad293c84540e4cd2280c51a0e0a53..62e351a6e1d69ee4155cd9d5adf348a4ace0b113 100644 --- a/src/lib-sieve/ext-reject.c +++ b/src/lib-sieve/ext-reject.c @@ -132,6 +132,7 @@ static bool act_reject_commit const struct sieve_action act_reject = { "reject", SIEVE_ACTFLAG_SENDS_RESPONSE, + NULL, act_reject_check_duplicate, act_reject_check_conflict, act_reject_print, diff --git a/src/lib-sieve/plugins/enotify/cmd-notify.c b/src/lib-sieve/plugins/enotify/cmd-notify.c index a25b588baddceacd0ac750186eb6d34d7e396b78..9d4112a6c037b48ce57e82f2c7e587cc136e05fd 100644 --- a/src/lib-sieve/plugins/enotify/cmd-notify.c +++ b/src/lib-sieve/plugins/enotify/cmd-notify.c @@ -154,6 +154,7 @@ static bool act_notify_commit const struct sieve_action act_notify = { "notify", 0, + NULL, act_notify_check_duplicate, NULL, act_notify_print, diff --git a/src/lib-sieve/plugins/vacation/cmd-vacation.c b/src/lib-sieve/plugins/vacation/cmd-vacation.c index 9ffdf5d385eca5c25499028b00590df602a56f91..9715e1eb42491547e436f1c5b4afef756c337164 100644 --- a/src/lib-sieve/plugins/vacation/cmd-vacation.c +++ b/src/lib-sieve/plugins/vacation/cmd-vacation.c @@ -190,6 +190,7 @@ static bool act_vacation_commit const struct sieve_action act_vacation = { "vacation", SIEVE_ACTFLAG_SENDS_RESPONSE, + NULL, act_vacation_check_duplicate, act_vacation_check_conflict, act_vacation_print, diff --git a/src/lib-sieve/sieve-actions.c b/src/lib-sieve/sieve-actions.c index c544cb79352a97199f44ab8c22a95694b6343e6f..57662dbeb23eaaff307ec61c1585617097d257c0 100644 --- a/src/lib-sieve/sieve-actions.c +++ b/src/lib-sieve/sieve-actions.c @@ -61,6 +61,9 @@ bool sieve_opr_side_effect_dump /* Forward declarations */ +static bool act_store_equals + (const struct sieve_script_env *senv, const void *ctx1, const void *ctx2); + static int act_store_check_duplicate (const struct sieve_runtime_env *renv, const struct sieve_action_data *act, @@ -87,6 +90,7 @@ static void act_store_rollback const struct sieve_action act_store = { "store", SIEVE_ACTFLAG_TRIES_DELIVER, + act_store_equals, act_store_check_duplicate, NULL, act_store_print, @@ -115,32 +119,40 @@ int sieve_act_store_add_to_result source_line, (void *) act, 0); } -/* Result verification */ +/* Equality */ -static int act_store_check_duplicate -(const struct sieve_runtime_env *renv, - const struct sieve_action_data *act, - const struct sieve_action_data *act_other) +static bool act_store_equals +(const struct sieve_script_env *senv, const void *ctx1, const void *ctx2) { - struct act_store_context *ctx1 = - (struct act_store_context *) act->context; - struct act_store_context *ctx2 = - (struct act_store_context *) act_other->context; + struct act_store_context *st_ctx1 = (struct act_store_context *) ctx1; + struct act_store_context *st_ctx2 = (struct act_store_context *) ctx2; const char *folder1, *folder2; - if ( ctx1 == NULL && ctx2 == NULL ) - return 1; + if ( st_ctx1 == NULL && st_ctx2 == NULL ) + return TRUE; - folder1 = ctx1 == NULL ? - SIEVE_SCRIPT_DEFAULT_MAILBOX(renv->scriptenv) : ctx1->folder; - folder2 = ctx2 == NULL ? - SIEVE_SCRIPT_DEFAULT_MAILBOX(renv->scriptenv) : ctx2->folder; + folder1 = ( st_ctx1 == NULL ? + SIEVE_SCRIPT_DEFAULT_MAILBOX(senv) : st_ctx1->folder ); + folder2 = ( st_ctx2 == NULL ? + SIEVE_SCRIPT_DEFAULT_MAILBOX(senv) : st_ctx2->folder ); if ( strcmp(folder1, folder2) == 0 ) - return 1; + return TRUE; return ( strcasecmp(folder1, "INBOX") == 0 && strcasecmp(folder2, "INBOX") == 0 ); + +} + +/* Result verification */ + +static int act_store_check_duplicate +(const struct sieve_runtime_env *renv, + const struct sieve_action_data *act, + const struct sieve_action_data *act_other) +{ + return ( act_store_equals(renv->scriptenv, act->context, act_other->context) + ? 1 : 0 ); } /* Result printing */ diff --git a/src/lib-sieve/sieve-actions.h b/src/lib-sieve/sieve-actions.h index 65a3228b5996cdfad8eb75c4049977bcae076b2b..c088ab58c42c846f5f69898ce0b2b18f037275f5 100644 --- a/src/lib-sieve/sieve-actions.h +++ b/src/lib-sieve/sieve-actions.h @@ -51,6 +51,9 @@ struct sieve_action_data { struct sieve_action { const char *name; unsigned int flags; + + bool (*equals) + (const struct sieve_script_env *senv, const void *ctx1, const void *ctx2); /* Result verification */ diff --git a/src/lib-sieve/sieve-result.c b/src/lib-sieve/sieve-result.c index 6a742d844f0f68939488e3dc68e3c109128dea64..55a9f1bd8f9e97e46bf24adf5e75b732ff71422a 100644 --- a/src/lib-sieve/sieve-result.c +++ b/src/lib-sieve/sieve-result.c @@ -316,6 +316,26 @@ static int sieve_result_side_effects_merge return 1; } +static void sieve_result_action_detach(struct sieve_result_action *raction) +{ + struct sieve_result *result = raction->result; + + if ( result->first_action == raction ) + result->first_action = raction->next; + + if ( result->last_action == raction ) + result->last_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_action *action, struct sieve_side_effects_list *seffects, @@ -344,23 +364,16 @@ static int _sieve_result_add_action if ( raction->data.executed ) { /* Keep action from preceeding execution */ - kaction = raction; - /* Detach existing keep action */ - if ( result->first_action == kaction ) - result->first_action = kaction->next; - if ( result->last_action == kaction ) - result->last_action = kaction->prev; - if ( kaction->next != NULL ) kaction->next->prev = kaction->prev; - if ( kaction->prev != NULL ) kaction->prev->next = kaction->next; - + sieve_result_action_detach(raction); + /* Merge existing side-effects with new keep action */ - if ( raction->data.action == NULL ) { - if ( (ret=sieve_result_side_effects_merge - (renv, action, seffects, kaction->seffects)) <= 0 ) - return ret; - } - + if ( kaction == NULL ) + kaction = raction; + + if ( (ret=sieve_result_side_effects_merge + (renv, action, kaction->seffects, seffects)) <= 0 ) + return ret; } else { /* True duplicate */ @@ -384,19 +397,28 @@ static int _sieve_result_add_action * 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, seffects)) < 0 ) return ret; + + if ( kaction == NULL ) { + raction->data.context = NULL; + raction->data.location = p_strdup(result->pool, act_data.location); - raction->keep = TRUE; - raction->data.context = NULL; - raction->data.location = p_strdup(result->pool, act_data.location); - - /* Note that existing execution status is retained, making sure that - * keep is not executed multiple times. - */ - - return 1; + /* Note that existing execution status is retained, making sure + * that keep is not executed multiple times. + */ + + kaction = raction; + + } else { + sieve_result_action_detach(raction); + + if ( (ret=sieve_result_side_effects_merge + (renv, action, kaction->seffects, raction->seffects)) < 0 ) + return ret; + } } else { /* Merge side-effects, but don't add new action */ return sieve_result_side_effects_merge @@ -405,14 +427,16 @@ static int _sieve_result_add_action } } } else { - /* Check conflict */ - if ( action->check_conflict != NULL && - (ret=action->check_conflict(renv, &act_data, &raction->data)) != 0 ) - return ret; + if ( action != NULL && oact != NULL ) { + /* Check conflict */ + if ( action->check_conflict != NULL && + (ret=action->check_conflict(renv, &act_data, &raction->data)) != 0 ) + return ret; - if ( !raction->data.executed && oact->check_conflict != NULL && - (ret=oact->check_conflict(renv, &raction->data, &act_data)) != 0 ) - return ret; + if ( !raction->data.executed && oact->check_conflict != NULL && + (ret=oact->check_conflict(renv, &raction->data, &act_data)) != 0 ) + return ret; + } } raction = raction->next; } @@ -438,71 +462,75 @@ static int _sieve_result_add_action /* Create new action object */ raction = p_new(result->pool, struct sieve_result_action, 1); raction->data.executed = FALSE; + raction->result = result; + raction->seffects = seffects; + raction->tr_context = NULL; + raction->success = FALSE; } - raction->result = result; raction->data.context = context; raction->data.action = action; raction->data.location = p_strdup(result->pool, act_data.location); - raction->tr_context = NULL; - raction->success = FALSE; raction->keep = keep; - raction->seffects = seffects; - /* Add */ - 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 ( raction->prev == NULL ) { + /* Add */ + 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++; - /* Apply any implicit side effects */ - if ( result->action_contexts != NULL ) { - struct sieve_result_action_context *actctx; + /* Apply any implicit side effects */ + if ( result->action_contexts != NULL ) { + struct sieve_result_action_context *actctx; - /* Check for implicit side effects to this particular action */ - actctx = (struct sieve_result_action_context *) - hash_table_lookup(result->action_contexts, action); + /* Check for implicit side effects to this particular action */ + actctx = (struct sieve_result_action_context *) + hash_table_lookup(result->action_contexts, action); - if ( actctx != NULL ) { - struct sieve_result_side_effect *iseff; + 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; + /* 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 == iseff->seffect ) { - exists = TRUE; - break; - } + /* Scan for presence */ + if ( seffects != NULL ) { + seff = seffects->first_effect; + while ( seff != NULL ) { + if ( seff->seffect == iseff->seffect ) { + exists = TRUE; + break; + } - seff = seff->next; + seff = seff->next; + } + } else { + raction->seffects = seffects = + sieve_side_effects_list_create(result); } - } 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->context); - } + /* If not present, add it */ + if ( !exists ) { + sieve_side_effects_list_add + (seffects, iseff->seffect, iseff->context); + } - iseff = iseff->next; + iseff = iseff->next; + } } } } @@ -527,6 +555,12 @@ int sieve_result_add_keep (renv, renv->result->keep_action, seffects, source_line, NULL, 0, TRUE); } +void sieve_result_set_keep_action +(struct sieve_result *result, const struct sieve_action *action) +{ + result->keep_action = action; +} + /* * Result printing */ @@ -574,10 +608,28 @@ void sieve_result_seffect_printf o_stream_send(penv->stream, str_data(outbuf), str_len(outbuf)); } +static void sieve_result_print_side_effect +(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; + const struct sieve_side_effect *sef; + + /* Print side effects */ + rsef = slist != NULL ? slist->first_effect : NULL; + while ( rsef != NULL ) { + sef = rsef->seffect; + if ( sef->print != NULL ) + sef->print(sef, action, rpenv, rsef->context, implicit_keep); + rsef = rsef->next; + } +} + bool sieve_result_print (struct sieve_result *result, const struct sieve_script_env *senv, struct ostream *stream, bool *keep) { + const 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; @@ -601,8 +653,6 @@ bool sieve_result_print rac = first_action; while ( rac != NULL ) { bool impl_keep = TRUE; - struct sieve_result_side_effect *rsef; - const struct sieve_side_effect *sef; const struct sieve_action *act = rac->data.action; if ( rac->keep && keep != NULL ) *keep = TRUE; @@ -622,13 +672,8 @@ bool sieve_result_print } /* Print side effects */ - rsef = rac->seffects != NULL ? rac->seffects->first_effect : NULL; - while ( rsef != NULL ) { - sef = rsef->seffect; - if ( sef->print != NULL ) - sef->print(sef, rac->data.action, &penv, rsef->context, &impl_keep); - rsef = rsef->next; - } + sieve_result_print_side_effect + (&penv, rac->data.action, rac->seffects, &impl_keep); implicit_keep = implicit_keep && impl_keep; @@ -637,9 +682,49 @@ bool sieve_result_print } if ( implicit_keep && keep != NULL ) *keep = TRUE; - - sieve_result_printf - (&penv, "\nImplicit keep: %s\n", implicit_keep ? "yes" : "no"); + + sieve_result_printf(&penv, "\nImplicit keep:\n\n"); + + if ( implicit_keep ) { + bool dummy = TRUE; + + if ( act_keep == NULL ) + sieve_result_action_printf(&penv, "keep"); + else { + /* Scan for execution of keep-equal actions */ + rac = result->first_action; + while ( act_keep != NULL && rac != NULL ) { + if ( rac->data.action == act_keep && act_keep->equals != NULL && + act_keep->equals(senv, NULL, rac->data.context) + && rac->data.executed ) { + act_keep = NULL; + } + + rac = rac->next; + } + + if ( act_keep == NULL ) { + sieve_result_printf(&penv, + " (none; keep or equivalent action executed earlier)\n"); + } else { + act_keep->print(act_keep, &penv, NULL, &dummy); + + /* Apply any implicit side effects if applicable */ + if ( result->action_contexts != NULL ) { + struct sieve_result_action_context *actctx; + + /* Check for implicit side effects to keep action */ + actctx = (struct sieve_result_action_context *) + hash_table_lookup(result->action_contexts, act_keep); + + if ( actctx != NULL && actctx->seffects != NULL ) + sieve_result_print_side_effect + (&penv, act_keep, actctx->seffects, &dummy); + } + } + } + } else + sieve_result_printf(&penv, " (none)\n"); return TRUE; } @@ -651,6 +736,7 @@ bool sieve_result_print static bool _sieve_result_implicit_keep (struct sieve_result *result, bool rollback) { + struct sieve_result_action *rac; bool success = TRUE; bool dummy = TRUE; struct sieve_result_side_effect *rsef, *rsef_first = NULL; @@ -659,7 +745,18 @@ static bool _sieve_result_implicit_keep /* If keep is a non-action, return right away */ if ( act_keep == NULL ) return TRUE; - + + /* Scan for execution of keep-equal actions */ + rac = result->first_action; + while ( rac != NULL ) { + if ( rac->data.action == act_keep && act_keep->equals != NULL && + act_keep->equals(result->action_env.scriptenv, NULL, rac->data.context) && + rac->data.executed ) + return TRUE; + + rac = rac->next; + } + /* Apply any implicit side effects if applicable */ if ( !rollback && result->action_contexts != NULL ) { struct sieve_result_action_context *actctx; @@ -754,7 +851,8 @@ void sieve_result_mark_executed(struct sieve_result *result) rac = first_action; while ( rac != NULL ) { - rac->data.executed = TRUE; + if ( rac->data.action != NULL ) + rac->data.executed = TRUE; rac = rac->next; } @@ -872,23 +970,17 @@ int sieve_result_execute if ( rac->keep && keep != NULL ) *keep = TRUE; - /* Skip executed actions */ - if ( rac->data.executed ) { + /* Skip non-actions (inactive keep) and executed ones */ + if ( act == NULL || rac->data.executed ) { rac = rac->next; continue; } - rac->data.executed = TRUE; - - /* Skip non-actions (inactive keep) */ - if ( act == NULL ) { - rac = rac->next; - continue; + if ( act->commit != NULL ) { + rac->data.executed = act->commit + (act, &result->action_env, rac->tr_context, &impl_keep); + commit_ok = rac->data.executed && commit_ok; } - - if ( act->commit != NULL ) - commit_ok = act->commit - (act, &result->action_env, rac->tr_context, &impl_keep) && commit_ok; /* Execute post_commit event of side effects */ rsef = rac->seffects != NULL ? rac->seffects->first_effect : NULL; diff --git a/src/lib-sieve/sieve-result.h b/src/lib-sieve/sieve-result.h index 41dff38f5bf164ca9a4a9bdb4683577d27e5b5be..6457d1655140a57b04e66f02dda4dc18fb87e14c 100644 --- a/src/lib-sieve/sieve-result.h +++ b/src/lib-sieve/sieve-result.h @@ -91,6 +91,9 @@ int sieve_result_add_keep (const struct sieve_runtime_env *renv, struct sieve_side_effects_list *seffects, unsigned int source_line); +void sieve_result_set_keep_action + (struct sieve_result *result, const struct sieve_action *action); + /* * Result execution */ diff --git a/src/lib-sieve/sieve.c b/src/lib-sieve/sieve.c index ffdd997365d213ae6efe595b160b3dcd388b9564..13f26ce3109928d2ff341efc913eae1f98f27e89 100644 --- a/src/lib-sieve/sieve.c +++ b/src/lib-sieve/sieve.c @@ -11,6 +11,7 @@ #include "sieve-script.h" #include "sieve-ast.h" #include "sieve-binary.h" +#include "sieve-actions.h" #include "sieve-result.h" #include "sieve-parser.h" @@ -357,6 +358,8 @@ struct sieve_multiscript *sieve_multiscript_start result = sieve_result_create(ehandler); pool = sieve_result_pool(result); + sieve_result_set_keep_action(result, NULL); + mscript = p_new(pool, struct sieve_multiscript, 1); mscript->result = result; mscript->msgdata = msgdata; @@ -370,9 +373,12 @@ struct sieve_multiscript *sieve_multiscript_start bool sieve_multiscript_test (struct sieve_multiscript *mscript, struct sieve_binary *sbin, - struct ostream *stream) + bool final, struct ostream *stream) { if ( !mscript->active ) return FALSE; + + if ( final ) + sieve_result_set_keep_action(mscript->result, &act_store); /* Run the script */ mscript->status = sieve_run(sbin, &mscript->result, mscript->msgdata, @@ -399,9 +405,13 @@ bool sieve_multiscript_test } bool sieve_multiscript_execute -(struct sieve_multiscript *mscript, struct sieve_binary *sbin) +(struct sieve_multiscript *mscript, struct sieve_binary *sbin, + bool final) { if ( !mscript->active ) return FALSE; + + if ( final ) + sieve_result_set_keep_action(mscript->result, &act_store); /* Run the script */ mscript->status = sieve_run(sbin, &mscript->result, mscript->msgdata, diff --git a/src/lib-sieve/sieve.h b/src/lib-sieve/sieve.h index 3c5a739dd5208b16cffe3da95e5ec2a717b07bb9..8294d2420fdd1078b59f5036859e4a5b44af9d1a 100644 --- a/src/lib-sieve/sieve.h +++ b/src/lib-sieve/sieve.h @@ -125,9 +125,10 @@ struct sieve_multiscript *sieve_multiscript_start bool sieve_multiscript_test (struct sieve_multiscript *mscript, struct sieve_binary *sbin, - struct ostream *stream); + bool final, struct ostream *stream); bool sieve_multiscript_execute - (struct sieve_multiscript *mscript, struct sieve_binary *sbin); + (struct sieve_multiscript *mscript, struct sieve_binary *sbin, + bool final); int sieve_multiscript_finish(struct sieve_multiscript **mscript); diff --git a/src/sieve-tools/sieve-test.c b/src/sieve-tools/sieve-test.c index 845c4ffa553903764fbdcff115820eecc52c41f0..b5ec3734c487db06c7d5c2c5515ea1f5dc1f5c6f 100644 --- a/src/sieve-tools/sieve-test.c +++ b/src/sieve-tools/sieve-test.c @@ -236,7 +236,8 @@ int main(int argc, char **argv) sieve_tool_dump_binary_to(sbin, dumpfile); /* Test script */ - result = ( sieve_multiscript_test(mscript, sbin, teststream) ? 1 : 0 ); + result = ( sieve_multiscript_test(mscript, sbin, FALSE, teststream) ? + 1 : 0 ); /* Close script */ sieve_close(&sbin); @@ -247,7 +248,7 @@ int main(int argc, char **argv) t_strdup_printf("\n## Executing script: %s\n", scriptfile)); sbin = main_sbin; - (void)sieve_multiscript_test(mscript, main_sbin, teststream); + (void)sieve_multiscript_test(mscript, main_sbin, TRUE, teststream); ret = sieve_multiscript_finish(&mscript); } else { ret = SIEVE_EXEC_FAILURE;