dovecot-2.2: lib-mail: Fixed various istream-qp-decoder bugs by ...

dovecot at dovecot.org dovecot at dovecot.org
Sun May 3 13:28:36 UTC 2015


details:   http://hg.dovecot.org/dovecot-2.2/rev/a8e9fdcb17c5
changeset: 18505:a8e9fdcb17c5
user:      Timo Sirainen <tss at iki.fi>
date:      Sun May 03 16:22:55 2015 +0300
description:
lib-mail: Fixed various istream-qp-decoder bugs by switching to qp-decoder API

diffstat:

 src/lib-mail/Makefile.am               |    2 +-
 src/lib-mail/istream-qp-decoder.c      |  149 +++++++++++++-------------------
 src/lib-mail/test-istream-qp-decoder.c |   42 ++++++--
 3 files changed, 89 insertions(+), 104 deletions(-)

diffs (truncated from 309 to 300 lines):

diff -r a9f8a617dc02 -r a8e9fdcb17c5 src/lib-mail/Makefile.am
--- a/src/lib-mail/Makefile.am	Sun May 03 14:50:01 2015 +0300
+++ b/src/lib-mail/Makefile.am	Sun May 03 16:22:55 2015 +0300
@@ -110,7 +110,7 @@
 test_istream_dot_DEPENDENCIES = $(test_deps)
 
 test_istream_qp_decoder_SOURCES = test-istream-qp-decoder.c
-test_istream_qp_decoder_LDADD = istream-qp-decoder.lo quoted-printable.lo $(test_libs)
+test_istream_qp_decoder_LDADD = istream-qp-decoder.lo qp-decoder.lo $(test_libs)
 test_istream_qp_decoder_DEPENDENCIES = $(test_deps)
 
 message_parser_objects = \
diff -r a9f8a617dc02 -r a8e9fdcb17c5 src/lib-mail/istream-qp-decoder.c
--- a/src/lib-mail/istream-qp-decoder.c	Sun May 03 14:50:01 2015 +0300
+++ b/src/lib-mail/istream-qp-decoder.c	Sun May 03 16:22:55 2015 +0300
@@ -3,78 +3,28 @@
 #include "lib.h"
 #include "buffer.h"
 #include "hex-binary.h"
+#include "qp-decoder.h"
 #include "istream-private.h"
-#include "quoted-printable.h"
 #include "istream-qp.h"
 
 struct qp_decoder_istream {
 	struct istream_private istream;
+	buffer_t *buf;
+	struct qp_decoder *qp;
 };
 
-static int
-i_stream_read_parent(struct istream_private *stream, size_t *prev_size)
+static void i_stream_qp_decoder_close(struct iostream_private *stream,
+				      bool close_parent)
 {
-	size_t size;
-	ssize_t ret;
+	struct qp_decoder_istream *bstream =
+		(struct qp_decoder_istream *)stream;
 
-	size = i_stream_get_data_size(stream->parent);
-	if (size >= 4 && size != *prev_size) {
-		*prev_size = size;
-		return 1;
-	}
-
-	ret = i_stream_read(stream->parent);
-	if (ret <= 0) {
-		stream->istream.stream_errno = stream->parent->stream_errno;
-		stream->istream.eof = stream->parent->eof;
-		return ret;
-	}
-	*prev_size = i_stream_get_data_size(stream->parent);
-	return 1;
-}
-
-static int
-i_stream_qp_try_decode_input(struct qp_decoder_istream *bstream, bool eof)
-{
-	struct istream_private *stream = &bstream->istream;
-	const unsigned char *data;
-	size_t size, avail, buffer_avail, pos;
-	buffer_t buf;
-	int ret;
-
-	data = i_stream_get_data(stream->parent, &size);
-	if (size == 0)
-		return 0;
-
-	/* normally the decoded quoted-printable content can't be larger than
-	   the encoded content, but because we always use CRLFs, it may use
-	   twice as much space by only converting LFs to CRLFs. */
-	i_stream_try_alloc(stream, size, &avail);
-	buffer_avail = stream->buffer_size - stream->pos;
-
-	if (size > buffer_avail/2) {
-		/* can't fit everything to destination buffer.
-		   write as much as we can. */
-		size = buffer_avail/2;
-		if (size == 0)
-			return -2;
-	}
-
-	buffer_create_from_data(&buf, stream->w_buffer + stream->pos,
-				buffer_avail);
-	ret = !eof ? quoted_printable_decode(data, size, &pos, &buf) :
-		quoted_printable_decode_final(data, size, &pos, &buf);
-	if (ret < 0) {
-		io_stream_set_error(&stream->iostream,
-			"Invalid quoted-printable data: 0x%s",
-			binary_to_hex(data+pos, I_MAX(size-pos, 8)));
-		stream->istream.stream_errno = EINVAL;
-		return -1;
-	}
-
-	stream->pos += buf.used;
-	i_stream_skip(stream->parent, pos);
-	return pos > 0 ? 1 : 0;
+	if (bstream->qp != NULL)
+		qp_decoder_deinit(&bstream->qp);
+	if (bstream->buf != NULL)
+		buffer_free(&bstream->buf);
+	if (close_parent)
+		i_stream_close(bstream->istream.parent);
 }
 
 static ssize_t i_stream_qp_decoder_read(struct istream_private *stream)
@@ -82,49 +32,63 @@
 	struct qp_decoder_istream *bstream =
 		(struct qp_decoder_istream *)stream;
 	const unsigned char *data;
-	size_t pre_count, post_count, size;
+	size_t size, avail, error_pos;
+	const char *error;
 	int ret;
-	size_t prev_size = 0;
 
-	do {
-		ret = i_stream_read_parent(stream, &prev_size);
+	for (;;) {
+		/* if something is already decoded, return as much of it as
+		   we can */
+		if (bstream->buf->used > 0) {
+			i_stream_try_alloc(stream, bstream->buf->used, &avail);
+			if (avail == 0)
+				return -2;
+			size = I_MIN(avail, bstream->buf->used);
+			memcpy(stream->w_buffer + stream->pos,
+			       bstream->buf->data, size);
+			buffer_delete(bstream->buf, 0, size);
+			stream->pos += size;
+			return size;
+		}
+		/* need to read more input */
+		ret = i_stream_read_data(stream->parent, &data, &size, 0);
 		if (ret <= 0) {
+			stream->istream.stream_errno = stream->parent->stream_errno;
+			stream->istream.eof = stream->parent->eof;
 			if (ret != -1 || stream->istream.stream_errno != 0)
-				return 0;
-
-			ret = i_stream_qp_try_decode_input(bstream, TRUE);
-			if (ret == 0) {
-				/* ended with =[whitespace] but without LF */
-				stream->istream.eof = TRUE;
+				return ret;
+			/* end of quoted-printable stream. verify that the
+			   ending is ok. */
+			if (qp_decoder_finish(bstream->qp, &error) == 0) {
+				i_assert(bstream->buf->used == 0);
 				return -1;
 			}
-			/* partial qp input */
-			i_assert(ret < 0);
-			data = i_stream_get_data(stream->parent, &size);
 			io_stream_set_error(&stream->iostream,
-				"quoted-printable input ends with a partial block: 0x%s",
-				binary_to_hex(data, size));
+				"Invalid quoted-printable input trailer: %s", error);
 			stream->istream.stream_errno = EINVAL;
 			return -1;
 		}
-
-		/* encode as much data as fits into destination buffer */
-		pre_count = stream->pos - stream->skip;
-		while ((ret = i_stream_qp_try_decode_input(bstream, FALSE)) > 0) ;
-		post_count = stream->pos - stream->skip;
-	} while (ret == 0 && pre_count == post_count);
-
-	if (ret < 0)
-		return ret;
-
-	i_assert(post_count > pre_count);
-	return post_count - pre_count;
+		if (qp_decoder_more(bstream->qp, data, size,
+				    &error_pos, &error) < 0) {
+			i_assert(error_pos < size);
+			io_stream_set_error(&stream->iostream,
+				"Invalid quoted-printable input 0x%s: %s",
+				binary_to_hex(data+error_pos, I_MIN(size-error_pos, 8)), error);
+			stream->istream.stream_errno = EINVAL;
+			return -1;
+		}
+		i_stream_skip(stream->parent, size);
+	}
 }
 
 static void
 i_stream_qp_decoder_seek(struct istream_private *stream,
 			     uoff_t v_offset, bool mark)
 {
+	struct qp_decoder_istream *bstream =
+		(struct qp_decoder_istream *)stream;
+	const char *error;
+
 	if (v_offset < stream->istream.v_offset) {
 		/* seeking backwards - go back to beginning and seek
 		   forward from there. */
@@ -132,6 +96,8 @@
 		stream->skip = stream->pos = 0;
 		stream->istream.v_offset = 0;
 		i_stream_seek(stream->parent, 0);
+		(void)qp_decoder_finish(bstream->qp, &error);
+		buffer_set_used_size(bstream->buf, 0);
 	}
 	i_stream_default_seek_nonseekable(stream, v_offset, mark);
 }
@@ -142,7 +108,10 @@
 
 	bstream = i_new(struct qp_decoder_istream, 1);
 	bstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
+	bstream->buf = buffer_create_dynamic(default_pool, 128);
+	bstream->qp = qp_decoder_init(bstream->buf);
 
+	bstream->istream.iostream.close = i_stream_qp_decoder_close;
 	bstream->istream.read = i_stream_qp_decoder_read;
 	bstream->istream.seek = i_stream_qp_decoder_seek;
 
diff -r a9f8a617dc02 -r a8e9fdcb17c5 src/lib-mail/test-istream-qp-decoder.c
--- a/src/lib-mail/test-istream-qp-decoder.c	Sun May 03 14:50:01 2015 +0300
+++ b/src/lib-mail/test-istream-qp-decoder.c	Sun May 03 16:22:55 2015 +0300
@@ -5,58 +5,74 @@
 #include "istream-private.h"
 #include "istream-qp.h"
 
-static const 
-struct {
+static const struct {
 	const char *input;
 	const char *output;
+	int ret;
 } tests[] = {
-	{ "p=C3=A4=C3=A4t=C3=B6s", "päätös" },
-	{ "p=c3=a4=c3=a4t=c3=b6s=  ", "päätös" },
-	{ "p=c3=a4=c3=a4t=c3=b6s=  \n", "päätös" },
-	{ "p=c3=a4= \t \n=c3=\r\n=a4t=  \r\n=c3=b6s", "päätös" },
+	{ "p=C3=A4=C3=A4t=C3=B6s", "päätös", 0 },
+	{ "p=c3=a4=c3=a4t=c3=b6s=  \n", "päätös", 0 },
+	{ "p=c3=a4= \t \n=c3=\r\n=a4t=  \r\n=c3=b6s", "päätös", 0 },
+
+	{ "p=c3=a4\rasdf", "pä", -1 },
+	{ "p=c", "p", -1 },
+	{ "p=A", "p", -1 },
+	{ "p=Ax", "p", -1 },
+	{ "p=c3=a4=c3=a4t=c3=b6s=  ", "päätös", -1 }
 };
 
 static void
-decode_test(const char *qp_input, const char *output, bool broken_input)
+decode_test(const char *qp_input, const char *output, bool broken_input,
+	    unsigned int buffer_size)
 {
 	unsigned int qp_input_len = strlen(qp_input);
 	struct istream *input_data, *input;
 	const unsigned char *data;
 	size_t i, size;
+	string_t *str = t_str_new(32);
 	int ret = 0;
 
 	input_data = test_istream_create_data(qp_input, qp_input_len);
+	test_istream_set_max_buffer_size(input_data, buffer_size);
 	test_istream_set_allow_eof(input_data, FALSE);
 	input = i_stream_create_qp_decoder(input_data);
 
 	for (i = 1; i <= qp_input_len; i++) {
 		test_istream_set_size(input_data, i);
-		while ((ret = i_stream_read(input)) > 0) ;
+		while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) {
+			str_append_n(str, data, size);
+			i_stream_skip(input, size);
+		}
 		if (ret == -1 && broken_input)
 			break;
 		test_assert(ret == 0);
 	}
 	if (ret == 0) {
 		test_istream_set_allow_eof(input_data, TRUE);
-		while ((ret = i_stream_read(input)) > 0) ;
+		while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) {
+			str_append_n(str, data, size);
+			i_stream_skip(input, size);
+		}
 	}
 	test_assert(ret == -1);
 	test_assert((input->stream_errno == 0 && !broken_input) ||
 		    (input->stream_errno == EINVAL && broken_input));
 
-	data = i_stream_get_data(input, &size);
-	test_assert(size == strlen(output) && memcmp(data, output, size) == 0);
+	test_assert(strcmp(str_c(str), output) == 0);
 	i_stream_unref(&input);
 	i_stream_unref(&input_data);
 }
 
 static void test_istream_qp_decoder(void)
 {
-	unsigned int i;
+	unsigned int i, j;
 
 	for (i = 0; i < N_ELEMENTS(tests); i++) {


More information about the dovecot-cvs mailing list