diff --git a/TODO b/TODO
index 7771e4386af3fb1ee37e627b4b36f7acff1bb143..e0483c575731d17e7bd3755121011b32e28daea7 100644
--- a/TODO
+++ b/TODO
@@ -1,12 +1,6 @@
 Current activities:
 
-* Make the sieve storage a base class with (possibly) various implementations,
-  just like mail-storage. This aims to provide support for alternate types
-  of script storage like LDAP or SQL database.
-	- Implement read/write script storages for using ManageSieve with dict
-	  database
-	- Implement infrastructure for loading a sequence of global scripts from
-	  a database (for sieve_before/sieve_after).
+* Implement LDAP Sieve script storage for read-only access.
 
 Parallel plugin-based efforts (on hold at the moment):
 
@@ -17,8 +11,21 @@ Parallel plugin-based efforts (on hold at the moment):
 Next (mostly in order of descending priority/precedence):
 
 * Implement index extension
+* Implement message modification and extraction API in order to:
+	- Implement replace, enclose, foreverypart, mime and extracttext extensions
 * Add normalize() method to comparators to normalize the string before matching
   (for efficiency).
+* Properly implement Sieve internationalization support (utf-8 handling),
+  currently it is not complete:
+	- Make this implementation fully conform section 2.7.2 of RFC5228 (Comparisons
+	  Across Character Sets).
+	- Verify validity of utf8 where necessary.
+	- Implement comparator-i;unicode-casemap.
+* Add support for stream matching for handling large values, e.g. from the body
+  extension.
+* Clean up doveadm sync implementation:
+	- Mostly comments referring specifically to the file storage type.
+	- API can be polished a lot as well.
 * Improve error handling.
 	- Implement dropping errors in the user's mailbox as a mail message.
 * Further develop regex extension and update it to the latest draft:
@@ -50,12 +57,6 @@ Next (mostly in order of descending priority/precedence):
 	  fails rather than an implicit keep.
 	- Add a method to implicitly pass environment variables such as SENDER and
 	  RECIPIENT through the script socket service.
-* Properly implement Sieve internationalization support (utf-8 handling),
-  currently it is not complete:
-	- Make this implementation fully conform section 2.7.2 of RFC5228 (Comparisons
-	  Across Character Sets).
-	- Verify validity of utf8 where necessary.
-	- Implement comparator-i;unicode-casemap.
 * Make testsuite much more exhaustive:
 	- Add support for testing the content of result actions
 	- Test as many error/warning/info conditions as possible.
@@ -79,10 +80,6 @@ Low priority items:
 * Implement extlists extension as a plugin
 * Enotify extension: detect use of variable values extracted from the message
   that are used in the method argument. RFC reports this as a security issue.
-* Add support for stream matching for handling large values, e.g. from the body
-  extension.
-* Implement message modification and extraction API in order to:
-	- Implement replace, enclose, foreverypart, mime and extracttext extensions
 * Provide a solution for mail_get_headers_utf8 reparsing the whole message each
   time it is called (header and address test; Timo might provide solution from
   within Dovecot)
@@ -91,7 +88,6 @@ Low priority items:
 
 * Implement IMAP plugin for IMAPSieve support:
 	- Requires some sort of Sieve transaction support.
-	- Requires (IMAP) METADATA support in Dovecot.
 	- This may include support for manually running a script on a set of messages
 	  through IMAP (no specification for something like this is available; we will
 	  have to provide our own)
diff --git a/configure.ac b/configure.ac
index 729dbc3a1e151c4af4a541b0dd9811da386b162f..a5beb25de74d279af35449470a26f5cc905aefa9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -101,6 +101,9 @@ doc/rfc/Makefile
 src/Makefile
 src/lib-sieve/Makefile
 src/lib-sieve/util/Makefile
+src/lib-sieve/storage/Makefile
+src/lib-sieve/storage/file/Makefile
+src/lib-sieve/storage/dict/Makefile
 src/lib-sieve/plugins/Makefile
 src/lib-sieve/plugins/vacation/Makefile
 src/lib-sieve/plugins/subaddress/Makefile
@@ -126,7 +129,6 @@ src/lib-sieve/plugins/duplicate/Makefile
 src/lib-sieve/plugins/vnd.dovecot/Makefile
 src/lib-sieve/plugins/vnd.dovecot/debug/Makefile
 src/lib-sieve-tool/Makefile
-src/lib-sievestorage/Makefile
 src/lib-managesieve/Makefile
 src/plugins/Makefile
 src/plugins/doveadm-sieve/Makefile
diff --git a/src/Makefile.am b/src/Makefile.am
index 6f0d3ef9eaf6a3f0f68e84138c1c37da637a1025..85c1e44642e9e5c62ea96994d3adf1b93093f2d3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -2,7 +2,6 @@
 
 sieve_subdirs = \
 	lib-sieve \
-	lib-sievestorage \
 	plugins \
 	lib-sieve-tool \
 	sieve-tools \
diff --git a/src/lib-sieve/Makefile.am b/src/lib-sieve/Makefile.am
index d6007ca91c01bdea48db816afaeb3222bad425d2..6cc3aad8f20627ae1cf331ae8eb889aa07c982bc 100644
--- a/src/lib-sieve/Makefile.am
+++ b/src/lib-sieve/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = util plugins
+SUBDIRS = util storage plugins
 
 dovecot_pkglib_LTLIBRARIES = libdovecot-sieve.la
 
@@ -43,11 +43,17 @@ comparators = \
 	cmp-i-ascii-casemap.c
 
 if BUILD_UNFINISHED
+unfinished_storages =
 unfinished_plugins = \
 	$(extdir)/metadata/libsieve_ext_metadata.la
 endif
 
-# These are not actual plugins just yet...
+strgdir = $(top_builddir)/src/lib-sieve/storage
+storages = \
+	$(strgdir)/file/libsieve_storage_file.la \
+	$(strgdir)/dict/libsieve_storage_dict.la \
+	$(unfinished_storages)
+
 extdir = $(top_builddir)/src/lib-sieve/plugins
 plugins = \
 	$(extdir)/vacation/libsieve_ext_vacation.la \
@@ -73,12 +79,14 @@ plugins = \
 	$(unfinished_plugins)
 
 libdovecot_sieve_la_DEPENDENCIES = \
+	$(storages) \
 	$(plugins) \
 	$(top_builddir)/src/lib-sieve/util/libsieve_util.la \
 	$(LIBDOVECOT_LDA_DEPS) \
 	$(LIBDOVECOT_STORAGE_DEPS) \
 	$(LIBDOVECOT_DEPS)
 libdovecot_sieve_la_LIBADD = \
+	$(storages) \
 	$(plugins) \
 	$(top_builddir)/src/lib-sieve/util/libsieve_util.la \
 	$(LIBDOVECOT_LDA) \
@@ -91,8 +99,8 @@ libdovecot_sieve_la_SOURCES = \
 	sieve-smtp.c \
 	sieve-lexer.c \
 	sieve-script.c \
-	sieve-script-file.c \
-	sieve-script-dict.c \
+	sieve-storage.c \
+	sieve-storage-sync.c \
 	sieve-ast.c \
 	sieve-binary.c \
 	sieve-binary-file.c \
@@ -137,7 +145,8 @@ headers = \
 	sieve-lexer.h \
 	sieve-script.h \
 	sieve-script-private.h \
-	sieve-script-file.h \
+	sieve-storage.h \
+	sieve-storage-private.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 e8804d50ceac099fc4d40d6a5d3b040da40052f3..47949203c9c494a637e3008a8e796a9abf81d966 100644
--- a/src/lib-sieve/plugins/include/cmd-include.c
+++ b/src/lib-sieve/plugins/include/cmd-include.c
@@ -6,6 +6,7 @@
 
 #include "sieve-common.h"
 #include "sieve-script.h"
+#include "sieve-storage.h"
 #include "sieve-ast.h"
 #include "sieve-code.h"
 #include "sieve-extensions.h"
@@ -213,8 +214,9 @@ static bool cmd_include_validate
 	struct sieve_ast_argument *arg = cmd->first_positional;
 	struct cmd_include_context_data *ctx_data =
 		(struct cmd_include_context_data *) cmd->data;
+	struct sieve_storage *storage;
 	struct sieve_script *script;
-	const char *script_location, *script_name;
+	const char *script_name;
 	enum sieve_error error = SIEVE_ERROR_NONE;
 	int ret;
 
@@ -247,30 +249,41 @@ static bool cmd_include_validate
 		return FALSE;
 	}
 
-	script_location = ext_include_get_script_location
-		(this_ext, ctx_data->location, script_name);
-	if ( script_location == NULL ) {
-		sieve_argument_validate_error(valdtr, arg,
-			"include: %s location for included script '%s' is unavailable "
-			"(contact system administrator for more information)",
-			ext_include_script_location_name(ctx_data->location),
-			str_sanitize(script_name, 80));
+	storage = ext_include_get_script_storage
+		(this_ext, ctx_data->location, script_name,	&error);
+	if ( storage == NULL ) {
+		// FIXME: handle ':optional' in this case
+		if (error == SIEVE_ERROR_NOT_FOUND) {
+			sieve_argument_validate_error(valdtr, arg,
+				"include: %s location for included script `%s' is unavailable "
+				"(contact system administrator for more information)",
+				ext_include_script_location_name(ctx_data->location),
+				str_sanitize(script_name, 80));
+		} else {
+			sieve_argument_validate_error(valdtr, arg,
+				"include: failed to access %s location for included script `%s' "
+				"(contact system administrator for more information)",
+				ext_include_script_location_name(ctx_data->location),
+				str_sanitize(script_name, 80));
+		}
 		return FALSE;
 	}
 
 	/* Create script object */
-	script = sieve_script_create
-		(this_ext->svinst, script_location, script_name,
-			sieve_validator_error_handler(valdtr), &error);
-
-	ret = 0;
-	if ( script != NULL )
-		ret = sieve_script_open(script, &error);
+	script = sieve_storage_get_script
+		(storage, script_name, &error);
+	if ( script == NULL )
+		return FALSE;
 
-	if ( script == NULL || ret < 0 ) {
+	ret = sieve_script_open(script, &error);
+	if ( ret < 0 ) {
 		if ( error != SIEVE_ERROR_NOT_FOUND ) {
-			if ( script != NULL )
-				sieve_script_unref(&script);
+			sieve_argument_validate_error(valdtr, arg,
+				"failed to access included %s script '%s': %s",
+				ext_include_script_location_name(ctx_data->location),
+				str_sanitize(script_name, 80),
+				sieve_script_get_last_error_lcase(script));
+			sieve_script_unref(&script);
 			return FALSE;
 
 		/* Not found */
@@ -295,8 +308,7 @@ static bool cmd_include_validate
 					"included %s script '%s' does not exist",
 					ext_include_script_location_name(ctx_data->location),
 					str_sanitize(script_name, 80));
-				if ( script != NULL )
-					sieve_script_unref(&script);
+				sieve_script_unref(&script);
 				return FALSE;
 			}
 		}
diff --git a/src/lib-sieve/plugins/include/ext-include-binary.c b/src/lib-sieve/plugins/include/ext-include-binary.c
index f1a85f768f4fba13ad6ecfd59cfbb176c7b0812d..3f523a0b7587daef74f2e5edbbb3600565c1292e 100644
--- a/src/lib-sieve/plugins/include/ext-include-binary.c
+++ b/src/lib-sieve/plugins/include/ext-include-binary.c
@@ -7,6 +7,7 @@
 #include "sieve-common.h"
 #include "sieve-error.h"
 #include "sieve-script.h"
+#include "sieve-storage.h"
 #include "sieve-binary.h"
 #include "sieve-generator.h"
 #include "sieve-interpreter.h"
@@ -277,7 +278,7 @@ static bool ext_include_binary_open
 		struct sieve_binary_block *inc_block = NULL;
 		unsigned int location, flags;
 		string_t *script_name;
-		const char *script_location;
+		struct sieve_storage *storage;
 		struct sieve_script *script;
 		enum sieve_error error;
 		int ret;
@@ -314,24 +315,24 @@ static bool ext_include_binary_open
 		}
 
 		/* Can we find the script dependency ? */
-		script_location = ext_include_get_script_location
-			(ext, location, str_c(script_name));
-		if ( script_location == NULL ) {
+		storage = ext_include_get_script_storage
+			(ext, location, str_c(script_name), NULL);
+		if ( storage == NULL ) {
 			/* No, recompile */
+			// FIXME: handle ':optional' in this case
 			return FALSE;
 		}
 
 		/* Can we open the script dependency ? */
-		script = sieve_script_create
-			(ext->svinst, script_location, str_c(script_name), NULL, &error);
+		script = sieve_storage_get_script
+			(storage, str_c(script_name), &error);
 		if ( script == NULL ) {
 			/* No, recompile */
 			return FALSE;
 		}
-		if ( sieve_script_open(script, &error) < 0 ) {			
+		if ( sieve_script_open(script, &error) < 0 ) {
 			if ( error != SIEVE_ERROR_NOT_FOUND ) {
 				/* No, recompile */
-				sieve_script_unref(&script);
 				return FALSE;
 			}
 
@@ -342,7 +343,6 @@ static bool ext_include_binary_open
 						"include: script '%s' included in binary %s is missing, "
 						"so recompile", str_c(script_name), sieve_binary_path(sbin));
 				}
-				sieve_script_unref(&script);
 				return FALSE;
 			}
 
diff --git a/src/lib-sieve/plugins/include/ext-include-common.c b/src/lib-sieve/plugins/include/ext-include-common.c
index ad69325629134fb5bf38b2c44e391aa014fe3048..3a595d572202343b4b2d3a464c99bb11635ad29a 100644
--- a/src/lib-sieve/plugins/include/ext-include-common.c
+++ b/src/lib-sieve/plugins/include/ext-include-common.c
@@ -10,6 +10,7 @@
 #include "sieve-settings.h"
 #include "sieve-error.h"
 #include "sieve-script.h"
+#include "sieve-storage.h"
 #include "sieve-ast.h"
 #include "sieve-binary.h"
 #include "sieve-commands.h"
@@ -76,36 +77,26 @@ bool ext_include_load
 {
 	struct sieve_instance *svinst = ext->svinst;
 	struct ext_include_context *ctx;
-	const char *sieve_dir;
+	const char *location;
 	unsigned long long int uint_setting;
 
 	if ( *context != NULL ) {
 		ext_include_unload(ext);
 	}
 
-	// FIXME: sieve_dir and sieve_global_dir settings have somewhat become
-	// misnomers; these are not necessary filesystem directories anymore.
-
 	ctx = i_new(struct ext_include_context, 1);
 
-	/* Get directory for :global scripts */
-	sieve_dir = sieve_setting_get(svinst, "sieve_global_dir");
+	/* Get location for :global scripts */
+	location = sieve_setting_get(svinst, "sieve_global");
+	if ( location == NULL )
+		location = sieve_setting_get(svinst, "sieve_global_dir");
 
-	if ( sieve_dir == NULL && svinst->debug ) {
-		sieve_sys_debug(svinst, "include: sieve_global_dir is not set; "
+	if ( location == NULL && svinst->debug ) {
+		sieve_sys_debug(svinst, "include: sieve_global is not set; "
 			"it is currently not possible to include `:global' scripts.");
 	}
 
-	ctx->global_dir = i_strdup(sieve_dir);
-
-	/* Get directory for :personal scripts */
- 	sieve_dir = sieve_setting_get(svinst, "sieve_dir");
-
-	if ( sieve_dir == NULL ) {
-		sieve_dir = "~/sieve";
-	}
-
-	ctx->personal_dir = i_strdup(sieve_dir);
+	ctx->global_location = i_strdup(location);
 
 	/* Get limits */
 	ctx->max_nesting_depth = EXT_INCLUDE_DEFAULT_MAX_NESTING_DEPTH;
@@ -135,8 +126,12 @@ void ext_include_unload
 	struct ext_include_context *ctx =
 		(struct ext_include_context *) ext->context;
 
-	i_free(ctx->personal_dir);
-	i_free(ctx->global_dir);
+	if ( ctx->global_storage != NULL )
+		sieve_storage_unref(&ctx->global_storage);
+	if ( ctx->personal_storage != NULL )
+		sieve_storage_unref(&ctx->personal_storage);
+
+	i_free(ctx->global_location);
 	i_free(ctx);
 }
 
@@ -144,44 +139,42 @@ void ext_include_unload
  * Script access
  */
 
-const char *ext_include_get_script_location
-(const struct sieve_extension *ext, enum ext_include_script_location location,
-   const char *script_name)
+struct sieve_storage *ext_include_get_script_storage
+(const struct sieve_extension *ext,
+	enum ext_include_script_location location,
+	const char *script_name, enum sieve_error *error_r)
 {
 	struct sieve_instance *svinst = ext->svinst;
 	struct ext_include_context *ctx =
 		(struct ext_include_context *) ext->context;
-	const char *sieve_dir;
 
 	switch ( location ) {
 	case EXT_INCLUDE_LOCATION_PERSONAL:
-
-		if ( ctx->personal_dir == NULL ) {
-			sieve_sys_error(svinst, "include: sieve_dir is unconfigured; "
-				"include of `:personal' script `%s' is therefore not possible",
-				str_sanitize(script_name, 80));
-			return NULL;
+		if ( ctx->personal_storage == NULL ) {
+			ctx->personal_storage = sieve_storage_create_main
+				(svinst, NULL, 0, error_r);
 		}
-
-		sieve_dir = ctx->personal_dir;
-		break;
+		return ctx->personal_storage;
 
 	case EXT_INCLUDE_LOCATION_GLOBAL:
-
-		if ( ctx->global_dir == NULL ) {
-			sieve_sys_error(svinst, "include: sieve_global_dir is unconfigured; "
+		if ( ctx->global_location == NULL ) {
+			sieve_sys_info(svinst, "include: sieve_global is unconfigured; "
 				"include of `:global' script `%s' is therefore not possible",
 				str_sanitize(script_name, 80));
+			*error_r = SIEVE_ERROR_NOT_FOUND;
 			return NULL;
 		}
-
-		sieve_dir = ctx->global_dir;
-		break;
+		if ( ctx->global_storage == NULL ) {
+			ctx->global_storage = sieve_storage_create
+				(svinst, ctx->global_location, 0, error_r);
+		}
+		return ctx->global_storage;
 	default:
-		i_unreached();
+		break;
 	}
 
-	return sieve_dir;
+	i_unreached();
+	return NULL;
 }
 
 /*
diff --git a/src/lib-sieve/plugins/include/ext-include-common.h b/src/lib-sieve/plugins/include/ext-include-common.h
index 54edc46a40a91d3c82f060e08a80449312b7b476..5c5377ff8f5370078b1cb698b247a9f839c12344 100644
--- a/src/lib-sieve/plugins/include/ext-include-common.h
+++ b/src/lib-sieve/plugins/include/ext-include-common.h
@@ -93,10 +93,10 @@ extern const struct sieve_operation_def global_operation;
  * Script access
  */
 
-const char *ext_include_get_script_location
+struct sieve_storage *ext_include_get_script_storage
 	(const struct sieve_extension *ext,
-		enum ext_include_script_location location, const char *script_name);
-
+		enum ext_include_script_location location,
+		const char *script_name, enum sieve_error *error_r);
 /*
  * Context
  */
@@ -108,8 +108,10 @@ struct ext_include_context {
 	const struct sieve_extension *var_ext;
 
 	/* Configuration */
- 	char *global_dir;
-	char *personal_dir;
+ 	char *global_location;
+
+	struct sieve_storage *global_storage;
+	struct sieve_storage *personal_storage;
 
 	unsigned int max_nesting_depth;
 	unsigned int max_includes;
diff --git a/src/lib-sieve/sieve-binary.h b/src/lib-sieve/sieve-binary.h
index 1e4fe5d5ef46ac2d35a17cfcdb80b0b92186df86..d2a172a797af64180a10eaf9ab3396f1fe49308c 100644
--- a/src/lib-sieve/sieve-binary.h
+++ b/src/lib-sieve/sieve-binary.h
@@ -13,7 +13,7 @@
  */
 
 #define SIEVE_BINARY_VERSION_MAJOR     1
-#define SIEVE_BINARY_VERSION_MINOR     0
+#define SIEVE_BINARY_VERSION_MINOR     1
 
 /*
  * Binary object
diff --git a/src/lib-sieve/sieve-common.h b/src/lib-sieve/sieve-common.h
index 5c5c1820f5e8b8ff7620c4fa4466303e7f7959e3..6903dde92c36aeae26564c4577e6fc6424166947 100644
--- a/src/lib-sieve/sieve-common.h
+++ b/src/lib-sieve/sieve-common.h
@@ -132,6 +132,11 @@ struct sieve_side_effect_def;
 
 /* sieve-script.h */
 struct sieve_script;
+struct sieve_script_sequence;
+
+/* sieve-storage.h */
+struct sieve_storage_class_registry;
+struct sieve_storage;
 
 /* sieve-message.h */
 struct sieve_message_context;
@@ -178,6 +183,9 @@ struct sieve_instance {
 	/* Extension registry */
 	struct sieve_extension_registry *ext_reg;
 
+	/* Storage class registry */
+	struct sieve_storage_class_registry *storage_reg;
+
 	/* System error handler */
 	struct sieve_error_handler *system_ehandler;
 
diff --git a/src/lib-sieve/sieve-error.c b/src/lib-sieve/sieve-error.c
index 7d28bfa9f3cb003689289c06d3c7fb80219e25ed..02d8b10e611a93d6b38bf678048aea3aabeed8b5 100644
--- a/src/lib-sieve/sieve-error.c
+++ b/src/lib-sieve/sieve-error.c
@@ -468,9 +468,6 @@ void sieve_vcritical
 (struct sieve_instance *svinst, struct sieve_error_handler *ehandler,
 	const char *location, const char *user_prefix, const char *fmt, va_list args)
 {
-	char str[256];
-	struct tm *tm;
-
 	if ( location == NULL || *location == '\0' ) {
 		sieve_direct_verror
 			(svinst, svinst->system_ehandler, 0, NULL, fmt, args);
@@ -479,19 +476,7 @@ void sieve_vcritical
 			(svinst, svinst->system_ehandler, 0, location, fmt, args);
 	}
 
-	if ( ehandler == NULL || ehandler == svinst->system_ehandler ) return;
-
-	tm = localtime(&ioloop_time);
-
-	if ( user_prefix == NULL || *user_prefix == '\0' ) {
-		sieve_direct_error(svinst, ehandler, 0, location, "%s",
-			( strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ?
-				str : CRITICAL_MSG ));
-	} else {
-		sieve_direct_error(svinst, ehandler, 0, location, "%s: %s", user_prefix,
-			( strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ?
-				str : CRITICAL_MSG ));
-	}
+	sieve_internal_error(ehandler, location, user_prefix);
 }
 
 void sieve_error
@@ -556,6 +541,32 @@ void sieve_critical
 	va_end(args);
 }
 
+void sieve_internal_error
+(struct sieve_error_handler *ehandler, const char *location,
+	const char *user_prefix)
+{
+	char str[256];
+	struct tm *tm;
+
+	if ( ehandler == NULL ||
+		ehandler == ehandler->svinst->system_ehandler )
+		return;
+
+	tm = localtime(&ioloop_time);
+
+	if ( user_prefix == NULL || *user_prefix == '\0' ) {
+		sieve_direct_error(ehandler->svinst, ehandler, 0,
+			location, "%s",
+			( strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ?
+				str : CRITICAL_MSG ));
+	} else {
+		sieve_direct_error(ehandler->svinst, ehandler, 0,
+			location, "%s: %s", user_prefix,
+			( strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ?
+				str : CRITICAL_MSG ));
+	}
+}
+
 /*
  * Error statistics
  */
diff --git a/src/lib-sieve/sieve-error.h b/src/lib-sieve/sieve-error.h
index 2a5f92ffdd259d0fc29e4a196994af260c64a62d..4ae9bbbd2b051faaf3ebd2ecc7a42d2b8b1aefcf 100644
--- a/src/lib-sieve/sieve-error.h
+++ b/src/lib-sieve/sieve-error.h
@@ -143,6 +143,10 @@ void sieve_critical
 		const char *location, const char *user_prefix, const char *fmt, ...)
 		ATTR_FORMAT(5, 6);
 
+void sieve_internal_error
+(struct sieve_error_handler *ehandler, const char *location,
+	const char *user_prefix) ATTR_NULL(1, 2, 3);
+
 /*
  * Error handler configuration
  */
diff --git a/src/lib-sieve/sieve-script-dict.c b/src/lib-sieve/sieve-script-dict.c
index 6d65c862bb3cd2422049dd7ddb9b3637c5107607..19abeb702b035c55630e1f860763844fec596351 100644
--- a/src/lib-sieve/sieve-script-dict.c
+++ b/src/lib-sieve/sieve-script-dict.c
@@ -263,9 +263,6 @@ static bool sieve_dict_script_equals
 	struct sieve_dict_script *script = (struct sieve_dict_script *)_script;
 	struct sieve_dict_script *other = (struct sieve_dict_script *)_other;
 
-	if ( script == NULL || other == NULL )
-		return FALSE;
-
 	if ( strcmp(script->dict_uri, other->dict_uri) != 0 )
 		return FALSE;
 
diff --git a/src/lib-sieve/sieve-script-file.h b/src/lib-sieve/sieve-script-file.h
deleted file mode 100644
index cba6021003228af89b6d4b389b1b99a20e845a98..0000000000000000000000000000000000000000
--- a/src/lib-sieve/sieve-script-file.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
- */
-
-#ifndef __SIEVE_SCRIPT_FILE_H
-#define __SIEVE_SCRIPT_FILE_H
-
-/*
- * Sieve script filenames
- */
-
-bool sieve_scriptfile_has_extension(const char *filename);
-const char *sieve_scriptfile_get_script_name(const char *filename);
-const char *sieve_scriptfile_from_name(const char *name);
-
-/*
- * File script specific functions
- */
-
-
-/* Return directory where script resides in. Returns NULL if this is not a file
- * script.
- */
-const char *sieve_file_script_get_dirpath
-	(const struct sieve_script *script);
-
-/* Return full path to file script. Returns NULL if this is not a file script.
- */
-const char *sieve_file_script_get_path
-	(const struct sieve_script *script);
-
-#endif /* __SIEVE_SCRIPT_FILE_H */
diff --git a/src/lib-sieve/sieve-script-private.h b/src/lib-sieve/sieve-script-private.h
index 3d80424e0f991a4b56c335e0fe5535e84d192012..0697511bc9763bb73108bead8cc65f57d7f6df8e 100644
--- a/src/lib-sieve/sieve-script-private.h
+++ b/src/lib-sieve/sieve-script-private.h
@@ -4,6 +4,7 @@
 #ifndef __SIEVE_SCRIPT_PRIVATE_H
 #define __SIEVE_SCRIPT_PRIVATE_H
 
+#include "sieve-common.h"
 #include "sieve-script.h"
 
 /*
@@ -11,17 +12,16 @@
  */
 
 struct sieve_script_vfuncs {
-	struct sieve_script *(*alloc)(void);
 	void (*destroy)(struct sieve_script *script);
 
 	int (*open)
-		(struct sieve_script *script, const char *data,
-			const char *const *options, enum sieve_error *error_r);
+		(struct sieve_script *script, enum sieve_error *error_r);
 
 	int (*get_stream)
 		(struct sieve_script *script, struct istream **stream_r,
 			enum sieve_error *error_r);
 	
+	/* binary */
 	int (*binary_read_metadata)
 		(struct sieve_script *_script, struct sieve_binary_block *sblock,
 			sieve_size_t *offset);
@@ -35,9 +35,18 @@ struct sieve_script_vfuncs {
 	const char *(*binary_get_prefix)
 		(struct sieve_script *script);
 
+	/* management */
+	int (*rename)
+		(struct sieve_script *script, const char *newname);
+	int (*delete)(struct sieve_script *script);
+	int (*is_active)(struct sieve_script *script);
+	int (*activate)(struct sieve_script *script);
+
+	/* properties */
 	int (*get_size)
 		(const struct sieve_script *script, uoff_t *size_r);
 
+	/* matching */
 	bool (*equals)
 		(const struct sieve_script *script, const struct sieve_script *other);
 };
@@ -45,18 +54,14 @@ struct sieve_script_vfuncs {
 struct sieve_script {
 	pool_t pool;
 	unsigned int refcount;
+	struct sieve_storage *storage;
 
 	const char *driver_name;
 	const struct sieve_script *script_class;
 	struct sieve_script_vfuncs v;
 
-	struct sieve_instance *svinst;
-	struct sieve_error_handler *ehandler;
-
 	const char *name;
-	const char *data;
 	const char *location;
-	const char *bin_dir;
 
 	/* Stream */
 	struct istream *stream;
@@ -65,40 +70,49 @@ struct sieve_script {
 };
 
 void sieve_script_init
-	(struct sieve_script *script, struct sieve_instance *svinst,
-		const struct sieve_script *script_class, const char *data,
-		const char *name, struct sieve_error_handler *ehandler);
-
-int sieve_script_setup_bindir
-	(struct sieve_script *script, mode_t mode);
+(struct sieve_script *script, struct sieve_storage *storage,
+	const struct sieve_script *script_class, const char *location,
+	const char *name);
 
 /*
- * Built-in file script driver
+ * Built-in script drivers
  */
 
-#define SIEVE_FILE_SCRIPT_DRIVER_NAME "file"
-
-struct sieve_file_script {
-	struct sieve_script script;
-
-	struct stat st;
-	struct stat lnk_st;
-
-	const char *path;
-	const char *dirpath;
-	const char *filename;
-	const char *binpath;
-	const char *binprefix;
-};
-
 extern const struct sieve_script sieve_file_script;
+extern const struct sieve_script sieve_dict_script;
+extern const struct sieve_script sieve_ldap_script;
 
 /*
- * Built-in dict script driver
+ * Error handling
  */
 
-#define SIEVE_DICT_SCRIPT_DRIVER_NAME "dict"
+void sieve_script_set_error
+	(struct sieve_script *script, enum sieve_error error,
+		const char *fmt, ...) ATTR_FORMAT(3, 4);
+void sieve_script_set_internal_error
+	(struct sieve_script *script);
+void sieve_script_set_critical
+	(struct sieve_script *script, const char *fmt, ...)
+		ATTR_FORMAT(2, 3);
+
+void sieve_script_sys_error
+	(struct sieve_script *script, const char *fmt, ...)
+		ATTR_FORMAT(2, 3);
+void sieve_script_sys_warning
+	(struct sieve_script *script, const char *fmt, ...)
+		ATTR_FORMAT(2, 3);
+void sieve_script_sys_info
+	(struct sieve_script *script, const char *fmt, ...)
+		ATTR_FORMAT(2, 3);
+void sieve_script_sys_debug
+	(struct sieve_script *script, const char *fmt, ...)
+		ATTR_FORMAT(2, 3);
 
-extern const struct sieve_script sieve_dict_script;
+/*
+ * Script sequence
+ */
+
+void sieve_script_sequence_init
+(struct sieve_script_sequence *seq, struct sieve_storage *storage);
 
 #endif /* __SIEVE_SCRIPT_PRIVATE_H */
diff --git a/src/lib-sieve/sieve-script.c b/src/lib-sieve/sieve-script.c
index 9e56951e08c06b710eb9cefd843884a18030be79..142c35530bcc35cbf9e747676841e2fd0270830f 100644
--- a/src/lib-sieve/sieve-script.c
+++ b/src/lib-sieve/sieve-script.c
@@ -5,10 +5,9 @@
 #include "compat.h"
 #include "unichar.h"
 #include "str.h"
+#include "str-sanitize.h"
 #include "hash.h"
 #include "array.h"
-#include "home-expand.h"
-#include "mkdir-parents.h"
 #include "eacces-error.h"
 #include "istream.h"
 
@@ -18,8 +17,10 @@
 #include "sieve-error.h"
 #include "sieve-binary.h"
 
+#include "sieve-storage-private.h"
 #include "sieve-script-private.h"
-#include "sieve-script-file.h"
+
+#include <ctype.h>
 
 /*
  * Script name
@@ -89,200 +90,103 @@ bool sieve_script_name_is_valid(const char *scriptname)
 }
 
 /*
- * Script object
+ * Script instance
  */
 
-static const char *split_next_arg(const char *const **_args)
+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 *const *args = *_args;
-	const char *str = args[0];
+	i_assert( storage != NULL );
 
-	/* join arguments for escaped ";" separator */
+	script->script_class = script_class;
+	script->refcount = 1;
+	script->storage = storage;
+	script->location = p_strdup_empty(script->pool, location);
+	script->name = p_strdup(script->pool, name);
 
-	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;
+	sieve_storage_ref(storage);
 }
 
-static bool sieve_script_location_parse
-(struct sieve_script *script, const char *data, const char **location_r,
-	const char *const **options_r, const char **error_r)
+struct sieve_script *sieve_script_create
+(struct sieve_instance *svinst, const char *location, const char *name,
+	enum sieve_error *error_r)
 {
-	ARRAY_TYPE(const_string) options;
-	const char *const *tmp;
-
-	if (*data == '\0') {
-		*options_r = NULL;
-		*location_r = data;
-		return TRUE;
-	}
+	struct sieve_storage *storage;
+	struct sieve_script *script;
+	enum sieve_error error;
 
-	/* <location> */
-	tmp = t_strsplit(data, ";");
-	*location_r = split_next_arg(&tmp);
-
-	if ( options_r != NULL ) {
-		t_array_init(&options, 8);
-
-		/* [<option> *(';' <option>)] */
-		while (*tmp != NULL) {
-			const char *option = split_next_arg(&tmp);
-
-			if ( strncasecmp(option, "name=", 5) == 0 ) {
-				if ( option[5] == '\0' ) {
-					*error_r = "empty name not allowed";
-					return FALSE;
-				}
-
-				if ( script->name == NULL )
-					script->name = p_strdup(script->pool, option+5);
-
-			} else if ( strncasecmp(option, "bindir=", 7) == 0 ) {
-				const char *bin_dir = option+7;
-
-				if ( bin_dir[0] == '\0' ) {
-					*error_r = "empty bindir not allowed";
-					return FALSE;
-				}
-
-				if ( bin_dir[0] == '~' ) {
-					/* home-relative path. change to absolute. */
-					const char *home = sieve_environment_get_homedir(script->svinst);
-
-					if ( home != NULL ) {
-						bin_dir = home_expand_tilde(bin_dir, home);
-					} else if ( bin_dir[1] == '/' || bin_dir[1] == '\0' ) {
-						*error_r = "bindir is relative to home directory (~/), "
-							"but home directory cannot be determined";
-						return FALSE;
-					}
-				}
-
-				script->bin_dir = p_strdup(script->pool, bin_dir);
-			} else {
-				array_append(&options, &option, 1);
-			}
-		}
+	if ( error_r != NULL )
+		*error_r = SIEVE_ERROR_NONE;
+	else
+		error_r = &error;
 
-		(void)array_append_space(&options);
-		*options_r = array_idx(&options, 0);
-	}
+	storage = sieve_storage_create(svinst, location, 0, error_r);
+	if ( storage == NULL )
+		return NULL;
 
-	return TRUE;
+	script = sieve_storage_get_script(storage, name, error_r);
+	
+	sieve_storage_unref(&storage);
+	return script;
 }
 
-void sieve_script_init
-(struct sieve_script *script, struct sieve_instance *svinst,
-	const struct sieve_script *script_class, const char *data,
-	const char *name, struct sieve_error_handler *ehandler)
+void sieve_script_ref(struct sieve_script *script)
 {
-	script->script_class = script_class;
-	script->refcount = 1;
-	script->svinst = svinst;
-	script->ehandler = ehandler;
-	script->data = p_strdup_empty(script->pool, data);
-	script->name = p_strdup_empty(script->pool, name);
-
-	sieve_error_handler_ref(ehandler);
+	script->refcount++;
 }
 
-struct sieve_script *sieve_script_create
-(struct sieve_instance *svinst, const char *location, const char *name,
-	struct sieve_error_handler *ehandler, enum sieve_error *error_r)
+void sieve_script_unref(struct sieve_script **_script)
 {
-	struct sieve_script *script;
-	const struct sieve_script *script_class;
-	const char *data, *p;
-
-	p = strchr(location, ':');
-	if ( p == NULL ) {
-		/* Default script driver is "file" (meaning that location is a fs path) */
-		data = location;
-		script_class = &sieve_file_script;
-	} else {
-		/* Lookup script driver */
-		T_BEGIN {
-			const char *driver;
-
-			data = p+1;
-			driver = t_strdup_until(location, p);
-
-			/* FIXME
-			script_class = sieve_script_class_lookup(driver);*/
-			if ( strcasecmp(driver, SIEVE_FILE_SCRIPT_DRIVER_NAME) == 0 )
-				script_class = &sieve_file_script;
-			else if ( strcasecmp(driver, SIEVE_DICT_SCRIPT_DRIVER_NAME) == 0 )
-				script_class = &sieve_dict_script;
-			else
-				script_class = NULL;
-
-			if ( script_class == NULL ) {
-				sieve_sys_error(svinst,
-					"Unknown sieve script driver module: %s", driver);
-			}
-		} T_END;
-	}
+	struct sieve_script *script = *_script;
 
-	if ( script_class == NULL ) {
-		if ( error_r != NULL )
-			*error_r = SIEVE_ERROR_TEMP_FAILURE;
-		return NULL;
-	}
+	i_assert( script->refcount > 0 );
 
-	script = script_class->v.alloc();
-	sieve_script_init(script, svinst, script_class, data, name, ehandler);
-	script->location = p_strdup(script->pool, location);
-	return script;
+	if ( --script->refcount != 0 )
+		return;
+
+	if ( script->stream != NULL )
+		i_stream_unref(&script->stream);
+
+	if ( script->v.destroy != NULL )
+		script->v.destroy(script);
+
+	sieve_storage_unref(&script->storage);
+	pool_unref(&script->pool);
+	*_script = NULL;
 }
 
 int sieve_script_open
 (struct sieve_script *script, enum sieve_error *error_r)
 {
-	struct sieve_instance *svinst = script->svinst;
-	struct sieve_error_handler *ehandler = script->ehandler;
 	enum sieve_error error;
-	const char *const *options = NULL;
-	const char *location = NULL, *parse_error = NULL;
 
 	if ( error_r != NULL )
 		*error_r = SIEVE_ERROR_NONE;
+	else
+		error_r = &error;
 
 	if ( script->open )
 		return 0;
 
-	if ( !sieve_script_location_parse
-		(script, script->data, &location, &options, &parse_error) ) {
-		sieve_critical(svinst, ehandler, NULL,
-			"failed to access sieve script", "failed to parse script location: %s",
-			parse_error);
-		if ( error_r != NULL )
-			*error_r = SIEVE_ERROR_TEMP_FAILURE;
-		return -1;
-	}
-
 	script->location = NULL;
-	if ( script->v.open(script, location, options, &error) < 0 ) {
-		if ( error_r == NULL ) {
-			if ( error == SIEVE_ERROR_NOT_FOUND )
-				sieve_error(ehandler, script->name, "sieve script does not exist");
-		} else {
-			*error_r = error;
-		}
+	if ( script->v.open(script, error_r) < 0 )
 		return -1;
-	}
 
 	i_assert( script->location != NULL );
 	i_assert( script->name != NULL );
 	script->open = TRUE;
+
+	if ( *script->name != '\0' ) {
+		sieve_script_sys_debug(script,
+			"Opened script `%s' from `%s'",
+			script->name, script->location);
+	} else {
+		sieve_script_sys_debug(script,
+			"Opened nameless script from `%s'",
+			script->location);
+	}
 	return 0;
 }
 
@@ -299,11 +203,11 @@ int sieve_script_open_as
 
 struct sieve_script *sieve_script_create_open
 (struct sieve_instance *svinst, const char *location, const char *name,
-	struct sieve_error_handler *ehandler, enum sieve_error *error_r)
+	enum sieve_error *error_r)
 {
 	struct sieve_script *script;
 
-	script = sieve_script_create(svinst, location, name, ehandler, error_r);
+	script = sieve_script_create(svinst, location, name, error_r);
 	if ( script == NULL )
 		return NULL;
 
@@ -315,51 +219,6 @@ struct sieve_script *sieve_script_create_open
 	return script;
 }
 
-struct sieve_script *sieve_script_create_open_as
-(struct sieve_instance *svinst, const char *location, const char *name,
-	struct sieve_error_handler *ehandler, enum sieve_error *error_r)
-{
-	struct sieve_script *script;
-
-	script = sieve_script_create(svinst, location, name, ehandler, error_r);
-	if ( script == NULL )
-		return NULL;
-
-	if ( sieve_script_open_as(script, name, error_r) < 0 ) {
-		sieve_script_unref(&script);
-		return NULL;
-	}
-	
-	return script;
-}
-
-void sieve_script_ref(struct sieve_script *script)
-{
-	script->refcount++;
-}
-
-void sieve_script_unref(struct sieve_script **_script)
-{
-	struct sieve_script *script = *_script;
-
-	i_assert(script->refcount > 0);
-
-	if (--script->refcount != 0)
-		return;
-
-	if ( script->stream != NULL )
-		i_stream_unref(&script->stream);
-
-	if ( script->ehandler != NULL )
-		sieve_error_handler_unref(&script->ehandler);
-
-	if ( script->v.destroy != NULL )
-		script->v.destroy(script);
-
-	pool_unref(&script->pool);
-	*_script = NULL;
-}
-
 /*
  * Properties
  */
@@ -376,7 +235,7 @@ const char *sieve_script_location(const struct sieve_script *script)
 
 struct sieve_instance *sieve_script_svinst(const struct sieve_script *script)
 {
-	return script->svinst;
+	return script->storage->svinst;
 }
 
 int sieve_script_get_size(struct sieve_script *script, uoff_t *size_r)
@@ -415,27 +274,23 @@ int sieve_script_get_stream
 
 	if ( error_r != NULL )
 		*error_r = SIEVE_ERROR_NONE;
+	else
+		error_r = &error;
 
 	if ( script->stream != NULL ) {
 		*stream_r = script->stream;
 		return 0;
 	}
 
+	// FIXME: necessary?
+	i_assert( script->open );
+
 	T_BEGIN {
-		ret = script->v.get_stream(script, &script->stream, &error);
+		ret = script->v.get_stream(script, &script->stream, error_r);
 	} T_END;
 
-	if ( ret < 0 ) {
-		if ( error_r == NULL ) {
-			if ( error == SIEVE_ERROR_NOT_FOUND ) {
-				sieve_error(script->ehandler, script->name,
-					"sieve script does not exist");
-			}
-		} else {
-			*error_r = error;
-		}
+	if ( ret < 0 )
 		return -1;
-	}
 
 	*stream_r = script->stream;
 	return 0;
@@ -481,23 +336,36 @@ int sieve_script_binary_read_metadata
 (struct sieve_script *script, struct sieve_binary_block *sblock,
 	sieve_size_t *offset)
 {
-	struct sieve_instance *svinst = script->svinst;
 	struct sieve_binary *sbin = sieve_binary_block_get_binary(sblock);
-	string_t *script_class;
+	string_t *storage_class, *location;
 
 	if ( sieve_binary_block_get_size(sblock) - *offset == 0 )
 		return 0;
 
-	if ( !sieve_binary_read_string(sblock, offset, &script_class) ) {
-		sieve_sys_error(svinst,
-			"sieve script: binary %s has invalid metadata for script %s",
+	/* storage class */
+	if ( !sieve_binary_read_string(sblock, offset, &storage_class) ) {
+		sieve_script_sys_error(script,
+			"Binary %s has invalid metadata for script %s: "
+			"Invalid storage class",
 			sieve_binary_path(sbin), sieve_script_location(script));
 		return -1;
 	}
-
-	if ( strcmp(str_c(script_class), script->driver_name) != 0 )
+	if ( strcmp(str_c(storage_class), script->driver_name) != 0 )
 		return 0;
 
+	/* location */
+	if ( !sieve_binary_read_string(sblock, offset, &location) ) {
+		sieve_script_sys_error(script,
+			"Binary %s has invalid metadata for script %s: "
+			"Invalid location",
+			sieve_binary_path(sbin), sieve_script_location(script));
+		return -1;
+	}
+	if ( script->location == NULL )
+		return 0;
+	if ( strcmp(str_c(location), script->location) != 0 )
+		return 0;
+	
 	if ( script->v.binary_read_metadata == NULL )
 		return 1;
 
@@ -508,6 +376,8 @@ void sieve_script_binary_write_metadata
 (struct sieve_script *script, struct sieve_binary_block *sblock)
 {
 	sieve_binary_emit_cstring(sblock, script->driver_name);
+	sieve_binary_emit_cstring(sblock,
+		( script->location == NULL ? "" : script->location ));
 
 	if ( script->v.binary_write_metadata == NULL )
 		return;
@@ -532,6 +402,7 @@ int sieve_script_binary_save
 {
 	struct sieve_script *bin_script = sieve_binary_script(sbin);
 
+
 	i_assert(bin_script == NULL || sieve_script_equals(bin_script, script));
 
 	if ( script->v.binary_save == NULL ) {
@@ -545,9 +416,11 @@ int sieve_script_binary_save
 const char *sieve_script_binary_get_prefix
 (struct sieve_script *script)
 {
-	if ( script->bin_dir != NULL &&
-		sieve_script_setup_bindir(script, 0700) >= 0 ) {
-		return t_strconcat(script->bin_dir, "/", script->name, NULL);
+	struct sieve_storage *storage = script->storage;
+
+	if ( storage->bin_dir != NULL &&
+		sieve_storage_setup_bindir(storage, 0700) >= 0 ) {
+		return t_strconcat(storage->bin_dir, "/", script->name, NULL);
 	}
 
 	if ( script->v.binary_get_prefix == NULL )
@@ -556,54 +429,263 @@ const char *sieve_script_binary_get_prefix
 	return script->v.binary_get_prefix(script);
 }
 
-int sieve_script_setup_bindir
-(struct sieve_script *script, mode_t mode)
-{
-	struct sieve_instance *svinst = script->svinst;
-	struct stat st;
+/* 
+ * Management
+ */
 
-	if ( script->bin_dir == NULL )
-		return -1;
+int sieve_script_rename
+(struct sieve_script *script, const char *newname)
+{
+	struct sieve_storage *storage = script->storage;
+	const char *oldname = script->name;
+	int ret;
 
-	if ( stat(script->bin_dir, &st) == 0 )
-		return 0;
+	i_assert( newname != NULL );
 
-	if ( errno == EACCES ) {
-		sieve_sys_error(svinst, "sieve script: "
-			"failed to setup directory for binaries: %s",
-			eacces_error_get("stat", script->bin_dir));
-		return -1;
-	} else if ( errno != ENOENT ) {
-		sieve_sys_error(svinst, "sieve script: "
-			"failed to setup directory for binaries: stat(%s) failed: %m",
-			script->bin_dir);
+	/* Check script name */
+	if ( !sieve_script_name_is_valid(newname) ) {
+		sieve_script_set_error(script,
+			SIEVE_ERROR_BAD_PARAMS,
+			"Invalid new Sieve script name `%s'.",
+			str_sanitize(newname, 80));
 		return -1;
 	}
 
-	if ( mkdir_parents(script->bin_dir, mode) == 0 ) {
-		if ( svinst->debug )
-			sieve_sys_debug(svinst, "sieve script: "
-				"created directory for binaries: %s", script->bin_dir);
-		return 1;
+	i_assert( (script->storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 );
+	i_assert( script->open ); // FIXME: auto-open?
+
+	i_assert( script->v.rename != NULL );
+	ret = script->v.rename(script, newname);
+
+	/* rename INBOX mailbox attribute */
+	if ( ret >= 0 && oldname != NULL )
+		sieve_storage_sync_script_rename(storage, oldname, newname);
+
+	return ret;
+}
+
+int sieve_script_delete(struct sieve_script **_script)
+{
+	struct sieve_script *script = *_script;
+	struct sieve_storage *storage = script->storage;
+	int ret = 0;
+
+	i_assert( (script->storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 );
+	i_assert( script->open ); // FIXME: auto-open?
+
+	/* Is the requested script active? */
+	if ( sieve_script_is_active(script) ) {
+		sieve_script_set_error(script, SIEVE_ERROR_ACTIVE,
+			"Cannot delete the active Sieve script.");
+		ret = -1;
+	} else {
+		i_assert( script->v.delete != NULL );
+		ret = script->v.delete(script);
 	}
 
-	switch ( errno ) {
-	case EEXIST:
+	/* unset INBOX mailbox attribute */
+	if ( ret >= 0 )
+		sieve_storage_sync_script_delete(storage, script->name);
+
+	/* Always deinitialize the script object */
+	sieve_script_unref(_script);
+	return ret;
+}
+
+int sieve_script_is_active(struct sieve_script *script)
+{
+	if ( script->v.is_active == NULL )
 		return 0;
-	case ENOENT:
-		sieve_sys_error(svinst, "sieve script: "
-			"directory for binaries was deleted while it was being created");
-		break;
-	case EACCES:
-		sieve_sys_error(svinst, "sieve script: %s",
-			eacces_error_get_creating("mkdir_parents_chgrp", script->bin_dir));
-		break;
-	default:
-		sieve_sys_error(svinst, "sieve script: "
-			"mkdir_parents_chgrp(%s) failed: %m", script->bin_dir);
-		break;
+	return script->v.is_active(script);
+}
+
+int sieve_script_activate(struct sieve_script *script, time_t mtime)
+{
+	int ret;
+
+	i_assert( (script->storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 );
+	i_assert( script->open ); // FIXME: auto-open?
+
+	i_assert( script->v.activate != NULL );
+	ret = script->v.activate(script);
+
+	sieve_storage_set_modified(script->storage, mtime);
+
+	return ret;
+}
+
+/*
+ * Error handling
+ */
+
+void sieve_script_set_error
+(struct sieve_script *script, enum sieve_error error,
+	const char *fmt, ...)
+{
+	struct sieve_storage *storage = script->storage;
+	va_list va;
+
+	sieve_storage_clear_error(storage);
+
+	if (fmt != NULL) {
+		va_start(va, fmt);
+		storage->error = i_strdup_vprintf(fmt, va);
+		va_end(va);
+	}
+	storage->error_code = error;
+}
+
+void sieve_script_set_internal_error
+(struct sieve_script *script)
+{
+	sieve_storage_set_internal_error(script->storage);
+}
+
+void sieve_script_set_critical
+(struct sieve_script *script, const char *fmt, ...)
+{
+	struct sieve_storage *storage = script->storage;
+
+	va_list va;
+
+	sieve_storage_clear_error(storage);
+	if (fmt != NULL) {
+		if ( (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 ) {
+			va_start(va, fmt);
+			sieve_sys_error(storage->svinst, "%s script: %s",
+				storage->driver_name, t_strdup_vprintf(fmt, va));
+			va_end(va);
+
+			sieve_storage_set_internal_error(storage);
+		} else {
+			/* no user is involved while synchronizing, so do it the
+			   normal way */
+			va_start(va, fmt);
+			storage->error = i_strdup_vprintf(fmt, va);
+			va_end(va);
+
+			storage->error_code = SIEVE_ERROR_TEMP_FAILURE;
+		}
 	}
+}
+
+const char *sieve_script_get_last_error
+(struct sieve_script *script, enum sieve_error *error_r)
+{
+	return sieve_storage_get_last_error(script->storage, error_r);
+}
+
+const char *sieve_script_get_last_error_lcase
+(struct sieve_script *script)
+{
+	char *errormsg = t_strdup_noconst(script->storage->error);
+	errormsg[0] = i_tolower(errormsg[0]);		
+	return errormsg;
+}
+
+void sieve_script_sys_error
+(struct sieve_script *script, const char *fmt, ...)
+{
+	struct sieve_instance *svinst = script->storage->svinst;
+	va_list va;
+
+	va_start(va, fmt);
+	sieve_sys_error(svinst, "%s script: %s",
+		script->driver_name, t_strdup_vprintf(fmt, va));
+	va_end(va);	
+}
+
+void sieve_script_sys_warning
+(struct sieve_script *script, const char *fmt, ...)
+{
+	struct sieve_instance *svinst = script->storage->svinst;
+	va_list va;
+
+	va_start(va, fmt);
+	sieve_sys_warning(svinst, "%s script: %s",
+		script->driver_name, t_strdup_vprintf(fmt, va));
+	va_end(va);	
+}
+
+void sieve_script_sys_info
+(struct sieve_script *script, const char *fmt, ...)
+{
+	struct sieve_instance *svinst = script->storage->svinst;
+	va_list va;
+
+	va_start(va, fmt);
+	sieve_sys_info(svinst, "%s script: %s",
+		script->driver_name, t_strdup_vprintf(fmt, va));
+	va_end(va);	
+}
+
+void sieve_script_sys_debug
+(struct sieve_script *script, const char *fmt, ...)
+{
+	struct sieve_instance *svinst = script->storage->svinst;
+	va_list va;
+
+	if (!svinst->debug)
+		return;
+
+	va_start(va, fmt);
+	sieve_sys_debug(svinst, "%s script: %s",
+		script->driver_name, t_strdup_vprintf(fmt, va));
+	va_end(va);	
+}
+
+/*
+ * Script sequence
+ */
+
+void sieve_script_sequence_init
+(struct sieve_script_sequence *seq, struct sieve_storage *storage)
+{
+	seq->storage = storage;
+	sieve_storage_ref(storage);
+}
+
+struct sieve_script_sequence *sieve_script_sequence_create
+(struct sieve_instance *svinst, const char *location,
+	enum sieve_error *error_r)
+{
+	struct sieve_storage *storage;
+	struct sieve_script_sequence *seq;
+	enum sieve_error error;
+
+	if ( error_r != NULL )
+		*error_r = SIEVE_ERROR_NONE;
+	else
+		error_r = &error;
+
+	storage = sieve_storage_create(svinst, location, 0, error_r);
+	if ( storage == NULL )
+		return NULL;
+
+	seq = sieve_storage_get_script_sequence(storage, error_r);
+	
+	sieve_storage_unref(&storage);
+	return seq;
+}
+
+struct sieve_script *sieve_script_sequence_next
+(struct sieve_script_sequence *seq, enum sieve_error *error_r)
+{
+	struct sieve_storage *storage = seq->storage;
+
+	i_assert( storage->v.script_sequence_next != NULL );
+	return storage->v.script_sequence_next(seq, error_r);	
+}
+
+void sieve_script_sequence_free(struct sieve_script_sequence **_seq)
+{
+	struct sieve_script_sequence *seq = *_seq;
+	struct sieve_storage *storage = seq->storage;
+
+	if ( storage->v.script_sequence_destroy != NULL )
+		storage->v.script_sequence_destroy(seq);
 
-	return -1;
+	sieve_storage_unref(&storage);
+	*_seq = NULL;
 }
 
diff --git a/src/lib-sieve/sieve-script.h b/src/lib-sieve/sieve-script.h
index b5cbc5482039ff154d1ded1cdb118b549b61b42f..ecf05558b6a4b321cd26ba56853a42b6f4657a34 100644
--- a/src/lib-sieve/sieve-script.h
+++ b/src/lib-sieve/sieve-script.h
@@ -16,44 +16,50 @@
 bool sieve_script_name_is_valid(const char *scriptname);
 
 /*
- * Sieve script object
+ * Sieve script file
+ */
+
+bool sieve_script_file_has_extension(const char *filename);
+
+/*
+ * Sieve script class
+ */
+
+void sieve_script_class_register
+	(struct sieve_instance *svinst, const struct sieve_script *script_class);
+void sieve_script_class_unregister
+	(struct sieve_instance *svinst, const struct sieve_script *script_class);	
+
+/*
+ * Sieve script instance
  */
 
 struct sieve_script;
 
-ARRAY_DEFINE_TYPE(sieve_scripts, struct sieve_script *);
+ARRAY_DEFINE_TYPE(sieve_script, struct sieve_script *);
 
 struct sieve_script *sieve_script_create
 	(struct sieve_instance *svinst, const char *location, const char *name,
-		struct sieve_error_handler *ehandler, enum sieve_error *error_r);
+		enum sieve_error *error_r)
+		ATTR_NULL(3,4);
 
 void sieve_script_ref(struct sieve_script *script);
 void sieve_script_unref(struct sieve_script **script);
 
 int sieve_script_open
-	(struct sieve_script *script, enum sieve_error *error_r);
+	(struct sieve_script *script, enum sieve_error *error_r)
+		ATTR_NULL(2);
 int sieve_script_open_as
-	(struct sieve_script *script, const char *name, enum sieve_error *error_r);
+	(struct sieve_script *script, const char *name, enum sieve_error *error_r)
+		ATTR_NULL(3);
 
 struct sieve_script *sieve_script_create_open
 	(struct sieve_instance *svinst, const char *location, const char *name,
-		struct sieve_error_handler *ehandler, enum sieve_error *error_r);
-struct sieve_script *sieve_script_create_open_as
-	(struct sieve_instance *svinst, const char *location, const char *name,
-		struct sieve_error_handler *ehandler, enum sieve_error *error_r);
+		enum sieve_error *error_r)
+		ATTR_NULL(3,4);
 
 /*
- * Accessors
- */
-
-const char *sieve_script_name(const struct sieve_script *script);
-const char *sieve_script_location(const struct sieve_script *script);
-struct sieve_instance *sieve_script_svinst(const struct sieve_script *script);
-
-bool sieve_script_is_open(const struct sieve_script *script);
-
-/*
- * Saving/loading Sieve binaries
+ * Binary
  */
 
 int sieve_script_binary_read_metadata
@@ -77,8 +83,39 @@ const char *sieve_script_binary_get_prefix
 
 int sieve_script_get_stream
 	(struct sieve_script *script, struct istream **stream_r,
-		enum sieve_error *error_r);
+		enum sieve_error *error_r) ATTR_NULL(3);
+
+/*
+ * Management
+ */
+
+// FIXME: check read/write flag!
+
+int sieve_script_rename
+	(struct sieve_script *script, const char *newname);
+int sieve_script_is_active(struct sieve_script *script);
+int sieve_script_activate(struct sieve_script *script, time_t mtime);
+int sieve_script_delete(struct sieve_script **script);
+
+/*
+ * Properties
+ */
+
+const char *sieve_script_name
+	(const struct sieve_script *script) ATTR_PURE;
+const char *sieve_script_location
+	(const struct sieve_script *script) ATTR_PURE;
+struct sieve_instance *sieve_script_svinst
+	(const struct sieve_script *script) ATTR_PURE;
+
 int sieve_script_get_size(struct sieve_script *script, uoff_t *size_r);
+bool sieve_script_is_open
+	(const struct sieve_script *script) ATTR_PURE;
+
+const char *sieve_file_script_get_dirpath
+	(const struct sieve_script *script) ATTR_PURE;
+const char *sieve_file_script_get_path
+	(const struct sieve_script *script) ATTR_PURE;
 
 /*
  * Comparison
@@ -94,5 +131,27 @@ static inline int sieve_script_cmp
 	return ( sieve_script_equals(script, other) ? 0 : -1 );
 }
 
+/*
+ * Error handling
+ */
+
+const char *sieve_script_get_last_error
+	(struct sieve_script *script, enum sieve_error *error_r)
+	ATTR_NULL(2);
+const char *sieve_script_get_last_error_lcase
+	(struct sieve_script *script);
+
+/*
+ * Script sequence
+ */
+
+struct sieve_script_sequence;
+
+struct sieve_script_sequence *sieve_script_sequence_create
+(struct sieve_instance *svinst, const char *location,
+	enum sieve_error *error_r);
+struct sieve_script *sieve_script_sequence_next
+	(struct sieve_script_sequence *seq, enum sieve_error *error_r);
+void sieve_script_sequence_free(struct sieve_script_sequence **_seq);
 
 #endif /* __SIEVE_SCRIPT_H */
diff --git a/src/lib-sieve/sieve-storage-private.h b/src/lib-sieve/sieve-storage-private.h
new file mode 100644
index 0000000000000000000000000000000000000000..16352843368204f260eb4bf2d766085b804fc4c9
--- /dev/null
+++ b/src/lib-sieve/sieve-storage-private.h
@@ -0,0 +1,244 @@
+/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
+ */
+
+#ifndef __SIEVE_STORAGE_PRIVATE_H
+#define __SIEVE_STORAGE_PRIVATE_H
+
+#include "sieve.h"
+#include "sieve-error-private.h"
+
+#include "sieve-storage.h"
+
+#define MAILBOX_ATTRIBUTE_PREFIX_SIEVE \
+	MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER"sieve/"
+#define MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES \
+	MAILBOX_ATTRIBUTE_PREFIX_SIEVE"files/"
+#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT \
+	MAILBOX_ATTRIBUTE_PREFIX_SIEVE"default"
+
+#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_LINK 'L'
+#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_SCRIPT 'S'
+
+struct sieve_storage;
+
+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,
+			enum sieve_error *error_r);
+
+	int (*get_last_change)
+		(struct sieve_storage *storage, time_t *last_change_r);
+	void (*set_modified)
+		(struct sieve_storage *storage, time_t mtime);
+
+	int (*is_singular)(struct sieve_storage *storage);
+
+	/* script access */
+	struct sieve_script *(*get_script)
+		(struct sieve_storage *storage, const char *name);
+
+	/* script sequence */
+	struct sieve_script_sequence *(*get_script_sequence)
+		(struct sieve_storage *storage, enum sieve_error *error_r);
+	struct sieve_script *(*script_sequence_next)
+		(struct sieve_script_sequence *seq, enum sieve_error *error_r);
+	void (*script_sequence_destroy)(struct sieve_script_sequence *seq);
+
+	/* active script */
+	int (*active_script_get_name)
+		(struct sieve_storage *storage, const char **name_r);
+	struct sieve_script *(*active_script_open)
+		(struct sieve_storage *storage);
+	int (*deactivate)
+		(struct sieve_storage *storage);
+	int (*active_script_get_last_change)
+		(struct sieve_storage *storage, time_t *last_change_r);
+
+	/* listing scripts */
+	struct sieve_storage_list_context *(*list_init)
+		(struct sieve_storage *storage);
+	const char *(*list_next)
+		(struct sieve_storage_list_context *lctx, bool *active_r);
+	int (*list_deinit)
+		(struct sieve_storage_list_context *lctx);
+
+	/* saving scripts */
+	// FIXME: simplify this API; reduce this mostly to a single save function
+	struct sieve_storage_save_context *(*save_init)
+		(struct sieve_storage *storage, const char *scriptname,
+			struct istream *input);
+	int (*save_continue)(struct sieve_storage_save_context *sctx);
+	int (*save_finish)(struct sieve_storage_save_context *sctx);
+	struct sieve_script *(*save_get_tempscript)
+		(struct sieve_storage_save_context *sctx);
+	void (*save_cancel)(struct sieve_storage_save_context *sctx);
+	int (*save_commit)(struct sieve_storage_save_context *sctx);
+	int (*save_as_active)
+		(struct sieve_storage *storage, struct istream *input,
+			time_t mtime);
+
+	/* checking quota */
+	int (*quota_havespace)
+		(struct sieve_storage *storage, const char *scriptname,
+			size_t size, enum sieve_storage_quota *quota_r, uint64_t *limit_r);
+};
+
+struct sieve_storage {
+	pool_t pool;
+	unsigned int refcount;
+	struct sieve_instance *svinst;
+
+	const char *driver_name;
+	const struct sieve_storage *storage_class;
+	struct sieve_storage_vfuncs v;
+
+	uint64_t max_scripts;
+	uint64_t max_storage;
+
+	char *error;
+	enum sieve_error error_code;
+
+	const char *data;
+	const char *location;
+	const char *script_name;
+	const char *bin_dir;
+
+	struct mailbox *sync_inbox;
+
+	enum sieve_storage_flags flags;
+
+	/* this is the main personal storage */
+	unsigned int main_storage:1;
+	unsigned int allows_synchronization:1;
+};
+
+struct sieve_storage *sieve_storage_alloc
+	(struct sieve_instance *svinst, 
+		const struct sieve_storage *storage_class, const char *data,
+		enum sieve_storage_flags flags, bool main);
+
+int sieve_storage_setup_bindir
+	(struct sieve_storage *storage, mode_t mode);
+
+/*
+ * Listing scripts
+ */
+
+struct sieve_storage_list_context {
+	struct sieve_storage *storage;
+
+	unsigned int seen_active:1; // Just present for assertions
+};
+
+/*
+ * Script sequence
+ */
+
+struct sieve_script_sequence {
+	struct sieve_storage *storage;
+};
+
+/*
+ * Saving scripts
+ */
+
+struct sieve_storage_save_context {
+	struct sieve_storage *storage;
+	const char *scriptname;
+	struct sieve_script *scriptobject;
+
+	struct istream *input;
+
+	time_t mtime;
+
+	unsigned int failed:1;
+	unsigned int finished:1;
+};
+
+/*
+ * Storage class
+ */
+
+struct sieve_storage_class_registry;
+
+void sieve_storages_init(struct sieve_instance *svinst);
+void sieve_storages_deinit(struct sieve_instance *svinst);
+
+void sieve_storage_class_register
+	(struct sieve_instance *svinst,
+		const struct sieve_storage *storage_class);
+void sieve_storage_class_unregister
+	(struct sieve_instance *svinst,
+		const struct sieve_storage *storage_class);
+const struct sieve_storage *sieve_storage_find_class
+	(struct sieve_instance *svinst, const char *name);
+
+/*
+ * Built-in storage drivers
+ */
+
+/* file */
+
+#define SIEVE_FILE_STORAGE_DRIVER_NAME "file"
+
+extern const struct sieve_storage sieve_file_storage;
+
+struct sieve_storage *sieve_file_storage_init_legacy
+	(struct sieve_instance *svinst, const char *active_path,
+		const char *storage_path, enum sieve_storage_flags flags,
+		enum sieve_error *error_r) ATTR_NULL(6);
+
+/* dict */
+
+#define SIEVE_DICT_STORAGE_DRIVER_NAME "dict"
+
+extern const struct sieve_storage sieve_dict_storage;
+
+/* ldap */
+
+#define SIEVE_LDAP_STORAGE_DRIVER_NAME "ldap"
+
+extern const struct sieve_storage sieve_ldap_storage;
+
+/*
+ * Error handling
+ */
+
+void sieve_storage_set_internal_error
+	(struct sieve_storage *storage);
+
+void sieve_storage_sys_error
+	(struct sieve_storage *storage, const char *fmt, ...)
+	ATTR_FORMAT(2, 3);
+void sieve_storage_sys_warning
+	(struct sieve_storage *storage, const char *fmt, ...)
+	ATTR_FORMAT(2, 3);
+void sieve_storage_sys_info
+	(struct sieve_storage *storage, const char *fmt, ...)
+	ATTR_FORMAT(2, 3);
+void sieve_storage_sys_debug
+	(struct sieve_storage *storage, const char *fmt, ...)
+	ATTR_FORMAT(2, 3);
+
+/*
+ * Synchronization
+ */
+
+int sieve_storage_sync_init
+	(struct sieve_storage *storage, struct mail_user *user);
+void sieve_storage_sync_deinit
+	(struct sieve_storage *storage);
+
+void sieve_storage_sync_script_save
+	(struct sieve_storage *storage, const char *name);
+void sieve_storage_sync_script_rename
+	(struct sieve_storage *storage, const char *oldname,
+		const char *newname);
+void sieve_storage_sync_script_delete
+	(struct sieve_storage *storage, const char *name);
+
+#endif
diff --git a/src/lib-sieve/sieve-storage-sync.c b/src/lib-sieve/sieve-storage-sync.c
new file mode 100644
index 0000000000000000000000000000000000000000..a8b089aa8d85979a82e5c88f4ca22b628119a167
--- /dev/null
+++ b/src/lib-sieve/sieve-storage-sync.c
@@ -0,0 +1,131 @@
+/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "array.h"
+#include "str-sanitize.h"
+#include "home-expand.h"
+#include "eacces-error.h"
+#include "mkdir-parents.h"
+#include "ioloop.h"
+#include "mail-storage-private.h"
+
+#include "sieve-common.h"
+#include "sieve-settings.h"
+#include "sieve-error-private.h"
+
+#include "sieve-script-private.h"
+#include "sieve-storage-private.h"
+
+/*
+ * Synchronization
+ */
+
+int sieve_storage_sync_init
+(struct sieve_storage *storage, struct mail_user *user)
+{
+	struct mail_namespace *ns;
+	struct mailbox *box;
+	enum mailbox_flags flags = MAILBOX_FLAG_IGNORE_ACLS;
+	enum mail_error error;
+		
+	if ( (flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 &&
+		(flags & SIEVE_STORAGE_FLAG_READWRITE) == 0 )
+		return 0;
+
+	if ( !storage->allows_synchronization ) {
+		if ( (flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) != 0 )
+			return -1;
+		return 0;
+	}
+
+
+	ns = mail_namespace_find_inbox(user->namespaces);
+	storage->sync_inbox = box = mailbox_alloc(ns->list, "INBOX", flags);
+	if (mailbox_open(box) == 0)
+		return 0;
+
+	sieve_storage_sys_warning(storage, "sync: "
+		"Failed to open user INBOX for attribute modifications: %s",
+		mailbox_get_last_error(box, &error));
+	return -1;
+}
+
+void sieve_storage_sync_deinit
+(struct sieve_storage *storage)
+{
+	if (storage->sync_inbox != NULL)
+		mailbox_free(&storage->sync_inbox);
+}
+
+/*
+ * Sync attributes
+ */
+
+static void sieve_storage_sync_transaction_finish
+(struct sieve_storage *storage, struct mailbox_transaction_context **t)
+{
+	struct mailbox *inbox = storage->sync_inbox;
+
+	if (mailbox_transaction_commit(t) < 0) {
+		enum mail_error error;
+		
+		sieve_storage_sys_warning(storage, "sync: "
+			"Failed to update INBOX attributes: %s",
+			mail_storage_get_last_error(mailbox_get_storage(inbox), &error));
+	}
+}
+
+void sieve_storage_sync_script_save
+(struct sieve_storage *storage, const char *name)
+{
+	struct mailbox_transaction_context *t;
+	const char *key;
+
+	if (storage->sync_inbox == NULL)
+		return;
+
+	key = t_strconcat
+		(MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, name, NULL);
+	t = mailbox_transaction_begin(storage->sync_inbox, 0);	
+	mail_index_attribute_set(t->itrans, TRUE, key, ioloop_time, 0);
+	sieve_storage_sync_transaction_finish(storage, &t);
+}
+
+void sieve_storage_sync_script_rename
+(struct sieve_storage *storage, const char *oldname,
+	const char *newname)
+{
+	struct mailbox_transaction_context *t;
+	const char *oldkey, *newkey;
+
+	if (storage->sync_inbox == NULL)
+		return;
+
+	oldkey = t_strconcat
+		(MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, oldname, NULL);
+	newkey = t_strconcat
+		(MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, newname, NULL);
+
+	t = mailbox_transaction_begin(storage->sync_inbox, 0);	
+	mail_index_attribute_unset(t->itrans, TRUE, oldkey, ioloop_time);
+	mail_index_attribute_set(t->itrans, TRUE, newkey, ioloop_time, 0);
+	sieve_storage_sync_transaction_finish(storage, &t);
+}
+
+void sieve_storage_sync_script_delete
+(struct sieve_storage *storage, const char *name)
+{
+	struct mailbox_transaction_context *t;
+	const char *key;
+
+	if (storage->sync_inbox == NULL)
+		return;
+
+	key = t_strconcat
+		(MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, name, NULL);
+	
+	t = mailbox_transaction_begin(storage->sync_inbox, 0);	
+	mail_index_attribute_unset(t->itrans, TRUE, key, ioloop_time);
+	sieve_storage_sync_transaction_finish(storage, &t);
+}
diff --git a/src/lib-sieve/sieve-storage.c b/src/lib-sieve/sieve-storage.c
new file mode 100644
index 0000000000000000000000000000000000000000..e9626d204c609cfb9499f60e86830090560039fa
--- /dev/null
+++ b/src/lib-sieve/sieve-storage.c
@@ -0,0 +1,1078 @@
+/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "array.h"
+#include "str-sanitize.h"
+#include "home-expand.h"
+#include "eacces-error.h"
+#include "mkdir-parents.h"
+#include "ioloop.h"
+
+#include "sieve-common.h"
+#include "sieve-settings.h"
+#include "sieve-error-private.h"
+
+#include "sieve-script-private.h"
+#include "sieve-storage-private.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <time.h>
+#include <utime.h>
+
+#define CRITICAL_MSG \
+  "Internal error occured. Refer to server log for more information."
+#define CRITICAL_MSG_STAMP CRITICAL_MSG " [%Y-%m-%d %H:%M:%S]"
+
+/*
+ * Storage class
+ */
+
+struct sieve_storage_class_registry {
+	ARRAY_TYPE(sieve_storage_class) storage_classes;
+};
+
+void sieve_storages_init(struct sieve_instance *svinst)
+{
+	svinst->storage_reg =
+		p_new(svinst->pool, struct sieve_storage_class_registry, 1);
+	p_array_init(&svinst->storage_reg->storage_classes, svinst->pool, 8);
+
+	sieve_storage_class_register(svinst, &sieve_file_storage);
+	sieve_storage_class_register(svinst, &sieve_dict_storage);
+}
+
+void sieve_storages_deinit(struct sieve_instance *svinst ATTR_UNUSED)
+{
+	/* nothing yet */
+}
+
+void sieve_storage_class_register
+(struct sieve_instance *svinst, const struct sieve_storage *storage_class)
+{
+	struct sieve_storage_class_registry *reg = svinst->storage_reg;
+	const struct sieve_storage *old_class;
+
+	old_class = sieve_storage_find_class
+		(svinst, storage_class->driver_name);
+	if (old_class != NULL) {
+		if (old_class->v.alloc == NULL) {
+			/* replacing a "support not compiled in" storage class */
+			sieve_storage_class_unregister(svinst, old_class);
+		} else {
+			i_panic("sieve_storage_class_register(%s): Already registered",
+				storage_class->driver_name);
+		}
+	}
+
+	array_append(&reg->storage_classes, &storage_class, 1);
+}
+
+void sieve_storage_class_unregister
+(struct sieve_instance *svinst, const struct sieve_storage *storage_class)
+{
+	struct sieve_storage_class_registry *reg = svinst->storage_reg;
+	const struct sieve_storage *const *classes;
+	unsigned int i, count;
+
+	classes = array_get(&reg->storage_classes, &count);
+	for ( i = 0; i < count; i++ ) {
+		if ( classes[i] == storage_class ) {
+			array_delete(&reg->storage_classes, i, 1);
+			break;
+		}
+	}
+}
+
+const struct sieve_storage *sieve_storage_find_class
+(struct sieve_instance *svinst, const char *name)
+{
+	struct sieve_storage_class_registry *reg = svinst->storage_reg;
+	const struct sieve_storage *const *classes;
+	unsigned int i, count;
+
+	i_assert(name != NULL);
+
+	classes = array_get(&reg->storage_classes, &count);
+	for ( i = 0; i < count; i++ ) {
+		if ( strcasecmp(classes[i]->driver_name, name) == 0 )
+			return classes[i];
+	}
+	return NULL;
+}
+
+/*
+ * Storage instance
+ */
+
+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_find_class(svinst, driver);
+		if ( storage_class == NULL ) {
+			sieve_sys_error(svinst,
+				"Unknown storage driver module `%s'",
+				driver);
+		} else if ( storage_class->v.alloc == NULL ) {
+			sieve_sys_error(svinst,
+				"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 bool sieve_storage_data_parse
+(struct sieve_storage *storage, const char *data, const char **location_r,
+	const char *const **options_r, const char **error_r)
+{
+	ARRAY_TYPE(const_string) options;
+	const char *const *tmp;
+
+	if (*data == '\0') {
+		*options_r = NULL;
+		*location_r = data;
+		return TRUE;
+	}
+
+	/* <location> */
+	tmp = t_strsplit(data, ";");
+	*location_r = split_next_arg(&tmp);
+
+	if ( options_r != NULL ) {
+		t_array_init(&options, 8);
+
+		/* [<option> *(';' <option>)] */
+		while (*tmp != NULL) {
+			const char *option = split_next_arg(&tmp);
+
+			if ( strncasecmp(option, "name=", 5) == 0 ) {
+				if ( option[5] == '\0' ) {
+					*error_r = "empty name not allowed";
+					return FALSE;
+				}
+
+				if ( storage->script_name == NULL )
+					storage->script_name = p_strdup(storage->pool, option+5);
+
+			} else if ( strncasecmp(option, "bindir=", 7) == 0 ) {
+				const char *bin_dir = option+7;
+
+				if ( bin_dir[0] == '\0' ) {
+					*error_r = "empty bindir not allowed";
+					return FALSE;
+				}
+
+				if ( bin_dir[0] == '~' ) {
+					/* home-relative path. change to absolute. */
+					const char *home = sieve_environment_get_homedir(storage->svinst);
+
+					if ( home != NULL ) {
+						bin_dir = home_expand_tilde(bin_dir, home);
+					} else if ( bin_dir[1] == '/' || bin_dir[1] == '\0' ) {
+						*error_r = "bindir is relative to home directory (~/), "
+							"but home directory cannot be determined";
+						return FALSE;
+					}
+				}
+
+				storage->bin_dir = p_strdup(storage->pool, bin_dir);
+			} else {
+				array_append(&options, &option, 1);
+			}
+		}
+
+		(void)array_append_space(&options);
+		*options_r = array_idx(&options, 0);
+	}
+
+	return TRUE;
+}
+
+struct sieve_storage *sieve_storage_alloc
+(struct sieve_instance *svinst, 
+	const struct sieve_storage *storage_class, const char *data,
+	enum sieve_storage_flags flags, bool main)
+{
+	struct sieve_storage *storage;
+
+	i_assert(storage_class->v.alloc != NULL);
+	storage = storage_class->v.alloc();
+
+	storage->storage_class = storage_class;
+	storage->refcount = 1;
+	storage->svinst = svinst;
+	storage->flags = flags;
+	storage->data = p_strdup_empty(storage->pool, data);
+	storage->main_storage = main;
+
+	return storage;
+}
+
+static struct sieve_storage *sieve_storage_init
+(struct sieve_instance *svinst, 
+	const struct sieve_storage *storage_class, const char *data,
+	enum sieve_storage_flags flags, bool main, enum sieve_error *error_r)
+{
+	struct sieve_storage *storage;
+	const char *const *options;
+	const char *location, *parse_error;
+	enum sieve_error error;
+
+	if ( error_r != NULL )
+		*error_r = SIEVE_ERROR_NONE;
+	else
+		error_r = &error;
+
+	i_assert( storage_class->v.init != NULL );	
+
+	if ((flags & SIEVE_STORAGE_FLAG_READWRITE) &&
+		storage_class->v.save_init == NULL) {
+		sieve_sys_error(svinst, "%s storage: "
+			"Storage does not support write access",
+			storage_class->driver_name);
+		*error_r = SIEVE_ERROR_TEMP_FAILURE;
+		return NULL;
+	}
+
+	T_BEGIN {	
+		storage = sieve_storage_alloc
+			(svinst, storage_class, data, flags, main);
+
+		if ( !sieve_storage_data_parse
+			(storage, data, &location, &options, &parse_error) ) {
+			sieve_sys_error(svinst, "%s storage: "
+				"Failed to parse storage location: %s",
+				storage_class->driver_name, parse_error);
+			*error_r = SIEVE_ERROR_TEMP_FAILURE;
+			sieve_storage_unref(&storage);
+			storage = NULL;
+		} else {
+			storage->location = p_strdup(storage->pool, location);
+
+			if ( storage_class->v.init
+					(storage, options, error_r) < 0 ) {
+				sieve_storage_unref(&storage);
+				storage = NULL;
+			}
+		}
+	} T_END;
+
+	return storage;
+}
+
+struct sieve_storage *sieve_storage_create
+(struct sieve_instance *svinst, const char *location,
+	enum sieve_storage_flags flags, enum sieve_error *error_r)
+{
+	const struct sieve_storage *storage_class;
+	enum sieve_error error;
+	const char *data;
+	int ret;
+
+	/* Dont use this function for creating a synchronizing storage */
+	i_assert( (flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0);
+
+	if ( error_r != NULL )
+		*error_r = SIEVE_ERROR_NONE;
+	else
+		error_r = &error;
+
+	data = location;
+	if ( (ret=sieve_storage_driver_parse
+		(svinst, &data, &storage_class)) < 0) {
+		*error_r = SIEVE_ERROR_TEMP_FAILURE;
+		return NULL;
+	}
+	
+	if ( ret == 0 )
+		storage_class = &sieve_file_storage;
+
+	return sieve_storage_init
+		(svinst, storage_class, data, flags, FALSE, error_r);
+}
+
+struct sieve_storage *sieve_storage_create_main
+(struct sieve_instance *svinst, struct mail_user *user,
+	enum sieve_storage_flags flags, enum sieve_error *error_r)
+{
+	bool debug = svinst->debug;
+	struct sieve_storage *storage = NULL;
+	const struct sieve_storage
+		*sieve_class = NULL,
+		*sieve_dir_class = NULL;
+	const char *set_sieve, *set_sieve_dir;
+	const char *data, *storage_path;
+	unsigned long long int uint_setting;
+	size_t size_setting;
+	enum sieve_error error;
+	int ret;	
+
+	if ( error_r != NULL )
+		*error_r = SIEVE_ERROR_NONE;
+	else
+		error_r = &error;
+
+	/* Sieve storage location */
+
+	set_sieve = sieve_setting_get(svinst, "sieve");
+
+	if ( set_sieve != NULL ) {
+		if ( *set_sieve == '\0' ) {
+			/* disabled */
+			if ( debug ) {
+				sieve_sys_debug(svinst,	"storage: "
+					"Sieve is disabled (sieve=\"\")");
+			}
+			*error_r = SIEVE_ERROR_NOT_FOUND;
+			return NULL;
+		}
+
+		data = set_sieve;
+		if ( (ret=sieve_storage_driver_parse
+			(svinst, &data, &sieve_class)) < 0 ) {
+			*error_r = SIEVE_ERROR_TEMP_FAILURE;
+			return NULL;
+		}
+
+		if ( ret > 0 ) {
+			/* The normal case: explicit driver name */
+			storage = sieve_storage_init
+				(svinst, sieve_class, data, flags, TRUE, error_r);
+			if ( storage == NULL )
+				return NULL;
+		}
+
+		/* No driver name */
+	}
+
+	if ( storage == NULL ) {
+		/* Script storage directory configuration (deprecated) */
+
+		set_sieve_dir = sieve_setting_get(svinst, "sieve_dir");
+		if ( set_sieve_dir == NULL )
+			set_sieve_dir = sieve_setting_get(svinst, "sieve_storage");
+	
+		if ( set_sieve_dir == NULL || *set_sieve_dir == '\0' ) {
+			storage_path = "";
+		} else {
+			const char *p;
+
+			/* Parse and check driver */
+			storage_path = set_sieve_dir;
+			if ( (ret=sieve_storage_driver_parse
+				(svinst, &storage_path, &sieve_dir_class)) < 0) {
+				*error_r = SIEVE_ERROR_TEMP_FAILURE;
+				return NULL;
+			}
+
+			if ( ret > 0 && sieve_dir_class != &sieve_file_storage ) {
+				sieve_sys_error(svinst, "storage: "
+					"Cannot use deprecated sieve_dir= setting "
+					"with `%s' driver for main Sieve storage",
+					sieve_dir_class->driver_name);
+			}
+
+			/* Ignore any options */
+			p = strchr(storage_path, ';');
+			if ( p != NULL )
+				storage_path = t_strdup_until(storage_path, p);
+		}
+
+		storage = sieve_file_storage_init_legacy
+			(svinst, data, storage_path, flags, error_r);
+	}
+
+	if ( storage == NULL )
+		return NULL;
+
+	(void)sieve_storage_sync_init(storage, user);
+
+	/* Get quota settings if storage driver provides none */
+
+	if ( storage->max_storage == 0 &&
+		sieve_setting_get_size_value
+			(svinst, "sieve_quota_max_storage", &size_setting) ) {
+		storage->max_storage = size_setting;
+	}
+
+	if ( storage->max_scripts == 0 &&
+		sieve_setting_get_uint_value
+			(svinst, "sieve_quota_max_scripts", &uint_setting) ) {
+		storage->max_scripts = uint_setting;
+	}
+
+	if ( debug ) {
+		if ( storage->max_storage > 0 ) {
+			sieve_storage_sys_debug(storage, "quota: "
+				"Storage limit: %llu bytes",
+				(unsigned long long int) storage->max_storage);
+		}
+		if ( storage->max_scripts > 0 ) {
+			sieve_storage_sys_debug(storage, "quota: "
+				"Script count limit: %llu scripts",
+				(unsigned long long int) storage->max_scripts);
+		}
+	}
+	return storage;
+}
+
+void sieve_storage_ref(struct sieve_storage *storage)
+{
+	storage->refcount++;
+}
+
+void sieve_storage_unref(struct sieve_storage **_storage)
+{
+	struct sieve_storage *storage = *_storage;
+
+	i_assert(storage->refcount > 0);
+
+	if (--storage->refcount != 0)
+		return;
+
+	(void)sieve_storage_sync_deinit(storage);
+
+	if ( storage->v.destroy != NULL )
+		storage->v.destroy(storage);
+
+	i_free(storage->error);
+	pool_unref(&storage->pool);
+	*_storage = NULL;
+}
+
+int sieve_storage_setup_bindir
+(struct sieve_storage *storage, mode_t mode)
+{
+	struct sieve_instance *svinst = storage->svinst;
+	const char *bin_dir = storage->bin_dir;
+	struct stat st;
+
+	if ( bin_dir == NULL )
+		return -1;
+
+	if ( stat(bin_dir, &st) == 0 )
+		return 0;
+
+	if ( errno == EACCES ) {
+		sieve_storage_sys_error(storage,
+			"Failed to setup directory for binaries: "
+			"%s",	eacces_error_get("stat", bin_dir));
+		return -1;
+	} else if ( errno != ENOENT ) {
+		sieve_storage_sys_error(storage,
+			"Failed to setup directory for binaries: "
+			"stat(%s) failed: %m",
+			bin_dir);
+		return -1;
+	}
+
+	if ( mkdir_parents(bin_dir, mode) == 0 ) {
+		if ( svinst->debug )
+			sieve_storage_sys_debug(storage,
+				"Created directory for binaries: %s", bin_dir);
+		return 1;
+	}
+
+	switch ( errno ) {
+	case EEXIST:
+		return 0;
+	case ENOENT:
+		sieve_storage_sys_error(storage,
+			"Directory for binaries was deleted while it was being created");
+		break;
+	case EACCES:
+		sieve_storage_sys_error(storage,
+			"%s",	eacces_error_get_creating("mkdir_parents_chgrp", bin_dir));
+		break;
+	default:
+		sieve_storage_sys_error(storage,
+			"mkdir_parents_chgrp(%s) failed: %m", bin_dir);
+		break;
+	}
+
+	return -1;
+}
+
+int sieve_storage_is_singular
+(struct sieve_storage *storage)
+{
+	if ( storage->v.is_singular == NULL )
+		return 1;
+	return storage->v.is_singular(storage);
+}
+
+int sieve_storage_get_last_change
+(struct sieve_storage *storage, time_t *last_change_r)
+{
+	i_assert(storage->v.get_last_change != NULL);
+	return storage->v.get_last_change(storage, last_change_r);
+}
+
+void sieve_storage_set_modified
+(struct sieve_storage *storage, time_t mtime)
+{
+	if (storage->v.set_modified == NULL)
+		return;
+
+	storage->v.set_modified(storage, mtime);
+}
+
+/*
+ * Script access
+ */
+
+struct sieve_script *sieve_storage_get_script
+(struct sieve_storage *storage, const char *name,
+	enum sieve_error *error_r)
+{
+	struct sieve_script *script;
+
+	if ( error_r != NULL )
+		*error_r = SIEVE_ERROR_NONE;
+	sieve_storage_clear_error(storage);
+
+	/* Validate script name */
+	if ( name != NULL && !sieve_script_name_is_valid(name) ) {
+		sieve_storage_set_error(storage,
+			SIEVE_ERROR_BAD_PARAMS,
+			"Invalid script name `%s'.",
+			str_sanitize(name, 80));
+		if (error_r != NULL)
+			*error_r = storage->error_code;
+		return NULL;
+	}
+
+	i_assert(storage->v.get_script != NULL);
+	script = storage->v.get_script(storage, name);
+	if (script == NULL && error_r != NULL)
+		*error_r = storage->error_code;
+	return script;
+}
+
+struct sieve_script *sieve_storage_open_script
+(struct sieve_storage *storage, const char *name,
+	enum sieve_error *error_r)
+{
+	struct sieve_script *script;
+
+	script = sieve_storage_get_script(storage, name,	error_r);
+	if ( script == NULL )
+		return NULL;
+
+	if ( sieve_script_open(script, error_r) < 0 ) {
+		sieve_script_unref(&script);
+		return NULL;
+	}
+	
+	return script;
+}
+
+/*
+ * Script sequence
+ */
+
+struct sieve_script_sequence *sieve_storage_get_script_sequence
+(struct sieve_storage *storage, enum sieve_error *error_r)
+{
+	enum sieve_error error;
+
+	if ( error_r != NULL )
+		*error_r = SIEVE_ERROR_NONE;
+	else
+		error_r = &error;
+
+	i_assert( storage->v.get_script_sequence != NULL );
+	return storage->v.get_script_sequence(storage, error_r);
+}
+
+/*
+ * Active script
+ */
+
+int sieve_storage_active_script_get_name
+(struct sieve_storage *storage, const char **name_r)
+{
+	i_assert(storage->v.active_script_get_name != NULL);
+	return storage->v.active_script_get_name(storage, name_r);
+}
+
+struct sieve_script *sieve_storage_active_script_open
+(struct sieve_storage *storage, enum sieve_error *error_r)
+{
+	struct sieve_script *script;
+
+	i_assert(storage->v.active_script_open != NULL);
+	script = storage->v.active_script_open(storage);
+
+	if ( error_r != NULL )
+		*error_r = storage->error_code;
+	return script;
+}
+
+int sieve_storage_deactivate
+(struct sieve_storage *storage, time_t mtime)
+{
+	int ret;
+
+	i_assert( (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 );
+
+	i_assert(storage->v.deactivate != NULL);
+	ret = storage->v.deactivate(storage);
+
+	sieve_storage_set_modified(storage, mtime);
+
+	return ret;
+}
+
+int sieve_storage_active_script_get_last_change
+(struct sieve_storage *storage, time_t *last_change_r)
+{
+	i_assert( storage->v.active_script_get_last_change != NULL);
+	
+	return storage->v.active_script_get_last_change(storage, last_change_r);
+}
+
+/*
+ * Listing scripts
+ */
+
+struct sieve_storage_list_context *sieve_storage_list_init
+(struct sieve_storage *storage)
+{
+	struct sieve_storage_list_context *lctx;
+	
+	i_assert(storage->v.list_init != NULL);
+	lctx = storage->v.list_init(storage);
+
+	if (lctx != NULL) {
+		lctx->storage = storage;
+	}
+	
+	return lctx;
+}
+
+const char *sieve_storage_list_next
+(struct sieve_storage_list_context *lctx, bool *active_r)
+{
+	struct sieve_storage *storage = lctx->storage;
+	const char *scriptname;
+	bool script_active = FALSE;
+
+	i_assert(storage->v.list_next != NULL);
+	scriptname = storage->v.list_next(lctx, &script_active);
+
+	i_assert(!script_active || !lctx->seen_active);
+	if (script_active)
+		lctx->seen_active = TRUE;
+
+	if (active_r != NULL)
+		*active_r = script_active;	
+	return scriptname;
+}
+
+int sieve_storage_list_deinit
+(struct sieve_storage_list_context **_lctx)
+{	
+	struct sieve_storage_list_context *lctx = *_lctx;
+	struct sieve_storage *storage = lctx->storage;
+	int ret;
+
+	i_assert(storage->v.list_deinit != NULL);
+	ret = storage->v.list_deinit(lctx);
+
+	_lctx = NULL;	
+	return ret;
+}
+
+/*
+ * Saving scripts
+ */
+
+struct sieve_storage_save_context *
+sieve_storage_save_init(struct sieve_storage *storage,
+	const char *scriptname, struct istream *input)
+{
+	struct sieve_storage_save_context *sctx;
+
+	if ( scriptname != NULL ) {
+		/* Validate script name */
+		if ( !sieve_script_name_is_valid(scriptname) ) {
+			sieve_storage_set_error(storage,
+				SIEVE_ERROR_BAD_PARAMS,
+				"Invalid Sieve script name `%s'.",
+				str_sanitize(scriptname, 80));
+			return NULL;
+		}
+	}
+
+	i_assert( (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 );
+
+	i_assert(storage->v.save_init != NULL);
+	if ((sctx=storage->v.save_init(storage, scriptname, input)) == NULL)
+		return NULL;
+
+	sctx->storage = storage;
+	sctx->mtime = (time_t)-1;
+
+	i_assert( sctx->input != NULL );
+
+	return sctx;
+}
+
+int sieve_storage_save_continue(struct sieve_storage_save_context *sctx)
+{
+	struct sieve_storage *storage = sctx->storage;
+	int ret;
+
+	i_assert(storage->v.save_continue != NULL);
+	ret = storage->v.save_continue(sctx);
+	if (ret < 0)
+		sctx->failed = TRUE;
+	return ret;
+}
+
+int sieve_storage_save_finish(struct sieve_storage_save_context *sctx)
+{
+	struct sieve_storage *storage = sctx->storage;
+
+	i_assert(!sctx->finished);
+	sctx->finished = TRUE;
+
+	i_assert(storage->v.save_finish != NULL);
+	return storage->v.save_finish(sctx);
+}
+
+void sieve_storage_save_set_mtime
+(struct sieve_storage_save_context *sctx, time_t mtime)
+{
+	sctx->mtime = mtime;
+}
+
+static void sieve_storage_save_deinit(struct sieve_storage_save_context *sctx)
+{
+	if (sctx->scriptobject != NULL)
+		sieve_script_unref(&sctx->scriptobject);
+}
+
+struct sieve_script *sieve_storage_save_get_tempscript
+(struct sieve_storage_save_context *sctx)
+{
+	struct sieve_storage *storage = sctx->storage;
+
+	if ( sctx->failed )
+		return NULL;
+
+	if ( sctx->scriptobject != NULL )
+		return sctx->scriptobject;
+
+	i_assert( storage->v.save_get_tempscript != NULL );
+	sctx->scriptobject = storage->v.save_get_tempscript(sctx);
+
+	i_assert( sctx->scriptobject != NULL ||
+		storage->error_code != SIEVE_ERROR_NONE );
+	return sctx->scriptobject;
+}
+
+bool sieve_storage_save_will_activate
+(struct sieve_storage_save_context *sctx)
+{
+	bool result = FALSE;
+
+	if ( sctx->scriptname != NULL ) T_BEGIN {
+		const char *scriptname;
+		int ret;
+
+		ret = sieve_storage_active_script_get_name(sctx->storage, &scriptname);
+		if ( ret > 0 ) {
+		 	/* Is the requested script active? */
+			result = ( strcmp(sctx->scriptname, scriptname) == 0 );
+		}
+	} T_END;
+
+	return result;
+}
+
+int sieve_storage_save_commit(struct sieve_storage_save_context **_sctx)
+{
+	struct sieve_storage_save_context *sctx = *_sctx;
+	struct sieve_storage *storage = sctx->storage;
+	const char *scriptname;
+	int ret;	
+
+	i_assert(sctx->finished);
+	i_assert(sctx->scriptname != NULL);
+
+	scriptname = t_strdup(sctx->scriptname);
+	sieve_storage_save_deinit(sctx);
+
+	i_assert(storage->v.save_commit != NULL);
+	ret = storage->v.save_commit(sctx);
+	*_sctx = NULL;
+
+	/* set INBOX mailbox attribute */
+	if ( ret >= 0 )
+		sieve_storage_sync_script_save(storage, scriptname);
+
+	return ret;
+}
+
+void sieve_storage_save_cancel(struct sieve_storage_save_context **_sctx)
+{
+	struct sieve_storage_save_context *sctx = *_sctx;
+	struct sieve_storage *storage = sctx->storage;
+
+	sctx->failed = TRUE;
+
+	sieve_storage_save_deinit(sctx);
+
+	if (!sctx->finished)
+		(void)sieve_storage_save_finish(sctx);
+	
+	i_assert(storage->v.save_cancel != NULL);
+	storage->v.save_cancel(sctx);
+	*_sctx = NULL;
+}
+
+int sieve_storage_save_as_active(struct sieve_storage *storage,
+	struct istream *input, time_t mtime)
+{
+	i_assert( storage->v.save_as_active != NULL );
+	return storage->v.save_as_active(storage, input, mtime);
+}
+
+/*
+ * Checking quota
+ */
+
+bool sieve_storage_quota_validsize
+(struct sieve_storage *storage, size_t size, uint64_t *limit_r)
+{
+	uint64_t max_size;
+
+	max_size = sieve_max_script_size(storage->svinst);
+	if ( max_size > 0 && size > max_size ) {
+		*limit_r = max_size;
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+uint64_t sieve_storage_quota_max_script_size
+(struct sieve_storage *storage)
+{
+	return sieve_max_script_size(storage->svinst);
+}
+
+int sieve_storage_quota_havespace
+(struct sieve_storage *storage, const char *scriptname, size_t size,
+	enum sieve_storage_quota *quota_r, uint64_t *limit_r)
+{
+	*quota_r = SIEVE_STORAGE_QUOTA_NONE;
+	*limit_r = 0;
+
+	/* Check the script size */
+	if ( !sieve_storage_quota_validsize(storage, size, limit_r) ) {
+		*quota_r = SIEVE_STORAGE_QUOTA_MAXSIZE;
+		return 0;
+	}
+
+	/* Do we need to scan the storage (quota enabled) ? */
+	if ( storage->max_scripts == 0 && storage->max_storage == 0 ) {
+		return 1;
+	}
+
+	if (storage->v.quota_havespace == NULL)
+		return 1;
+
+	return storage->v.quota_havespace
+		(storage, scriptname,	size, quota_r, limit_r);
+}
+
+/*
+ * Properties
+ */
+
+const char *sieve_storage_location(const struct sieve_storage *storage)
+{
+	return storage->location;
+}
+
+/*
+ * Error handling
+ */
+
+void sieve_storage_clear_error(struct sieve_storage *storage)
+{
+	i_free(storage->error);
+	storage->error_code = SIEVE_ERROR_NONE;
+	storage->error = NULL;
+}
+
+void sieve_storage_set_error
+(struct sieve_storage *storage, enum sieve_error error,
+	const char *fmt, ...)
+{
+	va_list va;
+
+	sieve_storage_clear_error(storage);
+
+	if (fmt != NULL) {
+		va_start(va, fmt);
+		storage->error = i_strdup_vprintf(fmt, va);
+		va_end(va);
+	}
+
+	storage->error_code = error;
+}
+
+void sieve_storage_set_internal_error
+(struct sieve_storage *storage)
+{
+	struct tm *tm;
+	char str[256];
+
+	/* critical errors may contain sensitive data, so let user
+	   see only "Internal error" with a timestamp to make it
+	   easier to look from log files the actual error message. */
+	tm = localtime(&ioloop_time);
+
+	storage->error =
+		strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ?
+		i_strdup(str) : i_strdup(CRITICAL_MSG);
+
+	storage->error_code = SIEVE_ERROR_TEMP_FAILURE;
+}
+
+void sieve_storage_set_critical
+(struct sieve_storage *storage, const char *fmt, ...)
+{
+	va_list va;
+
+	sieve_storage_clear_error(storage);
+	if (fmt != NULL) {
+		if ( (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 ) {
+			va_start(va, fmt);
+			sieve_sys_error(storage->svinst, "%s storage: %s",
+				storage->driver_name, t_strdup_vprintf(fmt, va));
+			va_end(va);
+
+			sieve_storage_set_internal_error(storage);
+		} else {
+			/* no user is involved while synchronizing, so do it the
+			   normal way */
+			va_start(va, fmt);
+			storage->error = i_strdup_vprintf(fmt, va);
+			va_end(va);
+
+			storage->error_code = SIEVE_ERROR_TEMP_FAILURE;
+		}
+	}
+}
+
+const char *sieve_storage_get_last_error
+(struct sieve_storage *storage, enum sieve_error *error_r)
+{
+	/* We get here only in error situations, so we have to return some
+	   error. If storage->error is NULL, it means we forgot to set it at
+	   some point..
+	 */
+
+	if ( error_r != NULL )
+		*error_r = storage->error_code;
+
+	return storage->error != NULL ? storage->error : "Unknown error";
+}
+
+void sieve_storage_sys_error
+(struct sieve_storage *storage, const char *fmt, ...)
+{
+	struct sieve_instance *svinst = storage->svinst;
+	va_list va;
+
+	va_start(va, fmt);
+	sieve_sys_error(svinst, "%s storage: %s",
+		storage->driver_name, t_strdup_vprintf(fmt, va));
+	va_end(va);	
+}
+
+void sieve_storage_sys_warning
+(struct sieve_storage *storage, const char *fmt, ...)
+{
+	struct sieve_instance *svinst = storage->svinst;
+	va_list va;
+
+	va_start(va, fmt);
+	sieve_sys_warning(svinst, "%s storage: %s",
+		storage->driver_name, t_strdup_vprintf(fmt, va));
+	va_end(va);	
+}
+
+void sieve_storage_sys_info
+(struct sieve_storage *storage, const char *fmt, ...)
+{
+	struct sieve_instance *svinst = storage->svinst;
+	va_list va;
+
+	va_start(va, fmt);
+	sieve_sys_info(svinst, "%s storage: %s",
+		storage->driver_name, t_strdup_vprintf(fmt, va));
+	va_end(va);	
+}
+
+void sieve_storage_sys_debug
+(struct sieve_storage *storage, const char *fmt, ...)
+{
+	struct sieve_instance *svinst = storage->svinst;
+	va_list va;
+
+	if (!svinst->debug)
+		return;
+
+	va_start(va, fmt);
+	sieve_sys_debug(svinst, "%s storage: %s",
+		storage->driver_name, t_strdup_vprintf(fmt, va));
+	va_end(va);	
+}
diff --git a/src/lib-sieve/sieve-storage.h b/src/lib-sieve/sieve-storage.h
new file mode 100644
index 0000000000000000000000000000000000000000..fc9c189f8ec86ab26de2fa16aa5967b4f4a59000
--- /dev/null
+++ b/src/lib-sieve/sieve-storage.h
@@ -0,0 +1,188 @@
+/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
+ */
+
+#ifndef __SIEVE_STORAGE_H
+#define __SIEVE_STORAGE_H
+
+#define MAILBOX_ATTRIBUTE_PREFIX_SIEVE \
+	MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER"sieve/"
+#define MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES \
+	MAILBOX_ATTRIBUTE_PREFIX_SIEVE"files/"
+#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT \
+	MAILBOX_ATTRIBUTE_PREFIX_SIEVE"default"
+
+#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_LINK 'L'
+#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_SCRIPT 'S'
+
+/*
+ * Storage object
+ */
+
+enum sieve_storage_flags {
+	/* Storage is opened for read/write access (e.g. ManageSieve) */
+	SIEVE_STORAGE_FLAG_READWRITE         = 0x01,
+	/* This storage is used for synchronization (and not normal ManageSieve)
+	 */
+	SIEVE_STORAGE_FLAG_SYNCHRONIZING     = 0x02
+};
+
+struct sieve_storage;
+
+struct sieve_storage *sieve_storage_create
+(struct sieve_instance *svinst, const char *location,
+	enum sieve_storage_flags flags, enum sieve_error *error_r)
+	ATTR_NULL(4);
+struct sieve_storage *sieve_storage_create_main
+(struct sieve_instance *svinst, struct mail_user *user,
+	enum sieve_storage_flags flags, enum sieve_error *error_r)
+	ATTR_NULL(4);
+
+void sieve_storage_ref(struct sieve_storage *storage);
+void sieve_storage_unref(struct sieve_storage **_storage);
+
+/*
+ * Script access
+ */
+
+struct sieve_script *sieve_storage_get_script
+	(struct sieve_storage *storage, const char *name,
+		enum sieve_error *error_r) ATTR_NULL(3);
+struct sieve_script *sieve_storage_open_script
+	(struct sieve_storage *storage, const char *name,
+		enum sieve_error *error_r) ATTR_NULL(3);
+
+/*
+ * Script sequence
+ */
+
+struct sieve_script_sequence *sieve_storage_get_script_sequence
+(struct sieve_storage *storage, enum sieve_error *error_r);
+
+/*
+ * Active script
+ */
+
+int sieve_storage_active_script_get_name
+	(struct sieve_storage *storage, const char **name_r);
+
+struct sieve_script *sieve_storage_active_script_open
+	(struct sieve_storage *storage, enum sieve_error *error_r)
+	ATTR_NULL(2);
+
+int sieve_storage_active_script_get_last_change
+	(struct sieve_storage *storage, time_t *last_change_r);
+
+/*
+ * Listing scripts in storage
+ */
+
+struct sieve_storage_list_context;
+
+/* Create a context for listing the scripts in the storage */
+struct sieve_storage_list_context *sieve_storage_list_init
+	(struct sieve_storage *storage);
+/* Get the next script in the storage. */
+const char *sieve_storage_list_next
+	(struct sieve_storage_list_context *lctx, bool *active_r)
+	ATTR_NULL(2);
+/* Destroy the listing context */
+int sieve_storage_list_deinit
+	(struct sieve_storage_list_context **lctx);
+
+/*
+ * Saving scripts in storage
+ */
+
+struct sieve_storage_save_context;
+
+struct sieve_storage_save_context *
+sieve_storage_save_init(struct sieve_storage *storage,
+	const char *scriptname, struct istream *input);
+
+int sieve_storage_save_continue(struct sieve_storage_save_context *sctx);
+
+int sieve_storage_save_finish(struct sieve_storage_save_context *sctx);
+
+struct sieve_script *sieve_storage_save_get_tempscript
+  (struct sieve_storage_save_context *sctx);
+
+bool sieve_storage_save_will_activate
+	(struct sieve_storage_save_context *sctx);
+
+void sieve_storage_save_set_mtime
+	(struct sieve_storage_save_context *sctx, time_t mtime);
+
+void sieve_storage_save_cancel(struct sieve_storage_save_context **sctx);
+
+int sieve_storage_save_commit(struct sieve_storage_save_context **sctx);
+
+/* Saves input directly as a regular file at the active script path.
+ * This is needed for the doveadm-sieve plugin.
+ */
+int sieve_storage_save_as_active
+	(struct sieve_storage *storage, struct istream *input, time_t mtime);
+
+/*
+ * Management
+ */
+
+int sieve_storage_deactivate(struct sieve_storage *storage, time_t mtime);
+
+/*
+ * Storage quota
+ */
+
+enum sieve_storage_quota {
+	SIEVE_STORAGE_QUOTA_NONE,
+	SIEVE_STORAGE_QUOTA_MAXSIZE,
+	SIEVE_STORAGE_QUOTA_MAXSCRIPTS,
+	SIEVE_STORAGE_QUOTA_MAXSTORAGE
+};
+
+bool sieve_storage_quota_validsize
+	(struct sieve_storage *storage, size_t size, uint64_t *limit_r);
+
+uint64_t sieve_storage_quota_max_script_size
+	(struct sieve_storage *storage);
+
+int sieve_storage_quota_havespace
+	(struct sieve_storage *storage, const char *scriptname, size_t size,
+		enum sieve_storage_quota *quota_r, uint64_t *limit_r);
+
+/*
+ * Properties
+ */
+
+const char *sieve_storage_location
+	(const struct sieve_storage *storage) ATTR_PURE;
+
+int sieve_storage_is_singular
+	(struct sieve_storage *storage);
+
+/*
+ * Error handling
+ */
+
+void sieve_storage_clear_error(struct sieve_storage *storage);
+
+void sieve_storage_set_error
+	(struct sieve_storage *storage, enum sieve_error error,
+		const char *fmt, ...) ATTR_FORMAT(3, 4);
+void sieve_storage_set_critical
+	(struct sieve_storage *storage, 	const char *fmt, ...)
+		ATTR_FORMAT(2, 3);
+
+const char *sieve_storage_get_last_error
+	(struct sieve_storage *storage, enum sieve_error *error_r)
+	ATTR_NULL(2);
+
+/*
+ *
+ */
+
+int sieve_storage_get_last_change
+	(struct sieve_storage *storage, time_t *last_change_r);
+void sieve_storage_set_modified
+	(struct sieve_storage *storage, time_t mtime);
+
+#endif
diff --git a/src/lib-sieve/sieve.c b/src/lib-sieve/sieve.c
index 6a2b671d76d9f2e48808c5fbd827ce090ad33b86..0bd041aaf88b22621bee8c0051e86f6bd560e0f1 100644
--- a/src/lib-sieve/sieve.c
+++ b/src/lib-sieve/sieve.c
@@ -14,7 +14,7 @@
 #include "sieve-plugins.h"
 
 #include "sieve-script.h"
-#include "sieve-script-file.h"
+#include "sieve-storage-private.h"
 #include "sieve-ast.h"
 #include "sieve-binary.h"
 #include "sieve-actions.h"
@@ -125,24 +125,29 @@ struct sieve_instance *sieve_init
 		return NULL;
 	}
 
+	/* Initialize storage classes */
+	sieve_storages_init(svinst);
+
+	/* Initialize plugins */
 	sieve_plugins_load(svinst, NULL, NULL);
 
+	/* Configure extensions */
 	sieve_extensions_configure(svinst);
 
 	return svinst;
 }
 
-void sieve_deinit(struct sieve_instance **svinst)
+void sieve_deinit(struct sieve_instance **_svinst)
 {
-	sieve_plugins_unload(*svinst);
-
-	sieve_extensions_deinit(*svinst);
-
-	sieve_errors_deinit(*svinst);
+	struct sieve_instance *svinst = *_svinst;
 
-	pool_unref(&(*svinst)->pool);
+	sieve_plugins_unload(svinst);
+	sieve_storages_deinit(svinst);
+	sieve_extensions_deinit(svinst);
+	sieve_errors_deinit(svinst);
 
-	*svinst = NULL;
+	pool_unref(&(svinst)->pool);
+	*_svinst = NULL;
 }
 
 void sieve_set_extensions
@@ -247,10 +252,22 @@ struct sieve_binary *sieve_compile_script
 {
 	struct sieve_ast *ast;
 	struct sieve_binary *sbin;
+	enum sieve_error error;
+
+	if ( error_r != NULL )
+		*error_r = SIEVE_ERROR_NONE;
+	else
+		error_r = &error;
 
 	/* Parse */
 	if ( (ast = sieve_parse(script, ehandler, error_r)) == NULL ) {
- 		sieve_error(ehandler, sieve_script_name(script), "parse failed");
+		switch ( *error_r ) {
+		case SIEVE_ERROR_NOT_FOUND:
+			sieve_error(ehandler, sieve_script_name(script), "script not found");
+			break;
+		default:
+	 		sieve_error(ehandler, sieve_script_name(script), "parse failed");
+		}
 		return NULL;
 	}
 
@@ -286,15 +303,26 @@ struct sieve_binary *sieve_compile
 {
 	struct sieve_script *script;
 	struct sieve_binary *sbin;
+	enum sieve_error error;
 
 	if ( (script = sieve_script_create_open
-		(svinst, script_location, script_name, ehandler, error_r)) == NULL )
+		(svinst, script_location, script_name, &error)) == NULL ) {
+		if (error_r != NULL)
+			*error_r = error;
+		switch ( error ) {
+		case SIEVE_ERROR_NOT_FOUND:
+			sieve_error(ehandler, script_name, "script not found");
+			break;
+		default:
+			sieve_internal_error(ehandler, script_name, "failed to open script");
+		}
 		return NULL;
+	}
 
 	sbin = sieve_compile_script(script, ehandler, flags, error_r);
 
 	if ( svinst->debug && sbin != NULL ) {
-		sieve_sys_debug(svinst, "script `%s' from %s successfully compiled",
+		sieve_sys_debug(svinst, "Script `%s' from %s successfully compiled",
 			sieve_script_name(script), sieve_script_location(script));
 	}
 
@@ -366,9 +394,10 @@ struct sieve_binary *sieve_open_script
 			/* Ok, it exists; now let's see if it is up to date */
 			if ( !sieve_binary_up_to_date(sbin, flags) ) {
 				/* Not up to date */
-				if ( svinst->debug )
-					sieve_sys_debug(svinst, "script binary %s is not up-to-date",
+				if ( svinst->debug ) {
+					sieve_sys_debug(svinst, "Script binary %s is not up-to-date",
 						sieve_binary_path(sbin));
+				}
 
 				sieve_binary_unref(&sbin);
 				sbin = NULL;
@@ -379,17 +408,21 @@ struct sieve_binary *sieve_open_script
 		 * to (re-)compile.
 		 */
 		if ( sbin != NULL ) {
-			if ( svinst->debug )
-				sieve_sys_debug(svinst, "script binary %s successfully loaded",
+			if ( svinst->debug ) {
+				sieve_sys_debug(svinst,
+					"Script binary %s successfully loaded",
 					sieve_binary_path(sbin));
+			}
 
 		} else {
 			sbin = sieve_compile_script(script, ehandler, flags, error_r);
 
 			if ( sbin != NULL ) {
-				if ( svinst->debug )
-					sieve_sys_debug(svinst, "script `%s' from %s successfully compiled",
+				if ( svinst->debug ) {
+					sieve_sys_debug(svinst,
+						"Script `%s' from %s successfully compiled",
 						sieve_script_name(script), sieve_script_location(script));
+				}
 			}
 		}
 	} T_END;
@@ -404,11 +437,21 @@ struct sieve_binary *sieve_open
 {
 	struct sieve_script *script;
 	struct sieve_binary *sbin;
+	enum sieve_error error;
 
 	/* First open the scriptfile itself */
 	if ( (script=sieve_script_create_open
-		(svinst, script_location, script_name, ehandler, error_r)) == NULL ) {
+		(svinst, script_location, script_name, &error)) == NULL ) {
 		/* Failed */
+		if (error_r != NULL)
+			*error_r = error;
+		switch ( error ) {
+		case SIEVE_ERROR_NOT_FOUND:
+			sieve_error(ehandler, script_name, "script not found");
+			break;
+		default:
+			sieve_internal_error(ehandler, script_name, "failed to open script");
+		}
 		return NULL;
 	}
 
@@ -771,160 +814,5 @@ size_t sieve_max_script_size(struct sieve_instance *svinst)
 	return svinst->max_script_size;
 }
 
-/*
- * Script directory
- */
-
-struct sieve_directory {
-		struct sieve_instance *svinst;
-		DIR *dirp;
-
-		const char *path;
-};
-
-struct sieve_directory *sieve_directory_open
-(struct sieve_instance *svinst, const char *path, enum sieve_error *error_r)
-{
-	struct sieve_directory *sdir = NULL;
-	DIR *dirp;
-	struct stat st;
-
-	if ( error_r != NULL )
-		*error_r = SIEVE_ERROR_NONE;
-
-	if ( (path[0] == '~' && (path[1] == '/' || path[1] == '\0')) ||
-		(((svinst->flags & SIEVE_FLAG_HOME_RELATIVE) != 0 ) && path[0] != '/') ) {
-		/* Home-relative path. change to absolute. */
-		const char *home = sieve_environment_get_homedir(svinst);
-
-		if ( home != NULL ) {
-			if ( path[0] == '~' && (path[1] == '/' || path[1] == '\0') )
-				path = home_expand_tilde(path, home);
-			else
-				path = t_strconcat(home, "/", path, NULL);
-		} else {
-			sieve_sys_error(svinst,
-				"sieve dir path %s is relative to home directory, "
-				"but home directory is not available.", path);
-			if ( error_r != NULL )
-				*error_r = SIEVE_ERROR_TEMP_FAILURE;
-			return NULL;
-		}
-	}
-
-	/* Specified path can either be a regular file or a directory */
-	if ( stat(path, &st) != 0 ) {
-		switch ( errno ) {
-		case ENOENT:
-			if ( error_r != NULL )
-				*error_r = SIEVE_ERROR_NOT_FOUND;
-			break;
-		case EACCES:
-			sieve_sys_error(svinst, "failed to open sieve dir: %s",
-				eacces_error_get("stat", path));
-			if ( error_r != NULL )
-				*error_r = SIEVE_ERROR_NO_PERMISSION;
-			break;
-		default:
-			sieve_sys_error(svinst, "failed to open sieve dir: stat(%s) failed: %m",
-				path);
-			if ( error_r != NULL )
-				*error_r = SIEVE_ERROR_TEMP_FAILURE;
-			break;
-		}
-		return NULL;
-	}
-
-	if ( S_ISDIR(st.st_mode) ) {
-
-		/* Open the directory */
-		if ( (dirp = opendir(path)) == NULL ) {
-			switch ( errno ) {
-			case ENOENT:
-				if ( error_r != NULL )
-					*error_r = SIEVE_ERROR_NOT_FOUND;
-				break;
-			case EACCES:
-				sieve_sys_error(svinst, "failed to open sieve dir: %s",
-					eacces_error_get("opendir", path));
-				if ( error_r != NULL )
-					*error_r = SIEVE_ERROR_NO_PERMISSION;
-				break;
-			default:
-				sieve_sys_error(svinst, "failed to open sieve dir: opendir(%s) failed: "
-					"%m", path);
-				if ( error_r != NULL )
-					*error_r = SIEVE_ERROR_TEMP_FAILURE;
-				break;
-			}
-			return NULL;
-		}
-
-		/* Create object */
-		sdir = t_new(struct sieve_directory, 1);
-		sdir->path = path;
-		sdir->dirp = dirp;
-	} else {
-		sdir = t_new(struct sieve_directory, 1);
-		sdir->path = path;
-		sdir->dirp = NULL;
-	}
-
-	sdir->svinst = svinst;
-
-	return sdir;
-}
-
-const char *sieve_directory_get_scriptfile(struct sieve_directory *sdir)
-{
-	const char *script = NULL;
-	struct dirent *dp;
-
-	if ( sdir->dirp != NULL ) {
-		while ( script == NULL ) {
-			const char *file;
-			struct stat st;
-
-			errno = 0;
-			if ( (dp = readdir(sdir->dirp)) == NULL ) {
-				if ( errno != 0 ) {
-					sieve_sys_error(sdir->svinst, "failed to read sieve dir: "
-						"readdir(%s) failed: %m", sdir->path);
-				}
-
-				return NULL;
-			}
-
-			if ( !sieve_scriptfile_has_extension(dp->d_name) )
-				continue;
-
-			if ( sdir->path[strlen(sdir->path)-1] == '/' )
-				file = t_strconcat(sdir->path, dp->d_name, NULL);
-			else
-				file = t_strconcat(sdir->path, "/", dp->d_name, NULL);
-
-			if ( stat(file, &st) != 0 || !S_ISREG(st.st_mode) )
-				continue;
-
-			script = file;
-		}
-	} else {
-		script = sdir->path;
-		sdir->path = NULL;
-	}
-
-	return script;
-}
-
-void sieve_directory_close(struct sieve_directory **sdir)
-{
-	/* Close the directory */
-	if ( (*sdir)->dirp != NULL && closedir((*sdir)->dirp) < 0 )
-		sieve_sys_error((*sdir)->svinst, "failed to close sieve dir: "
-			"closedir(%s) failed: %m", (*sdir)->path);
-
-	*sdir = NULL;
-}
-
 
 
diff --git a/src/lib-sieve/sieve.h b/src/lib-sieve/sieve.h
index 6058d6838f5a8b2da7e91e2e307c9b214270fb7f..ea4aa3c309c39ce25d185045d74848e00ca7aee6 100644
--- a/src/lib-sieve/sieve.h
+++ b/src/lib-sieve/sieve.h
@@ -26,7 +26,7 @@ struct sieve_instance *sieve_init
 /* sieve_deinit():
  *   Frees all memory allocated by the sieve engine.
  */
-void sieve_deinit(struct sieve_instance **svinst);
+void sieve_deinit(struct sieve_instance **_svinst);
 
 /* sieve_get_capabilities():
  *
@@ -48,7 +48,8 @@ void sieve_set_extensions
  */
 struct sieve_binary *sieve_compile_script
 	(struct sieve_script *script, struct sieve_error_handler *ehandler,
-		enum sieve_compile_flags flags, enum sieve_error *error_r);
+		enum sieve_compile_flags flags, enum sieve_error *error_r)
+		ATTR_NULL(2, 4);
 
 /* sieve_compile:
  *
@@ -57,7 +58,8 @@ struct sieve_binary *sieve_compile_script
 struct sieve_binary *sieve_compile
 	(struct sieve_instance *svinst, const char *script_location,
 		const char *script_name, struct sieve_error_handler *ehandler,
-		enum sieve_compile_flags flags, enum sieve_error *error_r);
+		enum sieve_compile_flags flags, enum sieve_error *error_r)
+		ATTR_NULL(3, 4, 6);
 
 /*
  * Reading/writing Sieve binaries
@@ -205,15 +207,4 @@ unsigned int sieve_max_redirects(struct sieve_instance *svinst);
 unsigned int sieve_max_actions(struct sieve_instance *svinst);
 size_t sieve_max_script_size(struct sieve_instance *svinst);
 
-/*
- * Script directory
- */
-
-struct sieve_directory;
-
-struct sieve_directory *sieve_directory_open
-	(struct sieve_instance *svinst, const char *path, enum sieve_error *error_r);
-const char *sieve_directory_get_scriptfile(struct sieve_directory *sdir);
-void sieve_directory_close(struct sieve_directory **sdir);
-
 #endif
diff --git a/src/lib-sieve/storage/Makefile.am b/src/lib-sieve/storage/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..f33b0bca4400cbead84801fc634e145c889cf870
--- /dev/null
+++ b/src/lib-sieve/storage/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = \
+	file \
+	dict 
diff --git a/src/lib-sieve/storage/dict/Makefile.am b/src/lib-sieve/storage/dict/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..2a73f4393f88bf997c8b9ad32c19810cd3528393
--- /dev/null
+++ b/src/lib-sieve/storage/dict/Makefile.am
@@ -0,0 +1,13 @@
+noinst_LTLIBRARIES = libsieve_storage_dict.la
+
+AM_CPPFLAGS = \
+	$(LIBDOVECOT_INCLUDE) \
+	-I$(top_srcdir) \
+	-I$(top_srcdir)/src/lib-sieve
+
+libsieve_storage_dict_la_SOURCES = \
+	sieve-dict-script.c \
+	sieve-dict-storage.c
+
+noinst_HEADERS = \
+	sieve-dict-storage.h
diff --git a/src/lib-sieve/storage/dict/sieve-dict-script.c b/src/lib-sieve/storage/dict/sieve-dict-script.c
new file mode 100644
index 0000000000000000000000000000000000000000..4d093f925d535ed8f490cd7c5458307e754d44ff
--- /dev/null
+++ b/src/lib-sieve/storage/dict/sieve-dict-script.c
@@ -0,0 +1,315 @@
+/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str.h"
+#include "strfuncs.h"
+#include "istream.h"
+#include "dict.h"
+
+#include "sieve-common.h"
+#include "sieve-error.h"
+#include "sieve-binary.h"
+
+#include "sieve-dict-storage.h"
+
+/*
+ * Script dict implementation
+ */
+
+static struct sieve_dict_script *sieve_dict_script_alloc(void)
+{
+	struct sieve_dict_script *dscript;
+	pool_t pool;
+
+	pool = pool_alloconly_create("sieve_dict_script", 1024);
+	dscript = p_new(pool, struct sieve_dict_script, 1);
+	dscript->script = sieve_dict_script;
+	dscript->script.pool = pool;
+
+	return dscript;
+}
+
+struct sieve_dict_script *sieve_dict_script_init
+(struct sieve_dict_storage *dstorage, const char *name)
+{
+	struct sieve_storage *storage = &dstorage->storage;
+	struct sieve_dict_script *dscript = NULL;
+
+	if ( name == NULL )
+		name = SIEVE_DICT_SCRIPT_DEFAULT;
+
+	dscript = sieve_dict_script_alloc();
+	sieve_script_init(&dscript->script,
+		storage, &sieve_dict_script, storage->location, name);
+
+	return dscript;
+}
+
+static void sieve_dict_script_destroy(struct sieve_script *script)
+{
+	struct sieve_dict_script *dscript =
+		(struct sieve_dict_script *)script;
+
+	if ( dscript->data_pool != NULL )
+		pool_unref(&dscript->data_pool);
+}
+
+static int sieve_dict_script_open
+(struct sieve_script *script, enum sieve_error *error_r)
+{
+	struct sieve_dict_script *dscript =
+		(struct sieve_dict_script *)script;
+	struct sieve_storage *storage = script->storage;
+	struct sieve_dict_storage *dstorage =
+		(struct sieve_dict_storage *)storage;
+	const char *name = script->name;
+	const char *path;
+	int ret;
+
+	if ( sieve_dict_storage_get_dict
+		(dstorage, &dscript->dict, error_r) < 0 )
+		return -1;
+		
+	path = t_strconcat
+		(DICT_SIEVE_NAME_PATH, dict_escape_string(name), NULL);
+
+	ret = dict_lookup
+		(dscript->dict, script->pool, path, &dscript->data_id);
+	if ( ret <= 0 ) {
+		if ( ret < 0 ) {
+			sieve_script_set_critical(script,
+				"Failed to lookup script id from path %s", path);
+			*error_r = SIEVE_ERROR_TEMP_FAILURE;
+		} else {
+			sieve_script_sys_debug(script,
+				"Script `%s' not found at path %s", name, path);
+			sieve_script_set_error(script,
+				SIEVE_ERROR_NOT_FOUND,
+				"Sieve script `%s' not found", name);
+			*error_r = SIEVE_ERROR_NOT_FOUND;
+		}
+		return -1;
+	}
+
+	if ( strcmp(name, SIEVE_DICT_SCRIPT_DEFAULT) == 0 ) {
+		script->location = p_strconcat(script->pool,
+			SIEVE_DICT_STORAGE_DRIVER_NAME, ":", storage->location, NULL);
+	} else {
+		script->location = p_strconcat(script->pool,
+			SIEVE_DICT_STORAGE_DRIVER_NAME, ":", storage->location,
+				";name=", name, NULL);
+	}
+
+	return 0;
+}
+
+static int sieve_dict_script_get_stream
+(struct sieve_script *script, struct istream **stream_r,
+	enum sieve_error *error_r)
+{
+	struct sieve_dict_script *dscript =
+		(struct sieve_dict_script *)script;
+	const char *path, *name = script->name;
+	int ret;
+
+	dscript->data_pool =
+		pool_alloconly_create("sieve_dict_script data pool", 1024);
+
+	path = t_strconcat
+		(DICT_SIEVE_DATA_PATH, dict_escape_string(dscript->data_id), NULL);
+
+	ret = dict_lookup
+		(dscript->dict, dscript->data_pool, path, &dscript->data);
+	if ( ret <= 0 ) {
+		if ( ret < 0 ) {
+			sieve_script_set_critical(script,
+				"Failed to lookup data with id `%s' "
+				"for script `%s' from path %s",
+				dscript->data_id, name, path);
+		} else {
+			sieve_script_set_critical(script,
+				"Data with id `%s' for script `%s' "
+				"not found at path %s",
+				dscript->data_id, name, path);
+		}
+		*error_r = SIEVE_ERROR_TEMP_FAILURE;
+		return -1;
+	}
+
+	*stream_r = i_stream_create_from_data(dscript->data, strlen(dscript->data));
+	return 0;
+}
+
+static int sieve_dict_script_binary_read_metadata
+(struct sieve_script *script, struct sieve_binary_block *sblock,
+	sieve_size_t *offset)
+{
+	struct sieve_dict_script *dscript =
+		(struct sieve_dict_script *)script;
+	struct sieve_binary *sbin =
+		sieve_binary_block_get_binary(sblock);
+	string_t *data_id;
+
+	if ( dscript->data_id == NULL &&
+		sieve_script_open(script, NULL) < 0 )
+		return 0;
+
+	if ( !sieve_binary_read_string(sblock, offset, &data_id) ) {
+		sieve_script_sys_error(script,
+			"Binary `%s' has invalid metadata for script `%s'",
+			sieve_binary_path(sbin), sieve_script_location(script));
+		return -1;
+	}
+	i_assert( dscript->data_id != NULL );
+	if ( strcmp(str_c(data_id), dscript->data_id) != 0 )
+		return 0;
+	return 1;
+}
+
+static void sieve_dict_script_binary_write_metadata
+(struct sieve_script *script, struct sieve_binary_block *sblock)
+{
+	struct sieve_dict_script *dscript =
+		(struct sieve_dict_script *)script;
+
+	sieve_binary_emit_cstring(sblock, dscript->data_id);
+}
+
+static const char * sieve_dict_script_get_binpath
+(struct sieve_dict_script *dscript)
+{
+	struct sieve_script *script = &dscript->script;
+	struct sieve_storage *storage = script->storage;
+
+	if ( dscript->binpath == NULL ) {
+		if ( storage->bin_dir == NULL )
+			return NULL;
+		dscript->binpath = p_strconcat(script->pool,
+			storage->bin_dir, "/",
+			sieve_binfile_from_name(script->name), NULL);
+	}
+
+	return dscript->binpath;
+}
+
+static struct sieve_binary *sieve_dict_script_binary_load
+(struct sieve_script *script, enum sieve_error *error_r)
+{
+	struct sieve_dict_script *dscript =
+		(struct sieve_dict_script *)script;
+
+	if ( sieve_dict_script_get_binpath(dscript) == NULL )
+		return NULL;
+
+	return sieve_binary_open(script->storage->svinst,
+		dscript->binpath, script, error_r);
+}
+
+static int sieve_dict_script_binary_save
+(struct sieve_script *script, struct sieve_binary *sbin, bool update,
+	enum sieve_error *error_r)
+{
+	struct sieve_dict_script *dscript =
+		(struct sieve_dict_script *)script;
+
+	if ( sieve_dict_script_get_binpath(dscript) == NULL )
+		return 0;
+	if ( sieve_storage_setup_bindir(script->storage, 0700) < 0 )
+		return -1;
+
+	return sieve_binary_save(sbin,
+		dscript->binpath, update, 0600, error_r);
+}
+
+static bool sieve_dict_script_equals
+(const struct sieve_script *script, const struct sieve_script *other)
+{
+	struct sieve_storage *storage = script->storage;
+	struct sieve_storage *sother = other->storage;
+
+	if ( strcmp(storage->location, sother->location) != 0 )
+		return FALSE;
+
+	i_assert( script->name != NULL && other->name != NULL );
+
+	return ( strcmp(script->name, other->name) == 0 );
+}
+
+const struct sieve_script sieve_dict_script = {
+	.driver_name = SIEVE_DICT_STORAGE_DRIVER_NAME,
+	.v = {
+		.destroy = sieve_dict_script_destroy,
+
+		.open = sieve_dict_script_open,
+
+		.get_stream = sieve_dict_script_get_stream,
+
+		.binary_read_metadata =sieve_dict_script_binary_read_metadata,
+		.binary_write_metadata = sieve_dict_script_binary_write_metadata,
+		.binary_load = sieve_dict_script_binary_load,
+		.binary_save = sieve_dict_script_binary_save,
+
+		.equals = sieve_dict_script_equals
+	}
+};
+
+/*
+ * Script sequence
+ */
+
+struct sieve_dict_script_sequence {
+	struct sieve_script_sequence seq;
+
+	unsigned int done:1;
+};
+
+struct sieve_script_sequence *sieve_dict_storage_get_script_sequence
+(struct sieve_storage *storage, enum sieve_error *error_r)
+{
+	struct sieve_dict_script_sequence *dseq = NULL;
+
+	if ( error_r != NULL )
+		*error_r = SIEVE_ERROR_NONE;
+
+	/* Create sequence object */
+	dseq = i_new(struct sieve_dict_script_sequence, 1);
+	sieve_script_sequence_init(&dseq->seq, storage);
+
+	return &dseq->seq;
+}
+
+struct sieve_script *sieve_dict_script_sequence_next
+(struct sieve_script_sequence *seq, enum sieve_error *error_r)
+{
+	struct sieve_dict_script_sequence *dseq =
+		(struct sieve_dict_script_sequence *)seq;
+	struct sieve_dict_storage *dstorage =
+		(struct sieve_dict_storage *)seq->storage;
+	struct sieve_dict_script *dscript;
+
+	if ( error_r != NULL )
+		*error_r = SIEVE_ERROR_NONE;
+
+	if ( dseq->done )
+		return NULL;
+	dseq->done = TRUE;
+
+	dscript = sieve_dict_script_init
+		(dstorage, seq->storage->script_name);
+	if ( sieve_script_open(&dscript->script, error_r) < 0 ) {
+		struct sieve_script *script = &dscript->script;
+		sieve_script_unref(&script);
+		return NULL;
+	}
+
+	return &dscript->script;
+}
+
+void sieve_dict_script_sequence_destroy(struct sieve_script_sequence *seq)
+{
+	struct sieve_dict_script_sequence *dseq =
+		(struct sieve_dict_script_sequence *)seq;
+	i_free(dseq);
+}
+
diff --git a/src/lib-sieve/storage/dict/sieve-dict-storage.c b/src/lib-sieve/storage/dict/sieve-dict-storage.c
new file mode 100644
index 0000000000000000000000000000000000000000..c89bee0edaba0a7a5f2d46daa356b967452dec9f
--- /dev/null
+++ b/src/lib-sieve/storage/dict/sieve-dict-storage.c
@@ -0,0 +1,185 @@
+/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "dict.h"
+
+#include "sieve-common.h"
+#include "sieve-error.h"
+
+#include "sieve-dict-storage.h"
+
+/*
+ * Storage class
+ */
+
+static struct sieve_storage *sieve_dict_storage_alloc(void)
+{
+	struct sieve_dict_storage *dstorage;
+	pool_t pool;
+
+	pool = pool_alloconly_create("sieve_dict_storage", 1024);
+	dstorage = p_new(pool, struct sieve_dict_storage, 1);
+	dstorage->storage = sieve_dict_storage;
+	dstorage->storage.pool = pool;
+
+	return &dstorage->storage;
+}
+
+static int sieve_dict_storage_init
+(struct sieve_storage *storage, const char *const *options,
+	enum sieve_error *error_r)
+{
+	struct sieve_dict_storage *dstorage =
+		(struct sieve_dict_storage *)storage;
+	struct sieve_instance *svinst = storage->svinst;
+	const char *username = NULL;
+
+	if ( options != NULL ) {
+		while ( *options != NULL ) {
+			const char *option = *options;
+
+			if ( strncasecmp(option, "user=", 5) == 0 && option[5] != '\0' ) {
+				username = option+5;
+			} else {
+				sieve_storage_set_critical(storage,
+					"Invalid option `%s'", option);
+				*error_r = SIEVE_ERROR_TEMP_FAILURE;
+				return -1;
+			}
+
+			options++;
+		}
+	}
+
+	if ( username == NULL ) {
+		if ( svinst->username == NULL ) {
+			sieve_storage_set_critical(storage,
+				"No username specified");
+			*error_r = SIEVE_ERROR_TEMP_FAILURE;
+			return -1;
+		}
+		username = svinst->username;
+	}
+
+	if ( svinst->base_dir == NULL ) {
+		sieve_storage_set_critical(storage,
+			"BUG: Sieve interpreter is initialized without a base_dir");
+		*error_r = SIEVE_ERROR_TEMP_FAILURE;
+		return -1;
+	}
+
+	sieve_storage_sys_debug(storage,
+		"user=%s, uri=%s", username, storage->location);
+
+	dstorage->username = p_strdup(storage->pool, username);
+	return 0;
+}
+
+int sieve_dict_storage_get_dict
+(struct sieve_dict_storage *dstorage, struct dict **dict_r,
+	enum sieve_error *error_r)
+{
+	struct sieve_storage *storage = &dstorage->storage;
+	struct sieve_instance *svinst = storage->svinst;
+	const char *error;
+	int ret;
+
+	if ( dstorage->dict == NULL ) {
+		ret = dict_init(storage->location, DICT_DATA_TYPE_STRING,
+			dstorage->username, svinst->base_dir, &dstorage->dict, &error);
+		if ( ret < 0 ) {
+			sieve_storage_set_critical(storage,
+				"Failed to initialize dict with data `%s' for user `%s': %s",
+				storage->location, dstorage->username, error);
+			*error_r = SIEVE_ERROR_TEMP_FAILURE;
+			return -1;
+		}
+	}
+
+	*dict_r = dstorage->dict;
+	return 0;
+}
+
+static void sieve_dict_storage_destroy(struct sieve_storage *storage)
+{
+	struct sieve_dict_storage *dstorage =
+		(struct sieve_dict_storage *)storage;
+
+	if ( dstorage->dict != NULL )
+		dict_deinit(&dstorage->dict);
+}
+
+/*
+ * Script access
+ */
+
+static struct sieve_script *sieve_dict_storage_get_script
+(struct sieve_storage *storage, const char *name)
+{
+	struct sieve_dict_storage *dstorage =
+		(struct sieve_dict_storage *)storage;
+	struct sieve_dict_script *dscript;
+
+	T_BEGIN {
+		dscript = sieve_dict_script_init(dstorage, name);
+	} T_END;
+
+	return &dscript->script;
+}
+
+/*
+ * Active script
+ */
+
+struct sieve_script *sieve_dict_storage_active_script_open
+(struct sieve_storage *storage)
+{
+	struct sieve_dict_storage *dstorage =
+		(struct sieve_dict_storage *)storage;
+	struct sieve_dict_script *dscript;
+
+	dscript = sieve_dict_script_init
+		(dstorage, storage->script_name);
+	if ( sieve_script_open(&dscript->script, NULL) < 0 ) {
+		struct sieve_script *script = &dscript->script;
+		sieve_script_unref(&script);
+		return NULL;
+	}
+
+	return &dscript->script;
+}
+
+int sieve_dict_storage_active_script_get_name
+(struct sieve_storage *storage, const char **name_r)
+{
+	if ( storage->script_name != NULL )
+		*name_r = storage->script_name;
+	else
+		*name_r = SIEVE_DICT_SCRIPT_DEFAULT;
+	return 0;
+}
+
+/*
+ * Driver definition
+ */
+
+const struct sieve_storage sieve_dict_storage = {
+	.driver_name = SIEVE_DICT_STORAGE_DRIVER_NAME,
+	.v = {
+		.alloc = sieve_dict_storage_alloc,
+		.destroy = sieve_dict_storage_destroy,
+		.init = sieve_dict_storage_init,
+
+		.get_script = sieve_dict_storage_get_script,
+
+		.get_script_sequence = sieve_dict_storage_get_script_sequence,
+		.script_sequence_next = sieve_dict_script_sequence_next,
+		.script_sequence_destroy = sieve_dict_script_sequence_destroy,
+
+		.active_script_get_name = sieve_dict_storage_active_script_get_name,
+		.active_script_open = sieve_dict_storage_active_script_open,
+
+		// FIXME: impement management interface
+	}
+};
diff --git a/src/lib-sieve/storage/dict/sieve-dict-storage.h b/src/lib-sieve/storage/dict/sieve-dict-storage.h
new file mode 100644
index 0000000000000000000000000000000000000000..30e9b778f96415684489f4852ee8062cbdddfe57
--- /dev/null
+++ b/src/lib-sieve/storage/dict/sieve-dict-storage.h
@@ -0,0 +1,68 @@
+/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
+ */
+
+#ifndef __SIEVE_DICT_STORAGE_H
+#define __SIEVE_DICT_STORAGE_H
+
+#include "sieve.h"
+#include "sieve-script-private.h"
+#include "sieve-storage-private.h"
+
+#define DICT_SIEVE_PATH DICT_PATH_PRIVATE"sieve/"
+#define DICT_SIEVE_NAME_PATH DICT_SIEVE_PATH"name/"
+#define DICT_SIEVE_DATA_PATH DICT_SIEVE_PATH"data/"
+
+#define SIEVE_DICT_SCRIPT_DEFAULT "default"
+
+/*
+ * Storage class
+ */
+
+struct sieve_dict_storage {
+	struct sieve_storage storage;
+
+	const char *username;
+
+	struct dict *dict;
+};
+
+int sieve_dict_storage_get_dict
+	(struct sieve_dict_storage *dstorage, struct dict **dict_r,
+		enum sieve_error *error_r);
+
+struct sieve_script *sieve_dict_storage_active_script_open
+	(struct sieve_storage *storage);
+int sieve_dict_storage_active_script_get_name
+	(struct sieve_storage *storage, const char **name_r);
+
+/*
+ * Script class
+ */
+
+struct sieve_dict_script {
+	struct sieve_script script;
+
+	struct dict *dict;
+
+	pool_t data_pool;
+	const char *data_id;
+	const char *data;
+
+	const char *binpath;
+};
+
+struct sieve_dict_script *sieve_dict_script_init
+	(struct sieve_dict_storage *dstorage, const char *name);
+
+/*
+ * Script sequence
+ */
+
+struct sieve_script_sequence *sieve_dict_storage_get_script_sequence
+	(struct sieve_storage *storage, enum sieve_error *error_r);
+
+struct sieve_script *sieve_dict_script_sequence_next
+    (struct sieve_script_sequence *seq, enum sieve_error *error_r);
+void sieve_dict_script_sequence_destroy(struct sieve_script_sequence *seq);
+
+#endif
diff --git a/src/lib-sieve/storage/file/Makefile.am b/src/lib-sieve/storage/file/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..a6fda424fa10e69827efe1e17d0b6fc67f5234a5
--- /dev/null
+++ b/src/lib-sieve/storage/file/Makefile.am
@@ -0,0 +1,18 @@
+noinst_LTLIBRARIES = libsieve_storage_file.la
+
+AM_CPPFLAGS = \
+	$(LIBDOVECOT_INCLUDE) \
+	-I$(top_srcdir) \
+	-I$(top_srcdir)/src/lib-sieve
+
+libsieve_storage_file_la_SOURCES = \
+	sieve-file-script.c \
+	sieve-file-script-sequence.c \
+	sieve-file-storage-active.c \
+	sieve-file-storage-save.c \
+	sieve-file-storage-list.c \
+	sieve-file-storage-quota.c \
+	sieve-file-storage.c
+
+noinst_HEADERS = \
+	sieve-file-storage.h
diff --git a/src/lib-sieve/storage/file/sieve-file-script-sequence.c b/src/lib-sieve/storage/file/sieve-file-script-sequence.c
new file mode 100644
index 0000000000000000000000000000000000000000..6822e71019375c72d94edcdfa5ee8334909f797f
--- /dev/null
+++ b/src/lib-sieve/storage/file/sieve-file-script-sequence.c
@@ -0,0 +1,239 @@
+/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str.h"
+#include "array.h"
+#include "eacces-error.h"
+
+#include "sieve-common.h"
+#include "sieve-script-private.h"
+
+#include "sieve-file-storage.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+/*
+ * Script sequence
+ */
+
+struct sieve_file_script_sequence {
+	struct sieve_script_sequence seq;
+	pool_t pool;
+
+	ARRAY_TYPE(const_string) script_files;
+	unsigned int index;
+};
+
+static int sieve_file_script_sequence_read_dir
+(struct sieve_file_script_sequence *fseq, const char *path)
+{
+	struct sieve_storage *storage = fseq->seq.storage;
+	DIR *dirp;
+	int ret = 0;
+
+	/* Open the directory */
+	if ( (dirp = opendir(path)) == NULL ) {
+		switch ( errno ) {
+		case ENOENT:
+			sieve_storage_set_error(storage,
+				SIEVE_ERROR_NOT_FOUND,
+				"Script sequence location not found");
+			break;
+		case EACCES:
+			sieve_storage_set_error(storage,
+				SIEVE_ERROR_NO_PERMISSION,
+				"Script sequence location not accessible");
+			sieve_storage_sys_error(storage,
+				"Failed to open sieve sequence: "
+				"%s",	eacces_error_get("stat", path));
+			break;
+		default:
+			sieve_storage_set_critical(storage,
+				"Failed to open sieve sequence: "
+				"opendir(%s) failed: %m", path);
+			break;
+		}
+		return -1;
+	}
+
+	/* Read and sort script files */
+	for (;;) {
+		const char *const *files;
+		unsigned int count, i;
+		const char *file;
+		struct dirent *dp;
+		struct stat st;
+
+		errno = 0;
+		if ( (dp=readdir(dirp)) == NULL )
+			break;
+
+		if ( !sieve_script_file_has_extension(dp->d_name) )
+			continue;
+
+		file = NULL;
+		T_BEGIN {
+			if ( path[strlen(path)-1] == '/' )
+				file = t_strconcat(path, dp->d_name, NULL);
+			else
+				file = t_strconcat(path, "/", dp->d_name, NULL);
+
+			if ( stat(file, &st) == 0 && S_ISREG(st.st_mode) )
+				file = p_strdup(fseq->pool, file);
+			else
+				file = NULL;
+		} T_END;
+
+		if (file == NULL)
+			continue;
+		
+		/* Insert into sorted array */
+		files = array_get(&fseq->script_files, &count);
+		for ( i = 0; i < count; i++ ) {
+			if ( strcmp(file, files[i]) < 0 )
+				break;
+		}
+
+		if ( i == count )
+			array_append(&fseq->script_files, &file, 1);
+		else
+			array_insert(&fseq->script_files, i, &file, 1);
+	} 
+
+	if ( errno != 0 ) {
+		sieve_storage_set_critical(storage,
+			"Failed to read sequence directory: "
+			"readdir(%s) failed: %m", path);
+		ret = -1;
+	}
+
+	/* Close the directory */
+	if ( dirp != NULL && closedir(dirp) < 0 ) {
+		sieve_storage_sys_error(storage,
+			"Failed to close sequence directory: "
+			"closedir(%s) failed: %m", path);
+	}
+	return ret;
+}
+
+struct sieve_script_sequence *sieve_file_storage_get_script_sequence
+(struct sieve_storage *storage, enum sieve_error *error_r)
+{
+	struct sieve_file_storage *fstorage =
+		(struct sieve_file_storage *)storage;
+	struct sieve_file_script_sequence *fseq = NULL;
+	const char *name = storage->script_name;
+	const char *file;
+	pool_t pool;
+	struct stat st;
+
+	/* Specified path can either be a regular file or a directory */
+	if ( stat(fstorage->path, &st) != 0 ) {
+		switch ( errno ) {
+		case ENOENT:
+			sieve_storage_set_error(storage,
+				SIEVE_ERROR_NOT_FOUND,
+				"Script sequence location not found");
+			break;
+		case EACCES:
+			sieve_storage_set_error(storage,
+				SIEVE_ERROR_NO_PERMISSION,
+				"Script sequence location not accessible");
+			sieve_storage_sys_error(storage,
+				"Failed to open sieve sequence: "
+				"%s",	eacces_error_get("stat", fstorage->path));
+			break;
+		default:
+			sieve_storage_set_critical(storage,
+				"Failed to open sieve sequence: "
+				"stat(%s) failed: %m", fstorage->path);
+			break;
+		}
+		*error_r = storage->error_code;
+		return NULL;
+	}
+
+	/* Create sequence object */
+	pool = pool_alloconly_create("sieve_file_script_sequence", 1024);
+	fseq = p_new(pool, struct sieve_file_script_sequence, 1);
+	fseq->pool = pool;
+	sieve_script_sequence_init(&fseq->seq, storage);
+	i_array_init(&fseq->script_files, 16);
+
+	if ( S_ISDIR(st.st_mode) ) {
+		/* Path is directory */
+		if (name == 0 || *name == '\0') {
+			/* Read all '.sieve' files in directory */
+			if (sieve_file_script_sequence_read_dir
+				(fseq, fstorage->path) < 0) {
+				*error_r = storage->error_code;
+				sieve_file_script_sequence_destroy(&fseq->seq);
+				return NULL;
+			}
+
+		}	else {
+			/* Read specific script file */
+			file = sieve_script_file_from_name(name);
+			file = sieve_file_storage_path_extend(fstorage, file);
+			file = p_strdup(pool, file);
+			array_append(&fseq->script_files, &file, 1);
+		}
+
+	} else {
+		/* Path is a file
+		   (apparently; we'll see about that once it is opened) */
+		file = p_strdup(pool, fstorage->path);
+		array_append(&fseq->script_files, &file, 1);
+	}
+		
+	return &fseq->seq;
+}
+
+struct sieve_script *sieve_file_script_sequence_next
+(struct sieve_script_sequence *seq, enum sieve_error *error_r)
+{
+	struct sieve_file_script_sequence *fseq =
+		(struct sieve_file_script_sequence *)seq;
+	struct sieve_file_storage *fstorage =
+		(struct sieve_file_storage *)seq->storage;
+	struct sieve_file_script *fscript;
+	const char *const *files;
+	unsigned int count;
+
+	if ( error_r != NULL )
+		*error_r = SIEVE_ERROR_NONE;
+
+	files = array_get(&fseq->script_files, &count);
+
+	fscript = NULL;
+	while ( fseq->index < count ) {
+		fscript = sieve_file_script_open_from_filename
+			(fstorage, files[fseq->index++], NULL);
+		if (fscript == NULL) {
+			if (seq->storage->error_code != SIEVE_ERROR_NOT_FOUND)
+				break;
+			sieve_storage_clear_error(seq->storage);
+		}
+	}
+
+	if (fscript == NULL ) {
+		if ( error_r != NULL ) 
+			*error_r = seq->storage->error_code;
+		return NULL;
+	}
+	return &fscript->script;
+}
+
+void sieve_file_script_sequence_destroy(struct sieve_script_sequence *seq)
+{
+	struct sieve_file_script_sequence *fseq =
+		(struct sieve_file_script_sequence *)seq;
+
+	array_free(&fseq->script_files);
+	pool_unref(&fseq->pool);
+}
diff --git a/src/lib-sieve/storage/file/sieve-file-script.c b/src/lib-sieve/storage/file/sieve-file-script.c
new file mode 100644
index 0000000000000000000000000000000000000000..b32468d475e1dce8942b4e2ad9cc5b87abf66d9d
--- /dev/null
+++ b/src/lib-sieve/storage/file/sieve-file-script.c
@@ -0,0 +1,757 @@
+/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "mempool.h"
+#include "abspath.h"
+#include "istream.h"
+#include "eacces-error.h"
+
+#include "sieve-binary.h"
+#include "sieve-script-private.h"
+
+#include "sieve-file-storage.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <time.h>
+#include <fcntl.h>
+
+/*
+ * Filename to name/name to filename
+ */
+
+const char *sieve_script_file_get_scriptname(const char *filename)
+{
+	const char *ext;
+
+	/* Extract the script name */
+	ext = strrchr(filename, '.');
+	if ( ext == NULL || ext == filename ||
+		strcmp(ext, "."SIEVE_SCRIPT_FILEEXT) != 0 )
+		return NULL;
+
+	return t_strdup_until(filename, ext);
+}
+
+bool sieve_script_file_has_extension(const char *filename)
+{
+	return ( sieve_script_file_get_scriptname(filename) != NULL );
+}
+
+const char *sieve_script_file_from_name(const char *name)
+{
+	return t_strconcat(name, "."SIEVE_SCRIPT_FILEEXT, NULL);
+}
+
+/*
+ * Common error handling
+ */
+
+static void sieve_file_script_handle_error
+(struct sieve_file_script *fscript, const char *path,
+	enum sieve_error *error_r)
+{
+	struct sieve_script *script = &fscript->script;
+
+	switch ( errno ) {
+	case ENOENT:
+		sieve_script_sys_debug(script, "File `%s' not found", t_abspath(path));
+		sieve_script_set_error(script,
+			SIEVE_ERROR_NOT_FOUND,
+			"Sieve script `%s' not found",
+			fscript->script.name);
+		*error_r = SIEVE_ERROR_NOT_FOUND;
+		break;
+	case EACCES:
+		sieve_script_set_critical(script,
+			"Failed to stat sieve script: %s", eacces_error_get("stat", path));
+		*error_r = SIEVE_ERROR_NO_PERMISSION;
+		break;
+	default:
+		sieve_script_set_critical(script,
+			"Failed to stat sieve script: stat(%s) failed: %m", path);
+		*error_r = SIEVE_ERROR_TEMP_FAILURE;
+		break;
+	}
+}
+
+/*
+ *
+ */
+
+static struct sieve_file_script *sieve_file_script_alloc(void)
+{
+	struct sieve_file_script *fscript;
+	pool_t pool;
+
+	pool = pool_alloconly_create("sieve_file_script", 1024);
+	fscript = p_new(pool, struct sieve_file_script, 1);
+	fscript->script = sieve_file_script;
+	fscript->script.pool = pool;
+
+	return fscript;
+}
+
+struct sieve_file_script *sieve_file_script_init_from_filename
+(struct sieve_file_storage *fstorage, const char *filename,
+	const char *scriptname)
+{
+	struct sieve_storage *storage = &fstorage->storage;
+	struct sieve_file_script *fscript = NULL;
+
+	/* Prevent initializing the active script link as a script when it
+	 * resides in the sieve storage directory.
+	 */
+	if ( scriptname != NULL && fstorage->link_path != NULL &&
+		*(fstorage->link_path) == '\0' ) {
+		if ( strcmp(filename, fstorage->active_fname) == 0 ) {
+			sieve_storage_set_error(storage,
+				SIEVE_ERROR_NOT_FOUND,
+				"Script `%s' does not exist.", scriptname);
+			return NULL;
+		}
+	}
+
+	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);
+	return fscript;
+}
+
+struct sieve_file_script *sieve_file_script_open_from_filename
+(struct sieve_file_storage *fstorage, const char *filename,
+	const char *scriptname)
+{
+	struct sieve_file_script *fscript;
+	enum sieve_error error;
+
+	fscript = sieve_file_script_init_from_filename
+		(fstorage, filename, scriptname);
+	if ( fscript == NULL )
+		return NULL;
+
+	if ( sieve_script_open(&fscript->script, &error) < 0 ) {
+		struct sieve_script *script = &fscript->script;
+		sieve_script_unref(&script);
+		return NULL;
+	}
+
+	return fscript;
+}
+
+struct sieve_file_script *sieve_file_script_init_from_name
+(struct sieve_file_storage *fstorage, const char *name)
+{
+	return sieve_file_script_init_from_filename
+		(fstorage, sieve_script_file_from_name(name), name);
+}
+
+struct sieve_file_script *sieve_file_script_init_from_path
+(struct sieve_file_storage *fstorage, const char *path,
+	const char *scriptname, enum sieve_error *error_r)
+{
+	struct sieve_instance *svinst = fstorage->storage.svinst;
+	struct sieve_file_storage *fsubstorage;
+	struct sieve_file_script *fscript;
+	struct sieve_storage *substorage;
+	enum sieve_error error;
+
+	if ( error_r != NULL )
+		*error_r = SIEVE_ERROR_NONE;
+	else
+		error_r = &error;
+
+	fsubstorage = sieve_file_storage_init_from_path
+		(svinst, path, 0, error_r);
+	if (fsubstorage == NULL)
+		return NULL;
+	substorage = &fsubstorage->storage;
+
+	fscript = sieve_file_script_alloc();
+	sieve_script_init(&fscript->script,
+		substorage, &sieve_file_script, path, scriptname);
+	sieve_storage_unref(&substorage);
+
+	return fscript;
+}
+
+struct sieve_file_script *sieve_file_script_open_from_path
+(struct sieve_file_storage *fstorage, const char *path,
+	const char *scriptname, enum sieve_error *error_r)
+{
+	struct sieve_storage *storage = &fstorage->storage;
+	struct sieve_file_script *fscript;
+	enum sieve_error error;
+
+	if ( error_r != NULL )
+		*error_r = SIEVE_ERROR_NONE;
+	else
+		error_r = &error;
+
+	fscript = sieve_file_script_init_from_path
+		(fstorage, path, scriptname, error_r);
+	if (fscript == NULL) {
+		sieve_storage_set_error(storage,
+			*error_r, "Failed to open script");
+		return NULL;
+	}
+
+	if ( sieve_script_open(&fscript->script, error_r) < 0 ) {
+		struct sieve_script *script = &fscript->script;
+		const char *errormsg;
+
+		errormsg = sieve_script_get_last_error(&fscript->script, error_r);
+		sieve_storage_set_error(storage,
+			*error_r, "%s", errormsg);
+		sieve_script_unref(&script);
+		return NULL;
+	}
+
+	return fscript;
+}
+
+/*
+ * Open
+ */
+
+static int sieve_file_script_stat
+(const char *path, struct stat *st, struct stat *lnk_st)
+{
+	if ( lstat(path, st) < 0 )
+		return -1;
+
+	*lnk_st = *st;
+
+	if ( S_ISLNK(st->st_mode) && stat(path, st) < 0 )
+		return -1;
+
+	return 1;
+}
+
+static int sieve_file_script_open
+(struct sieve_script *script, enum sieve_error *error_r)
+{
+	struct sieve_file_script *fscript =
+		(struct sieve_file_script *)script;
+	struct sieve_storage *storage = script->storage;
+	struct sieve_file_storage *fstorage =
+		(struct sieve_file_storage *)storage;
+	pool_t pool = script->pool;
+	const char *filename, *name, *path;
+	const char *dirpath, *basename, *binpath, *binprefix;
+	struct stat st;
+	struct stat lnk_st;
+	bool success = TRUE;
+	int ret;
+
+	filename = fscript->filename;
+	basename = NULL;
+	name = script->name;
+	path = fstorage->path;
+
+	if (name == NULL)
+		name = storage->script_name;
+
+	T_BEGIN {
+		if ( (ret=sieve_file_script_stat(path, &st, &lnk_st)) > 0 ) {
+			if ( S_ISDIR(st.st_mode) ) {
+				/* Path is directory */
+				if ( (filename == NULL || *filename == '\0') &&
+					name != NULL && *name != '\0' ) {
+					/* Name is used to find actual filename */
+					filename = sieve_script_file_from_name(name);
+					basename = name;
+				}
+				if ( filename == NULL && *filename == '\0' ) {
+					sieve_script_set_critical(script,
+						"Sieve script file path '%s' is a directory.", path);
+					*error_r = SIEVE_ERROR_TEMP_FAILURE;
+					success = FALSE;
+				}	else {
+					/* Extend storage path with filename */
+					if (name == NULL) {
+						if ( basename == NULL &&
+							(basename=sieve_script_file_get_scriptname(filename)) == NULL )
+							basename = filename;
+						name = basename;
+					} else if (basename == NULL) {
+						basename = name;
+					}
+					dirpath = path;
+
+					path = sieve_file_storage_path_extend(fstorage, filename);
+					ret = sieve_file_script_stat(path, &st, &lnk_st);
+				}
+
+			} else {
+
+				/* Extract filename from path */
+				filename = strrchr(path, '/');
+				if ( filename == NULL ) {
+					dirpath = "";
+					filename = path;
+				} else {
+					dirpath = t_strdup_until(path, filename);
+					filename++;
+				}
+
+				if ( (basename=sieve_script_file_get_scriptname(filename)) == NULL )
+					basename = filename;
+
+				if ( name == NULL )
+					name = basename;
+			}
+		} else {
+			basename = name;
+		}
+
+		if ( success ) {
+			if ( ret <= 0 ) {
+				if ( fscript->script.name == NULL )
+					fscript->script.name = p_strdup(pool, basename);
+				sieve_file_script_handle_error(fscript, path, error_r);
+				success = FALSE;
+			} else if (!S_ISREG(st.st_mode) ) {
+				sieve_script_set_critical(script,
+					"Sieve script file '%s' is not a regular file.", path);
+				*error_r = SIEVE_ERROR_TEMP_FAILURE;
+				success = FALSE;
+			}
+		}
+
+		if ( success ) {
+			const char *bpath, *bfile, *bprefix;
+
+			if ( storage->bin_dir != NULL ) {
+				bpath = storage->bin_dir;
+				bfile = sieve_binfile_from_name(name);
+				bprefix = name;
+
+			} else {
+				bpath = dirpath;
+				bfile = sieve_binfile_from_name(basename);
+				bprefix = basename;
+			}
+
+			if ( *bpath == '\0' ) {
+				binpath = bfile;
+				binprefix = bprefix;
+			} else 	if ( bpath[strlen(bpath)-1] == '/' ) {
+				binpath = t_strconcat(bpath, bfile, NULL);
+				binprefix = t_strconcat(bpath, bprefix, NULL);
+			} else {
+				binpath = t_strconcat(bpath, "/", bfile, NULL);
+				binprefix = t_strconcat(bpath, "/", bprefix, NULL);
+			}
+
+			fscript->st = st;
+			fscript->lnk_st = lnk_st;
+			fscript->path = p_strdup(pool, path);
+			fscript->filename = p_strdup(pool, filename);
+			fscript->dirpath = p_strdup(pool, dirpath);
+			fscript->binpath = p_strdup(pool, binpath);
+			fscript->binprefix = p_strdup(pool, binprefix);
+
+			if ( fscript->script.name == NULL || *fscript->script.name == '\0' ||
+				strcmp(fscript->script.name, basename) == 0 )
+				fscript->script.location = fscript->path;
+			else
+				fscript->script.location = p_strconcat
+					(pool, fscript->path, ";name=", fscript->script.name, NULL);
+
+			if ( fscript->script.name == NULL )
+				fscript->script.name = p_strdup(pool, basename);
+		}
+	} T_END;
+
+	return ( success ? 0 : -1 );
+}
+
+static int sieve_file_script_get_stream
+(struct sieve_script *script, struct istream **stream_r,
+	enum sieve_error *error_r)
+{
+	struct sieve_file_script *fscript = (struct sieve_file_script *)script;
+	struct stat st;
+	struct istream *result;
+	int fd;
+
+	if ( (fd=open(fscript->path, O_RDONLY)) < 0 ) {
+		sieve_file_script_handle_error
+			(fscript, fscript->path, error_r);
+		return -1;
+	}
+
+	if ( fstat(fd, &st) != 0 ) {
+		sieve_script_set_critical(script,
+			"Failed to open sieve script: fstat(fd=%s) failed: %m",
+			fscript->path);
+		*error_r = SIEVE_ERROR_TEMP_FAILURE;
+		result = NULL;
+	} else {
+		/* Re-check the file type just to be sure */
+		if ( !S_ISREG(st.st_mode) ) {
+			sieve_script_set_critical(script,
+				"Sieve script file `%s' is not a regular file", fscript->path);
+			*error_r = SIEVE_ERROR_TEMP_FAILURE;
+			result = NULL;
+		} else {
+			result = i_stream_create_fd_autoclose(&fd, SIEVE_FILE_READ_BLOCK_SIZE);
+			fscript->st = fscript->lnk_st = st;
+		}
+	}
+
+	if ( result == NULL ) {
+		/* Something went wrong, close the fd */
+		if ( fd >= 0 && close(fd) != 0 ) {
+			sieve_script_sys_error(script,
+				"Failed to close sieve script: "
+				"close(fd=%s) failed: %m", fscript->path);
+		}
+	}
+
+	*stream_r = result;
+	return 0;
+}
+
+/*
+ * Binary
+ */
+
+static int sieve_file_script_binary_read_metadata
+(struct sieve_script *script, struct sieve_binary_block *sblock,
+	sieve_size_t *offset ATTR_UNUSED)
+{
+	struct sieve_file_script *fscript = (struct sieve_file_script *)script;
+	struct sieve_binary *sbin = sieve_binary_block_get_binary(sblock);
+	time_t time = ( fscript->st.st_mtime > fscript->lnk_st.st_mtime ?
+		fscript->st.st_mtime : fscript->lnk_st.st_mtime );
+
+	if ( sieve_binary_mtime(sbin) <= time )
+		return 0;
+
+	return 1;
+}
+
+static struct sieve_binary *sieve_file_script_binary_load
+(struct sieve_script *script, enum sieve_error *error_r)
+{
+	struct sieve_file_script *fscript = (struct sieve_file_script *)script;
+	struct sieve_instance *svinst = script->storage->svinst;
+
+	return sieve_binary_open(svinst, fscript->binpath, script, error_r);
+}
+
+static int sieve_file_script_binary_save
+(struct sieve_script *script, struct sieve_binary *sbin, bool update,
+	enum sieve_error *error_r)
+{
+	struct sieve_storage *storage = script->storage;
+	struct sieve_file_script *fscript = (struct sieve_file_script *)script;
+
+	if ( storage->bin_dir != NULL &&
+		sieve_storage_setup_bindir(storage, 0700) < 0 )
+		return -1;
+
+	return sieve_binary_save(sbin, fscript->binpath, update,
+		fscript->st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO), error_r);
+}
+
+static const char *sieve_file_script_binary_get_prefix
+(struct sieve_script *script)
+{
+	struct sieve_file_script *fscript = (struct sieve_file_script *)script;
+	
+	return fscript->binprefix;
+}
+
+/*
+ * Management
+ */
+
+static int sieve_file_storage_script_is_active(struct sieve_script *script)
+{
+	struct sieve_file_script *fscript =
+		(struct sieve_file_script *) script;
+	struct sieve_file_storage *fstorage =
+		(struct sieve_file_storage *)script->storage;
+	const char *afile;
+	int ret = 0;
+
+	T_BEGIN {
+		ret = sieve_file_storage_active_script_get_file(fstorage, &afile);
+
+		if ( ret > 0 ) {
+		 	/* Is the requested script active? */
+			ret = ( strcmp(fscript->filename, afile) == 0 ? 1 : 0 );
+		}
+	} T_END;
+
+	return ret;
+}
+
+static int sieve_file_storage_script_delete(struct sieve_script *script)
+{
+	struct sieve_file_script *fscript =
+		(struct sieve_file_script *)script;
+	int ret = 0;
+
+	if ( sieve_file_storage_pre_modify(script->storage) < 0 )
+		return -1;
+
+	ret = unlink(fscript->path);
+	if ( ret < 0 ) {
+		if ( errno == ENOENT ) {
+			sieve_script_set_error(script,
+				SIEVE_ERROR_NOT_FOUND,
+				"Sieve script does not exist.");
+		} else {
+			sieve_script_set_critical(script,
+				"Performing unlink() failed on sieve file `%s': %m",
+				fscript->path);
+		}
+	}
+	return ret;
+}
+
+static int _sieve_file_storage_script_activate
+(struct sieve_file_script *fscript)
+{
+	struct sieve_script *script = &fscript->script;
+	struct sieve_storage *storage = script->storage;
+	struct sieve_file_storage *fstorage =
+		(struct sieve_file_storage *)storage;
+	struct stat st;
+	const char *link_path, *afile;
+	int activated = 0;
+	int ret;
+
+	/* Find out whether there is an active script, but recreate
+	 * the symlink either way. This way, any possible error in the symlink
+	 * resolves automatically. This step is only necessary to provide a
+	 * proper return value indicating whether the script was already active.
+	 */
+	ret = sieve_file_storage_active_script_get_file(fstorage, &afile);
+
+	/* Is the requested script already active? */
+	if ( ret <= 0 || strcmp(fscript->filename, afile) != 0 )
+		activated = 1;
+
+	/* Check the scriptfile we are trying to activate */
+	if ( lstat(fscript->path, &st) != 0 ) {
+		sieve_script_set_critical(script,
+		  "Failed to activate Sieve script: lstat(%s) failed: %m.",
+			fscript->path);
+		return -1;
+	}
+
+	/* Rescue a possible .dovecot.sieve regular file remaining from old
+	 * installations.
+	 */
+	if ( !sieve_file_storage_active_rescue_regular(fstorage) ) {
+		/* Rescue failed, manual intervention is necessary */
+		return -1;
+	}
+
+	/* Just try to create the symlink first */
+	link_path = t_strconcat
+	  ( fstorage->link_path, fscript->filename, NULL );
+
+ 	ret = symlink(link_path, fstorage->active_path);
+	if ( ret < 0 ) {
+		if ( errno == EEXIST ) {
+			ret = sieve_file_storage_active_replace_link(fstorage, link_path);
+			if ( ret < 0 ) {
+				return ret;
+			}
+		} else {
+			/* Other error, critical */
+			sieve_script_set_critical(script,
+				"Failed to activate Sieve script: "
+				"symlink(%s, %s) failed: %m",
+					link_path, fstorage->active_path);
+			return -1;
+		}
+	}
+	return activated;
+}
+
+static int sieve_file_storage_script_activate
+(struct sieve_script *script)
+{
+	struct sieve_file_script *fscript =
+		(struct sieve_file_script *)script;
+	int ret;
+
+	if ( sieve_file_storage_pre_modify(script->storage) < 0 )
+		return -1;
+
+	T_BEGIN {
+		ret = _sieve_file_storage_script_activate(fscript);
+	} T_END;
+
+	return ret;
+}
+
+static int sieve_file_storage_script_rename
+(struct sieve_script *script, const char *newname)
+{
+	struct sieve_file_script *fscript =
+		(struct sieve_file_script *)script;
+	struct sieve_storage *storage = script->storage;
+	struct sieve_file_storage *fstorage =
+		(struct sieve_file_storage *)storage;
+	const char *newpath, *newfile, *link_path;
+	int ret = 0;
+
+	if ( sieve_file_storage_pre_modify(storage) < 0 )
+		return -1;
+
+	T_BEGIN {
+		newfile = sieve_script_file_from_name(newname);
+		newpath = t_strconcat( fstorage->path, "/", newfile, NULL );
+
+		/* The normal rename() system call overwrites the existing file without
+		 * notice. Also, active scripts must not be disrupted by renaming a script.
+		 * That is why we use a link(newpath) [activate newpath] unlink(oldpath)
+		 */
+
+		/* Link to the new path */
+		ret = link(fscript->path, newpath);
+		if ( ret >= 0 ) {
+			/* Is the requested script active? */
+			if ( sieve_script_is_active(script) ) {
+				/* Active; make active link point to the new copy */
+				link_path = t_strconcat
+					( fstorage->link_path, newfile, NULL );
+
+				ret = sieve_file_storage_active_replace_link(fstorage, link_path);
+			}
+
+			if ( ret >= 0 ) {
+				/* If all is good, remove the old link */
+				if ( unlink(fscript->path) < 0 ) {
+					sieve_script_sys_error(script,
+						"Failed to clean up after rename: "
+						"unlink(%s) failed: %m", fscript->path);
+				}
+
+				if ( script->name != NULL && *script->name != '\0' )
+					script->name = p_strdup(script->pool, newname);
+				fscript->path = p_strdup(script->pool, newpath);
+				fscript->filename = p_strdup(script->pool, newfile);
+			} else {
+				/* If something went wrong, remove the new link to restore previous
+				 * state
+				 */
+				if ( unlink(newpath) < 0 ) {
+					sieve_script_sys_error(script,
+						"Failed to clean up after failed rename: "
+						"unlink(%s) failed: %m", newpath);
+				}
+			}
+		} else {
+			/* Our efforts failed right away */
+			switch ( errno ) {
+			case ENOENT:
+				sieve_script_set_error(script, SIEVE_ERROR_NOT_FOUND,
+					"Sieve script does not exist.");
+				break;
+			case EEXIST:
+				sieve_script_set_error(script, SIEVE_ERROR_EXISTS,
+					"A sieve script with that name already exists.");
+				break;
+			default:
+				sieve_script_set_critical(script,
+					"Failed to rename Sieve script: "
+					"link(%s, %s) failed: %m", fscript->path, newpath);
+			}
+		}
+	} T_END;
+
+	return ret;
+}
+
+/*
+ * Properties
+ */
+
+static int sieve_file_script_get_size
+(const struct sieve_script *script, uoff_t *size_r)
+{
+	struct sieve_file_script *fscript = (struct sieve_file_script *)script;
+
+	*size_r = fscript->st.st_size;
+	return 1;
+}
+
+const char *sieve_file_script_get_dirpath
+(const struct sieve_script *script)
+{
+	struct sieve_file_script *fscript = (struct sieve_file_script *)script;
+
+	if ( script->driver_name != sieve_file_script.driver_name )
+		return NULL;
+
+	return fscript->dirpath;
+}
+
+const char *sieve_file_script_get_path
+(const struct sieve_script *script)
+{
+	struct sieve_file_script *fscript = (struct sieve_file_script *)script;
+
+	if ( script->driver_name != sieve_file_script.driver_name )
+		return NULL;
+
+	return fscript->path;
+}
+
+/*
+ * Matching
+ */
+
+static bool sieve_file_script_equals
+(const struct sieve_script *script, const struct sieve_script *other)
+{
+	struct sieve_file_script *fscript =
+		(struct sieve_file_script *)script;
+	struct sieve_file_script *fother =
+		(struct sieve_file_script *)other;
+
+	return ( fscript->st.st_ino == fother->st.st_ino );
+}
+
+/*
+ * Driver definition
+ */
+
+const struct sieve_script sieve_file_script = {
+	.driver_name = SIEVE_FILE_STORAGE_DRIVER_NAME,
+	.v = {
+		.open = sieve_file_script_open,
+
+		.get_stream = sieve_file_script_get_stream,
+
+		.binary_read_metadata = sieve_file_script_binary_read_metadata,
+		.binary_load = sieve_file_script_binary_load,
+		.binary_save = sieve_file_script_binary_save,
+		.binary_get_prefix = sieve_file_script_binary_get_prefix,
+
+		.rename = sieve_file_storage_script_rename,
+		.delete = sieve_file_storage_script_delete,
+		.is_active = sieve_file_storage_script_is_active,
+		.activate = sieve_file_storage_script_activate,
+
+		.get_size = sieve_file_script_get_size,
+
+		.equals = sieve_file_script_equals
+	}
+};
+
diff --git a/src/lib-sieve/storage/file/sieve-file-storage-active.c b/src/lib-sieve/storage/file/sieve-file-storage-active.c
new file mode 100644
index 0000000000000000000000000000000000000000..0ba2d95578143ad957be51d0becc5b26c6d1bc5f
--- /dev/null
+++ b/src/lib-sieve/storage/file/sieve-file-storage-active.c
@@ -0,0 +1,428 @@
+/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "abspath.h"
+#include "ioloop.h"
+#include "hostpid.h"
+#include "file-copy.h"
+
+#include "sieve-file-storage.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+static int _file_path_cmp(const char *path1, const char *path2)
+{
+	const char *p1, *p2;
+	int ret;
+
+	p1 = path1; p2 = path2;
+	if (*p2 == '\0' && *p1 != '\0')
+		return 1;
+	if (*p1 == '\0' && *p2 != '\0')
+		return -1;
+	if (*p1 == '/' && *p2 != '/')
+		return 1;
+	if (*p2 == '/' && *p1 != '/')
+		return -1;
+	for (;;) {
+		const char *s1, *s2;
+		size_t size1, size2;
+
+		/* skip repeated slashes */
+		for (; *p1 == '/'; p1++);
+		for (; *p2 == '/'; p2++);
+		/* check for end of comparison */
+		if (*p1 == '\0' || *p2 == '\0')
+			break;
+		/* mark start of path element */
+		s1 = p1;
+		s2 = p2;
+		/* scan to end of path elements */
+		for (; *p1 != '\0' && *p1 != '/'; p1++);
+		for (; *p2 != '\0' && *p2 != '/'; p2++);
+		/* compare sizes */
+		size1 = p1 - s1;
+		size2 = p2 - s2;
+		if (size1 != size2)
+			return size1 - size2;
+		/* compare */
+		if (size1 > 0 && (ret=memcmp(s1, s2, size1)) != 0)
+			return ret;
+	}
+	if (*p1 == '\0') {
+		if (*p2 == '\0')
+			return 0;
+		return -1;
+	}
+	return 1;
+}
+
+/*
+ * Symlink manipulation
+ */
+
+static int sieve_file_storage_active_read_link
+(struct sieve_file_storage *fstorage, const char **link_r)
+{
+	struct sieve_storage *storage = &fstorage->storage;
+	int ret;
+
+	ret = t_readlink(fstorage->active_path, link_r);
+
+	if ( ret < 0 ) {
+		*link_r = NULL;
+
+		if ( errno == EINVAL ) {
+			/* Our symlink is no symlink. Report 'no active script'.
+			 * Activating a script will automatically resolve this, so
+			 * there is no need to panic on this one.
+			 */
+			if ( (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 ) {
+				sieve_storage_sys_warning(storage,
+					"Active sieve script symlink %s is no symlink.",
+				  fstorage->active_path);
+			}
+			return 0;
+		}
+
+		if ( errno == ENOENT ) {
+			/* Symlink not found */
+			return 0;
+		}
+
+		/* We do need to panic otherwise */
+		sieve_storage_set_critical(storage,
+			"Performing readlink() on active sieve symlink '%s' failed: %m",
+			fstorage->active_path);
+		return -1;
+	}
+
+	/* ret is now assured to be valid, i.e. > 0 */
+	return 1;
+}
+
+static const char *sieve_file_storage_active_parse_link
+(struct sieve_file_storage *fstorage, const char *link,
+	const char **scriptname_r)
+{
+	struct sieve_storage *storage = &fstorage->storage;
+	const char *fname, *scriptname, *scriptpath;
+
+	/* Split link into path and filename */
+	fname = strrchr(link, '/');
+	if ( fname != NULL ) {
+		scriptpath = t_strdup_until(link, fname+1);
+		fname++;
+	} else {
+		scriptpath = "";
+		fname = link;
+	}
+
+	/* Check the script name */
+	scriptname = sieve_script_file_get_scriptname(fname);
+
+	/* Warn if link is deemed to be invalid */
+	if ( scriptname == NULL ) {
+		sieve_storage_sys_warning(storage,
+			"Active sieve script symlink %s is broken: "
+			"invalid scriptname (points to %s).",
+			fstorage->active_path, link);
+		return NULL;
+	}
+
+	/* Check whether the path is any good */
+	if ( _file_path_cmp(scriptpath, fstorage->link_path) != 0 &&
+		_file_path_cmp(scriptpath, fstorage->path) != 0 ) {
+		sieve_storage_sys_warning(storage,
+			"Active sieve script symlink %s is broken: "
+			"invalid/unknown path to storage (points to %s).",
+			fstorage->active_path, link);
+		return NULL;
+	}
+
+	if ( scriptname_r != NULL )
+		*scriptname_r = scriptname;
+
+	return fname;
+}
+
+int sieve_file_storage_active_replace_link
+(struct sieve_file_storage *fstorage, const char *link_path)
+{
+	struct sieve_storage *storage = &fstorage->storage;
+	const char *active_path_new;
+	struct timeval *tv, tv_now;
+	int ret = 0;
+
+	tv = &ioloop_timeval;
+
+	for (;;) {
+		/* First the new symlink is created with a different filename */
+		active_path_new = t_strdup_printf
+			("%s-new.%s.P%sM%s.%s",
+				fstorage->active_path,
+				dec2str(tv->tv_sec), my_pid,
+				dec2str(tv->tv_usec), my_hostname);
+
+		ret = symlink(link_path, active_path_new);
+
+		if ( ret < 0 ) {
+			/* If link exists we try again later */
+			if ( errno == EEXIST ) {
+				/* Wait and try again - very unlikely */
+				sleep(2);
+				tv = &tv_now;
+				if (gettimeofday(&tv_now, NULL) < 0)
+					i_fatal("gettimeofday(): %m");
+				continue;
+			}
+
+			/* Other error, critical */
+			sieve_storage_set_critical(storage,
+				"Creating symlink() %s to %s failed: %m",
+				active_path_new, link_path);
+			return -1;
+		}
+
+		/* Link created */
+		break;
+	}
+
+	/* Replace the existing link. This activates the new script */
+	ret = rename(active_path_new, fstorage->active_path);
+
+	if ( ret < 0 ) {
+		/* Failed; created symlink must be deleted */
+		(void)unlink(active_path_new);
+		sieve_storage_set_critical(storage,
+			"Performing rename() %s to %s failed: %m",
+			active_path_new, fstorage->active_path);
+		return -1;
+	}
+
+	return 1;
+}
+
+/*
+ * Active script properties
+ */
+
+int sieve_file_storage_active_script_get_file
+(struct sieve_file_storage *fstorage, const char **file_r)
+{
+	const char *link, *scriptfile;
+	int ret;
+
+	*file_r = NULL;
+
+	/* Read the active link */
+	if ( (ret=sieve_file_storage_active_read_link(fstorage, &link)) <= 0 )
+		return ret;
+
+	/* Parse the link */
+	scriptfile = sieve_file_storage_active_parse_link(fstorage, link, NULL);
+
+	if (scriptfile == NULL) {
+		/* Obviously, someone has been playing with our symlink:
+		 * ignore this situation and report 'no active script'.
+		 * Activation should fix this situation.
+		 */
+		return 0;
+	}
+
+	*file_r = scriptfile;
+	return 1;
+}
+
+int sieve_file_storage_active_script_get_name
+(struct sieve_storage *storage, const char **name_r)
+{
+	struct sieve_file_storage *fstorage =
+		(struct sieve_file_storage *)storage;
+	const char *link;
+	int ret;
+
+	*name_r = NULL;
+
+	/* Read the active link */
+	if ( (ret=sieve_file_storage_active_read_link
+		(fstorage, &link)) <= 0 )
+		return ret;
+
+	if ( sieve_file_storage_active_parse_link
+		(fstorage, link, name_r) == NULL ) {
+		/* Obviously, someone has been playing with our symlink:
+		 * ignore this situation and report 'no active script'.
+		 * Activation should fix this situation.
+		 */
+		return 0;
+	}
+
+	return 1;
+}
+
+/*
+ * Active script
+ */ 
+
+struct sieve_script *sieve_file_storage_active_script_open
+(struct sieve_storage *storage)
+{
+	struct sieve_file_storage *fstorage =
+		(struct sieve_file_storage *)storage;
+	struct sieve_file_script *fscript;
+	const char *scriptfile, *link;
+	int ret;
+
+	sieve_storage_clear_error(storage);
+
+	/* Read the active link */
+	if ( (ret=sieve_file_storage_active_read_link(fstorage, &link)) <= 0 ) {
+		if ( ret < 0 )
+			return NULL;
+
+		/* Try to open the active_path as a regular file */
+		fscript = sieve_file_script_open_from_path(fstorage,
+			fstorage->active_path, NULL, NULL);
+		if ( fscript == NULL ) {
+			sieve_storage_set_critical(storage,
+				"Failed to open active path `%s' as regular file",
+				fstorage->active_path);
+			return NULL;
+		}
+
+		return &fscript->script;
+	} 
+
+	/* Parse the link */
+	scriptfile = sieve_file_storage_active_parse_link(fstorage, link, NULL);
+	if (scriptfile == NULL) {
+		/* Obviously someone has been playing with our symlink,
+		 * ignore this situation and report 'no active script'.
+		 * Activation should fix this situation.
+		 */
+		return NULL;
+	}
+
+	fscript = sieve_file_script_open_from_path(fstorage,
+		fstorage->active_path,
+		sieve_script_file_get_scriptname(scriptfile),
+		NULL);
+	if ( fscript == NULL && storage->error_code == SIEVE_ERROR_NOT_FOUND ) {
+		sieve_storage_sys_warning(storage,
+			"Active sieve script symlink %s points to non-existent script "
+			"(points to %s).", fstorage->active_path, link);
+	}
+	return (fscript != NULL ? &fscript->script : NULL);
+}
+
+int sieve_file_storage_active_script_get_last_change
+(struct sieve_storage *storage, time_t *last_change_r)
+{
+	struct sieve_file_storage *fstorage =
+		(struct sieve_file_storage *)storage;
+	struct stat st;
+
+	/* Try direct lstat first */
+	if ( lstat(fstorage->active_path, &st) == 0 ) {
+		if ( !S_ISLNK(st.st_mode) ) {
+			*last_change_r = st.st_mtime;
+			return 0;
+		}
+	}
+	/* Check error */
+	else if ( errno != ENOENT ) {
+		sieve_storage_set_critical(storage,
+			"lstat(%s) failed: %m", fstorage->active_path);
+	}
+
+	/* Fall back to statting storage directory */
+	return sieve_storage_get_last_change(storage, last_change_r);
+}
+
+bool sieve_file_storage_active_rescue_regular
+(struct sieve_file_storage *fstorage)
+{
+	struct sieve_storage *storage = &fstorage->storage;
+	struct stat st;
+
+	/* Stat the file */
+	if ( lstat(fstorage->active_path, &st) != 0 ) {
+		if ( errno != ENOENT ) {
+			sieve_storage_set_critical(storage,
+				"Failed to stat active sieve script symlink (%s): %m.",
+				fstorage->active_path);
+			return FALSE;
+		}
+		return TRUE;
+	}
+
+	if ( S_ISLNK( st.st_mode ) ) {
+		sieve_storage_sys_debug(storage,
+			"Nothing to rescue %s.", fstorage->active_path);
+		return TRUE; /* Nothing to rescue */
+	}
+
+	/* Only regular files can be rescued */
+	if ( S_ISREG( st.st_mode ) ) {
+		const char *dstpath;
+		bool result = TRUE;
+
+ 		T_BEGIN {
+
+			dstpath = t_strconcat( fstorage->path, "/",
+				sieve_script_file_from_name("dovecot.orig"), NULL );
+			if ( file_copy(fstorage->active_path, dstpath, 1) < 1 ) {
+				sieve_storage_set_critical(storage,
+					"Active sieve script file '%s' is a regular file "
+					"and copying it to the script storage as '%s' failed. "
+					"This needs to be fixed manually.",
+					fstorage->active_path, dstpath);
+				result = FALSE;
+			} else {
+				sieve_storage_sys_info(storage,
+					"Moved active sieve script file '%s' "
+					"to script storage as '%s'.",
+					fstorage->active_path, dstpath);
+			}
+		} T_END;
+
+		return result;
+	}
+
+	sieve_storage_set_critical(storage,
+		"Active sieve script file '%s' is no symlink nor a regular file. "
+		"This needs to be fixed manually.", fstorage->active_path);
+	return FALSE;
+}
+
+int sieve_file_storage_deactivate(struct sieve_storage *storage)
+{
+	struct sieve_file_storage *fstorage =
+		(struct sieve_file_storage *)storage;
+	int ret;
+
+	if ( sieve_file_storage_pre_modify(storage) < 0 )
+		return -1;
+
+	if ( !sieve_file_storage_active_rescue_regular(fstorage) )
+		return -1;
+
+	/* Delete the symlink, so no script is active */
+	ret = unlink(fstorage->active_path);
+
+	if ( ret < 0 ) {
+		if ( errno != ENOENT ) {
+			sieve_storage_set_critical(storage,
+				"Failed to deactivate Sieve: "
+				"unlink(%s) failed: %m", fstorage->active_path);
+			return -1;
+		} else {
+			return 0;
+		}
+	}
+	return 1;
+}
diff --git a/src/lib-sieve/storage/file/sieve-file-storage-list.c b/src/lib-sieve/storage/file/sieve-file-storage-list.c
new file mode 100644
index 0000000000000000000000000000000000000000..e01acc3a34bd1317a3abdb09496010f3e52068a7
--- /dev/null
+++ b/src/lib-sieve/storage/file/sieve-file-storage-list.c
@@ -0,0 +1,125 @@
+/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str.h"
+
+#include "sieve-common.h"
+#include "sieve-script-private.h"
+
+#include "sieve-file-storage.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+struct sieve_file_list_context {
+	struct sieve_storage_list_context context;
+	pool_t pool;
+
+	const char *active;
+	const char *dir;
+	DIR *dirp;
+};
+
+struct sieve_storage_list_context *sieve_file_storage_list_init
+(struct sieve_storage *storage)
+{
+	struct sieve_file_storage *fstorage =
+		(struct sieve_file_storage *)storage;
+	struct sieve_file_list_context *flctx;
+	const char *active = NULL;
+	pool_t pool;
+	DIR *dirp;
+
+	/* Open the directory */
+	if ( (dirp = opendir(fstorage->path)) == NULL ) {
+		sieve_storage_set_critical(storage,
+			"Failed to list scripts: "
+			"opendir(%s) failed: %m", fstorage->path);
+		return NULL;
+	}
+
+	T_BEGIN {
+		/* Get the name of the active script */
+		if ( sieve_file_storage_active_script_get_file(fstorage, &active) < 0) {
+			flctx = NULL;
+		} else {
+			pool = pool_alloconly_create("sieve_file_list_context", 1024);
+			flctx = p_new(pool, struct sieve_file_list_context, 1);
+			flctx->pool = pool;
+			flctx->dirp = dirp;
+			flctx->active = ( active != NULL ? p_strdup(pool, active) : NULL );
+		}
+	} T_END;
+
+	if ( flctx == NULL ) {
+		if ( closedir(dirp) < 0) {
+			sieve_storage_sys_error(storage,
+				"closedir(%s) failed: %m", fstorage->path);	
+		}
+		return NULL;
+	}
+	return &flctx->context;
+}
+
+const char *sieve_file_storage_list_next
+(struct sieve_storage_list_context *ctx, bool *active)
+{
+	struct sieve_file_list_context *flctx =
+		(struct sieve_file_list_context *)ctx;
+	const struct sieve_file_storage *fstorage =
+		(const struct sieve_file_storage *)ctx->storage;
+	struct dirent *dp;
+	const char *scriptname;
+
+	*active = FALSE;
+
+	for (;;) {
+		if ( (dp = readdir(flctx->dirp)) == NULL )
+			return NULL;
+
+		scriptname = sieve_script_file_get_scriptname(dp->d_name);
+		if (scriptname != NULL ) {
+			/* Don't list our active sieve script link if the link
+			 * resides in the script dir (generally a bad idea).
+			 */
+			if ( *(fstorage->link_path) == '\0' &&
+				strcmp(fstorage->active_fname, dp->d_name) == 0 )
+				continue;
+
+			break;
+		}
+	}
+
+	if ( flctx->active != NULL && strcmp(dp->d_name, flctx->active) == 0 ) {
+		*active = TRUE;
+		flctx->active = NULL;
+	}
+
+	return scriptname;
+}
+
+int sieve_file_storage_list_deinit(struct sieve_storage_list_context *lctx)
+{
+	struct sieve_file_list_context *flctx =
+		(struct sieve_file_list_context *)lctx;
+	const struct sieve_file_storage *fstorage =
+		(const struct sieve_file_storage *)lctx->storage;
+
+	if (closedir(flctx->dirp) < 0) {
+		sieve_storage_sys_error(lctx->storage,
+			"closedir(%s) failed: %m", fstorage->path);
+	}
+
+	pool_unref(&flctx->pool);
+
+	// FIXME: return error here if something went wrong during listing
+	return 0;
+}
+
+
+
+
diff --git a/src/lib-sievestorage/sieve-storage-quota.c b/src/lib-sieve/storage/file/sieve-file-storage-quota.c
similarity index 54%
rename from src/lib-sievestorage/sieve-storage-quota.c
rename to src/lib-sieve/storage/file/sieve-file-storage-quota.c
index efc5e0a68dde21802fd00c0321e52f92dfa0bf7a..2054445d63ef96f8034880278f6c313b1b9f363a 100644
--- a/src/lib-sievestorage/sieve-storage-quota.c
+++ b/src/lib-sieve/storage/file/sieve-file-storage-quota.c
@@ -6,11 +6,8 @@
 
 #include "sieve.h"
 #include "sieve-script.h"
-#include "sieve-script-file.h"
 
-#include "sieve-storage-private.h"
-#include "sieve-storage-script.h"
-#include "sieve-storage-quota.h"
+#include "sieve-file-storage.h"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -21,54 +18,22 @@
 #include <unistd.h>
 #include <fcntl.h>
 
-bool sieve_storage_quota_validsize
-(struct sieve_storage *storage, size_t size, uint64_t *limit_r)
-{
-	uint64_t max_size;
-
-	max_size = sieve_max_script_size(storage->svinst);
-	if ( max_size > 0 && size > max_size ) {
-		*limit_r = max_size;
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-uint64_t sieve_storage_quota_max_script_size
-(struct sieve_storage *storage)
-{
-	return sieve_max_script_size(storage->svinst);
-}
-
-int sieve_storage_quota_havespace
+int sieve_file_storage_quota_havespace
 (struct sieve_storage *storage, const char *scriptname, size_t size,
 	enum sieve_storage_quota *quota_r, uint64_t *limit_r)
 {
+	struct sieve_file_storage *fstorage =
+		(struct sieve_file_storage *)storage;
 	struct dirent *dp;
 	DIR *dirp;
 	uint64_t script_count = 1;
 	uint64_t script_storage = size;
 	int result = 1;
 
-	*limit_r = 0;
-	*quota_r = SIEVE_STORAGE_QUOTA_NONE;
-
-	/* Check the script size */
-	if ( !sieve_storage_quota_validsize(storage, size, limit_r) ) {
-		*quota_r = SIEVE_STORAGE_QUOTA_MAXSIZE;
-        return 0;
-    }
-
-	/* Do we need to scan the storage (quota enabled) ? */
-	if ( storage->max_scripts == 0 && storage->max_storage == 0 ) {
-		return 1;
-	}
-
 	/* Open the directory */
-	if ( (dirp = opendir(storage->dir)) == NULL ) {
-		sieve_storage_set_critical
-			(storage, "quota: opendir(%s) failed: %m", storage->dir);
+	if ( (dirp = opendir(fstorage->path)) == NULL ) {
+		sieve_storage_set_critical(storage,
+			"quota: opendir(%s) failed: %m", fstorage->path);
 		return -1;
 	}
 
@@ -81,15 +46,15 @@ int sieve_storage_quota_havespace
 		errno = 0;
 		if ( (dp = readdir(dirp)) == NULL ) {
 			if ( errno != 0 ) {
-				sieve_storage_set_critical
-		            (storage, "quota: readdir(%s) failed: %m", storage->dir);
+				sieve_storage_set_critical(storage,
+					"quota: readdir(%s) failed: %m", fstorage->path);
 				result = -1;
 			}
 			break;
 		}
 
 		/* Parse filename */
-		name = sieve_scriptfile_get_script_name(dp->d_name);
+		name = sieve_script_file_get_scriptname(dp->d_name);
 
 		/* Ignore non-script files */
 		if ( name == NULL )
@@ -98,14 +63,14 @@ int sieve_storage_quota_havespace
 		/* Don't list our active sieve script link if the link
 		 * resides in the script dir (generally a bad idea).
 		 */
-		if ( *(storage->link_path) == '\0' &&
-			strcmp(storage->active_fname, dp->d_name) == 0 )
+		if ( *(fstorage->link_path) == '\0' &&
+			strcmp(fstorage->active_fname, dp->d_name) == 0 )
 			continue;
 
 		if ( strcmp(name, scriptname) == 0 )
 			replaced = TRUE;
 
-		/* Check cont quota if necessary */
+		/* Check count quota if necessary */
 		if ( storage->max_scripts > 0 ) {
 			if ( !replaced ) {
 				script_count++;
@@ -124,11 +89,11 @@ int sieve_storage_quota_havespace
 			const char *path;
 			struct stat st;
 
-			path = t_strconcat(storage->dir, "/", dp->d_name, NULL);
+			path = t_strconcat(fstorage->path, "/", dp->d_name, NULL);
 
 			if ( stat(path, &st) < 0 ) {
-				i_warning
-					("sieve-storage: quota: stat(%s) failed: %m", path);
+				sieve_storage_sys_warning(storage,
+					"quota: stat(%s) failed: %m", path);
 				continue;
 			}
 
@@ -147,10 +112,9 @@ int sieve_storage_quota_havespace
 
 	/* Close directory */
 	if ( closedir(dirp) < 0 ) {
-		sieve_storage_set_critical
-			(storage, "quota: closedir(%s) failed: %m", storage->dir);
+		sieve_storage_set_critical(storage,
+			"quota: closedir(%s) failed: %m", fstorage->path);
 	}
-
 	return result;
 }
 
diff --git a/src/lib-sieve/storage/file/sieve-file-storage-save.c b/src/lib-sieve/storage/file/sieve-file-storage-save.c
new file mode 100644
index 0000000000000000000000000000000000000000..93da6ff4d538ff49086b33b1aacaa1c88ac1efef
--- /dev/null
+++ b/src/lib-sieve/storage/file/sieve-file-storage-save.c
@@ -0,0 +1,452 @@
+/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "hostpid.h"
+#include "ioloop.h"
+#include "array.h"
+#include "buffer.h"
+#include "ostream.h"
+#include "str.h"
+#include "eacces-error.h"
+#include "safe-mkstemp.h"
+
+#include "sieve-file-storage.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <utime.h>
+#include <sys/stat.h>
+
+struct sieve_file_save_context {
+	struct sieve_storage_save_context context;
+
+	pool_t pool;
+
+	struct ostream *output;
+	int fd;
+	const char *tmp_path;
+
+	time_t mtime;
+
+	unsigned int failed:1;
+	unsigned int finished:1;
+};
+
+static const char *sieve_generate_tmp_filename(const char *scriptname)
+{
+	static struct timeval last_tv = { 0, 0 };
+	struct timeval tv;
+
+	/* use secs + usecs to guarantee uniqueness within this process. */
+	if (ioloop_timeval.tv_sec > last_tv.tv_sec ||
+		(ioloop_timeval.tv_sec == last_tv.tv_sec &&
+		ioloop_timeval.tv_usec > last_tv.tv_usec)) {
+		tv = ioloop_timeval;
+	} else {
+		tv = last_tv;
+		if (++tv.tv_usec == 1000000) {
+			tv.tv_sec++;
+			tv.tv_usec = 0;
+		}
+	}
+	last_tv = tv;
+
+	if ( scriptname == NULL ) {
+		return t_strdup_printf("%s.M%sP%s.%s.tmp",
+			dec2str(tv.tv_sec), dec2str(tv.tv_usec),
+			my_pid, my_hostname);
+	}
+
+	scriptname = t_strdup_printf("%s_%s.M%sP%s.%s",
+		scriptname,	dec2str(tv.tv_sec), dec2str(tv.tv_usec),
+		my_pid, my_hostname);
+	return sieve_script_file_from_name(scriptname);
+}
+
+static int sieve_file_storage_create_tmp
+(struct sieve_file_storage *fstorage, const char *scriptname,
+	const char **fpath_r)
+{
+	struct sieve_storage *storage = &fstorage->storage;
+	struct stat st;
+	unsigned int prefix_len;
+	const char *tmp_fname = NULL;
+	string_t *path;
+	int fd;
+
+	path = t_str_new(256);
+	str_append(path, fstorage->path);
+	str_append(path, "/tmp/");
+	prefix_len = str_len(path);
+
+	for (;;) {
+		tmp_fname = sieve_generate_tmp_filename(scriptname);
+		str_truncate(path, prefix_len);
+		str_append(path, tmp_fname);
+
+		/* stat() first to see if it exists. pretty much the only
+		   possibility of that happening is if time had moved
+		   backwards, but even then it's highly unlikely. */
+		if (stat(str_c(path), &st) == 0) {
+			/* try another file name */
+		} else if (errno != ENOENT) {
+			sieve_storage_set_critical(storage, "save: "
+				"stat(%s) failed: %m", str_c(path));
+			return -1;
+		} else {
+			/* doesn't exist */
+			mode_t old_mask = umask(0777 & ~(fstorage->file_create_mode));
+			fd = open(str_c(path),
+				O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0777);
+			umask(old_mask);
+
+			if (fd != -1 || errno != EEXIST)
+				break;
+			/* race condition between stat() and open().
+				highly unlikely. */
+		}
+	}
+
+	*fpath_r = str_c(path);
+	if (fd == -1) {
+		if (ENOSPACE(errno)) {
+			sieve_storage_set_error(storage,
+				SIEVE_ERROR_NO_SPACE,
+				"Not enough disk space");
+		} else {
+			sieve_storage_set_critical(storage, "save: "
+				"open(%s) failed: %m", str_c(path));
+		}
+	}
+
+	return fd;
+}
+
+static int sieve_file_storage_script_move
+(struct sieve_file_save_context *fsctx, const char *dst)
+{
+	struct sieve_storage_save_context *sctx = &fsctx->context;
+	struct sieve_storage *storage = sctx->storage;
+	int result = 0;
+
+	T_BEGIN {
+
+		/* Using rename() to ensure existing files are replaced
+		 * without conflicts with other processes using the same
+		 * file. The kernel wont fully delete the original until
+		 * all processes have closed the file.
+		 */
+		if (rename(fsctx->tmp_path, dst) == 0)
+			result = 0;
+		else {
+			result = -1;
+			if ( ENOSPACE(errno) ) {
+				sieve_storage_set_error(storage,
+					SIEVE_ERROR_NO_SPACE,
+					"Not enough disk space");
+			} else if ( errno == EACCES ) {
+				sieve_storage_set_critical(storage, "save: "
+					"Failed to save Sieve script: "
+					"%s", eacces_error_get("rename", dst));
+			} else {
+				sieve_storage_set_critical(storage, "save: "
+					"rename(%s, %s) failed: %m", fsctx->tmp_path, dst);
+			}
+		}
+
+		/* Always destroy temp file */
+		if (unlink(fsctx->tmp_path) < 0 && errno != ENOENT) {
+			sieve_storage_sys_warning(storage, "save: "
+				"unlink(%s) failed: %m", fsctx->tmp_path);
+		}
+	} T_END;
+
+	return result;
+}
+
+struct sieve_storage_save_context *
+sieve_file_storage_save_init(struct sieve_storage *storage,
+	const char *scriptname, struct istream *input)
+{
+	struct sieve_file_storage *fstorage =
+		(struct sieve_file_storage *)storage;
+	struct sieve_file_save_context *fsctx;
+	pool_t pool;
+	const char *path;
+	int fd;
+
+	if ( sieve_file_storage_pre_modify(storage) < 0 )
+		return NULL;
+
+	if ( scriptname != NULL ) {
+		/* Prevent overwriting the active script link when it resides in the
+		 * sieve storage directory.
+		 */
+		if ( *(fstorage->link_path) == '\0' ) {
+			const char *svext;
+			size_t namelen;
+
+			svext = strrchr(fstorage->active_fname, '.');
+			namelen = svext - fstorage->active_fname;
+			if ( svext != NULL && strncmp(svext+1, "sieve", 5) == 0 &&
+				strlen(scriptname) == namelen &&
+				strncmp(scriptname, fstorage->active_fname, namelen) == 0 )
+			{
+				sieve_storage_set_error(storage,
+					SIEVE_ERROR_BAD_PARAMS,
+					"Script name `%s' is reserved for internal use.",
+					scriptname);
+				return NULL;
+			}
+		}
+	}
+
+	T_BEGIN {
+		fd = sieve_file_storage_create_tmp(fstorage, scriptname, &path);
+		if (fd == -1) {
+			fsctx = NULL;
+		} else {
+			pool = pool_alloconly_create("sieve_file_save_context", 1024);
+			fsctx = p_new(pool, struct sieve_file_save_context, 1);
+			fsctx->context.scriptname = p_strdup(pool, scriptname);
+			fsctx->context.input = input;
+			fsctx->pool = pool;
+			fsctx->fd = fd;
+			fsctx->output = o_stream_create_fd(fsctx->fd, 0, FALSE);
+			fsctx->tmp_path = p_strdup(pool, path);
+		}
+	} T_END;
+
+	return &fsctx->context;
+}
+
+int sieve_file_storage_save_continue
+(struct sieve_storage_save_context *sctx)
+{
+	struct sieve_file_save_context *fsctx =
+		(struct sieve_file_save_context *)sctx;
+
+	if (o_stream_send_istream(fsctx->output, sctx->input) < 0) {
+		sieve_storage_set_critical(sctx->storage, "save: "
+			"o_stream_send_istream(%s) failed: %m", fsctx->tmp_path);
+		return -1;
+	}
+	return 0;
+}
+
+int sieve_file_storage_save_finish
+(struct sieve_storage_save_context *sctx)
+{
+	struct sieve_file_save_context *fsctx =
+		(struct sieve_file_save_context *)sctx;
+	struct sieve_storage *storage = sctx->storage;
+	int output_errno;
+
+	if ( sctx->failed && fsctx->fd == -1 ) {
+		/* tmp file creation failed */
+		return -1;
+	}
+
+	T_BEGIN {
+		output_errno = fsctx->output->stream_errno;
+		o_stream_destroy(&fsctx->output);
+
+		if ( fsync(fsctx->fd) < 0 ) {
+			sieve_storage_set_critical(storage, "save: "
+				"fsync(%s) failed: %m", fsctx->tmp_path);
+			sctx->failed = TRUE;
+		}
+		if ( close(fsctx->fd) < 0 ) {
+			sieve_storage_set_critical(storage, "save: "
+				"close(%s) failed: %m", fsctx->tmp_path);
+			sctx->failed = TRUE;
+		}
+		fsctx->fd = -1;
+
+		if ( sctx->failed ) {
+			/* delete the tmp file */
+			if (unlink(fsctx->tmp_path) < 0 && errno != ENOENT) {
+				sieve_storage_sys_warning(storage, "save: "
+					"unlink(%s) failed: %m", fsctx->tmp_path);
+			}
+
+			fsctx->tmp_path = NULL;
+			
+			errno = output_errno;
+			if ( ENOSPACE(errno) ) {
+				sieve_storage_set_error(storage,
+					SIEVE_ERROR_NO_SPACE,
+					"Not enough disk space");
+			} else if ( errno != 0 ) {
+				sieve_storage_set_critical(storage, "save: "
+					"write(%s) failed: %m", fsctx->tmp_path);
+			}
+		}
+	} T_END;
+
+	return ( sctx->failed ? -1 : 0 );
+}
+
+struct sieve_script *sieve_file_storage_save_get_tempscript
+(struct sieve_storage_save_context *sctx)
+{
+	struct sieve_file_save_context *fsctx =
+		(struct sieve_file_save_context *)sctx;
+	struct sieve_file_storage *fstorage = 
+		(struct sieve_file_storage *)sctx->storage;
+	struct sieve_file_script *tmpscript;
+	enum sieve_error error;
+	const char *scriptname;
+
+	if (sctx->failed)
+		return NULL;
+
+	if ( sctx->scriptobject != NULL )
+		return sctx->scriptobject;
+
+	scriptname =
+		( sctx->scriptname == NULL ? "" : sctx->scriptname );
+	tmpscript = sieve_file_script_open_from_path
+		(fstorage, fsctx->tmp_path, scriptname, &error);
+
+	if ( tmpscript == NULL ) {
+		if ( error == SIEVE_ERROR_NOT_FOUND ) {
+			sieve_storage_set_critical(sctx->storage, "save: "
+				"Temporary script file `%s' got lost, "
+				"which should not happen (possibly deleted externally).",
+				fsctx->tmp_path);
+		} else {
+			sieve_storage_set_critical(sctx->storage, "save: "
+				"Failed to open temporary script file `%s'",
+				fsctx->tmp_path);
+		}
+		return NULL;
+	}
+
+	return &tmpscript->script;
+}
+
+static void sieve_file_storage_update_mtime
+(struct sieve_storage *storage, const char *path, time_t mtime)
+{
+	struct utimbuf times = { .actime = mtime, .modtime = mtime };
+
+	if ( utime(path, &times) < 0 ) {
+		switch ( errno ) {	
+		case ENOENT:
+			break;
+		case EACCES:
+			sieve_storage_sys_error(storage, "save: "
+				"%s", eacces_error_get("utime", path));
+			break;
+		default:
+			sieve_storage_sys_error(storage, "save: "
+				"utime(%s) failed: %m", path);
+		}
+	}
+}
+
+int sieve_file_storage_save_commit
+(struct sieve_storage_save_context *sctx)
+{
+	struct sieve_file_save_context *fsctx =
+		(struct sieve_file_save_context *)sctx;
+	struct sieve_storage *storage = sctx->storage;
+	struct sieve_file_storage *fstorage =
+		(struct sieve_file_storage *)sctx->storage;
+	const char *dest_path;
+	bool failed = FALSE;
+
+	i_assert(fsctx->output == NULL);
+
+	T_BEGIN {
+		dest_path = t_strconcat(fstorage->path, "/",
+			sieve_script_file_from_name(sctx->scriptname), NULL);
+
+		failed = ( sieve_file_storage_script_move(fsctx, dest_path) < 0 );
+		if ( sctx->mtime != (time_t)-1 )
+			sieve_file_storage_update_mtime(storage, dest_path, sctx->mtime);
+	} T_END;
+
+	pool_unref(&fsctx->pool);
+	
+	return ( failed ? -1 : 0 );
+}
+
+void sieve_file_storage_save_cancel(struct sieve_storage_save_context *sctx)
+{
+	struct sieve_file_save_context *fsctx =
+		(struct sieve_file_save_context *)sctx;
+	struct sieve_storage *storage = sctx->storage;
+
+	if (fsctx->tmp_path != NULL &&
+		unlink(fsctx->tmp_path) < 0 && errno != ENOENT) {
+		sieve_storage_sys_warning(storage, "save: "
+			"unlink(%s) failed: %m", fsctx->tmp_path);
+	}
+
+	i_assert(fsctx->output == NULL);
+}
+
+int sieve_file_storage_save_as_active(struct sieve_storage *storage,
+	struct istream *input, time_t mtime)
+{
+	struct sieve_file_storage *fstorage =
+		(struct sieve_file_storage *)storage;
+	string_t *temp_path;
+	struct ostream *output;
+	int fd;
+
+	temp_path = t_str_new(256);
+	str_append(temp_path, fstorage->active_path);
+	str_append_c(temp_path, '.');
+	fd = safe_mkstemp_hostpid
+		(temp_path, fstorage->file_create_mode, (uid_t)-1, (gid_t)-1);
+	if ( fd < 0 ) {
+		if ( errno == EACCES ) {
+			sieve_storage_set_critical(storage,
+				"Failed to create temporary file: %s",
+				eacces_error_get_creating("open", str_c(temp_path)));
+		} else {
+			sieve_storage_set_critical(storage,
+				"Failed to create temporary file: open(%s) failed: %m",
+				str_c(temp_path));
+		}
+		return -1;
+	}
+
+	output = o_stream_create_fd(fd, 0, FALSE);
+	if (o_stream_send_istream(output, input) < 0) {
+		sieve_storage_set_critical(storage,
+			"o_stream_send_istream(%s) failed: %m", str_c(temp_path));
+		o_stream_destroy(&output);
+		(void)unlink(str_c(temp_path));
+		return -1;
+	}
+	o_stream_destroy(&output);
+
+	if (rename(str_c(temp_path), fstorage->active_path) < 0) {
+		if ( ENOSPACE(errno) ) {
+			sieve_storage_set_error(storage,
+				SIEVE_ERROR_NO_SPACE,
+				"Not enough disk space");
+		} else if ( errno == EACCES ) {
+			sieve_storage_set_critical(storage,
+				"%s", eacces_error_get("rename", fstorage->active_path));
+		} else {
+			sieve_storage_set_critical(storage,
+				"rename(%s, %s) failed: %m",
+				str_c(temp_path), fstorage->active_path);
+		}
+	} else {
+		sieve_file_storage_update_mtime
+			(storage, fstorage->active_path, mtime);
+	}
+
+	(void)unlink(str_c(temp_path));
+	return 0;
+}
+
diff --git a/src/lib-sieve/storage/file/sieve-file-storage.c b/src/lib-sieve/storage/file/sieve-file-storage.c
new file mode 100644
index 0000000000000000000000000000000000000000..fd75db1efe76ac134915519d93ccdd6e385a48f6
--- /dev/null
+++ b/src/lib-sieve/storage/file/sieve-file-storage.c
@@ -0,0 +1,681 @@
+/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "home-expand.h"
+#include "ioloop.h"
+#include "mkdir-parents.h"
+#include "eacces-error.h"
+#include "unlink-old-files.h"
+#include "mail-storage-private.h"
+
+#include "sieve.h"
+#include "sieve-common.h"
+#include "sieve-settings.h"
+#include "sieve-error-private.h"
+#include "sieve-settings.h"
+
+#include "sieve-file-storage.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <utime.h>
+#include <sys/time.h>
+
+
+#define MAX_DIR_CREATE_MODE 0770
+
+/*
+ * Utility
+ */
+
+const char *sieve_file_storage_path_extend
+(struct sieve_file_storage *fstorage, const char *filename)
+{
+	const char *path = fstorage->path;
+
+	if ( path[strlen(path)-1] == '/' )
+		return t_strconcat(path, filename, NULL);
+
+	return t_strconcat(path, "/", filename , NULL);
+}
+
+/*
+ *
+ */
+
+static const char *sieve_storage_get_relative_link_path
+	(const char *active_path, const char *storage_dir)
+{
+	const char *link_path, *p;
+	size_t pathlen;
+
+	/* Determine to what extent the sieve storage and active script
+	 * paths match up. This enables the managed symlink to be short and the
+	 * sieve storages can be moved around without trouble (if the active
+	 * script path is common to the script storage).
+	 */
+	p = strrchr(active_path, '/');
+	if ( p == NULL ) {
+		link_path = storage_dir;
+	} else {
+		pathlen = p - active_path;
+
+		if ( strncmp( active_path, storage_dir, pathlen ) == 0 &&
+			(storage_dir[pathlen] == '/' || storage_dir[pathlen] == '\0') )
+		{
+			if ( storage_dir[pathlen] == '\0' )
+				link_path = "";
+			else
+				link_path = storage_dir + pathlen + 1;
+		} else
+			link_path = storage_dir;
+	}
+
+	/* Add trailing '/' when link path is not empty
+	 */
+	pathlen = strlen(link_path);
+    if ( pathlen != 0 && link_path[pathlen-1] != '/')
+        return t_strconcat(link_path, "/", NULL);
+
+	return t_strdup(link_path);
+}
+
+static mode_t get_dir_mode(mode_t mode)
+{
+	/* Add the execute bit if either read or write bit is set */
+
+	if ((mode & 0600) != 0) mode |= 0100;
+	if ((mode & 0060) != 0) mode |= 0010;
+	if ((mode & 0006) != 0) mode |= 0001;
+
+	return mode;
+}
+
+static void sieve_file_storage_get_permissions
+(struct sieve_storage *storage, const char *path,
+mode_t *file_mode_r, mode_t *dir_mode_r, gid_t *gid_r,
+	const char **gid_origin_r)
+{
+	struct stat st;
+
+	/* Use safe defaults */
+	*file_mode_r = 0600;
+	*dir_mode_r = 0700;
+	*gid_r = (gid_t)-1;
+	*gid_origin_r = "defaults";
+
+	if ( stat(path, &st) < 0 ) {
+		if ( !ENOTFOUND(errno) ) {
+			sieve_storage_sys_error(storage,
+				"stat(%s) failed: %m", path);
+		} else {
+			sieve_storage_sys_debug(storage,
+				"Permission lookup failed from %s", path);
+		}
+		return;
+
+	} else {
+		*file_mode_r = (st.st_mode & 0666) | 0600;
+		*dir_mode_r = (st.st_mode & 0777) | 0700;
+		*gid_origin_r = path;
+
+		if ( !S_ISDIR(st.st_mode) ) {
+			/* We're getting permissions from a file. Apply +x modes as necessary. */
+			*dir_mode_r = get_dir_mode(*dir_mode_r);
+		}
+
+		if (S_ISDIR(st.st_mode) && (st.st_mode & S_ISGID) != 0) {
+			/* Directory's GID is used automatically for new files */
+			*gid_r = (gid_t)-1;
+		} else if ((st.st_mode & 0070) >> 3 == (st.st_mode & 0007)) {
+			/* Group has same permissions as world, so don't bother changing it */
+			*gid_r = (gid_t)-1;
+		} else if (getegid() == st.st_gid) {
+			/* Using our own gid, no need to change it */
+			*gid_r = (gid_t)-1;
+		} else {
+			*gid_r = st.st_gid;
+		}
+	}
+
+	sieve_storage_sys_debug(storage,
+		"Using permissions from %s: mode=0%o gid=%ld",
+		path, (int)*dir_mode_r, *gid_r == (gid_t)-1 ? -1L : (long)*gid_r);
+}
+
+static int mkdir_verify
+(struct sieve_storage *storage, const char *dir,
+	mode_t mode, gid_t gid, const char *gid_origin)
+{
+	struct stat st;
+
+	if ( stat(dir, &st) == 0 )
+		return 0;
+
+	if ( errno == EACCES ) {
+		sieve_storage_sys_error(storage,
+			"mkdir_verify: %s", eacces_error_get("stat", dir));
+		return -1;
+	} else if ( errno != ENOENT ) {
+		sieve_storage_sys_error(storage,
+			"mkdir_verify: stat(%s) failed: %m", dir);
+		return -1;
+	}
+
+	if ( mkdir_parents_chgrp(dir, mode, gid, gid_origin) == 0 ) {
+		sieve_storage_sys_debug(storage,
+			"Created storage directory %s", dir);
+		return 0;
+	}
+
+	switch ( errno ) {
+	case EEXIST:
+		return 0;
+	case ENOENT:
+		sieve_storage_sys_error(storage,
+			"Storage was deleted while it was being created");
+		break;
+	case EACCES:
+		sieve_storage_sys_error(storage,
+			"%s",	eacces_error_get_creating("mkdir_parents_chgrp", dir));
+		break;
+	default:
+		sieve_storage_sys_error(storage,
+			"mkdir_parents_chgrp(%s) failed: %m", dir);
+		break;
+	}
+
+	return -1;
+}
+
+static int check_tmp(struct sieve_storage *storage, const char *path)
+{
+	struct stat st;
+
+	/* If tmp/ directory exists, we need to clean it up once in a while */
+	if ( stat(path, &st) < 0 ) {
+		if ( errno == ENOENT )
+			return 0;
+		if ( errno == EACCES ) {
+			sieve_storage_sys_error(storage,
+				"check_tmp: %s", eacces_error_get("stat", path));
+			return -1;
+		}
+		sieve_storage_sys_error(storage,
+			"check_tmp: stat(%s) failed: %m", path);
+		return -1;
+	}
+
+	if ( st.st_atime > st.st_ctime + SIEVE_FILE_STORAGE_TMP_DELETE_SECS ) {
+		/* The directory should be empty. we won't do anything
+		   until ctime changes. */
+	} else if ( st.st_atime < ioloop_time - SIEVE_FILE_STORAGE_TMP_SCAN_SECS ) {
+		/* Time to scan */
+		(void)unlink_old_files(path, "",
+			ioloop_time - SIEVE_FILE_STORAGE_TMP_DELETE_SECS);
+	}
+	return 1;
+}
+
+static struct sieve_storage *sieve_file_storage_alloc(void)
+{
+	struct sieve_file_storage *fstorage;
+	pool_t pool;
+
+	pool = pool_alloconly_create("sieve_file_storage", 1024);
+	fstorage = p_new(pool, struct sieve_file_storage, 1);
+	fstorage->storage = sieve_file_storage;
+	fstorage->storage.pool = pool;
+
+	return &fstorage->storage;
+}
+
+static int sieve_file_storage_init_paths
+(struct sieve_file_storage *fstorage, const char *active_path,
+	const char *storage_path, enum sieve_error *error_r)
+{
+	struct sieve_storage *storage = &fstorage->storage;
+	struct sieve_instance *svinst = storage->svinst;
+	const char *tmp_dir, *link_path, *path, *active_fname;
+	mode_t dir_create_mode, file_create_mode;
+	gid_t file_create_gid;
+	const char *file_create_gid_origin;
+	int ret;
+
+	fstorage->prev_mtime = (time_t)-1;
+
+	/* Active script path */
+
+	active_path = NULL;
+	if ( storage->main_storage ||
+		(storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 ) {
+		if ( active_path == NULL || *active_path == '\0' ) {
+			sieve_storage_sys_debug(storage,
+				"Active script path is unconfigured; "
+				"using default (path=%s)", SIEVE_FILE_DEFAULT_PATH);
+			active_path = SIEVE_FILE_DEFAULT_PATH;
+		}
+	}
+
+	path = active_path;
+	if ( path != NULL && *path != '\0' &&
+		((path[0] == '~' && (path[1] == '/' || path[1] == '\0')) ||
+		(((svinst->flags & SIEVE_FLAG_HOME_RELATIVE) != 0 ) && path[0] != '/'))
+		)	{
+		/* home-relative path. change to absolute. */
+		const char *home = sieve_environment_get_homedir(svinst);
+
+		if ( home != NULL ) {
+			if ( path[0] == '~' && (path[1] == '/' || path[1] == '\0') )
+				path = home_expand_tilde(path, home);
+			else
+				path = t_strconcat(home, "/", path, NULL);
+		} else {
+			sieve_storage_set_critical(storage,
+				"Sieve storage path `%s' is relative to home directory, "
+				"but home directory is not available.", path);
+			*error_r = SIEVE_ERROR_TEMP_FAILURE;
+			return -1;
+		}
+	}
+	active_path = path;
+
+	/* Get the filename for the active script link */
+
+	active_fname = NULL;
+	if ( active_path != NULL && *active_path != '\0' ) {
+		active_fname = strrchr(active_path, '/');
+		if ( active_fname == NULL )
+			active_fname = active_path;
+		else
+			active_fname++;
+
+		if ( *active_fname == '\0' ) {
+			/* Link cannot be just a path ending in '/' */
+			sieve_storage_set_critical(storage,
+				"Path to active symlink must include the link's filename "
+				"(path=%s)", active_path);
+			*error_r = SIEVE_ERROR_TEMP_FAILURE;
+			return -1;
+		}
+	}
+
+	/* Storage path */
+
+	path = storage_path;
+	if ( path != NULL &&
+		((path[0] == '~' && (path[1] == '/' || path[1] == '\0')) ||
+		(((svinst->flags & SIEVE_FLAG_HOME_RELATIVE) != 0 ) && path[0] != '/')) ) {
+		/* home-relative path. change to absolute. */
+		const char *home = sieve_environment_get_homedir(svinst);
+
+		if ( home != NULL ) {
+			if ( path[0] == '~' && (path[1] == '/' || path[1] == '\0') )
+				path = home_expand_tilde(path, home);
+			else
+				path = t_strconcat(home, "/", path, NULL);
+		} else {
+			sieve_storage_set_critical(storage,
+				"Sieve storage path `%s' is relative to home directory, "
+				"but home directory is not available.", path);
+			*error_r = SIEVE_ERROR_TEMP_FAILURE;
+			return -1;
+		}
+	}
+	storage_path = path;
+
+	if (storage_path == NULL || *storage_path == '\0') {
+		sieve_storage_set_critical(storage,
+			"Storage path cannot be empty");
+		*error_r = SIEVE_ERROR_TEMP_FAILURE;
+		return -1;
+	}
+	
+	sieve_storage_sys_debug(storage,
+		"Using script storage path: %s", storage_path);
+
+	fstorage->path = p_strdup(storage->pool, storage_path);
+
+	if ( active_path != NULL && *active_path != '\0' ) {
+		sieve_storage_sys_debug(storage,
+			"Using active Sieve script path: %s", active_path);
+
+		fstorage->active_path = p_strdup(storage->pool, active_path);
+		fstorage->active_fname = p_strdup(storage->pool, active_fname);
+
+		/* Get the path to be prefixed to the script name in the symlink pointing
+		 * to the active script.
+		 */
+		link_path = sieve_storage_get_relative_link_path
+			(fstorage->active_path, fstorage->path);
+
+		sieve_storage_sys_debug(storage,
+			"Relative path to sieve storage in active link: %s",
+			link_path);
+
+		fstorage->link_path = p_strdup(storage->pool, link_path);
+	}
+
+	/* Prepare for write access */
+
+	if ( (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 ) {
+		/* Get permissions */
+		sieve_file_storage_get_permissions(storage,
+			fstorage->path, &file_create_mode, &dir_create_mode, &file_create_gid,
+			&file_create_gid_origin);
+
+		/*
+		 * Ensure sieve local directory structure exists (full autocreate):
+		 *  This currently only consists of a ./tmp direcory
+		 */
+
+		tmp_dir = t_strconcat(fstorage->path, "/tmp", NULL);
+
+		/* Try to find and clean up tmp dir */
+		if ( (ret=check_tmp(storage, tmp_dir)) < 0 ) {
+			*error_r = SIEVE_ERROR_TEMP_FAILURE;
+			return -1;
+		}
+
+		/* Auto-create if necessary */
+		if ( ret == 0 && mkdir_verify(storage, tmp_dir,
+			dir_create_mode, file_create_gid, file_create_gid_origin) < 0 ) {
+			*error_r = SIEVE_ERROR_TEMP_FAILURE;
+			return -1;
+		}
+
+		fstorage->dir_create_mode = dir_create_mode;
+		fstorage->file_create_mode = file_create_mode;
+		fstorage->file_create_gid = file_create_gid;
+	}
+
+	return 0;
+}
+
+static int sieve_file_storage_init
+(struct sieve_storage *storage, const char *const *options,
+	enum sieve_error *error_r)
+{
+	struct sieve_file_storage *fstorage =
+		(struct sieve_file_storage *)storage;
+	const char *active_path = "";
+
+	if ( options != NULL ) {
+		while ( *options != NULL ) {
+			const char *option = *options;
+
+			if ( strncasecmp(option, "active=", 7) == 0 && option[7] != '\0' ) {
+				active_path = option+7;
+			} else {
+				sieve_storage_set_critical(storage,
+					"Invalid option `%s'", option);
+				*error_r = SIEVE_ERROR_TEMP_FAILURE;
+				return -1;
+			}
+
+			options++;
+		}
+	}
+
+	return sieve_file_storage_init_paths
+		(fstorage, active_path, storage->location, error_r);
+}
+
+struct sieve_storage *sieve_file_storage_init_legacy
+(struct sieve_instance *svinst, const char *active_path,
+	const char *storage_path, enum sieve_storage_flags flags,
+	enum sieve_error *error_r)
+{
+	struct sieve_storage *storage;
+	struct sieve_file_storage *fstorage;
+
+	storage = sieve_storage_alloc
+		(svinst, &sieve_file_storage, "", flags, TRUE);
+	storage->location = p_strdup(storage->pool, storage_path);
+	fstorage = (struct sieve_file_storage *)storage;
+	
+	T_BEGIN {
+		if ( storage_path == NULL || *storage_path == '\0' ) {
+			const char *home = sieve_environment_get_homedir(svinst);
+
+			sieve_storage_sys_debug(storage,
+				"Performing auto-detection");
+
+			/* We'll need to figure out the storage location ourself.
+			 *
+			 * It's $HOME/sieve or /sieve when (presumed to be) chrooted.
+			 */
+			if ( home != NULL && *home != '\0' ) {
+				if (access(home, R_OK|W_OK|X_OK) == 0) {
+					/* Use default ~/sieve */
+					sieve_storage_sys_debug(storage,
+						"Root exists (%s)", home);
+
+					storage_path = t_strconcat(home, "/sieve", NULL);
+				} else {
+					/* Don't have required access on the home directory */
+
+					sieve_storage_sys_debug(storage,
+						"access(%s, rwx) failed: %m", home);
+				}
+			} else {
+					sieve_storage_sys_debug(storage,
+						"HOME is not set");
+
+				if (access("/sieve", R_OK|W_OK|X_OK) == 0) {
+					storage_path = "/sieve";
+					sieve_storage_sys_debug(storage,
+						"Directory `/sieve' exists, assuming chroot");
+				}
+			}
+		}
+
+		if (storage_path == NULL || *storage_path == '\0') {
+			sieve_storage_set_critical(storage,
+				"Could not find storage root directory; "
+				"path was left unconfigured and autodetection failed");
+			*error_r = SIEVE_ERROR_TEMP_FAILURE;
+			sieve_storage_unref(&storage);
+			storage = NULL;
+		} else if (sieve_file_storage_init_paths
+			(fstorage, active_path, storage_path, error_r) < 0) {
+			sieve_storage_unref(&storage);
+			storage = NULL;
+		}
+	} T_END;
+
+	return storage;
+}
+
+struct sieve_file_storage *sieve_file_storage_init_from_path
+(struct sieve_instance *svinst, const char *path,
+	enum sieve_storage_flags flags, enum sieve_error *error_r)
+{
+	struct sieve_storage *storage;
+	struct sieve_file_storage *fstorage;
+
+	storage = sieve_storage_alloc
+		(svinst, &sieve_file_storage, "", flags, FALSE);
+	storage->location = p_strdup(storage->pool, path);
+	fstorage = (struct sieve_file_storage *)storage;
+	
+	T_BEGIN {
+		if ( sieve_file_storage_init_paths
+			(fstorage, NULL, path, error_r) < 0 ) {
+			sieve_storage_unref(&storage);
+			storage = NULL;
+		}
+	} T_END;
+
+	return fstorage;
+}
+
+static int sieve_file_storage_is_singular
+(struct sieve_storage *storage)
+{
+	struct sieve_file_storage *fstorage =
+		(struct sieve_file_storage *)storage;
+	struct stat st;
+
+	/* Stat the file */
+	if ( lstat(fstorage->active_path, &st) != 0 ) {
+		if ( errno != ENOENT ) {
+			sieve_storage_set_critical(storage,
+				"Failed to stat active sieve script symlink (%s): %m.",
+				fstorage->active_path);
+			return -1;
+		}
+		return 0;
+	}
+
+	if ( S_ISLNK( st.st_mode ) )
+		return 0;
+	if ( !S_ISREG( st.st_mode ) ) {
+		sieve_storage_set_critical(storage,
+			"Active sieve script file '%s' is no symlink nor a regular file.",
+			fstorage->active_path);
+		return -1;
+	}
+	return 1;
+}
+
+
+/*
+ *
+ */
+
+static int sieve_file_storage_get_last_change
+(struct sieve_storage *storage, time_t *last_change_r)
+{
+	struct sieve_file_storage *fstorage =
+		(struct sieve_file_storage *)storage;
+	struct stat st;
+
+	if ( fstorage->prev_mtime == (time_t)-1 ) {
+		/* Get the storage mtime before we modify it ourself */
+		if ( stat(fstorage->path, &st) < 0 ) {
+			if ( errno != ENOENT ) {
+				sieve_storage_sys_error(storage,
+					"stat(%s) failed: %m", fstorage->path);
+				return -1;
+			}
+			st.st_mtime = 0;
+		}
+
+		fstorage->prev_mtime = st.st_mtime;
+	}
+		
+	if ( last_change_r != NULL )
+		*last_change_r = fstorage->prev_mtime;
+	return 0;
+}
+
+int sieve_file_storage_pre_modify
+(struct sieve_storage *storage)
+{
+	i_assert( (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 );
+
+	return sieve_storage_get_last_change(storage, NULL);
+}
+
+static void sieve_file_storage_set_modified
+(struct sieve_storage *storage, time_t mtime)
+{
+	struct sieve_file_storage *fstorage =
+		(struct sieve_file_storage *)storage;
+	struct utimbuf times;
+	time_t cur_mtime;
+
+	if ( mtime != (time_t)-1 ) {
+		if ( sieve_storage_get_last_change(storage, &cur_mtime) >= 0 &&
+			cur_mtime > mtime )
+			return;
+	} else {
+		mtime = ioloop_time;
+	}
+
+	times.actime = mtime;
+	times.modtime = mtime;
+	if ( utime(fstorage->path, &times) < 0 ) {
+		switch ( errno ) {
+		case ENOENT:
+			break;
+		case EACCES:
+			sieve_storage_sys_error(storage,
+				"%s", eacces_error_get("utime", fstorage->path));
+			break;
+		default:
+			sieve_storage_sys_error(storage,
+				"utime(%s) failed: %m", fstorage->path);
+		}
+	} else {
+		fstorage->prev_mtime = mtime;
+	}
+}
+
+/*
+ * Script access
+ */
+
+static struct sieve_script *sieve_file_storage_get_script
+(struct sieve_storage *storage, const char *name)
+{
+	struct sieve_file_storage *fstorage =
+		(struct sieve_file_storage *)storage;
+	struct sieve_file_script *fscript;
+
+	T_BEGIN {
+		fscript = sieve_file_script_init_from_name(fstorage, name);
+	} T_END;
+
+	return &fscript->script;
+}
+
+/*
+ * Driver definition
+ */
+
+const struct sieve_storage sieve_file_storage = {
+	.driver_name = SIEVE_FILE_STORAGE_DRIVER_NAME,
+	.allows_synchronization = TRUE,
+	.v = {
+		.alloc = sieve_file_storage_alloc,
+		.init = sieve_file_storage_init,
+
+		.get_last_change = sieve_file_storage_get_last_change,
+		.set_modified = sieve_file_storage_set_modified,
+
+		.is_singular = sieve_file_storage_is_singular,
+
+		.get_script = sieve_file_storage_get_script,
+
+		.get_script_sequence = sieve_file_storage_get_script_sequence,
+		.script_sequence_next = sieve_file_script_sequence_next,
+		.script_sequence_destroy = sieve_file_script_sequence_destroy,
+
+		.active_script_get_name = sieve_file_storage_active_script_get_name,
+		.active_script_open = sieve_file_storage_active_script_open,
+		.deactivate = sieve_file_storage_deactivate,
+		.active_script_get_last_change =
+			sieve_file_storage_active_script_get_last_change,
+
+		.list_init = sieve_file_storage_list_init,
+		.list_next = sieve_file_storage_list_next,
+		.list_deinit = sieve_file_storage_list_deinit,
+
+		.save_init = sieve_file_storage_save_init,
+		.save_continue = sieve_file_storage_save_continue,
+		.save_finish = sieve_file_storage_save_finish,
+		.save_get_tempscript = sieve_file_storage_save_get_tempscript,
+		.save_cancel = sieve_file_storage_save_cancel,
+		.save_commit = sieve_file_storage_save_commit,
+		.save_as_active = sieve_file_storage_save_as_active,
+
+		.quota_havespace = sieve_file_storage_quota_havespace
+	}
+};
diff --git a/src/lib-sieve/storage/file/sieve-file-storage.h b/src/lib-sieve/storage/file/sieve-file-storage.h
new file mode 100644
index 0000000000000000000000000000000000000000..4a4e3182463b472e5894b30b72a9c68eb06b94b5
--- /dev/null
+++ b/src/lib-sieve/storage/file/sieve-file-storage.h
@@ -0,0 +1,177 @@
+/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
+ */
+
+#ifndef __SIEVE_FILE_STORAGE_H
+#define __SIEVE_FILE_STORAGE_H
+
+#include "lib.h"
+#include "mail-storage.h"
+#include "mail-user.h"
+
+#include "sieve.h"
+#include "sieve-script-private.h"
+#include "sieve-storage-private.h"
+
+#define SIEVE_FILE_READ_BLOCK_SIZE (1024*8)
+
+#define SIEVE_FILE_DEFAULT_PATH "~/.dovecot."SIEVE_SCRIPT_FILEEXT
+
+/* How often to scan tmp/ directory for old files (based on dir's atime) */
+#define SIEVE_FILE_STORAGE_TMP_SCAN_SECS (8*60*60)
+/* Delete files having ctime older than this from tmp/. 36h is standard. */
+#define SIEVE_FILE_STORAGE_TMP_DELETE_SECS (36*60*60)
+
+/*
+ * Storage class
+ */
+
+struct sieve_file_storage {
+	struct sieve_storage storage;
+
+	const char *path;
+	const char *active_path;
+	const char *active_fname;
+	const char *link_path;
+	
+	mode_t dir_create_mode;
+	mode_t file_create_mode;
+	gid_t file_create_gid;
+
+	time_t prev_mtime;
+};
+
+const char *sieve_file_storage_path_extend
+	(struct sieve_file_storage *fstorage, const char *filename);
+
+struct sieve_file_storage *sieve_file_storage_init_from_path
+(struct sieve_instance *svinst, const char *path,
+	enum sieve_storage_flags flags, enum sieve_error *error_r)
+	ATTR_NULL(4);
+
+int sieve_file_storage_pre_modify
+	(struct sieve_storage *storage);
+
+/* Active script */
+
+int sieve_file_storage_active_replace_link
+	(struct sieve_file_storage *fstorage, const char *link_path);
+bool sieve_file_storage_active_rescue_regular
+	(struct sieve_file_storage *fstorage);
+
+int sieve_file_storage_active_script_get_name
+	(struct sieve_storage *storage, const char **name_r);
+struct sieve_script *sieve_file_storage_active_script_open
+	(struct sieve_storage *storage);
+
+int sieve_file_storage_active_script_get_file
+	(struct sieve_file_storage *fstorage, const char **file_r);
+int sieve_file_storage_active_script_is_no_link
+	(struct sieve_file_storage *fstorage);
+
+int sieve_file_storage_deactivate
+	(struct sieve_storage *storage);
+
+int sieve_file_storage_active_script_get_last_change
+	(struct sieve_storage *storage, time_t *last_change_r);
+int sieve_file_storage_save_as_active
+	(struct sieve_storage *storage, struct istream *input,
+		time_t mtime);
+
+/* Listing */
+
+struct sieve_storage_list_context *sieve_file_storage_list_init
+	(struct sieve_storage *storage);
+const char *sieve_file_storage_list_next
+	(struct sieve_storage_list_context *ctx, bool *active);
+int sieve_file_storage_list_deinit
+	(struct sieve_storage_list_context *lctx);
+
+/* Saving */
+
+struct sieve_storage_save_context *sieve_file_storage_save_init
+	(struct sieve_storage *storage, 	const char *scriptname,
+		struct istream *input);
+int sieve_file_storage_save_continue
+	(struct sieve_storage_save_context *sctx);
+int sieve_file_storage_save_finish
+	(struct sieve_storage_save_context *sctx);
+struct sieve_script *sieve_file_storage_save_get_tempscript
+	(struct sieve_storage_save_context *sctx);
+int sieve_file_storage_save_commit
+	(struct sieve_storage_save_context *sctx);
+void sieve_file_storage_save_cancel
+	(struct sieve_storage_save_context *sctx);
+
+/* Quota */
+
+int sieve_file_storage_quota_havespace
+(struct sieve_storage *storage, const char *scriptname, size_t size,
+	enum sieve_storage_quota *quota_r, uint64_t *limit_r);
+
+/*
+ * Sieve script filenames
+ */
+
+const char *sieve_script_file_get_scriptname(const char *filename);
+const char *sieve_script_file_from_name(const char *name);
+
+/*
+ * Script class
+ */
+
+struct sieve_file_script {
+	struct sieve_script script;
+
+	struct stat st;
+	struct stat lnk_st;
+
+	const char *path;
+	const char *dirpath;
+	const char *filename;
+	const char *binpath;
+	const char *binprefix;
+
+	time_t prev_mtime;
+};
+
+struct sieve_file_script *sieve_file_script_init_from_filename
+	(struct sieve_file_storage *fstorage, const char *filename,
+		const char *scriptname);
+struct sieve_file_script *sieve_file_script_open_from_filename	
+	(struct sieve_file_storage *fstorage, const char *filename,
+		const char *scriptname);
+struct sieve_file_script *sieve_file_script_init_from_name
+	(struct sieve_file_storage *fstorage, const char *name);
+
+struct sieve_file_script *sieve_file_script_init_from_path
+	(struct sieve_file_storage *fstorage, const char *path,
+		const char *scriptname, enum sieve_error *error_r)
+		ATTR_NULL(4);
+struct sieve_file_script *sieve_file_script_open_from_path
+	(struct sieve_file_storage *fstorage, const char *path,
+		const char *scriptname, enum sieve_error *error_r)
+		ATTR_NULL(4);
+
+/* Return directory where script resides in. Returns NULL if this is not a file
+ * script.
+ */
+const char *sieve_file_script_get_dirpath
+	(const struct sieve_script *script);
+
+/* Return full path to file script. Returns NULL if this is not a file script.
+ */
+const char *sieve_file_script_get_path
+	(const struct sieve_script *script);
+
+/*
+ * Script sequence
+ */
+
+struct sieve_script_sequence *sieve_file_storage_get_script_sequence
+	(struct sieve_storage *storage, enum sieve_error *error_r);
+
+struct sieve_script *sieve_file_script_sequence_next
+	(struct sieve_script_sequence *seq, enum sieve_error *error_r);
+void sieve_file_script_sequence_destroy(struct sieve_script_sequence *seq);
+
+#endif
diff --git a/src/lib-sievestorage/Makefile.am b/src/lib-sievestorage/Makefile.am
deleted file mode 100644
index 67ff30a2ff808b580222e8d9acfb65fcfc44e1b1..0000000000000000000000000000000000000000
--- a/src/lib-sievestorage/Makefile.am
+++ /dev/null
@@ -1,21 +0,0 @@
-noinst_LTLIBRARIES = libsievestorage.la
-
-AM_CPPFLAGS = \
-	$(LIBDOVECOT_INCLUDE) \
-	-I$(top_srcdir) \
-	-I$(top_srcdir)/src/lib-sieve
-
-libsievestorage_la_SOURCES = \
-	sieve-storage-save.c \
-	sieve-storage-script.c \
-	sieve-storage-list.c \
-	sieve-storage-quota.c \
-	sieve-storage.c
-
-noinst_HEADERS = \
-	sieve-storage-save.h \
-	sieve-storage-script.h \
-	sieve-storage-list.h \
-	sieve-storage-quota.h \
-	sieve-storage-private.h \
-	sieve-storage.h
diff --git a/src/lib-sievestorage/sieve-storage-list.c b/src/lib-sievestorage/sieve-storage-list.c
deleted file mode 100644
index 9d5f2dd727b508e6db4cbc523d2f0aea3c28fa3d..0000000000000000000000000000000000000000
--- a/src/lib-sievestorage/sieve-storage-list.c
+++ /dev/null
@@ -1,115 +0,0 @@
-/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
- */
-
-#include "lib.h"
-#include "str.h"
-
-#include "sieve-common.h"
-#include "sieve-script.h"
-#include "sieve-script-file.h"
-
-#include "sieve-storage-private.h"
-#include "sieve-storage-script.h"
-#include "sieve-storage-list.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <sys/stat.h>
-
-struct sieve_list_context {
-	pool_t pool;
-	struct sieve_storage *storage;
-
-	const char *active;
-	const char *dir;
-	DIR *dirp;
-
-	unsigned int seen_active:1; // Just present for assertions
-};
-
-struct sieve_list_context *sieve_storage_list_init
-(struct sieve_storage *storage)
-{
-	struct sieve_list_context *ctx;
-	const char *active = NULL;
-	pool_t pool;
-	DIR *dirp;
-
-	/* Open the directory */
-	if ( (dirp = opendir(storage->dir)) == NULL ) {
-		sieve_storage_set_critical(storage, "opendir(%s) failed: %m", storage->dir);
-		return NULL;
-	}
-
-	T_BEGIN {
-		/* Get the name of the active script */
-		if ( sieve_storage_active_script_get_file(storage, &active) < 0) {
-			ctx = NULL;
-		} else {
-			pool = pool_alloconly_create("sieve_list_context", 4096);
-			ctx = p_new(pool, struct sieve_list_context, 1);
-			ctx->pool = pool;
-			ctx->storage = storage;
-			ctx->dirp = dirp;
-			ctx->active = ( active != NULL ? p_strdup(pool, active) : NULL );
-			ctx->seen_active = FALSE;
-		}
-	} T_END;
-
-	if ( ctx == NULL && closedir(dirp) < 0)
-		i_error("sieve-storage: closedir(%s) failed: %m",	storage->dir);
-
-	return ctx;
-}
-
-const char *sieve_storage_list_next
-(struct sieve_list_context *ctx, bool *active)
-{
-	const struct sieve_storage *storage = ctx->storage;
-	struct dirent *dp;
-	const char *scriptname;
-
-	*active = FALSE;
-
-	for (;;) {
-		if ( (dp = readdir(ctx->dirp)) == NULL )
-			return NULL;
-
-		scriptname = sieve_scriptfile_get_script_name(dp->d_name);
-		if (scriptname != NULL ) {
-			/* Don't list our active sieve script link if the link
-			 * resides in the script dir (generally a bad idea).
-			 */
-			if ( *(storage->link_path) == '\0' &&
-				strcmp(storage->active_fname, dp->d_name) == 0 )
-				continue;
-
-			break;
-		}
-	}
-
-	if ( ctx->active != NULL && strcmp(dp->d_name, ctx->active) == 0 ) {
-		*active = TRUE;
-		ctx->active = NULL;
-	}
-
-	return scriptname;
-}
-
-int sieve_storage_list_deinit(struct sieve_list_context **ctx)
-{
-	if (closedir((*ctx)->dirp) < 0) {
-		sieve_storage_set_critical((*ctx)->storage, "closedir(%s) failed: %m",
-			(*ctx)->storage->dir);
-	}
-
-	pool_unref(&(*ctx)->pool);
-	*ctx = NULL;
-	return 1;
-}
-
-
-
-
diff --git a/src/lib-sievestorage/sieve-storage-list.h b/src/lib-sievestorage/sieve-storage-list.h
deleted file mode 100644
index b7eeab52584e1a61887e2f47e06e827d3bbc274f..0000000000000000000000000000000000000000
--- a/src/lib-sievestorage/sieve-storage-list.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
- */
-
-#ifndef __SIEVE_STORAGE_LIST_H
-#define __SIEVE_STORAGE_LIST_H
-
-#include "lib.h"
-#include "str.h"
-#include "sieve-storage.h"
-
-struct sieve_list_context;
-
-/* Create a context for listing the scripts in the storage */
-struct sieve_list_context *sieve_storage_list_init
-	(struct sieve_storage *storage);
-
-/* Get the next script in the storage. */
-const char *sieve_storage_list_next
-	(struct sieve_list_context *ctx, bool *active);
-
-/* Destroy the listing context */
-int sieve_storage_list_deinit
-	(struct sieve_list_context **ctx);
-
-#endif
-
-
-
-
diff --git a/src/lib-sievestorage/sieve-storage-private.h b/src/lib-sievestorage/sieve-storage-private.h
deleted file mode 100644
index d848d56967002aad40aeaab8450447bde69e52fd..0000000000000000000000000000000000000000
--- a/src/lib-sievestorage/sieve-storage-private.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
- */
-
-#ifndef __SIEVE_STORAGE_PRIVATE_H
-#define __SIEVE_STORAGE_PRIVATE_H
-
-#include "sieve.h"
-#include "sieve-error-private.h"
-
-#include "sieve-storage.h"
-
-#define SIEVE_READ_BLOCK_SIZE (1024*8)
-
-/* How often to scan tmp/ directory for old files (based on dir's atime) */
-#define SIEVE_STORAGE_TMP_SCAN_SECS (8*60*60)
-/* Delete files having ctime older than this from tmp/. 36h is standard. */
-#define SIEVE_STORAGE_TMP_DELETE_SECS (36*60*60)
-
-struct sieve_storage;
-
-struct sieve_storage_ehandler {
-	struct sieve_error_handler handler;
-	struct sieve_storage *storage;
-};
-
-/* All methods returning int return either TRUE or FALSE. */
-struct sieve_storage {
-	pool_t pool;
-	struct sieve_instance *svinst;
-
-	char *name;
-	char *dir;
-
-	/* Private */
-	char *active_path;
-	char *active_fname;
-	char *link_path;
-	char *error;
-	char *username; /* name of user accessing the storage */
-
-	mode_t dir_create_mode;
-	mode_t file_create_mode;
-	gid_t file_create_gid;
-
-	struct mailbox *inbox;
-
-	uint64_t max_scripts;
-	uint64_t max_storage;
-
-	enum sieve_error error_code;
-	struct sieve_error_handler *ehandler;
-
-	enum sieve_storage_flags flags;
-	time_t prev_mtime;
-};
-
-struct sieve_script *sieve_storage_script_init_from_path
-	(struct sieve_storage *storage, const char *path, const char *scriptname);
-
-void sieve_storage_inbox_script_attribute_set
-	(struct sieve_storage *storage, const char *name);
-void sieve_storage_inbox_script_attribute_rename
-	(struct sieve_storage *storage, const char *oldname, const char *newname);
-void sieve_storage_inbox_script_attribute_unset
-	(struct sieve_storage *storage, const char *name);
-
-
-#endif
-
diff --git a/src/lib-sievestorage/sieve-storage-quota.h b/src/lib-sievestorage/sieve-storage-quota.h
deleted file mode 100644
index 4c43b8afe1efe46f3d1ddcb0ae3abda3541b86f3..0000000000000000000000000000000000000000
--- a/src/lib-sievestorage/sieve-storage-quota.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
- */
-
-#ifndef __SIEVE_STORAGE_QUOTA_H
-#define __SIEVE_STORAGE_QUOTA_H
-
-#include "lib.h"
-
-#include "sieve-storage.h"
-
-enum sieve_storage_quota {
-	SIEVE_STORAGE_QUOTA_NONE,
-	SIEVE_STORAGE_QUOTA_MAXSIZE,
-	SIEVE_STORAGE_QUOTA_MAXSCRIPTS,
-	SIEVE_STORAGE_QUOTA_MAXSTORAGE
-};
-
-bool sieve_storage_quota_validsize
-	(struct sieve_storage *storage, size_t size, uint64_t *limit_r);
-
-uint64_t sieve_storage_quota_max_script_size
-	(struct sieve_storage *storage);
-
-int sieve_storage_quota_havespace
-	(struct sieve_storage *storage, const char *scriptname, size_t size,
-		enum sieve_storage_quota *quota_r, uint64_t *limit_r);
-
-#endif /* __SIEVE_STORAGE_QUOTA_H */
diff --git a/src/lib-sievestorage/sieve-storage-save.c b/src/lib-sievestorage/sieve-storage-save.c
deleted file mode 100644
index 3b54b66dec6c8c9099746d7a27f3e3ae59abcfdf..0000000000000000000000000000000000000000
--- a/src/lib-sievestorage/sieve-storage-save.c
+++ /dev/null
@@ -1,459 +0,0 @@
-/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
- */
-
-#include "lib.h"
-#include "hostpid.h"
-#include "ioloop.h"
-#include "array.h"
-#include "buffer.h"
-#include "ostream.h"
-#include "str.h"
-#include "eacces-error.h"
-#include "safe-mkstemp.h"
-
-#include "sieve-script.h"
-#include "sieve-script-file.h"
-
-#include "sieve-storage-private.h"
-#include "sieve-storage-script.h"
-#include "sieve-storage-save.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <utime.h>
-#include <sys/stat.h>
-
-struct sieve_save_context {
-	pool_t pool;
-
-	struct sieve_storage *storage;
-	const char *scriptname;
-	struct sieve_script *scriptobject;
-
-	struct istream *input;
-	struct ostream *output;
-	int fd;
-	const char *tmp_path;
-
-	time_t mtime;
-
-	unsigned int failed:1;
-	unsigned int moving:1;
-	unsigned int finished:1;
-};
-
-static const char *sieve_generate_tmp_filename(const char *scriptname)
-{
-	static struct timeval last_tv = { 0, 0 };
-	struct timeval tv;
-
-	/* use secs + usecs to guarantee uniqueness within this process. */
-	if (ioloop_timeval.tv_sec > last_tv.tv_sec ||
-		(ioloop_timeval.tv_sec == last_tv.tv_sec &&
-		ioloop_timeval.tv_usec > last_tv.tv_usec)) {
-		tv = ioloop_timeval;
-	} else {
-		tv = last_tv;
-		if (++tv.tv_usec == 1000000) {
-			tv.tv_sec++;
-			tv.tv_usec = 0;
-		}
-	}
-	last_tv = tv;
-
-	scriptname = t_strdup_printf("%s_%s.M%sP%s.%s",
-		( scriptname == NULL ? "NULL" : scriptname ),
-		dec2str(tv.tv_sec), dec2str(tv.tv_usec), my_pid, my_hostname);
-
-	return sieve_scriptfile_from_name(scriptname);
-}
-
-static int sieve_storage_create_tmp
-(struct sieve_storage *storage, const char *scriptname, const char **fpath_r)
-{
-	struct stat st;
-	unsigned int prefix_len;
-	const char *tmp_fname = NULL;
-	string_t *path;
-	int fd;
-
-	path = t_str_new(256);
-	str_append(path, storage->dir);
-	str_append(path, "/tmp/");
-	prefix_len = str_len(path);
-
-	for (;;) {
-		tmp_fname = sieve_generate_tmp_filename(scriptname);
-		str_truncate(path, prefix_len);
-		str_append(path, tmp_fname);
-
-		/* stat() first to see if it exists. pretty much the only
-		   possibility of that happening is if time had moved
-		   backwards, but even then it's highly unlikely. */
-		if (stat(str_c(path), &st) == 0) {
-			/* try another file name */
-		} else if (errno != ENOENT) {
-			sieve_storage_set_critical(storage,
-				"stat(%s) failed: %m", str_c(path));
-			return -1;
-		} else {
-			/* doesn't exist */
-			mode_t old_mask = umask(0777 & ~(storage->file_create_mode));
-			fd = open(str_c(path),
-				O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0777);
-			umask(old_mask);
-
-			if (fd != -1 || errno != EEXIST)
-				break;
-			/* race condition between stat() and open().
-				highly unlikely. */
-		}
-	}
-
-	*fpath_r = str_c(path);
-	if (fd == -1) {
-		if (ENOSPACE(errno)) {
-			sieve_storage_set_error(storage, SIEVE_ERROR_NO_SPACE,
-				"Not enough disk space");
-		} else {
-			sieve_storage_set_critical(storage,
-				"open(%s) failed: %m", str_c(path));
-		}
-	}
-
-	return fd;
-}
-
-
-static int sieve_storage_script_move(struct sieve_save_context *ctx,
-  const char *dst)
-{
-	int failed;
-
-	T_BEGIN {
-
-		/* Using rename() to ensure existing files are replaced
-		 * without conflicts with other processes using the same
-		 * file. The kernel wont fully delete the original until
-		 * all processes have closed the file.
-		 */
-		if (rename(ctx->tmp_path, dst) == 0)
-			failed = FALSE;
-		else {
-			failed = TRUE;
-			if ( ENOSPACE(errno) ) {
-				sieve_storage_set_error
-				  (ctx->storage, SIEVE_ERROR_NO_SPACE, "Not enough disk space");
-			} else if ( errno == EACCES ) {
-				sieve_storage_set_critical
-				  (ctx->storage, "%s", eacces_error_get("rename", dst));
-			} else {
-				sieve_storage_set_critical
-				  (ctx->storage, "rename(%s, %s) failed: %m", ctx->tmp_path, dst);
-			}
-		}
-
-		/* Always destroy temp file */
-		(void)unlink(ctx->tmp_path);
-
-	} T_END;
-
-	return !failed;
-}
-
-struct sieve_save_context *
-sieve_storage_save_init(struct sieve_storage *storage,
-	const char *scriptname, struct istream *input)
-{
-	struct sieve_save_context *ctx;
-	pool_t pool;
-	const char *path;
-
-	if ( scriptname != NULL ) {
-		/* Validate script name */
-		if ( !sieve_script_name_is_valid(scriptname) ) {
-			sieve_storage_set_error(storage,
-				SIEVE_ERROR_BAD_PARAMS,
-				"Invalid script name '%s'.", scriptname);
-			return NULL;
-		}
-
-		/* Prevent overwriting the active script link when it resides in the
-		 * sieve storage directory.
-		 */
-		if ( *(storage->link_path) == '\0' ) {
-			const char *svext;
-			size_t namelen;
-
-			svext = strrchr(storage->active_fname, '.');
-			namelen = svext - storage->active_fname;
-			if ( svext != NULL && strncmp(svext+1, "sieve", 5) == 0 &&
-				strlen(scriptname) == namelen &&
-				strncmp(scriptname, storage->active_fname, namelen) == 0 )
-			{
-				sieve_storage_set_error(
-					storage, SIEVE_ERROR_BAD_PARAMS,
-					"Script name '%s' is reserved for internal use.", scriptname);
-				return NULL;
-			}
-		}
-	}
-
-	pool = pool_alloconly_create("sieve_save_context", 4096);
-	ctx = p_new(pool, struct sieve_save_context, 1);
-	ctx->pool = pool;
-	ctx->storage = storage;
-	ctx->scriptname = p_strdup(pool, scriptname);
-	ctx->scriptobject = NULL;
-	ctx->mtime = (time_t)-1;
-
-	T_BEGIN {
-		ctx->fd = sieve_storage_create_tmp(storage, scriptname, &path);
-		if (ctx->fd == -1) {
-			ctx->failed = TRUE;
-			pool_unref(&pool);
-			ctx = NULL;
-		} else {
-			ctx->input = input;
-			ctx->output = o_stream_create_fd(ctx->fd, 0, FALSE);
-			ctx->tmp_path = p_strdup(pool, path);
-			ctx->failed = FALSE;
-		}
-	} T_END;
-
-	return ctx;
-}
-
-int sieve_storage_save_continue(struct sieve_save_context *ctx)
-{
-	if (o_stream_send_istream(ctx->output, ctx->input) < 0) {
-		sieve_storage_set_critical(ctx->storage,
-			"o_stream_send_istream(%s) failed: %m", ctx->tmp_path);
-		ctx->failed = TRUE;
-		return -1;
-	}
-	return 0;
-}
-
-int sieve_storage_save_finish(struct sieve_save_context *ctx)
-{
-	int output_errno;
-
-	ctx->finished = TRUE;
-	if ( ctx->failed && ctx->fd == -1 ) {
-		/* tmp file creation failed */
-		return -1;
-	}
-
-	T_BEGIN {
-		output_errno = ctx->output->stream_errno;
-		o_stream_destroy(&ctx->output);
-
-		if ( fsync(ctx->fd) < 0 ) {
-			sieve_storage_set_critical(ctx->storage,
-				"fsync(%s) failed: %m", ctx->tmp_path);
-			ctx->failed = TRUE;
-		}
-		if ( close(ctx->fd) < 0 ) {
-			sieve_storage_set_critical(ctx->storage,
-				"close(%s) failed: %m", ctx->tmp_path);
-			ctx->failed = TRUE;
-		}
-		ctx->fd = -1;
-
-		if ( ctx->failed ) {
-			/* delete the tmp file */
-			if (unlink(ctx->tmp_path) < 0 && errno != ENOENT)
-				i_warning("sieve-storage: Unlink(%s) failed: %m", ctx->tmp_path);
-
-			errno = output_errno;
-			if ( ENOSPACE(errno) ) {
-				sieve_storage_set_error(ctx->storage, SIEVE_ERROR_NO_SPACE,
-					"Not enough disk space");
-			} else if ( errno != 0 ) {
-				sieve_storage_set_critical(ctx->storage,
-					"write(%s) failed: %m", ctx->tmp_path);
-			}
-		}
-	} T_END;
-
-	return ( ctx->failed ? -1 : 0 );
-}
-
-void sieve_storage_save_set_mtime
-(struct sieve_save_context *ctx, time_t mtime)
-{
-	ctx->mtime = mtime;
-}
-
-static void sieve_storage_save_destroy(struct sieve_save_context **ctx)
-{
-	if ((*ctx)->scriptobject != NULL)
-		sieve_script_unref(&((*ctx)->scriptobject));
-
-	pool_unref(&(*ctx)->pool);
-	*ctx = NULL;
-}
-
-struct sieve_script *sieve_storage_save_get_tempscript
-(struct sieve_save_context *ctx)
-{
-	const char *scriptname =
-		( ctx->scriptname == NULL ? "" : ctx->scriptname );
-
-	if (ctx->failed)
-		return NULL;
-
-	if ( ctx->scriptobject != NULL )
-		return ctx->scriptobject;
-
-	ctx->scriptobject = sieve_storage_script_init_from_path
-		(ctx->storage, ctx->tmp_path, scriptname);
-
-	if ( ctx->scriptobject == NULL ) {
-		if ( ctx->storage->error_code == SIEVE_ERROR_NOT_FOUND ) {
-			sieve_storage_set_critical(ctx->storage,
-				"save: Temporary script file '%s' got lost, "
-				"which should not happen (possibly deleted externally).",
-				ctx->tmp_path);
-		}
-		return NULL;
-	}
-
-	return ctx->scriptobject;
-}
-
-bool sieve_storage_save_will_activate
-(struct sieve_save_context *ctx)
-{
-	bool result = FALSE;
-
-	if ( ctx->scriptname != NULL ) T_BEGIN {
-		const char *scriptname;
-		int ret;
-
-		ret = sieve_storage_active_script_get_name(ctx->storage, &scriptname);
-		if ( ret > 0 ) {
-		 	/* Is the requested script active? */
-			result = ( strcmp(ctx->scriptname, scriptname) == 0 );
-		}
-	} T_END;
-
-	return result;
-}
-
-static void sieve_storage_update_mtime(const char *path, time_t mtime)
-{
-	struct utimbuf times = { .actime = mtime, .modtime = mtime };
-
-	if ( utime(path, &times) < 0 ) {
-		switch ( errno ) {	
-		case ENOENT:
-			break;
-		case EACCES:
-			i_error("sieve-storage: %s", eacces_error_get("utime", path));
-			break;
-		default:
-			i_error("sieve-storage: utime(%s) failed: %m", path);
-		}
-	}
-}
-
-int sieve_storage_save_commit(struct sieve_save_context **_ctx)
-{
-	struct sieve_save_context *ctx = *_ctx;
-	struct sieve_storage *storage = ctx->storage;
-	const char *dest_path;
-	bool failed = FALSE;
-
-	i_assert(ctx->output == NULL);
-	i_assert(ctx->finished);
-	i_assert(ctx->scriptname != NULL);
-
-	T_BEGIN {
-		dest_path = t_strconcat(storage->dir, "/",
-			sieve_scriptfile_from_name(ctx->scriptname), NULL);
-
-		failed = !sieve_storage_script_move(ctx, dest_path);
-		if ( ctx->mtime != (time_t)-1 )
-			sieve_storage_update_mtime(dest_path, ctx->mtime);
-	} T_END;
-
-	/* set INBOX mailbox attribute */
-	if ( !failed )
-		sieve_storage_inbox_script_attribute_set(storage, ctx->scriptname);
-
-	sieve_storage_save_destroy(_ctx);
-
-	return ( failed ? -1 : 0 );
-}
-
-void sieve_storage_save_cancel(struct sieve_save_context **ctx)
-{
-	(*ctx)->failed = TRUE;
-
-	if (!(*ctx)->finished)
-		(void)sieve_storage_save_finish(*ctx);
-	else
-		(void)unlink((*ctx)->tmp_path);
-
-	i_assert((*ctx)->output == NULL);
-
-	sieve_storage_save_destroy(ctx);
-}
-
-int sieve_storage_save_as_active_script(struct sieve_storage *storage,
-	struct istream *input, time_t mtime)
-{
-	int fd;
-	string_t *temp_path;
-	struct ostream *output;
-
-	temp_path = t_str_new(256);
-	str_append(temp_path, storage->active_path);
-	str_append_c(temp_path, '.');
-	fd = safe_mkstemp_hostpid
-		(temp_path, storage->file_create_mode, (uid_t)-1, (gid_t)-1);
-	if ( fd < 0 ) {
-		if ( errno == EACCES ) {
-			sieve_storage_set_critical(storage,
-				"failed to create temporary file: %s",
-				eacces_error_get_creating("open", str_c(temp_path)));
-		} else {
-			sieve_storage_set_critical(storage,
-				"failed to create temporary file: open(%s) failed: %m",
-				str_c(temp_path));
-		}
-		return -1;
-	}
-
-	output = o_stream_create_fd(fd, 0, FALSE);
-	if (o_stream_send_istream(output, input) < 0) {
-		sieve_storage_set_critical(storage,
-			"o_stream_send_istream(%s) failed: %m", str_c(temp_path));
-		o_stream_destroy(&output);
-		(void)unlink(str_c(temp_path));
-		return -1;
-	}
-	o_stream_destroy(&output);
-
-	if (rename(str_c(temp_path), storage->active_path) < 0) {
-		if ( ENOSPACE(errno) ) {
-			sieve_storage_set_error(storage,
-				SIEVE_ERROR_NO_SPACE, "Not enough disk space");
-		} else if ( errno == EACCES ) {
-			sieve_storage_set_critical(storage,
-				"%s", eacces_error_get("rename", storage->active_path));
-		} else {
-			sieve_storage_set_critical(storage,
-				"rename(%s, %s) failed: %m", str_c(temp_path), storage->active_path);
-		}
-	} else {
-		sieve_storage_update_mtime(storage->active_path, mtime);
-	}
-
-	(void)unlink(str_c(temp_path));
-	return 0;
-}
diff --git a/src/lib-sievestorage/sieve-storage-save.h b/src/lib-sievestorage/sieve-storage-save.h
deleted file mode 100644
index c9f84f2427ed40ec0671211f7de5ba852d7d28a9..0000000000000000000000000000000000000000
--- a/src/lib-sievestorage/sieve-storage-save.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
- */
-
-#ifndef __SIEVE_SAVE_H
-#define __SIEVE_SAVE_H
-
-#include "sieve-storage.h"
-
-struct sieve_save_context;
-
-struct sieve_save_context *
-sieve_storage_save_init(struct sieve_storage *storage,
-	const char *scriptname, struct istream *input);
-
-int sieve_storage_save_continue(struct sieve_save_context *ctx);
-
-int sieve_storage_save_finish(struct sieve_save_context *ctx);
-
-struct sieve_script *sieve_storage_save_get_tempscript
-  (struct sieve_save_context *ctx);
-
-bool sieve_storage_save_will_activate
-	(struct sieve_save_context *ctx);
-
-void sieve_storage_save_set_mtime
-	(struct sieve_save_context *ctx, time_t mtime);
-
-void sieve_storage_save_cancel(struct sieve_save_context **ctx);
-
-int sieve_storage_save_commit(struct sieve_save_context **ctx);
-
-/* Saves input directly as a regular file at the active script path.
- * This is needed for the doveadm-sieve plugin.
- */
-int sieve_storage_save_as_active_script(struct sieve_storage *storage,
-	struct istream *input, time_t mtime);
-
-#endif
-
diff --git a/src/lib-sievestorage/sieve-storage-script.c b/src/lib-sievestorage/sieve-storage-script.c
deleted file mode 100644
index 04c7e3d527ded1c3ac3ea8a6141935e93ab90d11..0000000000000000000000000000000000000000
--- a/src/lib-sievestorage/sieve-storage-script.c
+++ /dev/null
@@ -1,740 +0,0 @@
-/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
- */
-
-#include "lib.h"
-#include "abspath.h"
-#include "mempool.h"
-#include "hostpid.h"
-#include "ioloop.h"
-#include "istream.h"
-#include "file-copy.h"
-#include "eacces-error.h"
-
-#include "sieve-script-private.h"
-#include "sieve-script-file.h"
-
-#include "sieve-storage.h"
-#include "sieve-storage-private.h"
-#include "sieve-storage-script.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <ctype.h>
-#include <time.h>
-#include <fcntl.h>
-
-struct sieve_storage_script {
-	struct sieve_file_script file;
-
-	struct sieve_storage *storage;
-};
-
-static int _file_path_cmp(const char *path1, const char *path2)
-{
-	const char *p1, *p2;
-	int ret;
-	
-	p1 = path1; p2 = path2;
-	if (*p2 == '\0' && *p1 != '\0')
-		return 1;
-	if (*p1 == '\0' && *p2 != '\0')
-		return -1;
-	if (*p1 == '/' && *p2 != '/')
-		return 1;
-	if (*p2 == '/' && *p1 != '/')
-		return -1;	
-	for (;;) {
-		const char *s1, *s2;
-		size_t size1, size2;
-
-		/* skip repeated slashes */
-		for (; *p1 == '/'; p1++);
-		for (; *p2 == '/'; p2++);	
-		/* check for end of comparison */
-		if (*p1 == '\0' || *p2 == '\0')
-			break;
-		/* mark start of path element */
-		s1 = p1;
-		s2 = p2;
-		/* scan to end of path elements */
-		for (; *p1 != '\0' && *p1 != '/'; p1++);
-		for (; *p2 != '\0' && *p2 != '/'; p2++);
-		/* compare sizes */
-		size1 = p1 - s1;
-		size2 = p2 - s2;
-		if (size1 != size2)
-			return size1 - size2;
-		/* compare */
-		if (size1 > 0 && (ret=memcmp(s1, s2, size1)) != 0)
-			return ret;
-	}
-	if (*p1 == '\0') {
-		if (*p2 == '\0')
-			return 0;
-		return -1;
-	}
-	return 1;
-}
-
-struct sieve_script *sieve_storage_script_init_from_path
-(struct sieve_storage *storage, const char *path,
-	const char *scriptname)
-{
-	pool_t pool;
-	struct sieve_storage_script *st_script = NULL;
-	enum sieve_error error;
-
-	/* Prevent initializing the active script link as a script when it
-	 * resides in the sieve storage directory.
-	 */
-	if ( scriptname != NULL && *(storage->link_path) == '\0' ) {
-		const char *fname;
-
-		fname = strrchr(path, '/');
-		if ( fname == NULL )
-			fname = path;
-		else
-			fname++;
-
-		if ( strcmp(fname, storage->active_fname) == 0 ) {
-			sieve_storage_set_error
-				(storage, SIEVE_ERROR_NOT_FOUND, "Script does not exist.");
-			return NULL;
-		}
-	}
-
-	pool = pool_alloconly_create("sieve_storage_script", 4096);
-	st_script = p_new(pool, struct sieve_storage_script, 1);
-	st_script->file.script = sieve_file_script;
-	st_script->file.script.pool = pool;
-	st_script->storage = storage;
-
-	sieve_script_init
-		(&st_script->file.script, storage->svinst, &sieve_file_script, path,
-			scriptname,	sieve_storage_get_error_handler(storage));
-
-	if ( sieve_script_open(&st_script->file.script, &error) < 0 ) {
-		if ( error == SIEVE_ERROR_NOT_FOUND )
-			sieve_storage_set_error(storage, error, "Script does not exist.");
-		pool_unref(&pool);
-		return NULL;
-	}
-
-	return &st_script->file.script;
-}
-
-struct sieve_script *sieve_storage_script_init
-(struct sieve_storage *storage, const char *scriptname)
-{
-	struct sieve_script *script;
-	const char *path;
-
-	/* Validate script name */
-	if ( !sieve_script_name_is_valid(scriptname) ) {
-		sieve_storage_set_error(storage, SIEVE_ERROR_BAD_PARAMS,
-			"Invalid script name '%s'.", scriptname);
-		return NULL;
-	}
-
-	T_BEGIN {
-		path = t_strconcat
-			( storage->dir, "/", sieve_scriptfile_from_name(scriptname), NULL );
-
-		script = sieve_storage_script_init_from_path(storage, path, NULL);
-	} T_END;
-
-	return script;
-}
-
-static struct sieve_script *sieve_storage_script_init_from_file
-(struct sieve_storage *storage, const char *scriptfile)
-{
-	struct sieve_script *script;
-	const char *path;
-
-	T_BEGIN {
-		path = t_strconcat( storage->dir, "/", scriptfile, NULL );
-
-		script = sieve_storage_script_init_from_path(storage, path, NULL);
-	} T_END;
-
-	return script;
-}
-
-static int sieve_storage_read_active_link
-(struct sieve_storage *storage, const char **link_r)
-{
-	int ret;
-
-	ret = t_readlink(storage->active_path, link_r);
-
-	if ( ret < 0 ) {
-		*link_r = NULL;
-
-		if ( errno == EINVAL ) {
-			/* Our symlink is no symlink. Report 'no active script'.
-			 * Activating a script will automatically resolve this, so
-			 * there is no need to panic on this one.
-			 */
-			if ( (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 ) {
-				i_warning
-				  ("sieve-storage: Active sieve script symlink %s is no symlink.",
-				   storage->active_path);
-			}
-			return 0;
-		}
-
-		if ( errno == ENOENT ) {
-			/* Symlink not found */
-			return 0;
-		}
-
-		/* We do need to panic otherwise */
-		sieve_storage_set_critical(storage,
-			"Performing readlink() on active sieve symlink '%s' failed: %m",
-			storage->active_path);
-		return -1;
-	}
-
-	/* ret is now assured to be valid, i.e. > 0 */
-	return 1;
-}
-
-static const char *sieve_storage_parse_active_link
-(struct sieve_storage *storage, const char *link, const char **scriptname_r)
-{
-	const char *fname, *scriptname, *scriptpath;
-
-	/* Split link into path and filename */
-	fname = strrchr(link, '/');
-	if ( fname != NULL ) {
-		scriptpath = t_strdup_until(link, fname+1);
-		fname++;
-	} else {
-		scriptpath = "";
-		fname = link;
-	}
-
-	/* Check the script name */
-	scriptname = sieve_scriptfile_get_script_name(fname);
-
-	/* Warn if link is deemed to be invalid */
-	if ( scriptname == NULL ) {
-		i_warning
-			("sieve-storage: Active sieve script symlink %s is broken: "
-				"invalid scriptname (points to %s).",
-				storage->active_path, link);
-		return NULL;
-	}
-
-	/* Check whether the path is any good */
-	if ( _file_path_cmp(scriptpath, storage->link_path) != 0 &&
-		_file_path_cmp(scriptpath, storage->dir) != 0 ) {
-		i_warning
-			("sieve-storage: Active sieve script symlink %s is broken: "
-				"invalid/unknown path to storage (points to %s).",
-				storage->active_path, link);
-		return NULL;
-	}
-
-	if ( scriptname_r != NULL )
-		*scriptname_r = scriptname;
-
-	return fname;
-}
-
-int sieve_storage_active_script_get_file
-(struct sieve_storage *storage, const char **file_r)
-{
-	const char *link, *scriptfile;
-	int ret;
-
-	*file_r = NULL;
-
-	/* Read the active link */
-	if ( (ret=sieve_storage_read_active_link(storage, &link)) <= 0 )
-		return ret;
-
-	/* Parse the link */
-	scriptfile = sieve_storage_parse_active_link(storage, link, NULL);
-
-	if (scriptfile == NULL) {
-		/* Obviously someone has been playing with our symlink,
-		 * ignore this situation and report 'no active script'.
-		 * Activation should fix this situation.
-		 */
-		return 0;
-	}
-
-	*file_r = scriptfile;
-	return 1;
-}
-
-int sieve_storage_active_script_get_name
-(struct sieve_storage *storage, const char **name_r)
-{
-	const char *link;
-	int ret;
-
-	*name_r = NULL;
-
-	/* Read the active link */
-	if ( (ret=sieve_storage_read_active_link(storage, &link)) <= 0 )
-		return ret;
-
-	if ( sieve_storage_parse_active_link(storage, link, name_r) == NULL ) {
-		/* Obviously someone has been playing with our symlink,
-		 * ignore this situation and report 'no active script'.
-		 * Activation should fix this situation.
-		 */
-		return 0;
-	}
-
-	return 1;
-}
-
-const char *sieve_storage_active_script_get_path
-	(struct sieve_storage *storage)
-{
-	return storage->active_path;
-}
-
-struct sieve_script *sieve_storage_active_script_get
-(struct sieve_storage *storage)
-{
-	struct sieve_script *script;
-	const char *scriptfile, *link;
-	int ret;
-
-	sieve_storage_clear_error(storage);
-
-	/* Read the active link */
-	if ( (ret=sieve_storage_read_active_link(storage, &link)) <= 0 ) {
-		if ( ret == 0 ) {
-			/* Try to open the active_path as a regular file */
-			return sieve_storage_script_init_from_path
-				(storage, storage->active_path, NULL);
-		}
-
-		return NULL;
-	}
-
-	/* Parse the link */
-	scriptfile = sieve_storage_parse_active_link(storage, link, NULL);
-
-	if (scriptfile == NULL) {
-		/* Obviously someone has been playing with our symlink,
-		 * ignore this situation and report.
-		 */
-		return NULL;
-	}
-
-	script = sieve_storage_script_init_from_file(storage, scriptfile);
-
-	if ( script == NULL && storage->error_code == SIEVE_ERROR_NOT_FOUND ) {
-		i_warning
-		  ("sieve-storage: Active sieve script symlink %s "
-		   "points to non-existent script (points to %s).",
-		   storage->active_path, link);
-	}
-
-	return script;
-}
-
-int sieve_storage_active_script_get_last_change
-(struct sieve_storage *storage, time_t *last_change_r)
-{
-	struct stat st;
-
-	/* Try direct lstat first */
-	if (lstat(storage->active_path, &st) == 0) {
-		if (!S_ISLNK(st.st_mode)) {
-			*last_change_r = st.st_mtime;
-			return 0;
-		}
-	}
-	/* Check error */
-	else if (errno != ENOENT) {
-		sieve_storage_set_critical(storage, "lstat(%s) failed: %m",
-			   storage->active_path);
-	}
-
-	/* Fall back to statting storage directory */
-	return sieve_storage_get_last_change(storage, last_change_r);
-}
-
-int
-sieve_storage_active_script_is_no_link(struct sieve_storage *storage)
-{
-	struct stat st;
-
-	/* Stat the file */
-	if ( lstat(storage->active_path, &st) != 0 ) {
-		if ( errno != ENOENT ) {
-			sieve_storage_set_critical(storage,
-				"Failed to stat active sieve script symlink (%s): %m.",
-				storage->active_path);
-			return -1;
-		}
-		return 0;
-	}
-
-	if ( S_ISLNK( st.st_mode ) )
-		return 0;
-	if ( !S_ISREG( st.st_mode ) ) {
-		sieve_storage_set_critical( storage,
-			"Active sieve script file '%s' is no symlink nor a regular file.",
-			storage->active_path );
-		return -1;
-	}
-	return 1;
-}
-
-int sieve_storage_script_is_active(struct sieve_script *script)
-{
-	struct sieve_storage_script *st_script =
-		(struct sieve_storage_script *) script;
-	const char *afile;
-	int ret = 0;
-
-	T_BEGIN {
-		ret = sieve_storage_active_script_get_file(st_script->storage, &afile);
-
-		if ( ret > 0 ) {
-		 	/* Is the requested script active? */
-			ret = ( strcmp(st_script->file.filename, afile) == 0 ? 1 : 0 );
-		}
-	} T_END;
-
-	return ret;
-}
-
-int sieve_storage_script_delete(struct sieve_script **script)
-{
-	struct sieve_storage_script *st_script =
-		(struct sieve_storage_script *) *script;
-	struct sieve_storage *storage = st_script->storage;
-	int ret = 0;
-
-	/* Is the requested script active? */
-	if ( sieve_storage_script_is_active(*script) ) {
-		sieve_storage_set_error(storage, SIEVE_ERROR_ACTIVE,
-			"Cannot delete the active sieve script.");
-		ret = -1;
-	} else {
-		ret = unlink(st_script->file.path);
-
-		if ( ret < 0 ) {
-			if ( errno == ENOENT )
-				sieve_storage_set_error(storage, SIEVE_ERROR_NOT_FOUND,
-					"Sieve script does not exist.");
-			else
-				sieve_storage_set_critical(
-					storage, "Performing unlink() failed on sieve file '%s': %m",
-					st_script->file.path);
-		}
-	}
-
-	/* unset INBOX mailbox attribute */
-	if ( ret >= 0 )
-		sieve_storage_inbox_script_attribute_unset(storage, (*script)->name);
-
-	/* Always deinitialize the script object */
-	sieve_script_unref(script);
-	return ret;
-}
-
-static bool sieve_storage_rescue_regular_file(struct sieve_storage *storage)
-{
-	bool debug = ( (storage->flags & SIEVE_STORAGE_FLAG_DEBUG) != 0 );
-	struct stat st;
-
-	/* Stat the file */
-	if ( lstat(storage->active_path, &st) != 0 ) {
-		if ( errno != ENOENT ) {
-			sieve_storage_set_critical(storage,
-				"Failed to stat active sieve script symlink (%s): %m.",
-				storage->active_path);
-			return FALSE;
-		}
-		return TRUE;
-	}
-
-	if ( S_ISLNK( st.st_mode ) ) {
-		if ( debug )
-			i_debug( "sieve-storage: nothing to rescue %s.", storage->active_path);
-		return TRUE; /* Nothing to rescue */
-	}
-
-	/* Only regular files can be rescued */
-	if ( S_ISREG( st.st_mode ) ) {
-		const char *dstpath;
-		bool result = TRUE;
-
- 		T_BEGIN {
-
-			dstpath = t_strconcat
-				( storage->dir, "/", sieve_scriptfile_from_name("dovecot.orig"), NULL );
-			if ( file_copy(storage->active_path, dstpath, 1) < 1 ) {
-				sieve_storage_set_critical(storage,
-					"Active sieve script file '%s' is a regular file and copying it to "
-					"the script storage as '%s' failed. This needs to be fixed manually.",
-						storage->active_path, dstpath);
-				result = FALSE;
-			} else {
-				i_info("sieve-storage: Moved active sieve script file '%s' "
-					"to script storage as '%s'.",
-					storage->active_path, dstpath);
-    		}
-		} T_END;
-
-		return result;
-  	}
-
-	sieve_storage_set_critical( storage,
-		"Active sieve script file '%s' is no symlink nor a regular file. "
-		"This needs to be fixed manually.", storage->active_path );
-	return FALSE;
-}
-
-int sieve_storage_deactivate(struct sieve_storage *storage, time_t mtime)
-{
-	int ret;
-
-	if ( !sieve_storage_rescue_regular_file(storage) )
-		return -1;
-
-	/* Delete the symlink, so no script is active */
-	ret = unlink(storage->active_path);
-
-	if ( ret < 0 ) {
-		if ( errno != ENOENT ) {
-			sieve_storage_set_critical(storage, "sieve_storage_deactivate(): "
-				"error on unlink(%s): %m", storage->active_path);
-			return -1;
-		} else {
-			return 0;
-		}
-	}
-
-	sieve_storage_set_modified(storage, mtime);
-	return 1;
-}
-
-static int sieve_storage_replace_active_link
-	(struct sieve_storage *storage, const char *link_path)
-{
-	const char *active_path_new;
-	struct timeval *tv, tv_now;
-	int ret = 0;
-
-	tv = &ioloop_timeval;
-
-	for (;;) {
-		/* First the new symlink is created with a different filename */
-		active_path_new = t_strdup_printf
-			("%s-new.%s.P%sM%s.%s",
-				storage->active_path,
-				dec2str(tv->tv_sec), my_pid,
-				dec2str(tv->tv_usec), my_hostname);
-
-		ret = symlink(link_path, active_path_new);
-
-		if ( ret < 0 ) {
-			/* If link exists we try again later */
-			if ( errno == EEXIST ) {
-				/* Wait and try again - very unlikely */
-				sleep(2);
-				tv = &tv_now;
-				if (gettimeofday(&tv_now, NULL) < 0)
-					i_fatal("gettimeofday(): %m");
-				continue;
-			}
-
-			/* Other error, critical */
-			sieve_storage_set_critical
-				(storage, "Creating symlink() %s to %s failed: %m",
-				active_path_new, link_path);
-			return -1;
-		}
-
-		/* Link created */
-		break;
-	}
-
-	/* Replace the existing link. This activates the new script */
-	ret = rename(active_path_new, storage->active_path);
-
-	if ( ret < 0 ) {
-		/* Failed; created symlink must be deleted */
-		(void)unlink(active_path_new);
-		sieve_storage_set_critical
-			(storage, "Performing rename() %s to %s failed: %m",
-			active_path_new, storage->active_path);
-		return -1;
-	}
-
-	return 1;
-}
-
-static int _sieve_storage_script_activate(struct sieve_script *script, time_t mtime)
-{
-	struct sieve_storage_script *st_script =
-		(struct sieve_storage_script *) script;
-	struct sieve_storage *storage = st_script->storage;
-	struct stat st;
-	const char *link_path, *afile;
-	int activated = 0;
-	int ret;
-
-	/* Find out whether there is an active script, but recreate
-	 * the symlink either way. This way, any possible error in the symlink
-	 * resolves automatically. This step is only necessary to provide a
-	 * proper return value indicating whether the script was already active.
-	 */
-	ret = sieve_storage_active_script_get_file(storage, &afile);
-
-	/* Is the requested script already active? */
-	if ( ret <= 0 || strcmp(st_script->file.filename, afile) != 0 )
-		activated = 1;
-
-	/* Check the scriptfile we are trying to activate */
-	if ( lstat(st_script->file.path, &st) != 0 ) {
-		sieve_storage_set_critical(storage,
-		  "Stat on sieve script %s failed, but it is to be activated: %m.",
-			st_script->file.path);
-		return -1;
-	}
-
-	/* Rescue a possible .dovecot.sieve regular file remaining from old
-	 * installations.
-	 */
-	if ( !sieve_storage_rescue_regular_file(storage) ) {
-		/* Rescue failed, manual intervention is necessary */
-		return -1;
-	}
-
-	/* Just try to create the symlink first */
-	link_path = t_strconcat
-	  ( storage->link_path, st_script->file.filename, NULL );
-
- 	ret = symlink(link_path, storage->active_path);
-
-	if ( ret < 0 ) {
-		if ( errno == EEXIST ) {
-			ret = sieve_storage_replace_active_link(storage, link_path);
-			if ( ret < 0 ) {
-				return ret;
-			}
-		} else {
-			/* Other error, critical */
-			sieve_storage_set_critical
-				(storage,
-					"Creating symlink() %s to %s failed: %m",
-					storage->active_path, link_path);
-			return -1;
-		}
-	}
-
-	sieve_storage_set_modified(storage, mtime);
-	return activated;
-}
-
-int sieve_storage_script_activate(struct sieve_script *script, time_t mtime)
-{
-	int ret;
-
-	T_BEGIN {
-		ret = _sieve_storage_script_activate(script, mtime);
-	} T_END;
-
-	return ret;
-}
-
-int sieve_storage_script_rename
-(struct sieve_script *script, const char *newname)
-{
-	struct sieve_storage_script *st_script =
-		(struct sieve_storage_script *) script;
-	struct sieve_storage *storage = st_script->storage;
-	const char *oldname = script->name, *newpath, *newfile, *link_path;
-	int ret = 0;
-
-	/* Check script name */
-	if ( !sieve_script_name_is_valid(newname) ) {
-		sieve_storage_set_error(storage,
-			SIEVE_ERROR_BAD_PARAMS,
-			"Invalid new script name '%s'.", newname);
-		return -1;
-	}
-
-	T_BEGIN {
-		newfile = sieve_scriptfile_from_name(newname);
-		newpath = t_strconcat( storage->dir, "/", newfile, NULL );
-
-		/* The normal rename() system call overwrites the existing file without
-		 * notice. Also, active scripts must not be disrupted by renaming a script.
-		 * That is why we use a link(newpath) [activate newpath] unlink(oldpath)
-		 */
-
-		/* Link to the new path */
-		ret = link(st_script->file.path, newpath);
-		if ( ret >= 0 ) {
-			/* Is the requested script active? */
-			if ( sieve_storage_script_is_active(script) ) {
-				/* Active; make active link point to the new copy */
-				link_path = t_strconcat
-					( storage->link_path, newfile, NULL );
-
-				ret = sieve_storage_replace_active_link(storage, link_path);
-			}
-
-			if ( ret >= 0 ) {
-				/* If all is good, remove the old link */
-				if ( unlink(st_script->file.path) < 0 ) {
-					i_error("Failed to clean up old file link '%s' after rename: %m",
-						st_script->file.path);
-				}
-
-				if ( script->name != NULL && *script->name != '\0' )
-					script->name = p_strdup(script->pool, newname);
-				st_script->file.path = p_strdup(script->pool, newpath);
-				st_script->file.filename = p_strdup(script->pool, newfile);
-			} else {
-				/* If something went wrong, remove the new link to restore previous
-				 * state
-				 */
-				if ( unlink(newpath) < 0 ) {
-					i_error("Failed to clean up new file link '%s'"
-						" after failed rename: %m", newpath);
-				}
-			}
-		} else {
-			/* Our efforts failed right away */
-			switch ( errno ) {
-			case ENOENT:
-				sieve_storage_set_error(storage, SIEVE_ERROR_NOT_FOUND,
-					"Sieve script does not exist.");
-				break;
-			case EEXIST:
-				sieve_storage_set_error(storage, SIEVE_ERROR_EXISTS,
-					"A sieve script with that name already exists.");
-				break;
-			default:
-				sieve_storage_set_critical(
-					storage, "Performing link(%s, %s) failed: %m",
-						st_script->file.path, newpath);
-			}
-		}
-	} T_END;
-
-	/* rename INBOX mailbox attribute */
-	if ( ret >= 0 && oldname != NULL )
-		sieve_storage_inbox_script_attribute_rename(storage, oldname, newname);
-
-	return ret;
-}
-
-
diff --git a/src/lib-sievestorage/sieve-storage-script.h b/src/lib-sievestorage/sieve-storage-script.h
deleted file mode 100644
index 63f958d5faf87913e5d6e1419169142a16844277..0000000000000000000000000000000000000000
--- a/src/lib-sievestorage/sieve-storage-script.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
- */
-
-#ifndef __SIEVE_STORAGE_SCRIPT_H
-#define __SIEVE_STORAGE_SCRIPT_H
-
-#include "sieve-script.h"
-
-#include "sieve-storage.h"
-
-struct sieve_script *sieve_storage_script_init
-	(struct sieve_storage *storage, const char *scriptname);
-
-int sieve_storage_active_script_get_file
-	(struct sieve_storage *storage, const char **file_r);
-int sieve_storage_active_script_get_name
-	(struct sieve_storage *storage, const char **name_r);
-const char *sieve_storage_active_script_get_path
-	(struct sieve_storage *storage);
-int sieve_storage_active_script_is_no_link(struct sieve_storage *storage);
-struct sieve_script *sieve_storage_active_script_get
-	(struct sieve_storage *storage);
-int sieve_storage_active_script_get_last_change
-	(struct sieve_storage *storage, time_t *last_change_r);
-
-int sieve_storage_deactivate(struct sieve_storage *storage, time_t mtime);
-int sieve_storage_script_is_active(struct sieve_script *script);
-int sieve_storage_script_activate(struct sieve_script *script, time_t mtime);
-int sieve_storage_script_delete(struct sieve_script **script);
-int sieve_storage_script_rename
-	(struct sieve_script *script, const char *newname);
-
-#endif
-
diff --git a/src/lib-sievestorage/sieve-storage.c b/src/lib-sievestorage/sieve-storage.c
deleted file mode 100644
index 8082be5132981f9003efe063c69b302c7fe55bcf..0000000000000000000000000000000000000000
--- a/src/lib-sievestorage/sieve-storage.c
+++ /dev/null
@@ -1,744 +0,0 @@
-/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
- */
-
-#include "lib.h"
-#include "home-expand.h"
-#include "ioloop.h"
-#include "mkdir-parents.h"
-#include "eacces-error.h"
-#include "unlink-old-files.h"
-#include "mail-storage-private.h"
-
-#include "sieve.h"
-#include "sieve-common.h"
-#include "sieve-settings.h"
-#include "sieve-error-private.h"
-#include "sieve-settings.h"
-
-#include "sieve-storage-private.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <ctype.h>
-#include <time.h>
-#include <utime.h>
-
-#define SIEVE_DEFAULT_PATH "~/.dovecot."SIEVE_SCRIPT_FILEEXT
-
-#define MAX_DIR_CREATE_MODE 0770
-
-#define CRITICAL_MSG \
-  "Internal error occured. Refer to server log for more information."
-#define CRITICAL_MSG_STAMP CRITICAL_MSG " [%Y-%m-%d %H:%M:%S]"
-
-static void sieve_storage_verror
-	(struct sieve_error_handler *ehandler ATTR_UNUSED,
-		unsigned int flags ATTR_UNUSED, const char *location ATTR_UNUSED,
-		const char *fmt, va_list args);
-
-static const char *sieve_storage_get_relative_link_path
-	(const char *active_path, const char *storage_dir)
-{
-	const char *link_path, *p;
-	size_t pathlen;
-
-	/* Determine to what extent the sieve storage and active script
-	 * paths match up. This enables the managed symlink to be short and the
-	 * sieve storages can be moved around without trouble (if the active
-	 * script path is common to the script storage).
-	 */
-	p = strrchr(active_path, '/');
-	if ( p == NULL ) {
-		link_path = storage_dir;
-	} else {
-		pathlen = p - active_path;
-
-		if ( strncmp( active_path, storage_dir, pathlen ) == 0 &&
-			(storage_dir[pathlen] == '/' || storage_dir[pathlen] == '\0') )
-		{
-			if ( storage_dir[pathlen] == '\0' )
-				link_path = "";
-			else
-				link_path = storage_dir + pathlen + 1;
-		} else
-			link_path = storage_dir;
-	}
-
-	/* Add trailing '/' when link path is not empty
-	 */
-	pathlen = strlen(link_path);
-    if ( pathlen != 0 && link_path[pathlen-1] != '/')
-        return t_strconcat(link_path, "/", NULL);
-
-	return t_strdup(link_path);
-}
-
-static mode_t get_dir_mode(mode_t mode)
-{
-	/* Add the execute bit if either read or write bit is set */
-
-	if ((mode & 0600) != 0) mode |= 0100;
-	if ((mode & 0060) != 0) mode |= 0010;
-	if ((mode & 0006) != 0) mode |= 0001;
-
-	return mode;
-}
-
-static void sieve_storage_get_permissions
-(const char *path, mode_t *file_mode_r, mode_t *dir_mode_r, gid_t *gid_r,
-	const char **gid_origin_r, bool debug)
-{
-	struct stat st;
-
-	/* Use safe defaults */
-	*file_mode_r = 0600;
-	*dir_mode_r = 0700;
-	*gid_r = (gid_t)-1;
-	*gid_origin_r = "defaults";
-
-	if ( stat(path, &st) < 0 ) {
-		if ( !ENOTFOUND(errno) ) {
-			i_error("sieve-storage: stat(%s) failed: %m", path);
-		} else if ( debug ) {
-			i_debug("sieve-storage: permission lookup failed from %s", path);
-		}
-		return;
-
-	} else {
-		*file_mode_r = (st.st_mode & 0666) | 0600;
-		*dir_mode_r = (st.st_mode & 0777) | 0700;
-		*gid_origin_r = path;
-
-		if ( !S_ISDIR(st.st_mode) ) {
-			/* We're getting permissions from a file. Apply +x modes as necessary. */
-			*dir_mode_r = get_dir_mode(*dir_mode_r);
-		}
-
-		if (S_ISDIR(st.st_mode) && (st.st_mode & S_ISGID) != 0) {
-			/* Directory's GID is used automatically for new files */
-			*gid_r = (gid_t)-1;
-		} else if ((st.st_mode & 0070) >> 3 == (st.st_mode & 0007)) {
-			/* Group has same permissions as world, so don't bother changing it */
-			*gid_r = (gid_t)-1;
-		} else if (getegid() == st.st_gid) {
-			/* Using our own gid, no need to change it */
-			*gid_r = (gid_t)-1;
-		} else {
-			*gid_r = st.st_gid;
-		}
-	}
-
-	if ( debug ) {
-		i_debug("sieve-storage: using permissions from %s: mode=0%o gid=%ld",
-			path, (int)*dir_mode_r, *gid_r == (gid_t)-1 ? -1L : (long)*gid_r);
-	}
-}
-
-static int mkdir_verify
-(const char *dir, mode_t mode, gid_t gid, const char *gid_origin, bool debug)
-{
-	struct stat st;
-
-	if ( stat(dir, &st) == 0 )
-		return 0;
-
-	if ( errno == EACCES ) {
-		i_error("sieve-storage: mkdir_verify: %s", eacces_error_get("stat", dir));
-		return -1;
-	} else if ( errno != ENOENT ) {
-		i_error("sieve-storage: mkdir_verify: stat(%s) failed: %m", dir);
-		return -1;
-	}
-
-	if ( mkdir_parents_chgrp(dir, mode, gid, gid_origin) == 0 ) {
-		if ( debug )
-			i_debug("sieve-storage: created storage directory %s", dir);
-		return 0;
-	}
-
-	switch ( errno ) {
-	case EEXIST:
-		return 0;
-	case ENOENT:
-		i_error("sieve-storage: storage was deleted while it was being created");
-		break;
-	case EACCES:
-		i_error("sieve-storage: %s",
-			eacces_error_get_creating("mkdir_parents_chgrp", dir));
-		break;
-	default:
-		i_error("sieve-storage: mkdir_parents_chgrp(%s) failed: %m", dir);
-		break;
-	}
-
-	return -1;
-}
-
-static int check_tmp(const char *path)
-{
-	struct stat st;
-
-	/* If tmp/ directory exists, we need to clean it up once in a while */
-	if ( stat(path, &st) < 0 ) {
-		if ( errno == ENOENT )
-			return 0;
-		if ( errno == EACCES ) {
-			i_error("sieve-storage: check_tmp: %s", eacces_error_get("stat", path));
-			return -1;
-		}
-		i_error("sieve-storage: check_tmp: stat(%s) failed: %m", path);
-		return -1;
-	}
-
-	if ( st.st_atime > st.st_ctime + SIEVE_STORAGE_TMP_DELETE_SECS ) {
-		/* The directory should be empty. we won't do anything
-		   until ctime changes. */
-	} else if ( st.st_atime < ioloop_time - SIEVE_STORAGE_TMP_SCAN_SECS ) {
-		/* Time to scan */
-		(void)unlink_old_files(path, "",
-			ioloop_time - SIEVE_STORAGE_TMP_DELETE_SECS);
-	}
-	return 1;
-}
-
-static int _sieve_storage_open_inbox
-(struct mail_user *user, struct mailbox **box_r)
-{
-	struct mail_namespace *ns;
-	struct mailbox *box;
-	enum mailbox_flags flags = MAILBOX_FLAG_IGNORE_ACLS;
-	enum mail_error error;
-
-	ns = mail_namespace_find_inbox(user->namespaces);
-	*box_r = box = mailbox_alloc(ns->list, "INBOX", flags);
-	if (mailbox_open(box) == 0)
-		return 0;
-
-	i_warning("sieve-storage: "
-		"Failed to open user INBOX for attribute modifications: %s",
-		mailbox_get_last_error(box, &error));
-	return -1;
-}
-
-static struct sieve_storage *_sieve_storage_create
-(struct sieve_instance *svinst, struct mail_user *user, const char *home,
-	enum sieve_storage_flags flags)
-{
-	pool_t pool;
-	struct sieve_storage *storage;
-	struct mailbox *inbox = NULL;
-	bool debug = ( (flags & SIEVE_STORAGE_FLAG_DEBUG) != 0 );
-	const char *tmp_dir, *link_path, *path;
-	const char *sieve_data, *active_path, *active_fname, *storage_dir;
-	const char *username = user->username;
-	mode_t dir_create_mode, file_create_mode;
-	gid_t file_create_gid;
-	const char *file_create_gid_origin;
-	unsigned long long int uint_setting;
-	size_t size_setting;
-	int ret;
-
-	/*
-	 * Configure active script path
-	 */
-
-	active_path = sieve_setting_get(svinst, "sieve");
-
-	/* Get path to active Sieve script */
-
-	if ( active_path != NULL ) {
-		const char *p;
-
-		if ( *active_path == '\0' ) {
-			/* disabled */
-			if ( debug )
-				i_debug("sieve-storage: sieve is disabled (sieve=\"\")");
-			return NULL;
-		}
-
-		/* Parse full location into a file path */
-		p = strchr(active_path, ':');
-		if ( p != NULL ) {
-	 		if ( strncmp(active_path, "file", p-active_path) != 0 ) {
-				i_error("sieve-storage: Cannot open non-file script location "
-					"for active script `%s'",	active_path);
-				return NULL;
-			}
-			active_path = p+1;
-
-			p = strchr(active_path, ';');
-			if ( p != NULL )
-				active_path = t_strdup_until(active_path, p);
-		}
-
-	} else {
-		if ( debug ) {
-			i_debug("sieve-storage: sieve active script path is unconfigured; "
-				"using default (sieve=%s)", SIEVE_DEFAULT_PATH);
-		}
-
-		active_path = SIEVE_DEFAULT_PATH;
-	}
-
-	/* Substitute home dir if necessary */
-
-	path = home_expand_tilde(active_path, home);
-	if ( path == NULL ) {
-		i_error("sieve-storage: userdb(%s) didn't return a home directory "
-			"for substitition in active script path (sieve=%s)",
-			username, active_path);
-		return NULL;
-	}
-
-	active_path = path;
-
-	/* Get the filename for the active script link */
-
-	active_fname = strrchr(active_path, '/');
-	if ( active_fname == NULL )
-		active_fname = active_path;
-	else
-		active_fname++;
-
-	if ( *active_fname == '\0' ) {
-		/* Link cannot be just a path */
-		i_error("sieve-storage: "
-			"path to active symlink must include the link's filename. Path is: %s",
-			active_path);
-
-		return NULL;
-	}
-
-	/*
-	 * Configure script storage directory
-	 */
-
-	storage_dir = NULL;
-
-	/* Read setting */
-
-	sieve_data = sieve_setting_get(svinst, "sieve_dir");
-
-	if ( sieve_data == NULL )
-		sieve_data = sieve_setting_get(svinst, "sieve_storage");
-
-	/* Determine location */
-
-	if ( sieve_data == NULL || *sieve_data == '\0' ) {
-		/* We'll need to figure out the storage location ourself.
-		 *
-		 * It's $HOME/sieve or /sieve when (presumed to be) chrooted.
-		 */
-		if ( home != NULL && *home != '\0' ) {
-			if (access(home, R_OK|W_OK|X_OK) == 0) {
-				/* Use default ~/sieve */
-
-				if ( debug ) {
-					i_debug("sieve-storage: root exists (%s)", home);
-				}
-
-				storage_dir = t_strconcat(home, "/sieve", NULL);
-			} else {
-				/* Don't have required access on the home directory */
-
-				if ( debug ) {
-					i_debug("sieve-storage: access(%s, rwx): "
-						"failed: %m", home);
-				}
-			}
-		} else {
-			if ( debug )
-				i_debug("sieve-storage: HOME not set");
-
-			if (access("/sieve", R_OK|W_OK|X_OK) == 0) {
-				storage_dir = "/sieve";
-				if ( debug )
-					i_debug("sieve-storage: /sieve exists, assuming chroot");
-			}
-		}
-	} else {
-		const char *p;
-
-		/* Parse full location into a file path */
-		p = strchr(sieve_data, ':');
-		if ( p != NULL ) {
-	 		if (strncmp(sieve_data, "file", p-sieve_data) != 0 ) {
-				i_error("sieve-storage: Cannot open non-file script storage `%s'",
-					sieve_data);
-				return NULL;
-			}
-			sieve_data = p+1;
-
-			p = strchr(sieve_data, ';');
-			if ( p != NULL )
-				sieve_data = t_strdup_until(sieve_data, p);
-		}
-
-		storage_dir = sieve_data;
-	}
-
-	if (storage_dir == NULL || *storage_dir == '\0') {
-		i_error("sieve-storage: couldn't find storage root directory; "
-			"sieve_dir was left unconfigured and autodetection failed");
-		return NULL;
-	}
-
-	/* Expand home directory in path */
-
-	path = home_expand_tilde(storage_dir, home);
-	if ( path == NULL ) {
-		i_error("sieve-storage: userdb(%s) didn't return a home directory "
-			"for substitition in storage root directory (sieve_dir=%s)",
-			username, storage_dir);
-		return NULL;
-	}
-
-	storage_dir = path;
-
-	if ( debug ) {
-		i_debug("sieve-storage: "
-			"using active sieve script path: %s", active_path);
- 		i_debug("sieve-storage: "
-			"using sieve script storage directory: %s", storage_dir);
-	}
-
-	/* get the storage mtime before we modify it ourself. FIXME: do this
-	   later, only just before modifying the sieve dir */
-	struct stat st;
-	if (stat(storage_dir, &st) < 0) {
-		if (errno != ENOENT) {
-			i_error("stat(%s) failed: %m", storage_dir);
-			return NULL;
-		}
-		st.st_mtime = 0;
-	}
-
-	/* Get permissions */
-
-	sieve_storage_get_permissions
-		(storage_dir, &file_create_mode, &dir_create_mode, &file_create_gid,
-			&file_create_gid_origin, debug);
-
-	/*
-	 * Ensure sieve local directory structure exists (full autocreate):
-	 *  This currently only consists of a ./tmp direcory
-	 */
-
-	tmp_dir = t_strconcat(storage_dir, "/tmp", NULL);
-
-	/* Try to find and clean up tmp dir */
-	if ( (ret=check_tmp(tmp_dir)) < 0 )
-		return NULL;
-
-	/* Auto-create if necessary */
-	if ( ret == 0 && mkdir_verify(tmp_dir, dir_create_mode, file_create_gid,
-		file_create_gid_origin, debug) < 0 )
-		return NULL;
-
-	/* Open user's INBOX for attribute updates if necessary */
-	if ( (flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 )
-		(void)_sieve_storage_open_inbox(user, &inbox);
-
-	/*
-	 * Create storage object
-	 */
-
-	pool = pool_alloconly_create("sieve-storage", 512+256);
-	storage = p_new(pool, struct sieve_storage, 1);
-	storage->svinst = svinst;
-	storage->flags = flags;
-	storage->pool = pool;
-	storage->dir = p_strdup(pool, storage_dir);
-	storage->username = p_strdup(pool, username);
-	storage->active_path = p_strdup(pool, active_path);
-	storage->active_fname = p_strdup(pool, active_fname);
-	storage->prev_mtime = st.st_mtime;
-
-	storage->dir_create_mode = dir_create_mode;
-	storage->file_create_mode = file_create_mode;
-	storage->file_create_gid = file_create_gid;
-
-	storage->inbox = inbox;
-
-	/* Get the path to be prefixed to the script name in the symlink pointing
-	 * to the active script.
-	 */
-	link_path = sieve_storage_get_relative_link_path
-		(storage->active_path, storage->dir);
-
-	if ( debug )
-		i_debug("sieve-storage: "
-			"relative path to sieve storage in active link: %s", link_path);
-
-	storage->link_path = p_strdup(pool, link_path);
-
-	/* Get quota settings */
-
-	storage->max_storage = 0;
-	storage->max_scripts = 0;
-
-	if ( sieve_setting_get_size_value
-		(svinst, "sieve_quota_max_storage", &size_setting) ) {
-		storage->max_storage = size_setting;
-	}
-
-	if ( sieve_setting_get_uint_value
-		(svinst, "sieve_quota_max_scripts", &uint_setting) ) {
-		storage->max_scripts = uint_setting;
-	}
-
-	if ( debug ) {
-		if ( storage->max_storage > 0 ) {
-			i_debug("sieve-storage: quota: storage limit: %llu bytes",
-				(unsigned long long int) storage->max_storage);
-		}
-		if ( storage->max_scripts > 0 ) {
-			i_debug("sieve-storage: quota: script count limit: %llu scripts",
-				(unsigned long long int) storage->max_scripts);
-		}
-	}
-	return storage;
-}
-
-struct sieve_storage *sieve_storage_create
-(struct sieve_instance *svinst, struct mail_user *user, const char *home,
-	enum sieve_storage_flags flags)
-{
-	struct sieve_storage *storage;
-
-	T_BEGIN {
-		storage = _sieve_storage_create(svinst, user, home, flags);
-	} T_END;
-
-	return storage;
-}
-
-void sieve_storage_free(struct sieve_storage *storage)
-{
-	if (storage->inbox != NULL)
-		mailbox_free(&storage->inbox);
-	sieve_error_handler_unref(&storage->ehandler);
-
-	pool_unref(&storage->pool);
-}
-
-int sieve_storage_get_last_change
-(struct sieve_storage *storage, time_t *last_change_r)
-{
-	*last_change_r = storage->prev_mtime;
-	return 0;
-}
-
-void sieve_storage_set_modified
-(struct sieve_storage *storage, time_t mtime)
-{
-	struct utimbuf times;
-	time_t cur_mtime;
-
-	if ( mtime != (time_t)-1 ) {
-		if ( sieve_storage_get_last_change(storage, &cur_mtime) >= 0 &&
-			cur_mtime > mtime )
-			return;
-	} else {
-		mtime = ioloop_time;
-	}
-
-	times.actime = mtime;
-	times.modtime = mtime;
-	if ( utime(storage->dir, &times) < 0 ) {
-		switch ( errno ) {
-		case ENOENT:
-			break;
-		case EACCES:
-			i_error("sieve-storage: %s", eacces_error_get("utime", storage->dir));
-			break;
-		default:
-			i_error("sieve-storage: utime(%s) failed: %m", storage->dir);
-		}
-	} else {
-		storage->prev_mtime = mtime;
-	}
-}
-
-/* Error handling */
-
-struct sieve_error_handler *sieve_storage_get_error_handler
-(struct sieve_storage *storage)
-{
-	struct sieve_storage_ehandler *ehandler;
-
-	if ( storage->ehandler == NULL ) {
-		pool_t pool = pool_alloconly_create("sieve_storage_ehandler", 512);
-		ehandler = p_new(pool, struct sieve_storage_ehandler,1);
-		sieve_error_handler_init(&ehandler->handler, storage->svinst, pool, 1);
-
-		ehandler->handler.verror = sieve_storage_verror;
-		ehandler->storage = storage;
-
-		storage->ehandler = (struct sieve_error_handler *) ehandler;
-	}
-
-	return storage->ehandler;
-}
-
-static void ATTR_FORMAT(4, 0) sieve_storage_verror
-(struct sieve_error_handler *ehandler, unsigned int flags ATTR_UNUSED,
-	const char *location ATTR_UNUSED, const char *fmt, va_list args)
-{
-	struct sieve_storage_ehandler *sehandler =
-		(struct sieve_storage_ehandler *) ehandler;
-	struct sieve_storage *storage = sehandler->storage;
-
-	sieve_storage_clear_error(storage);
-
-	if (fmt != NULL) {
-		storage->error = i_strdup_vprintf(fmt, args);
-	}
-	storage->error_code = SIEVE_ERROR_TEMP_FAILURE;
-}
-
-void sieve_storage_clear_error(struct sieve_storage *storage)
-{
-	i_free(storage->error);
-	storage->error_code = SIEVE_ERROR_NONE;
-	storage->error = NULL;
-}
-
-void sieve_storage_set_error
-(struct sieve_storage *storage, enum sieve_error error,
-	const char *fmt, ...)
-{
-	va_list va;
-
-	sieve_storage_clear_error(storage);
-
-	if (fmt != NULL) {
-		va_start(va, fmt);
-		storage->error = i_strdup_vprintf(fmt, va);
-		va_end(va);
-	}
-
-	storage->error_code = error;
-}
-
-void sieve_storage_set_critical
-(struct sieve_storage *storage, const char *fmt, ...)
-{
-	va_list va;
-
-	sieve_storage_clear_error(storage);
-	if (fmt != NULL) {
-		i_free(storage->error);
-		storage->error_code = SIEVE_ERROR_TEMP_FAILURE;
-
-		if ( (storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0 ) {
-			struct tm *tm;
-			char str[256];
-
-			va_start(va, fmt);
-			i_error("sieve-storage: %s", t_strdup_vprintf(fmt, va));
-			va_end(va);
-
-			/* critical errors may contain sensitive data, so let user
-			   see only "Internal error" with a timestamp to make it
-			   easier to look from log files the actual error message. */
-			tm = localtime(&ioloop_time);
-			storage->error =
-				strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ?
-				i_strdup(str) : i_strdup(CRITICAL_MSG);
-		} else {
-			/* no user is involved while synchronizing, so do it the
-			   normal way */
-			va_start(va, fmt);
-			storage->error = i_strdup_vprintf(fmt, va);
-			va_end(va);
-		}
-	}
-}
-
-const char *sieve_storage_get_last_error
-(struct sieve_storage *storage, enum sieve_error *error_r)
-{
-	/* We get here only in error situations, so we have to return some
-	   error. If storage->error is NULL, it means we forgot to set it at
-	   some point..
-	 */
-
-	if ( error_r != NULL )
-		*error_r = storage->error_code;
-
-	return storage->error != NULL ? storage->error : "Unknown error";
-}
-
-/*
- * INBOX attributes
- */
-
-static void sieve_storage_inbox_transaction_finish
-(struct sieve_storage *storage, struct mailbox_transaction_context **t)
-{
-	struct mailbox *inbox = storage->inbox;
-
-	if (mailbox_transaction_commit(t) < 0) {
-		enum mail_error error;
-		
-		i_warning("sieve-storage: Failed to update INBOX attributes: %s",
-			mail_storage_get_last_error(mailbox_get_storage(inbox), &error));
-	}
-}
-
-void sieve_storage_inbox_script_attribute_set
-(struct sieve_storage *storage, const char *name)
-{
-	struct mailbox_transaction_context *t;
-	const char *key;
-
-	if (storage->inbox == NULL)
-		return;
-
-	key = t_strconcat
-		(MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, name, NULL);
-	t = mailbox_transaction_begin(storage->inbox, 0);	
-	mail_index_attribute_set(t->itrans, TRUE, key, ioloop_time, 0);
-	sieve_storage_inbox_transaction_finish(storage, &t);
-}
-
-void sieve_storage_inbox_script_attribute_rename
-(struct sieve_storage *storage, const char *oldname, const char *newname)
-{
-	struct mailbox_transaction_context *t;
-	const char *oldkey, *newkey;
-
-	if (storage->inbox == NULL)
-		return;
-
-	oldkey = t_strconcat
-		(MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, oldname, NULL);
-	newkey = t_strconcat
-		(MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, newname, NULL);
-
-	t = mailbox_transaction_begin(storage->inbox, 0);	
-	mail_index_attribute_unset(t->itrans, TRUE, oldkey, ioloop_time);
-	mail_index_attribute_set(t->itrans, TRUE, newkey, ioloop_time, 0);
-	sieve_storage_inbox_transaction_finish(storage, &t);
-}
-
-void sieve_storage_inbox_script_attribute_unset
-(struct sieve_storage *storage, const char *name)
-{
-	struct mailbox_transaction_context *t;
-	const char *key;
-
-	if (storage->inbox == NULL)
-		return;
-
-	key = t_strconcat
-		(MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES, name, NULL);
-	
-	t = mailbox_transaction_begin(storage->inbox, 0);	
-	mail_index_attribute_unset(t->itrans, TRUE, key, ioloop_time);
-	sieve_storage_inbox_transaction_finish(storage, &t);
-}
-
diff --git a/src/lib-sievestorage/sieve-storage.h b/src/lib-sievestorage/sieve-storage.h
deleted file mode 100644
index 45060125fa2c6ad5dbf8841d1013bb6ecf6d41c5..0000000000000000000000000000000000000000
--- a/src/lib-sievestorage/sieve-storage.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file
- */
-
-#ifndef __SIEVE_STORAGE_H
-#define __SIEVE_STORAGE_H
-
-#include "lib.h"
-#include "mail-storage.h"
-#include "mail-user.h"
-
-#include "sieve.h"
-
-#define MAILBOX_ATTRIBUTE_PREFIX_SIEVE \
-	MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER"sieve/"
-#define MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES \
-	MAILBOX_ATTRIBUTE_PREFIX_SIEVE"files/"
-#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT \
-	MAILBOX_ATTRIBUTE_PREFIX_SIEVE"default"
-
-#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_LINK 'L'
-#define MAILBOX_ATTRIBUTE_SIEVE_DEFAULT_SCRIPT 'S'
-
-enum sieve_storage_flags {
-	/* Print debugging information */
-	SIEVE_STORAGE_FLAG_DEBUG             = 0x01,
-	/* This storage is used for synchronization (and not normal ManageSieve)
-	 */
-	SIEVE_STORAGE_FLAG_SYNCHRONIZING     = 0x02
-};
-
-struct sieve_storage *sieve_storage_create
-	(struct sieve_instance *svinst, struct mail_user *user, const char *home,
-		enum sieve_storage_flags flags);
-void sieve_storage_free(struct sieve_storage *storage);
-
-struct sieve_error_handler *sieve_storage_get_error_handler
-	(struct sieve_storage *storage);
-
-/* Set error message in storage. Critical errors are logged with i_error(),
-   but user sees only "internal error" message. */
-void sieve_storage_clear_error(struct sieve_storage *storage);
-
-void sieve_storage_set_error
-	(struct sieve_storage *storage, enum sieve_error error,
-		const char *fmt, ...) ATTR_FORMAT(3, 4);
-
-void sieve_storage_set_critical(struct sieve_storage *storage,
-	const char *fmt, ...) ATTR_FORMAT(2, 3);
-
-const char *sieve_storage_get_last_error
-	(struct sieve_storage *storage, enum sieve_error *error_r);
-
-int sieve_storage_get_last_change
-	(struct sieve_storage *storage, time_t *last_change_r);
-void sieve_storage_set_modified
-	(struct sieve_storage *storage, time_t mtime);
-
-#endif
diff --git a/src/managesieve/Makefile.am b/src/managesieve/Makefile.am
index a523ffeac8f9d7de7fab21c851d1370b70bf96e2..e160088f0a0c7a1af6a4d1308c0cf3dfe834c324 100644
--- a/src/managesieve/Makefile.am
+++ b/src/managesieve/Makefile.am
@@ -8,8 +8,7 @@ AM_CPPFLAGS = \
 	-DMODULEDIR=\""$(dovecot_moduledir)"\" \
 	-I$(top_srcdir) \
 	-I$(top_srcdir)/src/lib-sieve \
-	-I$(top_srcdir)/src/lib-managesieve \
-	-I$(top_srcdir)/src/lib-sievestorage
+	-I$(top_srcdir)/src/lib-managesieve
 
 libmanagesieve_settings_la_LDFLAGS = -module -avoid-version
 
@@ -24,7 +23,6 @@ managesieve_LDFLAGS = -export-dynamic
 libs = \
 	managesieve-settings.lo \
 	$(top_builddir)/src/lib-managesieve/libmanagesieve.a \
-	$(top_builddir)/src/lib-sievestorage/libsievestorage.la \
 	$(top_builddir)/src/lib-sieve/libdovecot-sieve.la
 
 managesieve_LDADD = $(libs) $(LIBDOVECOT_STORAGE) $(LIBDOVECOT_LDA) $(LIBDOVECOT)
diff --git a/src/managesieve/cmd-deletescript.c b/src/managesieve/cmd-deletescript.c
index 0c76053cc900d878528446cf7156b1acaf5a322b..16c6766c838886dd12df8ed98b77462624e5292b 100644
--- a/src/managesieve/cmd-deletescript.c
+++ b/src/managesieve/cmd-deletescript.c
@@ -3,8 +3,9 @@
 
 #include "lib.h"
 
+#include "sieve.h"
+#include "sieve-script.h"
 #include "sieve-storage.h"
-#include "sieve-storage-script.h"
 
 #include "managesieve-common.h"
 #include "managesieve-commands.h"
@@ -20,14 +21,14 @@ bool cmd_deletescript(struct client_command_context *cmd)
 	if ( !client_read_string_args(cmd, 1, TRUE, &scriptname) )
 		return FALSE;
 
-	script = sieve_storage_script_init(storage, scriptname);
-
+	script = sieve_storage_open_script
+		(storage, scriptname, NULL);
 	if (script == NULL) {
 		client_send_storage_error(client, storage);
 		return TRUE;
 	}
 
-	if (sieve_storage_script_delete(&script) < 0) {
+	if (sieve_script_delete(&script) < 0) {
 		client_send_storage_error(client, storage);
 	} else {
 		client_send_ok(client, "Deletescript completed.");
@@ -36,6 +37,5 @@ bool cmd_deletescript(struct client_command_context *cmd)
 	/* Script object is deleted no matter what in
 	 * sieve_script_delete()
 	 */
-
 	return TRUE;
 }
diff --git a/src/managesieve/cmd-getscript.c b/src/managesieve/cmd-getscript.c
index edc56cc84df7c3252af507fc1ad07bf082c37af3..7498dff1f039a377da1e64c75bcb6fcfb794cc01 100644
--- a/src/managesieve/cmd-getscript.c
+++ b/src/managesieve/cmd-getscript.c
@@ -5,7 +5,8 @@
 #include "ostream.h"
 #include "istream.h"
 
-#include "sieve-storage-script.h"
+#include "sieve-script.h"
+#include "sieve-storage.h"
 
 #include "managesieve-common.h"
 #include "managesieve-commands.h"
@@ -98,8 +99,9 @@ bool cmd_getscript(struct client_command_context *cmd)
 	ctx->client = client;
 	ctx->storage = client->storage;
 	ctx->failed = FALSE;
-	ctx->script = sieve_storage_script_init(client->storage, scriptname);
 
+	ctx->script = sieve_storage_open_script
+		(client->storage, scriptname, NULL);
 	if (ctx->script == NULL) {
 		ctx->failed = TRUE;
 		return cmd_getscript_finish(ctx);
diff --git a/src/managesieve/cmd-havespace.c b/src/managesieve/cmd-havespace.c
index 237160bee925e0b3aab9e1e89793bbfa73450487..e00b31784a424a9378cba8741091c125ecb4e4d3 100644
--- a/src/managesieve/cmd-havespace.c
+++ b/src/managesieve/cmd-havespace.c
@@ -8,8 +8,7 @@
 
 #include "sieve.h"
 #include "sieve-script.h"
-
-#include "sieve-storage-quota.h"
+#include "sieve-storage.h"
 
 #include "managesieve-client.h"
 #include "managesieve-quota.h"
diff --git a/src/managesieve/cmd-listscripts.c b/src/managesieve/cmd-listscripts.c
index 93e7e6882613a54454350158d0dee01e7bb22cfb..75373b0514f9ec6d14e2a36be81e39156cf92f12 100644
--- a/src/managesieve/cmd-listscripts.c
+++ b/src/managesieve/cmd-listscripts.c
@@ -4,8 +4,8 @@
 #include "lib.h"
 #include "str.h"
 
+#include "sieve.h"
 #include "sieve-storage.h"
-#include "sieve-storage-list.h"
 
 #include "managesieve-quote.h"
 
@@ -15,7 +15,7 @@
 bool cmd_listscripts(struct client_command_context *cmd)
 {
   struct client *client = cmd->client;
-	struct sieve_list_context *ctx;
+	struct sieve_storage_list_context *ctx;
 	const char *scriptname;
 	bool active;
 	string_t *str;
diff --git a/src/managesieve/cmd-putscript.c b/src/managesieve/cmd-putscript.c
index 17f48b89cdca6907f85cf3e58c26e088770d8334..45c0a65ef7180261f4fec02f14aa0d5195c8c861 100644
--- a/src/managesieve/cmd-putscript.c
+++ b/src/managesieve/cmd-putscript.c
@@ -12,10 +12,8 @@
 #include "str.h"
 
 #include "sieve.h"
-
+#include "sieve-script.h"
 #include "sieve-storage.h"
-#include "sieve-storage-script.h"
-#include "sieve-storage-save.h"
 
 #include "managesieve-parser.h"
 
@@ -37,7 +35,7 @@ struct cmd_putscript_context {
 	uoff_t script_size, max_script_size;
 
 	struct managesieve_parser *save_parser;
-	struct sieve_save_context *save_ctx;
+	struct sieve_storage_save_context *save_ctx;
 };
 
 static void cmd_putscript_finish(struct cmd_putscript_context *ctx);
@@ -59,7 +57,7 @@ static void client_input_putscript(struct client *client)
 		/* Reset command so that client_destroy() doesn't try to call
 		   cmd_putscript_continue_script() anymore. */
 		_client_reset_command(client);
-		client_destroy(client, "Disconnected in PUTSCRIPT/SCRIPT");
+		client_destroy(client, "Disconnected in PUTSCRIPT/CHECKSCRIPT");
 		return;
 	case -2:
 		cmd_putscript_finish(cmd->context);
@@ -199,6 +197,7 @@ static bool cmd_putscript_finish_parsing(struct client_command_context *cmd)
 			enum sieve_compile_flags cpflags =
 				SIEVE_COMPILE_FLAG_NOGLOBAL | SIEVE_COMPILE_FLAG_UPLOADED;
 			struct sieve_binary *sbin;
+			enum sieve_error error;
 			string_t *errors;
 
 			/* Mark this as an activation when we are replacing the active script */
@@ -213,8 +212,17 @@ static bool cmd_putscript_finish_parsing(struct client_command_context *cmd)
 
 			/* Compile */
 			if ( (sbin=sieve_compile_script
-				(script, ehandler, cpflags, NULL)) == NULL ) {
-				client_send_no(client, str_c(errors));
+				(script, ehandler, cpflags, &error)) == NULL ) {
+				if ( error != SIEVE_ERROR_NOT_VALID ) {
+					const char *errormsg =
+						sieve_script_get_last_error(script, &error);
+					if ( error != SIEVE_ERROR_NONE )
+						client_send_no(client, errormsg);
+					else
+						client_send_no(client, str_c(errors));
+				} else {
+					client_send_no(client, str_c(errors));
+				}
 				success = FALSE;
 			} else {
 				sieve_close(&sbin);
diff --git a/src/managesieve/cmd-renamescript.c b/src/managesieve/cmd-renamescript.c
index 1d1d6a84e7884392e94f02310b957c2480ebb9e9..bf3373500bc106d9c51b8797b11c16dd0b5502c8 100644
--- a/src/managesieve/cmd-renamescript.c
+++ b/src/managesieve/cmd-renamescript.c
@@ -4,8 +4,9 @@
 #include "lib.h"
 #include "str.h"
 
+#include "sieve.h"
+#include "sieve-script.h"
 #include "sieve-storage.h"
-#include "sieve-storage-script.h"
 
 #include "managesieve-common.h"
 #include "managesieve-commands.h"
@@ -21,14 +22,14 @@ bool cmd_renamescript(struct client_command_context *cmd)
 	if (!client_read_string_args(cmd, 2, TRUE, &scriptname, &newname))
 		return FALSE;
 
-	script = sieve_storage_script_init(storage, scriptname);
-
+	script = sieve_storage_open_script
+		(storage, scriptname, NULL);
 	if (script == NULL) {
 		client_send_storage_error(client, storage);
 		return TRUE;
 	}
 
-	if (sieve_storage_script_rename(script, newname) < 0)
+	if (sieve_script_rename(script, newname) < 0)
 		client_send_storage_error(client, storage);
 	else
 		client_send_ok(client, "Renamescript completed.");
diff --git a/src/managesieve/cmd-setactive.c b/src/managesieve/cmd-setactive.c
index b39e7f328ec4929256a5a746b855917c11a35c46..247b2b56aac373d86b529490aa6f4318724ad59d 100644
--- a/src/managesieve/cmd-setactive.c
+++ b/src/managesieve/cmd-setactive.c
@@ -5,8 +5,8 @@
 #include "str.h"
 
 #include "sieve.h"
+#include "sieve-script.h"
 #include "sieve-storage.h"
-#include "sieve-storage-script.h"
 
 #include "managesieve-common.h"
 #include "managesieve-commands.h"
@@ -26,16 +26,18 @@ bool cmd_setactive(struct client_command_context *cmd)
 	/* Activate, or .. */
 	if ( *scriptname != '\0' ) {
 		string_t *errors = NULL;
+		const char *errormsg = NULL;
 		bool warnings = FALSE;
 		bool success = TRUE;
 
-		script = sieve_storage_script_init(storage, scriptname);
+		script = sieve_storage_open_script
+			(storage, scriptname, NULL);
 		if ( script == NULL ) {
 			client_send_storage_error(client, storage);
 			return TRUE;
 		}
 
-		if ( sieve_storage_script_is_active(script) <= 0 ) {
+		if ( sieve_script_is_active(script) <= 0 ) {
 			/* Script is first being activated; compile it again without the UPLOAD
 			 * flag.
 			 */
@@ -44,6 +46,7 @@ bool cmd_setactive(struct client_command_context *cmd)
 				enum sieve_compile_flags cpflags =
 					SIEVE_COMPILE_FLAG_NOGLOBAL | SIEVE_COMPILE_FLAG_ACTIVATED;
 				struct sieve_binary *sbin;
+				enum sieve_error error;
 
 				/* Prepare error handler */
 				errors = str_new(default_pool, 1024);
@@ -52,7 +55,12 @@ bool cmd_setactive(struct client_command_context *cmd)
 
 				/* Compile */
 				if ( (sbin=sieve_compile_script
-					(script, ehandler, cpflags, NULL)) == NULL ) {
+					(script, ehandler, cpflags, &error)) == NULL ) {
+					if (error != SIEVE_ERROR_NOT_VALID) {
+						errormsg = sieve_script_get_last_error(script, &error);
+						if ( error == SIEVE_ERROR_NONE )
+							errormsg = NULL;
+					}
 					success = FALSE;
 				} else {
 					sieve_close(&sbin);
@@ -68,7 +76,7 @@ bool cmd_setactive(struct client_command_context *cmd)
 			/* Refresh activation no matter what; this can also resolve some erroneous
 			 * situations.
 			 */
-			ret = sieve_storage_script_activate(script, (time_t)-1);
+			ret = sieve_script_activate(script, (time_t)-1);
 			if ( ret < 0 ) {
 				client_send_storage_error(client, storage);
 			} else {
@@ -80,8 +88,10 @@ bool cmd_setactive(struct client_command_context *cmd)
 						"Script is already active." ));
 				}
 			}
-		} else {
+		} else if ( errormsg == NULL ) {
 			client_send_no(client, str_c(errors));
+		} else {
+			client_send_no(client, errormsg);
 		}
 
 		if ( errors != NULL )
diff --git a/src/managesieve/managesieve-client.c b/src/managesieve/managesieve-client.c
index fdb59db5f97a836070d5b3c16d648fc01b74c1f4..84aa3146e5aa37be3448881ae1d62dd17803dc8e 100644
--- a/src/managesieve/managesieve-client.c
+++ b/src/managesieve/managesieve-client.c
@@ -14,6 +14,7 @@
 #include "mail-storage-service.h"
 #include "mail-namespace.h"
 
+#include "sieve.h"
 #include "sieve-storage.h"
 
 #include "managesieve-quote.h"
@@ -58,34 +59,35 @@ static void client_idle_timeout(struct client *client)
 }
 
 static struct sieve_storage *client_get_storage
-(struct sieve_instance *svinst, struct mail_user *user,
-	const struct managesieve_settings *set)
+(struct sieve_instance *svinst, struct mail_user *user)
 {
 	struct sieve_storage *storage;
-	enum sieve_storage_flags flags = 0;
-	const char *home;
-
-	if ( mail_user_get_home(user, &home) <= 0 )
-		home = NULL;
-
-	if ( set->mail_debug )
-		flags |= SIEVE_STORAGE_FLAG_DEBUG;
-
-	storage = sieve_storage_create
-		(svinst, user, home, flags);
+	enum sieve_error error;
 
+	storage = sieve_storage_create_main
+		(svinst, user, SIEVE_STORAGE_FLAG_READWRITE, &error);
 	if (storage == NULL) {
-		struct tm *tm;
-		char str[256];
+		switch (error) {
+		case SIEVE_ERROR_NOT_FOUND:
+			printf("BYE \"Sieve filtering is disabled for this user.\"\n");
+
+			i_fatal("Failed to open Sieve storage: Sieve disabled for user");
+			break;
+		default:
+			{ 
+				struct tm *tm;
+				char str[256];
 
-		tm = localtime(&ioloop_time);
+				tm = localtime(&ioloop_time);
 
-		printf("BYE \"%s\"\n",
-			strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ?
-				i_strdup(str) : i_strdup(CRITICAL_MSG));
+				printf("BYE \"%s\"\n",
+					strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ?
+						i_strdup(str) : i_strdup(CRITICAL_MSG));
 
-		i_fatal("Failed to open Sieve storage.");
-    }
+				i_fatal("Failed to open Sieve storage.");
+			}
+		}
+	}
 
 	return storage;
 }
@@ -102,7 +104,7 @@ struct client *client_create
 	struct sieve_storage *storage;
 	pool_t pool;
 
-	/* Initialize Sieve instance */
+	/* Initialize Sieve */
 
 	memset((void*)&svenv, 0, sizeof(svenv));
 	svenv.username = user->username;
@@ -115,7 +117,7 @@ struct client *client_create
 
 	/* Get Sieve storage */
 
-	storage = client_get_storage(svinst, user, set);
+	storage = client_get_storage(svinst, user);
 
 	/* always use nonblocking I/O */
 	net_set_nonblock(fd_in, TRUE);
@@ -252,7 +254,7 @@ void client_destroy(struct client *client, const char *reason)
 	if (client->fd_in != client->fd_out)
 		net_disconnect(client->fd_out);
 
-	sieve_storage_free(client->storage);
+	sieve_storage_unref(&client->storage);
 	sieve_deinit(&client->svinst);
 
 	pool_unref(&client->cmd.pool);
diff --git a/src/managesieve/managesieve-quota.c b/src/managesieve/managesieve-quota.c
index 467caa56547282e49c0adf5d1f69a00c5e48a590..41f3e5a0bd396e1e237868a2b75211673feabf22 100644
--- a/src/managesieve/managesieve-quota.c
+++ b/src/managesieve/managesieve-quota.c
@@ -4,8 +4,8 @@
 #include "lib.h"
 #include "strfuncs.h"
 
+#include "sieve.h"
 #include "sieve-storage.h"
-#include "sieve-storage-quota.h"
 
 #include "managesieve-client.h"
 #include "managesieve-quota.h"
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
index c8f8d976ad39a084cc59abda4cf9f62af9b733a9..b1f4d858b012902852d7b1b0d7f781ef270f0f87 100644
--- a/src/plugins/Makefile.am
+++ b/src/plugins/Makefile.am
@@ -1 +1,4 @@
-SUBDIRS = doveadm-sieve lda-sieve sieve-extprograms
+SUBDIRS = \
+	doveadm-sieve \
+	lda-sieve \
+	sieve-extprograms
diff --git a/src/plugins/doveadm-sieve/Makefile.am b/src/plugins/doveadm-sieve/Makefile.am
index ea1d992e30b6e4e38eaeaf2ff04b4185f812137f..48f7009d3b969544dccaf021dacab2d5a21e3e84 100644
--- a/src/plugins/doveadm-sieve/Makefile.am
+++ b/src/plugins/doveadm-sieve/Makefile.am
@@ -1,6 +1,5 @@
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/lib-sieve \
-	-I$(top_srcdir)/src/lib-sievestorage \
 	$(LIBDOVECOT_INCLUDE)
 
 doveadm_moduledir = $(dovecot_moduledir)/doveadm
@@ -11,5 +10,4 @@ doveadm_module_LTLIBRARIES = lib10_doveadm_sieve_plugin.la
 lib10_doveadm_sieve_plugin_la_SOURCES = \
 	doveadm-sieve-plugin.c
 lib10_doveadm_sieve_plugin_la_LIBADD = \
-	$(top_builddir)/src/lib-sieve/libdovecot-sieve.la \
-	$(top_builddir)/src/lib-sievestorage/libsievestorage.la
+	$(top_builddir)/src/lib-sieve/libdovecot-sieve.la
diff --git a/src/plugins/doveadm-sieve/doveadm-sieve-plugin.c b/src/plugins/doveadm-sieve/doveadm-sieve-plugin.c
index 6647360a94bfffb7f77ba181abc147a200abf0e8..d9a9e2f5aa0c738636fc8a887c986efcefed46ab 100644
--- a/src/plugins/doveadm-sieve/doveadm-sieve-plugin.c
+++ b/src/plugins/doveadm-sieve/doveadm-sieve-plugin.c
@@ -2,16 +2,15 @@
  */
 
 #include "lib.h"
+#include "str.h"
 #include "ioloop.h"
 #include "istream.h"
 #include "istream-concat.h"
+#include "mail-storage-private.h"
+
+#include "sieve.h"
 #include "sieve-script.h"
-#include "sieve-script-file.h"
 #include "sieve-storage.h"
-#include "sieve-storage-list.h"
-#include "sieve-storage-save.h"
-#include "sieve-storage-script.h"
-#include "mail-storage-private.h"
 
 #define SIEVE_MAIL_CONTEXT(obj) \
 	MODULE_CONTEXT(obj, sieve_storage_module)
@@ -29,7 +28,7 @@ struct sieve_mailbox_attribute_iter {
 	struct mailbox_attribute_iter iter;
 	struct mailbox_attribute_iter *super;
 
-	struct sieve_list_context *sieve_list;
+	struct sieve_storage_list_context *sieve_list;
 	string_t *name;
 
 	bool failed;
@@ -63,7 +62,7 @@ static void mail_sieve_user_deinit(struct mail_user *user)
 {
 	struct sieve_mail_user *suser = SIEVE_USER_CONTEXT(user);
 
-	sieve_storage_free(suser->sieve_storage);
+	sieve_storage_unref(&suser->sieve_storage);
 	sieve_deinit(&suser->svinst);
 
 	suser->module_ctx.super.deinit(user);
@@ -74,7 +73,9 @@ mail_sieve_user_init
 (struct mail_user *user, struct sieve_storage **svstorage_r)
 {
 	struct sieve_mail_user *suser = SIEVE_USER_CONTEXT(user);
-	enum sieve_storage_flags storage_flags = SIEVE_STORAGE_FLAG_SYNCHRONIZING;
+	enum sieve_storage_flags storage_flags =
+		SIEVE_STORAGE_FLAG_READWRITE |
+		SIEVE_STORAGE_FLAG_SYNCHRONIZING;
 	struct mail_user_vfuncs *v = user->vlast;
 	struct sieve_environment svenv;
 
@@ -95,13 +96,10 @@ mail_sieve_user_init
 	user->vlast = &suser->module_ctx.super;
 	v->deinit = mail_sieve_user_deinit;
 
-	if (user->mail_debug)
-		storage_flags |= SIEVE_STORAGE_FLAG_DEBUG;
-
 	suser->svinst = sieve_init(&svenv, &mail_sieve_callbacks,
 				   user, user->mail_debug);
-	suser->sieve_storage = sieve_storage_create(suser->svinst, user,
-					       svenv.home_dir, storage_flags);
+	suser->sieve_storage = sieve_storage_create_main
+			(suser->svinst, user, storage_flags, NULL);
 
 	MODULE_CONTEXT_SET(user, sieve_user_module, suser);
 	*svstorage_r = suser->sieve_storage;
@@ -117,9 +115,8 @@ static int sieve_attribute_unset_script(struct mail_storage *storage,
 	enum sieve_error error;
 	int ret = 0;
 
-	script = sieve_storage_script_init(svstorage, scriptname);
-	ret = script == NULL ? -1 :
-		sieve_storage_script_delete(&script);
+	script = sieve_storage_open_script(svstorage, scriptname, NULL);
+	ret = script == NULL ? -1 : sieve_script_delete(&script);
 	if (ret < 0) {
 		errstr = sieve_storage_get_last_error(svstorage, &error);
 		if (error == SIEVE_ERROR_NOT_FOUND) {
@@ -148,7 +145,7 @@ sieve_attribute_set_active(struct mail_storage *storage,
 
 	if (scriptname == NULL) {
 		/* don't affect non-link active script */
-		if ((ret=sieve_storage_active_script_is_no_link(svstorage)) != 0) {
+		if ((ret=sieve_storage_is_singular(svstorage)) != 0) {
 			if (ret < 0) {
 				mail_storage_set_internal_error(storage);
 				return -1;
@@ -169,9 +166,9 @@ sieve_attribute_set_active(struct mail_storage *storage,
 	scriptname++;
 
 	/* activate specified script */
-	script = sieve_storage_script_init(svstorage, scriptname);
+	script = sieve_storage_open_script(svstorage, scriptname, NULL);
 	ret = script == NULL ? -1 :
-		sieve_storage_script_activate(script, value->last_change);
+		sieve_script_activate(script, value->last_change);
 	if (ret < 0) {
 		mail_storage_set_critical(storage,
 			"Failed to activate Sieve script '%s': %s", scriptname,
@@ -189,7 +186,7 @@ sieve_attribute_unset_active_script(struct mail_storage *storage,
 {
 	int ret;
 
-	if ((ret=sieve_storage_active_script_is_no_link(svstorage)) <= 0) {
+	if ((ret=sieve_storage_is_singular(svstorage)) <= 0) {
 		if (ret < 0)
 			mail_storage_set_internal_error(storage);
 		return ret;
@@ -222,7 +219,7 @@ sieve_attribute_set_active_script(struct mail_storage *storage,
 	/* skip over the 'S' type */
 	i_stream_skip(input, 1);
 
-	if (sieve_storage_save_as_active_script
+	if (sieve_storage_save_as_active
 		(svstorage, input, value->last_change) < 0) {
 		mail_storage_set_critical(storage,
 			"Failed to save active sieve script: %s",
@@ -275,7 +272,7 @@ sieve_attribute_set_sieve(struct mail_storage *storage,
 			  const struct mail_attribute_value *value)
 {
 	struct sieve_storage *svstorage;
-	struct sieve_save_context *save_ctx;
+	struct sieve_storage_save_context *save_ctx;
 	struct istream *input;
 	const char *scriptname;
 	int ret;
@@ -437,7 +434,7 @@ sieve_attribute_get_active_script(struct mail_storage *storage,
 	const char *errstr;
 	int ret;
 
-	if ((ret=sieve_storage_active_script_is_no_link(svstorage)) <= 0) {
+	if ((ret=sieve_storage_is_singular(svstorage)) <= 0) {
 		if (ret == 0 && sieve_storage_active_script_get_last_change
 			(svstorage, &value_r->last_change) < 0) {
 			ret = -1;
@@ -447,8 +444,10 @@ sieve_attribute_get_active_script(struct mail_storage *storage,
 		return ret;
 	}
 
-	if ((script=sieve_storage_active_script_get(svstorage)) == NULL)
+	if ((script=sieve_storage_active_script_open
+		(svstorage, NULL)) == NULL)
 		return 0;
+
 	if ((ret=sieve_attribute_retrieve_script
 		(storage, svstorage, script, TRUE, value_r, &errstr)) < 0) {
 		mail_storage_set_critical(storage,
@@ -504,7 +503,7 @@ sieve_attribute_get_sieve(struct mail_storage *storage, const char *key,
 		return -1;
 	}
 	scriptname = key + strlen(MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES);
-	script = sieve_storage_script_init(svstorage, scriptname);
+	script = sieve_storage_open_script(svstorage, scriptname, NULL);
 	if ((ret=sieve_attribute_retrieve_script
 		(storage, svstorage, script, FALSE, value_r, &errstr)) < 0) {
 		mail_storage_set_critical(storage,
@@ -628,7 +627,7 @@ sieve_attribute_iter_next_script(struct sieve_mailbox_attribute_iter *siter)
 	}
 
 	/* Check whether active script is a proper symlink or a regular file */
-	if ((ret=sieve_storage_active_script_is_no_link(svstorage)) < 0) {
+	if ((ret=sieve_storage_is_singular(svstorage)) < 0) {
 		mail_storage_set_critical(siter->iter.box->storage,
 			"Failed to iterate sieve scripts: %s",
 			sieve_storage_get_last_error(svstorage, NULL));
diff --git a/src/plugins/lda-sieve/lda-sieve-plugin.c b/src/plugins/lda-sieve/lda-sieve-plugin.c
index b48edb0fdc3fbe0d052f7b6c86913954bb70649f..62a4cdf69e95a83e57a85729e88f60b45804763b 100644
--- a/src/plugins/lda-sieve/lda-sieve-plugin.c
+++ b/src/plugins/lda-sieve/lda-sieve-plugin.c
@@ -15,7 +15,7 @@
 
 #include "sieve.h"
 #include "sieve-script.h"
-#include "sieve-script-file.h"
+#include "sieve-storage.h"
 
 #include "lda-sieve-plugin.h"
 
@@ -156,109 +156,118 @@ struct lda_sieve_run_context {
 	const char *userlog;
 };
 
-static const char *lda_sieve_get_personal_location
-(struct sieve_instance *svinst, struct mail_user *user)
+static int lda_sieve_get_personal_storage
+(struct sieve_instance *svinst, struct mail_user *user,
+	struct sieve_storage **storage_r)
 {
-	const char *script_location;
-
-	script_location = mail_user_plugin_getenv(user, "sieve");
-
-	/* userdb may specify Sieve location */
-	if (script_location != NULL) {
+	enum sieve_error error;
 
-		if (*script_location == '\0') {
-			/* disabled */
-			if ( user->mail_debug )
-				sieve_sys_debug(svinst, "empty script location, disabled");
-			return NULL;
+	*storage_r = sieve_storage_create_main(svinst, user, 0, &error);
+	if (*storage_r == NULL) {
+		switch (error) {
+		case SIEVE_ERROR_NOT_FOUND:
+			break;
+		case SIEVE_ERROR_TEMP_FAILURE:
+			sieve_sys_error(svinst,
+				"Failed to access user storage "
+				"(temporary failure)");
+			return -1;
+		default:
+			sieve_sys_error(svinst,
+				"Failed to access user storage");
+			break;
 		}
-	} else {
-		script_location = LDA_SIEVE_DEFAULT_LOCATION;
+		return 0;
 	}
-
-	return script_location;
+	return 1;
 }
 
-static const char *lda_sieve_get_default_location
-(struct mail_user *user)
+static int lda_sieve_get_default_storage
+(struct sieve_instance *svinst, struct mail_user *user,
+	struct sieve_storage **storage_r)
 {
-	const char *script_location;
+	bool debug = user->mail_debug;
+	enum sieve_error error;
+	const char *location;
 
 	/* Use default script location, if one exists */
-	script_location = mail_user_plugin_getenv(user, "sieve_default");
-	if ( script_location == NULL ) {
+	location = mail_user_plugin_getenv(user, "sieve_default");
+	if ( location == NULL ) {
 		/* For backwards compatibility */
-		script_location = mail_user_plugin_getenv(user, "sieve_global_path");
+		location = mail_user_plugin_getenv(user, "sieve_global_path");
 	}
 
-	return script_location;
+	if ( location == NULL ) {
+		if ( debug ) {
+			sieve_sys_debug(svinst,
+				"No default script configured for user");
+		}
+		return 0;
+	}
+
+	*storage_r = sieve_storage_create(svinst, location, 0, &error);
+	if ( *storage_r == NULL ) {
+		switch ( error ) {
+		case SIEVE_ERROR_NOT_FOUND:
+			if ( debug ) {
+				sieve_sys_debug(svinst,
+					"Default user script location `%s' does not exist",
+					location);
+			}
+			break;
+		case SIEVE_ERROR_TEMP_FAILURE:
+			sieve_sys_error(svinst,
+				"Failed to access default user script location `%s' "
+				"(temporary failure)",
+				location);
+			return -1;
+		default:
+			sieve_sys_error(svinst,
+				"Failed to access default user script location `%s'",
+				location);
+			break;
+		}
+		return 0;
+	}
+	return 1;
 }
 
 static int lda_sieve_multiscript_get_scripts
 (struct sieve_instance *svinst, const char *label, const char *location,
-	struct sieve_error_handler *ehandler, ARRAY_TYPE(sieve_scripts) *scripts,
-	enum sieve_error *error_r)
+	ARRAY_TYPE(sieve_script) *scripts, enum sieve_error *error_r)
 {
-	struct sieve_directory *sdir;
-	ARRAY_TYPE(const_string) script_files;
-	const char *const *files;
-	unsigned int count, i;
-	const char *file;
-
-	// FIXME: make this a generic iteration API
-	if ( (sdir=sieve_directory_open(svinst, location, error_r)) == NULL )
-		return ( *error_r == SIEVE_ERROR_NOT_FOUND ? 0 : -1 );
-
-	t_array_init(&script_files, 16);
-
-	while ( (file=sieve_directory_get_scriptfile(sdir)) != NULL ) {
-		/* Insert into sorted array */
-
-		files = array_get(&script_files, &count);
-		for ( i = 0; i < count; i++ ) {
-			if ( strcmp(file, files[i]) < 0 )
-				break;
-		}
-
-		if ( i == count )
-			array_append(&script_files, &file, 1);
-		else
-			array_insert(&script_files, i, &file, 1);
-	}
-
-	sieve_directory_close(&sdir);
+	struct sieve_script_sequence *seq;
+	struct sieve_script *script;
+	bool finished = FALSE;
+	int ret = 1;
 
-	files = array_get(&script_files, &count);
-	for ( i = 0; i < count; i++ ) {
-		struct sieve_script *script = sieve_script_create_open
-			(svinst, files[i], NULL, ehandler, error_r);
+	seq = sieve_script_sequence_create(svinst, location, error_r);
+	if ( seq == NULL )
+		return ( *error_r == SIEVE_ERROR_NOT_FOUND ? 0 : -1 );
 
+	while ( ret > 0 && !finished ) {
+		script = sieve_script_sequence_next(seq, error_r);
 		if ( script == NULL ) {
 			switch ( *error_r ) {
-			case SIEVE_ERROR_NOT_FOUND:
-				/* Shouldn't normally happen, but the script could have disappeared */
-				sieve_sys_warning(svinst,
-					"%s script %s doesn't exist", label, files[i]);
+			case SIEVE_ERROR_NONE:
+				finished = TRUE;
 				break;
 			case SIEVE_ERROR_TEMP_FAILURE:
 				sieve_sys_error(svinst,
-					"failed to access %s script %s (temporary failure)",
-					label, files[i]);
-				return -1;
+					"Failed to access %s script from `%s' (temporary failure)",
+					label, location);
+				ret = -1;
 			default:
-				sieve_sys_error(svinst,
-					"failed to access %s script %s", label, files[i]);
 				break;
 			}
-
 			continue;
 		}
 
 		array_append(scripts, &script, 1);
 	}
 
-	*error_r = SIEVE_ERROR_NONE;
-	return 1;
+	sieve_script_sequence_free(&seq);
+	return ret;
 }
 
 static void lda_sieve_binary_save
@@ -273,10 +282,11 @@ static void lda_sieve_binary_save
 
 		/* Cannot save binary for global script */
 		sieve_sys_error(srctx->svinst,
-			"the lda sieve plugin does not have permission "
-			"to save global sieve script binaries; "
-			"global sieve scripts like %s need to be "
-			"pre-compiled using the sievec tool", sieve_script_location(script));
+			"The LDA Sieve plugin does not have permission "
+			"to save global Sieve script binaries; "
+			"global Sieve scripts like `%s' need to be "
+			"pre-compiled using the sievec tool",
+			sieve_script_location(script));
 	}
 }
 
@@ -293,12 +303,12 @@ static struct sieve_binary *lda_sieve_open
 	if ( recompile ) {
 		/* Warn */
 		sieve_sys_warning(svinst,
-			"encountered corrupt binary: re-compiling script %s",
+			"Encountered corrupt binary: re-compiling script %s",
 			sieve_script_location(script));
 		compile_name = "re-compile";
 	} else 	if ( debug ) {
 		sieve_sys_debug(svinst,
-			"loading script %s", sieve_script_location(script));
+			"Loading script %s", sieve_script_location(script));
 	}
 
 	if ( script == srctx->user_script )
@@ -319,30 +329,31 @@ static struct sieve_binary *lda_sieve_open
 		/* Script not found */
 		case SIEVE_ERROR_NOT_FOUND:
 			if ( debug ) {
-				sieve_sys_debug(svinst, "script file %s is missing for %s",
+				sieve_sys_debug(svinst, "Script `%s' is missing for %s",
 					sieve_script_location(script), compile_name);
 			}
 			break;
 		/* Temporary failure */
 		case SIEVE_ERROR_TEMP_FAILURE:
 			sieve_sys_error(svinst,
-				"failed to open script %s for %s (temporary failure)",
+				"Failed to open script `%s' for %s (temporary failure)",
 				sieve_script_location(script), compile_name);
 			break;
 		/* Compile failed */
 		case SIEVE_ERROR_NOT_VALID:
 			if (script == srctx->user_script && srctx->userlog != NULL ) {
-				sieve_sys_info(svinst, "failed to %s script %s "
-					"(view user logfile %s for more information)",
+				sieve_sys_info(svinst,
+					"Failed to %s script `%s' "
+					"(view user logfile `%s' for more information)",
 					compile_name, sieve_script_location(script), srctx->userlog);
 				break;
 			}
-			sieve_sys_error(svinst,	"failed to %s script %s",
+			sieve_sys_error(svinst,	"Failed to %s script `%s'",
 				compile_name, sieve_script_location(script));
 			break;
 		/* Something else */
 		default:
-			sieve_sys_error(svinst,	"failed to open script %s for %s",
+			sieve_sys_error(svinst,	"Failed to open script `%s' for %s",
 				sieve_script_location(script), compile_name);
 			break;
 		}
@@ -387,13 +398,13 @@ static int lda_sieve_handle_exec_status
 	switch ( status ) {
 	case SIEVE_EXEC_FAILURE:
 		user_error_func(svinst,
-			"execution of script %s failed, but implicit keep was successful%s",
+			"Execution of script %s failed, but implicit keep was successful%s",
 			sieve_script_location(script), userlog_notice);
 		ret = 1;
 		break;
 	case SIEVE_EXEC_TEMP_FAILURE:
 		error_func(svinst,
-			"execution of script %s was aborted due to temporary failure%s",
+			"Execution of script %s was aborted due to temporary failure%s",
 			sieve_script_location(script), userlog_notice);
 		if ( mail_error != MAIL_ERROR_TEMP && mdctx->tempfail_error == NULL ) {
 			mdctx->tempfail_error =
@@ -403,14 +414,14 @@ static int lda_sieve_handle_exec_status
 		break;
 	case SIEVE_EXEC_BIN_CORRUPT:
 		sieve_sys_error(svinst,
-			"!!BUG!!: binary compiled from %s is still corrupt; "
+			"!!BUG!!: Binary compiled from %s is still corrupt; "
 			"bailing out and reverting to default delivery",
 			sieve_script_location(script));
 		ret = -1;
 		break;
 	case SIEVE_EXEC_KEEP_FAILED:
 		error_func(svinst,
-			"execution of script %s failed with unsuccessful implicit keep%s",
+			"Execution of script %s failed with unsuccessful implicit keep%s",
 			sieve_script_location(script), userlog_notice);
 		ret = -1;
 		break;
@@ -461,8 +472,10 @@ static int lda_sieve_singlescript_execute
 
 	/* Execute */
 
-	if ( debug )
-		sieve_sys_debug(svinst, "executing script from %s", sieve_get_source(sbin));
+	if ( debug ) {
+		sieve_sys_debug(svinst,
+			"Executing script from `%s'", sieve_get_source(sbin));
+	}
 
 	ret = sieve_execute
 		(sbin, srctx->msgdata, srctx->scriptenv, ehandler, rtflags, NULL);
@@ -491,9 +504,10 @@ static int lda_sieve_singlescript_execute
 
 		/* Execute again */
 
-		if ( debug )
+		if ( debug ) {
 			sieve_sys_debug
-				(svinst, "executing script from %s", sieve_get_source(sbin));
+				(svinst, "Executing script from `%s'", sieve_get_source(sbin));
+		}
 
 		ret = sieve_execute
 			(sbin, srctx->msgdata, srctx->scriptenv, ehandler, rtflags, NULL);
@@ -554,7 +568,7 @@ static int lda_sieve_multiscript_execute
 
 		if ( debug ) {
 			sieve_sys_debug
-				(svinst, "opening script %d of %d from %s", i+1, count,
+				(svinst, "Opening script %d of %d from `%s'", i+1, count,
 					sieve_script_location(script));
 		}
 
@@ -568,7 +582,7 @@ static int lda_sieve_multiscript_execute
 
 		if ( debug ) {
 			sieve_sys_debug
-				(svinst, "executing script from %s", sieve_get_source(sbin));
+				(svinst, "Executing script from `%s'", sieve_get_source(sbin));
 		}
 
 		more = sieve_multiscript_run(mscript, sbin, ehandler, rtflags, final);
@@ -613,7 +627,7 @@ static int lda_sieve_multiscript_execute
 	/* Don't log additional messages about compile failure */
 	if ( compile_error && ret == SIEVE_EXEC_FAILURE ) {
 		sieve_sys_info(svinst,
-			"aborted script execution sequence with successful implicit keep");
+			"Aborted script execution sequence with successful implicit keep");
 		return 1;
 	}
 
@@ -624,11 +638,11 @@ 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_error_handler *master_ehandler = srctx->master_ehandler;
-	const char *user_location, *default_location, *sieve_before, *sieve_after;
+	struct sieve_storage *user_storage, *default_storage;
+	const char *sieve_before, *sieve_after;
 	const char *setting_name;
 	enum sieve_error error;
-	ARRAY_TYPE(sieve_scripts) script_sequence;
+	ARRAY_TYPE(sieve_script) script_sequence;
 	struct sieve_script *const *scripts;
 	bool debug = mdctx->dest_user->mail_debug;
 	unsigned int after_index, count, i;
@@ -636,71 +650,79 @@ static int lda_sieve_find_scripts(struct lda_sieve_run_context *srctx)
 
 	/* Find the personal script to execute */
 
-	user_location = lda_sieve_get_personal_location(svinst, mdctx->dest_user);
-	if ( user_location != NULL ) {
-		srctx->user_script = sieve_script_create_open_as
-			(svinst, user_location, "main_script", master_ehandler, &error);
+	ret = lda_sieve_get_personal_storage
+		(svinst, mdctx->dest_user, &user_storage);
+	if ( ret > 0 ) {
+		srctx->user_script =
+			sieve_storage_active_script_open(user_storage, &error);
 
 		if ( srctx->user_script == NULL ) {
 			switch ( error ) {
 			case SIEVE_ERROR_NOT_FOUND:
 				if ( debug ) {
-					sieve_sys_debug(svinst, "user's script %s doesn't exist "
-						"(trying default script location instead)", user_location);
+					sieve_sys_debug(svinst,
+						"No active Sieve script exists in user storage `%s' "
+						"(trying default script location instead)",
+						sieve_storage_location(user_storage));
 				}
 				break;
 			case SIEVE_ERROR_TEMP_FAILURE:
 				sieve_sys_error(svinst,
-					"failed to access user's Sieve script %s (temporary failure)",
-					user_location);
+					"Failed to access active Sieve script in user storage `%s' "
+					"(temporary failure)",
+					sieve_storage_location(user_storage));
 				ret = -1;
 				break;
 			default:
 				sieve_sys_error(svinst,
-					"failed to access user's Sieve script %s "
+					"Failed to access active Sieve script in user storage `%s' "
 					"(trying default script location instead)",
-					user_location);
+					sieve_storage_location(user_storage));
 				break;
 			}
 		} else {
 			srctx->main_script = srctx->user_script;
 		}
+		sieve_storage_unref(&user_storage);
 	}
 
 	if ( ret >= 0 && srctx->user_script == NULL ) {
-		default_location = lda_sieve_get_default_location(mdctx->dest_user);
-		if ( default_location != NULL ) {
-			srctx->main_script = sieve_script_create_open_as
-				(svinst, default_location, "main script", master_ehandler, &error);
+		ret = lda_sieve_get_default_storage
+			(svinst, mdctx->dest_user, &default_storage);
+		if ( ret > 0 ) {
+			srctx->main_script = sieve_storage_open_script
+				(default_storage, NULL, &error);
 
 			if ( srctx->main_script == NULL ) {
 				switch ( error ) {
 				case SIEVE_ERROR_NOT_FOUND: 
 					if ( debug ) {
-						sieve_sys_debug(svinst, "default user script %s doesn't exist",
-							default_location);
+						sieve_sys_debug(svinst,
+							"Default user script `%s' does not exist",
+							sieve_storage_location(default_storage));
 					}
 					break;
 				case SIEVE_ERROR_TEMP_FAILURE:
 					sieve_sys_error(svinst,
-						"failed to access default user script %s (temporary failure)",
-						default_location);
+						"Failed to access default user script `%s'"
+						"(temporary failure)",
+						sieve_storage_location(default_storage));
 					ret = -1;
 					break;
 				default:
-					sieve_sys_error(svinst, "failed to access default user script %s",
-						default_location);
+					sieve_sys_error(svinst,
+						"failed to access default user script %s",
+						sieve_storage_location(default_storage));
 					break;
 				}
 			}
-		} else {
-			sieve_sys_debug(svinst, "no default script configured for user");
 		}
+		sieve_storage_unref(&default_storage);
 	}
 
 	if ( debug && ret >= 0 && srctx->main_script == NULL ) {
 		sieve_sys_debug(svinst,
-			"user has no valid location for a personal script");
+			"User has no personal script");
 	}
 
 	/* Compose script array */
@@ -714,12 +736,12 @@ static int lda_sieve_find_scripts(struct lda_sieve_run_context *srctx)
 		sieve_before = mail_user_plugin_getenv(mdctx->dest_user, setting_name);
 		while ( ret >= 0 && sieve_before != NULL && *sieve_before != '\0' ) {
 			ret = lda_sieve_multiscript_get_scripts(svinst, setting_name,
-				sieve_before, master_ehandler, &script_sequence, &error);
+				sieve_before, &script_sequence, &error);
 			if ( ret < 0 && error == SIEVE_ERROR_TEMP_FAILURE ) {
 				ret = -1;
 				break;
 			} else if (ret == 0 && debug ) {
-				sieve_sys_debug(svinst, "%s location not found: %s",
+				sieve_sys_debug(svinst, "Location for %s not found: %s",
 					setting_name, sieve_before);
 			}
 			ret = 0;
@@ -731,7 +753,7 @@ static int lda_sieve_find_scripts(struct lda_sieve_run_context *srctx)
 			scripts = array_get(&script_sequence, &count);
 			for ( i = 0; i < count; i ++ ) {
 				sieve_sys_debug(svinst,
-					"executed before user's personal Sieve script(%d): %s",
+					"Executed before user's personal Sieve script(%d): %s",
 					i+1, sieve_script_location(scripts[i]));
 			}
 		}
@@ -743,7 +765,7 @@ static int lda_sieve_find_scripts(struct lda_sieve_run_context *srctx)
 
 		if ( ret >= 0 && debug ) {
 			sieve_sys_debug(svinst,
-				"using the following location for user's Sieve script: %s",
+				"Using the following location for user's Sieve script: %s",
 				sieve_script_location(srctx->main_script));
 		}
 	}
@@ -757,12 +779,12 @@ static int lda_sieve_find_scripts(struct lda_sieve_run_context *srctx)
 		sieve_after = mail_user_plugin_getenv(mdctx->dest_user, setting_name);
 		while ( sieve_after != NULL && *sieve_after != '\0' ) {
 			ret = lda_sieve_multiscript_get_scripts(svinst, setting_name,
-				sieve_after, master_ehandler, &script_sequence, &error);
+				sieve_after, &script_sequence, &error);
 			if ( ret < 0 && error == SIEVE_ERROR_TEMP_FAILURE ) {
 				ret = -1;
 				break;
 			} else if (ret == 0 && debug ) {
-				sieve_sys_debug(svinst, "%s location not found: %s",
+				sieve_sys_debug(svinst, "Location for %s not found: %s",
 					setting_name, sieve_after);
 			}
 			ret = 0;
@@ -804,7 +826,7 @@ static int lda_sieve_execute
 	if ( srctx->script_count == 0 ) {
 		if ( debug ) {
 			sieve_sys_debug(svinst,
-				"no scripts to execute: reverting to default delivery.");
+				"No scripts to execute: reverting to default delivery.");
 		}
 
 		/* No error, but no delivery by this plugin either. A return value of <= 0
diff --git a/src/sieve-tools/sievec.c b/src/sieve-tools/sievec.c
index 616314e950d363a95878f5da386d8b29a15f04db..87396e7eecbce95068c4782bb6974dfc242b1968 100644
--- a/src/sieve-tools/sievec.c
+++ b/src/sieve-tools/sievec.c
@@ -11,7 +11,6 @@
 #include "sieve.h"
 #include "sieve-extensions.h"
 #include "sieve-script.h"
-#include "sieve-script-file.h"
 #include "sieve-tool.h"
 
 #include <stdio.h>
@@ -113,7 +112,7 @@ int main(int argc, char **argv)
 				break;
 			}
 
-			if ( sieve_scriptfile_has_extension(dp->d_name) ) {
+			if ( sieve_script_file_has_extension(dp->d_name) ) {
 				const char *file;
 
 				if ( scriptfile[strlen(scriptfile)-1] == '/' )
diff --git a/src/testsuite/testsuite-binary.c b/src/testsuite/testsuite-binary.c
index 99847d508e8de5216f52627ae1c561a68ca3188a..3a2cc1504cb597a8041ec20b4f41c7e0e9ab3d16 100644
--- a/src/testsuite/testsuite-binary.c
+++ b/src/testsuite/testsuite-binary.c
@@ -11,7 +11,6 @@
 #include "sieve.h"
 #include "sieve-common.h"
 #include "sieve-script.h"
-#include "sieve-script-file.h"
 #include "sieve-binary.h"
 #include "sieve-error.h"
 
diff --git a/src/testsuite/testsuite-script.c b/src/testsuite/testsuite-script.c
index 7b3aa586203c43d3de0ae00a7127bd73ab50ef04..c8108253277346d2f0581e24b380e02cc9a3f945 100644
--- a/src/testsuite/testsuite-script.c
+++ b/src/testsuite/testsuite-script.c
@@ -6,7 +6,6 @@
 #include "sieve.h"
 #include "sieve-common.h"
 #include "sieve-script.h"
-#include "sieve-script-file.h"
 #include "sieve-binary.h"
 #include "sieve-interpreter.h"
 #include "sieve-runtime-trace.h"
diff --git a/tests/extensions/include/execute.svtest b/tests/extensions/include/execute.svtest
index 3d91423321258ccb4b52b485f31893fbc93fcb40..734ac66441060db6c7a35c0ece90b5aa9b532d18 100644
--- a/tests/extensions/include/execute.svtest
+++ b/tests/extensions/include/execute.svtest
@@ -44,25 +44,25 @@ test "Actions Fileinto" {
 }
 
 test "Namespace - file" {
-        if not test_script_compile "execute/namespace.sieve" {
-                test_fail "failed to compile sub-test";
-        }
+	if not test_script_compile "execute/namespace.sieve" {
+		test_fail "failed to compile sub-test";
+	}
 
-        if not test_script_run {
-                test_fail "failed to execute sub-test";
-        }
+	if not test_script_run {
+		test_fail "failed to execute sub-test";
+	}
 }
 
 test "Namespace - dict" {
-	test_config_set "sieve_dir" "dict:file:${tst.path}/included/namespace.dict";
-	test_config_set "sieve_global_dir" "dict:file:${tst.path}/included-global/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_reload :extension "include";
 
-        if not test_script_compile "execute/namespace.sieve" {
-                test_fail "failed to compile sub-test";
-        }
-
-        if not test_script_run {
-                test_fail "failed to execute sub-test";
-        }
+	if not test_script_compile "execute/namespace.sieve" {
+		test_fail "failed to compile sub-test";
+	}
+	
+	if not test_script_run {
+		test_fail "failed to execute sub-test";
+	}
 }