From: Timo Sirainen Date: Fri, 30 Jul 2010 17:01:34 +0000 (+0100) Subject: istream-header-filter: Added HEADER_FILTER_END_BODY_WITH_LF flag. X-Git-Tag: 2.0.rc4~27 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ef3ebb56989cb9b1b80edd133a091338e4206836;p=thirdparty%2Fdovecot%2Fcore.git istream-header-filter: Added HEADER_FILTER_END_BODY_WITH_LF flag. If body doesn't end with LF character, it adds it automatically. --- diff --git a/src/lib-mail/istream-header-filter.c b/src/lib-mail/istream-header-filter.c index 477302cdbd..797e4bf837 100644 --- a/src/lib-mail/istream-header-filter.c +++ b/src/lib-mail/istream-header-filter.c @@ -23,6 +23,7 @@ struct header_filter_istream { buffer_t *hdr_buf; struct message_size header_size; uoff_t skip_count; + uoff_t last_lf_offset; unsigned int cur_line, parsed_lines; ARRAY_DEFINE(match_change_lines, unsigned int); @@ -34,6 +35,8 @@ struct header_filter_istream { unsigned int crlf:1; unsigned int hide_body:1; unsigned int add_missing_eoh:1; + unsigned int end_body_with_lf:1; + unsigned int last_lf_added:1; }; header_filter_callback *null_header_filter_callback = NULL; @@ -64,18 +67,37 @@ read_mixed(struct header_filter_istream *mstream, size_t body_highwater_size) } data = i_stream_get_data(mstream->istream.parent, &pos); - if (pos == body_highwater_size) { + if (pos <= body_highwater_size) { + i_assert(pos == body_highwater_size || + (mstream->end_body_with_lf && + pos+1 == body_highwater_size)); + ret = i_stream_read(mstream->istream.parent); mstream->istream.istream.stream_errno = mstream->istream.parent->stream_errno; mstream->istream.istream.eof = mstream->istream.parent->eof; - if (ret <= 0) + if (ret <= 0) { + i_assert(pos > 0); + + data = mstream->hdr_buf->data; + pos = mstream->hdr_buf->used; + if (mstream->end_body_with_lf && data[pos-1] != '\n' && + ret == -1 && mstream->istream.istream.eof) { + /* add missing trailing LF to body */ + if (mstream->crlf) + buffer_append_c(mstream->hdr_buf, '\r'); + buffer_append_c(mstream->hdr_buf, '\n'); + mstream->istream.buffer = + buffer_get_data(mstream->hdr_buf, + &mstream->istream.pos); + return mstream->hdr_buf->used - pos; + } return ret; + } data = i_stream_get_data(mstream->istream.parent, &pos); } - i_assert(pos > body_highwater_size); buffer_append(mstream->hdr_buf, data + body_highwater_size, pos - body_highwater_size); @@ -273,6 +295,45 @@ static ssize_t read_header(struct header_filter_istream *mstream) return ret; } +static ssize_t +handle_end_body_with_lf(struct header_filter_istream *mstream, ssize_t ret) +{ + struct istream_private *stream = &mstream->istream; + const unsigned char *data; + size_t size, last_offset; + bool last_lf; + + data = i_stream_get_data(stream->parent, &size); + last_offset = stream->parent->v_offset + size-1; + + if (mstream->last_lf_offset == last_offset) + last_lf = TRUE; + else if (size > 0) + last_lf = data[size-1] == '\n'; + else + last_lf = FALSE; + + if (ret == -1 && stream->parent->eof && !last_lf) { + /* missing LF, need to add it */ + i_assert(size == 0 || data[size-1] != '\n'); + buffer_reset(mstream->hdr_buf); + buffer_append(mstream->hdr_buf, data, size); + if (mstream->crlf) + buffer_append_c(mstream->hdr_buf, '\r'); + buffer_append_c(mstream->hdr_buf, '\n'); + mstream->last_lf_offset = last_offset; + mstream->last_lf_added = TRUE; + + stream->skip = 0; + stream->pos = size + 1; + stream->buffer = mstream->hdr_buf->data; + return mstream->crlf ? 2 : 1; + } else { + mstream->last_lf_offset = last_lf ? last_offset : (uoff_t)-1; + } + return ret; +} + static ssize_t i_stream_header_filter_read(struct istream_private *stream) { struct header_filter_istream *mstream = @@ -280,6 +341,11 @@ static ssize_t i_stream_header_filter_read(struct istream_private *stream) uoff_t v_offset; ssize_t ret; + if (mstream->last_lf_added) { + stream->istream.eof = TRUE; + return -1; + } + if (!mstream->header_read || stream->istream.v_offset < mstream->header_size.virtual_size) { ret = read_header(mstream); @@ -296,7 +362,10 @@ static ssize_t i_stream_header_filter_read(struct istream_private *stream) mstream->header_size.virtual_size + mstream->header_size.physical_size; i_stream_seek(stream->parent, v_offset); - return i_stream_read_copy_from_parent(&stream->istream); + ret = i_stream_read_copy_from_parent(&stream->istream); + if (mstream->end_body_with_lf) + ret = handle_end_body_with_lf(mstream, ret); + return ret; } static void @@ -353,6 +422,8 @@ static void i_stream_header_filter_seek(struct istream_private *stream, struct header_filter_istream *mstream = (struct header_filter_istream *)stream; + mstream->last_lf_added = FALSE; + if (stream->istream.v_offset == v_offset) { /* just reset the input buffer */ stream_reset_to(mstream, v_offset); @@ -447,6 +518,8 @@ i_stream_create_header_filter(struct istream *input, mstream->crlf = (flags & HEADER_FILTER_NO_CR) == 0; mstream->hide_body = (flags & HEADER_FILTER_HIDE_BODY) != 0; mstream->add_missing_eoh = (flags & HEADER_FILTER_ADD_MISSING_EOH) != 0; + mstream->end_body_with_lf = + (flags & HEADER_FILTER_END_BODY_WITH_LF) != 0; mstream->istream.iostream.destroy = i_stream_header_filter_destroy; mstream->istream.read = i_stream_header_filter_read; diff --git a/src/lib-mail/istream-header-filter.h b/src/lib-mail/istream-header-filter.h index 4a4490a9ac..e667bd4a54 100644 --- a/src/lib-mail/istream-header-filter.h +++ b/src/lib-mail/istream-header-filter.h @@ -12,7 +12,9 @@ enum header_filter_flags { /* Return EOF at the beginning of message body. */ HEADER_FILTER_HIDE_BODY = 0x08, /* If the empty "end of headers" line doesn't exist, add it. */ - HEADER_FILTER_ADD_MISSING_EOH = 0x10 + HEADER_FILTER_ADD_MISSING_EOH = 0x10, + /* If body doesn't end with [CR]LF, add it/them. */ + HEADER_FILTER_END_BODY_WITH_LF = 0x20 }; struct message_header_line; diff --git a/src/lib-mail/test-istream-header-filter.c b/src/lib-mail/test-istream-header-filter.c index 17f2632430..f292fec3a5 100644 --- a/src/lib-mail/test-istream-header-filter.c +++ b/src/lib-mail/test-istream-header-filter.c @@ -1,6 +1,7 @@ /* Copyright (c) 2007-2010 Dovecot authors, see the included COPYING file */ #include "lib.h" +#include "str.h" #include "istream.h" #include "message-header-parser.h" #include "istream-header-filter.h" @@ -26,7 +27,7 @@ static void test_istream_filter(void) const unsigned char *data; size_t size; - test_begin("i_stream_create_header_filter()"); + test_begin("i_stream_create_header_filter(exclude)"); istream = test_istream_create(input); filter = i_stream_create_header_filter(istream, HEADER_FILTER_EXCLUDE | @@ -64,10 +65,68 @@ static void test_istream_filter(void) test_end(); } +static void test_istream_end_body_with_lf(void) +{ + const char *input = "From: foo\n\nhello world"; + const char *output = "From: foo\n\nhello world\n"; + struct istream *istream, *filter; + unsigned int i, input_len = strlen(input); + unsigned int output_len = strlen(output); + const unsigned char *data; + string_t *str = t_str_new(64); + size_t size; + + test_begin("i_stream_create_header_filter(end_body_with_lf)"); + istream = test_istream_create(input); + filter = i_stream_create_header_filter(istream, + HEADER_FILTER_EXCLUDE | + HEADER_FILTER_NO_CR | + HEADER_FILTER_END_BODY_WITH_LF, + NULL, 0, + null_header_filter_callback, NULL); + + for (i = 1; i < input_len; i++) { + test_istream_set_size(istream, i); + test_assert(i_stream_read(filter) >= 0); + } + test_istream_set_size(istream, input_len); + test_assert(i_stream_read(filter) > 0); + test_assert(i_stream_read(filter) > 0); + test_assert(i_stream_read(filter) == -1); + + data = i_stream_get_data(filter, &size); + test_assert(size == output_len && memcmp(data, output, size) == 0); + + i_stream_skip(filter, size); + i_stream_seek(filter, 0); + for (i = 1; i < input_len; i++) { + test_istream_set_size(istream, i); + test_assert(i_stream_read(filter) >= 0); + + data = i_stream_get_data(filter, &size); + str_append_n(str, data, size); + i_stream_skip(filter, size); + } + test_istream_set_size(istream, input_len); + test_assert(i_stream_read(filter) == 1); + test_assert(i_stream_read(filter) == 1); + test_assert(i_stream_read(filter) == -1); + + data = i_stream_get_data(filter, &size); + str_append_n(str, data, size); + test_assert(strcmp(str_c(str), output) == 0); + + i_stream_unref(&filter); + i_stream_unref(&istream); + + test_end(); +} + int main(void) { static void (*test_functions[])(void) = { test_istream_filter, + test_istream_end_body_with_lf, NULL }; return test_run(test_functions);