From bcbb3217bb55f7272c33398f25652d2b21477792 Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan@rename-it.nl>
Date: Sun, 4 Jan 2009 00:55:07 +0100
Subject: [PATCH] Multiscript: implemented execution of multiple scripts for
 the sieve-test command.

---
 src/lib-sieve/ext-reject.c                    |  38 ++++--
 src/lib-sieve/plugins/vacation/cmd-vacation.c |   2 +-
 src/lib-sieve/sieve-result.c                  | 115 +++++++++++-------
 src/lib-sieve/sieve.c                         |  51 ++++++--
 src/lib-sieve/sieve.h                         |   6 +-
 src/sieve-tools/sieve-test.c                  |  51 +++++++-
 6 files changed, 189 insertions(+), 74 deletions(-)

diff --git a/src/lib-sieve/ext-reject.c b/src/lib-sieve/ext-reject.c
index 77b442a05..26ee60db2 100644
--- a/src/lib-sieve/ext-reject.c
+++ b/src/lib-sieve/ext-reject.c
@@ -271,16 +271,16 @@ int act_reject_check_conflict
 	if ( (act_other->action->flags & SIEVE_ACTFLAG_TRIES_DELIVER) > 0 ) {
 		if ( !act_other->executed ) {
 			sieve_runtime_error(renv, act->location, 
-			"reject action conflicts with other action: "
-			"the %s action (%s) tries to deliver the message",
-			act_other->action->name, act_other->location);	
+				"reject action conflicts with other action: "
+				"the %s action (%s) tries to deliver the message",
+				act_other->action->name, act_other->location);	
 			return -1;
 		}
-		
-		return 1;
 	}
 
 	if ( (act_other->action->flags & SIEVE_ACTFLAG_SENDS_RESPONSE) > 0 ) {
+		struct act_reject_context *rj_ctx;
+
 		if ( !act_other->executed ) {
 			sieve_runtime_error(renv, act->location, 
 				"reject action conflicts with other action: "
@@ -288,10 +288,11 @@ int act_reject_check_conflict
 				act_other->action->name, act_other->location);	
 			return -1;
 		}
-		
-		return 1;
+
+		rj_ctx = (struct act_reject_context *) act->context;
+		rj_ctx->reason = NULL;
 	}
-	
+
 	return 0;
 }
  
@@ -299,10 +300,14 @@ static void act_reject_print
 (const struct sieve_action *action ATTR_UNUSED, 
 	const struct sieve_result_print_env *rpenv, void *context, bool *keep)	
 {
-	struct act_reject_context *ctx = (struct act_reject_context *) context;
+	struct act_reject_context *rj_ctx = (struct act_reject_context *) context;
 	
-	sieve_result_action_printf(rpenv, "reject message with reason: %s", 
-		str_sanitize(ctx->reason, 128));
+	if ( rj_ctx->reason != NULL ) {
+		sieve_result_action_printf(rpenv, "reject message with reason: %s", 
+			str_sanitize(rj_ctx->reason, 128));
+	} else {
+		sieve_result_action_printf(rpenv, "reject message without sending a response (discard)"); 		
+	}
 	
 	*keep = FALSE;
 }
@@ -420,8 +425,15 @@ static bool act_reject_commit
 	const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep)
 {
 	const struct sieve_message_data *msgdata = aenv->msgdata;
-	struct act_reject_context *ctx = (struct act_reject_context *) tr_context;
+	struct act_reject_context *rj_ctx = (struct act_reject_context *) tr_context;
 	
+	if ( rj_ctx->reason == NULL ) {
+		sieve_result_log(aenv, "discarded reject (would cause second response to sender)");
+    
+		*keep = FALSE;
+		return TRUE;
+	}
+
 	if ( msgdata->return_path == NULL || *(msgdata->return_path) == '\0' ) {
 		sieve_result_log(aenv, "discarded reject to <>");
     
@@ -429,7 +441,7 @@ static bool act_reject_commit
 		return TRUE;
 	}
 		
-	if ( act_reject_send(aenv, ctx) ) {
+	if ( act_reject_send(aenv, rj_ctx) ) {
 		sieve_result_log(aenv, "rejected message from <%s>",
 			str_sanitize(msgdata->return_path, 80));	
 
diff --git a/src/lib-sieve/plugins/vacation/cmd-vacation.c b/src/lib-sieve/plugins/vacation/cmd-vacation.c
index 9818421c2..9ffdf5d38 100644
--- a/src/lib-sieve/plugins/vacation/cmd-vacation.c
+++ b/src/lib-sieve/plugins/vacation/cmd-vacation.c
@@ -702,7 +702,7 @@ int act_vacation_check_conflict
 	const struct sieve_action_data *act_other)
 {
 	if ( (act_other->action->flags & SIEVE_ACTFLAG_SENDS_RESPONSE) > 0 ) {
-		if ( !act_other->executed ) {
+		if ( !act_other->executed && !act->executed) {
 			sieve_runtime_error(renv, act->location, 
 				"vacation action conflicts with other action: "
 				"the %s action (%s) also sends a response back to the sender",	
diff --git a/src/lib-sieve/sieve-result.c b/src/lib-sieve/sieve-result.c
index 4f22c853a..6a742d844 100644
--- a/src/lib-sieve/sieve-result.c
+++ b/src/lib-sieve/sieve-result.c
@@ -410,7 +410,7 @@ static int _sieve_result_add_action
 				(ret=action->check_conflict(renv, &act_data, &raction->data)) != 0 ) 
 				return ret;
 			
-			if ( oact->check_conflict != NULL &&
+			if ( !raction->data.executed && oact->check_conflict != NULL &&
 				(ret=oact->check_conflict(renv, &raction->data, &act_data)) != 0 )
 				return ret;
 		}
@@ -437,13 +437,13 @@ static int _sieve_result_add_action
 	else {
 		/* Create new action object */
 		raction = p_new(result->pool, struct sieve_result_action, 1);
+		raction->data.executed = FALSE;
 	}
 	
 	raction->result = result;
 	raction->data.context =	context;
 	raction->data.action = action;
 	raction->data.location = p_strdup(result->pool, act_data.location);
-	raction->data.executed = FALSE;
 	raction->tr_context = NULL;
 	raction->success = FALSE;
 	raction->keep = keep;
@@ -580,7 +580,10 @@ bool sieve_result_print
 {
 	struct sieve_result_print_env penv;
 	bool implicit_keep = TRUE;
-	struct sieve_result_action *rac;
+	struct sieve_result_action *rac, *first_action;
+	
+	first_action = ( result->last_attempted_action == NULL ?
+		result->first_action : result->last_attempted_action->next );
 	
 	if ( keep != NULL ) *keep = FALSE;
 	
@@ -591,41 +594,46 @@ bool sieve_result_print
 	penv.scriptenv = senv;
 	
 	sieve_result_printf(&penv, "\nPerformed actions:\n\n");
-	rac = result->first_action;
-	while ( rac != NULL ) {		
-		bool impl_keep = TRUE;
-		struct sieve_result_side_effect *rsef;
-		const struct sieve_side_effect *sef;
-		const struct sieve_action *act = rac->data.action;
+	
+	if ( first_action == NULL ) {
+		sieve_result_printf(&penv, "  (none)\n");
+	} else {		
+		rac = first_action;
+		while ( rac != NULL ) {		
+			bool impl_keep = TRUE;
+			struct sieve_result_side_effect *rsef;
+			const struct sieve_side_effect *sef;
+			const struct sieve_action *act = rac->data.action;
 
-		if ( rac->keep && keep != NULL ) *keep = TRUE;
+			if ( rac->keep && keep != NULL ) *keep = TRUE;
 
-		if ( act != NULL ) {
-			if ( act->print != NULL )
-				act->print(act, &penv, rac->data.context, &impl_keep);
-			else
-				sieve_result_action_printf(&penv, act->name); 
-		} else {
-			if ( rac->keep ) {
-				sieve_result_action_printf(&penv, "keep");
-				keep = FALSE;
+			if ( act != NULL ) {
+				if ( act->print != NULL )
+					act->print(act, &penv, rac->data.context, &impl_keep);
+				else
+					sieve_result_action_printf(&penv, act->name); 
 			} else {
-				sieve_result_action_printf(&penv, "[NULL]");
+				if ( rac->keep ) {
+					sieve_result_action_printf(&penv, "keep");
+					keep = FALSE;
+				} else {
+					sieve_result_action_printf(&penv, "[NULL]");
+				}
 			}
-		}
 	
-		/* Print side effects */
-		rsef = rac->seffects != NULL ? rac->seffects->first_effect : NULL;
-		while ( rsef != NULL ) {
-			sef = rsef->seffect;
-			if ( sef->print != NULL ) 
-				sef->print(sef, rac->data.action, &penv, rsef->context, &impl_keep);
-			rsef = rsef->next;
-		}
+			/* Print side effects */
+			rsef = rac->seffects != NULL ? rac->seffects->first_effect : NULL;
+			while ( rsef != NULL ) {
+				sef = rsef->seffect;
+				if ( sef->print != NULL ) 
+					sef->print(sef, rac->data.action, &penv, rsef->context, &impl_keep);
+				rsef = rsef->next;
+			}
 			
-		implicit_keep = implicit_keep && impl_keep;		
+			implicit_keep = implicit_keep && impl_keep;		
 		
-		rac = rac->next;	
+			rac = rac->next;	
+		}
 	}
 	
 	if ( implicit_keep && keep != NULL ) *keep = TRUE;
@@ -741,7 +749,7 @@ void sieve_result_mark_executed(struct sieve_result *result)
 	struct sieve_result_action *first_action, *rac;
 	
 	first_action = ( result->last_attempted_action == NULL ?
-		result->first_action : result->last_attempted_action );
+		result->first_action : result->last_attempted_action->next );
 	result->last_attempted_action = result->last_action;
 
 	rac = first_action;
@@ -774,7 +782,7 @@ int sieve_result_execute
 	/* Make notice of this attempt */
 	
 	first_action = ( result->last_attempted_action == NULL ?
-		result->first_action : result->last_attempted_action );
+		result->first_action : result->last_attempted_action->next );
 	result->last_attempted_action = result->last_action;
 		
 	/* 
@@ -785,8 +793,11 @@ int sieve_result_execute
 	while ( success && rac != NULL ) {
 		const struct sieve_action *act = rac->data.action;
 	
-		/* Skip non-action (inactive keep) */
-		if ( act == NULL ) continue;
+		/* Skip non-actions (inactive keep) and executed ones */
+		if ( act == NULL || rac->data.executed ) {
+			rac = rac->next;	
+			continue;
+		}
 	
 		if ( act->start != NULL ) {
 			rac->success = act->start(act, &result->action_env, rac->data.context, 
@@ -810,9 +821,12 @@ int sieve_result_execute
 		struct sieve_result_side_effect *rsef;
 		const struct sieve_side_effect *sef;
 		
-		/* Skip non-action (inactive keep) */
-		if ( act == NULL ) continue;
-		
+		/* Skip non-actions (inactive keep) and executed ones */
+		if ( act == NULL || rac->data.executed ) {
+			rac = rac->next;	
+			continue;
+		}
+				
 		/* Execute pre-execute event of side effects */
 		rsef = rac->seffects != NULL ? rac->seffects->first_effect : NULL;
 		while ( success && rsef != NULL ) {
@@ -856,12 +870,22 @@ int sieve_result_execute
 		if ( success ) {
 			bool impl_keep = TRUE;
 			
-			rac->data.executed = TRUE;
 			if ( rac->keep && keep != NULL ) *keep = TRUE;
+
+			/* Skip executed actions */
+			if ( rac->data.executed ) {
+				rac = rac->next;	
+				continue;
+			}
+			
+			rac->data.executed = TRUE;
+			
+			/* Skip non-actions (inactive keep) */
+			if ( act == NULL ) {
+				rac = rac->next;	
+				continue;
+			}
 			
-			/* Skip non-action (inactive keep) */
-			if ( act == NULL ) continue;
-		
 			if ( act->commit != NULL ) 
 				commit_ok = act->commit
 					(act, &result->action_env, rac->tr_context, &impl_keep) && commit_ok;
@@ -879,8 +903,11 @@ int sieve_result_execute
 			
 			implicit_keep = implicit_keep && impl_keep;
 		} else {
-			/* Skip non-action (inactive keep) */
-			if ( act == NULL ) continue;
+			/* Skip non-actions (inactive keep) and executed ones */
+			if ( act == NULL || rac->data.executed ) {
+				rac = rac->next;	
+				continue;
+			}
 		
 			if ( act->rollback != NULL ) 
 				act->rollback(act, &result->action_env, rac->tr_context, rac->success);
diff --git a/src/lib-sieve/sieve.c b/src/lib-sieve/sieve.c
index 31b64c014..ffdd99736 100644
--- a/src/lib-sieve/sieve.c
+++ b/src/lib-sieve/sieve.c
@@ -343,6 +343,7 @@ struct sieve_multiscript {
 	const struct sieve_script_env *scriptenv;
 	struct sieve_error_handler *ehandler;
 	int status;
+	bool active;
 };
  
 struct sieve_multiscript *sieve_multiscript_start
@@ -362,56 +363,82 @@ struct sieve_multiscript *sieve_multiscript_start
 	mscript->scriptenv = senv;
 	mscript->ehandler = ehandler;
 	mscript->status = SIEVE_EXEC_OK;
+	mscript->active = TRUE;
 	
 	return mscript;
 }
 
-int sieve_multiscript_test
+bool sieve_multiscript_test
 (struct sieve_multiscript *mscript, struct sieve_binary *sbin, 
 	struct ostream *stream)
-{	
+{		
+	if ( !mscript->active ) return FALSE;
+
 	/* Run the script */
 	mscript->status = sieve_run(sbin, &mscript->result, mscript->msgdata, 
 		mscript->scriptenv, mscript->ehandler);
 				
 	/* Print result if successful */
-	if ( mscript->status > 0 ) 
+	if ( mscript->status > 0 ) {
+		bool keep = FALSE;
+
 		mscript->status = sieve_result_print
-			(mscript->result, mscript->scriptenv, stream, NULL);
+			(mscript->result, mscript->scriptenv, stream, &keep);
+			
+		mscript->active = ( mscript->active && keep );
+	}
 	
-	return mscript->status;
+	if ( mscript->status <= 0 ) {
+		mscript->active = FALSE;
+		return FALSE;
+	}
+	
+	sieve_result_mark_executed(mscript->result);
+	
+	return mscript->active;
 }
 
-int sieve_multiscript_execute
+bool sieve_multiscript_execute
 (struct sieve_multiscript *mscript, struct sieve_binary *sbin)
 {
-	if ( mscript->status <= 0 )
-		return mscript->status;
+	if ( !mscript->active ) return FALSE;
 
 	/* Run the script */
 	mscript->status = sieve_run(sbin, &mscript->result, mscript->msgdata, 
 		mscript->scriptenv, mscript->ehandler);
 		
 	if ( mscript->status >= 0 ) {
+		bool keep = FALSE;
+		
 		if ( mscript->status > 0 )
 			mscript->status = sieve_result_execute
-				(mscript->result, mscript->msgdata, mscript->scriptenv, NULL);
+				(mscript->result, mscript->msgdata, mscript->scriptenv, &keep);
 		else {
 			if ( !sieve_result_implicit_keep
 				(mscript->result, mscript->msgdata, mscript->scriptenv) )
 				mscript->status = SIEVE_EXEC_KEEP_FAILED;
 		}
+		
+		mscript->active = ( mscript->active && keep );
 	}
 
-	return mscript->status;
+	if ( mscript->status <= 0 ) {
+		mscript->active = FALSE;
+		return FALSE;
+	}
+	
+	return mscript->active;
 }
 
-void sieve_multiscript_finish(struct sieve_multiscript **mscript)
+int sieve_multiscript_finish(struct sieve_multiscript **mscript)
 {
 	struct sieve_result *result = (*mscript)->result;
-	 
+	int ret = (*mscript)->status;
+	
 	/* Cleanup */
 	sieve_result_unref(&result);
 	*mscript = NULL;
+	
+	return ret;
 }	
 
diff --git a/src/lib-sieve/sieve.h b/src/lib-sieve/sieve.h
index 30f57164b..3c5a739dd 100644
--- a/src/lib-sieve/sieve.h
+++ b/src/lib-sieve/sieve.h
@@ -123,12 +123,12 @@ struct sieve_multiscript *sieve_multiscript_start
 	(const struct sieve_message_data *msgdata, const struct sieve_script_env *senv,
 		struct sieve_error_handler *ehandler);
 		
-int sieve_multiscript_test
+bool sieve_multiscript_test
 	(struct sieve_multiscript *mscript, struct sieve_binary *sbin, 
 		struct ostream *stream);
-int sieve_multiscript_execute
+bool sieve_multiscript_execute
 	(struct sieve_multiscript *mscript, struct sieve_binary *sbin);
 	
-void sieve_multiscript_finish(struct sieve_multiscript **mscript);
+int sieve_multiscript_finish(struct sieve_multiscript **mscript);
 
 #endif
diff --git a/src/sieve-tools/sieve-test.c b/src/sieve-tools/sieve-test.c
index 6cfd40ad3..845c4ffa5 100644
--- a/src/sieve-tools/sieve-test.c
+++ b/src/sieve-tools/sieve-test.c
@@ -207,7 +207,56 @@ int main(int argc, char **argv)
 			(void) unlink(sieve_binary_path(main_sbin));		
 		}
 	} else {
-	
+		struct sieve_binary *sbin;
+		const char *const *sfiles;
+		unsigned int i, count;
+		struct sieve_multiscript *mscript = sieve_multiscript_start
+			(&msgdata, &scriptenv, ehandler);
+		int result = 1; 
+		
+		sfiles = array_get(&scriptfiles, &count); 
+		for ( i = 0; i < count && result > 0; i++ ) {
+			o_stream_send_str(teststream, 
+				t_strdup_printf("\n## Executing script: %s\n", sfiles[i]));
+		
+			/* Compile sieve script */
+			if ( force_compile ) {
+				sbin = sieve_tool_script_compile(sfiles[i]);
+				(void) sieve_save(sbin, NULL);
+			} else {
+				sbin = sieve_tool_script_open(sfiles[i]);
+			}
+			
+			if ( sbin == NULL ) {
+				result = -1;
+				break;
+			}
+
+			/* Dump script */
+			sieve_tool_dump_binary_to(sbin, dumpfile);
+			
+			/* Test script */
+			result = ( sieve_multiscript_test(mscript, sbin, teststream) ? 1 : 0 );
+			
+			/* Close script */
+			sieve_close(&sbin);
+		}
+		
+		if ( result > 0 )	{
+			o_stream_send_str(teststream, 
+				t_strdup_printf("\n## Executing script: %s\n", scriptfile));
+				
+			sbin = main_sbin;
+			(void)sieve_multiscript_test(mscript, main_sbin, teststream);
+			ret = sieve_multiscript_finish(&mscript);
+		} else {
+			ret = SIEVE_EXEC_FAILURE;
+		}
+		
+		if ( ret == SIEVE_EXEC_BIN_CORRUPT ) {
+			i_info("Corrupt binary deleted.");
+			(void) unlink(sieve_binary_path(sbin));		
+		}
 	}
 
 	o_stream_destroy(&teststream);
-- 
GitLab