diff --git a/src/lib-sieve/Makefile.am b/src/lib-sieve/Makefile.am
index 6f66f428c14cb93a1c5415d2f31efa4766d64220..8d245ff11fc7757467037b78f483c5b56adedd66 100644
--- a/src/lib-sieve/Makefile.am
+++ b/src/lib-sieve/Makefile.am
@@ -98,6 +98,7 @@ libdovecot_sieve_la_SOURCES = \
 	sieve-code.c \
 	sieve-actions.c \
 	sieve-extensions.c \
+	sieve-plugins.c \
 	$(comparators) \
 	$(match_types) \
 	$(tests) \
@@ -139,6 +140,7 @@ headers = \
 	sieve-code.h \
 	sieve-actions.h \
 	sieve-extensions.h \
+	sieve-plugins.h \
 	sieve.h
 
 if INSTALL_HEADERS
diff --git a/src/lib-sieve/sieve-common.h b/src/lib-sieve/sieve-common.h
index 9c16ce7d130fed69ae502251195571218cb66ec5..cfbbd9be893094c52eaf3d47511e648c50ac13ff 100644
--- a/src/lib-sieve/sieve-common.h
+++ b/src/lib-sieve/sieve-common.h
@@ -125,6 +125,9 @@ struct sieve_script;
 /* sieve-message.h */
 struct sieve_message_context;
 
+/* sieve-plugins.h */
+struct sieve_plugin;
+
 /* sieve.c */
 struct sieve_ast *sieve_parse
 	(struct sieve_script *script, struct sieve_error_handler *ehandler);
@@ -146,6 +149,9 @@ struct sieve_instance {
 	/* Extension registry */
 	struct sieve_extension_registry *ext_reg;
 
+	/* Plugin modules */
+	struct sieve_plugin *plugins;
+
 	/* Limits */
 	size_t max_script_size;
 	unsigned int max_actions;
diff --git a/src/lib-sieve/sieve-plugins.c b/src/lib-sieve/sieve-plugins.c
new file mode 100644
index 0000000000000000000000000000000000000000..6f63003229f86d0ff108baf471fb61c25d1fba04
--- /dev/null
+++ b/src/lib-sieve/sieve-plugins.c
@@ -0,0 +1,210 @@
+/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file 
+ */
+
+#include "lib.h"
+#include "str.h"
+#include "module-dir.h"
+
+#include "sieve-settings.h"
+#include "sieve-extensions.h"
+
+#include "sieve-common.h"
+#include "sieve-plugins.h"
+
+struct sieve_plugin {
+	struct module *module;
+	struct sieve_plugin *next;
+};
+
+/*
+ * Plugin support
+ */
+
+static struct module *sieve_modules = NULL;
+static int sieve_modules_refcount = 0;
+
+static struct module *sieve_plugin_module_find(const char *path, const char *name)
+{
+	struct module *module;
+
+	module = sieve_modules;
+    while ( module != NULL ) {
+		const char *mod_path, *mod_name;
+		char *p;
+		size_t len;
+		
+		/* Strip module paths */
+
+		p = strrchr(module->path, '/');
+		if ( p == NULL ) continue;
+		while ( p > module->path && *p == '/' ) p--;
+		mod_path = t_strdup_until(module->path, p+1);
+	
+		len = strlen(path);
+		if ( path[len-1] == '/' )
+			path = t_strndup(path, len-1);
+
+		/* Strip module names */
+
+    	len = strlen(module->name);
+    	if (len > 7 && strcmp(module->name + len - 7, "_plugin") == 0)
+        	mod_name = t_strndup(module->name, len - 7);
+		else
+			mod_name = module->name;
+		
+		if ( strcmp(mod_path, path) == 0 && strcmp(mod_name, name) == 0 )
+			return module;
+
+		module = module->next;
+    }
+
+    return NULL;
+}
+
+void sieve_plugins_load(struct sieve_instance *svinst, const char *path, const char *plugins)
+{
+	struct module *module;
+	const char **module_names;
+	string_t *missing_modules;
+	unsigned int i;
+
+	/* Determine what to load */
+
+	if ( path == NULL && plugins == NULL ) {
+		path = sieve_setting_get(svinst, "sieve_plugin_dir");
+		plugins = sieve_setting_get(svinst, "sieve_plugins");
+	}
+
+	if ( plugins == NULL || *plugins == '\0' )
+		return;
+	
+	if ( path == NULL || *path == '\0' )
+		path = MODULEDIR"/sieve";
+
+	module_names = t_strsplit_spaces(plugins, ", ");
+
+ 	for (i = 0; module_names[i] != NULL; i++) {
+		/* Allow giving the module names also in non-base form. */
+ 		module_names[i] = module_file_get_name(module_names[i]);
+	}
+
+	/* Load missing modules 
+	 *   FIXME: Dovecot should provide this functionality (v2.0 does) 
+	 */
+
+	missing_modules = t_str_new(256);
+
+ 	for (i = 0; module_names[i] != NULL; i++) {
+		const char *name = module_names[i];
+
+		if ( sieve_plugin_module_find(path, name) == NULL ) {
+			if ( i > 0 ) str_append_c(missing_modules, ' ');
+	
+			str_append(missing_modules, name);
+		}
+	}
+
+	if ( str_len(missing_modules) > 0 ) {
+		struct module *new_modules = module_dir_load
+			(path, str_c(missing_modules), TRUE, SIEVE_VERSION);
+
+		if ( sieve_modules == NULL ) {
+			/* No modules loaded yet */
+			sieve_modules = new_modules;
+		} else {
+			/* Find the end of the list */
+			module = sieve_modules;
+			while ( module != NULL && module->next != NULL )
+				module = module->next;
+
+			/* Add newly loaded modules */
+			module->next = new_modules;
+		}
+	}
+
+	/* Call plugin load functions for this Sieve instance */
+
+	if ( svinst->plugins == NULL ) {
+		sieve_modules_refcount++;
+	}
+
+ 	for (i = 0; module_names[i] != NULL; i++) {
+		struct sieve_plugin *plugin;
+		const char *name = module_names[i];
+		void (*load_func)(struct sieve_instance *svinst);
+
+		/* Find the module */
+		module = sieve_plugin_module_find(path, name);
+		i_assert(module != NULL);
+
+		/* Check whether the plugin is already loaded in this instance */
+		plugin = svinst->plugins;
+		while ( plugin != NULL ) {
+			if ( plugin->module == module )
+				break;
+			plugin = plugin->next;
+		}
+
+		/* Skip it if it is loaded already */
+		if ( plugin != NULL )
+			continue;
+
+		/* Create plugin list item */
+		plugin = p_new(svinst->pool, struct sieve_plugin, 1);
+		plugin->module = module;
+	
+		/* Call load function */
+		load_func = module_get_symbol
+			(module, t_strdup_printf("%s_load", module->name));
+		if ( load_func != NULL ) {
+			load_func(svinst);
+		}
+
+		/* Add plugin to the instance */
+		if ( svinst->plugins == NULL )
+			svinst->plugins = plugin;
+		else {
+			struct sieve_plugin *plugin_last;
+
+			plugin_last = svinst->plugins;
+			while ( plugin_last != NULL )
+				plugin_last = plugin_last->next;
+
+			plugin_last->next = plugin;
+		}
+	}
+}
+
+void sieve_plugins_unload(struct sieve_instance *svinst)
+{
+	struct sieve_plugin *plugin;
+
+	if ( svinst->plugins == NULL )
+		return;
+	
+	/* Call plugin unload functions for this instance */
+
+	plugin = svinst->plugins;
+	while ( plugin != NULL ) {
+		struct module *module = plugin->module;
+		void (*unload_func)(struct sieve_instance *svinst);
+
+		unload_func = module_get_symbol
+			(module, t_strdup_printf("%s_unload", module->name));
+		if ( unload_func != NULL ) {
+			unload_func(svinst);
+		}
+
+		plugin = plugin->next;
+	}
+
+	/* Physically unload modules */
+
+	i_assert(sieve_modules_refcount > 0);
+
+	if ( --sieve_modules_refcount != 0 )
+        return;
+
+	module_dir_unload(&sieve_modules);
+}
+
diff --git a/src/lib-sieve/sieve-plugins.h b/src/lib-sieve/sieve-plugins.h
new file mode 100644
index 0000000000000000000000000000000000000000..2fd349dde80fa57d13c2d69c7dce52370f675d53
--- /dev/null
+++ b/src/lib-sieve/sieve-plugins.h
@@ -0,0 +1,12 @@
+/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file 
+ */
+
+#ifndef __SIEVE_PLUGINS_H
+#define __SIEVE_PLUGINS_H
+
+#include "sieve-common.h"
+
+void sieve_plugins_load(struct sieve_instance *svinst, const char *path, const char *plugins);
+void sieve_plugins_unload(struct sieve_instance *svinst);
+
+#endif /* __SIEVE_PLUGINS_H */
diff --git a/src/lib-sieve/sieve.c b/src/lib-sieve/sieve.c
index 00fdc4c9381ef89be1007aedfc844602e793804d..32e58c7ef9f6e2541d54fac84fccc461c55148dc 100644
--- a/src/lib-sieve/sieve.c
+++ b/src/lib-sieve/sieve.c
@@ -6,11 +6,11 @@
 #include "istream.h"
 #include "buffer.h"
 #include "eacces-error.h"
-#include "module-dir.h"
 
 #include "sieve-limits.h"
 #include "sieve-settings.h"
 #include "sieve-extensions.h"
+#include "sieve-plugins.h"
 
 #include "sieve-script.h"
 #include "sieve-ast.h"
@@ -35,81 +35,6 @@
 #include <stdio.h>
 #include <dirent.h>
 
-/*
- * Plugin support
- */
-
-static struct module *sieve_modules = NULL;
-static int sieve_modules_refcount = 0;
-
-static void sieve_plugins_load(struct sieve_instance *svinst)
-{
-	struct module *module;
-
-	/* Physically load modules */
-
-	/* FIXME: sieve plugins may/cannot differ between Sieve instances */
-
-	if ( sieve_modules == NULL ) {
-		const char *plugins = sieve_setting_get(svinst, "sieve_plugins");
-
-		if ( plugins != NULL && *plugins != '\0' ) { 
-			const char *plugin_dir = sieve_setting_get(svinst, "sieve_plugin_dir");
-		
-			if ( plugin_dir == NULL || *plugin_dir == '\0' )
-				plugin_dir = MODULEDIR"/sieve";
-
-			sieve_modules = module_dir_load(plugin_dir, plugins, TRUE, SIEVE_VERSION);
-		}
-	}
-
-	sieve_modules_refcount++;
-
-	/* Call plugin load functions for this instance */
-
-	module = sieve_modules;
-	while ( module != NULL ) {
-		void (*load_func)(struct sieve_instance *svinst);
-
-		load_func = module_get_symbol
-			(module, t_strdup_printf("%s_load", module->name));
-		if ( load_func != NULL ) {
-			load_func(svinst);
-		}
-
-		module = module->next;
-	}
-}
-
-static void sieve_plugins_unload(struct sieve_instance *svinst)
-{
-	struct module *module;
-
-	/* Call plugin unload functions for this instance */
-
-	module = sieve_modules;
-	while ( module != NULL ) {
-		void (*unload_func)(struct sieve_instance *svinst);
-
-		unload_func = module_get_symbol
-			(module, t_strdup_printf("%s_unload", module->name));
-		if ( unload_func != NULL ) {
-			unload_func(svinst);
-		}
-
-		module = module->next;
-	}
-
-	/* Physically unload modules */
-
-	i_assert(sieve_modules_refcount > 0);
-
-	if ( --sieve_modules_refcount != 0 )
-        return;
-
-	module_dir_unload(&sieve_modules);
-}
-
 /* 
  * Main Sieve library interface
  */
@@ -156,7 +81,7 @@ struct sieve_instance *sieve_init
 		return NULL;
 	}
 
-	sieve_plugins_load(svinst);
+	sieve_plugins_load(svinst, NULL, NULL);
 
 	return svinst;
 }