diff --git a/Makefile.am b/Makefile.am
index ca86a326e6e7e5c1ac85d78ff76b2d8430c6d592..8f05f1a8463f2005dea8390110a721ae66c56c1c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -39,6 +39,7 @@ test_cases = \
 	tests/extensions/include/errors.svtest \
 	tests/extensions/imapflags/basic.svtest \
 	tests/extensions/imapflags/hasflag.svtest \
+	tests/extensions/imapflags/execute.svtest \
 	tests/extensions/body/basic.svtest \
 	tests/extensions/body/match-values.svtest \
 	tests/extensions/regex/basic.svtest \
diff --git a/TODO b/TODO
index 14d62bdb87892640d942c2fb08e303d63831902a..1745b4462c5681c9e609e4bcc968613998402d6d 100644
--- a/TODO
+++ b/TODO
@@ -2,9 +2,6 @@ Next (in order of descending priority/precedence):
 * Fix standards compliance issues:
 	- Body: contains various bugs that need to be resolved for standards
 	  compliance.
-	- Imapflags: when keep/fileinto is used multiple times in a script and
-	  duplicate message elimination is performed, the last flag list value
-	  MUST win.
 	- '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 
diff --git a/src/lib-sieve/cmd-keep.c b/src/lib-sieve/cmd-keep.c
index 537ce6f6a33f83e1d0e2202475c7dbf97004dae8..88dc8f3d13d62c7544d0892cbc7065ec169328f3 100644
--- a/src/lib-sieve/cmd-keep.c
+++ b/src/lib-sieve/cmd-keep.c
@@ -66,7 +66,8 @@ static bool cmd_keep_generate
 	/* Emit line number */
     sieve_code_source_line_emit(cgenv->sbin, sieve_command_source_line(ctx));
 
-	return TRUE;
+	/* Generate arguments */
+	return sieve_generate_arguments(cgenv, ctx, NULL);
 }
 
 /* 
diff --git a/src/lib-sieve/plugins/copy/ext-copy.c b/src/lib-sieve/plugins/copy/ext-copy.c
index ba90cbc4475efd6810343ceb73d3086d57a0bf54..2548c8aa7d14fe29b2c3126c5068a7cea78a28bd 100644
--- a/src/lib-sieve/plugins/copy/ext-copy.c
+++ b/src/lib-sieve/plugins/copy/ext-copy.c
@@ -84,7 +84,7 @@ static bool seff_copy_post_commit
 const struct sieve_side_effect copy_side_effect = {
 	SIEVE_OBJECT("copy", &copy_side_effect_operand, 0),
 	&act_store,
-	NULL, NULL,
+	NULL, NULL, NULL,
 	seff_copy_print,
 	NULL, NULL,
 	seff_copy_post_commit, 
diff --git a/src/lib-sieve/plugins/imapflags/tag-flags.c b/src/lib-sieve/plugins/imapflags/tag-flags.c
index a8b9abeee0b042588fbec51fadbdea792ececebd..5eac559634a86d072e3f9e3d094742bdb878818a 100644
--- a/src/lib-sieve/plugins/imapflags/tag-flags.c
+++ b/src/lib-sieve/plugins/imapflags/tag-flags.c
@@ -49,6 +49,10 @@ static bool seff_flags_read_context
 	(const struct sieve_side_effect *seffect, 
 		const struct sieve_runtime_env *renv, sieve_size_t *address,
 		void **se_context);
+static int seff_flags_merge
+	(const struct sieve_runtime_env *renv, const struct sieve_action *action, 
+		const struct sieve_side_effect *seffect, 
+		void **old_context, void *new_context);
 static void seff_flags_print
 	(const struct sieve_side_effect *seffect, const struct sieve_action *action,
 		const struct sieve_result_print_env *rpenv, void *se_context, bool *keep);
@@ -63,6 +67,7 @@ const struct sieve_side_effect flags_side_effect = {
 
 	seff_flags_dump_context,
 	seff_flags_read_context,
+	seff_flags_merge,
 	seff_flags_print,
 	seff_flags_pre_execute, 
 	NULL, NULL, NULL
@@ -260,6 +265,19 @@ static struct seff_flags_context *seff_flags_get_implicit_context
 	return ctx;
 }
 
+/* Result verification */
+
+static int seff_flags_merge
+(const struct sieve_runtime_env *renv ATTR_UNUSED, 
+	const struct sieve_action *action ATTR_UNUSED, 
+	const struct sieve_side_effect *seffect ATTR_UNUSED, 
+	void **old_context, void *new_context)
+{
+	*old_context = new_context;
+	
+	return 1;
+}
+
 /* Result printing */
 
 static void seff_flags_print
diff --git a/src/lib-sieve/sieve-actions.h b/src/lib-sieve/sieve-actions.h
index f845f14b0d503d5656fa3caf3e6b77683af64bd1..915aca84290d1118306ede69af330e8625011d4a 100644
--- a/src/lib-sieve/sieve-actions.h
+++ b/src/lib-sieve/sieve-actions.h
@@ -82,8 +82,9 @@ struct sieve_side_effect {
 	struct sieve_object object;
 	
 	/* The action it is supposed to link to */
-	const struct sieve_action *to_action;
 	
+	const struct sieve_action *to_action;
+		
 	/* Context coding */
 	
 	bool (*dump_context)
@@ -96,8 +97,11 @@ struct sieve_side_effect {
 		
 	/* Result verification */
 	
-	/* ... */
-	
+	int (*merge)
+		(const struct sieve_runtime_env *renv, const struct sieve_action *action, 
+			const struct sieve_side_effect *seffect, 
+			void **old_context, void *new_context);
+
 	/* Result printing */	
 			
 	void (*print)
diff --git a/src/lib-sieve/sieve-generator.c b/src/lib-sieve/sieve-generator.c
index 937ea5652425faf40eeddba23ebe2b3fc8dec395..afac539c9f8cfdc6a4df506b2fc9ffcc9cece5b5 100644
--- a/src/lib-sieve/sieve-generator.c
+++ b/src/lib-sieve/sieve-generator.c
@@ -272,6 +272,10 @@ bool sieve_generate_arguments
 
 		arg = sieve_ast_argument_next(arg);
 	}
+
+	/* Mark end of optional list if it is still open */
+	if ( state == ARG_OPTIONAL )
+		sieve_binary_emit_byte(cgenv->sbin, 0);
 	
 	if ( last_arg != NULL )
 		*last_arg = arg;
diff --git a/src/lib-sieve/sieve-result.c b/src/lib-sieve/sieve-result.c
index e778286393919609e39357b4a942242926d33cfb..c2c1f902981a12979edff7fb2e57f9c35a970a44 100644
--- a/src/lib-sieve/sieve-result.c
+++ b/src/lib-sieve/sieve-result.c
@@ -225,6 +225,87 @@ void sieve_result_add_implicit_side_effect
 	sieve_side_effects_list_add(actctx->seffects, seffect, context);
 }
 
+static int sieve_result_side_effects_merge
+(const struct sieve_runtime_env *renv, const struct sieve_action *action, 
+	struct sieve_side_effects_list *old_seffects,
+	struct sieve_side_effects_list *new_seffects)
+{
+	int ret;
+	struct sieve_result_side_effect *rsef, *nrsef;
+
+	/* Allow side-effects to merge with existing copy */
+		
+	/* Merge existing side effects */
+	rsef = old_seffects != NULL ? old_seffects->first_effect : NULL;
+	while ( rsef != NULL ) {
+		const struct sieve_side_effect *seffect = rsef->seffect;
+		bool found = FALSE;
+		
+		if ( seffect->merge != NULL ) {
+
+			/* Try to find it among the new */
+			nrsef = new_seffects != NULL ? new_seffects->first_effect : NULL;
+			while ( nrsef != NULL ) {
+				if ( nrsef->seffect == seffect ) {
+					if ( seffect->merge
+						(renv, action, seffect, &rsef->context, nrsef->context) < 0 )
+						return -1;
+			
+					found = TRUE;
+					break;
+				}
+		
+				nrsef = nrsef->next;
+			}
+	
+			/* Not found? */
+			if ( !found && seffect->merge
+				(renv, action, seffect, &rsef->context, NULL) < 0 )
+				return -1;
+		}
+	
+		rsef = rsef->next;
+	}
+
+	/* Merge new Side effects */
+	nrsef = new_seffects != NULL ? new_seffects->first_effect : NULL;
+	while ( nrsef != NULL ) {
+		const struct sieve_side_effect *seffect = nrsef->seffect;
+		bool found = FALSE;
+		
+		if ( seffect->merge != NULL ) {
+		
+			/* Try to find it in among the exising */
+			rsef = old_seffects != NULL ? old_seffects->first_effect : NULL;
+			while ( rsef != NULL ) {
+				if ( rsef->seffect == seffect ) {
+					found = TRUE;
+					break;
+				}
+				rsef = rsef->next;
+			}
+	
+			/* Not found? */
+			if ( !found ) {
+				void *new_context = NULL; 
+		
+				if ( (ret=seffect->merge
+					(renv, action, seffect, &new_context, nrsef->context)) < 0 ) 
+					return -1;
+					
+				if ( ret != 0 ) {
+					/* Add side effect */
+					sieve_side_effects_list_add(new_seffects, seffect, new_context);
+				}
+			}
+		}
+	
+		nrsef = nrsef->next;
+	}
+	
+	return 1;
+}
+
 int sieve_result_add_action
 (const struct sieve_runtime_env *renv,
 	const struct sieve_action *action, struct sieve_side_effects_list *seffects,
@@ -249,10 +330,15 @@ int sieve_result_add_action
 			if ( action->check_duplicate != NULL ) {
 				if ( (ret=action->check_duplicate
 					(renv, action, context, raction->context,
-						location, raction->location)) != 0 )
+						location, raction->location)) < 0 )
 					return ret;
-			} else 
-				return 1; 
+				
+				/* Duplicate */	
+				if ( ret == 1 ) {
+					return sieve_result_side_effects_merge
+						(renv, action, raction->seffects, seffects);
+				}
+			}
 		} else {
 			/* Check conflict */
 			if ( action->check_conflict != NULL &&
@@ -270,14 +356,15 @@ int sieve_result_add_action
 
 	/* Check policy limit on total number of actions */
 	if ( sieve_max_actions > 0 && result->action_count >= sieve_max_actions ) {
-		sieve_runtime_error(renv, location, "total number of actions exceeds policy limit");
+		sieve_runtime_error(renv, location, 
+			"total number of actions exceeds policy limit");
 		return -1;
 	}
 
 	/* Check policy limit on number of this class of actions */
 	if ( instance_limit > 0 && instance_count >= instance_limit ) {
-		sieve_runtime_error(renv, location, "number of %s actions exceeds policy limit",
-			action->name);
+		sieve_runtime_error(renv, location, 
+			"number of %s actions exceeds policy limit", action->name);
 		return -1;
 	}