diff --git a/src/lib-sieve-tool/sieve-tool.c b/src/lib-sieve-tool/sieve-tool.c
index c21fd8eef7f79bcd0382a1e70b906611a8e9f85f..6937c9f3f3e6cfafef07c04ed8d3e3d1207bfd48 100644
--- a/src/lib-sieve-tool/sieve-tool.c
+++ b/src/lib-sieve-tool/sieve-tool.c
@@ -7,6 +7,7 @@
 #include "ioloop.h"
 #include "ostream.h"
 #include "hostpid.h"
+#include "settings.h"
 #include "dict.h"
 #include "mail-namespace.h"
 #include "mail-storage.h"
@@ -20,6 +21,7 @@
 #include "sieve.h"
 #include "sieve-plugins.h"
 #include "sieve-extensions.h"
+#include "sieve-storage.h"
 
 #include "mail-raw.h"
 
@@ -285,6 +287,8 @@ sieve_tool_init_finish(struct sieve_tool *tool, bool init_mailstore,
 	svenv.hostname = my_hostdomain();
 	svenv.base_dir = tool->mail_user_dovecot->set->base_dir;
 	svenv.temp_dir = tool->mail_user_dovecot->set->mail_temp_dir;
+	svenv.event_parent = tool->mail_user_dovecot->event;
+	svenv.flags = SIEVE_FLAG_COMMAND_LINE;
 	svenv.location = SIEVE_ENV_LOCATION_MS;
 	svenv.delivery_phase = SIEVE_DELIVERY_PHASE_POST;
 
@@ -578,19 +582,77 @@ struct ostream *sieve_tool_open_output_stream(const char *filename)
  * Sieve script handling
  */
 
+static void
+sieve_tool_script_parse_location(struct sieve_tool *tool, const char *location,
+				 const char **storage_name_r)
+{
+	struct sieve_instance *svinst = tool->svinst;
+	const char *data = strchr(location, ':');
+	const char *script_driver = "file";
+	const char *script_path = NULL;
+	const char *storage_name = "_file";
+
+	if (data != NULL) {
+		script_driver = t_strdup_until(location, data++);
+		if (strcmp(script_driver, "file") == 0)
+			script_path = data;
+		else
+			storage_name = data;
+	} else {
+		script_path = location;
+	}
+
+	struct settings_instance *set_instance =
+		settings_instance_find(svinst->event);
+	const char *prefix = t_strdup_printf("sieve_script/%s", storage_name);
+
+	settings_override(set_instance, "sieve_script", storage_name,
+			  SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM);
+	settings_override(set_instance,
+			  t_strdup_printf("%s/sieve_script_storage", prefix),
+			  storage_name, SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM);
+	settings_override(set_instance,
+			  t_strdup_printf("%s/sieve_script_type", prefix),
+			  "command-line", SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM);
+	settings_override(set_instance,
+			  t_strdup_printf("%s/sieve_script_driver", prefix),
+			  script_driver, SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM);
+	if (script_path != NULL) {
+		settings_override(
+			set_instance, t_strdup_printf("%s/sieve_script_path",
+						      prefix),
+			script_path, SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM);
+	}
+
+	*storage_name_r = storage_name;
+}
+
 struct sieve_binary *
 sieve_tool_script_compile(struct sieve_tool *tool, const char *location)
 {
 	struct sieve_instance *svinst = tool->svinst;
 	struct sieve_error_handler *ehandler;
-	struct sieve_binary *sbin;
+	enum sieve_error error_code;
+	struct sieve_binary *sbin = NULL;
 
 	ehandler = sieve_stderr_ehandler_create(svinst, 0);
 	sieve_error_handler_accept_infolog(ehandler, TRUE);
 	sieve_error_handler_accept_debuglog(ehandler, svinst->debug);
 
-	if (sieve_compile(svinst, location, NULL, ehandler, 0, &sbin, NULL) < 0)
-		i_fatal("failed to compile sieve script '%s'", location);
+	if (sieve_storage_name_is_valid(location) &&
+	    sieve_compile(svinst, SIEVE_SCRIPT_CAUSE_ANY, location, NULL,
+			  ehandler, 0, &sbin, &error_code) < 0 &&
+	    error_code != SIEVE_ERROR_NOT_FOUND)
+		i_fatal("failed to compile sieve script storage");
+
+	if (sbin == NULL) {
+		const char *storage_name;
+
+		sieve_tool_script_parse_location(tool, location, &storage_name);
+		if (sieve_compile(svinst, SIEVE_SCRIPT_CAUSE_ANY, storage_name,
+				  NULL, ehandler, 0, &sbin, NULL) < 0)
+			i_fatal("failed to compile sieve script");
+	}
 	i_assert(sbin != NULL);
 
 	sieve_error_handler_unref(&ehandler);
@@ -602,14 +664,27 @@ sieve_tool_script_open(struct sieve_tool *tool, const char *location)
 {
 	struct sieve_instance *svinst = tool->svinst;
 	struct sieve_error_handler *ehandler;
-	struct sieve_binary *sbin;
+	enum sieve_error error_code;
+	struct sieve_binary *sbin = NULL;
 
 	ehandler = sieve_stderr_ehandler_create(svinst, 0);
 	sieve_error_handler_accept_infolog(ehandler, TRUE);
 	sieve_error_handler_accept_debuglog(ehandler, svinst->debug);
 
-	if (sieve_open(svinst, location, NULL, ehandler, 0, &sbin, NULL) < 0)
-		i_fatal("failed to compile sieve script");
+	if (sieve_storage_name_is_valid(location) &&
+	    sieve_open(svinst, SIEVE_SCRIPT_CAUSE_ANY, location, NULL,
+		       ehandler, 0, &sbin, &error_code) < 0 &&
+	    error_code != SIEVE_ERROR_NOT_FOUND)
+		i_fatal("failed to open sieve script storage");
+
+	if  (sbin == NULL) {
+		const char *storage_name;
+
+		sieve_tool_script_parse_location(tool, location, &storage_name);
+		if (sieve_open(svinst, SIEVE_SCRIPT_CAUSE_ANY, storage_name,
+			       NULL, ehandler, 0, &sbin, NULL) < 0)
+			i_fatal("failed to open sieve script");
+	}
 	i_assert(sbin != NULL);
 
 	sieve_error_handler_unref(&ehandler);
diff --git a/src/lib-sieve/Makefile.am b/src/lib-sieve/Makefile.am
index 22420fae996d5c32bdeb1914b3141671381ec2ac..d697ee9d6a3c536cc964ad8fc91c409b8b846bca 100644
--- a/src/lib-sieve/Makefile.am
+++ b/src/lib-sieve/Makefile.am
@@ -104,6 +104,7 @@ libdovecot_sieve_la_SOURCES = \
 	sieve-lexer.c \
 	sieve-script.c \
 	sieve-storage.c \
+	sieve-storage-settings.c \
 	sieve-storage-sync.c \
 	sieve-ast.c \
 	sieve-binary.c \
@@ -154,6 +155,7 @@ headers = \
 	sieve-script-private.h \
 	sieve-storage.h \
 	sieve-storage-private.h \
+	sieve-storage-settings.h \
 	sieve-ast.h \
 	sieve-binary.h \
 	sieve-binary-private.h \
diff --git a/src/lib-sieve/plugins/include/cmd-include.c b/src/lib-sieve/plugins/include/cmd-include.c
index f83e4d9254eaae12f1eb79d8777322d0392557a7..8c2d1e70da9196f371328f2bff9d2ddd2a8209f5 100644
--- a/src/lib-sieve/plugins/include/cmd-include.c
+++ b/src/lib-sieve/plugins/include/cmd-include.c
@@ -258,6 +258,7 @@ cmd_include_validate(struct sieve_validator *valdtr,
 
 	/* Open script */
 	if (ext_include_open_script(this_ext, ctx_data->location,
+				    sieve_validator_script_cause(valdtr),
 				    script_name, &script, &error_code) < 0) {
 		if (error_code != SIEVE_ERROR_NOT_FOUND) {
 			sieve_argument_validate_error(
diff --git a/src/lib-sieve/plugins/include/ext-include-binary.c b/src/lib-sieve/plugins/include/ext-include-binary.c
index 0988424f2938bdf297637a43be31939a9107b121..57017bf5032d6b01ab910a97ba3dc3b25b8ebcd0 100644
--- a/src/lib-sieve/plugins/include/ext-include-binary.c
+++ b/src/lib-sieve/plugins/include/ext-include-binary.c
@@ -290,6 +290,10 @@ ext_include_binary_open(const struct sieve_extension *ext,
 	struct ext_include_context *extctx = ext->context;
 	struct ext_include_binary_context *binctx =
 		(struct ext_include_binary_context *)context;
+	struct sieve_script *bin_script = sieve_binary_script(sbin);
+	const char *cause = (bin_script == NULL ?
+			     SIEVE_SCRIPT_CAUSE_ANY :
+			     sieve_script_cause(bin_script));
 	struct sieve_binary_block *sblock;
 	unsigned int depcount, i, block_id;
 	sieve_size_t offset;
@@ -367,7 +371,8 @@ ext_include_binary_open(const struct sieve_extension *ext,
 		}
 
 		/* Can we open the script dependency ? */
-		if (ext_include_open_script(ext, location, str_c(script_name),
+		if (ext_include_open_script(ext, location, cause,
+					    str_c(script_name),
 					    &script, &error_code) < 0) {
 			if (error_code != SIEVE_ERROR_NOT_FOUND) {
 				/* No, recompile */
diff --git a/src/lib-sieve/plugins/include/ext-include-common.c b/src/lib-sieve/plugins/include/ext-include-common.c
index 6f9c2fddeae95c194af25d1abf870cfe22dce901..b2aee1de8dc3e7fc2c2ddc62fb6e49e9be0238b4 100644
--- a/src/lib-sieve/plugins/include/ext-include-common.c
+++ b/src/lib-sieve/plugins/include/ext-include-common.c
@@ -78,7 +78,6 @@ int ext_include_load(const struct sieve_extension *ext, void **context_r)
 	struct sieve_instance *svinst = ext->svinst;
 	const struct sieve_extension *var_ext;
 	struct ext_include_context *extctx;
-	const char *location;
 	unsigned long long int uint_setting;
 
 	/* Extension dependencies */
@@ -88,17 +87,6 @@ int ext_include_load(const struct sieve_extension *ext, void **context_r)
 	extctx = i_new(struct ext_include_context, 1);
 	extctx->var_ext = var_ext;
 
-	/* Get location for :global scripts */
-	location = sieve_setting_get(svinst, "sieve_global");
-
-	if (location == NULL) {
-		e_debug(svinst->event, "include: "
-			"sieve_global is not set; "
-			"it is currently not possible to include ':global' scripts.");
-	}
-
-	extctx->global_location = i_strdup(location);
-
 	/* Get limits */
 	extctx->max_nesting_depth = EXT_INCLUDE_DEFAULT_MAX_NESTING_DEPTH;
 	extctx->max_includes = EXT_INCLUDE_DEFAULT_MAX_INCLUDES;
@@ -119,8 +107,6 @@ void ext_include_unload(const struct sieve_extension *ext)
 	struct ext_include_context *extctx = ext->context;
 
 	sieve_storage_unref(&extctx->personal_storage);
-
-	i_free(extctx->global_location);
 	i_free(extctx);
 }
 
@@ -131,12 +117,12 @@ void ext_include_unload(const struct sieve_extension *ext)
 static int
 ext_include_open_script_personal(struct sieve_instance *svinst,
 				 struct ext_include_context *extctx,
-				 const char *script_name,
+				 const char *cause, const char *script_name,
 				 struct sieve_script **script_r,
 				 enum sieve_error *error_code_r)
 {
 	if (extctx->personal_storage == NULL &&
-	    sieve_storage_create_personal(svinst, NULL, 0,
+	    sieve_storage_create_personal(svinst, NULL, cause, 0,
 					  &extctx->personal_storage,
 					  error_code_r) < 0)
 		return -1;
@@ -147,29 +133,18 @@ ext_include_open_script_personal(struct sieve_instance *svinst,
 
 static int
 ext_include_open_script_global(struct sieve_instance *svinst,
-			       struct ext_include_context *extctx,
-			       const char *script_name,
+			       const char *cause, const char *script_name,
 			       struct sieve_script **script_r,
 			       enum sieve_error *error_code_r)
 {
-	if (extctx->global_location == NULL) {
-		e_info(svinst->event, "include: "
-			"sieve_global is unconfigured; "
-			"include of ':global' script '%s' is therefore not possible",
-			str_sanitize(script_name, 80));
-		if (error_code_r != NULL)
-			*error_code_r = SIEVE_ERROR_NOT_FOUND;
-		return -1;
-	}
-
-	return sieve_script_create_open(svinst, extctx->global_location,
-					script_name,
+	return sieve_script_create_open(svinst, cause,
+					SIEVE_STORAGE_TYPE_GLOBAL, script_name,
 					script_r, error_code_r, NULL);
 }
 
 int ext_include_open_script(const struct sieve_extension *ext,
 			    enum ext_include_script_location location,
-			    const char *script_name,
+			    const char *cause, const char *script_name,
 			    struct sieve_script **script_r,
 			    enum sieve_error *error_code_r)
 {
@@ -180,13 +155,12 @@ int ext_include_open_script(const struct sieve_extension *ext,
 	*script_r = NULL;
 	switch (location) {
 	case EXT_INCLUDE_LOCATION_PERSONAL:
-		ret = ext_include_open_script_personal(svinst, extctx,
+		ret = ext_include_open_script_personal(svinst, extctx, cause,
 						       script_name,
 						       script_r, error_code_r);
 		break;
 	case EXT_INCLUDE_LOCATION_GLOBAL:
-		ret = ext_include_open_script_global(svinst, extctx,
-						     script_name,
+		ret = ext_include_open_script_global(svinst, cause, script_name,
 						     script_r, error_code_r);
 		break;
 	default:
@@ -346,8 +320,22 @@ void ext_include_register_generator_context(
 	/* Initialize generator context if necessary */
 	if (ctx == NULL) {
 		i_assert(cgenv->script != NULL);
+
+		enum ext_include_script_location location;
+		const char *storage_type =
+			sieve_script_storage_type(cgenv->script);
+
+		if (strcasecmp(storage_type,
+			       SIEVE_STORAGE_TYPE_PERSONAL) == 0)
+			location = EXT_INCLUDE_LOCATION_PERSONAL;
+		else if (strcasecmp(storage_type,
+				    SIEVE_STORAGE_TYPE_GLOBAL) == 0)
+			location = EXT_INCLUDE_LOCATION_GLOBAL;
+		else
+			location = EXT_INCLUDE_LOCATION_INVALID;
+
 		ctx = ext_include_create_generator_context(
-			cgenv->gentr, NULL, EXT_INCLUDE_LOCATION_PERSONAL,
+			cgenv->gentr, NULL, location,
 			sieve_script_name(cgenv->script), cgenv->script);
 
 		sieve_generator_extension_set_context(
diff --git a/src/lib-sieve/plugins/include/ext-include-common.h b/src/lib-sieve/plugins/include/ext-include-common.h
index aacd1e097dcde5bb3ae95817d860f87b2094302a..6f82a4f1afcda23749dc7c4b696e316a98d4685f 100644
--- a/src/lib-sieve/plugins/include/ext-include-common.h
+++ b/src/lib-sieve/plugins/include/ext-include-common.h
@@ -6,6 +6,7 @@
 
 #include "sieve-common.h"
 #include "sieve-extensions.h"
+#include "sieve-storage.h"
 
 /*
  * Forward declarations
@@ -84,7 +85,7 @@ extern const struct sieve_operation_def global_operation;
 
 int ext_include_open_script(const struct sieve_extension *ext,
 			    enum ext_include_script_location location,
-			    const char *script_name,
+			    const char *cause, const char *script_name,
 			    struct sieve_script **script_r,
 			    enum sieve_error *error_code_r);
 
@@ -98,9 +99,6 @@ struct ext_include_context {
 	/* Extension dependencies */
 	const struct sieve_extension *var_ext;
 
-	/* Configuration */
- 	char *global_location;
-
 	struct sieve_storage *personal_storage;
 
 	unsigned int max_nesting_depth;
diff --git a/src/lib-sieve/sieve-binary.h b/src/lib-sieve/sieve-binary.h
index e9397b58e9dd5801e2f284358f109e38113a287f..227a1c46467bc79ca201f7d40b1a9a459fe84c52 100644
--- a/src/lib-sieve/sieve-binary.h
+++ b/src/lib-sieve/sieve-binary.h
@@ -9,7 +9,7 @@
  * Config
  */
 
-#define SIEVE_BINARY_VERSION_MAJOR     2
+#define SIEVE_BINARY_VERSION_MAJOR     3
 #define SIEVE_BINARY_VERSION_MINOR     0
 
 #define SIEVE_BINARY_BASE_HEADER_SIZE  20
diff --git a/src/lib-sieve/sieve-script-private.h b/src/lib-sieve/sieve-script-private.h
index 1e8455cc8d6dff54e8150e987368d84c9f279a7c..6a0c7bdb3cb195d5ba5f53e191e17136499adc5c 100644
--- a/src/lib-sieve/sieve-script-private.h
+++ b/src/lib-sieve/sieve-script-private.h
@@ -56,7 +56,6 @@ struct sieve_script {
 	struct sieve_script_vfuncs v;
 
 	const char *name;
-	const char *location;
 
 	/* Stream */
 	struct istream *stream;
@@ -67,7 +66,7 @@ struct sieve_script {
 void sieve_script_init(struct sieve_script *script,
 		       struct sieve_storage *storage,
 		       const struct sieve_script *script_class,
-		       const char *location, const char *name);
+		       const char *name);
 
 /*
  * Binary
diff --git a/src/lib-sieve/sieve-script.c b/src/lib-sieve/sieve-script.c
index be225b736fee7af10a3aff2fd7f6f3c7307a9360..5caeb9153f72e232223722482ba08e1b792287a7 100644
--- a/src/lib-sieve/sieve-script.c
+++ b/src/lib-sieve/sieve-script.c
@@ -102,18 +102,16 @@ static void sieve_script_update_event(struct sieve_script *script)
 void sieve_script_init(struct sieve_script *script,
 		       struct sieve_storage *storage,
 		       const struct sieve_script *script_class,
-		       const char *location, const char *name)
+		       const char *name)
 {
 	i_assert(storage != NULL);
 
 	script->script_class = script_class;
 	script->refcount = 1;
 	script->storage = storage;
-	script->location = p_strdup_empty(script->pool, location);
 	script->name = p_strdup_empty(script->pool, name);
 
 	script->event = event_create(storage->event);
-	event_add_str(script->event, "script_location", location);
 	sieve_script_update_event(script);
 
 	sieve_storage_ref(storage);
@@ -121,7 +119,8 @@ void sieve_script_init(struct sieve_script *script,
 
 static int
 sieve_script_create_common(struct sieve_instance *svinst,
-			   const char *location, const char *name, bool open,
+			   const char *cause, const char *type,
+			   const char *name, bool open,
 			   struct sieve_script **script_r,
 			   enum sieve_error *error_code_r,
 			   const char **error_r)
@@ -131,7 +130,7 @@ sieve_script_create_common(struct sieve_instance *svinst,
 	*script_r = NULL;
 	sieve_error_args_init(&error_code_r, &error_r);
 
-	if (sieve_storage_sequence_create(svinst, svinst->event, location,
+	if (sieve_storage_sequence_create(svinst, svinst->event, cause, type,
 					  &sseq, error_code_r, error_r) < 0)
 		return -1;
 
@@ -193,14 +192,35 @@ sieve_script_create_common(struct sieve_instance *svinst,
 }
 
 int sieve_script_create(struct sieve_instance *svinst,
-			const char *location, const char *name,
+			const char *cause, const char *type, const char *name,
 			struct sieve_script **script_r,
 			enum sieve_error *error_code_r, const char **error_r)
 {
-	return sieve_script_create_common(svinst, location, name, FALSE,
+	return sieve_script_create_common(svinst, cause, type, name, FALSE,
 					  script_r, error_code_r, error_r);
 }
 
+int sieve_script_create_in(struct sieve_instance *svinst, const char *cause,
+			   const char *storage_name, const char *name,
+			   struct sieve_script **script_r,
+			   enum sieve_error *error_code_r,
+			   const char **error_r)
+{
+	struct sieve_storage *storage;
+	int ret;
+
+	*script_r = NULL;
+
+	if (sieve_storage_create(svinst, svinst->event, cause, storage_name, 0,
+				 &storage, error_code_r, error_r) < 0)
+		return -1;
+	ret = sieve_storage_get_script_direct(storage, name, script_r, NULL);
+	if (ret < 0)
+		*error_r = sieve_storage_get_last_error(storage, error_code_r);
+	sieve_storage_unref(&storage);
+	return ret;
+}
+
 void sieve_script_ref(struct sieve_script *script)
 {
 	script->refcount++;
@@ -255,12 +275,11 @@ int sieve_script_open(struct sieve_script *script,
 		return -1;
 	}
 
-	i_assert(script->location != NULL);
 	i_assert(script->name != NULL);
 	script->open = TRUE;
 
 	sieve_script_update_event(script);
-	e_debug(script->event, "Opened from '%s'", script->location);
+	e_debug(script->event, "Opened from '%s'", storage->name);
 	return 0;
 }
 
@@ -278,17 +297,43 @@ int sieve_script_open_as(struct sieve_script *script, const char *name,
 }
 
 int sieve_script_create_open(struct sieve_instance *svinst,
-			     const char *location, const char *name,
-			     struct sieve_script **script_r,
+			     const char *cause, const char *type,
+			     const char *name, struct sieve_script **script_r,
 			     enum sieve_error *error_code_r,
 			     const char **error_r)
 {
-	return sieve_script_create_common(svinst, location, name, TRUE,
+	return sieve_script_create_common(svinst, cause, type, name, TRUE,
 					  script_r, error_code_r, error_r);
 }
 
+int sieve_script_create_open_in(struct sieve_instance *svinst,
+				const char *cause,
+				const char *storage_name, const char *name,
+				struct sieve_script **script_r,
+				enum sieve_error *error_code_r,
+				const char **error_r)
+{
+	struct sieve_script *script;
+
+	*script_r = NULL;
+	sieve_error_args_init(&error_code_r, &error_r);
+
+	if (sieve_script_create_in(svinst, cause, storage_name, name,
+				   &script, error_code_r, error_r) < 0)
+		return -1;
+
+	if (sieve_script_open(script, NULL) < 0) {
+		*error_r = sieve_script_get_last_error(script, error_code_r);
+		sieve_script_unref(&script);
+		return -1;
+	}
+
+	*script_r = script;
+	return 0;
+}
+
 int sieve_script_check(struct sieve_instance *svinst,
-		       const char *location, const char *name,
+		       const char *cause, const char *type, const char *name,
 		       enum sieve_error *error_code_r, const char **error_r)
 {
 	struct sieve_script *script;
@@ -296,7 +341,7 @@ int sieve_script_check(struct sieve_instance *svinst,
 
 	sieve_error_args_init(&error_code_r, &error_r);
 
-	if (sieve_script_create_open(svinst, location, name,
+	if (sieve_script_create_open(svinst, cause, type, name,
 				     &script, &error_code, error_r) < 0)
 		return (*error_code_r == SIEVE_ERROR_NOT_FOUND ? 0 : -1);
 
@@ -313,14 +358,21 @@ const char *sieve_script_name(const struct sieve_script *script)
 	return script->name;
 }
 
-const char *sieve_script_location(const struct sieve_script *script)
+const char *sieve_script_label(const struct sieve_script *script)
 {
-	return script->location;
+	if (*script->name == '\0')
+		return script->storage->name;
+	return t_strconcat(script->storage->name, "/", script->name, NULL);
 }
 
-const char *sieve_script_label(const struct sieve_script *script)
+const char *sieve_script_storage_type(const struct sieve_script *script)
 {
-	return script->location;
+	return script->storage->type;
+}
+
+const char *sieve_script_cause(const struct sieve_script *script)
+{
+	return script->storage->cause;
 }
 
 struct sieve_instance *sieve_script_svinst(const struct sieve_script *script)
@@ -462,7 +514,7 @@ int sieve_script_binary_read_metadata(struct sieve_script *script,
 				      sieve_size_t *offset)
 {
 	struct sieve_binary *sbin = sieve_binary_block_get_binary(sblock);
-	string_t *storage_class, *location;
+	string_t *storage_class, *storage_name, *name;
 	unsigned int version;
 
 	if ((sieve_binary_block_get_size(sblock) - *offset) == 0)
@@ -504,21 +556,38 @@ int sieve_script_binary_read_metadata(struct sieve_script *script,
 		return 0;
 	}
 
-	/* location */
-	if (!sieve_binary_read_string(sblock, offset, &location)) {
+	/* storage */
+	if (!sieve_binary_read_string(sblock, offset, &storage_name)) {
 		e_error(script->event,
 			"Binary '%s' has invalid metadata for script '%s': "
-			"Invalid location",
+			"Invalid storage name",
 			sieve_binary_path(sbin), sieve_script_label(script));
 		return -1;
 	}
-	i_assert(script->location != NULL);
-	if (strcmp(str_c(location), script->location) != 0) {
+	if (str_len(storage_name) > 0 &&
+	    strcmp(str_c(storage_name), script->storage->name) != 0) {
 		e_debug(script->event,
-			"Binary '%s' reports different location "
+			"Binary '%s' reports different storage "
 			"for script '%s' (binary points to '%s')",
-			sieve_binary_path(sbin), script->location,
-			str_c(location));
+			sieve_binary_path(sbin), sieve_script_label(script),
+			str_c(storage_name));
+		return 0;
+	}
+
+	/* name */
+	if (!sieve_binary_read_string(sblock, offset, &name)) {
+		e_error(script->event,
+			"Binary '%s' has invalid metadata for script '%s': "
+			"Invalid script name",
+			sieve_binary_path(sbin), sieve_script_label(script));
+		return -1;
+	}
+	if (str_len(name) > 0 && strcmp(str_c(name), script->name) != 0) {
+		e_debug(script->event,
+			"Binary '%s' reports different script name "
+			"for script '%s' (binary points to '%s/%s')",
+			sieve_binary_path(sbin), sieve_script_label(script),
+			str_c(storage_name), str_c(name));
 		return 0;
 	}
 
@@ -531,10 +600,19 @@ int sieve_script_binary_read_metadata(struct sieve_script *script,
 void sieve_script_binary_write_metadata(struct sieve_script *script,
 					struct sieve_binary_block *sblock)
 {
+	struct sieve_binary *sbin = sieve_binary_block_get_binary(sblock);
+	struct sieve_instance *svinst = sieve_binary_svinst(sbin);
+
 	sieve_binary_emit_cstring(sblock, script->driver_name);
 	sieve_binary_emit_unsigned(sblock, script->storage->version);
-	sieve_binary_emit_cstring(sblock, (script->location == NULL ?
-					   "" : script->location));
+
+	if (HAS_ALL_BITS(svinst->flags, SIEVE_FLAG_COMMAND_LINE)) {
+		sieve_binary_emit_cstring(sblock, "");
+		sieve_binary_emit_cstring(sblock, "");
+	} else {
+		sieve_binary_emit_cstring(sblock, script->storage->name);
+		sieve_binary_emit_cstring(sblock, script->name);
+	}
 
 	if (script->v.binary_write_metadata == NULL)
 		return;
@@ -549,7 +627,7 @@ bool sieve_script_binary_dump_metadata(struct sieve_script *script,
 {
 	struct sieve_binary *sbin = sieve_binary_block_get_binary(sblock);
 	struct sieve_instance *svinst = sieve_binary_svinst(sbin);
-	string_t *storage_class, *location;
+	string_t *storage_class, *storage_name, *name;
 	struct sieve_script *adhoc_script = NULL;
 	unsigned int version;
 	bool result = TRUE;
@@ -564,15 +642,27 @@ bool sieve_script_binary_dump_metadata(struct sieve_script *script,
 		return FALSE;
 	sieve_binary_dumpf(denv, "class.version = %d\n", version);
 
-	/* location */
-	if (!sieve_binary_read_string(sblock, offset, &location))
+	/* storage */
+	if (!sieve_binary_read_string(sblock, offset, &storage_name))
+		return FALSE;
+	if (str_len(storage_name) == 0)
+		sieve_binary_dumpf(denv, "storage = (unavailable)\n");
+	else
+		sieve_binary_dumpf(denv, "storage = %s\n", str_c(storage_name));
+
+	/* name */
+	if (!sieve_binary_read_string(sblock, offset, &name))
 		return FALSE;
-	sieve_binary_dumpf(denv, "location = %s\n", str_c(location));
+	if (str_len(name) == 0)
+		sieve_binary_dumpf(denv, "name = (unavailable)\n");
+	else
+		sieve_binary_dumpf(denv, "name = %s\n", str_c(name));
 
 	if (script == NULL) {
 		adhoc_script = NULL;
-		if (sieve_script_create(svinst, str_c(location),
-					NULL, &script, NULL, NULL) == 0)
+		if (sieve_script_create_in(svinst, SIEVE_SCRIPT_CAUSE_ANY,
+					   str_c(storage_name), str_c(name),
+					   &script, NULL, NULL) == 0)
 			adhoc_script = script;
 	}
 
@@ -1042,7 +1132,8 @@ const char *sieve_script_get_last_error_lcase(struct sieve_script *script)
  */
 
 int sieve_script_sequence_create(struct sieve_instance *svinst,
-				 const char *location,
+				 struct event *event_parent,
+				 const char *cause, const char *type,
 				 struct sieve_script_sequence **sseq_r,
 				 enum sieve_error *error_code_r,
 				 const char **error_r)
@@ -1053,8 +1144,8 @@ int sieve_script_sequence_create(struct sieve_instance *svinst,
 	*sseq_r = NULL;
 	sieve_error_args_init(&error_code_r, &error_r);
 
-	if (sieve_storage_sequence_create(svinst, svinst->event, location,
-					  &storage_seq,
+	if (sieve_storage_sequence_create(svinst, event_parent,
+					  cause, type, &storage_seq,
 					  error_code_r, error_r) < 0)
 		return -1;
 
diff --git a/src/lib-sieve/sieve-script.h b/src/lib-sieve/sieve-script.h
index 798ebafef9017b5ed0ac1dbc40cecf57dd0c14a1..f8918aa4bca7b17f5c605168f3a778fa68404065 100644
--- a/src/lib-sieve/sieve-script.h
+++ b/src/lib-sieve/sieve-script.h
@@ -17,6 +17,8 @@ bool sieve_script_name_is_valid(const char *scriptname);
  */
 
 bool sieve_script_file_has_extension(const char *filename);
+const char *sieve_script_file_get_scriptname(const char *filename);
+const char *sieve_script_file_from_name(const char *name);
 
 /*
  * Sieve script class
@@ -36,10 +38,14 @@ struct sieve_script;
 ARRAY_DEFINE_TYPE(sieve_script, struct sieve_script *);
 
 int sieve_script_create(struct sieve_instance *svinst,
-			const char *location, const char *name,
+			const char *cause, const char *type, const char *name,
 			struct sieve_script **script_r,
 			enum sieve_error *error_code_r, const char **error_r);
-
+int sieve_script_create_in(struct sieve_instance *svinst, const char *cause,
+			   const char *storage_name, const char *name,
+			   struct sieve_script **script_r,
+			   enum sieve_error *error_code_r,
+			   const char **error_r);
 void sieve_script_ref(struct sieve_script *script);
 void sieve_script_unref(struct sieve_script **script);
 
@@ -49,12 +55,19 @@ int sieve_script_open_as(struct sieve_script *script, const char *name,
 			 enum sieve_error *error_code_r);
 
 int sieve_script_create_open(struct sieve_instance *svinst,
-			     const char *location, const char *name,
-			     struct sieve_script **script_r,
+			     const char *cause, const char *type,
+			     const char *name, struct sieve_script **script_r,
 			     enum sieve_error *error_code_r,
 			     const char **error_r);
+int sieve_script_create_open_in(struct sieve_instance *svinst,
+				const char *cause,
+				const char *storage_name, const char *name,
+				struct sieve_script **script_r,
+				enum sieve_error *error_code_r,
+				const char **error_r);
+
 int sieve_script_check(struct sieve_instance *svinst,
-		       const char *location, const char *name,
+		       const char *cause, const char *type, const char *name,
 		       enum sieve_error *error_code_r, const char **error_r);
 
 /*
@@ -63,7 +76,8 @@ int sieve_script_check(struct sieve_instance *svinst,
 
 struct sieve_script *
 sieve_data_script_create_from_input(struct sieve_instance *svinst,
-				    const char *name, struct istream *input);
+				    const char *cause, const char *name,
+				    struct istream *input);
 
 /*
  * Binary
@@ -113,7 +127,9 @@ int sieve_script_delete(struct sieve_script *script, bool ignore_active);
 
 const char *sieve_script_name(const struct sieve_script *script) ATTR_PURE;
 const char *sieve_script_label(const struct sieve_script *script) ATTR_PURE;
-const char *sieve_script_location(const struct sieve_script *script) ATTR_PURE;
+const char *
+sieve_script_storage_type(const struct sieve_script *script) ATTR_PURE;
+const char *sieve_script_cause(const struct sieve_script *script) ATTR_PURE;
 struct sieve_instance *
 sieve_script_svinst(const struct sieve_script *script) ATTR_PURE;
 
@@ -156,7 +172,8 @@ const char *sieve_script_get_last_error_lcase(struct sieve_script *script);
 struct sieve_script_sequence;
 
 int sieve_script_sequence_create(struct sieve_instance *svinst,
-				 const char *location,
+				 struct event *event_parent,
+				 const char *cause, const char *type,
 				 struct sieve_script_sequence **sseq_r,
 				 enum sieve_error *error_code_r,
 				 const char **error_r);
diff --git a/src/lib-sieve/sieve-storage-private.h b/src/lib-sieve/sieve-storage-private.h
index a73a9b109e136d2955b0337ec866f74758ec1b34..46820227a48962500b8293fe727327637bac9b67 100644
--- a/src/lib-sieve/sieve-storage-private.h
+++ b/src/lib-sieve/sieve-storage-private.h
@@ -5,6 +5,7 @@
 #include "sieve-error-private.h"
 
 #include "sieve-storage.h"
+#include "sieve-storage-settings.h"
 
 #define MAILBOX_ATTRIBUTE_PREFIX_SIEVE \
 	MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER"sieve/"
@@ -23,10 +24,11 @@ ARRAY_DEFINE_TYPE(sieve_storage_class, const struct sieve_storage *);
 struct sieve_storage_vfuncs {
 	struct sieve_storage *(*alloc)(void);
 	void (*destroy)(struct sieve_storage *storage);
-	int (*init)(struct sieve_storage *storage, const char *const *options);
+	int (*init)(struct sieve_storage *storage);
 
 	int (*autodetect)(struct sieve_instance *svinst,
-			  const char *active_path,
+			  struct event *event, const char *cause,
+			  const struct sieve_storage_settings *storage_set,
 			  enum sieve_storage_flags flags,
 			  struct sieve_storage **storage_r,
 			  enum sieve_error *error_code_r,
@@ -100,8 +102,9 @@ struct sieve_storage {
 	const struct sieve_storage *storage_class;
 	struct sieve_storage_vfuncs v;
 
-	const char *data;
-	const char *location;
+	const char *name;
+	const char *cause;
+	const char *type;
 	const char *script_name;
 	const char *bin_path;
 
@@ -111,25 +114,33 @@ struct sieve_storage {
 	char *error;
 	enum sieve_error error_code;
 
-	const char *default_name;
-	const char *default_location;
 	struct sieve_storage *default_storage, *default_storage_for;
 
 	struct mail_namespace *sync_inbox_ns;
 
 	enum sieve_storage_flags flags;
 
-	/* this is the main personal storage */
-	bool main_storage:1;
 	bool allows_synchronization:1;
 	bool is_default:1;
 };
 
-int sieve_storage_alloc(struct sieve_instance *svinst, struct event *event,
+int sieve_storage_alloc(struct sieve_instance *svinst,
+			struct event *event_parent,
 			const struct sieve_storage *storage_class,
-			const char *data, enum sieve_storage_flags flags,
-			bool main, struct sieve_storage **storage_r,
+			const char *cause, const char *script_type,
+			const char *storage_name, const char *script_name,
+			enum sieve_storage_flags flags,
+			struct sieve_storage **storage_r,
 			enum sieve_error *error_code_r, const char **error_r);
+int sieve_storage_alloc_with_settings(struct sieve_instance *svinst,
+				      struct event *event_parent,
+				      const struct sieve_storage *storage_class,
+				      const char *cause,
+				      const struct sieve_storage_settings *set,
+				      enum sieve_storage_flags flags,
+				      struct sieve_storage **storage_r,
+				      enum sieve_error *error_code_r,
+				      const char **error_r);
 
 int sieve_storage_setup_bin_path(struct sieve_storage *storage, mode_t mode);
 
@@ -186,9 +197,12 @@ struct sieve_storage_save_context {
 struct sieve_storage_sequence {
 	struct sieve_instance *svinst;
 	struct event *event_parent;
-	char *location;
+	char *cause;
+	char *type;
 
-	bool done:1;
+	const struct sieve_storage_settings *storage_set;
+	const char **storage_names;
+	unsigned int storage_count, storage_index;
 };
 
 /*
diff --git a/src/lib-sieve/sieve-storage-settings.c b/src/lib-sieve/sieve-storage-settings.c
new file mode 100644
index 0000000000000000000000000000000000000000..1e08b23401c311f8edc58ffe89284d2cd3848389
--- /dev/null
+++ b/src/lib-sieve/sieve-storage-settings.c
@@ -0,0 +1,148 @@
+/* Copyright (c) 2024 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str-sanitize.h"
+#include "array.h"
+#include "sort.h"
+#include "settings.h"
+#include "settings-parser.h"
+
+#include "sieve-script.h"
+#include "sieve-storage.h"
+#include "sieve-storage-settings.h"
+
+static bool
+sieve_storage_settings_check(void *_set, pool_t pool, const char **error_r);
+
+#undef DEF
+#define DEF(type, name) SETTING_DEFINE_STRUCT_##type( \
+	"sieve_"#name, name, \
+	struct sieve_storage_settings)
+
+static const struct setting_filter_array_order sieve_storage_order_precedence = {
+	.info = &sieve_storage_setting_parser_info,
+	.field_name = "sieve_script_precedence",
+};
+
+static const struct setting_define sieve_storage_setting_defines[] = {
+	DEF(STR, script_storage),
+	DEF(UINT, script_precedence),
+
+	DEF(STR, script_type),
+	DEF(BOOLLIST, script_cause),
+	DEF(STR, script_driver),
+	DEF(STR, script_name),
+	DEF(STR, script_bin_path),
+
+	DEF(SIZE, quota_max_storage),
+	DEF(UINT, quota_max_scripts),
+
+	{ .type = SET_FILTER_ARRAY, .key = "sieve_script",
+	   .offset = offsetof(struct sieve_storage_settings, storages),
+	   .filter_array_field_name = "sieve_script_storage",
+	   .filter_array_order = &sieve_storage_order_precedence },
+
+	SETTING_DEFINE_LIST_END,
+};
+
+static const struct sieve_storage_settings sieve_storage_default_settings = {
+	.script_storage = "",
+	.script_precedence = UINT_MAX,
+
+	.script_type = SIEVE_STORAGE_TYPE_PERSONAL,
+	.script_cause = ARRAY_INIT,
+
+	.script_driver = "",
+	.script_name = "",
+	.script_bin_path = "",
+
+	.quota_max_storage = 0,
+	.quota_max_scripts = 0,
+
+	.storages = ARRAY_INIT,
+};
+
+const struct setting_parser_info sieve_storage_setting_parser_info = {
+	.name = "sieve_storage",
+
+	.defines = sieve_storage_setting_defines,
+	.defaults = &sieve_storage_default_settings,
+
+	.struct_size = sizeof(struct sieve_storage_settings),
+
+	.pool_offset1 = 1 + offsetof(struct sieve_storage_settings, pool),
+
+	.check_func = sieve_storage_settings_check,
+};
+
+/* <settings checks> */
+static bool
+sieve_storage_settings_check(void *_set, pool_t pool ATTR_UNUSED,
+			     const char **error_r)
+{
+	struct sieve_storage_settings *set = _set;
+
+	if (*set->script_storage != '\0' &&
+	    !sieve_storage_name_is_valid(set->script_storage)) {
+		*error_r = t_strdup_printf(
+			"Invalid script storage name '%s'",
+			str_sanitize(set->script_storage, 128));
+		return FALSE;
+	}
+	if (*set->script_name != '\0' &&
+	    !sieve_script_name_is_valid(set->script_name)) {
+		*error_r = t_strdup_printf(
+			"Invalid script name '%s'",
+			str_sanitize(set->script_name, 128));
+		return FALSE;
+	}
+
+	if (array_is_created(&set->script_cause))
+		array_sort(&set->script_cause, i_strcmp_p);
+
+	return TRUE;
+}
+/* </settings checks> */
+
+bool sieve_storage_settings_match_script_type(
+	const struct sieve_storage_settings *set, const char *type)
+{
+	if (strcasecmp(type, SIEVE_STORAGE_TYPE_ANY) == 0)
+		return TRUE;
+	if (strcasecmp(type, set->script_type) == 0)
+		return TRUE;
+	return FALSE;
+}
+
+bool sieve_storage_settings_match_script_cause(
+	const struct sieve_storage_settings *set, const char *cause)
+{
+	if (strcasecmp(cause, SIEVE_SCRIPT_CAUSE_ANY) == 0) {
+		/* Any cause will match */
+		return TRUE;
+	}
+	if (!array_is_created(&set->script_cause)) {
+		/* Causes are not configured for this storage */
+		if (strcasecmp(set->script_type,
+			       SIEVE_STORAGE_TYPE_PERSONAL) == 0) {
+			/* For personal storages the default is to match any
+			   cause. */
+			return TRUE;
+		}
+		if (strcasecmp(cause, SIEVE_SCRIPT_CAUSE_DELIVERY) == 0) {
+			/* The default cause is delivery */
+			return TRUE;
+		}
+		return FALSE;
+	}
+
+	/* Causes are configured for this storage: perform lookup */
+
+	unsigned int set_cause_count;
+	const char *const *set_cause;
+
+	set_cause = array_get(&set->script_cause, &set_cause_count);
+	return (i_bsearch(cause, set_cause, set_cause_count,
+			 sizeof(const char *), search_strcasecmp) != NULL);
+}
diff --git a/src/lib-sieve/sieve-storage-settings.h b/src/lib-sieve/sieve-storage-settings.h
new file mode 100644
index 0000000000000000000000000000000000000000..4d7cc215885f1d549c9856c22741f35df0121712
--- /dev/null
+++ b/src/lib-sieve/sieve-storage-settings.h
@@ -0,0 +1,33 @@
+#ifndef SIEVE_STORAGE_SETTINGS_H
+#define SIEVE_STORAGE_SETTINGS_H
+
+#define SIEVE_STORAGE_SETTINGS_FILTER "sieve_script"
+
+struct sieve_storage_settings {
+	pool_t pool;
+
+	const char *script_storage;
+	unsigned int script_precedence;
+
+	const char *script_type;
+	ARRAY_TYPE(const_string) script_cause;
+
+	const char *script_driver;
+	const char *script_name;
+	const char *script_bin_path;
+
+	uoff_t quota_max_storage;
+	unsigned int quota_max_scripts;
+
+	ARRAY_TYPE(const_string) storages;
+};
+
+extern const struct setting_parser_info sieve_storage_setting_parser_info;
+
+bool sieve_storage_settings_match_script_type(
+	const struct sieve_storage_settings *set, const char *type);
+bool sieve_storage_settings_match_script_cause(
+	const struct sieve_storage_settings *set, const char *cause);
+
+
+#endif
diff --git a/src/lib-sieve/sieve-storage.c b/src/lib-sieve/sieve-storage.c
index 9cd8aaabb7c4986b4a035f775ccaf1e91afcda8e..139a36be27b15b240308df90b361bc16366e6fe7 100644
--- a/src/lib-sieve/sieve-storage.c
+++ b/src/lib-sieve/sieve-storage.c
@@ -3,12 +3,14 @@
 
 #include "lib.h"
 #include "array.h"
+#include "str.h"
 #include "str-sanitize.h"
 #include "hash.h"
 #include "home-expand.h"
 #include "eacces-error.h"
 #include "mkdir-parents.h"
 #include "ioloop.h"
+#include "settings.h"
 
 #include "sieve-common.h"
 #include "sieve-settings.old.h"
@@ -28,6 +30,15 @@ struct event_category event_category_sieve_storage = {
 	.name = "sieve-storage",
 };
 
+/*
+ * Storage name
+ */
+
+bool sieve_storage_name_is_valid(const char *name)
+{
+	return sieve_script_name_is_valid(name);
+}
+
 /*
  * Storage class
  */
@@ -118,17 +129,37 @@ bool sieve_storage_class_exists(struct sieve_instance *svinst,
  * Storage event
  */
 
+static void
+sieve_storage_update_event_prefix(struct event *event, const char *storage_name,
+				  bool is_default)
+{
+	string_t *prefix = t_str_new(128);
+
+	str_append(prefix, "storage");
+	if (storage_name != NULL && *storage_name != '\0') {
+		str_append_c(prefix, ' ');
+		str_append(prefix, storage_name);
+	}
+	if (is_default)
+		str_append(prefix, " (default)");
+	str_append(prefix, ": ");
+	event_set_append_log_prefix(event, str_c(prefix));
+}
+
 static struct event *
 sieve_storage_create_event(struct sieve_instance *svinst,
-			   struct event *event_parent)
+			   struct event *event_parent,
+			   const char *storage_name)
 {
 	struct event *event;
 
 	event = event_create(event_parent == NULL ?
 			     svinst->event : event_parent);
+	if (event_parent != svinst->event)
+		event_add_category(event, &event_category_sieve);
 	event_add_category(event, &event_category_sieve_storage);
-	event_set_append_log_prefix(event, "storage: ");
 
+	sieve_storage_update_event_prefix(event, storage_name, FALSE);
 	return event;
 }
 
@@ -146,146 +177,6 @@ sieve_storage_create_driver_event(struct event *event_parent,
 	return event;
 }
 
-/*
- * Storage options
- */
-
-static const char *split_next_arg(const char *const **_args)
-{
-	const char *const *args = *_args;
-	const char *str = args[0];
-
-	/* join arguments for escaped ";" separator */
-
-	args++;
-	while (*args != NULL && **args == '\0') {
-		args++;
-		if (*args == NULL) {
-			/* string ends with ";", just ignore it. */
-			break;
-		}
-		str = t_strconcat(str, ";", *args, NULL);
-		args++;
-	}
-	*_args = args;
-	return str;
-}
-
-static int
-sieve_storage_driver_parse(struct sieve_instance *svinst, const char **data,
-			   const struct sieve_storage **driver_r)
-{
-	const struct sieve_storage *storage_class = NULL;
-	const char *p;
-
-	p = strchr(*data, ':');
-	if (p == NULL)
-		return 0;
-
-	/* Lookup storage driver */
-	T_BEGIN {
-		const char *driver;
-
-		driver = t_strdup_until(*data, p);
-		*data = p+1;
-
-		storage_class = sieve_storage_class_find(svinst, driver);
-		if (storage_class == NULL) {
-			e_error(svinst->event,
-				"Unknown storage driver module '%s'",
-				driver);
-		} else if (storage_class->v.alloc == NULL) {
-			e_error(svinst->event,
-				"Support not compiled in for storage driver '%s'",
-				driver);
-			storage_class = NULL;
-		}
-	} T_END;
-
-	*driver_r = storage_class;
-	return (storage_class == NULL ? -1 : 1);
-}
-
-static int
-sieve_storage_data_parse(struct sieve_storage *storage, const char *data,
-			 const char **location_r, const char *const **options_r)
-{
-	ARRAY_TYPE(const_string) options;
-	const char *const *args;
-	const char *value;
-
-	if (*data == '\0') {
-		*options_r = NULL;
-		*location_r = data;
-		return 0;
-	}
-
-	/* <location> */
-	args = t_strsplit(data, ";");
-	*location_r = split_next_arg(&args);
-
-	if (options_r != NULL) {
-		t_array_init(&options, 8);
-
-		/* [<option> *(';' <option>)] */
-		while (*args != NULL) {
-			const char *option = split_next_arg(&args);
-
-			if (str_begins_icase(option, "name=", &value)) {
-				if (*value == '\0') {
-					e_error(storage->event,
-						"Failed to parse storage location: "
-						"Empty name not allowed");
-					return -1;
-				}
-
-				if (storage->script_name == NULL) {
-					if (!sieve_script_name_is_valid(value)) {
-						e_error(storage->event,
-							"Failed to parse storage location: "
-							"Invalid script name '%s'.",
-							str_sanitize(value, 80));
-						return -1;
-					}
-					storage->script_name = p_strdup(storage->pool, value);
-				}
-
-			} else if (str_begins_icase(option, "bindir=", &value)) {
-				if (value[0] == '\0') {
-					e_error(storage->event,
-						"Failed to parse storage location: "
-						"Empty bindir not allowed");
-					return -1;
-				}
-
-				if (value[0] == '~') {
-					/* home-relative path. change to absolute. */
-					const char *home = sieve_environment_get_homedir(storage->svinst);
-
-					if (home != NULL) {
-						value = home_expand_tilde(value, home);
-					} else if (value[1] == '/' || value[1] == '\0') {
-						e_error(storage->event,
-							"Failed to parse storage location: "
-							"bindir is relative to home directory (~/), "
-							"but home directory cannot be determined");
-						return -1;
-					}
-				}
-
-				storage->bin_path = p_strdup(storage->pool, value);
-			} else {
-				array_append(&options, &option, 1);
-			}
-		}
-
-		(void)array_append_space(&options);
-		*options_r = array_idx(&options, 0);
-	}
-
-	return 0;
-}
-
 /*
  * Storage instance
  */
@@ -294,7 +185,9 @@ static int
 sieve_storage_alloc_from_class(struct sieve_instance *svinst,
 			       struct event *event,
 			       const struct sieve_storage *storage_class,
-			       const char *data,
+			       const char *cause, const char *script_type,
+			       const char *storage_name,
+			       const char *script_name,
 			       enum sieve_storage_flags flags,
 			       struct sieve_storage **storage_r,
 			       enum sieve_error *error_code_r,
@@ -307,7 +200,7 @@ sieve_storage_alloc_from_class(struct sieve_instance *svinst,
 	if (storage_class->v.alloc == NULL) {
 		e_error(event, "Support not compiled in for this driver");
 		sieve_error_create_script_not_found(
-			NULL, error_code_r, error_r);
+			script_name, error_code_r, error_r);
 		return -1;
 	}
 
@@ -328,9 +221,18 @@ sieve_storage_alloc_from_class(struct sieve_instance *svinst,
 	storage->storage_class = storage_class;
 	storage->refcount = 1;
 	storage->svinst = svinst;
-	storage->data = p_strdup_empty(storage->pool, data);
+	storage->cause = p_strdup(storage->pool, cause);
+	storage->type = p_strdup(storage->pool, script_type);
+	storage->script_name = p_strdup(storage->pool, script_name);
 	storage->flags = flags;
 
+	if (storage_name != NULL && *storage_name != '\0')
+		storage->name = p_strdup(storage->pool, storage_name);
+	else {
+		storage->name = p_strconcat(
+			storage->pool, "auto:", storage->type, NULL);
+	}
+
 	storage->event = event;
 	event_ref(event);
 
@@ -341,53 +243,62 @@ sieve_storage_alloc_from_class(struct sieve_instance *svinst,
 int sieve_storage_alloc(struct sieve_instance *svinst,
 			struct event *event_parent,
 			const struct sieve_storage *storage_class,
-			const char *data, enum sieve_storage_flags flags,
-			bool main, struct sieve_storage **storage_r,
+			const char *cause, const char *script_type,
+			const char *storage_name, const char *script_name,
+			enum sieve_storage_flags flags,
+			struct sieve_storage **storage_r,
 			enum sieve_error *error_code_r, const char **error_r)
 {
 	struct event *storage_event, *event;
-	struct sieve_storage *storage;
 	int ret;
 
 	*storage_r = NULL;
 	sieve_error_args_init(&error_code_r, &error_r);
 
-	storage_event = sieve_storage_create_event(svinst, event_parent);
+	storage_event = sieve_storage_create_event(svinst, event_parent,
+						   storage_name);
 	event = sieve_storage_create_driver_event(storage_event,
 						  storage_class->driver_name);
 	event_unref(&storage_event);
 
 	ret = sieve_storage_alloc_from_class(svinst, event, storage_class,
-					     data, flags,
-					     &storage, error_code_r, error_r);
-	if (ret == 0)
-		storage->main_storage = main;
+					     cause, script_type,
+					     storage_name, script_name, flags,
+					     storage_r, error_code_r, error_r);
 
 	event_unref(&event);
 
-	*storage_r = storage;
 	return ret;
 }
 
-static void sieve_storage_get_quota_settings(struct sieve_storage *storage)
+int sieve_storage_alloc_with_settings(struct sieve_instance *svinst,
+				      struct event *event_parent,
+				      const struct sieve_storage *storage_class,
+				      const char *cause,
+				      const struct sieve_storage_settings *set,
+				      enum sieve_storage_flags flags,
+				      struct sieve_storage **storage_r,
+				      enum sieve_error *error_code_r,
+				      const char **error_r)
 {
-	struct sieve_instance *svinst = storage->svinst;
-	unsigned long long int uint_setting;
-	size_t size_setting;
+	struct sieve_storage *storage;
+	int ret;
 
-	/* Get quota settings if storage driver provides none */
+	*storage_r = NULL;
+	sieve_error_args_init(&error_code_r, &error_r);
 
-	if (storage->max_storage == 0 &&
-	    sieve_setting_get_size_value(svinst, "sieve_quota_max_storage",
-					 &size_setting)) {
-		storage->max_storage = size_setting;
-	}
+	ret = sieve_storage_alloc_from_class(svinst, event_parent,
+					     storage_class,
+					     cause, set->script_type,
+					     set->script_storage,
+					     set->script_name, flags,
+					     &storage, error_code_r, error_r);
+	if (ret < 0)
+		return -1;
 
-	if (storage->max_scripts == 0 &&
-	    sieve_setting_get_uint_value(svinst, "sieve_quota_max_scripts",
-					 &uint_setting)) {
-		storage->max_scripts = uint_setting;
-	}
+	storage->bin_path = p_strdup_empty(storage->pool, set->script_bin_path);
+	storage->max_storage = set->quota_max_storage;
+	storage->max_scripts = set->quota_max_scripts;
 
 	if (storage->max_storage > 0) {
 		e_debug(storage->event, "quota: "
@@ -399,47 +310,205 @@ static void sieve_storage_get_quota_settings(struct sieve_storage *storage)
 			"Script count limit: %u scripts",
 			storage->max_scripts);
 	}
+
+	*storage_r = storage;
+	return 0;
+}
+
+static int
+sieve_storage_alloc_from_settings(struct sieve_instance *svinst,
+				  struct event *event_parent, const char *cause,
+				  const struct sieve_storage_settings *set,
+				  enum sieve_storage_flags flags,
+				  struct sieve_storage **storage_r,
+				  enum sieve_error *error_code_r,
+				  const char **error_r)
+{
+	const struct sieve_storage *storage_class;
+	struct event *event = event_parent;
+	int ret;
+
+	*storage_r = NULL;
+
+	if (!sieve_storage_settings_match_script_cause(set, cause))
+		return 0;
+
+	storage_class = sieve_storage_class_find(svinst, set->script_driver);
+	// FIXME: add support for automatic module loading (no such modules yet)
+	if (storage_class == NULL) {
+		e_error(event, "Unknown storage driver: %s",
+			set->script_driver);
+		sieve_error_create_script_not_found(set->script_name,
+						    error_code_r, error_r);
+		event_unref(&event);
+		return -1;
+	}
+
+	event = sieve_storage_create_driver_event(event_parent,
+						  storage_class->driver_name);
+
+	ret = sieve_storage_alloc_with_settings(svinst, event, storage_class,
+						cause, set, flags,
+						storage_r, error_code_r,
+						error_r);
+
+	event_unref(&event);
+
+	if (ret < 0)
+		return -1;
+	return 1;
+}
+
+static int
+sieve_storage_autodetect(struct sieve_instance *svinst, struct event *event,
+			 const char *cause, const char *type,
+			 const struct sieve_storage_settings *set,
+			 enum sieve_storage_flags flags,
+			 struct sieve_storage **storage_r,
+			 enum sieve_error *error_code_r, const char **error_r)
+{
+	struct sieve_storage_class_registry *reg = svinst->storage_reg;
+	int ret;
+
+	*storage_r = NULL;
+	sieve_error_args_init(&error_code_r, &error_r);
+
+	if (!sieve_storage_settings_match_script_cause(set, cause))
+		return 0;
+	if (!sieve_storage_settings_match_script_type(set, type))
+		return 0;
+
+	const struct sieve_storage *const *classes;
+	unsigned int i, count;
+
+	classes = array_get(&reg->storage_classes, &count);
+	ret = 0;
+	for (i = 0; i < count; i++) {
+		if (classes[i]->v.autodetect == NULL)
+			continue;
+		if (set->script_driver[0] != '\0' &&
+		    strcasecmp(set->script_driver,
+			       classes[i]->driver_name) != 0)
+			continue;
+
+		struct event *driver_event =
+			sieve_storage_create_driver_event(
+				event, classes[i]->driver_name);
+
+		*storage_r = NULL;
+		ret = classes[i]->v.autodetect(svinst, driver_event,
+					       cause, set, flags,
+					       storage_r, error_code_r,
+					       error_r);
+
+		event_unref(&driver_event);
+
+		if (ret < 0) {
+			i_assert(*error_code_r != SIEVE_ERROR_NONE);
+			i_assert(*error_r != NULL);
+			if (*error_code_r == SIEVE_ERROR_NOT_FOUND) {
+				*error_code_r = SIEVE_ERROR_NONE;
+				*error_r = NULL;
+				ret = 0;
+			}
+		}
+		i_assert(ret <= 0 || *storage_r != NULL);
+		if (ret != 0)
+			break;
+	}
+
+	if (ret == 0)
+		e_debug(event, "Autodetection failed");
+	return ret;
+}
+
+static int
+sieve_storage_autodetect_any(struct sieve_instance *svinst,
+			     struct event *event_parent,
+			     const char *cause, const char *type,
+			     const struct sieve_storage_settings *set,
+			     enum sieve_storage_flags flags,
+			     struct sieve_storage **storage_r,
+			     enum sieve_error *error_code_r,
+			     const char **error_r)
+{
+	struct event *event;
+	int ret;
+
+	event = sieve_storage_create_event(svinst, event_parent, NULL);
+
+	ret = sieve_storage_autodetect(svinst, event, cause, type, set,
+				       flags, storage_r, error_code_r, error_r);
+
+	event_unref(&event);
+	return ret;
 }
 
 static int
 sieve_storage_init_real(struct sieve_instance *svinst, struct event *event,
-			const struct sieve_storage *storage_class,
-			const char *data,
-			enum sieve_storage_flags flags, bool main,
+			const char *cause, const char *type,
+			const char *storage_name, bool try,
+			enum sieve_storage_flags flags,
 			struct sieve_storage **storage_r,
 			enum sieve_error *error_code_r, const char **error_r)
 {
+	const struct sieve_storage_settings *set;
 	struct sieve_storage *storage;
-	struct event *driver_event;
-	const char *const *options;
-	const char *location;
+	const char *error;
 	int ret;
 
-	driver_event = sieve_storage_create_driver_event(
-		event, storage_class->driver_name);
-	ret = sieve_storage_alloc(svinst, driver_event, storage_class,
-				  data, flags, main, &storage,
-				  error_code_r, error_r);
-	event_unref(&driver_event);
-	if (ret < 0)
+	if (try) {
+		ret = settings_try_get_filter(
+			event, "sieve_script", storage_name,
+			&sieve_storage_setting_parser_info, 0,
+			&set, &error);
+		if (ret == 0)
+			return 0;
+	} else {
+		ret = settings_get_filter(
+			event, "sieve_script", storage_name,
+			&sieve_storage_setting_parser_info, 0,
+			&set, &error);
+	}
+	if (ret < 0) {
+		e_error(event, "%s", error);
+		sieve_error_create_internal(error_code_r, error_r);
+		return -1;
+	}
+
+	if (!sieve_storage_settings_match_script_type(set, type)) {
+		settings_free(set);
+		return 0;
+	}
+
+	event_add_str(event, "sieve_script", storage_name);
+	if (set->script_driver[0] == '\0') {
+		ret = sieve_storage_autodetect(svinst, event, cause, type,
+					       set, flags, storage_r,
+					       error_code_r, error_r);
+		if (ret != 0) {
+			settings_free(set);
+			return ret;
+		}
+		e_error(event, "sieve_script_driver is empty");
+		sieve_error_create_script_not_found(set->script_name,
+						    error_code_r, error_r);
+		settings_free(set);
 		return -1;
+	}
+
+	ret = sieve_storage_alloc_from_settings(svinst, event, cause,
+						set, flags, &storage,
+						error_code_r, error_r);
+	settings_free(set);
+	if (ret <= 0)
+		return ret;
 	i_assert(storage != NULL);
 	i_assert(storage->v.init != NULL);
 
 	T_BEGIN {
-		if (sieve_storage_data_parse(storage, data,
-					     &location, &options) < 0) {
-			sieve_error_create_internal(error_code_r, error_r);
-			ret = -1;
-		} else {
-			storage->location = p_strdup(storage->pool, location);
-
-			event_add_str(event, "script_location",
-				      storage->location);
-
-			ret = storage_class->v.init(storage, options);
-			i_assert(ret <= 0);
-		}
+		ret = storage->v.init(storage);
+		i_assert(ret <= 0);
 	} T_END;
 	if (ret < 0) {
 		i_assert(storage->error_code != SIEVE_ERROR_NONE);
@@ -450,13 +519,14 @@ sieve_storage_init_real(struct sieve_instance *svinst, struct event *event,
 		return -1;
 	}
 	*storage_r = storage;
-	return 0;
+	return 1;
 }
 
 static int
 sieve_storage_init(struct sieve_instance *svinst, struct event *event_parent,
-		   const struct sieve_storage *storage_class, const char *data,
-		   enum sieve_storage_flags flags, bool main,
+		   const char *cause, const char *type,
+		   const char *storage_name, bool try,
+		   enum sieve_storage_flags flags,
 		   struct sieve_storage **storage_r,
 		   enum sieve_error *error_code_r, const char **error_r)
 {
@@ -465,10 +535,10 @@ sieve_storage_init(struct sieve_instance *svinst, struct event *event_parent,
 
 	*storage_r = NULL;
 
-	event = sieve_storage_create_event(svinst, event_parent);
+	event = sieve_storage_create_event(svinst, event_parent, storage_name);
 
-	ret = sieve_storage_init_real(svinst, event, storage_class,
-				      data, flags, main,
+	ret = sieve_storage_init_real(svinst, event, cause, type,
+				      storage_name, try, flags,
 				      storage_r, error_code_r, error_r);
 
 	event_unref(&event);
@@ -476,40 +546,112 @@ sieve_storage_init(struct sieve_instance *svinst, struct event *event_parent,
 	return ret;
 }
 
-int sieve_storage_create(struct sieve_instance *svinst, struct event *event,
-			 const char *location,
+int sieve_storage_create(struct sieve_instance *svinst,
+			 struct event *event, const char *cause,
+			 const char *storage_name,
 			 enum sieve_storage_flags flags,
 			 struct sieve_storage **storage_r,
 			 enum sieve_error *error_code_r, const char **error_r)
 {
-	const struct sieve_storage *storage_class;
-	const char *data;
+	struct sieve_storage *storage;
 	int ret;
 
-	/* Dont use this function for creating a synchronizing storage */
-	i_assert((flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0);
+	*storage_r = NULL;
+	sieve_error_args_init(&error_code_r, &error_r);
+
+	ret = sieve_storage_init(svinst, event, cause, SIEVE_STORAGE_TYPE_ANY,
+				 storage_name, TRUE, flags,
+				 &storage, error_code_r, error_r);
+	if (ret < 0) {
+		if (*error_code_r != SIEVE_ERROR_NOT_FOUND)
+			return -1;
+		ret = 0;
+	}
+	if (ret == 0) {
+		e_debug(event, "Sieve script storage '%s' not found (cause=%s)",
+			storage_name, cause);
+		sieve_error_create_script_not_found(
+			NULL, error_code_r, error_r);
+		return -1;
+	}
+	i_assert(storage != NULL);
+	*storage_r = storage;
+	return 0;
+}
+
+int sieve_storage_create_auto(struct sieve_instance *svinst,
+			      struct event *event,
+			      const char *cause, const char *type,
+			      enum sieve_storage_flags flags,
+			      struct sieve_storage **storage_r,
+			      enum sieve_error *error_code_r,
+			      const char **error_r)
+{
+	const struct sieve_storage_settings *storage_set;
+	const char *const *storage_names;
+	unsigned int i, count;
+	const char *error;
 
 	*storage_r = NULL;
 	sieve_error_args_init(&error_code_r, &error_r);
 
-	data = location;
-	if ((ret = sieve_storage_driver_parse(svinst, &data,
-					      &storage_class)) < 0) {
+	if (settings_get(event, &sieve_storage_setting_parser_info,
+			 SETTINGS_GET_FLAG_SORT_FILTER_ARRAYS,
+			 &storage_set, &error) < 0) {
+		e_error(event, "%s", error);
 		sieve_error_create_internal(error_code_r, error_r);
 		return -1;
 	}
+	if (array_is_created(&storage_set->storages))
+		storage_names = array_get(&storage_set->storages, &count);
+	else {
+		storage_names = NULL;
+		count = 0;
+	}
 
-	if (ret == 0)
-		storage_class = &sieve_file_storage;
+	struct sieve_storage *storage = NULL;
+	int ret = 0;
 
-	return sieve_storage_init(svinst, event, storage_class,
-				  data, flags, FALSE,
-				  storage_r, error_code_r, error_r);
+	for (i = 0; i < count; i++) {
+		ret = sieve_storage_init(svinst, event, cause, type,
+					 storage_names[i], FALSE, flags,
+					 &storage, error_code_r, error_r);
+		if (ret < 0 && *error_code_r != SIEVE_ERROR_NOT_FOUND) {
+			settings_free(storage_set);
+			return -1;
+		}
+		if (ret > 0) {
+			i_assert(storage != NULL);
+			break;
+		}
+	}
+	if (ret <= 0) {
+		ret = sieve_storage_autodetect_any(svinst, event, cause, type,
+						   storage_set, flags, &storage,
+						   error_code_r, error_r);
+		if (ret < 0) {
+			settings_free(storage_set);
+			return -1;
+		}
+	}
+	settings_free(storage_set);
+	if (ret <= 0) {
+		e_debug(event,
+			"storage: No matching Sieve storage configured "
+			"(type=%s and cause=%s)",
+			type, cause);
+		sieve_error_create_script_not_found(
+			NULL, error_code_r, error_r);
+		return -1;
+	}
+	i_assert(storage != NULL);
+	*storage_r = storage;
+	return 0;
 }
 
 static int
 sieve_storage_create_default(struct sieve_instance *svinst,
-			     struct event *event, const char *location,
+			     struct event *event, const char *cause,
 			     enum sieve_storage_flags flags,
 			     struct sieve_storage **storage_r,
 			     enum sieve_error *error_code_r,
@@ -522,33 +664,27 @@ sieve_storage_create_default(struct sieve_instance *svinst,
 	*storage_r = NULL;
 	sieve_error_args_init(&error_code_r, &error_r);
 
-	if (location == NULL) {
-		sieve_error_create_script_not_found(
-			NULL, error_code_r, error_r);
-		return -1;
-	}
-
-	ret = sieve_storage_create(svinst, event, location, flags,
-				   &storage, &error_code, error_r);
+	ret = sieve_storage_create_auto(svinst, event, cause,
+					SIEVE_STORAGE_TYPE_DEFAULT, flags,
+					&storage, &error_code, error_r);
 	if (ret >= 0) {
 		storage->is_default = TRUE;
+		sieve_storage_update_event_prefix(
+			event_get_parent(storage->event), storage->name, TRUE);
 	} else {
 		switch (error_code) {
 		case SIEVE_ERROR_NOT_FOUND:
 			e_debug(event, "storage: "
-				"Default script location '%s' not found",
-				location);
+				"Default script not found");
 			break;
 		case SIEVE_ERROR_TEMP_FAILURE:
 			e_error(event, "storage: "
-				"Failed to access default script location '%s' "
-				"(temporary failure)",
-				location);
+				"Failed to access default script "
+				"(temporary failure)");
 			break;
 		default:
 			e_error(event, "storage: "
-				"Failed to access default script location '%s'",
-				location);
+				"Failed to access default script");
 			break;
 		}
 		*error_code_r = error_code;
@@ -573,20 +709,13 @@ sieve_storage_create_default_for(struct sieve_storage *storage,
 		return 0;
 	}
 
-	if (storage->default_name == NULL) {
-		sieve_storage_set_not_found_error(storage, NULL);
-		*error_code_r = storage->error_code;
-		*error_r = storage->error;
-		return -1;
-	}
-
 	struct sieve_instance *svinst = storage->svinst;
 	enum sieve_error error_code;
 	const char *error;
 
 	i_assert(storage->default_storage_for == NULL);
 	if (sieve_storage_create_default(svinst, svinst->event,
-					 storage->default_location, 0,
+					 storage->cause, 0,
 					 &storage->default_storage,
 					 &error_code, &error) < 0) {
 		sieve_storage_set_error(storage, error_code, "%s", error);
@@ -602,77 +731,13 @@ sieve_storage_create_default_for(struct sieve_storage *storage,
 	return 0;
 }
 
-static int
-sieve_storage_do_create_personal(struct sieve_instance *svinst,
-				 enum sieve_storage_flags flags,
-				 struct sieve_storage **storage_r,
-				 enum sieve_error *error_code_r)
-{
-	struct sieve_storage *storage = NULL;
-	const struct sieve_storage *sieve_class = NULL;
-	const char *set_sieve, *data;
-	int ret;
-
-	/* Sieve storage location */
-
-	set_sieve = sieve_setting_get(svinst, "sieve");
-
-	if (set_sieve != NULL) {
-		if (*set_sieve == '\0') {
-			/* disabled */
-			e_debug(svinst->event, "storage: "
-				"Personal storage is disabled (sieve=\"\")");
-			*error_code_r = SIEVE_ERROR_NOT_FOUND;
-			return -1;
-		}
-
-		data = set_sieve;
-		if ((ret = sieve_storage_driver_parse(svinst, &data,
-						      &sieve_class)) < 0) {
-			sieve_error_create_internal(error_code_r, NULL);
-			return -1;
-		}
-
-		if (ret > 0) {
-			/* The normal case: explicit driver name */
-			if (sieve_storage_init(svinst, svinst->event,
-					       sieve_class, data, flags, TRUE,
-					       &storage, error_code_r,
-					       NULL) < 0)
-				return -1;
-		}
-
-		/* No driver name */
-	}
-
-	if (storage == NULL) {
-		const char *error;
-
-		i_assert(sieve_file_storage.v.autodetect != NULL);
-		if (sieve_file_storage.v.autodetect(svinst, set_sieve, flags,
-						    &storage, error_code_r,
-						    &error) < 0)
-			return -1;
-		i_assert(storage != NULL);
-	}
-
-	if (storage == NULL)
-		return -1;
-
-	sieve_storage_get_quota_settings(storage);
-
-	*storage_r = storage;
-	return 0;
-}
-
 int sieve_storage_create_personal(struct sieve_instance *svinst,
-				  struct mail_user *user,
+				  struct mail_user *user, const char *cause,
 				  enum sieve_storage_flags flags,
 				  struct sieve_storage **storage_r,
 				  enum sieve_error *error_code_r)
 {
-	struct sieve_storage *storage = NULL;
-	const char *set_default, *set_default_name;
+	struct sieve_storage *storage;
 	int ret;
 
 	*storage_r = NULL;
@@ -686,54 +751,20 @@ int sieve_storage_create_personal(struct sieve_instance *svinst,
 		return -1;
 	}
 
-	/* Determine location for default script */
-	set_default = sieve_setting_get(svinst, "sieve_default");
-
 	/* Attempt to locate user's main storage */
-	ret = sieve_storage_do_create_personal(svinst, flags,
-					       &storage, error_code_r);
+	ret = sieve_storage_create_auto(svinst, svinst->event, cause,
+					SIEVE_STORAGE_TYPE_PERSONAL, flags,
+					&storage, error_code_r, NULL);
 	if (ret == 0) {
 		(void)sieve_storage_sync_init(storage, user);
-
-		/* Success; record default script location for later use */
-		storage->default_location =
-			p_strdup_empty(storage->pool, set_default);
-
-		set_default_name =
-			sieve_setting_get(svinst, "sieve_default_name");
-		if (set_default_name != NULL && *set_default_name != '\0' &&
-		    !sieve_script_name_is_valid(set_default_name)) {
-			e_error(storage->event,
-				"Invalid script name '%s' for 'sieve_default_name' setting.",
-				str_sanitize(set_default_name, 80));
-			set_default_name = NULL;
-		}
-		storage->default_name =
-			p_strdup_empty(storage->pool, set_default_name);
-
-		if (storage->default_location != NULL &&
-			storage->default_name != NULL) {
-			e_debug(storage->event,
-				"Default script at '%s' is visible by name '%s'",
-				storage->default_location, storage->default_name);
-		}
 	} else if (*error_code_r != SIEVE_ERROR_TEMP_FAILURE &&
 		   (flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 &&
 		   (flags & SIEVE_STORAGE_FLAG_READWRITE) == 0) {
 
 		/* Failed; try using default script location
 		   (not for temporary failures, read/write access, or dsync) */
-		if (set_default == NULL) {
-			e_debug(svinst->event, "storage: "
-				"No default script location configured");
-		} else {
-			e_debug(svinst->event, "storage: "
-				"Trying default script location '%s'",
-				set_default);
-		}
-
 		ret = sieve_storage_create_default(svinst, svinst->event,
-						   set_default, 0, &storage,
+						   cause, flags, &storage,
 						   error_code_r, NULL);
 	}
 	*storage_r = storage;
@@ -864,6 +895,8 @@ void sieve_storage_set_modified(struct sieve_storage *storage, time_t mtime)
 int sieve_storage_cmp(const struct sieve_storage *storage1,
 		      const struct sieve_storage *storage2)
 {
+	int ret;
+
 	if (storage1 == storage2)
 		return 0;
 	if (storage1 == NULL || storage2 == NULL)
@@ -872,7 +905,10 @@ int sieve_storage_cmp(const struct sieve_storage *storage1,
 		return (storage1->storage_class > storage2->storage_class ?
 			1 : -1);
 	}
-	return null_strcmp(storage1->location, storage2->location);
+	ret = null_strcmp(storage1->type, storage2->type);
+	if (ret != 0)
+		return (ret > 0 ? 1 : -1);
+	return null_strcmp(storage1->name, storage2->name);
 }
 
 unsigned int sieve_storage_hash(const struct sieve_storage *storage)
@@ -880,7 +916,8 @@ unsigned int sieve_storage_hash(const struct sieve_storage *storage)
 	unsigned int hash = 0;
 
 	hash ^= POINTER_CAST_TO(storage->storage_class, unsigned int);
-	hash ^= str_hash(storage->location);
+	hash ^= str_hash(storage->type);
+	hash ^= str_hash(storage->name);
 
 	return hash;
 }
@@ -905,6 +942,8 @@ int sieve_storage_get_script_direct(struct sieve_storage *storage,
 		*error_code_r = storage->error_code;
 		return -1;
 	}
+	if (name == NULL)
+		name = storage->script_name;
 
 	i_assert(storage->v.get_script != NULL);
 	ret = storage->v.get_script(storage, name, script_r);
@@ -927,7 +966,8 @@ sieve_storage_get_default_script(struct sieve_storage *storage,
 	int ret;
 
 	if (*error_code_r != SIEVE_ERROR_NOT_FOUND ||
-	    (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) != 0)
+	    (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) != 0 ||
+	    !sieve_storage_is_personal(storage))
 		return -1;
 
 	/* Not found; if this name maps to the default script,
@@ -941,7 +981,7 @@ sieve_storage_get_default_script(struct sieve_storage *storage,
 	if (ret < 0)
 		return -1;
 
-	if (strcmp(storage->default_name, name) != 0) {
+	if (strcmp(def_storage->script_name, name) != 0) {
 		sieve_storage_set_error(storage,
 			SIEVE_ERROR_NOT_FOUND,
 			"Default script '%s' not found",
@@ -1089,10 +1129,10 @@ sieve_storage_active_script_do_get_name(struct sieve_storage *storage,
 	if (ret < 0)
 		return -1;
 
-	*name_r = storage->default_name;
+	*name_r = def_storage->script_name;
 
 	ret = sieve_storage_check_script(def_storage,
-					 storage->default_name, NULL);
+					 def_storage->script_name, NULL);
 	if (ret < 0) {
 		sieve_storage_copy_error(storage, def_storage);
 		i_assert(storage->error_code != SIEVE_ERROR_NONE);
@@ -1267,13 +1307,13 @@ sieve_storage_list_next(struct sieve_storage_list_context *lctx, bool *active_r)
 
 	struct sieve_storage *def_storage = lctx->def_storage;
 	bool have_default = (def_storage != NULL &&
-			     storage->default_name != NULL);
+			     def_storage->script_name != NULL);
 
 	if (scriptname != NULL) {
 		/* Remember when we see that the storage has its own script for
 		   default */
 		if (have_default &&
-		    strcmp(scriptname, storage->default_name) == 0)
+		    strcmp(scriptname, def_storage->script_name) == 0)
 			lctx->seen_default = TRUE;
 
 	} else if (have_default && !lctx->seen_default &&
@@ -1282,7 +1322,7 @@ sieve_storage_list_next(struct sieve_storage_list_context *lctx, bool *active_r)
 		/* Return default script at the end if it was not listed
 		   thus far (storage backend has no script under default
 		   name) */
-		scriptname = storage->default_name;
+		scriptname = def_storage->script_name;
 		lctx->seen_default = TRUE;
 
 		/* Mark default as active if no normal script is active */
@@ -1519,8 +1559,6 @@ sieve_storage_save_is_activating_default(
 {
 	struct sieve_storage *storage = sctx->storage;
 
-	if (storage->default_name == NULL || storage->default_location == NULL)
-		return 0;
 	if ((storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) != 0)
 		return 0;
 	if (!sieve_storage_save_will_activate(sctx))
@@ -1537,9 +1575,9 @@ sieve_storage_save_is_activating_default(
 		return -1;
 	}
 
-	if (strcmp(sctx->scriptname, storage->default_name) == 0) {
+	if (strcmp(sctx->scriptname, def_storage->script_name) == 0) {
 		ret = sieve_storage_check_script_direct(
-			storage, storage->default_name, &error_code);
+			storage, def_storage->script_name, &error_code);
 		if (ret == 0 ||
 		    (ret < 0 && error_code == SIEVE_ERROR_NOT_FOUND))
 			ret = 1;
@@ -1793,9 +1831,9 @@ int sieve_storage_quota_havespace(struct sieve_storage *storage,
  * Properties
  */
 
-const char *sieve_storage_location(const struct sieve_storage *storage)
+const char *sieve_storage_name(const struct sieve_storage *storage)
 {
-	return storage->location;
+	return storage->name;
 }
 
 bool sieve_storage_is_default(const struct sieve_storage *storage)
@@ -1803,6 +1841,11 @@ bool sieve_storage_is_default(const struct sieve_storage *storage)
 	return storage->is_default;
 }
 
+bool sieve_storage_is_personal(struct sieve_storage *storage)
+{
+	return (strcasecmp(storage->type, SIEVE_STORAGE_TYPE_PERSONAL) == 0);
+}
+
 /*
  * Error handling
  */
@@ -1910,19 +1953,43 @@ sieve_storage_get_last_error(struct sieve_storage *storage,
 
 int sieve_storage_sequence_create(struct sieve_instance *svinst,
 				  struct event *event_parent,
-				  const char *location,
+				  const char *cause, const char *type,
 				  struct sieve_storage_sequence **sseq_r,
 				  enum sieve_error *error_code_r,
 				  const char **error_r)
 {
+	const struct sieve_storage_settings *storage_set;
+	const char *const *storage_names;
+	unsigned int storage_count;
+	const char *error;
+
 	*sseq_r = NULL;
 	sieve_error_args_init(&error_code_r, &error_r);
 
+	if (settings_get(event_parent, &sieve_storage_setting_parser_info,
+			 SETTINGS_GET_FLAG_SORT_FILTER_ARRAYS,
+			 &storage_set, &error) < 0) {
+		e_error(event_parent, "%s", error);
+		sieve_error_create_internal(error_code_r, error_r);
+		return -1;
+	}
+	if (array_is_created(&storage_set->storages))
+		storage_names = array_get(&storage_set->storages,
+					  &storage_count);
+	else {
+		storage_names = empty_str_array;
+		storage_count = 0;
+	}
+
 	struct sieve_storage_sequence *sseq;
 
 	sseq = i_new(struct sieve_storage_sequence, 1);
 	sseq->svinst = svinst;
-	sseq->location = i_strdup(location);
+	sseq->cause = i_strdup(cause);
+	sseq->type = i_strdup(type);
+	sseq->storage_set = storage_set;
+	sseq->storage_names = p_strarray_dup(default_pool, storage_names);
+	sseq->storage_count = storage_count;
 
 	sseq->event_parent = event_parent;
 	event_ref(event_parent);
@@ -1937,18 +2004,33 @@ int sieve_storage_sequence_next(struct sieve_storage_sequence *sseq,
 				const char **error_r)
 {
 	struct sieve_instance *svinst = sseq->svinst;
+	int ret;
 
 	*storage_r = NULL;
 	sieve_error_args_init(&error_code_r, &error_r);
 
-	if (sseq->done)
-		return 0;
-	sseq->done = TRUE;
+	while (sseq->storage_index < sseq->storage_count) {
+		unsigned int index = sseq->storage_index++;
 
-	if (sieve_storage_create(svinst, sseq->event_parent, sseq->location, 0,
-				 storage_r, error_code_r, error_r) < 0)
-		return -1;
-	return 1;
+		ret = sieve_storage_init(svinst, sseq->event_parent,
+					 sseq->cause, sseq->type,
+					 sseq->storage_names[index], TRUE, 0,
+					 storage_r, error_code_r, error_r);
+		if (ret < 0) {
+			if (*error_code_r == SIEVE_ERROR_NOT_FOUND) {
+				*error_code_r = SIEVE_ERROR_NONE;
+				*error_r = NULL;
+				continue;
+			}
+			return -1;
+		}
+		if (ret > 0) {
+			i_assert(*storage_r != NULL);
+			return 1;
+		}
+	}
+
+	return 0;
 }
 
 void sieve_storage_sequence_free(struct sieve_storage_sequence **_sseq)
@@ -1960,6 +2042,9 @@ void sieve_storage_sequence_free(struct sieve_storage_sequence **_sseq)
 	*_sseq = NULL;
 
 	event_unref(&sseq->event_parent);
-	i_free(sseq->location);
+	i_free(sseq->cause);
+	i_free(sseq->type);
+	settings_free(sseq->storage_set);
+	i_free(sseq->storage_names);
 	i_free(sseq);
 }
diff --git a/src/lib-sieve/sieve-storage.h b/src/lib-sieve/sieve-storage.h
index 58ec5979e6a653f0872375b18bd6d3c4706baf14..868d8fea1563e33db6d7daa17bb3f8b58f33d073 100644
--- a/src/lib-sieve/sieve-storage.h
+++ b/src/lib-sieve/sieve-storage.h
@@ -1,6 +1,8 @@
 #ifndef SIEVE_STORAGE_H
 #define SIEVE_STORAGE_H
 
+#include "sieve-common.h"
+
 #define MAILBOX_ATTRIBUTE_PREFIX_SIEVE \
 	MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER"sieve/"
 #define MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES \
@@ -11,6 +13,25 @@
 #define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_LINK 'L'
 #define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_SCRIPT 'S'
 
+/*
+ * Storage name
+ */
+
+bool sieve_storage_name_is_valid(const char *name);
+
+/*
+ * Storage type
+ */
+
+/* Common storage types; others may be defined in specific contexts. */
+#define SIEVE_STORAGE_TYPE_ANY "any"
+#define SIEVE_STORAGE_TYPE_PERSONAL "personal"
+#define SIEVE_STORAGE_TYPE_DEFAULT "default"
+#define SIEVE_STORAGE_TYPE_GLOBAL "global"
+#define SIEVE_STORAGE_TYPE_BEFORE "before"
+#define SIEVE_STORAGE_TYPE_AFTER "after"
+#define SIEVE_STORAGE_TYPE_DISCARD "discard"
+
 /*
  * Storage object
  */
@@ -29,13 +50,21 @@ struct sieve_storage;
 bool sieve_storage_class_exists(struct sieve_instance *svinst,
 				const char *name);
 
-int sieve_storage_create(struct sieve_instance *svinst, struct event *event,
-			 const char *location,
+int sieve_storage_create(struct sieve_instance *svinst,
+			 struct event *event, const char *cause,
+			 const char *storage_name,
 			 enum sieve_storage_flags flags,
 			 struct sieve_storage **storage_r,
 			 enum sieve_error *error_code_r, const char **error_r);
+int sieve_storage_create_auto(struct sieve_instance *svinst,
+			      struct event *event,
+			      const char *cause, const char *type,
+			      enum sieve_storage_flags flags,
+			      struct sieve_storage **storage_r,
+			      enum sieve_error *error_code_r,
+			      const char **error_r);
 int sieve_storage_create_personal(struct sieve_instance *svinst,
-				  struct mail_user *user,
+				  struct mail_user *user, const char *cause,
 				  enum sieve_storage_flags flags,
 				  struct sieve_storage **storage_r,
 				  enum sieve_error *error_code_r);
@@ -160,6 +189,7 @@ const char *sieve_storage_name(const struct sieve_storage *storage) ATTR_PURE;
 const char *sieve_storage_location(const struct sieve_storage *storage)
 	ATTR_PURE;
 bool sieve_storage_is_default(const struct sieve_storage *storage) ATTR_PURE;
+bool sieve_storage_is_personal(struct sieve_storage *storage) ATTR_PURE;
 
 int sieve_storage_is_singular(struct sieve_storage *storage);
 
@@ -195,7 +225,7 @@ struct sieve_storage_sequence;
 
 int sieve_storage_sequence_create(struct sieve_instance *svinst,
 				  struct event *event_parent,
-				  const char *location,
+				  const char *cause, const char *type,
 				  struct sieve_storage_sequence **sseq_r,
 				  enum sieve_error *error_code_r,
 				  const char **error_r);
diff --git a/src/lib-sieve/sieve-types.h b/src/lib-sieve/sieve-types.h
index bb1cb85711f95361c0786d483b9edd2b67be2549..fbdf533971e581fbcba6178d9242a9dee6cb03bb 100644
--- a/src/lib-sieve/sieve-types.h
+++ b/src/lib-sieve/sieve-types.h
@@ -31,6 +31,8 @@ struct sieve_trace_log;
 enum sieve_flag {
 	/* Relative paths are resolved to HOME */
 	SIEVE_FLAG_HOME_RELATIVE = (1 << 0),
+	/* Sieve is running in command line tool */
+	SIEVE_FLAG_COMMAND_LINE = (1 << 1),
 };
 
 /* Sieve evaluation can be performed at various different points as messages
@@ -204,6 +206,13 @@ enum sieve_duplicate_check_result {
 	SIEVE_DUPLICATE_CHECK_RESULT_TEMP_FAILURE = -2,
 };
 
+/*
+ * Script invocation cause
+ */
+
+#define SIEVE_SCRIPT_CAUSE_ANY "any"
+#define SIEVE_SCRIPT_CAUSE_DELIVERY "delivery"
+
 /*
  * Script environment
  *
diff --git a/src/lib-sieve/sieve-validator.c b/src/lib-sieve/sieve-validator.c
index 63d5103bab2a89a04c3f6a7fb4536160edc8bac1..4914ab97c76bb6f44e357680eab3dd11267d1d89 100644
--- a/src/lib-sieve/sieve-validator.c
+++ b/src/lib-sieve/sieve-validator.c
@@ -218,6 +218,11 @@ struct sieve_script *sieve_validator_script(struct sieve_validator *valdtr)
 	return valdtr->script;
 }
 
+const char *sieve_validator_script_cause(struct sieve_validator *valdtr)
+{
+	return sieve_script_cause(valdtr->script);
+}
+
 struct sieve_instance *sieve_validator_svinst(struct sieve_validator *valdtr)
 {
 	return valdtr->svinst;
diff --git a/src/lib-sieve/sieve-validator.h b/src/lib-sieve/sieve-validator.h
index 21b41ccf166d84c351870c9ae682ee06a76272bf..a1af14b45bf74ffb1955e99100b60e3c71e8975d 100644
--- a/src/lib-sieve/sieve-validator.h
+++ b/src/lib-sieve/sieve-validator.h
@@ -43,6 +43,7 @@ struct sieve_error_handler *
 sieve_validator_error_handler(struct sieve_validator *valdtr);
 struct sieve_ast *sieve_validator_ast(struct sieve_validator *valdtr);
 struct sieve_script *sieve_validator_script(struct sieve_validator *valdtr);
+const char *sieve_validator_script_cause(struct sieve_validator *valdtr);
 struct sieve_instance *sieve_validator_svinst(struct sieve_validator *valdtr);
 enum sieve_compile_flags
 sieve_validator_compile_flags(struct sieve_validator *valdtr);
diff --git a/src/lib-sieve/sieve.c b/src/lib-sieve/sieve.c
index 70cd01c8509396843a51c632ee13d1ddb50f2892..409d65f210a1632142922a7ee3905a5e69284701 100644
--- a/src/lib-sieve/sieve.c
+++ b/src/lib-sieve/sieve.c
@@ -325,8 +325,9 @@ int sieve_compile_script(struct sieve_script *script,
 	return 0;
 }
 
-int sieve_compile(struct sieve_instance *svinst, const char *script_location,
-		  const char *script_name, struct sieve_error_handler *ehandler,
+int sieve_compile(struct sieve_instance *svinst, const char *script_cause,
+		  const char *storage_name, const char *script_name,
+		  struct sieve_error_handler *ehandler,
 		  enum sieve_compile_flags flags, struct sieve_binary **sbin_r,
 		  enum sieve_error *error_code_r)
 {
@@ -336,8 +337,9 @@ int sieve_compile(struct sieve_instance *svinst, const char *script_location,
 	*sbin_r = NULL;
 	sieve_error_args_init(&error_code_r, NULL);
 
-	if (sieve_script_create_open(svinst, script_location, script_name,
-				     &script, error_code_r, NULL) < 0) {
+	if (sieve_script_create_open_in(svinst, script_cause,
+					storage_name, script_name,
+					&script, error_code_r, NULL) < 0) {
 		switch (*error_code_r) {
 		case SIEVE_ERROR_NOT_FOUND:
 			if (no_error_result) {
@@ -499,8 +501,9 @@ int sieve_open_script(struct sieve_script *script,
 	return ret;
 }
 
-int sieve_open(struct sieve_instance *svinst, const char *script_location,
-	       const char *script_name, struct sieve_error_handler *ehandler,
+int sieve_open(struct sieve_instance *svinst, const char *script_cause,
+	       const char *storage_name, const char *script_name,
+	       struct sieve_error_handler *ehandler,
 	       enum sieve_compile_flags flags, struct sieve_binary **sbin_r,
 	       enum sieve_error *error_code_r)
 {
@@ -512,8 +515,9 @@ int sieve_open(struct sieve_instance *svinst, const char *script_location,
 	sieve_error_args_init(&error_code_r, NULL);
 
 	/* First open the scriptfile itself */
-	if (sieve_script_create_open(svinst, script_location, script_name,
-				     &script, error_code_r, NULL) < 0) {
+	if (sieve_script_create_open_in(svinst, script_cause,
+					storage_name, script_name,
+					&script, error_code_r, NULL) < 0) {
 		/* Failed */
 		switch (*error_code_r) {
 		case SIEVE_ERROR_NOT_FOUND:
diff --git a/src/lib-sieve/sieve.h b/src/lib-sieve/sieve.h
index 29bd320a2e7da822544bc62a88651f5b135fcd26..8fcf939681eb3f3885ce1a8e7939e67d4848a98d 100644
--- a/src/lib-sieve/sieve.h
+++ b/src/lib-sieve/sieve.h
@@ -50,8 +50,9 @@ int sieve_compile_script(struct sieve_script *script,
 /* Compile a Sieve script from a Sieve script location string. Returns Sieve
    binary upon success and NULL upon failure. The provided script_name is used
    for the internally created Sieve script object. */
-int sieve_compile(struct sieve_instance *svinst, const char *script_location,
-		  const char *script_name, struct sieve_error_handler *ehandler,
+int sieve_compile(struct sieve_instance *svinst, const char *script_cause,
+		  const char *storage_name, const char *script_name,
+		  struct sieve_error_handler *ehandler,
 		  enum sieve_compile_flags flags, struct sieve_binary **sbin_r,
 		  enum sieve_error *error_code_r);
 
@@ -75,8 +76,9 @@ int sieve_open_script(struct sieve_script *script,
    not exist or if it contains errors, the script is (re-)compiled. Note that
    errors in the bytecode are caught only at runtime.
  */
-int sieve_open(struct sieve_instance *svinst, const char *script_location,
-	       const char *script_name, struct sieve_error_handler *ehandler,
+int sieve_open(struct sieve_instance *svinst, const char *script_cause,
+	       const char *script_storage, const char *script_name,
+	       struct sieve_error_handler *ehandler,
 	       enum sieve_compile_flags flags, struct sieve_binary **sbin_r,
 	       enum sieve_error *error_code_r);
 
diff --git a/src/lib-sieve/storage/data/sieve-data-script.c b/src/lib-sieve/storage/data/sieve-data-script.c
index 6f50b29c1b0c00e6373593b35d74433c7ff8d6ed..f84933a0b1c48eb537146afedf570f3528db6aed 100644
--- a/src/lib-sieve/storage/data/sieve-data-script.c
+++ b/src/lib-sieve/storage/data/sieve-data-script.c
@@ -32,19 +32,20 @@ static struct sieve_data_script *sieve_data_script_alloc(void)
 
 struct sieve_script *
 sieve_data_script_create_from_input(struct sieve_instance *svinst,
-				    const char *name, struct istream *input)
+				    const char *cause, const char *name,
+				    struct istream *input)
 {
 	struct sieve_storage *storage;
 	struct sieve_data_script *dscript = NULL;
 	int ret;
 
-	ret = sieve_storage_alloc(svinst, NULL, &sieve_data_storage,
-				  "", 0, FALSE, &storage, NULL, NULL);
+	ret = sieve_storage_alloc(svinst, svinst->event, &sieve_data_storage,
+				  cause, "data", "data", "data", 0, &storage,
+				  NULL, NULL);
 	i_assert(ret >= 0);
 
 	dscript = sieve_data_script_alloc();
-	sieve_script_init(&dscript->script, storage, &sieve_data_script,
-			  "data:", name);
+	sieve_script_init(&dscript->script, storage, &sieve_data_script, name);
 
 	dscript->data = input;
 	i_stream_ref(dscript->data);
diff --git a/src/lib-sieve/storage/data/sieve-data-storage.c b/src/lib-sieve/storage/data/sieve-data-storage.c
index 4f645532f4ad64a9ad3128e84ba4a9bf0c080e16..1e62fa8e02f9728a1b582226e0a10a2b7bcf22be 100644
--- a/src/lib-sieve/storage/data/sieve-data-storage.c
+++ b/src/lib-sieve/storage/data/sieve-data-storage.c
@@ -26,8 +26,7 @@ static struct sieve_storage *sieve_data_storage_alloc(void)
 }
 
 static int
-sieve_data_storage_init(struct sieve_storage *storage ATTR_UNUSED,
-			const char *const *options ATTR_UNUSED)
+sieve_data_storage_init(struct sieve_storage *storage ATTR_UNUSED)
 {
 	return 0;
 }
diff --git a/src/lib-sieve/storage/data/sieve-data-storage.h b/src/lib-sieve/storage/data/sieve-data-storage.h
index 79132105c290bd5ee5ada2de9d327c2143b063a7..3bf31811b4c041f11e5d6e895753b3db7c9d36a8 100644
--- a/src/lib-sieve/storage/data/sieve-data-storage.h
+++ b/src/lib-sieve/storage/data/sieve-data-storage.h
@@ -25,6 +25,7 @@ struct sieve_data_script {
 
 struct sieve_script *
 sieve_data_script_create_from_input(struct sieve_instance *svinst,
-				    const char *name, struct istream *input);
+				    const char *cause, const char *name,
+				    struct istream *input);
 
 #endif
diff --git a/src/lib-sieve/storage/dict/sieve-dict-script.c b/src/lib-sieve/storage/dict/sieve-dict-script.c
index 8e8b836537607b691e8251bcef2421f85757f527..25de7d9d07c37a9c9a336335f8804f086c1e4827 100644
--- a/src/lib-sieve/storage/dict/sieve-dict-script.c
+++ b/src/lib-sieve/storage/dict/sieve-dict-script.c
@@ -36,18 +36,12 @@ sieve_dict_script_init(struct sieve_dict_storage *dstorage, const char *name)
 {
 	struct sieve_storage *storage = &dstorage->storage;
 	struct sieve_dict_script *dscript = NULL;
-	const char *location;
 
-	if (name == NULL) {
+	if (name == NULL || *name == '\0')
 		name = SIEVE_DICT_SCRIPT_DEFAULT;
-		location = storage->location;
-	} else {
-		location = t_strconcat(storage->location, ";name=", name, NULL);
-	}
 
 	dscript = sieve_dict_script_alloc();
-	sieve_script_init(&dscript->script, storage, &sieve_dict_script,
-			  location, name);
+	sieve_script_init(&dscript->script, storage, &sieve_dict_script, name);
 
 	return dscript;
 }
diff --git a/src/lib-sieve/storage/dict/sieve-dict-storage.c b/src/lib-sieve/storage/dict/sieve-dict-storage.c
index 894d6753036c3de06eb3bc2d56998cee39e8dae3..4bfba324eeae98197581a9f951f532d80a885df4 100644
--- a/src/lib-sieve/storage/dict/sieve-dict-storage.c
+++ b/src/lib-sieve/storage/dict/sieve-dict-storage.c
@@ -2,6 +2,7 @@
  */
 
 #include "lib.h"
+#include "settings.h"
 #include "dict.h"
 
 #include "sieve-common.h"
@@ -27,55 +28,21 @@ static struct sieve_storage *sieve_dict_storage_alloc(void)
 }
 
 static int
-sieve_dict_storage_init(struct sieve_storage *storage,
-			const char *const *options)
+sieve_dict_storage_init(struct sieve_storage *storage)
 {
 	struct sieve_dict_storage *dstorage =
 		container_of(storage, struct sieve_dict_storage, storage);
-	struct sieve_instance *svinst = storage->svinst;
-	const char *value, *uri = storage->location;
-
-	if (options != NULL) {
-		while (*options != NULL) {
-			const char *option = *options;
-
-			if (str_begins_icase(option, "user=", &value) &&
-			    *value != '\0') {
-				/* Ignore */
-			} else {
-				sieve_storage_set_critical(
-					storage, "Invalid option '%s'", option);
-				return -1;
-			}
-
-			options++;
-		}
-	}
-
-	if (svinst->base_dir == NULL) {
-		sieve_storage_set_critical(
-			storage,
-			"BUG: Sieve interpreter is initialized without a base_dir");
-		return -1;
-	}
-
-	e_debug(storage->event, "user=%s, uri=%s", svinst->username, uri);
-
-	storage->location = p_strconcat(
-		storage->pool, SIEVE_DICT_STORAGE_DRIVER_NAME, ":",
-		storage->location, ";user=", svinst->username, NULL);
-
-	struct dict_legacy_settings dict_set;
 	const char *error;
 	int ret;
 
-	i_zero(&dict_set);
-	dict_set.base_dir = svinst->base_dir;
-	ret = dict_init_legacy(uri, &dict_set, &dstorage->dict, &error);
-	if (ret < 0) {
+	struct event *event = event_create(storage->event);
+	event_set_ptr(event, SETTINGS_EVENT_FILTER_NAME, "sieve_script_dict");
+	ret = dict_init_auto(event, &dstorage->dict, &error);
+	event_unref(&event);
+	if (ret <= 0) {
 		sieve_storage_set_critical(storage,
-			"Failed to initialize dict with data '%s' for user '%s': %s",
-			uri, svinst->username, error);
+			"Failed to initialize sieve_script %s dict: %s",
+			storage->name, error);
 		return -1;
 	}
 
diff --git a/src/lib-sieve/storage/file/Makefile.am b/src/lib-sieve/storage/file/Makefile.am
index 86798121aa96458d7ccc74b1cbd039eda8c99cff..104470d931ab17d4bd4f0d7020effd7a054fcb05 100644
--- a/src/lib-sieve/storage/file/Makefile.am
+++ b/src/lib-sieve/storage/file/Makefile.am
@@ -9,6 +9,7 @@ AM_CPPFLAGS = \
 libsieve_storage_file_la_SOURCES = \
 	sieve-file-script.c \
 	sieve-file-script-sequence.c \
+	sieve-file-storage-settings.c \
 	sieve-file-storage-active.c \
 	sieve-file-storage-save.c \
 	sieve-file-storage-list.c \
diff --git a/src/lib-sieve/storage/file/sieve-file-script.c b/src/lib-sieve/storage/file/sieve-file-script.c
index 6f7f7bd73b7ffe47b81e60c958362cf51d441736..6cc1dc9387b4e3104b6b6ee15ffe6d8660ac0565 100644
--- a/src/lib-sieve/storage/file/sieve-file-script.c
+++ b/src/lib-sieve/storage/file/sieve-file-script.c
@@ -3,11 +3,13 @@
 
 #include "lib.h"
 #include "mempool.h"
+#include "str.h"
 #include "path-util.h"
 #include "istream.h"
 #include "time-util.h"
 #include "eacces-error.h"
 
+#include "sieve-dump.h"
 #include "sieve-binary.h"
 #include "sieve-script-private.h"
 
@@ -125,10 +127,12 @@ int sieve_file_script_init_from_filename(struct sieve_file_storage *fstorage,
 
 	fscript = sieve_file_script_alloc();
 	sieve_script_init(&fscript->script, storage, &sieve_file_script,
-			  sieve_file_storage_path_extend(fstorage, filename),
 			  scriptname);
 	fscript->filename = p_strdup(fscript->script.pool, filename);
 
+	event_add_str(fscript->script.event, "sieve_script_file_path",
+		      sieve_file_storage_path_extend(fstorage, filename));
+
 	*fscript_r = fscript;
 	return 0;
 }
@@ -173,8 +177,11 @@ int sieve_file_script_init_from_name(struct sieve_file_storage *fstorage,
 	}
 
 	fscript = sieve_file_script_alloc();
-	sieve_script_init(&fscript->script, storage, &sieve_file_script,
-			  fstorage->active_path, name);
+	sieve_script_init(&fscript->script, storage, &sieve_file_script, name);
+
+	event_add_str(fscript->script.event, "sieve_script_file_path",
+		      fstorage->active_path);
+
 	*fscript_r = fscript;
 	return 0;
 }
@@ -215,7 +222,9 @@ int sieve_file_script_init_from_path(struct sieve_file_storage *fstorage,
 
 	*fscript_r = NULL;
 
-	if (sieve_file_storage_init_from_path(svinst, path, 0, &fsubstorage,
+	if (sieve_file_storage_init_from_path(svinst, storage->cause,
+					      storage->type, storage->name,
+					      path, 0, &fsubstorage,
 					      &error_code, &error) < 0) {
 		sieve_storage_set_error(storage, error_code, "%s", error);
 		return -1;
@@ -224,9 +233,12 @@ int sieve_file_script_init_from_path(struct sieve_file_storage *fstorage,
 
 	fscript = sieve_file_script_alloc();
 	sieve_script_init(&fscript->script, substorage, &sieve_file_script,
-			  path, scriptname);
+			  scriptname);
 	sieve_storage_unref(&substorage);
 
+	event_add_str(fscript->script.event, "sieve_script_file_path",
+		      fstorage->active_path);
+
 	*fscript_r = fscript;
 	return 0;
 }
@@ -309,7 +321,8 @@ static int sieve_file_script_open(struct sieve_script *script)
 	st = fstorage->st;
 	lnk_st = fstorage->lnk_st;
 
-	if (name == NULL)
+	if (name == NULL && storage->script_name != NULL &&
+	    *storage->script_name != '\0')
 		name = storage->script_name;
 
 	T_BEGIN {
@@ -411,10 +424,11 @@ static int sieve_file_script_open(struct sieve_script *script)
 			fscript->bin_path = p_strdup(pool, bin_path);
 			fscript->bin_prefix = p_strdup(pool, bin_prefix);
 
-			fscript->script.location = fscript->path;
-
 			if (fscript->script.name == NULL)
 				fscript->script.name = p_strdup(pool, basename);
+
+			event_add_str(script->event, "sieve_script_file_path",
+				      fscript->path);
 		}
 	} T_END;
 
@@ -477,12 +491,36 @@ sieve_file_script_get_stream(struct sieve_script *script,
 static int
 sieve_file_script_binary_read_metadata(struct sieve_script *script,
 				       struct sieve_binary_block *sblock,
-				       sieve_size_t *offset ATTR_UNUSED)
+				       sieve_size_t *offset)
 {
+	struct sieve_instance *svinst = script->storage->svinst;
 	struct sieve_file_script *fscript =
 		container_of(script, struct sieve_file_script, script);
-	struct sieve_instance *svinst = script->storage->svinst;
 	struct sieve_binary *sbin = sieve_binary_block_get_binary(sblock);
+	string_t *path;
+
+	/* Open if not open already */
+	if (sieve_script_open(script, NULL) < 0)
+		return 0;
+
+	/* Metadata: path */
+	if (!sieve_binary_read_string(sblock, offset, &path)) {
+		e_error(script->event,
+			"Binary '%s' has invalid metadata for script '%s': "
+			"Invalid file path",
+			sieve_binary_path(sbin), sieve_script_label(script));
+		return -1;
+	}
+	i_assert(fscript->path != NULL);
+	if (strcmp(str_c(path), fscript->path) != 0) {
+		e_debug(script->event,
+			"Binary '%s' reports different file path for script '%s' "
+			"('%s' rather than '%s')",
+			sieve_binary_path(sbin), sieve_script_label(script),
+			str_c(path), fscript->path);
+		return 0;
+	}
+
 	const struct stat *sstat, *bstat;
 
 	bstat = sieve_binary_stat(sbin);
@@ -516,6 +554,31 @@ sieve_file_script_binary_read_metadata(struct sieve_script *script,
 	return 1;
 }
 
+static void
+sieve_file_script_binary_write_metadata(struct sieve_script *script,
+					struct sieve_binary_block *sblock)
+{
+	struct sieve_file_script *fscript =
+		container_of(script, struct sieve_file_script, script);
+
+	sieve_binary_emit_cstring(sblock, fscript->path);
+}
+
+static bool
+sieve_file_script_binary_dump_metadata(struct sieve_script *script ATTR_UNUSED,
+				       struct sieve_dumptime_env *denv,
+				       struct sieve_binary_block *sblock,
+				       sieve_size_t *offset)
+{
+	string_t *path;
+
+	if (!sieve_binary_read_string(sblock, offset, &path))
+		return FALSE;
+	sieve_binary_dumpf(denv, "file.path = %s\n", str_c(path));
+
+	return TRUE;
+}
+
 static int
 sieve_file_script_binary_load(struct sieve_script *script,
 			      struct sieve_binary **sbin_r)
@@ -851,6 +914,9 @@ const struct sieve_script sieve_file_script = {
 		.get_stream = sieve_file_script_get_stream,
 
 		.binary_read_metadata = sieve_file_script_binary_read_metadata,
+		.binary_write_metadata =
+			sieve_file_script_binary_write_metadata,
+		.binary_dump_metadata = sieve_file_script_binary_dump_metadata,
 		.binary_load = sieve_file_script_binary_load,
 		.binary_save = sieve_file_script_binary_save,
 		.binary_get_prefix = sieve_file_script_binary_get_prefix,
diff --git a/src/lib-sieve/storage/file/sieve-file-storage-active.c b/src/lib-sieve/storage/file/sieve-file-storage-active.c
index 491d97981ff2b957115c6f61769b561adc7c98ba..8415d2c3b3c2f22903f5fd4554e9a2092a6cef5e 100644
--- a/src/lib-sieve/storage/file/sieve-file-storage-active.c
+++ b/src/lib-sieve/storage/file/sieve-file-storage-active.c
@@ -113,8 +113,9 @@ sieve_file_storage_active_parse_link(struct sieve_file_storage *fstorage,
 	if (strcmp(scriptpath, fstorage->path) != 0) {
 		e_warning(storage->event,
 			  "Active sieve script symlink %s is broken: "
-			  "Invalid/unknown path to storage (points to %s).",
-			  fstorage->active_path, scriptpath);
+			  "Invalid/unknown path to storage "
+			  "(points to %s, expected %s)",
+			  fstorage->active_path, scriptpath, fstorage->path);
 		return NULL;
 	}
 
diff --git a/src/lib-sieve/storage/file/sieve-file-storage-settings.c b/src/lib-sieve/storage/file/sieve-file-storage-settings.c
new file mode 100644
index 0000000000000000000000000000000000000000..255bc6d1b4450b4aef212bc9fa00c3c7768d5e90
--- /dev/null
+++ b/src/lib-sieve/storage/file/sieve-file-storage-settings.c
@@ -0,0 +1,40 @@
+/* Copyright (c) 2024 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str-sanitize.h"
+#include "array.h"
+#include "settings.h"
+#include "settings-parser.h"
+
+#include "sieve-script.h"
+
+#include "sieve-file-storage-settings.h"
+
+#undef DEF
+#define DEF(type, name) SETTING_DEFINE_STRUCT_##type( \
+	"sieve_"#name, name, \
+	struct sieve_file_storage_settings)
+
+static const struct setting_define sieve_file_storage_setting_defines[] = {
+	DEF(STR, script_path),
+	DEF(STR, script_active_path),
+
+	SETTING_DEFINE_LIST_END,
+};
+
+static const struct sieve_file_storage_settings sieve_file_storage_default_settings = {
+	.script_path = "",
+	.script_active_path = "",
+};
+
+const struct setting_parser_info sieve_file_storage_setting_parser_info = {
+	.name = "sieve_file_storage",
+
+	.defines = sieve_file_storage_setting_defines,
+	.defaults = &sieve_file_storage_default_settings,
+
+	.struct_size = sizeof(struct sieve_file_storage_settings),
+
+	.pool_offset1 = 1 + offsetof(struct sieve_file_storage_settings, pool),
+};
diff --git a/src/lib-sieve/storage/file/sieve-file-storage-settings.h b/src/lib-sieve/storage/file/sieve-file-storage-settings.h
index c8a9f8501c795ac2b7bb2bd9d5444c5a6ae96f31..35e97d7d93f77fbd9d0357c0db41bc3c3cf41b86 100644
--- a/src/lib-sieve/storage/file/sieve-file-storage-settings.h
+++ b/src/lib-sieve/storage/file/sieve-file-storage-settings.h
@@ -3,4 +3,13 @@
 
 #define SIEVE_FILE_DEFAULT_ACTIVE_PATH "~/.dovecot."SIEVE_SCRIPT_FILEEXT
 
+struct sieve_file_storage_settings {
+	pool_t pool;
+
+	const char *script_path;
+	const char *script_active_path;
+};
+
+extern const struct setting_parser_info sieve_file_storage_setting_parser_info;
+
 #endif
diff --git a/src/lib-sieve/storage/file/sieve-file-storage.c b/src/lib-sieve/storage/file/sieve-file-storage.c
index 90fc18e4800a2362500d4da8908594b40bfedd1a..829ee2e1394f639f36cfbbadec8a7377e9f4f566 100644
--- a/src/lib-sieve/storage/file/sieve-file-storage.c
+++ b/src/lib-sieve/storage/file/sieve-file-storage.c
@@ -8,6 +8,7 @@
 #include "mkdir-parents.h"
 #include "eacces-error.h"
 #include "unlink-old-files.h"
+#include "settings.h"
 #include "mail-storage-private.h"
 
 #include "sieve.h"
@@ -473,38 +474,19 @@ sieve_file_storage_init_common(struct sieve_file_storage *fstorage,
 	}
 
 	fstorage->path = p_strdup(storage->pool, storage_path);
-	storage->location = fstorage->path;
-
 	return 0;
 }
 
 static int
 sieve_file_storage_init_from_settings(
 	struct sieve_file_storage *fstorage,
-	const char *const *options)
+	const struct sieve_file_storage_settings *set)
 {
 	struct sieve_storage *storage = &fstorage->storage;
-	const char *storage_path = storage->location;
-	const char *value, *active_path = "";
+	const char *storage_path = set->script_path;
+	const char *active_path = set->script_active_path;
 	bool exists = FALSE;
 
-	if (options != NULL) {
-		while (*options != NULL) {
-			const char *option = *options;
-
-			if (str_begins_icase(option, "active=", &value) &&
-			    *value != '\0') {
-				active_path = value;
-			} else {
-				sieve_storage_set_critical(
-					storage, "Invalid option '%s'", option);
-				return -1;
-			}
-
-			options++;
-		}
-	}
-
 	/* Get full storage path */
 
 	if (sieve_file_storage_get_full_path(fstorage, &storage_path) < 0)
@@ -512,9 +494,12 @@ sieve_file_storage_init_from_settings(
 
 	/* Stat storage directory */
 
+	bool is_personal = sieve_storage_is_personal(storage);
+
 	if (storage_path != NULL && *storage_path != '\0') {
 		if (sieve_file_storage_stat(fstorage, storage_path) < 0) {
-			if (storage->error_code != SIEVE_ERROR_NOT_FOUND)
+			if (!is_personal ||
+			    storage->error_code != SIEVE_ERROR_NOT_FOUND)
 				return -1;
 			if ((storage->flags & SIEVE_STORAGE_FLAG_READWRITE) == 0) {
 				/* For backwards compatibility, recognize when
@@ -561,7 +546,7 @@ sieve_file_storage_init_from_settings(
 	}
 
 	if ((active_path == NULL || *active_path == '\0') &&
-	    (storage->main_storage ||
+	    (is_personal ||
 	     (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0)) {
 		e_debug(storage->event,
 			"Active script path is unconfigured; "
@@ -574,15 +559,23 @@ sieve_file_storage_init_from_settings(
 					      storage_path, exists);
 }
 
-static int
-sieve_file_storage_init(struct sieve_storage *storage,
-			const char *const *options)
+static int sieve_file_storage_init(struct sieve_storage *storage)
 {
 	struct sieve_file_storage *fstorage =
 		container_of(storage, struct sieve_file_storage, storage);
+	const struct sieve_file_storage_settings *fstorage_set;
+	const char *error;
 	int ret;
 
-	ret = sieve_file_storage_init_from_settings(fstorage, options);
+	if (settings_get(storage->event,
+			 &sieve_file_storage_setting_parser_info, 0,
+			 &fstorage_set, &error) < 0) {
+		sieve_storage_set_critical(storage, "%s",error);
+		return -1;
+	}
+
+	ret = sieve_file_storage_init_from_settings(fstorage, fstorage_set);
+	settings_free(fstorage_set);
 	if (ret < 0)
 		return -1;
 
@@ -591,15 +584,16 @@ sieve_file_storage_init(struct sieve_storage *storage,
 
 static int
 sieve_file_storage_do_autodetect(
-	struct sieve_instance *svinst, struct event *event,
-	const char *active_path,
+	struct sieve_instance *svinst, struct event *event, const char *cause,
+	const struct sieve_storage_settings *storage_set,
+	const struct sieve_file_storage_settings *fstorage_set,
 	enum sieve_storage_flags flags, struct sieve_storage **storage_r,
 	enum sieve_error *error_code_r, const char **error_r)
 {
 	const char *home = sieve_environment_get_homedir(svinst);
 	int mode = ((flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 ?
 		    R_OK|W_OK|X_OK : R_OK|X_OK);
-	const char *storage_path = NULL;
+	const char *storage_path = fstorage_set->script_path;
 
 	if (storage_path == NULL || *storage_path == '\0') {
 		/* We'll need to figure out the storage location ourself.
@@ -631,12 +625,14 @@ sieve_file_storage_do_autodetect(
 
 	struct sieve_storage *storage;
 	struct sieve_file_storage *fstorage;
+	const char *active_path = NULL;
 	bool exists = FALSE;
 	int ret;
 
-	ret = sieve_storage_alloc(svinst, event, &sieve_file_storage,
-				  "", flags, TRUE, &storage,
-				  error_code_r, error_r);
+	ret = sieve_storage_alloc_with_settings(svinst, event,
+						&sieve_file_storage, cause,
+						storage_set, flags, &storage,
+						error_code_r, error_r);
 	if (ret < 0)
 		return -1;
 
@@ -647,7 +643,7 @@ sieve_file_storage_do_autodetect(
 	bool tried_active = FALSE;
 	while (!tried_active) {
 		if (storage_path == NULL || *storage_path == '\0') {
-			storage_path = active_path;
+			storage_path = fstorage_set->script_active_path;
 			if (storage_path == NULL || *storage_path == '\0')
 				storage_path = SIEVE_FILE_DEFAULT_ACTIVE_PATH;
 			tried_active = TRUE;
@@ -701,6 +697,7 @@ sieve_file_storage_do_autodetect(
 
 		/* Success */
 		exists = TRUE;
+		active_path = fstorage_set->script_active_path;
 	} else if ((storage->flags & SIEVE_STORAGE_FLAG_READWRITE) == 0) {
 		exists = TRUE;
 		active_path = storage_path;
@@ -723,32 +720,48 @@ sieve_file_storage_do_autodetect(
 	}
 
 	*storage_r = storage;
-	return 0;
+	return 1;
 }
 
 static int
 sieve_file_storage_autodetect(struct sieve_instance *svinst,
-			      const char *active_path,
+			      struct event *event, const char *cause,
+			      const struct sieve_storage_settings *storage_set,
 			      enum sieve_storage_flags flags,
 			      struct sieve_storage **storage_r,
 			      enum sieve_error *error_code_r,
 			      const char **error_r)
 {
-	struct event *event = svinst->event;
+	const struct sieve_file_storage_settings *fstorage_set;
 	int ret;
 
+	if (!sieve_storage_settings_match_script_type(
+		storage_set, SIEVE_STORAGE_TYPE_PERSONAL))
+		return 0;
+
 	e_debug(event, "Performing auto-detection");
 
-	T_BEGIN {
-		ret = sieve_file_storage_do_autodetect(
-			svinst, event, active_path, flags,
-			storage_r, error_code_r, error_r);
-	}  T_END_PASS_STR_IF(ret < 0, error_r);
-	
+	const char *error;
+
+	if (settings_get(event, &sieve_file_storage_setting_parser_info, 0,
+			 &fstorage_set, &error) < 0) {
+		e_error(event, "%s", error);
+		sieve_error_create_internal(error_code_r, error_r);
+		return -1;
+	}
+
+	ret = sieve_file_storage_do_autodetect(
+		svinst, event, cause, storage_set, fstorage_set, flags,
+		storage_r, error_code_r, error_r);
+
+	settings_free(fstorage_set);
 	return ret;
 }
 
 int sieve_file_storage_init_from_path(struct sieve_instance *svinst,
+				      const char *cause,
+				      const char *script_type,
+				      const char *storage_name,
 				      const char *path,
 				      enum sieve_storage_flags flags,
 				      struct sieve_file_storage **fstorage_r,
@@ -764,9 +777,10 @@ int sieve_file_storage_init_from_path(struct sieve_instance *svinst,
 	*fstorage_r = NULL;
 	sieve_error_args_init(&error_code_r, &error_r);
 
-	ret = sieve_storage_alloc(svinst, NULL, &sieve_file_storage,
-				  "", flags, FALSE, &storage,
-				  error_code_r, error_r);
+	ret = sieve_storage_alloc(svinst, svinst->event, &sieve_file_storage,
+				  cause, script_type, storage_name,
+				  sieve_script_file_get_scriptname(path),
+				  flags, &storage, error_code_r, error_r);
 	if (ret < 0)
 		return -1;
 	fstorage = container_of(storage, struct sieve_file_storage, storage);
diff --git a/src/lib-sieve/storage/file/sieve-file-storage.h b/src/lib-sieve/storage/file/sieve-file-storage.h
index e07d8bff9f0199ddc37044a6584ef1cf90b5c9b9..0187beb8037f9ccad40eb737f279a6791595b964 100644
--- a/src/lib-sieve/storage/file/sieve-file-storage.h
+++ b/src/lib-sieve/storage/file/sieve-file-storage.h
@@ -49,6 +49,9 @@ sieve_file_storage_path_extend(struct sieve_file_storage *fstorage,
 			       const char *filename);
 
 int sieve_file_storage_init_from_path(struct sieve_instance *svinst,
+				      const char *cause,
+				      const char *script_type,
+				      const char *storage_name,
 				      const char *path,
 				      enum sieve_storage_flags flags,
 				      struct sieve_file_storage **fstorage_r,
diff --git a/src/lib-sieve/storage/ldap/sieve-ldap-db.c b/src/lib-sieve/storage/ldap/sieve-ldap-db.c
index b5accce7d9870bcd4b61dc1e8ba992ca501796ac..bdfb9eb29f76dd7e7056017895528ac97fd181e9 100644
--- a/src/lib-sieve/storage/ldap/sieve-ldap-db.c
+++ b/src/lib-sieve/storage/ldap/sieve-ldap-db.c
@@ -256,19 +256,19 @@ void db_ldap_request(struct ldap_connection *conn,
 static int db_ldap_connect_finish(struct ldap_connection *conn, int ret)
 {
 	struct sieve_storage *storage = &conn->lstorage->storage;
-	const struct sieve_ldap_storage_settings *set = conn->lstorage->set;
+	const struct sieve_ldap_settings *set = conn->lstorage->ldap_set;
 
 	if (ret == LDAP_SERVER_DOWN) {
 		e_error(storage->event, "db: "
 			"Can't connect to server: %s",
-			set->uris != NULL ?
+			*set->uris != '\0' ?
 			set->uris : set->hosts);
 		return -1;
 	}
 	if (ret != LDAP_SUCCESS) {
 		e_error(storage->event, "db: "
 			"binding failed (dn %s): %s",
-			set->dn == NULL ? "(none)" : set->dn,
+			*set->dn == '\0' ? "(none)" : set->dn,
 			ldap_get_error(conn));
 		return -1;
 	}
@@ -277,7 +277,7 @@ static int db_ldap_connect_finish(struct ldap_connection *conn, int ret)
 	conn->conn_state = LDAP_CONN_STATE_BOUND;
 	e_debug(storage->event, "db: "
 		"Successfully bound (dn %s)",
-		set->dn == NULL ? "(none)" : set->dn);
+		*set->dn == '\0' ? "(none)" : set->dn);
 	while (db_ldap_request_queue_next(conn))
 		;
 	return 0;
@@ -575,7 +575,7 @@ static void ldap_connection_timeout(struct ldap_connection *conn)
 
 static int db_ldap_bind(struct ldap_connection *conn)
 {
-	const struct sieve_ldap_storage_settings *set = conn->lstorage->set;
+	const struct sieve_ldap_settings *set = conn->lstorage->ldap_set;
 	int msgid;
 
 	i_assert(conn->conn_state != LDAP_CONN_STATE_BINDING);
@@ -653,7 +653,7 @@ db_ldap_set_opt_str(struct ldap_connection *conn, int opt, const char *value,
 
 static int db_ldap_set_tls_options(struct ldap_connection *conn)
 {
-	const struct sieve_ldap_storage_settings *set = conn->lstorage->set;
+	const struct sieve_ldap_settings *set = conn->lstorage->ldap_set;
 
 	if (!set->tls)
 		return 0;
@@ -674,7 +674,7 @@ static int db_ldap_set_tls_options(struct ldap_connection *conn)
 	if (db_ldap_set_opt_str(conn, LDAP_OPT_X_TLS_CIPHER_SUITE,
 				set->tls_cipher_suite, "tls_cipher_suite") < 0)
 		return -1;
-	if (set->tls_require_cert != NULL) {
+	if (*set->tls_require_cert != '\0') {
 		if (db_ldap_set_opt(conn, LDAP_OPT_X_TLS_REQUIRE_CERT,
 				    &set->parsed.tls_require_cert,
 				    "tls_require_cert",
@@ -682,11 +682,11 @@ static int db_ldap_set_tls_options(struct ldap_connection *conn)
 			return -1;
 	}
 #else
-	if (set->tls_ca_cert_file != NULL ||
-	    set->tls_ca_cert_dir != NULL ||
-	    set->tls_cert_file != NULL ||
-	    set->tls_key_file != NULL ||
-	    set->tls_cipher_suite != NULL) {
+	if (*set->tls_ca_cert_file != '\0' ||
+	    *set->tls_ca_cert_dir != '\0' ||
+	    *set->tls_cert_file != '\0' ||
+	    *set->tls_key_file != '\0' ||
+	    *set->tls_cipher_suite != '\0') {
 		e_warning(&conn->lstorage->storage, "db: "
 			  "tls_* settings ignored, "
 			  "your LDAP library doesn't seem to support them");
@@ -697,7 +697,7 @@ static int db_ldap_set_tls_options(struct ldap_connection *conn)
 
 static int db_ldap_set_options(struct ldap_connection *conn)
 {
-	const struct sieve_ldap_storage_settings *set = conn->lstorage->set;
+	const struct sieve_ldap_settings *set = conn->lstorage->ldap_set;
 	struct sieve_storage *storage = &conn->lstorage->storage;
 	unsigned int ldap_version;
 	int value;
@@ -737,7 +737,7 @@ static int db_ldap_set_options(struct ldap_connection *conn)
 
 int sieve_ldap_db_connect(struct ldap_connection *conn)
 {
-	const struct sieve_ldap_storage_settings *set = conn->lstorage->set;
+	const struct sieve_ldap_settings *set = conn->lstorage->ldap_set;
 	struct sieve_storage *storage = &conn->lstorage->storage;
 	struct timeval start, end;
 	int debug_level;
@@ -757,7 +757,7 @@ int sieve_ldap_db_connect(struct ldap_connection *conn)
 		i_gettimeofday(&start);
 	i_assert(conn->pending_count == 0);
 	if (conn->ld == NULL) {
-		if (set->uris != NULL) {
+		if (*set->uris != '\0') {
 #ifdef LDAP_HAVE_INITIALIZE
 			if (ldap_initialize(&conn->ld,
 					    set->uris) != LDAP_SUCCESS)
@@ -787,7 +787,7 @@ int sieve_ldap_db_connect(struct ldap_connection *conn)
 		ret = ldap_start_tls_s(conn->ld, NULL, NULL);
 		if (ret != LDAP_SUCCESS) {
 			if (ret == LDAP_OPERATIONS_ERROR &&
-			    set->uris != NULL &&
+			    *set->uris != '\0' &&
 			    str_begins_with(set->uris, "ldaps:")) {
 				e_error(storage->event, "db: "
 					"Don't use both tls=yes and ldaps URI");
@@ -1052,7 +1052,7 @@ sieve_ldap_db_get_script_modattr(struct ldap_connection *conn,
 
 	attr = ldap_first_attribute(conn->ld, entry, &ber);
 	while (attr != NULL) {
-		if (strcmp(attr, set->sieve_ldap_mod_attr) == 0) {
+		if (strcmp(attr, set->mod_attr) == 0) {
 			vals = ldap_get_values(conn->ld, entry, attr);
 			if (vals == NULL || vals[0] == NULL)
 				return 0;
@@ -1061,7 +1061,7 @@ sieve_ldap_db_get_script_modattr(struct ldap_connection *conn,
 				e_warning(storage->event, "db: "
 					  "Search returned more than one Sieve modified attribute '%s'; "
 					  "using only the first one.",
-					  set->sieve_ldap_mod_attr);
+					  set->mod_attr);
 			}
 
 			*modattr_r = p_strdup(pool, vals[0]);
@@ -1092,7 +1092,7 @@ sieve_ldap_db_get_script(struct ldap_connection *conn, LDAPMessage *entry,
 
 	attr = ldap_first_attribute(conn->ld, entry, &ber);
 	while (attr != NULL) {
-		if (strcmp(attr, set->sieve_ldap_script_attr) == 0) {
+		if (strcmp(attr, set->script_attr) == 0) {
 			vals = ldap_get_values_len(conn->ld, entry, attr);
 			if (vals == NULL || vals[0] == NULL)
 				return 0;
@@ -1101,7 +1101,7 @@ sieve_ldap_db_get_script(struct ldap_connection *conn, LDAPMessage *entry,
 				e_warning(storage->event, "db: "
 					  "Search returned more than one Sieve script attribute '%s'; "
 					  "using only the first one.",
-					  set->sieve_ldap_script_attr);
+					  set->script_attr);
 			}
 
 			size = vals[0]->bv_len;
@@ -1210,6 +1210,7 @@ int sieve_ldap_db_lookup_script(struct ldap_connection *conn, const char *name,
 {
 	struct sieve_ldap_storage *lstorage = conn->lstorage;
 	struct sieve_storage *storage = &lstorage->storage;
+	const struct sieve_ldap_settings *ldap_set = lstorage->ldap_set;
 	const struct sieve_ldap_storage_settings *set = lstorage->set;
 	struct sieve_ldap_script_lookup_request *request;
 	char **attr_names;
@@ -1226,31 +1227,31 @@ int sieve_ldap_db_lookup_script(struct ldap_connection *conn, const char *name,
 	};
 
 	str = t_str_new(512);
-	if (var_expand(str, set->base, &params, &error) < 0) {
+	if (var_expand(str, ldap_set->base, &params, &error) < 0) {
 		e_error(storage->event, "db: "
 			"Failed to expand base=%s: %s",
-			set->base, error);
+			ldap_set->base, error);
 		return -1;
 	}
 	request->request.base = p_strdup(pool, str_c(str));
 
 	attr_names = p_new(pool, char *, 3);
-	attr_names[0] = p_strdup(pool, set->sieve_ldap_mod_attr);
+	attr_names[0] = p_strdup(pool, set->mod_attr);
 
 	str_truncate(str, 0);
-	if (var_expand(str, set->sieve_ldap_filter, &params, &error) < 0) {
+	if (var_expand(str, set->filter, &params, &error) < 0) {
 		e_error(storage->event, "db: "
 			"Failed to expand sieve_ldap_filter=%s: %s",
-			set->sieve_ldap_filter, error);
+			set->filter, error);
 		return -1;
 	}
 
-	request->request.scope = lstorage->set->parsed.scope;
+	request->request.scope = ldap_set->parsed.scope;
 	request->request.filter = p_strdup(pool, str_c(str));
 	request->request.attributes = attr_names;
 
 	e_debug(storage->event, "base=%s scope=%s filter=%s fields=%s",
-		request->request.base, lstorage->set->scope,
+		request->request.base, ldap_set->scope,
 		request->request.filter,
 		t_strarray_join((const char **)attr_names, ","));
 
@@ -1317,7 +1318,7 @@ int sieve_ldap_db_read_script(struct ldap_connection *conn,
 	request->request.base = p_strdup(pool, dn);
 
 	attr_names = p_new(pool, char *, 3);
-	attr_names[0] = p_strdup(pool, set->sieve_ldap_script_attr);
+	attr_names[0] = p_strdup(pool, set->script_attr);
 
 	request->request.scope = LDAP_SCOPE_BASE;
 	request->request.filter = "(objectClass=*)";
diff --git a/src/lib-sieve/storage/ldap/sieve-ldap-script.c b/src/lib-sieve/storage/ldap/sieve-ldap-script.c
index c372cf69a7353d341040e7c5fc857423da31e060..e282ecbcaf754f0dfc1197a7f8505221a86d4c70 100644
--- a/src/lib-sieve/storage/ldap/sieve-ldap-script.c
+++ b/src/lib-sieve/storage/ldap/sieve-ldap-script.c
@@ -38,18 +38,12 @@ sieve_ldap_script_init(struct sieve_ldap_storage *lstorage, const char *name)
 {
 	struct sieve_storage *storage = &lstorage->storage;
 	struct sieve_ldap_script *lscript = NULL;
-	const char *location;
 
-	if (name == NULL) {
+	if (name == NULL || *name == '\0')
 		name = SIEVE_LDAP_SCRIPT_DEFAULT;
-		location = storage->location;
-	} else {
-		location = t_strconcat(storage->location, ";name=", name, NULL);
-	}
 
 	lscript = sieve_ldap_script_alloc();
-	sieve_script_init(&lscript->script, storage, &sieve_ldap_script,
-			  location, name);
+	sieve_script_init(&lscript->script, storage, &sieve_ldap_script, name);
 	return lscript;
 }
 
@@ -118,26 +112,12 @@ sieve_ldap_script_binary_read_metadata(struct sieve_script *script,
 	struct sieve_ldap_script *lscript =
 		container_of(script, struct sieve_ldap_script, script);
 	struct sieve_storage *storage = script->storage;
-	struct sieve_instance *svinst = storage->svinst;
 	struct sieve_ldap_storage *lstorage =
 		container_of(storage, struct sieve_ldap_storage, storage);
 	struct sieve_binary *sbin = sieve_binary_block_get_binary(sblock);
-	time_t bmtime = sieve_binary_mtime(sbin);
 	string_t *dn, *modattr;
 
-	/* Config file changed? */
-	if (bmtime <= lstorage->set_mtime) {
-		if (svinst->debug) {
-			e_debug(script->event,
-				"Sieve binary '%s' is not newer "
-				"than the LDAP configuration '%s' (%s <= %s)",
-				sieve_binary_path(sbin), lstorage->config_file,
-				t_strflocaltime("%Y-%m-%d %H:%M:%S", bmtime),
-				t_strflocaltime("%Y-%m-%d %H:%M:%S",
-						lstorage->set_mtime));
-		}
-		return 0;
-	}
+	// FIXME: Maybe detect config changes somehow to trigger recompile
 
 	/* Open script if not open already */
 	if (lscript->dn == NULL && sieve_script_open(script, NULL) < 0)
@@ -149,7 +129,7 @@ sieve_ldap_script_binary_read_metadata(struct sieve_script *script,
 			"LDAP entry for script '%s' "
 			"has no modified attribute '%s'",
 			sieve_script_label(script),
-			lstorage->set->sieve_ldap_mod_attr);
+			lstorage->set->mod_attr);
 		return 0;
 	}
 
diff --git a/src/lib-sieve/storage/ldap/sieve-ldap-storage-settings.c b/src/lib-sieve/storage/ldap/sieve-ldap-storage-settings.c
index a61b8c03de17fb53462a4902c783b69784d0845f..a385b6a6f37e874f84dc6e792b6ee813ed55f30f 100644
--- a/src/lib-sieve/storage/ldap/sieve-ldap-storage-settings.c
+++ b/src/lib-sieve/storage/ldap/sieve-ldap-storage-settings.c
@@ -3,11 +3,12 @@
 
 #include "lib.h"
 #include "env-util.h"
-#include "settings-legacy.h"
+#include "settings-parser.h"
 
 #include "sieve-common.h"
 
 #include "sieve-ldap-storage.h"
+#include "sieve-ldap-storage-settings.h"
 
 #if defined(SIEVE_BUILTIN_LDAP) || defined(PLUGIN_BUILD)
 
@@ -15,15 +16,15 @@
 
 #include "sieve-ldap-db.h"
 
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
 #undef DEF
 #define DEF(type, name) \
-	DEF_STRUCT_##type(name, sieve_ldap_storage_settings)
+	SETTING_DEFINE_STRUCT_##type("ldap_"#name, name, \
+				     struct sieve_ldap_settings)
+
+static bool
+sieve_ldap_settings_check(void *_set, pool_t pool, const char **error_r);
 
-static struct setting_def setting_defs[] = {
+static const struct setting_define sieve_ldap_setting_defines[] = {
 	DEF(STR, hosts),
 	DEF(STR, uris),
 	DEF(STR, dn),
@@ -39,54 +40,80 @@ static struct setting_def setting_defs[] = {
 	DEF(STR, tls_key_file),
 	DEF(STR, tls_cipher_suite),
 	DEF(STR, tls_require_cert),
-	DEF(STR, deref),
-	DEF(STR, scope),
+	DEF(ENUM, deref),
+	DEF(ENUM, scope),
 	DEF(STR, base),
-	DEF(INT, ldap_version),
+	DEF(UINT, ldap_version),
 	DEF(STR, debug_level),
 	DEF(STR, ldaprc_path),
-	DEF(STR, sieve_ldap_script_attr),
-	DEF(STR, sieve_ldap_mod_attr),
-	DEF(STR, sieve_ldap_filter),
 
-	{ 0, NULL, 0 }
+	SETTING_DEFINE_LIST_END
 };
 
-static struct sieve_ldap_storage_settings default_settings = {
-	.hosts = NULL,
-	.uris = NULL,
-	.dn = NULL,
-	.dnpass = NULL,
+const struct sieve_ldap_settings sieve_ldap_default_settings = {
+	.hosts = "",
+	.uris = "",
+	.dn = "",
+	.dnpass = "",
 	.tls = FALSE,
 	.sasl_bind = FALSE,
-	.sasl_mech = NULL,
-	.sasl_realm = NULL,
-	.sasl_authz_id = NULL,
-	.tls_ca_cert_file = NULL,
-	.tls_ca_cert_dir = NULL,
-	.tls_cert_file = NULL,
-	.tls_key_file = NULL,
-	.tls_cipher_suite = NULL,
-	.tls_require_cert = NULL,
-	.deref = "never",
-	.scope = "subtree",
-	.base = NULL,
+	.sasl_mech = "",
+	.sasl_realm = "",
+	.sasl_authz_id = "",
+	.tls_ca_cert_file = "",
+	.tls_ca_cert_dir = "",
+	.tls_cert_file = "",
+	.tls_key_file = "",
+	.tls_cipher_suite = "",
+	.tls_require_cert = "",
+	.deref = "never:searching:finding:always",
+	.scope = "subtree:onelevel:base",
+	.base = "",
 	.ldap_version = 3,
 	.debug_level = "0",
 	.ldaprc_path = "",
-	.sieve_ldap_script_attr = "mailSieveRuleSource",
-	.sieve_ldap_mod_attr = "modifyTimestamp",
-	.sieve_ldap_filter = "(&(objectClass=posixAccount)(uid=%u))",
 };
 
-static const char *
-parse_setting(const char *key, const char *value,
-	      struct sieve_ldap_storage *lstorage)
-{
-	return parse_setting_from_defs(lstorage->storage.pool, setting_defs,
-				       lstorage->set, key, value);
-}
+const struct setting_parser_info sieve_ldap_setting_parser_info = {
+	.name = "sieve_ldap",
+	.defines = sieve_ldap_setting_defines,
+	.defaults = &sieve_ldap_default_settings,
+
+	.pool_offset1 = 1 + offsetof(struct sieve_ldap_settings, pool),
+	.struct_size = sizeof(struct sieve_ldap_settings),
+	.check_func = sieve_ldap_settings_check,
+};
+
+#undef DEF
+#define DEF(type, name) \
+	SETTING_DEFINE_STRUCT_##type("sieve_script_ldap_"#name, name, \
+				     struct sieve_ldap_storage_settings)
+
+static const struct setting_define sieve_ldap_storage_setting_defines[] = {
+	DEF(STR, script_attr),
+	DEF(STR, mod_attr),
+	DEF(STR, filter),
 
+	SETTING_DEFINE_LIST_END
+};
+
+static struct sieve_ldap_storage_settings sieve_ldap_storage_server_default_settings = {
+	.script_attr = "mailSieveRuleSource",
+	.mod_attr = "modifyTimestamp",
+	.filter = "(&(objectClass=posixAccount)(uid=%u))",
+};
+
+const struct setting_parser_info sieve_ldap_storage_setting_parser_info = {
+	.name = "sieve_ldap_storage",
+
+	.defines = sieve_ldap_storage_setting_defines,
+	.defaults = &sieve_ldap_storage_server_default_settings,
+
+	.pool_offset1 = 1 + offsetof(struct sieve_ldap_storage_settings, pool),
+	.struct_size = sizeof(struct sieve_ldap_storage_settings),
+};
+
+/* <settings checks> */
 static int ldap_deref_from_str(const char *str, int *deref_r)
 {
 	if (strcasecmp(str, "never") == 0)
@@ -135,25 +162,22 @@ static int ldap_tls_require_cert_from_str(const char *str, int *opt_x_tls_r)
 #endif
 
 static bool
-sieve_ldap_settings_check(struct sieve_ldap_storage_settings *set,
+sieve_ldap_settings_check(void *_set, pool_t pool ATTR_UNUSED,
 			  const char **error_r)
 {
+	struct sieve_ldap_settings *set = _set;
 	const char *str;
 
-	if (set->base == NULL) {
-		*error_r = "No search base given";
-		return FALSE;
-	}
-
-	if (set->uris == NULL && set->hosts == NULL) {
-		*error_r = "No uris or hosts set";
+	if (set->base[0] == '\0' &&
+	    settings_get_config_binary() == SETTINGS_BINARY_OTHER) {
+		*error_r = "ldap: No search base given";
 		return FALSE;
 	}
 
 	if (*set->ldaprc_path != '\0') {
 		str = getenv("LDAPRC");
 		if (str != NULL && strcmp(str, set->ldaprc_path) != 0) {
-			*error_r = t_strdup_printf(
+			*error_r = t_strdup_printf("ldap: "
 				"Multiple different ldaprc_path settings not allowed "
 				"(%s and %s)", str, set->ldaprc_path);
 			return FALSE;
@@ -162,23 +186,23 @@ sieve_ldap_settings_check(struct sieve_ldap_storage_settings *set,
 	}
 
 	if (ldap_deref_from_str(set->deref, &set->parsed.deref) < 0) {
-		*error_r = t_strdup_printf(
+		*error_r = t_strdup_printf("ldap: "
 			"Invalid deref option '%s'", set->deref);
 		return FALSE;
 	}
 
 	if (ldap_scope_from_str(set->scope, &set->parsed.scope) < 0) {
-		*error_r = t_strdup_printf(
+		*error_r = t_strdup_printf("ldap: "
 			"Invalid scope option '%s'", set->scope);
 		return FALSE;
 	}
 
 #ifdef OPENLDAP_TLS_OPTIONS
-	if (set->tls_require_cert != NULL &&
+	if (*set->tls_require_cert != '\0' &&
 	    ldap_tls_require_cert_from_str(
 		set->tls_require_cert,
 		&set->parsed.tls_require_cert) < 0) {
-		*error_r = t_strdup_printf(
+		*error_r = t_strdup_printf("ldap: "
 			"Invalid tls_require_cert option '%s'",
 			set->tls_require_cert);
 		return FALSE;
@@ -187,41 +211,6 @@ sieve_ldap_settings_check(struct sieve_ldap_storage_settings *set,
 
 	return TRUE;
 }
-
-int sieve_ldap_storage_read_settings(struct sieve_ldap_storage *lstorage,
-				     const char *config_path)
-{
-	struct sieve_storage *storage = &lstorage->storage;
-	const char *error;
-	struct stat st;
-
-	if (stat(config_path, &st) < 0) {
-		sieve_storage_set_critical(storage,
-			"Failed to read LDAP storage config: "
-			"stat(%s) failed: %m", config_path);
-		return -1;
-	}
-
-	lstorage->set = p_new(storage->pool,
-			      struct sieve_ldap_storage_settings, 1);
-	*lstorage->set = default_settings;
-	lstorage->set_mtime = st.st_mtime;
-
-	if (!settings_read_nosection(config_path, parse_setting, lstorage,
-				     &error)) {
-		sieve_storage_set_critical(storage,
-			"Failed to read LDAP storage config '%s': %s",
-			config_path, error);
-		return -1;
-	}
-
-	if (!sieve_ldap_settings_check(lstorage->set, &error)) {
-		sieve_storage_set_critical(storage,
-			"Invalid LDAP storage config '%s': %s",
-			config_path, error);
-		return -1;
-	}
-	return 0;
-}
+/* </settings checks> */
 
 #endif
diff --git a/src/lib-sieve/storage/ldap/sieve-ldap-storage-settings.h b/src/lib-sieve/storage/ldap/sieve-ldap-storage-settings.h
index 61587a6965d38f58ace93607032d4e130ddf65c7..22f90db74714918e4df8dd709f5cdeac0367661c 100644
--- a/src/lib-sieve/storage/ldap/sieve-ldap-storage-settings.h
+++ b/src/lib-sieve/storage/ldap/sieve-ldap-storage-settings.h
@@ -1,7 +1,9 @@
 #ifndef SIEVE_LDAP_STORAGE_SETTINGS_H
 #define SIEVE_LDAP_STORAGE_SETTINGS_H
 
-struct sieve_ldap_storage_settings {
+struct sieve_ldap_settings {
+	pool_t pool;
+
 	const char *hosts;
 	const char *uris;
 	const char *dn;
@@ -28,14 +30,21 @@ struct sieve_ldap_storage_settings {
 	const char *ldaprc_path;
 	const char *debug_level;
 
-	const char *sieve_ldap_script_attr;
-	const char *sieve_ldap_mod_attr;
-	const char *sieve_ldap_filter;
-
 	/* ... */
 	struct {
 		int deref, scope, tls_require_cert;
 	} parsed;
 };
 
+struct sieve_ldap_storage_settings {
+	pool_t pool;
+
+	const char *script_attr;
+	const char *mod_attr;
+	const char *filter;
+};
+
+extern const struct setting_parser_info sieve_ldap_setting_parser_info;
+extern const struct setting_parser_info sieve_ldap_storage_setting_parser_info;
+
 #endif
diff --git a/src/lib-sieve/storage/ldap/sieve-ldap-storage.c b/src/lib-sieve/storage/ldap/sieve-ldap-storage.c
index 8a7e0980cd1fa605fee9ac1924a70dd668898340..11ff505d4f371b6ddbbfd6b1de31d951069acd61 100644
--- a/src/lib-sieve/storage/ldap/sieve-ldap-storage.c
+++ b/src/lib-sieve/storage/ldap/sieve-ldap-storage.c
@@ -2,6 +2,7 @@
  */
 
 #include "lib.h"
+#include "settings.h"
 //#include "ldap.h"
 
 #include "sieve-common.h"
@@ -41,44 +42,44 @@ static struct sieve_storage *sieve_ldap_storage_alloc(void)
 }
 
 static int
-sieve_ldap_storage_init(struct sieve_storage *storage,
-			const char *const *options)
+sieve_ldap_storage_init(struct sieve_storage *storage)
 {
 	struct sieve_ldap_storage *lstorage =
 		container_of(storage, struct sieve_ldap_storage, storage);
-	struct sieve_instance *svinst = storage->svinst;
-	const char *value;
-
-	if (options != NULL) {
-		while (*options != NULL) {
-			const char *option = *options;
-
-			if (str_begins_icase(option, "user=", &value) &&
-			    *value != '\0') {
-				/* Ignore */
-			} else {
-				sieve_storage_set_critical(
-					storage, "Invalid option '%s'", option);
-				return -1;
-			}
-
-			options++;
-		}
+	const struct sieve_ldap_settings *ldap_set;
+	const struct sieve_ldap_storage_settings *set;
+	const char *error;
+	int ret;
+
+	struct event *event = event_create(storage->event);
+	event_set_ptr(event, SETTINGS_EVENT_FILTER_NAME, "ldap");
+	ret = settings_get(event, &sieve_ldap_setting_parser_info, 0,
+			   &ldap_set, &error);
+	event_unref(&event);
+	if (ret < 0) {
+		sieve_storage_set_critical(storage, "%s", error);
+		return -1;
+	}
+	if (*ldap_set->uris == '\0' && *ldap_set->hosts == '\0') {
+		sieve_storage_set_critical(storage,
+			"sieve_script %s { ldap_uris / ldap_hosts } not set",
+			storage->name);
+		settings_free(ldap_set);
+		return -1;
 	}
 
-	e_debug(storage->event, "user=%s, config=%s",
-		svinst->username, storage->location);
-
-	if (sieve_ldap_storage_read_settings(lstorage, storage->location) < 0)
+	if (settings_get(storage->event,
+			 &sieve_ldap_storage_setting_parser_info, 0,
+			 &set, &error) < 0) {
+		sieve_storage_set_critical(storage, "%s", error);
+		settings_free(ldap_set);
 		return -1;
+	}
 
-	lstorage->config_file = p_strdup(storage->pool, storage->location);
+	lstorage->ldap_set = ldap_set;
+	lstorage->set = set;
 	lstorage->conn = sieve_ldap_db_init(lstorage);
 
-	storage->location = p_strconcat(
-		storage->pool, SIEVE_LDAP_STORAGE_DRIVER_NAME, ":",
-		storage->location, ";user=", svinst->username, NULL);
-
 	return 0;
 }
 
@@ -88,6 +89,8 @@ static void sieve_ldap_storage_destroy(struct sieve_storage *storage)
 		container_of(storage, struct sieve_ldap_storage, storage);
 
 	sieve_ldap_db_unref(&lstorage->conn);
+	settings_free(lstorage->ldap_set);
+	settings_free(lstorage->set);
 }
 
 /*
diff --git a/src/lib-sieve/storage/ldap/sieve-ldap-storage.h b/src/lib-sieve/storage/ldap/sieve-ldap-storage.h
index 70380562814cd00382d21cc50769f342d4657547..394ddcc2c9278639059bb74347bb096d2b11919f 100644
--- a/src/lib-sieve/storage/ldap/sieve-ldap-storage.h
+++ b/src/lib-sieve/storage/ldap/sieve-ldap-storage.h
@@ -14,13 +14,6 @@
 
 struct sieve_ldap_storage;
 
-/*
- * LDAP settings
- */
-
-int sieve_ldap_storage_read_settings(struct sieve_ldap_storage *lstorage,
-				     const char *config_path);
-
 /*
  * Storage class
  */
@@ -28,11 +21,10 @@ int sieve_ldap_storage_read_settings(struct sieve_ldap_storage *lstorage,
 struct sieve_ldap_storage {
 	struct sieve_storage storage;
 
-	struct sieve_ldap_storage_settings *set;
+	const struct sieve_ldap_settings *ldap_set;
+	const struct sieve_ldap_storage_settings *set;
 	time_t set_mtime;
 
-	const char *config_file;
-
 	struct ldap_connection *conn;
 };
 
diff --git a/src/managesieve/managesieve-client.c b/src/managesieve/managesieve-client.c
index 72594610810ce5ab510841948618a62a2f626314..09e204eddb580d23235f2e27967c90dab489beea 100644
--- a/src/managesieve/managesieve-client.c
+++ b/src/managesieve/managesieve-client.c
@@ -74,6 +74,7 @@ client_get_storage(struct sieve_instance *svinst, struct mail_user *user,
 
 	/* Open personal script storage */
 	if (sieve_storage_create_personal(svinst, user,
+					  SIEVE_SCRIPT_CAUSE_DELIVERY,
 					  SIEVE_STORAGE_FLAG_READWRITE,
 					  &storage, &error_code) < 0) {
 		switch (error_code) {
diff --git a/src/plugins/doveadm-sieve/doveadm-sieve-cmd.c b/src/plugins/doveadm-sieve/doveadm-sieve-cmd.c
index 53127b90b72be9cbc0b5a5791c73cbfa85593d7e..be3915c7f5f64c049b14a0e213b55c201dbd64f3 100644
--- a/src/plugins/doveadm-sieve/doveadm-sieve-cmd.c
+++ b/src/plugins/doveadm-sieve/doveadm-sieve-cmd.c
@@ -118,6 +118,7 @@ doveadm_sieve_cmd_run(struct doveadm_mail_cmd_context *_ctx,
 		return -1;
 
 	if (sieve_storage_create_personal(ctx->svinst, user,
+					  SIEVE_SCRIPT_CAUSE_ANY,
 					  SIEVE_STORAGE_FLAG_READWRITE,
 					  &ctx->storage, &error_code) < 0) {
 		switch (error_code) {
diff --git a/src/plugins/doveadm-sieve/doveadm-sieve-sync.c b/src/plugins/doveadm-sieve/doveadm-sieve-sync.c
index 2b1cae32fc0767b078ac9406cfdd60a460d8dd37..baec83ebf02b13d90763e21bbafd5c255027199e 100644
--- a/src/plugins/doveadm-sieve/doveadm-sieve-sync.c
+++ b/src/plugins/doveadm-sieve/doveadm-sieve-sync.c
@@ -99,7 +99,8 @@ mail_sieve_user_init(struct mail_user *user, struct sieve_storage **svstorage_r)
 		       user->set->mail_debug, &suser->svinst) < 0)
 		return -1;
 
-	if (sieve_storage_create_personal(suser->svinst, user, storage_flags,
+	if (sieve_storage_create_personal(suser->svinst, user,
+					  SIEVE_SCRIPT_CAUSE_ANY, storage_flags,
 					  &suser->sieve_storage,
 					  &error_code) < 0) {
 		switch (error_code) {
diff --git a/src/plugins/imap-filter-sieve/imap-filter-sieve.c b/src/plugins/imap-filter-sieve/imap-filter-sieve.c
index 1bc4f52950dcae160d14215d81a92f1288cae624..0057dcd79fb675fb058c9dc4e237d74dd9af0306 100644
--- a/src/plugins/imap-filter-sieve/imap-filter-sieve.c
+++ b/src/plugins/imap-filter-sieve/imap-filter-sieve.c
@@ -189,8 +189,9 @@ imap_filter_sieve_get_personal_storage(struct imap_filter_sieve_context *sctx,
 		return -1;
 	}
 
-	if (sieve_storage_create_personal(svinst, user, storage_flags,
-					  &ifsuser->storage,
+	if (sieve_storage_create_personal(svinst, user,
+					  SIEVE_SCRIPT_CAUSE_DELIVERY,
+					  storage_flags, &ifsuser->storage,
 					  &error_code) == 0) {
 		*storage_r = ifsuser->storage;
 		return 0;
@@ -437,7 +438,8 @@ void imap_filter_sieve_open_input(struct imap_filter_sieve_context *sctx,
 	svinst = imap_filter_sieve_get_svinst(sctx);
 	i_assert(svinst != NULL);
 
-	script = sieve_data_script_create_from_input(svinst, "script", input);
+	script = sieve_data_script_create_from_input(
+		svinst, SIEVE_SCRIPT_CAUSE_DELIVERY, "script", input);
 
 	sctx->user_script = script;
 	sctx->scripts = p_new(sctx->pool, struct imap_filter_sieve_script, 1);
@@ -493,7 +495,6 @@ int imap_filter_sieve_open_global(struct imap_filter_sieve_context *sctx,
 				  const char **error_r)
 {
 	struct sieve_instance *svinst;
-	const char *location;
 	struct sieve_script *script;
 	enum sieve_error error_code;
 
@@ -504,17 +505,8 @@ int imap_filter_sieve_open_global(struct imap_filter_sieve_context *sctx,
 		return -1;
 	}
 
-	location = mail_user_plugin_getenv(sctx->user, "sieve_global");
-	if (location == NULL) {
-		e_info(sieve_get_event(svinst),
-		       "include: sieve_global is unconfigured; "
-		       "include of ':global' script is therefore not possible");
-		*error_code_r = MAIL_ERROR_NOTFOUND;
-		*error_r = "No global Sieve scripts available";
-		return -1;
-	}
-
-	if (sieve_script_create_open(svinst, location, name,
+	if (sieve_script_create_open(svinst, SIEVE_SCRIPT_CAUSE_DELIVERY,
+				     SIEVE_STORAGE_TYPE_GLOBAL, name,
 				     &script, &error_code, error_r) < 0) {
 		switch (error_code) {
 		case SIEVE_ERROR_NOT_FOUND:
diff --git a/src/plugins/imapsieve/imap-sieve-settings.c b/src/plugins/imapsieve/imap-sieve-settings.c
index 2291cf277f52edd3695a8c33d06270cddbb34efc..a7afe0cf74d3c9118e8872ba99830c4cc6e440cb 100644
--- a/src/plugins/imapsieve/imap-sieve-settings.c
+++ b/src/plugins/imapsieve/imap-sieve-settings.c
@@ -18,12 +18,20 @@ static const struct setting_define imap_sieve_setting_defines[] = {
 	DEF(STR, url),
 	DEF(BOOL, expunge_discarded),
 
+	{ .type = SET_FILTER_ARRAY, .key = "imapsieve_from",
+	   .offset = offsetof(struct imap_sieve_settings, from),
+	   .filter_array_field_name = "imapsieve_from_name" },
+	DEF(STR, from_name),
+
 	SETTING_DEFINE_LIST_END,
 };
 
 static const struct imap_sieve_settings imap_sieve_default_settings = {
 	.url = "",
 	.expunge_discarded = FALSE,
+
+	.from = ARRAY_INIT,
+	.from_name = "",
 };
 
 static bool
diff --git a/src/plugins/imapsieve/imap-sieve-settings.h b/src/plugins/imapsieve/imap-sieve-settings.h
index 3b4eb52f5453318f52a416812b3c864745024d91..f3ddaa35acd45e43aed81598f8fdac081470fb8b 100644
--- a/src/plugins/imapsieve/imap-sieve-settings.h
+++ b/src/plugins/imapsieve/imap-sieve-settings.h
@@ -6,6 +6,9 @@ struct imap_sieve_settings {
 
 	const char *url;
 	bool expunge_discarded;
+
+	ARRAY_TYPE(const_string) from;
+	const char *from_name;
 };
 
 extern const struct setting_parser_info imap_sieve_setting_parser_info;
diff --git a/src/plugins/imapsieve/imap-sieve-storage.c b/src/plugins/imapsieve/imap-sieve-storage.c
index d9a849df1ba34c452f5f1d10b86fb2f54df9efa0..3b25867498a42477901162dad69887755bfc6ccd 100644
--- a/src/plugins/imapsieve/imap-sieve-storage.c
+++ b/src/plugins/imapsieve/imap-sieve-storage.c
@@ -33,7 +33,6 @@
 #define IMAP_SIEVE_MAIL_CONTEXT(obj) \
 	MODULE_CONTEXT_REQUIRE(obj, imap_sieve_mail_module)
 
-struct imap_sieve_mailbox_rule;
 struct imap_sieve_user;
 struct imap_sieve_mailbox_event;
 struct imap_sieve_mailbox_transaction;
@@ -48,24 +47,9 @@ enum imap_sieve_command {
 	IMAP_SIEVE_CMD_OTHER
 };
 
-ARRAY_DEFINE_TYPE(imap_sieve_mailbox_rule,
-	struct imap_sieve_mailbox_rule *);
 ARRAY_DEFINE_TYPE(imap_sieve_mailbox_event,
 	struct imap_sieve_mailbox_event);
 
-HASH_TABLE_DEFINE_TYPE(imap_sieve_mailbox_rule,
-	struct imap_sieve_mailbox_rule *,
-	struct imap_sieve_mailbox_rule *);
-
-struct imap_sieve_mailbox_rule {
-	unsigned int index;
-	const char *mailbox;
-	const char *from;
-	const char *const *causes;
-	const char *before, *after;
-	const char *copy_source_after;
-};
-
 struct imap_sieve_user {
 	union mail_user_module_context module_ctx;
 	struct client *client;
@@ -74,9 +58,6 @@ struct imap_sieve_user {
 
 	enum imap_sieve_command cur_cmd;
 
-	HASH_TABLE_TYPE(imap_sieve_mailbox_rule) mbox_rules;
-	ARRAY_TYPE(imap_sieve_mailbox_rule) mbox_patterns;
-
 	bool sieve_active:1;
 	bool user_script:1;
 	bool expunge_discarded:1;
@@ -120,12 +101,6 @@ static MODULE_CONTEXT_DEFINE_INIT(imap_sieve_storage_module,
 static MODULE_CONTEXT_DEFINE_INIT(imap_sieve_mail_module,
 				  &mail_module_register);
 
-static void
-imap_sieve_mailbox_rules_get(struct mail_user *user,
-			     struct mailbox *dst_box, struct mailbox *src_box,
-			     const char *cause,
-			     ARRAY_TYPE(imap_sieve_mailbox_rule) *rules);
-
 struct event_category event_category_imap_sieve = {
 	.name = "imapsieve",
 };
@@ -592,51 +567,20 @@ imap_sieve_mailbox_transaction_run(
 
 	/* Initialize execution */
 	T_BEGIN {
-		ARRAY_TYPE(imap_sieve_mailbox_rule) mbrules;
-		ARRAY_TYPE(const_string) scripts_before, scripts_after;
-		ARRAY_TYPE(const_string) scripts_copy_source;
-		struct imap_sieve_mailbox_rule *rule;
-
-		/* Find matching rules */
-		t_array_init(&mbrules, 16);
-		imap_sieve_mailbox_rules_get(user, dest_box, src_box, cause,
-					     &mbrules);
-
-		/* Apply all matched rules */
-		t_array_init(&scripts_before, 8);
-		t_array_init(&scripts_after, 8);
-		t_array_init(&scripts_copy_source, 4);
-		array_foreach_elem(&mbrules, rule) {
-			if (rule->before != NULL)
-				array_append(&scripts_before, &rule->before, 1);
-			if (rule->after != NULL)
-				array_append(&scripts_after, &rule->after, 1);
-			if (rule->copy_source_after != NULL) {
-				array_append(&scripts_copy_source,
-					     &rule->copy_source_after, 1);
-			}
-		}
-		(void)array_append_space(&scripts_before);
-		(void)array_append_space(&scripts_after);
-
 		/* Initialize */
-		ret = imap_sieve_run_init(isuser->isieve, dest_box, src_box,
-					  cause, script_name,
-					  array_idx(&scripts_before, 0),
-					  array_idx(&scripts_after, 0), &isrun);
+		ret = imap_sieve_run_init(isuser->isieve, dest_isbox->event,
+					  dest_box, src_box, cause, script_name,
+					  SIEVE_STORAGE_TYPE_BEFORE,
+					  SIEVE_STORAGE_TYPE_AFTER, &isrun);
 
 		/* Initialize source script execution */
 		isrun_src = NULL;
 		if (ret > 0 && ismt->src_mail_trans != NULL &&
-		    isuser->cur_cmd == IMAP_SIEVE_CMD_COPY &&
-		    array_count(&scripts_copy_source) > 0) {
-			const char *no_scripts = NULL;
-
-			(void)array_append_space(&scripts_copy_source);
+		    isuser->cur_cmd == IMAP_SIEVE_CMD_COPY) {
 			if (imap_sieve_run_init(
-				isuser->isieve, dest_box, src_box, cause, NULL,
-				&no_scripts, array_idx(&scripts_copy_source, 0),
-				&isrun_src) <= 0)
+				isuser->isieve, dest_isbox->event,
+				dest_box, src_box, cause, NULL,
+				NULL, "copy-source-after", &isrun_src) <= 0)
 				isrun_src = NULL;
 		}
 	} T_END;
@@ -808,289 +752,6 @@ static void imap_sieve_mailbox_allocated(struct mailbox *box)
 	MODULE_CONTEXT_SET(box, imap_sieve_storage_module, isbox);
 }
 
-/*
- * Mailbox rules
- */
-
-static unsigned int
-imap_sieve_mailbox_rule_hash(const struct imap_sieve_mailbox_rule *rule)
-{
-	unsigned int hash = str_hash(rule->mailbox);
-
-	if (rule->from != NULL)
-		hash += str_hash(rule->from);
-	return hash;
-}
-
-static int
-imap_sieve_mailbox_rule_cmp(const struct imap_sieve_mailbox_rule *rule1,
-			    const struct imap_sieve_mailbox_rule *rule2)
-{
-	int ret;
-
-	ret = strcmp(rule1->mailbox, rule2->mailbox);
-	if (ret != 0)
-		return ret;
-	return null_strcmp(rule1->from, rule2->from);
-}
-
-static bool rule_pattern_has_wildcards(const char *pattern)
-{
-	for (; *pattern != '\0'; pattern++) {
-		if (*pattern == '%' || *pattern == '*')
-			return TRUE;
-	}
-	return FALSE;
-}
-
-static void imap_sieve_mailbox_rules_init(struct mail_user *user)
-{
-	struct imap_sieve_user *isuser = IMAP_SIEVE_USER_CONTEXT_REQUIRE(user);
-	string_t *identifier;
-	unsigned int i = 0;
-	size_t prefix_len;
-
-	if (hash_table_is_created(isuser->mbox_rules))
-		return;
-
-	hash_table_create(&isuser->mbox_rules, default_pool, 0,
-			  imap_sieve_mailbox_rule_hash,
-			  imap_sieve_mailbox_rule_cmp);
-	i_array_init(&isuser->mbox_patterns, 8);
-
-	identifier = t_str_new(256);
-	str_append(identifier, "imapsieve_mailbox");
-	prefix_len = str_len(identifier);
-
-	for (i = 1; ; i++) {
-		struct imap_sieve_mailbox_rule *mbrule;
-		const char *setval;
-		size_t id_len;
-
-		str_truncate(identifier, prefix_len);
-		str_printfa(identifier, "%u", i);
-		id_len = str_len(identifier);
-
-		str_append(identifier, "_name");
-		setval = mail_user_plugin_getenv(user, str_c(identifier));
-		if (setval == NULL || *setval == '\0')
-			break;
-		setval = t_str_trim(setval, "\t ");
-		if (strcasecmp(setval, "INBOX") == 0)
-			setval = t_str_ucase(setval);
-
-		mbrule = p_new(user->pool, struct imap_sieve_mailbox_rule, 1);
-		mbrule->index = i;
-		mbrule->mailbox = p_strdup(user->pool, setval);
-
-		str_truncate(identifier, id_len);
-		str_append(identifier, "_from");
-		setval = mail_user_plugin_getenv(user, str_c(identifier));
-		if (setval != NULL && *setval != '\0') {
-			setval = t_str_trim(setval, "\t ");
-			if (strcasecmp(setval, "INBOX") == 0)
-				setval = t_str_ucase(setval);
-			mbrule->from = p_strdup(user->pool, setval);
-			if (strcmp(mbrule->from, "*") == 0)
-				mbrule->from = NULL;
-		}
-
-		if ((strcmp(mbrule->mailbox, "*") == 0 ||
-		    !rule_pattern_has_wildcards(mbrule->mailbox)) &&
-		    (mbrule->from == NULL ||
-		     !rule_pattern_has_wildcards(mbrule->from)) &&
-		    hash_table_lookup(isuser->mbox_rules, mbrule) != NULL) {
-			e_warning(isuser->event,
-				  "Duplicate static mailbox rule [%u] for mailbox '%s' "
-				  "(skipped)", i, mbrule->mailbox);
-			continue;
-		}
-
-		str_truncate(identifier, id_len);
-		str_append(identifier, "_causes");
-		setval = mail_user_plugin_getenv(user, str_c(identifier));
-		if (setval != NULL && *setval != '\0') {
-			const char *const *cause;
-
-			mbrule->causes = (const char *const *)
-				p_strsplit_spaces(user->pool, setval, " \t,");
-
-			for (cause = mbrule->causes; *cause != NULL; cause++) {
-				if (!imap_sieve_event_cause_valid(*cause))
-					break;
-			}
-			if (*cause != NULL) {
-				e_warning(isuser->event,
-					  "Static mailbox rule [%u] has invalid event cause '%s' "
-					  "(skipped)", i, *cause);
-				continue;
-			}
-		}
-
-		str_truncate(identifier, id_len);
-		str_append(identifier, "_before");
-		setval = mail_user_plugin_getenv(user, str_c(identifier));
-		mbrule->before = p_strdup_empty(user->pool, setval);
-
-		str_truncate(identifier, id_len);
-		str_append(identifier, "_after");
-		setval = mail_user_plugin_getenv(user, str_c(identifier));
-		mbrule->after = p_strdup_empty(user->pool, setval);
-
-		str_truncate(identifier, id_len);
-		str_append(identifier, "_copy_source_after");
-		setval = mail_user_plugin_getenv(user, str_c(identifier));
-		mbrule->copy_source_after = p_strdup_empty(user->pool, setval);
-
-		e_debug(isuser->event, "Static mailbox rule [%u]: "
-			"mailbox='%s' from='%s' causes=(%s) => "
-			"before=%s after=%s%s",
-			mbrule->index, mbrule->mailbox,
-			(mbrule->from == NULL ? "*" : mbrule->from),
-			t_strarray_join(mbrule->causes, " "),
-			(mbrule->before == NULL ? "(none)" :
-			 t_strconcat("'", mbrule->before, "'", NULL)),
-			(mbrule->after == NULL ? "(none)" :
-			 t_strconcat("'", mbrule->after, "'", NULL)),
-			(mbrule->copy_source_after == NULL ? "":
-			 t_strconcat(" copy_source_after='",
-				     mbrule->copy_source_after, "'", NULL)));
-
-		if ((strcmp(mbrule->mailbox, "*") == 0 ||
-		    !rule_pattern_has_wildcards(mbrule->mailbox)) &&
-		    (mbrule->from == NULL ||
-		     !rule_pattern_has_wildcards(mbrule->from))) {
-			hash_table_insert(isuser->mbox_rules, mbrule, mbrule);
-		} else {
-			array_append(&isuser->mbox_patterns, &mbrule, 1);
-		}
-	}
-
-	if (i == 0)
-		e_debug(isuser->event, "No static mailbox rules");
-}
-
-static bool
-imap_sieve_mailbox_rule_match_cause(struct imap_sieve_mailbox_rule *rule,
-				    const char *cause)
-{
-	const char *const *cp;
-
-	if (rule->causes == NULL || *rule->causes == NULL)
-		return TRUE;
-
-	for (cp = rule->causes; *cp != NULL; cp++) {
-		if (strcasecmp(cause, *cp) == 0)
-			return TRUE;
-	}
-	return FALSE;
-}
-
-static void
-imap_sieve_mailbox_rules_match_patterns(
-	struct mail_user *user, struct mailbox *dst_box,
-	struct mailbox *src_box, const char *cause,
-	ARRAY_TYPE(imap_sieve_mailbox_rule) *rules)
-{
-	struct imap_sieve_user *isuser = IMAP_SIEVE_USER_CONTEXT_REQUIRE(user);
-	struct imap_sieve_mailbox_rule *rule;
-	struct mail_namespace *dst_ns, *src_ns;
-
-	if (array_count(&isuser->mbox_patterns) == 0)
-		return;
-
-	dst_ns = mailbox_get_namespace(dst_box);
-	src_ns = (src_box == NULL ? NULL : mailbox_get_namespace(src_box));
-
-	array_foreach_elem(&isuser->mbox_patterns, rule) {
-		struct imap_match_glob *glob;
-
-		if (src_ns == NULL && rule->from != NULL)
-			continue;
-		if (!imap_sieve_mailbox_rule_match_cause(rule, cause))
-			continue;
-
-		if (strcmp(rule->mailbox, "*") != 0) {
-			glob = imap_match_init(
-				pool_datastack_create(), rule->mailbox, TRUE,
-				mail_namespace_get_sep(dst_ns));
-			if (imap_match(glob, mailbox_get_vname(dst_box))
-				!= IMAP_MATCH_YES)
-				continue;
-		}
-		if (rule->from != NULL) {
-			glob = imap_match_init(
-				pool_datastack_create(), rule->from, TRUE,
-				mail_namespace_get_sep(src_ns));
-			if (imap_match(glob, mailbox_get_vname(src_box)) !=
-			    IMAP_MATCH_YES)
-				continue;
-		}
-
-		e_debug(isuser->event, "Matched static mailbox rule [%u]",
-			rule->index);
-		array_append(rules, &rule, 1);
-	}
-}
-
-static void
-imap_sieve_mailbox_rules_match(struct mail_user *user,
-			       const char *dst_box, const char *src_box,
-			       const char *cause,
-			       ARRAY_TYPE(imap_sieve_mailbox_rule) *rules)
-{
-	struct imap_sieve_user *isuser = IMAP_SIEVE_USER_CONTEXT_REQUIRE(user);
-	struct imap_sieve_mailbox_rule lookup_rule;
-	struct imap_sieve_mailbox_rule *rule;
-
-	i_zero(&lookup_rule);
-	lookup_rule.mailbox = dst_box;
-	lookup_rule.from = src_box;
-	rule = hash_table_lookup(isuser->mbox_rules, &lookup_rule);
-
-	if (rule != NULL && imap_sieve_mailbox_rule_match_cause(rule, cause)) {
-		struct imap_sieve_mailbox_rule *const *rule_idx;
-		unsigned int insert_idx = array_count(rules);
-
-		/* Insert sorted by rule index */
-		array_foreach(rules, rule_idx) {
-			if (rule->index < (*rule_idx)->index) {
-				insert_idx = array_foreach_idx(rules, rule_idx);
-				break;
-			}
-		}
-		array_insert(rules, insert_idx, &rule, 1);
-
-		e_debug(isuser->event, "Matched static mailbox rule [%u]",
-			rule->index);
-	}
-}
-
-static void
-imap_sieve_mailbox_rules_get(struct mail_user *user,
-			     struct mailbox *dst_box, struct mailbox *src_box,
-			     const char *cause,
-			     ARRAY_TYPE(imap_sieve_mailbox_rule) *rules)
-{
-	const char *dst_name, *src_name;
-
-	imap_sieve_mailbox_rules_init(user);
-
-	imap_sieve_mailbox_rules_match_patterns(user, dst_box, src_box,
-						cause, rules);
-
-	dst_name = mailbox_get_vname(dst_box);
-	src_name = (src_box == NULL ? NULL : mailbox_get_vname(src_box));
-
-	imap_sieve_mailbox_rules_match(user, dst_name, src_name, cause, rules);
-	imap_sieve_mailbox_rules_match(user, "*", src_name, cause, rules);
-	if (src_name != NULL) {
-		imap_sieve_mailbox_rules_match(user, dst_name, NULL,
-					       cause, rules);
-		imap_sieve_mailbox_rules_match(user, "*", NULL, cause, rules);
-	}
-}
-
 /*
  * User
  */
@@ -1102,10 +763,6 @@ static void imap_sieve_user_deinit(struct mail_user *user)
 	if (isuser->isieve != NULL)
 		imap_sieve_deinit(&isuser->isieve);
 
-	hash_table_destroy(&isuser->mbox_rules);
-	if (array_is_created(&isuser->mbox_patterns))
-		array_free(&isuser->mbox_patterns);
-
 	event_unref(&isuser->event);
 
 	isuser->module_ctx.super.deinit(user);
diff --git a/src/plugins/imapsieve/imap-sieve.c b/src/plugins/imapsieve/imap-sieve.c
index 7f0cfa80835d115cfeadb5bda8553505d01ab3ec..bb1a7e0a8f74d2896ec92a0c0082b5bd71d4c743 100644
--- a/src/plugins/imapsieve/imap-sieve.c
+++ b/src/plugins/imapsieve/imap-sieve.c
@@ -128,7 +128,7 @@ void imap_sieve_deinit(struct imap_sieve **_isieve)
 }
 
 static int
-imap_sieve_get_storage(struct imap_sieve *isieve,
+imap_sieve_get_storage(struct imap_sieve *isieve, const char *cause,
 		       struct sieve_storage **storage_r)
 {
 	enum sieve_storage_flags storage_flags = 0;
@@ -147,7 +147,8 @@ imap_sieve_get_storage(struct imap_sieve *isieve,
 		return -1;
 	}
 
-	if (sieve_storage_create_personal(isieve->svinst, user, storage_flags,
+	if (sieve_storage_create_personal(isieve->svinst, user,
+					  cause, storage_flags,
 					  &isieve->storage, &error_code) < 0) {
 		if (error_code == SIEVE_ERROR_TEMP_FAILURE)
 			return -1;
@@ -415,35 +416,94 @@ imap_sieve_run_init_trace_log(struct imap_sieve_run *isrun,
 	*trace_log_r = isrun->trace_log;
 }
 
+static int
+imap_sieve_multiscript_get_scripts(struct sieve_instance *svinst,
+				   struct event *event_parent,
+				   const char *cause, const char *type,
+				   ARRAY_TYPE(imap_sieve_run_script) *scripts,
+				   enum sieve_error *error_code_r)
+{
+	struct sieve_script_sequence *sseq;
+	struct sieve_script *script;
+	bool found = FALSE;
+	int ret;
+
+	ret = sieve_script_sequence_create(svinst, event_parent, cause, type,
+					   &sseq, error_code_r, NULL);
+	if (ret < 0)
+		return (*error_code_r == SIEVE_ERROR_NOT_FOUND ? 0 : -1);
+
+	while (ret >= 0) {
+		ret = sieve_script_sequence_next(sseq, &script,
+						 error_code_r, NULL);
+		if (ret < 0) {
+			if (*error_code_r == SIEVE_ERROR_TEMP_FAILURE) {
+				sieve_script_sequence_free(&sseq);
+				return -1;
+			}
+			continue;
+		}
+		if (ret == 0)
+			break;
+
+		struct imap_sieve_run_script *rscript;
+
+		rscript = array_append_space(scripts);
+		rscript->script = script;
+		found = TRUE;
+	}
+
+	sieve_script_sequence_free(&sseq);
+	return (found ? 1 : 0);
+}
+
+static int
+imap_sieve_multiscript_get_scripts_for(
+	struct sieve_instance *svinst, struct event *dest_mbox_event,
+	struct mailbox *src_mailbox, const char *cause, const char *type,
+	ARRAY_TYPE(imap_sieve_run_script) *scripts,
+	enum sieve_error *error_code_r)
+{
+	struct event *event;
+	int ret;
+
+	/* Restrict script storages to source mailbox if we have one. */
+	event = event_create(dest_mbox_event);
+	if (strcasecmp(cause, "copy") == 0 && src_mailbox != NULL) {
+		event_add_str(event, "imapsieve_from",
+			      mailbox_get_vname(src_mailbox));
+	}
+	ret = imap_sieve_multiscript_get_scripts(svinst, event,
+						 cause, type, scripts,
+						 error_code_r);
+	event_unref(&event);
+	if (ret != 0)
+		return ret;
+
+	/* None found */
+	return 0;
+}
+
 static int
 imap_sieve_run_init_scripts(struct imap_sieve *isieve,
+			    struct event *dest_mbox_event,
 			    ARRAY_TYPE(imap_sieve_run_script) *scripts,
+			    struct mailbox *src_mailbox, const char *cause,
 			    struct sieve_storage *storage,
 			    const char *script_name,
-			    const char *const *scripts_before,
-			    const char *const *scripts_after)
+			    const char *before_type, const char *after_type)
 {
 	struct sieve_instance *svinst = isieve->svinst;
 	enum sieve_error error_code;
-	const char *const *sp;
 
 	/* Admin scripts before user script */
-	if (scripts_before != NULL) {
-		for (sp = scripts_before; *sp != NULL; sp++) {
-			struct sieve_script *script;
-
-			if (sieve_script_create_open(svinst, *sp, NULL, &script,
-						     &error_code, NULL) < 0) {
-				if (error_code == SIEVE_ERROR_TEMP_FAILURE)
-					return -1;
-				continue;
-			}
-
-			struct imap_sieve_run_script *rscript;
-
-			rscript = array_append_space(scripts);
-			rscript->script = script;
-		}
+	if (before_type != NULL &&
+	    imap_sieve_multiscript_get_scripts_for(svinst, dest_mbox_event,
+						   src_mailbox, cause,
+						   before_type, scripts,
+						   &error_code) < 0) {
+		if (error_code == SIEVE_ERROR_TEMP_FAILURE)
+			return -1;
 	}
 
 	/* The user script */
@@ -464,33 +524,23 @@ imap_sieve_run_init_scripts(struct imap_sieve *isieve,
 	}
 
 	/* Admin scripts after user script */
-	if (scripts_after != NULL) {
-		for (sp = scripts_after; *sp != NULL; sp++) {
-			struct sieve_script *script;
-
-			if (sieve_script_create_open(svinst, *sp, NULL, &script,
-						     &error_code, NULL) < 0) {
-				if (error_code == SIEVE_ERROR_TEMP_FAILURE)
-					return -1;
-				continue;
-			}
-
-			struct imap_sieve_run_script *rscript;
-
-			rscript = array_append_space(scripts);
-			rscript->script = script;
-		}
+	if (imap_sieve_multiscript_get_scripts_for(svinst, dest_mbox_event,
+						   src_mailbox, cause,
+						   after_type, scripts,
+						   &error_code) < 0) {
+		if (error_code == SIEVE_ERROR_TEMP_FAILURE)
+			return -1;
 	}
 
 	return 0;
 }
 
 int imap_sieve_run_init(struct imap_sieve *isieve,
+			struct event *dest_mbox_event,
 			struct mailbox *dest_mailbox,
 			struct mailbox *src_mailbox,
 			const char *cause, const char *script_name,
-			const char *const *scripts_before,
-			const char *const *scripts_after,
+			const char *before_type, const char *after_type,
 			struct imap_sieve_run **isrun_r)
 {
 	struct imap_sieve_run *isrun;
@@ -506,16 +556,17 @@ int imap_sieve_run_init(struct imap_sieve *isieve,
 	/* Get storage for user script */
 	storage = NULL;
 	if (script_name != NULL && *script_name != '\0' &&
-	    (ret = imap_sieve_get_storage(isieve, &storage)) < 0)
+	    (ret = imap_sieve_get_storage(isieve, cause, &storage)) < 0)
 		return ret;
 
 	/* Open all scripts */
 	pool = pool_alloconly_create("imap_sieve_run", 256);
 	p_array_init(&scripts, pool, 16);
 
-	ret = imap_sieve_run_init_scripts(isieve, &scripts,
+	ret = imap_sieve_run_init_scripts(isieve, dest_mbox_event, &scripts,
+					  src_mailbox, cause,
 					  storage, script_name,
-					  scripts_before, scripts_after);
+					  before_type, after_type);
 	if (ret < 0) {
 		struct imap_sieve_run_script *rscript;
 
diff --git a/src/plugins/imapsieve/imap-sieve.h b/src/plugins/imapsieve/imap-sieve.h
index 12d0ac7b00a5c9dd0b3811395b2316f7d81282cf..c3b95b3dd39c087b1e3b9e97bceabf7c371e969a 100644
--- a/src/plugins/imapsieve/imap-sieve.h
+++ b/src/plugins/imapsieve/imap-sieve.h
@@ -1,6 +1,8 @@
 #ifndef IMAP_SIEVE_H
 #define IMAP_SIEVE_H
 
+#include "sieve-storage.h"
+
 struct client;
 
 /*
@@ -43,13 +45,13 @@ void imap_sieve_deinit(struct imap_sieve **_isieve);
 
 struct imap_sieve_run;
 
-int imap_sieve_run_init(struct imap_sieve *isieve, struct mailbox *dest_mailbox,
-			struct mailbox *src_mailbox, const char *cause,
-			const char *script_name,
-			const char *const *scripts_before,
-			const char *const *scripts_after,
-			struct imap_sieve_run **isrun_r)
-			ATTR_NULL(4, 5, 6);
+int imap_sieve_run_init(struct imap_sieve *isieve,
+			struct event *dest_mbox_event,
+			struct mailbox *dest_mailbox,
+			struct mailbox *src_mailbox,
+			const char *cause, const char *script_name,
+			const char *before_type, const char *after_type,
+			struct imap_sieve_run **isrun_r);
 
 int imap_sieve_run_mail(struct imap_sieve_run *isrun, struct mail *mail,
 			const char *changed_flags, bool *fatal_r);
diff --git a/src/plugins/lda-sieve/lda-sieve-plugin.c b/src/plugins/lda-sieve/lda-sieve-plugin.c
index 5a4ad72a21a418e2c6488296ec2f659ec1d95d9c..fd5e0a47cb4c4e0f165d84475e8e0e85f9292bce 100644
--- a/src/plugins/lda-sieve/lda-sieve-plugin.c
+++ b/src/plugins/lda-sieve/lda-sieve-plugin.c
@@ -257,7 +257,8 @@ lda_sieve_get_personal_storage(struct sieve_instance *svinst,
 			       struct sieve_storage **storage_r,
 			       enum sieve_error *error_code_r)
 {
-	if (sieve_storage_create_personal(svinst, user, 0,
+	if (sieve_storage_create_personal(svinst, user,
+					  SIEVE_SCRIPT_CAUSE_DELIVERY, 0,
 					  storage_r, error_code_r) < 0) {
 		switch (*error_code_r) {
 		case SIEVE_ERROR_NOT_POSSIBLE:
@@ -279,15 +280,13 @@ lda_sieve_get_personal_storage(struct sieve_instance *svinst,
 }
 
 static void
-lda_sieve_multiscript_log_error(struct event *event,
-				const char *label, const char *location,
+lda_sieve_multiscript_log_error(struct event *event, const char *type,
 				enum sieve_error error_code)
 {
 	switch (error_code) {
 	case SIEVE_ERROR_TEMP_FAILURE:
-		e_error(event, "Failed to access %s script from '%s' "
-			"(temporary failure)",
-			label, location);
+		e_error(event, "Failed to access '%s' script sequence"
+			"(temporary failure)", type);
 		break;
 	default:
 		break;
@@ -296,7 +295,7 @@ lda_sieve_multiscript_log_error(struct event *event,
 
 static int
 lda_sieve_multiscript_get_scripts(struct sieve_instance *svinst,
-				  const char *label, const char *location,
+				  const char *type,
 				  ARRAY_TYPE(sieve_script) *scripts,
 				  enum sieve_error *error_code_r)
 {
@@ -304,14 +303,15 @@ lda_sieve_multiscript_get_scripts(struct sieve_instance *svinst,
 	struct sieve_script *script;
 	int ret;
 
-	ret = sieve_script_sequence_create(svinst, location,
+	ret = sieve_script_sequence_create(svinst, svinst->event,
+					   SIEVE_SCRIPT_CAUSE_DELIVERY, type,
 					   &sseq, error_code_r, NULL);
 	if (ret < 0) {
 		if (*error_code_r == SIEVE_ERROR_NOT_FOUND) {
 			*error_code_r = SIEVE_ERROR_NONE;
 			return 0;
 		}
-		lda_sieve_multiscript_log_error(svinst->event, label, location,
+		lda_sieve_multiscript_log_error(svinst->event, type,
 						*error_code_r);
 		return -1;
 	}
@@ -322,7 +322,7 @@ lda_sieve_multiscript_get_scripts(struct sieve_instance *svinst,
 
 	sieve_script_sequence_free(&sseq);
 	if (ret < 0) {
-		lda_sieve_multiscript_log_error(svinst->event, label, location,
+		lda_sieve_multiscript_log_error(svinst->event, type,
 						*error_code_r);
 		return -1;
 	}
@@ -718,8 +718,6 @@ static int lda_sieve_find_scripts(struct lda_sieve_run_context *srctx)
 	struct mail_deliver_context *mdctx = srctx->mdctx;
 	struct sieve_instance *svinst = srctx->svinst;
 	struct sieve_storage *main_storage;
-	const char *sieve_before, *sieve_after, *sieve_discard;
-	const char *setting_name;
 	enum sieve_error error_code;
 	ARRAY_TYPE(sieve_script) script_sequence;
 	struct sieve_script *const *scripts;
@@ -740,20 +738,22 @@ static int lda_sieve_find_scripts(struct lda_sieve_run_context *srctx)
 		switch (error_code) {
 		case SIEVE_ERROR_NOT_FOUND:
 			e_debug(sieve_get_event(svinst),
-				"User has no active script in storage '%s'",
-				sieve_storage_location(main_storage));
+				"User has no active script in personal storage '%s'",
+				sieve_storage_name(main_storage));
 			break;
 		case SIEVE_ERROR_TEMP_FAILURE:
 			e_error(sieve_get_event(svinst),
-				"Failed to access active Sieve script in user storage '%s' "
+				"Failed to access active Sieve script in parsonal storage '%s': %s "
 				"(temporary failure)",
-				sieve_storage_location(main_storage));
+				sieve_storage_name(main_storage),
+				sieve_storage_get_last_error(main_storage, NULL));
 			ret = -1;
 			break;
 		default:
 			e_error(sieve_get_event(svinst),
-				"Failed to access active Sieve script in user storage '%s'",
-				sieve_storage_location(main_storage));
+				"Failed to access active Sieve script in personal storage '%s': %s",
+				sieve_storage_name(main_storage),
+				sieve_storage_get_last_error(main_storage, NULL));
 			break;
 		}
 	} else if (!sieve_script_is_default(srctx->main_script)) {
@@ -772,29 +772,9 @@ static int lda_sieve_find_scripts(struct lda_sieve_run_context *srctx)
 
 	/* before */
 	if (ret >= 0) {
-		i = 2;
-		setting_name = "sieve_before";
-		sieve_before = mail_user_plugin_getenv(
-			mdctx->rcpt_user, setting_name);
-		while (ret >= 0 &&
-		       sieve_before != NULL && *sieve_before != '\0') {
-			ret = lda_sieve_multiscript_get_scripts(
-				svinst, setting_name, sieve_before,
-				&script_sequence, &error_code);
-			if (ret < 0 && error_code == SIEVE_ERROR_TEMP_FAILURE) {
-				ret = -1;
-				break;
-			} else if (ret == 0) {
-				e_debug(sieve_get_event(svinst),
-					"Location for %s not found: %s",
-					setting_name, sieve_before);
-			}
-			ret = 0;
-			setting_name = t_strdup_printf("sieve_before%u", i++);
-			sieve_before = mail_user_plugin_getenv(
-				mdctx->rcpt_user, setting_name);
-		}
-
+		ret = lda_sieve_multiscript_get_scripts(
+			svinst, SIEVE_STORAGE_TYPE_BEFORE,
+			&script_sequence, &error_code);
 		if (ret >= 0) {
 			scripts = array_get(&script_sequence, &count);
 			for (i = 0; i < count; i ++) {
@@ -820,27 +800,9 @@ static int lda_sieve_find_scripts(struct lda_sieve_run_context *srctx)
 
 	/* after */
 	if (ret >= 0) {
-		i = 2;
-		setting_name = "sieve_after";
-		sieve_after = mail_user_plugin_getenv(mdctx->rcpt_user, setting_name);
-		while (sieve_after != NULL && *sieve_after != '\0') {
-			ret = lda_sieve_multiscript_get_scripts(
-				svinst, setting_name, sieve_after,
-				&script_sequence, &error_code);
-			if (ret < 0 && error_code == SIEVE_ERROR_TEMP_FAILURE) {
-				ret = -1;
-				break;
-			} else if (ret == 0) {
-				e_debug(sieve_get_event(svinst),
-					"Location for %s not found: %s",
-					setting_name, sieve_after);
-			}
-			ret = 0;
-			setting_name = t_strdup_printf("sieve_after%u", i++);
-			sieve_after = mail_user_plugin_getenv(
-				mdctx->rcpt_user, setting_name);
-		}
-
+		ret = lda_sieve_multiscript_get_scripts(
+			svinst, SIEVE_STORAGE_TYPE_AFTER,
+			&script_sequence, &error_code);
 		if (ret >= 0) {
 			scripts = array_get(&script_sequence, &count);
 			for ( i = after_index; i < count; i ++ ) {
@@ -852,25 +814,13 @@ static int lda_sieve_find_scripts(struct lda_sieve_run_context *srctx)
 	}
 
 	/* discard */
-	sieve_discard = mail_user_plugin_getenv(
-		mdctx->rcpt_user, "sieve_discard");
-	if (sieve_discard != NULL && *sieve_discard != '\0') {
-		if (sieve_script_create_open(svinst, sieve_discard, NULL,
-					     &srctx->discard_script,
-					     &error_code, NULL) < 0) {
-			switch (error_code) {
-			case SIEVE_ERROR_NOT_FOUND:
-				e_debug(sieve_get_event(svinst),
-					"Location for sieve_discard not found: %s",
-					sieve_discard);
-				break;
-			case SIEVE_ERROR_TEMP_FAILURE:
-				ret = -1;
-				break;
-			default:
-				break;
-			}
-		}
+	if (ret >= 0) {
+		ret = sieve_script_create_open(
+			svinst, SIEVE_SCRIPT_CAUSE_DELIVERY,
+			SIEVE_STORAGE_TYPE_DISCARD, NULL,
+			&srctx->discard_script, &error_code, NULL);
+		if (error_code == SIEVE_ERROR_NOT_FOUND)
+			ret = 0;
 	}
 
 	if (ret < 0) {
diff --git a/src/plugins/settings/Makefile.am b/src/plugins/settings/Makefile.am
index b55bb834e2b71b116a046453a6aab44cd2c40626..8388b32fddc2e23b92d528b2c04b24b0d35dbdfe 100644
--- a/src/plugins/settings/Makefile.am
+++ b/src/plugins/settings/Makefile.am
@@ -4,8 +4,12 @@ AM_CPPFLAGS = \
 	$(LIBDOVECOT_INCLUDE) \
 	-I$(top_srcdir) \
 	-I$(top_srcdir)/src/lib-sieve \
+	-I$(top_srcdir)/src/lib-sieve/storage/ldap \
 	-I$(top_srcdir)/src/lib-managesieve \
 	-DSETTINGS_PLUGIN
+if LDAP_PLUGIN
+AM_CPPFLAGS += -DPLUGIN_BUILD -DLDAP_PLUGIN
+endif
 
 libpigeonhole_settings_la_LDFLAGS = -module -avoid-version
 
@@ -17,10 +21,12 @@ libpigeonhole_settings_la_SOURCES = \
 	pigeonhole-settings.c
 libpigeonhole_settings_la_LIBADD = \
 	$(top_builddir)/src/lib-sieve/libdovecot-sieve.la \
-	$(top_builddir)/src/lib-managesieve/libdovecot-managesieve.la
+	$(top_builddir)/src/lib-managesieve/libdovecot-managesieve.la \
+	$(LDAP_LIBS)
 libpigeonhole_settings_la_DEPENDENCIES = \
 	$(top_builddir)/src/lib-sieve/libdovecot-sieve.la \
-	$(top_builddir)/src/lib-managesieve/libdovecot-managesieve.la
+	$(top_builddir)/src/lib-managesieve/libdovecot-managesieve.la \
+	$(LDAP_LIBS)
 
 noinst_HEADERS = \
 	pigeonhole-settings.h
diff --git a/src/plugins/settings/settings-get.pl b/src/plugins/settings/settings-get.pl
index edf7e8aa263e502bb941672fa546a9611c9a6d53..568df24836a8702f2e2e9147885a02c89bc542db 100755
--- a/src/plugins/settings/settings-get.pl
+++ b/src/plugins/settings/settings-get.pl
@@ -14,6 +14,7 @@ print '#include "lib.h"'."\n";
 print '#include "array.h"'."\n";
 print '#include "str.h"'."\n";
 print '#include "ipwd.h"'."\n";
+print '#include "env-util.h"'."\n";
 print '#include "var-expand.h"'."\n";
 print '#include "file-lock.h"'."\n";
 print '#include "fsync-mode.h"'."\n";
@@ -29,6 +30,9 @@ print '#include "sieve-address-source.h"'."\n";
 print '#include "managesieve-url.h"'."\n";
 print '#include "pigeonhole-settings.h"'."\n";
 print '#include <unistd.h>'."\n";
+print "#ifdef LDAP_PLUGIN\n";
+print '#include <ldap.h>'."\n";
+print "#endif\n";
 print '#define CONFIG_BINARY'."\n";
 
 my @services = ();
diff --git a/src/testsuite/testsuite-script.c b/src/testsuite/testsuite-script.c
index 102909cbb58f04b46ee2e8ce9ab0f06011398077..44f131474fe55c5fd6cb4fed5f883b75ce0c7789 100644
--- a/src/testsuite/testsuite-script.c
+++ b/src/testsuite/testsuite-script.c
@@ -2,6 +2,7 @@
  */
 
 #include "lib.h"
+#include "settings.h"
 
 #include "sieve.h"
 #include "sieve-common.h"
@@ -63,6 +64,7 @@ static struct sieve_binary *
 _testsuite_script_compile(const struct sieve_runtime_env *renv,
 			  const char *script)
 {
+	static unsigned int storage_id = 0;
 	struct sieve_instance *svinst = testsuite_sieve_instance;
 	struct sieve_binary *sbin;
 	const char *script_path;
@@ -75,8 +77,36 @@ _testsuite_script_compile(const struct sieve_runtime_env *renv,
 		return NULL;
 
 	script_path = t_strconcat(script_path, "/", script, NULL);
-	if (sieve_compile(svinst, script_path, NULL,
-			  testsuite_log_ehandler, 0, &sbin, NULL) < 0)
+
+	const char *storage_name = t_strdup_printf("testsuite-script%u",
+						   storage_id++);
+	const char *script_name = testsuite_script_get_name(script_path);
+
+	struct settings_instance *set_instance =
+		settings_instance_find(svinst->event);
+	settings_override(set_instance, "sieve_script+", storage_name,
+			  SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM);
+	settings_override(set_instance,
+			  t_strdup_printf("sieve_script/%s/sieve_script_name",
+					  storage_name),
+			  script_name,
+			  SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM);
+	settings_override(set_instance,
+			  t_strdup_printf("sieve_script/%s/sieve_script_type",
+					  storage_name),
+			  "testsuite", SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM);
+	settings_override(set_instance,
+			  t_strdup_printf("sieve_script/%s/sieve_script_driver",
+					  storage_name),
+			  "file", SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM);
+	settings_override(set_instance,
+			  t_strdup_printf("sieve_script/%s/sieve_script_path",
+					  storage_name),
+			  script_path, SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM);
+
+	if (sieve_compile(svinst, SIEVE_SCRIPT_CAUSE_ANY,
+			  storage_name, script_name, testsuite_log_ehandler, 0,
+			  &sbin, NULL) < 0)
 		return NULL;
 
 	return sbin;
diff --git a/src/testsuite/testsuite.c b/src/testsuite/testsuite.c
index 6f65e3c5bd9478cfb9762a029a3043a73582f120..d556297eec71478fd843296f0e6698e2d367bd7b 100644
--- a/src/testsuite/testsuite.c
+++ b/src/testsuite/testsuite.c
@@ -8,10 +8,12 @@
 #include "ostream.h"
 #include "hostpid.h"
 #include "path-util.h"
+#include "settings.h"
 
 #include "sieve.h"
 #include "sieve-extensions.h"
 #include "sieve-script.h"
+#include "sieve-storage.h"
 #include "sieve-binary.h"
 #include "sieve-result.h"
 #include "sieve-interpreter.h"
@@ -23,6 +25,7 @@
 #include "testsuite-settings.h"
 #include "testsuite-result.h"
 #include "testsuite-message.h"
+#include "testsuite-script.h"
 #include "testsuite-smtp.h"
 #include "testsuite-mailstore.h"
 
@@ -156,21 +159,77 @@ int main(int argc, char **argv)
 	else
 		sieve_dir = t_strdup_until(abspath, sieve_dir);
 
-	testsuite_setting_set("sieve",
-			      t_strdup_printf("file:%s/included;active=~/.dovecot.sieve",
-					      sieve_dir));
-	testsuite_setting_set("sieve_global",
-			      t_strdup_printf("%s/included-global", sieve_dir));
-
 	/* Finish testsuite initialization */
 	svinst = sieve_tool_init_finish(sieve_tool, FALSE, FALSE);
 	testsuite_init(svinst, sieve_dir, log_stdout);
 
 	printf("Test case: %s:\n\n", scriptfile);
 
+	struct settings_instance *set_instance =
+		settings_instance_find(svinst->event);
+
+	/* Configure main test script */
+	settings_override(set_instance, "sieve_script+", "testsuite-main",
+			  SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM);
+	settings_override(set_instance,
+			  "sieve_script/testsuite-main/sieve_script_storage",
+			  "testsuite-main",
+			  SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM);
+	settings_override(set_instance,
+			  "sieve_script/testsuite-main/sieve_script_name",
+			  testsuite_script_get_name(scriptfile),
+			  SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM);
+	settings_override(set_instance,
+			  "sieve_script/testsuite-main/sieve_script_type",
+			  "testsuite", SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM);
+	settings_override(set_instance,
+			  "sieve_script/testsuite-main/sieve_script_driver",
+			  "file", SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM);
+	settings_override(set_instance,
+			  "sieve_script/testsuite-main/sieve_script_path",
+			  scriptfile, SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM);
+
+	/* Configure personal storage */
+	settings_override(set_instance, "sieve_script+", "included",
+			  SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM);
+	settings_override(set_instance,
+			  "sieve_script/included/sieve_script_storage",
+			  "included",
+			  SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM);
+	settings_override(set_instance,
+			  "sieve_script/included/sieve_script_type",
+			  SIEVE_STORAGE_TYPE_PERSONAL,
+			  SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM);
+	settings_override(set_instance,
+			  "sieve_script/included/sieve_script_driver",
+			  "file", SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM);
+	settings_override(set_instance,
+			  "sieve_script/included/sieve_script_path",
+			  t_strdup_printf("%s/included", sieve_dir),
+			  SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM);
+
+	/* Configure global storage */
+	settings_override(set_instance, "sieve_script+", "included-global",
+			  SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM);
+	settings_override(set_instance,
+			  "sieve_script/included-global/sieve_script_storage",
+			  "included-global",
+			  SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM);
+	settings_override(set_instance,
+			  "sieve_script/included-global/sieve_script_type",
+			  SIEVE_STORAGE_TYPE_GLOBAL,
+			  SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM);
+	settings_override(set_instance,
+			  "sieve_script/included-global/sieve_script_driver",
+			  "file", SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM);
+	settings_override(set_instance,
+			  "sieve_script/included-global/sieve_script_path",
+			  t_strdup_printf("%s/included-global", sieve_dir),
+			  SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM);
+
 	/* Compile sieve script */
-	if (sieve_compile(svinst, scriptfile, NULL,
-			  testsuite_log_main_ehandler, 0,
+	if (sieve_compile(svinst, SIEVE_SCRIPT_CAUSE_ANY, "testsuite-main",
+			  NULL, testsuite_log_main_ehandler, 0,
 			  &sbin, NULL) < 0) {
 		testsuite_testcase_fail("failed to compile testcase script");
 	} else {
diff --git a/tests/extensions/include/execute.svtest b/tests/extensions/include/execute.svtest
index 734ac66441060db6c7a35c0ece90b5aa9b532d18..a1b849519468543f44eea5afdda6e579afdfa0ec 100644
--- a/tests/extensions/include/execute.svtest
+++ b/tests/extensions/include/execute.svtest
@@ -54,8 +54,29 @@ test "Namespace - file" {
 }
 
 test "Namespace - dict" {
-	test_config_set "sieve" "dict:file:${tst.path}/included/namespace.dict";
-	test_config_set "sieve_global" "dict:file:${tst.path}/included-global/namespace.dict";
+	test_config_set "sieve_script/included/sieve_script_type" "personal";
+	test_config_set "sieve_script/included/sieve_script_driver" "dict";
+	test_config_set "sieve_script/included/sieve_script_dict/dict" "file";
+	test_config_set
+		"sieve_script/included/sieve_script_dict/dict/file/driver"
+		"file";
+	test_config_set
+		"sieve_script/included/sieve_script_dict/dict/file/path"
+		"${tst.path}/included/namespace.dict";
+
+	test_config_set
+		"sieve_script/included-global/sieve_script_type" "global";
+	test_config_set
+		"sieve_script/included-global/sieve_script_driver" "dict";
+	test_config_set
+		"sieve_script/included-global/sieve_script_dict/dict" "file";
+	test_config_set
+		"sieve_script/included-global/sieve_script_dict/dict/file/driver"
+		"file";
+	test_config_set
+		"sieve_script/included-global/sieve_script_dict/dict/file/path"
+		"${tst.path}/included-global/namespace.dict";
+
 	test_config_reload :extension "include";
 
 	if not test_script_compile "execute/namespace.sieve" {