diff --git a/doc/plugins/sieve_extprograms.txt b/doc/plugins/sieve_extprograms.txt
index d2b59a668b1f6ff8dd1c152a3a112574135b4caf..4110fce857613ea30b681194d4e8537be6c8f6ca 100644
--- a/doc/plugins/sieve_extprograms.txt
+++ b/doc/plugins/sieve_extprograms.txt
@@ -106,9 +106,16 @@ sieve_<extension>_bin_dir =
   execute directly and pipe messages to.
 
 sieve_<extension>_exec_timeout = 10s
-  Configures the maximum execution time after which the program is forcefully
+  Configures the maximum execution time after which the program is forcibly
   terminated.
 
+sieve_<extension>_input_eol = crlf
+  Determines the end-of-line character sequence used for the data piped to
+  external programs. The default is currently "crlf", which represents a
+  sequence of the carriage return (CR) and line feed (LF) characters. This
+  matches the Internet Message Format (RFC5322) and what Sieve itself uses as a
+  line ending. Set this setting to "lf" to use a single LF character instead.
+
 Examples
 --------
 
diff --git a/src/plugins/sieve-extprograms/sieve-extprograms-common.c b/src/plugins/sieve-extprograms/sieve-extprograms-common.c
index 1619f095ee00f96b46e2e2e614095d4e558ec3a3..cf52b80a93e8585e9e19a7adef318e311fc04552 100644
--- a/src/plugins/sieve-extprograms/sieve-extprograms-common.c
+++ b/src/plugins/sieve-extprograms/sieve-extprograms-common.c
@@ -62,7 +62,7 @@ struct sieve_extprograms_config *sieve_extprograms_config_init
 	struct sieve_instance *svinst = ext->svinst;
 	struct sieve_extprograms_config *ext_config;
 	const char *extname = sieve_extension_name(ext);
-	const char *bin_dir, *socket_dir;
+	const char *bin_dir, *socket_dir, *input_eol;
 	sieve_number_t execute_timeout;
 
 	extname = strrchr(extname, '.');
@@ -73,6 +73,8 @@ struct sieve_extprograms_config *sieve_extprograms_config_init
 		(svinst, t_strdup_printf("sieve_%s_bin_dir", extname));
 	socket_dir = sieve_setting_get
 		(svinst, t_strdup_printf("sieve_%s_socket_dir", extname));
+	input_eol = sieve_setting_get
+		(svinst, t_strdup_printf("sieve_%s_input_eol", extname));
 	
 	ext_config = i_new(struct sieve_extprograms_config, 1);
 	ext_config->execute_timeout = 
@@ -94,6 +96,10 @@ struct sieve_extprograms_config *sieve_extprograms_config_init
 				&execute_timeout)) {
 			ext_config->execute_timeout = execute_timeout;
 		}
+
+		ext_config->default_input_eol = SIEVE_EXTPROGRAMS_EOL_CRLF;
+		if (input_eol != NULL && strcasecmp(input_eol, "lf") == 0)
+			ext_config->default_input_eol = SIEVE_EXTPROGRAMS_EOL_LF;
 	}
 
 	if ( sieve_extension_is(ext, vnd_pipe_extension) ) 
@@ -366,6 +372,8 @@ int sieve_extprogram_command_read_operands
 
 struct sieve_extprogram {
 	struct sieve_instance *svinst;
+	const struct sieve_extprograms_config *ext_config;
+
 	const struct sieve_script_env *scriptenv;
 	struct program_client_settings set;
 	struct program_client *program_client;
@@ -515,6 +523,7 @@ struct sieve_extprogram *sieve_extprogram_create
 
 	sprog = i_new(struct sieve_extprogram, 1);
 	sprog->svinst = ext->svinst;
+	sprog->ext_config = ext_config;
 	sprog->scriptenv = senv;
 
 	sprog->set.client_connect_timeout_msecs =
@@ -574,7 +583,20 @@ void sieve_extprogram_set_output
 void sieve_extprogram_set_input
 (struct sieve_extprogram *sprog, struct istream *input)
 {
+	switch (sprog->ext_config->default_input_eol) {
+	case SIEVE_EXTPROGRAMS_EOL_LF:
+		input = i_stream_create_lf(input);
+		break;
+	case SIEVE_EXTPROGRAMS_EOL_CRLF:
+		input = i_stream_create_crlf(input);
+		break;
+	default:
+		i_unreached();
+	}
+
 	program_client_set_input(sprog->program_client, input);
+
+	i_stream_unref(&input);
 }
 
 void sieve_extprogram_set_output_seekable
@@ -601,12 +623,7 @@ int sieve_extprogram_set_input_mail
 	if (mail_get_stream(mail, NULL, NULL, &input) < 0)
 		return -1;
 
-	/* Make sure the message contains CRLF consistently */
-	input = i_stream_create_crlf(input);
-
-	program_client_set_input(sprog->program_client, input);
-	i_stream_unref(&input);
-
+	sieve_extprogram_set_input(sprog, input);
 	return 1;
 }
 
diff --git a/src/plugins/sieve-extprograms/sieve-extprograms-common.h b/src/plugins/sieve-extprograms/sieve-extprograms-common.h
index d769e36070865c81815cddc74c26c1a59fe75c4b..1c09d96ee6e6462006f45abff04b8cd02fda9567 100644
--- a/src/plugins/sieve-extprograms/sieve-extprograms-common.h
+++ b/src/plugins/sieve-extprograms/sieve-extprograms-common.h
@@ -10,6 +10,11 @@
  * Extension configuration
  */
 
+enum sieve_extprograms_eol {
+	SIEVE_EXTPROGRAMS_EOL_CRLF = 0,
+	SIEVE_EXTPROGRAMS_EOL_LF
+};
+
 struct sieve_extprograms_config {
 	const struct sieve_extension *copy_ext;
 	const struct sieve_extension *var_ext;
@@ -17,6 +22,8 @@ struct sieve_extprograms_config {
 	char *socket_dir;
 	char *bin_dir;
 
+	enum sieve_extprograms_eol default_input_eol;
+
 	unsigned int execute_timeout;
 };
 
diff --git a/tests/plugins/extprograms/bin/crlf b/tests/plugins/extprograms/bin/crlf
new file mode 100755
index 0000000000000000000000000000000000000000..a0028cf5530ae2922d3572a622f5793ad8d9e0b2
--- /dev/null
+++ b/tests/plugins/extprograms/bin/crlf
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+tr -s '\r' '#'
diff --git a/tests/plugins/extprograms/execute/execute.svtest b/tests/plugins/extprograms/execute/execute.svtest
index f8fde2698f6910a431452929a332c551b1b06d2e..5e671c57169b74b7eba7efec8974ec12841e9918 100644
--- a/tests/plugins/extprograms/execute/execute.svtest
+++ b/tests/plugins/extprograms/execute/execute.svtest
@@ -4,6 +4,7 @@ require "vnd.dovecot.debug";
 require "variables";
 require "relational";
 require "environment";
+require "encoded-character";
 
 test_set "message" text:
 From: stephan@example.com
@@ -115,4 +116,34 @@ test "Execute - used as test" {
 	}
 }
 	
-	
+test_config_set "sieve_execute_input_eol" "crlf";
+test_config_reload :extension "vnd.dovecot.execute";
+test_result_reset;
+set "out" "";
+
+test "Execute - CRLF" {
+	execute
+		:input "FROP${hex:0A}FRIEP${hex:0a}"
+		:output "out"
+		"crlf";
+
+	if not string "${out}" "FROP#${hex:0A}FRIEP#${hex:0a}" {
+		test_fail "wrong string returned: '${out}'";
+	}
+}
+
+test_config_set "sieve_execute_input_eol" "lf";
+test_config_reload :extension "vnd.dovecot.execute";
+test_result_reset;
+set "out" "";
+
+test "Execute - LF" {
+	execute
+		:input "FROP${hex:0D 0A}FRIEP${hex:0d 0a}"
+		:output "out"
+		"crlf";
+
+	if not string "${out}" "FROP${hex:0A}FRIEP${hex:0a}" {
+		test_fail "wrong string returned: '${out}'";
+	}
+}