From 0f4500e8bfe9fed421377d7edff3f0252a1c5a99 Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan.bosch@open-xchange.com>
Date: Mon, 7 Oct 2024 00:30:59 +0200
Subject: [PATCH] lib-sieve: sieve-storage - Refactor handling of default
 script

Open the default storage separately, rather then the script directly.
---
 src/lib-sieve/sieve-storage-private.h |   3 +-
 src/lib-sieve/sieve-storage.c         | 208 +++++++++++++++++++-------
 2 files changed, 160 insertions(+), 51 deletions(-)

diff --git a/src/lib-sieve/sieve-storage-private.h b/src/lib-sieve/sieve-storage-private.h
index 8b609f51d..7ed7f31dc 100644
--- a/src/lib-sieve/sieve-storage-private.h
+++ b/src/lib-sieve/sieve-storage-private.h
@@ -106,7 +106,7 @@ struct sieve_storage {
 
 	const char *default_name;
 	const char *default_location;
-	struct sieve_storage *default_storage_for;
+	struct sieve_storage *default_storage, *default_storage_for;
 
 	struct mail_namespace *sync_inbox_ns;
 
@@ -138,6 +138,7 @@ int sieve_storage_active_script_is_default(struct sieve_storage *storage);
 
 struct sieve_storage_list_context {
 	struct sieve_storage *storage;
+	struct sieve_storage *def_storage;
 
 	bool seen_active:1; // Just present for assertions
 	bool seen_default:1;
diff --git a/src/lib-sieve/sieve-storage.c b/src/lib-sieve/sieve-storage.c
index df53080c3..90a36541d 100644
--- a/src/lib-sieve/sieve-storage.c
+++ b/src/lib-sieve/sieve-storage.c
@@ -473,6 +473,62 @@ int sieve_storage_create(struct sieve_instance *svinst, struct event *event,
 				  storage_r, error_code_r);
 }
 
+static int
+sieve_storage_create_default_for(struct sieve_storage *storage,
+				 struct sieve_storage **storage_r,
+				 enum sieve_error *error_code_r)
+{
+	*storage_r = NULL;
+	sieve_error_args_init(&error_code_r, NULL);
+
+	if (storage->default_storage != NULL) {
+		sieve_storage_ref(storage->default_storage);
+		*storage_r = storage->default_storage;
+		return 0;
+	}
+
+	if (storage->default_name == NULL ||
+	    storage->default_location == NULL) {
+		sieve_storage_set_not_found_error(storage, NULL);
+		*error_code_r = storage->error_code;
+		return -1;
+	}
+
+	struct sieve_instance *svinst = storage->svinst;
+	enum sieve_error error_code;
+
+	i_assert(storage->default_storage_for == NULL);
+	i_assert(*storage->default_location != '\0');
+	if (sieve_storage_create(svinst, svinst->event,
+				 storage->default_location, 0,
+				 &storage->default_storage, &error_code) < 0) {
+		switch (error_code) {
+		case SIEVE_ERROR_NOT_FOUND:
+			sieve_storage_set_error(storage, error_code,
+				"Default script not found");
+			break;
+		case SIEVE_ERROR_TEMP_FAILURE:
+			sieve_storage_set_error(storage, error_code,
+				"Failed to access default script "
+				"(temporary failure)");
+			break;
+		default:
+			sieve_storage_set_error(storage, error_code,
+				"Failed to access default script");
+			break;
+		}
+		*error_code_r = error_code;
+		return -1;
+	}
+
+	storage->default_storage->default_storage_for = storage;
+	storage->default_storage->is_default = TRUE;
+	sieve_storage_ref(storage);
+
+	*storage_r = storage->default_storage;
+	return 0;
+}
+
 static int
 sieve_storage_do_create_personal(struct sieve_instance *svinst,
 				 struct mail_user *user,
@@ -672,6 +728,7 @@ void sieve_storage_unref(struct sieve_storage **_storage)
 
 	if (storage->default_storage_for != NULL) {
 		i_assert(storage->is_default);
+		storage->default_storage_for->default_storage = NULL;
 		sieve_storage_unref(&storage->default_storage_for);
 	}
 
@@ -803,7 +860,8 @@ sieve_storage_get_default_script(struct sieve_storage *storage,
 				 struct sieve_script **script_r,
 				 enum sieve_error *error_code_r)
 {
-	struct sieve_instance *svinst = storage->svinst;
+	struct sieve_storage *def_storage;
+	int ret;
 
 	if (*error_code_r != SIEVE_ERROR_NOT_FOUND ||
 	    (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) != 0)
@@ -811,27 +869,37 @@ sieve_storage_get_default_script(struct sieve_storage *storage,
 
 	/* Not found; if this name maps to the default script,
 	   try to access that instead */
+	e_debug(storage->event, "Trying default script instead");
 
-	if (storage->default_name == NULL ||
-	    storage->default_location == NULL ||
-	    strcmp(storage->default_name, name) != 0)
+	/* Failed; try using default script location
+	   (not for temporary failures, read/write access, or dsync) */
+	ret = sieve_storage_create_default_for(storage, &def_storage,
+					       error_code_r);
+	if (ret < 0)
 		return -1;
 
-	struct sieve_script *script;
-
-	i_assert(*storage->default_location != '\0');
+	if (strcmp(storage->default_name, name) != 0) {
+		sieve_storage_set_error(storage,
+			SIEVE_ERROR_NOT_FOUND,
+			"Default script '%s' not found",
+			str_sanitize(name, 80));
+		*error_code_r = storage->error_code;
+		sieve_storage_unref(&def_storage);
+		return -1;
+	}
 
-	e_debug(storage->event, "Trying default script instead");
+	struct sieve_script *def_script;
 
-	if (sieve_script_create(svinst,	storage->default_location, NULL,
-				&script, error_code_r) < 0)
+	ret = sieve_storage_get_script_direct(def_storage, name,
+					      &def_script, error_code_r);
+	if (ret < 0)
+		sieve_storage_copy_error(storage, def_storage);
+	sieve_storage_unref(&def_storage);
+	if (ret < 0)
 		return -1;
+	i_assert(def_script != NULL);
 
-	script->storage->is_default = TRUE;
-	script->storage->default_storage_for = storage;
-	sieve_storage_ref(storage);
-
-	*script_r = script;
+	*script_r = def_script;
 	return 0;
 }
 
@@ -929,8 +997,6 @@ sieve_storage_active_script_do_get_name(struct sieve_storage *storage,
 					const char **name_r, bool *default_r)
 					ATTR_NULL(3)
 {
-	struct sieve_instance *svinst = storage->svinst;
-	enum sieve_error error_code;
 	int ret;
 
 	if (default_r != NULL)
@@ -940,16 +1006,24 @@ sieve_storage_active_script_do_get_name(struct sieve_storage *storage,
 	ret = storage->v.active_script_get_name(storage, name_r);
 
 	if (ret != 0 ||
-	    (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) != 0 ||
-	    storage->default_location == NULL ||
-	    storage->default_name == NULL) {
+	    (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) != 0)
 		return ret;
-	}
+
+	struct sieve_storage *def_storage;
+
+	/* Failed; try using default script location
+	   (not for temporary failures, read/write access, or dsync) */
+	ret = sieve_storage_create_default_for(storage, &def_storage, NULL);
+	if (ret < 0)
+		return -1;
 
 	*name_r = storage->default_name;
 
-	ret = sieve_script_check(svinst, storage->default_location,
-				 NULL, &error_code);
+	ret = sieve_storage_check_script(def_storage,
+					 storage->default_name, NULL);
+	if (ret < 0)
+		sieve_storage_copy_error(storage, def_storage);
+	sieve_storage_unref(&def_storage);
 	if (ret <= 0)
 		return ret;
 
@@ -979,7 +1053,6 @@ int sieve_storage_active_script_open(struct sieve_storage *storage,
 				     struct sieve_script **script_r,
 				     enum sieve_error *error_code_r)
 {
-	struct sieve_instance *svinst = storage->svinst;
 	struct sieve_script *script = NULL;
 	int ret;
 
@@ -992,25 +1065,27 @@ int sieve_storage_active_script_open(struct sieve_storage *storage,
 	i_assert(ret <= 0);
 
 	if (ret == 0 ||
-	    (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) != 0 ||
-	    storage->default_location == NULL) {
+	    (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) != 0) {
 		if (ret < 0)
 			*error_code_r = storage->error_code;
 		*script_r = script;
 		return ret;
 	}
 
+	struct sieve_storage *def_storage;
+
 	/* Try default script location */
-	if (sieve_script_create_open(svinst, storage->default_location, NULL,
-				     &script, error_code_r) < 0)
+	ret = sieve_storage_create_default_for(storage, &def_storage,
+					       error_code_r);
+	if (ret < 0)
 		return -1;
 
-	script->storage->is_default = TRUE;
-	script->storage->default_storage_for = storage;
-	sieve_storage_ref(storage);
-
-	*script_r = script;
-	return 0;
+	ret = sieve_storage_open_script(def_storage, NULL,
+					script_r, error_code_r);
+	if (ret < 0)
+		sieve_storage_copy_error(storage, def_storage);
+	sieve_storage_unref(&def_storage);
+	return ret;
 }
 
 int sieve_storage_deactivate(struct sieve_storage *storage, time_t mtime)
@@ -1069,6 +1144,23 @@ int sieve_storage_list_init(struct sieve_storage *storage,
 		return -1;
 
 	lctx->storage = storage;
+	sieve_storage_ref(storage);
+
+	if ((storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) != 0) {
+		/* No default script involved; return right away */
+		*lctx_r = lctx;
+		return 0;
+	}
+
+	/* May need to list default script as well */
+
+	enum sieve_error error_code;
+
+	if (sieve_storage_create_default_for(storage, &lctx->def_storage,
+					     &error_code) < 0 &&
+	    error_code != SIEVE_ERROR_NOT_FOUND)
+		return -1;
+
 	*lctx_r = lctx;
 	return 0;
 }
@@ -1077,14 +1169,8 @@ const char *
 sieve_storage_list_next(struct sieve_storage_list_context *lctx, bool *active_r)
 {
 	struct sieve_storage *storage = lctx->storage;
-	struct sieve_instance *svinst = storage->svinst;
 	const char *scriptname;
-	bool have_default, script_active = FALSE;
-
-	have_default = (storage->default_name != NULL &&
-			storage->default_location != NULL &&
-			(storage->flags &
-			 SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0);
+	bool script_active = FALSE;
 
 	sieve_storage_clear_error(storage);
 
@@ -1095,6 +1181,10 @@ sieve_storage_list_next(struct sieve_storage_list_context *lctx, bool *active_r)
 	if (script_active)
 		lctx->seen_active = TRUE;
 
+	struct sieve_storage *def_storage = lctx->def_storage;
+	bool have_default = (def_storage != NULL &&
+			     storage->default_name != NULL);
+
 	if (scriptname != NULL) {
 		/* Remember when we see that the storage has its own script for
 		   default */
@@ -1103,8 +1193,7 @@ sieve_storage_list_next(struct sieve_storage_list_context *lctx, bool *active_r)
 			lctx->seen_default = TRUE;
 
 	} else if (have_default && !lctx->seen_default &&
-		sieve_script_check(svinst, storage->default_location,
-				   NULL, NULL) > 0) {
+		   sieve_storage_check_script(def_storage, NULL, NULL) > 0) {
 
 		/* Return default script at the end if it was not listed
 		   thus far (storage backend has no script under default
@@ -1133,11 +1222,14 @@ int sieve_storage_list_deinit(struct sieve_storage_list_context **_lctx)
 	*_lctx = NULL;
 
 	struct sieve_storage *storage = lctx->storage;
+	struct sieve_storage *def_storage = lctx->def_storage;
 	int ret;
 
 	i_assert(storage->v.list_deinit != NULL);
 	ret = storage->v.list_deinit(lctx);
 
+	sieve_storage_unref(&def_storage);
+	sieve_storage_unref(&storage);
 	return ret;
 }
 
@@ -1337,13 +1429,29 @@ sieve_storage_save_is_activating_default(
 	if (!sieve_storage_save_will_activate(sctx))
 		return 0;
 
-	if (strcmp(sctx->scriptname, storage->default_name) != 0 ||
-	    !sieve_storage_save_will_activate(sctx))
-		return 0;
-	if (sieve_storage_check_script_direct(storage, storage->default_name,
-					      NULL) > 0)
-		return 0;
-	return 1;
+	struct sieve_storage *def_storage;
+	enum sieve_error error_code;
+	int ret = 0;
+
+	if (sieve_storage_create_default_for(storage, &def_storage,
+					     &error_code) < 0) {
+		if (error_code == SIEVE_ERROR_NOT_FOUND)
+			return 0;
+		return -1;
+	}
+
+	if (strcmp(sctx->scriptname, storage->default_name) == 0) {
+		ret = sieve_storage_check_script_direct(
+			storage, storage->default_name, &error_code);
+		if (ret == 0 ||
+		    (ret < 0 && error_code == SIEVE_ERROR_NOT_FOUND))
+			ret = 1;
+		else if (ret > 0)
+			ret = 0;
+	}
+
+	sieve_storage_unref(&def_storage);
+	return ret;
 }
 
 int sieve_storage_save_commit(struct sieve_storage_save_context **_sctx)
-- 
GitLab