From 750ccb616db1083beb424f6e71d524e2fd142d92 Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan@rename-it.nl>
Date: Sat, 1 Dec 2007 19:06:18 +0100
Subject: [PATCH] Implemented actions reject and vacation.

---
 sieve/tests/vacation.sieve                    |   2 +-
 src/lib-sieve/cmd-redirect.c                  |  51 ++-
 src/lib-sieve/ext-reject.c                    | 181 +++++++-
 src/lib-sieve/plugins/vacation/ext-vacation.c | 386 +++++++++++++++++-
 src/lib-sieve/sieve-actions.c                 |  14 +
 src/lib-sieve/sieve-actions.h                 |   5 +
 src/lib-sieve/sieve-address-parts.c           |   2 +-
 src/lib-sieve/sieve.h                         |  26 +-
 src/sieve-bin/sieve-exec.c                    |  94 +++--
 src/sieve-bin/sieve-test.c                    |   1 +
 10 files changed, 706 insertions(+), 56 deletions(-)

diff --git a/sieve/tests/vacation.sieve b/sieve/tests/vacation.sieve
index f2565fb5f..b7e232a43 100644
--- a/sieve/tests/vacation.sieve
+++ b/sieve/tests/vacation.sieve
@@ -2,7 +2,7 @@ require "vacation";
 
 if address :contains "to" "vestingbar" {
 
-	vacation :subject "I am gone" :from "sirius@drunksnipers.com"
+	vacation :from "sirius@drunksnipers.com"
 		:days 30 "I am on vacation.";
 	stop;
 }
diff --git a/src/lib-sieve/cmd-redirect.c b/src/lib-sieve/cmd-redirect.c
index 2ceb9d520..608179435 100644
--- a/src/lib-sieve/cmd-redirect.c
+++ b/src/lib-sieve/cmd-redirect.c
@@ -1,5 +1,7 @@
 #include "lib.h"
 #include "str-sanitize.h"
+#include "istream.h"
+#include "istream-header-filter.h"
 
 #include "sieve-commands.h"
 #include "sieve-commands-private.h"
@@ -9,6 +11,8 @@
 #include "sieve-interpreter.h"
 #include "sieve-result.h"
 
+#include <stdio.h>
+
 /* Forward declarations */
 
 static bool cmd_redirect_opcode_dump
@@ -188,19 +192,52 @@ static void act_redirect_print
 	*keep = FALSE;
 }
 
+static bool act_redirect_send	
+	(const struct sieve_action_exec_env *aenv, struct act_redirect_context *ctx)
+{
+	const struct sieve_message_data *msgdata = aenv->msgdata;
+	const struct sieve_mail_environment *mailenv = aenv->mailenv;
+	struct istream *input;
+	static const char *hide_headers[] = { "Return-Path" };
+	void *smtp_handle;
+	FILE *f;
+	const unsigned char *data;
+	size_t size;
+	int ret;
+	
+	/* Just to be sure */
+	if ( mailenv->smtp_open == NULL || mailenv->smtp_close == NULL ) {
+		sieve_result_error(aenv, "redirect action has no means to send mail.");
+		return FALSE;
+	}
+	
+	if (mail_get_stream(msgdata->mail, NULL, NULL, &input) < 0)
+		return -1;
+		
+  smtp_handle = mailenv->smtp_open(ctx->to_address, msgdata->return_path, &f);
+
+  input = i_stream_create_header_filter
+  	(input, HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR, hide_headers,
+		N_ELEMENTS(hide_headers), null_header_filter_callback, NULL);
+
+	while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) {	
+		if (fwrite(data, size, 1, f) == 0)
+			break;
+		i_stream_skip(input, size);
+	}
+
+	return mailenv->smtp_close(smtp_handle);
+}
+
 static bool act_redirect_commit
 (const struct sieve_action *action ATTR_UNUSED, 
 	const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep)
 {
-	const struct sieve_message_data *msgdata = aenv->msgdata;
 	struct act_redirect_context *ctx = (struct act_redirect_context *) tr_context;
-	int res;
 	
-	if ((res = aenv->mailenv->
-		send_forward(msgdata, ctx->to_address)) == 0) {
-		i_info("msgid=%s: forwarded to <%s>",
-			msgdata->id == NULL ? "" : str_sanitize(msgdata->id, 80),
-			str_sanitize(ctx->to_address, 80));
+	if ( act_redirect_send(aenv, ctx) ) {
+		sieve_result_log(aenv, "forwarded to <%s>", 
+			str_sanitize(ctx->to_address, 80));	
 
 		*keep = FALSE;
   	return TRUE;
diff --git a/src/lib-sieve/ext-reject.c b/src/lib-sieve/ext-reject.c
index ff7af5004..c8ef054d7 100644
--- a/src/lib-sieve/ext-reject.c
+++ b/src/lib-sieve/ext-reject.c
@@ -9,12 +9,20 @@
  *
  */
 
-#include <stdio.h>
+#include "lib.h"
+#include "ioloop.h"
+#include "hostpid.h"
+#include "message-date.h"
+#include "message-size.h"
+#include "istream.h"
+#include "istream-header-filter.h"
 
 #include "sieve-extensions.h"
 #include "sieve-commands.h"
+#include "sieve-actions.h"
 #include "sieve-validator.h"
 #include "sieve-interpreter.h"
+#include "sieve-result.h"
 
 /* Forward declarations */
 
@@ -80,6 +88,28 @@ struct sieve_opcode reject_opcode = {
 	ext_reject_opcode_execute 
 };
 
+/* Reject action */
+
+static void act_reject_print
+	(const struct sieve_action *action, void *context, bool *keep);	
+static bool act_reject_commit
+(const struct sieve_action *action ATTR_UNUSED, 
+	const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep);
+		
+struct act_reject_context {
+	const char *reason;
+};
+
+const struct sieve_action act_reject = {
+	"reject",
+	NULL, 
+	NULL,
+	act_reject_print,
+	NULL, NULL,
+	act_reject_commit,
+	NULL
+};
+
 /* 
  * Validation 
  */
@@ -144,9 +174,15 @@ static bool ext_reject_opcode_execute
 (const struct sieve_opcode *opcode ATTR_UNUSED,
 	const struct sieve_runtime_env *renv, sieve_size_t *address)
 {
+	struct sieve_side_effects_list *slist = NULL;
+	struct act_reject_context *act;
 	string_t *reason;
+	pool_t pool;
 
 	t_push();
+	
+	if ( !sieve_interpreter_handle_optional_operands(renv, address, &slist) )
+		return FALSE;
 
 	if ( !sieve_opr_string_read(renv->sbin, address, &reason) ) {
 		t_pop();
@@ -155,7 +191,150 @@ static bool ext_reject_opcode_execute
 
 	printf(">> REJECT \"%s\"\n", str_c(reason));
 
+	/* Add reject action to the result */
+	pool = sieve_result_pool(renv->result);
+	act = p_new(pool, struct act_reject_context, 1);
+	act->reason = p_strdup(pool, str_c(reason));
+	
+	(void) sieve_result_add_action(renv, &act_reject, slist, (void *) act);
+	
 	t_pop();
 	return TRUE;
 }
 
+/*
+ * Action
+ */
+ 
+static void act_reject_print
+(const struct sieve_action *action ATTR_UNUSED, void *context, bool *keep)	
+{
+	struct act_reject_context *ctx = (struct act_reject_context *) context;
+	
+	printf("* reject message with reason: %s\n", ctx->reason);
+	
+	*keep = FALSE;
+}
+
+static bool act_reject_send	
+	(const struct sieve_action_exec_env *aenv, struct act_reject_context *ctx)
+{
+	const struct sieve_message_data *msgdata = aenv->msgdata;
+	const struct sieve_mail_environment *mailenv = aenv->mailenv;
+	struct istream *input;
+	void *smtp_handle;
+	struct message_size hdr_size;
+  FILE *f;
+	const char *new_msgid, *boundary;
+	const unsigned char *data;
+	const char *header;
+	size_t size;
+	int ret;
+
+	/* Just to be sure */
+	if ( mailenv->smtp_open == NULL || mailenv->smtp_close == NULL ) {
+		sieve_result_error(aenv, "reject action has no means to send mail.");
+		return FALSE;
+	}
+
+	smtp_handle = mailenv->smtp_open(msgdata->return_path, NULL, &f);
+
+  new_msgid = sieve_get_new_message_id(mailenv);
+	boundary = t_strdup_printf("%s/%s", my_pid, mailenv->hostname);
+
+	fprintf(f, "Message-ID: %s\r\n", new_msgid);
+	fprintf(f, "Date: %s\r\n", message_date_create(ioloop_time));
+	fprintf(f, "From: Mail Delivery Subsystem <%s>\r\n",
+	  mailenv->postmaster_address);
+	fprintf(f, "To: <%s>\r\n", msgdata->return_path);
+	fprintf(f, "MIME-Version: 1.0\r\n");
+	fprintf(f, "Content-Type: "
+	  "multipart/report; report-type=disposition-notification;\r\n"
+	  "\tboundary=\"%s\"\r\n", boundary);
+	fprintf(f, "Subject: Automatically rejected mail\r\n");
+	fprintf(f, "Auto-Submitted: auto-replied (rejected)\r\n");
+	fprintf(f, "Precedence: bulk\r\n");
+	fprintf(f, "\r\nThis is a MIME-encapsulated message\r\n\r\n");
+
+	/* Human readable status report */
+	fprintf(f, "--%s\r\n", boundary);
+	fprintf(f, "Content-Type: text/plain; charset=utf-8\r\n");
+	fprintf(f, "Content-Disposition: inline\r\n");
+	fprintf(f, "Content-Transfer-Encoding: 8bit\r\n\r\n");
+
+	/* FIXME: var_expand_table expansion not possible */
+	fprintf(f, "Your message to <%s> was automatically rejected:\r\n"	
+		"%s\r\n", msgdata->to_address, ctx->reason);
+
+  /* MDN status report */
+	fprintf(f, "--%s\r\n"
+		"Content-Type: message/disposition-notification\r\n\r\n", boundary);
+	fprintf(f, "Reporting-UA: %s; Dovecot Mail Delivery Agent\r\n",
+		mailenv->hostname);
+	if (mail_get_first_header(msgdata->mail, "Original-Recipient", &header) > 0)
+		fprintf(f, "Original-Recipient: rfc822; %s\r\n", header);
+	fprintf(f, "Final-Recipient: rfc822; %s\r\n",	msgdata->to_address);
+
+	if ( msgdata->id != NULL )
+		fprintf(f, "Original-Message-ID: %s\r\n", msgdata->id);
+	fprintf(f, "Disposition: "
+		"automatic-action/MDN-sent-automatically; deleted\r\n");
+	fprintf(f, "\r\n");
+
+	/* original message's headers */
+	fprintf(f, "--%s\r\nContent-Type: message/rfc822\r\n\r\n", boundary);
+
+  if (mail_get_stream(msgdata->mail, &hdr_size, NULL, &input) == 0) {
+    /* Note: If you add more headers, they need to be sorted.
+       We'll drop Content-Type because we're not including the message
+       body, and having a multipart Content-Type may confuse some
+       MIME parsers when they don't see the message boundaries. */
+    static const char *const exclude_headers[] = {
+	    "Content-Type"
+    };
+
+    input = i_stream_create_header_filter(input,
+    	HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR | HEADER_FILTER_HIDE_BODY, 
+    	exclude_headers, N_ELEMENTS(exclude_headers), 
+    	null_header_filter_callback, NULL);
+
+		while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) {
+			if (fwrite(data, size, 1, f) == 0)
+				break;
+				i_stream_skip(input, size);
+		}
+		i_stream_unref(&input);
+			
+		i_assert(ret != 0);
+	}
+
+	fprintf(f, "\r\n\r\n--%s--\r\n", boundary);
+
+	return mailenv->smtp_close(smtp_handle);
+}
+
+static bool act_reject_commit
+(const struct sieve_action *action ATTR_UNUSED, 
+	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;
+	
+	if ( msgdata->return_path == NULL || *(msgdata->return_path) == '\0' ) {
+    sieve_result_log(aenv, "discarded reject to <>");
+    
+    *keep = FALSE;
+    return TRUE;
+  }
+	
+	if ( act_reject_send(aenv, ctx) ) {
+		sieve_result_log(aenv, "rejected");	
+
+		*keep = FALSE;
+  	return TRUE;
+  }
+  
+	return FALSE;
+}
+
+
diff --git a/src/lib-sieve/plugins/vacation/ext-vacation.c b/src/lib-sieve/plugins/vacation/ext-vacation.c
index 9d2cd138f..b3f749cd2 100644
--- a/src/lib-sieve/plugins/vacation/ext-vacation.c
+++ b/src/lib-sieve/plugins/vacation/ext-vacation.c
@@ -9,16 +9,27 @@
  * 
  */
 
-#include <stdio.h>
+#include "lib.h"
+#include "md5.h"
+#include "hostpid.h"
+#include "str-sanitize.h"
+#include "message-address.h"
+#include "message-date.h"
+#include "ioloop.h"
 
 #include "sieve-common.h"
 
 #include "sieve-code.h"
 #include "sieve-extensions.h"
 #include "sieve-commands.h"
+#include "sieve-actions.h"
 #include "sieve-validator.h"
 #include "sieve-generator.h"
 #include "sieve-interpreter.h"
+#include "sieve-result.h"
+
+#include <stdio.h>
+
 
 /* Forward declarations */
 
@@ -89,6 +100,35 @@ const struct sieve_opcode vacation_opcode = {
 	ext_vacation_opcode_execute
 };
 
+/* Vacation action */
+
+static void act_vacation_print
+	(const struct sieve_action *action, void *context, bool *keep);	
+static bool act_vacation_commit
+(const struct sieve_action *action ATTR_UNUSED, 
+	const struct sieve_action_exec_env *aenv, void *tr_context, bool *keep);
+		
+struct act_vacation_context {
+	const char *reason;
+
+	sieve_size_t days;
+	const char *subject;
+	const char *handle;
+	bool mime;
+	const char *from;
+	const char *const *addresses;
+};
+
+const struct sieve_action act_vacation = {
+	"vacation",
+	NULL, 
+	NULL,
+	act_vacation_print,
+	NULL, NULL,
+	act_vacation_commit,
+	NULL
+};
+
 /* Tag validation */
 
 static bool cmd_vacation_validate_number_tag
@@ -338,9 +378,12 @@ static bool ext_vacation_opcode_execute
 (const struct sieve_opcode *opcode ATTR_UNUSED,
 	const struct sieve_runtime_env *renv, sieve_size_t *address)
 {	
+	struct sieve_side_effects_list *slist = NULL;
+	struct act_vacation_context *act;
+	pool_t pool;
 	int opt_code = 1;
-	sieve_size_t days = 0;
-	string_t *reason, *subject, *from, *handle;
+	sieve_size_t days = 7;
+	string_t *reason, *subject = NULL, *from = NULL, *handle = NULL; 
 		
 	if ( sieve_operand_optional_present(renv->sbin, address) ) {
 		while ( opt_code != 0 ) {
@@ -378,7 +421,344 @@ static bool ext_vacation_opcode_execute
 	
 	printf(">> VACATION \"%s\"\n", str_c(reason));
 	
+	/* Add vacation action to the result */
+	pool = sieve_result_pool(renv->result);
+	act = p_new(pool, struct act_vacation_context, 1);
+	act->reason = p_strdup(pool, str_c(reason));
+	if ( subject != NULL )
+		act->subject = p_strdup(pool, str_c(subject));
+	if ( from != NULL )
+		act->from = p_strdup(pool, str_c(from));
+	if ( handle != NULL )
+		act->handle = p_strdup(pool, str_c(handle));
+	act->days = days;
+	
+	/* FIXME: :addresses is ignored */
+	
+	(void) sieve_result_add_action(renv, &act_vacation, slist, (void *) act);
+	
 	return TRUE;
 }
 
+/*
+ * Action
+ */
+ 
+static void act_vacation_print
+(const struct sieve_action *action ATTR_UNUSED, 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);
+	if ( ctx->subject != NULL )
+		printf(	"    => subject: %s\n", ctx->subject);
+	if ( ctx->from != NULL )
+		printf(	"    => 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);
+}
+
+static const char * const _list_headers[] = {
+	"list-id",
+	"list-owner",
+	"list-subscribe",
+	"list-post",	
+	"list-unsubscribe",
+	"list-help",
+	"list-archive",
+	NULL
+};
+
+static const char * const _my_address_headers[] = {
+	"to",
+	"cc",
+	"bcc",
+	"resent-to",	
+	"resent-cc",
+	"resent-bcc",
+	NULL
+};
+
+static inline bool _is_system_address(const char *address)
+{
+	if ( strncasecmp(address, "MAILER-DAEMON", 13) == 0 )
+		return TRUE;
+
+	if ( strncasecmp(address, "LISTSERV", 8) == 0 )
+		return TRUE;
+
+	if ( strncasecmp(address, "majordomo", 9) == 0 )
+		return TRUE;
+
+	if ( strstr(address, "-request@") != NULL )
+		return TRUE;
+
+	if ( strncmp(address, "owner-", 6) == 0 )
+		return TRUE;
+
+	return FALSE;
+}
+
+static inline bool _contains_my_address
+	(const char * const *headers, const char *my_address)
+{
+	const char *const *hdsp = headers;
+	
+	while ( *hdsp != NULL ) {
+		const struct message_address *addr;
+
+		t_push();
+	
+		addr = message_address_parse
+			(pool_datastack_create(), (const unsigned char *) *hdsp, 
+				strlen(*hdsp), 256, FALSE);
+
+		while ( addr != NULL ) {
+			if (addr->domain != NULL) {
+				i_assert(addr->mailbox != NULL);
+
+				if ( strcmp(t_strconcat(addr->mailbox, "@", addr->domain, NULL),
+					my_address) == 0 ) {
+					t_pop();
+					return TRUE;
+				}
+			}
+
+			addr = addr->next;
+		}
+
+		t_pop();
+		
+		hdsp++;
+	}
+	
+	return FALSE;
+}
+
+static bool act_vacation_send	
+	(const struct sieve_action_exec_env *aenv, struct act_vacation_context *ctx)
+{
+	const struct sieve_message_data *msgdata = aenv->msgdata;
+	const struct sieve_mail_environment *mailenv = aenv->mailenv;
+	void *smtp_handle;
+  FILE *f;
+ 	const char *outmsgid;
+
+	/* Just to be sure */
+	if ( mailenv->smtp_open == NULL || mailenv->smtp_close == NULL ) {
+		sieve_result_error(aenv, "vacation action has no means to send mail.");
+		return FALSE;
+	}
+
+  smtp_handle = mailenv->smtp_open(msgdata->return_path, NULL, &f);
+  outmsgid = sieve_get_new_message_id(mailenv);
+    
+	fprintf(f, "Message-ID: %s\r\n", outmsgid);
+	fprintf(f, "Date: %s\r\n", message_date_create(ioloop_time));
+	if ( ctx->from != NULL && *(ctx->from) != '\0' )
+		fprintf(f, "From: <%s>\r\n", ctx->from);
+	else
+		fprintf(f, "From: <%s>\r\n", msgdata->to_address);
+		
+	fprintf(f, "To: <%s>\r\n", msgdata->return_path);
+	fprintf(f, "Subject: %s\r\n", str_sanitize(ctx->subject, 80));
+	if ( msgdata->id != NULL ) 
+		fprintf(f, "In-Reply-To: %s\r\n", msgdata->id);
+	fprintf(f, "Auto-Submitted: auto-replied (vacation)\r\n");
+
+	/* FIXME: What about the required references header ? */
+
+	fprintf(f, "X-Sieve: %s\r\n", SIEVE_IMPLEMENTATION);
+
+	fprintf(f, "Precedence: bulk\r\n");
+	fprintf(f, "MIME-Version: 1.0\r\n");
+    
+	if (	ctx->mime	) {
+		fprintf(f, "Content-Type: multipart/mixed;"
+			"\r\n\tboundary=\"%s/%s\"\r\n", my_pid, mailenv->hostname);
+		fprintf(f, "\r\nThis is a MIME-encapsulated message\r\n\r\n");
+		fprintf(f, "--%s/%s\r\n", my_pid, mailenv->hostname);
+	} else {
+		fprintf(f, "Content-Type: text/plain; charset=utf-8\r\n");
+		fprintf(f, "Content-Transfer-Encoding: 8bit\r\n");
+		fprintf(f, "\r\n");
+	}
+
+	fprintf(f, "%s\r\n", ctx->reason);
+    
+	if ( ctx->mime )
+		fprintf(f, "\r\n--%s/%s--\r\n", my_pid, mailenv->hostname);
+
+	if ( mailenv->smtp_close(smtp_handle) ) {
+		/*mailenv->duplicate_mark(outmsgid, strlen(outmsgid),
+		  mailenv->username, ioloop_time + DUPLICATE_DEFAULT_KEEP);*/
+		return TRUE;
+	}
+	
+	return FALSE;
+}
+
+static void act_vacation_hash
+(const struct sieve_message_data *msgdata, struct act_vacation_context *vctx, 
+	unsigned char hash_r[])
+{
+	struct md5_context ctx;
+
+	md5_init(&ctx);
+	md5_update(&ctx, msgdata->return_path, strlen(msgdata->return_path));
+
+	if ( vctx->handle != NULL && *(vctx->handle) != '\0' ) 
+		md5_update(&ctx, vctx->handle, strlen(vctx->handle));
+	else {
+		md5_update(&ctx, msgdata->to_address, strlen(msgdata->to_address));
+		md5_update(&ctx, vctx->reason, strlen(vctx->reason));
+	}
+
+	md5_final(&ctx, hash_r);
+}
+
+static bool act_vacation_commit
+(const struct sieve_action *action ATTR_UNUSED, 
+	const struct sieve_action_exec_env *aenv, void *tr_context, 
+	bool *keep ATTR_UNUSED)
+{
+	const char *const *hdsp;
+	const struct sieve_message_data *msgdata = aenv->msgdata;
+	const struct sieve_mail_environment *mailenv = aenv->mailenv;
+	struct act_vacation_context *ctx = (struct act_vacation_context *) tr_context;
+	unsigned char dupl_hash[MD5_RESULTLEN];
+	const char *const *headers;
+	pool_t pool;
+
+	/* Is the return_path unset ?
+	 */
+	if ( msgdata->return_path == NULL || *(msgdata->return_path) == '\0' ) {
+		sieve_result_log(aenv, "discarded vacation reply to <>");
+  	return TRUE;
+  }    
+	
+	/* Are we perhaps trying to respond to ourselves ? 
+	 * (FIXME: verify this to :addresses as well)
+	 */
+	if ( strcmp(msgdata->return_path, msgdata->to_address) == 0 ) {
+		sieve_result_log(aenv, "discarded vacation reply to own address");
+  	return TRUE;
+	}
+	
+	/* Did whe respond to this user before? */
+  if (mailenv->duplicate_check(dupl_hash, sizeof(dupl_hash), mailenv->username)) 
+  {
+		sieve_result_log(aenv, "discarded duplicate vacation response to <%s>",
+			str_sanitize(msgdata->return_path, 80));
+		return TRUE;
+	}
+	
+	/* Are we trying to respond to a mailing list ? */
+	hdsp = _list_headers;
+	while ( *hdsp != NULL ) {
+		if ( mail_get_headers_utf8
+			(msgdata->mail, *hdsp, &headers) >= 0 && headers[0] != NULL ) {	
+			/* Yes, bail out */
+			sieve_result_log(aenv, 
+				"discarding vacation response to mailinglist recipient <%s>", 
+				msgdata->return_path);	
+			return TRUE;				 
+		}
+		hdsp++;
+	}
+	
+	/* Is the message that we are replying to an automatic reply ? */
+	if ( mail_get_headers_utf8
+		(msgdata->mail, "auto-submitted", &headers) >= 0 ) {
+		/* Theoretically multiple headers could exist, so lets make sure */
+		hdsp = headers;
+		while ( *hdsp != NULL ) {
+			if ( strcasecmp(*hdsp, "no") != 0 ) {
+				sieve_result_log(aenv, 
+					"discardig vacation response to auto-submitted message from <%s>", 
+					msgdata->return_path);	
+					return TRUE;				 
+			}
+			hdsp++;
+		}
+	}
+	
+	/* Check for non-standard precedence header */
+	if ( mail_get_headers_utf8
+		(msgdata->mail, "precedence", &headers) >= 0 ) {
+		/* Theoretically multiple headers could exist, so lets make sure */
+		hdsp = headers;
+		while ( *hdsp != NULL ) {
+			if ( strcasecmp(*hdsp, "junk") == 0 || strcasecmp(*hdsp, "bulk") == 0 ||
+				strcasecmp(*hdsp, "list") == 0 ) {
+				sieve_result_log(aenv, 
+					"discarding vacation response to precedence=%s message from <%s>", 
+					*hdsp, msgdata->return_path);	
+					return TRUE;				 
+			}
+			hdsp++;
+		}
+	}
+	
+	/* Do not reply to system addresses */
+	if ( _is_system_address(msgdata->return_path) ) {
+		sieve_result_log(aenv, 
+			"not sending vacation response to system address <%s>", 
+			msgdata->return_path);	
+		return TRUE;				
+	} 
+	
+	/* Is the original message directly addressed to me? */
+	hdsp = _my_address_headers;
+	while ( *hdsp != NULL ) {
+		if ( mail_get_headers_utf8
+			(msgdata->mail, *hdsp, &headers) >= 0 && headers[0] != NULL ) {	
+			
+			if ( _contains_my_address(headers, msgdata->to_address) ) 
+				break;
+		}
+		hdsp++;
+	}	
+
+	if ( *hdsp == NULL ) {
+		/* No, bail out */
+		sieve_result_log(aenv, 
+			"discarding vacation response for implicitly delivered message", 
+			msgdata->return_path);	
+		return TRUE;				 
+	}	
+		
+	/* Make sure we have a subject for our reply */
+	if ( ctx->subject == NULL || *(ctx->subject) == '\0' ) {
+		if ( mail_get_headers_utf8
+			(msgdata->mail, "subject", &headers) >= 0 && headers[0] != NULL ) {
+			pool = sieve_result_pool(aenv->result);
+			ctx->subject = p_strconcat(pool, "Auto: ", headers[0], NULL);
+		}	else {
+			ctx->subject = "Automated reply";
+		}
+	}	
+	
+	/* Send the message */
+	
+	if ( act_vacation_send(aenv, ctx) ) {
+		sieve_result_log(aenv, "sent vacation response to <%s>", 
+			str_sanitize(msgdata->return_path, 80));	
+
+		mailenv->duplicate_mark(dupl_hash, sizeof(dupl_hash), mailenv->username,
+			ioloop_time + ctx->days * (24 * 60 * 60));
+
+  	return TRUE;
+  }
+
+	sieve_result_error(aenv, "failed to send vacation response to <%s>", 
+		str_sanitize(msgdata->return_path, 80));	
+	return FALSE;
+}
+
+
+
 
diff --git a/src/lib-sieve/sieve-actions.c b/src/lib-sieve/sieve-actions.c
index 7a2c73a25..6590abc89 100644
--- a/src/lib-sieve/sieve-actions.c
+++ b/src/lib-sieve/sieve-actions.c
@@ -1,4 +1,5 @@
 #include "lib.h"
+#include "ioloop.h"
 #include "str-sanitize.h"
 #include "mail-storage.h"
 #include "mail-namespace.h"
@@ -12,6 +13,19 @@
 
 #include <ctype.h>
 
+/*
+ * Message transmission (FIXME: place this somewhere more appropriate)
+ */
+const char *sieve_get_new_message_id
+	(const struct sieve_mail_environment *mailenv)
+{
+	static int count = 0;
+	
+	return t_strdup_printf("<dovecot-sieve-%s-%s-%d@%s>",
+		dec2str(ioloop_timeval.tv_sec), dec2str(ioloop_timeval.tv_usec),
+    count++, mailenv->hostname);
+}
+
 /* 
  * Side-effects 'extension' 
  */
diff --git a/src/lib-sieve/sieve-actions.h b/src/lib-sieve/sieve-actions.h
index c3f97bd10..91548f3ae 100644
--- a/src/lib-sieve/sieve-actions.h
+++ b/src/lib-sieve/sieve-actions.h
@@ -128,5 +128,10 @@ bool sieve_act_store_add_to_result
 	(const struct sieve_runtime_env *renv, 
 		struct sieve_side_effects_list *seffects, const char *folder);
 
+/* Message transmission */
+
+const char *sieve_get_new_message_id
+	(const struct sieve_mail_environment *mailenv);
+
 		
 #endif /* __SIEVE_ACTIONS_H */
diff --git a/src/lib-sieve/sieve-address-parts.c b/src/lib-sieve/sieve-address-parts.c
index f8155eb23..a48046766 100644
--- a/src/lib-sieve/sieve-address-parts.c
+++ b/src/lib-sieve/sieve-address-parts.c
@@ -347,7 +347,7 @@ bool sieve_address_match
 	t_push();
 	
 	addr = message_address_parse
-		(unsafe_data_stack_pool, (const unsigned char *) data, 
+		(pool_datastack_create(), (const unsigned char *) data, 
 			strlen(data), 256, FALSE);
 	
 	while (!matched && addr != NULL) {
diff --git a/src/lib-sieve/sieve.h b/src/lib-sieve/sieve.h
index 97dd8e51a..2516185b6 100644
--- a/src/lib-sieve/sieve.h
+++ b/src/lib-sieve/sieve.h
@@ -4,6 +4,11 @@
 #include "lib.h"
 #include "mail-storage.h"
 
+#include <stdio.h>
+
+#define SIEVE_VERSION "0.0.1"
+#define SIEVE_IMPLEMENTATION "Dovecot Sieve " SIEVE_VERSION
+
 struct sieve_binary;
 
 struct sieve_message_data {
@@ -18,12 +23,21 @@ struct sieve_mail_environment {
 	const char *inbox;
 	struct mail_namespace *namespaces;
 	
-	/* Interface for sending mail (callbacks if you like) */
-	int (*send_rejection)
-		(const struct sieve_message_data *msgdata, const char *recipient, 
-			const char *reason);
-	int (*send_forward)
-		(const struct sieve_message_data *msgdata, const char *forwardto);
+	const char *username;
+	const char *hostname;
+	const char *postmaster_address;
+	
+	/* Callbacks */
+	
+	/* Interface for sending mail */
+	void *(*smtp_open)
+		(const char *destination, const char *return_path, FILE **file_r);
+	bool (*smtp_close)(void *handle);
+	
+	/* Interface for marking and checking duplicates */
+	int (*duplicate_check)(const void *id, size_t id_size, const char *user);
+	void (*duplicate_mark)(const void *id, size_t id_size,
+                    const char *user, time_t time);
 };	
 
 bool sieve_init(const char *plugins);
diff --git a/src/sieve-bin/sieve-exec.c b/src/sieve-bin/sieve-exec.c
index 612154e96..447e2d0a6 100644
--- a/src/sieve-bin/sieve-exec.c
+++ b/src/sieve-bin/sieve-exec.c
@@ -20,28 +20,43 @@
 #define DEFAULT_SENDMAIL_PATH "/usr/lib/sendmail"
 #define DEFAULT_ENVELOPE_SENDER "MAILER-DAEMON"
 
-static int sieve_send_rejection
-(const struct sieve_message_data *msgdata ATTR_UNUSED, 
-	const char *recipient, const char *reason)
+static void *sieve_smtp_open(const char *destination,
+	const char *return_path, FILE **file_r)
+{getenv("HOSTNAME");
+	printf("Sending mesage from <%s> to <%s>:\n\nSTART MESSAGE:\n", 
+		return_path == NULL || *return_path == '\0' ? "" : return_path, 
+		destination);
+	
+	*file_r = stdout;
+	
+	return NULL;	
+}
+
+static bool sieve_smtp_close(void *handle ATTR_UNUSED)
 {
-	i_info("<<NOT PERFORMED>> Rejected mail to %s with reason \"%s\"\n", 
-		recipient, reason);  
-	return 0;
+	printf("END MESSAGE\n\n");
+	return TRUE;
 }
 
-static int sieve_send_forward
-(const struct sieve_message_data *msgdata ATTR_UNUSED, 
-	const char *forwardto)
+static int duplicate_check(const void *id ATTR_UNUSED, size_t id_size ATTR_UNUSED, 
+	const char *user)
 {
-	i_info("<<NOT PERFORMED>> Forwarded mail to %s.", forwardto);
+	printf("Checked duplicate for user %s.\n", user);
 	return 0;
 }
 
+static void duplicate_mark
+(const void *id ATTR_UNUSED, size_t id_size ATTR_UNUSED, const char *user, 
+	time_t time ATTR_UNUSED)
+{
+	printf("Marked duplicate for user %s.\n", user);
+}
+
 int main(int argc, char **argv) 
 {
 	const char *user;
-    struct passwd *pw;
-    uid_t process_euid;
+	struct passwd *pw;
+	uid_t process_euid;
 	int sfd, mfd;
 	pool_t namespaces_pool;
 	struct mail_namespace *ns;
@@ -57,7 +72,7 @@ int main(int argc, char **argv)
  		exit(1);
  	}
   
-  	/* Open sieve script */
+	/* Open sieve script */
   
 	if ( (sfd = open(argv[1], O_RDONLY)) < 0 ) {
 		perror("open()");
@@ -67,17 +82,17 @@ int main(int argc, char **argv)
  	/* Open mail file */
  
 	if ( argc > 2 )
-    {
-        if ( strcmp(argv[2], "-") == 0 )
-            mfd = 0;
-        else {
-            if ( (mfd = open(argv[2], O_RDONLY)) < 0 ) {
-                perror("Failed to open mail file");
-                exit(1);
-            }
-        }
-    } else
-        mfd = 0;
+	{
+		if ( strcmp(argv[2], "-") == 0 )
+			mfd = 0;
+		else {
+			if ( (mfd = open(argv[2], O_RDONLY)) < 0 ) {
+				perror("Failed to open mail file");
+				exit(1);
+			}
+		}
+	} else
+		mfd = 0;
 
 	/* Compile sieve script */
 	
@@ -97,14 +112,14 @@ int main(int argc, char **argv)
 
  	close(sfd);
 
-    process_euid = geteuid();
-    pw = getpwuid(process_euid);
-    if (pw != NULL) {
-        user = t_strdup(pw->pw_name);
-    } else {
-        i_fatal("Couldn't lookup our username (uid=%s)",
-            dec2str(process_euid));
-    }
+	process_euid = geteuid();
+	pw = getpwuid(process_euid);
+	if (pw != NULL) {
+		user = t_strdup(pw->pw_name);
+	} else {
+		i_fatal("Couldn't lookup our username (uid=%s)",
+		dec2str(process_euid));
+	}
 
 	env_put(t_strdup_printf("NAMESPACE_1=%s", "maildir:/home/stephan/Maildir"));
 	env_put("NAMESPACE_1_INBOX=1");
@@ -115,7 +130,7 @@ int main(int argc, char **argv)
 	namespaces_pool = namespaces_init();
 	
 	if (mail_namespaces_init(namespaces_pool, user, &ns) < 0)
-        i_fatal("Namespace initialization failed");
+		i_fatal("Namespace initialization failed");
 
 	mail_raw_init(namespaces_pool, user);
 	mailr = mail_raw_open(mfd);
@@ -124,15 +139,20 @@ int main(int argc, char **argv)
 	memset(&msgdata, 0, sizeof(msgdata));
 	msgdata.mail = mailr->mail;
 	msgdata.return_path = "nico@example.com";
-	msgdata.to_address = "sirius+sieve@rename-it.nl";
-	msgdata.auth_user = "stephan";
+	msgdata.to_address = "sirius@rename-it.nl";
+	msgdata.auth_user = "nico";
 	(void)mail_get_first_header(mailr->mail, "Message-ID", &msgdata.id);
 	
 	memset(&mailenv, 0, sizeof(mailenv));
 	mailenv.inbox = "INBOX";
 	mailenv.namespaces = ns;
-	mailenv.send_forward = sieve_send_forward;
-	mailenv.send_rejection = sieve_send_rejection;
+	mailenv.username = "stephan";
+	mailenv.hostname = "host.example.com";
+	mailenv.postmaster_address = "postmaster@example.com";
+	mailenv.smtp_open = sieve_smtp_open;
+	mailenv.smtp_close = sieve_smtp_close;
+	mailenv.duplicate_mark = duplicate_mark;
+	mailenv.duplicate_check = duplicate_check;
 	
 	/* Run */
 	sieve_execute(sbin, &msgdata, &mailenv);
diff --git a/src/sieve-bin/sieve-test.c b/src/sieve-bin/sieve-test.c
index 3a60aaa09..d62b28992 100644
--- a/src/sieve-bin/sieve-test.c
+++ b/src/sieve-bin/sieve-test.c
@@ -98,6 +98,7 @@ int main(int argc, char **argv)
 
 	memset(&mailenv, 0, sizeof(mailenv));
     mailenv.inbox = "INBOX";
+	mailenv.username = "stephan";
 	
 	/* Run the test */
 	(void) sieve_test(sbin, &msgdata, &mailenv);
-- 
GitLab