From 0bcd59d75dadd14d82ccf278f424f735d7587cd7 Mon Sep 17 00:00:00 2001 From: Stephan Bosch <stephan@dovecot.fi> Date: Sat, 9 Apr 2016 22:11:35 +0200 Subject: [PATCH] lib-sieve: Added a "sieve_user_email" setting that configures the user's primary email address. This is used mainly when no other means of obtaining the user's email is available, such as when there is no envelope (in IMAP). Before, the postmaster address or the null "<>" address was used as a fallback for sending messages. The redirect and enotify commands can be explicitly configured to use what is configured with sieve_user_email. --- INSTALL | 12 ++++ doc/example-config/conf.d/90-sieve.conf | 12 ++++ src/lib-sieve/cmd-redirect.c | 17 ++++-- .../plugins/enotify/mailto/ntfy-mailto.c | 27 +++++---- src/lib-sieve/sieve-address-source.c | 16 ++++- src/lib-sieve/sieve-address-source.h | 12 ++-- src/lib-sieve/sieve-common.h | 1 + src/lib-sieve/sieve-settings.c | 16 ++++- tests/execute/smtp.svtest | 50 ++++++++++++++++ tests/extensions/enotify/mailto.svtest | 60 +++++++++++++++++++ 10 files changed, 197 insertions(+), 26 deletions(-) diff --git a/INSTALL b/INSTALL index 8be860b67..bfb3336f8 100644 --- a/INSTALL +++ b/INSTALL @@ -217,6 +217,14 @@ plugin section of the config file (default values are shown if applicable): release. LDAP support may also be compiled as a plugin called `sieve_storage_ldap'. + sieve_user_email = + The primary e-mail address for the user. This is used as a default when no + other appropriate address is available for sending messages. If this setting + is not configured, either the postmaster or null "<>" address is used as a + sender, depending on the action involved. This setting is important when + there is no message envelope to extract addresses from, such as when the + script is executed in IMAP. + sieve_user_log = The path to the file where the user log file is written. If not configured, a default location is used. If the main user's personal Sieve (as configured @@ -241,6 +249,10 @@ plugin section of the config file (default values are shown if applicable): "sender" - The sender address is used (default). "recipient" - The final recipient address is used. "orig_recipient" - The original recipient is used. + "user_email" - The user's primary address is used. This is + configured with the "sieve_user_email" setting. If + that setting is unconfigured, "user_mail" is equal to + "sender" (the default). "postmaster" - The postmaster_address configured for the LDA. "<user@domain>" - Redirected messages are always sent from user@domain. The angle brackets are mandatory. The null "<>" address diff --git a/doc/example-config/conf.d/90-sieve.conf b/doc/example-config/conf.d/90-sieve.conf index f516ba42d..edc952dbb 100644 --- a/doc/example-config/conf.d/90-sieve.conf +++ b/doc/example-config/conf.d/90-sieve.conf @@ -136,6 +136,14 @@ plugin { # (Currently only relevant for ManageSieve) #sieve_quota_max_storage = 0 + # The primary e-mail address for the user. This is used as a default when no + # other appropriate address is available for sending messages. If this setting + # is not configured, either the postmaster or null "<>" address is used as a + # sender, depending on the action involved. This setting is important when + # there is no message envelope to extract addresses from, such as when the + # script is executed in IMAP. + #sieve_user_email = + # The path to the file where the user log is written. If not configured, a # default location is used. If the main user's personal Sieve (as configured # with sieve=) is a file, the logfile is set to <filename>.log by default. If @@ -148,6 +156,10 @@ plugin { # "sender" - The sender address is used (default). # "recipient" - The final recipient address is used. # "orig_recipient" - The original recipient is used. + # "user_email" - The user's primary address is used. This is + # configured with the "sieve_user_email" setting. If + # that setting is unconfigured, "user_mail" is equal to + # "recipient". # "postmaster" - The postmaster_address configured for the LDA. # "<user@domain>" - Redirected messages are always sent from user@domain. # The angle brackets are mandatory. The null "<>" address diff --git a/src/lib-sieve/cmd-redirect.c b/src/lib-sieve/cmd-redirect.c index a2b006fa0..af3464949 100644 --- a/src/lib-sieve/cmd-redirect.c +++ b/src/lib-sieve/cmd-redirect.c @@ -312,13 +312,12 @@ static int act_redirect_send { static const char *hide_headers[] = { "Return-Path", "X-Sieve", "X-Sieve-Redirected-From" }; - + struct sieve_instance *svinst = aenv->svinst; struct sieve_message_context *msgctx = aenv->msgctx; const struct sieve_script_env *senv = aenv->scriptenv; const char *sender; const char *recipient = sieve_message_get_final_recipient(msgctx); - struct sieve_address_source env_from = - aenv->svinst->redirect_from; + struct sieve_address_source env_from = svinst->redirect_from; struct istream *input; struct ostream *output; const char *error; @@ -354,14 +353,20 @@ static int act_redirect_send /* Envelope available */ sender = sieve_message_get_sender(msgctx); if ( sender != NULL && - sieve_address_source_get_address(&env_from, + sieve_address_source_get_address(&env_from, svinst, senv, msgctx, aenv->flags, &sender) < 0 ) sender = NULL; } else { /* No envelope available */ - if ( sieve_address_source_get_address(&env_from, - senv, msgctx, aenv->flags, &sender) <= 0 ) + if ( (ret=sieve_address_source_get_address(&env_from, svinst, + senv, msgctx, aenv->flags, &sender)) < 0 ) { sender = NULL; + } else if ( ret == 0 ) { + if ( svinst->user_email == NULL ) + sender = NULL; + else + sieve_address_to_string(svinst->user_email); + } } /* Open SMTP transport */ diff --git a/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c b/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c index f14cd88b3..4967b58ac 100644 --- a/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c +++ b/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c @@ -431,6 +431,7 @@ static int ntfy_mailto_send (const struct sieve_enotify_exec_env *nenv, const struct sieve_enotify_action *nact, const char *recipient) { + struct sieve_instance *svinst = nenv->svinst; const struct sieve_message_data *msgdata = nenv->msgdata; const struct sieve_script_env *senv = nenv->scriptenv; struct ntfy_mailto_context *mtctx = @@ -467,13 +468,6 @@ static int ntfy_mailto_send return 0; } - /* Determine message from address */ - if ( nact->from == NULL ) { - from = t_strdup_printf("Postmaster <%s>", senv->postmaster_address); - } else { - from = nact->from; - } - /* Determine which sender to use From RFC 5436, Section 2.3: @@ -493,12 +487,23 @@ static int ntfy_mailto_send env_from.type = SIEVE_ADDRESS_SOURCE_EXPLICIT; } } - if ( (ret=sieve_address_source_get_address(&env_from, + if ( (ret=sieve_address_source_get_address(&env_from, svinst, senv, nenv->msgctx, nenv->flags, &from_smtp)) < 0 ) { from_smtp = NULL; } else if ( ret == 0 ) { - from_smtp = ( mtctx->from_normalized != NULL ? - mtctx->from_normalized : senv->postmaster_address ); + if ( mtctx->from_normalized != NULL ) + from_smtp = mtctx->from_normalized; + else if ( svinst->user_email != NULL ) + from_smtp = sieve_address_to_string(svinst->user_email); + else + from_smtp = senv->postmaster_address; + } + + /* Determine message from address */ + if ( nact->from == NULL ) { + from = t_strdup_printf("<%s>", from_smtp); + } else { + from = nact->from; } /* Determine subject */ @@ -551,7 +556,7 @@ static int ntfy_mailto_send } msg = t_str_new(512); - outmsgid = sieve_message_get_new_id(nenv->svinst); + outmsgid = sieve_message_get_new_id(svinst); rfc2822_header_write(msg, "X-Sieve", SIEVE_IMPLEMENTATION); rfc2822_header_write(msg, "Message-ID", outmsgid); diff --git a/src/lib-sieve/sieve-address-source.c b/src/lib-sieve/sieve-address-source.c index c51d1c6dc..e0124ec24 100644 --- a/src/lib-sieve/sieve-address-source.c +++ b/src/lib-sieve/sieve-address-source.c @@ -33,6 +33,8 @@ bool sieve_address_source_parse asrc->type = SIEVE_ADDRESS_SOURCE_RECIPIENT; } else if ( strcmp(value, "orig_recipient") == 0 ) { asrc->type = SIEVE_ADDRESS_SOURCE_ORIG_RECIPIENT; + } else if ( strcmp(value, "user_email") == 0 ) { + asrc->type = SIEVE_ADDRESS_SOURCE_USER_EMAIL; } else if ( strcmp(value, "postmaster") == 0 ) { asrc->type = SIEVE_ADDRESS_SOURCE_POSTMASTER; } else if ( value[0] == '<' && value[val_len-1] == '>') { @@ -70,6 +72,7 @@ bool sieve_address_source_parse_from_setting int sieve_address_source_get_address (struct sieve_address_source *asrc, + struct sieve_instance *svinst, const struct sieve_script_env *senv, struct sieve_message_context *msgctx, enum sieve_execute_flags flags, @@ -77,8 +80,12 @@ int sieve_address_source_get_address { enum sieve_address_source_type type = asrc->type; + if ( type == SIEVE_ADDRESS_SOURCE_USER_EMAIL && + svinst->user_email == NULL ) + type = SIEVE_ADDRESS_SOURCE_RECIPIENT; + if ( (flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) != 0 ) { - switch ( asrc->type ) { + switch ( type ) { case SIEVE_ADDRESS_SOURCE_SENDER: case SIEVE_ADDRESS_SOURCE_RECIPIENT: case SIEVE_ADDRESS_SOURCE_ORIG_RECIPIENT: @@ -100,12 +107,15 @@ int sieve_address_source_get_address case SIEVE_ADDRESS_SOURCE_ORIG_RECIPIENT: *addr_r = sieve_message_get_orig_recipient(msgctx); return 1; - case SIEVE_ADDRESS_SOURCE_EXPLICIT: - *addr_r = sieve_address_to_string(asrc->address); + case SIEVE_ADDRESS_SOURCE_USER_EMAIL: + *addr_r = sieve_address_to_string(svinst->user_email); return 1; case SIEVE_ADDRESS_SOURCE_POSTMASTER: *addr_r = senv->postmaster_address; return 1; + case SIEVE_ADDRESS_SOURCE_EXPLICIT: + *addr_r = sieve_address_to_string(asrc->address); + return 1; case SIEVE_ADDRESS_SOURCE_DEFAULT: break; } diff --git a/src/lib-sieve/sieve-address-source.h b/src/lib-sieve/sieve-address-source.h index 7dad09bda..d6bea2bad 100644 --- a/src/lib-sieve/sieve-address-source.h +++ b/src/lib-sieve/sieve-address-source.h @@ -11,6 +11,7 @@ enum sieve_address_source_type { SIEVE_ADDRESS_SOURCE_SENDER, SIEVE_ADDRESS_SOURCE_RECIPIENT, SIEVE_ADDRESS_SOURCE_ORIG_RECIPIENT, + SIEVE_ADDRESS_SOURCE_USER_EMAIL, SIEVE_ADDRESS_SOURCE_POSTMASTER, SIEVE_ADDRESS_SOURCE_EXPLICIT }; @@ -28,10 +29,11 @@ bool sieve_address_source_parse_from_setting const char *setting, struct sieve_address_source *asrc); int sieve_address_source_get_address -(struct sieve_address_source *asrc, - const struct sieve_script_env *senv, - struct sieve_message_context *msgctx, - enum sieve_execute_flags flags, - const char **addr_r); + (struct sieve_address_source *asrc, + struct sieve_instance *svinst, + const struct sieve_script_env *senv, + struct sieve_message_context *msgctx, + enum sieve_execute_flags flags, + const char **addr_r); #endif diff --git a/src/lib-sieve/sieve-common.h b/src/lib-sieve/sieve-common.h index 2c505549b..705f9d95f 100644 --- a/src/lib-sieve/sieve-common.h +++ b/src/lib-sieve/sieve-common.h @@ -201,6 +201,7 @@ struct sieve_instance { size_t max_script_size; unsigned int max_actions; unsigned int max_redirects; + const struct sieve_address *user_email; struct sieve_address_source redirect_from; }; diff --git a/src/lib-sieve/sieve-settings.c b/src/lib-sieve/sieve-settings.c index 92fcb3d2f..8a9673293 100644 --- a/src/lib-sieve/sieve-settings.c +++ b/src/lib-sieve/sieve-settings.c @@ -8,7 +8,8 @@ #include "sieve-common.h" #include "sieve-limits.h" #include "sieve-error.h" -#include "sieve-actions.h" +#include "sieve-address.h" +#include "sieve-address-source.h" #include "sieve-settings.h" #include <ctype.h> @@ -204,6 +205,7 @@ bool sieve_setting_get_duration_value void sieve_settings_load (struct sieve_instance *svinst) { + const char *str_setting; unsigned long long int uint_setting; size_t size_setting; @@ -228,6 +230,18 @@ void sieve_settings_load (void)sieve_address_source_parse_from_setting(svinst, svinst->pool, "sieve_redirect_envelope_from", &svinst->redirect_from); + + str_setting = sieve_setting_get(svinst, "sieve_user_email"); + if ( str_setting != NULL && *str_setting != '\0' ) { + svinst->user_email = + sieve_address_parse_envelope_path + (svinst->pool, str_setting); + if ( svinst->user_email == NULL ) { + sieve_sys_warning(svinst, + "Invalid address value for setting " + "`sieve_user_email': `%s'", str_setting); + } + } } diff --git a/tests/execute/smtp.svtest b/tests/execute/smtp.svtest index 08cbdcd29..e3d762582 100644 --- a/tests/execute/smtp.svtest +++ b/tests/execute/smtp.svtest @@ -188,3 +188,53 @@ test "Redirect from <> with [<explicit>]" { test_fail "envelope sender incorrect"; } } + +test_result_reset; +test_set "envelope.from" "sirius@example.org"; +test_set "envelope.to" "timo@example.net"; +test_set "envelope.orig_to" "tss@example.net"; + +test_config_set "sieve_redirect_envelope_from" "user_email"; +test_config_reload; + +test "Redirect from [user email - fallback default]" { + redirect "cras@example.net"; + + if not test_result_execute { + test_fail "failed to execute redirect"; + } + + test_message :smtp 0; + + if not envelope :is "from" "timo@example.net" { + test_fail "envelope sender incorrect"; + } +} + +test_result_reset; +test_set "envelope.from" "sirius@example.org"; +test_set "envelope.to" "timo@example.net"; +test_set "envelope.orig_to" "tss@example.net"; + +test_config_set "sieve_redirect_envelope_from" "user_email"; +test_config_set "sieve_user_email" "t.sirainen@example.net"; +test_config_reload; + +test "Redirect from [user email]" { + redirect "cras@example.net"; + + if not test_result_execute { + test_fail "failed to execute redirect"; + } + + test_message :smtp 0; + + if envelope :is "from" "sirius@example.org" { + test_fail "envelope sender incorrect (not changed)"; + } + + if not envelope :is "from" "t.sirainen@example.net" { + test_fail "envelope sender incorrect"; + } +} + diff --git a/tests/extensions/enotify/mailto.svtest b/tests/extensions/enotify/mailto.svtest index afee1531c..82f47be84 100644 --- a/tests/extensions/enotify/mailto.svtest +++ b/tests/extensions/enotify/mailto.svtest @@ -423,3 +423,63 @@ test "Envelope config - recipient" { test_fail "envelope recipient set incorrectly"; } } + +/* + * Envelope config - user_email + */ + +test_set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop! + +Klutsefluts. +. +; + +test_set "envelope.from" "sirius@example.org"; +test_set "envelope.to" "bertus@frop.example.org"; + +test_config_set "sieve_notify_mailto_envelope_from" + "user_email"; +test_config_set "sieve_user_email" "b.wortel@example.org"; +test_config_reload; +test_config_reload :extension "enotify"; +test_result_reset; + +test "Envelope config - user_email" { + notify :from "nico@frop.example.org" + "mailto:stephan@example.org?cc=timo@example.com"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not header :is "from" "nico@frop.example.org" { + test_fail "from set incorrectly"; + } + + if not envelope :is "from" "b.wortel@example.org" { + test_fail "envelope sender set incorrectly"; + } + + if not envelope :is "to" "stephan@example.org" { + test_fail "envelope recipient set incorrectly"; + } + + test_message :smtp 1; + + if not header :is "from" "nico@frop.example.org" { + test_fail "from set incorrectly"; + } + + if not envelope :is "from" "b.wortel@example.org" { + test_fail "envelope sender set incorrectly"; + } + + if not envelope :is "to" "timo@example.com" { + test_fail "envelope recipient set incorrectly"; + } +} -- GitLab