From d3f85202995853bba91c481b11bc88713158277c Mon Sep 17 00:00:00 2001 From: Stephan Bosch <stephan.bosch@open-xchange.com> Date: Sun, 27 Mar 2022 22:27:12 +0200 Subject: [PATCH] lib-sieve: plugins: Remove support for deprecated notify extension. --- Makefile.am | 5 - configure.ac | 1 - src/lib-sieve/Makefile.am | 1 - src/lib-sieve/plugins/Makefile.am | 1 - src/lib-sieve/plugins/notify/Makefile.am | 20 - src/lib-sieve/plugins/notify/cmd-denotify.c | 389 -------- src/lib-sieve/plugins/notify/cmd-notify.c | 900 ------------------ .../plugins/notify/ext-notify-common.c | 354 ------- .../plugins/notify/ext-notify-common.h | 66 -- .../plugins/notify/ext-notify-limits.h | 7 - src/lib-sieve/plugins/notify/ext-notify.c | 108 --- src/lib-sieve/sieve-extensions.c | 2 - tests/deprecated/notify/basic.svtest | 59 -- tests/deprecated/notify/denotify.svtest | 279 ------ tests/deprecated/notify/errors.svtest | 33 - .../notify/errors/conflict-ihave.sieve | 8 - tests/deprecated/notify/errors/conflict.sieve | 4 - tests/deprecated/notify/errors/options.sieve | 11 - tests/deprecated/notify/execute.svtest | 25 - .../notify/execute/duplicates.sieve | 4 - tests/deprecated/notify/mailto.svtest | 317 ------ 21 files changed, 2594 deletions(-) delete mode 100644 src/lib-sieve/plugins/notify/Makefile.am delete mode 100644 src/lib-sieve/plugins/notify/cmd-denotify.c delete mode 100644 src/lib-sieve/plugins/notify/cmd-notify.c delete mode 100644 src/lib-sieve/plugins/notify/ext-notify-common.c delete mode 100644 src/lib-sieve/plugins/notify/ext-notify-common.h delete mode 100644 src/lib-sieve/plugins/notify/ext-notify-limits.h delete mode 100644 src/lib-sieve/plugins/notify/ext-notify.c delete mode 100644 tests/deprecated/notify/basic.svtest delete mode 100644 tests/deprecated/notify/denotify.svtest delete mode 100644 tests/deprecated/notify/errors.svtest delete mode 100644 tests/deprecated/notify/errors/conflict-ihave.sieve delete mode 100644 tests/deprecated/notify/errors/conflict.sieve delete mode 100644 tests/deprecated/notify/errors/options.sieve delete mode 100644 tests/deprecated/notify/execute.svtest delete mode 100644 tests/deprecated/notify/execute/duplicates.sieve delete mode 100644 tests/deprecated/notify/mailto.svtest diff --git a/Makefile.am b/Makefile.am index 39f32d6a5..a46430b65 100644 --- a/Makefile.am +++ b/Makefile.am @@ -182,11 +182,6 @@ test_cases = \ tests/extensions/vnd.dovecot/environment/variables.svtest \ tests/extensions/vnd.dovecot/report/errors.svtest \ tests/extensions/vnd.dovecot/report/execute.svtest \ - tests/deprecated/notify/basic.svtest \ - tests/deprecated/notify/mailto.svtest \ - tests/deprecated/notify/errors.svtest \ - tests/deprecated/notify/execute.svtest \ - tests/deprecated/notify/denotify.svtest \ tests/deprecated/imapflags/execute.svtest \ tests/deprecated/imapflags/errors.svtest \ $(test_unfinished) diff --git a/configure.ac b/configure.ac index ebd85d9a3..e4defa0bc 100644 --- a/configure.ac +++ b/configure.ac @@ -214,7 +214,6 @@ src/lib-sieve/plugins/body/Makefile src/lib-sieve/plugins/variables/Makefile src/lib-sieve/plugins/enotify/Makefile src/lib-sieve/plugins/enotify/mailto/Makefile -src/lib-sieve/plugins/notify/Makefile src/lib-sieve/plugins/environment/Makefile src/lib-sieve/plugins/mailbox/Makefile src/lib-sieve/plugins/date/Makefile diff --git a/src/lib-sieve/Makefile.am b/src/lib-sieve/Makefile.am index 2e80871bb..7fb2a4fa2 100644 --- a/src/lib-sieve/Makefile.am +++ b/src/lib-sieve/Makefile.am @@ -67,7 +67,6 @@ plugins = \ $(extdir)/body/libsieve_ext_body.la \ $(extdir)/variables/libsieve_ext_variables.la \ $(extdir)/enotify/libsieve_ext_enotify.la \ - $(extdir)/notify/libsieve_ext_notify.la \ $(extdir)/environment/libsieve_ext_environment.la \ $(extdir)/mailbox/libsieve_ext_mailbox.la \ $(extdir)/date/libsieve_ext_date.la \ diff --git a/src/lib-sieve/plugins/Makefile.am b/src/lib-sieve/plugins/Makefile.am index 2a1c0548f..eb3c8f50d 100644 --- a/src/lib-sieve/plugins/Makefile.am +++ b/src/lib-sieve/plugins/Makefile.am @@ -14,7 +14,6 @@ SUBDIRS = \ body \ variables \ enotify \ - notify \ environment \ mailbox \ date \ diff --git a/src/lib-sieve/plugins/notify/Makefile.am b/src/lib-sieve/plugins/notify/Makefile.am deleted file mode 100644 index aaa76b357..000000000 --- a/src/lib-sieve/plugins/notify/Makefile.am +++ /dev/null @@ -1,20 +0,0 @@ -noinst_LTLIBRARIES = libsieve_ext_notify.la - -AM_CPPFLAGS = \ - -I$(srcdir)/../.. \ - -I$(srcdir)/../../util \ - $(LIBDOVECOT_INCLUDE) - -commands = \ - cmd-notify.c \ - cmd-denotify.c - -libsieve_ext_notify_la_SOURCES = \ - ext-notify.c \ - ext-notify-common.c \ - $(commands) - -noinst_HEADERS = \ - ext-notify-common.h \ - ext-notify-limits.h - diff --git a/src/lib-sieve/plugins/notify/cmd-denotify.c b/src/lib-sieve/plugins/notify/cmd-denotify.c deleted file mode 100644 index 876980885..000000000 --- a/src/lib-sieve/plugins/notify/cmd-denotify.c +++ /dev/null @@ -1,389 +0,0 @@ -/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file - */ - -#include "lib.h" - -#include "sieve-common.h" -#include "sieve-stringlist.h" -#include "sieve-code.h" -#include "sieve-extensions.h" -#include "sieve-ast.h" -#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" -#include "sieve-interpreter.h" -#include "sieve-dump.h" -#include "sieve-result.h" - -#include "ext-notify-common.h" - -/* - * Denotify command - * - * Syntax: - * denotify [MATCH-TYPE string] [<":low" / ":normal" / ":high">] - */ - -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); - -const struct sieve_command_def cmd_denotify = { - .identifier = "denotify", - .type = SCT_COMMAND, - .positional_args = 0, - .subtests = 0, - .block_allowed = FALSE, - .block_required = FALSE, - .registered = cmd_denotify_registered, - .pre_validate = cmd_denotify_pre_validate, - .validate = cmd_denotify_validate, - .generate = cmd_denotify_generate -}; - -/* - * Tagged arguments - */ - -/* Forward declarations */ - -static bool tag_match_type_is_instance_of - (struct sieve_validator *validator, struct sieve_command *cmd, - const struct sieve_extension *ext, const char *identifier, void **data); -static bool tag_match_type_validate - (struct sieve_validator *validator, struct sieve_ast_argument **arg, - struct sieve_command *cmd); - -/* Argument object */ - -const struct sieve_argument_def denotify_match_tag = { - .identifier = "MATCH-TYPE-STRING", - .is_instance_of = tag_match_type_is_instance_of, - .validate = tag_match_type_validate -}; - -/* Codes for optional operands */ - -enum cmd_denotify_optional { - OPT_END, - OPT_IMPORTANCE, - OPT_MATCH_TYPE, - OPT_MATCH_KEY -}; - -/* - * Denotify operation - */ - -static bool cmd_denotify_operation_dump - (const struct sieve_dumptime_env *denv, sieve_size_t *address); -static int cmd_denotify_operation_execute - (const struct sieve_runtime_env *renv, sieve_size_t *address); - -const struct sieve_operation_def denotify_operation = { - .mnemonic = "DENOTIFY", - .ext_def = ¬ify_extension, - .code = EXT_NOTIFY_OPERATION_DENOTIFY, - .dump = cmd_denotify_operation_dump, - .execute = cmd_denotify_operation_execute -}; - -/* - * Command validation context - */ - -struct cmd_denotify_context_data { - struct sieve_ast_argument *match_key_arg; -}; - -/* - * Tag validation - */ - -static bool tag_match_type_is_instance_of -(struct sieve_validator *valdtr, struct sieve_command *cmd, - const struct sieve_extension *ext, const char *identifier, void **data) -{ - return match_type_tag.is_instance_of(valdtr, cmd, ext, identifier, data); -} - -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; - - i_assert(tag != NULL); - - if ( !match_type_tag.validate(valdtr, arg, cmd) ) - return FALSE; - - if ( *arg == NULL ) { - sieve_argument_validate_error(valdtr, tag, - "the MATCH-TYPE argument (:%s) for the denotify command requires " - "an additional key-string parameter, but no more arguments were found", - sieve_ast_argument_tag(tag)); - return FALSE; - } - - if ( sieve_ast_argument_type(*arg) != SAAT_STRING ) - { - sieve_argument_validate_error(valdtr, *arg, - "the MATCH-TYPE argument (:%s) for the denotify command requires " - "an additional key-string parameter, but %s was found", - sieve_ast_argument_tag(tag), sieve_ast_argument_name(*arg)); - return FALSE; - } - - if ( !sieve_validator_argument_activate(valdtr, cmd, *arg, FALSE) ) - 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); - - return TRUE; -} - -/* - * Command registration - */ - -static bool cmd_denotify_registered -(struct sieve_validator *valdtr, const struct sieve_extension *ext, - struct sieve_command_registration *cmd_reg) -{ - sieve_validator_register_tag - (valdtr, cmd_reg, ext, &denotify_match_tag, OPT_MATCH_TYPE); - - ext_notify_register_importance_tags(valdtr, cmd_reg, ext, OPT_IMPORTANCE); - - 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 - */ - -static bool cmd_denotify_generate -(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) -{ - sieve_operation_emit(cgenv->sblock, cmd->ext, &denotify_operation); - - /* Generate arguments */ - return sieve_generate_arguments(cgenv, cmd, NULL); -} - -/* - * Code dump - */ - -static bool cmd_denotify_operation_dump -(const struct sieve_dumptime_env *denv, sieve_size_t *address) -{ - const struct sieve_operation *op = denv->oprtn; - int opt_code = 0; - - sieve_code_dumpf(denv, "%s", sieve_operation_mnemonic(op)); - sieve_code_descend(denv); - - for (;;) { - int opt; - bool opok = TRUE; - - if ( (opt=sieve_opr_optional_dump(denv, address, &opt_code)) < 0 ) - return FALSE; - - if ( opt == 0 ) break; - - switch ( opt_code ) { - case OPT_MATCH_KEY: - opok = sieve_opr_string_dump(denv, address, "key-string"); - break; - case OPT_MATCH_TYPE: - opok = sieve_opr_match_type_dump(denv, address); - break; - case OPT_IMPORTANCE: - opok = sieve_opr_number_dump(denv, address, "importance"); - break; - default: - return FALSE; - } - - if ( !opok ) return FALSE; - } - - return TRUE; -} - -/* - * Code execution - */ - -static int cmd_denotify_operation_execute -(const struct sieve_runtime_env *renv, sieve_size_t *address) -{ - int opt_code = 0; - struct sieve_match_type mcht = - SIEVE_MATCH_TYPE_DEFAULT(is_match_type); - const struct sieve_comparator cmp = - SIEVE_COMPARATOR_DEFAULT(i_octet_comparator); - struct sieve_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; - int ret; - - /* - * Read operands - */ - - /* Optional operands */ - - for (;;) { - int opt; - - if ( (opt=sieve_opr_optional_read(renv, address, &opt_code)) < 0 ) - return SIEVE_EXEC_BIN_CORRUPT; - - if ( opt == 0 ) break; - - switch ( opt_code ) { - case OPT_MATCH_TYPE: - ret = sieve_opr_match_type_read(renv, address, &mcht); - break; - case OPT_MATCH_KEY: - ret = sieve_opr_stringlist_read(renv, address, "match key", &match_key); - break; - case OPT_IMPORTANCE: - ret = sieve_opr_number_read(renv, address, "importance", &importance); - break; - default: - sieve_runtime_trace_error(renv, "unknown optional operand"); - return SIEVE_EXEC_BIN_CORRUPT; - } - - if ( ret <= 0 ) return ret; - } - - /* - * Perform operation - */ - - /* Enforce 0 < importance < 4 (just to be sure) */ - - if ( importance < 1 ) - importance = 1; - else if ( importance > 3 ) - importance = 3; - - /* Trace */ - - sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "denotify action"); - - /* Either do string matching or just kill all notify actions */ - if ( match_key != NULL ) { - /* Initialize match */ - mctx = sieve_match_begin(renv, &mcht, &cmp); - - /* Iterate through all notify actions and delete those that match */ - rictx = sieve_result_iterate_init(renv->result); - - while ( (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 ) { - int match; - - if ( (match=sieve_match_value - (mctx, nact->id, strlen(nact->id), match_key)) < 0 ) - break; - - if ( match > 0 ) - sieve_result_iterate_delete(rictx); - } - } - } - - /* Finish match */ - if ( sieve_match_end(&mctx, &ret) < 0 ) - return ret; - - } else { - /* Delete all notify actions */ - rictx = sieve_result_iterate_init(renv->result); - - while ( (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/cmd-notify.c b/src/lib-sieve/plugins/notify/cmd-notify.c deleted file mode 100644 index a683c3131..000000000 --- a/src/lib-sieve/plugins/notify/cmd-notify.c +++ /dev/null @@ -1,900 +0,0 @@ -/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file - */ - -#include "lib.h" -#include "array.h" -#include "str.h" -#include "ioloop.h" -#include "str-sanitize.h" -#include "ostream.h" -#include "message-date.h" -#include "mail-storage.h" - -#include "rfc2822.h" - -#include "sieve-common.h" -#include "sieve-stringlist.h" -#include "sieve-code.h" -#include "sieve-extensions.h" -#include "sieve-commands.h" -#include "sieve-actions.h" -#include "sieve-validator.h" -#include "sieve-generator.h" -#include "sieve-interpreter.h" -#include "sieve-dump.h" -#include "sieve-result.h" -#include "sieve-address.h" -#include "sieve-message.h" -#include "sieve-smtp.h" - -#include "ext-notify-common.h" -#include "ext-notify-limits.h" - -#include <ctype.h> - -/* Notify command (DEPRECATED) - * - * Syntax: - * notify [":method" string] [":id" string] [":options" string-list] - * [<":low" / ":normal" / ":high">] ["message:" string] - * - */ - -static bool -cmd_notify_registered(struct sieve_validator *valdtr, - const struct sieve_extension *ext, - struct sieve_command_registration *cmd_reg); -static bool -cmd_notify_pre_validate(struct sieve_validator *valdtr, - struct sieve_command *cmd); -static bool -cmd_notify_validate(struct sieve_validator *valdtr, - struct sieve_command *cmd); -static bool -cmd_notify_generate(const struct sieve_codegen_env *cgenv, - struct sieve_command *ctx); - -const struct sieve_command_def cmd_notify_old = { - .identifier = "notify", - .type = SCT_COMMAND, - .positional_args = 0, - .subtests = 0, - .block_allowed = FALSE, - .block_required = FALSE, - .registered = cmd_notify_registered, - .pre_validate = cmd_notify_pre_validate, - .validate = cmd_notify_validate, - .generate = cmd_notify_generate -}; - -/* - * Tagged arguments - */ - -/* Forward declarations */ - -static bool -cmd_notify_validate_string_tag(struct sieve_validator *valdtr, - struct sieve_ast_argument **arg, - struct sieve_command *cmd); -static bool -cmd_notify_validate_stringlist_tag(struct sieve_validator *valdtr, - struct sieve_ast_argument **arg, - struct sieve_command *cmd); - -/* Argument objects */ - -static const struct sieve_argument_def notify_method_tag = { - .identifier = "method", - .validate = cmd_notify_validate_string_tag -}; - -static const struct sieve_argument_def notify_options_tag = { - .identifier = "options", - .validate = cmd_notify_validate_stringlist_tag -}; - -static const struct sieve_argument_def notify_id_tag = { - .identifier = "id", - .validate = cmd_notify_validate_string_tag -}; - -static const struct sieve_argument_def notify_message_tag = { - .identifier = "message", - .validate = cmd_notify_validate_string_tag -}; - -/* - * Notify operation - */ - -static bool -cmd_notify_operation_dump(const struct sieve_dumptime_env *denv, - sieve_size_t *address); -static int -cmd_notify_operation_execute(const struct sieve_runtime_env *renv, - sieve_size_t *address); - -const struct sieve_operation_def notify_old_operation = { - .mnemonic = "NOTIFY", - .ext_def = ¬ify_extension, - .code = EXT_NOTIFY_OPERATION_NOTIFY, - .dump = cmd_notify_operation_dump, - .execute = cmd_notify_operation_execute -}; - -/* Codes for optional operands */ - -enum cmd_notify_optional { - OPT_END, - OPT_MESSAGE, - OPT_IMPORTANCE, - OPT_OPTIONS, - OPT_ID -}; - -/* - * Notify action - */ - -/* Forward declarations */ - -static int -act_notify_check_duplicate(const struct sieve_runtime_env *renv, - const struct sieve_action *act, - const struct sieve_action *act_other); -static void -act_notify_print(const struct sieve_action *action, - const struct sieve_result_print_env *rpenv, bool *keep); -static int -act_notify_commit(const struct sieve_action_exec_env *aenv, - void *tr_context); - -/* Action object */ - -const struct sieve_action_def act_notify_old = { - .name = "notify", - .check_duplicate = act_notify_check_duplicate, - .print = act_notify_print, - .commit = act_notify_commit -}; - -/* - * Command validation context - */ - -struct cmd_notify_context_data { - struct sieve_ast_argument *id; - struct sieve_ast_argument *method; - struct sieve_ast_argument *options; - struct sieve_ast_argument *message; -}; - -/* - * Tag validation - */ - -static bool -cmd_notify_validate_string_tag(struct sieve_validator *valdtr, - struct sieve_ast_argument **arg, - struct sieve_command *cmd) -{ - struct sieve_ast_argument *tag = *arg; - struct cmd_notify_context_data *ctx_data = - (struct cmd_notify_context_data *)cmd->data; - - /* Detach the tag itself */ - *arg = sieve_ast_arguments_detach(*arg, 1); - - /* Check syntax: - * :id <string> - * :method <string> - * :message <string> - */ - if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0, - SAAT_STRING, FALSE)) - return FALSE; - - if (sieve_argument_is(tag, notify_method_tag)) { - ctx_data->method = *arg; - - /* Removed */ - *arg = sieve_ast_arguments_detach(*arg, 1); - } else if (sieve_argument_is(tag, notify_id_tag)) { - ctx_data->id = *arg; - - /* Skip parameter */ - *arg = sieve_ast_argument_next(*arg); - } else if (sieve_argument_is(tag, notify_message_tag)) { - ctx_data->message = *arg; - - /* Skip parameter */ - *arg = sieve_ast_argument_next(*arg); - } - return TRUE; -} - -static bool -cmd_notify_validate_stringlist_tag(struct sieve_validator *valdtr, - struct sieve_ast_argument **arg, - struct sieve_command *cmd) -{ - struct sieve_ast_argument *tag = *arg; - struct cmd_notify_context_data *ctx_data = - (struct cmd_notify_context_data *)cmd->data; - - /* Detach the tag itself */ - *arg = sieve_ast_arguments_detach(*arg,1); - - /* Check syntax: - * :options string-list - */ - if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0, - SAAT_STRING_LIST, FALSE)) - return FALSE; - - /* Assign context */ - ctx_data->options = *arg; - - /* Skip parameter */ - *arg = sieve_ast_argument_next(*arg); - - return TRUE; -} - -/* - * Command registration - */ - -static bool -cmd_notify_registered(struct sieve_validator *valdtr, - const struct sieve_extension *ext, - struct sieve_command_registration *cmd_reg) -{ - sieve_validator_register_tag(valdtr, cmd_reg, ext, - ¬ify_method_tag, 0); - sieve_validator_register_tag(valdtr, cmd_reg, ext, - ¬ify_id_tag, OPT_ID); - sieve_validator_register_tag(valdtr, cmd_reg, ext, - ¬ify_message_tag, OPT_MESSAGE); - sieve_validator_register_tag(valdtr, cmd_reg, ext, - ¬ify_options_tag, OPT_OPTIONS); - - ext_notify_register_importance_tags(valdtr, cmd_reg, ext, - OPT_IMPORTANCE); - - return TRUE; -} - -/* - * Command validation - */ - -static bool -cmd_notify_pre_validate(struct sieve_validator *valdtr ATTR_UNUSED, - struct sieve_command *cmd) -{ - struct cmd_notify_context_data *ctx_data; - - /* Create context */ - ctx_data = p_new(sieve_command_pool(cmd), - struct cmd_notify_context_data, 1); - cmd->data = ctx_data; - - return TRUE; -} - -static int -cmd_notify_address_validate(void *context, struct sieve_ast_argument *arg) -{ - struct sieve_validator *valdtr = (struct sieve_validator *)context; - - if (sieve_argument_is_string_literal(arg)) { - string_t *address = sieve_ast_argument_str(arg); - const char *error; - int result; - - T_BEGIN { - result = (sieve_address_validate_str(address, &error) ? - 1 : -1); - - if (result <= 0) { - sieve_argument_validate_error( - valdtr, arg, - "specified :options address '%s' is invalid for " - "the mailto notify method: %s", - str_sanitize(str_c(address), 128), - error); - } - } T_END; - - return result; - } - - return 1; -} - -static bool -cmd_notify_validate(struct sieve_validator *valdtr, struct sieve_command *cmd) -{ - struct cmd_notify_context_data *ctx_data = - (struct cmd_notify_context_data *)cmd->data; - - /* Check :method argument */ - if (ctx_data->method != NULL) { - const char *method = sieve_ast_argument_strc(ctx_data->method); - - if (strcasecmp(method, "mailto") != 0) { - sieve_command_validate_error( - valdtr, cmd, - "the notify command of the deprecated notify extension " - "only supports the 'mailto' notification method"); - return FALSE; - } - } - - /* Check :options argument */ - if (ctx_data->options != NULL) { - struct sieve_ast_argument *option = ctx_data->options; - - /* Parse and check options */ - if (sieve_ast_stringlist_map( - &option, (void *)valdtr, - cmd_notify_address_validate) <= 0) { - return FALSE; - } - } else { - sieve_command_validate_warning( - valdtr, cmd, - "no :options (and hence recipients) specified for the notify command"); - } - - return TRUE; -} - -/* - * Code generation - */ - -static bool -cmd_notify_generate(const struct sieve_codegen_env *cgenv, - struct sieve_command *cmd) -{ - sieve_operation_emit(cgenv->sblock, cmd->ext, ¬ify_old_operation); - - /* Generate arguments */ - return sieve_generate_arguments(cgenv, cmd, NULL); -} - -/* - * Code dump - */ - -static bool -cmd_notify_operation_dump(const struct sieve_dumptime_env *denv, - sieve_size_t *address) -{ - int opt_code = 0; - - sieve_code_dumpf(denv, "NOTIFY"); - sieve_code_descend(denv); - - /* Dump optional operands */ - for (;;) { - int opt; - bool opok = TRUE; - - if ((opt = sieve_opr_optional_dump(denv, address, - &opt_code)) < 0) - return FALSE; - - if (opt == 0) - break; - - switch (opt_code) { - case OPT_IMPORTANCE: - opok = sieve_opr_number_dump(denv, address, - "importance"); - break; - case OPT_ID: - opok = sieve_opr_string_dump(denv, address, - "id"); - break; - case OPT_OPTIONS: - opok = sieve_opr_stringlist_dump(denv, address, - "options"); - break; - case OPT_MESSAGE: - opok = sieve_opr_string_dump(denv, address, - "message"); - break; - default: - return FALSE; - } - - if (!opok) - return FALSE; - } - - return TRUE; -} - -/* - * Code execution - */ - - -static int -cmd_notify_operation_execute(const struct sieve_runtime_env *renv, - sieve_size_t *address) -{ - const struct sieve_extension *this_ext = renv->oprtn->ext; - struct ext_notify_action *act; - pool_t pool; - int opt_code = 0; - sieve_number_t importance = 1; - struct sieve_stringlist *options = NULL; - string_t *message = NULL, *id = NULL; - int ret = 0; - - /* - * Read operands - */ - - /* Optional operands */ - - for (;;) { - int opt; - - if ((opt = sieve_opr_optional_read(renv, address, - &opt_code)) < 0) - return SIEVE_EXEC_BIN_CORRUPT; - - if (opt == 0) - break; - - switch (opt_code) { - case OPT_IMPORTANCE: - ret = sieve_opr_number_read(renv, address, "importance", - &importance); - break; - case OPT_ID: - ret = sieve_opr_string_read(renv, address, "id", &id); - break; - case OPT_MESSAGE: - ret = sieve_opr_string_read(renv, address, "from", - &message); - break; - case OPT_OPTIONS: - ret = sieve_opr_stringlist_read(renv, address, - "options", &options); - break; - default: - sieve_runtime_trace_error( - renv, "unknown optional operand"); - return SIEVE_EXEC_BIN_CORRUPT; - } - - if (ret <= 0) return ret; - } - - /* - * Perform operation - */ - - /* Enforce 0 < importance < 4 (just to be sure) */ - - if (importance < 1) - importance = 1; - else if (importance > 3) - importance = 3; - - /* Trace */ - - sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "notify action"); - - /* Compose action */ - if (options != NULL) { - string_t *raw_address; - string_t *out_message; - - pool = sieve_result_pool(renv->result); - act = p_new(pool, struct ext_notify_action, 1); - if (id != NULL) - act->id = p_strdup(pool, str_c(id)); - act->importance = importance; - - /* Process message */ - - out_message = t_str_new(1024); - if ((ret = ext_notify_construct_message( - renv, (message == NULL ? NULL : str_c(message)), - out_message)) <= 0) - return ret; - act->message = p_strdup(pool, str_c(out_message)); - - /* Normalize and verify all :options addresses */ - - sieve_stringlist_reset(options); - - p_array_init(&act->recipients, pool, 4); - - raw_address = NULL; - while ((ret = sieve_stringlist_next_item( - options, &raw_address)) > 0) { - const char *error = NULL; - const struct smtp_address *address; - - /* Add if valid address */ - address = sieve_address_parse_str(raw_address, &error); - if (address != NULL) { - const struct ext_notify_recipient *rcpts; - unsigned int rcpt_count, i; - - /* Prevent duplicates */ - rcpts = array_get(&act->recipients, &rcpt_count); - - for (i = 0; i < rcpt_count; i++) { - if (smtp_address_equals(rcpts[i].address, - address)) - break; - } - - /* Add only if unique */ - if (i != rcpt_count) { - sieve_runtime_warning( - renv, NULL, - "duplicate recipient '%s' specified in the :options argument of " - "the deprecated notify command", - str_sanitize(str_c(raw_address), 128)); - - } else if (array_count(&act->recipients) >= - EXT_NOTIFY_MAX_RECIPIENTS) { - sieve_runtime_warning(renv, NULL, - "more than the maximum %u recipients are specified " - "for the deprecated notify command; " - "the rest is discarded", - EXT_NOTIFY_MAX_RECIPIENTS); - break; - - } else { - struct ext_notify_recipient recipient; - - recipient.full = - p_strdup(pool, str_c(raw_address)); - recipient.address = - smtp_address_clone(pool, address); - - array_append(&act->recipients, &recipient, 1); - } - } else { - sieve_runtime_error( - renv, NULL, - "specified :options address '%s' is invalid for " - "the deprecated notify command: %s", - str_sanitize(str_c(raw_address), 128), error); - return SIEVE_EXEC_FAILURE; - } - } - - if (ret < 0) { - sieve_runtime_trace_error( - renv, "invalid options stringlist"); - return SIEVE_EXEC_BIN_CORRUPT; - } - - if (sieve_result_add_action(renv, this_ext, "notify", - &act_notify_old, NULL, (void *)act, - 0, FALSE) < 0) - return SIEVE_EXEC_FAILURE; - } - - return SIEVE_EXEC_OK; -} - -/* - * Action - */ - -/* Runtime verification */ - -static int -act_notify_check_duplicate(const struct sieve_runtime_env *renv ATTR_UNUSED, - const struct sieve_action *act ATTR_UNUSED, - const struct sieve_action *act_other ATTR_UNUSED) -{ - struct ext_notify_action *new_nact, *old_nact; - const struct ext_notify_recipient *new_rcpts; - const struct ext_notify_recipient *old_rcpts; - unsigned int new_count, old_count, i, j; - unsigned int del_start = 0, del_len = 0; - - if (act->context == NULL || act_other->context == NULL) - return 0; - - new_nact = (struct ext_notify_action *)act->context; - old_nact = (struct ext_notify_action *)act_other->context; - - new_rcpts = array_get(&new_nact->recipients, &new_count); - old_rcpts = array_get(&old_nact->recipients, &old_count); - - for (i = 0; i < new_count; i++) { - for (j = 0; j < old_count; j++) { - if (smtp_address_equals(new_rcpts[i].address, - old_rcpts[j].address)) - break; - } - - if (j == old_count) { - /* Not duplicate */ - if (del_len > 0) { - /* Perform pending deletion */ - array_delete(&new_nact->recipients, - del_start, del_len); - - /* Make sure the loop integrity is maintained */ - i -= del_len; - new_rcpts = array_get(&new_nact->recipients, - &new_count); - } - - del_len = 0; - } else { - /* Mark deletion */ - if (del_len == 0) - del_start = i; - del_len++; - } - } - - /* Perform pending deletion */ - if (del_len > 0) - array_delete(&new_nact->recipients, del_start, del_len); - - return (array_count(&new_nact->recipients) > 0 ? 0 : 1); -} - -/* Result printing */ - -static void -act_notify_print(const struct sieve_action *action, - const struct sieve_result_print_env *rpenv, - bool *keep ATTR_UNUSED) -{ - const struct ext_notify_action *act = - (const struct ext_notify_action *)action->context; - const struct ext_notify_recipient *recipients; - unsigned int count, i; - - sieve_result_action_printf( - rpenv, "send (deprecated) notification with method 'mailto':"); - - /* Print main method parameters */ - - sieve_result_printf(rpenv, " => importance : %llu\n", - (unsigned long long)act->importance); - - if (act->message != NULL) { - sieve_result_printf( - rpenv, " => message : %s\n", act->message); - } - if (act->id != NULL) { - sieve_result_printf( - rpenv, " => id : %s \n", act->id); - } - - /* Print mailto: recipients */ - - sieve_result_printf(rpenv, " => recipients :\n"); - - recipients = array_get(&act->recipients, &count); - if (count == 0) { - sieve_result_printf( - rpenv, " NONE, action has no effect\n"); - } else { - for (i = 0; i < count; i++) { - sieve_result_printf( - rpenv, " + To: %s\n", recipients[i].full); - } - } - - /* Finish output with an empty line */ - - sieve_result_printf(rpenv, "\n"); -} - -/* Result execution */ - -static bool contains_8bit(const char *msg) -{ - const unsigned char *s = (const unsigned char *)msg; - - for (; *s != '\0'; s++) { - if ((*s & 0x80) != 0) - return TRUE; - } - return FALSE; -} - -static bool -act_notify_send(const struct sieve_action_exec_env *aenv, - const struct ext_notify_action *act) -{ - const struct sieve_execute_env *eenv = aenv->exec_env; - const struct sieve_script_env *senv = eenv->scriptenv; - const struct ext_notify_recipient *recipients; - struct sieve_smtp_context *sctx; - unsigned int count, i; - struct ostream *output; - string_t *msg, *to, *all; - const char *outmsgid, *error; - int ret; - - /* Get recipients */ - recipients = array_get(&act->recipients, &count); - if (count == 0) { - sieve_result_warning( - aenv, "notify action specifies no recipients; " - "action has no effect"); - return TRUE; - } - - /* Just to be sure */ - if (!sieve_smtp_available(senv)) { - sieve_result_global_warning( - aenv, "notify action has no means to send mail"); - return TRUE; - } - - /* Compose common headers */ - msg = t_str_new(512); - rfc2822_header_write(msg, "X-Sieve", SIEVE_IMPLEMENTATION); - rfc2822_header_write(msg, "Date", message_date_create(ioloop_time)); - - /* Set importance */ - switch (act->importance) { - case 1: - rfc2822_header_write(msg, "X-Priority", "1 (Highest)"); - rfc2822_header_write(msg, "Importance", "High"); - break; - case 3: - rfc2822_header_write(msg, "X-Priority", "5 (Lowest)"); - rfc2822_header_write(msg, "Importance", "Low"); - break; - case 2: - default: - rfc2822_header_write(msg, "X-Priority", "3 (Normal)"); - rfc2822_header_write(msg, "Importance", "Normal"); - break; - } - - rfc2822_header_write(msg, "From", sieve_get_postmaster_address(senv)); - - rfc2822_header_write(msg, "Subject", "[SIEVE] New mail notification"); - - rfc2822_header_write(msg, "Auto-Submitted", "auto-generated (notify)"); - rfc2822_header_write(msg, "Precedence", "bulk"); - - rfc2822_header_write(msg, "MIME-Version", "1.0"); - if (contains_8bit(act->message)) { - rfc2822_header_write(msg, "Content-Type", - "text/plain; charset=utf-8"); - rfc2822_header_write(msg, "Content-Transfer-Encoding", "8bit"); - } else { - rfc2822_header_write(msg, "Content-Type", - "text/plain; charset=us-ascii"); - rfc2822_header_write(msg, "Content-Transfer-Encoding", "7bit"); - } - - outmsgid = sieve_message_get_new_id(eenv->svinst); - rfc2822_header_write(msg, "Message-ID", outmsgid); - - if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0 && - sieve_message_get_sender(aenv->msgctx) != NULL) { - sctx = sieve_smtp_start(senv, sieve_get_postmaster_smtp(senv)); - } else { - sctx = sieve_smtp_start(senv, NULL); - } - - /* Add all recipients (and compose To header field) */ - to = t_str_new(128); - all = t_str_new(256); - for (i = 0; i < count; i++) { - sieve_smtp_add_rcpt(sctx, recipients[i].address); - if (i > 0) - str_append(to, ", "); - str_append(to, recipients[i].full); - if (i < 3) { - if (i > 0) - str_append(all, ", "); - str_append(all, smtp_address_encode_path( - recipients[i].address)); - } else if (i == 3) { - str_printfa(all, ", ... (%u total)", count); - } - } - - rfc2822_header_write_address(msg, "To", str_c(to)); - - /* Generate message body */ - str_printfa(msg, "\r\n%s\r\n", act->message); - - output = sieve_smtp_send(sctx); - o_stream_nsend(output, str_data(msg), str_len(msg)); - - if ((ret = sieve_smtp_finish(sctx, &error)) <= 0) { - if (ret < 0) { - sieve_result_global_error( - aenv, "failed to send mail notification to %s: " - "%s (temporary failure)", str_c(all), - str_sanitize(error, 512)); - } else { - sieve_result_global_log_error( - aenv, "failed to send mail notification to %s: " - "%s (permanent failure)", str_c(all), - str_sanitize(error, 512)); - } - } else { - struct event_passthrough *e = - sieve_action_create_finish_event(aenv)-> - add_str("notify_target", str_c(all)); - - sieve_result_event_log(aenv, e->event(), - "sent mail notification to %s", - str_c(all)); - } - - return TRUE; -} - -static int -act_notify_commit(const struct sieve_action_exec_env *aenv, - void *tr_context ATTR_UNUSED) -{ - const struct sieve_execute_env *eenv = aenv->exec_env; - struct mail *mail = eenv->msgdata->mail; - const struct ext_notify_action *act = - (const struct ext_notify_action *)aenv->action->context; - const char *const *hdsp; - bool result; - int ret; - - /* Is the message an automatic reply ? */ - if ((ret = mail_get_headers(mail, "auto-submitted", &hdsp)) < 0) { - return sieve_result_mail_error( - aenv, mail, - "failed to read `auto-submitted' header field"); - } - - /* Theoretically multiple headers could exist, so lets make sure */ - if (ret > 0) { - while (*hdsp != NULL) { - if (strcasecmp(*hdsp, "no") != 0) { - const struct smtp_address *sender = NULL; - const char *from; - - if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) == 0) - sender = sieve_message_get_sender(aenv->msgctx); - from = (sender == NULL ? "" : - t_strdup_printf(" from <%s>", - smtp_address_encode(sender))); - - sieve_result_global_log( - aenv, - "not sending notification for auto-submitted message%s", - from); - return SIEVE_EXEC_OK; - } - hdsp++; - } - } - - T_BEGIN { - result = act_notify_send(aenv, act); - } T_END; - - if (!result) - return SIEVE_EXEC_FAILURE; - eenv->exec_status->significant_action_executed = TRUE; - return SIEVE_EXEC_OK; -} diff --git a/src/lib-sieve/plugins/notify/ext-notify-common.c b/src/lib-sieve/plugins/notify/ext-notify-common.c deleted file mode 100644 index ad344bb0f..000000000 --- a/src/lib-sieve/plugins/notify/ext-notify-common.c +++ /dev/null @@ -1,354 +0,0 @@ -/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file - */ - -#include "lib.h" -#include "str.h" -#include "istream.h" -#include "rfc822-parser.h" -#include "message-parser.h" -#include "message-decoder.h" -#include "mail-storage.h" - -#include "sieve-common.h" -#include "sieve-code.h" -#include "sieve-message.h" -#include "sieve-extensions.h" -#include "sieve-commands.h" -#include "sieve-actions.h" -#include "sieve-validator.h" -#include "sieve-generator.h" -#include "sieve-interpreter.h" -#include "sieve-dump.h" -#include "sieve-result.h" - -#include "ext-notify-common.h" - -#include <ctype.h> - -/* - * Importance argument - */ - -static bool -tag_importance_validate(struct sieve_validator *valdtr, - struct sieve_ast_argument **arg, - struct sieve_command *cmd); - -static const struct sieve_argument_def importance_low_tag = { - .identifier = "low", - .validate = tag_importance_validate, -}; - -static const struct sieve_argument_def importance_normal_tag = { - .identifier = "normal", - .validate = tag_importance_validate, -}; - -static const struct sieve_argument_def importance_high_tag = { - .identifier = "high", - .validate = tag_importance_validate, -}; - -static bool -tag_importance_validate(struct sieve_validator *valdtr ATTR_UNUSED, - struct sieve_ast_argument **arg, - struct sieve_command *cmd ATTR_UNUSED) -{ - struct sieve_ast_argument *tag = *arg; - - if (sieve_argument_is(tag, importance_low_tag)) - sieve_ast_argument_number_substitute(tag, 3); - else if (sieve_argument_is(tag, importance_normal_tag)) - sieve_ast_argument_number_substitute(tag, 2); - else - sieve_ast_argument_number_substitute(tag, 1); - - tag->argument = sieve_argument_create(tag->ast, &number_argument, - tag->argument->ext, - tag->argument->id_code); - - /* Skip parameter */ - *arg = sieve_ast_argument_next(*arg); - - return TRUE; -} - -void ext_notify_register_importance_tags( - struct sieve_validator *valdtr, - struct sieve_command_registration *cmd_reg, - const struct sieve_extension *ext, unsigned int id_code) -{ - sieve_validator_register_tag(valdtr, cmd_reg, ext, - &importance_low_tag, id_code); - sieve_validator_register_tag(valdtr, cmd_reg, ext, - &importance_normal_tag, id_code); - sieve_validator_register_tag(valdtr, cmd_reg, ext, - &importance_high_tag, id_code); -} - -/* - * Body extraction - */ - -/* FIXME: overlaps somewhat with body extension */ - -struct ext_notify_message_context { - pool_t pool; - buffer_t *body_text; -}; - -static struct ext_notify_message_context * -ext_notify_get_message_context(const struct sieve_extension *this_ext, - struct sieve_message_context *msgctx) -{ - struct ext_notify_message_context *ctx; - - /* Get message context (contains cached message body information) */ - ctx = (struct ext_notify_message_context *) - sieve_message_context_extension_get(msgctx, this_ext); - - /* Create it if it does not exist already */ - if (ctx == NULL) { - pool_t pool = sieve_message_context_pool(msgctx); - ctx = p_new(pool, struct ext_notify_message_context, 1); - ctx->pool = pool; - ctx->body_text = NULL; - - /* Register context */ - sieve_message_context_extension_set(msgctx, this_ext, - (void *)ctx); - } - - return ctx; -} - -static bool _is_text_content(const struct message_header_line *hdr) -{ - struct rfc822_parser_context parser; - string_t *content_type; - const char *data; - - /* Initialize parsing */ - rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL); - (void)rfc822_skip_lwsp(&parser); - - /* Parse content type */ - content_type = t_str_new(64); - if (rfc822_parse_content_type(&parser, content_type) < 0) - return FALSE; - - /* Content-type value must end here, otherwise it is invalid after all - */ - (void)rfc822_skip_lwsp(&parser); - if (parser.data != parser.end && *parser.data != ';') - return FALSE; - - /* Success */ - data = str_c(content_type); - if (str_begins_with(data, "text/")) - return TRUE; - return FALSE; -} - -static int -cmd_notify_extract_body_text(const struct sieve_runtime_env *renv, - const char **body_text_r, size_t *body_size_r) -{ - const struct sieve_execute_env *eenv = renv->exec_env; - const struct sieve_extension *this_ext = renv->oprtn->ext; - struct ext_notify_message_context *mctx; - struct mail *mail = eenv->msgdata->mail; - struct message_parser_ctx *parser; - struct message_decoder_context *decoder; - struct message_part *parts; - struct message_block block, decoded; - struct istream *input; - bool is_text, save_body; - int ret = 1; - - *body_text_r = NULL; - *body_size_r = 0; - - /* Return cached result if available */ - mctx = ext_notify_get_message_context(this_ext, renv->msgctx); - if (mctx->body_text != NULL) { - *body_text_r = (const char *) - buffer_get_data(mctx->body_text, body_size_r); - return SIEVE_EXEC_OK; - } - - /* Create buffer */ - mctx->body_text = buffer_create_dynamic(mctx->pool, 1024*64); - - /* Get the message stream */ - if (mail_get_stream(mail, NULL, NULL, &input) < 0) { - return sieve_runtime_mail_error(renv, mail, "notify action: " - "failed to read input message"); - } - - /* Initialize body decoder */ - decoder = message_decoder_init(NULL, 0); - - struct message_parser_settings mparser_set = { - .hdr_flags = 0, - .flags = 0, - }; - parser = message_parser_init(mctx->pool, input, &mparser_set); - is_text = TRUE; - save_body = FALSE; - while ((ret = message_parser_parse_next_block(parser, &block)) > 0) { - if (block.hdr != NULL || block.size == 0) { - /* Decode block */ - (void)message_decoder_decode_next_block(decoder, &block, - &decoded); - - /* Check for end of headers */ - if (block.hdr == NULL) { - save_body = is_text; - continue; - } - - /* We're interested of only Content-Type: header */ - if (strcasecmp(block.hdr->name, "Content-Type") != 0) - continue; - - /* Header can have folding whitespace. Acquire the full - value before continuing */ - if (block.hdr->continues) { - block.hdr->use_full_value = TRUE; - continue; - } - - /* Is it a text part? */ - T_BEGIN { - is_text = _is_text_content(block.hdr); - } T_END; - - continue; - } - - /* Read text body */ - if (save_body) { - (void)message_decoder_decode_next_block(decoder, &block, - &decoded); - buffer_append(mctx->body_text, decoded.data, - decoded.size); - is_text = TRUE; - } - } - - /* Cleanup */ - (void)message_parser_deinit(&parser, &parts); - message_decoder_deinit(&decoder); - - if (ret < 0 && input->stream_errno != 0) { - sieve_runtime_critical(renv, NULL, "notify action: " - "failed to read input message", - "notify action: read(%s) failed: %s", - i_stream_get_name(input), - i_stream_get_error(input)); - return SIEVE_EXEC_TEMP_FAILURE; - } - - /* Return status */ - *body_text_r = (const char *)buffer_get_data(mctx->body_text, - body_size_r); - return SIEVE_EXEC_OK; -} - -int ext_notify_construct_message(const struct sieve_runtime_env *renv, - const char *msg_format, - string_t *out_msg) -{ - const struct sieve_execute_env *eenv = renv->exec_env; - const struct sieve_message_data *msgdata = eenv->msgdata; - struct sieve_message_context *msgctx = renv->msgctx; - const struct smtp_address *return_path = - sieve_message_get_sender(msgctx); - const char *p; - int ret; - - if (msg_format == NULL) - msg_format = "$from$: $subject$"; - - /* Scan message for substitutions */ - p = msg_format; - while (*p != '\0') { - const char *header; - - if (str_begins_icase(p, "$from$", &p)) { - /* Fetch sender from original message */ - if ((ret = mail_get_first_header_utf8( - msgdata->mail, "from", &header)) < 0) { - return sieve_runtime_mail_error( - renv, msgdata->mail, - "failed to read header field `from'"); - } - if (ret > 0) - str_append(out_msg, header); - } else if (str_begins_icase(p, "$env-from$", &p)) { - if (return_path != NULL) - smtp_address_write(out_msg, return_path); - } else if (str_begins_icase(p, "$subject$", &p)) { - /* Fetch sender from oriinal message */ - if ((ret = mail_get_first_header_utf8( - msgdata->mail, "subject", &header)) < 0) { - return sieve_runtime_mail_error( - renv, msgdata->mail, - "failed to read header field `subject'"); - } - if (ret > 0) - str_append(out_msg, header); - } else if (str_begins_icase(p, "$text", &p) && - (*p == '[' || *p == '$')) { - size_t num = 0; - const char *begin = p; - bool valid = TRUE; - - if (*p == '[') { - p += 1; - - while (i_isdigit(*p)) { - num = num * 10 + (*p - '0'); - p++; - } - - if (*p++ != ']' || *p++ != '$') { - str_append_data(out_msg, begin, - p-begin); - valid = FALSE; - } - } else { - p += 1; - } - - if (valid) { - size_t body_size; - const char *body_text; - - if ((ret = cmd_notify_extract_body_text( - renv, &body_text, &body_size)) <= 0) - return ret; - - if (num > 0 && num < body_size) { - str_append_data(out_msg, body_text, - num); - } else { - str_append_data(out_msg, body_text, - body_size); - } - } - } else { - size_t len; - - /* Find next substitution */ - len = strcspn(p + 1, "$") + 1; - - /* Copy normal text */ - str_append_data(out_msg, p, len); - p += len; - } - } - - 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 deleted file mode 100644 index 09db2cb2e..000000000 --- a/src/lib-sieve/plugins/notify/ext-notify-common.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef EXT_NOTIFY_COMMON_H -#define EXT_NOTIFY_COMMON_H - -/* - * Extension - */ - -extern const struct sieve_extension_def notify_extension; - -/* - * Commands - */ - -extern const struct sieve_command_def cmd_notify_old; -extern const struct sieve_command_def cmd_denotify; - -/* - * Arguments - */ - -void ext_notify_register_importance_tags( - struct sieve_validator *valdtr, - struct sieve_command_registration *cmd_reg, - const struct sieve_extension *this_ext, unsigned int id_code); - -/* - * Operations - */ - -extern const struct sieve_operation_def notify_old_operation; -extern const struct sieve_operation_def denotify_operation; - -enum ext_notify_opcode { - EXT_NOTIFY_OPERATION_NOTIFY, - EXT_NOTIFY_OPERATION_DENOTIFY, -}; - -/* - * Actions - */ - -extern const struct sieve_action_def act_notify_old; - -struct ext_notify_recipient { - const char *full; - const struct smtp_address *address; -}; - -ARRAY_DEFINE_TYPE(recipients, struct ext_notify_recipient); - -struct ext_notify_action { - const char *id; - const char *message; - sieve_number_t importance; - - ARRAY_TYPE(recipients) recipients; -}; - -/* - * Message construct - */ - -int ext_notify_construct_message(const struct sieve_runtime_env *renv, - const char *msg_format, string_t *out_msg); - -#endif diff --git a/src/lib-sieve/plugins/notify/ext-notify-limits.h b/src/lib-sieve/plugins/notify/ext-notify-limits.h deleted file mode 100644 index 5fab7cb47..000000000 --- a/src/lib-sieve/plugins/notify/ext-notify-limits.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef EXT_NOTIFY_LIMITS_H -#define EXT_NOTIFY_LIMITS_H - -#define EXT_NOTIFY_MAX_RECIPIENTS 8 -#define EXT_NOTIFY_MAX_MESSAGE 256 - -#endif diff --git a/src/lib-sieve/plugins/notify/ext-notify.c b/src/lib-sieve/plugins/notify/ext-notify.c deleted file mode 100644 index e79e04975..000000000 --- a/src/lib-sieve/plugins/notify/ext-notify.c +++ /dev/null @@ -1,108 +0,0 @@ -/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file - */ - -/* Extension notify - * ---------------- - * - * Authors: Stephan Bosch - * Specification: draft-ietf-sieve-notify-00.txt - * Implementation: full, but deprecated; provided for backwards compatibility - * Status: testing - * - */ - -#include "sieve-common.h" - -#include "sieve-code.h" -#include "sieve-extensions.h" -#include "sieve-actions.h" -#include "sieve-commands.h" -#include "sieve-validator.h" -#include "sieve-generator.h" -#include "sieve-interpreter.h" -#include "sieve-result.h" - -#include "ext-notify-common.h" - -/* - * Operations - */ - -const struct sieve_operation_def *ext_notify_operations[] = { - ¬ify_old_operation, - &denotify_operation -}; - -/* - * Extension - */ - -static bool ext_notify_validator_load - (const struct sieve_extension *ext, struct sieve_validator *valdtr); - -const struct sieve_extension_def notify_extension = { - .name = "notify", - .validator_load = ext_notify_validator_load, - SIEVE_EXT_DEFINE_OPERATIONS(ext_notify_operations) -}; - -/* - * Extension validation - */ - -static bool ext_notify_validator_check_conflict - (const struct sieve_extension *ext, - struct sieve_validator *valdtr, void *context, - struct sieve_ast_argument *require_arg, - const struct sieve_extension *ext_other, - bool required); -static bool ext_notify_validator_validate - (const struct sieve_extension *ext, - struct sieve_validator *valdtr, void *context, - struct sieve_ast_argument *require_arg, - bool required); - -const struct sieve_validator_extension notify_validator_extension = { - .ext = ¬ify_extension, - .check_conflict = ext_notify_validator_check_conflict, - .validate = ext_notify_validator_validate -}; - -static bool ext_notify_validator_load -(const struct sieve_extension *ext, struct sieve_validator *valdtr) -{ - /* Register validator extension to check for conflict with enotify */ - sieve_validator_extension_register - (valdtr, ext, ¬ify_validator_extension, NULL); - return TRUE; -} - -static bool ext_notify_validator_check_conflict -(const struct sieve_extension *ext ATTR_UNUSED, - struct sieve_validator *valdtr, void *context ATTR_UNUSED, - struct sieve_ast_argument *require_arg, - const struct sieve_extension *ext_other, - bool required ATTR_UNUSED) -{ - /* Check for conflict with enotify */ - if ( sieve_extension_name_is(ext_other, "enotify") ) { - sieve_argument_validate_error(valdtr, require_arg, - "the (deprecated) notify extension cannot be used " - "together with the enotify extension"); - return FALSE; - } - - return TRUE; -} - -static bool ext_notify_validator_validate -(const struct sieve_extension *ext, - struct sieve_validator *valdtr, void *context ATTR_UNUSED, - struct sieve_ast_argument *require_arg ATTR_UNUSED, - bool required ATTR_UNUSED) -{ - /* No conflicts: register new commands */ - sieve_validator_register_command(valdtr, ext, &cmd_notify_old); - sieve_validator_register_command(valdtr, ext, &cmd_denotify); - return TRUE; -} diff --git a/src/lib-sieve/sieve-extensions.c b/src/lib-sieve/sieve-extensions.c index a1cb81056..f794a25da 100644 --- a/src/lib-sieve/sieve-extensions.c +++ b/src/lib-sieve/sieve-extensions.c @@ -165,12 +165,10 @@ const unsigned int sieve_extra_extensions_count = */ extern const struct sieve_extension_def imapflags_extension; -extern const struct sieve_extension_def notify_extension; extern const struct sieve_extension_def vnd_duplicate_extension; const struct sieve_extension_def *sieve_deprecated_extensions[] = { &imapflags_extension, - ¬ify_extension, &vnd_duplicate_extension }; diff --git a/tests/deprecated/notify/basic.svtest b/tests/deprecated/notify/basic.svtest deleted file mode 100644 index 974f8ca5b..000000000 --- a/tests/deprecated/notify/basic.svtest +++ /dev/null @@ -1,59 +0,0 @@ -require "vnd.dovecot.testsuite"; -require "notify"; -require "body"; - -test "Execute" { - /* Test to catch runtime segfaults */ - notify - :message "This is probably very important" - :low - :method "mailto" - :options ["stephan@example.com", "stephan@example.org"]; - - if not test_result_execute { - test_fail "Execute failed"; - } -} - -test_result_reset; - -test_set "message" text: -To: user@example.com -From: stephan@example.org -Subject: Mail - -Test! -. -; - -test "Substitutions" { - notify - :message "$from$: $subject$" - :options "stephan@example.com"; - if not test_result_execute { - test_fail "Execute failed"; - } - test_message :smtp 0; - if not body :contains "stephan@example.org: Mail" { - test_fail "Substitution failed"; - } -} - -test_result_reset; - -test_set "message" text: -To: user@example.com - -Test! -. -; - -test "Empty substitutions" { - notify - :message "$from$: $subject$" - :options "stephan@example.com"; - if not test_result_execute { - test_fail "Execute failed"; - } -} - diff --git a/tests/deprecated/notify/denotify.svtest b/tests/deprecated/notify/denotify.svtest deleted file mode 100644 index 9f752e127..000000000 --- a/tests/deprecated/notify/denotify.svtest +++ /dev/null @@ -1,279 +0,0 @@ -require "vnd.dovecot.testsuite"; -require "notify"; -require "envelope"; - -/* - * Denotify all - */ - -test_set "message" text: -From: stephan@example.org -To: nico@frop.example.org -Subject: Frop! - -Klutsefluts. -. -; - -test "Denotify All" { - notify :options "timo@example.com"; - notify :options "stephan@dovecot.example.net"; - notify :options "postmaster@frop.example.org"; - 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@example.org -To: nico@frop.example.org -Subject: Frop! - -Klutsefluts. -. -; - -test "Denotify ID First" { - /* #1 */ - notify :options "timo@example.com" :id "aap"; - - /* #2 */ - notify :options "stephan@dovecot.example.net" :id "noot"; - - /* #3 */ - notify :options "postmaster@frop.example.org" :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.example.net" { - 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@frop.example.org" { - 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@example.org -To: nico@frop.example.org -Subject: Frop! - -Klutsefluts. -. -; - -test "Denotify ID Middle" { - /* #1 */ - notify :options "timo@example.com" :id "aap"; - - /* #2 */ - notify :options "stephan@dovecot.example.net" :id "noot"; - - /* #3 */ - notify :options "postmaster@frop.example.org" :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@frop.example.org" { - 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@example.org -To: nico@frop.example.org -Subject: Frop! - -Klutsefluts. -. -; - -test "Denotify ID Last" { - /* #1 */ - notify :options "timo@example.com" :id "aap"; - - /* #2 */ - notify :options "stephan@dovecot.example.net" :id "noot"; - - /* #3 */ - notify :options "postmaster@frop.example.org" :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.example.net" { - 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@example.org -To: nico@frop.example.org -Subject: Frop! - -Klutsefluts. -. -; - -test "Denotify Matching" { - /* #1 */ - notify :options "timo@example.com" :id "frop"; - - /* #2 */ - notify :options "stephan@dovecot.example.net" :id "noot"; - - /* #3 */ - notify :options "postmaster@frop.example.org" :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.example.net" { - 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@example.org -To: nico@frop.example.org -Subject: Frop! - -Klutsefluts. -. -; - -test "Denotify Matching Importance" { - /* #1 */ - notify :options "timo@example.com" :id "frop" :low; - - /* #2 */ - notify :options "stephan@dovecot.example.net" :id "frml" :high; - - /* #3 */ - notify :options "postmaster@frop.example.org" :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.example.net" { - test_fail "message #2 unexpectedly missing from output"; - } - - if test_message :smtp 1 { - test_fail "too many notifications sent"; - } -} - - diff --git a/tests/deprecated/notify/errors.svtest b/tests/deprecated/notify/errors.svtest deleted file mode 100644 index 549cb6b87..000000000 --- a/tests/deprecated/notify/errors.svtest +++ /dev/null @@ -1,33 +0,0 @@ -require "vnd.dovecot.testsuite"; -require "comparator-i;ascii-numeric"; -require "relational"; - -test "Invalid :options argument (FIXME: count only)" { - if test_script_compile "errors/options.sieve" { - test_fail "compile should have failed"; - } - - if not test_error :count "eq" :comparator "i;ascii-numeric" "3" { - test_fail "wrong number of errors reported"; - } -} - -test "Deprecated notify extension used with enotify" { - if test_script_compile "errors/conflict.sieve" { - test_fail "compile should have failed"; - } - - if not test_error :count "eq" :comparator "i;ascii-numeric" "3" { - test_fail "wrong number of errors reported"; - } -} - -test "Deprecated notify extension used with enotify (ihave)" { - if test_script_compile "errors/conflict-ihave.sieve" { - test_fail "compile should have failed"; - } - - if not test_error :count "eq" :comparator "i;ascii-numeric" "3" { - test_fail "wrong number of errors reported"; - } -} diff --git a/tests/deprecated/notify/errors/conflict-ihave.sieve b/tests/deprecated/notify/errors/conflict-ihave.sieve deleted file mode 100644 index 9686f033c..000000000 --- a/tests/deprecated/notify/errors/conflict-ihave.sieve +++ /dev/null @@ -1,8 +0,0 @@ -require "enotify"; -require "ihave"; - -# 1: Conflict -if ihave "notify" { - # 2: Syntax wrong for enotify (and not skipped in compile) - notify :options "frop@frop.example.org"; -} diff --git a/tests/deprecated/notify/errors/conflict.sieve b/tests/deprecated/notify/errors/conflict.sieve deleted file mode 100644 index 46a62835f..000000000 --- a/tests/deprecated/notify/errors/conflict.sieve +++ /dev/null @@ -1,4 +0,0 @@ -require "enotify"; -require "notify"; - -notify :options "frop@frop.example.org"; diff --git a/tests/deprecated/notify/errors/options.sieve b/tests/deprecated/notify/errors/options.sieve deleted file mode 100644 index c86fea071..000000000 --- a/tests/deprecated/notify/errors/options.sieve +++ /dev/null @@ -1,11 +0,0 @@ -require "notify"; - -# 1: empty option -notify :options ""; - -# 2: invalid address syntax -notify :options "frop#frop.example.org"; - -# Valid -notify :options "frop@frop.example.org"; - diff --git a/tests/deprecated/notify/execute.svtest b/tests/deprecated/notify/execute.svtest deleted file mode 100644 index 90fde474e..000000000 --- a/tests/deprecated/notify/execute.svtest +++ /dev/null @@ -1,25 +0,0 @@ -require "vnd.dovecot.testsuite"; -require "relational"; - - -/* - * Execution testing (currently just meant to trigger any segfaults) - */ - -test "Duplicate recipients" { - if not test_script_compile "execute/duplicates.sieve" { - test_fail "script compile failed"; - } - - if not test_script_run { - test_fail "script execute failed"; - } - - if test_result_action :count "ne" "2" { - test_fail "second notify action was discarded entirely"; - } - - if not test_result_execute { - test_fail "result execute failed"; - } -} diff --git a/tests/deprecated/notify/execute/duplicates.sieve b/tests/deprecated/notify/execute/duplicates.sieve deleted file mode 100644 index ef3fa5fd0..000000000 --- a/tests/deprecated/notify/execute/duplicates.sieve +++ /dev/null @@ -1,4 +0,0 @@ -require "notify"; - -notify :message "Incoming stupidity." :options ["stephan@example.org", "stephan@friep.example.com", "idiot@example.org"]; -notify :message "There it is." :options ["tss@example.net", "stephan@example.org", "idiot@example.org", "nico@frop.example.org", "stephan@friep.example.com"]; diff --git a/tests/deprecated/notify/mailto.svtest b/tests/deprecated/notify/mailto.svtest deleted file mode 100644 index 172433984..000000000 --- a/tests/deprecated/notify/mailto.svtest +++ /dev/null @@ -1,317 +0,0 @@ -require "vnd.dovecot.testsuite"; - -require "notify"; -require "body"; -require "relational"; -require "comparator-i;ascii-numeric"; - -/* - * Simple test - */ - -test_set "message" text: -From: stephan@example.org -To: nico@frop.example.org -Subject: Frop! - -Klutsefluts. -. -; - -test "Simple" { - notify :method "mailto" :options "stephan@example.org"; - - if not test_result_execute { - test_fail "failed to execute notify"; - } - - test_message :smtp 0; - - if not header :matches "Auto-Submitted" "auto-generated*" { - test_fail "auto-submitted header set inappropriately"; - } - - if not exists "X-Sieve" { - test_fail "x-sieve header missing from outgoing message"; - } -} - -/* - * Multiple recipients - */ - -test_result_reset; - -test_set "message" text: -From: stephan@example.org -To: nico@frop.example.org -Subject: Frop! - -Klutsefluts. -. -; - -test "Multiple recipients" { - notify :options ["timo@example.com","stephan@dovecot.example.net","postmaster@frop.example.org"]; - - if not test_result_execute { - test_fail "failed to execute notify"; - } - - test_message :smtp 0; - - if not address :is "to" "timo@example.com" { - test_fail "first To address missing"; - } - - test_message :smtp 1; - - if not address :is "to" "stephan@dovecot.example.net" { - test_fail "second To address missing"; - } - - if not header :matches "Auto-Submitted" "auto-generated*" { - test_fail "auto-submitted header not found for second message"; - } - - test_message :smtp 2; - - if not address :is "to" "postmaster@frop.example.org" { - test_fail "third To address missing"; - } - - if not header :matches "Auto-Submitted" "auto-generated*" { - test_fail "auto-submitted header not found for third message"; - } - - if not address :count "eq" :comparator "i;ascii-numeric" "to" "3" { - test_fail "wrong number of recipients in To header"; - } - - if not address :count "eq" :comparator "i;ascii-numeric" "cc" "0" { - test_fail "too many recipients in Cc header"; - } -} - -/* - * Duplicate recipients - */ - -test_result_reset; - -test_set "message" text: -From: stephan@example.org -To: nico@frop.example.org -Subject: Frop! - -Klutsefluts. -. -; - -test "Duplicate recipients" { - notify :options ["timo@example.com", "stephan@dovecot.example.net", "stephan@dovecot.example.net"]; - notify :options ["timo@example.com", "stephan@example.org"]; - - if not test_result_execute { - test_fail "failed to execute notify"; - } - - test_message :smtp 2; - - if address "To" "stephan@dovecot.example.net" { - test_fail "duplicate recipient not removed from first message"; - } - - if address "To" "timo@example.com" { - test_fail "duplicate recipient not removed from second message"; - } -} - -/* - * Notifying on automated messages - */ - -test_result_reset; - -test_set "message" text: -From: stephan@example.org -To: nico@frop.example.org -Auto-submitted: auto-notify -Subject: Frop! - -Klutsefluts. -. -; - -test "Notifying on automated messages" { - notify :options "stephan@example.org"; - - if not test_result_execute { - test_fail "failed to execute notify"; - } - - if test_message :smtp 0 { - test_fail "notified of auto-submitted message"; - } -} - -test_result_reset; - -test_set "message" text: -To: nico@frop.example.org -From: stephan@example.org -Subject: Test - -Test. Test -Frop! -. -; - -test "Body; Singular Message" { - notify :low :id "frop" :options "stephan@example.org" - :message text: -Received interesting message: - -$text$ - -You have been notified. -. -; - - if not test_result_execute { - test_fail "failed to execute notify"; - } - - test_message :smtp 0; - - if not body :raw :contains "Received interesting message" { - test_fail "notification has no heading"; - } - - if not body :raw :contains "You have been notified" { - test_fail "notification has no footer"; - } - - if not allof( - body :raw :contains "Test. Test", - body :raw :contains "Frop" ) { - test_fail "notification has no original message"; - } -} - -test_result_reset; - -test_set "message" text: -To: nico@frop.example.org -From: stephan@example.org -Subject: Test - -Test. Test -Frop! -. -; - -test "Body; $text[maxsize]$" { - notify :low :id "frop" :options "sirius@example.org" - :message text: -Received interesting message: - -$text[5]$ - -You have been notified. -. -; - - if not test_result_execute { - test_fail "failed to execute notify"; - } - - test_message :smtp 0; - - if not body :raw :contains "Received interesting message" { - test_fail "notification has no heading"; - } - - if not body :raw :contains "You have been notified" { - test_fail "notification has no footer"; - } - - if anyof( - body :raw :contains "Test. Test", - body :raw :contains "Frop" ) { - test_fail "original message in notification is not truncated"; - } - - if not body :raw :contains "Test." { - test_fail "notification does not contain the required message"; - } -} - -test_result_reset; - -test_set "message" text: -From: Whomever <whoever@example.com> -To: Someone <someone@example.com> -Date: Sat, 10 Oct 2009 00:30:04 +0200 -Subject: whatever -Content-Type: multipart/mixed; boundary=outer - -This is a multi-part message in MIME format. - ---outer -Content-Type: multipart/alternative; boundary=inner - -This is a nested multi-part message in MIME format. - ---inner -Content-Type: application/sieve; charset="us-ascii" - -keep; - ---inner -Content-Type: text/plain; charset="us-ascii" - -Friep! - ---inner-- - -This is the end of the inner MIME multipart. - ---outer -Content-Type: message/rfc822 - -From: Someone Else -Subject: hello request - -Please say Hello - ---outer-- - -This is the end of the outer MIME multipart. -. -; - -test "Body; Multipart Message" { - notify :low :id "frop" :options "stephan@example.org" - :message text: -Received interesting message: - -$text$ - -You have been notified. -. -; - - if not test_result_execute { - test_fail "failed to execute notify"; - } - - test_message :smtp 0; - - if not body :raw :contains "Friep!" { - test_fail "notification has incorrect content"; - } -} - - - -- GitLab