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