lib-sieve: vacation extension: finally added support for using the original recipient in vacation address check.
authorStephan Bosch <stephan@rename-it.nl>
Tue Aug 02 17:50:15 2011 +0200 (9 months ago)
changeset 151781f7acac82f2
parent 1516 efad75f779de
child 1518 04b4edd4bf6e
lib-sieve: vacation extension: finally added support for using the original recipient in vacation address check.
doc/vacation.txt
src/lib-sieve/plugins/vacation/cmd-vacation.c
src/lib-sieve/plugins/vacation/ext-vacation-common.c
src/lib-sieve/plugins/vacation/ext-vacation-common.h
src/testsuite/testsuite-message.c
src/testsuite/testsuite-message.h
src/testsuite/testsuite-objects.c
tests/extensions/vacation/reply.svtest
     1.1 --- a/doc/vacation.txt	Tue Aug 02 16:36:06 2011 +0200
     1.2 +++ b/doc/vacation.txt	Tue Aug 02 17:50:15 2011 +0200
     1.3 @@ -31,9 +31,9 @@
     1.4  day.  
     1.5  
     1.6  The vacation and vacation-seconds extensions have their own specific
     1.7 -settings. The settings that specify a period (currently all of them) 
     1.8 -are specified in s(econds), unless followed by a d(ay), h(our) or m(inute)
     1.9 -specifier character. 
    1.10 +settings. The settings that specify a period are specified in
    1.11 +s(econds), unless followed by a d(ay), h(our) or m(inute) specifier
    1.12 +character. 
    1.13  
    1.14  The following settings can be configured the vacation extension (default
    1.15  values are indicated):
    1.16 @@ -56,6 +56,27 @@
    1.17    tag is specified. The configured value must lie between the
    1.18    sieve_vacation_min_period and sieve_vacation_max_period.
    1.19  
    1.20 +sieve_vacation_use_original_recipient = no
    1.21 +  This specifies whether the original envelope recipient should be used in
    1.22 +  the check for implicit delivery.  The vacation command checks headers of
    1.23 +  the incoming message, such as To: and Cc: for the address of the
    1.24 +  recipient, to verify that the message is explicitly addressed at the
    1.25 +  recipient. If the recipient address is not found, the vacation action
    1.26 +  will not trigger a response to prevent sending a reply when it is not
    1.27 +  appropriate. Normally only the final recipient address is used in this
    1.28 +  check. This setting allows including the original recipient specified in
    1.29 +  the SMTP session if available. This is useful to handle mail accounts
    1.30 +  with aliases. Use this option with caution: if you are using aliases that
    1.31 +  point to more than a single account, senders can get multiple vacation
    1.32 +  responses for a single message. 
    1.33 +  
    1.34 +sieve_vacation_dont_check_recipient = no
    1.35 +  This disables the checks for implicit delivery entirely. This means that
    1.36 +  the vacation command does not verify that the message is explicitly
    1.37 +  addressed at the recipient. Use this option with caution. Specifying
    1.38 +  'yes' will violate the Sieve standards and can cause vacation replies to
    1.39 +  be sent for messages not directly addressed at the recipient. 
    1.40 +
    1.41  Invalid values for the settings above will make the Sieve interpreter log
    1.42  a warning and revert to the default values. 
    1.43  
     2.1 --- a/src/lib-sieve/plugins/vacation/cmd-vacation.c	Tue Aug 02 16:36:06 2011 +0200
     2.2 +++ b/src/lib-sieve/plugins/vacation/cmd-vacation.c	Tue Aug 02 17:50:15 2011 +0200
     2.3 @@ -1017,11 +1017,11 @@
     2.4  	const struct sieve_script_env *senv = aenv->scriptenv;
     2.5  	struct act_vacation_context *ctx = 
     2.6  		(struct act_vacation_context *) action->context;
     2.7 -	const char *const *hdsp;
     2.8  	unsigned char dupl_hash[MD5_RESULTLEN];
     2.9 -	const char *const *headers;
    2.10  	const char *sender = sieve_message_get_sender(aenv->msgctx);
    2.11  	const char *recipient = sieve_message_get_final_recipient(aenv->msgctx);
    2.12 +	const char *const *hdsp;
    2.13 +	const char *const *headers;
    2.14  	const char *reply_from = NULL;
    2.15  
    2.16  	/* Is the recipient unset? 
    2.17 @@ -1037,14 +1037,14 @@
    2.18  	if ( sender == NULL ) {
    2.19  		sieve_result_global_log(aenv, "discarded vacation reply to <>");
    2.20  		return TRUE;
    2.21 -	}    
    2.22 -	
    2.23 -	/* Are we perhaps trying to respond to ourselves ? 
    2.24 +	}
    2.25 +
    2.26 +	/* Are we perhaps trying to respond to ourselves ?
    2.27  	 */
    2.28  	if ( sieve_address_compare(sender, recipient, TRUE) == 0 ) {
    2.29  		sieve_result_global_log(aenv,
    2.30  			"discarded vacation reply to own address <%s>",
    2.31 -			str_sanitize(sender, 128));	
    2.32 +			str_sanitize(sender, 128));
    2.33  		return TRUE;
    2.34  	}
    2.35  
    2.36 @@ -1052,24 +1052,24 @@
    2.37  	 */
    2.38  	if ( ctx->addresses != NULL ) {
    2.39  		const char * const *alt_address = ctx->addresses;
    2.40 -		
    2.41 +
    2.42  		while ( *alt_address != NULL ) {
    2.43  			if ( sieve_address_compare(sender, *alt_address, TRUE) == 0 ) {
    2.44  				sieve_result_global_log(aenv,
    2.45  					"discarded vacation reply to own address <%s> "
    2.46  					"(as specified using :addresses argument)",
    2.47 -					str_sanitize(sender, 128));	
    2.48 +					str_sanitize(sender, 128));
    2.49  				return TRUE;
    2.50  			}
    2.51  			alt_address++;
    2.52 -		}			
    2.53 +		}
    2.54  	}
    2.55 -	
    2.56 +
    2.57  	/* Did whe respond to this user before? */
    2.58  	if ( sieve_action_duplicate_check_available(senv) ) {
    2.59  		act_vacation_hash(ctx, sender, dupl_hash);
    2.60 -	
    2.61 -		if ( sieve_action_duplicate_check(senv, dupl_hash, sizeof(dupl_hash)) ) 
    2.62 +
    2.63 +		if ( sieve_action_duplicate_check(senv, dupl_hash, sizeof(dupl_hash)) )
    2.64  		{
    2.65  			sieve_result_global_log(aenv,
    2.66  				"discarded duplicate vacation response to <%s>",
    2.67 @@ -1077,7 +1077,7 @@
    2.68  			return TRUE;
    2.69  		}
    2.70  	}
    2.71 -	
    2.72 +
    2.73  	/* Are we trying to respond to a mailing list ? */
    2.74  	hdsp = _list_headers;
    2.75  	while ( *hdsp != NULL ) {
    2.76 @@ -1101,7 +1101,7 @@
    2.77  			if ( strcasecmp(*hdsp, "no") != 0 ) {
    2.78  				sieve_result_global_log(aenv, 
    2.79  					"discarding vacation response to auto-submitted message from <%s>", 
    2.80 -					str_sanitize(sender, 128));	
    2.81 + 					str_sanitize(sender, 128));	
    2.82  					return TRUE;				 
    2.83  			}
    2.84  			hdsp++;
    2.85 @@ -1124,59 +1124,73 @@
    2.86  			hdsp++;
    2.87  		}
    2.88  	}
    2.89 -	
    2.90 +
    2.91  	/* Do not reply to system addresses */
    2.92  	if ( _is_system_address(sender) ) {
    2.93 -		sieve_result_global_log(aenv, 
    2.94 -			"not sending vacation response to system address <%s>", 
    2.95 -			str_sanitize(sender, 128));	
    2.96 -		return TRUE;				
    2.97 -	} 
    2.98 -	
    2.99 +		sieve_result_global_log(aenv,
   2.100 +			"not sending vacation response to system address <%s>",
   2.101 +			str_sanitize(sender, 128));
   2.102 +		return TRUE;
   2.103 +	}
   2.104 +
   2.105  	/* Is the original message directly addressed to the user or the addresses
   2.106 -	 * specified using the :addresses tag? 
   2.107 +	 * specified using the :addresses tag?
   2.108  	 */
   2.109 -	hdsp = _my_address_headers;
   2.110 -	while ( *hdsp != NULL ) {
   2.111 -		if ( mail_get_headers
   2.112 -			(msgdata->mail, *hdsp, &headers) >= 0 && headers[0] != NULL ) {	
   2.113 -			
   2.114 -			if ( _contains_my_address(headers, recipient) ) {
   2.115 -				reply_from = recipient;
   2.116 -				break;
   2.117 +	if ( !config->dont_check_recipient ) {
   2.118 +		const char *orig_recipient = NULL;
   2.119 +
   2.120 +		if ( config->use_original_recipient  )
   2.121 +			orig_recipient = sieve_message_get_orig_recipient(aenv->msgctx);
   2.122 +
   2.123 +		hdsp = _my_address_headers;
   2.124 +		while ( *hdsp != NULL ) {
   2.125 +			if ( mail_get_headers
   2.126 +				(msgdata->mail, *hdsp, &headers) >= 0 && headers[0] != NULL ) {	
   2.127 +
   2.128 +				if ( _contains_my_address(headers, recipient) ) {
   2.129 +					reply_from = recipient;
   2.130 +					break;
   2.131 +				}
   2.132 +
   2.133 +				if ( orig_recipient != NULL && _contains_my_address(headers, orig_recipient) ) {
   2.134 +					reply_from = orig_recipient;
   2.135 +					break;
   2.136 +				}
   2.137 +
   2.138 +				if ( ctx->addresses != NULL ) {
   2.139 +					bool found = FALSE;
   2.140 +					const char * const *my_address = ctx->addresses;
   2.141 +
   2.142 +					while ( !found && *my_address != NULL ) {
   2.143 +						if ( (found=_contains_my_address(headers, *my_address)) )
   2.144 +							reply_from = *my_address;
   2.145 +						my_address++;
   2.146 +					}
   2.147 +
   2.148 +					if ( found ) break;
   2.149 +				}
   2.150  			}
   2.151 -			
   2.152 -			if ( ctx->addresses != NULL ) {
   2.153 -				bool found = FALSE;
   2.154 -				const char * const *my_address = ctx->addresses;
   2.155 -		
   2.156 -				while ( !found && *my_address != NULL ) {
   2.157 -					found = _contains_my_address(headers, *my_address);
   2.158 -					reply_from = *my_address;
   2.159 -					my_address++;
   2.160 -				}
   2.161 -				
   2.162 -				if ( found ) break;
   2.163 -			}
   2.164 +			hdsp++;
   2.165  		}
   2.166 -		hdsp++;
   2.167 -	}	
   2.168  
   2.169 -	if ( *hdsp == NULL ) {
   2.170 -		/* No, bail out */
   2.171 -		sieve_result_global_log(aenv, 
   2.172 -			"discarding vacation response for message implicitly delivered to <%s>",
   2.173 -			recipient );	
   2.174 -		return TRUE;				 
   2.175 -	}	
   2.176 -	
   2.177 +
   2.178 +		/* My address not found in the headers; we got an implicit delivery */
   2.179 +		if ( *hdsp == NULL ) {
   2.180 +			/* No, bail out */
   2.181 +			sieve_result_global_log(aenv,
   2.182 +				"discarding vacation response for message implicitly delivered to <%s>",
   2.183 +				recipient );
   2.184 +			return TRUE;
   2.185 +		}
   2.186 +	}
   2.187 +
   2.188  	/* Send the message */
   2.189 -	
   2.190 +
   2.191  	if ( act_vacation_send(aenv, ctx, sender, reply_from) ) {
   2.192  		sieve_number_t seconds; 
   2.193  
   2.194  		sieve_result_global_log(aenv, "sent vacation response to <%s>", 
   2.195 -			str_sanitize(sender, 128));	
   2.196 +			str_sanitize(sender, 128));
   2.197  
   2.198  		/* Check period limits once more */
   2.199  		seconds = ctx->seconds;
     3.1 --- a/src/lib-sieve/plugins/vacation/ext-vacation-common.c	Tue Aug 02 16:36:06 2011 +0200
     3.2 +++ b/src/lib-sieve/plugins/vacation/ext-vacation-common.c	Tue Aug 02 17:50:15 2011 +0200
     3.3 @@ -16,6 +16,7 @@
     3.4  	struct sieve_instance *svinst = ext->svinst;
     3.5  	struct ext_vacation_config *config;
     3.6  	sieve_number_t min_period, max_period, default_period;
     3.7 +	bool use_original_recipient, dont_check_recipient;
     3.8  
     3.9  	if ( *context != NULL ) {
    3.10  		ext_vacation_unload(ext);
    3.11 @@ -48,11 +49,23 @@
    3.12  			"sieve_vacation_min_period < sieve_vacation_default_period < "
    3.13  			"sieve_vacation_max_period");
    3.14  	}
    3.15 -	
    3.16 +
    3.17 +	if ( !sieve_setting_get_bool_value
    3.18 +		(svinst, "sieve_vacation_use_original_recipient", &use_original_recipient) ) {
    3.19 +		use_original_recipient = FALSE;
    3.20 +	}
    3.21 +
    3.22 +	if ( !sieve_setting_get_bool_value
    3.23 +		(svinst, "sieve_vacation_dont_check_recipient", &dont_check_recipient) ) {
    3.24 +		dont_check_recipient = FALSE;
    3.25 +	}
    3.26 +
    3.27  	config = i_new(struct ext_vacation_config, 1);
    3.28  	config->min_period = min_period;
    3.29  	config->max_period = max_period;
    3.30  	config->default_period = default_period;
    3.31 +	config->use_original_recipient = use_original_recipient;
    3.32 +	config->dont_check_recipient = dont_check_recipient;
    3.33  
    3.34  	*context = (void *) config;
    3.35  
     4.1 --- a/src/lib-sieve/plugins/vacation/ext-vacation-common.h	Tue Aug 02 16:36:06 2011 +0200
     4.2 +++ b/src/lib-sieve/plugins/vacation/ext-vacation-common.h	Tue Aug 02 17:50:15 2011 +0200
     4.3 @@ -18,6 +18,8 @@
     4.4  	unsigned int min_period;
     4.5  	unsigned int max_period;
     4.6  	unsigned int default_period;
     4.7 +	bool use_original_recipient;
     4.8 +	bool dont_check_recipient;
     4.9  };
    4.10  
    4.11  /* 
     5.1 --- a/src/testsuite/testsuite-message.c	Tue Aug 02 16:36:06 2011 +0200
     5.2 +++ b/src/testsuite/testsuite-message.c	Tue Aug 02 17:50:15 2011 +0200
     5.3 @@ -33,6 +33,7 @@
     5.4  
     5.5  static string_t *envelope_from;
     5.6  static string_t *envelope_to;
     5.7 +static string_t *envelope_orig_to;
     5.8  static string_t *envelope_auth;
     5.9  
    5.10  pool_t message_pool;
    5.11 @@ -82,6 +83,7 @@
    5.12  	testsuite_message_set_data(testsuite_mail);
    5.13  
    5.14  	envelope_to = str_new(message_pool, 256);
    5.15 +	envelope_orig_to = str_new(message_pool, 256);
    5.16  	envelope_from = str_new(message_pool, 256);
    5.17  	envelope_auth = str_new(message_pool, 256);
    5.18  }
    5.19 @@ -111,7 +113,7 @@
    5.20  
    5.21  	sieve_message_context_flush(renv->msgctx);
    5.22  }
    5.23 -	
    5.24 +
    5.25  void testsuite_message_deinit(void)
    5.26  {
    5.27  	pool_unref(&message_pool);
    5.28 @@ -144,6 +146,19 @@
    5.29  	sieve_message_context_flush(renv->msgctx);
    5.30  }
    5.31  
    5.32 +void testsuite_envelope_set_orig_recipient
    5.33 +(const struct sieve_runtime_env *renv, const char *value)
    5.34 +{
    5.35 +	str_truncate(envelope_orig_to, 0);
    5.36 +
    5.37 +	if ( value != NULL )
    5.38 +		str_append(envelope_orig_to, value);
    5.39 +
    5.40 +	testsuite_msgdata.orig_envelope_to = str_c(envelope_orig_to);
    5.41 +
    5.42 +	sieve_message_context_flush(renv->msgctx);
    5.43 +}
    5.44 +
    5.45  void testsuite_envelope_set_auth_user
    5.46  (const struct sieve_runtime_env *renv, const char *value)
    5.47  {
     6.1 --- a/src/testsuite/testsuite-message.h	Tue Aug 02 16:36:06 2011 +0200
     6.2 +++ b/src/testsuite/testsuite-message.h	Tue Aug 02 17:50:15 2011 +0200
     6.3 @@ -26,6 +26,8 @@
     6.4  	(const struct sieve_runtime_env *renv, const char *value);
     6.5  void testsuite_envelope_set_recipient
     6.6  	(const struct sieve_runtime_env *renv, const char *value);
     6.7 +void testsuite_envelope_set_orig_recipient
     6.8 +	(const struct sieve_runtime_env *renv, const char *value);
     6.9  void testsuite_envelope_set_auth_user
    6.10  	(const struct sieve_runtime_env *renv, const char *value);
    6.11  
     7.1 --- a/src/testsuite/testsuite-objects.c	Tue Aug 02 16:36:06 2011 +0200
     7.2 +++ b/src/testsuite/testsuite-objects.c	Tue Aug 02 17:50:15 2011 +0200
     7.3 @@ -305,6 +305,7 @@
     7.4  enum testsuite_object_envelope_field {
     7.5  	TESTSUITE_OBJECT_ENVELOPE_FROM,
     7.6  	TESTSUITE_OBJECT_ENVELOPE_TO,
     7.7 +	TESTSUITE_OBJECT_ENVELOPE_ORIG_TO,
     7.8  	TESTSUITE_OBJECT_ENVELOPE_AUTH_USER
     7.9  };
    7.10  
    7.11 @@ -324,6 +325,8 @@
    7.12  		return TESTSUITE_OBJECT_ENVELOPE_FROM;
    7.13  	if ( strcasecmp(identifier, "to") == 0 )
    7.14  		return TESTSUITE_OBJECT_ENVELOPE_TO;
    7.15 +	if ( strcasecmp(identifier, "orig_to") == 0 )
    7.16 +		return TESTSUITE_OBJECT_ENVELOPE_ORIG_TO;
    7.17  	if ( strcasecmp(identifier, "auth") == 0 )
    7.18  		return TESTSUITE_OBJECT_ENVELOPE_AUTH_USER;	
    7.19  	
    7.20 @@ -337,6 +340,8 @@
    7.21  		return "from";
    7.22  	case TESTSUITE_OBJECT_ENVELOPE_TO: 
    7.23  		return "to";
    7.24 +	case TESTSUITE_OBJECT_ENVELOPE_ORIG_TO: 
    7.25 +		return "orig_to";
    7.26  	case TESTSUITE_OBJECT_ENVELOPE_AUTH_USER: 
    7.27  		return "auth";
    7.28  	}
    7.29 @@ -354,6 +359,9 @@
    7.30  	case TESTSUITE_OBJECT_ENVELOPE_TO:
    7.31  		testsuite_envelope_set_recipient(renv, str_c(value));
    7.32  		return TRUE;
    7.33 +	case TESTSUITE_OBJECT_ENVELOPE_ORIG_TO:
    7.34 +		testsuite_envelope_set_orig_recipient(renv, str_c(value));
    7.35 +		return TRUE;
    7.36  	case TESTSUITE_OBJECT_ENVELOPE_AUTH_USER: 
    7.37  		testsuite_envelope_set_auth_user(renv, str_c(value));
    7.38  		return TRUE;
     8.1 --- a/tests/extensions/vacation/reply.svtest	Tue Aug 02 16:36:06 2011 +0200
     8.2 +++ b/tests/extensions/vacation/reply.svtest	Tue Aug 02 17:50:15 2011 +0200
     8.3 @@ -207,6 +207,37 @@
     8.4  }
     8.5  
     8.6  /*
     8.7 + * No reply to original recipient
     8.8 + */
     8.9 +
    8.10 +test_result_reset;
    8.11 +
    8.12 +test_set "message" text:
    8.13 +From: timo@example.com
    8.14 +To: all@example.com
    8.15 +Subject: Frop!
    8.16 +
    8.17 +Frop!
    8.18 +.
    8.19 +;
    8.20 +
    8.21 +test_set "envelope.from" "timo@example.com";
    8.22 +test_set "envelope.to" "stephan@example.com";
    8.23 +test_set "envelope.orig_to" "all@example.com";
    8.24 +
    8.25 +test "No reply for original recipient" {
    8.26 +	vacation "I am gone";
    8.27 +
    8.28 +	if not test_result_execute {
    8.29 +		test_fail "failed to execute vacation";
    8.30 +	}
    8.31 +
    8.32 +	if test_message :smtp 0 {
    8.33 +		test_fail "vacation not supposed to send message";
    8.34 +	}
    8.35 +}
    8.36 +
    8.37 +/*
    8.38   * Reply for normal mail
    8.39   */
    8.40  
    8.41 @@ -268,6 +299,73 @@
    8.42  	}
    8.43  }
    8.44  
    8.45 +/*
    8.46 + * Reply for original recipient
    8.47 + */
    8.48  
    8.49 +test_result_reset;
    8.50  
    8.51 +test_set "message" text:
    8.52 +From: timo@example.com
    8.53 +To: all@example.com
    8.54 +Subject: Frop!
    8.55  
    8.56 +Frop!
    8.57 +.
    8.58 +;
    8.59 +
    8.60 +test_set "envelope.from" "timo@example.com";
    8.61 +test_set "envelope.to" "stephan@example.com";
    8.62 +test_set "envelope.orig_to" "all@example.com";
    8.63 +
    8.64 +test_config_set "sieve_vacation_use_original_recipient" "yes";
    8.65 +test_config_reload :extension "vacation";
    8.66 +
    8.67 +test "Reply for original recipient" {
    8.68 +	vacation "I am gone";
    8.69 +
    8.70 +	if not test_result_execute {
    8.71 +		test_fail "failed to execute vacation";
    8.72 +	}
    8.73 +
    8.74 +	if not test_message :smtp 0 {
    8.75 +		test_fail "vacation did not reply";
    8.76 +	}
    8.77 +}
    8.78 +
    8.79 +/*
    8.80 + * Reply for any recipient
    8.81 + */
    8.82 +
    8.83 +test_result_reset;
    8.84 +
    8.85 +test_set "message" text:
    8.86 +From: timo@example.com
    8.87 +To: all@example.com
    8.88 +Subject: Frop!
    8.89 +
    8.90 +Frop!
    8.91 +.
    8.92 +;
    8.93 +
    8.94 +test_set "envelope.from" "timo@example.com";
    8.95 +test_set "envelope.to" "stephan@example.com";
    8.96 +
    8.97 +test_config_set "sieve_vacation_dont_check_recipient" "yes";
    8.98 +test_config_reload :extension "vacation";
    8.99 +
   8.100 +test "Reply for any recipient" {
   8.101 +	vacation "I am gone";
   8.102 +
   8.103 +	if not test_result_execute {
   8.104 +		test_fail "failed to execute vacation";
   8.105 +	}
   8.106 +
   8.107 +	if not test_message :smtp 0 {
   8.108 +		test_fail "vacation did not reply";
   8.109 +	}
   8.110 +}
   8.111 +
   8.112 +
   8.113 +
   8.114 +