dovecot-2.2: Added istream-qp-decoder

dovecot at dovecot.org dovecot at dovecot.org
Fri Aug 10 07:32:20 EEST 2012


details:   http://hg.dovecot.org/dovecot-2.2/rev/b26aa0641080
changeset: 14849:b26aa0641080
user:      Timo Sirainen <tss at iki.fi>
date:      Fri Aug 10 07:32:14 2012 +0300
description:
Added istream-qp-decoder

diffstat:

 src/lib-mail/Makefile.am               |    7 +
 src/lib-mail/istream-qp-decoder.c      |  130 +++++++++++++++++++++++++++++++++
 src/lib-mail/istream-qp.h              |    6 +
 src/lib-mail/test-istream-qp-decoder.c |   69 +++++++++++++++++
 4 files changed, 212 insertions(+), 0 deletions(-)

diffs (255 lines):

diff -r 9195486cb5c2 -r b26aa0641080 src/lib-mail/Makefile.am
--- a/src/lib-mail/Makefile.am	Fri Aug 10 07:31:28 2012 +0300
+++ b/src/lib-mail/Makefile.am	Fri Aug 10 07:32:14 2012 +0300
@@ -12,6 +12,7 @@
 	istream-dot.c \
 	istream-header-filter.c \
 	istream-nonuls.c \
+	istream-qp-decoder.c \
 	mail-user-hash.c \
 	mbox-from.c \
 	message-address.c \
@@ -37,6 +38,7 @@
 	istream-dot.h \
 	istream-header-filter.h \
 	istream-nonuls.h \
+	istream-qp.h \
 	mail-user-hash.h \
 	mbox-from.h \
 	mail-types.h \
@@ -64,6 +66,7 @@
 	test-istream-attachment \
 	test-istream-binary-converter \
 	test-istream-header-filter \
+	test-istream-qp-decoder \
 	test-mbox-from \
 	test-message-address \
 	test-message-date \
@@ -86,6 +89,10 @@
 test_istream_dot_LDADD = istream-dot.lo $(test_libs)
 test_istream_dot_DEPENDENCIES = istream-dot.lo $(test_libs)
 
+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_DEPENDENCIES = istream-qp-decoder.lo quoted-printable.lo $(test_libs)
+
 message_parser_objects = \
 	message-parser.lo \
 	message-header-parser.lo \
diff -r 9195486cb5c2 -r b26aa0641080 src/lib-mail/istream-qp-decoder.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-mail/istream-qp-decoder.c	Fri Aug 10 07:32:14 2012 +0300
@@ -0,0 +1,130 @@
+/* Copyright (c) 2012 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "buffer.h"
+#include "istream-private.h"
+#include "quoted-printable.h"
+#include "istream-qp.h"
+
+struct qp_decoder_istream {
+	struct istream_private istream;
+};
+
+static int
+i_stream_read_parent(struct istream_private *stream, size_t *prev_size)
+{
+	size_t size;
+	ssize_t ret;
+
+	size = i_stream_get_data_size(stream->parent);
+	if (size >= 4 && size != *prev_size) {
+		*prev_size = size;
+		return 1;
+	}
+
+	/* we have less than one qp block.
+	   see if there is more data available. */
+	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_block(struct qp_decoder_istream *bstream)
+{
+	struct istream_private *stream = &bstream->istream;
+	const unsigned char *data;
+	size_t size, avail, buffer_avail, pos;
+	buffer_t buf;
+
+	data = i_stream_get_data(stream->parent, &size);
+	if (size == 0)
+		return 0;
+
+	i_stream_try_alloc(stream, (size+3)/4*3, &avail);
+	buffer_avail = stream->buffer_size - stream->pos;
+
+	if ((size + 3) / 4 * 3 > buffer_avail) {
+		/* can't fit everything to destination buffer.
+		   write as much as we can. */
+		size = (buffer_avail / 3) * 4;
+		if (size == 0)
+			return -2;
+	}
+
+	buffer_create_data(&buf, stream->w_buffer + stream->pos, buffer_avail);
+	quoted_printable_decode(data, size, &pos, &buf);
+
+	stream->pos += buf.used;
+	i_stream_skip(stream->parent, pos);
+	return pos > 0 ? 1 : 0;
+}
+
+static ssize_t i_stream_qp_decoder_read(struct istream_private *stream)
+{
+	struct qp_decoder_istream *bstream =
+		(struct qp_decoder_istream *)stream;
+	size_t pre_count, post_count;
+	int ret;
+	size_t prev_size = 0;
+
+	do {
+		ret = i_stream_read_parent(stream, &prev_size);
+		if (ret <= 0) {
+			if (ret < 0 && stream->istream.stream_errno == 0 &&
+			    i_stream_get_data_size(stream->parent) > 0) {
+				/* qp input with a partial block */
+				stream->istream.stream_errno = EINVAL;
+			}
+			return ret;
+		}
+
+		/* encode as many blocks as fits into destination buffer */
+		pre_count = stream->pos - stream->skip;
+		while ((ret = i_stream_qp_try_decode_block(bstream)) > 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;
+}
+
+static void
+i_stream_qp_decoder_seek(struct istream_private *stream,
+			     uoff_t v_offset, bool mark)
+{
+	if (v_offset < stream->istream.v_offset) {
+		/* seeking backwards - go back to beginning and seek
+		   forward from there. */
+		stream->parent_expected_offset = stream->parent_start_offset;
+		stream->skip = stream->pos = 0;
+		stream->istream.v_offset = 0;
+		i_stream_seek(stream->parent, 0);
+	}
+	i_stream_default_seek_nonseekable(stream, v_offset, mark);
+}
+
+struct istream *i_stream_create_qp_decoder(struct istream *input)
+{
+	struct qp_decoder_istream *bstream;
+
+	bstream = i_new(struct qp_decoder_istream, 1);
+	bstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
+
+	bstream->istream.read = i_stream_qp_decoder_read;
+	bstream->istream.seek = i_stream_qp_decoder_seek;
+
+	bstream->istream.istream.readable_fd = FALSE;
+	bstream->istream.istream.blocking = input->blocking;
+	bstream->istream.istream.seekable = input->seekable;
+	return i_stream_create(&bstream->istream, input,
+			       i_stream_get_fd(input));
+}
diff -r 9195486cb5c2 -r b26aa0641080 src/lib-mail/istream-qp.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-mail/istream-qp.h	Fri Aug 10 07:32:14 2012 +0300
@@ -0,0 +1,6 @@
+#ifndef ISTREAM_QP_H
+#define ISTREAM_QP_H
+
+struct istream *i_stream_create_qp_decoder(struct istream *input);
+
+#endif
diff -r 9195486cb5c2 -r b26aa0641080 src/lib-mail/test-istream-qp-decoder.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-mail/test-istream-qp-decoder.c	Fri Aug 10 07:32:14 2012 +0300
@@ -0,0 +1,69 @@
+/* Copyright (c) 2010-2012 Dovecot authors, see the included COPYING file */
+
+#include "test-lib.h"
+#include "str.h"
+#include "istream-private.h"
+#include "istream-qp.h"
+
+struct {
+	const char *input;
+	const char *output;
+} tests[] = {
+	{ "p=C3=A4=C3=A4t=C3=B6s", "päätös" },
+	{ "p=c3=a4=c3=a4t=c3=b6s", "päätös" },
+	{ "p=c3=a4= \t \n=c3=\r\n=a4t=  \r\n=c3=b6s", "päätös" },
+};
+
+static void
+decode_test(const char *qp_input, const char *output, bool broken_input)
+{
+	unsigned int qp_input_len = strlen(qp_input);
+	struct istream *input_data, *input;
+	const unsigned char *data;
+	size_t i, size;
+	int ret = 0;
+
+	input_data = test_istream_create_data(qp_input, qp_input_len);
+	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) ;
+		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) ;
+	}
+	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);
+	i_stream_unref(&input);
+	i_stream_unref(&input_data);
+}
+
+static void test_istream_qp_decoder(void)
+{
+	unsigned int i;
+
+	for (i = 0; i < N_ELEMENTS(tests); i++) {
+		test_begin(t_strdup_printf("istream qp decoder %u", i+1));
+		decode_test(tests[i].input, tests[i].output, FALSE);
+		test_end();
+	}
+}
+
+int main(void)
+{
+	static void (*test_functions[])(void) = {
+		test_istream_qp_decoder,
+		NULL
+	};
+	return test_run(test_functions);
+}


More information about the dovecot-cvs mailing list