diff --git a/Makefile.am b/Makefile.am
index 330c5046ad12bec7b3bbb9a6a07c801c403fbb7a..064df045809da3a50c5915660ed673a0b378d22f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -55,6 +55,7 @@ test_cases = \
 	tests/extensions/variables/regex.svtest \
 	tests/extensions/include/errors.svtest \
 	tests/extensions/include/variables.svtest \
+	tests/extensions/include/once.svtest \
 	tests/extensions/imap4flags/basic.svtest \
 	tests/extensions/imap4flags/hasflag.svtest \
 	tests/extensions/imap4flags/errors.svtest \
diff --git a/README b/README
index 4585a30d0065145b468c507b043cae820494fa1f..05ef0d1c8aa2e0f2d7cee328b9b0b07630ff4a2e 100644
--- a/README
+++ b/README
@@ -96,7 +96,7 @@ following list outlines the implementation status of each supported extension:
     regex: mostly full; but suboptimal and no UTF-8
     body: mostly full, but text body-transform implementation is simple
         and some issues make it still not completely RFC incompliant. 
-    include: almost full; new specification requires changes.
+    include: almost full; global namespace missing 
     vacation: mostly full; handling of utf-8 in headers is non-existant
     imap4flags: full (old imapflags supported for backwards compatibility)
     variables: mostly full; currently no support for future namespaces 
diff --git a/TODO b/TODO
index 3abfdf05438e8504c3044d56825f3bfe9afd396d..02389df35baeb6a8d60db052e3af415cc7247840 100644
--- a/TODO
+++ b/TODO
@@ -6,9 +6,6 @@ Current:
 
 Next (in order of descending priority/precedence):
 
-* Upgrade include extension to latest draft specification.
-* Implement namespace support for variables extension (to complete include 
-  extension)
 * Improve error handling. Now it is not very consistent, especially for the Sieve
   command line tools. 
 * Improve debugging support in the sieve-test tool:
@@ -39,6 +36,8 @@ Next (in order of descending priority/precedence):
 	  compliance. Body test support currently matches but barely exceeds the
 	  original CMU Sieve implentation in terms of standards compliance.
 	- Improve handling of invalid addresses in headers (requires Dovecot changes)
+* Implement namespace support for variables extension (to complete include 
+  extension)
 * Implement environment extension
 * Add normalize() method to comparators to normalize the string before matching
   (for efficiency).
diff --git a/src/lib-sieve/plugins/include/cmd-include.c b/src/lib-sieve/plugins/include/cmd-include.c
index 5dbb27ceb29b03883eb66739eb7dcc1b3f4ee09e..8ee6a33099f59240a5c83218013b5214b0ad4c29 100644
--- a/src/lib-sieve/plugins/include/cmd-include.c
+++ b/src/lib-sieve/plugins/include/cmd-include.c
@@ -277,7 +277,8 @@ static bool cmd_include_generate
 	 * This yields the id of the binary block containing the compiled byte code.  
 	 */
 	if ( !ext_include_generate_include
-		(cgenv, cmd, ctx_data->location, ctx_data->script, &included) )
+		(cgenv, cmd, ctx_data->location, ctx_data->script, &included,
+			ctx_data->include_once) )
  		return FALSE;
  		
  	(void)sieve_operation_emit_code(cgenv->sbin, &include_operation);
diff --git a/src/lib-sieve/plugins/include/ext-include-common.c b/src/lib-sieve/plugins/include/ext-include-common.c
index d7559e8449f36581edeee625b73472d98925a4f3..fd9cc2f23834884d70a3baba91c977f69968a3df 100644
--- a/src/lib-sieve/plugins/include/ext-include-common.c
+++ b/src/lib-sieve/plugins/include/ext-include-common.c
@@ -2,6 +2,7 @@
  */
 
 #include "lib.h"
+#include "array.h"
 #include "str-sanitize.h"
 
 #include "sieve-common.h"
@@ -38,14 +39,19 @@ static inline struct ext_include_generator_context *
 
 /* Interpreter context */
 
+struct ext_include_interpreter_global {
+	ARRAY_DEFINE(included_scripts, struct sieve_script *);
+
+	struct sieve_variable_storage *variables;
+};
+
 struct ext_include_interpreter_context {
 	struct ext_include_interpreter_context *parent;
+	struct ext_include_interpreter_global *global;
 
 	struct sieve_interpreter *interp;
 	pool_t pool;
 
-	struct sieve_variable_storage *global_variables;
-
 	unsigned int nesting_level;
 
 	struct sieve_script *script;
@@ -231,14 +237,16 @@ static void ext_include_runtime_init
 		(struct ext_include_interpreter_context *) context;
 
 	if ( ctx->parent == NULL ) {
-		ctx->global_variables = sieve_variable_storage_create
+		ctx->global = p_new(ctx->pool, struct ext_include_interpreter_global, 1);
+		ctx->global->variables = sieve_variable_storage_create
 			(ctx->pool, ext_include_binary_get_global_scope(renv->sbin), 0);
+		p_array_init(&ctx->global->included_scripts, ctx->pool, 10);
 	} else {
-		ctx->global_variables = ctx->parent->global_variables;
+		ctx->global = ctx->parent->global;
 	}
 
 	sieve_ext_variables_set_storage
-		(renv->interp, ctx->global_variables, &include_extension);	
+		(renv->interp, ctx->global->variables, &include_extension);	
 }
 
 static struct sieve_interpreter_extension include_interpreter_extension = {
@@ -325,7 +333,7 @@ struct sieve_variable_storage *ext_include_interpreter_get_global_variables
 	struct ext_include_interpreter_context *ctx =
 		ext_include_get_interpreter_context(interp);
 		
-	return ctx->global_variables;
+	return ctx->global->variables;
 }
 
 /* 
@@ -335,7 +343,7 @@ struct sieve_variable_storage *ext_include_interpreter_get_global_variables
 bool ext_include_generate_include
 (const struct sieve_codegen_env *cgenv, struct sieve_command_context *cmd,
 	enum ext_include_script_location location, struct sieve_script *script, 
-	const struct ext_include_script_info **included_r)
+	const struct ext_include_script_info **included_r, bool once)
 {
 	bool result = TRUE;
 	struct sieve_ast *ast;
@@ -366,16 +374,18 @@ bool ext_include_generate_include
 	}
 	
 	/* Check for circular include */
-	pctx = ctx;
-	while ( pctx != NULL ) {
-		if ( sieve_script_equals(pctx->script, script) ) {
-			sieve_command_generate_error(gentr, cmd, "circular include");
+	if ( !once ) {
+		pctx = ctx;
+		while ( pctx != NULL ) {
+			if ( sieve_script_equals(pctx->script, script) ) {
+				sieve_command_generate_error(gentr, cmd, "circular include");
 				
-			return FALSE;
-		}
+				return FALSE;
+			}
 		
-		pctx = pctx->parent;
-	}	
+			pctx = pctx->parent;
+		}
+	}
 
 	/* Get binary context */
 	binctx = ext_include_binary_init(sbin, cgenv->ast);
@@ -438,7 +448,11 @@ bool ext_include_generate_include
 			if ( sbin != NULL )		
 				(void) sieve_binary_block_set_active(sbin, this_block_id, NULL); 	
 			sieve_generator_free(&subgentr);
-		} else result = FALSE;
+		} else {
+			sieve_sys_error("include: failed to activate binary  block %d for "
+				"generating code for the included script", inc_block_id);
+			result = FALSE;
+		}
 		
 		/* Cleanup */
 		sieve_ast_unref(&ast);		
@@ -454,27 +468,39 @@ bool ext_include_generate_include
  */
 
 static int ext_include_runtime_check_circular
-(const struct sieve_runtime_env *renv, 
-	struct ext_include_interpreter_context *ctx,
+(struct ext_include_interpreter_context *ctx,
 	const struct ext_include_script_info *include)
 {
 	struct ext_include_interpreter_context *pctx;
-	int result = SIEVE_EXEC_OK;
 
 	pctx = ctx;
-	while ( result > 0 && pctx != NULL ) {
+	while ( pctx != NULL ) {
 
-		if ( sieve_script_equals(include->script, pctx->script) ) {
-			sieve_runtime_trace_error(renv, 
-				"circular include for script: %s [%d]", 
-				sieve_script_name(include->script), include->block_id);
-			result = SIEVE_EXEC_BIN_CORRUPT;
-		}
+		if ( sieve_script_equals(include->script, pctx->script) )
+			return TRUE;
 
 		pctx = pctx->parent;
 	}
 
-	return result;
+	return FALSE;
+}
+
+static bool ext_include_runtime_include_mark
+(struct ext_include_interpreter_context *ctx,
+	const struct ext_include_script_info *include, bool once)
+{
+	struct sieve_script *const *includes;
+	unsigned int count, i;
+	
+	includes = array_get(&ctx->global->included_scripts, &count);
+	for ( i = 0; i < count; i++ )	{
+		if ( sieve_script_equals(include->script, includes[i]) )
+			return ( !once );
+	}
+	
+	array_append(&ctx->global->included_scripts, &include->script, 1);
+
+	return TRUE;
 }
 
 bool ext_include_execute_include
@@ -490,7 +516,7 @@ bool ext_include_execute_include
 	included = ext_include_binary_script_get_included(binctx, include_id);
 	if ( included == NULL ) {
 		sieve_runtime_trace_error(renv, "invalid include id: %d", include_id);
-        return SIEVE_EXEC_BIN_CORRUPT;
+		return SIEVE_EXEC_BIN_CORRUPT;
 	}
 
 	ctx = ext_include_get_interpreter_context(renv->interp);
@@ -499,6 +525,27 @@ bool ext_include_execute_include
 		"INCLUDE command (script: %s, id: %d block: %d) START::", 
 		sieve_script_name(included->script), include_id, included->block_id);
 
+	/* If :once modifier is specified, check for duplicate include */
+	if ( !ext_include_runtime_include_mark(ctx, included, once) ) {
+		/* skip */
+
+		sieve_runtime_trace(renv, 
+			"INCLUDE command (block: %d) SKIPPED ::", included->block_id);
+		return result;
+	}
+
+	/* Check circular include during interpretation as well. 
+	 * Let's not trust binaries.
+	 */
+	if ( ext_include_runtime_check_circular(ctx, included) ) {
+		sieve_runtime_trace_error(renv, 
+			"circular include for script: %s [%d]", 
+			sieve_script_name(included->script), included->block_id);
+
+		/* Situation has no valid way to emerge at runtime */
+		return SIEVE_EXEC_BIN_CORRUPT; 
+	}
+
 	if ( ctx->parent == NULL ) {
 		struct ext_include_interpreter_context *curctx = NULL;
 		struct sieve_error_handler *ehandler = 
@@ -508,7 +555,7 @@ bool ext_include_execute_include
 		bool interrupted = FALSE;	
 
 		/* We are the top-level interpreter instance */	
-
+		
 		/* Activate block for included script */
 		if ( !sieve_binary_block_set_active
 			(renv->sbin, included->block_id, &this_block_id) ) {			
@@ -571,28 +618,23 @@ bool ext_include_execute_include
 					if ( curctx->include != NULL ) {
 
 						/* Sub-include requested */
-				
-						/* Check circular include during interpretation as well. 
-						 * Let's not trust binaries.
-						 */
-						result = ext_include_runtime_check_circular
-							(renv, curctx, curctx->include);
-													
+															
 						/* Activate the sub-include's block */
-						if ( result > 0 && !sieve_binary_block_set_active
+						if ( !sieve_binary_block_set_active
 							(renv->sbin, curctx->include->block_id, NULL) ) {
 							sieve_runtime_trace_error(renv, "invalid block id: %d", 
 								curctx->include->block_id);
 							result = SIEVE_EXEC_BIN_CORRUPT;
 						}
-						
+				
 						if ( result > 0 ) {
 							/* Create sub-interpreter */
 							subinterp = sieve_interpreter_create(renv->sbin, ehandler);			
 
 							if ( subinterp != NULL ) {
 								curctx = ext_include_interpreter_context_init_child
-									(subinterp, curctx, curctx->include->script, curctx->include);
+									(subinterp, curctx, curctx->include->script, 
+										curctx->include);
 
 								/* Start the sub-include's interpreter */
 								curctx->include = NULL;
@@ -633,8 +675,9 @@ bool ext_include_execute_include
 		(void) sieve_binary_block_set_active(renv->sbin, this_block_id, NULL); 	
 	} else {
 		/* We are an included script already, defer inclusion to main interpreter */
+
 		ctx->include = included;
-		sieve_interpreter_interrupt(renv->interp);	
+		sieve_interpreter_interrupt(renv->interp);
 	}
 	
 	return result;
diff --git a/src/lib-sieve/plugins/include/ext-include-common.h b/src/lib-sieve/plugins/include/ext-include-common.h
index 5d2e8d84f333f7da83234e7782d1d7695d39922d..06e191a2a94ad72cebafc06de13638374b6a0686 100644
--- a/src/lib-sieve/plugins/include/ext-include-common.h
+++ b/src/lib-sieve/plugins/include/ext-include-common.h
@@ -94,7 +94,7 @@ void ext_include_register_generator_context
 bool ext_include_generate_include
 	(const struct sieve_codegen_env *cgenv, struct sieve_command_context *cmd,
 		enum ext_include_script_location location, struct sieve_script *script, 
-		const struct ext_include_script_info **included_r);
+		const struct ext_include_script_info **included_r, bool once);
 
 /* Interpreter context */
 
diff --git a/src/lib-sieve/plugins/include/ext-include.c b/src/lib-sieve/plugins/include/ext-include.c
index a1430d9ad56e794a2d46069f3e0500eae07c5d32..14c7eac45bec282c3d1b726bcedf515caaaf0e87 100644
--- a/src/lib-sieve/plugins/include/ext-include.c
+++ b/src/lib-sieve/plugins/include/ext-include.c
@@ -6,7 +6,7 @@
  *
  * Authors: Stephan Bosch
  * Specification: draft-ietf-sieve-include-01
- * Implementation: almost full; :once modifier and global namespace are missing. 
+ * Implementation: almost full; global namespace is missing. 
  * Status: experimental
  * 
  */
diff --git a/tests/extensions/include/included/once-2.sieve b/tests/extensions/include/included/once-2.sieve
index 226df6de39d1ac05cb357db22cabd76a4f936e58..53dc287630b31a72c6da555bfd87c7979b63d08c 100644
--- a/tests/extensions/include/included/once-2.sieve
+++ b/tests/extensions/include/included/once-2.sieve
@@ -7,6 +7,6 @@ set "result" "${result} TWO";
 
 keep;
 
-include "once-1.sieve";
+include :once "once-1.sieve";
 
 return;
diff --git a/tests/extensions/include/included/once-3.sieve b/tests/extensions/include/included/once-3.sieve
new file mode 100644
index 0000000000000000000000000000000000000000..739651ed063c40c443218b54a8f9ae2e44305090
--- /dev/null
+++ b/tests/extensions/include/included/once-3.sieve
@@ -0,0 +1,3 @@
+require "include";
+
+include "once-4";
diff --git a/tests/extensions/include/included/once-4.sieve b/tests/extensions/include/included/once-4.sieve
new file mode 100644
index 0000000000000000000000000000000000000000..9cc1a47a3601c85718ccb6989021db04d10f09d4
--- /dev/null
+++ b/tests/extensions/include/included/once-4.sieve
@@ -0,0 +1,3 @@
+require "include";
+
+include :once "once-3";
diff --git a/tests/extensions/include/once.svtest b/tests/extensions/include/once.svtest
index 1a08c8da4d1a05684fee7288ecb6a76a5160c4c9..3395c6b6b8f2b59968709b7510aa438b168900b4 100644
--- a/tests/extensions/include/once.svtest
+++ b/tests/extensions/include/once.svtest
@@ -18,3 +18,7 @@ test "Included Once" {
 		test_fail "unexpected result value: ${result}";
 	}
 }
+
+test "Included Once recursive" {
+	include "once-3";
+}