diff --git a/.hgignore b/.hgignore
index 92b1d2a9bc1188229854dc1d96c5f7ced982aa0a..534f802d959e6ce138f7a60fa79050b339f29ebd 100644
--- a/.hgignore
+++ b/.hgignore
@@ -37,6 +37,7 @@ Makefile.in
 *~
 *.o
 *.lo
+*.loT
 *.Po
 *.Plo
 *.a
diff --git a/src/Makefile.am b/src/Makefile.am
index de5f667f3e79e587642927521b2ddeb745ee65ee..e3940c66446bf9e83d65392116a8520794ae7bc0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1 +1 @@
-SUBDIRS = lib-sieve sieve-bin plugins
+SUBDIRS = lib-sieve sieve-bin plugins testsuite
diff --git a/src/lib-sieve/Makefile.am b/src/lib-sieve/Makefile.am
index 2cf105300ccbfe4fdabc3145dbc10ae4b70b7bfe..2dcf2ccb0abd8819aaaf44c2688074fab43dcec6 100644
--- a/src/lib-sieve/Makefile.am
+++ b/src/lib-sieve/Makefile.am
@@ -61,6 +61,7 @@ libsieve_la_SOURCES = \
 	sieve-generator.c \
 	sieve-interpreter.c \
 	sieve-code-dumper.c \
+	sieve-binary-dumper.c \
 	sieve-result.c \
 	sieve-error.c \
 	sieve-comparators.c \
@@ -87,6 +88,7 @@ noinst_HEADERS = \
 	sieve-generator.h \
 	sieve-interpreter.h \
 	sieve-code-dumper.h \
+	sieve-binary-dumper.h \
 	sieve-result.h \
 	sieve-error.h \
 	sieve-error-private.h \
diff --git a/src/lib-sieve/ext-reject.c b/src/lib-sieve/ext-reject.c
index 87d29086f76623a55e2256cc6b86fc996ae86768..eaf7cae8b1bbcde5ffabb5ce74a0371b5a87e017 100644
--- a/src/lib-sieve/ext-reject.c
+++ b/src/lib-sieve/ext-reject.c
@@ -24,7 +24,7 @@
 #include "sieve-generator.h"
 #include "sieve-binary.h"
 #include "sieve-interpreter.h"
-#include "sieve-code-dumper.h"
+#include "sieve-dump.h"
 #include "sieve-result.h"
 
 /* Forward declarations */
diff --git a/src/lib-sieve/plugins/body/ext-body.c b/src/lib-sieve/plugins/body/ext-body.c
index 0a3218d5033b10a25fda5d6a9fff55d3d79a4480..1aad477c828a450b2237194f4202fa428e4b6ec4 100644
--- a/src/lib-sieve/plugins/body/ext-body.c
+++ b/src/lib-sieve/plugins/body/ext-body.c
@@ -37,7 +37,7 @@
 #include "sieve-generator.h"
 #include "sieve-binary.h"
 #include "sieve-interpreter.h"
-#include "sieve-code-dumper.h"
+#include "sieve-dump.h"
 
 #include "ext-body-common.h"
 
diff --git a/src/lib-sieve/plugins/body/tst-body.c b/src/lib-sieve/plugins/body/tst-body.c
index 03fa540d154fafbf3541db2f32d14c70cae97434..60f180cfe517dad983e93badbbfcd5794c5b0476 100644
--- a/src/lib-sieve/plugins/body/tst-body.c
+++ b/src/lib-sieve/plugins/body/tst-body.c
@@ -9,7 +9,7 @@
 #include "sieve-generator.h"
 #include "sieve-binary.h"
 #include "sieve-interpreter.h"
-#include "sieve-code-dumper.h"
+#include "sieve-dump.h"
 
 #include "ext-body-common.h"
 
diff --git a/src/lib-sieve/plugins/imapflags/ext-imapflags-common.c b/src/lib-sieve/plugins/imapflags/ext-imapflags-common.c
index 10335f2ad6b54a2025b8df18f65b0f6536b265e7..4c203b9247d14320278469a192ddfbce0dd8b0fc 100644
--- a/src/lib-sieve/plugins/imapflags/ext-imapflags-common.c
+++ b/src/lib-sieve/plugins/imapflags/ext-imapflags-common.c
@@ -5,7 +5,7 @@
 #include "sieve-validator.h" 
 #include "sieve-generator.h"
 #include "sieve-interpreter.h"
-#include "sieve-code-dumper.h"
+#include "sieve-dump.h"
 
 #include "ext-imapflags-common.h"
 
diff --git a/src/lib-sieve/plugins/imapflags/imapflags.sieve b/src/lib-sieve/plugins/imapflags/imapflags.sieve
index 44a39ec7c7a05336ee6c793e1a017aa64ffe2fb7..e33b13ebe6603738c6a6a5338e8993a02e7dbf0f 100644
--- a/src/lib-sieve/plugins/imapflags/imapflags.sieve
+++ b/src/lib-sieve/plugins/imapflags/imapflags.sieve
@@ -25,4 +25,4 @@ if hasflag :count "ge" :comparator "i;ascii-numeric" "2" {
 	fileinto "imap-twoflags";
 }
 
-fileinto :flags "\\Seen $MDNRequired \\Draft" "INBOX";
+fileinto :flags "\\Seen MDNRequired \\Draft" "INBOX";
diff --git a/src/lib-sieve/plugins/imapflags/tag-flags.c b/src/lib-sieve/plugins/imapflags/tag-flags.c
index ff716cc6e5003232a6d6d7576106fc737f5d7eb8..9489d5e61b07c9f0ff8a4542e926e77d50738df0 100644
--- a/src/lib-sieve/plugins/imapflags/tag-flags.c
+++ b/src/lib-sieve/plugins/imapflags/tag-flags.c
@@ -234,20 +234,25 @@ static void seff_flags_print
 static bool seff_flags_post_execute
 (const struct sieve_side_effect *seffect ATTR_UNUSED, 
 	const struct sieve_action *action ATTR_UNUSED, 
-	const struct sieve_action_exec_env *aenv ATTR_UNUSED, 
+	const struct sieve_action_exec_env *aenv, 
 	void *se_context, void *tr_context)
 {	
 	struct seff_flags_context *ctx = (struct seff_flags_context *) se_context;
 	struct act_store_transaction *trans = 
 		(struct act_store_transaction *) tr_context;
+	struct mail_keywords *keywords;
 
 	if ( trans->dest_mail == NULL ) return TRUE;
 
-	printf("SETTING FLAGS\n");
+ 	if (mailbox_keywords_create(trans->box, ctx->keywords, &keywords) < 0) {
+		sieve_result_error(aenv, "invalid keywords");
+		return FALSE;
+	}
+
 	/* Update message flags. */
 	mail_update_flags(trans->dest_mail, MODIFY_ADD, ctx->flags);
 	/* Update message keywords. */
-	//mail_update_keywords(trans->dest_mail, MODIFY_REPLACE, keywords);
+	mail_update_keywords(trans->dest_mail, MODIFY_ADD, keywords);
 	
 	return TRUE;
 }
diff --git a/src/lib-sieve/plugins/imapflags/tst-hasflag.c b/src/lib-sieve/plugins/imapflags/tst-hasflag.c
index 152e9ae74920dd7bb470831c4eac0a76e56bc682..97d651c4b4f69c451a1960ddc595ecc0e484c039 100644
--- a/src/lib-sieve/plugins/imapflags/tst-hasflag.c
+++ b/src/lib-sieve/plugins/imapflags/tst-hasflag.c
@@ -7,7 +7,7 @@
 #include "sieve-validator.h" 
 #include "sieve-generator.h"
 #include "sieve-interpreter.h"
-#include "sieve-code-dumper.h"
+#include "sieve-dump.h"
 
 #include "ext-imapflags-common.h"
 
diff --git a/src/lib-sieve/plugins/include/cmd-include.c b/src/lib-sieve/plugins/include/cmd-include.c
index c9eca4896768de8b8fe0ad091592d8e9b7e49f01..3a373e1ffbe69b68ae107a8869601f49bb8e4041 100644
--- a/src/lib-sieve/plugins/include/cmd-include.c
+++ b/src/lib-sieve/plugins/include/cmd-include.c
@@ -10,7 +10,7 @@
 #include "sieve-binary.h"
 #include "sieve-generator.h"
 #include "sieve-interpreter.h"
-#include "sieve-code-dumper.h"
+#include "sieve-dump.h"
 
 #include "ext-include-common.h"
 
diff --git a/src/lib-sieve/plugins/vacation/ext-vacation.c b/src/lib-sieve/plugins/vacation/ext-vacation.c
index c54c82c009385d5628701d9b3a714ec1e47c4f4b..a3336a4e457101e1c89430ee8f3c1aa9e304103e 100644
--- a/src/lib-sieve/plugins/vacation/ext-vacation.c
+++ b/src/lib-sieve/plugins/vacation/ext-vacation.c
@@ -26,7 +26,7 @@
 #include "sieve-validator.h"
 #include "sieve-generator.h"
 #include "sieve-interpreter.h"
-#include "sieve-code-dumper.h"
+#include "sieve-dump.h"
 #include "sieve-result.h"
 
 #include <stdio.h>
diff --git a/src/lib-sieve/plugins/variables/cmd-set.c b/src/lib-sieve/plugins/variables/cmd-set.c
index e4180228b0a1233f692d7e96390847ecb55be9b4..0dd06f7b5f894e28491ca61e2100989d4039b09a 100644
--- a/src/lib-sieve/plugins/variables/cmd-set.c
+++ b/src/lib-sieve/plugins/variables/cmd-set.c
@@ -13,7 +13,7 @@
 #include "sieve-validator.h"
 #include "sieve-generator.h"
 #include "sieve-interpreter.h"
-#include "sieve-code-dumper.h"
+#include "sieve-dump.h"
 
 #include "ext-variables-common.h"
 
diff --git a/src/lib-sieve/plugins/variables/ext-variables-arguments.c b/src/lib-sieve/plugins/variables/ext-variables-arguments.c
index 0e59920e989414ad7ef3ca2667e37685cef13186..672fdf7f47fd6d46518b41d906bdf589113c4d6b 100644
--- a/src/lib-sieve/plugins/variables/ext-variables-arguments.c
+++ b/src/lib-sieve/plugins/variables/ext-variables-arguments.c
@@ -9,7 +9,7 @@
 #include "sieve-commands.h"
 #include "sieve-validator.h"
 #include "sieve-generator.h"
-#include "sieve-code-dumper.h"
+#include "sieve-dump.h"
 
 #include "ext-variables-common.h"
 #include "ext-variables-name.h"
diff --git a/src/lib-sieve/plugins/variables/ext-variables-common.c b/src/lib-sieve/plugins/variables/ext-variables-common.c
index 96abf265c6a1095468d2a1b38f399e3255472631..35530a0c5044f1f737405295fc882c6991e8daec 100644
--- a/src/lib-sieve/plugins/variables/ext-variables-common.c
+++ b/src/lib-sieve/plugins/variables/ext-variables-common.c
@@ -13,7 +13,7 @@
 #include "sieve-commands.h"
 #include "sieve-validator.h"
 #include "sieve-generator.h"
-#include "sieve-code-dumper.h"
+#include "sieve-dump.h"
 #include "sieve-interpreter.h"
 
 #include "ext-variables-common.h"
diff --git a/src/lib-sieve/plugins/variables/ext-variables-operands.c b/src/lib-sieve/plugins/variables/ext-variables-operands.c
index 31a2e644600a4a61216b5733289c36bb773f7f51..638f8f90ebcd89162c13d026f466443836ce888d 100644
--- a/src/lib-sieve/plugins/variables/ext-variables-operands.c
+++ b/src/lib-sieve/plugins/variables/ext-variables-operands.c
@@ -13,7 +13,7 @@
 #include "sieve-commands.h"
 #include "sieve-validator.h"
 #include "sieve-generator.h"
-#include "sieve-code-dumper.h"
+#include "sieve-dump.h"
 #include "sieve-interpreter.h"
 
 #include "ext-variables-common.h"
diff --git a/src/lib-sieve/plugins/variables/tst-string.c b/src/lib-sieve/plugins/variables/tst-string.c
index 44f0020afddb07bea535060357439c8e70fb7dbb..e14d9e28289bb5f1f7a3a076b776da01a8677862 100644
--- a/src/lib-sieve/plugins/variables/tst-string.c
+++ b/src/lib-sieve/plugins/variables/tst-string.c
@@ -8,7 +8,7 @@
 #include "sieve-validator.h"
 #include "sieve-generator.h"
 #include "sieve-interpreter.h"
-#include "sieve-code-dumper.h"
+#include "sieve-dump.h"
 
 #include "ext-variables-common.h"
 
diff --git a/src/lib-sieve/sieve-actions.c b/src/lib-sieve/sieve-actions.c
index 088265038c4177b3b3ae013bd147512f091f1035..afd48bc8a275d6ee0ec7fb76a916b5f75471d475 100644
--- a/src/lib-sieve/sieve-actions.c
+++ b/src/lib-sieve/sieve-actions.c
@@ -8,7 +8,7 @@
 #include "sieve-extensions-private.h"
 #include "sieve-binary.h"
 #include "sieve-interpreter.h"
-#include "sieve-code-dumper.h"
+#include "sieve-dump.h"
 #include "sieve-result.h"
 #include "sieve-actions.h"
 
@@ -277,8 +277,13 @@ static bool act_store_start
 		
 		box = mailbox_open(ns->storage, ctx->folder, NULL, MAILBOX_OPEN_FAST |
 			MAILBOX_OPEN_KEEP_RECENT);
+	
+		if (box != NULL && mailbox_sync(box, 0, 0, NULL) < 0) {
+			mailbox_close(&box);
+			box = NULL;
+		}
 	}
-					
+				
 	pool = sieve_result_pool(aenv->result);
 	trans = p_new(pool, struct act_store_transaction, 1);
 	trans->context = ctx;
diff --git a/src/lib-sieve/sieve-address-parts.c b/src/lib-sieve/sieve-address-parts.c
index 26e72f7b78730d8584aef63a2d49be39651dd313..3b9dc27331095ca974917a747d62cc28f4ae53d2 100644
--- a/src/lib-sieve/sieve-address-parts.c
+++ b/src/lib-sieve/sieve-address-parts.c
@@ -16,7 +16,7 @@
 #include "sieve-validator.h"
 #include "sieve-generator.h"
 #include "sieve-interpreter.h"
-#include "sieve-code-dumper.h"
+#include "sieve-dump.h"
 
 #include "sieve-address-parts.h"
 
diff --git a/src/lib-sieve/sieve-binary-dumper.c b/src/lib-sieve/sieve-binary-dumper.c
new file mode 100644
index 0000000000000000000000000000000000000000..8b0a0323b0d437f684df192c8164cec816e20a2a
--- /dev/null
+++ b/src/lib-sieve/sieve-binary-dumper.c
@@ -0,0 +1,57 @@
+#include "lib.h"
+
+#include "sieve-common.h"
+#include "sieve-binary.h"
+
+#include "sieve-dump.h"
+
+struct sieve_binary_dumper {
+	pool_t pool;
+	
+	/* Dumptime environment */
+	struct sieve_dumptime_env dumpenv; 
+};
+
+struct sieve_binary_dumper *sieve_binary_dumper_create
+	(struct sieve_binary *sbin) 
+{
+	pool_t pool;
+	struct sieve_binary_dumper *dumper;
+	
+	pool = pool_alloconly_create("sieve_binary_dumper", 4096);	
+	dumper = p_new(pool, struct sieve_binary_dumper, 1);
+	dumper->pool = pool;
+	dumper->dumpenv.dumper = dumper;
+	
+	dumper->dumpenv.sbin = sbin;
+	sieve_binary_ref(sbin);
+	
+	return dumper;
+}
+
+void sieve_binary_dumper_free(struct sieve_binary_dumper **dumper) 
+{
+	sieve_binary_unref(&(*dumper)->dumpenv.sbin);
+	pool_unref(&((*dumper)->pool));
+	
+	*dumper = NULL;
+}
+
+pool_t sieve_binary_dumper_pool(struct sieve_binary_dumper *dumper)
+{
+	return dumper->pool;
+}
+
+/* */
+
+void sieve_binary_dumper_run
+	(struct sieve_binary_dumper *dumper, struct ostream *stream) 
+{	
+	dumper->dumpenv.stream = stream;
+	
+	dumper->dumpenv.cdumper = sieve_code_dumper_create(&(dumper->dumpenv));
+
+	sieve_code_dumper_run(dumper->dumpenv.cdumper);
+		
+	sieve_code_dumper_free(&dumper->dumpenv.cdumper);	
+}
diff --git a/src/lib-sieve/sieve-binary-dumper.h b/src/lib-sieve/sieve-binary-dumper.h
new file mode 100644
index 0000000000000000000000000000000000000000..69e7fc79b0cdd5ad40ea00a3b60533097ca842c5
--- /dev/null
+++ b/src/lib-sieve/sieve-binary-dumper.h
@@ -0,0 +1,22 @@
+#ifndef __SIEVE_BINARY_DUMPER_H
+#define __SIEVE_BINARY_DUMPER_H
+
+#include "sieve-common.h"
+
+struct sieve_binary_dumper;
+
+struct sieve_binary_dumper *sieve_binary_dumper_create
+	(struct sieve_binary *sbin);
+void sieve_binary_dumper_free
+	(struct sieve_binary_dumper **dumper);
+
+pool_t sieve_binary_dumper_pool
+	(struct sieve_binary_dumper *dumper);
+
+/* */
+
+void sieve_binary_dumper_run
+	(struct sieve_binary_dumper *dumper, struct ostream *stream);
+
+
+#endif /* __SIEVE_BINARY_DUMPER_H */
diff --git a/src/lib-sieve/sieve-binary.c b/src/lib-sieve/sieve-binary.c
index b640e821e2b595dda7a3e0d675728f5fb6cb5d3b..949464ecea58b9e1a68b03b4b6329c0683c58975 100644
--- a/src/lib-sieve/sieve-binary.c
+++ b/src/lib-sieve/sieve-binary.c
@@ -57,7 +57,7 @@ static inline int sieve_binary_extension_register
 		struct sieve_binary_extension_reg **reg);
 
 static inline sieve_size_t sieve_binary_emit_dynamic_data
-	(struct sieve_binary *binary, void *data, size_t size);
+	(struct sieve_binary *binary, const void *data, size_t size);
 
 /* 
  * Internal structures
@@ -1367,7 +1367,7 @@ int sieve_binary_extensions_count(struct sieve_binary *sbin)
 /* Low-level emission functions */
 
 inline sieve_size_t sieve_binary_emit_data
-(struct sieve_binary *binary, void *data, sieve_size_t size) 
+(struct sieve_binary *binary, const void *data, sieve_size_t size) 
 {
 	sieve_size_t address = buffer_get_used_size(binary->data);
 	  
@@ -1383,7 +1383,7 @@ inline sieve_size_t sieve_binary_emit_byte
 }
 
 inline void sieve_binary_update_data
-(struct sieve_binary *binary, sieve_size_t address, void *data, 
+(struct sieve_binary *binary, sieve_size_t address, const void *data, 
 	sieve_size_t size) 
 {
 	buffer_write(binary->data, address, data, size);
@@ -1445,7 +1445,7 @@ sieve_size_t sieve_binary_emit_integer
 }
 
 static inline sieve_size_t sieve_binary_emit_dynamic_data
-	(struct sieve_binary *binary, void *data, size_t size)
+	(struct sieve_binary *binary, const void *data, size_t size)
 {
 	sieve_size_t address = sieve_binary_emit_integer(binary, size);
 	(void) sieve_binary_emit_data(binary, data, size);
diff --git a/src/lib-sieve/sieve-binary.h b/src/lib-sieve/sieve-binary.h
index 003ae11e7605ee0f5ecf29aa04722aa73ea1b015..fadf15fbbab9bf0ace2f8b49d1024e0155b0bc99 100644
--- a/src/lib-sieve/sieve-binary.h
+++ b/src/lib-sieve/sieve-binary.h
@@ -88,11 +88,11 @@ int sieve_binary_extensions_count(struct sieve_binary *sbin);
 /* Low-level emission functions */
 
 inline sieve_size_t sieve_binary_emit_data
-	(struct sieve_binary *binary, void *data, sieve_size_t size);
+	(struct sieve_binary *binary, const void *data, sieve_size_t size);
 inline sieve_size_t sieve_binary_emit_byte
 	(struct sieve_binary *binary, unsigned char byte);
 inline void sieve_binary_update_data
-	(struct sieve_binary *binary, sieve_size_t address, void *data, 
+	(struct sieve_binary *binary, sieve_size_t address, const void *data, 
 		sieve_size_t size);
 inline sieve_size_t sieve_binary_get_code_size(struct sieve_binary *binary);
 
diff --git a/src/lib-sieve/sieve-code-dumper.c b/src/lib-sieve/sieve-code-dumper.c
index 64b406fd1f555ba008ddb4ee3ff8c74ed597049d..b4421619306749e17349ea4b3ddd772e1705c221 100644
--- a/src/lib-sieve/sieve-code-dumper.c
+++ b/src/lib-sieve/sieve-code-dumper.c
@@ -15,7 +15,7 @@
 #include "sieve-result.h"
 #include "sieve-comparators.h"
 
-#include "sieve-code-dumper.h"
+#include "sieve-dump.h"
 
 struct sieve_code_dumper {
 	pool_t pool;
@@ -27,11 +27,12 @@ struct sieve_code_dumper {
 	sieve_size_t mark_address;
 	unsigned int indent;
 	
-	/* Runtime environment environment */
-	struct sieve_dumptime_env dumpenv; 
+	/* Dump environment */
+	struct sieve_dumptime_env *dumpenv; 
 };
 
-struct sieve_code_dumper *sieve_code_dumper_create(struct sieve_binary *sbin) 
+struct sieve_code_dumper *sieve_code_dumper_create
+	(struct sieve_dumptime_env *denv) 
 {
 	pool_t pool;
 	struct sieve_code_dumper *dumper;
@@ -39,23 +40,20 @@ struct sieve_code_dumper *sieve_code_dumper_create(struct sieve_binary *sbin)
 	pool = pool_alloconly_create("sieve_code_dumper", 4096);	
 	dumper = p_new(pool, struct sieve_code_dumper, 1);
 	dumper->pool = pool;
-	dumper->dumpenv.dumper = dumper;
-	
-	dumper->dumpenv.sbin = sbin;
-	sieve_binary_ref(sbin);
-	
+	dumper->dumpenv = denv;
 	dumper->pc = 0;
 	
 	return dumper;
 }
 
-void sieve_code_dumper_free(struct sieve_code_dumper *dumper) 
+void sieve_code_dumper_free(struct sieve_code_dumper **dumper) 
 {
-	sieve_binary_unref(&dumper->dumpenv.sbin);
-	pool_unref(&(dumper->pool));
+	pool_unref(&((*dumper)->pool));
+	
+	*dumper = NULL;
 }
 
-inline pool_t sieve_code_dumper_pool(struct sieve_code_dumper *dumper)
+pool_t sieve_code_dumper_pool(struct sieve_code_dumper *dumper)
 {
 	return dumper->pool;
 }
@@ -65,13 +63,14 @@ inline pool_t sieve_code_dumper_pool(struct sieve_code_dumper *dumper)
 void sieve_code_dumpf
 (const struct sieve_dumptime_env *denv, const char *fmt, ...)
 {
-	unsigned tab = denv->dumper->indent;
+	struct sieve_code_dumper *cdumper = denv->cdumper;	
+	unsigned tab = cdumper->indent;
 	 
 	string_t *outbuf = t_str_new(128);
 	va_list args;
 	
 	va_start(args, fmt);	
-	str_printfa(outbuf, "%08x: ", denv->dumper->mark_address);
+	str_printfa(outbuf, "%08x: ", cdumper->mark_address);
 	
 	while ( tab > 0 )	{
 		str_append(outbuf, "  ");
@@ -85,26 +84,26 @@ void sieve_code_dumpf
 	o_stream_send(denv->stream, str_data(outbuf), str_len(outbuf));
 }
 
-inline void sieve_code_mark(const struct sieve_dumptime_env *denv)
+void sieve_code_mark(const struct sieve_dumptime_env *denv)
 {
-	denv->dumper->mark_address = denv->dumper->pc;
+	denv->cdumper->mark_address = denv->cdumper->pc;
 }
 
-inline void sieve_code_mark_specific
+void sieve_code_mark_specific
 (const struct sieve_dumptime_env *denv, sieve_size_t location)
 {
-	denv->dumper->mark_address = location;
+	denv->cdumper->mark_address = location;
 }
 
-inline void sieve_code_descend(const struct sieve_dumptime_env *denv)
+void sieve_code_descend(const struct sieve_dumptime_env *denv)
 {
-	denv->dumper->indent++;
+	denv->cdumper->indent++;
 }
 
-inline void sieve_code_ascend(const struct sieve_dumptime_env *denv)
+void sieve_code_ascend(const struct sieve_dumptime_env *denv)
 {
-	if ( denv->dumper->indent > 0 )
-		denv->dumper->indent--;
+	if ( denv->cdumper->indent > 0 )
+		denv->cdumper->indent--;
 }
 
 /* Operations and operands */
@@ -134,49 +133,58 @@ static bool sieve_code_dumper_print_operation
 	(struct sieve_code_dumper *dumper) 
 {	
 	const struct sieve_operation *op;
+	struct sieve_dumptime_env *denv = dumper->dumpenv;
+	sieve_size_t address;
+	const char *opcode_string;
 	
 	/* Mark start address of operation */
 	dumper->indent = 0;
-	dumper->mark_address = dumper->pc;
+	address = dumper->mark_address = dumper->pc;
 
 	/* Read operation */
 	dumper->operation = op = 
-		sieve_operation_read(dumper->dumpenv.sbin, &(dumper->pc));
+		sieve_operation_read(denv->sbin, &(dumper->pc));
 
 	/* Try to dump it */
 	if ( op != NULL ) {
 		if ( op->dump != NULL )
-			return op->dump(op, &(dumper->dumpenv), &(dumper->pc));
+			return op->dump(op, denv, &(dumper->pc));
 		else if ( op->mnemonic != NULL )
-			sieve_code_dumpf(&(dumper->dumpenv), "%s", op->mnemonic);
+			sieve_code_dumpf(denv, "%s", op->mnemonic);
 		else
 			return FALSE;
 			
 		return TRUE;
 	}		
 	
+	opcode_string = sieve_operation_read_string(denv->sbin, &address);
+
+	if ( opcode_string != NULL )
+		sieve_code_dumpf(denv, "Unknown upcode: %s", opcode_string);
+	else
+		sieve_code_dumpf(denv, "Failed to read opcode.");
 	return FALSE;
 }
 
-void sieve_code_dumper_run
-	(struct sieve_code_dumper *dumper, struct ostream *stream) 
+void sieve_code_dumper_run(struct sieve_code_dumper *dumper) 
 {
+	struct sieve_binary *sbin = dumper->dumpenv->sbin;
+	
 	dumper->pc = 0;
-	dumper->dumpenv.stream = stream;
 	
 	while ( dumper->pc < 
-		sieve_binary_get_code_size(dumper->dumpenv.sbin) ) {
+		sieve_binary_get_code_size(sbin) ) {
 		if ( !sieve_code_dumper_print_operation(dumper) ) {
-			sieve_code_dumpf(&(dumper->dumpenv), "Binary is corrupt.");
+			sieve_code_dumpf(dumper->dumpenv, "Binary is corrupt.");
 			return;
 		}
 	}
 	
 	/* Mark end of the binary */
 	dumper->indent = 0;
-	dumper->mark_address = sieve_binary_get_code_size(dumper->dumpenv.sbin);
-	sieve_code_dumpf(&(dumper->dumpenv), "[End of code]");	
+	dumper->mark_address = sieve_binary_get_code_size(sbin);
+	sieve_code_dumpf(dumper->dumpenv, "[End of code]");	
 
 	/* Add empty line to the file */
-	o_stream_send_str(dumper->dumpenv.stream, "\n");
+	o_stream_send_str(dumper->dumpenv->stream, "\n");
 }
diff --git a/src/lib-sieve/sieve-code-dumper.h b/src/lib-sieve/sieve-code-dumper.h
index 4a2781f4763caf091050d8fcccb9dec6458c0d4c..4f246ba470d4dffac6daf5f233c3532d8c8a479a 100644
--- a/src/lib-sieve/sieve-code-dumper.h
+++ b/src/lib-sieve/sieve-code-dumper.h
@@ -5,16 +5,12 @@
 
 struct sieve_code_dumper;
 
-struct sieve_dumptime_env {
-	struct sieve_code_dumper *dumper;
-	struct sieve_binary *sbin;
-	
-	struct ostream *stream;
-};
-
-struct sieve_code_dumper *sieve_code_dumper_create(struct sieve_binary *sbin);
-void sieve_code_dumper_free(struct sieve_code_dumper *dumper);
-inline pool_t sieve_code_dumper_pool(struct sieve_code_dumper *dumper);
+struct sieve_code_dumper *sieve_code_dumper_create
+	(struct sieve_dumptime_env *denv);
+void sieve_code_dumper_free
+	(struct sieve_code_dumper **dumper);
+pool_t sieve_code_dumper_pool
+	(struct sieve_code_dumper *dumper);
 	
 /*  */	
 	
@@ -22,11 +18,11 @@ void sieve_code_dumpf
 	(const struct sieve_dumptime_env *denv, const char *fmt, ...)
 		ATTR_FORMAT(2, 3);
 
-inline void sieve_code_mark(const struct sieve_dumptime_env *denv);
-inline void sieve_code_mark_specific
+void sieve_code_mark(const struct sieve_dumptime_env *denv);
+void sieve_code_mark_specific
 	(const struct sieve_dumptime_env *denv, sieve_size_t location);
-inline void sieve_code_descend(const struct sieve_dumptime_env *denv);
-inline void sieve_code_ascend(const struct sieve_dumptime_env *denv);
+void sieve_code_descend(const struct sieve_dumptime_env *denv);
+void sieve_code_ascend(const struct sieve_dumptime_env *denv);
 
 /* Operations and operands */
 	
@@ -35,7 +31,6 @@ bool sieve_code_dumper_print_optional_operands
 
 /* Code dump (debugging purposes) */
 
-void sieve_code_dumper_run
-	(struct sieve_code_dumper *dumper, struct ostream *stream);
+void sieve_code_dumper_run(struct sieve_code_dumper *dumper);
 
 #endif /* __SIEVE_CODE_DUMPER_H */
diff --git a/src/lib-sieve/sieve-code.c b/src/lib-sieve/sieve-code.c
index 750211ae33d21552326756a47e79f3a0cf1039f0..c36fbb11df524a73edc42d53db4c080f87e76d0b 100644
--- a/src/lib-sieve/sieve-code.c
+++ b/src/lib-sieve/sieve-code.c
@@ -8,7 +8,7 @@
 #include "sieve-binary.h"
 #include "sieve-generator.h"
 #include "sieve-interpreter.h"
-#include "sieve-code-dumper.h"
+#include "sieve-dump.h"
 
 #include "sieve-code.h"
 
@@ -704,6 +704,14 @@ const struct sieve_operation *sieve_operation_read
 		(struct sieve_operation, sbin, address, &oprt_default_reg, 
 			sieve_operation_registry_get);
 }
+
+const char *sieve_operation_read_string
+	(struct sieve_binary *sbin, sieve_size_t *address) 
+{
+	return sieve_extension_read_obj_string
+		(sbin, address, &oprt_default_reg, 
+			sieve_operation_registry_get);
+}
 	
 /* Code dump for core commands */
 
diff --git a/src/lib-sieve/sieve-code.h b/src/lib-sieve/sieve-code.h
index dbb4902c96498c53463df3c6087c701eee4cebc7..33be0bc5cf7c4f501dcc250697ba6a17df4a2677 100644
--- a/src/lib-sieve/sieve-code.h
+++ b/src/lib-sieve/sieve-code.h
@@ -172,6 +172,8 @@ inline sieve_size_t sieve_operation_emit_code
 	(struct sieve_binary *sbin, const struct sieve_operation *op, int ext_id);	
 const struct sieve_operation *sieve_operation_read
 	(struct sieve_binary *sbin, sieve_size_t *address);
+const char *sieve_operation_read_string
+    (struct sieve_binary *sbin, sieve_size_t *address);
 
 bool sieve_operation_string_dump
 	(const struct sieve_operation *op,
diff --git a/src/lib-sieve/sieve-common.h b/src/lib-sieve/sieve-common.h
index 4e851c5b28f122a54af5406e06a5e8e60ee0aed7..79d41b3b7812d18e70aaf78330abf83d2fc6e50d 100644
--- a/src/lib-sieve/sieve-common.h
+++ b/src/lib-sieve/sieve-common.h
@@ -52,8 +52,11 @@ struct sieve_generator;
 struct sieve_runtime_env;
 struct sieve_interpreter;
 
-/* sieve-code-dumper.h */
+/* sieve-binary-dumper.h */
 struct sieve_dumptime_env;
+struct sieve_binary_dumper;
+
+/* sieve-code-dumper.h */
 struct sieve_code_dumper;
 
 /* sieve-extension.h */
diff --git a/src/lib-sieve/sieve-comparators.c b/src/lib-sieve/sieve-comparators.c
index d6a571e5e37231b230caf8e45d3e665c8d24e2e1..5732ae20bc768e342ba499ae1c4eea9e85a036a5 100644
--- a/src/lib-sieve/sieve-comparators.c
+++ b/src/lib-sieve/sieve-comparators.c
@@ -10,7 +10,7 @@
 #include "sieve-validator.h"
 #include "sieve-generator.h"
 #include "sieve-interpreter.h"
-#include "sieve-code-dumper.h"
+#include "sieve-dump.h"
 
 #include "sieve-comparators.h"
 
diff --git a/src/lib-sieve/sieve-dump.h b/src/lib-sieve/sieve-dump.h
new file mode 100644
index 0000000000000000000000000000000000000000..1105dc4460e5229017c3815acca0976d8c8c23ee
--- /dev/null
+++ b/src/lib-sieve/sieve-dump.h
@@ -0,0 +1,17 @@
+#ifndef __SIEVE_DUMP_H
+#define __SIEVE_DUMP_H
+
+#include "sieve-common.h"
+
+#include "sieve-binary-dumper.h"
+#include "sieve-code-dumper.h"
+
+struct sieve_dumptime_env {
+	struct sieve_binary_dumper *dumper;
+	struct sieve_code_dumper *cdumper;
+	struct sieve_binary *sbin;
+	
+	struct ostream *stream;
+};
+
+#endif /* __SIEVE_DUMP_H */
diff --git a/src/lib-sieve/sieve-extensions-private.h b/src/lib-sieve/sieve-extensions-private.h
index 0135bc2a8b03be1ef05dee8023b2f89ef82a3a5c..2ebc0e9e95a292859fcd30dca80c038103e8e9f6 100644
--- a/src/lib-sieve/sieve-extensions-private.h
+++ b/src/lib-sieve/sieve-extensions-private.h
@@ -79,4 +79,33 @@ static inline const void *_sieve_extension_read_obj
 	((const type *) _sieve_extension_read_obj \
 		(sbin, address, defreg, get_reg_func))
 
+static inline const char *sieve_extension_read_obj_string
+(struct sieve_binary *sbin, sieve_size_t *address, 
+	const struct sieve_extension_obj_registry *defreg, 
+	const struct sieve_extension_obj_registry *(*get_reg_func)
+		(struct sieve_binary *sbin, unsigned int index)) 
+{ 
+	unsigned int obj_code; 
+
+	if ( sieve_binary_read_byte(sbin, address, &obj_code) ) { 
+		if ( obj_code < defreg->count ) { 
+			return t_strdup_printf("[CODE: %d]", obj_code); 
+		} else {
+			unsigned int code = 0; 	 
+			const struct sieve_extension_obj_registry *reg;
+		
+			if ( (reg=get_reg_func(sbin, obj_code - defreg->count)) == NULL ||
+				reg->count == 0 ) 
+				return t_strdup_printf("[EXT: %d; NO CODES!]", obj_code);
+		
+			if ( reg->count > 1) 
+				sieve_binary_read_byte(sbin, address, &code); 
+	
+			return t_strdup_printf("[EXT: %d; CODE: %d]", obj_code, code);
+		}
+	}
+	
+	return NULL;
+}
+
 #endif /* __SIEVE_EXTENSIONS_PRIVATE_H */
diff --git a/src/lib-sieve/sieve-match-types.c b/src/lib-sieve/sieve-match-types.c
index 079b2fe75f1a70f5c857445dbdd64c7d07802c33..2c098c0b2fd0c21a777e95801287a1ae8d4b33dc 100644
--- a/src/lib-sieve/sieve-match-types.c
+++ b/src/lib-sieve/sieve-match-types.c
@@ -14,7 +14,7 @@
 #include "sieve-validator.h"
 #include "sieve-generator.h"
 #include "sieve-interpreter.h"
-#include "sieve-code-dumper.h"
+#include "sieve-dump.h"
 
 #include "sieve-match-types.h"
 
diff --git a/src/lib-sieve/sieve-result.c b/src/lib-sieve/sieve-result.c
index 7884ffb4970df4daecab0c533870ba9a6ce1d330..f674ff318a8ae72fc45b77f563fecbffc994e582 100644
--- a/src/lib-sieve/sieve-result.c
+++ b/src/lib-sieve/sieve-result.c
@@ -294,7 +294,7 @@ int sieve_result_execute
 		while ( rsef != NULL ) {
 			sef = rsef->seffect;
 			if ( sef->pre_execute != NULL ) 
-				sef->pre_execute
+				success = success & sef->pre_execute
 					(sef, act, &result->action_env, &rsef->context, context);
 			rsef = rsef->next;
 		}
@@ -305,12 +305,12 @@ int sieve_result_execute
 			success = success && rac->success;
 		}
 		
-		/* Execute pre-execute event of side effects */
+		/* Execute post-execute event of side effects */
 		rsef = rac->seffects != NULL ? rac->seffects->first_effect : NULL;
 		while ( rsef != NULL ) {
 			sef = rsef->seffect;
 			if ( sef->post_execute != NULL ) 
-				sef->post_execute
+				success = success && sef->post_execute
 					(sef, act, &result->action_env, rsef->context, context);
 			rsef = rsef->next;
 		}
diff --git a/src/lib-sieve/sieve.c b/src/lib-sieve/sieve.c
index 3062207fd56172b95d695b220abc083ec24ffb39..dc0d252c61d4ac85f9124be139c9b37faa88de74 100644
--- a/src/lib-sieve/sieve.c
+++ b/src/lib-sieve/sieve.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002-2007 Dovecot authors, see the included COPYING file */
+/* Copyright (c) 2002-2007 Dovecot Sieve authors, see the included COPYING file */
 
 #include "lib.h"
 #include "str.h"
@@ -16,7 +16,7 @@
 #include "sieve-validator.h"
 #include "sieve-generator.h"
 #include "sieve-interpreter.h"
-#include "sieve-code-dumper.h"
+#include "sieve-binary-dumper.h"
 
 #include "sieve.h"
 #include "sieve-common.h"
@@ -174,11 +174,11 @@ struct sieve_binary *sieve_open
 
 void sieve_dump(struct sieve_binary *sbin, struct ostream *stream) 
 {
-	struct sieve_code_dumper *dumpr = sieve_code_dumper_create(sbin);			
+	struct sieve_binary_dumper *dumpr = sieve_binary_dumper_create(sbin);			
 
-	sieve_code_dumper_run(dumpr, stream);	
+	sieve_binary_dumper_run(dumpr, stream);	
 	
-	sieve_code_dumper_free(dumpr);
+	sieve_binary_dumper_free(&dumpr);
 }
 
 int sieve_test
@@ -214,7 +214,6 @@ int sieve_execute
 	ret = sieve_interpreter_run(interp, msgdata, senv, &sres);
 				
 	sieve_interpreter_free(&interp);
-	sieve_result_unref(&sres);
 	return ret;
 }
 
diff --git a/src/lib-sieve/tst-header.c b/src/lib-sieve/tst-header.c
index e2fd446448b7bb4d9934d8570c8a77c9f182c03f..fc5e2e19f0406c43d201a3accab0fbe152f471a3 100644
--- a/src/lib-sieve/tst-header.c
+++ b/src/lib-sieve/tst-header.c
@@ -8,7 +8,7 @@
 #include "sieve-validator.h"
 #include "sieve-generator.h"
 #include "sieve-interpreter.h"
-#include "sieve-code-dumper.h"
+#include "sieve-dump.h"
 
 /* Header test 
  *
diff --git a/src/testsuite/Makefile.am b/src/testsuite/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..826cb8141ae0991c4a800687f420e6f65b8a872f
--- /dev/null
+++ b/src/testsuite/Makefile.am
@@ -0,0 +1,53 @@
+noinst_PROGRAMS = testsuite
+
+AM_CPPFLAGS = \
+	-I../lib-sieve \
+	-I$(dovecot_incdir) \
+	-I$(dovecot_incdir)/src/lib \
+	-I$(dovecot_incdir)/src/lib-mail \
+	-I$(dovecot_incdir)/src/lib-index \
+	-I$(dovecot_incdir)/src/lib-storage \
+	-I$(dovecot_incdir)/src/lib-storage/index \
+	-I$(dovecot_incdir)/src/lib-storage/index/raw \
+	-I$(dovecot_incdir)/src/lib-storage/index/mbox \
+	-I$(dovecot_incdir)/src/lib-storage/index/maildir
+
+testsuite_LDFLAGS = -export-dynamic -Wl,--start-group 
+
+libs = \
+	$(top_srcdir)/src/lib-sieve/libsieve.la \
+	$(dovecot_incdir)/src/lib-storage/list/libstorage_list.a \
+	$(dovecot_incdir)/src/lib-storage/index/raw/libstorage_raw.a \
+	$(dovecot_incdir)/src/lib-storage/index/maildir/libstorage_maildir.a \
+	$(dovecot_incdir)/src/lib-storage/index/mbox/libstorage_mbox.a \
+    $(dovecot_incdir)/src/lib-storage/index/libstorage_index.a \
+    $(dovecot_incdir)/src/lib-storage/libstorage.a \
+    $(dovecot_incdir)/src/lib-index/libindex.a \
+    $(dovecot_incdir)/src/lib-imap/libimap.a \
+    $(dovecot_incdir)/src/lib-mail/libmail.a \
+    $(dovecot_incdir)/src/lib-charset/libcharset.a \
+	$(dovecot_incdir)/src/lib/liblib.a
+
+ldadd = \
+	$(libs) \
+ 	$(LIBICONV) \
+    $(RAND_LIBS) \
+    $(MODULE_LIBS)
+
+testsuite_LDADD = $(ldadd)
+testsuite_DEPENDENCIES = $(libs)
+
+cmds = \
+	cmd-test-message.c
+
+testsuite_SOURCES = \
+	namespaces.c \
+	mail-raw.c \
+	$(cmds) \
+	ext-testsuite.c \
+	testsuite.c
+
+noinst_HEADERS = \
+	testsuite-common.h \
+	namespaces.h \
+	mail-raw.h
diff --git a/src/testsuite/cmd-test-message.c b/src/testsuite/cmd-test-message.c
new file mode 100644
index 0000000000000000000000000000000000000000..25f7e79c576d9628dbc446a7145246d5b0c39be4
--- /dev/null
+++ b/src/testsuite/cmd-test-message.c
@@ -0,0 +1,134 @@
+#include "lib.h"
+#include "ioloop.h"
+#include "str-sanitize.h"
+#include "istream.h"
+#include "istream-header-filter.h"
+
+#include "sieve-commands.h"
+#include "sieve-commands-private.h"
+#include "sieve-code.h"
+#include "sieve-actions.h"
+#include "sieve-validator.h" 
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-code-dumper.h"
+#include "sieve-result.h"
+
+#include "testsuite-common.h"
+
+#include <stdio.h>
+
+/* Forward declarations */
+
+static bool cmd_test_message_operation_dump
+	(const struct sieve_operation *op,
+		const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static bool cmd_test_message_operation_execute
+	(const struct sieve_operation *op, 
+		const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+static bool cmd_test_message_validate
+	(struct sieve_validator *validator, struct sieve_command_context *cmd);
+static bool cmd_test_message_generate
+	(struct sieve_generator *generator,	struct sieve_command_context *ctx);
+
+/* Redirect command 
+ * 
+ * Syntax
+ *   redirect <address: string>
+ */
+
+const struct sieve_command cmd_test_message = { 
+	"test_message", 
+	SCT_COMMAND,
+	1, 0, FALSE, FALSE, 
+	NULL, NULL,
+	cmd_test_message_validate, 
+	cmd_test_message_generate, 
+	NULL 
+};
+
+/* Redirect operation */
+
+const struct sieve_operation test_message_operation = { 
+	"TEST_MESSAGE",
+	&testsuite_extension, 
+	0,
+	cmd_test_message_operation_dump, 
+	cmd_test_message_operation_execute 
+};
+
+/* Validation */
+
+static bool cmd_test_message_validate
+	(struct sieve_validator *validator, struct sieve_command_context *cmd) 
+{
+	struct sieve_ast_argument *arg = cmd->first_positional;
+
+	/* Check argument */
+	if ( !sieve_validate_positional_argument
+		(validator, cmd, arg, "address", 1, SAAT_STRING) ) {
+		return FALSE;
+	}
+	
+	return sieve_validator_argument_activate(validator, cmd, arg, FALSE);
+}
+
+/*
+ * Generation
+ */
+ 
+static bool cmd_test_message_generate
+	(struct sieve_generator *generator,	struct sieve_command_context *ctx) 
+{
+	sieve_generator_emit_operation_ext(generator, &test_message_operation, 
+		ext_testsuite_my_id);
+
+	/* Generate arguments */
+	if ( !sieve_generate_arguments(generator, ctx, NULL) )
+		return FALSE;
+	
+	return TRUE;
+}
+
+/* 
+ * Code dump
+ */
+ 
+static bool cmd_test_message_operation_dump
+(const struct sieve_operation *op ATTR_UNUSED,
+	const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+	sieve_code_dumpf(denv, "TEST MESSAGE:");
+	sieve_code_descend(denv);
+
+	return 
+		sieve_opr_string_dump(denv, address);
+}
+
+/*
+ * Intepretation
+ */
+
+static bool cmd_test_message_operation_execute
+(const struct sieve_operation *op ATTR_UNUSED,
+	const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+	string_t *message;
+	int ret = 0;
+
+	t_push();
+
+	if ( !sieve_opr_string_read(renv, address, &message) ) {
+		t_pop();
+		return FALSE;
+	}
+
+	printf(">> TEST MESSAGE \"%s\"\n", str_c(message));
+	
+	t_pop();
+	return (ret >= 0);
+}
+
+
+
diff --git a/src/testsuite/ext-testsuite.c b/src/testsuite/ext-testsuite.c
new file mode 100644
index 0000000000000000000000000000000000000000..d4facaf75982cb2233f8c2dce205a46cd2d18564
--- /dev/null
+++ b/src/testsuite/ext-testsuite.c
@@ -0,0 +1,75 @@
+/* Extension testsuite
+ * -------------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: vendor-specific 
+ *   (FIXME: provide specification for test authors)
+ * Implementation: skeleton
+ * Status: under development
+ * Purpose: This custom extension is used to add sieve commands that act
+ *   on the test suite. This provides the ability to specify and change the input 
+ *   message inside the script and to fail validation, code generation and 
+ *   particularly execution based on predicates.
+ */
+
+#include <stdio.h>
+
+#include "sieve-common.h"
+
+#include "sieve-code.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-result.h"
+
+/* Forward declarations */
+
+static bool ext_testsuite_load(int ext_id);
+static bool ext_testsuite_validator_load(struct sieve_validator *validator);
+static bool ext_testsuite_binary_load(struct sieve_binary *sbin);
+
+/* Extension definitions */
+
+int ext_testsuite_my_id;
+
+extern const struct sieve_operation test_message_operation;
+
+const struct sieve_operation *testsuite_operations[] =
+    { &test_message_operation };
+
+extern const struct sieve_command cmd_test_message;
+
+const struct sieve_extension testsuite_extension = { 
+	"vnd.dovecot.testsuite", 
+	ext_testsuite_load,
+	ext_testsuite_validator_load, 
+	NULL, 
+	ext_testsuite_binary_load, 
+	NULL, 
+	SIEVE_EXT_DEFINE_OPERATION(test_message_operation),
+	SIEVE_EXT_DEFINE_NO_OPERANDS
+};
+
+static bool ext_testsuite_load(int ext_id)
+{
+	ext_testsuite_my_id = ext_id;
+
+	return TRUE;
+}
+
+/* Load extension into validator */
+static bool ext_testsuite_validator_load(struct sieve_validator *validator)
+{
+	sieve_validator_register_command(validator, &cmd_test_message);
+	return TRUE;
+}
+
+static bool ext_testsuite_binary_load(struct sieve_binary *sbin)
+{
+	return TRUE;
+}
+
+
+
diff --git a/src/testsuite/mail-raw.c b/src/testsuite/mail-raw.c
new file mode 100644
index 0000000000000000000000000000000000000000..269e01e873fa4f712b62435f17c898aa4b16b580
--- /dev/null
+++ b/src/testsuite/mail-raw.c
@@ -0,0 +1,101 @@
+/* Copyright (c) 2005-2007 Dovecot authors, see the included COPYING file */
+
+/* This file was gratefully stolen from dovecot/src/deliver/deliver.c and altered
+ * to suit our needs. So, this contains lots and lots of duplicated code. 
+ * The sieve_test program needs to read an email message from stdin and it needs 
+ * to build a struct mail (to be fed to the sieve library). Deliver does something
+ * similar already, so that source was a basis for this test binary. 
+ */
+
+#include "lib.h"
+#include "istream.h"
+#include "istream-seekable.h"
+#include "fd-set-nonblock.h"
+#include "str.h"
+#include "str-sanitize.h"
+#include "strescape.h"
+#include "message-address.h"
+#include "raw-storage.h"
+#include "mail-namespace.h"
+
+#include "mail-raw.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pwd.h>
+
+#define DEFAULT_ENVELOPE_SENDER "MAILER-DAEMON"
+
+/* After buffer grows larger than this, create a temporary file to /tmp
+   where to read the mail. */
+#define MAIL_MAX_MEMORY_BUFFER (1024*128)
+
+static struct mail_namespace *raw_ns;
+
+void mail_raw_init(pool_t namespaces_pool, const char *user) 
+{
+	const char *error;
+
+	raw_ns = mail_namespaces_init_empty(namespaces_pool);
+	raw_ns->flags |= NAMESPACE_FLAG_INTERNAL;
+	if (mail_storage_create(raw_ns, "raw", "/tmp", user,
+				0, FILE_LOCK_METHOD_FCNTL, &error) < 0)
+		i_fatal("Couldn't create internal raw storage: %s", error);	
+}	
+	
+struct mail_raw *mail_raw_open(string_t *mail_data)
+{
+	pool_t pool;
+	struct raw_mailbox *raw_box;
+	struct mail_raw *mailr;
+	
+	pool = pool_alloconly_create("mail_raw", 1024);
+	mailr = p_new(pool, struct mail_raw, 1);
+	mailr->pool = pool;
+
+	mailr->input = i_stream_create_from_data
+		(str_data(mail_data), str_len(mail_data));
+
+	mailr->box = mailbox_open(raw_ns->storage, 
+		"Dovecot Raw Mail", mailr->input, MAILBOX_OPEN_NO_INDEX_FILES);
+	
+	if (mailr->box == NULL)
+		i_fatal("Can't open mail stream as raw");
+
+	if (mailbox_sync(mailr->box, 0, 0, NULL) < 0) {
+		enum mail_error error;
+
+		i_fatal("Can't sync raw mail: %s",
+		mail_storage_get_last_error(raw_ns->storage, &error));
+	}
+    raw_box = (struct raw_mailbox *) mailr->box;
+    raw_box->envelope_sender = DEFAULT_ENVELOPE_SENDER;
+
+	mailr->trans = mailbox_transaction_begin(mailr->box, 0);
+	mailr->mail = mail_alloc(mailr->trans, 0, NULL);
+	mail_set_seq(mailr->mail, 1);
+
+	/* */
+	i_stream_seek(mailr->input, 0);
+
+	return mailr;
+}
+
+void mail_raw_close(struct mail_raw *mailr) 
+{
+	i_stream_unref(&mailr->input);
+
+	mail_free(&mailr->mail);
+	mailbox_transaction_rollback(&mailr->trans);
+	mailbox_close(&mailr->box);
+
+	pool_unref(&mailr->pool);
+}
+
+void mail_raw_deinit(void)
+{
+	mail_namespaces_deinit(&raw_ns);
+}
+
diff --git a/src/testsuite/mail-raw.h b/src/testsuite/mail-raw.h
new file mode 100644
index 0000000000000000000000000000000000000000..789b133e3cc496b9ca9cb13583f23b3eeedc8321
--- /dev/null
+++ b/src/testsuite/mail-raw.h
@@ -0,0 +1,21 @@
+#ifndef __MAIL_RAW_H
+#define __MAIL_RAW_H
+
+#include "lib.h"
+#include "str.h"
+
+struct mail_raw {
+    pool_t pool;
+    struct mail *mail;
+
+    struct istream *input;
+    struct mailbox *box;
+    struct mailbox_transaction_context *trans;
+};
+
+void mail_raw_init(pool_t namespaces_pool, const char *user);
+struct mail_raw *mail_raw_open(string_t *mail_data);
+void mail_raw_close(struct mail_raw *mailr);
+void mail_raw_deinit(void);
+
+#endif /* __MAIL_RAW_H */
diff --git a/src/testsuite/namespaces.c b/src/testsuite/namespaces.c
new file mode 100644
index 0000000000000000000000000000000000000000..cc1378953022d6bde03267b21d6f053043187e8c
--- /dev/null
+++ b/src/testsuite/namespaces.c
@@ -0,0 +1,64 @@
+/* Copyright (c) 2005-2007 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "istream.h"
+#include "istream-seekable.h"
+#include "fd-set-nonblock.h"
+#include "str.h"
+#include "str-sanitize.h"
+#include "strescape.h"
+#include "message-address.h"
+#include "raw-storage.h"
+#include "mbox-storage.h"
+#include "maildir-storage.h"
+#include "mail-namespace.h"
+
+#include "namespaces.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pwd.h>
+
+#define DEFAULT_ENVELOPE_SENDER "MAILER-DAEMON"
+
+/* Hideous .... */
+
+extern struct mail_storage raw_storage;
+extern struct mail_storage maildir_storage;
+extern struct mail_storage mbox_storage;
+void mail_storage_register_all(void) {
+	mail_storage_class_register(&raw_storage);
+	mail_storage_class_register(&mbox_storage);
+	mail_storage_class_register(&maildir_storage);
+}
+
+extern struct mailbox_list maildir_mailbox_list;
+extern struct mailbox_list fs_mailbox_list;
+void index_mailbox_list_init(void);
+void mailbox_list_register_all(void) {
+mailbox_list_register(&maildir_mailbox_list);
+mailbox_list_register(&fs_mailbox_list);
+index_mailbox_list_init();
+}
+
+static pool_t namespaces_pool;
+
+pool_t namespaces_init(void) 
+{
+	mail_storage_init();
+	mail_storage_register_all();
+	mailbox_list_register_all();
+
+	namespaces_pool = pool_alloconly_create("namespaces", 1024);
+
+	return namespaces_pool;
+}	
+	
+void namespaces_deinit(void)
+{
+	mail_storage_deinit();
+	pool_unref(&namespaces_pool);
+}
+
diff --git a/src/testsuite/namespaces.h b/src/testsuite/namespaces.h
new file mode 100644
index 0000000000000000000000000000000000000000..5f542d3f62d0d796937d97e0818f9003b5417e9b
--- /dev/null
+++ b/src/testsuite/namespaces.h
@@ -0,0 +1,7 @@
+#ifndef __NAMESPACES_H
+#define __NAMESPACES_H
+
+pool_t namespaces_init(void);
+void namespaces_deinit(void);
+
+#endif /* __MAIL_RAW_H */
diff --git a/src/testsuite/tests/testsuite.sieve b/src/testsuite/tests/testsuite.sieve
new file mode 100644
index 0000000000000000000000000000000000000000..4d05ffc3a7c0f850eeee61cbde86c25d8e1b736d
--- /dev/null
+++ b/src/testsuite/tests/testsuite.sieve
@@ -0,0 +1,12 @@
+require "vnd.dovecot.testsuite";
+
+test_message text:
+From: sirius@rename-it.nl
+To: nico@vestingbar.nl
+Subject: Frop!
+
+Frop!
+.
+;
+
+keep;
diff --git a/src/testsuite/testsuite b/src/testsuite/testsuite
new file mode 100755
index 0000000000000000000000000000000000000000..1ca190549a46153e848c26e08711ab46d1eb5248
Binary files /dev/null and b/src/testsuite/testsuite differ
diff --git a/src/testsuite/testsuite-common.h b/src/testsuite/testsuite-common.h
new file mode 100644
index 0000000000000000000000000000000000000000..5cc7e5a66add76e18cbf46a928afe070ffbd439b
--- /dev/null
+++ b/src/testsuite/testsuite-common.h
@@ -0,0 +1,8 @@
+#ifndef __EXT_TESTSUITE_COMMON_H
+#define __EXT_TESTSUITE_COMMON_H
+
+extern const struct sieve_extension testsuite_extension;
+
+extern int ext_testsuite_my_id;
+
+#endif
diff --git a/src/testsuite/testsuite.c b/src/testsuite/testsuite.c
new file mode 100644
index 0000000000000000000000000000000000000000..bb7c6184d483383c11ad2d0a8e84afd8ab1a51f2
--- /dev/null
+++ b/src/testsuite/testsuite.c
@@ -0,0 +1,257 @@
+/* Copyright (c) 2005-2007 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "ostream.h"
+#include "mail-storage.h"
+
+#include "mail-raw.h"
+#include "namespaces.h"
+#include "sieve.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pwd.h>
+
+#define DEFAULT_SENDMAIL_PATH "/usr/lib/sendmail"
+#define DEFAULT_ENVELOPE_SENDER "MAILER-DAEMON"
+
+#include "lib.h"
+#include "lib-signals.h"
+#include "ioloop.h"
+#include "ostream.h"
+#include "hostpid.h"
+#include "mail-storage.h"
+
+#include "sieve.h"
+#include "sieve-extensions.h"
+
+#include "testsuite-common.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pwd.h>
+
+/* Functionality common to all sieve test binaries */
+
+/* FIXME: this file is currently very messy */
+
+static struct ioloop *ioloop;
+
+static void sig_die(int signo, void *context ATTR_UNUSED)
+{
+	/* warn about being killed because of some signal, except SIGINT (^C)
+	   which is too common at least while testing :) */
+	if (signo != SIGINT)
+		i_warning("Killed with signal %d", signo);
+	// io_loop_stop(ioloop); We are not running an ioloop
+	exit(1);
+}
+
+static void testsuite_init(void) 
+{
+	lib_init();
+	ioloop = io_loop_create();
+
+	lib_signals_init();
+	lib_signals_set_handler(SIGINT, TRUE, sig_die, NULL);
+	lib_signals_set_handler(SIGTERM, TRUE, sig_die, NULL);
+	lib_signals_ignore(SIGPIPE, TRUE);
+	lib_signals_ignore(SIGALRM, FALSE);
+
+	if ( !sieve_init("") ) 
+		i_fatal("Failed to initialize sieve implementation\n");
+
+	sieve_extension_register(&testsuite_extension);
+}
+
+static void testsuite_deinit(void)
+{
+	sieve_deinit();
+	
+	lib_signals_deinit();
+
+	io_loop_destroy(&ioloop);
+	lib_deinit();
+}
+
+static const char *_get_user(void)
+{
+	uid_t process_euid = geteuid();
+	struct passwd *pw = getpwuid(process_euid);
+	if (pw != NULL) {
+		return t_strdup(pw->pw_name);
+	} 
+		
+	i_fatal("Couldn't lookup our username (uid=%s)", dec2str(process_euid));
+	return NULL;
+}
+
+static struct sieve_binary *_compile_sieve_script(const char *filename)
+{
+	struct sieve_error_handler *ehandler;
+	struct sieve_binary *sbin;
+	
+	ehandler = sieve_stderr_ehandler_create();
+	sieve_error_handler_accept_infolog(ehandler, TRUE);
+
+	if ( (sbin = sieve_compile(filename, ehandler)) == NULL ) {
+		sieve_error_handler_unref(&ehandler);
+		i_fatal("Failed to compile sieve script\n");
+	}
+
+	sieve_error_handler_unref(&ehandler);
+		
+	return sbin;
+}
+		
+static void _dump_sieve_binary_to(struct sieve_binary *sbin, const char *filename)	
+{
+	int dfd = -1;
+	struct ostream *dumpstream;
+	
+	if ( filename == NULL ) return;
+	
+	if ( strcmp(filename, "-") == 0 ) 
+		dumpstream = o_stream_create_fd(1, 0, FALSE);
+	else {
+		if ( (dfd = open(filename, O_WRONLY | O_CREAT)) < 0 ) {
+			i_fatal("Failed to open dump-file for writing: %m");
+			exit(1);
+		}
+		
+		dumpstream = o_stream_create_fd(dfd, 0, FALSE);
+	}
+	
+	if ( dumpstream != NULL ) {
+		(void) sieve_dump(sbin, dumpstream);
+		o_stream_destroy(&dumpstream);
+	} else {
+		i_fatal("Failed to create stream for sieve code dump.");
+	}
+	
+	if ( dfd != -1 )
+		close(dfd);
+}
+
+static void _fill_in_envelope
+	(struct mail *mail, const char **recipient, const char **sender)
+{
+	/* Get recipient address */
+	if ( *recipient == NULL ) 
+		(void)mail_get_first_header(mail, "Envelope-To", recipient);
+	if ( *recipient == NULL ) 
+		(void)mail_get_first_header(mail, "To", recipient);
+	if ( *recipient == NULL ) 
+		*recipient = "recipient@example.com";
+	
+	/* Get sender address */
+	if ( *sender == NULL ) 
+		(void)mail_get_first_header(mail, "Return-path", sender);
+	if ( *sender == NULL ) 
+		(void)mail_get_first_header(mail, "Sender", sender);
+	if ( *sender == NULL ) 
+		(void)mail_get_first_header(mail, "From", sender);
+	if ( *sender == NULL ) 
+		*sender = "sender@example.com";
+}
+
+static void print_help(void)
+{
+	printf(
+"Usage: testsuite [-d <dump filename>] <scriptfile>\n"
+	);
+}
+
+static const char *message_data = 
+"From: stephan@rename-it.nl\n"
+"To: sirius@drunksnipers.com\n"
+"Subject: Frop!\n"
+"\n"
+"Friep!\n";
+
+int main(int argc, char **argv) 
+{
+	const char *scriptfile, *dumpfile, *recipient, *sender; 
+	const char *user;
+	int i;
+	pool_t namespaces_pool;
+	struct mail_raw *mailr;
+	struct sieve_binary *sbin;
+	struct sieve_message_data msgdata;
+	struct sieve_script_env scriptenv;
+	struct sieve_error_handler *ehandler;
+	string_t *mail_buffer;
+
+	testsuite_init();
+
+	mail_buffer = t_str_new(1024);
+	str_append(mail_buffer, message_data);
+
+	/* Parse arguments */
+	scriptfile = dumpfile = recipient = sender = NULL;
+	for (i = 1; i < argc; i++) {
+		if (strcmp(argv[i], "-d") == 0) {
+			/* dump file */
+			i++;
+			if (i == argc)
+				i_fatal("Missing -d argument");
+			dumpfile = argv[i];
+		} else if ( scriptfile == NULL ) {
+			scriptfile = argv[i];
+		} else {
+			print_help();
+			i_fatal("Unknown argument: %s", argv[i]);
+		}
+	}
+	
+	if ( scriptfile == NULL ) {
+		print_help();
+		i_fatal("Missing <scriptfile> argument");
+	}
+	
+	/* Compile sieve script */
+	sbin = _compile_sieve_script(scriptfile);
+
+	/* Dump script */
+	_dump_sieve_binary_to(sbin, dumpfile);
+	
+	user = _get_user();
+
+	namespaces_pool = namespaces_init();
+	mail_raw_init(namespaces_pool, user);
+	mailr = mail_raw_open(mail_buffer);
+
+	_fill_in_envelope(mailr->mail, &recipient, &sender);
+
+	/* Collect necessary message data */
+	memset(&msgdata, 0, sizeof(msgdata));
+	msgdata.mail = mailr->mail;
+	msgdata.return_path = recipient;
+	msgdata.to_address = sender;
+	msgdata.auth_user = user;
+	(void)mail_get_first_header(mailr->mail, "Message-ID", &msgdata.id);
+
+	memset(&scriptenv, 0, sizeof(scriptenv));
+	scriptenv.inbox = "INBOX";
+	scriptenv.username = user;
+
+	ehandler = sieve_stderr_ehandler_create();	
+	
+	/* Run the test */
+	(void) sieve_test(sbin, &msgdata, &scriptenv, ehandler);
+
+	sieve_close(&sbin);
+	sieve_error_handler_unref(&ehandler);
+
+	mail_raw_close(mailr);
+	mail_raw_deinit();
+	namespaces_deinit();
+
+	testsuite_deinit();  
+
+	return 0;
+}