dovecot: Handle returning mixed header+body multiple times witho...

dovecot at dovecot.org dovecot at dovecot.org
Thu Nov 8 19:44:55 EET 2007


details:   http://hg.dovecot.org/dovecot/rev/87a0f26edb39
changeset: 6742:87a0f26edb39
user:      Timo Sirainen <tss at iki.fi>
date:      Thu Nov 08 19:44:52 2007 +0200
description:
Handle returning mixed header+body multiple times without breaking. Handle
correctly callbacks excluding headers and seeking back and re-reading header.

diffstat:

1 file changed, 59 insertions(+), 13 deletions(-)
src/lib-mail/istream-header-filter.c |   72 +++++++++++++++++++++++++++-------

diffs (149 lines):

diff -r d329f3c77c65 -r 87a0f26edb39 src/lib-mail/istream-header-filter.c
--- a/src/lib-mail/istream-header-filter.c	Thu Nov 08 19:43:18 2007 +0200
+++ b/src/lib-mail/istream-header-filter.c	Thu Nov 08 19:44:52 2007 +0200
@@ -1,7 +1,7 @@
 /* Copyright (c) 2003-2007 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
-#include "buffer.h"
+#include "array.h"
 #include "message-parser.h"
 #include "istream-internal.h"
 #include "istream-header-filter.h"
@@ -27,6 +27,7 @@ struct header_filter_istream {
 	uoff_t skip_count;
 
 	unsigned int cur_line, parsed_lines;
+	ARRAY_DEFINE(match_change_lines, unsigned int);
 
 	unsigned int header_read:1;
 	unsigned int header_parsed:1;
@@ -45,6 +46,8 @@ static void i_stream_header_filter_destr
 	if (mstream->hdr_ctx != NULL)
 		message_parse_header_deinit(&mstream->hdr_ctx);
 	i_stream_unref(&mstream->input);
+	if (array_is_created(&mstream->match_change_lines))
+		array_free(&mstream->match_change_lines);
 	pool_unref(&mstream->pool);
 }
 
@@ -59,7 +62,8 @@ i_stream_header_filter_set_max_buffer_si
 	i_stream_set_max_buffer_size(mstream->input, max_size);
 }
 
-static ssize_t read_mixed(struct header_filter_istream *mstream)
+static ssize_t
+read_mixed(struct header_filter_istream *mstream, size_t body_highwater_size)
 {
 	const unsigned char *data;
 	size_t pos;
@@ -71,7 +75,7 @@ static ssize_t read_mixed(struct header_
 	}
 
 	data = i_stream_get_data(mstream->input, &pos);
-	if (pos == 0) {
+	if (pos == body_highwater_size) {
 		ret = i_stream_read(mstream->input);
 		mstream->istream.istream.stream_errno =
 			mstream->input->stream_errno;
@@ -82,7 +86,9 @@ static ssize_t read_mixed(struct header_
 
 		data = i_stream_get_data(mstream->input, &pos);
 	}
-	buffer_append(mstream->hdr_buf, data, pos);
+	i_assert(pos > body_highwater_size);
+	buffer_append(mstream->hdr_buf, data + body_highwater_size,
+		      pos - body_highwater_size);
 
 	mstream->istream.buffer = buffer_get_data(mstream->hdr_buf, &pos);
 	ret = (ssize_t)(pos - mstream->istream.pos - mstream->istream.skip);
@@ -90,9 +96,31 @@ static ssize_t read_mixed(struct header_
 	return ret;
 }
 
+static int cmp_uint(const void *p1, const void *p2)
+{
+	const unsigned int *i1 = p1, *i2 = p2;
+
+	return *i1 < *i2 ? -1 :
+		(*i1 > *i2 ? 1 : 0);
+}
+
+static bool match_line_changed(struct header_filter_istream *mstream)
+{
+	const unsigned int *lines;
+	unsigned int count;
+
+	if (!array_is_created(&mstream->match_change_lines))
+		return FALSE;
+
+	lines = array_get(&mstream->match_change_lines, &count);
+	return bsearch(&mstream->cur_line, lines, count, sizeof(*lines),
+		       cmp_uint) != NULL;
+}
+
 static ssize_t read_header(struct header_filter_istream *mstream)
 {
 	struct message_header_line *hdr;
+	uoff_t highwater_offset;
 	size_t pos;
 	ssize_t ret;
 	bool matched;
@@ -111,12 +139,15 @@ static ssize_t read_header(struct header
 	mstream->istream.skip = 0;
 	buffer_set_used_size(mstream->hdr_buf, mstream->istream.pos);
 
-	if (mstream->header_read &&
-	    mstream->istream.istream.v_offset +
-	    (mstream->istream.pos - mstream->istream.skip) >=
-	    mstream->header_size.virtual_size) {
-		/* we want to return mixed headers and body */
-		return read_mixed(mstream);
+	if (mstream->header_read) {
+		highwater_offset = mstream->istream.istream.v_offset +
+			(mstream->istream.pos - mstream->istream.skip);
+		if (highwater_offset >= mstream->header_size.virtual_size) {
+			/* we want to return mixed headers and body */
+			size_t body_highwater_size = highwater_offset -
+				mstream->header_size.virtual_size;
+			return read_mixed(mstream, body_highwater_size);
+		}
 	}
 
 	while ((hdr_ret = message_parse_header_next(mstream->hdr_ctx,
@@ -146,10 +177,24 @@ static ssize_t read_header(struct header
 				mstream->headers_count,
 				sizeof(*mstream->headers),
 				bsearch_strcasecmp) != NULL;
-		if (mstream->cur_line > mstream->parsed_lines &&
-		    mstream->callback != NULL) {
-                        mstream->parsed_lines = mstream->cur_line;
+		if (mstream->callback == NULL) {
+			/* nothing gets excluded */
+		} else if (mstream->cur_line > mstream->parsed_lines) {
+			/* first time in this line */
+			bool orig_matched = matched;
+
+			mstream->parsed_lines = mstream->cur_line;
 			mstream->callback(hdr, &matched, mstream->context);
+			if (matched != orig_matched) {
+				i_array_init(&mstream->match_change_lines, 8);
+				array_append(&mstream->match_change_lines,
+					     &mstream->cur_line, 1);
+			}
+		} else {
+			/* second time in this line. was it excluded by the
+			   callback the first time? */
+			if (match_line_changed(mstream))
+				matched = !matched;
 		}
 
 		if (matched == mstream->exclude) {
@@ -290,6 +335,7 @@ static void i_stream_header_filter_seek(
 	stream->istream.v_offset = v_offset;
 	stream->skip = stream->pos = 0;
 	stream->buffer = NULL;
+	buffer_set_used_size(mstream->hdr_buf, 0);
 
 	if (mstream->hdr_ctx != NULL) {
 		message_parse_header_deinit(&mstream->hdr_ctx);


More information about the dovecot-cvs mailing list