diff --git a/doc/extensions/editheader.txt b/doc/extensions/editheader.txt index 172ce9c52e6b85b5501a4240ae95128bf33e5e40..1ac00d1e92f2c79be3d21565f9ff7a491a73a679 100644 --- a/doc/extensions/editheader.txt +++ b/doc/extensions/editheader.txt @@ -26,11 +26,23 @@ sieve_editheader_max_header_size = 2048 command. The minimum value for this setting is 1024 bytes. The value is in bytes, unless followed by a k(ilo). +sieve_editheader_forbid_add = + A space-separated list of headers that cannot be added to the message header. + Addition of the `Subject:' header cannot be prohibited, as required by the RFC + specification. Therefore, adding this header to this setting has no effect. + +sieve_editheader_forbid_delete = + A space-separated list of headers that cannot be deleted from the message + header. Deleting the `Received:' and `Auto-Submitted:' fields is always + forbidden, while removing the `Subject:' header cannot be prohibited, + as required by the RFC specification. Therefore, adding one of these headers + to this setting has no effect. + sieve_editheader_protected = - A space-separated list of headers that cannot be added to nor removed from the - message header. The `Received:' and `Auto-Submitted:' fields are always - protected and the `Subject:' header cannot be protected, as required by the - RFC specification; adding one of these headers to this setting has no effect. + A space-separated list of headers that cannot be added to or deleted from + the message header. This setting is provided for backwards compatibility. It + is a combination of the sieve_editheader_forbid_add and + sieve_editheader_forbid_delete settings. The same limitations apply. Invalid values for the settings above will make the Sieve interpreter log a warning and revert to the default values. @@ -45,7 +57,8 @@ plugin { # Header fiels must not exceed one 1k sieve_editheader_max_header_size = 1k - # Protect special header - sieve_editheader_protected = X-Verified + # Protected special headers + sieve_editheader_forbid_add = X-Verified + sieve_editheader_forbid_delete = X-Verified X-Seen } diff --git a/src/lib-sieve/plugins/editheader/cmd-addheader.c b/src/lib-sieve/plugins/editheader/cmd-addheader.c index b84fd71ff9b38272d9311fbd673dae86cefb7f44..401d65e7a8ad3a35b74156c9f9ce35f2c20427b9 100644 --- a/src/lib-sieve/plugins/editheader/cmd-addheader.c +++ b/src/lib-sieve/plugins/editheader/cmd-addheader.c @@ -130,10 +130,13 @@ static bool cmd_addheader_validate return FALSE; } - if ( ext_editheader_header_is_protected(cmd->ext, str_c(fname)) ) { - sieve_argument_validate_warning(valdtr, arg, "addheader command: " - "specified header field `%s' is protected; " - "modification will be denied", str_sanitize(str_c(fname), 80)); + if ( !ext_editheader_header_allow_add + (cmd->ext, str_c(fname)) ) { + sieve_argument_validate_warning + (valdtr, arg, "addheader command: " + "adding specified header field `%s' is forbidden; " + "modification will be denied", + str_sanitize(str_c(fname), 80)); } } @@ -298,10 +301,11 @@ static int cmd_addheader_operation_execute return SIEVE_EXEC_FAILURE; } - if ( ext_editheader_header_is_protected(this_ext, str_c(field_name)) ) { + if ( !ext_editheader_header_allow_add + (this_ext, str_c(field_name)) ) { sieve_runtime_warning(renv, NULL, "addheader action: " - "specified header field `%s' is protected; modification denied", - str_sanitize(str_c(field_name), 80)); + "adding specified header field `%s' is forbidden; " + "modification denied", str_sanitize(str_c(field_name), 80)); return SIEVE_EXEC_OK; } diff --git a/src/lib-sieve/plugins/editheader/cmd-deleteheader.c b/src/lib-sieve/plugins/editheader/cmd-deleteheader.c index 9aa00fcb40dbc72fc8747f6aa15f425b76d668ab..e4ec54623c999b378ae78ac1482e20ff9c27b630 100644 --- a/src/lib-sieve/plugins/editheader/cmd-deleteheader.c +++ b/src/lib-sieve/plugins/editheader/cmd-deleteheader.c @@ -259,10 +259,13 @@ static bool cmd_deleteheader_validate return FALSE; } - if ( ext_editheader_header_is_protected(cmd->ext, str_c(fname)) ) { - sieve_argument_validate_warning(valdtr, arg, "deleteheader command: " - "specified header field `%s' is protected; " - "modification will be denied", str_sanitize(str_c(fname), 80)); + if ( !ext_editheader_header_allow_delete + (cmd->ext, str_c(fname)) ) { + sieve_argument_validate_warning + (valdtr, arg, "deleteheader command: " + "deleting specified header field `%s' is forbidden; " + "modification will be denied", + str_sanitize(str_c(fname), 80)); } } @@ -426,9 +429,11 @@ static int cmd_deleteheader_operation_execute return SIEVE_EXEC_FAILURE; } - if ( ext_editheader_header_is_protected(this_ext, str_c(field_name)) ) { + if ( !ext_editheader_header_allow_delete + (this_ext, str_c(field_name)) ) { sieve_runtime_warning(renv, NULL, "deleteheader action: " - "specified header field `%s' is protected; modification denied", + "deleting specified header field `%s' is forbidden; " + "modification denied", str_sanitize(str_c(field_name), 80)); return SIEVE_EXEC_OK; } diff --git a/src/lib-sieve/plugins/editheader/ext-editheader-common.c b/src/lib-sieve/plugins/editheader/ext-editheader-common.c index b886b8e262e690f7d82daa97d36711b280b40800..3b36509ba4e3e25dbe99de1935db15304794f421 100644 --- a/src/lib-sieve/plugins/editheader/ext-editheader-common.c +++ b/src/lib-sieve/plugins/editheader/ext-editheader-common.c @@ -22,8 +22,8 @@ struct ext_editheader_header { const char *name; - /* may extend this later */ - unsigned int protected:1; + unsigned int forbid_add:1; + unsigned int forbid_delete:1; }; struct ext_editheader_config { @@ -49,12 +49,49 @@ static struct ext_editheader_header *ext_editheader_config_header_find return NULL; } +static void ext_editheader_config_headers +(struct sieve_instance *svinst, + struct ext_editheader_config *ext_config, + const char *setting, bool forbid_add, bool forbid_delete) +{ + const char *setval; + + setval = sieve_setting_get(svinst, setting); + if ( setval != NULL ) { + const char **headers = t_strsplit_spaces(setval, " \t"); + + while ( *headers != NULL ) { + struct ext_editheader_header *header; + + if ( !rfc2822_header_field_name_verify + (*headers, strlen(*headers)) ) { + sieve_sys_warning(svinst, "editheader: " + "setting %s contains invalid header field name " + "`%s' (ignored)", setting, *headers); + continue; + } + + header=ext_editheader_config_header_find(ext_config, *headers); + if ( header == NULL ) { + header = array_append_space(&ext_config->headers); + header->name = p_strdup(ext_config->pool, *headers); + } + + if (forbid_add) + header->forbid_add = TRUE; + if (forbid_delete) + header->forbid_delete = TRUE; + + headers++; + } + } +} + bool ext_editheader_load (const struct sieve_extension *ext, void **context) { struct ext_editheader_config *ext_config; struct sieve_instance *svinst = ext->svinst; - const char *protected; size_t max_header_size; pool_t pool; @@ -71,31 +108,12 @@ bool ext_editheader_load p_array_init(&ext_config->headers, pool, 16); - protected = sieve_setting_get(svinst, "sieve_editheader_protected"); - if ( protected != NULL ) { - const char **headers = t_strsplit_spaces(protected, " \t"); - - while ( *headers != NULL ) { - struct ext_editheader_header *header; - - if ( !rfc2822_header_field_name_verify(*headers, strlen(*headers)) ) { - sieve_sys_warning(svinst, - "editheader: setting sieve_editheader_protected contains " - "invalid header field name `%s' (ignored)", *headers); - continue; - } - - header=ext_editheader_config_header_find(ext_config, *headers); - if ( header == NULL ) { - header = array_append_space(&ext_config->headers); - header->name = p_strdup(pool, *headers); - } - - header->protected = TRUE; - - headers++; - } - } + ext_editheader_config_headers(svinst, ext_config, + "sieve_editheader_protected", TRUE, TRUE); + ext_editheader_config_headers(svinst, ext_config, + "sieve_editheader_forbid_add", TRUE, FALSE); + ext_editheader_config_headers(svinst, ext_config, + "sieve_editheader_forbid_delete", FALSE, TRUE); if ( sieve_setting_get_size_value (svinst, "sieve_editheader_max_header_size", &max_header_size) ) { @@ -129,26 +147,42 @@ void ext_editheader_unload(const struct sieve_extension *ext) * Protected headers */ -bool ext_editheader_header_is_protected +bool ext_editheader_header_allow_add (const struct sieve_extension *ext, const char *hname) { struct ext_editheader_config *ext_config = (struct ext_editheader_config *) ext->context; const struct ext_editheader_header *header; - if ( strcasecmp(hname, "received") == 0 - || strcasecmp(hname, "auto-submitted") == 0 ) { + if ( strcasecmp(hname, "subject") == 0 ) return TRUE; - } - if ( strcasecmp(hname, "subject") == 0 ) { - return FALSE; - } + if ( (header=ext_editheader_config_header_find + (ext_config, hname)) == NULL ) + return TRUE; + + return !header->forbid_add; +} - if ( (header=ext_editheader_config_header_find(ext_config, hname)) == NULL ) +bool ext_editheader_header_allow_delete +(const struct sieve_extension *ext, const char *hname) +{ + struct ext_editheader_config *ext_config = + (struct ext_editheader_config *) ext->context; + const struct ext_editheader_header *header; + + if ( strcasecmp(hname, "received") == 0 + || strcasecmp(hname, "auto-submitted") == 0 ) return FALSE; - return header->protected; + if ( strcasecmp(hname, "subject") == 0 ) + return TRUE; + + if ( (header=ext_editheader_config_header_find + (ext_config, hname)) == NULL ) + return TRUE; + + return !header->forbid_delete; } /* diff --git a/src/lib-sieve/plugins/editheader/ext-editheader-common.h b/src/lib-sieve/plugins/editheader/ext-editheader-common.h index fc21d61c5bd7320f0f2967da7dba2c03ebcff341..f3d075b3f90f6bdd6c6f46af694a11504ec8d81a 100644 --- a/src/lib-sieve/plugins/editheader/ext-editheader-common.h +++ b/src/lib-sieve/plugins/editheader/ext-editheader-common.h @@ -37,8 +37,10 @@ void ext_editheader_unload(const struct sieve_extension *ext); * Protected headers */ -bool ext_editheader_header_is_protected - (const struct sieve_extension *ext, const char *header); +bool ext_editheader_header_allow_add + (const struct sieve_extension *ext, const char *hname); +bool ext_editheader_header_allow_delete + (const struct sieve_extension *ext, const char *hname); /* * Limits diff --git a/tests/extensions/editheader/protected.svtest b/tests/extensions/editheader/protected.svtest index e21415b02539c5da3c626975e643cab937f6363c..398fc9cb120f773eea80bde6e4264c0426c39284 100644 --- a/tests/extensions/editheader/protected.svtest +++ b/tests/extensions/editheader/protected.svtest @@ -9,6 +9,7 @@ Received: by example.com (Postfix, from userid 202) id 32A131WFW23QWE4; Mon, 21 Nov 2011 05:25:26 +0200 (EET) Delivery-date: Mon, 21 Nov 2011 04:26:04 +0100 Auto-Submitted: yes +X-Friep: frop 3 Subject: Frop! From: stephan@example.com To: tss@example.com @@ -29,6 +30,7 @@ test "Default protected" { deleteheader "received"; deleteheader "auto-submitted"; + deleteheader "subject"; if not exists "received" { test_fail "protected received header was deleted"; @@ -37,6 +39,10 @@ test "Default protected" { if not exists "auto-submitted" { test_fail "protected auto-submitted header was deleted"; } + + if exists "subject" { + test_fail "subject header cannot be protected, but it was not deleted"; + } } test_config_set "sieve_editheader_protected" "subject delivery-date x-frop"; @@ -72,3 +78,73 @@ test "Configured protected" { test_fail "protected x-frop header was added"; } } + +test_config_set "sieve_editheader_protected" ""; +test_config_set "sieve_editheader_forbid_add" "subject x-frop"; +test_config_set "sieve_editheader_forbid_delete" "subject x-friep"; +test_config_reload :extension "editheader"; + +test_set "message" "${message}"; +test "Configured forbid_add/forbid_delete" { + if not exists "delivery-date" { + test_fail "received header did not exist in the first place"; + } + + if not exists "subject" { + test_fail "received header did not exist in the first place"; + } + + if not exists "x-friep" { + test_fail "x-friep header did not exist in the first place"; + } + + if exists "x-frop" { + test_fail "x-frop header already present"; + } + + deleteheader "delivery-date"; + deleteheader "subject"; + deleteheader "x-friep"; + + if exists "delivery-date" { + test_fail "unprotected delivery-date header was not deleted"; + } + + if exists "subject" { + test_fail "subject header cannot be protected, but it was not deleted"; + } + + if not exists "x-friep" { + test_fail "protected x-friep header was deleted"; + } + + addheader "delivery-date" "Yesterday"; + addheader "subject" "Fropfrop!"; + addheader "x-frop" "Frop!"; + addheader "received" text: +by sieve.example.com (My little Sieve script) +id 3jhl22khhf23f; Mon, 24 Aug 2015 04:11:54 -0600; +. +; + addheader "auto-submitted" "no way"; + + if not header "delivery-date" "Yesterday" { + test_fail "unprotected delivery-date header was not added"; + } + + if not header "subject" "Fropfrop!" { + test_fail "subject header cannot be protected, but it was not added"; + } + + if exists "x-frop" { + test_fail "protected x-frop header was added"; + } + + if not header :contains "received" "sieve.example.com" { + test_fail "received header was not added"; + } + + if not header "auto-submitted" "no way" { + test_fail "autosubmitted header was not added"; + } +}