From: Timo Sirainen Date: Fri, 10 Aug 2012 04:32:14 +0000 (+0300) Subject: Added istream-qp-decoder X-Git-Tag: 2.2.alpha1~379 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5dc71fc15055d1de5e7db1bc56d12606c80ce7c4;p=thirdparty%2Fdovecot%2Fcore.git Added istream-qp-decoder --- diff --git a/src/lib-mail/Makefile.am b/src/lib-mail/Makefile.am index 660fdfc273..34d6f80b3d 100644 --- a/src/lib-mail/Makefile.am +++ b/src/lib-mail/Makefile.am @@ -12,6 +12,7 @@ libmail_la_SOURCES = \ 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 @@ headers = \ 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_programs = \ 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_SOURCES = test-istream-dot.c 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 --git a/src/lib-mail/istream-qp-decoder.c b/src/lib-mail/istream-qp-decoder.c new file mode 100644 index 0000000000..89ba715df0 --- /dev/null +++ b/src/lib-mail/istream-qp-decoder.c @@ -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 --git a/src/lib-mail/istream-qp.h b/src/lib-mail/istream-qp.h new file mode 100644 index 0000000000..e1dc4cfc6b --- /dev/null +++ b/src/lib-mail/istream-qp.h @@ -0,0 +1,6 @@ +#ifndef ISTREAM_QP_H +#define ISTREAM_QP_H + +struct istream *i_stream_create_qp_decoder(struct istream *input); + +#endif diff --git a/src/lib-mail/test-istream-qp-decoder.c b/src/lib-mail/test-istream-qp-decoder.c new file mode 100644 index 0000000000..f28bcd9bcb --- /dev/null +++ b/src/lib-mail/test-istream-qp-decoder.c @@ -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); +}