dovecot-2.0: istream-header-filter: Added HEADER_FILTER_END_BODY...

dovecot at dovecot.org dovecot at dovecot.org
Fri Jul 30 20:02:22 EEST 2010


details:   http://hg.dovecot.org/dovecot-2.0/rev/22e20ccc14bc
changeset: 11908:22e20ccc14bc
user:      Timo Sirainen <tss at iki.fi>
date:      Fri Jul 30 18:01:34 2010 +0100
description:
istream-header-filter: Added HEADER_FILTER_END_BODY_WITH_LF flag.
If body doesn't end with LF character, it adds it automatically.

diffstat:

 src/lib-mail/istream-header-filter.c      |  81 +++++++++++++++++++++++++-
 src/lib-mail/istream-header-filter.h      |   4 +-
 src/lib-mail/test-istream-header-filter.c |  61 ++++++++++++++++++++-
 3 files changed, 140 insertions(+), 6 deletions(-)

diffs (252 lines):

diff -r b43c8d765d44 -r 22e20ccc14bc src/lib-mail/istream-header-filter.c
--- a/src/lib-mail/istream-header-filter.c	Fri Jul 30 16:55:58 2010 +0100
+++ b/src/lib-mail/istream-header-filter.c	Fri Jul 30 18:01:34 2010 +0100
@@ -23,6 +23,7 @@
 	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 @@
 	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 @@
 	}
 
 	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 @@
 	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 @@
 	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 @@
 		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 @@
 	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 @@
 	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 -r b43c8d765d44 -r 22e20ccc14bc src/lib-mail/istream-header-filter.h
--- a/src/lib-mail/istream-header-filter.h	Fri Jul 30 16:55:58 2010 +0100
+++ b/src/lib-mail/istream-header-filter.h	Fri Jul 30 18:01:34 2010 +0100
@@ -12,7 +12,9 @@
 	/* 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 -r b43c8d765d44 -r 22e20ccc14bc src/lib-mail/test-istream-header-filter.c
--- a/src/lib-mail/test-istream-header-filter.c	Fri Jul 30 16:55:58 2010 +0100
+++ b/src/lib-mail/test-istream-header-filter.c	Fri Jul 30 18:01:34 2010 +0100
@@ -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 @@
 	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 @@
 	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);


More information about the dovecot-cvs mailing list