diff --git a/Makefile.am b/Makefile.am
index 385857e09c775e49bfa7f4e2471a3f94543a9ca5..ff26045e92b760ca2985865198c22299957f87fe 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -123,6 +123,7 @@ test_cases = \
 	tests/extensions/spamvirustest/virustest.svtest \
 	tests/extensions/spamvirustest/spamtestplus.svtest \
 	tests/extensions/spamvirustest/errors.svtest \
+	tests/extensions/ihave/execute.svtest \
 	tests/extensions/vnd.dovecot/debug/execute.svtest \
 	tests/deprecated/notify/basic.svtest \
 	tests/deprecated/notify/mailto.svtest \
diff --git a/configure.in b/configure.in
index f47b46deeaf4223314a3fa76a4ad511aef40e972..54d1b0d7110e7ab466b11bd2b9a3fc2ef31924e5 100644
--- a/configure.in
+++ b/configure.in
@@ -117,6 +117,7 @@ src/lib-sieve/plugins/environment/Makefile
 src/lib-sieve/plugins/mailbox/Makefile
 src/lib-sieve/plugins/date/Makefile
 src/lib-sieve/plugins/spamvirustest/Makefile
+src/lib-sieve/plugins/ihave/Makefile
 src/lib-sieve/plugins/vnd.dovecot/Makefile
 src/lib-sieve/plugins/vnd.dovecot/debug/Makefile
 src/lib-sieve-tool/Makefile
diff --git a/src/lib-sieve/Makefile.am b/src/lib-sieve/Makefile.am
index 17312919dc875d243882888c529969b61908b228..30aab213deb800ba41906a43bd02bf944d72831a 100644
--- a/src/lib-sieve/Makefile.am
+++ b/src/lib-sieve/Makefile.am
@@ -63,6 +63,7 @@ plugins = \
 	$(extdir)/mailbox/libsieve_ext_mailbox.la \
 	$(extdir)/date/libsieve_ext_date.la \
 	$(extdir)/spamvirustest/libsieve_ext_spamvirustest.la \
+	$(extdir)/ihave/libsieve_ext_ihave.la \
 	$(extdir)/vnd.dovecot/debug/libsieve_ext_debug.la \
 	$(unfinished_plugins)
 
diff --git a/src/lib-sieve/cmd-require.c b/src/lib-sieve/cmd-require.c
index 66506731822206be133adf05661d06eb2df961fc..62ecce05ebc0e42693850af2a76a7752684dcac4 100644
--- a/src/lib-sieve/cmd-require.c
+++ b/src/lib-sieve/cmd-require.c
@@ -55,8 +55,8 @@ static bool cmd_require_validate
 	arg = cmd->first_positional;
 	if ( sieve_ast_argument_type(arg) == SAAT_STRING ) {
 		/* Single string */
-		const struct sieve_extension *ext = sieve_validator_extension_load
-			(valdtr, cmd, arg, sieve_ast_argument_str(arg));	
+		const struct sieve_extension *ext = sieve_validator_extension_load_by_name
+			(valdtr, cmd, arg, sieve_ast_argument_strc(arg));	
 
 		if ( ext == NULL ) result = FALSE;
 		
@@ -65,8 +65,8 @@ static bool cmd_require_validate
 		struct sieve_ast_argument *stritem = sieve_ast_strlist_first(arg);
 		
 		while ( stritem != NULL ) {
-			const struct sieve_extension *ext = sieve_validator_extension_load
-				(valdtr, cmd, stritem, sieve_ast_strlist_str(stritem));
+			const struct sieve_extension *ext = sieve_validator_extension_load_by_name
+				(valdtr, cmd, stritem, sieve_ast_strlist_strc(stritem));
 
 			if ( ext == NULL ) result = FALSE;
 	
diff --git a/src/lib-sieve/plugins/Makefile.am b/src/lib-sieve/plugins/Makefile.am
index 25db0dfa1efa1cf192e5e4614d8655deb13df722..8a660edbbf7e525302d348502a19a5b92546a7b9 100644
--- a/src/lib-sieve/plugins/Makefile.am
+++ b/src/lib-sieve/plugins/Makefile.am
@@ -19,6 +19,7 @@ SUBDIRS = \
 	mailbox \
 	date \
 	spamvirustest \
+	ihave \
 	vnd.dovecot \
 	$(UNFINISHED)
 
diff --git a/src/lib-sieve/plugins/ihave/Makefile.am b/src/lib-sieve/plugins/ihave/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..ba12d807646fcef181285b39236ede4679cff3fc
--- /dev/null
+++ b/src/lib-sieve/plugins/ihave/Makefile.am
@@ -0,0 +1,18 @@
+noinst_LTLIBRARIES = libsieve_ext_ihave.la
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/src/lib-sieve \
+	$(LIBDOVECOT_INCLUDE)
+
+tests = \
+	tst-ihave.c
+
+libsieve_ext_ihave_la_SOURCES = \
+	$(tests) \
+	ext-ihave-binary.c \
+	ext-ihave-common.c \
+	ext-ihave.c
+
+noinst_HEADERS = \
+	ext-ihave-binary.h \
+	ext-ihave-common.h
diff --git a/src/lib-sieve/plugins/ihave/ext-ihave-binary.c b/src/lib-sieve/plugins/ihave/ext-ihave-binary.c
new file mode 100644
index 0000000000000000000000000000000000000000..b416c4656286041148842b66022266a9b58cf2b4
--- /dev/null
+++ b/src/lib-sieve/plugins/ihave/ext-ihave-binary.c
@@ -0,0 +1,242 @@
+/* Copyright (c) 2002-2011 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str.h"
+
+#include "sieve-common.h"
+#include "sieve-extensions.h"
+#include "sieve-error.h"
+#include "sieve-script.h"
+#include "sieve-binary.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+
+#include "ext-ihave-common.h"
+#include "ext-ihave-binary.h"
+
+/*
+ * Forward declarations
+ */
+ 
+static bool ext_ihave_binary_save
+	(const struct sieve_extension *ext, struct sieve_binary *sbin, void *context);
+static bool ext_ihave_binary_open
+	(const struct sieve_extension *ext, struct sieve_binary *sbin, void *context);
+static bool ext_ihave_binary_up_to_date
+	(const struct sieve_extension *ext, struct sieve_binary *sbin, void *context);
+
+/* 
+ * Binary include extension
+ */
+ 
+const struct sieve_binary_extension ihave_binary_ext = {
+	&ihave_extension,
+	ext_ihave_binary_save,
+	ext_ihave_binary_open,
+	NULL,
+	ext_ihave_binary_up_to_date
+};
+
+/*
+ * Binary context management
+ */
+ 
+struct ext_ihave_binary_context {
+	struct sieve_binary *binary;
+	struct sieve_binary_block *block;
+	
+	ARRAY_DEFINE(missing_extensions, const char *);
+};
+
+static struct ext_ihave_binary_context *ext_ihave_binary_create_context
+(const struct sieve_extension *this_ext, struct sieve_binary *sbin)
+{
+	pool_t pool = sieve_binary_pool(sbin);
+	
+	struct ext_ihave_binary_context *ctx = 
+		p_new(pool, struct ext_ihave_binary_context, 1);
+	
+	ctx->binary = sbin;			
+	p_array_init(&ctx->missing_extensions, pool, 64);
+
+	sieve_binary_extension_set(sbin, this_ext, &ihave_binary_ext, ctx);
+	return ctx;
+}
+
+struct ext_ihave_binary_context *ext_ihave_binary_get_context
+(const struct sieve_extension *this_ext, struct sieve_binary *sbin)
+{	
+	struct ext_ihave_binary_context *ctx = (struct ext_ihave_binary_context *)
+		sieve_binary_extension_get_context(sbin, this_ext);
+	
+	if ( ctx == NULL )
+		ctx = ext_ihave_binary_create_context(this_ext, sbin);
+	
+	return ctx;
+}
+ 
+struct ext_ihave_binary_context *ext_ihave_binary_init
+(const struct sieve_extension *this_ext, struct sieve_binary *sbin, 
+	struct sieve_ast *ast)
+{
+	struct ext_ihave_ast_context *ast_ctx =
+		ext_ihave_get_ast_context(this_ext, ast);
+	struct ext_ihave_binary_context *binctx;
+	const char *const *exts;
+	unsigned int i, count;
+	
+	binctx = ext_ihave_binary_get_context(this_ext, sbin);
+
+	exts = array_get(&ast_ctx->missing_extensions, &count);
+
+	if ( count > 0 ) {
+		pool_t pool = sieve_binary_pool(sbin);
+
+		if ( binctx->block == NULL )
+			binctx->block = sieve_binary_extension_create_block(sbin, this_ext);
+	
+		for ( i = 0; i < count; i++ ) {
+			const char *ext_name = p_strdup(pool, exts[i]);
+
+			array_append(&binctx->missing_extensions, &ext_name, 1);
+		}
+	}	
+				
+	return binctx;
+}
+
+/*
+ * Binary extension
+ */
+
+static bool ext_ihave_binary_save
+(const struct sieve_extension *ext, struct sieve_binary *sbin, void *context)
+{
+	struct ext_ihave_binary_context *binctx = 
+		(struct ext_ihave_binary_context *) context;
+	const char *const *exts;
+	unsigned int count, i;
+
+	exts = array_get(&binctx->missing_extensions, &count);
+
+	if ( binctx->block != NULL )
+		sieve_binary_block_clear(binctx->block);
+	
+	if ( count > 0 ) {
+		if ( binctx->block == NULL )
+			binctx->block = sieve_binary_extension_create_block(sbin, ext);
+
+		sieve_binary_emit_unsigned(binctx->block, count);
+
+		for ( i = 0; i < count; i++ ) {
+			sieve_binary_emit_cstring(binctx->block, exts[i]);
+		}
+	}
+	
+	return TRUE;
+}
+
+static bool ext_ihave_binary_open
+(const struct sieve_extension *ext, struct sieve_binary *sbin, void *context)
+{
+	struct sieve_instance *svinst = ext->svinst;
+	struct ext_ihave_binary_context *binctx = 
+		(struct ext_ihave_binary_context *) context;
+	struct sieve_binary_block *sblock;
+	unsigned int i, count, block_id;
+	sieve_size_t offset;
+	
+	sblock = sieve_binary_extension_get_block(sbin, ext);
+
+	if ( sblock != NULL ) {
+		binctx->block = sblock;
+		block_id = sieve_binary_block_get_id(sblock);
+			
+		offset = 0;	
+		
+		/* Read number of missing extensions to read subsequently */
+		if ( !sieve_binary_read_unsigned(sblock, &offset, &count) ) {
+			sieve_sys_error(svinst, 
+				"ihave: failed to read missing extension count "
+				"from block %d of binary %s", block_id, sieve_binary_path(sbin));
+			return FALSE;
+		}
+		
+		/* Read dependencies */
+		for ( i = 0; i < count; i++ ) {
+			string_t *ext_name;
+			const char *name;
+		
+			if ( !sieve_binary_read_string(sblock, &offset, &ext_name) ) {
+				/* Binary is corrupt, recompile */
+				sieve_sys_error(svinst,
+					"ihave: failed to read missing extension name "
+					"from block %d of binary %s", block_id, sieve_binary_path(sbin)); 
+				return FALSE;
+			}
+
+			name = str_c(ext_name);
+			array_append(&binctx->missing_extensions, &name, 1);
+		}
+	}
+	
+	return TRUE;	
+}
+
+static bool ext_ihave_binary_up_to_date
+(const struct sieve_extension *ext, struct sieve_binary *sbin ATTR_UNUSED, 
+	void *context)
+{
+	struct ext_ihave_binary_context *binctx = 
+		(struct ext_ihave_binary_context *) context;
+	const char *const *exts;
+	unsigned int count, i;
+	
+	exts = array_get(&binctx->missing_extensions, &count);
+	for ( i = 0; i < count; i++ ) {
+		if ( sieve_extension_get_by_name(ext->svinst, exts[i]) != NULL )
+			return FALSE;
+	}
+	
+	return TRUE;
+}
+
+/*
+ * Main extension interface
+ */
+
+bool ext_ihave_binary_load
+(const struct sieve_extension *ext, struct sieve_binary *sbin)
+{
+	(void)ext_ihave_binary_get_context(ext, sbin);
+
+	return TRUE;
+}
+
+bool ext_ihave_binary_dump
+(const struct sieve_extension *ext, struct sieve_dumptime_env *denv)
+{
+	struct sieve_binary *sbin = denv->sbin;
+	struct ext_ihave_binary_context *binctx = 
+		ext_ihave_binary_get_context(ext, sbin);
+	const char *const *exts;
+	unsigned int count, i;
+	
+	exts = array_get(&binctx->missing_extensions, &count);
+
+	if ( count > 0 ) {
+		sieve_binary_dump_sectionf(denv,
+			"Extensions missing at compile (block: %d)", 
+			sieve_binary_block_get_id(binctx->block));
+
+		for ( i = 0; i < count; i++ ) {		
+			sieve_binary_dumpf(denv, "  -  %s\n", exts[i]);
+		}
+	}	
+	
+	return TRUE;
+}
+
+
diff --git a/src/lib-sieve/plugins/ihave/ext-ihave-binary.h b/src/lib-sieve/plugins/ihave/ext-ihave-binary.h
new file mode 100644
index 0000000000000000000000000000000000000000..770b625d001d122341c8ac00ef619a390759e82a
--- /dev/null
+++ b/src/lib-sieve/plugins/ihave/ext-ihave-binary.h
@@ -0,0 +1,36 @@
+/* Copyright (c) 2002-2011 Pigeonhole authors, see the included COPYING file
+ */
+
+#ifndef __EXT_IHAVE_BINARY_H
+#define __EXT_IHAVE_BINARY_H
+
+/*
+ * Binary context management
+ */
+ 
+struct ext_ihave_binary_context;
+
+struct ext_ihave_binary_context *ext_ihave_binary_get_context
+	(const struct sieve_extension *this_ext, struct sieve_binary *sbin);
+struct ext_ihave_binary_context *ext_ihave_binary_init
+	(const struct sieve_extension *this_ext, struct sieve_binary *sbin, 
+		struct sieve_ast *ast);
+
+/*
+ * Registering missing extension
+ */
+
+void ext_ihave_binary_add_missing_extension
+	(struct ext_ihave_binary_context *binctx, const char *ext_name);
+
+/*
+ * Main extension interface
+ */
+
+bool ext_ihave_binary_load
+	(const struct sieve_extension *ext, struct sieve_binary *sbin);
+bool ext_ihave_binary_dump
+	(const struct sieve_extension *ext, struct sieve_dumptime_env *denv);
+
+#endif /* __EXT_IHAVE_BINARY_H */
+
diff --git a/src/lib-sieve/plugins/ihave/ext-ihave-common.c b/src/lib-sieve/plugins/ihave/ext-ihave-common.c
new file mode 100644
index 0000000000000000000000000000000000000000..5f56ae8d47b920f27d83acafc158a4be33331b14
--- /dev/null
+++ b/src/lib-sieve/plugins/ihave/ext-ihave-common.c
@@ -0,0 +1,52 @@
+/* Copyright (c) 2002-2011 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "array.h"
+
+#include "sieve-common.h"
+#include "sieve-ast.h"
+
+#include "ext-ihave-common.h"
+
+/*
+ * AST context management
+ */
+
+struct ext_ihave_ast_context *ext_ihave_get_ast_context
+(const struct sieve_extension *this_ext, struct sieve_ast *ast)
+{
+	struct ext_ihave_ast_context *actx = (struct ext_ihave_ast_context *)
+		sieve_ast_extension_get_context(ast, this_ext);
+	pool_t pool;
+
+	if ( actx != NULL )
+		return actx;
+
+	pool = sieve_ast_pool(ast);
+	actx = p_new(pool, struct ext_ihave_ast_context, 1);
+	p_array_init(&actx->missing_extensions, pool, 64);
+
+	sieve_ast_extension_set_context(ast, this_ext, (void *) actx);
+
+	return actx;
+}
+
+void ext_ihave_ast_add_missing_extension
+(const struct sieve_extension *this_ext, struct sieve_ast *ast,
+	const char *ext_name) 
+{
+	struct ext_ihave_ast_context *actx = 
+		ext_ihave_get_ast_context(this_ext, ast);
+	const char *const *exts;
+	unsigned int i, count;
+
+	exts = array_get(&actx->missing_extensions, &count);
+	for ( i = 0; i < count; i++ ) {
+		if ( strcmp(exts[i], ext_name) == 0 )
+			return;
+	}
+
+	array_append(&actx->missing_extensions, &ext_name, 1);
+}
+
diff --git a/src/lib-sieve/plugins/ihave/ext-ihave-common.h b/src/lib-sieve/plugins/ihave/ext-ihave-common.h
new file mode 100644
index 0000000000000000000000000000000000000000..ad0ac476181dffcfc79a96c13893ae5a40ebbcf6
--- /dev/null
+++ b/src/lib-sieve/plugins/ihave/ext-ihave-common.h
@@ -0,0 +1,49 @@
+/* Copyright (c) 2002-2011 Pigeonhole authors, see the included COPYING file
+ */
+
+#ifndef __EXT_IHAVE_COMMON_H
+#define __EXT_IHAVE_COMMON_H
+
+#include "sieve-common.h"
+
+/*
+ * Extensions
+ */
+
+extern const struct sieve_extension_def ihave_extension;
+
+/*
+ * Tests
+ */
+
+extern const struct sieve_command_def ihave_test;
+
+/*
+ * Commands
+ */
+
+//extern const struct sieve_command_def error_command;
+
+/*
+ * Operations
+ */
+
+//extern const struct sieve_operation_def error_operation;
+
+/*
+ * AST context
+ */
+
+struct ext_ihave_ast_context {
+  ARRAY_DEFINE(missing_extensions, const char *);
+};
+
+struct ext_ihave_ast_context *ext_ihave_get_ast_context
+	(const struct sieve_extension *this_ext, struct sieve_ast *ast);
+
+void ext_ihave_ast_add_missing_extension
+	(const struct sieve_extension *this_ext, struct sieve_ast *ast,
+		const char *ext_name);
+
+
+#endif /* __EXT_IHAVE_COMMON_H */
diff --git a/src/lib-sieve/plugins/ihave/ext-ihave.c b/src/lib-sieve/plugins/ihave/ext-ihave.c
new file mode 100644
index 0000000000000000000000000000000000000000..1ce4b45dc4c1bf3eb2a8d8e9c9439bc9db2c0ae0
--- /dev/null
+++ b/src/lib-sieve/plugins/ihave/ext-ihave.c
@@ -0,0 +1,65 @@
+/* Copyright (c) 2002-2011 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension ihave
+ * ---------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: RFC 5463
+ * Implementation: full
+ * Status: experimental
+ *
+ */
+
+#include "lib.h"
+
+#include "sieve-common.h"
+#include "sieve-extensions.h"
+#include "sieve-code.h"
+#include "sieve-binary.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+
+#include "ext-ihave-common.h"
+#include "ext-ihave-binary.h"
+
+/*
+ * Extension
+ */
+
+static bool ext_ihave_validator_load
+	(const struct sieve_extension *ext, struct sieve_validator *validator);
+static bool ext_ihave_generator_load
+	(const struct sieve_extension *ext, const struct sieve_codegen_env *cgenv);
+
+const struct sieve_extension_def ihave_extension = {
+	"ihave",
+	NULL, NULL,
+	ext_ihave_validator_load,
+	ext_ihave_generator_load,
+	NULL, 
+	ext_ihave_binary_load,
+	ext_ihave_binary_dump,
+	NULL,
+//	SIEVE_EXT_DEFINE_OPERATION(error_operation),
+	SIEVE_EXT_DEFINE_NO_OPERATIONS,
+	SIEVE_EXT_DEFINE_NO_OPERANDS
+};
+
+static bool ext_ihave_validator_load
+(const struct sieve_extension *ext, struct sieve_validator *validator)
+{
+	/* Register new test */
+	sieve_validator_register_command(validator, ext, &ihave_test);
+
+	return TRUE;
+}
+
+static bool ext_ihave_generator_load
+(const struct sieve_extension *ext, const struct sieve_codegen_env *cgenv)
+{
+	(void)ext_ihave_binary_init(ext, cgenv->sbin, cgenv->ast);
+
+	return TRUE;
+}
+
diff --git a/src/lib-sieve/plugins/ihave/tst-ihave.c b/src/lib-sieve/plugins/ihave/tst-ihave.c
new file mode 100644
index 0000000000000000000000000000000000000000..6be5e4c48047e973a8bc492aedf5e8b8be48c6a6
--- /dev/null
+++ b/src/lib-sieve/plugins/ihave/tst-ihave.c
@@ -0,0 +1,154 @@
+/* Copyright (c) 2002-2011 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "array.h"
+
+#include "sieve-common.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+
+#include "ext-ihave-common.h"
+
+/* 
+ * Ihave test 
+ *
+ * Syntax:
+ *   ihave <capabilities: string-list>
+ */
+
+static bool tst_ihave_validate
+	(struct sieve_validator *valdtr, struct sieve_command *tst);
+static bool tst_ihave_validate_const
+	(struct sieve_validator *valdtr, struct sieve_command *tst,
+		int *const_current, int const_next);
+
+const struct sieve_command_def ihave_test = { 
+	"ihave", 
+	SCT_TEST, 
+	1, 0, FALSE, FALSE,
+	NULL, NULL,
+	tst_ihave_validate,
+	tst_ihave_validate_const,
+	NULL, NULL
+};
+
+/*
+ * Code validation
+ */
+
+static bool tst_ihave_validate
+(struct sieve_validator *valdtr, struct sieve_command *tst)
+{ 		
+	struct _capability { 
+		const struct sieve_extension *ext; 
+		struct sieve_ast_argument *arg;
+	};
+
+	struct sieve_ast_argument *arg = tst->first_positional;
+	struct sieve_ast_argument *stritem;
+	ARRAY_DEFINE(capabilities, struct _capability);
+	struct _capability capability;
+	const struct _capability *caps;
+	unsigned int i, count;
+	bool all_known = TRUE;
+
+	t_array_init(&capabilities, 64);
+
+	tst->data = (void *) FALSE;
+
+	/* Check stringlist argument */
+	if ( !sieve_validate_positional_argument
+		(valdtr, tst, arg, "capabilities", 1, SAAT_STRING_LIST) ) {
+		return FALSE;
+	}
+
+	switch ( sieve_ast_argument_type(arg) ) {
+	case SAAT_STRING:
+		/* Single string */
+		capability.arg = arg;
+		capability.ext = sieve_extension_get_by_name
+			(tst->ext->svinst, sieve_ast_argument_strc(arg));
+		array_append(&capabilities, &capability, 1);
+
+		if ( capability.ext == NULL ) {
+			all_known = FALSE;
+
+			ext_ihave_ast_add_missing_extension
+				(tst->ext, tst->ast_node->ast, sieve_ast_argument_strc(arg));
+		}
+
+		break;
+
+	case SAAT_STRING_LIST:
+		/* String list */
+		stritem = sieve_ast_strlist_first(arg);
+		
+		while ( stritem != NULL ) {
+			capability.arg = stritem;
+			capability.ext = sieve_extension_get_by_name
+				(tst->ext->svinst, sieve_ast_argument_strc(stritem));
+			array_append(&capabilities, &capability, 1);
+
+			if ( capability.ext == NULL ) {
+				all_known = FALSE;
+
+				ext_ihave_ast_add_missing_extension
+					(tst->ext, tst->ast_node->ast, sieve_ast_argument_strc(stritem));
+			}
+	
+			stritem = sieve_ast_strlist_next(stritem);
+		}
+
+		break;
+	default:
+		i_unreached();
+	}
+
+	if ( !all_known )
+		return TRUE;
+
+	/* RFC 5463, Section 4, page 4:
+	 *
+	 * The "ihave" extension is designed to be used with other extensions
+   * that add tests, actions, comparators, or arguments.  Implementations
+   * MUST NOT allow it to be used with extensions that change the
+   * underlying Sieve grammar, or extensions like encoded-character
+   * [RFC5228], or variables [RFC5229] that change how the content of
+   * Sieve scripts are interpreted.  The test MUST fail and the extension
+   * MUST NOT be enabled if such usage is attempted.
+	 *
+	 * FIXME: current implementation of this restriction is hardcoded and
+	 * therefore highly inflexible
+	 */
+	caps = array_get(&capabilities, &count);
+	for ( i = 0; i < count; i++ ) {
+		if ( sieve_extension_name_is(caps[i].ext, "variables") ||
+			sieve_extension_name_is(caps[i].ext, "encoded-character") )
+			return TRUE;
+	}
+
+	/* Load all extensions */
+	caps = array_get(&capabilities, &count);
+	for ( i = 0; i < count; i++ ) {
+		if ( !sieve_validator_extension_load
+			(valdtr, tst, caps[i].arg, caps[i].ext) )
+			return FALSE;
+	}
+
+	tst->data = (void *) TRUE;
+	return TRUE;	
+}
+
+static bool tst_ihave_validate_const
+(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *tst,
+	int *const_current, int const_next ATTR_UNUSED)
+{
+	if ( (bool)tst->data == TRUE )
+		*const_current = 1;
+	else
+		*const_current = 0;
+	return TRUE;
+}
diff --git a/src/lib-sieve/sieve-ast.c b/src/lib-sieve/sieve-ast.c
index ecd27029ee164450fb5628a7f0226fb9a9b6f459..1e385ef4d4e2d0c3eff1acf96c025aa8eb009d16 100644
--- a/src/lib-sieve/sieve-ast.c
+++ b/src/lib-sieve/sieve-ast.c
@@ -129,23 +129,24 @@ struct sieve_script *sieve_ast_script(struct sieve_ast *ast)
  * Extension support 
  */
 
-void sieve_ast_extension_link
+bool sieve_ast_extension_link
 (struct sieve_ast *ast, const struct sieve_extension *ext)
 {
 	unsigned int i, ext_count;
 	const struct sieve_extension *const *extensions;
 	
-	if ( ext->id < 0 ) return;
+	if ( ext->id < 0 ) return TRUE;
 	 
 	/* Prevent duplicates */
 	extensions = array_get(&ast->linked_extensions, &ext_count);
 	for ( i = 0; i < ext_count; i++ ) {
 		if ( extensions[i] == ext )
-			return;
+			return FALSE;
 	}
 
 	/* Add extension */
 	array_append(&ast->linked_extensions, &ext, 1);	
+	return TRUE;
 }
 
 const struct sieve_extension * const *sieve_ast_extensions_get
@@ -169,6 +170,17 @@ void sieve_ast_extension_register
 	reg->context = context;	
 }
 
+void sieve_ast_extension_set_context
+(struct sieve_ast *ast, const struct sieve_extension *ext, void *context)
+{
+	struct sieve_ast_extension_reg *reg;
+
+	if ( ext->id < 0 ) return;
+	
+	reg = array_idx_modifiable(&ast->extensions, (unsigned int) ext->id);
+	reg->context = context;
+}
+
 void *sieve_ast_extension_get_context
 (struct sieve_ast *ast, const struct sieve_extension *ext) 
 {
diff --git a/src/lib-sieve/sieve-ast.h b/src/lib-sieve/sieve-ast.h
index c6a40ffc54cda3cd0aadcf35aad6f170f96e7f44..007b4a730d15e8a281efc26fc260da31e292dcd0 100644
--- a/src/lib-sieve/sieve-ast.h
+++ b/src/lib-sieve/sieve-ast.h
@@ -186,7 +186,7 @@ struct sieve_ast_extension {
 		void *context);
 };
 
-void sieve_ast_extension_link
+bool sieve_ast_extension_link
 	(struct sieve_ast *ast, const struct sieve_extension *ext);
 const struct sieve_extension * const *sieve_ast_extensions_get
 	(struct sieve_ast *ast, unsigned int *count_r);
@@ -194,6 +194,8 @@ const struct sieve_extension * const *sieve_ast_extensions_get
 void sieve_ast_extension_register
 	(struct sieve_ast *ast, const struct sieve_extension *ext,
 		const struct sieve_ast_extension *ast_ext, void *context);
+void sieve_ast_extension_set_context
+	(struct sieve_ast *ast, const struct sieve_extension *ext, void *context);
 void *sieve_ast_extension_get_context
 	(struct sieve_ast *ast, const struct sieve_extension *ext);
 
diff --git a/src/lib-sieve/sieve-extensions.c b/src/lib-sieve/sieve-extensions.c
index c8b78f9c0e1414bc35d966803f3971dceba0067c..5294b29dd81a139ce7a33e1de5f11b3ca4d200fb 100644
--- a/src/lib-sieve/sieve-extensions.c
+++ b/src/lib-sieve/sieve-extensions.c
@@ -103,6 +103,7 @@ extern const struct sieve_extension_def date_extension;
 extern const struct sieve_extension_def spamtest_extension;
 extern const struct sieve_extension_def spamtestplus_extension;
 extern const struct sieve_extension_def virustest_extension;
+extern const struct sieve_extension_def ihave_extension;
 
 /* vnd.dovecot. */
 extern const struct sieve_extension_def debug_extension;
@@ -132,7 +133,7 @@ const struct sieve_extension_def *sieve_core_extensions[] = {
 	&relational_extension, &regex_extension, &imap4flags_extension,
 	&copy_extension, &include_extension, &body_extension,
 	&variables_extension, &enotify_extension, &environment_extension,
-	&mailbox_extension, &date_extension
+	&mailbox_extension, &date_extension, &ihave_extension
 };
 
 const unsigned int sieve_core_extensions_count =
@@ -463,6 +464,9 @@ const struct sieve_extension *sieve_extension_get_by_name
 	if ( *name == '@' )
 		return NULL;
 
+	if ( strlen(name) > 128 )
+		return NULL;
+
 	ext = (const struct sieve_extension *)
 		hash_table_lookup(ext_reg->extension_index, name);
 
diff --git a/src/lib-sieve/sieve-extensions.h b/src/lib-sieve/sieve-extensions.h
index c5f62d83e9b117007120cae956edc578418cbd30..cff814e1ad2d12bbdc7a8c440cd311cede1db3f6 100644
--- a/src/lib-sieve/sieve-extensions.h
+++ b/src/lib-sieve/sieve-extensions.h
@@ -77,10 +77,12 @@ struct sieve_extension {
 	unsigned int dummy:1;
 };
 
-#define sieve_extension_name(ext) \
-	(ext)->def->name
 #define sieve_extension_is(ext, definition) \
 	( (ext)->def == &(definition) )
+#define sieve_extension_name(ext) \
+	(ext)->def->name
+#define sieve_extension_name_is(ext, _name) \
+	( strcmp((ext)->def->name, (_name)) == 0 )
 
 /* 
  * Defining opcodes and operands 
diff --git a/src/lib-sieve/sieve-validator.c b/src/lib-sieve/sieve-validator.c
index a443b74d97c9142c4a674a0d4c85a1bd36ffdccf..03c31887571992a1c3c998ef7eb7d59f9444a485 100644
--- a/src/lib-sieve/sieve-validator.c
+++ b/src/lib-sieve/sieve-validator.c
@@ -528,24 +528,52 @@ static struct sieve_tag_registration *sieve_validator_command_tag_get
  * Extension support 
  */
 
-const struct sieve_extension *sieve_validator_extension_load
+bool sieve_validator_extension_load
 (struct sieve_validator *valdtr, struct sieve_command *cmd,
-	struct sieve_ast_argument *ext_arg, string_t *ext_name) 
+	struct sieve_ast_argument *ext_arg, const struct sieve_extension *ext)
 {
+	const struct sieve_extension_def *extdef = ext->def;
 	struct sieve_validator_extension_reg *reg;
-	const struct sieve_extension *ext;
-	const struct sieve_extension_def *extdef;
-	const char *name = str_c(ext_name);
 
-	if ( str_len(ext_name) > 128 ) {
-		sieve_argument_validate_error(valdtr, ext_arg, 
-			"%s %s: unknown Sieve capability '%s' (name is impossibly long)",
-			sieve_command_identifier(cmd), sieve_command_type_name(cmd),
-			str_sanitize(name, 128));
-		return NULL;
+	if ( !sieve_ast_extension_link(valdtr->ast, ext) ) {
+		/*if ( cmd != NULL && ext_arg != NULL ) {
+			sieve_argument_validate_warning(valdtr, ext_arg, 
+				"%s %s: sieve capability `%s' already loaded",
+				sieve_command_identifier(cmd), sieve_command_type_name(cmd),
+				sieve_extension_name(ext));
+		}*/
+	} else {
+		if ( extdef->validator_load != NULL && 
+			!extdef->validator_load(ext, valdtr) ) {
+			if ( cmd != NULL && ext_arg != NULL ) {
+				sieve_argument_validate_error(valdtr, ext_arg, 
+					"%s %s: failed to load Sieve capability '%s'",
+					sieve_command_identifier(cmd), sieve_command_type_name(cmd),
+					sieve_extension_name(ext));
+			}
+			return FALSE;
+		}
+	}
+
+	/* Register extension no matter what and store the AST argument registering it 
+	 */
+	if ( ext->id >= 0 ) {
+		reg = array_idx_modifiable(&valdtr->extensions, (unsigned int) ext->id);
+		if ( reg->arg == NULL )
+			reg->arg = ext_arg;
+		reg->loaded = TRUE;
 	}
 
-	ext = sieve_extension_get_by_name(valdtr->svinst, name); 
+	return TRUE;
+}
+
+const struct sieve_extension *sieve_validator_extension_load_by_name
+(struct sieve_validator *valdtr, struct sieve_command *cmd,
+	struct sieve_ast_argument *ext_arg, const char *ext_name) 
+{
+	const struct sieve_extension *ext;
+
+	ext = sieve_extension_get_by_name(valdtr->svinst, ext_name); 
 
 	if ( ext == NULL || ext->def == NULL || !ext->enabled ) {
 		unsigned int i;
@@ -553,12 +581,12 @@ const struct sieve_extension *sieve_validator_extension_load
 		bool core_command = FALSE;
 
 		for ( i = 0; !core_command && i < sieve_core_commands_count; i++ ) {
-			if ( strcasecmp(sieve_core_commands[i]->identifier, name) == 0 )
+			if ( strcasecmp(sieve_core_commands[i]->identifier, ext_name) == 0 )
 				core_command = TRUE;
 		}
 
 		for ( i = 0; !core_test && i < sieve_core_tests_count; i++ ) {
-			if ( strcasecmp(sieve_core_tests[i]->identifier, name) == 0 )
+			if ( strcasecmp(sieve_core_tests[i]->identifier, ext_name) == 0 )
 				core_test = TRUE;
 		}
 
@@ -567,36 +595,18 @@ const struct sieve_extension *sieve_validator_extension_load
 				"%s %s: '%s' is not known as a Sieve capability, "
 				"but it is known as a Sieve %s that is always available",
 				sieve_command_identifier(cmd), sieve_command_type_name(cmd),
-				name, ( core_test ? "test" : "command" ));
+				str_sanitize(ext_name, 128), ( core_test ? "test" : "command" ));
 		} else {
 			sieve_argument_validate_error(valdtr, ext_arg,
 				"%s %s: unknown Sieve capability '%s'", 
 				sieve_command_identifier(cmd), sieve_command_type_name(cmd),
-				name);
+				str_sanitize(ext_name, 128));
 		}
 		return NULL;
 	}
 
-	sieve_ast_extension_link(valdtr->ast, ext);
-
-	extdef = ext->def;
-
-	if ( extdef->validator_load != NULL && 
-		!extdef->validator_load(ext, valdtr) ) {
-		sieve_argument_validate_error(valdtr, ext_arg, 
-			"%s %s: failed to load Sieve capability '%s'",
-			sieve_command_identifier(cmd), sieve_command_type_name(cmd),
-			sieve_extension_name(ext));
+	if ( !sieve_validator_extension_load(valdtr, cmd, ext_arg, ext) )
 		return NULL;
-	}
-
-	/* Register extension no matter what and store the AST argument registering it 
-	 */
-	if ( ext->id >= 0 ) {
-		reg = array_idx_modifiable(&valdtr->extensions, (unsigned int) ext->id);
-		reg->arg = ext_arg;
-		reg->loaded = TRUE;
-	}
 
 	return ext;
 }
@@ -604,29 +614,16 @@ const struct sieve_extension *sieve_validator_extension_load
 const struct sieve_extension *sieve_validator_extension_load_implicit
 (struct sieve_validator *valdtr, const char *ext_name)
 {
-	struct sieve_validator_extension_reg *reg;
 	const struct sieve_extension *ext;
-	const struct sieve_extension_def *extdef;
 
 	ext = sieve_extension_get_by_name(valdtr->svinst, ext_name); 
 
 	if ( ext == NULL || ext->def == NULL )
 		return NULL;
 
-	sieve_ast_extension_link(valdtr->ast, ext);
-
-	extdef = ext->def;
-
-	if ( extdef->validator_load != NULL && !extdef->validator_load(ext, valdtr) )
+	if ( !sieve_validator_extension_load(valdtr, NULL, NULL, ext) )
 		return NULL;
 
-	/* Register extension no matter what and store the AST argument registering it 
-	 */
-	if ( ext->id >= 0 ) {
-		reg = array_idx_modifiable(&valdtr->extensions, (unsigned int) ext->id);
-		reg->loaded = TRUE;
-	}
-
 	return ext;
 }
 
@@ -1261,7 +1258,9 @@ static bool sieve_validate_command
 			}
 		}
 
-		/* Skip block if result of test is const FALSE */
+		/* Skip block if result of test is const FALSEconst struct sieve_extension *sieve_validator_extension_load
+(struct sieve_validator *valdtr, struct sieve_command *cmd,
+	struct sieve_ast_argument *ext_arg, string_t *ext_name)  */
 		if ( result && *const_r == 0 )
 			return TRUE;
 
diff --git a/src/lib-sieve/sieve-validator.h b/src/lib-sieve/sieve-validator.h
index 90b0308b6a1aa1022ac1534f27408b9489efcd68..e9d6eefdacb6e8533cdbc31a49110c6d6006a7d5 100644
--- a/src/lib-sieve/sieve-validator.h
+++ b/src/lib-sieve/sieve-validator.h
@@ -133,9 +133,12 @@ struct sieve_validator_extension {
 			void *context);
 };
 
-const struct sieve_extension *sieve_validator_extension_load
+bool sieve_validator_extension_load
 	(struct sieve_validator *valdtr, struct sieve_command *cmd,
-		struct sieve_ast_argument *ext_arg, string_t *ext_name);
+		struct sieve_ast_argument *ext_arg, const struct sieve_extension *ext);
+const struct sieve_extension *sieve_validator_extension_load_by_name
+	(struct sieve_validator *valdtr, struct sieve_command *cmd,
+		struct sieve_ast_argument *ext_arg, const char *ext_name);
 const struct sieve_extension *sieve_validator_extension_load_implicit
 	(struct sieve_validator *valdtr, const char *ext_name);
 
diff --git a/tests/extensions/ihave/execute.svtest b/tests/extensions/ihave/execute.svtest
new file mode 100644
index 0000000000000000000000000000000000000000..701d817a4ee45dfc281635e5ebef00392436f983
--- /dev/null
+++ b/tests/extensions/ihave/execute.svtest
@@ -0,0 +1,23 @@
+require "vnd.dovecot.testsuite";
+
+/*
+ * Execution testing (currently just meant to trigger any segfaults)
+ */
+
+test "Basic" {
+	if not test_script_compile "execute/ihave.sieve" {
+		test_fail "script compile failed";
+	}
+
+	if not test_script_run {
+		test_fail "script run failed";
+	}
+
+	if not test_result_execute {
+		test_fail "result execute failed";
+	}
+
+        test_binary_save "ihave-basic";
+        test_binary_load "ihave-basic";
+}
+
diff --git a/tests/extensions/ihave/execute/ihave.sieve b/tests/extensions/ihave/execute/ihave.sieve
new file mode 100644
index 0000000000000000000000000000000000000000..0fe84c8278b9cb8d997b6f9ac8db9139f88fc754
--- /dev/null
+++ b/tests/extensions/ihave/execute/ihave.sieve
@@ -0,0 +1,7 @@
+require "ihave";
+
+if ihave "nonsense-extension" {
+	nonsense_command "Frop!";
+}
+
+redirect "frop@example.com";