From dd70d6650f15374ac2284bec757d4a766d12623f Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan@rename-it.nl>
Date: Fri, 7 Aug 2009 23:50:27 +0200
Subject: [PATCH] Mailbox extension: implemented the :create tagged argument
 for the fileinto command.

---
 .../plugins/mailbox/tag-mailbox-create.c      | 71 +++++++++++++---
 src/lib-sieve/sieve-actions.c                 | 81 ++++++++++++-------
 src/lib-sieve/sieve-actions.h                 |  6 +-
 3 files changed, 116 insertions(+), 42 deletions(-)

diff --git a/src/lib-sieve/plugins/mailbox/tag-mailbox-create.c b/src/lib-sieve/plugins/mailbox/tag-mailbox-create.c
index d6a70424d..228c0044c 100644
--- a/src/lib-sieve/plugins/mailbox/tag-mailbox-create.c
+++ b/src/lib-sieve/plugins/mailbox/tag-mailbox-create.c
@@ -2,6 +2,8 @@
  */
 
 #include "lib.h"
+#include "mail-storage.h"
+#include "mail-namespace.h"
 
 #include "sieve-common.h"
 #include "sieve-commands.h"
@@ -38,19 +40,18 @@ const struct sieve_argument mailbox_create_tag = {
 static void seff_mailbox_create_print
 	(const struct sieve_side_effect *seffect, const struct sieve_action *action, 
 		const struct sieve_result_print_env *rpenv, void *se_context, bool *keep);
-static void seff_mailbox_create_post_commit
+static bool seff_mailbox_create_pre_execute
 	(const struct sieve_side_effect *seffect, const struct sieve_action *action, 
-		const struct sieve_action_exec_env *aenv, void *se_context,
-		void *tr_context, bool *keep);
+		const struct sieve_action_exec_env *aenv, void **se_context, 
+		void *tr_context);;
 
 const struct sieve_side_effect mailbox_create_side_effect = {
 	SIEVE_OBJECT("create", &mailbox_create_operand, 0),
 	&act_store,
 	NULL, NULL, NULL,
 	seff_mailbox_create_print,
-	NULL, NULL,
-	seff_mailbox_create_post_commit, 
-	NULL
+	seff_mailbox_create_pre_execute, 
+	NULL, NULL, NULL
 };
 
 /*
@@ -112,13 +113,63 @@ static void seff_mailbox_create_print
 	sieve_result_seffect_printf(rpenv, "create mailbox if it does not exist");
 }
 
-static void seff_mailbox_create_post_commit
+static bool seff_mailbox_create_pre_execute
 (const struct sieve_side_effect *seffect ATTR_UNUSED, 
-	const struct sieve_action *action ATTR_UNUSED, 
+	const struct sieve_action *action, 
 	const struct sieve_action_exec_env *aenv ATTR_UNUSED,
-	void *se_context ATTR_UNUSED, void *tr_context ATTR_UNUSED, 
-	bool *keep ATTR_UNUSED)
+	void **se_context ATTR_UNUSED, void *tr_context ATTR_UNUSED)
 {	
+	struct act_store_transaction *trans = 
+		(struct act_store_transaction *) tr_context;
+	struct mail_storage **storage = &(aenv->exec_status->last_storage);
+	enum mailbox_flags flags =
+		MAILBOX_FLAG_KEEP_RECENT | MAILBOX_FLAG_SAVEONLY |
+		MAILBOX_FLAG_POST_SESSION;
+	struct mailbox *box = NULL;
+	
+	/* Check whether creation is necessary */
+	if ( trans->box != NULL || trans->redundant || trans->disabled ) 
+		return TRUE;
+
+	/* Check availability of namespace and folder name */
+	if ( trans->namespace == NULL || trans->folder == NULL )
+		return FALSE;
+
+	/* Check whether creation has a chance of working */
+	if ( trans->error_code != MAIL_ERROR_NONE 
+		&& trans->error_code != MAIL_ERROR_NOTFOUND )
+		return FALSE;
+
+	*storage = trans->namespace->storage; 
+
+    box = mailbox_alloc(trans->namespace->list, trans->folder, NULL, flags);
+	/* Create mailbox */
+	if ( mailbox_create(box, NULL, FALSE) < 0 ) {
+		mailbox_close(&box);
+		box = NULL;
+
+	} else {
+		/* Subscribe to it if necessary */
+		if ( aenv->scriptenv->mailbox_autosubscribe ) {
+			(void)mailbox_list_set_subscribed
+				(trans->namespace->list, trans->folder, TRUE);
+		}
+
+		/* Try opening again */
+		if ( mailbox_open(box) < 0 || mailbox_sync(box, 0, 0, NULL) < 0 ) {
+			/* Failed definitively */
+			mailbox_close(&box);
+			box = NULL;
+		}
+	} 
+
+	/* Fetch error */
+	if ( box == NULL )
+		sieve_act_store_get_storage_error(aenv, trans);	
+
+	trans->box = box;
+	
+	return ( box != NULL );
 }
 
 
diff --git a/src/lib-sieve/sieve-actions.c b/src/lib-sieve/sieve-actions.c
index 249278a6b..cee98d669 100644
--- a/src/lib-sieve/sieve-actions.c
+++ b/src/lib-sieve/sieve-actions.c
@@ -106,7 +106,7 @@ const struct sieve_action act_store = {
 
 int sieve_act_store_add_to_result
 (const struct sieve_runtime_env *renv, 
-	struct sieve_side_effects_list *seffects, const char *folder,
+	struct sieve_side_effects_list *seffects, const char *mailbox,
 	unsigned int source_line)
 {
 	pool_t pool;
@@ -115,7 +115,7 @@ int sieve_act_store_add_to_result
 	/* Add redirect action to the result */
 	pool = sieve_result_pool(renv->result);
 	act = p_new(pool, struct act_store_context, 1);
-	act->folder = p_strdup(pool, folder);
+	act->mailbox = p_strdup(pool, mailbox);
 
 	return sieve_result_add_action(renv, &act_store, seffects, 
 		source_line, (void *) act, 0);
@@ -168,6 +168,16 @@ void sieve_act_store_add_flags
 	trans->flags_altered = TRUE;
 }
 
+void sieve_act_store_get_storage_error
+(const struct sieve_action_exec_env *aenv, struct act_store_transaction *trans)
+{
+	pool_t pool = sieve_result_pool(aenv->result);
+	
+	trans->error = p_strdup(pool, 
+		mail_storage_get_last_error(trans->namespace->storage, &trans->error_code));
+}
+
+
 /* Equality */
 
 static bool act_store_equals
@@ -175,21 +185,23 @@ static bool act_store_equals
 {
 	struct act_store_context *st_ctx1 = (struct act_store_context *) ctx1;
 	struct act_store_context *st_ctx2 = (struct act_store_context *) ctx2;
-	const char *folder1, *folder2;
+	const char *mailbox1, *mailbox2;
 	
+	/* FIXME: consider namespace aliases */
+
 	if ( st_ctx1 == NULL && st_ctx2 == NULL )
 		return TRUE;
 		
-	folder1 = ( st_ctx1 == NULL ? 
-		SIEVE_SCRIPT_DEFAULT_MAILBOX(senv) : st_ctx1->folder );
-	folder2 = ( st_ctx2 == NULL ? 
-		SIEVE_SCRIPT_DEFAULT_MAILBOX(senv) : st_ctx2->folder );
+	mailbox1 = ( st_ctx1 == NULL ? 
+		SIEVE_SCRIPT_DEFAULT_MAILBOX(senv) : st_ctx1->mailbox );
+	mailbox2 = ( st_ctx2 == NULL ? 
+		SIEVE_SCRIPT_DEFAULT_MAILBOX(senv) : st_ctx2->mailbox );
 	
-	if ( strcmp(folder1, folder2) == 0 ) 
+	if ( strcmp(mailbox1, mailbox2) == 0 ) 
 		return TRUE;
 		
 	return 
-		( strcasecmp(folder1, "INBOX") == 0 && strcasecmp(folder2, "INBOX") == 0 ); 
+		( strcasecmp(mailbox1, "INBOX") == 0 && strcasecmp(mailbox2, "INBOX") == 0 ); 
 
 }
 
@@ -211,13 +223,13 @@ static void act_store_print
 	const struct sieve_result_print_env *rpenv, void *context, bool *keep)	
 {
 	struct act_store_context *ctx = (struct act_store_context *) context;
-	const char *folder;
+	const char *mailbox;
 
-	folder = ( ctx == NULL ? 
-		SIEVE_SCRIPT_DEFAULT_MAILBOX(rpenv->scriptenv) : ctx->folder );	
+	mailbox = ( ctx == NULL ? 
+		SIEVE_SCRIPT_DEFAULT_MAILBOX(rpenv->scriptenv) : ctx->mailbox );	
 
 	sieve_result_action_printf(rpenv, "store message in folder: %s", 
-		str_sanitize(folder, 128));
+		str_sanitize(mailbox, 128));
 	
 	*keep = FALSE;
 }
@@ -235,8 +247,8 @@ static void act_store_get_storage_error
 }
 
 static struct mailbox *act_store_mailbox_open
-(const struct sieve_action_exec_env *aenv, const char **name,
-	struct mail_namespace **ns_r)
+(const struct sieve_action_exec_env *aenv, const char **mailbox,
+	struct mail_namespace **ns_r, const char **folder_r)
 {
 	struct mail_storage **storage = &(aenv->exec_status->last_storage);
 	enum mailbox_flags flags =
@@ -244,28 +256,28 @@ static struct mailbox *act_store_mailbox_open
 		MAILBOX_FLAG_POST_SESSION;
 	struct mailbox *box;
 	enum mail_error error;
-	const char *folder = *name;
 
-	if (strcasecmp(folder, "INBOX") == 0) {
+	if (strcasecmp(*mailbox, "INBOX") == 0) {
 		/* Deliveries to INBOX must always succeed, regardless of ACLs */
 		flags |= MAILBOX_FLAG_IGNORE_ACLS;
 	}
 
-	*ns_r = mail_namespace_find(aenv->scriptenv->namespaces, &folder);
+	*folder_r = *mailbox;
+	*ns_r = mail_namespace_find(aenv->scriptenv->namespaces, folder_r);
 	if ( *ns_r == NULL) {
 		*storage = NULL;
 		return NULL;
 	}
 	
-	if ( *folder == '\0' ) {
+	if ( **folder_r == '\0' ) {
 		/* delivering to a namespace prefix means we actually want to
 		 * deliver to the INBOX instead
 		 */
-		folder = *name = "INBOX";
+		*folder_r = *mailbox = "INBOX";
 		flags |= MAILBOX_FLAG_IGNORE_ACLS;
 
-		*ns_r = mail_namespace_find(aenv->scriptenv->namespaces, &folder);
-    	if ( *ns_r == NULL) {
+		*ns_r = mail_namespace_find(aenv->scriptenv->namespaces, folder_r);
+		if ( *ns_r == NULL) {
 			*storage = NULL;
 			return NULL;
 		}
@@ -274,7 +286,7 @@ static struct mailbox *act_store_mailbox_open
 	}
 
 	/* First attempt at opening the box */
-	box = mailbox_alloc((*ns_r)->list, folder, NULL, flags);
+	box = mailbox_alloc((*ns_r)->list, *folder_r, NULL, flags);
 	if ( mailbox_open(box) == 0 ) {
 		/* Success */
 		return box;
@@ -302,7 +314,7 @@ static struct mailbox *act_store_mailbox_open
 
 	/* Subscribe to it if required */
 	if ( aenv->scriptenv->mailbox_autosubscribe ) {
-		(void)mailbox_list_set_subscribed((*ns_r)->list, folder, TRUE);
+		(void)mailbox_list_set_subscribed((*ns_r)->list, *folder_r, TRUE);
 	}
 
 	/* Try opening again */
@@ -325,13 +337,14 @@ static bool act_store_start
 	struct act_store_transaction *trans;
 	struct mail_namespace *ns = NULL;
 	struct mailbox *box = NULL;
+	const char *folder;
 	pool_t pool = sieve_result_pool(aenv->result);
 	bool disabled = FALSE, redundant = FALSE;
 
 	/* If context is NULL, the store action is the result of (implicit) keep */	
 	if ( ctx == NULL ) {
 		ctx = p_new(pool, struct act_store_context, 1);
-		ctx->folder = p_strdup(pool, SIEVE_SCRIPT_DEFAULT_MAILBOX(senv));
+		ctx->mailbox = p_strdup(pool, SIEVE_SCRIPT_DEFAULT_MAILBOX(senv));
 	}
 
 	/* Open the requested mailbox */
@@ -340,10 +353,10 @@ static bool act_store_start
 	 * to NULL. This implementation will then skip actually storing the message.
 	 */
 	if ( senv->namespaces != NULL ) {
-		box = act_store_mailbox_open(aenv, &ctx->folder, &ns);
+		box = act_store_mailbox_open(aenv, &ctx->mailbox, &ns, &folder);
 
 		/* Check whether we are trying to store the message in the folder it
-		 * originates from. In that case skip
+		 * originates from. In that case we skip actually storing it.
 		 */
 		if ( box != NULL && mailbox_backends_equal(box, msgdata->mail->box) ) {
 			mailbox_close(&box);
@@ -360,15 +373,21 @@ static bool act_store_start
 
 	trans->context = ctx;
 	trans->namespace = ns;
+	trans->folder = folder;
 	trans->box = box;
 	trans->flags = 0;
 
 	trans->disabled = disabled;
 	trans->redundant = redundant;
+
+	if ( ns != NULL && box == NULL )
+		sieve_act_store_get_storage_error(aenv, trans);
 	
 	*tr_context = (void *)trans;
 
-	return ( disabled || redundant || (box != NULL) );
+	return ( (box != NULL)
+		|| (trans->error_code == MAIL_ERROR_NOTFOUND)
+		|| disabled || redundant );
 }
 
 static struct mail_keywords *act_store_keywords_create
@@ -437,7 +456,7 @@ static bool act_store_execute
 		return FALSE;
 
 	/* Mark attempt to store in default mailbox */
-	if ( strcmp(trans->context->folder, 
+	if ( strcmp(trans->context->mailbox, 
 		SIEVE_SCRIPT_DEFAULT_MAILBOX(aenv->scriptenv)) == 0 ) 
 		aenv->exec_status->tried_default_save = TRUE;
 
@@ -465,7 +484,7 @@ static bool act_store_execute
 	}
 
 	if ( mailbox_copy(&save_ctx, aenv->msgdata->mail) < 0 ) {
-		act_store_get_storage_error(aenv, trans);
+		sieve_act_store_get_storage_error(aenv, trans);
  		result = FALSE;
  	}
  	
@@ -483,7 +502,7 @@ static void act_store_log_status
 {
 	const char *mailbox_name;
 	
-	mailbox_name = str_sanitize(trans->context->folder, 128);
+	mailbox_name = str_sanitize(trans->context->mailbox, 128);
 
 	/* Store disabled? */
 	if ( trans->disabled ) {
diff --git a/src/lib-sieve/sieve-actions.h b/src/lib-sieve/sieve-actions.h
index c9128a8cb..e14a8a615 100644
--- a/src/lib-sieve/sieve-actions.h
+++ b/src/lib-sieve/sieve-actions.h
@@ -187,7 +187,7 @@ extern const struct sieve_action act_discard;
 
 struct act_store_context {
 	/* Folder name represented in modified utf-7 */
-	const char *folder; 
+	const char *mailbox; 
 };
 
 struct act_store_transaction {
@@ -196,7 +196,11 @@ struct act_store_transaction {
 	struct mailbox *box;
 	struct mailbox_transaction_context *mail_trans;
 	struct mail *dest_mail;
+
+	const char *folder;
+
 	const char *error;
+	enum mail_error error_code;
 	
 	enum mail_flags flags;
 	ARRAY_TYPE(const_string) keywords;
-- 
GitLab