From 641e1c06be6c8b248fe742a2e25b2d9454b833fd Mon Sep 17 00:00:00 2001 From: Stephan Bosch <stephan@rename-it.nl> Date: Tue, 29 Dec 2009 23:14:28 +0100 Subject: [PATCH] Deprecated notify extension: implemented denotify command. --- Makefile.am | 3 +- src/lib-sieve/plugins/notify/cmd-denotify.c | 148 ++++++++-- .../plugins/notify/ext-notify-common.h | 6 +- src/lib-sieve/plugins/notify/ext-notify.c | 9 +- src/lib-sieve/sieve-actions.h | 3 + src/lib-sieve/sieve-comparators.h | 5 +- src/lib-sieve/sieve-result.c | 39 ++- src/lib-sieve/sieve-result.h | 2 + tests/deprecated/notify/denotify.svtest | 279 ++++++++++++++++++ 9 files changed, 457 insertions(+), 37 deletions(-) create mode 100644 tests/deprecated/notify/denotify.svtest diff --git a/Makefile.am b/Makefile.am index 0a1c959a1..095b8d96d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -96,7 +96,8 @@ test_cases = \ tests/deprecated/notify/basic.svtest \ tests/deprecated/notify/mailto.svtest \ tests/deprecated/notify/errors.svtest \ - tests/deprecated/notify/execute.svtest + tests/deprecated/notify/execute.svtest \ + tests/deprecated/notify/denotify.svtest if HAVE_DOVECOT_LIBS diff --git a/src/lib-sieve/plugins/notify/cmd-denotify.c b/src/lib-sieve/plugins/notify/cmd-denotify.c index 2a9ccb80d..d3e879918 100644 --- a/src/lib-sieve/plugins/notify/cmd-denotify.c +++ b/src/lib-sieve/plugins/notify/cmd-denotify.c @@ -10,6 +10,7 @@ #include "sieve-commands.h" #include "sieve-match-types.h" #include "sieve-comparators.h" +#include "sieve-match.h" #include "sieve-actions.h" #include "sieve-validator.h" #include "sieve-generator.h" @@ -20,7 +21,7 @@ #include "ext-notify-common.h" /* - * Denotify command (NOT IMPLEMENTED) + * Denotify command * * Syntax: * denotify [MATCH-TYPE string] [<":low" / ":normal" / ":high">] @@ -29,6 +30,10 @@ static bool cmd_denotify_registered (struct sieve_validator *valdtr, const struct sieve_extension *ext, struct sieve_command_registration *cmd_reg); +static bool cmd_denotify_pre_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd); +static bool cmd_denotify_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd); static bool cmd_denotify_generate (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd); @@ -37,8 +42,8 @@ const struct sieve_command_def cmd_denotify = { SCT_COMMAND, 0, 0, FALSE, FALSE, cmd_denotify_registered, - NULL, - NULL, + cmd_denotify_pre_validate, + cmd_denotify_validate, cmd_denotify_generate, NULL }; @@ -68,9 +73,9 @@ const struct sieve_argument_def denotify_match_tag = { /* Codes for optional operands */ enum cmd_denotify_optional { - OPT_END, - OPT_IMPORTANCE, - OPT_MATCH_TYPE, + OPT_END, + OPT_IMPORTANCE, + OPT_MATCH_TYPE, OPT_MATCH_KEY }; @@ -91,6 +96,14 @@ const struct sieve_operation_def denotify_operation = { cmd_denotify_operation_execute }; +/* + * Command validation context + */ + +struct cmd_denotify_context_data { + struct sieve_ast_argument *match_key_arg; +}; + /* * Tag validation */ @@ -106,11 +119,9 @@ static bool tag_match_type_validate (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, struct sieve_command *cmd) { + struct cmd_denotify_context_data *cmd_data = + (struct cmd_denotify_context_data *) cmd->data; struct sieve_ast_argument *tag = *arg; - const struct sieve_match_type mcht_default = - SIEVE_MATCH_TYPE_DEFAULT(is_match_type); - const struct sieve_comparator cmp_default = - SIEVE_COMPARATOR_DEFAULT(i_octet_comparator); if ( !match_type_tag.validate(valdtr, arg, cmd) ) return FALSE; @@ -118,7 +129,7 @@ static bool tag_match_type_validate if ( *arg == NULL ) { sieve_argument_validate_error(valdtr, tag, "the MATCH-TYPE argument (:%s) for the denotify command requires " - "an additional key-string paramterer, but no more arguments were found", + "an additional key-string parameter, but no more arguments were found", sieve_ast_argument_tag(tag)); return FALSE; } @@ -135,14 +146,11 @@ static bool tag_match_type_validate if ( !sieve_validator_argument_activate(valdtr, cmd, *arg, FALSE) ) return FALSE; - if ( !sieve_match_type_validate - (valdtr, cmd, *arg, &mcht_default, &cmp_default) ) - return FALSE; - tag->argument->def = &match_type_tag; tag->argument->ext = NULL; (*arg)->argument->id_code = OPT_MATCH_KEY; + cmd_data->match_key_arg = *arg; *arg = sieve_ast_argument_next(*arg); @@ -165,6 +173,44 @@ static bool cmd_denotify_registered return TRUE; } +/* + * Command validation + */ + +static bool cmd_denotify_pre_validate +(struct sieve_validator *valdtr ATTR_UNUSED, + struct sieve_command *cmd) +{ + struct cmd_denotify_context_data *ctx_data; + + /* Assign context */ + ctx_data = p_new(sieve_command_pool(cmd), + struct cmd_denotify_context_data, 1); + cmd->data = (void *) ctx_data; + + return TRUE; +} + +static bool cmd_denotify_validate +(struct sieve_validator *valdtr, struct sieve_command *cmd) +{ + struct cmd_denotify_context_data *ctx_data = + (struct cmd_denotify_context_data *) cmd->data; + struct sieve_ast_argument *key_arg = ctx_data->match_key_arg; + const struct sieve_match_type mcht_default = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + const struct sieve_comparator cmp_default = + SIEVE_COMPARATOR_DEFAULT(i_octet_comparator); + + if ( key_arg != NULL ) { + if ( !sieve_match_type_validate + (valdtr, cmd, key_arg, &mcht_default, &cmp_default) ) + return FALSE; + } + + return TRUE; +} + /* * Code generation */ @@ -237,14 +283,19 @@ static bool cmd_denotify_operation_dump static int cmd_denotify_operation_execute (const struct sieve_runtime_env *renv, sieve_size_t *address) { + bool result = TRUE; int opt_code = 1; - sieve_number_t importance = 1; struct sieve_match_type mcht = SIEVE_MATCH_TYPE_DEFAULT(is_match_type); -/* const struct sieve_comparator cmp = - SIEVE_COMPARATOR_DEFAULT(i_octet_comparator);*/ - string_t *match_key = NULL; + const struct sieve_comparator cmp = + SIEVE_COMPARATOR_DEFAULT(i_octet_comparator); + struct sieve_coded_stringlist *match_key = NULL; + sieve_number_t importance = 0; + struct sieve_match_context *mctx; + struct sieve_result_iterate_context *rictx; + const struct sieve_action *action; unsigned int source_line; + int ret; /* * Read operands @@ -274,8 +325,8 @@ static int cmd_denotify_operation_execute } break; case OPT_MATCH_KEY: - if ( !sieve_opr_string_read(renv, address, &match_key) ) { - sieve_runtime_trace_error(renv, "invalid from operand"); + if ( (match_key=sieve_opr_stringlist_read(renv, address)) == NULL ) { + sieve_runtime_trace_error(renv, "invalid match key operand"); return SIEVE_EXEC_BIN_CORRUPT; } break; @@ -303,7 +354,60 @@ static int cmd_denotify_operation_execute * Perform operation */ - sieve_runtime_trace(renv, "DENOTIFY action"); + sieve_runtime_trace(renv, "DENOTIFY action"); + + /* Either do string matching or just kill all notify actions */ + if ( match_key != NULL ) { + + /* Initialize match */ + mctx = sieve_match_begin(renv->interp, &mcht, &cmp, NULL, match_key); + + /* Iterate through all actions */ + rictx = sieve_result_iterate_init(renv->result); + + while ( result && + (action=sieve_result_iterate_next(rictx, NULL)) != NULL ) { + if ( sieve_action_is(action, act_notify_old) ) { + struct ext_notify_action *nact = + (struct ext_notify_action *) action->context; + + if ( importance == 0 || nact->importance == importance ) { + if ( (ret=sieve_match_value(mctx, nact->id, strlen(nact->id))) + < 0 ) { + result = FALSE; + break; + } + + if ( ret > 0 ) + sieve_result_iterate_delete(rictx); + } + } + } + + /* Finish match */ + if ( sieve_match_end(&mctx) < 0 ) + result = FALSE; + + if ( !result ) { + sieve_runtime_trace_error(renv, "invalid string-list item"); + return SIEVE_EXEC_BIN_CORRUPT; + } + } else { + /* Iterate through all actions */ + rictx = sieve_result_iterate_init(renv->result); + + while ( result && + (action=sieve_result_iterate_next(rictx, NULL)) != NULL ) { + + if ( sieve_action_is(action, act_notify_old) ) { + struct ext_notify_action *nact = + (struct ext_notify_action *) action->context; + + if ( importance == 0 || nact->importance == importance ) + sieve_result_iterate_delete(rictx); + } + } + } return SIEVE_EXEC_OK; } diff --git a/src/lib-sieve/plugins/notify/ext-notify-common.h b/src/lib-sieve/plugins/notify/ext-notify-common.h index 87deb188d..dd2f07cf5 100644 --- a/src/lib-sieve/plugins/notify/ext-notify-common.h +++ b/src/lib-sieve/plugins/notify/ext-notify-common.h @@ -37,7 +37,11 @@ enum ext_notify_opcode { EXT_NOTIFY_OPERATION_DENOTIFY, }; -/* Action context */ +/* + * Actions + */ + +const struct sieve_action_def act_notify_old; struct ext_notify_recipient { const char *full; diff --git a/src/lib-sieve/plugins/notify/ext-notify.c b/src/lib-sieve/plugins/notify/ext-notify.c index 1fd110f5f..5251f6cf0 100644 --- a/src/lib-sieve/plugins/notify/ext-notify.c +++ b/src/lib-sieve/plugins/notify/ext-notify.c @@ -6,16 +6,11 @@ * * Authors: Stephan Bosch * Specification: draft-ietf-sieve-notify-00.txt - * Implementation: deprecated; provided for backwards compatibility - * Status: deprecated + * Implementation: fully backwards compatible + * Status: testing (deprecated) * */ -/* FIXME: Currently the following CMUSieve features are not supported: - * - * (*) The $text$ substitution is not available for the :message argument. - */ - #include <stdio.h> #include "sieve-common.h" diff --git a/src/lib-sieve/sieve-actions.h b/src/lib-sieve/sieve-actions.h index 48e6eecf5..411dac845 100644 --- a/src/lib-sieve/sieve-actions.h +++ b/src/lib-sieve/sieve-actions.h @@ -92,6 +92,9 @@ struct sieve_action { bool executed; }; +#define sieve_action_is(act, definition) \ + ( (act)->def == &(definition) ) + /* * Action side effects */ diff --git a/src/lib-sieve/sieve-comparators.h b/src/lib-sieve/sieve-comparators.h index 501ee395f..1eee4cfa7 100644 --- a/src/lib-sieve/sieve-comparators.h +++ b/src/lib-sieve/sieve-comparators.h @@ -91,9 +91,10 @@ static inline const struct sieve_comparator *sieve_comparator_copy extern const struct sieve_argument_def comparator_tag; static inline bool sieve_argument_is_comparator - (struct sieve_ast_argument *arg) +(struct sieve_ast_argument *arg) { - return arg->argument->def == &comparator_tag; + return ( arg->argument != NULL && + (arg->argument->def == &comparator_tag) ); } void sieve_comparators_link_tag diff --git a/src/lib-sieve/sieve-result.c b/src/lib-sieve/sieve-result.c index ff875591c..6cc0515e5 100644 --- a/src/lib-sieve/sieve-result.c +++ b/src/lib-sieve/sieve-result.c @@ -1237,7 +1237,8 @@ int sieve_result_execute struct sieve_result_iterate_context { struct sieve_result *result; - struct sieve_result_action *action; + struct sieve_result_action *current_action; + struct sieve_result_action *next_action; }; struct sieve_result_iterate_context *sieve_result_iterate_init @@ -1247,7 +1248,8 @@ struct sieve_result_iterate_context *sieve_result_iterate_init t_new(struct sieve_result_iterate_context, 1); rictx->result = result; - rictx->action = result->first_action; + rictx->current_action = NULL; + rictx->next_action = result->first_action; return rictx; } @@ -1260,9 +1262,9 @@ const struct sieve_action *sieve_result_iterate_next if ( rictx == NULL ) return NULL; - rac = rictx->action; + rac = rictx->current_action = rictx->next_action; if ( rac != NULL ) { - rictx->action = rac->next; + rictx->next_action = rac->next; if ( keep != NULL ) *keep = rac->keep; @@ -1273,6 +1275,35 @@ const struct sieve_action *sieve_result_iterate_next 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->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; + + /* Skip to next action in iteration */ + + rictx->current_action = NULL; +} + /* * Side effects list */ diff --git a/src/lib-sieve/sieve-result.h b/src/lib-sieve/sieve-result.h index 1414a80e1..d2ebac946 100644 --- a/src/lib-sieve/sieve-result.h +++ b/src/lib-sieve/sieve-result.h @@ -148,6 +148,8 @@ struct sieve_result_iterate_context *sieve_result_iterate_init (struct sieve_result *result); const struct sieve_action *sieve_result_iterate_next (struct sieve_result_iterate_context *rictx, bool *keep); +void sieve_result_iterate_delete + (struct sieve_result_iterate_context *rictx); /* * Side effects list diff --git a/tests/deprecated/notify/denotify.svtest b/tests/deprecated/notify/denotify.svtest new file mode 100644 index 000000000..7b2ff619d --- /dev/null +++ b/tests/deprecated/notify/denotify.svtest @@ -0,0 +1,279 @@ +require "vnd.dovecot.testsuite"; +require "notify"; +require "envelope"; + +/* + * Denotify all + */ + +test_set "message" text: +From: stephan@rename-it.nl +To: nico@vestingbar.nl +Subject: Frop! + +Klutsefluts. +. +; + +test "Denotify All" { + notify :options "timo@example.com"; + notify :options "stephan@dovecot.org"; + notify :options "postmaster@vestingbar.nl"; + denotify; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + if test_message :smtp 0 { + test_fail "no notifications should have been sent"; + } +} + +/* + * Denotify First + */ + +test_result_reset; + +test_set "message" text: +From: stephan@rename-it.nl +To: nico@vestingbar.nl +Subject: Frop! + +Klutsefluts. +. +; + +test "Denotify ID First" { + /* #1 */ + notify :options "timo@example.com" :id "aap"; + + /* #2 */ + notify :options "stephan@dovecot.org" :id "noot"; + + /* #3 */ + notify :options "postmaster@vestingbar.nl" :id "mies"; + + denotify :is "aap"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + if not test_message :smtp 0 { + test_fail "two notifications should have been sent (#2 missing)"; + } + + if not envelope "to" "stephan@dovecot.org" { + test_fail "message #2 unexpectedly missing from output"; + } + + if not test_message :smtp 1 { + test_fail "two notifications should have been sent (#3 missing)"; + } + + if not envelope "to" "postmaster@vestingbar.nl" { + test_fail "message #3 unexpectedly missing from output"; + } + + if test_message :smtp 2 { + test_fail "too many notifications sent"; + } +} + +/* + * Denotify Middle + */ + +test_result_reset; + +test_set "message" text: +From: stephan@rename-it.nl +To: nico@vestingbar.nl +Subject: Frop! + +Klutsefluts. +. +; + +test "Denotify ID Middle" { + /* #1 */ + notify :options "timo@example.com" :id "aap"; + + /* #2 */ + notify :options "stephan@dovecot.org" :id "noot"; + + /* #3 */ + notify :options "postmaster@vestingbar.nl" :id "mies"; + + denotify :is "noot"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + if not test_message :smtp 0 { + test_fail "two notifications should have been sent (#1 missing)"; + } + + if not envelope "to" "timo@example.com" { + test_fail "message #1 unexpectedly missing from output"; + } + + if not test_message :smtp 1 { + test_fail "two notifications should have been sent (#3 missing)"; + } + + if not envelope "to" "postmaster@vestingbar.nl" { + test_fail "message #3 unexpectedly missing from output"; + } + + if test_message :smtp 2 { + test_fail "too many notifications sent"; + } +} + +/* + * Denotify Last + */ + +test_result_reset; + +test_set "message" text: +From: stephan@rename-it.nl +To: nico@vestingbar.nl +Subject: Frop! + +Klutsefluts. +. +; + +test "Denotify ID Last" { + /* #1 */ + notify :options "timo@example.com" :id "aap"; + + /* #2 */ + notify :options "stephan@dovecot.org" :id "noot"; + + /* #3 */ + notify :options "postmaster@vestingbar.nl" :id "mies"; + + denotify :is "mies"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + if not test_message :smtp 0 { + test_fail "two notifications should have been sent (#1 missing)"; + } + + if not envelope "to" "timo@example.com" { + test_fail "message #1 unexpectedly missing from output"; + } + + if not test_message :smtp 1 { + test_fail "two notifications should have been sent (#2 missing)"; + } + + if not envelope "to" "stephan@dovecot.org" { + test_fail "message #2 unexpectedly missing from output"; + } + + if test_message :smtp 2 { + test_fail "too many notifications sent"; + } +} + + +/* + * Denotify Matching + */ + +test_result_reset; + +test_set "message" text: +From: stephan@rename-it.nl +To: nico@vestingbar.nl +Subject: Frop! + +Klutsefluts. +. +; + +test "Denotify Matching" { + /* #1 */ + notify :options "timo@example.com" :id "frop"; + + /* #2 */ + notify :options "stephan@dovecot.org" :id "noot"; + + /* #3 */ + notify :options "postmaster@vestingbar.nl" :id "friep"; + + denotify :matches "fr*"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + if not test_message :smtp 0 { + test_fail "one notification should have been sent"; + } + + if not envelope "to" "stephan@dovecot.org" { + test_fail "message #2 unexpectedly missing from output"; + } + + if test_message :smtp 1 { + test_fail "too many notifications sent"; + } +} + + +/* + * Denotify Matching + */ + +test_result_reset; + +test_set "message" text: +From: stephan@rename-it.nl +To: nico@vestingbar.nl +Subject: Frop! + +Klutsefluts. +. +; + +test "Denotify Matching Importance" { + /* #1 */ + notify :options "timo@example.com" :id "frop" :low; + + /* #2 */ + notify :options "stephan@dovecot.org" :id "frml" :high; + + /* #3 */ + notify :options "postmaster@vestingbar.nl" :id "friep" :low; + + denotify :matches "fr*" :low; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + if not test_message :smtp 0 { + test_fail "one notification should have been sent"; + } + + if not envelope "to" "stephan@dovecot.org" { + test_fail "message #2 unexpectedly missing from output"; + } + + if test_message :smtp 1 { + test_fail "too many notifications sent"; + } +} + + -- GitLab