diff --git a/src/lib-mail/istream-header-filter.c b/src/lib-mail/istream-header-filter.c index d094f27dc1..e7cae489ee 100644 --- a/src/lib-mail/istream-header-filter.c +++ b/src/lib-mail/istream-header-filter.c @@ -547,6 +547,28 @@ i_stream_header_filter_stat(struct istream_private *stream, bool exact) if (skip_header(mstream) < 0) return -1; + if (!mstream->end_body_with_lf) { + /* no last-LF */ + } else if (mstream->last_lf_added) { + /* yes, we have added LF */ + stream->statbuf.st_size += mstream->crlf ? 2 : 1; + } else if (mstream->last_lf_offset != (uoff_t)-1) { + /* no, we didn't need to add LF */ + } else { + /* check if we need to add LF */ + i_stream_seek(stream->parent, st->st_size - 1); + (void)i_stream_read(stream->parent); + if (stream->parent->stream_errno != 0) { + stream->istream.stream_errno = + stream->parent->stream_errno; + return -1; + } + i_assert(stream->parent->eof); + ssize_t ret = handle_end_body_with_lf(mstream, -1); + if (ret > 0) + stream->statbuf.st_size += ret; + } + stream->statbuf.st_size -= (off_t)mstream->header_size.physical_size - (off_t)mstream->header_size.virtual_size; @@ -601,6 +623,7 @@ i_stream_create_header_filter(struct istream *input, 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->last_lf_offset = (uoff_t)-1; mstream->istream.iostream.destroy = i_stream_header_filter_destroy; mstream->istream.read = i_stream_header_filter_read; diff --git a/src/lib-mail/test-istream-header-filter.c b/src/lib-mail/test-istream-header-filter.c index cfa544ccb5..6c1357c524 100644 --- a/src/lib-mail/test-istream-header-filter.c +++ b/src/lib-mail/test-istream-header-filter.c @@ -12,6 +12,7 @@ test_istream_run(struct istream *test_istream, struct istream *filter, unsigned int input_len, const char *output) { unsigned int i, output_len = strlen(output); + const struct stat *st; const unsigned char *data; size_t size; @@ -32,6 +33,8 @@ test_istream_run(struct istream *test_istream, struct istream *filter, while (i_stream_read(filter) > 0) ; data = i_stream_get_data(filter, &size); test_assert(size == output_len && memcmp(data, output, size) == 0); + test_assert(i_stream_stat(filter, TRUE, &st) == 0 && + (uoff_t)st->st_size == size); } static void ATTR_NULL(3) @@ -55,6 +58,7 @@ static void test_istream_filter(void) unsigned int i, input_len = strlen(input); unsigned int output_len = strlen(output); const unsigned char *data; + const struct stat *st; size_t size; test_begin("i_stream_create_header_filter(exclude)"); @@ -86,11 +90,16 @@ static void test_istream_filter(void) data = i_stream_get_data(filter, &size); test_assert(size == output_len && memcmp(data, output, size) == 0); + test_assert(i_stream_stat(filter, TRUE, &st) == 0 && + (uoff_t)st->st_size == size); + i_stream_skip(filter, size); i_stream_seek(filter, 0); while (i_stream_read(filter) > 0) ; data = i_stream_get_data(filter, &size); test_assert(size == output_len && memcmp(data, output, size) == 0); + test_assert(i_stream_stat(filter, TRUE, &st) == 0 && + (uoff_t)st->st_size == size); i_stream_unref(&filter); i_stream_unref(&istream); @@ -119,6 +128,7 @@ static void test_istream_filter_large_buffer(void) { string_t *input, *output; struct istream *istream, *filter; + const struct stat *st; const unsigned char *data; size_t size, prefix_len; const char *p; @@ -172,6 +182,9 @@ static void test_istream_filter_large_buffer(void) i_assert(p != NULL); test_assert(strcmp(p+1, str_c(output) + prefix_len) == 0); + test_assert(i_stream_stat(filter, TRUE, &st) == 0 && + (uoff_t)st->st_size == filter->v_offset + size); + /* seek back and retry once with caching and different buffer size */ i_stream_seek(filter, 0); @@ -199,6 +212,7 @@ filter3_callback(struct header_filter_istream *input ATTR_UNUSED, static void test_istream_callbacks(void) { string_t *input, *output; + const struct stat *st; struct istream *istream, *filter; unsigned int i; @@ -230,6 +244,8 @@ static void test_istream_callbacks(void) i_stream_skip(filter, i_stream_get_data_size(filter)); } + test_assert(i_stream_stat(filter, TRUE, &st) == 0 && + (uoff_t)st->st_size == str_len(output)); test_assert(strcmp(str_c(output), str_c(input)) == 0); str_free(&input); str_free(&output); @@ -276,6 +292,7 @@ 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"; + const struct stat *st; struct istream *istream, *filter; unsigned int i, input_len = strlen(input); unsigned int output_len = strlen(output); @@ -304,6 +321,8 @@ static void test_istream_end_body_with_lf(void) data = i_stream_get_data(filter, &size); test_assert(size == output_len && memcmp(data, output, size) == 0); + test_assert(i_stream_stat(filter, TRUE, &st) == 0 && + (uoff_t)st->st_size == filter->v_offset + size); i_stream_skip(filter, size); i_stream_seek(filter, 0);