From a195bd824fd60f06812e3b2161a7a8af748cae2d Mon Sep 17 00:00:00 2001 From: Stephan Bosch <stephan.bosch@dovecot.fi> Date: Tue, 11 Oct 2016 16:40:47 +0200 Subject: [PATCH] lib-sieve: vnd.dovecot.report extension: Added support for configuring the "From:" address used in the report. --- doc/extensions/vnd.dovecot.report.txt | 54 ++++++ .../plugins/vnd.dovecot/report/cmd-report.c | 31 +++- .../report/ext-vnd-report-common.c | 16 ++ .../report/ext-vnd-report-common.h | 11 ++ .../vnd.dovecot/report/ext-vnd-report.c | 1 + .../vnd.dovecot/report/execute.svtest | 166 ++++++++++++++++++ 6 files changed, 271 insertions(+), 8 deletions(-) create mode 100644 doc/extensions/vnd.dovecot.report.txt diff --git a/doc/extensions/vnd.dovecot.report.txt b/doc/extensions/vnd.dovecot.report.txt new file mode 100644 index 000000000..c5126071a --- /dev/null +++ b/doc/extensions/vnd.dovecot.report.txt @@ -0,0 +1,54 @@ +Vnd.dovecot.report Extension + +Relevant specifications +======================= + + doc/rfc/spec-bosch-sieve-report.txt + +Description +=========== + +The "vnd.dovecot.report" extension provides the means to send Messaging Abuse +Reporting Format (MARF) reports (RFC 5965). This format is intended for +communications regarding email abuse and related issues. The "report" command +allows (partially) automating the exchange of these reports, which is +particularly useful when the Sieve script is executed for an IMAP event +(RFC 6785) that is triggered by direct user action. + +Configuration +============= + +The "vnd.dovecot.report" extension is not available by default; it needs +to be added to the sieve_extensions setting (or any of the alternatives). + +The "vnd.dovecot.report" extension has its own specific settings. The following +settings can be configured for the vacation extension (default values are +indicated): + + sieve_report_from = postmaster + Specifies what address is used for the "From:" header field in reports. + The following values are supported for this setting: + + "postmaster" - The postmaster_address configured for the LDA (default). + "sender" - The sender address is used. + "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". + "<user@domain>" - Redirected messages are always sent from user@domain. + The angle brackets are mandatory. The null "<>" address + not supported and interpreted as "postmaster". + +Invalid values for the settings above will make the Sieve interpreter log a +warning and revert to the default values. + +Example +======= + +plugin { + sieve = file:~/sieve;active=~/.dovecot.sieve + + sieve_report_from = <reporter@example.com> +} diff --git a/src/lib-sieve/plugins/vnd.dovecot/report/cmd-report.c b/src/lib-sieve/plugins/vnd.dovecot/report/cmd-report.c index 7ce5eafd7..4eaba3e77 100644 --- a/src/lib-sieve/plugins/vnd.dovecot/report/cmd-report.c +++ b/src/lib-sieve/plugins/vnd.dovecot/report/cmd-report.c @@ -291,6 +291,7 @@ static bool cmd_report_operation_dump static int cmd_report_operation_execute (const struct sieve_runtime_env *renv, sieve_size_t *address) { + const struct sieve_extension *this_ext = renv->oprtn->ext; struct act_report_data *act; string_t *fbtype, *message, *to_address; const char *norm_address, *feedback_type, *error; @@ -378,7 +379,7 @@ static int cmd_report_operation_execute act->to_address = p_strdup(pool, norm_address); if ( sieve_result_add_action(renv, - NULL, &act_report, NULL, (void *) act, 0, TRUE) < 0 ) + this_ext, &act_report, NULL, (void *) act, 0, TRUE) < 0 ) return SIEVE_EXEC_FAILURE; return SIEVE_EXEC_OK; @@ -445,18 +446,20 @@ static bool _contains_8bit(const char *msg) static int act_report_send (const struct sieve_action_exec_env *aenv, + const struct ext_report_config *config, const struct act_report_data *act) { struct sieve_instance *svinst = aenv->svinst; struct sieve_message_context *msgctx = aenv->msgctx; const struct sieve_script_env *senv = aenv->scriptenv; const struct sieve_message_data *msgdata = aenv->msgdata; + struct sieve_address_source report_from = config->report_from; struct sieve_smtp_context *sctx; struct istream *input; struct ostream *output; string_t *msg; const char *const *headers; - const char *outmsgid, *boundary, *error, *subject; + const char *outmsgid, *boundary, *error, *subject, *from; int ret; /* Just to be sure */ @@ -479,6 +482,18 @@ static int act_report_send subject = "Report: (message without subject)"; } + /* Determine from address */ + if ( report_from.type == SIEVE_ADDRESS_SOURCE_POSTMASTER ) { + report_from.type = SIEVE_ADDRESS_SOURCE_DEFAULT; + report_from.address = NULL; + } + if ( (ret=sieve_address_source_get_address + (&report_from, svinst, senv, msgctx, + aenv->flags, &from)) <= 0 || from == NULL || *from == '\0') { + from = t_strdup_printf + ("Postmaster <%s>", senv->postmaster_address); + } + /* Start message */ sctx = sieve_smtp_start_single (senv, act->to_address, NULL, &output); @@ -492,11 +507,8 @@ static int act_report_send rfc2822_header_write(msg, "Message-ID", outmsgid); rfc2822_header_write(msg, "Date", message_date_create(ioloop_time)); - rfc2822_header_printf(msg, "From", - "Postmaster <%s>", senv->postmaster_address); - - rfc2822_header_printf(msg, "To", - "<%s>", act->to_address); + rfc2822_header_write(msg, "From", from); + rfc2822_header_printf(msg, "To", "<%s>", act->to_address); if ( _contains_8bit(subject) ) rfc2822_header_utf8_printf(msg, "Subject", "%s", subject); @@ -647,12 +659,15 @@ static int act_report_commit void *tr_context ATTR_UNUSED, bool *keep ATTR_UNUSED) { + const struct sieve_extension *ext = action->ext; + const struct ext_report_config *config = + (const struct ext_report_config *) ext->context; const struct act_report_data *act = (const struct act_report_data *) action->context; int ret; T_BEGIN { - ret = act_report_send(aenv, act); + ret = act_report_send(aenv, config, act); } T_END; if ( ret == SIEVE_EXEC_TEMP_FAILURE ) diff --git a/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report-common.c b/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report-common.c index 93827cb58..911a8c036 100644 --- a/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report-common.c +++ b/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report-common.c @@ -6,9 +6,25 @@ #include "rfc822-parser.h" #include "sieve-common.h" +#include "sieve-extensions.h" #include "ext-vnd-report-common.h" +bool ext_report_load +(const struct sieve_extension *ext, void **context) +{ + struct sieve_instance *svinst = ext->svinst; + struct ext_report_config *config; + + config = p_new(svinst->pool, struct ext_report_config, 1); + + (void)sieve_address_source_parse_from_setting(svinst, + svinst->pool, "sieve_report_from", &config->report_from); + + *context = (void *) config; + return TRUE; +} + const char * ext_vnd_report_parse_feedback_type(const char *feedback_type) { diff --git a/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report-common.h b/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report-common.h index 67b499bca..6292b1a09 100644 --- a/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report-common.h +++ b/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report-common.h @@ -4,12 +4,23 @@ #ifndef __EXT_REPORT_COMMON_H #define __EXT_REPORT_COMMON_H +/* + * Extension configuration + */ + +struct ext_report_config { + struct sieve_address_source report_from; +}; + /* * Extension */ extern const struct sieve_extension_def vnd_report_extension; +bool ext_report_load + (const struct sieve_extension *ext, void **context); + /* * Commands */ diff --git a/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report.c b/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report.c index 6db8189c5..45714fcf9 100644 --- a/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report.c +++ b/src/lib-sieve/plugins/vnd.dovecot/report/ext-vnd-report.c @@ -33,6 +33,7 @@ static bool ext_report_validator_load const struct sieve_extension_def vnd_report_extension = { .name = "vnd.dovecot.report", + .load = ext_report_load, .validator_load = ext_report_validator_load, SIEVE_EXT_DEFINE_OPERATION(report_operation) }; diff --git a/tests/extensions/vnd.dovecot/report/execute.svtest b/tests/extensions/vnd.dovecot/report/execute.svtest index fdb80433a..3df6032f6 100644 --- a/tests/extensions/vnd.dovecot/report/execute.svtest +++ b/tests/extensions/vnd.dovecot/report/execute.svtest @@ -3,6 +3,7 @@ require "vnd.dovecot.report"; require "relational"; require "comparator-i;ascii-numeric"; require "body"; +require "variables"; /* * Simple test @@ -68,3 +69,168 @@ test "Simple - :headers_only" { test_fail "report contains message body"; } } + +/* + * Configuration + */ + +set "message" text: +From: stephan@example.org +To: nico@frop.example.org +Subject: Frop! + +Klutsefluts. +. +; + +/* default */ + +test_set "message" "${message}"; +test_set "envelope.from" "from@example.com"; +test_set "envelope.to" "to@example.com"; +test_set "envelope.orig_to" "orig_to@example.com"; + +test_result_reset; + +test "Configuration - from default" { + report "abuse" "This message is spam!" "abuse@example.com"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not address :localpart "from" "postmaster" { + test_fail "not sent from postmaster"; + } +} + +/* from sender */ + +test_set "message" "${message}"; +test_set "envelope.from" "from@example.com"; +test_set "envelope.to" "to@example.com"; +test_set "envelope.orig_to" "orig_to@example.com"; + +test_config_set "sieve_report_from" "sender"; +test_config_reload :extension "vnd.dovecot.report"; +test_result_reset; + +test "Configuration - from sender" { + report "abuse" "This message is spam!" "abuse@example.com"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not address :localpart "from" "from" { + test_fail "not sent from sender"; + } +} + +/* from recipient */ + +test_set "message" "${message}"; +test_set "envelope.from" "from@example.com"; +test_set "envelope.to" "to@example.com"; +test_set "envelope.orig_to" "orig_to@example.com"; + +test_config_set "sieve_report_from" "recipient"; +test_config_reload :extension "vnd.dovecot.report"; +test_result_reset; + +test "Configuration - from recipient" { + report "abuse" "This message is spam!" "abuse@example.com"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not address :localpart "from" "to" { + test_fail "not sent from recipient"; + } +} + +/* from original recipient */ + +test_set "message" "${message}"; +test_set "envelope.from" "from@example.com"; +test_set "envelope.to" "to@example.com"; +test_set "envelope.orig_to" "orig_to@example.com"; + +test_config_set "sieve_report_from" "orig_recipient"; +test_config_reload :extension "vnd.dovecot.report"; +test_result_reset; + +test "Configuration - from original recipient" { + report "abuse" "This message is spam!" "abuse@example.com"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not address :localpart "from" "orig_to" { + test_fail "not sent from original recipient"; + } +} + +/* from user email */ + +test_set "message" "${message}"; +test_set "envelope.from" "from@example.com"; +test_set "envelope.to" "to@example.com"; +test_set "envelope.orig_to" "orig_to@example.com"; + +test_config_set "sieve_report_from" "user_email"; +test_config_set "sieve_user_email" "user@example.com"; +test_config_reload; +test_config_reload :extension "vnd.dovecot.report"; +test_result_reset; + +test "Configuration - from user email" { + report "abuse" "This message is spam!" "abuse@example.com"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not address :localpart "from" "user" { + test_fail "not sent from user email"; + } +} + +/* explicit */ + +test_set "message" "${message}"; +test_set "envelope.from" "from@example.com"; +test_set "envelope.to" "to@example.com"; +test_set "envelope.orig_to" "orig_to@example.com"; + +test_config_set "sieve_report_from" "<frop@example.com>"; +test_config_reload :extension "vnd.dovecot.report"; +test_result_reset; + +test "Configuration - explicit" { + report "abuse" "This message is spam!" "abuse@example.com"; + + if not test_result_execute { + test_fail "failed to execute notify"; + } + + test_message :smtp 0; + + if not address :localpart "from" "frop" { + test_fail "not sent from explicit address"; + } +} + + -- GitLab