diff --git a/src/lib-sieve/plugins/variables/Makefile.am b/src/lib-sieve/plugins/variables/Makefile.am
index 592a8eb63c5e2f03b71ff99fce2fe538174657be..f3457b79972de3706f231d95babf43d3a33658d3 100644
--- a/src/lib-sieve/plugins/variables/Makefile.am
+++ b/src/lib-sieve/plugins/variables/Makefile.am
@@ -7,6 +7,19 @@ AM_CPPFLAGS = \
 	-I$(dovecot_incdir)/src/lib-mail \
 	-I$(dovecot_incdir)/src/lib-storage 
 
+cmds = \
+	cmd-set.c
+
+tsts = \
+	tst-string.c
+
 libsieve_ext_variables_la_SOURCES = \
+	ext-variables-common.c \
+	$(cmds) \
+	$(tsts) \
 	ext-variables.c
 
+noinst_HEADERS = \
+	ext-variables-common.h
+
+
diff --git a/src/lib-sieve/plugins/variables/cmd-set.c b/src/lib-sieve/plugins/variables/cmd-set.c
new file mode 100644
index 0000000000000000000000000000000000000000..b05747b269c0eafb2d42f63b756d4d236ffb3417
--- /dev/null
+++ b/src/lib-sieve/plugins/variables/cmd-set.c
@@ -0,0 +1,217 @@
+#include "lib.h"
+
+#include "sieve-common.h"
+
+#include "sieve-code.h"
+#include "sieve-commands.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-code-dumper.h"
+
+#include "ext-variables-common.h"
+
+/* Forward declarations */
+
+static bool opc_set_dump
+	(const struct sieve_opcode *opcode,	
+		const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static bool opc_set_execute
+	(const struct sieve_opcode *opcode, 
+		const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+static bool cmd_set_registered
+	(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg);
+static bool cmd_set_validate
+	(struct sieve_validator *validator, struct sieve_command_context *cmd);
+static bool cmd_set_generate
+	(struct sieve_generator *generator,	struct sieve_command_context *ctx);
+
+/* Set command 
+ *	
+ * Syntax: 
+ *    set [MODIFIER] <name: string> <value: string>
+ */
+const struct sieve_command cmd_set = { 
+	"set",
+	SCT_COMMAND, 
+	2, 0, FALSE, FALSE, 
+	cmd_set_registered,
+	NULL,  
+	cmd_set_validate, 
+	cmd_set_generate, 
+	NULL 
+};
+
+/* set opcode */
+const struct sieve_opcode cmd_set_opcode = { 
+	"SET",
+	SIEVE_OPCODE_CUSTOM,
+	&variables_extension,
+	EXT_VARIABLES_OPCODE_SET,
+	opc_set_dump, 
+	opc_set_execute
+};
+
+/* Tag validation */
+
+/* [MODIFIER]:
+ *   ":lower" / ":upper" / ":lowerfirst" / ":upperfirst" /
+ *             ":quotewildcard" / ":length"
+ *
+ * FIXME: Provide support to add further modifiers (as needed by notify) 
+ */
+ 
+static bool tag_modifier_is_instance_of
+	(struct sieve_validator *validator, const char *tag)
+{
+	return ext_variables_set_modifier_find(validator, tag) != NULL;
+}
+
+static bool tag_modifier_validate
+(struct sieve_validator *validator, struct sieve_ast_argument **arg, 
+	struct sieve_command_context *cmd)
+{
+	struct sieve_ast_argument *tag = *arg;
+	
+	/* Skip parameter */
+	*arg = sieve_ast_argument_next(*arg);
+	
+	return TRUE;
+}
+
+static bool tag_modifier_generate
+(struct sieve_generator *generator, struct sieve_ast_argument *arg, 
+	struct sieve_command_context *cmd ATTR_UNUSED)
+{
+	return TRUE;
+}
+
+const struct sieve_argument modifier_tag = { 
+	"MODIFIER",
+	tag_modifier_is_instance_of, 
+	tag_modifier_validate, 
+	NULL,
+	tag_modifier_generate 
+};
+
+/* Pre-defined modifiers */
+
+const struct ext_variables_set_modifier lower_modifier = {
+	"lower", 
+	EXT_VARIABLES_SET_MODIFIER_LOWER,
+	40
+};
+
+const struct ext_variables_set_modifier upper_modifier = {
+	"upper", 
+	EXT_VARIABLES_SET_MODIFIER_UPPER,
+	40
+};
+
+const struct ext_variables_set_modifier lowerfirst_modifier = {
+	"lowerfirst", 
+	EXT_VARIABLES_SET_MODIFIER_LOWERFIRST,
+	30
+};
+
+const struct ext_variables_set_modifier upperfirst_modifier = {
+	"upperfirst", 
+	EXT_VARIABLES_SET_MODIFIER_UPPERFIRST,
+	30
+};
+
+const struct ext_variables_set_modifier quotewildcard_modifier = {
+	"quotewildcard",
+	EXT_VARIABLES_SET_MODIFIER_QUOTEWILDCARD,
+	20
+};
+
+const struct ext_variables_set_modifier length_modifier = {
+	"length", 
+	EXT_VARIABLES_SET_MODIFIER_LENGTH,
+	10
+};
+
+/* Command registration */
+
+static bool cmd_set_registered
+	(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg) 
+{
+	sieve_validator_register_tag(validator, cmd_reg, &modifier_tag, 0); 	
+
+	return TRUE;
+}
+
+/* Command validation */
+
+static bool cmd_set_validate(struct sieve_validator *validator, 
+	struct sieve_command_context *cmd) 
+{ 	
+	struct sieve_ast_argument *arg = cmd->first_positional;
+
+	if ( !sieve_validate_positional_argument
+		(validator, cmd, arg, "name", 1, SAAT_STRING) ) {
+		return FALSE;
+	}
+	sieve_validator_argument_activate(validator, cmd, arg, TRUE);	
+	
+	arg = sieve_ast_argument_next(arg);
+	
+	if ( !sieve_validate_positional_argument
+		(validator, cmd, arg, "value", 2, SAAT_STRING) ) {
+		return FALSE;
+	}
+	sieve_validator_argument_activate(validator, cmd, arg, TRUE);	
+	
+	return TRUE;
+}
+
+/*
+ * Generation
+ */
+ 
+static bool cmd_set_generate
+	(struct sieve_generator *generator,	struct sieve_command_context *ctx) 
+{
+	sieve_generator_emit_opcode_ext
+		(generator, &cmd_set_opcode, ext_variables_my_id);
+
+	/* Generate arguments */
+	if ( !sieve_generate_arguments(generator, ctx, NULL) )
+		return FALSE;	
+
+	return TRUE;
+}
+
+/* 
+ * Code dump
+ */
+ 
+static bool opc_set_dump
+(const struct sieve_opcode *opcode ATTR_UNUSED,
+	const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{	
+	sieve_code_dumpf(denv, "SET");
+	sieve_code_descend(denv);
+
+	return 
+		sieve_opr_string_dump(denv, address) &&
+		sieve_opr_string_dump(denv, address);
+}
+
+/* 
+ * Code execution
+ */
+ 
+static bool opc_set_execute
+(const struct sieve_opcode *opcode ATTR_UNUSED,
+	const struct sieve_runtime_env *renv, sieve_size_t *address)
+{	
+	return TRUE;
+}
+
+
+
+
+
diff --git a/src/lib-sieve/plugins/variables/ext-variables-common.c b/src/lib-sieve/plugins/variables/ext-variables-common.c
new file mode 100644
index 0000000000000000000000000000000000000000..932e7631905c0112ac17acdcadca7d68cf2bf448
--- /dev/null
+++ b/src/lib-sieve/plugins/variables/ext-variables-common.c
@@ -0,0 +1,81 @@
+#include "lib.h"
+#include "hash.h"
+
+#include "sieve-common.h"
+#include "sieve-validator.h"
+
+#include "ext-variables-common.h"
+
+/* Forward declarations */
+
+extern const struct ext_variables_set_modifier lower_modifier;
+extern const struct ext_variables_set_modifier upper_modifier;
+extern const struct ext_variables_set_modifier lowerfirst_modifier;
+extern const struct ext_variables_set_modifier upperfirst_modifier;
+extern const struct ext_variables_set_modifier quotewildcard_modifier;
+extern const struct ext_variables_set_modifier length_modifier;
+
+const struct ext_variables_set_modifier *default_set_modifiers[] = { 
+	&lower_modifier, &upper_modifier, &lowerfirst_modifier, &upperfirst_modifier,
+	&quotewildcard_modifier, &length_modifier
+};
+
+const unsigned int default_set_modifiers_count = 
+	N_ELEMENTS(default_set_modifiers);
+
+/* Validator context */
+
+struct ext_variables_validator_context {
+	struct hash_table *set_modifiers;
+};
+
+static struct ext_variables_validator_context *
+ext_variables_validator_context_create(struct sieve_validator *validator)
+{		
+	pool_t pool = sieve_validator_pool(validator);
+	struct ext_variables_validator_context *ctx;
+	
+	ctx = p_new(pool, struct ext_variables_validator_context, 1);
+	ctx->set_modifiers = hash_create
+		(pool, pool, 0, str_hash, (hash_cmp_callback_t *)strcmp);
+
+	sieve_validator_extension_set_context
+		(validator, ext_variables_my_id, (void *) ctx);
+
+	return ctx;
+}
+
+void ext_variables_validator_initialize(struct sieve_validator *validator)
+{
+	unsigned int i;
+	struct ext_variables_validator_context *ctx;
+	
+	/* Create our context */
+	ctx = ext_variables_validator_context_create(validator);
+	
+	for ( i = 0; i < default_set_modifiers_count; i++ ) {
+		const struct ext_variables_set_modifier *mod = default_set_modifiers[i];
+	
+		hash_insert(ctx->set_modifiers, (void *) mod->identifier, (void *) mod);
+	}
+}
+
+static inline struct ext_variables_validator_context *
+ext_variables_validator_context_get(struct sieve_validator *validator)
+{
+	return (struct ext_variables_validator_context *)
+		sieve_validator_extension_get_context(validator, ext_variables_my_id);
+}
+
+/* Set modifier registration */
+
+const struct ext_variables_set_modifier *ext_variables_set_modifier_find
+(struct sieve_validator *validator, const char *identifier)
+{
+	struct ext_variables_validator_context *ctx = 
+		ext_variables_validator_context_get(validator);
+		
+	return (const struct ext_variables_set_modifier *) 
+		hash_lookup(ctx->set_modifiers, identifier);
+}
+
diff --git a/src/lib-sieve/plugins/variables/ext-variables-common.h b/src/lib-sieve/plugins/variables/ext-variables-common.h
new file mode 100644
index 0000000000000000000000000000000000000000..b86c242ada1508214980fc03e75e32f242aa3fa8
--- /dev/null
+++ b/src/lib-sieve/plugins/variables/ext-variables-common.h
@@ -0,0 +1,40 @@
+#ifndef __EXT_VARIABLES_COMMON_H
+#define __EXT_VARIABLES_COMMON_H
+
+#include "sieve-common.h"
+
+extern int ext_variables_my_id;
+
+extern struct sieve_extension variables_extension;
+
+enum ext_variables_opcode {
+	EXT_VARIABLES_OPCODE_SET,
+	EXT_VARIABLES_OPCODE_STRING
+};
+
+/* Extension */
+
+void ext_variables_validator_initialize(struct sieve_validator *validator);
+
+/* Set modifiers */
+
+enum ext_variables_set_modifier_code {
+	EXT_VARIABLES_SET_MODIFIER_LOWER,
+	EXT_VARIABLES_SET_MODIFIER_UPPER,
+	EXT_VARIABLES_SET_MODIFIER_LOWERFIRST,
+	EXT_VARIABLES_SET_MODIFIER_UPPERFIRST,
+	EXT_VARIABLES_SET_MODIFIER_QUOTEWILDCARD,
+	EXT_VARIABLES_SET_MODIFIER_LENGTH
+};
+
+struct ext_variables_set_modifier {
+	const char *identifier;
+	int code;
+	
+	unsigned int precedence;
+};
+
+const struct ext_variables_set_modifier *ext_variables_set_modifier_find
+	(struct sieve_validator *validator, const char *identifier);
+
+#endif /* __EXT_VARIABLES_COMMON_H */
diff --git a/src/lib-sieve/plugins/variables/ext-variables.c b/src/lib-sieve/plugins/variables/ext-variables.c
index 9ce50ea54177a618ee8c9bf86fc0960840c2067c..d836a82fbfa6a580a3841d6f575bc0f58c5e384d 100644
--- a/src/lib-sieve/plugins/variables/ext-variables.c
+++ b/src/lib-sieve/plugins/variables/ext-variables.c
@@ -15,6 +15,8 @@
 #include "sieve-commands.h"
 #include "sieve-validator.h"
 
+#include "ext-variables-common.h"
+
 #include <ctype.h>
 
 /* Forward declarations */
@@ -22,22 +24,36 @@
 static bool ext_variables_load(int ext_id);
 static bool ext_variables_validator_load(struct sieve_validator *validator);
 
+/* Commands */
+
+extern const struct sieve_command cmd_set;
+extern const struct sieve_command tst_string;
+
+/* Opcodes */
+
+extern const struct sieve_opcode cmd_set_opcode;
+extern const struct sieve_opcode tst_string_opcode;
+
+const struct sieve_opcode *ext_variables_opcodes[] = {
+	&cmd_set_opcode, &tst_string_opcode
+};
+
 /* Extension definitions */
 
-static int ext_my_id;
+int ext_variables_my_id;
 	
 struct sieve_extension variables_extension = { 
 	"variables", 
 	ext_variables_load,
 	ext_variables_validator_load, 
 	NULL, NULL, NULL, 
-	SIEVE_EXT_DEFINE_NO_OPCODES, 
+	SIEVE_EXT_DEFINE_OPCODES(ext_variables_opcodes), 
 	NULL 
 };
 
 static bool ext_variables_load(int ext_id) 
 {
-	ext_my_id = ext_id;
+	ext_variables_my_id = ext_id;
 	return TRUE;
 }
 
@@ -142,7 +158,13 @@ static bool arg_variable_string_validate
 static bool ext_variables_validator_load
 	(struct sieve_validator *validator ATTR_UNUSED)
 {
-	sieve_validator_argument_override(validator, SAT_VAR_STRING, 
-		&variable_string_argument); 
+	/*sieve_validator_argument_override(validator, SAT_VAR_STRING, 
+		&variable_string_argument);*/ 
+		
+	sieve_validator_register_command(validator, &cmd_set);
+	sieve_validator_register_command(validator, &tst_string);
+	
+	ext_variables_validator_initialize(validator);
+
 	return TRUE;
 }
diff --git a/src/lib-sieve/plugins/variables/tst-string.c b/src/lib-sieve/plugins/variables/tst-string.c
new file mode 100644
index 0000000000000000000000000000000000000000..23e01d6190d0b9f780bde310a926755646e64f82
--- /dev/null
+++ b/src/lib-sieve/plugins/variables/tst-string.c
@@ -0,0 +1,237 @@
+#include <stdio.h>
+
+#include "sieve-commands.h"
+#include "sieve-commands-private.h"
+#include "sieve-code.h"
+#include "sieve-comparators.h"
+#include "sieve-match-types.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-code-dumper.h"
+
+#include "ext-variables-common.h"
+
+/* String test 
+ *
+ * Syntax:
+ *   string [COMPARATOR] [MATCH-TYPE]
+ *     <source: string-list> <key-list: string-list>
+ */
+
+static bool tst_string_registered
+	(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg);
+static bool tst_string_validate
+	(struct sieve_validator *validator, struct sieve_command_context *tst);
+static bool tst_string_generate
+	(struct sieve_generator *generator,	struct sieve_command_context *ctx);
+
+const struct sieve_command tst_string = { 
+	"string", 
+	SCT_TEST, 
+	2, 0, FALSE, FALSE,
+	tst_string_registered, 
+	NULL,
+	tst_string_validate, 
+	tst_string_generate, 
+	NULL 
+};
+
+/* Opcode */
+
+static bool tst_string_opcode_dump
+	(const struct sieve_opcode *opcode, 
+		const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static bool tst_string_opcode_execute
+	(const struct sieve_opcode *opcode, 
+		const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_opcode tst_string_opcode = { 
+	"string",
+	SIEVE_OPCODE_CUSTOM,
+	&variables_extension, 
+	EXT_VARIABLES_OPCODE_STRING, 
+	tst_string_opcode_dump, 
+	tst_string_opcode_execute 
+};
+
+/* Optional arguments */
+
+enum tst_string_optional {	
+	OPT_END,
+	OPT_COMPARATOR,
+	OPT_MATCH_TYPE
+};
+
+/* Test registration */
+
+static bool tst_string_registered
+	(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg) 
+{
+	/* The order of these is not significant */
+	sieve_comparators_link_tag(validator, cmd_reg, OPT_COMPARATOR);
+	sieve_match_types_link_tags(validator, cmd_reg, OPT_MATCH_TYPE);
+
+	return TRUE;
+}
+
+/* Test validation */
+
+static bool tst_string_validate
+	(struct sieve_validator *validator, struct sieve_command_context *tst) 
+{ 		
+	struct sieve_ast_argument *arg = tst->first_positional;
+	
+	if ( !sieve_validate_positional_argument
+		(validator, tst, arg, "source", 1, SAAT_STRING_LIST) ) {
+		return FALSE;
+	}
+	sieve_validator_argument_activate(validator, tst, arg, FALSE);
+	
+	arg = sieve_ast_argument_next(arg);
+
+	if ( !sieve_validate_positional_argument
+		(validator, tst, arg, "key list", 2, SAAT_STRING_LIST) ) {
+		return FALSE;
+	}
+	sieve_validator_argument_activate(validator, tst, arg, FALSE);
+
+	/* Validate the key argument to a specified match type */
+	sieve_match_type_validate(validator, tst, arg);
+	
+	return TRUE;
+}
+
+/* Test generation */
+
+static bool tst_string_generate
+	(struct sieve_generator *generator,	struct sieve_command_context *ctx) 
+{
+	sieve_generator_emit_opcode(generator, &tst_string_opcode);
+
+ 	/* Generate arguments */
+	if ( !sieve_generate_arguments(generator, ctx, NULL) )
+		return FALSE;
+	
+	return TRUE;
+}
+
+/* Code dump */
+
+static bool tst_string_opcode_dump
+(const struct sieve_opcode *opcode ATTR_UNUSED,
+	const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+	int opt_code = 1;
+
+	sieve_code_dumpf(denv, "STRING-TEST");
+	sieve_code_descend(denv);
+
+	/* Handle any optional arguments */
+	if ( sieve_operand_optional_present(denv->sbin, address) ) {
+		while ( opt_code != 0 ) {
+			if ( !sieve_operand_optional_read(denv->sbin, address, &opt_code) ) 
+				return FALSE;
+
+			switch ( opt_code ) {
+			case 0:
+				break;
+			case OPT_COMPARATOR:
+				sieve_opr_comparator_dump(denv, address);
+				break;
+			case OPT_MATCH_TYPE:
+				sieve_opr_match_type_dump(denv, address);
+				break;
+			default: 
+				return FALSE;
+			}
+ 		}
+	}
+
+	return
+		sieve_opr_stringlist_dump(denv, address) &&
+		sieve_opr_stringlist_dump(denv, address);
+}
+
+/* Code execution */
+
+static bool tst_string_opcode_execute
+(const struct sieve_opcode *opcode ATTR_UNUSED, 
+	const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+	bool result = TRUE;
+	int opt_code = 1;
+	const struct sieve_comparator *cmp = &i_octet_comparator;
+	const struct sieve_match_type *mtch = &is_match_type;
+	struct sieve_match_context *mctx;
+	struct sieve_coded_stringlist *source;
+	struct sieve_coded_stringlist *key_list;
+	string_t *src_item;
+	bool matched;
+	
+	printf("?? STRING\n");
+
+	/* Handle any optional arguments */
+	if ( sieve_operand_optional_present(renv->sbin, address) ) {
+		while ( opt_code != 0 ) {
+			if ( !sieve_operand_optional_read(renv->sbin, address, &opt_code) )
+				return FALSE;
+
+			switch ( opt_code ) {
+			case 0: 
+				break;
+			case OPT_COMPARATOR:
+				cmp = sieve_opr_comparator_read(renv->sbin, address);
+				break;
+			case OPT_MATCH_TYPE:
+				mtch = sieve_opr_match_type_read(renv->sbin, address);
+				break;
+			default:
+				return FALSE;
+			}
+		}
+	}
+
+	t_push();
+		
+	/* Read string-list */
+	if ( (source=sieve_opr_stringlist_read(renv->sbin, address)) == NULL ) {
+		t_pop();
+		return FALSE;
+	}
+	
+	/* Read key-list */
+	if ( (key_list=sieve_opr_stringlist_read(renv->sbin, address)) == NULL ) {
+		t_pop();
+		return FALSE;
+	}
+
+	mctx = sieve_match_begin(mtch, cmp, key_list); 	
+
+	/* Iterate through all requested strings to match */
+	src_item = NULL;
+	matched = FALSE;
+	while ( !matched && 
+		(result=sieve_coded_stringlist_next_item(source, &src_item)) 
+		&& src_item != NULL ) {
+			
+		/*
+		if ( mail_get_strings_utf8(renv->msgdata->mail, str_c(hdr_item), &strings) >= 0 ) {	
+			
+			int i;
+			for ( i = 0; !matched && strings[i] != NULL; i++ ) {
+				if ( sieve_match_value(mctx, strings[i], strlen(strings[i])) )
+					matched = TRUE;				
+			} 
+		}*/
+	}
+
+	matched = sieve_match_end(mctx) || matched; 	
+	
+	t_pop();
+	
+	if ( result )
+		sieve_interpreter_set_test_result(renv->interp, matched);
+	
+	return result;
+}
diff --git a/src/lib-sieve/plugins/variables/variables.sieve b/src/lib-sieve/plugins/variables/variables.sieve
index 43681b5c9becf81046c6897742619ed69567d192..498c48ae7cc92183594f60686ca1e4badc73af85 100644
--- a/src/lib-sieve/plugins/variables/variables.sieve
+++ b/src/lib-sieve/plugins/variables/variables.sieve
@@ -1,3 +1,4 @@
 require "variables";
 
-
+set :upper "foo" "foosome";
+set :lower "bar" "bareable";