diff --git a/TODO b/TODO
index 4aa084effbeed2a7f63abde049c49ef9b26cf289..abf1cf31e0a1cbecf841a31d2c16b2730fe49b9e 100644
--- a/TODO
+++ b/TODO
@@ -1,18 +1,18 @@
 Next (in order of descending priority/precedence):
 * Get rid of all <stdio.h> printf()s in the library; use trace macro instead
+* Emit line numbers for certain action commands to provide useful runtime error
+  messages.
 * Revise extension support for comparators, match-types, address-parts and 
   side-effects. Current implementation emits redundant bytes and associated code
   is not very neat.
-* Emit line numbers for certain action commands to provide useful runtime error
-  messages.
 * Improve handling of old/corrupt binaries.
 
-* Full security review. Enforce limits on number of created objects, script 
-  size, execution time, etc...
 * Full standards compliance review for the engine and all fully implemented 
-  sieve extensions.
+  sieve extensions.\
 * Code cleanup 
 * Make sure cmusieve can be replaced seamlessly with the new plugin.
+* Full security review. Enforce limits on number of created objects, script 
+  size, execution time, etc...
 * Make simple test suite for the base functionality
 
 * ## MAKE A FIRST RELEASE ##
diff --git a/src/lib-sieve/cmd-discard.c b/src/lib-sieve/cmd-discard.c
index 42af9c1565ceaa51d1fa7e80452889f45cfd49fb..f972c35d80ad1f84667d6618e9543a10332c79bc 100644
--- a/src/lib-sieve/cmd-discard.c
+++ b/src/lib-sieve/cmd-discard.c
@@ -46,7 +46,7 @@ const struct sieve_operation cmd_discard_operation = {
 /* discard action */
 
 static void act_discard_print
-	(const struct sieve_action *action, struct sieve_result *result,
+	(const struct sieve_action *action, const struct sieve_result_print_env *rpenv,
 		void *context, bool *keep);	
 static bool act_discard_commit
 (const struct sieve_action *action, 
@@ -96,10 +96,10 @@ static bool cmd_discard_operation_execute
  
 static void act_discard_print
 (const struct sieve_action *action ATTR_UNUSED, 
-	struct sieve_result *result ATTR_UNUSED, void *context ATTR_UNUSED, 
+	const struct sieve_result_print_env *rpenv, void *context ATTR_UNUSED, 
 	bool *keep)	
 {
-	printf("* discard\n");
+	sieve_result_action_printf(rpenv, "discard");
 	
 	*keep = FALSE;
 }
diff --git a/src/lib-sieve/cmd-redirect.c b/src/lib-sieve/cmd-redirect.c
index 342bb87a4788a86b2fcd2d30b2fa4a6779c0b494..46831a2a8d4fe392379866cc5f5cdc726ae90725 100644
--- a/src/lib-sieve/cmd-redirect.c
+++ b/src/lib-sieve/cmd-redirect.c
@@ -67,7 +67,7 @@ static int act_redirect_check_duplicate
 	(const struct sieve_runtime_env *renv,
 		const struct sieve_action *action1, void *context1, void *context2);
 static void act_redirect_print
-	(const struct sieve_action *action, struct sieve_result *result,
+	(const struct sieve_action *action, const struct sieve_result_print_env *rpenv,
 		void *context, bool *keep);	
 static bool act_redirect_commit
 	(const struct sieve_action *action ATTR_UNUSED, 
@@ -210,11 +210,11 @@ static int act_redirect_check_duplicate
 
 static void act_redirect_print
 (const struct sieve_action *action ATTR_UNUSED, 
-	struct sieve_result *result ATTR_UNUSED, void *context, bool *keep)	
+	const struct sieve_result_print_env *rpenv, void *context, bool *keep)	
 {
 	struct act_redirect_context *ctx = (struct act_redirect_context *) context;
 	
-	printf("* redirect message to: %s\n", ctx->to_address);
+	sieve_result_action_printf(rpenv, "redirect message to: %s", ctx->to_address);
 	
 	*keep = FALSE;
 }
diff --git a/src/lib-sieve/ext-reject.c b/src/lib-sieve/ext-reject.c
index bd044cbbc5e4c0ad3d7c3008cb4fe77392946ec2..69118395ad49f7d1e0eb4c1965cbb5440796c1c8 100644
--- a/src/lib-sieve/ext-reject.c
+++ b/src/lib-sieve/ext-reject.c
@@ -99,7 +99,7 @@ int act_reject_check_conflict
 	(const struct sieve_runtime_env *renv, const struct sieve_action *action,
     	const struct sieve_action *other_action, void *context);
 static void act_reject_print
-	(const struct sieve_action *action, struct sieve_result *result,
+	(const struct sieve_action *action, const struct sieve_result_print_env *rpenv,
 		void *context, bool *keep);	
 static bool act_reject_commit
 	(const struct sieve_action *action ATTR_UNUSED, 
@@ -256,11 +256,11 @@ int act_reject_check_conflict
  
 static void act_reject_print
 (const struct sieve_action *action ATTR_UNUSED, 
-	struct sieve_result *result ATTR_UNUSED, void *context, bool *keep)	
+	const struct sieve_result_print_env *rpenv, void *context, bool *keep)	
 {
 	struct act_reject_context *ctx = (struct act_reject_context *) context;
 	
-	printf("* reject message with reason: %s\n", ctx->reason);
+	sieve_result_action_printf(rpenv, "reject message with reason: %s", ctx->reason);
 	
 	*keep = FALSE;
 }
diff --git a/src/lib-sieve/plugins/copy/ext-copy.c b/src/lib-sieve/plugins/copy/ext-copy.c
index a7e659deee8b4512f543eeb53f0887ea0d6ad5e5..27daac3775e48ad235342845d8c870b8b1d64937 100644
--- a/src/lib-sieve/plugins/copy/ext-copy.c
+++ b/src/lib-sieve/plugins/copy/ext-copy.c
@@ -55,7 +55,7 @@ const struct sieve_side_effect_extension ext_copy_side_effect;
 
 static void seff_copy_print
 	(const struct sieve_side_effect *seffect, const struct sieve_action *action, 
-		struct sieve_result *result, void *se_context, bool *keep);
+		const struct sieve_result_print_env *rpenv, void *se_context, bool *keep);
 static bool seff_copy_post_commit
 	(const struct sieve_side_effect *seffect, const struct sieve_action *action, 
 		const struct sieve_action_exec_env *aenv, void *se_context,
@@ -126,10 +126,10 @@ static const struct sieve_argument copy_tag = {
 static void seff_copy_print
 (const struct sieve_side_effect *seffect ATTR_UNUSED, 
 	const struct sieve_action *action ATTR_UNUSED, 
-	struct sieve_result *result ATTR_UNUSED,
+	const struct sieve_result_print_env *rpenv,
 	void *se_context ATTR_UNUSED, bool *keep)
 {
-	printf("        + preserve implicit keep\n");
+	sieve_result_seffect_printf(rpenv, "preserve implicit keep");
 
 	*keep = TRUE;
 }
@@ -140,7 +140,6 @@ static bool seff_copy_post_commit
 	const struct sieve_action_exec_env *aenv ATTR_UNUSED, 
 		void *se_context ATTR_UNUSED,	void *tr_context ATTR_UNUSED, bool *keep)
 {	
-	sieve_result_log(aenv, "implicit keep preserved after %s action.", action->name);
 	*keep = TRUE;
 	return TRUE;
 }
diff --git a/src/lib-sieve/plugins/imapflags/imapflags-implicit.sieve b/src/lib-sieve/plugins/imapflags/imapflags-implicit.sieve
new file mode 100644
index 0000000000000000000000000000000000000000..8754fad5cca6ae2afbbeb7adaa333bb6aacc4c82
--- /dev/null
+++ b/src/lib-sieve/plugins/imapflags/imapflags-implicit.sieve
@@ -0,0 +1,29 @@
+require "imap4flags";
+require "fileinto";
+require "relational";
+require "comparator-i;ascii-numeric";
+require "copy";
+
+setflag "\\Seen";
+addflag "$DSNRequired";
+removeflag "$DSNRequired";
+
+if header :contains "from" "boss@frobnitzm.example.edu" {
+	setflag "\\Flagged";
+	fileinto :copy "From Boss";
+}
+
+if header :contains "Disposition-Notification-To" "mel@example.com" {
+	addflag "$MDNRequired";
+}
+
+if header :contains "from" "imap@cac.washington.example.edu" {
+	removeflag "$MDNRequired \\Flagged \\Seen \\Deleted";
+	fileinto :copy "imap-list";
+}
+
+if hasflag :count "ge" :comparator "i;ascii-numeric" "2" {
+	fileinto :copy "imap-twoflags";
+}
+
+fileinto :copy :flags "MDNRequired \\Draft" "imap-draft";
diff --git a/src/lib-sieve/plugins/imapflags/tag-flags.c b/src/lib-sieve/plugins/imapflags/tag-flags.c
index 5f1e6c5b85d1a7c4c32be7a979d2d790527b2ad4..dd062ab289379a9ebb52be7c71170ce73875f1f2 100644
--- a/src/lib-sieve/plugins/imapflags/tag-flags.c
+++ b/src/lib-sieve/plugins/imapflags/tag-flags.c
@@ -42,8 +42,8 @@ static bool seff_flags_read_context
 		const struct sieve_runtime_env *renv, sieve_size_t *address,
 		void **se_context);
 static void seff_flags_print
-	(const struct sieve_side_effect *seffect,	const struct sieve_action *action,
-		struct sieve_result *result, void *se_context, bool *keep);
+	(const struct sieve_side_effect *seffect, const struct sieve_action *action,
+		const struct sieve_result_print_env *rpenv, void *se_context, bool *keep);
 static bool seff_flags_pre_execute
 	(const struct sieve_side_effect *seffect, const struct sieve_action *action, 
 		const struct sieve_action_exec_env *aenv, 
@@ -168,7 +168,10 @@ static bool seff_flags_read_context
 		while ( (flag=ext_imapflags_iter_get_flag(&flit)) != NULL ) {		
 			if (flag != NULL && *flag != '\\') {
 				/* keyword */
-				array_append(&ctx->keywords, &flag, 1);
+				const char *keyword = p_strdup(pool, flag);
+
+				array_append(&ctx->keywords, &keyword, 1);
+
 			} else {
 				/* system flag */
 				if (flag == NULL || strcasecmp(flag, "\\flagged") == 0)
@@ -210,7 +213,8 @@ static struct seff_flags_context *seff_flags_get_implicit_context
 	while ( (flag=ext_imapflags_iter_get_flag(&flit)) != NULL ) {		
 		if (flag != NULL && *flag != '\\') {
 			/* keyword */
-			array_append(&ctx->keywords, &flag, 1);
+			const char *keyword = p_strdup(pool, flag);
+			array_append(&ctx->keywords, &keyword, 1);
 		} else {
 			/* system flag */
 			if (flag == NULL || strcasecmp(flag, "\\flagged") == 0)
@@ -234,40 +238,43 @@ static struct seff_flags_context *seff_flags_get_implicit_context
 static void seff_flags_print
 (const struct sieve_side_effect *seffect ATTR_UNUSED, 
 	const struct sieve_action *action ATTR_UNUSED, 
-	struct sieve_result *result,
-	void *se_context ATTR_UNUSED, bool *keep ATTR_UNUSED)
+	const struct sieve_result_print_env *rpenv,
+	void *se_context, bool *keep ATTR_UNUSED)
 {
+	struct sieve_result *result = rpenv->result;
 	struct seff_flags_context *ctx = (struct seff_flags_context *) se_context;
 	unsigned int i;
 	
 	if ( ctx == NULL )
 		ctx = seff_flags_get_implicit_context(result);
 	
-	if ( ctx->flags != 0 || array_count(&ctx->keywords) ) { 
-		printf("        + add flags:");
-
-		if ( (ctx->flags & MAIL_FLAGGED) > 0 )
-			printf(" \\flagged\n");
-
-		if ( (ctx->flags & MAIL_ANSWERED) > 0 )
-			printf(" \\answered");
+	if ( ctx->flags != 0 || array_count(&ctx->keywords) > 0 ) {
+		T_BEGIN {
+			string_t *flags = t_str_new(128);
+ 
+			if ( (ctx->flags & MAIL_FLAGGED) > 0 )
+				str_printfa(flags, " \\flagged\n");
+
+			if ( (ctx->flags & MAIL_ANSWERED) > 0 )
+				str_printfa(flags, " \\answered");
 		
-		if ( (ctx->flags & MAIL_DELETED) > 0 )
-			printf(" \\deleted");
+			if ( (ctx->flags & MAIL_DELETED) > 0 )
+				str_printfa(flags, " \\deleted");
 					
-		if ( (ctx->flags & MAIL_SEEN) > 0 )
-			printf(" \\seen");
-		
-		if ( (ctx->flags & MAIL_DRAFT) > 0 )
-			printf(" \\draft");
+			if ( (ctx->flags & MAIL_SEEN) > 0 )
+				str_printfa(flags, " \\seen");
+			
+			if ( (ctx->flags & MAIL_DRAFT) > 0 )
+				str_printfa(flags, " \\draft");
+
+			for ( i = 0; i < array_count(&ctx->keywords); i++ ) {
+				const char *const *keyword = array_idx(&ctx->keywords, i);
+				str_printfa(flags, " %s", *keyword);
+			}
 
-		for ( i = 0; i < array_count(&ctx->keywords); i++ ) {
-			const char *const *keyword = array_idx(&ctx->keywords, i);
-			printf(" %s", *keyword);
-		};
+			sieve_result_seffect_printf(rpenv, "add flags:%s", str_c(flags));
+		} T_END;
 	}
-	
-	printf("\n");
 }
 
 static bool seff_flags_pre_execute
diff --git a/src/lib-sieve/plugins/vacation/cmd-vacation.c b/src/lib-sieve/plugins/vacation/cmd-vacation.c
index ac7b0c30f848fb8a0a5b9e92ec48196956ec900f..7fee602306c665c7dd6284b9022a7c0c468562fc 100644
--- a/src/lib-sieve/plugins/vacation/cmd-vacation.c
+++ b/src/lib-sieve/plugins/vacation/cmd-vacation.c
@@ -56,7 +56,7 @@ int act_vacation_check_conflict
 	(const struct sieve_runtime_env *renv, const struct sieve_action *action,
 		const struct sieve_action *other_action, void *context);
 static void act_vacation_print
-	(const struct sieve_action *action, struct sieve_result *result,
+	(const struct sieve_action *action, const struct sieve_result_print_env *rpenv,
 		void *context, bool *keep);	
 static bool act_vacation_commit
 	(const struct sieve_action *action,	const struct sieve_action_exec_env *aenv, 
@@ -469,20 +469,21 @@ int act_vacation_check_conflict
 }
  
 static void act_vacation_print
-(const struct sieve_action *action ATTR_UNUSED, struct sieve_result *result ATTR_UNUSED,
-	void *context, bool *keep ATTR_UNUSED)	
+(const struct sieve_action *action ATTR_UNUSED, 
+	const struct sieve_result_print_env *rpenv, void *context, 
+	bool *keep ATTR_UNUSED)	
 {
 	struct act_vacation_context *ctx = (struct act_vacation_context *) context;
 	
-	printf( 	"* send vacation message:\n"
-						"    => days   : %d\n", ctx->days);
+	sieve_result_action_printf( rpenv, "send vacation message:");
+	sieve_result_printf(rpenv, "    => days   : %d\n", ctx->days);
 	if ( ctx->subject != NULL )
-		printf(	"    => subject: %s\n", ctx->subject);
+		sieve_result_printf(rpenv, "    => subject: %s\n", ctx->subject);
 	if ( ctx->from != NULL )
-		printf(	"    => from   : %s\n", ctx->from);
+		sieve_result_printf(rpenv, "    => from   : %s\n", ctx->from);
 	if ( ctx->handle != NULL )
-		printf(	"    => handle : %s\n", ctx->handle);
-	printf(		"\nSTART MESSAGE\n%s\nEND MESSAGE\n", ctx->reason);
+		sieve_result_printf(rpenv, "    => handle : %s\n", ctx->handle);
+	sieve_result_printf(rpenv, "\nSTART MESSAGE\n%s\nEND MESSAGE\n", ctx->reason);
 }
 
 static const char * const _list_headers[] = {
diff --git a/src/lib-sieve/sieve-actions.c b/src/lib-sieve/sieve-actions.c
index c5cb0f7e7e943aad251aaf76906df69b4a1c48d6..cae579a3d15ff18e2e2a82e71826d7229a302c88 100644
--- a/src/lib-sieve/sieve-actions.c
+++ b/src/lib-sieve/sieve-actions.c
@@ -183,7 +183,7 @@ static int act_store_check_duplicate
 	(const struct sieve_runtime_env *renv, const struct sieve_action *action1, 
 		void *context1, void *context2);
 static void act_store_print
-	(const struct sieve_action *action, struct sieve_result *result,
+	(const struct sieve_action *action, const struct sieve_result_print_env *rpenv,
 		void *context, bool *keep);
 
 static bool act_store_start
@@ -244,11 +244,11 @@ static int act_store_check_duplicate
 
 static void act_store_print
 (const struct sieve_action *action ATTR_UNUSED, 
-	struct sieve_result *result ATTR_UNUSED, void *context, bool *keep)	
+	const struct sieve_result_print_env *rpenv, void *context, bool *keep)	
 {
 	struct act_store_context *ctx = (struct act_store_context *) context;
 	
-	printf("* store message in folder: %s\n", ctx->folder);
+	sieve_result_action_printf(rpenv, "store message in folder: %s", ctx->folder);
 	
 	*keep = FALSE;
 }
diff --git a/src/lib-sieve/sieve-actions.h b/src/lib-sieve/sieve-actions.h
index b17ffe838744bf3dd4553853fc4db7e7a6be846c..2f40d5a377f24105a132f267ee8e1bb6e462dd50 100644
--- a/src/lib-sieve/sieve-actions.h
+++ b/src/lib-sieve/sieve-actions.h
@@ -34,8 +34,8 @@ struct sieve_action {
 			void *context);
 
 	void (*print)
-		(const struct sieve_action *action, struct sieve_result *result, 
-			void *context, bool *keep);	
+		(const struct sieve_action *action, 
+			const struct sieve_result_print_env *penv, void *context, bool *keep);	
 		
 	bool (*start)
 		(const struct sieve_action *action, 
@@ -73,7 +73,7 @@ struct sieve_side_effect {
 			
 	void (*print)
 		(const struct sieve_side_effect *seffect, const struct sieve_action *action, 
-			struct sieve_result *result, void *se_context, bool *keep);
+			const struct sieve_result_print_env *penv, void *se_context, bool *keep);
 
 	bool (*pre_execute)
 		(const struct sieve_side_effect *seffect, const struct sieve_action *action, 
diff --git a/src/lib-sieve/sieve-ast.h b/src/lib-sieve/sieve-ast.h
index ca76da300cb19f6dcbf8d546b862ff99c87c9a6c..0e0b5758af8dbde2a4637af4d2a71f0a5897638a 100644
--- a/src/lib-sieve/sieve-ast.h
+++ b/src/lib-sieve/sieve-ast.h
@@ -82,7 +82,7 @@ struct sieve_ast_argument {
 
 	/* Argument associated with this ast element  */
 	const struct sieve_argument *argument;
-	unsigned int arg_id_code;
+	int arg_id_code;
 
 	/* Parameters to this (tag) argument */
 	struct sieve_ast_argument *parameters;
diff --git a/src/lib-sieve/sieve-common.h b/src/lib-sieve/sieve-common.h
index 7ce1f23600bfefc61cfd4532ef46f261595d6dc6..a8a49af024ef26b2364b1e8a9d933cecf0218e11 100644
--- a/src/lib-sieve/sieve-common.h
+++ b/src/lib-sieve/sieve-common.h
@@ -84,6 +84,7 @@ struct sieve_address_part;
 /* sieve-result.h */
 struct sieve_result;
 struct sieve_side_effects_list;
+struct sieve_result_print_env;
 
 /* sieve-actions.h */
 struct sieve_action_exec_env;
diff --git a/src/lib-sieve/sieve-result.c b/src/lib-sieve/sieve-result.c
index a27dd83f741e8ef50ec0dc8982fc1d9b3d9d11e6..8eab847c3a1773b6ef4a7fa267b18f96b4853df9 100644
--- a/src/lib-sieve/sieve-result.c
+++ b/src/lib-sieve/sieve-result.c
@@ -1,6 +1,8 @@
 #include "lib.h"
 #include "mempool.h"
+#include "ostream.h"
 #include "hash.h"
+#include "str.h"
 #include "strfuncs.h"
 #include "str-sanitize.h"
 
@@ -289,25 +291,77 @@ int sieve_result_add_action
 	}
 	
 	return 0;
-}	
+}
+
+/*
+ * Result printing
+ */
+
+void sieve_result_printf
+(const struct sieve_result_print_env *penv, const char *fmt, ...)
+{	
+	string_t *outbuf = t_str_new(128);
+	va_list args;
+	
+	va_start(args, fmt);	
+	str_vprintfa(outbuf, fmt, args);
+	va_end(args);
+	
+	o_stream_send(penv->stream, str_data(outbuf), str_len(outbuf));
+}
 
-bool sieve_result_print(struct sieve_result *result)
+void sieve_result_action_printf
+(const struct sieve_result_print_env *penv, const char *fmt, ...)
+{	
+	string_t *outbuf = t_str_new(128);
+	va_list args;
+	
+	va_start(args, fmt);	
+	str_append(outbuf, "\n * ");
+	str_vprintfa(outbuf, fmt, args);
+	str_append_c(outbuf, '\n');
+	va_end(args);
+	
+	o_stream_send(penv->stream, str_data(outbuf), str_len(outbuf));
+}
+
+void sieve_result_seffect_printf
+(const struct sieve_result_print_env *penv, const char *fmt, ...)
+{	
+	string_t *outbuf = t_str_new(128);
+	va_list args;
+	
+	va_start(args, fmt);	
+	str_append(outbuf, "        + ");
+	str_vprintfa(outbuf, fmt, args);
+	str_append_c(outbuf, '\n');
+	va_end(args);
+	
+	o_stream_send(penv->stream, str_data(outbuf), str_len(outbuf));
+}
+
+bool sieve_result_print
+(struct sieve_result *result, struct ostream *stream)
 {
+	struct sieve_result_print_env penv;
 	bool implicit_keep = TRUE;
-	struct sieve_result_action *rac = result->first_action;
+	struct sieve_result_action *rac;
+	
+	penv.result = result;
+	penv.stream = stream;
 	
-	printf("\nPerformed actions:\n");
+	sieve_result_printf(&penv, "\nPerformed actions:\n");
+	rac = result->first_action;
 	while ( rac != NULL ) {		
 		bool keep = TRUE;
 		const struct sieve_action *act = rac->action;
 		struct sieve_result_side_effect *rsef;
 		const struct sieve_side_effect *sef;
 
-	
 		if ( act->print != NULL ) {
-			act->print(act, result, rac->context, &keep);
+			act->print(act, &penv, rac->context, &keep);
 		} else {
-			printf("* %s\n", act->name); 
+			sieve_result_action_printf(&penv, act->name); 
 		}
 	
 		/* Print side effects */
@@ -316,15 +370,16 @@ bool sieve_result_print(struct sieve_result *result)
 			sef = rsef->seffect;
 			if ( sef->print != NULL ) 
 				sef->print
-					(sef, act, result, rsef->context, &keep);
+					(sef, act, &penv, rsef->context, &keep);
 			rsef = rsef->next;
 		}
-
+			
 		implicit_keep = implicit_keep && keep;		
 		rac = rac->next;	
 	}
 	
-	printf("\nImplicit keep: %s\n", implicit_keep ? "yes" : "no");
+	sieve_result_printf
+		(&penv, "\nImplicit keep: %s\n", implicit_keep ? "yes" : "no");
 	
 	return TRUE;
 }
diff --git a/src/lib-sieve/sieve-result.h b/src/lib-sieve/sieve-result.h
index f5917cc8aacf42f2cfc6253d70239cff71999920..d476326eca992866188bf5710c5f700102da3d3c 100644
--- a/src/lib-sieve/sieve-result.h
+++ b/src/lib-sieve/sieve-result.h
@@ -18,6 +18,22 @@ void sieve_result_extension_set_context
 const void *sieve_result_extension_get_context
 	(struct sieve_result *result, int ext_id); 
 
+/* Printing */
+
+struct sieve_result_print_env {
+	struct sieve_result *result;
+	struct ostream *stream;
+};
+
+void sieve_result_printf
+	(const struct sieve_result_print_env *penv, const char *fmt, ...);
+void sieve_result_action_printf
+	(const struct sieve_result_print_env *penv, const char *fmt, ...);
+void sieve_result_seffect_printf
+	(const struct sieve_result_print_env *penv, const char *fmt, ...);
+
+bool sieve_result_print(struct sieve_result *result, struct ostream *stream);
+
 /* Error handling */
 
 void sieve_result_log
@@ -36,8 +52,6 @@ int sieve_result_add_action
 	const struct sieve_action *action, struct sieve_side_effects_list *seffects,
 	void *context);
 
-bool sieve_result_print(struct sieve_result *result);
-
 void sieve_result_cancel_implicit_keep(struct sieve_result *result);
 
 int sieve_result_execute
diff --git a/src/lib-sieve/sieve-validator.c b/src/lib-sieve/sieve-validator.c
index 505d9703a266d2c5aee31849711a867ed4356ef3..aaa3caa7b8c250a92b7c7aa306212bb2dce1a240 100644
--- a/src/lib-sieve/sieve-validator.c
+++ b/src/lib-sieve/sieve-validator.c
@@ -371,7 +371,7 @@ static void sieve_validator_register_unknown_tag
 
 static const struct sieve_argument *sieve_validator_find_tag
 (struct sieve_validator *validator, struct sieve_command_context *cmd, 
-	struct sieve_ast_argument *arg, unsigned int *id_code) 
+	struct sieve_ast_argument *arg, int *id_code) 
 {
 	const char *tag;
 	unsigned int i;
@@ -609,7 +609,7 @@ static bool sieve_validate_command_arguments
 		
 	/* Visit tagged and optional arguments */
 	while ( sieve_ast_argument_type(arg) == SAAT_TAG ) {
-		unsigned int id_code;
+		int id_code;
 		struct sieve_ast_argument *parg; 
 		const struct sieve_argument *tag = 
 			sieve_validator_find_tag(validator, cmd, arg, &id_code);
@@ -639,7 +639,7 @@ static bool sieve_validate_command_arguments
 		parg = sieve_ast_argument_prev(arg);
 		while ( parg != NULL ) {
 			if ( (sieve_ast_argument_type(parg) == SAAT_TAG && parg->argument == tag) 
-				|| parg->arg_id_code == id_code ) 
+				|| (id_code > 0 && parg->arg_id_code == id_code) ) 
 			{
 				const char *tag_id = sieve_ast_argument_tag(arg);
 				const char *tag_desc =
diff --git a/src/lib-sieve/sieve.c b/src/lib-sieve/sieve.c
index 15304adf250207d5947629323badb3760c359199..688d1211f7d8d8ad9f5dc995a8b83c297d1e24e5 100644
--- a/src/lib-sieve/sieve.c
+++ b/src/lib-sieve/sieve.c
@@ -189,7 +189,7 @@ void sieve_dump(struct sieve_binary *sbin, struct ostream *stream)
 
 int sieve_test
 	(struct sieve_binary *sbin, const struct sieve_message_data *msgdata,
-		const struct sieve_script_env *senv, 
+		const struct sieve_script_env *senv, struct ostream *stream,
 		struct sieve_error_handler *ehandler) 	
 {
 	struct sieve_result *sres = sieve_result_create(ehandler);
@@ -200,7 +200,7 @@ int sieve_test
 	ret = sieve_interpreter_run(interp, msgdata, senv, &sres);
 	
 	if ( ret > 0 ) 
-		ret = sieve_result_print(sres);
+		ret = sieve_result_print(sres, stream);
 	
 	sieve_interpreter_free(&interp);
 	sieve_result_unref(&sres);
diff --git a/src/lib-sieve/sieve.h b/src/lib-sieve/sieve.h
index a81c79a7acb7a9a3bc110c6a52c9d7ad561a2e2a..444d9f6e50d24b43be4860f1c1cec26fd3c3130b 100644
--- a/src/lib-sieve/sieve.h
+++ b/src/lib-sieve/sieve.h
@@ -88,11 +88,11 @@ bool sieve_save
 
 /* sieve_test:
  *
- *   Executes the bytecode, but only prints the result to stdout.
+ *   Executes the bytecode, but only prints the result to the given stream.
  */ 
 int sieve_test
 	(struct sieve_binary *sbin, const struct sieve_message_data *msgdata, 
-		const struct sieve_script_env *senv, 
+		const struct sieve_script_env *senv, struct ostream *stream,
 		struct sieve_error_handler *ehandler);
 
 /* sieve_execute:
diff --git a/src/sieve-bin/sieve-test.c b/src/sieve-bin/sieve-test.c
index 35ac5a398b01be44f574023df0902622a58a1db0..d6b63f5f56e9e246453cfa31003bf8537cdb32de 100644
--- a/src/sieve-bin/sieve-test.c
+++ b/src/sieve-bin/sieve-test.c
@@ -37,6 +37,7 @@ int main(int argc, char **argv)
 	struct sieve_message_data msgdata;
 	struct sieve_script_env scriptenv;
 	struct sieve_error_handler *ehandler;
+	struct ostream *teststream;
 	bool force_compile = FALSE;
 
 	bin_init();
@@ -128,9 +129,13 @@ int main(int argc, char **argv)
 	scriptenv.username = user;
 
 	ehandler = sieve_stderr_ehandler_create(0);	
-	
+
+	teststream = o_stream_create_fd(1, 0, FALSE);	
+
 	/* Run the test */
-	(void) sieve_test(sbin, &msgdata, &scriptenv, ehandler);
+	(void) sieve_test(sbin, &msgdata, &scriptenv, teststream, ehandler);
+
+	o_stream_destroy(&teststream);
 
 	sieve_close(&sbin);
 	sieve_error_handler_unref(&ehandler);
diff --git a/src/testsuite/testsuite.c b/src/testsuite/testsuite.c
index 51b5e01164f8df6eb38c1f5e25baca7db0266832..df45c84d6b21f161378251cbf557b94b4adc919b 100644
--- a/src/testsuite/testsuite.c
+++ b/src/testsuite/testsuite.c
@@ -143,6 +143,7 @@ int main(int argc, char **argv)
 	struct sieve_binary *sbin;
 	struct sieve_script_env scriptenv;
 	struct sieve_error_handler *ehandler;
+	struct ostream *teststream;
 
 	testsuite_init();
 
@@ -183,9 +184,13 @@ int main(int argc, char **argv)
 	scriptenv.username = user;
 
 	ehandler = sieve_stderr_ehandler_create(0);	
+
+	teststream = o_stream_create_fd(1, 0, FALSE);
 	
 	/* Run the test */
-	(void) sieve_test(sbin, &testsuite_msgdata, &scriptenv, ehandler);
+	(void) sieve_test(sbin, &testsuite_msgdata, &scriptenv, teststream, ehandler);
+
+	o_stream_destroy(&teststream);
 
 	sieve_close(&sbin);
 	sieve_error_handler_unref(&ehandler);