diff --git a/Makefile.am b/Makefile.am
index a1e1eb6e7ff06079fd21e86ed6868cf59856676d..a3daf563fa0291fb7d2e7f9dfb465ebf6ee5e9aa 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -130,6 +130,7 @@ test_cases = \
 	tests/extensions/editheader/deleteheader.svtest \
 	tests/extensions/editheader/alternating.svtest \
 	tests/extensions/editheader/utf8.svtest \
+	tests/extensions/editheader/protected.svtest \
 	tests/extensions/editheader/errors.svtest \
 	tests/extensions/vnd.dovecot/debug/execute.svtest \
 	tests/deprecated/notify/basic.svtest \
diff --git a/TODO b/TODO
index 42517f433a0759e21cce43b8d91465e73e35682e..f32a61568806ba7892df357154369bc4ee6ae7e1 100644
--- a/TODO
+++ b/TODO
@@ -2,8 +2,6 @@ Current activities:
 
 * Implement editheader extension
 	- Implement configurable limit on header value length
-	- Implement configurable list of protected headers, with Received: and
-	  Auto-Submitted: headers always protected.
 	- Add command syntax checks to the test suite.
 
 Parallel plugin-based efforts:
diff --git a/src/lib-sieve/plugins/editheader/Makefile.am b/src/lib-sieve/plugins/editheader/Makefile.am
index 25ca1c9940b5745ae6c69a83cc4c5965be4fda55..78c2fd449a9c71390e468cf9b9b6bbec50c45159 100644
--- a/src/lib-sieve/plugins/editheader/Makefile.am
+++ b/src/lib-sieve/plugins/editheader/Makefile.am
@@ -10,7 +10,8 @@ commands = \
 
 libsieve_ext_editheader_la_SOURCES = \
 	$(commands) \
-	ext-editheader.c
+	ext-editheader.c \
+	ext-editheader-common.c
 
 noinst_HEADERS = \
 	ext-editheader-common.h
diff --git a/src/lib-sieve/plugins/editheader/cmd-addheader.c b/src/lib-sieve/plugins/editheader/cmd-addheader.c
index 8625684f076be298004f9362b6bcd3bd5f87ba7f..f7b001da374b624e139e3fcae3f5aebf8c90d1e5 100644
--- a/src/lib-sieve/plugins/editheader/cmd-addheader.c
+++ b/src/lib-sieve/plugins/editheader/cmd-addheader.c
@@ -88,18 +88,18 @@ const struct sieve_operation_def addheader_operation = {
  */
 
 static bool cmd_addheader_validate
-(struct sieve_validator *valdtr, struct sieve_command *tst)
+(struct sieve_validator *valdtr, struct sieve_command *cmd)
 {
-	struct sieve_ast_argument *arg = tst->first_positional;
+	struct sieve_ast_argument *arg = cmd->first_positional;
 
 	/* Check field-name syntax */
 
 	if ( !sieve_validate_positional_argument
-		(valdtr, tst, arg, "field-name", 1, SAAT_STRING) ) {
+		(valdtr, cmd, arg, "field-name", 1, SAAT_STRING) ) {
 		return FALSE;
 	}
 
-	if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) )
+	if ( !sieve_validator_argument_activate(valdtr, cmd, arg, FALSE) )
 		return FALSE;
 	
 	if ( sieve_argument_is_string_literal(arg) ) {
@@ -111,6 +111,12 @@ static bool cmd_addheader_validate
 					str_sanitize(str_c(fname), 80));
 			return FALSE;
 		}
+
+		if ( ext_editheader_header_is_protected(cmd->ext, str_c(fname)) ) {
+			sieve_argument_validate_warning(valdtr, arg, "addheader command: "
+				"specified header field `%s' is protected "
+				"(modification will be denied)", str_sanitize(str_c(fname), 80));
+		}
 	}
 
 	/* Check value syntax */
@@ -118,11 +124,11 @@ static bool cmd_addheader_validate
 	arg = sieve_ast_argument_next(arg);
 
 	if ( !sieve_validate_positional_argument
-		(valdtr, tst, arg, "value", 2, SAAT_STRING) ) {
+		(valdtr, cmd, arg, "value", 2, SAAT_STRING) ) {
 		return FALSE;
 	}
 
-	if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) )
+	if ( !sieve_validator_argument_activate(valdtr, cmd, arg, FALSE) )
 		return FALSE;
 
 	if ( sieve_argument_is_string_literal(arg) ) {
@@ -207,6 +213,7 @@ static bool cmd_addheader_operation_dump
 static int cmd_addheader_operation_execute
 (const struct sieve_runtime_env *renv, sieve_size_t *address)
 {
+	const struct sieve_extension *this_ext = renv->oprtn->ext;
 	string_t *field_name;
 	string_t *value;
 	struct edit_mail *edmail;
@@ -259,7 +266,14 @@ static int cmd_addheader_operation_execute
 			str_sanitize(str_c(field_name), 80));
 		return SIEVE_EXEC_FAILURE;
 	}
-
+	
+	if ( ext_editheader_header_is_protected(this_ext, str_c(field_name)) ) {
+		sieve_runtime_warning(renv, NULL, "addheader action: "
+			"specified header field `%s' is protected (modification denied)",
+			str_sanitize(str_c(field_name), 80));
+		return SIEVE_EXEC_OK;
+	}
+	
 	if ( !rfc2822_header_field_body_verify
 		(str_c(value), str_len(value), TRUE, TRUE) ) {
 		sieve_runtime_error(renv, NULL, "addheader action: "
diff --git a/src/lib-sieve/plugins/editheader/cmd-deleteheader.c b/src/lib-sieve/plugins/editheader/cmd-deleteheader.c
index fa59314a59e548dfd415c391fa61f3a78dbef669..8f2bdccd222f8d9be33a89e8f6524cac6f288e9a 100644
--- a/src/lib-sieve/plugins/editheader/cmd-deleteheader.c
+++ b/src/lib-sieve/plugins/editheader/cmd-deleteheader.c
@@ -258,6 +258,12 @@ static bool cmd_deleteheader_validate
 				str_sanitize(str_c(fname), 80));
 			return FALSE;
 		}
+
+		if ( ext_editheader_header_is_protected(cmd->ext, str_c(fname)) ) {
+			sieve_argument_validate_warning(valdtr, arg, "deleteheader command: "
+				"specified header field `%s' is protected "
+				"(modification will be denied)", str_sanitize(str_c(fname), 80));
+		}
 	}
 	
 	/* Value patterns argument */
@@ -363,6 +369,7 @@ static bool cmd_deleteheader_operation_dump
 static int cmd_deleteheader_operation_execute
 (const struct sieve_runtime_env *renv, sieve_size_t *address)
 {
+	const struct sieve_extension *this_ext = renv->oprtn->ext;
 	int opt_code = 0;
 	struct sieve_operand oprnd;
 	struct sieve_comparator cmp = 
@@ -437,6 +444,13 @@ static int cmd_deleteheader_operation_execute
 		return SIEVE_EXEC_FAILURE;
 	}
 
+	if ( ext_editheader_header_is_protected(this_ext, str_c(field_name)) ) {
+		sieve_runtime_warning(renv, NULL, "deleteheader action: "
+			"specified header field `%s' is protected (modification denied)",
+			str_sanitize(str_c(field_name), 80));
+		return SIEVE_EXEC_OK;
+	}
+
 	/*
 	 * Execute command
 	 */
diff --git a/src/lib-sieve/plugins/editheader/ext-editheader-common.c b/src/lib-sieve/plugins/editheader/ext-editheader-common.c
index 5ecd19674f88a49062a0d68a0cc2d8851101034d..c019ee12271ead082936f90478daeb592c11389a 100644
--- a/src/lib-sieve/plugins/editheader/ext-editheader-common.c
+++ b/src/lib-sieve/plugins/editheader/ext-editheader-common.c
@@ -1,28 +1,135 @@
 /* Copyright (c) 2002-2011 Pigeonhole authors, see the included COPYING file
  */
 
-#ifndef __EXT_EDITHEADER_COMMON_H
-#define __EXT_EDITHEADER_COMMON_H
+#include "lib.h"
+#include "mempool.h"
+#include "array.h"
 
-/*
- * Extensions
- */
+#include "rfc2822.h"
+
+#include "sieve-common.h"
+#include "sieve-error.h"
+#include "sieve-settings.h"
+#include "sieve-extensions.h"
 
-extern const struct sieve_extension_def editheader_extension;
+#include "ext-editheader-common.h"
 
 /*
- * Commands
+ * Extension configuration
  */
 
-extern const struct sieve_command_def addheader_command;
-//extern const struct sieve_command_def deleteheader_command;
+struct ext_editheader_header {
+	const char *name;
+	
+	/* may extend this later */
+	unsigned int protected:1;
+};
+
+struct ext_editheader_config {
+	pool_t pool;
+
+	ARRAY_DEFINE(headers, struct ext_editheader_header);
+};
+
+static struct ext_editheader_header *ext_editheader_config_header_find
+(struct ext_editheader_config *ext_config, const char *hname)
+{
+	struct ext_editheader_header *headers;
+	unsigned int count, i;
+	
+	headers = array_get_modifiable(&ext_config->headers, &count);
+	for ( i = 0; i < count; i++ ) {
+		if ( strcasecmp(hname, headers[i].name) == 0 )
+			return &headers[i];
+	}
+
+	return NULL;	
+}
+
+bool ext_editheader_load
+(const struct sieve_extension *ext, void **context)
+{
+	struct ext_editheader_config *ext_config =
+		(struct ext_editheader_config *) *context;
+	struct sieve_instance *svinst = ext->svinst;
+	const char *protected;
+	pool_t pool;
+
+	if ( *context != NULL ) {
+		ext_editheader_unload(ext);
+		*context = NULL;
+	}
+
+	T_BEGIN {
+		pool = pool_alloconly_create("editheader_config", 512);
+		ext_config = p_new(pool, struct ext_editheader_config, 1);
+		ext_config->pool = pool;
+
+		p_array_init(&ext_config->headers, pool, 16);
+
+		protected = sieve_setting_get(svinst, "sieve_editheader_protected");
+		if ( protected != NULL ) {
+			const char **headers = t_strsplit_spaces(protected, " \t");
+
+			while ( *headers != NULL ) {
+				struct ext_editheader_header *header;
+
+				if ( !rfc2822_header_field_name_verify(*headers, strlen(*headers)) ) {
+					sieve_sys_warning(svinst, 
+						"editheader: setting sieve_editheader_protected contains "
+						"invalid header field name `%s' (ignored)", *headers);
+					continue;
+				}
+
+				header=ext_editheader_config_header_find(ext_config, *headers);
+				if ( header == NULL ) {
+					header = array_append_space(&ext_config->headers);
+					header->name = p_strdup(pool, *headers);
+				}
+
+				header->protected = TRUE;
+
+				headers++;
+			}
+		}
+	} T_END;
+
+	*context = (void *) ext_config;
+	return TRUE;	
+}
+
+void ext_editheader_unload(const struct sieve_extension *ext)
+{
+	struct ext_editheader_config *ext_config = 
+		(struct ext_editheader_config *) ext->context;
+	
+	if ( ext_config != NULL ) {
+		pool_unref(&ext_config->pool);
+	}
+}
 
 /*
- * Operations
+ * Protected headers
  */
 
-extern const struct sieve_operation_def addheader_operation;
-//extern const struct sieve_operation_def deleteheader_operation;
+bool ext_editheader_header_is_protected
+(const struct sieve_extension *ext, const char *hname)
+{
+	struct ext_editheader_config *ext_config = 
+		(struct ext_editheader_config *) ext->context;
+	const struct ext_editheader_header *header;
+	
+	if ( strcasecmp(hname, "received") == 0 
+		|| strcasecmp(hname, "auto-submitted") == 0 ) {
+		return TRUE;
+	}
+
+	if ( strcasecmp(hname, "subject") == 0 ) {
+		return FALSE;
+	}
 
+	if ( (header=ext_editheader_config_header_find(ext_config, hname)) == NULL )
+		return FALSE;
 
-#endif /* __EXT_EDITHEADER_COMMON_H */
+	return header->protected;
+}
diff --git a/src/lib-sieve/plugins/editheader/ext-editheader-common.h b/src/lib-sieve/plugins/editheader/ext-editheader-common.h
index 990ecb32a85ac51fdca7f5926ce9ad6ac73234b7..94e3d2698f68d4348b9e6d8c1a23ca4c165646d0 100644
--- a/src/lib-sieve/plugins/editheader/ext-editheader-common.h
+++ b/src/lib-sieve/plugins/editheader/ext-editheader-common.h
@@ -4,12 +4,6 @@
 #ifndef __EXT_EDITHEADER_COMMON_H
 #define __EXT_EDITHEADER_COMMON_H
 
-/*
- * Extensions
- */
-
-extern const struct sieve_extension_def editheader_extension;
-
 /*
  * Commands
  */
@@ -29,5 +23,21 @@ enum ext_imap4flags_opcode {
 extern const struct sieve_operation_def addheader_operation;
 extern const struct sieve_operation_def deleteheader_operation;
 
+/*
+ * Extension
+ */
+
+extern const struct sieve_extension_def editheader_extension;
+
+bool ext_editheader_load
+	(const struct sieve_extension *ext, void **context);
+void ext_editheader_unload(const struct sieve_extension *ext);
+
+/*
+ * Protected headers
+ */
+
+bool ext_editheader_header_is_protected
+	(const struct sieve_extension *ext, const char *header);
 
 #endif /* __EXT_EDITHEADER_COMMON_H */
diff --git a/src/lib-sieve/plugins/editheader/ext-editheader.c b/src/lib-sieve/plugins/editheader/ext-editheader.c
index 884468f65e7f2581e67cd62fd5ceeb2c6eadd2f4..dab480e4b80433ef87713427fbe8457e021dc18e 100644
--- a/src/lib-sieve/plugins/editheader/ext-editheader.c
+++ b/src/lib-sieve/plugins/editheader/ext-editheader.c
@@ -46,7 +46,8 @@ static bool ext_editheader_validator_load
 
 const struct sieve_extension_def editheader_extension = {
 	"editheader",
-	NULL, NULL,
+	ext_editheader_load,
+	ext_editheader_unload,
 	ext_editheader_validator_load,
 	NULL, NULL, NULL, NULL, NULL,
 	SIEVE_EXT_DEFINE_OPERATIONS(editheader_operations),
diff --git a/tests/extensions/editheader/protected.svtest b/tests/extensions/editheader/protected.svtest
new file mode 100644
index 0000000000000000000000000000000000000000..70a0a6fa8675e95a0088a25dd4367bec6affa5fb
--- /dev/null
+++ b/tests/extensions/editheader/protected.svtest
@@ -0,0 +1,74 @@
+require "vnd.dovecot.testsuite";
+
+require "variables";
+
+require "editheader";
+
+set "message" text:
+Received: by example.com (Postfix, from userid 202)
+	id 32A131WFW23QWE4; Mon, 21 Nov 2011 05:25:26 +0200 (EET)
+Delivery-date: Mon, 21 Nov 2011 04:26:04 +0100
+Auto-Submitted: yes
+Subject: Frop!
+From: stephan@example.com
+To: tss@example.com
+
+Frop!
+.
+;
+
+test_set "message" "${message}";
+test "Default protected" {
+	if not exists "received" {
+		test_fail "received header did not exist in the first place";
+	}
+
+	if not exists "auto-submitted" {
+		test_fail "auto-submitted header did not exist in the first place";
+	}
+
+	deleteheader "received";
+	deleteheader "auto-submitted";
+	
+	if not exists "received" {
+		test_fail "protected received header was deleted";
+	}	
+
+	if not exists "auto-submitted" {
+		test_fail "protected auto-submitted header was deleted";
+	}	
+}
+
+test_config_set "sieve_editheader_protected" "subject delivery-date x-frop";
+test_config_reload :extension "editheader";
+
+test_set "message" "${message}";
+test "Configured protected" {
+	if not exists "delivery-date" {
+		test_fail "received header did not exist in the first place";
+	}
+
+	if not exists "subject" {
+		test_fail "received header did not exist in the first place";
+	}
+
+	if exists "x-frop" {
+		test_fail "x-frop header already present";
+	}
+
+	deleteheader "delivery-date";
+	deleteheader "subject";
+	addheader "x-frop" "Frop!";
+	
+	if not exists "delivery-date" {
+		test_fail "protected delivery-date header was deleted";
+	}	
+
+	if exists "subject" {
+		test_fail "subject header cannot be protected, but it was not deleted";
+	}	
+
+	if exists "x-frop" {
+		test_fail "protected x-frop header was added";
+	}
+}