From e34883258326119f41961dac357059227ab1f9d3 Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan@rename-it.nl>
Date: Wed, 12 Nov 2008 17:05:48 +0100
Subject: [PATCH] Imap4flags: fixed bug in the handling of the internal
 variable.

Previously the final value of internal variable was for every store action that didn't
specify a :flags argument explicitly. This results in out-of order assignment/removal
of flags, e.g. also the flags assigned keep actions that were executed before the
addflag/setflag command were modified.
---
 .../plugins/imapflags/ext-imapflags-common.c  |   7 +-
 src/lib-sieve/plugins/imapflags/tag-flags.c   | 160 ++++++++++++------
 src/lib-sieve/sieve-code.c                    |  24 ++-
 src/lib-sieve/sieve-code.h                    |  11 ++
 src/lib-sieve/sieve-validator.c               |  22 +--
 5 files changed, 159 insertions(+), 65 deletions(-)

diff --git a/src/lib-sieve/plugins/imapflags/ext-imapflags-common.c b/src/lib-sieve/plugins/imapflags/ext-imapflags-common.c
index cfb76cbce..1fbc05c6d 100644
--- a/src/lib-sieve/plugins/imapflags/ext-imapflags-common.c
+++ b/src/lib-sieve/plugins/imapflags/ext-imapflags-common.c
@@ -227,7 +227,12 @@ void ext_imapflags_attach_flags_tag
 	 */
 	 
 	/* Tag specified by user */
-	sieve_validator_register_external_tag(valdtr, &tag_flags, command, -1);
+	sieve_validator_register_external_tag
+		(valdtr, &tag_flags, command, -1);
+
+    /* Implicit tag if none is specified */
+	sieve_validator_register_persistent_tag
+		(valdtr, &tag_flags_implicit, command);
 }
 
 /* 
diff --git a/src/lib-sieve/plugins/imapflags/tag-flags.c b/src/lib-sieve/plugins/imapflags/tag-flags.c
index db8cab61e..a77bdfd51 100644
--- a/src/lib-sieve/plugins/imapflags/tag-flags.c
+++ b/src/lib-sieve/plugins/imapflags/tag-flags.c
@@ -14,6 +14,7 @@
 #include "sieve-generator.h"
 #include "sieve-interpreter.h"
 #include "sieve-actions.h"
+#include "sieve-dump.h"
 
 #include "ext-imapflags-common.h"
 
@@ -26,6 +27,8 @@
 static bool tag_flags_validate
 	(struct sieve_validator *validator,	struct sieve_ast_argument **arg, 
 		struct sieve_command_context *cmd);
+static bool tag_flags_validate_persistent
+	(struct sieve_validator *validator, struct sieve_command_context *cmd);
 static bool tag_flags_generate
 	(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg,
 		struct sieve_command_context *cmd);
@@ -38,6 +41,14 @@ const struct sieve_argument tag_flags = {
 	tag_flags_generate 
 };
 
+const struct sieve_argument tag_flags_implicit = { 
+	"flags-implicit", 
+	NULL,
+	tag_flags_validate_persistent, 
+	NULL, NULL,
+	tag_flags_generate
+};
+
 /* 
  * Side effect 
  */
@@ -49,6 +60,7 @@ 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, 
@@ -92,6 +104,16 @@ const struct sieve_operand flags_side_effect_operand = {
  * Tag validation 
  */
 
+static bool tag_flags_validate_persistent
+(struct sieve_validator *validator ATTR_UNUSED, struct sieve_command_context *cmd)
+{
+	if ( sieve_command_find_argument(cmd, &tag_flags) == NULL ) {
+		sieve_command_add_dynamic_tag(cmd, &tag_flags_implicit, -1);
+	}
+	
+	return TRUE;
+}
+
 static bool tag_flags_validate
 (struct sieve_validator *validator,	struct sieve_ast_argument **arg, 
 	struct sieve_command_context *cmd)
@@ -132,13 +154,24 @@ static bool tag_flags_generate
 	}
 
 	sieve_opr_side_effect_emit(cgenv->sbin, &flags_side_effect);
-	  
-	param = arg->parameters;
 
-	/* Call the generation function for the argument */ 
-	if ( param->argument != NULL && param->argument->generate != NULL && 
-		!param->argument->generate(cgenv, param, cmd) ) 
-		return FALSE;
+	if ( arg->argument == &tag_flags ) {
+		/* Explicit :flags tag */
+		param = arg->parameters;
+
+		/* Call the generation function for the argument */ 
+		if ( param->argument != NULL && param->argument->generate != NULL && 
+			!param->argument->generate(cgenv, param, cmd) ) 
+			return FALSE;
+
+	} else if ( arg->argument == &tag_flags_implicit ) {
+		/* Implicit flags */
+		sieve_opr_omitted_emit(cgenv->sbin);
+	
+	} else {
+		/* Something else?! */
+		i_unreached();
+	}
 	
 	return TRUE;
 }
@@ -160,7 +193,57 @@ static bool seff_flags_dump_context
 (const struct sieve_side_effect *seffect ATTR_UNUSED, 
 	const struct sieve_dumptime_env *denv, sieve_size_t *address)
 {
-	return sieve_opr_stringlist_dump(denv, address, "flags");
+    const struct sieve_operand *operand;
+
+    operand = sieve_operand_read(denv->sbin, address);
+
+    if ( sieve_operand_is_omitted(operand) ) {
+		sieve_code_dumpf(denv, "flags: INTERNAL");
+		return TRUE;
+    }
+
+    return sieve_opr_stringlist_dump_data(denv, operand, address,
+            "flags");
+}
+
+static struct seff_flags_context *seff_flags_get_implicit_context
+(struct sieve_result *result)
+{
+	pool_t pool = sieve_result_pool(result);
+	struct seff_flags_context *ctx;
+	const char *flag;
+	struct ext_imapflags_iter flit;
+	
+	ctx = p_new(pool, struct seff_flags_context, 1);
+	p_array_init(&ctx->keywords, pool, 2);
+	
+	t_push();
+		
+	/* Unpack */
+	ext_imapflags_get_implicit_flags_init(&flit, result);
+	while ( (flag=ext_imapflags_iter_get_flag(&flit)) != NULL ) {		
+		if (flag != NULL && *flag != '\\') {
+			/* keyword */
+			const char *keyword = p_strdup(pool, flag);
+			array_append(&ctx->keywords, &keyword, 1);
+		} else {
+			/* system flag */
+			if (flag == NULL || strcasecmp(flag, "\\flagged") == 0)
+				ctx->flags |= MAIL_FLAGGED;
+			else if (strcasecmp(flag, "\\answered") == 0)
+				ctx->flags |= MAIL_ANSWERED;
+			else if (strcasecmp(flag, "\\deleted") == 0)
+				ctx->flags |= MAIL_DELETED;
+			else if (strcasecmp(flag, "\\seen") == 0)
+				ctx->flags |= MAIL_SEEN;
+			else if (strcasecmp(flag, "\\draft") == 0)
+				ctx->flags |= MAIL_DRAFT;
+		}
+	}
+
+	t_pop();
+	
+	return ctx;
 }
 
 static bool seff_flags_read_context
@@ -169,6 +252,8 @@ static bool seff_flags_read_context
 	void **se_context)
 {
 	bool result = TRUE;
+	sieve_size_t op_address = *address;
+	const struct sieve_operand *operand;
 	pool_t pool = sieve_result_pool(renv->result);
 	struct seff_flags_context *ctx;
 	string_t *flags_item;
@@ -178,9 +263,28 @@ static bool seff_flags_read_context
 	p_array_init(&ctx->keywords, pool, 2);
 	
 	t_push();
+
+	/* Check whether explicit flag list operand is present */
+	operand = sieve_operand_read(renv->sbin, address);
+
+    if ( operand == NULL ) {
+        sieve_runtime_trace_error(renv, "invalid operand");
+		t_pop();
+        return FALSE;
+    }
+
+    if ( sieve_operand_is_omitted(operand) ) {
+		/* Flag list is omitted, use current value of internal 
+		 * variable to construct side effect context.
+		 */
+		*se_context = seff_flags_get_implicit_context(renv->result);
+		t_pop();
+		return TRUE;
+	}
 	
 	/* Read flag-list */
-	if ( (flag_list=sieve_opr_stringlist_read(renv, address)) == NULL ) {
+	if ( (flag_list=sieve_opr_stringlist_read_data
+		(renv, operand, op_address, address)) == NULL ) {
 		t_pop();
 		return FALSE;
 	}
@@ -225,46 +329,6 @@ static bool seff_flags_read_context
 	return result;
 }
 
-static struct seff_flags_context *seff_flags_get_implicit_context
-(struct sieve_result *result)
-{
-	pool_t pool = sieve_result_pool(result);
-	struct seff_flags_context *ctx;
-	const char *flag;
-	struct ext_imapflags_iter flit;
-	
-	ctx = p_new(pool, struct seff_flags_context, 1);
-	p_array_init(&ctx->keywords, pool, 2);
-	
-	t_push();
-		
-	/* Unpack */
-	ext_imapflags_get_implicit_flags_init(&flit, result);
-	while ( (flag=ext_imapflags_iter_get_flag(&flit)) != NULL ) {		
-		if (flag != NULL && *flag != '\\') {
-			/* keyword */
-			const char *keyword = p_strdup(pool, flag);
-			array_append(&ctx->keywords, &keyword, 1);
-		} else {
-			/* system flag */
-			if (flag == NULL || strcasecmp(flag, "\\flagged") == 0)
-				ctx->flags |= MAIL_FLAGGED;
-			else if (strcasecmp(flag, "\\answered") == 0)
-				ctx->flags |= MAIL_ANSWERED;
-			else if (strcasecmp(flag, "\\deleted") == 0)
-				ctx->flags |= MAIL_DELETED;
-			else if (strcasecmp(flag, "\\seen") == 0)
-				ctx->flags |= MAIL_SEEN;
-			else if (strcasecmp(flag, "\\draft") == 0)
-				ctx->flags |= MAIL_DRAFT;
-		}
-	}
-
-	t_pop();
-	
-	return ctx;
-}
-
 /* Result verification */
 
 static int seff_flags_merge
diff --git a/src/lib-sieve/sieve-code.c b/src/lib-sieve/sieve-code.c
index 7155f987b..11334944f 100644
--- a/src/lib-sieve/sieve-code.c
+++ b/src/lib-sieve/sieve-code.c
@@ -190,16 +190,12 @@ bool sieve_code_source_line_read
  * Core operands
  */
  
-const struct sieve_operand number_operand;
-const struct sieve_operand string_operand;
-const struct sieve_operand stringlist_operand;
-
 extern const struct sieve_operand comparator_operand;
 extern const struct sieve_operand match_type_operand;
 extern const struct sieve_operand address_part_operand;
 
 const struct sieve_operand *sieve_operands[] = {
-	NULL, /* SIEVE_OPERAND_OPTIONAL */
+	&omitted_operand, /* SIEVE_OPERAND_OPTIONAL */
 	&number_operand,
 	&string_operand,
 	&stringlist_operand,
@@ -278,6 +274,17 @@ bool sieve_operand_optional_read
 /* 
  * Operand definitions
  */
+
+/* Omitted */
+
+const struct sieve_operand_class omitted_class =
+    { "OMITTED" };
+
+const struct sieve_operand omitted_operand = {
+	"@OMITTED",
+	NULL, SIEVE_OPERAND_OPTIONAL,	
+	&omitted_class, NULL
+};
  
 /* Number */
 
@@ -352,6 +359,13 @@ const struct sieve_operand stringlist_operand =	{
 /* 
  * Operand implementations 
  */
+
+/* Omitted */
+
+void sieve_opr_omitted_emit(struct sieve_binary *sbin)
+{
+    (void) sieve_operand_emit_code(sbin, &omitted_operand);
+}
  
 /* Number */
 
diff --git a/src/lib-sieve/sieve-code.h b/src/lib-sieve/sieve-code.h
index 9f1be317b..8fb2aa901 100644
--- a/src/lib-sieve/sieve-code.h
+++ b/src/lib-sieve/sieve-code.h
@@ -98,6 +98,7 @@ const struct sieve_operand_class stringlist_class;
 
 /* Operand objects */
 
+extern const struct sieve_operand omitted_operand;
 extern const struct sieve_operand number_operand;
 extern const struct sieve_operand string_operand;
 extern const struct sieve_operand stringlist_operand;
@@ -137,6 +138,16 @@ struct sieve_opr_stringlist_interface {
  * Core operand functions 
  */
 
+/* Omitted */
+
+void sieve_opr_omitted_emit(struct sieve_binary *sbin);
+
+static inline bool sieve_operand_is_omitted
+(const struct sieve_operand *operand)
+{
+	return ( operand != NULL && operand == &omitted_operand );
+}
+
 /* Number */
 
 void sieve_opr_number_emit(struct sieve_binary *sbin, sieve_number_t number);
diff --git a/src/lib-sieve/sieve-validator.c b/src/lib-sieve/sieve-validator.c
index 3a70e7cd0..0406373f2 100644
--- a/src/lib-sieve/sieve-validator.c
+++ b/src/lib-sieve/sieve-validator.c
@@ -830,19 +830,19 @@ static bool sieve_validate_command_arguments
 	}
 	
 	/* Call initial validation for persistent arguments */
-  if ( array_is_created(&cmd_reg->persistent_tags) ) {
-  	unsigned int i;
+	if ( array_is_created(&cmd_reg->persistent_tags) ) {
+  		unsigned int i;
   	
-	  for ( i = 0; i < array_count(&cmd_reg->persistent_tags); i++ ) {
-	  	struct sieve_tag_registration * const *reg = 
-	  		array_idx(&cmd_reg->persistent_tags, i);
+		for ( i = 0; i < array_count(&cmd_reg->persistent_tags); i++ ) {
+			struct sieve_tag_registration * const *reg = 
+	  			array_idx(&cmd_reg->persistent_tags, i);
 			const struct sieve_argument *tag = (*reg)->tag;
-  	
-	  	if ( tag != NULL && tag->validate_persistent != NULL ) { /* To be sure */
-	  		if ( !tag->validate_persistent(validator, cmd) )
-	  			return FALSE;
-	  	}
-	  }
+  
+			if ( tag != NULL && tag->validate_persistent != NULL ) { /* To be sure */
+				if ( !tag->validate_persistent(validator, cmd) )
+	  				return FALSE;
+			}
+		}
 	}
 
 	return TRUE;
-- 
GitLab