diff --git a/configure.in b/configure.in index 2bfc9724003bf2f6407699f99fb958a69cfc8fca..461d2c85932b6e7a5af2ea5eab0c73c22a682789 100644 --- a/configure.in +++ b/configure.in @@ -159,6 +159,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/enotify/mailto/Makefile src/lib-sieve/plugins/notify/Makefile src/lib-sieve/plugins/environment/Makefile src/lib-sieve/plugins/mailbox/Makefile diff --git a/src/lib-sieve/plugins/enotify/Makefile.am b/src/lib-sieve/plugins/enotify/Makefile.am index 662bf4dc107d6bffb8ddc170bec720a66e0152d5..648fe6be1100e6e3eeef79169d8396eb7061718e 100644 --- a/src/lib-sieve/plugins/enotify/Makefile.am +++ b/src/lib-sieve/plugins/enotify/Makefile.am @@ -1,3 +1,5 @@ +SUBDIRS = mailto + noinst_LTLIBRARIES = libsieve_ext_enotify.la AM_CPPFLAGS = \ @@ -16,15 +18,19 @@ var_modifiers = \ vmodf-encodeurl.c notify_methods = \ - ntfy-mailto.c + ./mailto/libsieve_ext_enotify_mailto.la +libsieve_ext_enotify_la_DEPENDENCIES = \ + $(notify_methods) +libsieve_ext_enotify_la_LIBADD = \ + $(notify_methods) + libsieve_ext_enotify_la_SOURCES = \ ext-enotify.c \ ext-enotify-common.c \ $(commands) \ $(tests) \ - $(var_modifiers) \ - $(notify_methods) + $(var_modifiers) public_headers = \ sieve-ext-enotify.h diff --git a/src/lib-sieve/plugins/enotify/cmd-notify.c b/src/lib-sieve/plugins/enotify/cmd-notify.c index 5b325f822ee0ee5955aa0a092ffa137546866f95..7f160327f364a6b4c97dae7d549fb80ce7cb9669 100644 --- a/src/lib-sieve/plugins/enotify/cmd-notify.c +++ b/src/lib-sieve/plugins/enotify/cmd-notify.c @@ -4,6 +4,7 @@ #include "lib.h" #include "sieve-common.h" +#include "sieve-error.h" #include "sieve-code.h" #include "sieve-extensions.h" #include "sieve-commands.h" @@ -523,7 +524,9 @@ static int act_notify_check_duplicate const struct sieve_action *act_other ATTR_UNUSED) { const struct sieve_enotify_action *nact1, *nact2; - struct sieve_enotify_log nlog; + const struct sieve_enotify_method_def *nmth_def; + struct sieve_enotify_env nenv; + bool result = TRUE; if ( act->context == NULL || act_other->context == NULL ) return 0; @@ -531,15 +534,24 @@ static int act_notify_check_duplicate nact1 = (const struct sieve_enotify_action *) act->context; nact2 = (const struct sieve_enotify_action *) act_other->context; - if ( nact1->method == NULL || nact1->method->action_check_duplicates == NULL ) + if ( nact1->method == NULL || nact1->method->def == NULL ) return 0; - memset(&nlog, 0, sizeof(nlog)); - nlog.location = act->location; - nlog.ehandler = sieve_result_get_error_handler(renv->result); + nmth_def = nact1->method->def; + if ( nmth_def->action_check_duplicates == NULL ) + return 0; + + memset(&nenv, sizeof(nenv), 0); + nenv.method = nact1->method; + nenv.ehandler = sieve_prefix_ehandler_create + (sieve_result_get_error_handler(renv->result), act->location, "notify"); - return nact1->method->action_check_duplicates - (&nlog, nact1->method_context, nact2->method_context, act_other->location); + result = nmth_def->action_check_duplicates + (&nenv, nact1->method_context, nact2->method_context, act_other->location); + + sieve_error_handler_unref(&nenv.ehandler); + + return result; } /* Result printing */ @@ -550,17 +562,22 @@ static void act_notify_print { const struct sieve_enotify_action *act = (const struct sieve_enotify_action *) action->context; + const struct sieve_enotify_method *method; - sieve_result_action_printf - ( rpenv, "send notification with method '%s:':", act->method->identifier); - - if ( act->method->action_print != NULL ) { - struct sieve_enotify_print_env penv; + method = act->method; + + if ( method->def != NULL ) { + sieve_result_action_printf + ( rpenv, "send notification with method '%s:':", method->def->identifier); - memset(&penv, 0, sizeof(penv)); - penv.result_penv = rpenv; + if ( method->def->action_print != NULL ) { + struct sieve_enotify_print_env penv; - act->method->action_print(&penv, act); + memset(&penv, 0, sizeof(penv)); + penv.result_penv = rpenv; + + method->def->action_print(&penv, act); + } } } @@ -572,21 +589,25 @@ static bool act_notify_commit { const struct sieve_enotify_action *act = (const struct sieve_enotify_action *) action->context; + const struct sieve_enotify_method *method = act->method; struct sieve_enotify_exec_env nenv; - struct sieve_enotify_log nlog; - - memset(&nlog, 0, sizeof(nlog)); - nlog.aenv = aenv; + bool result = TRUE; + + if ( method->def != NULL && method->def->action_execute != NULL ) { + /* Compose log structure */ + memset(&nenv, sizeof(nenv), 0); + nenv.method = method; + nenv.scriptenv = aenv->scriptenv; + nenv.msgdata = aenv->msgdata; + nenv.msgctx = aenv->msgctx; - nenv.scriptenv = aenv->scriptenv; - nenv.msgdata = aenv->msgdata; - nenv.msgctx = aenv->msgctx; - nenv.notify_log = &nlog; + nenv.ehandler = sieve_prefix_ehandler_create + (aenv->ehandler, action->location, "notify action"); - if ( act->method->action_execute != NULL ) - return act->method->action_execute(&nenv, act); + result = method->def->action_execute(&nenv, act); + } - return TRUE; + return result; } diff --git a/src/lib-sieve/plugins/enotify/ext-enotify-common.c b/src/lib-sieve/plugins/enotify/ext-enotify-common.c index fb27c315113bac4bc6fe1d367dda50629b42e4aa..5e34d842d68c1cc2e5017e26968b03461398e31d 100644 --- a/src/lib-sieve/plugins/enotify/ext-enotify-common.c +++ b/src/lib-sieve/plugins/enotify/ext-enotify-common.c @@ -51,30 +51,57 @@ const struct sieve_extension_capabilities notify_capabilities = { ext_notify_get_methods_string }; +/* + * Core notification methods + */ + +extern const struct sieve_enotify_method_def mailto_notify; + /* * Notify method registry */ -static void ext_enotify_method_register -(struct ext_enotify_context *ectx, const struct sieve_enotify_method *method) +static const struct sieve_enotify_method *ext_enotify_method_register +(struct sieve_instance *svinst, struct ext_enotify_context *ectx, + const struct sieve_enotify_method_def *nmth_def) { - array_append(&ectx->notify_methods, &method, 1); + struct sieve_enotify_method *nmth; + + nmth = array_append_space(&ectx->notify_methods); + nmth->def = nmth_def; + nmth->svinst = svinst; + + if ( nmth_def->load != NULL ) + nmth_def->load(nmth, &nmth->context); + + return nmth; } -void ext_enotify_methods_init(struct ext_enotify_context *ectx) +void ext_enotify_methods_init +(struct sieve_instance *svinst, struct ext_enotify_context *ectx) { p_array_init(&ectx->notify_methods, default_pool, 4); - ext_enotify_method_register(ectx, &mailto_notify); + ext_enotify_method_register(svinst, ectx, &mailto_notify); } void ext_enotify_methods_deinit(struct ext_enotify_context *ectx) { + const struct sieve_enotify_method *methods; + unsigned int meth_count, i; + + methods = array_get(&ectx->notify_methods, &meth_count); + for ( i = 0; i < meth_count; i++ ) { + if ( methods[i].def != NULL && methods[i].def->unload != NULL ) + methods[i].def->unload(&methods[i]); + } + array_free(&ectx->notify_methods); } -void sieve_enotify_method_register -(struct sieve_instance *svinst, const struct sieve_enotify_method *method) +const struct sieve_enotify_method *sieve_enotify_method_register +(struct sieve_instance *svinst, + const struct sieve_enotify_method_def *nmth_def) { const struct sieve_extension *ntfy_ext = sieve_extension_get_by_name(svinst, "enotify"); @@ -83,8 +110,10 @@ void sieve_enotify_method_register struct ext_enotify_context *ectx = (struct ext_enotify_context *) ntfy_ext->context; - ext_enotify_method_register(ectx, method); + return ext_enotify_method_register(svinst, ectx, nmth_def); } + + return NULL; } const struct sieve_enotify_method *ext_enotify_method_find @@ -93,13 +122,15 @@ const struct sieve_enotify_method *ext_enotify_method_find struct ext_enotify_context *ectx = (struct ext_enotify_context *) ntfy_ext->context; unsigned int meth_count, i; - const struct sieve_enotify_method *const *methods; + const struct sieve_enotify_method *methods; methods = array_get(&ectx->notify_methods, &meth_count); for ( i = 0; i < meth_count; i++ ) { - if ( strcasecmp(methods[i]->identifier, identifier) == 0 ) { - return methods[i]; + if ( methods[i].def == NULL ) continue; + + if ( strcasecmp(methods[i].def->identifier, identifier) == 0 ) { + return &methods[i]; } } @@ -112,17 +143,17 @@ static const char *ext_notify_get_methods_string struct ext_enotify_context *ectx = (struct ext_enotify_context *) ntfy_ext->context; unsigned int meth_count, i; - const struct sieve_enotify_method *const *methods; + const struct sieve_enotify_method *methods; string_t *result = t_str_new(128); methods = array_get(&ectx->notify_methods, &meth_count); if ( meth_count > 0 ) { - str_append(result, methods[0]->identifier); + str_append(result, methods[0].def->identifier); for ( i = 1; i < meth_count; i++ ) { str_append_c(result, ' '); - str_append(result, methods[i]->identifier); + str_append(result, methods[i].def->identifier); } return str_c(result); @@ -173,7 +204,7 @@ static const char *ext_enotify_uri_scheme_parse(const char **uri_p) } static bool ext_enotify_option_parse -(struct sieve_enotify_log *nlog, const char *option, bool name_only, +(struct sieve_enotify_env *nenv, const char *option, bool name_only, const char **opt_name_r, const char **opt_value_r) { const char *p = option; @@ -194,7 +225,7 @@ static bool ext_enotify_option_parse /* Explicitly report empty option as such */ if ( *p == '\0' ) { - sieve_enotify_error(nlog, "empty option specified"); + sieve_enotify_error(nenv, "empty option specified"); return FALSE; } @@ -209,7 +240,7 @@ static bool ext_enotify_option_parse /* Parsing must end at '=' and we must parse at least one character */ if ( *p != '=' || p == option ) { - sieve_enotify_error(nlog, "invalid option name specified in option '%s'", + sieve_enotify_error(nenv, "invalid option name specified in option '%s'", str_sanitize(option, 80)); return FALSE; } @@ -235,7 +266,7 @@ static bool ext_enotify_option_parse /* Parse must end at end of string */ if ( *p != '\0' ) { - sieve_enotify_error(nlog, + sieve_enotify_error(nenv, "notify command: invalid option value specified in option '%s'", str_sanitize(option, 80)); return FALSE; @@ -260,20 +291,22 @@ static int _ext_enotify_option_check (struct _ext_enotify_option_check_context *) context; struct sieve_validator *valdtr = optn_context->valdtr; const struct sieve_enotify_method *method = optn_context->method; - struct sieve_enotify_log nlog; + struct sieve_enotify_env nenv; const char *option = sieve_ast_argument_strc(arg); const char *opt_name = NULL, *opt_value = NULL; - bool literal = sieve_argument_is_string_literal(arg); + bool result = TRUE, check = TRUE; /* Compose log structure */ - memset(&nlog, 0, sizeof(nlog)); - nlog.ehandler = sieve_validator_error_handler(valdtr); - nlog.prefix = "notify command"; - nlog.location = sieve_error_script_location - (sieve_validator_script(valdtr), arg->source_line); + memset(&nenv, sizeof(nenv), 0); + nenv.method = method; + nenv.ehandler = sieve_prefix_ehandler_create + (sieve_validator_error_handler(valdtr), + sieve_error_script_location + (sieve_validator_script(valdtr), arg->source_line), + "notify command"); /* Parse option */ - if ( !literal ) { + if ( !sieve_argument_is_string_literal(arg) ) { /* Variable string: partial option parse * * If the string item is not a string literal, it cannot be validated fully @@ -284,19 +317,22 @@ static int _ext_enotify_option_check * of the option itself and not the assigned value. */ if ( !ext_enotify_option_parse(NULL, option, TRUE, &opt_name, &opt_value) ) - return TRUE; + check = FALSE; } else { /* Literal string: full option parse */ if ( !ext_enotify_option_parse - (&nlog, option, FALSE, &opt_name, &opt_value) ) - return FALSE; + (&nenv, option, FALSE, &opt_name, &opt_value) ) + result = FALSE; } /* Call method's option check function */ - if ( method->compile_check_option != NULL ) - return method->compile_check_option(&nlog, opt_name, opt_value); + if ( result && check && method->def != NULL && + method->def->compile_check_option != NULL ) + result = method->def->compile_check_option(&nenv, opt_name, opt_value); - return TRUE; + sieve_error_handler_unref(&nenv.ehandler); + + return result; } bool ext_enotify_compile_check_arguments @@ -308,7 +344,8 @@ bool ext_enotify_compile_check_arguments const char *uri = sieve_ast_argument_strc(uri_arg); const char *scheme; const struct sieve_enotify_method *method; - struct sieve_enotify_log nlog; + struct sieve_enotify_env nenv; + bool result = TRUE; /* If the uri string is not a constant literal, we cannot determine which * method is used, so we bail out successfully and defer checking to runtime. @@ -331,67 +368,78 @@ bool ext_enotify_compile_check_arguments return FALSE; } + if ( method->def == NULL ) return TRUE; + /* Compose log structure */ - memset(&nlog, 0, sizeof(nlog)); - nlog.ehandler = sieve_validator_error_handler(valdtr); - nlog.prefix = "notify command"; + memset(&nenv, sizeof(nenv), 0); + nenv.method = method; /* Check URI itself */ - if ( method->compile_check_uri != NULL ) { + if ( result && method->def->compile_check_uri != NULL ) { /* Set log location to location of URI argument */ - nlog.location = sieve_error_script_location - (sieve_validator_script(valdtr), uri_arg->source_line); + nenv.ehandler = sieve_prefix_ehandler_create + (sieve_validator_error_handler(valdtr), + sieve_error_script_location + (sieve_validator_script(valdtr), uri_arg->source_line), + "notify command"); /* Execute method check function */ - if ( !method->compile_check_uri - (&nlog, sieve_ast_argument_strc(uri_arg), uri) ) - return FALSE; + result = method->def->compile_check_uri + (&nenv, sieve_ast_argument_strc(uri_arg), uri); } /* Check :message argument */ - if ( msg_arg != NULL && sieve_argument_is_string_literal(msg_arg) && - method->compile_check_message != NULL ) { + if ( result && msg_arg != NULL && sieve_argument_is_string_literal(msg_arg) + && method->def->compile_check_message != NULL ) { /* Set log location to location of :message argument */ - nlog.location = sieve_error_script_location - (sieve_validator_script(valdtr), msg_arg->source_line); + sieve_error_handler_unref(&nenv.ehandler); + nenv.ehandler = sieve_prefix_ehandler_create + (sieve_validator_error_handler(valdtr), + sieve_error_script_location + (sieve_validator_script(valdtr), msg_arg->source_line), + "notify command"); /* Execute method check function */ - if ( !method->compile_check_message - (&nlog, sieve_ast_argument_str(msg_arg)) ) - return FALSE; + result = method->def->compile_check_message + (&nenv, sieve_ast_argument_str(msg_arg)); } /* Check :from argument */ - if ( from_arg != NULL && sieve_argument_is_string_literal(from_arg) && - method->compile_check_from != NULL ) { + if ( result && from_arg != NULL && sieve_argument_is_string_literal(from_arg) + && method->def->compile_check_from != NULL ) { /* Set log location to location of :from argument */ - nlog.location = sieve_error_script_location - (sieve_validator_script(valdtr), from_arg->source_line); + sieve_error_handler_unref(&nenv.ehandler); + nenv.ehandler = sieve_prefix_ehandler_create + (sieve_validator_error_handler(valdtr), + sieve_error_script_location + (sieve_validator_script(valdtr), from_arg->source_line), + "notify command"); /* Execute method check function */ - if ( !method->compile_check_from(&nlog, sieve_ast_argument_str(from_arg)) ) - return FALSE; + result = method->def->compile_check_from + (&nenv, sieve_ast_argument_str(from_arg)); } + + sieve_error_handler_unref(&nenv.ehandler); /* Check :options argument */ - if ( options_arg != NULL ) { + if ( result && options_arg != NULL ) { struct sieve_ast_argument *option = options_arg; struct _ext_enotify_option_check_context optn_context = { valdtr, method }; /* Parse and check options */ - if ( sieve_ast_stringlist_map - (&option, (void *) &optn_context, _ext_enotify_option_check) <= 0 ) - return FALSE; + result = ( sieve_ast_stringlist_map + (&option, (void *) &optn_context, _ext_enotify_option_check) > 0 ); /* Discard argument if options are not accepted by method */ - if ( method->compile_check_option == NULL ) { + if ( result && method->def->compile_check_option == NULL ) { sieve_argument_validate_warning(valdtr, options_arg, "notify command: method '%s' accepts no options", scheme); (void)sieve_ast_arguments_detach(options_arg,1); } } - return TRUE; + return result; } /* @@ -406,6 +454,7 @@ bool ext_enotify_runtime_method_validate const struct sieve_enotify_method *method; const char *uri = str_c(method_uri); const char *scheme; + bool result = TRUE; /* Get the method */ @@ -417,20 +466,23 @@ bool ext_enotify_runtime_method_validate /* Validate the provided URI */ - if ( method->runtime_check_uri != NULL ) { - struct sieve_enotify_log nlog; + if ( method->def != NULL && method->def->runtime_check_uri != NULL ) { + struct sieve_enotify_env nenv; - memset(&nlog, 0, sizeof(nlog)); - nlog.location = sieve_error_script_location(renv->script, source_line); - nlog.ehandler = sieve_interpreter_get_error_handler(renv->interp); - nlog.prefix = "valid_notify_method test"; + memset(&nenv, 0, sizeof(nenv)); + nenv.method = method; + nenv.ehandler = sieve_prefix_ehandler_create + (sieve_interpreter_get_error_handler(renv->interp), + sieve_error_script_location(renv->script, source_line), + "valid_notify_method test"); /* Use the method check function to validate the URI */ - return method->runtime_check_uri(&nlog, str_c(method_uri), uri); + result = method->def->runtime_check_uri(&nenv, str_c(method_uri), uri); + + sieve_error_handler_unref(&nenv.ehandler); } - /* Method has no check function */ - return TRUE; + return result; } static const struct sieve_enotify_method *ext_enotify_get_method @@ -471,28 +523,33 @@ const char *ext_enotify_runtime_get_method_capability string_t *method_uri, const char *capability) { const struct sieve_enotify_method *method; - const char *uri; + const char *uri_body; + const char *result = NULL; /* Get method */ - method = ext_enotify_get_method(renv, source_line, method_uri, &uri); + method = ext_enotify_get_method(renv, source_line, method_uri, &uri_body); if ( method == NULL ) return NULL; /* Get requested capability */ - if ( method->runtime_get_method_capability != NULL ) { - struct sieve_enotify_log nlog; + if ( method->def != NULL && + method->def->runtime_get_method_capability != NULL ) { + struct sieve_enotify_env nenv; + + memset(&nenv, 0, sizeof(nenv)); + nenv.method = method; + nenv.ehandler = sieve_prefix_ehandler_create + (sieve_interpreter_get_error_handler(renv->interp), + sieve_error_script_location(renv->script, source_line), + "notify_method_capability test"); - /* Compose log structure */ - memset(&nlog, 0, sizeof(nlog)); - nlog.location = sieve_error_script_location(renv->script, source_line); - nlog.ehandler = sieve_interpreter_get_error_handler(renv->interp); - nlog.prefix = "notify_method_capability test"; - /* Execute method function to acquire capability value */ - return method->runtime_get_method_capability - (&nlog, str_c(method_uri), uri, capability); + result = method->def->runtime_get_method_capability + (&nenv, str_c(method_uri), uri_body, capability); + + sieve_error_handler_unref(&nenv.ehandler); } - return NULL; + return result; } int ext_enotify_runtime_check_operands @@ -502,25 +559,28 @@ int ext_enotify_runtime_check_operands const struct sieve_enotify_method **method_r, void **method_context) { const struct sieve_enotify_method *method; - const char *uri; + const char *uri_body; /* Get method */ - method = ext_enotify_get_method(renv, source_line, method_uri, &uri); + method = ext_enotify_get_method(renv, source_line, method_uri, &uri_body); if ( method == NULL ) return SIEVE_EXEC_FAILURE; /* Check provided operands */ - if ( method->runtime_check_operands != NULL ) { - struct sieve_enotify_log nlog; - - /* Compose log structure */ - memset(&nlog, 0, sizeof(nlog)); - nlog.location = sieve_error_script_location(renv->script, source_line); - nlog.ehandler = sieve_interpreter_get_error_handler(renv->interp); - nlog.prefix = "notify action"; + if ( method->def != NULL && method->def->runtime_check_operands != NULL ) { + struct sieve_enotify_env nenv; + int ret = SIEVE_EXEC_OK; + + memset(&nenv, 0, sizeof(nenv)); + nenv.method = method; + nenv.ehandler = sieve_prefix_ehandler_create + (sieve_interpreter_get_error_handler(renv->interp), + sieve_error_script_location(renv->script, source_line), + "notify action"); /* Execute check function */ - if ( method->runtime_check_operands(&nlog, str_c(method_uri), uri, message, - from, sieve_result_pool(renv->result), method_context) ) { + if ( method->def->runtime_check_operands + (&nenv, str_c(method_uri), uri_body, message, from, + sieve_result_pool(renv->result), method_context) ) { /* Check any provided options */ if ( options != NULL ) { @@ -535,12 +595,12 @@ int ext_enotify_runtime_check_operands /* Parse option into <optionname> and <value> */ if ( ext_enotify_option_parse - (&nlog, str_c(option), FALSE, &opt_name, &opt_value) ) { + (&nenv, str_c(option), FALSE, &opt_name, &opt_value) ) { /* Set option */ - if ( method->runtime_set_option != NULL ) { - (void) method->runtime_set_option - (&nlog, *method_context, opt_name, opt_value); + if ( method->def->runtime_set_option != NULL ) { + (void) method->def->runtime_set_option + (&nenv, *method_context, opt_name, opt_value); } } } @@ -549,21 +609,25 @@ int ext_enotify_runtime_check_operands */ if ( result ) { *method_r = method; - return SIEVE_EXEC_OK; + } else { + /* Binary corrupt */ + sieve_runtime_trace_error + (renv, "invalid item in options string list"); + ret = SIEVE_EXEC_BIN_CORRUPT; } - - /* Binary corrupt */ - sieve_runtime_trace_error(renv, "invalid item in options string list"); - return SIEVE_EXEC_BIN_CORRUPT; + + } else { + /* No options */ + *method_r = method; } - /* No options */ - *method_r = method; - return SIEVE_EXEC_OK; + } else { + /* Operand check failed */ + ret = SIEVE_EXEC_FAILURE; } - - /* Check failed */ - return SIEVE_EXEC_FAILURE; + + sieve_error_handler_unref(&nenv.ehandler); + return ret; } /* No check function defined: a most unlikely situation */ @@ -586,59 +650,3 @@ void sieve_enotify_method_printf va_end(args); } -/* - * Method logging - */ - -static void sieve_enotify_vlog_message -(const struct sieve_enotify_log *nlog, sieve_error_func_t log_func, - const char *fmt, va_list args) -{ - if ( nlog == NULL ) return; - - T_BEGIN { - if ( nlog->aenv != NULL ) { - if ( nlog->prefix == NULL ) - sieve_result_vlog_message(nlog->aenv, log_func, fmt, args); - else - sieve_result_log_message(nlog->aenv, log_func, "%s: %s", nlog->prefix, - t_strdup_vprintf(fmt, args)); - } else { - if ( nlog->prefix == NULL ) - log_func(nlog->ehandler, nlog->location, "%s", - t_strdup_vprintf(fmt, args)); - else - log_func(nlog->ehandler, nlog->location, "%s: %s", nlog->prefix, - t_strdup_vprintf(fmt, args)); - } - } T_END; -} - -void sieve_enotify_error -(const struct sieve_enotify_log *nlog, const char *fmt, ...) -{ - va_list args; - - va_start(args, fmt); - sieve_enotify_vlog_message(nlog, sieve_error, fmt, args); - va_end(args); -} - -void sieve_enotify_warning -(const struct sieve_enotify_log *nlog, const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - sieve_enotify_vlog_message(nlog, sieve_warning, fmt, args); - va_end(args); -} - -void sieve_enotify_log -(const struct sieve_enotify_log *nlog, const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - sieve_enotify_vlog_message(nlog, sieve_info, fmt, args); - va_end(args); -} - diff --git a/src/lib-sieve/plugins/enotify/ext-enotify-common.h b/src/lib-sieve/plugins/enotify/ext-enotify-common.h index 01b52fe6bbdf5401af01fe099eb3cfa646e4af7f..97ab61746caff6f89a5d3d146c0f866cb00cfc16 100644 --- a/src/lib-sieve/plugins/enotify/ext-enotify-common.h +++ b/src/lib-sieve/plugins/enotify/ext-enotify-common.h @@ -22,7 +22,7 @@ extern const struct sieve_extension_capabilities notify_capabilities; struct ext_enotify_context { const struct sieve_extension *var_ext; - ARRAY_DEFINE(notify_methods, const struct sieve_enotify_method *); + ARRAY_DEFINE(notify_methods, struct sieve_enotify_method); }; @@ -78,11 +78,11 @@ extern const struct sieve_variables_modifier_def encodeurl_modifier; /* * Notify methods */ - -extern const struct sieve_enotify_method mailto_notify; - -void ext_enotify_methods_init(struct ext_enotify_context *ectx); -void ext_enotify_methods_deinit(struct ext_enotify_context *ectx); + +void ext_enotify_methods_init + (struct sieve_instance *svinst, struct ext_enotify_context *ectx); +void ext_enotify_methods_deinit + (struct ext_enotify_context *ectx); const struct sieve_enotify_method *ext_enotify_method_find (const struct sieve_extension *ntfy_ext, const char *identifier); @@ -123,17 +123,4 @@ struct sieve_enotify_print_env { const struct sieve_result_print_env *result_penv; }; -/* - * Method logging - */ - -struct sieve_enotify_log { - struct sieve_error_handler *ehandler; - const char *location; - - const struct sieve_action_exec_env *aenv; - - const char *prefix; -}; - #endif /* __EXT_ENOTIFY_COMMON_H */ diff --git a/src/lib-sieve/plugins/enotify/ext-enotify.c b/src/lib-sieve/plugins/enotify/ext-enotify.c index 4d5f44348670053b17a9e1e7b13d43aa122b5d6b..3507d593952bd7eebe70b76c66f097e985c260b7 100644 --- a/src/lib-sieve/plugins/enotify/ext-enotify.c +++ b/src/lib-sieve/plugins/enotify/ext-enotify.c @@ -69,7 +69,7 @@ static bool ext_enotify_load(const struct sieve_extension *ext, void **context) ectx->var_ext = sieve_ext_variables_get_extension(ext->svinst); *context = (void *) ectx; - ext_enotify_methods_init(ectx); + ext_enotify_methods_init(ext->svinst, ectx); sieve_extension_capabilities_register(ext->svinst, ext, ¬ify_capabilities); diff --git a/src/lib-sieve/plugins/enotify/mailto/Makefile.am b/src/lib-sieve/plugins/enotify/mailto/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..36a7ad005cb00700f9d85d4a50c9c65026a9e211 --- /dev/null +++ b/src/lib-sieve/plugins/enotify/mailto/Makefile.am @@ -0,0 +1,18 @@ +noinst_LTLIBRARIES = libsieve_ext_enotify_mailto.la + +AM_CPPFLAGS = \ + -I../ \ + -I../../../ \ + -I$(dovecot_incdir) \ + -I$(dovecot_incdir)/src/lib \ + -I$(dovecot_incdir)/src/lib-mail \ + -I$(dovecot_incdir)/src/lib-storage + +libsieve_ext_enotify_mailto_la_SOURCES = \ + uri-mailto.c \ + ntfy-mailto.c + +noinst_HEADERS = \ + uri-mailto.h + + diff --git a/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c b/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c new file mode 100644 index 0000000000000000000000000000000000000000..0352add26eb0ea15366191f97a7d134dbb9f2a0a --- /dev/null +++ b/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c @@ -0,0 +1,585 @@ +/* Copyright (c) 2002-2010 Dovecot Sieve authors, see the included COPYING file + */ + +/* Notify method mailto + * -------------------- + * + * Authors: Stephan Bosch + * Specification: RFC 5436 + * Implementation: full + * Status: testing + * + */ + +/* FIXME: URI syntax conforms to something somewhere in between RFC 2368 and + * draft-duerst-mailto-bis-05.txt. Should fully migrate to new specification + * when it matures. This requires modifications to the address parser (no + * whitespace allowed within the address itself) and UTF-8 support will be + * required in the URL. + */ + +#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 "sieve-common.h" +#include "sieve-address.h" +#include "sieve-message.h" +#include "sieve-smtp.h" + +#include "sieve-ext-enotify.h" + +#include "rfc2822.h" + +#include "uri-mailto.h" + +/* + * Configuration + */ + +#define NTFY_MAILTO_MAX_RECIPIENTS 8 +#define NTFY_MAILTO_MAX_HEADERS 16 +#define NTFY_MAILTO_MAX_SUBJECT 256 + +/* + * Mailto notification method + */ + +static bool ntfy_mailto_compile_check_uri + (const struct sieve_enotify_env *nenv, const char *uri, const char *uri_body); +static bool ntfy_mailto_compile_check_from + (const struct sieve_enotify_env *nenv, string_t *from); + +static const char *ntfy_mailto_runtime_get_notify_capability + (const struct sieve_enotify_env *nenv, const char *uri, const char *uri_body, + const char *capability); +static bool ntfy_mailto_runtime_check_uri + (const struct sieve_enotify_env *nenv, const char *uri, const char *uri_body); +static bool ntfy_mailto_runtime_check_operands + (const struct sieve_enotify_env *nenv, const char *uri,const char *uri_body, + string_t *message, string_t *from, pool_t context_pool, + void **method_context); + +static int ntfy_mailto_action_check_duplicates + (const struct sieve_enotify_env *nenv, void *method_ctx1, void *method_ctx2, + const char *dupl_location); + +static void ntfy_mailto_action_print + (const struct sieve_enotify_print_env *penv, + const struct sieve_enotify_action *act); + +static bool ntfy_mailto_action_execute + (const struct sieve_enotify_exec_env *nenv, + const struct sieve_enotify_action *act); + +const struct sieve_enotify_method_def mailto_notify = { + "mailto", + NULL, NULL, + ntfy_mailto_compile_check_uri, + NULL, + ntfy_mailto_compile_check_from, + NULL, + ntfy_mailto_runtime_check_uri, + ntfy_mailto_runtime_get_notify_capability, + ntfy_mailto_runtime_check_operands, + NULL, + ntfy_mailto_action_check_duplicates, + ntfy_mailto_action_print, + ntfy_mailto_action_execute +}; + +/* + * Reserved and unique headers + */ + +static const char *_reserved_headers[] = { + "auto-submitted", + "received", + "message-id", + "data", + "bcc", + "in-reply-to", + "references", + "resent-date", + "resent-from", + "resent-sender", + "resent-to", + "resent-cc", + "resent-bcc", + "resent-msg-id", + "from", + "sender", + NULL +}; + +static const char *_unique_headers[] = { + "reply-to", + NULL +}; + +/* + * Method context data + */ + +struct ntfy_mailto_context { + struct uri_mailto *uri; + const char *from_normalized; +}; + +/* + * Validation + */ + +static bool ntfy_mailto_compile_check_uri +(const struct sieve_enotify_env *nenv, const char *uri ATTR_UNUSED, + const char *uri_body) +{ + return uri_mailto_validate + (uri_body, _reserved_headers, _unique_headers, + NTFY_MAILTO_MAX_RECIPIENTS, NTFY_MAILTO_MAX_HEADERS, nenv->ehandler); +} + +static bool ntfy_mailto_compile_check_from +(const struct sieve_enotify_env *nenv, string_t *from) +{ + const char *error; + bool result = FALSE; + + T_BEGIN { + result = sieve_address_validate(from, &error); + + if ( !result ) { + sieve_enotify_error(nenv, + "specified :from address '%s' is invalid for " + "the mailto method: %s", + str_sanitize(str_c(from), 128), error); + } + } T_END; + + return result; +} + +/* + * Runtime + */ + +static const char *ntfy_mailto_runtime_get_notify_capability +(const struct sieve_enotify_env *nenv ATTR_UNUSED, const char *uri ATTR_UNUSED, + const char *uri_body, const char *capability) +{ + if ( !uri_mailto_validate(uri_body, _reserved_headers, _unique_headers, + NTFY_MAILTO_MAX_RECIPIENTS, NTFY_MAILTO_MAX_HEADERS, NULL) ) { + return NULL; + } + + if ( strcasecmp(capability, "online") == 0 ) + return "maybe"; + + return NULL; +} + +static bool ntfy_mailto_runtime_check_uri +(const struct sieve_enotify_env *nenv ATTR_UNUSED, const char *uri ATTR_UNUSED, + const char *uri_body) +{ + return uri_mailto_validate + (uri_body, _reserved_headers, _unique_headers, + NTFY_MAILTO_MAX_RECIPIENTS, NTFY_MAILTO_MAX_HEADERS, NULL); +} + +static bool ntfy_mailto_runtime_check_operands +(const struct sieve_enotify_env *nenv, const char *uri ATTR_UNUSED, + const char *uri_body, string_t *message ATTR_UNUSED, string_t *from, + pool_t context_pool, void **method_context) +{ + struct ntfy_mailto_context *mtctx; + struct uri_mailto *parsed_uri; + const char *error, *normalized; + + /* Need to create context before validation to have arrays present */ + mtctx = p_new(context_pool, struct ntfy_mailto_context, 1); + + /* Validate :from */ + if ( from != NULL ) { + T_BEGIN { + normalized = sieve_address_normalize(from, &error); + + if ( normalized == NULL ) { + sieve_enotify_error(nenv, + "specified :from address '%s' is invalid for " + "the mailto method: %s", + str_sanitize(str_c(from), 128), error); + } else + mtctx->from_normalized = p_strdup(context_pool, normalized); + } T_END; + + if ( !normalized ) return FALSE; + } + + if ( (parsed_uri=uri_mailto_parse + (uri_body, context_pool, _reserved_headers, + _unique_headers, NTFY_MAILTO_MAX_RECIPIENTS, NTFY_MAILTO_MAX_HEADERS, + nenv->ehandler)) == NULL ) { + return FALSE; + } + + mtctx->uri = parsed_uri; + *method_context = (void *) mtctx; + return TRUE; +} + +/* + * Action duplicates + */ + +static int ntfy_mailto_action_check_duplicates +(const struct sieve_enotify_env *nenv ATTR_UNUSED, + void *method_ctx1, void *method_ctx2, + const char *dupl_location ATTR_UNUSED) +{ + struct ntfy_mailto_context *mt_new = + (struct ntfy_mailto_context *) method_ctx1; + struct ntfy_mailto_context *mt_old = + (struct ntfy_mailto_context *) method_ctx2; + const struct uri_mailto_recipient *new_rcpts, *old_rcpts; + unsigned int new_count, old_count, i, j; + unsigned int del_start = 0, del_len = 0; + + new_rcpts = array_get(&mt_new->uri->recipients, &new_count); + old_rcpts = array_get(&mt_old->uri->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(&mt_new->uri->recipients, del_start, del_len); + + /* Make sure the loop integrity is maintained */ + i -= del_len; + new_rcpts = array_get(&mt_new->uri->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(&mt_new->uri->recipients, del_start, del_len); + } + + return ( array_count(&mt_new->uri->recipients) > 0 ? 0 : 1 ); +} + +/* + * Action printing + */ + +static void ntfy_mailto_action_print +(const struct sieve_enotify_print_env *penv, + const struct sieve_enotify_action *act) +{ + unsigned int count, i; + const struct uri_mailto_recipient *recipients; + const struct uri_mailto_header_field *headers; + struct ntfy_mailto_context *mtctx = + (struct ntfy_mailto_context *) act->method_context; + + /* Print main method parameters */ + + sieve_enotify_method_printf + (penv, " => importance : %d\n", act->importance); + + if ( act->message != NULL ) + sieve_enotify_method_printf + (penv, " => subject : %s\n", act->message); + else if ( mtctx->uri->subject != NULL ) + sieve_enotify_method_printf + (penv, " => subject : %s\n", mtctx->uri->subject); + + if ( act->from != NULL ) + sieve_enotify_method_printf + (penv, " => from : %s\n", act->from); + + /* Print mailto: recipients */ + + sieve_enotify_method_printf(penv, " => recipients :\n" ); + + recipients = array_get(&mtctx->uri->recipients, &count); + if ( count == 0 ) { + sieve_enotify_method_printf(penv, " NONE, action has no effect\n"); + } else { + for ( i = 0; i < count; i++ ) { + if ( recipients[i].carbon_copy ) + sieve_enotify_method_printf + (penv, " + Cc: %s\n", recipients[i].full); + else + sieve_enotify_method_printf + (penv, " + To: %s\n", recipients[i].full); + } + } + + /* Print accepted headers for notification message */ + + headers = array_get(&mtctx->uri->headers, &count); + if ( count > 0 ) { + sieve_enotify_method_printf(penv, " => headers :\n" ); + for ( i = 0; i < count; i++ ) { + sieve_enotify_method_printf(penv, " + %s: %s\n", + headers[i].name, headers[i].body); + } + } + + /* Print body for notification message */ + + if ( mtctx->uri->body != NULL ) + sieve_enotify_method_printf + (penv, " => body : \n--\n%s\n--\n", mtctx->uri->body); + + /* Finish output with an empty line */ + + sieve_enotify_method_printf(penv, "\n"); +} + +/* + * Action 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 ntfy_mailto_send +(const struct sieve_enotify_exec_env *nenv, + const struct sieve_enotify_action *act, const char *recipient) +{ + const struct sieve_message_data *msgdata = nenv->msgdata; + const struct sieve_script_env *senv = nenv->scriptenv; + struct ntfy_mailto_context *mtctx = + (struct ntfy_mailto_context *) act->method_context; + const char *from = NULL, *from_smtp = NULL; + const char *subject = mtctx->uri->subject; + const char *body = mtctx->uri->body; + string_t *to, *cc; + const struct uri_mailto_recipient *recipients; + void *smtp_handle; + unsigned int count, i; + FILE *f; + const char *outmsgid; + + /* Get recipients */ + recipients = array_get(&mtctx->uri->recipients, &count); + if ( count == 0 ) { + sieve_enotify_warning(nenv, + "notify mailto uri specifies no recipients; action has no effect"); + return TRUE; + } + + /* Just to be sure */ + if ( !sieve_smtp_available(senv) ) { + sieve_enotify_warning(nenv, + "notify mailto method has no means to send mail"); + return TRUE; + } + + /* Determine message from address */ + if ( act->from == NULL ) { + from = t_strdup_printf("Postmaster <%s>", senv->postmaster_address); + } else { + from = act->from; + } + + /* Determine SMTP from address */ + if ( sieve_message_get_sender(nenv->msgctx) != NULL ) { + if ( mtctx->from_normalized == NULL ) { + from_smtp = senv->postmaster_address; + } else { + from_smtp = mtctx->from_normalized; + } + } + + /* Determine subject */ + if ( act->message != NULL ) { + /* FIXME: handle UTF-8 */ + subject = str_sanitize(act->message, NTFY_MAILTO_MAX_SUBJECT); + } else if ( subject == NULL ) { + const char *const *hsubject; + + /* Fetch subject from original message */ + if ( mail_get_headers_utf8 + (msgdata->mail, "subject", &hsubject) >= 0 ) + subject = str_sanitize(t_strdup_printf("Notification: %s", hsubject[0]), + NTFY_MAILTO_MAX_SUBJECT); + else + subject = "Notification: (no subject)"; + } + + /* Compose To and Cc headers */ + to = NULL; + cc = NULL; + for ( i = 0; i < count; i++ ) { + if ( recipients[i].carbon_copy ) { + if ( cc == NULL ) { + cc = t_str_new(256); + str_append(cc, recipients[i].full); + } else { + str_append(cc, ", "); + str_append(cc, recipients[i].full); + } + } else { + if ( to == NULL ) { + to = t_str_new(256); + str_append(to, recipients[i].full); + } else { + str_append(to, ", "); + str_append(to, recipients[i].full); + } + } + } + + /* Send message to all recipients */ + for ( i = 0; i < count; i++ ) { + const struct uri_mailto_header_field *headers; + unsigned int h, hcount; + + smtp_handle = sieve_smtp_open + (senv, recipients[i].normalized, from_smtp, &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)); + rfc2822_header_field_utf8_printf(f, "Subject", "%s", subject); + + rfc2822_header_field_utf8_printf(f, "From", "%s", from); + + if ( to != NULL ) + rfc2822_header_field_utf8_printf(f, "To", "%s", str_c(to)); + + if ( cc != NULL ) + rfc2822_header_field_utf8_printf(f, "Cc", "%s", str_c(cc)); + + rfc2822_header_field_printf(f, "Auto-Submitted", + "auto-notified; owner-email=\"%s\"", recipient); + rfc2822_header_field_write(f, "Precedence", "bulk"); + + /* 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; + } + + /* Add custom headers */ + + headers = array_get(&mtctx->uri->headers, &hcount); + for ( h = 0; h < hcount; h++ ) { + const char *name = rfc2822_header_field_name_sanitize(headers[h].name); + + rfc2822_header_field_write(f, name, headers[h].body); + } + + /* Generate message body */ + if ( body != NULL ) { + if (_contains_8bit(body)) { + 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"); + } + + fprintf(f, "\r\n"); + fprintf(f, "%s\r\n", body); + + } else { + fprintf(f, "\r\n"); + fprintf(f, "Notification of new message.\r\n"); + } + + if ( sieve_smtp_close(senv, smtp_handle) ) { + sieve_enotify_info(nenv, + "sent mail notification to <%s>", + str_sanitize(recipients[i].normalized, 80)); + } else { + sieve_enotify_error(nenv, + "failed to send mail notification to <%s> " + "(refer to system log for more information)", + str_sanitize(recipients[i].normalized, 80)); + } + } + + return TRUE; +} + +static bool ntfy_mailto_action_execute +(const struct sieve_enotify_exec_env *nenv, + const struct sieve_enotify_action *act) +{ + const char *const *headers; + const char *sender = sieve_message_get_sender(nenv->msgctx); + const char *recipient = sieve_message_get_recipient(nenv->msgctx); + + /* Is the recipient unset? + */ + if ( recipient == NULL ) { + sieve_enotify_warning(nenv, + "notify mailto action aborted: envelope recipient is <>"); + return TRUE; + } + + /* Is the message an automatic reply ? */ + if ( mail_get_headers + (nenv->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_enotify_info(nenv, + "not sending notification for auto-submitted message from <%s>", + str_sanitize(sender, 128)); + return TRUE; + } + hdsp++; + } + } + + return ntfy_mailto_send(nenv, act, recipient); +} + + + + diff --git a/src/lib-sieve/plugins/enotify/mailto/uri-mailto.c b/src/lib-sieve/plugins/enotify/mailto/uri-mailto.c new file mode 100644 index 0000000000000000000000000000000000000000..a7115a22c0c1189d1d9d2c04a3d160da98351e1d --- /dev/null +++ b/src/lib-sieve/plugins/enotify/mailto/uri-mailto.c @@ -0,0 +1,612 @@ +/* Copyright (c) 2002-2010 Dovecot Sieve authors, see the included COPYING file + */ + +/* FIXME: URI syntax conforms to something somewhere in between RFC 2368 and + * draft-duerst-mailto-bis-05.txt. Should fully migrate to new specification + * when it matures. This requires modifications to the address parser (no + * whitespace allowed within the address itself) and UTF-8 support will be + * required in the URL. + */ + +#include "lib.h" +#include "array.h" +#include "str.h" +#include "str-sanitize.h" + +#include "rfc2822.h" + +#include "sieve-common.h" +#include "sieve-error.h" +#include "sieve-address.h" +#include "sieve-message.h" + +#include "uri-mailto.h" + +/* Util macros */ + +#define uri_mailto_error(PARSER, ...) \ + sieve_error((PARSER)->ehandler, NULL, "invalid mailto URI: " __VA_ARGS__ ) + +#define uri_mailto_warning(PARSER, ...) \ + sieve_warning((PARSER)->ehandler, NULL, "mailto URI: " __VA_ARGS__ ) + +/* Parser object */ + +struct uri_mailto_parser { + pool_t pool; + struct sieve_error_handler *ehandler; + + struct uri_mailto *uri; + + const char **reserved_headers; + const char **unique_headers; + + int max_recipients; + int max_headers; +}; + +/* + * Reserved and unique headers + */ + +static inline bool uri_mailto_header_is_reserved +(struct uri_mailto_parser *parser, const char *field_name) +{ + const char **hdr = parser->reserved_headers; + + if ( hdr == NULL ) return FALSE; + + /* Check whether it is reserved */ + while ( *hdr != NULL ) { + if ( strcasecmp(field_name, *hdr) == 0 ) + return TRUE; + hdr++; + } + + return FALSE; +} + +static inline bool uri_mailto_header_is_unique +(struct uri_mailto_parser *parser, const char *field_name) +{ + const char **hdr = parser->unique_headers; + + if ( hdr == NULL ) return FALSE; + + /* Check whether it is supposed to be unique */ + while ( *hdr != NULL ) { + if ( strcasecmp(field_name, *hdr) == 0 ) + return TRUE; + hdr++; + } + + return FALSE; +} + +/* + * Low-level URI parsing. + * + * FIXME: much of this implementation will be common to other URI schemes. This + * should be merged into a common implementation. + */ + +static const char _qchar_lookup[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10 + 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 20 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, // 30 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 50 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, // 70 + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 90 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // F0 +}; + +static inline bool _is_qchar(unsigned char c) +{ + return _qchar_lookup[c]; +} + +static inline int _decode_hex_digit(char digit) +{ + switch ( digit ) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return (int) digit - '0'; + + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + return (int) digit - 'a' + 0x0a; + + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + return (int) digit - 'A' + 0x0A; + } + + return -1; +} + +static bool _parse_hex_value(const char **in, char *out) +{ + char value; + + if ( **in == '\0' || (value=_decode_hex_digit(**in)) < 0 ) + return FALSE; + + *out = value << 4; + (*in)++; + + if ( **in == '\0' || (value=_decode_hex_digit(**in)) < 0 ) + return FALSE; + + *out |= value; + (*in)++; + return (*out != '\0'); +} + +/* + * URI recipient parsing + */ + +static bool uri_mailto_add_valid_recipient +(struct uri_mailto_parser *parser, string_t *recipient, bool cc) +{ + struct uri_mailto *uri = parser->uri; + struct uri_mailto_recipient *new_recipient; + struct uri_mailto_recipient *rcpts; + unsigned int count, i; + const char *error; + const char *normalized; + + /* Verify recipient */ + if ( (normalized=sieve_address_normalize(recipient, &error)) == NULL ) { + uri_mailto_error(parser, "invalid recipient '%s': %s", + str_sanitize(str_c(recipient), 80), error); + return FALSE; + } + + /* Add recipient to the uri */ + if ( uri != NULL ) { + /* Get current recipients */ + rcpts = array_get_modifiable(&uri->recipients, &count); + + /* Enforce limits */ + if ( parser->max_recipients > 0 && (int)count >= parser->max_recipients ) { + if ( (int)count == parser->max_recipients) { + uri_mailto_warning(parser, + "more than the maximum %u recipients specified; " + "rest is discarded", parser->max_recipients); + } + return TRUE; + } + + /* Check for duplicate first */ + for ( i = 0; i < count; i++ ) { + if ( sieve_address_compare(rcpts[i].normalized, normalized, TRUE) == 0 ) + { + /* Upgrade existing Cc: recipient to a To: recipient if possible */ + rcpts[i].carbon_copy = ( rcpts[i].carbon_copy && cc ); + + uri_mailto_warning(parser, "ignored duplicate recipient '%s'", + str_sanitize(str_c(recipient), 80)); + return TRUE; + } + } + + /* Add */ + new_recipient = array_append_space(&uri->recipients); + new_recipient->carbon_copy = cc; + new_recipient->full = p_strdup(parser->pool, str_c(recipient)); + new_recipient->normalized = p_strdup(parser->pool, normalized); + } + + return TRUE; +} + +static bool uri_mailto_parse_recipients +(struct uri_mailto_parser *parser, const char **uri_p) +{ + string_t *to = t_str_new(128); + const char *p = *uri_p; + + if ( *p == '\0' || *p == '?' ) + return TRUE; + + while ( *p != '\0' && *p != '?' ) { + if ( *p == '%' ) { + /* % encoded character */ + char ch; + + p++; + + /* Parse 2-digit hex value */ + if ( !_parse_hex_value(&p, &ch) ) { + uri_mailto_error(parser, "invalid %% encoding"); + return FALSE; + } + + /* Check for delimiter */ + if ( ch == ',' ) { + /* Verify and add recipient */ + if ( !uri_mailto_add_valid_recipient(parser, to, FALSE) ) + return FALSE; + + /* Reset for next recipient */ + str_truncate(to, 0); + } else { + /* Content character */ + str_append_c(to, ch); + } + } else { + if ( *p == ':' || *p == ';' || *p == ',' || !_is_qchar(*p) ) { + uri_mailto_error + (parser, "invalid character '%c' in 'to' part", *p); + return FALSE; + } + + /* Content character */ + str_append_c(to, *p); + p++; + } + } + + /* Skip '?' */ + if ( *p != '\0' ) p++; + + /* Verify and add recipient */ + if ( !uri_mailto_add_valid_recipient(parser, to, FALSE) ) + return FALSE; + + *uri_p = p; + return TRUE; +} + +static bool uri_mailto_parse_header_recipients +(struct uri_mailto_parser *parser, string_t *rcpt_header, bool cc) +{ + string_t *to = t_str_new(128); + const char *p = (const char *) str_data(rcpt_header); + const char *pend = p + str_len(rcpt_header); + + while ( p < pend ) { + if ( *p == ',' ) { + /* Verify and add recipient */ + if ( !uri_mailto_add_valid_recipient(parser, to, cc) ) + return FALSE; + + /* Reset for next recipient */ + str_truncate(to, 0); + } else { + /* Content character */ + str_append_c(to, *p); + } + p++; + } + + /* Verify and add recipient */ + if ( !uri_mailto_add_valid_recipient(parser, to, cc) ) + return FALSE; + + return TRUE; +} + +/* URI header parsing */ + +static bool uri_mailto_header_is_duplicate +(struct uri_mailto_parser *parser, const char *field_name) +{ + struct uri_mailto *uri = parser->uri; + + if ( uri == NULL ) return FALSE; + + if ( uri_mailto_header_is_unique(parser, field_name) ) { + const struct uri_mailto_header_field *hdrs; + unsigned int count, i; + + hdrs = array_get(&uri->headers, &count); + for ( i = 0; i < count; i++ ) { + if ( strcasecmp(hdrs[i].name, field_name) == 0 ) + return TRUE; + } + } + + return FALSE; +} + +static bool uri_mailto_parse_headers +(struct uri_mailto_parser *parser, const char **uri_p) +{ + struct uri_mailto *uri = parser->uri; + unsigned int header_count = 0; + string_t *field = t_str_new(128); + const char *p = *uri_p; + + while ( *p != '\0' ) { + enum { + _HNAME_IGNORED, + _HNAME_GENERIC, + _HNAME_TO, + _HNAME_CC, + _HNAME_SUBJECT, + _HNAME_BODY + } hname_type = _HNAME_GENERIC; + struct uri_mailto_header_field *hdrf = NULL; + const char *field_name; + + /* Parse field name */ + while ( *p != '\0' && *p != '=' ) { + char ch = *p; + p++; + + if ( ch == '%' ) { + /* Encoded, parse 2-digit hex value */ + if ( !_parse_hex_value(&p, &ch) ) { + uri_mailto_error(parser, "invalid %% encoding"); + return FALSE; + } + } else if ( ch != '=' && !_is_qchar(ch) ) { + uri_mailto_error + (parser, "invalid character '%c' in header field name part", ch); + return FALSE; + } + + str_append_c(field, ch); + } + if ( *p != '\0' ) p++; + + /* Verify field name */ + if ( !rfc2822_header_field_name_verify(str_c(field), str_len(field)) ) { + uri_mailto_error(parser, "invalid header field name"); + return FALSE; + } + + if ( parser->max_headers > -1 && + (int)header_count >= parser->max_headers ) { + /* Refuse to accept more headers than allowed by policy */ + if ( (int)header_count == parser->max_headers ) { + uri_mailto_warning(parser, "more than the maximum %u headers specified; " + "rest is discarded", parser->max_headers); + } + + hname_type = _HNAME_IGNORED; + } else { + /* Add new header field to array and assign its name */ + + field_name = str_c(field); + if ( strcasecmp(field_name, "to") == 0 ) + hname_type = _HNAME_TO; + else if ( strcasecmp(field_name, "cc") == 0 ) + hname_type = _HNAME_CC; + else if ( strcasecmp(field_name, "subject") == 0 ) + hname_type = _HNAME_SUBJECT; + else if ( strcasecmp(field_name, "body") == 0 ) + hname_type = _HNAME_BODY; + else if ( !uri_mailto_header_is_reserved(parser, field_name) ) { + if ( uri != NULL ) { + if ( !uri_mailto_header_is_duplicate(parser, field_name) ) { + hdrf = array_append_space(&uri->headers); + hdrf->name = p_strdup(parser->pool, field_name); + } else { + uri_mailto_warning(parser, + "ignored duplicate for unique header field '%s'", + str_sanitize(field_name, 32)); + hname_type = _HNAME_IGNORED; + } + } else { + hname_type = _HNAME_IGNORED; + } + } else { + uri_mailto_warning(parser, "ignored reserved header field '%s'", + str_sanitize(field_name, 32)); + hname_type = _HNAME_IGNORED; + } + } + + header_count++; + + /* Reset for field body */ + str_truncate(field, 0); + + /* Parse field body */ + while ( *p != '\0' && *p != '&' ) { + char ch = *p; + p++; + + if ( ch == '%' ) { + /* Encoded, parse 2-digit hex value */ + if ( !_parse_hex_value(&p, &ch) ) { + uri_mailto_error(parser, "invalid %% encoding"); + return FALSE; + } + } else if ( ch != '=' && !_is_qchar(ch) ) { + uri_mailto_error + (parser, "invalid character '%c' in header field value part", ch); + return FALSE; + } + str_append_c(field, ch); + } + if ( *p != '\0' ) p++; + + /* Verify field body */ + if ( hname_type == _HNAME_BODY ) { + // FIXME: verify body ... + } else { + if ( !rfc2822_header_field_body_verify(str_c(field), str_len(field)) ) { + uri_mailto_error(parser, "invalid header field body"); + return FALSE; + } + } + + /* Assign field body */ + + switch ( hname_type ) { + case _HNAME_IGNORED: + break; + case _HNAME_TO: + /* Gracefully allow duplicate To fields */ + if ( !uri_mailto_parse_header_recipients(parser, field, FALSE) ) + return FALSE; + break; + case _HNAME_CC: + /* Gracefully allow duplicate Cc fields */ + if ( !uri_mailto_parse_header_recipients(parser, field, TRUE) ) + return FALSE; + break; + case _HNAME_SUBJECT: + /* Igore duplicate subject field */ + if ( uri != NULL ) { + if ( uri->subject == NULL ) + uri->subject = p_strdup(parser->pool, str_c(field)); + else + uri_mailto_warning(parser, "ignored duplicate subject field"); + } + break; + case _HNAME_BODY: + /* Igore duplicate body field */ + if ( uri != NULL ) { + if ( uri->body == NULL ) + uri->body = p_strdup(parser->pool, str_c(field)); + else + uri_mailto_warning(parser, "ignored duplicate body field"); + } + break; + case _HNAME_GENERIC: + if ( uri != NULL && hdrf != NULL ) + hdrf->body = p_strdup(parser->pool, str_c(field)); + break; + } + + /* Reset for next name */ + str_truncate(field, 0); + } + + /* Skip '&' */ + if ( *p != '\0' ) p++; + + *uri_p = p; + return TRUE; +} + +static bool uri_mailto_parse_uri +(struct uri_mailto_parser *parser, const char *uri_body) +{ + const char *p = uri_body; + + /* + * mailtoURI = "mailto:" [ to ] [ hfields ] + * to = [ addr-spec *("%2C" addr-spec ) ] + * hfields = "?" hfield *( "&" hfield ) + * hfield = hfname "=" hfvalue + * hfname = *qchar + * hfvalue = *qchar + * addr-spec = local-part "@" domain + * local-part = dot-atom / quoted-string + * qchar = unreserved / pct-encoded / some-delims + * some-delims = "!" / "$" / "'" / "(" / ")" / "*" + * / "+" / "," / ";" / ":" / "@" + * + * to ~= *tqchar + * tqchar ~= <qchar> without ";" and ":" + * + * Scheme 'mailto:' already parsed, starting parse after colon + */ + + /* First extract to-part by searching for '?' and decoding % items + */ + + if ( !uri_mailto_parse_recipients(parser, &p) ) + return FALSE; + + /* Extract hfield items */ + + while ( *p != '\0' ) { + /* Extract hfield item by searching for '&' and decoding '%' items */ + if ( !uri_mailto_parse_headers(parser, &p) ) + return FALSE; + } + + return TRUE; +} + +/* + * Validation + */ + +bool uri_mailto_validate +(const char *uri_body, const char **reserved_headers, + const char **unique_headers, int max_recipients, int max_headers, + struct sieve_error_handler *ehandler) +{ + struct uri_mailto_parser parser; + + memset(&parser, 0, sizeof(parser)); + parser.ehandler = ehandler; + parser.max_recipients = max_recipients; + parser.max_headers = max_headers; + + /* If no errors are reported, we don't need to record any data */ + if ( ehandler != NULL ) { + parser.pool = pool_datastack_create(); + parser.reserved_headers = reserved_headers; + parser.unique_headers = unique_headers; + + parser.uri = p_new(parser.pool, struct uri_mailto, 1); + p_array_init(&parser.uri->recipients, parser.pool, max_recipients); + p_array_init(&parser.uri->headers, parser.pool, max_headers); + } + + if ( !uri_mailto_parse_uri(&parser, uri_body) ) + return FALSE; + + if ( ehandler != NULL ) { + if ( array_count(&parser.uri->recipients) == 0 ) + uri_mailto_warning(&parser, "notification URI specifies no recipients"); + } + + return TRUE; +} + +/* + * Parsing + */ + +struct uri_mailto *uri_mailto_parse +(const char *uri_body, pool_t pool, const char **reserved_headers, + const char **unique_headers, int max_recipients, int max_headers, + struct sieve_error_handler *ehandler) +{ + struct uri_mailto_parser parser; + + /* If no errors are reported, we don't need to record any data */ + parser.pool = pool; + parser.ehandler = ehandler; + parser.max_recipients = max_recipients; + parser.max_headers = max_headers; + parser.reserved_headers = reserved_headers; + parser.unique_headers = unique_headers; + + parser.uri = p_new(pool, struct uri_mailto, 1); + p_array_init(&parser.uri->recipients, pool, max_recipients); + p_array_init(&parser.uri->headers, pool, max_headers); + + if ( !uri_mailto_parse_uri(&parser, uri_body) ) + return FALSE; + + if ( ehandler != NULL ) { + if ( array_count(&parser.uri->recipients) == 0 ) + uri_mailto_warning(&parser, "notification URI specifies no recipients"); + } + + return parser.uri; +} + + + + + diff --git a/src/lib-sieve/plugins/enotify/mailto/uri-mailto.h b/src/lib-sieve/plugins/enotify/mailto/uri-mailto.h new file mode 100644 index 0000000000000000000000000000000000000000..11264739a81740e99c506478b026e06517a99b95 --- /dev/null +++ b/src/lib-sieve/plugins/enotify/mailto/uri-mailto.h @@ -0,0 +1,45 @@ +/* Copyright (c) 2002-2010 Dovecot Sieve authors, see the included COPYING file + */ + +#ifndef __URI_MAILTO_H +#define __URI_MAILTO_H + +/* + * Types + */ + +struct uri_mailto_header_field { + const char *name; + const char *body; +}; + +struct uri_mailto_recipient { + const char *full; + const char *normalized; + bool carbon_copy; +}; + +ARRAY_DEFINE_TYPE(recipients, struct uri_mailto_recipient); +ARRAY_DEFINE_TYPE(headers, struct uri_mailto_header_field); + +struct uri_mailto { + ARRAY_TYPE(recipients) recipients; + ARRAY_TYPE(headers) headers; + const char *subject; + const char *body; + const char *from_normalized; +}; + +bool uri_mailto_validate + (const char *uri_body, const char **reserved_headers, + const char **unique_headers, int max_recipients, int max_headers, + struct sieve_error_handler *ehandler); + +struct uri_mailto *uri_mailto_parse +(const char *uri_body, pool_t pool, const char **reserved_headers, + const char **unique_headers, int max_recipients, int max_headers, + struct sieve_error_handler *ehandler); + +#endif /* __URI_MAILTO_H */ + + diff --git a/src/lib-sieve/plugins/enotify/sieve-ext-enotify.h b/src/lib-sieve/plugins/enotify/sieve-ext-enotify.h index 636df9671a9ca8c76faf40200bc6656a02918120..17cdb493f6de822f90762eaf3144023b4e22f084 100644 --- a/src/lib-sieve/plugins/enotify/sieve-ext-enotify.h +++ b/src/lib-sieve/plugins/enotify/sieve-ext-enotify.h @@ -15,64 +15,55 @@ * Forward declarations */ -struct sieve_enotify_log; -struct sieve_enotify_context; +struct sieve_enotify_method; +struct sieve_enotify_env; struct sieve_enotify_action; struct sieve_enotify_print_env; struct sieve_enotify_exec_env; /* - * Notify context - */ - -struct sieve_enotify_context { - struct sieve_error_handler *ehandler; - - /* Script location */ - const struct sieve_script *script; - unsigned int source_line; - - const struct sieve_message_data *msgdata; - pool_t pool; -}; - -/* - * Notify methods + * Notify method definition */ -struct sieve_enotify_method { +struct sieve_enotify_method_def { const char *identifier; + + /* Registration */ + bool (*load) + (const struct sieve_enotify_method *nmth, void **context); + void (*unload) + (const struct sieve_enotify_method *nmth); /* Validation */ bool (*compile_check_uri) - (const struct sieve_enotify_log *nlog, const char *uri, + (const struct sieve_enotify_env *nenv, const char *uri, const char *uri_body); bool (*compile_check_message) - (const struct sieve_enotify_log *nlog, string_t *message); + (const struct sieve_enotify_env *nenv, string_t *message); bool (*compile_check_from) - (const struct sieve_enotify_log *nlog, string_t *from); + (const struct sieve_enotify_env *nenv, string_t *from); bool (*compile_check_option) - (const struct sieve_enotify_log *nlog, const char *option, + (const struct sieve_enotify_env *nenv, const char *option, const char *value); /* Runtime */ bool (*runtime_check_uri) - (const struct sieve_enotify_log *nlog, const char *uri, + (const struct sieve_enotify_env *nenv, const char *uri, const char *uri_body); const char *(*runtime_get_method_capability) - (const struct sieve_enotify_log *nlog, const char *uri, + (const struct sieve_enotify_env *nenv, const char *uri, const char *uri_body, const char *capability); bool (*runtime_check_operands) - (const struct sieve_enotify_log *nlog, const char *uri, + (const struct sieve_enotify_env *nenv, const char *uri, const char *uri_body, string_t *message, string_t *from, pool_t context_pool, void **method_context); bool (*runtime_set_option) - (const struct sieve_enotify_log *nlog, void *method_context, + (const struct sieve_enotify_env *nenv, void *method_context, const char *option, const char *value); /* Action duplicates */ int (*action_check_duplicates) - (const struct sieve_enotify_log *nlog, void *method_ctx1, + (const struct sieve_enotify_env *nenv, void *method_ctx1, void *method_ctx2, const char *dupl_location); /* Action print */ @@ -86,8 +77,30 @@ struct sieve_enotify_method { const struct sieve_enotify_action *act); }; -void sieve_enotify_method_register -(struct sieve_instance *svinst, const struct sieve_enotify_method *method); +/* + * Notify method instance + */ + +struct sieve_enotify_method { + const struct sieve_enotify_method_def *def; + + struct sieve_instance *svinst; + void *context; +}; + +const struct sieve_enotify_method *sieve_enotify_method_register + (struct sieve_instance *svinst, + const struct sieve_enotify_method_def *nmth_def); + +/* + * Notify method environment + */ + +struct sieve_enotify_env { + const struct sieve_enotify_method *method; + + struct sieve_error_handler *ehandler; +}; /* * Notify method printing @@ -102,11 +115,13 @@ void sieve_enotify_method_printf */ struct sieve_enotify_exec_env { - const struct sieve_enotify_log *notify_log; + const struct sieve_enotify_method *method; const struct sieve_script_env *scriptenv; const struct sieve_message_data *msgdata; struct sieve_message_context *msgctx; + + struct sieve_error_handler *ehandler; }; /* @@ -123,18 +138,18 @@ struct sieve_enotify_action { }; /* - * Logging + * Error handling */ -void sieve_enotify_error - (const struct sieve_enotify_log *nlog, const char *fmt, ...) - ATTR_FORMAT(2, 3); -void sieve_enotify_warning - (const struct sieve_enotify_log *nlog, const char *fmt, ...) - ATTR_FORMAT(2, 3); -void sieve_enotify_log - (const struct sieve_enotify_log *nlog, const char *fmt, ...) - ATTR_FORMAT(2, 3); +#define sieve_enotify_error(ENV, ...) \ + sieve_error((ENV)->ehandler, NULL, __VA_ARGS__ ) + +#define sieve_enotify_warning(ENV, ...) \ + sieve_warning((ENV)->ehandler, NULL, __VA_ARGS__ ) + +#define sieve_enotify_info(ENV, ...) \ + sieve_info((ENV)->ehandler, NULL, __VA_ARGS__ ) + #endif /* __SIEVE_EXT_ENOTIFY_H */ diff --git a/src/lib-sieve/sieve-actions.h b/src/lib-sieve/sieve-actions.h index e8494fc6e43422a146e7ee0b34d7253c50158d8d..060e88ce163adb083ebce27d0b9d8f849bed0900 100644 --- a/src/lib-sieve/sieve-actions.h +++ b/src/lib-sieve/sieve-actions.h @@ -17,6 +17,8 @@ struct sieve_action_exec_env { struct sieve_result *result; + struct sieve_error_handler *ehandler; + const struct sieve_message_data *msgdata; struct sieve_message_context *msgctx; const struct sieve_script_env *scriptenv; diff --git a/src/lib-sieve/sieve-error-private.h b/src/lib-sieve/sieve-error-private.h index 110c247ecd7112f10036b967d48b80bc283dc807..fa0793b0ae51e19d3118d01686c2afd094672d93 100644 --- a/src/lib-sieve/sieve-error-private.h +++ b/src/lib-sieve/sieve-error-private.h @@ -28,18 +28,10 @@ struct sieve_error_handler { bool log_info; bool log_debug; - void (*verror) - (struct sieve_error_handler *ehandler, const char *location, - const char *fmt, va_list args); - void (*vwarning) - (struct sieve_error_handler *ehandler, const char *location, - const char *fmt, va_list args); - void (*vinfo) - (struct sieve_error_handler *ehandler, const char *location, - const char *fmt, va_list args); - void (*vdebug) - (struct sieve_error_handler *ehandler, const char *location, - const char *fmt, va_list args); + sieve_error_vfunc_t verror; + sieve_error_vfunc_t vwarning; + sieve_error_vfunc_t vinfo; + sieve_error_vfunc_t vdebug; void (*free) (struct sieve_error_handler *ehandler); @@ -48,4 +40,97 @@ struct sieve_error_handler { void sieve_error_handler_init (struct sieve_error_handler *ehandler, pool_t pool, unsigned int max_errors); +/* + * Direct handler calls + */ + +static inline void sieve_direct_verror +(struct sieve_error_handler *ehandler, const char *location, + const char *fmt, va_list args) +{ + if ( sieve_errors_more_allowed(ehandler) ) { + if ( ehandler->verror != NULL ) + ehandler->verror(ehandler, location, fmt, args); + + if ( ehandler->pool != NULL ) + ehandler->errors++; + } +} + +static inline void sieve_direct_vwarning +(struct sieve_error_handler *ehandler, const char *location, + const char *fmt, va_list args) +{ + if ( ehandler->vwarning != NULL ) + ehandler->vwarning(ehandler, location, fmt, args); + + if ( ehandler->pool != NULL ) + ehandler->warnings++; +} + +static inline void sieve_direct_vinfo +(struct sieve_error_handler *ehandler, const char *location, + const char *fmt, va_list args) +{ + if ( ehandler->log_info && ehandler->vinfo != NULL ) + ehandler->vinfo(ehandler, location, fmt, args); +} + +static inline void sieve_direct_vdebug +(struct sieve_error_handler *ehandler, const char *location, + const char *fmt, va_list args) +{ + if ( ehandler->log_info && ehandler->vdebug != NULL ) + ehandler->vdebug(ehandler, location, fmt, args); +} + +static inline void sieve_direct_error +(struct sieve_error_handler *ehandler, const char *location, + const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + + sieve_direct_verror(ehandler, location, fmt, args); + + va_end(args); +} + +static inline void sieve_direct_warning +(struct sieve_error_handler *ehandler, const char *location, + const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + + sieve_direct_vwarning(ehandler, location, fmt, args); + + va_end(args); +} + +static inline void sieve_direct_info +(struct sieve_error_handler *ehandler, const char *location, + const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + + sieve_direct_vinfo(ehandler, location, fmt, args); + + va_end(args); +} + +static inline void sieve_direct_debug +(struct sieve_error_handler *ehandler, const char *location, + const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + + sieve_direct_vdebug(ehandler, location, fmt, args); + + va_end(args); +} + + #endif /* __SIEVE_ERROR_PRIVATE_H */ diff --git a/src/lib-sieve/sieve-error.c b/src/lib-sieve/sieve-error.c index fd82038add8dd317a262603ac3e4b1c6c3a7c73c..08af504302dc2679dde00118b36d03aa31de90d2 100644 --- a/src/lib-sieve/sieve-error.c +++ b/src/lib-sieve/sieve-error.c @@ -3,7 +3,9 @@ #include "lib.h" #include "str.h" +#include "array.h" #include "ostream.h" +#include "var-expand.h" #include "eacces-error.h" #include "sieve-common.h" @@ -35,16 +37,17 @@ const char *sieve_error_script_location (const struct sieve_script *script, unsigned int source_line) { - const char *sname; + const char *sname; sname = ( script == NULL ? NULL : sieve_script_name(script) ); - if ( sname == NULL || *sname == '\0' ) - return t_strdup_printf("line %d", source_line); + if ( sname == NULL || *sname == '\0' ) + return t_strdup_printf("line %d", source_line); - return t_strdup_printf("%s: line %d", sname, source_line); + return t_strdup_printf("%s: line %d", sname, source_line); } + /* * Main error functions */ @@ -69,13 +72,7 @@ void sieve_verror if ( ehandler->log_master ) sieve_vcopy_master(location, sieve_verror, fmt, args); - if ( sieve_errors_more_allowed(ehandler) ) { - if ( ehandler->verror != NULL ) - ehandler->verror(ehandler, location, fmt, args); - - if ( ehandler->pool != NULL ) - ehandler->errors++; - } + sieve_direct_verror(ehandler, location, fmt, args); } void sieve_vwarning @@ -87,11 +84,7 @@ void sieve_vwarning if ( ehandler->log_master ) sieve_vcopy_master(location, sieve_vwarning, fmt, args); - if ( ehandler->vwarning != NULL ) - ehandler->vwarning(ehandler, location, fmt, args); - - if ( ehandler->pool != NULL ) - ehandler->warnings++; + sieve_direct_vwarning(ehandler, location, fmt, args); } void sieve_vinfo @@ -103,8 +96,7 @@ void sieve_vinfo if ( ehandler->log_master ) sieve_vcopy_master(location, sieve_vinfo, fmt, args); - if ( ehandler->log_info && ehandler->vinfo != NULL ) - ehandler->vinfo(ehandler, location, fmt, args); + sieve_direct_vinfo(ehandler, location, fmt, args); } void sieve_vdebug @@ -116,8 +108,7 @@ void sieve_vdebug if ( ehandler->log_master ) sieve_vcopy_master(location, sieve_vdebug, fmt, args); - if ( ehandler->log_debug && ehandler->vdebug != NULL ) - ehandler->vdebug(ehandler, location, fmt, args); + sieve_direct_vdebug(ehandler, location, fmt, args); } void sieve_vcritical @@ -792,3 +783,237 @@ struct sieve_error_handler *sieve_logfile_ehandler_create return &(ehandler->handler); } +/* + * Prefix error handler + * + * Encapsulates an existing error handler and prefixes all messages with + * the given prefix. + */ + +struct sieve_prefix_ehandler { + struct sieve_error_handler handler; + + struct sieve_error_handler *parent; + + const char *location; + const char *prefix; +}; + +static const char *_prefix_message +(struct sieve_prefix_ehandler *ehandler, + const char *location, const char *fmt, va_list args) +{ + string_t *str = t_str_new(256); + + if ( ehandler->prefix != NULL) + str_printfa(str, "%s: ", ehandler->prefix); + if ( location != NULL) + str_printfa(str, "%s: ", location); + str_vprintfa(str, fmt, args); + + return str_c(str); +} + +static void sieve_prefix_verror +(struct sieve_error_handler *_ehandler, const char *location, + const char *fmt, va_list args) +{ + struct sieve_prefix_ehandler *ehandler = + (struct sieve_prefix_ehandler *) _ehandler; + + if ( ehandler->parent == NULL ) return; + + sieve_direct_error(ehandler->parent, ehandler->location, "%s", + _prefix_message(ehandler, location, fmt, args)); +} + +static void sieve_prefix_vwarning +(struct sieve_error_handler *_ehandler, const char *location, + const char *fmt, va_list args) +{ + struct sieve_prefix_ehandler *ehandler = + (struct sieve_prefix_ehandler *) _ehandler; + + if ( ehandler->parent == NULL ) return; + + sieve_direct_warning(ehandler->parent, ehandler->location, "%s", + _prefix_message(ehandler, location, fmt, args)); +} + +static void sieve_prefix_vinfo +(struct sieve_error_handler *_ehandler, const char *location, + const char *fmt, va_list args) +{ + struct sieve_prefix_ehandler *ehandler = + (struct sieve_prefix_ehandler *) _ehandler; + + if ( ehandler->parent == NULL ) return; + + sieve_direct_info(ehandler->parent, ehandler->location, "%s", + _prefix_message(ehandler, location, fmt, args)); +} + +static void sieve_prefix_vdebug +(struct sieve_error_handler *_ehandler, const char *location, + const char *fmt, va_list args) +{ + struct sieve_prefix_ehandler *ehandler = + (struct sieve_prefix_ehandler *) _ehandler; + + if ( ehandler->parent == NULL ) return; + + sieve_direct_debug(ehandler->parent, ehandler->location, "%s", + _prefix_message(ehandler, location, fmt, args)); +} + +struct sieve_error_handler *sieve_prefix_ehandler_create +(struct sieve_error_handler *parent, const char *location, const char *prefix) +{ + pool_t pool; + struct sieve_prefix_ehandler *ehandler; + + pool = pool_alloconly_create("sieve_prefix_error_handler", 256); + ehandler = p_new(pool, struct sieve_prefix_ehandler, 1); + ehandler->parent = parent; + ehandler->location = p_strdup(pool, location); + ehandler->prefix = p_strdup(pool, prefix); + + sieve_error_handler_init(&ehandler->handler, pool, parent->max_errors); + + ehandler->handler.verror = sieve_prefix_verror; + ehandler->handler.vwarning = sieve_prefix_vwarning; + ehandler->handler.vinfo = sieve_prefix_vinfo; + ehandler->handler.vdebug = sieve_prefix_vdebug; + + return &(ehandler->handler); +} + +/* + * Varexpand error handler + * + * Encapsulates an existing error handler and formats all messages using the + * provided format string and variables; + */ + +struct sieve_varexpand_ehandler { + struct sieve_error_handler handler; + + struct sieve_error_handler *parent; + + const char *format; + ARRAY_DEFINE(table, struct var_expand_table); +}; + +static const char *_expand_message +(struct sieve_varexpand_ehandler *ehandler, + const char *location, const char *fmt, va_list args) +{ + struct var_expand_table *table = array_get_modifiable(&ehandler->table, NULL); + string_t *str = t_str_new(256); + + /* Fill in substitution items */ + table[0].value = location; + table[1].value = t_strdup_vprintf(fmt, args); + + /* Expand variables */ + var_expand(str, ehandler->format, table); + + return str_c(str); +} + +static void sieve_varexpand_verror +(struct sieve_error_handler *_ehandler, const char *location, + const char *fmt, va_list args) +{ + struct sieve_varexpand_ehandler *ehandler = + (struct sieve_varexpand_ehandler *) _ehandler; + + if ( ehandler->parent == NULL ) return; + + sieve_direct_error(ehandler->parent, location, "%s", + _expand_message(ehandler, location, fmt, args)); +} + +static void sieve_varexpand_vwarning +(struct sieve_error_handler *_ehandler, const char *location, + const char *fmt, va_list args) +{ + struct sieve_varexpand_ehandler *ehandler = + (struct sieve_varexpand_ehandler *) _ehandler; + + if ( ehandler->parent == NULL ) return; + + sieve_direct_warning(ehandler->parent, location, "%s", + _expand_message(ehandler, location, fmt, args)); +} + +static void sieve_varexpand_vinfo +(struct sieve_error_handler *_ehandler, const char *location, + const char *fmt, va_list args) +{ + struct sieve_varexpand_ehandler *ehandler = + (struct sieve_varexpand_ehandler *) _ehandler; + + if ( ehandler->parent == NULL ) return; + + sieve_direct_info(ehandler->parent, location, "%s", + _expand_message(ehandler, location, fmt, args)); +} + +static void sieve_varexpand_vdebug +(struct sieve_error_handler *_ehandler, const char *location, + const char *fmt, va_list args) +{ + struct sieve_varexpand_ehandler *ehandler = + (struct sieve_varexpand_ehandler *) _ehandler; + + if ( ehandler->parent == NULL ) return; + + sieve_direct_debug(ehandler->parent, location, "%s", + _expand_message(ehandler, location, fmt, args)); +} + +struct sieve_error_handler *sieve_varexpand_ehandler_create +(struct sieve_error_handler *parent, const char *format, + struct var_expand_table *table) +{ + pool_t pool; + struct sieve_varexpand_ehandler *ehandler; + struct var_expand_table *entry; + int i; + + pool = pool_alloconly_create("sieve_varexpand_error_handler", 256); + ehandler = p_new(pool, struct sieve_varexpand_ehandler, 1); + ehandler->parent = parent; + ehandler->format = format; + p_array_init(&ehandler->table, pool, 10); + + sieve_error_handler_init(&ehandler->handler, pool, parent->max_errors); + + entry = array_append_space(&ehandler->table); + entry->key = '$'; + entry = array_append_space(&ehandler->table); + entry->key = 'l'; + entry->long_key = "location"; + + for (i = 0; table[i].key != '\0'; i++) { + entry = array_append_space(&ehandler->table); + + /* Sanitize substitution items */ + entry->key = table[i].key; + + if ( table[i].value != NULL ) + entry->value = p_strdup(pool, table[i].value); + if ( table[i].long_key != NULL ) + entry->long_key = p_strdup(pool, table[i].long_key); + } + + (void)array_append_space(&ehandler->table); + + ehandler->handler.verror = sieve_varexpand_verror; + ehandler->handler.vwarning = sieve_varexpand_vwarning; + ehandler->handler.vinfo = sieve_varexpand_vinfo; + ehandler->handler.vdebug = sieve_varexpand_vdebug; + + return &(ehandler->handler); +} diff --git a/src/lib-sieve/sieve-error.h b/src/lib-sieve/sieve-error.h index 0b35e9e7de930e34c4afc80658222a9f9d951e4d..0f344b8b9767dcb1d6f2cf32515e3e3f3e964f36 100644 --- a/src/lib-sieve/sieve-error.h +++ b/src/lib-sieve/sieve-error.h @@ -13,6 +13,8 @@ * Forward declarations */ +struct var_expand_table; + struct sieve_script; struct sieve_error_handler; @@ -65,7 +67,7 @@ void sieve_vdebug (struct sieve_error_handler *ehandler, const char *location, const char *fmt, va_list args); void sieve_vcritical - (struct sieve_error_handler *ehandler, const char *location, + (struct sieve_error_handler *ehandler, const char *location, const char *fmt, va_list args); void sieve_error @@ -133,4 +135,14 @@ struct sieve_error_handler *sieve_strbuf_ehandler_create struct sieve_error_handler *sieve_logfile_ehandler_create (const char *logfile, unsigned int max_errors); +/* Wrapper: prefix all log messages */ +struct sieve_error_handler *sieve_prefix_ehandler_create + (struct sieve_error_handler *parent, const char *location, + const char *prefix); + +/* Wrapper: make messages part of var expansion */ +struct sieve_error_handler *sieve_varexpand_ehandler_create +(struct sieve_error_handler *parent, const char *format, + struct var_expand_table *table); + #endif /* __SIEVE_ERROR_H */ diff --git a/src/lib-sieve/sieve-result.c b/src/lib-sieve/sieve-result.c index d5f5fe78b56c4bf3e3da7144dfdba4dc6bc85fcc..1947ecd18a470739f3619af0c819b21ad73b1e5f 100644 --- a/src/lib-sieve/sieve-result.c +++ b/src/lib-sieve/sieve-result.c @@ -220,94 +220,13 @@ const void *sieve_result_extension_get_context * Error handling */ -static const char *_get_from_address(struct mail *mail) -{ - struct message_address *addr; - const char *str; - - if ( mail_get_first_header(mail, "from", &str) <= 0 ) - return NULL; - - addr = message_address_parse - (pool_datastack_create(), (const unsigned char *)str, strlen(str), 1, - FALSE); - - return addr == NULL || addr->mailbox == NULL || addr->domain == NULL || - *addr->mailbox == '\0' || *addr->domain == '\0' ? - NULL : t_strconcat(addr->mailbox, "@", addr->domain, NULL); -} - -void sieve_result_vlog_message -(const struct sieve_action_exec_env *aenv, sieve_error_func_t log_func, - const char *fmt, va_list args) -{ - const struct sieve_message_data *msgdata = aenv->msgdata; - string_t *str; - const char *msgid, *msg; - - if ( aenv->result->ehandler == NULL ) return; - - msg = t_strdup_vprintf(fmt, args); - - msgid = msgdata->id; - msgid = ( msgid == NULL ? "unspecified" : str_sanitize(msgid, 80) ); - - if ( aenv->scriptenv->action_log_format == NULL ) { - log_func(aenv->result->ehandler, NULL, "msgid=%s: %s", msgid, msg); - - } else { - static struct var_expand_table static_tab[] = { - { '$', NULL, NULL }, - { 'm', NULL, "msgid" }, - { 's', NULL, "subject" }, - { 'f', NULL, "from" }, - { 'l', NULL, "location" }, - { '\0', NULL, NULL } - }; - struct var_expand_table *tab; - unsigned int i; - - tab = t_malloc(sizeof(static_tab)); - memcpy(tab, static_tab, sizeof(static_tab)); - - /* Fill in substitution items */ - tab[0].value = msg; - tab[1].value = msgid; - (void)mail_get_first_header_utf8(msgdata->mail, "Subject", &tab[2].value); - tab[3].value = _get_from_address(msgdata->mail); - tab[4].value = ""; - - /* Sanitize substitution items */ - for (i = 1; tab[i].key != '\0'; i++) - tab[i].value = str_sanitize(tab[i].value, 80); - - /* Expand variables */ - str = t_str_new(256); - var_expand(str, aenv->scriptenv->action_log_format, tab); - - /* Log message */ - log_func(aenv->result->ehandler, NULL, "%s", str_c(str)); - } -} - -void sieve_result_log_message -(const struct sieve_action_exec_env *aenv, sieve_error_func_t log_func, - const char *fmt, ...) -{ - va_list args; - - va_start(args, fmt); - sieve_result_vlog_message(aenv, log_func, fmt, args); - va_end(args); -} - void sieve_result_error (const struct sieve_action_exec_env *aenv, const char *fmt, ...) { va_list args; va_start(args, fmt); - sieve_result_vlog_message(aenv, sieve_error, fmt, args); + sieve_verror(aenv->ehandler, NULL, fmt, args); va_end(args); } @@ -317,7 +236,7 @@ void sieve_result_warning va_list args; va_start(args, fmt); - sieve_result_vlog_message(aenv, sieve_warning, fmt, args); + sieve_vwarning(aenv->ehandler, NULL, fmt, args); va_end(args); } @@ -327,7 +246,7 @@ void sieve_result_log va_list args; va_start(args, fmt); - sieve_result_vlog_message(aenv, sieve_info, fmt, args); + sieve_vinfo(aenv->ehandler, NULL, fmt, args); va_end(args); } @@ -918,8 +837,61 @@ bool sieve_result_print * Result execution */ +static const char *_get_from_address(struct mail *mail) +{ + struct message_address *addr; + const char *str; + + if ( mail_get_first_header(mail, "from", &str) <= 0 ) + return NULL; + + addr = message_address_parse + (pool_datastack_create(), (const unsigned char *)str, strlen(str), 1, + FALSE); + + return addr == NULL || addr->mailbox == NULL || addr->domain == NULL || + *addr->mailbox == '\0' || *addr->domain == '\0' ? + NULL : t_strconcat(addr->mailbox, "@", addr->domain, NULL); +} + +static void _sieve_result_prepare_environment(struct sieve_result *result) +{ + const struct sieve_message_data *msgdata = result->action_env.msgdata; + const struct sieve_script_env *senv = result->action_env.scriptenv; + const struct var_expand_table static_tab[] = { + { 'm', NULL, "msgid" }, + { 's', NULL, "subject" }, + { 'f', NULL, "from" }, + { '\0', NULL, NULL } + }; + const char *msgid = msgdata->id; + struct var_expand_table *tab; + unsigned int i; + + tab = t_malloc(sizeof(static_tab)); + memcpy(tab, static_tab, sizeof(static_tab)); + + msgid = ( msgid == NULL ? "unspecified" : str_sanitize(msgid, 80) ); + + /* Fill in substitution items */ + tab[0].value = msgid; + (void)mail_get_first_header_utf8(msgdata->mail, "Subject", &tab[1].value); + tab[2].value = _get_from_address(msgdata->mail); + tab[3].value = ""; + + /* Sanitize substitution items */ + for (i = 0; tab[i].key != '\0'; i++) + tab[i].value = str_sanitize(tab[i].value, 80); + + result->action_env.exec_status = + ( senv->exec_status == NULL ? + t_new(struct sieve_exec_status, 1) : senv->exec_status ); + result->action_env.ehandler = sieve_varexpand_ehandler_create + (result->ehandler, senv->action_log_format, tab); +} + static bool _sieve_result_implicit_keep - (struct sieve_result *result, bool rollback) +(struct sieve_result *result, bool rollback) { struct sieve_result_action *rac; bool success = TRUE; @@ -1024,11 +996,7 @@ static bool _sieve_result_implicit_keep bool sieve_result_implicit_keep (struct sieve_result *result) { - const struct sieve_script_env *senv = result->action_env.scriptenv; - struct sieve_exec_status dummy_status; - - result->action_env.exec_status = - ( senv->exec_status == NULL ? &dummy_status : senv->exec_status ); + _sieve_result_prepare_environment(result); return _sieve_result_implicit_keep(result, TRUE); } @@ -1053,8 +1021,6 @@ void sieve_result_mark_executed(struct sieve_result *result) int sieve_result_execute (struct sieve_result *result, bool *keep) { - const struct sieve_script_env *senv = result->action_env.scriptenv; - struct sieve_exec_status dummy_status; bool implicit_keep = TRUE; bool success = TRUE, commit_ok; struct sieve_result_action *rac, *first_action; @@ -1064,8 +1030,7 @@ int sieve_result_execute /* Prepare environment */ - result->action_env.exec_status = - ( senv->exec_status == NULL ? &dummy_status : senv->exec_status ); + _sieve_result_prepare_environment(result); /* Make notice of this attempt */ diff --git a/src/lib-sieve/sieve-result.h b/src/lib-sieve/sieve-result.h index 8840544d6fb850b28e52d6e480a235b8022a577e..0bb4e64912658fadcd4f2995e17799ce2c610b2e 100644 --- a/src/lib-sieve/sieve-result.h +++ b/src/lib-sieve/sieve-result.h @@ -86,22 +86,15 @@ bool sieve_result_print * Error handling */ -void sieve_result_vlog_message - (const struct sieve_action_exec_env *aenv, sieve_error_func_t log_func, - const char *fmt, va_list args); -void sieve_result_log_message - (const struct sieve_action_exec_env *aenv, sieve_error_func_t log_func, - const char *fmt, ...) ATTR_FORMAT(3, 4); - -void sieve_result_log - (const struct sieve_action_exec_env *aenv, const char *fmt, ...) - ATTR_FORMAT(2, 3); -void sieve_result_warning - (const struct sieve_action_exec_env *aenv, const char *fmt, ...) - ATTR_FORMAT(2, 3); void sieve_result_error - (const struct sieve_action_exec_env *aenv, const char *fmt, ...) - ATTR_FORMAT(2, 3); +(const struct sieve_action_exec_env *aenv, const char *fmt, ...) + ATTR_FORMAT(2, 3); +void sieve_result_warning +(const struct sieve_action_exec_env *aenv, const char *fmt, ...) + ATTR_FORMAT(2, 3); +void sieve_result_log +(const struct sieve_action_exec_env *aenv, const char *fmt, ...) + ATTR_FORMAT(2, 3); /* * Result composition