From 94f8e03af24b222d931f26b7ae3a783c88616d75 Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan.bosch@dovecot.fi>
Date: Sun, 1 Oct 2017 20:57:38 +0200
Subject: [PATCH] lib-sieve: sieve-result - Sort action side effects based on
 precedence value.

This way, the execution order can be made explicit.
---
 src/lib-sieve/plugins/imap4flags/tag-flags.c  | 14 +++---
 .../plugins/mailbox/tag-mailbox-create.c      |  1 +
 src/lib-sieve/sieve-actions.h                 |  4 ++
 src/lib-sieve/sieve-result.c                  | 43 +++++++++++++------
 4 files changed, 42 insertions(+), 20 deletions(-)

diff --git a/src/lib-sieve/plugins/imap4flags/tag-flags.c b/src/lib-sieve/plugins/imap4flags/tag-flags.c
index bd85baabf..0536b829f 100644
--- a/src/lib-sieve/plugins/imap4flags/tag-flags.c
+++ b/src/lib-sieve/plugins/imap4flags/tag-flags.c
@@ -74,14 +74,12 @@ seff_flags_pre_execute(const struct sieve_side_effect *seffect,
 
 const struct sieve_side_effect_def flags_side_effect = {
 	SIEVE_OBJECT("flags", &flags_side_effect_operand, 0),
-	&act_store,
-
-	seff_flags_dump_context,
-	seff_flags_read_context,
-	seff_flags_merge,
-	seff_flags_print,
-	seff_flags_pre_execute,
-	NULL, NULL, NULL
+	.to_action = &act_store,
+	.dump_context = seff_flags_dump_context,
+	.read_context = seff_flags_read_context,
+	.merge = seff_flags_merge,
+	.print = seff_flags_print,
+	.pre_execute = seff_flags_pre_execute
 };
 
 /*
diff --git a/src/lib-sieve/plugins/mailbox/tag-mailbox-create.c b/src/lib-sieve/plugins/mailbox/tag-mailbox-create.c
index 026c697bf..04977d9a5 100644
--- a/src/lib-sieve/plugins/mailbox/tag-mailbox-create.c
+++ b/src/lib-sieve/plugins/mailbox/tag-mailbox-create.c
@@ -50,6 +50,7 @@ seff_mailbox_create_pre_execute(const struct sieve_side_effect *seffect,
 
 const struct sieve_side_effect_def mailbox_create_side_effect = {
 	SIEVE_OBJECT("create", &mailbox_create_operand, 0),
+	.precedence = 100,
 	.to_action = &act_store,
 	.print = seff_mailbox_create_print,
 	.pre_execute = seff_mailbox_create_pre_execute
diff --git a/src/lib-sieve/sieve-actions.h b/src/lib-sieve/sieve-actions.h
index e86c688c8..4c9017ec6 100644
--- a/src/lib-sieve/sieve-actions.h
+++ b/src/lib-sieve/sieve-actions.h
@@ -103,6 +103,10 @@ struct sieve_action {
 struct sieve_side_effect_def {
 	struct sieve_object_def obj_def;
 
+	/* Precedence (side effects with higher value are executed first) */
+
+	unsigned int precedence;
+
 	/* The action it is supposed to link to */
 	const struct sieve_action_def *to_action;
 
diff --git a/src/lib-sieve/sieve-result.c b/src/lib-sieve/sieve-result.c
index dcb9e21c1..9a90d903b 100644
--- a/src/lib-sieve/sieve-result.c
+++ b/src/lib-sieve/sieve-result.c
@@ -1604,13 +1604,24 @@ sieve_side_effects_list_create(struct sieve_result *result)
 void sieve_side_effects_list_add(struct sieve_side_effects_list *list,
 				 const struct sieve_side_effect *seffect)
 {
-	struct sieve_result_side_effect *reffect;
+	struct sieve_result_side_effect *reffect, *reffect_pos;
 
 	/* Prevent duplicates */
 	reffect = list->first_effect;
+	reffect_pos = NULL;
 	while (reffect != NULL) {
-		if (reffect->seffect.def == seffect->def)
+		const struct sieve_side_effect_def *ref_def = reffect->seffect.def;
+		const struct sieve_side_effect_def *sef_def = seffect->def;
+
+		if (sef_def == ref_def) {
+			/* already listed */
+			i_assert(reffect_pos == NULL);
 			return;
+		}
+		if (sef_def->precedence > ref_def->precedence) {
+			/* insert it before this position */
+			reffect_pos = reffect;
+		}
 
 		reffect = reffect->next;
 	}
@@ -1619,17 +1630,25 @@ void sieve_side_effects_list_add(struct sieve_side_effects_list *list,
 	reffect = p_new(list->result->pool, struct sieve_result_side_effect, 1);
 	reffect->seffect = *seffect;
 
-	/* Add */
-	if (list->first_effect == NULL) {
-		list->first_effect = reffect;
-		list->last_effect = reffect;
-		reffect->prev = NULL;
-		reffect->next = NULL;
+	if (reffect_pos != NULL) {
+		/* Insert */
+		reffect->next = reffect_pos;
+		reffect_pos->prev = reffect;
+		if (list->first_effect == reffect_pos)
+			list->first_effect = reffect;
 	} else {
-		list->last_effect->next = reffect;
-		reffect->prev = list->last_effect;
-		list->last_effect = reffect;
-		reffect->next = NULL;
+		/* Add */
+		if ( list->first_effect == NULL ) {
+			list->first_effect = reffect;
+			list->last_effect = reffect;
+			reffect->prev = NULL;
+			reffect->next = NULL;
+		} else {
+			list->last_effect->next = reffect;
+			reffect->prev = list->last_effect;
+			list->last_effect = reffect;
+			reffect->next = NULL;
+		}
 	}
 }
 
-- 
GitLab