From d3d9775297186292668da722a6b5e6b0ea8b4119 Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan@rename-it.nl>
Date: Fri, 25 Jul 2008 19:17:20 +0200
Subject: [PATCH] Implemented graceful handling of corrupt binaries by the
 sieve lda plugin.

---
 src/lib-sieve/sieve-binary.c             | 14 +++++++
 src/lib-sieve/sieve-interpreter.c        |  4 +-
 src/plugins/lda-sieve/lda-sieve-plugin.c | 49 ++++++++++++++++++++++--
 src/sieve-bin/sieve-exec.c               | 11 ++++--
 src/sieve-bin/sieve-test.c               | 10 ++++-
 5 files changed, 78 insertions(+), 10 deletions(-)

diff --git a/src/lib-sieve/sieve-binary.c b/src/lib-sieve/sieve-binary.c
index 9236beb41..cf7cf4658 100644
--- a/src/lib-sieve/sieve-binary.c
+++ b/src/lib-sieve/sieve-binary.c
@@ -604,6 +604,16 @@ bool sieve_binary_save
 	struct ostream *stream;
 	int fd;
 	
+	/* Use default path if none is specified */
+	if ( path == NULL ) {
+		if ( sbin->script == NULL ) {
+			sieve_sys_error("cannot determine default binary save path "
+				"with missing script object");
+        	return FALSE;
+		}
+		path = sieve_script_binpath(sbin->script);
+	}
+
 	/* Open it as temp file first, as not to overwrite an existing just yet */
 	temp_path = t_strconcat(path, ".tmp", NULL);
 	fd = open(temp_path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
@@ -629,6 +639,10 @@ bool sieve_binary_save
 	if ( !result ) {
 		/* Get rid of temp output (if any) */
 		(void) unlink(temp_path);
+	} else {
+		if ( sbin->path == NULL || strcmp(sbin->path, path) != 0 ) {
+			sbin->path = p_strdup(sbin->pool, path);
+		}
 	}
 	
 	return result;
diff --git a/src/lib-sieve/sieve-interpreter.c b/src/lib-sieve/sieve-interpreter.c
index bbe112d38..edb5adc6d 100644
--- a/src/lib-sieve/sieve-interpreter.c
+++ b/src/lib-sieve/sieve-interpreter.c
@@ -199,10 +199,10 @@ void _sieve_runtime_trace_error
 	va_list args;
 
 	va_start(args, fmt);
-	str_printfa(outbuf, "%08x: %s: ", runenv->interp->pc
+	str_printfa(outbuf, "%08x: [[ERROR: %s: ", runenv->interp->pc
 		, runenv->interp->current_op->mnemonic);
 	str_vprintfa(outbuf, fmt, args);
-    str_append_c(outbuf, '\n');
+    str_append(outbuf, "]]\n");
 	va_end(args);
 
 	o_stream_send(runenv->trace_stream, str_data(outbuf), str_len(outbuf));
diff --git a/src/plugins/lda-sieve/lda-sieve-plugin.c b/src/plugins/lda-sieve/lda-sieve-plugin.c
index 6b8be27f4..98587a77d 100644
--- a/src/plugins/lda-sieve/lda-sieve-plugin.c
+++ b/src/plugins/lda-sieve/lda-sieve-plugin.c
@@ -136,12 +136,55 @@ static int lda_sieve_run
 	/* Execute the script */	
 	
 	if ( debug )
-		sieve_sys_info("executing (in-memory) script %s", script_path);
+		sieve_sys_info("executing compiled script %s", script_path);
 
 	ret = sieve_execute(sbin, &msgdata, &scriptenv, ehandler, NULL);
 
-	if ( ret < 0 )
-		sieve_sys_error("failed to execute script %s", script_path);
+	if ( ret == SIEVE_EXEC_BIN_CORRUPT ) {
+		sieve_sys_warning("encountered corrupt binary: recompiling script %s", 
+			script_path);
+
+		/* 
+		 * Try again; possibly author forgot to increase binary version number 
+		 */
+
+		/* Close corrupt script */
+		sieve_close(&sbin);
+
+		/* Recompile */	
+	
+		sieve_error_handler_copy_masterlog(ehandler, FALSE);
+	
+		if ( (sbin=sieve_compile(script_path, ehandler)) == NULL ) {
+        	sieve_sys_error("failed to compile script %s; "
+            	"log should be available as %s", script_path, scriptlog);
+
+	        sieve_error_handler_unref(&ehandler);
+    	    return -1;
+    	}
+
+		sieve_error_handler_copy_masterlog(ehandler, TRUE);
+
+		/* Execute again */
+	
+		ret = sieve_execute(sbin, &msgdata, &scriptenv, ehandler, NULL);
+	}
+
+	switch ( ret ) {
+	case SIEVE_EXEC_FAILURE:
+		sieve_sys_error("execution of script %s failed, but implicit keep was successful", 
+			script_path);
+		break;
+	case SIEVE_EXEC_BIN_CORRUPT:		
+   	    sieve_sys_error("!!BUG!!: binary compiled from %s is still corrupt; bailing out", 
+			script_path);
+		break;
+	case SIEVE_EXEC_KEEP_FAILED:
+		sieve_sys_error("script %s failed with unsuccessful implicit keep", script_path);
+        break;
+	default:
+		break;
+	}
 
 	/* Clean up */
 	sieve_close(&sbin);
diff --git a/src/sieve-bin/sieve-exec.c b/src/sieve-bin/sieve-exec.c
index 9c0bcf5b8..3eebaa6c0 100644
--- a/src/sieve-bin/sieve-exec.c
+++ b/src/sieve-bin/sieve-exec.c
@@ -10,6 +10,7 @@
 #include "mail-raw.h"
 #include "namespaces.h"
 #include "sieve.h"
+#include "sieve-binary.h"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -191,17 +192,19 @@ int main(int argc, char **argv)
 	/* Run */
 	switch ( sieve_execute(sbin, &msgdata, &scriptenv, ehandler, NULL) ) {
 	case SIEVE_EXEC_OK:
-		i_info("Final result: success\n");
+		i_info("Final result: success");
 		break;
 	case SIEVE_EXEC_FAILURE:
-		i_info("Final result: failed; resolved with successful implicit keep\n");
+		i_info("Final result: failed; resolved with successful implicit keep");
 		break;
 	case SIEVE_EXEC_BIN_CORRUPT:
+		i_info("Corrupt binary deleted.");
+		(void) unlink(sieve_binary_path(sbin));
 	case SIEVE_EXEC_KEEP_FAILED:
-		i_info("Final result: utter failure (caller please handle implicit keep!)\n");
+		i_info("Final result: utter failure (caller please handle implicit keep!)");
 		break;
 	default:
-		i_info("Final result: unrecognized return value?!\n");	
+		i_info("Final result: unrecognized return value?!");	
 	}
 
 	sieve_close(&sbin);
diff --git a/src/sieve-bin/sieve-test.c b/src/sieve-bin/sieve-test.c
index dd5bec3b2..d5b2be8d2 100644
--- a/src/sieve-bin/sieve-test.c
+++ b/src/sieve-bin/sieve-test.c
@@ -8,6 +8,7 @@
 #include "mail-raw.h"
 #include "namespaces.h"
 #include "sieve.h"
+#include "sieve-binary.h"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -45,6 +46,7 @@ int main(int argc, char **argv)
 	struct sieve_error_handler *ehandler;
 	struct ostream *teststream;
 	bool force_compile = FALSE;
+	int ret;
 
 #ifdef SIEVE_RUNTIME_TRACE
 	bool trace = FALSE;
@@ -114,6 +116,7 @@ int main(int argc, char **argv)
 	/* Compile sieve script */
 	if ( force_compile ) {
 		sbin = bin_compile_sieve_script(scriptfile);
+		(void) sieve_save(sbin, NULL);
 	} else {
 		sbin = bin_open_sieve_script(scriptfile);
 	}
@@ -156,9 +159,14 @@ int main(int argc, char **argv)
 #endif
 
 	/* Run the test */
-	(void) sieve_test
+	ret = sieve_test
 		(sbin, &msgdata, &scriptenv, teststream, ehandler, trace_stream);
 
+	if ( ret == SIEVE_EXEC_BIN_CORRUPT ) {
+		i_info("Corrupt binary deleted.");
+		(void) unlink(sieve_binary_path(sbin));		
+	}
+
 	o_stream_destroy(&teststream);
 
 	sieve_close(&sbin);
-- 
GitLab