diff --git a/INSTALL b/INSTALL
index 3d41903d17b2f9941f014b588507d266b8332b05..43afdb3c9db7449a0dcf196052c2d99b330b1cbe 100644
--- a/INSTALL
+++ b/INSTALL
@@ -85,23 +85,21 @@ The Sieve interpreter recognizes the following configuration options in the
 plugin section of the config file (default values are shown if applicable):
 
  sieve = ~/.dovecot.sieve
-   The path to the user's main active script. 
+   The location of the user's main active script. 
  
  sieve_default = 
-   The path to the default personal sieve script file, which gets executed ONLY
-   if user's private Sieve script does no exist, e.g.
-   /var/lib/dovecot/default.sieve. This is usually a global script, so sure to
-   pre-compile this script manually using the sievec command line tool, as
-   explained in the README file.
-
- sieve_global_path =
-   This is the DEPRECATED former name of the sieve_default setting.
+   The location of the default personal sieve script file, which gets executed
+   ONLY if user's private Sieve script does no exist, e.g.
+   /var/lib/dovecot/default.sieve. This is usually a global script, so be sure
+   to pre-compile this script manually using the sievec command line tool, as
+   explained in the README file. This setting used to be called
+   `sieve_global_path', but that name is now deprecated.
 
  sieve_global_dir =
-   Directory for :global include scripts for the Sieve include extension. 
+   Location for :global include scripts for the Sieve include extension. 
 
- sieve_dir = ~/
-   Directory for :personal include scripts for the Sieve include extension. 
+ sieve_dir = ~/sieve
+   Location for :personal include scripts for the Sieve include extension. 
 
  sieve_extensions =
    Which Sieve language extensions are available to users. By default, all 
@@ -131,6 +129,12 @@ plugin section of the config file (default values are shown if applicable):
    (wiki2.dovecot.org) or the pigeonhole website (http://pigeonhole.dovecot.org)
    for available plugins. 
 
+ sieve_user_log =
+   The path to the file where the user log file is written. If not configured, a
+   default location is used. If the main user's personal Sieve (as configured
+   with sieve=) is a file, the logfile is set to <filename>.log by default. If
+   it is not a file, the default user log file is ~/.dovecot.sieve.log. 
+
  recipient_delimiter = +
    The separator that is expected between the :user and :detail address parts 
    introduced by the subaddress extension. This may also be a sequence of
@@ -176,7 +180,48 @@ Sieve Interpreter - Configurable Limits
    The maximum number of redirect actions that can be performed during a single
    script execution. If set to 0, no redirect actions are allowed.
 
-Sieve Interpreter - Per-user Sieve script location
+Sieve Interpreter - Script Locations
+------------------------------------
+
+The location of Sieve scripts is not limited to the file system. The Sieve
+interpreter can be extended to retrieve Sieve scripts from other sources as
+well, such as a database. Currently, all settings that are used to obtain the
+location of a single Sieve script, such as sieve=, sieve_default=, sieve_dir=
+and sieve_global_dir= accept the following extended syntax:
+
+location = [<type>:]path[;<option>[=<value>][;...]]
+
+The following script location types are implemented by default:
+
+  file    - The location path is a file system path pointing to the script file
+            or a directory containing script files with names structured as
+            `<script-name>.sieve'.
+	dict    - Dovecot dict lookup. The location path is a dict uri. Read
+            doc/scipt-location-dict.txt for more information and examples. 
+
+If the type prefix is omitted, the script location type is 'file'.
+
+The following options are defined for all location types:
+
+  name=<script-name>
+    Set the name of the Sieve script that this location points to. If the name
+    of the Sieve script is not contained in the location path, this option is
+    required (e.g. for dict locations that must point to a particular script).
+    If the name of the script is contained in the location, the value of the
+    name option overrides the name retrieved from the location. If the Sieve
+    interpreter explicitly queries for a specific name (e.g. to include a script
+    from the sieve_dir= location), this option has no effect.
+
+  bindir=<dirpath>
+		Points to the directory where the compiled binaries for this script location
+    are stored. If this option is omitted, the behavior depends on the location
+    type. For `file' type locations, the binary is then stored in the same
+    directory as where the script file was found if possible. For `dict' type
+    locations, the binary is not stored at all in that case. Don't specify the
+    same directory for different script locations, as this will result in
+    undefined behavior. 
+	
+Sieve Interpreter - Per-user Sieve Script Location
 --------------------------------------------------
 
 By default, the Pigeonhole LDA Sieve plugin looks for the user's Sieve script
@@ -184,8 +229,8 @@ file in the user's home directory (~/.dovecot.sieve). This requires that the
 home directory is set for the user.
 
 If you want to store the script elsewhere, you can override the default using 
-the sieve setting, which specifies the path to the user's script file. This can 
-be done in two ways:
+the sieve= setting, which specifies the location of the user's script file. This
+can be done in two ways:
 
  1. Define the sieve setting in the plugin section of dovecot.conf.
  2. Return sieve extra field from userdb extra fields. 
diff --git a/TODO b/TODO
index 25704b2657de699d53efa68d7e519e6aa6415366..552647464d2f6404656aeeed3ddfeb788bb6e201 100644
--- a/TODO
+++ b/TODO
@@ -1,7 +1,9 @@
 Current activities:
 
-* Update include extension to latest draft (v13 currently):
-	- Implement :optional tag.
+* Implement generic Sieve script object that abstracts from its location and
+  add support for retrieving scripts from dict database.
+	- Implement infrastructure for loading a sequence of global scripts from
+	  a database (for sieve_before/sieve_after).
 
 Parallel plugin-based efforts:
 
@@ -14,6 +16,13 @@ Parallel plugin-based efforts:
 
 Next (mostly in order of descending priority/precedence):
 
+* 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
+* Update include extension to latest draft (v13 currently):
+	- Implement :optional tag.
 * Implement index extension
 * Add normalize() method to comparators to normalize the string before matching
   (for efficiency).
@@ -69,9 +78,6 @@ Next (mostly in order of descending priority/precedence):
 * 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.
-* 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.
 * Add support for stream matching for handling large values, e.g. from the body
   extension.
 * Implement message modification and extraction API in order to:
diff --git a/doc/script-location-dict.txt b/doc/script-location-dict.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3ee5a894d7f867e241874c7c70b3ef399451604a
--- /dev/null
+++ b/doc/script-location-dict.txt
@@ -0,0 +1,145 @@
+DICT Sieve Script Location Type
+
+Description
+===========
+
+This location type is used to retrieve Sieve scripts from a Dovecot dict lookup.
+Such dictionaries use a file or an SQL database as backend. Refer to the Dovecot
+dict documentation for more information on dict lookups.
+
+To retrieve a Sieve script from the dict database, two lookups are performed.
+First, the name of the Sieve script is queried from the dict path
+`/priv/sieve/name/<name>'. If the Sieve script exists, this yields a data ID
+which in turn points to the actual script text. The script text is subsequently
+queried from the dict path '/priv/sieve/data/<dict-id>'.
+
+The second query is only necessary when no compiled binary is available or when
+the script has changed and needs to be recompiled. The data ID is used to detect
+changes in the dict's underlying database. Changing a Sieve script in the
+database must be done by first making a new script data item with a new data ID.
+Then, the mapping from name to data ID must be changed to point to the new
+script text, thereby changing the data ID returned from the name lookup, i.e.
+the first query mentioned above. Script binaries compiled from Sieve scripts
+contained in a dict database record the data ID. While the data ID contained in
+the binary is identical to the one returned from the dict lookup, the binary is
+assumed up-to-date. When the returned data ID is different, the new script text
+is retrieved using the second query and compiled into a new binary containing
+the updated data ID.
+
+Note that, by default, compiled binaries are not stored at all for Sieve scripts
+retrieved from a dict database. The bindir= option needs to be specified in the
+location specification. Refer to the INSTALL file for more general information
+about configuration of script locations.
+
+Configuration
+=============
+
+The script location syntax is specialized as follows:
+
+location = dict:<dict-uri>[;<option>[=<value>][;...]]
+
+The following additional options are recognized:
+
+  user=<username>
+    Overrides the user name used for the dict lookup. Normally, the name of the
+    user running the Sieve interpreter is used.
+
+If the name of the Script is left unspecified and not otherwise provided by the
+Sieve interpreter, the name defaults to `default'. 
+
+Examples
+========
+
+Example 1
+---------
+
+This example is mainly useful for performing a quick test of the dict location
+configuration without configuring an actual (SQL) database. For this example, a
+very simple file dict is assumed to be contained in the file
+/etc/dovecot/sieve.dict:
+
+priv/sieve/name/keep
+1
+priv/sieve/name/discard
+2
+priv/sieve/name/spam
+3
+priv/sieve/data/1
+keep;
+priv/sieve/data/2
+discard;
+priv/sieve/data/3
+require ["fileinto", "mailbox"]; fileinto :create "spam";
+
+To use this file dict for the main active script, you can change the
+configuration as follows (e.g. in /etc/dovecot/conf.d/90-sieve.conf):
+
+plugin {
+  sieve = dict:file:/etc/dovecot/sieve.dict;name=keep;bindir=~/.sieve-bin
+}
+
+The Sieve script named "keep" is retrieved from the file dict as the main
+script. Binaries are stored in the ~/.sieve-bin directory. 
+
+Example 2
+---------
+
+This example uses a PostgreSQL database. Our database contains the following
+table:
+
+CREATE TABLE user_sieve_scripts (
+  id integer,
+  username varchar(40),
+  script_name varchar(256),
+  script_data varchar(10240),
+
+  PRIMARY KEY (id),
+  UNIQUE(username, script_name)
+);
+
+We create a file /etc/dovecot/dict-sieve-sql.conf with the following content:
+
+connect = host=localhost dbname=dovecot user=dovecot password=password
+map {
+  pattern = priv/sieve/name/$script_name
+  table = user_sieve_scripts
+  username_field = username
+  value_field = id
+  fields {
+    script_name = $script_name
+  }
+}
+map {
+  pattern = priv/sieve/data/$id
+  table = user_sieve_scripts
+  username_field = username
+  value_field = script_data
+  fields {
+    id = $id
+  }
+}
+
+These are the mappings used by the SQL dict. The first mapping is the name query
+that yields the id of the Sieve script. The second mapping is the query used to
+retrieve the Sieve script itself.
+
+Much like the dict configuration for mailbox quota, it is often not possible to
+directly use an SQL dict because the SQL drivers are not linked to binaries such
+as dovecot-lda and lmtp. You need to use the dict proxy service. Add the dict
+URI to the dict section (typically located in your main dovecot.conf):
+
+dict {
+  sieve = pgsql:/etc/dovecot/dict-sieve-sql.conf.ext
+}
+
+To use this SQL dict for the main active script, you can change the
+configuration as follows (e.g. in /etc/dovecot/conf.d/90-sieve.conf):
+
+plugin {
+  sieve = dict:proxy::sieve;name=active;bindir=~/.sieve-bin
+}
+
+This uses the proxy dict uri `proxy::sieve'. This refers to the `sieve =' entry
+in the dict {...} section above. With this configuration, a Sieve script called
+"main" is retrieved from the SQL dict. Binaries are stored in the ~/.sieve-bin
+directory.
diff --git a/m4/dovecot.m4 b/m4/dovecot.m4
index 02e6e015352b661232fa8fd366491b046aacc90d..3ce503692e4cec2e8fcf66025bb7c5a9a9b9527b 100644
--- a/m4/dovecot.m4
+++ b/m4/dovecot.m4
@@ -6,7 +6,7 @@
 # unlimited permission to copy and/or distribute it, with or without
 # modifications, as long as this notice is preserved.
 
-# serial 4
+# serial 5
 
 AC_DEFUN([DC_DOVECOT_MODULEDIR],[
 	AC_ARG_WITH(moduledir,
@@ -48,13 +48,13 @@ AC_DEFUN([DC_DOVECOT],[
 
 	AC_ARG_WITH(dovecot-install-dirs,
 		[AC_HELP_STRING([--with-dovecot-install-dirs],
-	    	[Use install directories configured for Dovecot (default)])],
-	    if test x$withval = xno; then
-    	    use_install_dirs=no
-    	else
-        	use_install_dirs=yes
-	    fi,
-    	use_install_dirs=yes)
+		[Use install directories configured for Dovecot (default)])],
+	if test x$withval = xno; then
+		use_install_dirs=no
+	else
+		use_install_dirs=yes
+	fi,
+	use_install_dirs=yes)
 
 	AC_MSG_CHECKING([for dovecot-config in "$dovecotdir"])
 	if test -f "$dovecotdir/dovecot-config"; then
@@ -72,7 +72,7 @@ AC_DEFUN([DC_DOVECOT],[
 	cd $old
 	DISTCHECK_CONFIGURE_FLAGS="--with-dovecot=$abs_dovecotdir --without-dovecot-install-dirs"
 
-	eval `grep -i '^dovecot_[[a-z]]*=' "$dovecotdir"/dovecot-config`
+	eval `grep -i '^dovecot_[[a-z_]]*=' "$dovecotdir"/dovecot-config`
 	eval `grep '^LIBDOVECOT[[A-Z_]]*=' "$dovecotdir"/dovecot-config`
 
 	if test "$use_install_dirs" = "no"; then
@@ -83,8 +83,8 @@ AC_DEFUN([DC_DOVECOT],[
 		dovecot_moduledir='$(moduledir)'
 	fi
 
-	AX_SUBST_L([DISTCHECK_CONFIGURE_FLAGS], [dovecot_moduledir], [dovecot_pkgincludedir], [dovecot_pkglibexecdir], [dovecot_pkglibdir], [dovecot_docdir])
-	AX_SUBST_L([DOVECOT_CFLAGS], [DOVECOT_LIBS], [DOVECOT_SSL_LIBS])
+	AX_SUBST_L([DISTCHECK_CONFIGURE_FLAGS], [dovecotdir], [dovecot_moduledir], [dovecot_pkgincludedir], [dovecot_pkglibexecdir], [dovecot_pkglibdir], [dovecot_docdir])
+	AX_SUBST_L([DOVECOT_CFLAGS], [DOVECOT_LIBS], [DOVECOT_SSL_LIBS], [DOVECOT_SQL_LIBS])
 	AX_SUBST_L([LIBDOVECOT], [LIBDOVECOT_LOGIN], [LIBDOVECOT_SQL], [LIBDOVECOT_LDA], [LIBDOVECOT_STORAGE])
 	AX_SUBST_L([LIBDOVECOT_DEPS], [LIBDOVECOT_LOGIN_DEPS], [LIBDOVECOT_SQL_DEPS], [LIBDOVECOT_LDA_DEPS], [LIBDOVECOT_STORAGE_DEPS])
 	AX_SUBST_L([LIBDOVECOT_INCLUDE], [LIBDOVECOT_LDA_INCLUDE], [LIBDOVECOT_SERVICE_INCLUDE], [LIBDOVECOT_STORAGE_INCLUDE], [LIBDOVECOT_LOGIN_INCLUDE], [LIBDOVECOT_CONFIG_INCLUDE])
diff --git a/src/lib-sieve-tool/sieve-tool.c b/src/lib-sieve-tool/sieve-tool.c
index 2627e28cb0ee0694996e9140ef2e22ca9251582d..d242cb89c4f23e21ee5b646e8afed9f4333816f5 100644
--- a/src/lib-sieve-tool/sieve-tool.c
+++ b/src/lib-sieve-tool/sieve-tool.c
@@ -89,7 +89,7 @@ static const char *sieve_tool_sieve_get_homedir
 	return sieve_tool_get_homedir(tool);
 }
 
-const struct sieve_environment sieve_tool_sieve_env = {
+const struct sieve_callbacks sieve_tool_callbacks = {
 	sieve_tool_sieve_get_homedir,
 	sieve_tool_sieve_get_setting
 };
@@ -223,6 +223,7 @@ struct sieve_instance *sieve_tool_init_finish
 		MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT |
 		MAIL_STORAGE_SERVICE_FLAG_USE_SYSEXITS;
 	struct mail_storage_service_input service_input;
+	struct sieve_environment svenv;
 	const char *username = tool->username;
 	const char *homedir = tool->homedir;
 	const char *errstr;
@@ -261,9 +262,15 @@ struct sieve_instance *sieve_tool_init_finish
 		(master_service, "mail_full_filesystem_access=yes") < 0 )
 		i_unreached();
 
+	memset((void *)&svenv, 0, sizeof(svenv));
+	svenv.username = username;
+	(void)mail_user_get_home(tool->mail_user_dovecot, &svenv.home_dir);
+	svenv.hostname = "host.example.com";
+	svenv.base_dir = tool->mail_user_dovecot->set->base_dir;
+
 	/* Initialize Sieve Engine */
-	if ( (tool->svinst=sieve_init(&sieve_tool_sieve_env, tool, tool->debug)) 
-		== NULL )
+	if ( (tool->svinst=sieve_init
+		(&svenv, &sieve_tool_callbacks, tool, tool->debug)) == NULL )
 		i_fatal("failed to initialize sieve implementation");
 
 	/* Load Sieve plugins */
@@ -545,7 +552,7 @@ struct sieve_binary *sieve_tool_script_open
 
 	sieve_error_handler_unref(&ehandler);
 
-	sieve_save(sbin, NULL, FALSE, NULL);
+	sieve_save(sbin, FALSE, NULL);
 	return sbin;
 }
 
diff --git a/src/lib-sieve/Makefile.am b/src/lib-sieve/Makefile.am
index 0b3321ce3ab2a4df40f38581e1506bc0a3183e47..5d4c163676282c875de2d7136c1779a3b7fbed57 100644
--- a/src/lib-sieve/Makefile.am
+++ b/src/lib-sieve/Makefile.am
@@ -80,6 +80,8 @@ libdovecot_sieve_la_SOURCES = \
 	sieve-smtp.c \
 	sieve-lexer.c \
 	sieve-script.c \
+	sieve-script-file.c \
+	sieve-script-dict.c \
 	sieve-ast.c \
 	sieve-binary.c \
 	sieve-binary-file.c \
@@ -126,6 +128,7 @@ headers = \
 	sieve-lexer.h \
 	sieve-script.h \
 	sieve-script-private.h \
+	sieve-script-file.h \
 	sieve-ast.h \
 	sieve-binary.h \
 	sieve-binary-private.h \
diff --git a/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c b/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c
index 953080760707bb48816dc9c17042b93d1bb26a4f..f9279ded1ad052c4cd078a4b21bb90da74f9961b 100644
--- a/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c
+++ b/src/lib-sieve/plugins/enotify/mailto/ntfy-mailto.c
@@ -465,7 +465,7 @@ static bool ntfy_mailto_send
 	}
 
 	msg = t_str_new(512);
-	outmsgid = sieve_message_get_new_id(senv);
+	outmsgid = sieve_message_get_new_id(nenv->svinst);
 	
 	rfc2822_header_write(msg, "X-Sieve", SIEVE_IMPLEMENTATION);
 	rfc2822_header_write(msg, "Message-ID", outmsgid);
diff --git a/src/lib-sieve/plugins/environment/ext-environment-common.c b/src/lib-sieve/plugins/environment/ext-environment-common.c
index 0afd6c664f9e20a38d5c7d465c229a7bb26273ea..7657dc24c94ee2270d7e8030e45eda9f4e7f781d 100644
--- a/src/lib-sieve/plugins/environment/ext-environment-common.c
+++ b/src/lib-sieve/plugins/environment/ext-environment-common.c
@@ -103,8 +103,11 @@ const char *ext_environment_item_get_value
 	if ( item->value != NULL )
 		return item->value;
 
-	if ( item->get_value != NULL ) 
-		return item->get_value(senv);
+	if ( item->get_value != NULL ) {
+		const char *value = item->get_value(ext->svinst, senv);
+
+		return ( value == NULL ? "" : value );
+	}
 
 	return NULL; 
 }
@@ -130,9 +133,10 @@ const struct sieve_environment_item domain_env_item = {
  *   executing.
  */
 
-static const char *envit_host_get_value(const struct sieve_script_env *senv)
+static const char *envit_host_get_value
+(struct sieve_instance *svinst, const struct sieve_script_env *senv ATTR_UNUSED)
 {
-	return senv->hostname != NULL ? senv->hostname : "";
+	return svinst->hostname;
 }
 
 const struct sieve_environment_item host_env_item = {
diff --git a/src/lib-sieve/plugins/environment/sieve-ext-environment.h b/src/lib-sieve/plugins/environment/sieve-ext-environment.h
index cb2ef1ea4f6c3975df5b72e38a1b3bfaee99ddf3..cede06d3f6e96f0d04365191ceb03d3b42412825 100644
--- a/src/lib-sieve/plugins/environment/sieve-ext-environment.h
+++ b/src/lib-sieve/plugins/environment/sieve-ext-environment.h
@@ -10,7 +10,8 @@ struct sieve_environment_item {
 	const char *name;
 	
 	const char *value;
-	const char *(*get_value)(const struct sieve_script_env *senv);
+	const char *(*get_value)
+		(struct sieve_instance *svinst, const struct sieve_script_env *senv);
 };
 
 void sieve_ext_environment_item_register
diff --git a/src/lib-sieve/plugins/include/cmd-include.c b/src/lib-sieve/plugins/include/cmd-include.c
index 9e7ab2157615800823a4260c97a35de682883881..23193387bb17717e4a6fd5492fe85bb0a615abea 100644
--- a/src/lib-sieve/plugins/include/cmd-include.c
+++ b/src/lib-sieve/plugins/include/cmd-include.c
@@ -202,7 +202,7 @@ static bool cmd_include_validate
 	struct cmd_include_context_data *ctx_data = 
 		(struct cmd_include_context_data *) cmd->data;
 	struct sieve_script *script;
-	const char *script_path, *script_name;
+	const char *script_location, *script_name;
 	enum sieve_error error = SIEVE_ERROR_NONE;
 	bool include = TRUE;
 	
@@ -235,9 +235,9 @@ static bool cmd_include_validate
 		return FALSE;
 	}
 		
-	script_path = ext_include_get_script_directory
+	script_location = ext_include_get_script_location
 		(this_ext, ctx_data->location, script_name);
-	if ( script_path == NULL ) {
+	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)",
@@ -247,8 +247,8 @@ static bool cmd_include_validate
 	}
 	
 	/* Create script object */
-	script = sieve_script_create_in_directory
-		(this_ext->svinst, script_path, script_name, 
+	script = sieve_script_create
+		(this_ext->svinst, script_location, script_name, 
 			sieve_validator_error_handler(valdtr), &error);
 
 	if ( script == NULL ) {
@@ -343,11 +343,12 @@ static bool opc_include_dump
 	included = ext_include_binary_script_get_included(binctx, include_id);
 	if ( included == NULL )
 		return FALSE;
-		
+
 	sieve_code_descend(denv);
-	sieve_code_dumpf(denv, "script: %s %s[ID: %d, BLOCK: %d]", 
-		sieve_script_filename(included->script), (flags & 0x01 ? "(once) " : ""),
-		include_id, sieve_binary_block_get_id(included->block));
+	sieve_code_dumpf(denv, "script: `%s' from %s %s[ID: %d, BLOCK: %d]", 
+		sieve_script_name(included->script), sieve_script_location(included->script),
+		(flags & 0x01 ? "(once) " : ""), include_id,
+		sieve_binary_block_get_id(included->block));
 
 	return TRUE;
 }
diff --git a/src/lib-sieve/plugins/include/ext-include-binary.c b/src/lib-sieve/plugins/include/ext-include-binary.c
index dd0fb988e4af05e842cf4028f5a94149d304e859..da520c84b36d2707c9a446f17fb5eb007bd7962a 100644
--- a/src/lib-sieve/plugins/include/ext-include-binary.c
+++ b/src/lib-sieve/plugins/include/ext-include-binary.c
@@ -57,9 +57,10 @@ struct ext_include_binary_context {
 	ARRAY_DEFINE(include_index, struct ext_include_script_info *);
 
 	struct sieve_variable_scope_binary *global_vars;
+
+	unsigned int outdated:1;
 };
 
- 
 static struct ext_include_binary_context *ext_include_binary_create_context
 (const struct sieve_extension *this_ext, struct sieve_binary *sbin)
 {
@@ -224,6 +225,7 @@ static bool ext_include_binary_save
 		sieve_binary_emit_unsigned(sblock, sieve_binary_block_get_id(incscript->block));
 		sieve_binary_emit_byte(sblock, incscript->location);
 		sieve_binary_emit_cstring(sblock, sieve_script_name(incscript->script));
+		sieve_script_binary_write_metadata(incscript->script, sblock);
 	}
 
 	result = ext_include_variables_save(sblock, binctx->global_vars);
@@ -270,8 +272,9 @@ static bool ext_include_binary_open
 		struct sieve_binary_block *inc_block;
 		unsigned int location;
 		string_t *script_name;
-		const char *script_dir;
+		const char *script_location;
 		struct sieve_script *script;
+		int ret;
 		
 		if ( 
 			!sieve_binary_read_unsigned(sblock, &offset, &inc_block_id) ||
@@ -298,20 +301,33 @@ static bool ext_include_binary_open
 			/* Binary is corrupt, recompile */
 			sieve_sys_error(svinst,
 				"include: dependency block %d of binary %s "
-				"reports invalid script location (id %d)", 
+				"uses invalid script location (id %d)", 
 				block_id, sieve_binary_path(sbin), location); 
 			return FALSE;
 		}		
 		
 		/* Can we find/open the script dependency ? */
-		script_dir = ext_include_get_script_directory
+		script_location = ext_include_get_script_location
 			(ext, location, str_c(script_name));		
-		if ( script_dir == NULL || 
-			!(script=sieve_script_create_in_directory
-				(ext->svinst, script_dir, str_c(script_name), NULL, NULL)) ) {
+		if ( script_location == NULL || (script=sieve_script_create
+			(ext->svinst, script_location, str_c(script_name), NULL, NULL)) == NULL )
+			{
 			/* No, recompile */
 			return FALSE;
 		}
+
+		if ( (ret=sieve_script_binary_read_metadata(script, sblock, &offset))
+			< 0 ) {
+			/* Binary is corrupt, recompile */
+			sieve_sys_error(svinst,
+				"include: dependency block %d of binary %s "
+				"contains invalid script metadata for script %s", 
+				block_id, sieve_binary_path(sbin), sieve_script_location(script)); 
+			return FALSE;
+		}
+
+		if ( ret == 0 )
+			binctx->outdated = TRUE;
 		
 		(void)ext_include_binary_script_include
 			(binctx, script, location, inc_block);
@@ -327,29 +343,14 @@ static bool ext_include_binary_open
 }
 
 static bool ext_include_binary_up_to_date
-(const struct sieve_extension *ext ATTR_UNUSED, struct sieve_binary *sbin, 
-	void *context, enum sieve_compile_flags cpflags ATTR_UNUSED)
+(const struct sieve_extension *ext ATTR_UNUSED,
+	struct sieve_binary *sbin ATTR_UNUSED, void *context,
+	enum sieve_compile_flags cpflags ATTR_UNUSED)
 {
 	struct ext_include_binary_context *binctx = 
 		(struct ext_include_binary_context *) context;
-	struct hash_iterate_context *hctx;
-	void *key, *value;
-		
-	/* Check all included scripts for changes */
-	hctx = hash_table_iterate_init(binctx->included_scripts);
-	while ( hash_table_iterate(hctx, &key, &value) ) {
-		struct ext_include_script_info *incscript = 
-			(struct ext_include_script_info *) value;
-		
-		/* Is the binary old than this dependency? */
-		if ( sieve_binary_script_newer(sbin, incscript->script) ) {
-			/* No, recompile */
-			return FALSE;
-		}
-	}
-	hash_table_iterate_deinit(&hctx);
 
-	return TRUE;
+	return !binctx->outdated;
 }
 
 static void ext_include_binary_free
diff --git a/src/lib-sieve/plugins/include/ext-include-common.c b/src/lib-sieve/plugins/include/ext-include-common.c
index e9d3bc22fb3645a73d923c3a3bd70698ab1b6b55..1c2e68a27e7f752f2eab2f6ff66d8e92b28d2cbe 100644
--- a/src/lib-sieve/plugins/include/ext-include-common.c
+++ b/src/lib-sieve/plugins/include/ext-include-common.c
@@ -76,13 +76,16 @@ bool ext_include_load
 {
 	struct sieve_instance *svinst = ext->svinst;
 	struct ext_include_context *ctx;
-	const char *sieve_dir, *home;
+	const char *sieve_dir;
 	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 */
@@ -98,23 +101,10 @@ bool ext_include_load
 	/* Get directory for :personal scripts */
  	sieve_dir = sieve_setting_get(svinst, "sieve_dir");
 
-	home = sieve_environment_get_homedir(svinst);
-
 	if ( sieve_dir == NULL ) {
-		if ( home == NULL )	{
-			if ( svinst->debug ) {		
-				sieve_sys_debug(svinst, "include: sieve_dir is not set "
-					"and no home directory is set for the default `~/sieve'; "
-					"it is currently not possible to include `:personal' scripts.");
-			}
-		} else {
-			sieve_dir = "~/sieve"; 
-		}
+		sieve_dir = "~/sieve"; 
 	}
 
-	if ( home != NULL )
-		sieve_dir = home_expand_tilde(sieve_dir, home);
-
 	ctx->personal_dir = i_strdup(sieve_dir);
 
 	/* Get limits */
@@ -154,7 +144,7 @@ void ext_include_unload
  * Script access 
  */
 
-const char *ext_include_get_script_directory
+const char *ext_include_get_script_location
 (const struct sieve_extension *ext, enum ext_include_script_location location,
    const char *script_name)
 {
diff --git a/src/lib-sieve/plugins/include/ext-include-common.h b/src/lib-sieve/plugins/include/ext-include-common.h
index 71b7435726e07a0ee553d676a263a70c116155de..d62503743f79c79bda4095a49202cb6eeb72db3f 100644
--- a/src/lib-sieve/plugins/include/ext-include-common.h
+++ b/src/lib-sieve/plugins/include/ext-include-common.h
@@ -87,7 +87,7 @@ extern const struct sieve_operation_def global_operation;
  * Script access 
  */
 
-const char *ext_include_get_script_directory
+const char *ext_include_get_script_location
 	(const struct sieve_extension *ext,
 		enum ext_include_script_location location, const char *script_name);
 
diff --git a/src/lib-sieve/plugins/notify/cmd-notify.c b/src/lib-sieve/plugins/notify/cmd-notify.c
index c09644559a7472c783e95578eee5f4d4a7a361f8..35e564435bec019e505b488f1d93459ddf0dbb28 100644
--- a/src/lib-sieve/plugins/notify/cmd-notify.c
+++ b/src/lib-sieve/plugins/notify/cmd-notify.c
@@ -765,7 +765,7 @@ static bool act_notify_send
 
 		str_truncate(msg, hdr_size);
 	
-		outmsgid = sieve_message_get_new_id(senv);
+		outmsgid = sieve_message_get_new_id(aenv->svinst);
 		rfc2822_header_write(msg, "Message-ID", outmsgid);
 		rfc2822_header_write(msg, "To", recipients[i].full);
 
diff --git a/src/lib-sieve/plugins/vacation/cmd-vacation.c b/src/lib-sieve/plugins/vacation/cmd-vacation.c
index b30f18b2c1197a2905bffbc4ad7aa13e9d360fda..cd1bc141b8e3067f55dd5b1bd41deebfdf022776 100644
--- a/src/lib-sieve/plugins/vacation/cmd-vacation.c
+++ b/src/lib-sieve/plugins/vacation/cmd-vacation.c
@@ -935,7 +935,7 @@ static bool act_vacation_send
 	/* Open smtp session */
 
 	smtp_handle = sieve_smtp_open(senv, reply_to, NULL, &output);
-	outmsgid = sieve_message_get_new_id(senv);
+	outmsgid = sieve_message_get_new_id(aenv->svinst);
 
 	/* Produce a proper reply */
 
diff --git a/src/lib-sieve/sieve-actions.c b/src/lib-sieve/sieve-actions.c
index e69312a6ed7f63a527641a6c82539daab677630a..b1832238aa56fceb42e338c07c5a304621f38067 100644
--- a/src/lib-sieve/sieve-actions.c
+++ b/src/lib-sieve/sieve-actions.c
@@ -723,8 +723,7 @@ int sieve_action_duplicate_check
 	if ( senv->duplicate_check == NULL || senv->duplicate_mark == NULL)
 		return 0;
 
-	return senv->duplicate_check
-		(senv->script_context, id, id_size, senv->username); 
+	return senv->duplicate_check(senv, id, id_size); 
 }
 
 void sieve_action_duplicate_mark
@@ -734,8 +733,7 @@ void sieve_action_duplicate_mark
 	if ( senv->duplicate_check == NULL || senv->duplicate_mark == NULL)
 		return;
 
-	senv->duplicate_mark
-		(senv->script_context, id, id_size, senv->username, time);
+	senv->duplicate_mark(senv, id, id_size, time);
 }
 
 /* Rejecting the mail */
@@ -744,6 +742,7 @@ static bool sieve_action_do_reject_mail
 (const struct sieve_action_exec_env *aenv, const char *sender, 
 	const char *recipient, const char *reason)
 {
+	struct sieve_instance *svinst = aenv->svinst;
 	const struct sieve_script_env *senv = aenv->scriptenv;
 	const struct sieve_message_data *msgdata = aenv->msgdata;
 	struct istream *input;
@@ -763,8 +762,8 @@ static bool sieve_action_do_reject_mail
 
 	smtp_handle = sieve_smtp_open(senv, sender, NULL, &output);
 
-	new_msgid = sieve_message_get_new_id(senv);
-	boundary = t_strdup_printf("%s/%s", my_pid, senv->hostname);
+	new_msgid = sieve_message_get_new_id(svinst);
+	boundary = t_strdup_printf("%s/%s", my_pid, svinst->hostname);
 
   hdr = t_str_new(512);	
 	rfc2822_header_write(hdr, "X-Sieve", SIEVE_IMPLEMENTATION);
@@ -798,7 +797,7 @@ static bool sieve_action_do_reject_mail
 	rfc2822_header_write(hdr, "Content-Type", "message/disposition-notification");
 	str_append(hdr, "\r\n");
 	rfc2822_header_write(hdr, "Reporting-UA: %s; Dovecot Mail Delivery Agent",
-		senv->hostname);
+		svinst->hostname);
 	if (mail_get_first_header(msgdata->mail, "Original-Recipient", &header) > 0)
 		rfc2822_header_printf(hdr, "Original-Recipient", "rfc822; %s", header);
 	rfc2822_header_printf(hdr, "Final-Recipient", "rfc822; %s", recipient);
diff --git a/src/lib-sieve/sieve-binary-file.c b/src/lib-sieve/sieve-binary-file.c
index 16dfe0060aa523d50123d6c0b0c0d3cf3c40b689..f03bdd1cb8fbc6fd037200cf5e18cfb5ef1a003c 100644
--- a/src/lib-sieve/sieve-binary-file.c
+++ b/src/lib-sieve/sieve-binary-file.c
@@ -269,30 +269,15 @@ static bool _sieve_binary_save
 } 
 
 int sieve_binary_save
-(struct sieve_binary *sbin, const char *path, bool update, 
+(struct sieve_binary *sbin, const char *path, bool update, mode_t save_mode,
 	enum sieve_error *error_r)
 {
 	int result, fd;
 	string_t *temp_path;
 	struct ostream *stream;
-	mode_t save_mode =
-		sbin->script == NULL ? 0600 : sieve_script_permissions(sbin->script);
 
 	if ( error_r != NULL )
 		*error_r = SIEVE_ERROR_NONE;
-	
-	/* Use default path if none is specified */
-	if ( path == NULL ) {
-		if ( sbin->script == NULL ) {
-			sieve_sys_error(sbin->svinst, 
-				"binary save: cannot determine default path "
-				"with missing script object");
-			if ( error_r != NULL )
-				*error_r = SIEVE_ERROR_NOT_POSSIBLE;
-			return -1;
-		}
-		path = sieve_script_binpath(sbin->script);
-	}
 
 	/* Check whether saving is necessary */
 	if ( !update && sbin->path != NULL && strcmp(sbin->path, path) == 0 ) {
@@ -853,13 +838,11 @@ static bool _sieve_binary_open(struct sieve_binary *sbin)
 		ext_block = sieve_binary_block_get(sbin, SBIN_SYSBLOCK_EXTENSIONS);
 		if ( ext_block == NULL ) {
 			result = FALSE;
-		} else {
-			if ( !_read_extensions(ext_block) ) {
-				sieve_sys_error(sbin->svinst,
-					"binary open: binary %s is corrupt: failed to load extension block", 
-					sbin->path);
-				result = FALSE;
-			}
+		} else if ( !_read_extensions(ext_block) ) {
+			sieve_sys_error(sbin->svinst,
+				"binary open: binary %s is corrupt: failed to load extension block", 
+				sbin->path);
+			result = FALSE;
 		}
 	} T_END;
 		
diff --git a/src/lib-sieve/sieve-binary.c b/src/lib-sieve/sieve-binary.c
index 3f34efb31225e690209ea519ad0ded9ed9f40fb3..d6c730a1d55d3ea8184a9f1e8fecabb426ad101d 100644
--- a/src/lib-sieve/sieve-binary.c
+++ b/src/lib-sieve/sieve-binary.c
@@ -59,7 +59,7 @@ struct sieve_binary *sieve_binary_create
 	p_array_init(&sbin->extensions, pool, ext_count);
 	p_array_init(&sbin->extension_index, pool, ext_count);
 	
-	p_array_init(&sbin->blocks, pool, 3);
+	p_array_init(&sbin->blocks, pool, 16);
 
 	/* Pre-load core language features implemented as 'extensions' */
 	ext_preloaded = sieve_extensions_get_preloaded(svinst, &ext_count); 
@@ -76,13 +76,18 @@ struct sieve_binary *sieve_binary_create
 struct sieve_binary *sieve_binary_create_new(struct sieve_script *script) 
 {
 	struct sieve_binary *sbin = sieve_binary_create
-		(sieve_script_svinst(script), script); 
-	
-	/* Extensions block */
-	(void) sieve_binary_block_create(sbin);
+		(sieve_script_svinst(script), script);
+	struct sieve_binary_block *sblock;
+	unsigned int i;
 	
-	/* Main program block */
-	(void) sieve_binary_block_create(sbin);
+	/* Create script metadata block */
+	sblock = sieve_binary_block_create(sbin);
+	sieve_script_binary_write_metadata(script, sblock);
+
+	/* Create other system blocks */
+	for ( i = 1; i < SBIN_SYSBLOCK_LAST; i++ ) {
+		(void) sieve_binary_block_create(sbin);
+	}
 	
 	return sbin;
 }
@@ -127,6 +132,10 @@ void sieve_binary_unref(struct sieve_binary **sbin)
 	*sbin = NULL;
 }
 
+/*
+ * Accessors
+ */
+
 pool_t sieve_binary_pool(struct sieve_binary *sbin)
 {
 	return sbin->pool;
@@ -155,7 +164,7 @@ bool sieve_binary_loaded(struct sieve_binary *sbin)
 const char *sieve_binary_source(struct sieve_binary *sbin)
 {
 	if ( sbin->script != NULL && (sbin->path == NULL || sbin->file == NULL) )
-		return sieve_script_path(sbin->script);
+		return sieve_script_location(sbin->script);
 
 	return sbin->path;
 }
@@ -165,11 +174,11 @@ struct sieve_instance *sieve_binary_svinst(struct sieve_binary *sbin)
 	return sbin->svinst;
 }
 
-bool sieve_binary_script_newer
-(struct sieve_binary *sbin, struct sieve_script *script)
+time_t sieve_binary_mtime
+(struct sieve_binary *sbin)
 {
 	i_assert(sbin->file != NULL);
-	return ( sieve_script_newer(script, sbin->file->st.st_mtime) );
+	return sbin->file->st.st_mtime;
 }
 
 const char *sieve_binary_script_name(struct sieve_binary *sbin)
@@ -177,9 +186,18 @@ const char *sieve_binary_script_name(struct sieve_binary *sbin)
 	return ( sbin->script == NULL ? NULL : sieve_script_name(sbin->script) );
 }
 
-const char *sieve_binary_script_path(struct sieve_binary *sbin)
+const char *sieve_binary_script_location(struct sieve_binary *sbin)
+{
+	return ( sbin->script == NULL ? NULL : sieve_script_location(sbin->script) );
+}
+
+/*
+ * Utility
+ */
+
+const char *sieve_binfile_from_name(const char *name)
 {
-	return ( sbin->script == NULL ? NULL : sieve_script_path(sbin->script) );
+	return t_strconcat(name, "."SIEVE_BINARY_FILEEXT, NULL);
 }
 
 /* 
@@ -294,12 +312,15 @@ bool sieve_binary_up_to_date
 (struct sieve_binary *sbin, enum sieve_compile_flags cpflags)
 {
 	struct sieve_binary_extension_reg *const *regs;
+	struct sieve_binary_block *sblock;
+	sieve_size_t offset = 0;
 	unsigned int ext_count, i;
 	
 	i_assert(sbin->file != NULL);
 
-	if ( sbin->script == NULL || sieve_script_newer
-		(sbin->script, sbin->file->st.st_mtime) )
+	sblock = sieve_binary_block_get(sbin, SBIN_SYSBLOCK_SCRIPT_DATA);
+	if ( sblock == NULL || sbin->script == NULL ||
+		sieve_script_binary_read_metadata(sbin->script, sblock, &offset) <= 0 )
 		return FALSE;
 	
 	regs = array_get(&sbin->extensions, &ext_count);	
diff --git a/src/lib-sieve/sieve-binary.h b/src/lib-sieve/sieve-binary.h
index b675013aed9fb3d4ba254972e5be248e3d6bf485..20d12cf894270b3a7ed13e5cfeb399a622965167 100644
--- a/src/lib-sieve/sieve-binary.h
+++ b/src/lib-sieve/sieve-binary.h
@@ -13,7 +13,7 @@
  */
  
 #define SIEVE_BINARY_VERSION_MAJOR     0
-#define SIEVE_BINARY_VERSION_MINOR     3
+#define SIEVE_BINARY_VERSION_MINOR     4
 
 /*
  * Binary object
@@ -34,15 +34,20 @@ struct sieve_instance *sieve_binary_svinst(struct sieve_binary *sbin);
 const char *sieve_binary_path(struct sieve_binary *sbin);
 struct sieve_script *sieve_binary_script(struct sieve_binary *sbin);
 
-bool sieve_binary_script_newer
-	(struct sieve_binary *sbin, struct sieve_script *script);
+time_t sieve_binary_mtime(struct sieve_binary *sbin);
 const char *sieve_binary_script_name(struct sieve_binary *sbin);
-const char *sieve_binary_script_path(struct sieve_binary *sbin);
+const char *sieve_binary_script_location(struct sieve_binary *sbin);
 
 const char *sieve_binary_source(struct sieve_binary *sbin);
 bool sieve_binary_loaded(struct sieve_binary *sbin);
 bool sieve_binary_saved(struct sieve_binary *sbin);
 
+/*
+ * Utility
+ */
+
+const char *sieve_binfile_from_name(const char *name);
+
 /*
  * Activation after code generation
  */ 
@@ -54,8 +59,8 @@ void sieve_binary_activate(struct sieve_binary *sbin);
  */
  
 int sieve_binary_save
-(struct sieve_binary *sbin, const char *path, bool update,
-	enum sieve_error *error_r);
+	(struct sieve_binary *sbin, const char *path, bool update, mode_t save_mode,
+		enum sieve_error *error_r);
 	
 /* 
  * Loading the binary
@@ -72,6 +77,7 @@ bool sieve_binary_up_to_date
  */
  
 enum sieve_binary_system_block {
+	SBIN_SYSBLOCK_SCRIPT_DATA,
 	SBIN_SYSBLOCK_EXTENSIONS,
 	SBIN_SYSBLOCK_MAIN_PROGRAM,
 	SBIN_SYSBLOCK_LAST
diff --git a/src/lib-sieve/sieve-common.h b/src/lib-sieve/sieve-common.h
index f7f9f500adfb3389a74dff3c60c7eafefd5c8a52..92ad7b9069cbc2ab5537bf3cc94e37fe615b4a51 100644
--- a/src/lib-sieve/sieve-common.h
+++ b/src/lib-sieve/sieve-common.h
@@ -155,8 +155,19 @@ struct sieve_instance {
 	/* Main engine pool */
 	pool_t pool;
 
+	/* System environment */
+	const char *hostname;
+	const char *base_dir;
+
+	/* User environment */
+	const char *username;
+	const char *home_dir;
+
+	/* Flags */
+	enum sieve_flag flags;
+
 	/* Callbacks */
-	const struct sieve_environment *env;
+	const struct sieve_callbacks *callbacks;
 	void *context;
 
 	/* Engine debug */
diff --git a/src/lib-sieve/sieve-message.c b/src/lib-sieve/sieve-message.c
index 3d7e90ccbb6f528ec6a1bcb8ae977bf6eb53757c..242611dc775344c1ff7d2f8048bd432ad8e40ec2 100644
--- a/src/lib-sieve/sieve-message.c
+++ b/src/lib-sieve/sieve-message.c
@@ -30,14 +30,13 @@
  * Message transmission
  */
  
-const char *sieve_message_get_new_id
-(const struct sieve_script_env *senv)
+const char *sieve_message_get_new_id(const struct sieve_instance *svinst)
 {
 	static int count = 0;
 	
 	return t_strdup_printf("<dovecot-sieve-%s-%s-%d@%s>",
 		dec2str(ioloop_timeval.tv_sec), dec2str(ioloop_timeval.tv_usec),
-    count++, senv->hostname);
+    count++, svinst->hostname);
 }
 
 /* 
diff --git a/src/lib-sieve/sieve-message.h b/src/lib-sieve/sieve-message.h
index deb6063d83790d3effd43278f7540cf55e7c23b1..4fc1549b6bd424c0dd1e32dc2af73e7b19d71314 100644
--- a/src/lib-sieve/sieve-message.h
+++ b/src/lib-sieve/sieve-message.h
@@ -8,8 +8,7 @@
  * Message transmission
  */
 
-const char *sieve_message_get_new_id
-	(const struct sieve_script_env *senv);
+const char *sieve_message_get_new_id(const struct sieve_instance *svinst);
 
 /* 
  * Message context 
diff --git a/src/lib-sieve/sieve-script-dict.c b/src/lib-sieve/sieve-script-dict.c
new file mode 100644
index 0000000000000000000000000000000000000000..d761155ac04aff8a082f9484e9e3afc899f33ae5
--- /dev/null
+++ b/src/lib-sieve/sieve-script-dict.c
@@ -0,0 +1,285 @@
+/* Copyright (c) 2002-2012 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-script-private.h"
+
+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;
+};
+
+#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"
+
+/*
+ * Script file implementation
+ */
+
+static struct sieve_script *sieve_dict_script_alloc(void)
+{
+	struct sieve_dict_script *script;
+	pool_t pool;
+
+	pool = pool_alloconly_create("sieve_dict_script", 1024);
+	script = p_new(pool, struct sieve_dict_script, 1);
+	script->script = sieve_dict_script; 
+	script->script.pool = pool;
+	
+	return &script->script;
+}
+
+static int sieve_dict_script_create
+(struct sieve_script *_script, const char *data, const char *const *options,
+	enum sieve_error *error_r)
+{
+	struct sieve_dict_script *script = (struct sieve_dict_script *)_script;
+	struct sieve_instance *svinst = _script->svinst;
+	struct sieve_error_handler *ehandler = _script->ehandler;
+	const char *username = NULL, *name = _script->name;
+	const char *path;
+	int ret;
+
+	if ( options != NULL ) {
+		while ( *options != NULL ) {
+			const char *option = *options;
+
+			if ( strncasecmp(option, "user=", 5) == 0 && option[5] != '\0' ) {
+				username = option+5;
+			} else {
+				sieve_critical(svinst, ehandler, NULL, "failed to open sieve script",
+					"sieve dict backend: invalid option `%s'", option);
+				*error_r = SIEVE_ERROR_TEMP_FAIL;
+				return -1;
+			}
+
+			options++;
+		}
+	}
+
+	if ( name == NULL ) {
+		name = _script->name = SIEVE_DICT_SCRIPT_DEFAULT;
+	}
+
+	if ( username == NULL ) {
+		if ( svinst->username == NULL ) {
+			sieve_critical(svinst, ehandler, name, "failed to open sieve script",
+				"sieve dict backend: no username specified");
+			*error_r = SIEVE_ERROR_TEMP_FAIL;
+			return -1;
+		}
+		username = svinst->username;
+	}
+
+	if ( svinst->base_dir == NULL ) {
+		sieve_critical(svinst, ehandler, name, "failed to open sieve script",
+			"sieve dict backend: BUG: Sieve interpreter is initialized without "
+			"a base_dir");
+		*error_r = SIEVE_ERROR_TEMP_FAIL;
+		return -1;
+	}
+
+	if ( _script->svinst->debug ) {
+		sieve_sys_debug(_script->svinst, "sieve dict backend: "
+			"user=%s, uri=%s, script=%s", username, data, name);
+	}
+
+	script->dict = dict_init
+		(data, DICT_DATA_TYPE_STRING, username, svinst->base_dir);
+	if ( script->dict == NULL ) {
+		sieve_critical(svinst, ehandler, name, "failed to open sieve script",
+			"sieve dict backend: failed to initialize dict with data `%s' "
+			"for user `%s'", data, username);
+		*error_r = SIEVE_ERROR_TEMP_FAIL;
+		dict_deinit(&script->dict);
+		return -1;
+	}
+
+	path = t_strconcat
+		(DICT_SIEVE_NAME_PATH, dict_escape_string(name), NULL);
+	
+	ret = dict_lookup
+		(script->dict, script->script.pool, path, &script->data_id);
+	if ( ret <= 0 ) {
+		if ( ret < 0 ) {
+			sieve_critical(svinst, ehandler, name, "failed to open sieve script", 
+				"sieve dict backend: failed to lookup script id from path %s", path);
+			*error_r = SIEVE_ERROR_TEMP_FAIL;
+		} else {
+			if ( svinst->debug ) {
+				sieve_sys_debug(svinst, "sieve dict backend: "
+					"script `%s' not found at path %s", name, path);
+			}
+			*error_r = SIEVE_ERROR_NOT_FOUND;
+		}
+
+		dict_deinit(&script->dict);
+		return -1;
+	}
+
+	if ( _script->bin_dir != NULL ) {
+		script->binpath = p_strconcat(_script->pool, _script->bin_dir, "/",
+			sieve_binfile_from_name(name), NULL);
+	}
+
+	if ( strcmp(name, SIEVE_DICT_SCRIPT_DEFAULT) == 0 ) {
+		_script->location = p_strconcat(_script->pool,
+			SIEVE_DICT_SCRIPT_DRIVER_NAME, ":", data, NULL);
+	} else {
+		_script->location = p_strconcat(_script->pool,
+			SIEVE_DICT_SCRIPT_DRIVER_NAME, ":", data, ";name=", name, NULL);
+	}
+
+	return 0;
+}
+
+static void sieve_dict_script_destroy(struct sieve_script *_script)
+{
+	struct sieve_dict_script *script = (struct sieve_dict_script *)_script;
+
+	if ( script->dict != NULL )
+		dict_deinit(&script->dict);
+
+	if ( script->data_pool != NULL )
+		pool_unref(&script->data_pool);
+}
+
+static struct istream *sieve_dict_script_open
+(struct sieve_script *_script, enum sieve_error *error_r)
+{
+	struct sieve_dict_script *script = (struct sieve_dict_script *)_script;
+	struct sieve_instance *svinst = _script->svinst;
+	struct sieve_error_handler *ehandler = _script->ehandler;
+	const char *path, *name = _script->name;
+	int ret;
+
+	script->data_pool =
+		pool_alloconly_create("sieve_dict_script data pool", 1024);
+	
+	path = t_strconcat
+		(DICT_SIEVE_DATA_PATH, dict_escape_string(script->data_id), NULL);
+
+	ret = dict_lookup
+		(script->dict, script->data_pool, path, &script->data);
+	if ( ret <= 0 ) {
+		if ( ret < 0 ) {
+			sieve_critical(svinst, ehandler, name, "failed to open sieve script", 
+				"sieve dict backend: failed to lookup data with id `%s' "
+				"for script `%s' from path %s",	script->data_id, name,
+				path);
+		} else {
+			sieve_critical(svinst, ehandler, name, "failed to open sieve script", 
+				"sieve dict backend: data with id `%s' for script `%s' "
+				"not found at path %s",	script->data_id, name, path);
+		}
+		*error_r = SIEVE_ERROR_TEMP_FAIL;
+		return NULL;
+	}
+
+	return i_stream_create_from_data(script->data, strlen(script->data));
+}
+
+static void sieve_dict_script_close(struct sieve_script *_script)
+{
+	struct sieve_dict_script *script = (struct sieve_dict_script *)_script;
+
+	pool_unref(&script->data_pool);
+
+	script->data_pool = NULL;
+	script->data_id = NULL;
+	script->data = NULL;
+}
+
+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 *script = (struct sieve_dict_script *)_script;
+	struct sieve_instance *svinst = _script->svinst;
+	struct sieve_binary *sbin = sieve_binary_block_get_binary(sblock);
+	string_t *data_id;
+
+	if ( !sieve_binary_read_string(sblock, offset, &data_id) ) {
+		sieve_sys_error(svinst,
+			"sieve dict script: binary %s has invalid metadata for script %s",
+			sieve_binary_path(sbin), sieve_script_location(_script));
+		return -1;
+	}
+
+	if ( strcmp(str_c(data_id), script->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 *script = (struct sieve_dict_script *)_script;
+
+	sieve_binary_emit_cstring(sblock, script->data_id);
+}
+
+static struct sieve_binary *sieve_dict_script_binary_load
+(struct sieve_script *_script, enum sieve_error *error_r)
+{
+	struct sieve_dict_script *script = (struct sieve_dict_script *)_script;
+
+	if (script->binpath == NULL)
+		return NULL;
+
+	return sieve_binary_open(_script->svinst, script->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 *script = (struct sieve_dict_script *)_script;
+
+	if (script->binpath == NULL)
+		return 0;
+
+	if ( sieve_script_setup_bindir(_script, 0700) < 0 )
+		return -1;
+
+	return sieve_binary_save(sbin, script->binpath, update, 0600, error_r);
+}
+
+const struct sieve_script sieve_dict_script = {
+	.driver_name = SIEVE_DICT_SCRIPT_DRIVER_NAME,
+	.v = {
+		sieve_dict_script_alloc,
+		sieve_dict_script_create,
+		sieve_dict_script_destroy,
+
+		sieve_dict_script_open,
+		sieve_dict_script_close,	
+
+		sieve_dict_script_binary_read_metadata,
+		sieve_dict_script_binary_write_metadata,
+		sieve_dict_script_binary_load,
+		sieve_dict_script_binary_save,
+
+		NULL, NULL
+	}
+};
diff --git a/src/lib-sieve/sieve-script-file.c b/src/lib-sieve/sieve-script-file.c
new file mode 100644
index 0000000000000000000000000000000000000000..1ba30ef22f6f84e49ce39e807f5bfe0504b21ca6
--- /dev/null
+++ b/src/lib-sieve/sieve-script-file.c
@@ -0,0 +1,397 @@
+/* Copyright (c) 2002-2012 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "strfuncs.h"
+#include "abspath.h"
+#include "eacces-error.h"
+#include "istream.h"
+#include "home-expand.h"
+
+#include "sieve-common.h"
+#include "sieve-settings.h"
+#include "sieve-error.h"
+#include "sieve-binary.h"
+#include "sieve-script-private.h"
+#include "sieve-script-file.h"
+
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+/*
+ * Configuration
+ */
+ 
+#define SIEVE_FILE_READ_BLOCK_SIZE (1024*8)
+
+/*
+ * Filename to name/name to filename
+ */
+
+const char *sieve_scriptfile_get_script_name(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_scriptfile_has_extension(const char *filename)
+{
+	return ( sieve_scriptfile_get_script_name(filename) != NULL );
+}
+
+const char *sieve_scriptfile_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_script *script, const char *path, const char *name,
+	enum sieve_error *error_r)
+{
+	struct sieve_instance *svinst = script->svinst;
+	struct sieve_error_handler *ehandler = script->ehandler;
+
+	switch ( errno ) {
+	case ENOENT:
+		if ( svinst->debug )
+			sieve_sys_debug(svinst, "script file %s not found", t_abspath(path));
+		*error_r = SIEVE_ERROR_NOT_FOUND;
+		break;
+	case EACCES:
+		sieve_critical(svinst, ehandler, name, "failed to open sieve script", 
+			"failed to stat sieve script: %s", eacces_error_get("stat", path));
+		*error_r = SIEVE_ERROR_NO_PERM;
+		break;
+	default:
+		sieve_critical(svinst, ehandler, name, "failed to open sieve script", 
+			"failed to stat sieve script: stat(%s) failed: %m", path);
+		*error_r = SIEVE_ERROR_TEMP_FAIL;
+		break;
+	}
+}
+
+/*
+ * Script file implementation
+ */
+
+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 struct sieve_script *sieve_file_script_alloc(void)
+{
+	struct sieve_file_script *script;
+	pool_t pool;
+
+	pool = pool_alloconly_create("sieve_file_script", 1024);
+	script = p_new(pool, struct sieve_file_script, 1);
+	script->script = sieve_file_script; 
+	script->script.pool = pool;
+	
+	return &script->script;
+}
+
+static int sieve_file_script_create
+(struct sieve_script *_script, const char *path, const char *const *options,
+	enum sieve_error *error_r)
+{
+	struct sieve_file_script *script = (struct sieve_file_script *)_script;
+	struct sieve_instance *svinst = _script->svinst;
+	struct sieve_error_handler *ehandler = _script->ehandler;
+	pool_t pool = _script->pool;
+	const char *name = _script->name;
+	const char *filename, *dirpath, *basename, *binpath;
+	struct stat st;
+	struct stat lnk_st;
+	bool success = TRUE;
+	int ret;
+
+	if ( options != NULL && *options != NULL ) {
+		const char *option = *options;
+
+		sieve_critical(svinst, ehandler, NULL, "failed to open sieve script",
+			"sieve file backend: invalid option `%s'", option);
+		*error_r = SIEVE_ERROR_TEMP_FAIL;
+		return -1;
+	}
+
+	T_BEGIN {
+		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_critical(svinst, ehandler, NULL, 
+					"failed to open sieve script",
+					"sieve script file path %s is relative to home directory, "
+					"but home directory is not available.", path);
+				*error_r = SIEVE_ERROR_TEMP_FAIL;
+				success = FALSE;
+			}
+		}
+
+		if ( success && (ret=sieve_file_script_stat(path, &st, &lnk_st)) > 0 ) {
+			if ( S_ISDIR(st.st_mode) ) {
+				/* Path is directory; name is used to find actual file */
+				if (name == 0 || *name == '\0') {
+					sieve_critical(svinst, ehandler, NULL, 
+						"failed to open sieve script",
+						"sieve script file path '%s' is a directory.", path);
+					*error_r = SIEVE_ERROR_TEMP_FAIL;
+					success = FALSE;
+				}	else {
+					/* Extend path with filename */	
+					filename = sieve_scriptfile_from_name(name);
+					basename = name;
+					dirpath = path;
+
+					if ( path[strlen(path)-1] == '/' )
+						path = t_strconcat(dirpath, filename, NULL);
+					else
+						path = t_strconcat(dirpath, "/", filename , NULL);
+			
+					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_scriptfile_get_script_name(filename)) == NULL )
+					basename = filename;
+
+				if ( name == NULL )
+					name = basename;
+			}
+		} else {
+			basename = name;
+		}
+			
+		if ( success ) {
+			if ( ret <= 0 ) {
+				sieve_file_script_handle_error(_script, path, name, error_r);
+				success = FALSE;
+			} else if (!S_ISREG(st.st_mode) ) {
+				sieve_critical(svinst, ehandler, name, 
+					"failed to open sieve script",
+					"sieve script file '%s' is not a regular file.", path);
+				*error_r = SIEVE_ERROR_TEMP_FAIL;
+				success = FALSE;
+			} 
+		}
+
+		if ( success ) {
+			if ( _script->bin_dir != NULL ) {
+				binpath = sieve_binfile_from_name(name);
+				binpath =	t_strconcat(_script->bin_dir, "/", binpath, NULL);
+			} else {
+				binpath = sieve_binfile_from_name(basename);
+				if ( *dirpath != '\0' )
+					binpath = t_strconcat(dirpath, "/", binpath, NULL);
+			}
+
+			script->st = st;
+			script->lnk_st = lnk_st;
+			script->path = p_strdup(pool, path);
+			script->filename = p_strdup(pool, filename);
+			script->dirpath = p_strdup(pool, dirpath);
+			script->binpath = p_strdup(pool, binpath);
+			
+			if ( script->script.name == NULL ||
+				strcmp(script->script.name, basename) == 0 )
+				script->script.location = script->path;
+			else
+				script->script.location = p_strconcat
+					(pool, script->path, ";name=", script->script.name, NULL);
+
+			if ( script->script.name == NULL )
+				script->script.name = p_strdup(pool, basename);
+		}
+	} T_END;	
+
+	return ( success ? 0 : -1 );
+}
+
+static struct istream *sieve_file_script_open
+(struct sieve_script *_script, enum sieve_error *error_r)
+{
+	struct sieve_file_script *script = (struct sieve_file_script *)_script;
+	struct sieve_instance *svinst = _script->svinst;
+	struct sieve_error_handler *ehandler = _script->ehandler;
+	const char *name = _script->name;
+	struct stat st;
+	struct istream *result;
+	int fd;
+
+	if ( (fd=open(script->path, O_RDONLY)) < 0 ) {
+		sieve_file_script_handle_error(_script, script->path, name, error_r);
+		return NULL;
+	}	
+	
+	if ( fstat(fd, &st) != 0 ) {
+		sieve_critical(svinst, ehandler, name,
+			"failed to open sieve script",
+			"failed to open sieve script: fstat(fd=%s) failed: %m", script->path);
+		*error_r = SIEVE_ERROR_TEMP_FAIL;
+		result = NULL;
+	} else {
+		/* Re-check the file type just to be sure */
+		if ( !S_ISREG(st.st_mode) ) {
+			sieve_critical(svinst, ehandler, name,
+				"failed to open sieve script",
+				"sieve script file '%s' is not a regular file", script->path);
+			*error_r = SIEVE_ERROR_TEMP_FAIL;
+			result = NULL;
+		} else {
+			result = i_stream_create_fd(fd, SIEVE_FILE_READ_BLOCK_SIZE, TRUE);
+			script->st = script->lnk_st = st;
+		}
+	}
+
+	if ( result == NULL ) {
+		/* Something went wrong, close the fd */
+		if ( close(fd) != 0 ) {
+			sieve_sys_error(svinst, 
+				"failed to close sieve script: close(fd=%s) failed: %m", script->path);
+		}
+	}
+	
+	return result;
+}
+
+static int sieve_file_script_get_size
+(const struct sieve_script *_script, uoff_t *size_r)
+{
+	struct sieve_file_script *script = (struct sieve_file_script *)_script;
+
+	*size_r = script->st.st_size;
+	return 1;
+}
+
+static bool sieve_file_script_equals
+(const struct sieve_script *_script, const struct sieve_script *_other)
+{
+	struct sieve_file_script *script = (struct sieve_file_script *)_script;
+	struct sieve_file_script *other = (struct sieve_file_script *)_other;
+
+	if ( script == NULL || other == NULL ) 
+		return -1;	
+
+	return ( script->st.st_ino == other->st.st_ino );
+}
+
+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 *script = (struct sieve_file_script *)_script;
+	struct sieve_binary *sbin = sieve_binary_block_get_binary(sblock);
+	time_t time = ( script->st.st_mtime > script->lnk_st.st_mtime ?
+		script->st.st_mtime : script->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 *script = (struct sieve_file_script *)_script;
+
+	return sieve_binary_open(_script->svinst, script->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_file_script *script = (struct sieve_file_script *)_script;
+
+	if ( sieve_script_setup_bindir(_script, 0700) < 0 )
+		return -1;
+
+	return sieve_binary_save(sbin, script->binpath, update,
+		script->st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO), error_r);
+}
+
+const struct sieve_script sieve_file_script = {
+	.driver_name = SIEVE_FILE_SCRIPT_DRIVER_NAME,
+	.v = {
+		sieve_file_script_alloc,
+		sieve_file_script_create,
+		NULL,
+
+		sieve_file_script_open,
+		NULL,	
+
+		sieve_file_script_binary_read_metadata,
+		NULL,
+		sieve_file_script_binary_load,
+		sieve_file_script_binary_save,
+
+		sieve_file_script_get_size,
+
+		sieve_file_script_equals
+	}
+};
+
+const char *sieve_file_script_get_dirpath
+(const struct sieve_script *_script)
+{
+	struct sieve_file_script *script = (struct sieve_file_script *)_script;
+
+	if ( _script->driver_name != sieve_file_script.driver_name )
+		return NULL;
+
+	return script->dirpath;
+}
+
+const char *sieve_file_script_get_path
+(const struct sieve_script *_script)
+{
+	struct sieve_file_script *script = (struct sieve_file_script *)_script;
+
+	if ( _script->driver_name != sieve_file_script.driver_name )
+		return NULL;
+
+	return script->path;
+}
+
diff --git a/src/lib-sieve/sieve-script-file.h b/src/lib-sieve/sieve-script-file.h
new file mode 100644
index 0000000000000000000000000000000000000000..c71fa87eeffd66271397c4d69f8acd0e944cef42
--- /dev/null
+++ b/src/lib-sieve/sieve-script-file.h
@@ -0,0 +1,31 @@
+/* Copyright (c) 2002-2012 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 970484831f5ebdf90877a20bc671c7dd81af93f5..49e1ca3720839a002036d291e072f360fa995c07 100644
--- a/src/lib-sieve/sieve-script-private.h
+++ b/src/lib-sieve/sieve-script-private.h
@@ -10,34 +10,93 @@
  * Script object
  */
 
+struct sieve_script_vfuncs {
+	struct sieve_script *(*alloc)(void);
+	int (*create)
+		(struct sieve_script *script, const char *data, const char *const *options,
+			enum sieve_error *error_r);
+	void (*destroy)
+		(struct sieve_script *script);
+
+	struct istream *(*open)
+		(struct sieve_script *script, enum sieve_error *error_r);
+	void (*close)
+		(struct sieve_script *script);
+
+	int (*binary_read_metadata)
+		(struct sieve_script *_script, struct sieve_binary_block *sblock,
+			sieve_size_t *offset);
+	void (*binary_write_metadata)
+		(struct sieve_script *script, struct sieve_binary_block *sblock);
+	struct sieve_binary *(*binary_load)
+		(struct sieve_script *script, enum sieve_error *error_r);
+	int (*binary_save)
+		(struct sieve_script *script, struct sieve_binary *sbin,	
+			bool update, enum sieve_error *error_r);
+
+	int (*get_size)
+		(const struct sieve_script *script, uoff_t *size_r);
+
+	bool (*equals)
+		(const struct sieve_script *script, const struct sieve_script *other);
+};
+
 struct sieve_script {
 	pool_t pool;
 	unsigned int refcount;
 
+	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 *location;
+	const char *bin_dir;
+
+	/* Stream */
+	struct istream *stream;
+};
+
+struct sieve_script *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,
+		enum sieve_error *error_r);
+
+int sieve_script_setup_bindir
+	(struct sieve_script *script, mode_t mode);
+
+/*
+ * Built-in file script driver
+ */
+
+#define SIEVE_FILE_SCRIPT_DRIVER_NAME "file"
+
+struct sieve_file_script {
+	struct sieve_script script;
 
 	struct stat st;
 	struct stat lnk_st;
-	time_t mtime;
 
-	struct sieve_error_handler *ehandler;
-
-	/* Parameters */
-	const char *name;
-	const char *basename;
-	const char *filename;
 	const char *path;
 	const char *dirpath;
+	const char *filename;
 	const char *binpath;
 
-	/* Stream */
-	int fd; /* FIXME: we could use the stream's autoclose facility */
-	struct istream *stream;
+	int fd;
 };
 
-struct sieve_script *sieve_script_init
-(struct sieve_script *script, struct sieve_instance *svinst,
-	const char *path, const char *name, struct sieve_error_handler *ehandler, 
-	enum sieve_error *error_r);
+extern const struct sieve_script sieve_file_script;
+
+/*
+ * Built-in dict script driver
+ */
+
+#define SIEVE_DICT_SCRIPT_DRIVER_NAME "dict"
+
+extern const struct sieve_script sieve_dict_script;
 
 #endif /* __SIEVE_SCRIPT_PRIVATE_H */
diff --git a/src/lib-sieve/sieve-script.c b/src/lib-sieve/sieve-script.c
index a529165165a7b71c8ae0b4386b7e699384f67023..b5017d604b840a1d820d34bcf0ec5998470b8b49 100644
--- a/src/lib-sieve/sieve-script.c
+++ b/src/lib-sieve/sieve-script.c
@@ -4,26 +4,22 @@
 #include "lib.h"
 #include "compat.h"
 #include "unichar.h"
+#include "str.h"
+#include "hash.h"
 #include "array.h"
-#include "abspath.h"
-#include "istream.h"
+#include "home-expand.h"
+#include "mkdir-parents.h"
 #include "eacces-error.h"
+#include "istream.h"
 
 #include "sieve-common.h"
 #include "sieve-limits.h"
+#include "sieve-settings.h"
 #include "sieve-error.h"
+#include "sieve-binary.h"
 
 #include "sieve-script-private.h"
-
-#include <unistd.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-/*
- * Configuration
- */
- 
-#define SIEVE_READ_BLOCK_SIZE (1024*8)
+#include "sieve-script-file.h"
 
 /*
  * Script name
@@ -92,201 +88,202 @@ bool sieve_script_name_is_valid(const char *scriptname)
 	return TRUE;
 }
 
-/*
- * Filename to name/name to filename
+/* 
+ * Script object 
  */
 
-const char *sieve_scriptfile_get_script_name(const char *filename)
+static const char *split_next_arg(const char *const **_args)
 {
-	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);
-}
+	const char *const *args = *_args;
+	const char *str = args[0];
 
-bool sieve_scriptfile_has_extension(const char *filename)
-{
-	return ( sieve_scriptfile_get_script_name(filename) != NULL );
-}
+	/* join arguments for escaped ";" separator */
 
-const char *sieve_scriptfile_from_name(const char *name)
-{
-	return t_strconcat(name, "."SIEVE_SCRIPT_FILEEXT, NULL);
+	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;
 }
 
-const char *sieve_binfile_from_name(const char *name)
+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)
 {
-	return t_strconcat(name, "."SIEVE_BINARY_FILEEXT, NULL);
-}
+	ARRAY_TYPE(const_string) options;
+	const char *const *tmp;
 
+	if (*data == '\0') {
+		*options_r = NULL;
+		*location_r = data;		
+		return TRUE;
+	}
 
-/*
- * Common error handling
- */
+	/* <location> */
+	tmp = t_strsplit(data, ";");
+	*location_r = split_next_arg(&tmp);
 
-static void sieve_script_handle_file_error
-(struct sieve_instance *svinst, const char *path, const char *name,
-	struct sieve_error_handler *ehandler, enum sieve_error *error_r)
-{
-	switch ( errno ) {
-	case ENOENT:
-		if ( svinst->debug )
-			sieve_sys_debug(svinst, "script file %s not found", t_abspath(path));
-		if ( error_r == NULL )
-			sieve_error(ehandler, name, "sieve script does not exist");
-		else {
-			*error_r = SIEVE_ERROR_NOT_FOUND;
+	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);
+			}
 		}
-		break;
-	case EACCES:
-		sieve_critical(svinst, ehandler, name, "failed to open sieve script", 
-			"failed to stat sieve script: %s", eacces_error_get("stat", path));
-		if ( error_r != NULL )
-			*error_r = SIEVE_ERROR_NO_PERM;
-		break;
-	default:
-		sieve_critical(svinst, ehandler, name, "failed to open sieve script", 
-			"failed to stat sieve script: stat(%s) failed: %m", path);
-		if ( error_r != NULL )
-			*error_r = SIEVE_ERROR_TEMP_FAIL;
-		break;
+
+		(void)array_append_space(&options);
+		*options_r = array_idx(&options, 0);
 	}
+
+	return TRUE;
 }
 
-/* 
- * Script object 
- */
- 
 struct sieve_script *sieve_script_init
-(struct sieve_script *script, struct sieve_instance *svinst, 
-	const char *path, const char *name, struct sieve_error_handler *ehandler, 
-	enum sieve_error *error_r)
+(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, enum sieve_error *error_r)
 {
-	int ret;
-	pool_t pool;
-	struct stat st;
-	struct stat lnk_st;
-	const char *filename, *dirpath, *basename, *binpath;
+	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;
 
-	T_BEGIN {
-
-		/* Extract filename from path */
+	script->script_class = script_class;
+	script->refcount = 1;
+	script->svinst = svinst;
 
-		filename = strrchr(path, '/');
-		if ( filename == NULL ) {
-			dirpath = "";
-			filename = path;
-		} else {
-			dirpath = t_strdup_until(path, filename);
-			filename++;
-		}
+	script->ehandler = ehandler;
+	
+	script->name = p_strdup_empty(script->pool, name);
+	
+	if ( !sieve_script_location_parse
+		(script, data, &location, &options, &parse_error) ) {
+		sieve_critical(svinst, ehandler, NULL,
+			"failed to access sieve script", "failed to parse script location: %s",
+			parse_error);
+		*error_r = SIEVE_ERROR_TEMP_FAIL;
+		return NULL;
+	}
 
-		if ( (basename=sieve_scriptfile_get_script_name(filename)) == NULL )
-			basename = filename;
-
-		binpath = sieve_binfile_from_name(basename);
-		if ( *dirpath != '\0' )
-			binpath = t_strconcat(dirpath, "/", binpath, NULL);
-				
-		if ( name == NULL ) {
-			name = basename; 
-		} else if ( *name == '\0' ) {
-			name = NULL;
-		} else {
-			basename = name;
-		}
-			
-		/* First obtain stat data from the system */
-		
-		if ( (ret=lstat(path, &st)) < 0 ) {
-			sieve_script_handle_file_error(svinst, path, basename, ehandler, error_r);
-			script = NULL;
-			ret = 1;
+	if ( script->v.create(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 {
-			/* Record stat information from the symlink */
-			lnk_st = st;
-
-			/* Only create/init the object if it stat()s without problems */
-			if ( S_ISLNK(st.st_mode) && (ret=stat(path, &st)) < 0 ) { 
-				sieve_script_handle_file_error
-					(svinst, path, basename, ehandler, error_r);
-				script = NULL;	
-				ret = 1;
-			}
-
-			if ( ret == 0 && !S_ISREG(st.st_mode) ) {
-				sieve_critical(svinst, ehandler, basename, 
-					"failed to open sieve script",
-					"sieve script file '%s' is not a regular file.", path);
-				if ( error_r != NULL )
-					*error_r = SIEVE_ERROR_NOT_POSSIBLE;
-				script = NULL;
-				ret = 1;
-			} 
+			*error_r = error;
 		}
+		return NULL;
+	}
 
-		if ( ret <= 0 ) {
-			if ( script == NULL ) {
-				pool = pool_alloconly_create("sieve_script", 1024);
-				script = p_new(pool, struct sieve_script, 1);
-				script->pool = pool;
-			} else 
-				pool = script->pool;
-		
-			script->refcount = 1;
-			script->svinst = svinst;
-
-			script->ehandler = ehandler;
-			sieve_error_handler_ref(ehandler);
-		
-			script->st = st;
-			script->lnk_st = lnk_st;
-			script->path = p_strdup(pool, path);
-			script->filename = p_strdup(pool, filename);
-			script->dirpath = p_strdup(pool, dirpath);
-			script->binpath = p_strdup(pool, binpath);
-			script->basename = p_strdup(pool, basename);
-
-			if ( name != NULL )
-				script->name = p_strdup(pool, name);
-			else
-				script->name = NULL;
-		}
-	} T_END;	
+	i_assert( script->location != NULL );
 
+	sieve_error_handler_ref(ehandler);
 	return script;
 }
 
 struct sieve_script *sieve_script_create
-(struct sieve_instance *svinst, const char *path, const char *name, 
+(struct sieve_instance *svinst, const char *location, const char *name, 
 	struct sieve_error_handler *ehandler, enum sieve_error *error_r)
 {
-	return sieve_script_init(NULL, svinst, path, name, ehandler, error_r);
+	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 )
+				i_error("Unknown sieve script driver module: %s", driver);
+		} T_END;
+	}
+
+	script = script_class->v.alloc();
+	if ( sieve_script_init
+		(script, svinst, script_class, data, name, ehandler, error_r) == NULL ) {
+		pool_unref(&script->pool);
+		return NULL;
+	}
+
+	return script;
 }
 
-struct sieve_script *sieve_script_create_in_directory
-(struct sieve_instance *svinst, const char *dirpath, const char *name,
+struct sieve_script *sieve_script_create_as
+(struct sieve_instance *svinst, const char *location, const char *name, 
 	struct sieve_error_handler *ehandler, enum sieve_error *error_r)
 {
-	const char *path;
+	struct sieve_script *script;
 
-	if ( dirpath[strlen(dirpath)-1] == '/' )
-		path = t_strconcat(dirpath, 
-			sieve_scriptfile_from_name(name), NULL);
-	else
-		path = t_strconcat(dirpath, "/",
-			sieve_scriptfile_from_name(name), NULL);
+	if ( (script=sieve_script_create(svinst, location, NULL, ehandler, error_r))
+		== NULL )
+		return NULL;
 
-	return sieve_script_init(NULL, svinst, path, name, ehandler, error_r);
+	/* override name */
+	script->name = p_strdup(script->pool, name);
+	return script;	
 }
 
 void sieve_script_ref(struct sieve_script *script)
@@ -294,25 +291,30 @@ void sieve_script_ref(struct sieve_script *script)
 	script->refcount++;
 }
 
-void sieve_script_unref(struct sieve_script **script)
+void sieve_script_unref(struct sieve_script **_script)
 {
-	i_assert((*script)->refcount > 0);
+	struct sieve_script *script = *_script;
 
-	if (--(*script)->refcount != 0)
+	i_assert(script->refcount > 0);
+
+	if (--script->refcount != 0)
 		return;
 
-	if ( (*script)->stream != NULL )
-		i_stream_destroy(&(*script)->stream);
+	if ( script->stream != NULL )
+		i_stream_unref(&script->stream);
 
-	sieve_error_handler_unref(&(*script)->ehandler);
+	if ( script->ehandler != NULL )
+		sieve_error_handler_unref(&script->ehandler);
 
-	pool_unref(&(*script)->pool);
+	if ( script->v.destroy != NULL )
+		script->v.destroy(script);
 
-	*script = NULL;
+	pool_unref(&script->pool);
+	*_script = NULL;
 }
 
 /* 
- * Accessors 
+ * Properties
  */
 
 const char *sieve_script_name(const struct sieve_script *script)
@@ -320,127 +322,226 @@ const char *sieve_script_name(const struct sieve_script *script)
 	return script->name;
 }
 
-const char *sieve_script_filename(const struct sieve_script *script)
+const char *sieve_script_location(const struct sieve_script *script)
 {
-	return script->filename;
+	return script->location;
 }
 
-const char *sieve_script_path(const struct sieve_script *script)
-{
-	return script->path;
-}
-
-const char *sieve_script_dirpath(const struct sieve_script *script)
+struct sieve_instance *sieve_script_svinst(const struct sieve_script *script)
 {
-	return script->dirpath;
+	return script->svinst;
 }
 
-const char *sieve_script_binpath(const struct sieve_script *script)
+int sieve_script_get_size(struct sieve_script *script, uoff_t *size_r)
 {
-	return script->binpath;
-}
+	int ret;
 
-mode_t sieve_script_permissions(const struct sieve_script *script)
-{
-	return script->st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
-}
+	if ( script->v.get_size != NULL ) {
+		if ( (ret=script->v.get_size(script, size_r)) != 0)
+			return ret;
+	}
 
-struct sieve_instance *sieve_script_svinst(const struct sieve_script *script)
-{
-	return script->svinst;
-}
+	/* Try getting size from the stream */
+	if ( script->stream == NULL && sieve_script_open(script, NULL) == NULL )
+		return -1;
 
-size_t sieve_script_size(const struct sieve_script *script)
-{
-	return script->st.st_size;
+	return i_stream_get_size(script->stream, TRUE, size_r);
 }
 
 /* 
- * Stream manageement 
+ * Stream management 
  */
 
 struct istream *sieve_script_open
 (struct sieve_script *script, enum sieve_error *error_r)
 {
-	int fd;
-	struct stat st;
-	struct istream *result;
+	enum sieve_error error;
 
 	if ( error_r != NULL )
 		*error_r = SIEVE_ERROR_NONE;
 
-	if ( (fd=open(script->path, O_RDONLY)) < 0 ) {
-		sieve_script_handle_file_error
-			(script->svinst, script->path, script->basename, script->ehandler, 
-				error_r);
-		return NULL;
-	}	
-	
-	if ( fstat(fd, &st) != 0 ) {
-		sieve_critical(script->svinst, script->ehandler, script->basename,
-			"failed to open sieve script",
-			"failed to open sieve script: fstat(fd=%s) failed: %m", script->path);
-		if ( error_r != NULL )
-			*error_r = SIEVE_ERROR_TEMP_FAIL;
-		result = NULL;
-	} else {
-		/* Re-check the file type just to be sure */
-		if ( !S_ISREG(st.st_mode) ) {
-			sieve_critical(script->svinst, script->ehandler, script->basename,
-				"failed to open sieve script",
-				"sieve script file '%s' is not a regular file", script->path);
-			if ( error_r != NULL )
-				*error_r = SIEVE_ERROR_NOT_POSSIBLE;
-			result = NULL;
-		} else {
-			result = script->stream = 
-				i_stream_create_fd(fd, SIEVE_READ_BLOCK_SIZE, TRUE);
-			script->st = script->lnk_st = st;
-		}
+	if ( script->stream == NULL ) {
+		T_BEGIN {
+			script->stream = script->v.open(script, &error);
+		} T_END;
 	}
 
-	if ( result == NULL ) {
-		/* Something went wrong, close the fd */
-		if ( close(fd) != 0 ) {
-			sieve_sys_error(script->svinst, 
-				"failed to close sieve script: close(fd=%s) failed: %m", 
-				script->path);
+	if ( script->stream == NULL ) {
+		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;
 		}
 	}
-	
-	return result;
+
+	return script->stream;
 }
 
 void sieve_script_close(struct sieve_script *script)
 {
-	i_stream_destroy(&script->stream);
-}
+	if ( script->stream != NULL )
+		return;
 
-uoff_t sieve_script_get_size(const struct sieve_script *script)
-{
-	return script->st.st_size;
+	i_stream_unref(&script->stream);
+
+	if ( script->v.close != NULL ) { 
+		T_BEGIN {
+			script->v.close(script);
+		} T_END;
+	}
 }
 
 /* 
  * Comparison 
  */
 
-int sieve_script_cmp
-(const struct sieve_script *script1, const struct sieve_script *script2)
+bool sieve_script_equals
+(const struct sieve_script *script, const struct sieve_script *other)
 {
-	if ( script1 == NULL || script2 == NULL ) 
-		return -1;	
+	if ( script == other )
+		return TRUE;
+
+	if ( script == NULL || other == NULL )
+		return FALSE;
+
+	if ( script->script_class != other->script_class )
+		return FALSE;
+
+	if ( script->name != NULL && other->name != NULL &&
+		strcmp(script->name, other->name) == 0 )
+		return TRUE;
 
-	return ( script1->st.st_ino == script2->st.st_ino ) ? 0 : -1;
+	if ( script->v.equals == NULL )
+		return FALSE;
+
+	return script->v.equals(script, other);
 }
 
 unsigned int sieve_script_hash(const struct sieve_script *script)
 {	
-	return (unsigned int) script->st.st_ino;
+	return str_hash(script->name);
+}
+
+/*
+ * Binary
+ */
+
+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;
+	
+	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",
+			sieve_binary_path(sbin), sieve_script_location(script));
+		return -1;
+	}
+
+	if ( strcmp(str_c(script_class), script->driver_name) != 0 )
+		return 0;
+
+	if ( script->v.binary_read_metadata == NULL )
+		return 1;
+
+	return script->v.binary_read_metadata(script, sblock, offset);
+}
+
+void sieve_script_binary_write_metadata
+(struct sieve_script *script, struct sieve_binary_block *sblock)
+{
+	sieve_binary_emit_cstring(sblock, script->driver_name);
+
+	if ( script->v.binary_write_metadata == NULL )
+		return;
+
+	script->v.binary_write_metadata(script, sblock);
+}
+
+struct sieve_binary *sieve_script_binary_load
+(struct sieve_script *script, enum sieve_error *error_r)
+{
+	if ( script->v.binary_load == NULL ) {
+		*error_r = SIEVE_ERROR_NOT_POSSIBLE;
+		return NULL;
+	}
+
+	return script->v.binary_load(script, error_r);
 }
 
-bool sieve_script_newer
-(const struct sieve_script *script, time_t time)
+int sieve_script_binary_save
+(struct sieve_script *script, struct sieve_binary *sbin, bool update,
+	enum sieve_error *error_r)
 {
-	return ( script->st.st_mtime > time || script->lnk_st.st_mtime > time );
+	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 ) {
+		*error_r = SIEVE_ERROR_NOT_POSSIBLE;
+		return -1;
+	}
+
+	return script->v.binary_save(script, sbin, update, error_r);
 }
+
+int sieve_script_setup_bindir
+(struct sieve_script *script, mode_t mode)
+{
+	struct sieve_instance *svinst = script->svinst;
+	struct stat st;
+
+	if ( script->bin_dir == NULL )
+		return -1;
+
+	if ( stat(script->bin_dir, &st) == 0 )
+		return 0;
+
+	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);
+		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;
+	}
+
+	switch ( errno ) {
+	case EEXIST:
+		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 -1;
+}
+
diff --git a/src/lib-sieve/sieve-script.h b/src/lib-sieve/sieve-script.h
index dc1e052f049ec110ca2ca886d6bf634678355d5e..896cfdfad182da3defbd981b6b09502d3d67b7c6 100644
--- a/src/lib-sieve/sieve-script.h
+++ b/src/lib-sieve/sieve-script.h
@@ -15,28 +15,20 @@
 
 bool sieve_script_name_is_valid(const char *scriptname);
 
-/*
- * 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);
-const char *sieve_binfile_from_name(const char *name);
-
 /*
  * Sieve script object
  */
 
 struct sieve_script;
 
+ARRAY_DEFINE_TYPE(sieve_scripts, struct sieve_script *);
+
 struct sieve_script *sieve_script_create
-	(struct sieve_instance *svinst, const char *path, const char *name, 
+	(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_as
+	(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_in_directory
-	(struct sieve_instance *svinst, const char *dirpath, const char *name,
-    	struct sieve_error_handler *ehandler, enum sieve_error *error_r);
 
 void sieve_script_ref(struct sieve_script *script);
 void sieve_script_unref(struct sieve_script **script);
@@ -46,15 +38,24 @@ void sieve_script_unref(struct sieve_script **script);
  */
  
 const char *sieve_script_name(const struct sieve_script *script);
-const char *sieve_script_filename(const struct sieve_script *script);
-const char *sieve_script_path(const struct sieve_script *script);
-const char *sieve_script_binpath(const struct sieve_script *script);
-const char *sieve_script_dirpath(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);
 
-mode_t sieve_script_permissions(const struct sieve_script *script);
+/*
+ * Saving/loading Sieve binaries
+ */
 
-struct sieve_instance *sieve_script_svinst(const struct sieve_script *script);
-size_t sieve_script_size(const struct sieve_script *script);
+int sieve_script_binary_read_metadata
+	(struct sieve_script *script, struct sieve_binary_block *sblock,
+		sieve_size_t *offset);
+void sieve_script_binary_write_metadata
+	(struct sieve_script *script, struct sieve_binary_block *sblock);
+
+struct sieve_binary *sieve_script_binary_load
+	(struct sieve_script *script, enum sieve_error *error_r);
+int sieve_script_binary_save
+	(struct sieve_script *script, struct sieve_binary *sbin, bool update,
+		enum sieve_error *error_r);
 
 /* 
  * Stream management 
@@ -64,21 +65,21 @@ struct istream *sieve_script_open
 	(struct sieve_script *script, enum sieve_error *error_r);
 void sieve_script_close(struct sieve_script *script);
 
-uoff_t sieve_script_get_size(const struct sieve_script *script);
+int sieve_script_get_size(struct sieve_script *script, uoff_t *size_r);
 
 /*
  * Comparison
  */
  
-int sieve_script_cmp
-	(const struct sieve_script *script1, const struct sieve_script *script2);
-unsigned int sieve_script_hash(const struct sieve_script *script);
-bool sieve_script_newer(const struct sieve_script *script, time_t time);
+bool sieve_script_equals
+	(const struct sieve_script *script, const struct sieve_script *other);
 
-static inline bool sieve_script_equals
-	(const struct sieve_script *script1, const struct sieve_script *script2)
+unsigned int sieve_script_hash(const struct sieve_script *script);
+static inline int sieve_script_cmp
+(const struct sieve_script *script, const struct sieve_script *other)
 {
-	return ( sieve_script_cmp(script1, script2) == 0 );
+	return ( sieve_script_equals(script, other) ? 0 : -1 );
 }
 
+
 #endif /* __SIEVE_SCRIPT_H */
diff --git a/src/lib-sieve/sieve-settings.h b/src/lib-sieve/sieve-settings.h
index be588d99e588c31d9c0815bb71d2b62955f783f5..c835d686ed765339e0846a4eb3805d5733035f0e 100644
--- a/src/lib-sieve/sieve-settings.h
+++ b/src/lib-sieve/sieve-settings.h
@@ -13,12 +13,12 @@
 static inline const char *sieve_setting_get
 (struct sieve_instance *svinst, const char *identifier)
 {
-	const struct sieve_environment *env = svinst->env;
+	const struct sieve_callbacks *callbacks = svinst->callbacks;
 
-	if ( env == NULL || env->get_setting == NULL )
+	if ( callbacks == NULL || callbacks->get_setting == NULL )
 		return NULL;
 
-	return env->get_setting(svinst->context, identifier);
+	return callbacks->get_setting(svinst->context, identifier);
 }
 
 bool sieve_setting_get_uint_value
@@ -44,12 +44,15 @@ bool sieve_setting_get_duration_value
 static inline const char *sieve_environment_get_homedir
 (struct sieve_instance *svinst)
 {
-	const struct sieve_environment *env = svinst->env;
+	const struct sieve_callbacks *callbacks = svinst->callbacks;
 
-	if ( env == NULL || env->get_homedir == NULL )
+	if ( svinst->home_dir != NULL )
+		return svinst->home_dir;
+
+	if ( callbacks == NULL || callbacks->get_homedir == NULL )
 		return NULL;
 
-	return env->get_homedir(svinst->context);
+	return callbacks->get_homedir(svinst->context);
 }
 
 #endif /* __SIEVE_SETTINGS_H */
diff --git a/src/lib-sieve/sieve-types.h b/src/lib-sieve/sieve-types.h
index 5f75b08e60b3cf45fbcc2fa7deea4d2f19fcb430..2c9e7a731c680936d1ca87467a44e7ddbaee4980 100644
--- a/src/lib-sieve/sieve-types.h
+++ b/src/lib-sieve/sieve-types.h
@@ -23,10 +23,29 @@ struct sieve_script_env;
 struct sieve_exec_status;
 
 /*
- * Callbacks
+ * System environment
  */
 
+enum sieve_flag {
+	/* Relative paths are resolved to HOME */
+	SIEVE_FLAG_HOME_RELATIVE = (1 << 0),
+};
+
 struct sieve_environment {
+	const char *hostname;
+	const char *base_dir;
+
+	const char *username;
+	const char *home_dir;
+
+	enum sieve_flag flags;
+};
+
+/*
+ * Callbacks
+ */
+
+struct sieve_callbacks {
 	const char *(*get_homedir)(void *context);
 	const char *(*get_setting)(void *context, const char *identifier);
 };
@@ -136,13 +155,9 @@ struct sieve_script_env {
 	/* Mail-related */
 	struct mail_user *user;
 	const char *default_mailbox;
+	const char *postmaster_address;
 	bool mailbox_autocreate;
 	bool mailbox_autosubscribe;
-	
-	/* System-related */
-	const char *username;
-	const char *hostname;
-	const char *postmaster_address;
 		
 	/* External context data */
 
@@ -152,19 +167,19 @@ struct sieve_script_env {
 	
 	/* Interface for sending mail */
 	void *(*smtp_open)
-		(void *script_ctx, const char *destination, 
+		(const struct sieve_script_env *senv, const char *destination, 
 			const char *return_path, struct ostream **output_r);
-	bool (*smtp_close)(void *script_ctx, void *handle);
+	bool (*smtp_close)(const struct sieve_script_env *senv, void *handle);
 	
 	/* Interface for marking and checking duplicates */
 	int (*duplicate_check)
-		(void *script_ctx, const void *id, size_t id_size, const char *user);
+		(const struct sieve_script_env *senv, const void *id, size_t id_size);
 	void (*duplicate_mark)
-		(void *script_ctx, const void *id, size_t id_size, const char *user, 
+		(const struct sieve_script_env *senv, const void *id, size_t id_size,
 			time_t time);
 
 	/* Interface for rejecting mail */
-	int (*reject_mail)(void *script_ctx, const char *recipient,
+	int (*reject_mail)(const struct sieve_script_env *senv, const char *recipient,
 			const char *reason);
 	
 	/* Execution status record */	
diff --git a/src/lib-sieve/sieve.c b/src/lib-sieve/sieve.c
index 14c9b4e02403e394135fd9b33a3ea2a2ef831f03..b6c2f24e424d0e6bba62bf4e53286a0d535faea6 100644
--- a/src/lib-sieve/sieve.c
+++ b/src/lib-sieve/sieve.c
@@ -13,6 +13,7 @@
 #include "sieve-plugins.h"
 
 #include "sieve-script.h"
+#include "sieve-script-file.h"
 #include "sieve-ast.h"
 #include "sieve-binary.h"
 #include "sieve-actions.h"
@@ -41,7 +42,8 @@
  */
 
 struct sieve_instance *sieve_init
-(const struct sieve_environment *env, void *context, bool debug)
+(const struct sieve_environment *env,
+	const struct sieve_callbacks *callbacks, void *context, bool debug)
 {
 	struct sieve_instance *svinst;
 	unsigned long long int uint_setting;
@@ -52,9 +54,14 @@ struct sieve_instance *sieve_init
 	pool = pool_alloconly_create("sieve", 8192);
 	svinst = p_new(pool, struct sieve_instance, 1);
 	svinst->pool = pool;
-	svinst->env = env;
+	svinst->callbacks = callbacks;
 	svinst->context = context;
 	svinst->debug = debug;
+	svinst->hostname = p_strdup_empty(pool, env->hostname);
+	svinst->base_dir = p_strdup_empty(pool, env->base_dir);
+	svinst->username = p_strdup_empty(pool, env->username);
+	svinst->home_dir = p_strdup_empty(pool, env->home_dir);
+	svinst->flags = env->flags;
 
 	sieve_errors_init(svinst);
 
@@ -240,7 +247,7 @@ struct sieve_binary *sieve_compile_script
 }
 
 struct sieve_binary *sieve_compile
-(struct sieve_instance *svinst, const char *script_path,
+(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)
 {
@@ -248,17 +255,17 @@ struct sieve_binary *sieve_compile
 	struct sieve_binary *sbin;
 
 	if ( (script = sieve_script_create
-		(svinst, script_path, script_name, ehandler, error_r)) == NULL )
+		(svinst, script_location, script_name, ehandler, error_r)) == NULL )
 		return NULL;
 	
 	sbin = sieve_compile_script(script, ehandler, flags, error_r);
-	
-	sieve_script_unref(&script);
 
 	if ( svinst->debug && sbin != NULL ) {
-		sieve_sys_debug(svinst, "script file %s successfully compiled",
-			script_path);
+		sieve_sys_debug(svinst, "script `%s' from %s successfully compiled",
+			sieve_script_name(script), sieve_script_location(script));
 	}
+
+	sieve_script_unref(&script);
 	
 	return sbin;
 }
@@ -311,28 +318,16 @@ struct sieve_binary *sieve_load
 	return sieve_binary_open(svinst, bin_path, NULL, error_r);
 }
 
-struct sieve_binary *sieve_open
-(struct sieve_instance *svinst, const char *script_path, 
-	const char *script_name, struct sieve_error_handler *ehandler,
+struct sieve_binary *sieve_open_script
+(struct sieve_script *script, struct sieve_error_handler *ehandler,
 	enum sieve_compile_flags flags, enum sieve_error *error_r)
 {
-	struct sieve_script *script;
+	struct sieve_instance *svinst = sieve_script_svinst(script);
 	struct sieve_binary *sbin;
-	const char *bin_path;
 	
-	/* First open the scriptfile itself */
-	script = sieve_script_create
-		(svinst, script_path, script_name, ehandler, error_r);
-
-	if ( script == NULL ) {
-		/* Failed */
-		return NULL;
-	}
-
 	T_BEGIN {
 		/* Then try to open the matching binary */
-		bin_path = sieve_script_binpath(script);	
-		sbin = sieve_binary_open(svinst, bin_path, script, error_r);
+		sbin = sieve_script_binary_load(script, error_r);
 	
 		if (sbin != NULL) {
 			/* Ok, it exists; now let's see if it is up to date */
@@ -340,7 +335,7 @@ struct sieve_binary *sieve_open
 				/* Not up to date */
 				if ( svinst->debug )
 					sieve_sys_debug(svinst, "script binary %s is not up-to-date",
-						bin_path);
+						sieve_binary_path(sbin));
 
 				sieve_binary_unref(&sbin);
 				sbin = NULL;
@@ -353,7 +348,7 @@ struct sieve_binary *sieve_open
 		if ( sbin != NULL ) {
 			if ( svinst->debug )
 				sieve_sys_debug(svinst, "script binary %s successfully loaded",
-					bin_path);
+					sieve_binary_path(sbin));
 			
 		} else {	
 			sbin = sieve_compile_script(script, ehandler, flags, error_r);
@@ -361,12 +356,34 @@ struct sieve_binary *sieve_open
 			/* Save the binary if compile was successful */
 			if ( sbin != NULL ) {
 				if ( svinst->debug )
-					sieve_sys_debug(svinst, "script %s successfully compiled", 
-						script_path);
+					sieve_sys_debug(svinst, "script `%s' from %s successfully compiled", 
+						sieve_script_name(script), sieve_script_location(script));
 			}
 		}
 	} T_END;
+
+	return sbin;
+}
+
+struct sieve_binary *sieve_open
+(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)
+{
+	struct sieve_script *script;
+	struct sieve_binary *sbin;
 	
+	/* First open the scriptfile itself */
+	script = sieve_script_create
+		(svinst, script_location, script_name, ehandler, error_r);
+
+	if ( script == NULL ) {
+		/* Failed */
+		return NULL;
+	}
+
+	sbin = sieve_open_script(script, ehandler, flags, error_r);
+
 	/* Drop script reference, if sbin != NULL it holds a reference of its own. 
 	 * Otherwise the script object is freed here.
 	 */
@@ -385,11 +402,23 @@ bool sieve_is_loaded(struct sieve_binary *sbin)
 	return sieve_binary_loaded(sbin);
 }
 
-int sieve_save
+int sieve_save_as
 (struct sieve_binary *sbin, const char *bin_path, bool update,
-	enum sieve_error *error_r)
+	mode_t save_mode, enum sieve_error *error_r)
 {
-	return sieve_binary_save(sbin, bin_path, update, error_r);
+	return sieve_binary_save(sbin, bin_path, update, save_mode, error_r);
+}
+
+int sieve_save
+(struct sieve_binary *sbin, bool update, enum sieve_error *error_r)
+{
+	struct sieve_script *script = sieve_binary_script(sbin);
+
+	if ( script == NULL ) {
+		return sieve_binary_save(sbin, NULL, update, 0600, error_r);
+	}
+
+	return sieve_script_binary_save(script, sbin, update, error_r);
 }
 
 void sieve_close(struct sieve_binary **sbin)
diff --git a/src/lib-sieve/sieve.h b/src/lib-sieve/sieve.h
index b95c776b455b58e2c6d00d27ac3f776420ac4a60..20e5d8299072a17b26081180c84acb2225871d35 100644
--- a/src/lib-sieve/sieve.h
+++ b/src/lib-sieve/sieve.h
@@ -20,7 +20,8 @@ struct sieve_binary;
  *   is used.
  */
 struct sieve_instance *sieve_init
-	(const struct sieve_environment *env, void *context, bool debug);
+	(const struct sieve_environment *env, const struct sieve_callbacks *callbacks,
+		void *context, bool debug);
 
 /* sieve_deinit():
  *   Frees all memory allocated by the sieve engine. 
@@ -54,7 +55,7 @@ struct sieve_binary *sieve_compile_script
  *   Compiles the script into a binary.
  */
 struct sieve_binary *sieve_compile
-	(struct sieve_instance *svinst, const char *script_path, 
+	(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);
 
@@ -70,6 +71,16 @@ struct sieve_binary *sieve_load
 	(struct sieve_instance *svinst, const char *bin_path,
 		enum sieve_error *error_r);
 
+/* sieve_open_script:
+ *
+ *   First tries to open the binary version of the specified script and if it 
+ *   does not exist or if it contains errors, the script is (re-)compiled. Note 
+ *   that errors in the bytecode are caught only at runtime.
+ */
+struct sieve_binary *sieve_open_script
+	(struct sieve_script *script, struct sieve_error_handler *ehandler,
+		enum sieve_compile_flags flags, enum sieve_error *error_r);
+
 /* sieve_open:
  *
  *   First tries to open the binary version of the specified script and if it 
@@ -77,20 +88,28 @@ struct sieve_binary *sieve_load
  *   that errors in the bytecode are caught only at runtime.
  */
 struct sieve_binary *sieve_open
-	(struct sieve_instance *svinst, const char *script_path, 
+	(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);
 
+/* sieve_save_as:
+ *
+ *  Saves the binary as the file indicated by the path parameter. This function
+ *  will not write the binary to disk when it was loaded from the indicated
+ *  bin_path, unless update is TRUE. 
+ */
+int sieve_save_as
+	(struct sieve_binary *sbin, const char *bin_path, bool update,
+		mode_t save_mode, enum sieve_error *error_r);
+
 /* sieve_save:
  *
- *  Saves the binary as the file indicated by the path parameter. If 
- *  path is NULL, it chooses the default path relative to the original
- *  script. This function will not write the binary to disk when it was
- *  loaded from the indicated bin_path, unless update is TRUE. 
+ *  Saves the binary to the default location. This function will not overwrite
+ *  the binary it was loaded earlier from the default location, unless update
+ *  is TRUE. 
  */
 int sieve_save
-    (struct sieve_binary *sbin, const char *bin_path, bool update,
-			enum sieve_error *error_r);
+	(struct sieve_binary *sbin, bool update, enum sieve_error *error_r);
 
 /* sieve_close:
  *
diff --git a/src/lib-sievestorage/sieve-storage-list.c b/src/lib-sievestorage/sieve-storage-list.c
index 0e36d1ea8d18d41521d61a074c4f4758d860eff4..3941f41b5a8ae1b862c0a141e1c8d4a7c21346cf 100644
--- a/src/lib-sievestorage/sieve-storage-list.c
+++ b/src/lib-sievestorage/sieve-storage-list.c
@@ -3,6 +3,11 @@
 
 #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"
diff --git a/src/lib-sievestorage/sieve-storage-quota.c b/src/lib-sievestorage/sieve-storage-quota.c
index 8317b716d1085cc8205e60363b8a261edc5698e4..f081ed51b4acad5b493566c2c900290badce46be 100644
--- a/src/lib-sievestorage/sieve-storage-quota.c
+++ b/src/lib-sievestorage/sieve-storage-quota.c
@@ -3,6 +3,11 @@
 
 #include "lib.h"
 #include "str.h"
+
+#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"
diff --git a/src/lib-sievestorage/sieve-storage-save.c b/src/lib-sievestorage/sieve-storage-save.c
index 0db76c2478326bc4f7569f92c438394119da997d..ddc1afefd5dad4aca3debe4bad01e623198d8515 100644
--- a/src/lib-sievestorage/sieve-storage-save.c
+++ b/src/lib-sievestorage/sieve-storage-save.c
@@ -11,6 +11,7 @@
 #include "eacces-error.h"
 
 #include "sieve-script.h"
+#include "sieve-script-file.h"
 
 #include "sieve-storage-private.h"
 #include "sieve-storage-script.h"
diff --git a/src/lib-sievestorage/sieve-storage-script.c b/src/lib-sievestorage/sieve-storage-script.c
index 08c6cd9aa02ecff70bb8737a7e86e637f8f0151c..d240697212f074115ca398a32e21db0812e2007c 100644
--- a/src/lib-sievestorage/sieve-storage-script.c
+++ b/src/lib-sievestorage/sieve-storage-script.c
@@ -10,6 +10,7 @@
 #include "eacces-error.h"
 
 #include "sieve-script-private.h"
+#include "sieve-script-file.h"
 
 #include "sieve-storage.h"
 #include "sieve-storage-private.h"
@@ -24,7 +25,7 @@
 #include <fcntl.h>
 
 struct sieve_storage_script {
-	struct sieve_script script;	
+	struct sieve_file_script file;	
 
 	struct sieve_storage *storage;
 };
@@ -58,12 +59,14 @@ struct sieve_script *sieve_storage_script_init_from_path
 
 	pool = pool_alloconly_create("sieve_storage_script", 4096);	
 	st_script = p_new(pool, struct sieve_storage_script, 1);
-	st_script->script.pool = pool;
+	st_script->file.script = sieve_file_script;
+	st_script->file.script.pool = pool;
 	st_script->storage = storage;
 
-	if ( sieve_script_init(&st_script->script, storage->svinst, path, scriptname, 
-		sieve_storage_get_error_handler(storage), &error) != NULL ) {
-		return &st_script->script;
+	if ( sieve_script_init
+		(&st_script->file.script, storage->svinst, &sieve_file_script, path,
+			scriptname,	sieve_storage_get_error_handler(storage), &error) != NULL ) {
+		return &st_script->file.script;
 	}
 
 	pool_unref(&pool);
@@ -298,7 +301,7 @@ int sieve_storage_script_is_active(struct sieve_script *script)
 	
 		if ( ret > 0 ) {
 		 	/* Is the requested script active? */
-			ret = ( strcmp(script->filename, afile) == 0 ? 1 : 0 );
+			ret = ( strcmp(st_script->file.filename, afile) == 0 ? 1 : 0 );
 		}
 	} T_END;
 
@@ -318,7 +321,7 @@ int sieve_storage_script_delete(struct sieve_script **script)
 			"Cannot delete the active sieve script.");
 		ret = -1;
 	} else {
-		ret = unlink((*script)->path);
+		ret = unlink(st_script->file.path);
 
 		if ( ret < 0 ) {
 			if ( errno == ENOENT ) 
@@ -327,7 +330,7 @@ int sieve_storage_script_delete(struct sieve_script **script)
 			else
 				sieve_storage_set_critical(
 					storage, "Performing unlink() failed on sieve file '%s': %m", 
-					(*script)->path);
+					st_script->file.path);
 		}	
 	}
 
@@ -484,14 +487,14 @@ static int _sieve_storage_script_activate(struct sieve_script *script)
 	ret = sieve_storage_get_active_scriptfile(storage, &afile);
 
 	/* Is the requested script already active? */
-	if ( ret <= 0 || strcmp(script->filename, afile) != 0 ) 
+	if ( ret <= 0 || strcmp(st_script->file.filename, afile) != 0 ) 
 		activated = 1; 
 
 	/* Check the scriptfile we are trying to activate */
-	if ( lstat(script->path, &st) != 0 ) {
+	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.", 
-			script->path);
+			st_script->file.path);
 		return -1;
 	}
 
@@ -505,7 +508,7 @@ static int _sieve_storage_script_activate(struct sieve_script *script)
 
 	/* Just try to create the symlink first */
 	link_path = t_strconcat
-	  ( storage->link_path, script->filename, NULL );
+	  ( storage->link_path, st_script->file.filename, NULL );
 		
  	ret = symlink(link_path, storage->active_path);
 
@@ -566,7 +569,7 @@ int sieve_storage_script_rename
 		 */
 
 		/* Link to the new path */
-		ret = link(script->path, newpath);
+		ret = link(st_script->file.path, newpath);
 		if ( ret >= 0 ) {
 			/* Is the requested script active? */
 			if ( sieve_storage_script_is_active(script) ) {
@@ -579,16 +582,15 @@ int sieve_storage_script_rename
 
 			if ( ret >= 0 ) {
 				/* If all is good, remove the old link */
-				if ( unlink(script->path) < 0 ) {
+				if ( unlink(st_script->file.path) < 0 ) {
 					i_error("Failed to clean up old file link '%s' after rename: %m", 
-						script->path);
+						st_script->file.path);
 				}
 
 				if ( script->name != NULL && *script->name != '\0' )
 					script->name = p_strdup(script->pool, newname);
-				script->path = p_strdup(script->pool, newpath);
-				script->filename = p_strdup(script->pool, newfile);
-				script->basename = 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 
@@ -612,7 +614,7 @@ int sieve_storage_script_rename
 			default:
 				sieve_storage_set_critical(
 					storage, "Performing link(%s, %s) failed: %m", 
-						script->path, newpath);
+						st_script->file.path, newpath);
 			}				
 		}
 	} T_END;
diff --git a/src/lib-sievestorage/sieve-storage.c b/src/lib-sievestorage/sieve-storage.c
index 568834ed7d82c913c280e4b9e86ac0c5d2f83870..5984f15b393cab7d9a98b7ee2f9e5869dd6bfe48 100644
--- a/src/lib-sievestorage/sieve-storage.c
+++ b/src/lib-sievestorage/sieve-storage.c
@@ -224,12 +224,30 @@ static struct sieve_storage *_sieve_storage_create
 	/* 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; "
@@ -315,6 +333,23 @@ static struct sieve_storage *_sieve_storage_create
 			}
 		}
 	} 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;
 	}
 
diff --git a/src/managesieve-login/client-authenticate.c b/src/managesieve-login/client-authenticate.c
index 3b4be6f157747cb2167fea31285e1627199ebe39..c2c7e3ad60d34a9300385e64cb11a9bf8d9faf6a 100644
--- a/src/managesieve-login/client-authenticate.c
+++ b/src/managesieve-login/client-authenticate.c
@@ -135,6 +135,7 @@ static int managesieve_client_auth_read_response
 	bool fatal;
 	const unsigned char *data;
 	size_t size;
+	uoff_t resp_size;
 	int ret;
 
 	if ( i_stream_read(client->input) == -1 ) {	
@@ -186,7 +187,7 @@ static int managesieve_client_auth_read_response
 		}
 
 		if ( !managesieve_arg_get_string_stream
-				(&args[0], &msieve_client->auth_response_input) 
+				(&args[0], &msieve_client->auth_response_input)
 			|| !MANAGESIEVE_ARG_IS_EOL(&args[1]) ) {
 			if ( !initial )
 				*error_r = "Invalid AUTHENTICATE client response.";
@@ -197,11 +198,11 @@ static int managesieve_client_auth_read_response
 		}
 
 		if ( i_stream_get_size
-			(msieve_client->auth_response_input, FALSE, &size) <= 0 )
-			size = 0;
+			(msieve_client->auth_response_input, FALSE, &resp_size) <= 0 )
+			resp_size = 0;
 
 		if (client->auth_response == NULL)
-			client->auth_response = str_new(default_pool, I_MAX(size+1, 256));
+			client->auth_response = str_new(default_pool, I_MAX(resp_size+1, 256));
 	}
 
 	while ( (ret=i_stream_read_data
diff --git a/src/managesieve/cmd-getscript.c b/src/managesieve/cmd-getscript.c
index 08ae77c882899a3e38e7f25ff19ea8a2284fc30a..59e24230a344347561910272856906919d4a2f6f 100644
--- a/src/managesieve/cmd-getscript.c
+++ b/src/managesieve/cmd-getscript.c
@@ -18,7 +18,7 @@ struct cmd_getscript_context {
 
 	struct sieve_script *script;
 	struct istream *script_stream;
-	
+
 	unsigned int failed:1;
 };
 
@@ -54,8 +54,8 @@ static bool cmd_getscript_continue(struct client_command_context *cmd)
 
 	if ( ret < 0 ) {
 		sieve_storage_set_critical(ctx->storage,
-			"o_stream_send_istream(%s) failed: %m", 
-			sieve_script_filename(ctx->script));
+			"o_stream_send_istream() failed for script `%s' from %s: %m", 
+			sieve_script_name(ctx->script), sieve_script_location(ctx->script));
 		ctx->failed = TRUE;
 		return cmd_getscript_finish(ctx);
 	}
@@ -67,9 +67,9 @@ static bool cmd_getscript_continue(struct client_command_context *cmd)
 		if ( !i_stream_have_bytes_left(ctx->script_stream) ) {
 			/* Input stream gave less data than expected */
 			sieve_storage_set_critical(ctx->storage,
-				"GETSCRIPT for SCRIPT %s got too little data: "
+				"GETSCRIPT for script `%s' from %s got too little data: "
 				"%"PRIuUOFF_T" vs %"PRIuUOFF_T, sieve_script_name(ctx->script),
-				ctx->script_offset, ctx->script_size);
+				sieve_script_location(ctx->script), ctx->script_offset, ctx->script_size);
 
 			client_disconnect(ctx->client, "GETSCRIPT failed");
 			ctx->failed = TRUE;
@@ -108,13 +108,20 @@ bool cmd_getscript(struct client_command_context *cmd)
 	ctx->script_stream = sieve_script_open(ctx->script, &error);
 
 	if ( ctx->script_stream == NULL ) {
-		ctx->failed = TRUE;
 		if ( error == SIEVE_ERROR_NOT_FOUND )
-			sieve_storage_set_error(client->storage, error, "Script does not exist.");		
+			sieve_storage_set_error(client->storage, error, "Script does not exist.");
+		ctx->failed = TRUE;
+		return cmd_getscript_finish(ctx);
+	}
+
+	if ( sieve_script_get_size(ctx->script, &ctx->script_size) <= 0 ) {
+		sieve_storage_set_critical(ctx->storage,
+			"failed to obtain script size for script `%s' from %s",
+			sieve_script_name(ctx->script), sieve_script_location(ctx->script));
+		ctx->failed = TRUE;
 		return cmd_getscript_finish(ctx);
 	}
 
-	ctx->script_size = sieve_script_get_size(ctx->script);
 	ctx->script_offset = 0;	
 
 	client_send_line
diff --git a/src/managesieve/main.c b/src/managesieve/main.c
index 64985567df784e6b4e050ae959d0a8e2d7057100..a50b4378ae381baaaa0216b9805d1dac0fee5aa7 100644
--- a/src/managesieve/main.c
+++ b/src/managesieve/main.c
@@ -139,11 +139,11 @@ client_create_from_input(const struct mail_storage_service_input *input,
 	if (set->verbose_proctitle)
 		verbose_proctitle = TRUE;
 
-	client = client_create(fd_in, fd_out, mail_user, user, set);
+	client = client_create
+		(fd_in, fd_out, input->session_id, mail_user, user, set);
 	T_BEGIN {
 		client_add_input(client, input_buf);
 	} T_END;
-
 	return 0;
 }
 
@@ -178,6 +178,7 @@ static void
 login_client_connected(const struct master_login_client *client,
 		       const char *username, const char *const *extra_fields)
 {
+#define MSG_BYE_INTERNAL_ERROR "BYE \""CRITICAL_MSG"\"\r\n"
 	struct mail_storage_service_input input;
 	const char *error;
 	buffer_t input_buf;
@@ -188,11 +189,17 @@ login_client_connected(const struct master_login_client *client,
 	input.remote_ip = client->auth_req.remote_ip;
 	input.username = username;
 	input.userdb_fields = extra_fields;
+	input.session_id = client->session_id;
 
 	buffer_create_const_data(&input_buf, client->data,
 				 client->auth_req.data_size);
 	if (client_create_from_input(&input, client->fd, client->fd,
 				     &input_buf, &error) < 0) {
+		if (write(client->fd, MSG_BYE_INTERNAL_ERROR,
+			  strlen(MSG_BYE_INTERNAL_ERROR)) < 0) {
+			if (errno != EAGAIN && errno != EPIPE)
+				i_error("write(client) failed: %m");
+		}
 		i_error("%s", error);
 		(void)close(client->fd);
 		master_service_client_connection_destroyed(master_service);
diff --git a/src/managesieve/managesieve-capabilities.c b/src/managesieve/managesieve-capabilities.c
index 3edf3b530a8463a9c1d9c68a8725ad27d7664f52..e2f573da0e34939a2a19e34c2caa99aec596c2d3 100644
--- a/src/managesieve/managesieve-capabilities.c
+++ b/src/managesieve/managesieve-capabilities.c
@@ -88,11 +88,6 @@ static const char *plugin_settings_get
  * Sieve environment
  */
 
-static const char *sieve_get_homedir(void *context ATTR_UNUSED)
-{
-	return "/tmp";
-}
-
 static const char *sieve_get_setting
 (void *context, const char *identifier)
 {
@@ -101,8 +96,8 @@ static const char *sieve_get_setting
   return plugin_settings_get(set, identifier);
 }
 
-static const struct sieve_environment sieve_env = {
-	sieve_get_homedir,
+static const struct sieve_callbacks sieve_callbacks = {
+	NULL,
 	sieve_get_setting
 };
 
@@ -113,6 +108,7 @@ static const struct sieve_environment sieve_env = {
 void managesieve_capabilities_dump(void)
 {
 	const struct plugin_settings *global_plugin_settings;
+	struct sieve_environment svenv;
 	struct sieve_instance *svinst;
 	const char *notify_cap;
 	
@@ -122,7 +118,11 @@ void managesieve_capabilities_dump(void)
 
 	/* Initialize Sieve engine */
 
-	svinst = sieve_init(&sieve_env, (void *) global_plugin_settings, FALSE);
+	memset((void*)&svenv, 0, sizeof(svenv));
+	svenv.home_dir = "/tmp";
+
+	svinst = sieve_init
+		(&svenv, &sieve_callbacks, (void *) global_plugin_settings, FALSE);
 
 	/* Dump capabilities */
 
diff --git a/src/managesieve/managesieve-client.c b/src/managesieve/managesieve-client.c
index bde2b957664ce26d8b06574d52e8b04b56e26502..a1a4342689759f819a374a4ebe79708cde3824e9 100644
--- a/src/managesieve/managesieve-client.c
+++ b/src/managesieve/managesieve-client.c
@@ -24,44 +24,25 @@
 #include <stdlib.h>
 #include <unistd.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]"
-
 extern struct mail_storage_callbacks mail_storage_callbacks;
 struct managesieve_module_register managesieve_module_register = { 0 };
 
 struct client *managesieve_clients = NULL;
 unsigned int managesieve_client_count = 0;
 
-static const char *managesieve_sieve_get_homedir
-(void *context)
-{
-    struct mail_user *mail_user = (struct mail_user *) context;
-    const char *home = NULL;
-
-    if ( mail_user == NULL )
-        return NULL;
-
-    if ( mail_user_get_home(mail_user, &home) <= 0 )
-        return NULL;
-
-    return home;
-}
-
 static const char *managesieve_sieve_get_setting
 (void *context, const char *identifier)
 {
-    struct mail_user *mail_user = (struct mail_user *) context;
+	struct mail_user *mail_user = (struct mail_user *) context;
 
-    if ( mail_user == NULL )
-        return NULL;
+	if ( mail_user == NULL )
+		return NULL;
 
-    return mail_user_plugin_getenv(mail_user, identifier);
+	return mail_user_plugin_getenv(mail_user, identifier);
 }
 
-static const struct sieve_environment managesieve_sieve_env = {
-	managesieve_sieve_get_homedir,
+static const struct sieve_callbacks managesieve_sieve_callbacks = {
+	NULL,
 	managesieve_sieve_get_setting
 };
 
@@ -106,14 +87,16 @@ static struct sieve_storage *client_get_storage
 }
 
 struct client *client_create
-(int fd_in, int fd_out, struct mail_user *user,
+(int fd_in, int fd_out, const char *session_id, struct mail_user *user,
 	struct mail_storage_service_user *service_user,
 	const struct managesieve_settings *set)
 {
 	struct client *client;
 	const char *ident;
+	struct sieve_environment svenv;
 	struct sieve_instance *svinst;
 	struct sieve_storage *storage;
+	pool_t pool;
 
 	/* Always use nonblocking I/O */
 
@@ -122,7 +105,14 @@ struct client *client_create
 
 	/* Initialize Sieve instance */
 
-	svinst = sieve_init(&managesieve_sieve_env, (void *) user, set->mail_debug);
+	memset((void*)&svenv, 0, sizeof(svenv));
+	svenv.username = user->username;
+	(void)mail_user_get_home(user, &svenv.home_dir);
+	svenv.base_dir = user->set->base_dir;
+	svenv.flags = SIEVE_FLAG_HOME_RELATIVE;
+
+	svinst = sieve_init
+		(&svenv, &managesieve_sieve_callbacks, (void *) user, set->mail_debug);
 
 	/* Get Sieve storage */
 
@@ -132,9 +122,12 @@ struct client *client_create
 	net_set_nonblock(fd_in, TRUE);
 	net_set_nonblock(fd_out, TRUE);
 
-	client = i_new(struct client, 1);
+	pool = pool_alloconly_create("managesieve client", 1024);
+	client = p_new(pool, struct client, 1);
+	client->pool = pool;
 	client->set = set;
 	client->service_user = service_user;
+	client->session_id = p_strdup(pool, session_id);
 	client->fd_in = fd_in;
 	client->fd_out = fd_out;
 	client->input = i_stream_create_fd
@@ -179,6 +172,7 @@ static const char *client_stats(struct client *client)
 	static struct var_expand_table static_tab[] = {
 		{ 'i', NULL, "input" },
 		{ 'o', NULL, "output" },
+		{ '\0', NULL, "session" },
 		{ '\0', NULL, NULL }
 	};
 	struct var_expand_table *tab;
@@ -189,6 +183,7 @@ static const char *client_stats(struct client *client)
 
 	tab[0].value = dec2str(client->input->v_offset);
 	tab[1].value = dec2str(client->output->offset);
+	tab[2].value = client->session_id;
 
 	str = t_str_new(128);
 	var_expand(str, client->set->managesieve_logout_format, tab);
@@ -264,7 +259,7 @@ void client_destroy(struct client *client, const char *reason)
 	sieve_deinit(&client->svinst);
 
 	pool_unref(&client->cmd.pool);
-	i_free(client);
+	pool_unref(&client->pool);
 
 	master_service_client_connection_destroyed(master_service);
 	managesieve_refresh_proctitle();
diff --git a/src/managesieve/managesieve-client.h b/src/managesieve/managesieve-client.h
index 50f9290e2e85677d2afd315a71908b75c146e675..c5b24c0681f15175cc2aba8940c05cff4cb834a0 100644
--- a/src/managesieve/managesieve-client.h
+++ b/src/managesieve/managesieve-client.h
@@ -35,12 +35,14 @@ extern struct managesieve_module_register managesieve_module_register;
 struct client {
 	struct client *prev, *next;
 
+	const char *session_id;
 	int fd_in, fd_out;
 	struct io *io;
 	struct istream *input;
 	struct ostream *output;
 	struct timeout *to_idle, *to_idle_output;
 
+	pool_t pool;
 	struct mail_storage_service_user *service_user;
 	const struct managesieve_settings *set;
 
@@ -71,9 +73,10 @@ extern unsigned int managesieve_client_count;
 
 /* Create new client with specified input/output handles. socket specifies
    if the handle is a socket. */
-struct client *client_create(int fd_in, int fd_out, struct mail_user *user,
-			     struct mail_storage_service_user *service_user,
-			     const struct managesieve_settings *set);
+struct client *client_create
+	(int fd_in, int fd_out, const char *session_id, struct mail_user *user, 
+		struct mail_storage_service_user *service_user,
+		const struct managesieve_settings *set);
 void client_destroy(struct client *client, const char *reason);
 
 void client_dump_capability(struct client *client);
diff --git a/src/managesieve/managesieve-common.h b/src/managesieve/managesieve-common.h
index c9eebdc9f354a0461a6964085f28fbc3df1790d9..6f3591e323c728a2099718d466ef9b668e0307e3 100644
--- a/src/managesieve/managesieve-common.h
+++ b/src/managesieve/managesieve-common.h
@@ -18,6 +18,10 @@
 /* Disconnect client when it sends too many bad commands in a row */
 #define CLIENT_MAX_BAD_COMMANDS 20
 
+#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]"
+
 #include "lib.h"
 #include "managesieve-client.h"
 #include "managesieve-settings.h"
diff --git a/src/plugins/lda-sieve/lda-sieve-plugin.c b/src/plugins/lda-sieve/lda-sieve-plugin.c
index 3a4c02df88b7c1952c761d84a802751e1fd34039..dd41984a145fc19578f624a3b3222508761acd85 100644
--- a/src/plugins/lda-sieve/lda-sieve-plugin.c
+++ b/src/plugins/lda-sieve/lda-sieve-plugin.c
@@ -14,6 +14,8 @@
 #include "lda-settings.h"
 
 #include "sieve.h"
+#include "sieve-script.h"
+#include "sieve-script-file.h"
 
 #include "lda-sieve-plugin.h"
 
@@ -25,7 +27,7 @@
  * Configuration
  */
 
-#define SIEVE_DEFAULT_PATH "~/.dovecot.sieve"
+#define LDA_SIEVE_DEFAULT_LOCATION "~/.dovecot.sieve"
 
 #define LDA_SIEVE_MAX_USER_ERRORS 30
 
@@ -39,21 +41,6 @@ static deliver_mail_func_t *next_deliver_mail;
  * Settings handling
  */
 
-static const char *lda_sieve_get_homedir
-(void *context)
-{
-	struct mail_deliver_context *mdctx = (struct mail_deliver_context *)context;
-	const char *home = NULL;
-
-	if ( mdctx == NULL || mdctx->dest_user == NULL )
-		return NULL;
-
-	if ( mail_user_get_home(mdctx->dest_user, &home) <= 0 )
-		return NULL;
-
-	return home;
-}
-
 static const char *lda_sieve_get_setting
 (void *context, const char *identifier)
 {
@@ -72,9 +59,9 @@ static const char *lda_sieve_get_setting
 	return value;
 }
 
-static const struct sieve_environment lda_sieve_env = {
-	lda_sieve_get_homedir,
-    lda_sieve_get_setting
+static const struct sieve_callbacks lda_sieve_callbacks = {
+	NULL,
+	lda_sieve_get_setting
 };
 
 /*
@@ -82,29 +69,30 @@ static const struct sieve_environment lda_sieve_env = {
  */
 
 static void *lda_sieve_smtp_open
-(void *script_ctx, const char *destination,
+(const struct sieve_script_env *senv, const char *destination,
 	const char *return_path, struct ostream **output_r)
 {
 	struct mail_deliver_context *dctx =
-		(struct mail_deliver_context *) script_ctx;
+		(struct mail_deliver_context *) senv->script_context;
 
 	return (void *)smtp_client_open
 		(dctx->set, destination, return_path, output_r);
 }
 
 static bool lda_sieve_smtp_close
-(void *script_ctx ATTR_UNUSED, void *handle)
+(const struct sieve_script_env *senv ATTR_UNUSED, void *handle)
 {
 	struct smtp_client *smtp_client = (struct smtp_client *) handle;
 
 	return ( smtp_client_close(smtp_client) == 0 );
 }
 
-static int lda_sieve_reject_mail(void *script_ctx, const char *recipient,
+static int lda_sieve_reject_mail
+(const struct sieve_script_env *senv, const char *recipient,
 	const char *reason)
 {
 	struct mail_deliver_context *dctx =
-		(struct mail_deliver_context *) script_ctx;
+		(struct mail_deliver_context *) senv->script_context;
 	
 	return mail_send_rejection(dctx, recipient, reason);
 }
@@ -114,20 +102,22 @@ static int lda_sieve_reject_mail(void *script_ctx, const char *recipient,
  */
 
 static int lda_sieve_duplicate_check
-(void *script_ctx, const void *id, size_t id_size, const char *user)
+(const struct sieve_script_env *senv, const void *id, size_t id_size)
 {
-	struct mail_deliver_context *dctx = (struct mail_deliver_context *) script_ctx;
+	struct mail_deliver_context *dctx =
+		(struct mail_deliver_context *) senv->script_context;
 
-	return duplicate_check(dctx->dup_ctx, id, id_size, user);
+	return duplicate_check(dctx->dup_ctx, id, id_size, senv->user->username);
 }
 
 static void lda_sieve_duplicate_mark
-(void *script_ctx, const void *id, size_t id_size, const char *user,
+(const struct sieve_script_env *senv, const void *id, size_t id_size,
 	time_t time)
 {
-	struct mail_deliver_context *dctx = (struct mail_deliver_context *) script_ctx;
+	struct mail_deliver_context *dctx =
+		(struct mail_deliver_context *) senv->script_context;
 
-	duplicate_mark(dctx->dup_ctx, id, id_size, user, time);
+	duplicate_mark(dctx->dup_ctx, id, id_size, senv->user->username, time);
 }
 
 /*
@@ -139,11 +129,11 @@ struct lda_sieve_run_context {
 
 	struct mail_deliver_context *mdctx;
 
-	const char *const *script_files;
+	struct sieve_script **scripts;
 	unsigned int script_count;
 
-	const char *user_script;
-	const char *main_script;
+	struct sieve_script *user_script;
+	struct sieve_script *main_script;
 
 	const struct sieve_message_data *msgdata;
 	const struct sieve_script_env *scriptenv;
@@ -153,172 +143,173 @@ struct lda_sieve_run_context {
 	const char *userlog;
 };
 
-static const char *lda_sieve_get_personal_path
+static const char *lda_sieve_get_personal_location
 (struct sieve_instance *svinst, struct mail_user *user)
 {
-	const char *script_path, *home;
+	const char *script_location;
 
-	if ( mail_user_get_home(user, &home) <= 0 )
-        home = NULL;
+	script_location = mail_user_plugin_getenv(user, "sieve");
 
-	script_path = mail_user_plugin_getenv(user, "sieve");
+	/* userdb may specify Sieve location */
+	if (script_location != NULL) {
 
-	/* userdb may specify Sieve path */
-	if (script_path != NULL) {
-		if (*script_path == '\0') {
+		if (*script_location == '\0') {
 			/* disabled */
 			if ( user->mail_debug )
-				sieve_sys_debug(svinst, "empty script path, disabled");
+				sieve_sys_debug(svinst, "empty script location, disabled");
 			return NULL;
 		}
-
-		script_path = mail_user_home_expand(user, script_path);
-
-		if (*script_path != '/' && *script_path != '\0') {
-			/* relative path. change to absolute. */
-
-			if ( home == NULL || *home == '\0' ) {
-				sieve_sys_error(svinst, "relative script path, but empty home dir: %s", script_path);
-				return NULL;
-			}
-
-			script_path = t_strconcat(home, "/", script_path, NULL);
-		}
 	} else {
-		if ( home == NULL || *home == '\0' ) {
-			sieve_sys_error(svinst, 
-				"path to user's main active personal script is unknown. "
-				"See http://wiki2.dovecot.org/Pigeonhole/Sieve/Configuration");
-			return NULL;
-		}
-
-		script_path = mail_user_home_expand(user, SIEVE_DEFAULT_PATH);
+		script_location = LDA_SIEVE_DEFAULT_LOCATION;
 	}
 
-	return script_path;
+	return script_location;
 }
 
-static const char *lda_sieve_get_default_path
+static const char *lda_sieve_get_default_location
 (struct mail_user *user)
 {
-	const char *script_path;
+	const char *script_location;
 
-	/* Use default script path, if one exists */
-	script_path = mail_user_plugin_getenv(user, "sieve_default");
-	if ( script_path == NULL ) {
+	/* Use default script location, if one exists */
+	script_location = mail_user_plugin_getenv(user, "sieve_default");
+	if ( script_location == NULL ) {
 		/* For backwards compatibility */
-		script_path = mail_user_plugin_getenv(user, "sieve_global_path");
+		script_location = mail_user_plugin_getenv(user, "sieve_global_path");
 	}
 
-	return script_path;
+	return script_location;
 }
 
-static int lda_sieve_multiscript_get_scriptfiles
-(struct sieve_instance *svinst, const char *script_path,
-	ARRAY_TYPE(const_string) *scriptfiles)
+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)
 {
 	struct sieve_directory *sdir;
 	enum sieve_error error;
+	ARRAY_TYPE(const_string) script_files;
+	const char *const *files;
+	unsigned int count, i;
 	const char *file;
 
-	if ( (sdir=sieve_directory_open(svinst, script_path, &error)) == NULL )
+	// FIXME: make this a generic iteration API
+	if ( (sdir=sieve_directory_open(svinst, location, &error)) == NULL )
 		return ( error == SIEVE_ERROR_NOT_FOUND ? 0 : -1 );
 
-	while ( (file=sieve_directory_get_scriptfile(sdir)) != NULL ) {
-		const char *const *scripts;
-		unsigned int count, i;
+	t_array_init(&script_files, 16);
 
+	while ( (file=sieve_directory_get_scriptfile(sdir)) != NULL ) {
 		/* Insert into sorted array */
 
-		scripts = array_get(scriptfiles, &count);
+		files = array_get(&script_files, &count);
 		for ( i = 0; i < count; i++ ) {
-			if ( strcmp(file, scripts[i]) < 0 )
+			if ( strcmp(file, files[i]) < 0 )
 				break;
 		}
 
 		if ( i == count ) 
-			array_append(scriptfiles, &file, 1);
+			array_append(&script_files, &file, 1);
 		else
-			array_insert(scriptfiles, i, &file, 1);
+			array_insert(&script_files, i, &file, 1);
 	}
 
 	sieve_directory_close(&sdir);
+
+	files = array_get(&script_files, &count);
+	for ( i = 0; i < count; i++ ) {
+		struct sieve_script *script = sieve_script_create
+			(svinst, files[i], NULL, ehandler, &error);
+
+		if ( script == NULL ) {
+			switch ( errno ) {
+			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]);
+				break;
+		
+			default:
+				sieve_sys_error
+					(svinst, "failed to access %s script %s", label, files[i]);
+				break;
+			}
+
+			continue;
+		}
+
+		array_append(scripts, &script, 1);
+	}
+
 	return 1;
 }
 
 static void lda_sieve_binary_save
 (struct lda_sieve_run_context *srctx, struct sieve_binary *sbin, 
-	unsigned int script_index)
+	struct sieve_script *script)
 {
-	const char *script_path = srctx->script_files[script_index];
 	enum sieve_error error;
 
 	/* Save binary when compiled */
-	if ( sieve_save(sbin, NULL, FALSE, &error) < 0 &&
-		error == SIEVE_ERROR_NO_PERM  && script_path != srctx->user_script ) {
+	if ( sieve_save(sbin, FALSE, &error) < 0 &&
+		error == SIEVE_ERROR_NO_PERM && script != srctx->user_script ) {
+
 		/* 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", script_path);
+			"pre-compiled using the sievec tool", sieve_script_location(script));
 	}
 }
 
 static struct sieve_binary *lda_sieve_open
-(struct lda_sieve_run_context *srctx, unsigned int script_index,
+(struct lda_sieve_run_context *srctx, struct sieve_script *script,
 	enum sieve_compile_flags cpflags, enum sieve_error *error_r)
 {
 	struct sieve_instance *svinst = srctx->svinst;
-	const char *script_path = srctx->script_files[script_index];
-	const char *script_name =
-		( script_path == srctx->main_script ? "main_script" : NULL );
 	struct sieve_error_handler *ehandler;
 	struct sieve_binary *sbin;
 	bool debug = srctx->mdctx->dest_user->mail_debug;
 
-	if ( script_path == srctx->user_script )
+	if ( script == srctx->user_script )
 		ehandler = srctx->user_ehandler;
 	else
 		ehandler = srctx->master_ehandler;
 
 	if ( debug )
-		sieve_sys_debug(svinst, "opening script %s", script_path);
+		sieve_sys_debug(svinst, "opening script %s", sieve_script_location(script));
 
 	sieve_error_handler_reset(ehandler);
 
 	/* Open the sieve script */
-	if ( (sbin=sieve_open
-		(svinst, script_path, script_name, ehandler, cpflags, error_r)) == NULL ) {
+	if ( (sbin=sieve_open_script(script, ehandler, cpflags, error_r)) == NULL ) {
 		if ( *error_r == SIEVE_ERROR_NOT_FOUND ) {
-			if ( debug )
-				sieve_sys_debug(svinst, "script file %s is missing", script_path);
+			if ( debug ) {
+				sieve_sys_debug(svinst, "script file %s is missing",
+					sieve_script_location(script));
+			}
 		} else if ( *error_r == SIEVE_ERROR_NOT_VALID && 
-			script_path == srctx->user_script && srctx->userlog != NULL ) {
-			sieve_sys_error(svinst,
-				"failed to open script %s "
+			script == srctx->user_script && srctx->userlog != NULL ) {
+			sieve_sys_error(svinst,	"failed to open script %s "
 				"(view user logfile %s for more information)",
-				script_path, srctx->userlog);
+				sieve_script_location(script), srctx->userlog);
 		} else {
-			sieve_sys_error(svinst,
-				"failed to open script %s", script_path);
+			sieve_sys_error(svinst,	"failed to open script %s",
+				sieve_script_location(script));
 		}
 
 		return NULL;
 	}
 
-	lda_sieve_binary_save(srctx, sbin, script_index);
+	lda_sieve_binary_save(srctx, sbin, script);
 	return sbin;
 }
 
 static struct sieve_binary *lda_sieve_recompile
-(struct lda_sieve_run_context *srctx, unsigned int script_index,
+(struct lda_sieve_run_context *srctx, struct sieve_script *script,
 	enum sieve_compile_flags cpflags, enum sieve_error *error_r)
 {
 	struct sieve_instance *svinst = srctx->svinst;
-	const char *script_path = srctx->script_files[script_index];
-	const char *script_name =
-		( script_path == srctx->main_script ? "main_script" : NULL );
 	struct sieve_error_handler *ehandler;
 	struct sieve_binary *sbin;
 	bool debug = srctx->mdctx->dest_user->mail_debug;
@@ -326,31 +317,32 @@ static struct sieve_binary *lda_sieve_recompile
 	/* Warn */
 
 	sieve_sys_warning(svinst,
-		"encountered corrupt binary: re-compiling script %s", script_path);
+		"encountered corrupt binary: re-compiling script %s",
+		sieve_script_location(script));
 
 	/* Recompile */
 
-	if ( script_path == srctx->user_script )
+	if ( script == srctx->user_script )
 		ehandler = srctx->user_ehandler;
 	else
 		ehandler = srctx->master_ehandler;
 
-	if ( (sbin=sieve_compile
-		(svinst, script_path, script_name, ehandler, cpflags, error_r)) == NULL ) {
+	if ( (sbin=sieve_compile_script(script, ehandler,	cpflags, error_r))
+		== NULL ) {
 
 		if ( *error_r == SIEVE_ERROR_NOT_FOUND ) {
 			if ( debug )
 				sieve_sys_debug(svinst, "script file %s is missing for re-compile", 
-					script_path);
+					sieve_script_location(script));
 		} else if ( *error_r == SIEVE_ERROR_NOT_VALID && 
-			script_path == srctx->user_script && srctx->userlog != NULL ) {
+			script == srctx->user_script && srctx->userlog != NULL ) {
 			sieve_sys_error(svinst,
 				"failed to re-compile script %s "
 				"(view user logfile %s for more information)",
-				script_path, srctx->userlog);
+				sieve_script_location(script), srctx->userlog);
 		} else {
 			sieve_sys_error(svinst,
-				"failed to re-compile script %s", script_path);
+				"failed to re-compile script %s", sieve_script_location(script));
 		}
 
 		return NULL;
@@ -360,13 +352,13 @@ static struct sieve_binary *lda_sieve_recompile
 }
 
 static int lda_sieve_handle_exec_status
-(struct lda_sieve_run_context *srctx, const char *script_path, int status)
+(struct lda_sieve_run_context *srctx, struct sieve_script *script, int status)
 {
 	struct sieve_instance *svinst = srctx->svinst;
 	const char *userlog_notice = "";
 	int ret;
 
-	if ( script_path == srctx->user_script && srctx->userlog != NULL ) {
+	if ( script == srctx->user_script && srctx->userlog != NULL ) {
 		userlog_notice = t_strdup_printf
 			(" (user logfile %s may reveal additional details)", srctx->userlog);
 	}
@@ -375,20 +367,20 @@ static int lda_sieve_handle_exec_status
 	case SIEVE_EXEC_FAILURE:
 		sieve_sys_error(svinst,
 			"execution of script %s failed, but implicit keep was successful%s", 
-			script_path, userlog_notice);
+			sieve_script_location(script), userlog_notice);
 		ret = 1;
 		break;
 	case SIEVE_EXEC_BIN_CORRUPT:
 		sieve_sys_error(svinst,
 			"!!BUG!!: binary compiled from %s is still corrupt; "
 			"bailing out and reverting to default delivery", 
-			script_path);
+			sieve_script_location(script));
 		ret = -1;
 		break;
 	case SIEVE_EXEC_KEEP_FAILED:
 		sieve_sys_error(svinst,
 			"script %s failed with unsuccessful implicit keep%s", 
-			script_path, userlog_notice);
+			sieve_script_location(script), userlog_notice);
 		ret = -1;
 		break;
 	default:
@@ -403,8 +395,8 @@ static int lda_sieve_singlescript_execute
 (struct lda_sieve_run_context *srctx)
 {
 	struct sieve_instance *svinst = srctx->svinst;
-	const char *script_file = srctx->script_files[0];
-	bool user_script = ( script_file == srctx->user_script );
+	struct sieve_script *script = srctx->scripts[0];
+	bool user_script = ( script == srctx->user_script );
 	struct sieve_error_handler *ehandler;
 	struct sieve_binary *sbin;
 	bool debug = srctx->mdctx->dest_user->mail_debug;
@@ -423,7 +415,7 @@ static int lda_sieve_singlescript_execute
 
 	/* Open the script */
 
-	if ( (sbin=lda_sieve_open(srctx, 0, cpflags, &error)) == NULL )
+	if ( (sbin=lda_sieve_open(srctx, script, cpflags, &error)) == NULL )
 		return ( error == SIEVE_ERROR_NOT_FOUND ? 0 : -1 );
 
 	/* Execute */
@@ -443,7 +435,7 @@ static int lda_sieve_singlescript_execute
 
 		/* Recompile */
 
-		if ( (sbin=lda_sieve_recompile(srctx, 0, cpflags, &error)) == NULL )
+		if ( (sbin=lda_sieve_recompile(srctx, script, cpflags, &error)) == NULL )
 			return ( error == SIEVE_ERROR_NOT_FOUND ? 0 : -1 );
 
 		/* Execute again */
@@ -458,25 +450,25 @@ static int lda_sieve_singlescript_execute
 		/* Save new version */
 
 		if ( ret != SIEVE_EXEC_BIN_CORRUPT )
-			lda_sieve_binary_save(srctx, sbin, 0);
+			lda_sieve_binary_save(srctx, sbin, script);
 	}
 
 	sieve_close(&sbin);
 
 	/* Report status */
-	return lda_sieve_handle_exec_status(srctx, script_file, ret);
+	return lda_sieve_handle_exec_status(srctx, script, ret);
 }
 
 static int lda_sieve_multiscript_execute
 (struct lda_sieve_run_context *srctx)
 {
 	struct sieve_instance *svinst = srctx->svinst;
-	const char *const *scripts = srctx->script_files;
+	struct sieve_script *const *scripts = srctx->scripts;
 	unsigned int count = srctx->script_count;
 	struct sieve_multiscript *mscript;
 	struct sieve_error_handler *ehandler = srctx->master_ehandler;
 	bool debug = srctx->mdctx->dest_user->mail_debug;
-	const char *last_script = NULL;
+	struct sieve_script *last_script = NULL;
 	bool user_script = FALSE;
 	unsigned int i;
 	int ret = 1; 
@@ -488,17 +480,17 @@ static int lda_sieve_multiscript_execute
 	mscript = sieve_multiscript_start_execute
 		(svinst, srctx->msgdata, srctx->scriptenv);
 
-	/* Execute scripts before main script */
+	/* Execute scripts */
 
 	for ( i = 0; i < count && more; i++ ) {
 		struct sieve_binary *sbin = NULL;
-		const char *script_file = scripts[i];
+		struct sieve_script *script = scripts[i];
 		enum sieve_compile_flags cpflags = 0;
 		enum sieve_runtime_flags rtflags = 0;
 		bool final = ( i == count - 1 );
 
-		user_script = ( script_file == srctx->user_script );
-		last_script = script_file;
+		user_script = ( script == srctx->user_script );
+		last_script = script;
 
 		if ( user_script ) {
 			cpflags |= SIEVE_COMPILE_FLAG_NOGLOBAL;
@@ -510,7 +502,7 @@ static int lda_sieve_multiscript_execute
 
 		/* Open */
 
-		if ( (sbin=lda_sieve_open(srctx, i, cpflags, &error)) == NULL ) {
+		if ( (sbin=lda_sieve_open(srctx, script, cpflags, &error)) == NULL ) {
 			ret = ( error == SIEVE_ERROR_NOT_FOUND ? 0 : -1 );
 			break;
 		}
@@ -532,7 +524,8 @@ static int lda_sieve_multiscript_execute
 
 				/* Recompile */
 
-				if ( (sbin=lda_sieve_recompile(srctx, i, cpflags, &error)) == NULL ) {
+				sbin=lda_sieve_recompile(srctx, script, cpflags, &error);
+				if ( sbin	== NULL ) {
 					ret = ( error == SIEVE_ERROR_NOT_FOUND ? 0 : -1 );
 					break;
 				}
@@ -544,8 +537,9 @@ static int lda_sieve_multiscript_execute
 				/* Save new version */
 
 				if ( more && 
-					sieve_multiscript_status(mscript) != SIEVE_EXEC_BIN_CORRUPT )
-					lda_sieve_binary_save(srctx, sbin, i);
+					sieve_multiscript_status(mscript) != SIEVE_EXEC_BIN_CORRUPT ) {
+					lda_sieve_binary_save(srctx, sbin, script);
+				}
 			}
 		}
 
@@ -559,133 +553,32 @@ static int lda_sieve_multiscript_execute
 	return lda_sieve_handle_exec_status(srctx, last_script, ret);
 }
 
-static int lda_sieve_run
-(struct mail_deliver_context *mdctx, struct sieve_instance *svinst,
-	const char *user_script, const char *default_script,
-	const ARRAY_TYPE (const_string) *scripts_before,
-	const ARRAY_TYPE (const_string) *scripts_after,
-	struct mail_storage **storage_r)
+static int lda_sieve_deliver_mail
+(struct mail_deliver_context *mdctx, struct mail_storage **storage_r)
 {
-	ARRAY_TYPE (const_string) scripts;
 	struct lda_sieve_run_context srctx;
+	struct sieve_environment svenv;
+	struct sieve_instance *svinst;
 	struct sieve_message_data msgdata;
 	struct sieve_script_env scriptenv;
 	struct sieve_exec_status estatus;
-	int ret = 0;
-
-	*storage_r = NULL;
-
-	/* Initialize */
-
-	memset(&srctx, 0, sizeof(srctx));
-	srctx.svinst = svinst;
-	srctx.mdctx = mdctx;
-
-	/* Compose execution sequence */
-
-	t_array_init(&scripts, 32);
-
-	array_append_array(&scripts, scripts_before);
-
-	if ( user_script != NULL ) {
-		array_append(&scripts, &user_script, 1);
-		srctx.user_script = user_script;
-		srctx.main_script = user_script;
-	} else if ( default_script != NULL ) {
-		array_append(&scripts, &default_script, 1);
-		srctx.user_script = NULL;
-		srctx.main_script = default_script;
-	} else {
-		srctx.user_script = NULL;
-		srctx.main_script = NULL;
-	}
-
-	array_append_array(&scripts, scripts_after);
-
-	/* Initialize error handlers */
-
-	srctx.master_ehandler = sieve_system_ehandler_get(svinst);
-
-	if ( user_script != NULL ) {
-		srctx.userlog = t_strconcat(user_script, ".log", NULL);
-		srctx.user_ehandler = sieve_logfile_ehandler_create
-			(svinst, srctx.userlog, LDA_SIEVE_MAX_USER_ERRORS);
-	}
-
-	/* Collect necessary message data */
-
-	memset(&msgdata, 0, sizeof(msgdata));
-
-	msgdata.mail = mdctx->src_mail;
-	msgdata.return_path = mail_deliver_get_return_address(mdctx);
-	msgdata.orig_envelope_to = mdctx->dest_addr;
-	msgdata.final_envelope_to = mdctx->final_dest_addr;
-	msgdata.auth_user = mdctx->dest_user->username;
-	(void)mail_get_first_header(msgdata.mail, "Message-ID", &msgdata.id);
-
-	srctx.msgdata = &msgdata;
-
-	/* Compose script execution environment */
-
-	memset(&scriptenv, 0, sizeof(scriptenv));
-	memset(&estatus, 0, sizeof(estatus));
-
-	scriptenv.action_log_format = mdctx->set->deliver_log_format;
-	scriptenv.default_mailbox = mdctx->dest_mailbox_name;
-	scriptenv.mailbox_autocreate = mdctx->set->lda_mailbox_autocreate;
-	scriptenv.mailbox_autosubscribe = mdctx->set->lda_mailbox_autosubscribe;
-	scriptenv.user = mdctx->dest_user;
-	scriptenv.username = mdctx->dest_user->username;
-	scriptenv.hostname = mdctx->set->hostname;
-	scriptenv.postmaster_address = mdctx->set->postmaster_address;
-	scriptenv.smtp_open = lda_sieve_smtp_open;
-	scriptenv.smtp_close = lda_sieve_smtp_close;
-	scriptenv.duplicate_mark = lda_sieve_duplicate_mark;
-	scriptenv.duplicate_check = lda_sieve_duplicate_check;
-	scriptenv.reject_mail = lda_sieve_reject_mail;
-	scriptenv.script_context = (void *) mdctx;
-	scriptenv.exec_status = &estatus;
-
-	srctx.scriptenv = &scriptenv;
-
-	/* Assign script sequence */
-
-	srctx.script_files = array_get(&scripts, &srctx.script_count);
-
-	/* Execute script(s) */
-
-	if ( srctx.script_count == 1 )
-		ret = lda_sieve_singlescript_execute(&srctx);
-	else
-		ret = lda_sieve_multiscript_execute(&srctx);
-
-	/* Record status */
-
-	mdctx->tried_default_save = estatus.tried_default_save;
-	*storage_r = estatus.last_storage;
-
-	/* Clean up */
-
-	if ( srctx.user_ehandler != NULL )
-		sieve_error_handler_unref(&srctx.user_ehandler);
-
-	return ret;
-}
-
-static int lda_sieve_deliver_mail
-(struct mail_deliver_context *mdctx, struct mail_storage **storage_r)
-{
-	struct sieve_instance *svinst;
-	struct sieve_error_handler *master_ehandler; 
-	const char *user_script, *default_script, *sieve_before, *sieve_after;
-	ARRAY_TYPE (const_string) scripts_before;
-	ARRAY_TYPE (const_string) scripts_after;
+	struct sieve_error_handler *master_ehandler;
+	const char *user_location, *default_location, *sieve_before, *sieve_after;
+	ARRAY_TYPE(sieve_scripts) script_sequence;
 	bool debug = mdctx->dest_user->mail_debug;
+	enum sieve_error error;
 	int ret = 0;
 
 	/* Initialize Sieve engine */
 
-	svinst = sieve_init(&lda_sieve_env, mdctx, debug);
+	memset((void*)&svenv, 0, sizeof(svenv));
+	svenv.username = mdctx->dest_user->username;
+	(void)mail_user_get_home(mdctx->dest_user, &svenv.home_dir);
+	svenv.hostname = mdctx->set->hostname;
+	svenv.base_dir = mdctx->dest_user->set->base_dir;
+	svenv.flags = SIEVE_FLAG_HOME_RELATIVE;
+
+	svinst = sieve_init(&svenv, &lda_sieve_callbacks, mdctx, debug);
 
 	/* Initialize master error handler */
 
@@ -700,105 +593,224 @@ static int lda_sieve_deliver_mail
 	/* Find scripts and run them */
 
 	T_BEGIN { 
-		struct stat st;
+		struct sieve_script *const *scripts;
+		unsigned int count, i;
 
-		/* Find the personal script to execute */
+		/* Initialize run context */
 
-		user_script = lda_sieve_get_personal_path(svinst, mdctx->dest_user);
-		default_script = lda_sieve_get_default_path(mdctx->dest_user);
+		memset(&srctx, 0, sizeof(srctx));
+		srctx.svinst = svinst;
+		srctx.mdctx = mdctx;
+		srctx.master_ehandler = master_ehandler;
 
-		if ( user_script != NULL && stat(user_script, &st) < 0 ) {
+		/* Find the personal script to execute */
 
-			switch ( errno ) {
-			case ENOENT:
-				if ( debug )
-					sieve_sys_debug(svinst, "user's script path %s doesn't exist "
-						"(using global script path in stead)", user_script);
-				break;
-			case EACCES:
-				sieve_sys_error(svinst, "failed to stat user's sieve script: %s "
-					"(using global script path in stead)",
-					eacces_error_get("stat", user_script));
-				break;
-			default:
-				sieve_sys_error(svinst, "failed to stat user's sieve script: "
-					"stat(%s) failed: %m (using global script path in stead)",
-					user_script);
-				break;
+		user_location = lda_sieve_get_personal_location(svinst, mdctx->dest_user);
+		if ( user_location != NULL ) {
+			srctx.user_script = sieve_script_create_as
+				(svinst, user_location, "main script", master_ehandler, &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 "
+							"(using default script location instead)", user_location);
+					break;
+				default:
+					sieve_sys_error(svinst, "failed to access user's sieve script %s "
+						"(using default script location instead)",
+						user_location);
+					break;
+				}
+			} else {
+				srctx.main_script = srctx.user_script;
 			}
-
-			user_script = NULL;
 		}
 
-		if ( debug ) {
-			const char *script = user_script == NULL ? default_script : user_script;
+		if ( srctx.user_script == NULL ) {
+			default_location = lda_sieve_get_default_location(mdctx->dest_user);
+			if ( default_location != NULL ) {
+				srctx.main_script = sieve_script_create_as
+					(svinst, default_location, "main script", master_ehandler, &error);
 
-			if ( script == NULL )
-				sieve_sys_debug(svinst, "user has no valid personal script");
-			else
-				sieve_sys_debug(svinst, "using sieve path for user's script: %s", script);
+				if ( srctx.main_script == NULL && error == SIEVE_ERROR_NOT_FOUND &&
+					debug ) {
+					sieve_sys_debug(svinst, "default user script %s doesn't exist",
+						default_location);
+				}
+			} else {
+				sieve_sys_debug(svinst, "no default script configured for user");
+			}
 		}
 
-		/* Check for multiscript */
+		if ( debug && srctx.main_script == NULL ) {
+			sieve_sys_debug(svinst,
+				"user has no valid location for a personal script");
+		}
 
-		t_array_init(&scripts_before, 16);
-		t_array_init(&scripts_after, 16);
+		/* Compose script array */
 
-		sieve_before = mail_user_plugin_getenv(mdctx->dest_user, "sieve_before");
-		sieve_after = mail_user_plugin_getenv(mdctx->dest_user, "sieve_after");
+		t_array_init(&script_sequence, 16);
 
+		sieve_before = mail_user_plugin_getenv(mdctx->dest_user, "sieve_before");
 		if ( sieve_before != NULL && *sieve_before != '\0' ) {
-			if ( lda_sieve_multiscript_get_scriptfiles
-				(svinst, sieve_before, &scripts_before) == 0 && debug ) {
-				sieve_sys_debug(svinst, "sieve_before path not found: %s", sieve_before);
+			if ( lda_sieve_multiscript_get_scripts(svinst, "sieve_before",
+				sieve_before, master_ehandler, &script_sequence) == 0 && debug ) {
+				sieve_sys_debug(svinst, "sieve_before location not found: %s",
+					sieve_before);
+			}
+
+			if ( debug ) {
+				scripts = array_get(&script_sequence, &count);
+				for ( i = 0; i < count; i ++ ) {
+					sieve_sys_debug(svinst, "executed before user's Sieve script(%d): %s",
+						i+1, sieve_script_location(scripts[i]));
+				}				
 			}
 		}
 
-		if ( sieve_after != NULL && *sieve_after != '\0' ) {
-			if ( lda_sieve_multiscript_get_scriptfiles
-				(svinst, sieve_after, &scripts_after) == 0 && debug ) {
-				sieve_sys_debug(svinst, "sieve_after path not found: %s", sieve_after);
+		if ( srctx.main_script != NULL ) {
+			array_append(&script_sequence, &srctx.main_script, 1);
+
+			if ( debug ) {
+				sieve_sys_debug(svinst,
+					"using the following location for user's Sieve script: %s",
+					sieve_script_location(srctx.main_script));
 			}
 		}
 
-		if ( debug ) {
-			const char *const *scriptfiles;
-			unsigned int count, i;
+		sieve_after = mail_user_plugin_getenv(mdctx->dest_user, "sieve_after");
+		if ( sieve_after != NULL && *sieve_after != '\0' ) {
+			unsigned int after_index = array_count(&script_sequence);
 
-			scriptfiles = array_get(&scripts_before, &count);
-			for ( i = 0; i < count; i ++ ) {
-				sieve_sys_debug(svinst, "executed before user's script(%d): %s",
-					i+1, scriptfiles[i]);
+			if ( lda_sieve_multiscript_get_scripts(svinst, "sieve_after",
+				sieve_after, master_ehandler, &script_sequence) == 0 && debug ) {
+				sieve_sys_debug(svinst, "sieve_after location not found: %s",
+					sieve_after);
 			}
 
-			scriptfiles = array_get(&scripts_after, &count);
-			for ( i = 0; i < count; i ++ ) {
-				sieve_sys_debug(svinst, "executed after user's script(%d): %s",
-					i+1, scriptfiles[i]);
+			if ( debug ) {
+				scripts = array_get(&script_sequence, &count);
+				for ( i = after_index; i < count; i ++ ) {
+					sieve_sys_debug(svinst, "executed after user's Sieve script(%d): %s",
+						i+1, sieve_script_location(scripts[i]));
+				}				
 			}
 		}
 
-		/* Check whether there are any scripts to execute */
+		srctx.scripts =
+			array_get_modifiable(&script_sequence, &srctx.script_count);
 
-		if ( array_count(&scripts_before) == 0 && array_count(&scripts_after) == 0 &&
-			user_script == NULL && default_script == NULL ) {
-			if ( debug )
-				sieve_sys_debug(svinst, "no scripts to execute: reverting to default delivery.");
+		/* Check whether there are any scripts to execute at all */
+
+		if ( srctx.script_count == 0 ) {
+			if ( debug ) {
+				sieve_sys_debug(svinst,
+					"no scripts to execute: reverting to default delivery.");
+			}
 
-			/* No error, but no delivery by this plugin either. A return value of <= 0 for a 
-			 * deliver plugin is is considered a failure. In deliver itself, saved_mail and 
-			 * tried_default_save remain unset, meaning that deliver will then attempt the 
-			 * default delivery. We return 0 to signify the lack of a real error. 
+			/* No error, but no delivery by this plugin either. A return value of <= 0
+			 * for a deliver plugin is is considered a failure. In deliver itself,
+			 * saved_mail and tried_default_save remain unset, meaning that deliver
+			 * will then attempt the default delivery. We return 0 to signify the lack
+			 * of a real error. 
 			 */
 			ret = 0; 
 		} else {
-			/* Run the script(s) */
+			/* Initialize user error handler */
+
+			if ( srctx.user_script != NULL ) {
+				const char *log_path = NULL;
+
+				/* Determine user log file path */
+				if ( (log_path=mail_user_plugin_getenv
+					(mdctx->dest_user, "sieve_user_log")) == NULL ) {
+					const char *path;
+
+					if ( (path=sieve_file_script_get_path(srctx.user_script)) == NULL ) {
+						/* Default */
+						if ( svenv.home_dir != NULL ) {
+							log_path = t_strconcat
+								(svenv.home_dir, "/.dovecot.sieve.log", NULL);
+						}
+					} else {
+						/* Use script file as a basic (legacy behavior) */
+						log_path = t_strconcat(path, ".log", NULL);	
+					}	
+				} else if ( svenv.home_dir != NULL ) {
+					/* Expand home dir if necessary */
+					if ( log_path[0] == '~' ) {
+						log_path = home_expand_tilde(log_path, svenv.home_dir);
+					} else if ( log_path[0] != '/' ) {
+						log_path = t_strconcat(svenv.home_dir, "/", log_path, NULL);
+					}
+				}
 
-			ret = lda_sieve_run
-				(mdctx, svinst, user_script, default_script, &scripts_before,
-					&scripts_after, storage_r);
+				if ( log_path != NULL ) {
+					srctx.userlog = log_path;
+					srctx.user_ehandler = sieve_logfile_ehandler_create
+						(svinst, srctx.userlog, LDA_SIEVE_MAX_USER_ERRORS);
+				}
+			}
+
+			/* Collect necessary message data */
+
+			memset(&msgdata, 0, sizeof(msgdata));
+
+			msgdata.mail = mdctx->src_mail;
+			msgdata.return_path = mail_deliver_get_return_address(mdctx);
+			msgdata.orig_envelope_to = mdctx->dest_addr;
+			msgdata.final_envelope_to = mdctx->final_dest_addr;
+			msgdata.auth_user = mdctx->dest_user->username;
+			(void)mail_get_first_header(msgdata.mail, "Message-ID", &msgdata.id);
+
+			srctx.msgdata = &msgdata;
+
+			/* Compose script execution environment */
+
+			memset(&scriptenv, 0, sizeof(scriptenv));
+			memset(&estatus, 0, sizeof(estatus));
+
+			scriptenv.action_log_format = mdctx->set->deliver_log_format;
+			scriptenv.default_mailbox = mdctx->dest_mailbox_name;
+			scriptenv.mailbox_autocreate = mdctx->set->lda_mailbox_autocreate;
+			scriptenv.mailbox_autosubscribe = mdctx->set->lda_mailbox_autosubscribe;
+			scriptenv.user = mdctx->dest_user;
+			scriptenv.postmaster_address = mdctx->set->postmaster_address;
+			scriptenv.smtp_open = lda_sieve_smtp_open;
+			scriptenv.smtp_close = lda_sieve_smtp_close;
+			scriptenv.duplicate_mark = lda_sieve_duplicate_mark;
+			scriptenv.duplicate_check = lda_sieve_duplicate_check;
+			scriptenv.reject_mail = lda_sieve_reject_mail;
+			scriptenv.script_context = (void *) mdctx;
+			scriptenv.exec_status = &estatus;
+
+			srctx.scriptenv = &scriptenv;
+
+			/* Execute script(s) */
+
+			if ( srctx.script_count == 1 )
+				ret = lda_sieve_singlescript_execute(&srctx);
+			else
+				ret = lda_sieve_multiscript_execute(&srctx);
+
+			/* Record status */
+
+			mdctx->tried_default_save = estatus.tried_default_save;
+			*storage_r = estatus.last_storage;
+
+			/* Clean up user error handlers */
+
+			if ( srctx.user_ehandler != NULL )
+				sieve_error_handler_unref(&srctx.user_ehandler);
 		}
 
+		/* Cleanup scripts */
+		for ( i = 0; i < srctx.script_count; i++ ) {
+			sieve_script_unref(&srctx.scripts[i]);
+		}				
+
 	} T_END;
 
 	sieve_deinit(&svinst);
diff --git a/src/sieve-tools/sieve-filter.c b/src/sieve-tools/sieve-filter.c
index db39e4b8e12a12ab5110e7713daaca4e81321b46..32c14bd92d30ae502b8ccbbbd01b3a14af002785 100644
--- a/src/sieve-tools/sieve-filter.c
+++ b/src/sieve-tools/sieve-filter.c
@@ -95,7 +95,7 @@ static int filter_message
 	msgdata.return_path = sender;
 	msgdata.orig_envelope_to = recipient;
 	msgdata.final_envelope_to = recipient;
-	msgdata.auth_user = senv->username;
+	msgdata.auth_user = senv->user->username;
 	(void)mail_get_first_header(mail, "Message-ID", &msgdata.id);
 					
 	if ( mail_get_virtual_size(mail, &size) < 0 ) {
@@ -488,7 +488,7 @@ int main(int argc, char **argv)
 	if ( force_compile ) {
 		main_sbin = sieve_tool_script_compile(svinst, scriptfile, NULL);
 		if ( main_sbin != NULL )
-			(void) sieve_save(main_sbin, NULL, TRUE, NULL);
+			(void) sieve_save(main_sbin, TRUE, NULL);
 	} else {
 		main_sbin = sieve_tool_script_open(svinst, scriptfile);
 	}
@@ -537,8 +537,6 @@ int main(int argc, char **argv)
 	scriptenv.mailbox_autocreate = FALSE;
 	scriptenv.default_mailbox = dst_mailbox;
 	scriptenv.user = mail_user;
-	scriptenv.username = sieve_tool_get_username(sieve_tool);
-	scriptenv.hostname = "host.example.com";
 	scriptenv.postmaster_address = "postmaster@example.com";
 	scriptenv.smtp_open = NULL;
 	scriptenv.smtp_close = NULL;
diff --git a/src/sieve-tools/sieve-test.c b/src/sieve-tools/sieve-test.c
index 85937821500e7b143301c12d473121431c8cc9c9..b69ccd4018cd6716a899be222c5544bbd32421dc 100644
--- a/src/sieve-tools/sieve-test.c
+++ b/src/sieve-tools/sieve-test.c
@@ -57,7 +57,7 @@ static void print_help(void)
  */
 
 static void *sieve_smtp_open
-(void *script_ctx ATTR_UNUSED, const char *destination,
+(const struct sieve_script_env *senv ATTR_UNUSED, const char *destination,
 	const char *return_path, struct ostream **output_r)
 {
 	i_info("sending message from <%s> to <%s>:",
@@ -70,7 +70,7 @@ static void *sieve_smtp_open
 }
 
 static bool sieve_smtp_close
-(void *script_ctx ATTR_UNUSED, void *handle ATTR_UNUSED)
+(const struct sieve_script_env *senv ATTR_UNUSED, void *handle ATTR_UNUSED)
 {
 	struct ostream *output = (struct ostream *)handle;
 
@@ -84,19 +84,18 @@ static bool sieve_smtp_close
  */
 
 static int duplicate_check
-(void *script_context ATTR_UNUSED, const void *id ATTR_UNUSED, 
-	size_t id_size ATTR_UNUSED, const char *user)
+(const struct sieve_script_env *senv, const void *id ATTR_UNUSED, 
+	size_t id_size ATTR_UNUSED)
 {
-	i_info("checked duplicate for user %s.\n", user);
+	i_info("checked duplicate for user %s.\n", senv->user->username);
 	return 0;
 }
 
 static void duplicate_mark
-(void *script_context ATTR_UNUSED, const void *id ATTR_UNUSED, 
-	size_t id_size ATTR_UNUSED, const char *user, 
-	time_t time ATTR_UNUSED)
+(const struct sieve_script_env *senv, const void *id ATTR_UNUSED, 
+	size_t id_size ATTR_UNUSED, time_t time ATTR_UNUSED)
 {
-	i_info("marked duplicate for user %s.\n", user);
+	i_info("marked duplicate for user %s.\n", senv->user->username);
 }
 
 /*
@@ -220,12 +219,13 @@ int main(int argc, char **argv)
 	ehandler = sieve_stderr_ehandler_create(svinst, 0);
 	sieve_system_ehandler_set(ehandler);
 	sieve_error_handler_accept_infolog(ehandler, TRUE);
+	sieve_error_handler_accept_debuglog(ehandler, svinst->debug);
 
 	/* Compile main sieve script */
 	if ( force_compile ) {
 		main_sbin = sieve_tool_script_compile(svinst, scriptfile, NULL);
 		if ( main_sbin != NULL )
-			(void) sieve_save(main_sbin, NULL, TRUE, NULL);
+			(void) sieve_save(main_sbin, TRUE, NULL);
 	} else {
 		main_sbin = sieve_tool_script_open(svinst, scriptfile);
 	}
@@ -271,8 +271,6 @@ int main(int argc, char **argv)
 		memset(&scriptenv, 0, sizeof(scriptenv));
 		scriptenv.default_mailbox = "INBOX";
 		scriptenv.user = sieve_tool_get_mail_user(sieve_tool);
-		scriptenv.username = sieve_tool_get_username(sieve_tool);
-		scriptenv.hostname = "host.example.com";
 		scriptenv.postmaster_address = "postmaster@example.com";
 		scriptenv.smtp_open = sieve_smtp_open;
 		scriptenv.smtp_close = sieve_smtp_close;
@@ -326,7 +324,7 @@ int main(int argc, char **argv)
 				if ( force_compile ) {
 					sbin = sieve_tool_script_compile(svinst, sfiles[i], sfiles[i]);
 					if ( sbin != NULL )
-						(void) sieve_save(sbin, NULL, FALSE, NULL);
+						(void) sieve_save(sbin, FALSE, NULL);
 				} else {
 					sbin = sieve_tool_script_open(svinst, sfiles[i]);
 				}
diff --git a/src/sieve-tools/sievec.c b/src/sieve-tools/sievec.c
index b1e1a3cb0691a1b91038f98294e8c7bc5c5f601f..8f0e38cd4a2c802be169a93a1f09bd076002a5fb 100644
--- a/src/sieve-tools/sievec.c
+++ b/src/sieve-tools/sievec.c
@@ -11,6 +11,7 @@
 #include "sieve.h"
 #include "sieve-extensions.h"
 #include "sieve-script.h"
+#include "sieve-script-file.h"
 #include "sieve-tool.h"
 
 #include <stdio.h>
@@ -123,7 +124,7 @@ int main(int argc, char **argv)
 				sbin = sieve_tool_script_compile(svinst, file, dp->d_name);
 
 				if ( sbin != NULL ) {
-					sieve_save(sbin, NULL, TRUE, NULL);		
+					sieve_save(sbin, TRUE, NULL);		
 					sieve_close(&sbin);
 				}
 			}
@@ -143,7 +144,7 @@ int main(int argc, char **argv)
 			if ( dump ) 
 				sieve_tool_dump_binary_to(sbin, outfile, FALSE);
 			else {
-				sieve_save(sbin, outfile, TRUE, NULL);
+				sieve_save_as(sbin, outfile, TRUE, 0600, NULL);
 			}
 		
 			sieve_close(&sbin);
diff --git a/src/testsuite/testsuite-binary.c b/src/testsuite/testsuite-binary.c
index 72f3212f40305a037bc03353f55b74569bea74fe..d8232ef97276285eaf6c421e6f802aa4c93c8065 100644
--- a/src/testsuite/testsuite-binary.c
+++ b/src/testsuite/testsuite-binary.c
@@ -9,10 +9,12 @@
 #include "unlink-directory.h"
 
 #include "sieve.h"
-#include "sieve-common.h" 
+#include "sieve-common.h"
 #include "sieve-script.h"
+#include "sieve-script-file.h"
+#include "sieve-binary.h"
 #include "sieve-error.h"
- 
+
 #include "testsuite-common.h"
 #include "testsuite-binary.h"
 
@@ -62,8 +64,9 @@ void testsuite_binary_reset(void)
 
 bool testsuite_binary_save(struct sieve_binary *sbin, const char *name)
 {
-	return sieve_save(sbin, t_strdup_printf
-		("%s/%s", testsuite_binary_tmp, sieve_binfile_from_name(name)), TRUE, NULL);
+	return sieve_save_as(sbin, t_strdup_printf
+		("%s/%s", testsuite_binary_tmp, sieve_binfile_from_name(name)), TRUE,
+			0600, NULL);
 }
 
 struct sieve_binary *testsuite_binary_load(const char *name)
diff --git a/src/testsuite/testsuite-script.c b/src/testsuite/testsuite-script.c
index f35bdb63760c42889ac42088dd76602862df4050..386dcf1919a9cba5e197abb5434f091d491c146e 100644
--- a/src/testsuite/testsuite-script.c
+++ b/src/testsuite/testsuite-script.c
@@ -6,6 +6,7 @@
 #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"
@@ -45,8 +46,8 @@ static struct sieve_binary *_testsuite_script_compile
 
 	sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "compile script `%s'", script);
 
-	script_path = sieve_script_dirpath(renv->script);
-	if ( script_path == NULL ) 
+	script_path = sieve_file_script_get_dirpath(renv->script);
+	if ( script_path == NULL )
 		return SIEVE_EXEC_FAILURE;
 
 	script_path = t_strconcat(script_path, "/", script, NULL);
@@ -95,8 +96,6 @@ bool testsuite_script_run(const struct sieve_runtime_env *renv)
 	/* Compose script execution environment */
 	memset(&scriptenv, 0, sizeof(scriptenv));
 	scriptenv.default_mailbox = "INBOX";
-	scriptenv.username = "user";
-	scriptenv.hostname = "host.example.com";
 	scriptenv.postmaster_address = "postmaster@example.com";
 	scriptenv.smtp_open = NULL;
 	scriptenv.smtp_close = NULL;
@@ -157,8 +156,6 @@ bool testsuite_script_multiscript
 	/* Compose script execution environment */
 	memset(&scriptenv, 0, sizeof(scriptenv));
 	scriptenv.default_mailbox = "INBOX";
-	scriptenv.username = "user";
-	scriptenv.hostname = "host.example.com";
 	scriptenv.postmaster_address = "postmaster@example.com";
 	scriptenv.smtp_open = NULL;
 	scriptenv.smtp_close = NULL;
diff --git a/src/testsuite/testsuite-smtp.c b/src/testsuite/testsuite-smtp.c
index 643bf62617c039df4c3f657623871beab4d09c5c..9d8b5d85d801ad1084bb1783f7b96e846984d10b 100644
--- a/src/testsuite/testsuite-smtp.c
+++ b/src/testsuite/testsuite-smtp.c
@@ -73,7 +73,7 @@ struct testsuite_smtp {
 };
  
 void *testsuite_smtp_open
-(void *script_ctx ATTR_UNUSED, const char *destination, 
+(const struct sieve_script_env *senv ATTR_UNUSED, const char *destination, 
 	const char *return_path, struct ostream **output_r)
 {	
 	struct testsuite_smtp_message smtp_msg;
@@ -104,7 +104,7 @@ void *testsuite_smtp_open
 }
 
 bool testsuite_smtp_close
-(void *script_ctx ATTR_UNUSED, void *handle)
+(const struct sieve_script_env *senv ATTR_UNUSED, void *handle)
 {
 	struct testsuite_smtp *smtp = (struct testsuite_smtp *) handle;
 
diff --git a/src/testsuite/testsuite-smtp.h b/src/testsuite/testsuite-smtp.h
index 67145fdd6e0863bd3aa5e6dff79ac2f351ed6923..58d4cfad918bace3f80554fae42d2da660f1d49a 100644
--- a/src/testsuite/testsuite-smtp.h
+++ b/src/testsuite/testsuite-smtp.h
@@ -13,10 +13,10 @@ void testsuite_smtp_reset(void);
  */
  
 void *testsuite_smtp_open
-	(void *script_ctx, const char *destination, const char *return_path, 
-		struct ostream **output_r);
+	(const struct sieve_script_env *senv ATTR_UNUSED, const char *destination,
+		const char *return_path, struct ostream **output_r);
 bool testsuite_smtp_close
-	(void *script_ctx, void *handle);
+	(const struct sieve_script_env *senv, void *handle);
 
 /*
  * Access
diff --git a/src/testsuite/testsuite.c b/src/testsuite/testsuite.c
index 8a54124a6c5f671496840083df1284fb73b515cd..9590116612cb63fc218477b3b33207b477c300e0 100644
--- a/src/testsuite/testsuite.c
+++ b/src/testsuite/testsuite.c
@@ -178,9 +178,7 @@ int main(int argc, char **argv)
 		memset(&scriptenv, 0, sizeof(scriptenv));
 		scriptenv.user = sieve_tool_get_mail_user(sieve_tool);
 		scriptenv.default_mailbox = "INBOX";
-		scriptenv.hostname = "testsuite.example.com";
 		scriptenv.postmaster_address = "postmaster@example.com";
-		scriptenv.username = sieve_tool_get_username(sieve_tool);
 		scriptenv.smtp_open = testsuite_smtp_open;
 		scriptenv.smtp_close = testsuite_smtp_close;
 		scriptenv.trace_stream = tracestream;
diff --git a/stamp.h.in b/stamp.h.in
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4bd8276f349276147c911ae2c08b37e7b8811544 100644
--- a/stamp.h.in
+++ b/stamp.h.in
@@ -0,0 +1 @@
+// empty file
diff --git a/tests/extensions/environment/basic.svtest b/tests/extensions/environment/basic.svtest
index 7eeaa248b3446b711683017485bc2eb271030b18..82f8981639980e7ed85e6cf0874b00ab89fe3cc3 100644
--- a/tests/extensions/environment/basic.svtest
+++ b/tests/extensions/environment/basic.svtest
@@ -25,7 +25,7 @@ test "Name" {
 }
 
 test "Host" {
-	if not environment "host" "testsuite.example.com" {
+	if not environment "host" "host.example.com" {
 		test_fail "wrong testsuite hostname";
 	}
 }