From f5268a9c37a811fcf51855089f3a76fa095cf1f3 Mon Sep 17 00:00:00 2001 From: Stephan Bosch <stephan@rename-it.nl> Date: Fri, 17 Jul 2009 03:28:04 +0200 Subject: [PATCH] Made deprecated notify extension implementation compatible with cmusieve, except for the denotify command. --- configure.in | 1 + src/lib-sieve/Makefile.am | 1 + src/lib-sieve/plugins/Makefile.am | 1 + src/lib-sieve/plugins/enotify/Makefile.am | 1 - src/lib-sieve/plugins/enotify/ext-notify.c | 374 -------- src/lib-sieve/plugins/notify/Makefile.am | 22 + src/lib-sieve/plugins/notify/cmd-denotify.c | 61 ++ src/lib-sieve/plugins/notify/cmd-notify.c | 849 ++++++++++++++++++ src/lib-sieve/plugins/notify/ext-enotify.c | 88 ++ .../plugins/notify/ext-notify-common.h | 49 + .../plugins/notify/ext-notify-limits.h | 10 + src/lib-sieve/plugins/notify/ext-notify.c | 105 +++ 12 files changed, 1187 insertions(+), 375 deletions(-) delete mode 100644 src/lib-sieve/plugins/enotify/ext-notify.c create mode 100644 src/lib-sieve/plugins/notify/Makefile.am create mode 100644 src/lib-sieve/plugins/notify/cmd-denotify.c create mode 100644 src/lib-sieve/plugins/notify/cmd-notify.c create mode 100644 src/lib-sieve/plugins/notify/ext-enotify.c create mode 100644 src/lib-sieve/plugins/notify/ext-notify-common.h create mode 100644 src/lib-sieve/plugins/notify/ext-notify-limits.h create mode 100644 src/lib-sieve/plugins/notify/ext-notify.c diff --git a/configure.in b/configure.in index 02171ecc2..f323269e6 100644 --- a/configure.in +++ b/configure.in @@ -113,6 +113,7 @@ src/lib-sieve/plugins/include/Makefile src/lib-sieve/plugins/body/Makefile src/lib-sieve/plugins/variables/Makefile src/lib-sieve/plugins/enotify/Makefile +src/lib-sieve/plugins/notify/Makefile src/lib-sieve/plugins/environment/Makefile src/lib-sieve-tool/Makefile src/plugins/Makefile diff --git a/src/lib-sieve/Makefile.am b/src/lib-sieve/Makefile.am index 366cf2910..ce9d26726 100644 --- a/src/lib-sieve/Makefile.am +++ b/src/lib-sieve/Makefile.am @@ -55,6 +55,7 @@ plugins = \ ./plugins/body/libsieve_ext_body.la \ ./plugins/variables/libsieve_ext_variables.la \ ./plugins/enotify/libsieve_ext_enotify.la \ + ./plugins/notify/libsieve_ext_notify.la \ ./plugins/environment/libsieve_ext_environment.la libsieve_la_DEPENDENCIES = $(plugins) diff --git a/src/lib-sieve/plugins/Makefile.am b/src/lib-sieve/plugins/Makefile.am index c75e88566..92389755b 100644 --- a/src/lib-sieve/plugins/Makefile.am +++ b/src/lib-sieve/plugins/Makefile.am @@ -10,5 +10,6 @@ SUBDIRS = \ body \ variables \ enotify \ + notify \ environment diff --git a/src/lib-sieve/plugins/enotify/Makefile.am b/src/lib-sieve/plugins/enotify/Makefile.am index ad0818c46..4d91fd8c4 100644 --- a/src/lib-sieve/plugins/enotify/Makefile.am +++ b/src/lib-sieve/plugins/enotify/Makefile.am @@ -23,7 +23,6 @@ notify_methods = \ libsieve_ext_enotify_la_SOURCES = \ ext-enotify.c \ - ext-notify.c \ ext-enotify-common.c \ $(commands) \ $(tests) \ diff --git a/src/lib-sieve/plugins/enotify/ext-notify.c b/src/lib-sieve/plugins/enotify/ext-notify.c deleted file mode 100644 index 776fae4dc..000000000 --- a/src/lib-sieve/plugins/enotify/ext-notify.c +++ /dev/null @@ -1,374 +0,0 @@ -/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file - */ - -/* Extension notify - * ---------------- - * - * Authors: Stephan Bosch - * Specification: draft-ietf-sieve-notify-00.txt - * Implementation: deprecated; provided for backwards compatibility - * denotify command is explicitly not supported. - * Status: deprecated - * - */ - -#include <stdio.h> - -#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-enotify-common.h" - -/* - * Commands - */ - -static bool cmd_notify_registered - (struct sieve_validator *valdtr, - struct sieve_command_registration *cmd_reg); -static bool cmd_notify_pre_validate - (struct sieve_validator *valdtr, struct sieve_command_context *cmd); -static bool cmd_notify_validate - (struct sieve_validator *valdtr, struct sieve_command_context *cmd); -static bool cmd_notify_generate - (const struct sieve_codegen_env *cgenv, - struct sieve_command_context *ctx); - -static bool cmd_denotify_pre_validate - (struct sieve_validator *valdtr, struct sieve_command_context *cmd); - -/* Notify command - * - * Syntax: - * notify [":method" string] [":id" string] - * [<":low" / ":normal" / ":high">] [":message" string] - */ - -static const struct sieve_command cmd_notify = { - "notify", - SCT_COMMAND, - 0, 0, FALSE, FALSE, - cmd_notify_registered, - cmd_notify_pre_validate, - cmd_notify_validate, - cmd_notify_generate, - NULL, -}; - -/* Denotify command (NOT IMPLEMENTED) - * - * Syntax: - * denotify [MATCH-TYPE string] [<":low" / ":normal" / ":high">] - */ -static const struct sieve_command cmd_denotify = { - "denotify", - SCT_COMMAND, - 0, 0, FALSE, FALSE, - NULL, - cmd_denotify_pre_validate, - NULL, NULL, NULL -}; - -/* - * Extension - */ - -static bool ext_notify_load(void); -static bool ext_notify_validator_load(struct sieve_validator *valdtr); - -static int ext_notify_my_id = -1; - -const struct sieve_extension notify_extension = { - "notify", - &ext_notify_my_id, - ext_notify_load, - NULL, - ext_notify_validator_load, - NULL, NULL, NULL, NULL, NULL, - SIEVE_EXT_DEFINE_NO_OPERATIONS, - SIEVE_EXT_DEFINE_NO_OPERANDS, -}; - -static bool ext_notify_load(void) -{ - /* Make sure real extension is registered, it is needed by the binary */ - (void)sieve_extension_require(&enotify_extension); - - return TRUE; -} - -/* - * Extension validation - */ - -static bool ext_notify_validator_extension_validate - (struct sieve_validator *valdtr, void *context, struct sieve_ast_argument *require_arg); - -const struct sieve_validator_extension notify_validator_extension = { - ¬ify_extension, - ext_notify_validator_extension_validate, - NULL -}; - -static bool ext_notify_validator_load(struct sieve_validator *valdtr) -{ - /* Register validator extension to check for conflict with enotify */ - sieve_validator_extension_register - (valdtr, ¬ify_validator_extension, NULL); - - /* Register new commands */ - sieve_validator_register_command(valdtr, &cmd_notify); - sieve_validator_register_command(valdtr, &cmd_denotify); - - return TRUE; -} - -static bool ext_notify_validator_extension_validate -(struct sieve_validator *valdtr, void *context ATTR_UNUSED, - struct sieve_ast_argument *require_arg) -{ - /* Check for conflict with enotify */ - if ( sieve_validator_extension_loaded(valdtr, &enotify_extension) ) { - sieve_argument_validate_error(valdtr, require_arg, - "the (deprecated) notify extension cannot be used " - "together with the enotify extension"); - return FALSE; - } - - return TRUE; -} - -/* - * Denotify/Notify command tags - */ - -/* Forward declarations */ - -static bool cmd_notify_validate_string_tag - (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, - struct sieve_command_context *cmd); -static bool cmd_notify_validate_importance_tag - (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, - struct sieve_command_context *cmd); - -/* Argument objects */ - -static const struct sieve_argument notify_method_tag = { - "method", - NULL, NULL, - cmd_notify_validate_string_tag, - NULL, NULL -}; - -static const struct sieve_argument notify_id_tag = { - "id", - NULL, NULL, - cmd_notify_validate_string_tag, - NULL, NULL -}; - -static const struct sieve_argument notify_message_tag = { - "message", - NULL, NULL, - cmd_notify_validate_string_tag, - NULL, NULL -}; - -static const struct sieve_argument notify_low_tag = { - "low", - NULL, NULL, - cmd_notify_validate_importance_tag, - NULL, NULL -}; - -static const struct sieve_argument notify_normal_tag = { - "normal", - NULL, NULL, - cmd_notify_validate_importance_tag, - NULL, NULL -}; - -static const struct sieve_argument notify_high_tag = { - "high", - NULL, NULL, - cmd_notify_validate_importance_tag, - NULL, NULL -}; - -/* - * Command validation context - */ - -struct cmd_notify_context_data { - struct sieve_ast_argument *method; - 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_context *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, SAAT_STRING) ) - return FALSE; - - if ( tag->argument == ¬ify_method_tag ) { - ctx_data->method = *arg; - - /* Removed */ - *arg = sieve_ast_arguments_detach(*arg, 1); - - } else if ( tag->argument == ¬ify_id_tag ) { - - /* Ignored */ - *arg = sieve_ast_arguments_detach(*arg, 1); - - } else if ( tag->argument == ¬ify_message_tag ) { - ctx_data->message = *arg; - - /* Skip parameter */ - *arg = sieve_ast_argument_next(*arg); - } - - return TRUE; -} - -static bool cmd_notify_validate_importance_tag -(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_ast_argument **arg, - struct sieve_command_context *cmd ATTR_UNUSED) -{ - struct sieve_ast_argument *tag = *arg; - - if ( tag->argument == ¬ify_low_tag ) - sieve_ast_argument_number_substitute(tag, 1); - else if ( tag->argument == ¬ify_normal_tag ) - sieve_ast_argument_number_substitute(tag, 2); - else - sieve_ast_argument_number_substitute(tag, 3); - - tag->argument = &number_argument; - - /* Skip parameter */ - *arg = sieve_ast_argument_next(*arg); - - return TRUE; -} - -/* - * Command registration - */ - -static bool cmd_notify_registered -(struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg) -{ - sieve_validator_register_tag - (valdtr, cmd_reg, ¬ify_method_tag, 0); - sieve_validator_register_tag - (valdtr, cmd_reg, ¬ify_id_tag, 0); - sieve_validator_register_tag - (valdtr, cmd_reg, ¬ify_message_tag, CMD_NOTIFY_OPT_MESSAGE); - - sieve_validator_register_tag - (valdtr, cmd_reg, ¬ify_low_tag, CMD_NOTIFY_OPT_IMPORTANCE); - sieve_validator_register_tag - (valdtr, cmd_reg, ¬ify_normal_tag, CMD_NOTIFY_OPT_IMPORTANCE); - sieve_validator_register_tag - (valdtr, cmd_reg, ¬ify_high_tag, CMD_NOTIFY_OPT_IMPORTANCE); - - return TRUE; -} - -/* - * Command validation - */ - -/* Notify */ - -static bool cmd_notify_pre_validate -(struct sieve_validator *valdtr ATTR_UNUSED, - struct sieve_command_context *cmd) -{ - struct cmd_notify_context_data *ctx_data; - - /* Assign context */ - ctx_data = p_new(sieve_command_pool(cmd), - struct cmd_notify_context_data, 1); - cmd->data = ctx_data; - - return TRUE; -} - -static bool cmd_notify_validate -(struct sieve_validator *valdtr, struct sieve_command_context *cmd) -{ - struct cmd_notify_context_data *ctx_data = - (struct cmd_notify_context_data *) cmd->data; - - if ( ctx_data->method == NULL ) { - sieve_command_validate_error(valdtr, cmd, - "the notify command must have a ':method' argument " - "(the deprecated notify extension is not fully implemented)"); - return FALSE; - } - - if ( !sieve_ast_argument_attach(cmd->ast_node, ctx_data->method) ) { - /* Very unlikely */ - sieve_command_validate_error(valdtr, cmd, - "generate notify command; script is too complex"); - return FALSE; - } - - return ext_enotify_compile_check_arguments - (valdtr, ctx_data->method, ctx_data->message, NULL, NULL); -} - -/* Denotify */ - -static bool cmd_denotify_pre_validate -(struct sieve_validator *valdtr, struct sieve_command_context *cmd) -{ - sieve_command_validate_error(valdtr, cmd, - "the denotify command cannot be used " - "(the deprecated notify extension is not fully implemented)"); - return FALSE; -} - -/* - * Code generation - */ - -static bool cmd_notify_generate -(const struct sieve_codegen_env *cgenv, struct sieve_command_context *ctx) -{ - sieve_operation_emit_code(cgenv->sbin, ¬ify_operation); - - /* Emit source line */ - sieve_code_source_line_emit(cgenv->sbin, sieve_command_source_line(ctx)); - - /* Generate arguments */ - return sieve_generate_arguments(cgenv, ctx, NULL); -} - diff --git a/src/lib-sieve/plugins/notify/Makefile.am b/src/lib-sieve/plugins/notify/Makefile.am new file mode 100644 index 000000000..26e00b129 --- /dev/null +++ b/src/lib-sieve/plugins/notify/Makefile.am @@ -0,0 +1,22 @@ +noinst_LTLIBRARIES = libsieve_ext_notify.la + +AM_CPPFLAGS = \ + -I../../ \ + -I../variables \ + -I$(dovecot_incdir) \ + -I$(dovecot_incdir)/src/lib \ + -I$(dovecot_incdir)/src/lib-mail \ + -I$(dovecot_incdir)/src/lib-storage + +commands = \ + cmd-notify.c \ + cmd-denotify.c + +libsieve_ext_notify_la_SOURCES = \ + ext-notify.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 new file mode 100644 index 000000000..7f6f33bb5 --- /dev/null +++ b/src/lib-sieve/plugins/notify/cmd-denotify.c @@ -0,0 +1,61 @@ +/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file + */ + +#include "lib.h" + +#include "sieve-common.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 "ext-notify-common.h" + +/* Denotify command (NOT IMPLEMENTED) + * + * Syntax: + * denotify [MATCH-TYPE string] [<":low" / ":normal" / ":high">] + */ + +static bool cmd_denotify_pre_validate + (struct sieve_validator *valdtr, struct sieve_command_context *cmd); + +const struct sieve_command cmd_denotify = { + "denotify", + SCT_COMMAND, + 0, 0, FALSE, FALSE, + NULL, + cmd_denotify_pre_validate, + NULL, NULL, NULL +}; + +/* + * Denotify operation + */ + +const struct sieve_operation denotify_operation = { + "DENOTIFY", + ¬ify_extension, + EXT_NOTIFY_OPERATION_DENOTIFY, + NULL, NULL +}; + +/* + * Command validation + */ + +static bool cmd_denotify_pre_validate +(struct sieve_validator *valdtr, struct sieve_command_context *cmd) +{ + sieve_command_validate_error(valdtr, cmd, + "the denotify command cannot be used " + "(the deprecated notify extension is not fully implemented)"); + return FALSE; +} + + diff --git a/src/lib-sieve/plugins/notify/cmd-notify.c b/src/lib-sieve/plugins/notify/cmd-notify.c new file mode 100644 index 000000000..41fa21ff0 --- /dev/null +++ b/src/lib-sieve/plugins/notify/cmd-notify.c @@ -0,0 +1,849 @@ +/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" +#include "str.h" +#include "ioloop.h" +#include "str-sanitize.h" +#include "message-date.h" +#include "mail-storage.h" + +#include "rfc2822.h" + +#include "sieve-common.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 "ext-notify-common.h" +#include "ext-notify-limits.h" + +/* Notify command + * + * Syntax: + * notify [":method" string] [":id" string] [":options" string-list] + * [<":low" / ":normal" / ":high">] ["message:" string] + * + */ + +static bool cmd_notify_registered + (struct sieve_validator *valdtr, + struct sieve_command_registration *cmd_reg); +static bool cmd_notify_pre_validate + (struct sieve_validator *valdtr, struct sieve_command_context *cmd); +static bool cmd_notify_validate + (struct sieve_validator *valdtr, struct sieve_command_context *cmd); +static bool cmd_notify_generate + (const struct sieve_codegen_env *cgenv, + struct sieve_command_context *ctx); + +const struct sieve_command cmd_notify_old = { + "notify", + SCT_COMMAND, + 0, 0, FALSE, FALSE, + cmd_notify_registered, + cmd_notify_pre_validate, + cmd_notify_validate, + cmd_notify_generate, + NULL, +}; + +/* + * Tagged arguments + */ + +/* Forward declarations */ + +static bool cmd_notify_validate_string_tag + (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command_context *cmd); +static bool cmd_notify_validate_stringlist_tag + (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command_context *cmd); +static bool cmd_notify_validate_importance_tag + (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command_context *cmd); + +/* Argument objects */ + +static const struct sieve_argument notify_method_tag = { + "method", + NULL, NULL, + cmd_notify_validate_string_tag, + NULL, NULL +}; + +static const struct sieve_argument notify_options_tag = { + "options", + NULL, NULL, + cmd_notify_validate_stringlist_tag, + NULL, NULL +}; + +static const struct sieve_argument notify_id_tag = { + "id", + NULL, NULL, + cmd_notify_validate_string_tag, + NULL, NULL +}; + +static const struct sieve_argument notify_message_tag = { + "message", + NULL, NULL, + cmd_notify_validate_string_tag, + NULL, NULL +}; + +static const struct sieve_argument notify_low_tag = { + "low", + NULL, NULL, + cmd_notify_validate_importance_tag, + NULL, NULL +}; + +static const struct sieve_argument notify_normal_tag = { + "normal", + NULL, NULL, + cmd_notify_validate_importance_tag, + NULL, NULL +}; + +static const struct sieve_argument notify_high_tag = { + "high", + NULL, NULL, + cmd_notify_validate_importance_tag, + NULL, NULL +}; + +/* + * Notify operation + */ + +static bool cmd_notify_operation_dump + (const struct sieve_operation *op, + const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int cmd_notify_operation_execute + (const struct sieve_operation *op, + const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation notify_old_operation = { + "NOTIFY", + ¬ify_extension, + EXT_NOTIFY_OPERATION_NOTIFY, + cmd_notify_operation_dump, + 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_data *act, + const struct sieve_action_data *act_other); +static void act_notify_print + (const struct sieve_action *action, const struct sieve_result_print_env *rpenv, + void *context, bool *keep); +static bool act_notify_commit + (const struct sieve_action *action, const struct sieve_action_exec_env *aenv, + void *tr_context, bool *keep); + +/* Action object */ + +const struct sieve_action act_notify_old = { + "notify", + 0, + NULL, + act_notify_check_duplicate, + NULL, + act_notify_print, + NULL, NULL, + act_notify_commit, + NULL +}; + +/* + * 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_context *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, SAAT_STRING) ) + return FALSE; + + if ( tag->argument == ¬ify_method_tag ) { + ctx_data->method = *arg; + + /* Removed */ + *arg = sieve_ast_arguments_detach(*arg, 1); + + } else if ( tag->argument == ¬ify_id_tag ) { + ctx_data->id = *arg; + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + + } else if ( tag->argument == ¬ify_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_context *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, SAAT_STRING_LIST) ) + return FALSE; + + /* Assign context */ + ctx_data->options = *arg; + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + + return TRUE; +} + +static bool cmd_notify_validate_importance_tag +(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_ast_argument **arg, + struct sieve_command_context *cmd ATTR_UNUSED) +{ + struct sieve_ast_argument *tag = *arg; + + if ( tag->argument == ¬ify_low_tag ) + sieve_ast_argument_number_substitute(tag, 3); + else if ( tag->argument == ¬ify_normal_tag ) + sieve_ast_argument_number_substitute(tag, 2); + else + sieve_ast_argument_number_substitute(tag, 1); + + tag->argument = &number_argument; + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + + return TRUE; +} + +/* + * Command registration + */ + +static bool cmd_notify_registered +(struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg) +{ + sieve_validator_register_tag + (valdtr, cmd_reg, ¬ify_method_tag, 0); + sieve_validator_register_tag + (valdtr, cmd_reg, ¬ify_id_tag, OPT_ID); + sieve_validator_register_tag + (valdtr, cmd_reg, ¬ify_message_tag, OPT_MESSAGE); + sieve_validator_register_tag + (valdtr, cmd_reg, ¬ify_options_tag, OPT_OPTIONS); + + sieve_validator_register_tag + (valdtr, cmd_reg, ¬ify_low_tag, OPT_IMPORTANCE); + sieve_validator_register_tag + (valdtr, cmd_reg, ¬ify_normal_tag, OPT_IMPORTANCE); + sieve_validator_register_tag + (valdtr, cmd_reg, ¬ify_high_tag, OPT_IMPORTANCE); + + return TRUE; +} + +/* + * Command validation + */ + +static bool cmd_notify_pre_validate +(struct sieve_validator *valdtr ATTR_UNUSED, + struct sieve_command_context *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; + bool result = FALSE; + + T_BEGIN { + result = sieve_address_validate(address, &error); + + if ( !result ) { + 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 TRUE; +} + +static bool cmd_notify_validate +(struct sieve_validator *valdtr, struct sieve_command_context *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_context *ctx) +{ + sieve_operation_emit_code(cgenv->sbin, ¬ify_old_operation); + + /* Emit source line */ + sieve_code_source_line_emit(cgenv->sbin, sieve_command_source_line(ctx)); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, ctx, NULL); +} + +/* + * Code dump + */ + +static bool cmd_notify_operation_dump +(const struct sieve_operation *op ATTR_UNUSED, + const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + int opt_code = 1; + + sieve_code_dumpf(denv, "NOTIFY"); + sieve_code_descend(denv); + + /* Source line */ + if ( !sieve_code_source_line_dump(denv, address) ) + return FALSE; + + /* Dump optional operands */ + if ( sieve_operand_optional_present(denv->sbin, address) ) { + while ( opt_code != 0 ) { + sieve_code_mark(denv); + + if ( !sieve_operand_optional_read(denv->sbin, address, &opt_code) ) + return FALSE; + + switch ( opt_code ) { + case 0: + break; + case OPT_IMPORTANCE: + if ( !sieve_opr_number_dump(denv, address, "importance") ) + return FALSE; + break; + case OPT_ID: + if ( !sieve_opr_string_dump(denv, address, "id") ) + return FALSE; + break; + case OPT_OPTIONS: + if ( !sieve_opr_stringlist_dump(denv, address, "options") ) + return FALSE; + break; + case OPT_MESSAGE: + if ( !sieve_opr_string_dump(denv, address, "message") ) + return FALSE; + break; + default: + return FALSE; + } + } + } + + return TRUE; +} + +/* + * Code execution + */ + +static int cmd_notify_operation_execute +(const struct sieve_operation *op ATTR_UNUSED, + const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + struct ext_notify_action *act; + pool_t pool; + int opt_code = 1; + sieve_number_t importance = 1; + struct sieve_coded_stringlist *options = NULL; + string_t *message = NULL, *id = NULL; + unsigned int source_line; + + /* + * Read operands + */ + + /* Source line */ + if ( !sieve_code_source_line_read(renv, address, &source_line) ) { + sieve_runtime_trace_error(renv, "invalid source line"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + /* Optional operands */ + if ( sieve_operand_optional_present(renv->sbin, address) ) { + while ( opt_code != 0 ) { + if ( !sieve_operand_optional_read(renv->sbin, address, &opt_code) ) { + sieve_runtime_trace_error(renv, "invalid optional operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + switch ( opt_code ) { + case 0: + break; + case OPT_IMPORTANCE: + if ( !sieve_opr_number_read(renv, address, &importance) ) { + sieve_runtime_trace_error(renv, "invalid importance operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + /* Enforce 0 < importance < 4 (just to be sure) */ + if ( importance < 1 ) + importance = 1; + else if ( importance > 3 ) + importance = 3; + break; + case OPT_ID: + if ( !sieve_opr_string_read(renv, address, &id) ) { + sieve_runtime_trace_error(renv, "invalid id operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + break; + case OPT_MESSAGE: + if ( !sieve_opr_string_read(renv, address, &message) ) { + sieve_runtime_trace_error(renv, "invalid from operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + break; + case OPT_OPTIONS: + if ( (options=sieve_opr_stringlist_read(renv, address)) == NULL ) { + sieve_runtime_trace_error(renv, "invalid options operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + break; + default: + sieve_runtime_trace_error(renv, "unknown optional operand: %d", + opt_code); + return SIEVE_EXEC_BIN_CORRUPT; + } + } + } + + /* + * Perform operation + */ + + sieve_runtime_trace(renv, "NOTIFY action"); + + /* Normalize and verify all :options addresses */ + if ( options != NULL ) { + string_t *raw_address; + bool result; + + 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)); + if ( message != NULL ) + act->message = p_strdup(pool, str_c(message)); + act->importance = importance; + + sieve_coded_stringlist_reset(options); + + p_array_init(&act->recipients, pool, 4); + + raw_address = NULL; + while ( (result=sieve_coded_stringlist_next_item(options, &raw_address)) + && raw_address != NULL ) { + const char *error; + const char *addr_norm = sieve_address_normalize(raw_address, &error); + + if ( addr_norm != NULL ) { + struct ext_notify_recipient recipient; + + recipient.full = p_strdup(pool, str_c(raw_address)); + recipient.normalized = p_strdup(pool, addr_norm); + + array_append(&act->recipients, &recipient, 1); + } else { + sieve_runtime_error(renv, + sieve_error_script_location(renv->script, source_line), + "specified :options address '%s' is invalid for " + "the mailto notify method: %s", + str_sanitize(str_c(raw_address), 128), error); + return SIEVE_EXEC_FAILURE; + } + } + + if ( !result ) { + sieve_runtime_trace_error(renv, "invalid options stringlist"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + return ( sieve_result_add_action + (renv, &act_notify_old, NULL, source_line, (void *) act, 0) >= 0 ); + } + + 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_data *act ATTR_UNUSED, + const struct sieve_action_data *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 ( sieve_address_compare + (new_rcpts[i].normalized, old_rcpts[j].normalized, TRUE) == 0 ) + 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 ATTR_UNUSED, + const struct sieve_result_print_env *rpenv, void *context, + bool *keep ATTR_UNUSED) +{ + const struct ext_notify_action *act = + (const struct ext_notify_action *) context; + const struct ext_notify_recipient *recipients; + unsigned int count, i; + + sieve_result_action_printf + ( rpenv, "send (depricated) notification with method 'mailto':"); + + /* Print main method parameters */ + + sieve_result_printf + ( rpenv, " => importance : %d\n", 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_message_data *msgdata = aenv->msgdata; + const struct sieve_script_env *senv = aenv->scriptenv; + const struct ext_notify_recipient *recipients; + void *smtp_handle; + unsigned int count, i; + FILE *f; + const char *outmsgid; + + /* 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 ( senv->smtp_open == NULL || senv->smtp_close == NULL ) { + sieve_result_warning(aenv, + "notify action has no means to send mail"); + return TRUE; + } + + /* Send message to all recipients */ + for ( i = 0; i < count; i++ ) { + + if ( msgdata->return_path != NULL ) + smtp_handle = senv->smtp_open(recipients[i].normalized, + senv->postmaster_address, &f); + else + smtp_handle = senv->smtp_open(recipients[i].normalized, NULL, &f); + + outmsgid = sieve_message_get_new_id(senv); + + rfc2822_header_field_write(f, "X-Sieve", SIEVE_IMPLEMENTATION); + rfc2822_header_field_write(f, "Message-ID", outmsgid); + rfc2822_header_field_write(f, "Date", message_date_create(ioloop_time)); + + /* Set importance */ + switch ( act->importance ) { + case 1: + rfc2822_header_field_write(f, "X-Priority", "1 (Highest)"); + rfc2822_header_field_write(f, "Importance", "High"); + break; + case 3: + rfc2822_header_field_write(f, "X-Priority", "5 (Lowest)"); + rfc2822_header_field_write(f, "Importance", "Low"); + break; + case 2: + default: + rfc2822_header_field_write(f, "X-Priority", "3 (Normal)"); + rfc2822_header_field_write(f, "Importance", "Normal"); + break; + } + + rfc2822_header_field_printf(f, "From", "%s", + t_strdup_printf("Postmaster <%s>", senv->postmaster_address)); + + rfc2822_header_field_printf(f, "To", "%s", recipients[i].full); + + rfc2822_header_field_write(f, "Subject", "[SIEVE] New mail notification"); + + rfc2822_header_field_write(f, "Auto-Submitted", "auto-generated (notify)"); + rfc2822_header_field_write(f, "Precedence", "bulk"); + + if (contains_8bit(act->message)) { + rfc2822_header_field_write(f, "MIME-Version", "1.0"); + rfc2822_header_field_write(f, + "Content-Type", "text/plain; charset=UTF-8"); + rfc2822_header_field_write(f, "Content-Transfer-Encoding", "8bit"); + } + + /* Generate message body */ + fprintf(f, "\r\n"); + fprintf(f, "%s\r\n", act->message); + + if ( senv->smtp_close(smtp_handle) ) { + sieve_result_log(aenv, + "sent mail notification to <%s>", + str_sanitize(recipients[i].normalized, 80)); + } else { + sieve_result_error(aenv, + "failed to send mail notification to <%s> " + "(refer to system log for more information)", + str_sanitize(recipients[i].normalized, 80)); + } + } + + return TRUE; +} + +static bool act_notify_commit +(const struct sieve_action *action ATTR_UNUSED, + const struct sieve_action_exec_env *aenv, void *tr_context, + bool *keep ATTR_UNUSED) +{ + const struct ext_notify_action *act = + (const struct ext_notify_action *) tr_context; + const struct sieve_message_data *msgdata = aenv->msgdata; + const char *const *headers; + + /* Is the message an automatic reply ? */ + if ( mail_get_headers + (msgdata->mail, "auto-submitted", &headers) >= 0 ) { + const char *const *hdsp = headers; + + /* Theoretically multiple headers could exist, so lets make sure */ + while ( *hdsp != NULL ) { + if ( strcasecmp(*hdsp, "no") != 0 ) { + sieve_result_log(aenv, + "not sending notification for auto-submitted message from <%s>", + str_sanitize(msgdata->return_path, 128)); + return TRUE; + } + hdsp++; + } + } + + return act_notify_send(aenv, act); +} + + + + + + + diff --git a/src/lib-sieve/plugins/notify/ext-enotify.c b/src/lib-sieve/plugins/notify/ext-enotify.c new file mode 100644 index 000000000..2ed08298d --- /dev/null +++ b/src/lib-sieve/plugins/notify/ext-enotify.c @@ -0,0 +1,88 @@ +/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file + */ + +/* Extension enotify + * ------------------ + * + * Authors: Stephan Bosch + * Specification: RFC 5435 + * Implementation: full + * Status: testing + * + */ + +#include <stdio.h> + +#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 "sieve-ext-variables.h" + +#include "ext-enotify-common.h" + +/* + * Operations + */ + +const struct sieve_operation *ext_enotify_operations[] = { + ¬ify_operation, + &valid_notify_method_operation, + ¬ify_method_capability_operation +}; + +/* + * Extension + */ + +static bool ext_enotify_load(void); +static void ext_enotify_unload(void); +static bool ext_enotify_validator_load(struct sieve_validator *valdtr); + +static int ext_my_id = -1; + +const struct sieve_extension enotify_extension = { + "enotify", + &ext_my_id, + ext_enotify_load, + ext_enotify_unload, + ext_enotify_validator_load, + NULL, NULL, NULL, NULL, NULL, + SIEVE_EXT_DEFINE_OPERATIONS(ext_enotify_operations), + SIEVE_EXT_DEFINE_OPERAND(encodeurl_operand) +}; + +static bool ext_enotify_load(void) +{ + ext_enotify_methods_init(); + + sieve_extension_capabilities_register(¬ify_capabilities); + + return TRUE; +} + +static void ext_enotify_unload(void) +{ + ext_enotify_methods_deinit(); +} + +static bool ext_enotify_validator_load(struct sieve_validator *valdtr) +{ + /* Register new commands */ + sieve_validator_register_command(valdtr, ¬ify_command); + sieve_validator_register_command(valdtr, &valid_notify_method_test); + sieve_validator_register_command(valdtr, ¬ify_method_capability_test); + + /* Register new set modifier for variables extension */ + sieve_variables_modifier_register(valdtr, &encodeurl_modifier); + + return TRUE; +} + diff --git a/src/lib-sieve/plugins/notify/ext-notify-common.h b/src/lib-sieve/plugins/notify/ext-notify-common.h new file mode 100644 index 000000000..8f07f5397 --- /dev/null +++ b/src/lib-sieve/plugins/notify/ext-notify-common.h @@ -0,0 +1,49 @@ +/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file + */ + +#ifndef __EXT_NOTIFY_COMMON_H +#define __EXT_NOTIFY_COMMON_H + +/* + * Extension + */ + +extern const struct sieve_extension notify_extension; + +/* + * Commands + */ + +extern const struct sieve_command cmd_notify_old; +extern const struct sieve_command cmd_denotify; + +/* + * Operations + */ + +extern const struct sieve_operation notify_old_operation; +extern const struct sieve_operation denotify_operation; + +enum ext_notify_opcode { + EXT_NOTIFY_OPERATION_NOTIFY, + EXT_NOTIFY_OPERATION_DENOTIFY, +}; + +/* Action context */ + +struct ext_notify_recipient { + const char *full; + const char *normalized; +}; + +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; +}; + +#endif /* __EXT_NOTIFY_COMMON_H */ diff --git a/src/lib-sieve/plugins/notify/ext-notify-limits.h b/src/lib-sieve/plugins/notify/ext-notify-limits.h new file mode 100644 index 000000000..d6fc88eca --- /dev/null +++ b/src/lib-sieve/plugins/notify/ext-notify-limits.h @@ -0,0 +1,10 @@ +/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file + */ + +#ifndef __EXT_NOTIFY_LIMITS_H +#define __EXT_NOTIFY_LIMITS_H + +#define EXT_NOTIFY_MAX_RECIPIENTS 8 +#define EXT_NOTIFY_MAX_MESSAGE 256 + +#endif /* __EXT_NOTIFY_LIMITS_H */ diff --git a/src/lib-sieve/plugins/notify/ext-notify.c b/src/lib-sieve/plugins/notify/ext-notify.c new file mode 100644 index 000000000..53426863c --- /dev/null +++ b/src/lib-sieve/plugins/notify/ext-notify.c @@ -0,0 +1,105 @@ +/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file + */ + +/* Extension notify + * ---------------- + * + * Authors: Stephan Bosch + * Specification: draft-ietf-sieve-notify-00.txt + * Implementation: deprecated; provided for backwards compatibility + * denotify command is explicitly not supported. + * Status: deprecated + * + */ + +#include <stdio.h> + +#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 *ext_notify_operations[] = { + ¬ify_old_operation, + &denotify_operation +}; + +/* + * Extension + */ + +static bool ext_notify_validator_load(struct sieve_validator *valdtr); + +static int ext_notify_my_id = -1; + +const struct sieve_extension notify_extension = { + "notify", + &ext_notify_my_id, + NULL, + NULL, + ext_notify_validator_load, + NULL, NULL, NULL, NULL, NULL, + SIEVE_EXT_DEFINE_OPERATIONS(ext_notify_operations), + SIEVE_EXT_DEFINE_NO_OPERANDS, +}; + +/* + * Extension validation + */ + +static bool ext_notify_validator_extension_validate + (struct sieve_validator *valdtr, void *context, + struct sieve_ast_argument *require_arg); + +const struct sieve_validator_extension notify_validator_extension = { + ¬ify_extension, + ext_notify_validator_extension_validate, + NULL +}; + +static bool ext_notify_validator_load(struct sieve_validator *valdtr) +{ + /* Register validator extension to check for conflict with enotify */ + sieve_validator_extension_register + (valdtr, ¬ify_validator_extension, NULL); + + /* Register new commands */ + sieve_validator_register_command(valdtr, &cmd_notify_old); + sieve_validator_register_command(valdtr, &cmd_denotify); + + return TRUE; +} + +static bool ext_notify_validator_extension_validate +(struct sieve_validator *valdtr, void *context ATTR_UNUSED, + struct sieve_ast_argument *require_arg) +{ + const struct sieve_extension *ext; + + if ( (ext=sieve_extension_get_by_name("enotify")) != NULL ) { + + /* Check for conflict with enotify */ + if ( sieve_validator_extension_loaded(valdtr, ext) ) { + sieve_argument_validate_error(valdtr, require_arg, + "the (deprecated) notify extension cannot be used " + "together with the enotify extension"); + return FALSE; + } + } + + return TRUE; +} + + -- GitLab