From c4b07aac224919757104c7023cbd7e4e8a23fcce Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan@rename-it.nl>
Date: Sat, 10 Oct 2009 11:14:58 +0200
Subject: [PATCH] Body extension: implemented proper handling of the :raw
 transform.

---
 Makefile.am                                  |  1 +
 src/lib-sieve/plugins/body/ext-body-common.c | 58 ++++++++++++-
 src/lib-sieve/plugins/body/ext-body-common.h |  7 +-
 src/lib-sieve/plugins/body/tst-body.c        | 12 ++-
 tests/extensions/body/basic.svtest           |  6 +-
 tests/extensions/body/raw.svtest             | 85 ++++++++++++++++++++
 6 files changed, 160 insertions(+), 9 deletions(-)
 create mode 100644 tests/extensions/body/raw.svtest

diff --git a/Makefile.am b/Makefile.am
index 43a7b56dc..c85dd942c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -78,6 +78,7 @@ test_cases = \
 	tests/extensions/imap4flags/execute.svtest \
 	tests/extensions/imap4flags/flagstore.svtest \
 	tests/extensions/body/basic.svtest \
+	tests/extensions/body/raw.svtest \
 	tests/extensions/body/match-values.svtest \
 	tests/extensions/regex/basic.svtest \
 	tests/extensions/regex/match-values.svtest \
diff --git a/src/lib-sieve/plugins/body/ext-body-common.c b/src/lib-sieve/plugins/body/ext-body-common.c
index 1d9281753..45767de5c 100644
--- a/src/lib-sieve/plugins/body/ext-body-common.c
+++ b/src/lib-sieve/plugins/body/ext-body-common.c
@@ -38,6 +38,7 @@ struct ext_body_message_context {
 	ARRAY_DEFINE(cached_body_parts, struct ext_body_part_cached);
 	ARRAY_DEFINE(return_body_parts, struct ext_body_part);
 	buffer_t *tmp_buffer;
+	buffer_t *raw_body;
 };
 
 static bool _is_wanted_content_type
@@ -310,7 +311,8 @@ static struct ext_body_message_context *ext_body_get_context
 		p_array_init(&ctx->cached_body_parts, pool, 8);
 		p_array_init(&ctx->return_body_parts, pool, 8);
 		ctx->tmp_buffer = buffer_create_dynamic(pool, 1024*64);
-		
+		ctx->raw_body = NULL;		
+
 		/* Register context */
 		sieve_message_context_extension_set(msgctx, &body_extension, (void *) ctx);
 	}
@@ -341,3 +343,57 @@ bool ext_body_get_content
 
 	return result;
 }
+
+bool ext_body_get_raw
+(const struct sieve_runtime_env *renv, struct ext_body_part **parts_r)
+{
+	struct ext_body_message_context *ctx = ext_body_get_context(renv->msgctx);
+	struct ext_body_part *return_part;
+	buffer_t *buf;
+
+	if ( ctx->raw_body == NULL ) {
+		struct mail *mail = renv->msgdata->mail;
+		struct istream *input;
+		struct message_size hdr_size, body_size;
+		const unsigned char *data;
+		size_t size;
+		int ret;
+
+		ctx->raw_body = buf = buffer_create_dynamic(ctx->pool, 1024*64);
+
+		/* Get stream for message */
+ 		if ( mail_get_stream(mail, &hdr_size, &body_size, &input) < 0 )
+			return FALSE;
+
+		/* Skip stream to beginning of body */
+		i_stream_skip(input, hdr_size.physical_size);
+
+		/* Read raw message body */
+		while ( (ret = i_stream_read_data(input, &data, &size, 0)) > 0 ) {	
+			buffer_append(buf, data, size);
+
+			i_stream_skip(input, size);
+		}
+	} else {
+		buf = ctx->raw_body;	
+	}
+
+	/* Clear result array */
+	array_clear(&ctx->return_body_parts);
+
+	if ( buf->used > 0  ) {
+		/* Add terminating NUL to the body part buffer */
+		buffer_append_c(buf, '\0');
+	
+		/* Add single item to the result */
+		return_part = array_append_space(&ctx->return_body_parts);
+		return_part->content = buf->data;
+		return_part->size = buf->used - 1;
+	}
+
+	/* Return the array of body items */
+	(void) array_append_space(&ctx->return_body_parts); /* NULL-terminate */
+	*parts_r = array_idx_modifiable(&ctx->return_body_parts, 0);
+
+	return TRUE;
+}
diff --git a/src/lib-sieve/plugins/body/ext-body-common.h b/src/lib-sieve/plugins/body/ext-body-common.h
index edb8fe96f..33a0f6c8c 100644
--- a/src/lib-sieve/plugins/body/ext-body-common.h
+++ b/src/lib-sieve/plugins/body/ext-body-common.h
@@ -32,7 +32,10 @@ struct ext_body_part {
 };
 
 bool ext_body_get_content
-(const struct sieve_runtime_env *renv, const char * const *content_types,
-	int decode_to_plain, struct ext_body_part **parts_r);
+	(const struct sieve_runtime_env *renv, const char * const *content_types,
+		int decode_to_plain, struct ext_body_part **parts_r);
+
+bool ext_body_get_raw
+	(const struct sieve_runtime_env *renv, struct ext_body_part **parts_r);
 
 #endif /* __EXT_BODY_COMMON_H */
diff --git a/src/lib-sieve/plugins/body/tst-body.c b/src/lib-sieve/plugins/body/tst-body.c
index a775e29fc..c1057cc21 100644
--- a/src/lib-sieve/plugins/body/tst-body.c
+++ b/src/lib-sieve/plugins/body/tst-body.c
@@ -380,9 +380,15 @@ static int ext_body_operation_execute
 	
 	/* Extract requested parts */
 	
-	if ( !ext_body_get_content
-		(renv, content_types, transform != TST_BODY_TRANSFORM_RAW, &body_parts) ) {
-		return SIEVE_EXEC_FAILURE;
+	if ( transform == TST_BODY_TRANSFORM_RAW ) {
+		if ( !ext_body_get_raw(renv, &body_parts) ) {
+			return SIEVE_EXEC_FAILURE;
+		}
+	} else {
+		if ( !ext_body_get_content
+			(renv, content_types, TRUE, &body_parts) ) {
+			return SIEVE_EXEC_FAILURE;
+		}
 	}
 
 	/* Disable match values processing as required by RFC */
diff --git a/tests/extensions/body/basic.svtest b/tests/extensions/body/basic.svtest
index 042b88a96..7f886435f 100644
--- a/tests/extensions/body/basic.svtest
+++ b/tests/extensions/body/basic.svtest
@@ -29,7 +29,7 @@ Test!
 
 .
 	{
-		test_fail "invalid message body extracted";
+		test_fail "invalid message body extracted (1)";
 	}
 
 	if body :raw :is text:
@@ -38,12 +38,12 @@ Test!
 
 .
 	{
-		test_fail "invalid message body extracted";
+		test_fail "invalid message body extracted (2)";
 	}
 
 	if body :raw :is "Test"
 	{
-		test_fail "body test matches nonsense";
+		test_fail "body test matches nonsense (3)";
 	}
 }
 
diff --git a/tests/extensions/body/raw.svtest b/tests/extensions/body/raw.svtest
new file mode 100644
index 000000000..ba6696eac
--- /dev/null
+++ b/tests/extensions/body/raw.svtest
@@ -0,0 +1,85 @@
+require "vnd.dovecot.testsuite";
+require "body";
+
+test_set "message" text:
+From: Whomever <whoever@example.com>
+To: Someone <someone@example.com>
+Date: Sat, 10 Oct 2009 00:30:04 +0200
+Subject: whatever
+Content-Type: multipart/mixed; boundary=outer
+
+This is a multi-part message in MIME format.
+
+--outer
+Content-Type: multipart/alternative; boundary=inner
+
+This is a nested multi-part message in MIME format.
+
+--inner
+Content-Type: text/plain; charset="us-ascii"
+
+Hello
+        
+--inner
+Content-Type: text/html; charset="us-ascii"
+
+<html><body>Hello</body></html>
+
+--inner--
+
+This is the end of the inner MIME multipart.
+
+--outer
+Content-Type: message/rfc822
+
+From: Someone Else
+Subject: hello request
+
+Please say Hello
+
+--outer--
+
+This is the end of the outer MIME multipart.
+.
+;
+
+/*
+ *
+ * RFC 5173:
+ *  The ":raw" transform matches against the entire undecoded body of a
+ *  message as a single item.
+ *
+ *  If the specified body-transform is ":raw", the [MIME] structure of
+ *  the body is irrelevant.  The implementation MUST NOT remove any
+ *  transfer encoding from the message, MUST NOT refuse to filter
+ *  messages with syntactic errors (unless the environment it is part of
+ *  rejects them outright), and MUST treat multipart boundaries or the
+ *  MIME headers of enclosed body parts as part of the content being
+ *  matched against, instead of MIME structures to interpret.
+ */
+
+test "Multipart Boundaries" {
+	if not body :raw :contains "--inner" {
+		test_fail "Raw body does not contain '--inner'";
+	}
+
+	if not body :raw :contains "--outer" {
+		test_fail "Raw body does not contain '--outer'";
+	}
+}
+
+test "Multipart Headers" {
+	if not body :raw :contains "boundary=inner" {
+		test_fail "Raw body does not contain 'boundary=inner'";
+	}
+
+	if not body :raw :contains "rfc822" {
+		test_fail "Raw body does not contain 'rfc822'";
+	}
+}
+
+test "Multipart Content" {
+	if not body :raw :contains "<html><body>Hello</body></html>" {
+		test_fail "Raw body does not contain '<html><body>Hello</body></html>'";
+	}
+}
-- 
GitLab