changeset 1322:97f8c00b8d4c HEAD

Better handling for multiline headers. Before we skipped headers larger than input buffer size (8k with read (default), 256k with mmap). The skipping was also a bit buggy. Now we parse the lines one at a time. There's also a way to read the header fully into memory before parsing it, if really needed.
author Timo Sirainen <tss@iki.fi>
date Wed, 26 Mar 2003 19:29:01 +0200
parents 1a5705eb23d4
children c0ecdcaca2a6
files src/imap/imap-fetch-body-section.c src/lib-imap/imap-bodystructure.c src/lib-imap/imap-envelope.c src/lib-imap/imap-envelope.h src/lib-imap/imap-quote.c src/lib-index/mail-index-update.c src/lib-index/mbox/mbox-index.c src/lib-index/mbox/mbox-index.h src/lib-index/mbox/mbox-rewrite.c src/lib-mail/message-body-search.c src/lib-mail/message-parser.c src/lib-mail/message-parser.h src/lib-storage/index/index-mail.c src/lib-storage/index/index-mail.h src/lib-storage/index/index-save.c src/lib-storage/index/index-search.c src/lib-storage/index/index-storage.h src/lib-storage/index/mbox/mbox-save.c
diffstat 18 files changed, 896 insertions(+), 679 deletions(-) [+]
line wrap: on
line diff
--- a/src/imap/imap-fetch-body-section.c	Wed Mar 26 17:40:16 2003 +0200
+++ b/src/imap/imap-fetch-body-section.c	Wed Mar 26 19:29:01 2003 +0200
@@ -19,8 +19,6 @@
    double-parsing. */
 #define MAX_HEADER_BUFFER_SIZE (32*1024)
 
-#define UNSIGNED_CRLF (const unsigned char *) "\r\n"
-
 struct fetch_header_field_context {
 	string_t *dest;
 	struct ostream *output;
@@ -216,53 +214,12 @@
 	return ctx->dest_size < ctx->max_size;
 }
 
-static void fetch_header_field(struct message_part *part __attr_unused__,
-			       const unsigned char *name, size_t name_len,
-			       const unsigned char *value __attr_unused__,
-			       size_t value_len __attr_unused__,
-			       void *context)
-{
-	struct fetch_header_field_context *ctx = context;
-	const unsigned char *field_start, *field_end, *cr, *p;
-
-	/* see if we want this field. */
-	if (!ctx->match_func(ctx->fields, name, name_len) || name_len == 0)
-		return;
-
-	/* add the field, inserting CRs when needed. FIXME: is this too
-	   kludgy? we assume name continues with ": value". but otherwise
-	   we wouldn't reply with correct LWSP around ":". */
-	field_start = name;
-	field_end = value + value_len;
-
-	cr = NULL;
-	for (p = field_start; p != field_end; p++) {
-		if (*p == '\r')
-			cr = p;
-		else if (*p == '\n' && cr != p-1) {
-			/* missing CR */
-			if (!fetch_header_append(ctx, field_start,
-						 (size_t) (p-field_start)))
-				return;
-			if (!fetch_header_append(ctx, UNSIGNED_CRLF, 2))
-				return;
-
-			field_start = p+1;
-		}
-	}
-
-	if (field_start != field_end) {
-		if (!fetch_header_append(ctx, field_start,
-					 (size_t) (field_end-field_start)))
-			return;
-	}
-
-	(void)fetch_header_append(ctx, UNSIGNED_CRLF, 2);
-}
-
 static int fetch_header_fields(struct istream *input, const char *section,
 			       struct fetch_header_field_context *ctx)
 {
+	struct message_header_parser_ctx *hdr_ctx;
+	struct message_header_line *hdr;
+
 	if (strncmp(section, "HEADER.FIELDS ", 14) == 0) {
 		ctx->fields = get_fields_array(section + 14);
 		ctx->match_func = header_match;
@@ -279,13 +236,31 @@
 	}
 
 	ctx->dest_size = 0;
-	message_parse_header(NULL, input, NULL, fetch_header_field, ctx);
+
+	hdr_ctx = message_parse_header_init(input, NULL);
+	while ((hdr = message_parse_header_next(hdr_ctx)) != NULL) {
+		/* see if we want this field.
+		   we always want the end-of-headers line */
+		if (!ctx->match_func(ctx->fields, hdr->name, hdr->name_len) &&
+		    !hdr->eoh)
+			continue;
 
-	/* FIXME: The blank line must not be filtered, says RFC. However, we
-	   shouldn't add it if it wasn't there in the first place. Not very
-	   easy to know currently so we'll just do it always, it'll be present
-	   in all sane messages anyway.. */
-	(void)fetch_header_append(ctx, UNSIGNED_CRLF, 2);
+		if (!hdr->continued && !hdr->eoh) {
+			if (!fetch_header_append(ctx, hdr->name, hdr->name_len))
+				break;
+			if (!fetch_header_append(ctx,
+					(const unsigned char *) ": ", 2))
+				break;
+		}
+		if (!fetch_header_append(ctx, hdr->value, hdr->value_len))
+			break;
+		if (!hdr->no_newline) {
+			if (!fetch_header_append(ctx,
+					(const unsigned char *) "\r\n", 2))
+				break;
+		}
+	}
+	message_parse_header_deinit(hdr_ctx);
 
 	i_assert(ctx->dest_size <= ctx->max_size);
 	i_assert(ctx->dest == NULL || str_len(ctx->dest) == ctx->dest_size);
--- a/src/lib-imap/imap-bodystructure.c	Wed Mar 26 17:40:16 2003 +0200
+++ b/src/lib-imap/imap-bodystructure.c	Wed Mar 26 19:29:01 2003 +0200
@@ -1,6 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
+#include "buffer.h"
 #include "istream.h"
 #include "str.h"
 #include "message-parser.h"
@@ -141,19 +142,97 @@
 	message_tokenize_deinit(tok);
 }
 
+static void parse_content_header(struct message_part_body_data *d,
+				 struct message_header_line *hdr,
+				 pool_t pool)
+{
+	const char *name = hdr->name;
+	const unsigned char *value;
+	size_t value_len;
+
+	if (strncasecmp(name, "Content-", 8) != 0)
+		return;
+	name += 8;
+
+	if (hdr->continues) {
+		hdr->use_full_value = TRUE;
+		return;
+	}
+
+	value = hdr->full_value;
+	value_len = hdr->full_value_len;
+
+	switch (*name) {
+	case 'i':
+	case 'I':
+		if (strcasecmp(name, "ID") == 0 && d->content_id == NULL)
+			d->content_id = imap_quote(pool, value, value_len);
+		break;
+
+	case 'm':
+	case 'M':
+		if (strcasecmp(name, "MD5") == 0 && d->content_md5 == NULL)
+			d->content_md5 = imap_quote(pool, value, value_len);
+		break;
+
+	case 't':
+	case 'T':
+		if (strcasecmp(name, "Type") == 0 && d->content_type == NULL) {
+			d->str = t_str_new(256);
+			message_content_parse_header(value, value_len,
+						     parse_content_type,
+						     parse_save_params_list, d);
+			d->content_type_params =
+				p_strdup_empty(pool, str_c(d->str));
+		}
+		if (strcasecmp(name, "Transfer-Encoding") == 0 &&
+		    d->content_transfer_encoding == NULL) {
+			message_content_parse_header(value, value_len,
+				parse_content_transfer_encoding,
+				NULL, d);
+		}
+		break;
+
+	case 'l':
+	case 'L':
+		if (strcasecmp(name, "Language") == 0 &&
+		    d->content_language == NULL)
+			parse_content_language(value, value_len, d);
+		break;
+
+	case 'd':
+	case 'D':
+		if (strcasecmp(name, "Description") == 0 &&
+		    d->content_description == NULL) {
+			d->content_description =
+				imap_quote(pool, value, value_len);
+		}
+		if (strcasecmp(name, "Disposition") == 0 &&
+		    d->content_disposition_params == NULL) {
+			d->str = t_str_new(256);
+			message_content_parse_header(value, value_len,
+						     parse_content_disposition,
+						     parse_save_params_list, d);
+			d->content_disposition_params =
+				p_strdup_empty(pool, str_c(d->str));
+		}
+		break;
+	}
+}
+
 static void parse_header(struct message_part *part,
-			 const unsigned char *name, size_t name_len,
-			 const unsigned char *value, size_t value_len,
-			 void *context)
+                         struct message_header_line *hdr, void *context)
 {
 	pool_t pool = context;
 	struct message_part_body_data *part_data;
 	int parent_rfc822;
 
+	if (hdr == NULL)
+		return;
+
 	parent_rfc822 = part->parent != NULL &&
 		(part->parent->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822);
-	if (!parent_rfc822 && (name_len <= 8 ||
-			       memcasecmp(name, "Content-", 8) != 0))
+	if (!parent_rfc822 && strncasecmp(hdr->name, "Content-", 8) != 0)
 		return;
 
 	if (part->context == NULL) {
@@ -166,74 +245,11 @@
 
 	t_push();
 
-	switch (name_len) {
-	case 10:
-		if (memcasecmp(name, "Content-ID", 10) == 0 &&
-		    part_data->content_id == NULL) {
-			part_data->content_id =
-				imap_quote(pool, value, value_len);
-		}
-		break;
-
-	case 11:
-		if (memcasecmp(name, "Content-MD5", 11) == 0 &&
-		    part_data->content_md5 == NULL) {
-			part_data->content_md5 =
-				imap_quote(pool, value, value_len);
-		}
-		break;
-
-	case 12:
-		if (memcasecmp(name, "Content-Type", 12) != 0 ||
-		    part_data->content_type != NULL)
-			break;
-
-		part_data->str = t_str_new(256);
-		message_content_parse_header(value, value_len,
-					     parse_content_type,
-					     parse_save_params_list, part_data);
-		part_data->content_type_params =
-			p_strdup_empty(pool, str_c(part_data->str));
-		break;
-
-	case 16:
-		if (memcasecmp(name, "Content-Language", 16) == 0)
-			parse_content_language(value, value_len, part_data);
-		break;
-
-	case 19:
-		if (memcasecmp(name, "Content-Description", 19) == 0 &&
-		    part_data->content_description == NULL) {
-			part_data->content_description =
-				imap_quote(pool, value, value_len);
-		}
-		if (memcasecmp(name, "Content-Disposition", 19) == 0 &&
-		    part_data->content_disposition_params == NULL) {
-			part_data->str = t_str_new(256);
-			message_content_parse_header(value, value_len,
-						     parse_content_disposition,
-						     parse_save_params_list,
-						     part_data);
-			part_data->content_disposition_params =
-				p_strdup_empty(pool, str_c(part_data->str));
-		}
-		break;
-
-	case 25:
-		if (memcasecmp(name, "Content-Transfer-Encoding", 25) != 0 ||
-		    part_data->content_transfer_encoding != NULL)
-			break;
-
-		message_content_parse_header(value, value_len,
-					     parse_content_transfer_encoding,
-					     NULL, part_data);
-		break;
-	}
+	parse_content_header(part_data, hdr, pool);
 
 	if (parent_rfc822) {
 		/* message/rfc822, we need the envelope */
-		imap_envelope_parse_header(pool, &part_data->envelope,
-					   name, name_len, value, value_len);
+		imap_envelope_parse_header(pool, &part_data->envelope, hdr);
 	}
 	t_pop();
 }
@@ -249,7 +265,6 @@
 			      (input->v_offset - start_offset));
 
 		message_parse_header(part, input, NULL, parse_header, pool);
-
 		if (part->children != NULL) {
 			part_parse_headers(part->children, input,
 					   start_offset, pool);
--- a/src/lib-imap/imap-envelope.c	Wed Mar 26 17:40:16 2003 +0200
+++ b/src/lib-imap/imap-envelope.c	Wed Mar 26 19:29:01 2003 +0200
@@ -4,6 +4,7 @@
 #include "istream.h"
 #include "str.h"
 #include "message-address.h"
+#include "message-parser.h"
 #include "imap-parser.h"
 #include "imap-envelope.h"
 #include "imap-quote.h"
@@ -18,12 +19,80 @@
 	char *in_reply_to, *message_id;
 };
 
+int imap_envelope_get_field(const char *name, enum imap_envelope_field *ret)
+{
+	*ret = (enum imap_envelope_field)-1;
+
+	switch (*name) {
+	case 'B':
+	case 'b':
+		if (strcasecmp(name, "Bcc") == 0)
+			*ret = IMAP_ENVELOPE_BCC;
+		break;
+	case 'C':
+	case 'c':
+		if (strcasecmp(name, "Cc") == 0)
+			*ret = IMAP_ENVELOPE_CC;
+		break;
+	case 'D':
+	case 'd':
+		if (strcasecmp(name, "Date") == 0)
+			*ret = IMAP_ENVELOPE_DATE;
+		break;
+	case 'F':
+	case 'f':
+		if (strcasecmp(name, "From") == 0)
+			*ret = IMAP_ENVELOPE_FROM;
+		break;
+	case 'I':
+	case 'i':
+		if (strcasecmp(name, "In-reply-to") == 0)
+			*ret = IMAP_ENVELOPE_IN_REPLY_TO;
+		break;
+	case 'M':
+	case 'm':
+		if (strcasecmp(name, "Message-id") == 0)
+			*ret = IMAP_ENVELOPE_MESSAGE_ID;
+		break;
+	case 'R':
+	case 'r':
+		if (strcasecmp(name, "Reply-to") == 0)
+			*ret = IMAP_ENVELOPE_REPLY_TO;
+		break;
+	case 'S':
+	case 's':
+		if (strcasecmp(name, "Subject") == 0)
+			*ret = IMAP_ENVELOPE_SUBJECT;
+		if (strcasecmp(name, "Sender") == 0)
+			*ret = IMAP_ENVELOPE_SENDER;
+		break;
+	case 'T':
+	case 't':
+		if (strcasecmp(name, "To") == 0)
+			*ret = IMAP_ENVELOPE_TO;
+		break;
+	}
+
+	return *ret != (enum imap_envelope_field)-1;
+}
+
 void imap_envelope_parse_header(pool_t pool,
 				struct message_part_envelope_data **data,
-				const unsigned char *name, size_t name_len,
-				const unsigned char *value, size_t value_len)
+				struct message_header_line *hdr)
 {
 	struct message_part_envelope_data *d;
+	enum imap_envelope_field field;
+	struct message_address **addr_p;
+	char **str_p;
+
+	if (hdr == NULL || !imap_envelope_get_field(hdr->name, &field))
+		return;
+
+	if (hdr->continues) {
+		/* wait for full value */
+		hdr->use_full_value = TRUE;
+		return;
+	}
 
 	if (*data == NULL) {
 		*data = p_new(pool, struct message_part_envelope_data, 1);
@@ -31,61 +100,50 @@
 	}
 	d = *data;
 
-	t_push();
-
-	switch (name_len) {
-	case 2:
-		if (memcasecmp(name, "To", 2) == 0 && d->to == NULL) {
-			d->to = message_address_parse(pool, value,
-						      value_len, 0);
-		} else if (memcasecmp(name, "Cc", 2) == 0 && d->cc == NULL) {
-			d->cc = message_address_parse(pool, value,
-						      value_len, 0);
-		}
+	addr_p = NULL; str_p = NULL;
+	switch (field) {
+	case IMAP_ENVELOPE_DATE:
+		str_p = &d->date;
 		break;
-	case 3:
-		if (memcasecmp(name, "Bcc", 3) == 0 && d->bcc == NULL) {
-			d->bcc = message_address_parse(pool, value,
-						       value_len, 0);
-		}
+	case IMAP_ENVELOPE_SUBJECT:
+		str_p = &d->subject;
 		break;
-	case 4:
-		if (memcasecmp(name, "From", 4) == 0 && d->from == NULL) {
-			d->from = message_address_parse(pool, value,
-							value_len, 0);
-		} else if (memcasecmp(name, "Date", 4) == 0 && d->date == NULL)
-			d->date = imap_quote(pool, value, value_len);
+	case IMAP_ENVELOPE_MESSAGE_ID:
+		str_p = &d->message_id;
+		break;
+	case IMAP_ENVELOPE_IN_REPLY_TO:
+		str_p = &d->in_reply_to;
 		break;
-	case 6:
-		if (memcasecmp(name, "Sender", 6) == 0 && d->sender == NULL) {
-			d->sender = message_address_parse(pool, value,
-							  value_len, 0);
-		}
+
+	case IMAP_ENVELOPE_CC:
+		addr_p = &d->cc;
 		break;
-	case 7:
-		if (memcasecmp(name, "Subject", 7) == 0 && d->subject == NULL)
-			d->subject = imap_quote(pool, value, value_len);
+	case IMAP_ENVELOPE_BCC:
+		addr_p = &d->bcc;
+		break;
+	case IMAP_ENVELOPE_FROM:
+		addr_p = &d->from;
 		break;
-	case 8:
-		if (memcasecmp(name, "Reply-To", 8) == 0 &&
-		    d->reply_to == NULL) {
-			d->reply_to = message_address_parse(pool, value,
-							    value_len, 0);
-		}
+	case IMAP_ENVELOPE_SENDER:
+		addr_p = &d->sender;
 		break;
-	case 10:
-		if (memcasecmp(name, "Message-Id", 10) == 0 &&
-		    d->message_id == NULL)
-			d->message_id = imap_quote(pool, value, value_len);
+	case IMAP_ENVELOPE_TO:
+		addr_p = &d->to;
 		break;
-	case 11:
-		if (memcasecmp(name, "In-Reply-To", 11) == 0 &&
-		    d->in_reply_to == NULL)
-			d->in_reply_to = imap_quote(pool, value, value_len);
+	case IMAP_ENVELOPE_REPLY_TO:
+		addr_p = &d->reply_to;
+		break;
+	case IMAP_ENVELOPE_FIELDS:
 		break;
 	}
 
-	t_pop();
+	if (addr_p != NULL) {
+		*addr_p = message_address_parse(pool, hdr->full_value,
+						hdr->full_value_len, 0);
+	}
+
+	if (str_p != NULL)
+		*str_p = imap_quote(pool, hdr->full_value, hdr->full_value_len);
 }
 
 static void imap_write_address(string_t *str, struct message_address *addr)
--- a/src/lib-imap/imap-envelope.h	Wed Mar 26 17:40:16 2003 +0200
+++ b/src/lib-imap/imap-envelope.h	Wed Mar 26 19:29:01 2003 +0200
@@ -1,6 +1,8 @@
 #ifndef __IMAP_ENVELOPE_H
 #define __IMAP_ENVELOPE_H
 
+struct message_header_line;
+
 enum imap_envelope_field {
 	/* NOTE: in the same order as listed in ENVELOPE */
 	IMAP_ENVELOPE_DATE = 0,
@@ -24,11 +26,12 @@
 
 struct message_part_envelope_data;
 
+int imap_envelope_get_field(const char *name, enum imap_envelope_field *ret);
+
 /* Update envelope data based from given header field */
 void imap_envelope_parse_header(pool_t pool,
 				struct message_part_envelope_data **data,
-				const unsigned char *name, size_t name_len,
-				const unsigned char *value, size_t value_len);
+				struct message_header_line *hdr);
 
 /* Write envelope to given string */
 void imap_envelope_write_part_data(struct message_part_envelope_data *data,
--- a/src/lib-imap/imap-quote.c	Wed Mar 26 17:40:16 2003 +0200
+++ b/src/lib-imap/imap-quote.c	Wed Mar 26 19:29:01 2003 +0200
@@ -53,11 +53,18 @@
 char *imap_quote(pool_t pool, const unsigned char *value, size_t value_len)
 {
 	string_t *str;
+	char *ret;
+
+	i_assert(pool != data_stack_pool);
 
 	if (value == NULL)
 		return "NIL";
 
+	t_push();
 	str = t_str_new(value_len + MAX_INT_STRLEN + 5);
 	imap_quote_append(str, value, value_len);
-	return p_strndup(pool, str_data(str), str_len(str));
+	ret = p_strndup(pool, str_data(str), str_len(str));
+	t_pop();
+
+	return ret;
 }
--- a/src/lib-index/mail-index-update.c	Wed Mar 26 17:40:16 2003 +0200
+++ b/src/lib-index/mail-index-update.c	Wed Mar 26 19:29:01 2003 +0200
@@ -2,6 +2,7 @@
 
 #include "lib.h"
 #include "buffer.h"
+#include "str.h"
 #include "istream.h"
 #include "ioloop.h"
 #include "str.h"
@@ -384,9 +385,7 @@
 };
 
 static void update_header_cb(struct message_part *part,
-			     const unsigned char *name, size_t name_len,
-			     const unsigned char *value, size_t value_len,
-			     void *context)
+			     struct message_header_line *hdr, void *context)
 {
 	struct header_update_context *ctx = context;
 
@@ -399,14 +398,12 @@
 			ctx->envelope_pool =
 				pool_alloconly_create("index envelope", 2048);
 		}
-		imap_envelope_parse_header(ctx->envelope_pool, &ctx->envelope,
-					   name, name_len, value, value_len);
+		imap_envelope_parse_header(ctx->envelope_pool,
+					   &ctx->envelope, hdr);
 	}
 
-	if (ctx->header_cb != NULL) {
-		ctx->header_cb(part, name, name_len,
-			       value, value_len, ctx->context);
-	}
+	if (ctx->header_cb != NULL)
+		ctx->header_cb(part, hdr, ctx->context);
 }
 
 void mail_index_update_headers(struct mail_index_update *update,
--- a/src/lib-index/mbox/mbox-index.c	Wed Mar 26 17:40:16 2003 +0200
+++ b/src/lib-index/mbox/mbox-index.c	Wed Mar 26 19:29:01 2003 +0200
@@ -248,16 +248,14 @@
 }
 
 void mbox_header_cb(struct message_part *part __attr_unused__,
-		    const unsigned char *name, size_t name_len,
-		    const unsigned char *value, size_t value_len,
-		    void *context)
+		    struct message_header_line *hdr, void *context)
 {
 	struct mbox_header_context *ctx = context;
 	uoff_t start_offset, end_offset;
 	size_t i;
 	int fixed = FALSE;
 
-	if (name_len == 0) {
+	if (hdr == NULL) {
 		/* End of headers */
 		if (!ctx->set_read_limit)
 			return;
@@ -281,99 +279,109 @@
 		return;
 	}
 
+	if (hdr->eoh)
+		return;
+
 	/* Pretty much copy&pasted from popa3d by Solar Designer */
-	switch (*name) {
+	switch (*hdr->name) {
 	case 'R':
 	case 'r':
-		if (!ctx->received && name_len == 8 &&
-		    memcasecmp(name, "Received", 8) == 0) {
-			ctx->received = TRUE;
+		if (!ctx->received &&
+		    strcasecmp(hdr->name, "Received") == 0) {
+			/* get only the first received-header */
 			fixed = TRUE;
+			if (!hdr->continues)
+				ctx->received = TRUE;
 		}
 		break;
 
 	case 'C':
 	case 'c':
-		if (name_len == 14 && ctx->set_read_limit &&
-		    memcasecmp(name, "Content-Length", 14) == 0) {
+		if (ctx->set_read_limit &&
+		    strcasecmp(hdr->name, "Content-Length") == 0) {
 			/* manual parsing, so we can deal with uoff_t */
 			ctx->content_length = 0;
-			for (i = 0; i < value_len; i++) {
-				if (value[i] < '0' || value[i] > '9') {
+			for (i = 0; i < hdr->value_len; i++) {
+				if (hdr->value[i] < '0' ||
+				    hdr->value[i] > '9') {
 					/* invalid */
 					ctx->content_length = 0;
 					break;
 				}
 
 				ctx->content_length = ctx->content_length * 10 +
-					(value[i] - '0');
+					(hdr->value[i] - '0');
 			}
 		}
 		break;
 
 	case 'D':
 	case 'd':
-		if (name_len == 12)
-			fixed = memcasecmp(name, "Delivered-To", 12) == 0;
-		else if (name_len == 4) {
+		if (strcasecmp(hdr->name, "Delivered-To") == 0)
+			fixed = TRUE;
+		else if (!ctx->received && strcasecmp(hdr->name, "Date") == 0) {
 			/* Received-header contains date too,
 			   and more trusted one */
-			fixed = !ctx->received &&
-				memcasecmp(name, "Date", 4) == 0;
+			fixed = TRUE;
 		}
 		break;
 
 	case 'M':
 	case 'm':
-		if (name_len == 10) {
+		if (!ctx->received &&
+		    strcasecmp(hdr->name, "Message-ID") == 0) {
 			/* Received-header contains unique ID too,
 			   and more trusted one */
-			fixed = !ctx->received &&
-				memcasecmp(name, "Message-ID", 10) == 0;
+			fixed = TRUE;
 		}
 		break;
 
 	case 'S':
 	case 's':
-		if (name_len == 6 && memcasecmp(name, "Status", 6) == 0) {
+		if (strcasecmp(hdr->name, "Status") == 0) {
 			/* update message flags */
-			ctx->flags |= mbox_get_status_flags(value, value_len);
+			ctx->flags |= mbox_get_status_flags(hdr->value,
+							    hdr->value_len);
 		}
 		break;
 
 	case 'X':
 	case 'x':
-		if (name_len == 13) {
+		if (strcasecmp(hdr->name, "X-Delivery-ID:") == 0) {
 			/* Let the local delivery agent help generate unique
 			   ID's but don't blindly trust this header alone as
 			   it could just as easily come from the remote. */
-			fixed = memcasecmp(name, "X-Delivery-ID:", 13) == 0;
-		} else if (name_len == 5 &&
-			   memcasecmp(name, "X-UID", 5) == 0) {
+			fixed = TRUE;
+		} else if (strcasecmp(hdr->name, "X-UID") == 0) {
 			ctx->uid = 0;
-			for (i = 0; i < value_len; i++) {
-				if (value[i] < '0' || value[i] > '9')
+			for (i = 0; i < hdr->value_len; i++) {
+				if (hdr->value[i] < '0' ||
+				    hdr->value[i] > '9')
 					break;
-				ctx->uid = ctx->uid * 10 + (value[i]-'0');
+				ctx->uid = ctx->uid * 10 + (hdr->value[i]-'0');
 			}
-		} else if (name_len == 8 &&
-			   memcasecmp(name, "X-Status", 8) == 0) {
+		} else if (strcasecmp(hdr->name, "X-Status") == 0) {
 			/* update message flags */
-			ctx->flags |= mbox_get_status_flags(value, value_len);
-		} else if (name_len == 10 &&
-			   memcasecmp(name, "X-Keywords", 10) == 0) {
+			ctx->flags |= mbox_get_status_flags(hdr->value,
+							    hdr->value_len);
+		} else if (strcasecmp(hdr->name, "X-Keywords") == 0) {
 			/* update custom message flags */
-			ctx->flags |= mbox_get_keyword_flags(value, value_len,
+			ctx->flags |= mbox_get_keyword_flags(hdr->value,
+							     hdr->value_len,
 							     ctx->custom_flags);
-		} else if (name_len == 10 &&
-			   memcasecmp(name, "X-IMAPbase", 10) == 0) {
-			mbox_parse_imapbase(value, value_len, ctx);
+		} else if (strcasecmp(hdr->name, "X-IMAPbase") == 0) {
+			if (hdr->continues) {
+				hdr->use_full_value = TRUE;
+				break;
+			}
+			mbox_parse_imapbase(hdr->full_value,
+					    hdr->full_value_len, ctx);
 		}
 		break;
 	}
 
 	if (fixed)
-		md5_update(&ctx->md5, value, value_len);
+		md5_update(&ctx->md5, hdr->value, hdr->value_len);
 }
 
 void mbox_keywords_parse(const unsigned char *value, size_t len,
--- a/src/lib-index/mbox/mbox-index.h	Wed Mar 26 17:40:16 2003 +0200
+++ b/src/lib-index/mbox/mbox-index.h	Wed Mar 26 19:29:01 2003 +0200
@@ -33,10 +33,8 @@
 			      struct mail_index *index,
 			      struct istream *input);
 void mbox_header_free_context(struct mbox_header_context *ctx);
-void mbox_header_cb(struct message_part *part __attr_unused__,
-		    const unsigned char *name, size_t name_len,
-		    const unsigned char *value, size_t value_len,
-		    void *context);
+void mbox_header_cb(struct message_part *part,
+		    struct message_header_line *hdr, void *context);
 void mbox_keywords_parse(const unsigned char *value, size_t len,
 			 const char *custom_flags[MAIL_CUSTOM_FLAGS_COUNT],
 			 void (*func)(const unsigned char *, size_t,
--- a/src/lib-index/mbox/mbox-rewrite.c	Wed Mar 26 17:40:16 2003 +0200
+++ b/src/lib-index/mbox/mbox-rewrite.c	Wed Mar 26 19:29:01 2003 +0200
@@ -20,7 +20,6 @@
 
 struct mbox_rewrite_context {
 	struct ostream *output;
-	int failed;
 
 	uoff_t content_length;
 	unsigned int seq, uid;
@@ -258,51 +257,96 @@
 	return str_len(str) == 0 ? NULL : str_c(str);
 }
 
-static void header_cb(struct message_part *part __attr_unused__,
-		      const unsigned char *name, size_t name_len,
-		      const unsigned char *value, size_t value_len,
-		      void *context)
+static int write_header(struct mbox_rewrite_context *ctx,
+			struct message_header_line *hdr)
 {
-	struct mbox_rewrite_context *ctx = context;
 	const char *str;
 
-	if (ctx->failed)
-		return;
+	switch (hdr->name_len) {
+	case 5:
+		if (strcasecmp(hdr->name, "X-UID") == 0) {
+			if (ctx->xuid_found)
+				return TRUE;
+
+			ctx->xuid_found = TRUE;
+			return mbox_write_xuid(ctx);
+		}
+		break;
+	case 6:
+		if (strcasecmp(hdr->name, "Status") == 0) {
+			if (ctx->status_found)
+				return TRUE;
+			if (hdr->continues) {
+				hdr->use_full_value = TRUE;
+				return TRUE;
+			}
+
+			ctx->status_found = TRUE;
+			str = strip_chars(hdr->full_value,
+					  hdr->full_value_len, "RO");
+			return mbox_write_status(ctx, str);
+		}
+		break;
+	case 8:
+		if (strcasecmp(hdr->name, "X-Status") == 0) {
+			if (ctx->xstatus_found)
+				return TRUE;
+			if (hdr->continues) {
+				hdr->use_full_value = TRUE;
+				return TRUE;
+			}
 
-	if (name_len == 6 && memcasecmp(name, "Status", 6) == 0) {
-		ctx->status_found = TRUE;
-		str = strip_chars(value, value_len, "RO");
-		(void)mbox_write_status(ctx, str);
-	} else if (name_len == 8 && memcasecmp(name, "X-Status", 8) == 0) {
-		ctx->xstatus_found = TRUE;
-		str = strip_chars(value, value_len, "ADFT");
-		(void)mbox_write_xstatus(ctx, str);
-	} else if (name_len == 10 && memcasecmp(name, "X-Keywords", 10) == 0) {
-		ctx->xkeywords_found = TRUE;
-		str = strip_custom_flags(value, value_len, ctx);
-		(void)mbox_write_xkeywords(ctx, str);
-	} else if (name_len == 10 && memcasecmp(name, "X-IMAPbase", 10) == 0) {
-		if (ctx->seq == 1) {
+			ctx->xstatus_found = TRUE;
+			str = strip_chars(hdr->full_value,
+					  hdr->full_value_len, "ADFT");
+			return mbox_write_xstatus(ctx, str);
+		}
+		break;
+	case 10:
+		if (strcasecmp(hdr->name, "X-Keywords") == 0) {
+			if (ctx->xkeywords_found)
+				return TRUE;
+			if (hdr->continues) {
+				hdr->use_full_value = TRUE;
+				return TRUE;
+			}
+
+			ctx->xkeywords_found = TRUE;
+			str = strip_custom_flags(hdr->full_value,
+						 hdr->full_value_len, ctx);
+			return mbox_write_xkeywords(ctx, str);
+		} else if (strcasecmp(hdr->name, "X-IMAPbase") == 0) {
+			if (ctx->seq != 1 || ctx->ximapbase_found)
+				return TRUE;
+
 			ctx->ximapbase_found = TRUE;
-			(void)mbox_write_ximapbase(ctx);
+			return mbox_write_ximapbase(ctx);
 		}
-	} else if (name_len == 5 && memcasecmp(name, "X-UID", 5) == 0) {
-		ctx->xuid_found = TRUE;
-		(void)mbox_write_xuid(ctx);
-	} else if (name_len == 14 &&
-		   memcasecmp(name, "Content-Length", 14) == 0) {
-		ctx->content_length_found = TRUE;
-		(void)mbox_write_content_length(ctx);
-	} else if (name_len > 0) {
-		/* save this header */
-		(void)o_stream_send(ctx->output, name, name_len);
-		(void)o_stream_send(ctx->output, ": ", 2);
-		(void)o_stream_send(ctx->output, value, value_len);
-		(void)o_stream_send(ctx->output, "\n", 1);
+		break;
+	case 14:
+		if (strcasecmp(hdr->name, "Content-Length") == 0) {
+			if (ctx->content_length_found)
+				return TRUE;
+
+			ctx->content_length_found = TRUE;
+			return mbox_write_content_length(ctx);
+		}
+		break;
 	}
 
-	if (ctx->output->closed)
-		ctx->failed = TRUE;
+	if (!hdr->eoh) {
+		/* save this header */
+		if (!hdr->continued) {
+			(void)o_stream_send(ctx->output, hdr->name,
+					    hdr->name_len);
+			(void)o_stream_send(ctx->output, ": ", 2);
+		}
+		(void)o_stream_send(ctx->output, hdr->value, hdr->value_len);
+		if (!hdr->no_newline)
+			(void)o_stream_send(ctx->output, "\n", 1);
+	}
+
+	return !ctx->output->closed;
 }
 
 static int mbox_write_header(struct mail_index *index,
@@ -323,6 +367,8 @@
 	   Last used UID is also not updated, and set to 0 initially.
 	*/
 	struct mbox_rewrite_context ctx;
+	struct message_header_parser_ctx *hdr_ctx;
+	struct message_header_line *hdr;
 	struct message_size hdr_parsed_size;
 
 	if (input->v_offset >= end_offset) {
@@ -346,7 +392,12 @@
 	ctx.custom_flags = mail_custom_flags_list_get(index->custom_flags);
 
 	i_stream_set_read_limit(input, input->v_offset + hdr_size);
-	message_parse_header(NULL, input, &hdr_parsed_size, header_cb, &ctx);
+
+	hdr_ctx = message_parse_header_init(input, &hdr_parsed_size);
+	while ((hdr = message_parse_header_next(hdr_ctx)) != NULL)
+		write_header(&ctx, hdr);
+	message_parse_header_deinit(hdr_ctx);
+
 	i_stream_set_read_limit(input, 0);
 
 	i_assert(hdr_parsed_size.physical_size == hdr_size);
--- a/src/lib-mail/message-body-search.c	Wed Mar 26 17:40:16 2003 +0200
+++ b/src/lib-mail/message-body-search.c	Wed Mar 26 19:29:01 2003 +0200
@@ -28,7 +28,6 @@
 struct part_search_context {
 	struct body_search_context *body_ctx;
 
-	struct header_search_context *hdr_search_ctx;
 	struct charset_translation *translation;
 
 	buffer_t *decode_buf;
@@ -42,7 +41,6 @@
 	unsigned int content_unknown:1;
 	unsigned int content_type_text:1; /* text/any or message/any */
 	unsigned int ignore_header:1;
-	unsigned int found:1;
 };
 
 static void parse_content_type(const unsigned char *value, size_t value_len,
@@ -101,47 +99,59 @@
 	}
 }
 
-static void header_find(struct message_part *part __attr_unused__,
-			const unsigned char *name, size_t name_len,
-			const unsigned char *value, size_t value_len,
-			void *context)
-{
-	struct part_search_context *ctx = context;
-
-	if (ctx->found)
-		return;
-
-	if (!ctx->ignore_header) {
-		ctx->found = message_header_search(value, value_len,
-						   ctx->hdr_search_ctx);
-	}
-
-	if (name_len == 12 && memcasecmp(name, "Content-Type", 12) == 0) {
-		message_content_parse_header(value, value_len,
-					     parse_content_type,
-					     parse_content_type_param,
-					     ctx);
-	} else if (name_len == 25 &&
-		   memcasecmp(name, "Content-Transfer-Encoding", 25) == 0) {
-		message_content_parse_header(value, value_len,
-					     parse_content_encoding,
-					     NULL, ctx);
-	}
-}
-
 static int message_search_header(struct part_search_context *ctx,
 				 struct istream *input)
 {
-	ctx->hdr_search_ctx = message_header_search_init(data_stack_pool,
-							 ctx->body_ctx->key,
-							 ctx->body_ctx->charset,
-							 NULL);
+	struct header_search_context *hdr_search_ctx;
+	struct message_header_parser_ctx *hdr_ctx;
+	struct message_header_line *hdr;
+	int found = FALSE;
+
+	hdr_search_ctx = message_header_search_init(data_stack_pool,
+						    ctx->body_ctx->key,
+						    ctx->body_ctx->charset,
+						    NULL);
 
 	/* we default to text content-type */
 	ctx->content_type_text = TRUE;
-	message_parse_header(NULL, input, NULL, header_find, ctx);
+
+	hdr_ctx = message_parse_header_init(input, NULL);
+	while ((hdr = message_parse_header_next(hdr_ctx)) != NULL) {
+		if (!ctx->ignore_header) {
+			if (message_header_search(hdr->value, hdr->value_len,
+						  hdr_search_ctx)) {
+				found = TRUE;
+				break;
+			}
+		}
 
-	return ctx->found;
+		if (hdr->name_len == 12 &&
+		    strcasecmp(hdr->name, "Content-Type") == 0) {
+			if (hdr->continues) {
+				hdr->use_full_value = TRUE;
+				continue;
+			}
+			message_content_parse_header(hdr->full_value,
+						     hdr->full_value_len,
+						     parse_content_type,
+						     parse_content_type_param,
+						     ctx);
+		} else if (hdr->name_len == 25 &&
+			   strcasecmp(hdr->name,
+				      "Content-Transfer-Encoding") == 0) {
+			if (hdr->continues) {
+				hdr->use_full_value = TRUE;
+				continue;
+			}
+			message_content_parse_header(hdr->full_value,
+						     hdr->full_value_len,
+						     parse_content_encoding,
+						     NULL, ctx);
+		}
+	}
+	message_parse_header_deinit(hdr_ctx);
+
+	return found;
 }
 
 static int message_search_decoded_block(struct part_search_context *ctx,
--- a/src/lib-mail/message-parser.c	Wed Mar 26 17:40:16 2003 +0200
+++ b/src/lib-mail/message-parser.c	Wed Mar 26 19:29:01 2003 +0200
@@ -1,7 +1,9 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
+#include "buffer.h"
 #include "istream.h"
+#include "str.h"
 #include "strescape.h"
 #include "message-content-parser.h"
 #include "message-parser.h"
@@ -27,6 +29,17 @@
 	void *context;
 };
 
+struct message_header_parser_ctx {
+	struct message_header_line line;
+
+	struct istream *input;
+	struct message_size *hdr_size;
+
+	string_t *name;
+	buffer_t *value_buf;
+	size_t skip;
+};
+
 static struct message_part *
 message_parse_part(struct istream *input,
 		   struct parser_context *parser_ctx);
@@ -119,28 +132,6 @@
 	}
 }
 
-static void parse_header_field(struct message_part *part,
-			       const unsigned char *name, size_t name_len,
-			       const unsigned char *value, size_t value_len,
-			       void *context)
-{
-	struct parser_context *parser_ctx = context;
-
-	/* call the user-defined header parser */
-	if (parser_ctx->callback != NULL) {
-		parser_ctx->callback(part, name, name_len, value, value_len,
-				     parser_ctx->context);
-	}
-
-	if (name_len == 12 && memcasecmp(name, "Content-Type", 12) == 0) {
-		/* we need to know the boundary */
-		message_content_parse_header(value, value_len,
-					     parse_content_type,
-					     parse_content_type_param,
-					     parser_ctx);
-	}
-}
-
 static struct message_part *
 message_parse_multipart(struct istream *input,
 			struct parser_context *parser_ctx)
@@ -197,12 +188,38 @@
 static struct message_part *
 message_parse_part(struct istream *input, struct parser_context *parser_ctx)
 {
+	struct message_header_parser_ctx *hdr_ctx;
+	struct message_header_line *hdr;
 	struct message_part *next_part, *part;
 	uoff_t hdr_size;
 
-	message_parse_header(parser_ctx->part, input,
-			     &parser_ctx->part->header_size,
-			     parse_header_field, parser_ctx);
+	hdr_ctx = message_parse_header_init(input,
+					    &parser_ctx->part->header_size);
+	while ((hdr = message_parse_header_next(hdr_ctx)) != NULL) {
+		/* call the user-defined header parser */
+		if (parser_ctx->callback != NULL) {
+			parser_ctx->callback(parser_ctx->part, hdr,
+					     parser_ctx->context);
+		}
+
+		if (strcasecmp(hdr->name, "Content-Type") == 0) {
+			if (hdr->continues) {
+				hdr->use_full_value = TRUE;
+				continue;
+			}
+			/* we need to know the boundary */
+			message_content_parse_header(hdr->full_value,
+						     hdr->full_value_len,
+						     parse_content_type,
+						     parse_content_type_param,
+						     parser_ctx);
+		}
+	}
+	if (parser_ctx->callback != NULL) {
+		parser_ctx->callback(parser_ctx->part, NULL,
+				     parser_ctx->context);
+	}
+	message_parse_header_deinit(hdr_ctx);
 
 	i_assert((parser_ctx->part->flags & MUTEX_FLAGS) != MUTEX_FLAGS);
 
@@ -252,23 +269,6 @@
 	return next_part;
 }
 
-struct message_part *message_parse(pool_t pool, struct istream *input,
-				   message_header_callback_t *callback,
-				   void *context)
-{
-	struct message_part *part;
-	struct parser_context parser_ctx;
-
-	memset(&parser_ctx, 0, sizeof(parser_ctx));
-	parser_ctx.pool = pool;
-	parser_ctx.callback = callback;
-	parser_ctx.context = context;
-	parser_ctx.part = part = p_new(pool, struct message_part, 1);
-
-	message_parse_part(input, &parser_ctx);
-	return part;
-}
-
 static void message_skip_line(struct istream *input,
 			      struct message_size *msg_size, int skip_lf)
 {
@@ -315,135 +315,6 @@
 	}
 }
 
-void message_parse_header(struct message_part *part, struct istream *input,
-			  struct message_size *hdr_size,
-			  message_header_callback_t *callback, void *context)
-{
-	const unsigned char *msg;
-	size_t i, size, parse_size, startpos, missing_cr_count;
-	size_t line_start, colon_pos, end_pos, name_len, value_len;
-	int ret;
-
-	if (hdr_size != NULL)
-		memset(hdr_size, 0, sizeof(struct message_size));
-
-	missing_cr_count = startpos = line_start = 0;
-	colon_pos = UINT_MAX;
-	for (;;) {
-		ret = i_stream_read_data(input, &msg, &size, startpos+1);
-		if (ret == -2) {
-			/* overflow, line is too long. just skip it. */
-			i_assert(size > 2);
-
-                        message_skip_line(input, hdr_size, TRUE);
-			startpos = line_start = 0;
-			colon_pos = UINT_MAX;
-			continue;
-		}
-
-		if (ret < 0 || (ret <= 0 && size == startpos)) {
-			/* EOF and nothing in buffer. the later check is
-			   needed only when there's no message body */
-			break;
-		}
-
-		parse_size = size <= startpos+1 ? size : size-1;
-		for (i = startpos; i < parse_size; i++) {
-			if (msg[i] == ':' && colon_pos == UINT_MAX) {
-				colon_pos = i;
-				continue;
-			}
-
-			if (msg[i] != '\n')
-				continue;
-
-			if (hdr_size != NULL)
-				hdr_size->lines++;
-
-			if (i == 0 || msg[i-1] != '\r') {
-				/* missing CR */
-				missing_cr_count++;
-			}
-
-			if (i == 0 || (i == 1 && msg[i-1] == '\r')) {
-				/* no headers at all */
-				break;
-			}
-
-			if ((i > 0 && msg[i-1] == '\n') ||
-			    (i > 1 && msg[i-2] == '\n' && msg[i-1] == '\r')) {
-				/* \n\n or \n\r\n - end of headers */
-				break;
-			}
-
-			/* make sure the header doesn't continue to next line */
-			if (i+1 == size || !IS_LWSP(msg[i+1])) {
-				if (colon_pos != UINT_MAX &&
-				    colon_pos != line_start &&
-				    callback != NULL &&
-				    !IS_LWSP(msg[line_start])) {
-					/* we have a valid header line */
-
-					/* get length of name-field */
-					end_pos = colon_pos-1;
-					while (end_pos > line_start &&
-					       IS_LWSP(msg[end_pos]))
-						end_pos--;
-					name_len = end_pos - line_start + 1;
-
-					/* get length of value field.
-					   skip all LWSP after ':'. */
-					colon_pos++;
-					while (colon_pos < i &&
-					       IS_LWSP(msg[colon_pos]))
-						colon_pos++;
-					value_len = i - colon_pos;
-					if (msg[i-1] == '\r') value_len--;
-
-					/* and finally call the function */
-					callback(part,
-						 msg + line_start, name_len,
-						 msg + colon_pos, value_len,
-						 context);
-				}
-
-				colon_pos = UINT_MAX;
-				line_start = i+1;
-			}
-		}
-
-		if (i < parse_size) {
-			/* end of header */
-			startpos = i+1;
-			break;
-		}
-
-		/* leave the last line to buffer */
-		if (colon_pos != UINT_MAX)
-			colon_pos -= line_start;
-		if (hdr_size != NULL)
-			hdr_size->physical_size += line_start;
-		i_stream_skip(input, line_start);
-
-		startpos = i-line_start;
-		line_start = 0;
-	}
-
-	i_stream_skip(input, startpos);
-
-	if (hdr_size != NULL) {
-		hdr_size->physical_size += startpos;
-		hdr_size->virtual_size +=
-			hdr_size->physical_size + missing_cr_count;
-		i_assert(hdr_size->virtual_size >= hdr_size->physical_size);
-	}
-
-	if (callback != NULL) {
-		/* "end of headers" notify */
-		callback(part, NULL, 0, NULL, 0, context);
-	}
-}
-
 static struct message_boundary *
 boundary_find(struct message_boundary *boundaries,
 	      const unsigned char *msg, size_t len)
@@ -616,3 +487,245 @@
 
 	return boundary == NULL ? NULL : boundary->part;
 }
+
+struct message_part *message_parse(pool_t pool, struct istream *input,
+				   message_header_callback_t *callback,
+				   void *context)
+{
+	struct message_part *part;
+	struct parser_context parser_ctx;
+
+	memset(&parser_ctx, 0, sizeof(parser_ctx));
+	parser_ctx.pool = pool;
+	parser_ctx.callback = callback;
+	parser_ctx.context = context;
+	parser_ctx.part = part = p_new(pool, struct message_part, 1);
+
+	message_parse_part(input, &parser_ctx);
+	return part;
+}
+
+void message_parse_header(struct message_part *part, struct istream *input,
+			  struct message_size *hdr_size,
+			  message_header_callback_t *callback, void *context)
+{
+	struct message_header_parser_ctx *hdr_ctx;
+	struct message_header_line *hdr;
+
+	hdr_ctx = message_parse_header_init(input, hdr_size);
+	while ((hdr = message_parse_header_next(hdr_ctx)) != NULL)
+		callback(part, hdr, context);
+	callback(part, NULL, context);
+	message_parse_header_deinit(hdr_ctx);
+}
+
+struct message_header_parser_ctx *
+message_parse_header_init(struct istream *input, struct message_size *hdr_size)
+{
+	struct message_header_parser_ctx *ctx;
+
+	ctx = i_new(struct message_header_parser_ctx, 1);
+	ctx->input = input;
+	ctx->hdr_size = hdr_size;
+	ctx->name = str_new(default_pool, 128);
+
+	if (hdr_size != NULL)
+		memset(hdr_size, 0, sizeof(*hdr_size));
+	return ctx;
+}
+
+void message_parse_header_deinit(struct message_header_parser_ctx *ctx)
+{
+	i_stream_skip(ctx->input, ctx->skip);
+	if (ctx->value_buf != NULL)
+		buffer_free(ctx->value_buf);
+	str_free(ctx->name);
+	i_free(ctx);
+}
+
+struct message_header_line *
+message_parse_header_next(struct message_header_parser_ctx *ctx)
+{
+        struct message_header_line *line = &ctx->line;
+	const unsigned char *msg;
+	size_t i, size, startpos, colon_pos, parse_size;
+	int ret;
+
+	if (line->eoh)
+		return NULL;
+
+	if (ctx->skip > 0) {
+		i_stream_skip(ctx->input, ctx->skip);
+		ctx->skip = 0;
+	}
+
+	startpos = 0; colon_pos = UINT_MAX;
+
+	line->no_newline = FALSE;
+
+	if (line->continues) {
+		if (line->use_full_value && !line->continued) {
+			/* save the first line */
+			if (ctx->value_buf != NULL)
+				buffer_set_used_size(ctx->value_buf, 0);
+			else {
+				ctx->value_buf =
+					buffer_create_dynamic(default_pool,
+							      4096, (size_t)-1);
+			}
+			buffer_append(ctx->value_buf,
+				      line->value, line->value_len);
+		}
+
+		line->continued = TRUE;
+		line->continues = FALSE;
+		colon_pos = 0;
+	} else {
+		/* new header line */
+		line->continued = FALSE;
+	}
+
+	for (;;) {
+		ret = i_stream_read_data(ctx->input, &msg, &size, startpos+1);
+
+		if (ret != 0) {
+			/* we want to know one byte in advance to find out
+			   if it's multiline header */
+			parse_size = size-1;
+		} else {
+			parse_size = size;
+		}
+
+		if (ret <= 0 && (ret != 0 || startpos == size)) {
+			if (ret == -1) {
+				/* error / EOF with no bytes */
+				return NULL;
+			}
+
+			/* a) line is larger than input buffer
+			   b) header ended unexpectedly */
+			if (colon_pos == UINT_MAX) {
+				/* header name is huge. just skip it. */
+				message_skip_line(ctx->input, ctx->hdr_size,
+						  TRUE);
+				continue;
+			}
+
+			/* go back to last LWSP if found. */
+			for (i = size-1; i > colon_pos; i--) {
+				if (IS_LWSP(msg[i])) {
+					size = i;
+					break;
+				}
+			}
+
+			line->no_newline = TRUE;
+			line->continues = TRUE;
+			ctx->skip = size;
+			break;
+		}
+
+		/* find ':' */
+		if (colon_pos == UINT_MAX) {
+			for (i = startpos; i < parse_size; i++) {
+				if (msg[i] <= ':') {
+					if (msg[i] == ':') {
+						colon_pos = i;
+						break;
+					}
+					if (msg[i] == '\n') {
+						/* end of headers, or error */
+						break;
+					}
+				}
+			}
+		}
+
+		/* find '\n' */
+		for (i = startpos; i < parse_size; i++) {
+			if (msg[i] == '\n')
+				break;
+		}
+
+		if (i < parse_size) {
+			/* got a line */
+			line->continues = i+1 < size && IS_LWSP(msg[i+1]);
+
+			if (ctx->hdr_size != NULL)
+				ctx->hdr_size->lines++;
+			if (i == 0 || msg[i-1] != '\r') {
+				/* missing CR */
+				if (ctx->hdr_size != NULL)
+					ctx->hdr_size->virtual_size++;
+				size = i;
+			} else {
+				size = i-1;
+			}
+
+			ctx->skip = i+1;
+			break;
+		}
+
+		startpos = i;
+	}
+
+	if (size == 0 || (size == 1 && msg[0] == '\r')) {
+		/* end of headers */
+		line->eoh = TRUE;
+		line->name_len = line->value_len = 0;
+	} else if (line->continued) {
+		line->value = msg;
+		line->value_len = size;
+	} else if (colon_pos == UINT_MAX) {
+		/* missing ':', assume the whole line is name */
+		line->value = NULL;
+		line->value_len = 0;
+
+		str_truncate(ctx->name, 0);
+		str_append_n(ctx->name, msg, size);
+		line->name = str_c(ctx->name);
+		line->name_len = str_len(ctx->name);
+	} else {
+		/* get value, skip only first LWSP after ':' */
+		line->value = msg + colon_pos+1;
+		line->value_len = size - colon_pos - 1;
+		if (line->value_len > 0 &&
+		    IS_LWSP(line->value[0])) {
+			line->value++;
+			line->value_len--;
+		}
+
+		/* get name, skip LWSP before ':' */
+		while (colon_pos > 0 && IS_LWSP(msg[colon_pos-1]))
+			colon_pos--;
+
+		str_truncate(ctx->name, 0);
+		str_append_n(ctx->name, msg, colon_pos);
+		line->name = str_c(ctx->name);
+		line->name_len = str_len(ctx->name);
+	}
+
+	if (!line->continued) {
+		/* first header line, set full_value = value */
+		line->full_value = line->value;
+		line->full_value_len = line->value_len;
+	} else if (line->use_full_value) {
+		/* continue saving the full value */
+		buffer_append(ctx->value_buf, line->value, line->value_len);
+		line->full_value = buffer_get_data(ctx->value_buf,
+						   &line->full_value_len);
+	} else {
+		/* we didn't want full_value, and this is a continued line. */
+		line->full_value = NULL;
+		line->full_value_len = 0;
+	}
+
+	/* always reset it */
+	line->use_full_value = FALSE;
+
+	if (ctx->hdr_size != NULL) {
+		ctx->hdr_size->physical_size += ctx->skip;
+		ctx->hdr_size->virtual_size += ctx->skip;
+	}
+	return line;
+}
--- a/src/lib-mail/message-parser.h	Wed Mar 26 17:40:16 2003 +0200
+++ b/src/lib-mail/message-parser.h	Wed Mar 26 19:29:01 2003 +0200
@@ -31,27 +31,44 @@
 	void *context;
 };
 
-/* NOTE: name and value aren't \0-terminated. Also called once at end of
-   headers with name_len = value_len = 0. */
+struct message_header_parser_ctx;
+
+struct message_header_line {
+	const char *name;
+	size_t name_len;
+
+	const unsigned char *value;
+	size_t value_len;
+
+	const unsigned char *full_value;
+	size_t full_value_len;
+
+	unsigned int continues:1; /* multiline header, continues in next line */
+	unsigned int continued:1; /* multiline header, continues */
+	unsigned int eoh:1; /* "end of headers" line */
+	unsigned int no_newline:1; /* no \n after this line */
+	unsigned int use_full_value:1; /* set if you want full_value */
+};
+
+/* called once with hdr = NULL at end of headers */
 typedef void message_header_callback_t(struct message_part *part,
-				       const unsigned char *name,
-				       size_t name_len,
-				       const unsigned char *value,
-				       size_t value_len,
+				       struct message_header_line *hdr,
 				       void *context);
 
 /* callback is called for each field in message header. */
 struct message_part *message_parse(pool_t pool, struct istream *input,
 				   message_header_callback_t *callback,
 				   void *context);
-
-/* Call callback for each field in message header. Fills the hdr_size.
-   part can be NULL, just make sure your header function works with it.
-   This function doesn't use data stack so your header function may save
-   values to it. When finished, input will point to beginning of message
-   body. */
 void message_parse_header(struct message_part *part, struct istream *input,
 			  struct message_size *hdr_size,
 			  message_header_callback_t *callback, void *context);
 
+struct message_header_parser_ctx *
+message_parse_header_init(struct istream *input, struct message_size *hdr_size);
+void message_parse_header_deinit(struct message_header_parser_ctx *ctx);
+
+/* Read and return next header line. */
+struct message_header_line *
+message_parse_header_next(struct message_header_parser_ctx *ctx);
+
 #endif
--- a/src/lib-storage/index/index-mail.c	Wed Mar 26 17:40:16 2003 +0200
+++ b/src/lib-storage/index/index-mail.c	Wed Mar 26 19:29:01 2003 +0200
@@ -16,55 +16,6 @@
 
 #include <ctype.h>
 
-static int get_envelope_header_field(const char *name,
-				     enum imap_envelope_field *ret)
-{
-	*ret = (enum imap_envelope_field)-1;
-
-	switch (i_toupper(*name)) {
-	case 'B':
-		if (strcasecmp(name, "bcc") == 0)
-			*ret = IMAP_ENVELOPE_BCC;
-		break;
-	case 'C':
-		if (strcasecmp(name, "cc") == 0)
-			*ret = IMAP_ENVELOPE_CC;
-		break;
-	case 'D':
-		if (strcasecmp(name, "date") == 0)
-			*ret = IMAP_ENVELOPE_DATE;
-		break;
-	case 'F':
-		if (strcasecmp(name, "from") == 0)
-			*ret = IMAP_ENVELOPE_FROM;
-		break;
-	case 'I':
-		if (strcasecmp(name, "in-reply-to") == 0)
-			*ret = IMAP_ENVELOPE_IN_REPLY_TO;
-		break;
-	case 'M':
-		if (strcasecmp(name, "message-id") == 0)
-			*ret = IMAP_ENVELOPE_MESSAGE_ID;
-		break;
-	case 'R':
-		if (strcasecmp(name, "reply-to") == 0)
-			*ret = IMAP_ENVELOPE_REPLY_TO;
-		break;
-	case 'S':
-		if (strcasecmp(name, "subject") == 0)
-			*ret = IMAP_ENVELOPE_SUBJECT;
-		if (strcasecmp(name, "sender") == 0)
-			*ret = IMAP_ENVELOPE_SENDER;
-		break;
-	case 'T':
-		if (strcasecmp(name, "to") == 0)
-			*ret = IMAP_ENVELOPE_TO;
-		break;
-	}
-
-	return *ret != (enum imap_envelope_field)-1;
-}
-
 static struct message_part *get_cached_parts(struct index_mail *mail)
 {
 	struct message_part *part;
@@ -192,20 +143,17 @@
 }
 
 void index_mail_parse_header(struct message_part *part __attr_unused__,
-			     const unsigned char *name, size_t name_len,
-			     const unsigned char *value, size_t value_len,
-			     void *context)
+			     struct message_header_line *hdr, void *context)
 {
 	struct index_mail *mail = context;
 	struct index_mail_data *data = &mail->data;
-	struct cached_header *hdr;
+	struct cached_header *cached_hdr;
 
 	if (data->save_envelope) {
+		imap_envelope_parse_header(mail->pool,
+					   &data->envelope_data, hdr);
 
-		imap_envelope_parse_header(mail->pool, &data->envelope_data,
-					   name, name_len, value, value_len);
-
-		if (name_len == 0) {
+		if (hdr == NULL) {
 			/* finalize the envelope */
 			string_t *str;
 
@@ -215,9 +163,24 @@
 		}
 	}
 
-	if (name_len == 4 && data->save_sent_time &&
-	    memcasecmp(name, "date",4) == 0) {
-		if (!message_date_parse(value, value_len, &data->sent_time,
+	if (hdr == NULL) {
+		/* end of headers */
+		if (data->save_sent_time) {
+			/* not found */
+			data->sent_time = 0;
+			data->sent_timezone = 0;
+			data->save_sent_time = FALSE;
+		}
+		return;
+	}
+
+	if (data->save_sent_time && strcasecmp(hdr->name, "Date") == 0) {
+		if (hdr->continues) {
+			hdr->use_full_value = TRUE;
+			return;
+		}
+		if (!message_date_parse(hdr->full_value, hdr->full_value_len,
+					&data->sent_time,
 					&data->sent_timezone)) {
 			/* 0 == parse error */
 			data->sent_time = 0;
@@ -226,26 +189,25 @@
 		data->save_sent_time = FALSE;
 	}
 
-	if (name_len == 0) {
-		/* end of headers */
-		if (data->save_sent_time) {
-			/* not found */
-			data->sent_time = 0;
-			data->sent_timezone = 0;
-			data->save_sent_time = FALSE;
-		}
-	}
+        cached_hdr = data->headers;
+	while (cached_hdr != NULL) {
+		if (cached_hdr->name_len == hdr->name_len &&
+		    memcasecmp(hdr->name, hdr->name, hdr->name_len) == 0) {
+			/* save only the first header */
+			if (cached_hdr->value != NULL)
+				break;
 
-	for (hdr = data->headers; hdr != NULL; hdr = hdr->next) {
-		if (hdr->name_len == name_len &&
-		    memcasecmp(hdr->name, name, name_len) == 0) {
-			/* save only the first header */
-			if (hdr->value == NULL) {
-				hdr->value = p_strndup(mail->pool,
-						       value, value_len);
+			if (hdr->continues) {
+				hdr->use_full_value = TRUE;
+				break;
 			}
+
+			cached_hdr->value = p_strndup(mail->pool,
+						      hdr->full_value,
+						      hdr->full_value_len);
 			break;
 		}
+                cached_hdr = cached_hdr->next;
 	}
 }
 
@@ -479,7 +441,7 @@
 	}
 
 	if (data->parse_header || data->envelope == NULL ||
-	    !get_envelope_header_field(field, &env_field)) {
+	    !imap_envelope_get_field(field, &env_field)) {
 		/* if we have to parse the header, do it even if we could use
 		   envelope - envelope parsing would just slow up. */
                 prepend_cached_header(mail, field);
@@ -528,7 +490,7 @@
 	const char *ret = NULL;
 
 	if (data->envelope != NULL &&
-	    get_envelope_header_field(field, &env_field)) {
+	    imap_envelope_get_field(field, &env_field)) {
 		/* prefer parsing envelope - faster than having to actually
 		   parse the header field */
 		t_push();
@@ -748,7 +710,7 @@
 		int envelope_headers = FALSE;
 
 		for (tmp = mail->wanted_headers; *tmp != NULL; tmp++) {
-			if (get_envelope_header_field(*tmp, &env_field))
+			if (imap_envelope_get_field(*tmp, &env_field))
 				envelope_headers = TRUE;
 			else {
 				open_mail = TRUE;
--- a/src/lib-storage/index/index-mail.h	Wed Mar 26 17:40:16 2003 +0200
+++ b/src/lib-storage/index/index-mail.h	Wed Mar 26 19:29:01 2003 +0200
@@ -38,6 +38,7 @@
 
 	pool_t pool;
 	struct index_mailbox *ibox;
+	buffer_t *header_buf;
 
 	enum mail_fetch_field wanted_fields;
 	const char *const *wanted_headers;
@@ -51,8 +52,6 @@
 
 void index_mail_init_parse_header(struct index_mail *mail);
 void index_mail_parse_header(struct message_part *part,
-			     const unsigned char *name, size_t name_len,
-			     const unsigned char *value, size_t value_len,
-			     void *context);
+			     struct message_header_line *hdr, void *context);
 
 #endif
--- a/src/lib-storage/index/index-save.c	Wed Mar 26 17:40:16 2003 +0200
+++ b/src/lib-storage/index/index-save.c	Wed Mar 26 19:29:01 2003 +0200
@@ -9,19 +9,6 @@
 #include <stdlib.h>
 #include <unistd.h>
 
-struct save_header_context {
-	struct mail_storage *storage;
-	const char *path;
-
-	struct ostream *output;
-	write_func_t *write_func;
-
-	header_callback_t *header_callback;
-	void *context;
-
-	int failed;
-};
-
 static int write_with_crlf(struct ostream *output, const void *v_data,
 			   size_t size)
 {
@@ -103,45 +90,53 @@
 	}
 }
 
-static void save_header_callback(struct message_part *part __attr_unused__,
-				 const unsigned char *name, size_t name_len,
-				 const unsigned char *value, size_t value_len,
-				 void *context)
+static int save_headers(struct istream *input, struct ostream *output,
+			header_callback_t *header_callback, void *context,
+			write_func_t *write_func)
 {
-	struct save_header_context *ctx = context;
-	int ret;
+	struct message_header_parser_ctx *hdr_ctx;
+	struct message_header_line *hdr;
+	int ret, failed = FALSE;
 
-	if (ctx->failed)
-		return;
+	hdr_ctx = message_parse_header_init(input, NULL);
+	while ((hdr = message_parse_header_next(hdr_ctx)) != NULL) {
+		ret = header_callback(hdr->name, write_func, context);
+		if (ret <= 0) {
+			if (ret < 0) {
+				failed = TRUE;
+				break;
+			}
+			continue;
+		}
 
-	ret = ctx->header_callback(name, name_len, ctx->write_func,
-				   ctx->context);
-	if (ret <= 0) {
-		if (ret < 0)
-			ctx->failed = TRUE;
-		return;
+		if (!hdr->eoh) {
+			if (!hdr->continued) {
+				(void)o_stream_send(output, hdr->name,
+						    hdr->name_len);
+				(void)o_stream_send(output, ": ", 2);
+			}
+			(void)o_stream_send(output, hdr->value, hdr->value_len);
+			if (!hdr->no_newline)
+				write_func(output, "\n", 1);
+		}
 	}
+	if (!failed) {
+		if (header_callback(NULL, write_func, context) < 0)
+			failed = TRUE;
 
-	if (name_len == 0) {
-		name = "\n"; value_len = 1;
-	} else {
-		if (value[value_len] == '\r')
-			value_len++;
-		i_assert(value[value_len] == '\n');
-		value_len += (size_t) (value-name) + 1;
+		/* end of headers */
+		write_func(output, "\n", 1);
 	}
+	message_parse_header_deinit(hdr_ctx);
 
-	if (ctx->write_func(ctx->output, name, value_len) < 0) {
-		set_write_error(ctx->storage, ctx->output, ctx->path);
-		ctx->failed = TRUE;
-	}
+	return !failed;
 }
 
 int index_storage_save(struct mail_storage *storage, const char *path,
 		       struct istream *input, struct ostream *output,
 		       header_callback_t *header_callback, void *context)
 {
-	int (*write_func)(struct ostream *, const void *, size_t);
+        write_func_t *write_func;
 	const unsigned char *data;
 	size_t size;
 	ssize_t ret;
@@ -150,20 +145,8 @@
 	write_func = getenv("MAIL_SAVE_CRLF") ? write_with_crlf : write_with_lf;
 
 	if (header_callback != NULL) {
-		struct save_header_context ctx;
-
-		memset(&ctx, 0, sizeof(ctx));
-		ctx.storage = storage;
-		ctx.output = output;
-		ctx.path = path;
-		ctx.write_func = write_func;
-		ctx.header_callback = header_callback;
-		ctx.context = context;
-
-		message_parse_header(NULL, input, NULL,
-				     save_header_callback, &ctx);
-
-		if (ctx.failed)
+		if (!save_headers(input, output, header_callback,
+				  context, write_func))
 			return FALSE;
 	}
 
--- a/src/lib-storage/index/index-search.c	Wed Mar 26 17:40:16 2003 +0200
+++ b/src/lib-storage/index/index-search.c	Wed Mar 26 19:29:01 2003 +0200
@@ -41,8 +41,7 @@
         struct mail_search_context *index_context;
 	struct mail_search_arg *args;
 
-	const unsigned char *name, *value;
-	size_t name_len, value_len;
+        struct message_header_line *hdr;
 
 	unsigned int custom_header:1;
 	unsigned int threading:1;
@@ -283,7 +282,7 @@
 }
 
 static int search_sent(enum mail_search_arg_type type, const char *search_value,
-		       const char *sent_value)
+		       const unsigned char *sent_value, size_t sent_value_len)
 {
 	time_t search_time, sent_time;
 	int timezone_offset;
@@ -296,7 +295,7 @@
 
 	/* NOTE: Latest IMAP4rev1 draft specifies that timezone is ignored
 	   in searches. sent_time is returned as UTC, so change it. */
-	if (!message_date_parse((const unsigned char *) sent_value, (size_t)-1,
+	if (!message_date_parse(sent_value, sent_value_len,
 				&sent_time, &timezone_offset))
 		return 0;
 	sent_time -= timezone_offset * 60;
@@ -404,7 +403,9 @@
 		case SEARCH_SENTBEFORE:
 		case SEARCH_SENTON:
 		case SEARCH_SENTSINCE:
-			ret = search_sent(arg->type, arg->value.str, field);
+			ret = search_sent(arg->type, arg->value.str,
+					  (const unsigned char *) field,
+					  (size_t)-1);
 			break;
 		default:
 			if (arg->value.str[0] == '\0') {
@@ -457,7 +458,6 @@
 {
 	struct search_header_context *ctx = context;
         struct header_search_context *hdr_search_ctx;
-	size_t len;
 	int ret;
 
 	/* first check that the field name matches to argument. */
@@ -466,41 +466,50 @@
 	case SEARCH_SENTON:
 	case SEARCH_SENTSINCE:
 		/* date is handled differently than others */
-		if (ctx->name_len == 4 &&
-		    memcasecmp(ctx->name, "Date", 4) == 0) {
+		if (strcasecmp(ctx->hdr->name, "Date") == 0) {
+			if (ctx->hdr->continues) {
+				ctx->hdr->use_full_value = TRUE;
+				return;
+			}
 			ret = search_sent(arg->type, arg->value.str,
-				t_strndup(ctx->value, ctx->value_len));
+					  ctx->hdr->full_value,
+					  ctx->hdr->full_value_len);
 			ARG_SET_RESULT(arg, ret);
 		}
 		return;
 
 	case SEARCH_FROM:
-		if (ctx->name_len != 4 || memcasecmp(ctx->name, "From", 4) != 0)
+		if (strcasecmp(ctx->hdr->name, "From") != 0)
 			return;
 		break;
 	case SEARCH_TO:
-		if (ctx->name_len != 2 || memcasecmp(ctx->name, "To", 2) != 0)
+		if (strcasecmp(ctx->hdr->name, "To") != 0)
 			return;
 		break;
 	case SEARCH_CC:
-		if (ctx->name_len != 2 || memcasecmp(ctx->name, "Cc", 2) != 0)
+		if (strcasecmp(ctx->hdr->name, "Cc") != 0)
 			return;
 		break;
 	case SEARCH_BCC:
-		if (ctx->name_len != 3 || memcasecmp(ctx->name, "Bcc", 3) != 0)
+		if (strcasecmp(ctx->hdr->name, "Bcc") != 0)
 			return;
 		break;
 	case SEARCH_SUBJECT:
-		if (ctx->name_len != 7 ||
-		    memcasecmp(ctx->name, "Subject", 7) != 0)
+		if (strcasecmp(ctx->hdr->name, "Subject") != 0)
+			return;
+		break;
+	case SEARCH_IN_REPLY_TO:
+		if (strcasecmp(ctx->hdr->name, "In-Reply-To") != 0)
+			return;
+		break;
+	case SEARCH_MESSAGE_ID:
+		if (strcasecmp(ctx->hdr->name, "Message-ID") != 0)
 			return;
 		break;
 	case SEARCH_HEADER:
 		ctx->custom_header = TRUE;
 
-		len = strlen(arg->hdr_field_name);
-		if (ctx->name_len != len ||
-		    memcasecmp(ctx->name, arg->hdr_field_name, len) != 0)
+		if (strcasecmp(ctx->hdr->name, arg->hdr_field_name) != 0)
 			return;
 	case SEARCH_TEXT:
 		/* TEXT goes through all headers */
@@ -514,6 +523,11 @@
 		/* we're just testing existence of the field. always matches. */
 		ret = 1;
 	} else {
+		if (ctx->hdr->continues) {
+			ctx->hdr->use_full_value = TRUE;
+			return;
+		}
+
 		t_push();
 
 		hdr_search_ctx = search_header_context(ctx->index_context, arg);
@@ -526,14 +540,16 @@
 			string_t *str;
 
 			addr = message_address_parse(data_stack_pool,
-						     ctx->value, ctx->value_len,
+						     ctx->hdr->full_value,
+						     ctx->hdr->full_value_len,
 						     0);
-			str = t_str_new(ctx->value_len);
+			str = t_str_new(ctx->hdr->value_len);
 			message_address_write(str, addr);
 			ret = message_header_search(str_data(str), str_len(str),
 						    hdr_search_ctx) ? 1 : 0;
 		} else {
-			ret = message_header_search(ctx->value, ctx->value_len,
+			ret = message_header_search(ctx->hdr->full_value,
+						    ctx->hdr->full_value_len,
 						    hdr_search_ctx) ? 1 : 0;
 		}
 		t_pop();
@@ -565,6 +581,8 @@
 	case SEARCH_BCC:
 	case SEARCH_SUBJECT:
 	case SEARCH_HEADER:
+	case SEARCH_IN_REPLY_TO:
+	case SEARCH_MESSAGE_ID:
 		ARG_SET_RESULT(arg, 0);
 		break;
 	default:
@@ -573,32 +591,34 @@
 }
 
 static void search_header(struct message_part *part,
-			  const unsigned char *name, size_t name_len,
-			  const unsigned char *value, size_t value_len,
-			  void *context)
+                          struct message_header_line *hdr, void *context)
 {
 	struct search_header_context *ctx = context;
 
-	index_mail_parse_header(part, name, name_len, value, value_len,
-				ctx->index_context->mail);
+	if (hdr == NULL) {
+		/* end of headers, mark all unknown SEARCH_HEADERs unmatched */
+		mail_search_args_foreach(ctx->args, search_header_unmatch, ctx);
+		return;
+	}
+
+	if (hdr->eoh)
+		return;
+
+	index_mail_parse_header(part, hdr, ctx->index_context->mail);
 
-	if ((ctx->custom_header && name_len > 0) ||
-	    (name_len == 4 && memcasecmp(name, "Date", 4) == 0) ||
-	    (name_len == 4 && memcasecmp(name, "From", 4) == 0) ||
-	    (name_len == 2 && memcasecmp(name, "To", 2) == 0) ||
-	    (name_len == 2 && memcasecmp(name, "Cc", 2) == 0) ||
-	    (name_len == 3 && memcasecmp(name, "Bcc", 3) == 0) ||
-	    (name_len == 7 && memcasecmp(name, "Subject", 7) == 0)) {
-		ctx->name = name;
-		ctx->value = value;
-		ctx->name_len = name_len;
-		ctx->value_len = value_len;
+	if (ctx->custom_header ||
+	    strcasecmp(hdr->name, "Date") == 0 ||
+	    strcasecmp(hdr->name, "From") == 0 ||
+	    strcasecmp(hdr->name, "To") == 0 ||
+	    strcasecmp(hdr->name, "Cc") == 0 ||
+	    strcasecmp(hdr->name, "Bcc") == 0 ||
+	    strcasecmp(hdr->name, "Subject") == 0 ||
+	    strcasecmp(hdr->name, "In-Reply-To") == 0 ||
+	    strcasecmp(hdr->name, "Message-ID") == 0) {
+		ctx->hdr = hdr;
 
 		ctx->custom_header = FALSE;
 		mail_search_args_foreach(ctx->args, search_header_arg, ctx);
-	} else if (name_len == 0) {
-		/* last header, mark all unknown SEARCH_HEADERs unmatched */
-		mail_search_args_foreach(ctx->args, search_header_unmatch, ctx);
 	}
 }
 
--- a/src/lib-storage/index/index-storage.h	Wed Mar 26 17:40:16 2003 +0200
+++ b/src/lib-storage/index/index-storage.h	Wed Mar 26 19:29:01 2003 +0200
@@ -8,7 +8,7 @@
 typedef int write_func_t(struct ostream *, const void *, size_t);
 
 /* Return -1 = failure, 0 = don't write the header, 1 = write it */
-typedef int header_callback_t(const unsigned char *name, size_t len,
+typedef int header_callback_t(const char *name,
 			      write_func_t *write_func, void *context);
 
 struct index_autosync_file {
--- a/src/lib-storage/index/mbox/mbox-save.c	Wed Mar 26 17:40:16 2003 +0200
+++ b/src/lib-storage/index/mbox/mbox-save.c	Wed Mar 26 19:29:01 2003 +0200
@@ -169,8 +169,8 @@
 	return str_c(str);
 }
 
-static int save_header_callback(const unsigned char *name, size_t len,
-				write_func_t *write_func, void *context)
+static int save_header_callback(const char *name, write_func_t *write_func,
+				void *context)
 {
 	static const char *content_length = "Content-Length: ";
 	struct mail_save_context *ctx = context;
@@ -178,8 +178,7 @@
 	char *buf;
 	size_t space;
 
-	switch (len) {
-	case 0:
+	if (name == NULL) {
 		/* write system flags */
 		str = get_system_flags(ctx->flags->flags);
 		if (write_func(ctx->output, str, strlen(str)) < 0)
@@ -209,27 +208,29 @@
 			return -1;
 		}
 		ctx->eoh_offset = ctx->output->offset;
-		break;
-	case 5:
-		if (memcasecmp(name, "X-UID", 5) == 0)
-			return 0;
-		break;
-	case 6:
-		if (memcasecmp(name, "Status", 6) == 0)
+		return 1;
+	}
+
+	switch (*name) {
+	case 'C':
+	case 'c':
+		if (strcasecmp(name, "Content-Length") == 0)
 			return 0;
 		break;
-	case 8:
-		if (memcasecmp(name, "X-Status", 8) == 0)
+	case 'S':
+	case 's':
+		if (strcasecmp(name, "Status") == 0)
 			return 0;
 		break;
-	case 10:
-		if (memcasecmp(name, "X-Keywords", 10) == 0)
+	case 'X':
+	case 'x':
+		if (strcasecmp(name, "X-UID") == 0)
 			return 0;
-		if (memcasecmp(name, "X-IMAPbase", 10) == 0)
+		if (strcasecmp(name, "X-Status") == 0)
 			return 0;
-		break;
-	case 14:
-		if (memcasecmp(name, "Content-Length", 14) == 0)
+		if (strcasecmp(name, "X-Keywords") == 0)
+			return 0;
+		if (strcasecmp(name, "X-IMAPbase") == 0)
 			return 0;
 		break;
 	}