diff --git a/Makefile.am b/Makefile.am
index 8f05f1a8463f2005dea8390110a721ae66c56c1c..6d9aeb309679fcd27de717fa3a56b6b6469ca90b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -50,6 +50,7 @@ test_cases = \
 	tests/extensions/subaddress/basic.svtest \
 	tests/extensions/subaddress/rfc.svtest \
 	tests/extensions/vacation/errors.svtest \
+	tests/extensions/vacation/execute.svtest \
 	tests/compile/compile.svtest \
 	tests/compile/examples.svtest \
 	tests/compile/errors.svtest \
diff --git a/TODO b/TODO
index a82973f35f202efbfa3de0246ff9e81d1664e213..4f14f2e0e5233418a5a487b0d412eeb0179ac858 100644
--- a/TODO
+++ b/TODO
@@ -4,9 +4,6 @@ Next (in order of descending priority/precedence):
 	  compliance.
 	- 'If an address is not syntactically valid, then it will not be matched
 	  by tests specifying ":localpart" or ":domain"'.
-	- Vacation: If the Sieve variables extension is used, the arguments 
-	  MUST NOT have undergone variable expansion prior to their use in response 
-	  tracking.
 * Fix security issues:
 	- Impose limitations on the imapflags extension regarding the number of
 	  set flags and the length of each flag name.
diff --git a/src/lib-sieve/plugins/vacation/cmd-vacation.c b/src/lib-sieve/plugins/vacation/cmd-vacation.c
index a639715b05bc3a287dd453d840ba4ffab2e0219f..22d1e3abd686450a42bfda995eeb92bd9f9818e6 100644
--- a/src/lib-sieve/plugins/vacation/cmd-vacation.c
+++ b/src/lib-sieve/plugins/vacation/cmd-vacation.c
@@ -48,6 +48,8 @@ static const struct sieve_argument vacation_handle_tag;
 static bool cmd_vacation_registered
 	(struct sieve_validator *validator, 
 		struct sieve_command_registration *cmd_reg);
+static bool cmd_vacation_pre_validate
+	(struct sieve_validator *validator, struct sieve_command_context *cmd); 
 static bool cmd_vacation_validate
 	(struct sieve_validator *validator, struct sieve_command_context *cmd);
 static bool cmd_vacation_generate
@@ -58,7 +60,7 @@ const struct sieve_command vacation_command = {
 	SCT_COMMAND, 
 	1, 0, FALSE, FALSE, 
 	cmd_vacation_registered,
-	NULL,  
+	cmd_vacation_pre_validate, 
 	cmd_vacation_validate, 
 	cmd_vacation_generate, 
 	NULL 
@@ -79,6 +81,9 @@ static bool cmd_vacation_validate_string_tag
 static bool cmd_vacation_validate_stringlist_tag
 	(struct sieve_validator *validator, struct sieve_ast_argument **arg, 
 		struct sieve_command_context *cmd);
+static bool cmd_vacation_validate_mime_tag
+	(struct sieve_validator *validator, struct sieve_ast_argument **arg, 
+		struct sieve_command_context *cmd);
 
 /* Argument objects */
 
@@ -112,7 +117,9 @@ static const struct sieve_argument vacation_addresses_tag = {
 
 static const struct sieve_argument vacation_mime_tag = { 
 	"mime",	
-	NULL, NULL, NULL, NULL, NULL /* Only generate opt_code */ 
+	NULL, NULL, 
+	cmd_vacation_validate_mime_tag,
+	NULL, NULL
 };
 
 static const struct sieve_argument vacation_handle_tag = { 
@@ -130,8 +137,7 @@ enum cmd_vacation_optional {
 	OPT_SUBJECT,
 	OPT_FROM,
 	OPT_ADDRESSES,
-	OPT_MIME,
-	OPT_HANDLE
+	OPT_MIME
 };
 
 /* 
@@ -200,6 +206,19 @@ struct act_vacation_context {
 	const char *const *addresses;
 };
 
+/*
+ * Command validation context
+ */
+ 
+struct cmd_vacation_context_data {
+	string_t *from;
+	string_t *subject;
+	
+	bool mime;
+	
+	string_t *handle;
+};
+
 /* 
  * Tag validation 
  */
@@ -237,6 +256,8 @@ static bool cmd_vacation_validate_string_tag
 	struct sieve_command_context *cmd)
 {
 	struct sieve_ast_argument *tag = *arg;
+	struct cmd_vacation_context_data *ctx_data = 
+		(struct cmd_vacation_context_data *) cmd->data; 
 
 	/* Detach the tag itself */
 	*arg = sieve_ast_arguments_detach(*arg,1);
@@ -251,29 +272,44 @@ static bool cmd_vacation_validate_string_tag
 		return FALSE;
 	}
 
-	if ( tag->argument == &vacation_from_tag && 
-		sieve_argument_is_string_literal(*arg) ) {
-		string_t *address = sieve_ast_argument_str(*arg);
-		const char *error;
- 		bool result;
- 		
- 		T_BEGIN {
- 			result = sieve_address_validate(address, &error);
- 
-			if ( !result ) {
-				sieve_command_validate_error(validator, cmd, 
-					"specified :from address '%s' is invalid for vacation action: %s", 
-					str_sanitize(str_c(address), 128), error);
-			}
-		} T_END;
+	if ( tag->argument == &vacation_from_tag ) {
+		if ( sieve_argument_is_string_literal(*arg) ) {
+			string_t *address = sieve_ast_argument_str(*arg);
+			const char *error;
+	 		bool result;
+	 		
+	 		T_BEGIN {
+	 			result = sieve_address_validate(address, &error);
+	 
+				if ( !result ) {
+					sieve_command_validate_error(validator, cmd, 
+						"specified :from address '%s' is invalid for vacation action: %s", 
+						str_sanitize(str_c(address), 128), error);
+				}
+			} T_END;
 		
-		if ( !result )
-			return FALSE;
-	}
+			if ( !result )
+				return FALSE;
+		}
 		
-	/* Skip parameter */
-	*arg = sieve_ast_argument_next(*arg);
-
+		ctx_data->from = sieve_ast_argument_str(*arg);
+		
+		/* Skip parameter */
+		*arg = sieve_ast_argument_next(*arg);
+		
+	} else if ( tag->argument == &vacation_subject_tag ) {
+		ctx_data->subject = sieve_ast_argument_str(*arg);
+		
+		/* Skip parameter */
+		*arg = sieve_ast_argument_next(*arg);
+		
+	} else if ( tag->argument == &vacation_handle_tag ) {
+		ctx_data->handle = sieve_ast_argument_str(*arg);
+		
+		/* Detach optional argument (emitted as mandatory) */
+		*arg = sieve_ast_arguments_detach(*arg,1);
+	}
+			
 	return TRUE;
 }
 
@@ -300,6 +336,21 @@ static bool cmd_vacation_validate_stringlist_tag
 	return TRUE;
 }
 
+static bool cmd_vacation_validate_mime_tag
+(struct sieve_validator *validator ATTR_UNUSED, struct sieve_ast_argument **arg, 
+	struct sieve_command_context *cmd)
+{
+	struct cmd_vacation_context_data *ctx_data = 
+		(struct cmd_vacation_context_data *) cmd->data; 
+
+	ctx_data->mime = TRUE;
+
+	/* Skip tag */
+	*arg = sieve_ast_argument_next(*arg);
+	
+	return TRUE;
+}
+
 /* 
  * Command registration 
  */
@@ -318,7 +369,7 @@ static bool cmd_vacation_registered
 	sieve_validator_register_tag
 		(validator, cmd_reg, &vacation_mime_tag, OPT_MIME); 	
 	sieve_validator_register_tag
-		(validator, cmd_reg, &vacation_handle_tag, OPT_HANDLE); 	
+		(validator, cmd_reg, &vacation_handle_tag, 0); 	
 
 	return TRUE;
 }
@@ -326,18 +377,73 @@ static bool cmd_vacation_registered
 /* 
  * Command validation 
  */
+ 
+static bool cmd_vacation_pre_validate
+(struct sieve_validator *validator ATTR_UNUSED, 
+	struct sieve_command_context *cmd) 
+{
+	struct cmd_vacation_context_data *ctx_data;
+	
+	/* Assign context */
+	ctx_data = p_new(sieve_command_pool(cmd), 
+		struct cmd_vacation_context_data, 1);
+	cmd->data = ctx_data;
+
+	return TRUE;
+}
+
+static const char _handle_empty_subject[] = "<default-subject>";
+static const char _handle_empty_from[] = "<default-from>";
+static const char _handle_mime_enabled[] = "<MIME>";
+static const char _handle_mime_disabled[] = "<NO-MIME>";
 
 static bool cmd_vacation_validate
 (struct sieve_validator *validator, struct sieve_command_context *cmd) 
 { 	
 	struct sieve_ast_argument *arg = cmd->first_positional;
+	struct cmd_vacation_context_data *ctx_data = 
+		(struct cmd_vacation_context_data *) cmd->data; 
 
 	if ( !sieve_validate_positional_argument
 		(validator, cmd, arg, "reason", 1, SAAT_STRING) ) {
 		return FALSE;
 	}
 	
-	return sieve_validator_argument_activate(validator, cmd, arg, FALSE);	
+	if ( !sieve_validator_argument_activate(validator, cmd, arg, FALSE) )
+		return FALSE;
+		
+	/* Construct handle if not set explicitly */
+	if ( ctx_data->handle == NULL ) {
+		string_t *reason = sieve_ast_argument_str(arg);
+		unsigned int size = str_len(reason);
+		
+		/* Precalculate the size of it all */
+		size += ctx_data->subject == NULL ? 
+			sizeof(_handle_empty_subject) - 1 : str_len(ctx_data->subject);
+		size += ctx_data->from == NULL ? 
+			sizeof(_handle_empty_from) - 1 : str_len(ctx_data->from); 
+		size += ctx_data->mime ? 
+			sizeof(_handle_mime_enabled) - 1 : sizeof(_handle_mime_disabled) - 1; 
+			
+		/* Construct the string */
+		ctx_data->handle = str_new(sieve_command_pool(cmd), size);
+		str_append_str(ctx_data->handle, reason);
+		
+		if ( ctx_data->subject != NULL )
+			str_append_str(ctx_data->handle, ctx_data->subject);
+		else
+			str_append(ctx_data->handle, _handle_empty_subject);
+		
+		if ( ctx_data->from != NULL )
+			str_append_str(ctx_data->handle, ctx_data->from);
+		else
+			str_append(ctx_data->handle, _handle_empty_from);
+			
+		str_append(ctx_data->handle, 
+			ctx_data->mime ? _handle_mime_enabled : _handle_mime_disabled );
+	}
+	
+	return TRUE;
 }
 
 /*
@@ -347,6 +453,9 @@ static bool cmd_vacation_validate
 static bool cmd_vacation_generate
 (const struct sieve_codegen_env *cgenv, struct sieve_command_context *ctx) 
 {
+	struct cmd_vacation_context_data *ctx_data = 
+		(struct cmd_vacation_context_data *) ctx->data;
+		 
 	sieve_operation_emit_code(cgenv->sbin, &vacation_operation);
 
 	/* Emit source line */
@@ -356,6 +465,8 @@ static bool cmd_vacation_generate
 	if ( !sieve_generate_arguments(cgenv, ctx, NULL) )
 		return FALSE;	
 
+	sieve_opr_string_emit(cgenv->sbin, ctx_data->handle);
+		
 	return TRUE;
 }
 
@@ -393,7 +504,6 @@ static bool ext_vacation_operation_dump
 				break;
 			case OPT_SUBJECT:
 			case OPT_FROM:
-			case OPT_HANDLE: 
 				if ( !sieve_opr_string_dump(denv, address) )
 					return FALSE;
 				break;
@@ -411,8 +521,10 @@ static bool ext_vacation_operation_dump
 		}
 	}
 	
-	/* Dump reason operand */
-	return sieve_opr_string_dump(denv, address);
+	/* Dump reason and handle operands */
+	return 
+		sieve_opr_string_dump(denv, address) &&
+		sieve_opr_string_dump(denv, address);
 }
 
 /* 
@@ -479,13 +591,6 @@ static int ext_vacation_operation_execute
 					return SIEVE_EXEC_BIN_CORRUPT;
 				}
 				break;
-			case OPT_HANDLE: 
-				if ( !sieve_opr_string_read(renv, address, &handle) ) {
-					sieve_runtime_trace_error(renv, 
-						"invalid handle operand");
-					return SIEVE_EXEC_BIN_CORRUPT;
-				}
-				break;
 			case OPT_ADDRESSES:
 				if ( (addresses=sieve_opr_stringlist_read(renv, address))
 					== NULL ) {
@@ -511,6 +616,12 @@ static int ext_vacation_operation_execute
 		return SIEVE_EXEC_BIN_CORRUPT;
 	}
 	
+	/* Handle operand */
+	if ( !sieve_opr_string_read(renv, address, &handle) ) {
+		sieve_runtime_trace_error(renv, "invalid handle operand");
+		return SIEVE_EXEC_BIN_CORRUPT;
+	}
+	
 	/*
 	 * Perform operation
 	 */
@@ -522,14 +633,13 @@ static int ext_vacation_operation_execute
 	pool = sieve_result_pool(renv->result);
 	act = p_new(pool, struct act_vacation_context, 1);
 	act->reason = p_strdup(pool, str_c(reason));
+	act->handle = p_strdup(pool, str_c(handle));
 	act->days = days;
 	act->mime = mime;
 	if ( subject != NULL )
 		act->subject = p_strdup(pool, str_c(subject));
 	if ( from != NULL )
 		act->from = p_strdup(pool, str_c(from));
-	if ( handle != NULL )
-		act->handle = p_strdup(pool, str_c(handle));
 	if ( addresses != NULL )
 		sieve_coded_stringlist_read_all(addresses, pool, &(act->addresses));
 		
@@ -744,28 +854,8 @@ static void act_vacation_hash
 	md5_init(&ctx);
 	md5_update(&ctx, msgdata->return_path, strlen(msgdata->return_path));
 
-	if ( vctx->handle != NULL && *(vctx->handle) != '\0' ) 
-		md5_update(&ctx, vctx->handle, strlen(vctx->handle));
-	else {
-		const char *from;
-		const char *mime;
-
-		if ( vctx->from != NULL && *(vctx->from) != '\0' )
-			from = vctx->from;
-		else
-			from = msgdata->to_address;
-
-		if ( vctx->mime )
-			mime = "MIME";
-		else
-			mime = "NOMIME";
-
-		md5_update(&ctx, vctx->subject, strlen(vctx->subject));
-		md5_update(&ctx, from, strlen(from));
-		md5_update(&ctx, mime, strlen(mime));
-		md5_update(&ctx, vctx->reason, strlen(vctx->reason));
-	}
-
+	md5_update(&ctx, vctx->handle, strlen(vctx->handle));
+	
 	md5_final(&ctx, hash_r);
 }
 
diff --git a/tests/extensions/vacation/execute.svtest b/tests/extensions/vacation/execute.svtest
new file mode 100644
index 0000000000000000000000000000000000000000..6ad15b86b0d00101e70388e690d6cb957da3af0d
--- /dev/null
+++ b/tests/extensions/vacation/execute.svtest
@@ -0,0 +1,11 @@
+require "vnd.dovecot.testsuite";
+
+test "No :handle specified" {
+	if not test_compile "execute/no-handle.sieve" {
+		test_fail "script compile failed";
+	}
+
+	if not test_execute {
+		test_fail "script execute failed";
+	}
+}
diff --git a/tests/extensions/vacation/execute/no-handle.sieve b/tests/extensions/vacation/execute/no-handle.sieve
new file mode 100644
index 0000000000000000000000000000000000000000..b86298601f032f5656d36977f440b3b94c85c258
--- /dev/null
+++ b/tests/extensions/vacation/execute/no-handle.sieve
@@ -0,0 +1,7 @@
+require "vacation";
+require "variables";
+
+set "reason" "I have a conference in Seattle";
+
+vacation :subject "I am not in: ${reason}" :from "stephan@rename-it.nl" "I am gone for today: ${reason}.";
+