changeset 2298:5beb0c20b6e8 HEAD

Cache file fixes, API changes, etc. It's still in somewhat ugly state, but getting better..
author Timo Sirainen <tss@iki.fi>
date Thu, 08 Jul 2004 23:26:15 +0300
parents 59062bb4eaac
children ef0cd18d674c
files src/lib-imap/imap-bodystructure.c src/lib-imap/imap-bodystructure.h src/lib-index/mail-cache-compress.c src/lib-index/mail-cache-decisions.c src/lib-index/mail-cache-lookup.c src/lib-index/mail-cache-private.h src/lib-index/mail-cache-transaction.c src/lib-index/mail-cache.c src/lib-index/mail-cache.h src/lib-storage/index/index-fetch.c src/lib-storage/index/index-mail-headers.c src/lib-storage/index/index-mail.c src/lib-storage/index/index-mail.h src/lib-storage/index/index-search.c src/lib-storage/index/index-storage.c src/lib-storage/index/maildir/maildir-mail.c src/lib-storage/index/maildir/maildir-save.c src/lib-storage/index/mbox/mbox-mail.c src/lib-storage/index/mbox/mbox-save.c
diffstat 19 files changed, 595 insertions(+), 675 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-imap/imap-bodystructure.c	Thu Jul 08 23:24:48 2004 +0300
+++ b/src/lib-imap/imap-bodystructure.c	Thu Jul 08 23:26:15 2004 +0300
@@ -36,9 +36,6 @@
 	unsigned int charset_found:1;
 };
 
-static void part_write_bodystructure(struct message_part *part,
-				     string_t *str, int extended);
-
 static void parse_content_type(const unsigned char *value, size_t value_len,
 			       void *context)
 {
@@ -294,7 +291,7 @@
 	}
 
 	if (part->children != NULL)
-		part_write_bodystructure(part->children, str, extended);
+		imap_bodystructure_write(part->children, str, extended);
 	else {
 		/* no parts in multipart message,
 		   that's not allowed. write a single
@@ -416,7 +413,7 @@
 		imap_envelope_write_part_data(env_data, str);
 		str_append(str, ") ");
 
-		part_write_bodystructure(part->children, str, extended);
+		imap_bodystructure_write(part->children, str, extended);
 		str_printfa(str, " %u", part->body_size.lines);
 	}
 
@@ -459,37 +456,27 @@
 	}
 }
 
-static void part_write_bodystructure(struct message_part *part,
-				     string_t *str, int extended)
+void imap_bodystructure_write(struct message_part *part,
+			      string_t *dest, int extended)
 {
 	i_assert(part->parent != NULL || part->next == NULL);
 
 	while (part != NULL) {
 		if (part->parent != NULL)
-			str_append_c(str, '(');
+			str_append_c(dest, '(');
 
 		if (part->flags & MESSAGE_PART_FLAG_MULTIPART)
-			part_write_body_multipart(part, str, extended);
+			part_write_body_multipart(part, dest, extended);
 		else
-			part_write_body(part, str, extended);
+			part_write_body(part, dest, extended);
 
 		if (part->parent != NULL)
-			str_append_c(str, ')');
+			str_append_c(dest, ')');
 
 		part = part->next;
 	}
 }
 
-const char *imap_bodystructure_parse_finish(struct message_part *root,
-					    int extended)
-{
-	string_t *str;
-
-	str = t_str_new(2048);
-	part_write_bodystructure(root, str, extended);
-	return str_c(str);
-}
-
 static int str_append_imap_arg(string_t *str, const struct imap_arg *arg)
 {
 	switch (arg->type) {
@@ -645,35 +632,27 @@
 	return TRUE;
 }
 
-const char *imap_body_parse_from_bodystructure(const char *bodystructure)
+int imap_body_parse_from_bodystructure(const char *bodystructure,
+				       string_t *dest)
 {
 	struct istream *input;
 	struct imap_parser *parser;
 	struct imap_arg *args;
-	string_t *str;
-	const char *value;
-	size_t len;
 	int ret;
 
-	len = strlen(bodystructure);
-	str = t_str_new(len);
-
 	input = i_stream_create_from_data(pool_datastack_create(),
-					  bodystructure, len);
+					  bodystructure, strlen(bodystructure));
 	(void)i_stream_read(input);
 
 	parser = imap_parser_create(input, NULL, (size_t)-1);
 	ret = imap_parser_finish_line(parser, 0, IMAP_PARSE_FLAG_NO_UNESCAPE |
 				      IMAP_PARSE_FLAG_LITERAL_TYPE, &args);
-	if (ret <= 0 || !imap_parse_bodystructure_args(args, str))
-		value = NULL;
-	else
-		value = str_c(str);
+	ret = ret > 0 && imap_parse_bodystructure_args(args, dest);
 
-	if (value == NULL)
+	if (!ret)
 		i_error("Error parsing IMAP bodystructure: %s", bodystructure);
 
 	imap_parser_destroy(parser);
 	i_stream_unref(input);
-	return value;
+	return ret;
 }
--- a/src/lib-imap/imap-bodystructure.h	Thu Jul 08 23:24:48 2004 +0300
+++ b/src/lib-imap/imap-bodystructure.h	Thu Jul 08 23:26:15 2004 +0300
@@ -8,10 +8,11 @@
 void imap_bodystructure_parse_header(pool_t pool, struct message_part *part,
 				     struct message_header_line *hdr);
 
-const char *imap_bodystructure_parse_finish(struct message_part *root,
-					    int extended);
+void imap_bodystructure_write(struct message_part *part,
+			      string_t *dest, int extended);
 
 /* Return BODY part from BODYSTRUCTURE */
-const char *imap_body_parse_from_bodystructure(const char *bodystructure);
+int imap_body_parse_from_bodystructure(const char *bodystructure,
+				       string_t *dest);
 
 #endif
--- a/src/lib-index/mail-cache-compress.c	Thu Jul 08 23:24:48 2004 +0300
+++ b/src/lib-index/mail-cache-compress.c	Thu Jul 08 23:26:15 2004 +0300
@@ -8,98 +8,76 @@
 
 static unsigned char null4[4] = { 0, 0, 0, 0 };
 
-static const struct mail_cache_record *
-mail_cache_compress_record(struct mail_cache_view *view, uint32_t seq,
-			   enum mail_cache_field orig_cached_fields,
-			   int header_idx, uint32_t *size_r)
+struct mail_cache_copy_context {
+	int new_msg;
+	char field_seen[32], keep_fields[32], temp_fields[32];
+	buffer_t *buffer, *header;
+};
+
+static int
+mail_cache_compress_callback(struct mail_cache_view *view __attr_unused__,
+			     enum mail_cache_field field,
+			     const void *data, size_t data_size, void *context)
 {
-	enum mail_cache_field cached_fields, field;
-	struct mail_cache_record cache_rec;
-	buffer_t *buffer;
-	pool_t pool;
-	const void *data;
-	size_t size, pos;
-	uint32_t *p, size32 = 0;
+        struct mail_cache_copy_context *ctx = context;
+	uint32_t size32;
 	int i;
 
-	memset(&cache_rec, 0, sizeof(cache_rec));
-	pool = pool_datastack_create();
-	buffer = buffer_create_dynamic(pool, 4096, (size_t)-1);
-
-	cached_fields = orig_cached_fields & ~MAIL_CACHE_HEADERS_MASK;
-	buffer_append(buffer, &cache_rec, sizeof(cache_rec));
-	for (i = 0, field = 1; i < 31; i++, field <<= 1) {
-		if ((cached_fields & field) == 0)
-			continue;
-
-		pos = buffer_get_used_size(buffer);
-		if ((field & MAIL_CACHE_FIXED_MASK) == 0)
-			buffer_append(buffer, &size32, sizeof(size32));
-
-		if (!mail_cache_lookup_field(view, buffer, seq, field)) {
-			cached_fields &= ~field;
-			buffer_set_used_size(buffer, pos);
-			continue;
-		}
-
-		if ((field & MAIL_CACHE_FIXED_MASK) == 0) {
-			p = buffer_get_space_unsafe(buffer, pos,
-						    sizeof(uint32_t));
-			*p = (uint32_t)size;
-		}
-
-		if ((size & 3) != 0)
-			buffer_append(buffer, null4, 4 - (size & 3));
+	if (ctx->new_msg) {
+		if (!ctx->temp_fields[field])
+			return 1;
+	} else {
+		if (!ctx->keep_fields[field])
+			return 1;
 	}
 
-	/* now merge all the headers if we have them all */
-	if ((orig_cached_fields & mail_cache_header_fields[header_idx]) != 0) {
-		size32 = 0;
-		pos = buffer_get_used_size(buffer);
-		buffer_append(buffer, &size32, sizeof(size32));
+	if (ctx->field_seen[field]) {
+		/* drop duplicates */
+		return 1;
+	}
+	ctx->field_seen[field] = TRUE;
 
-		for (i = 0; i <= header_idx; i++) {
-			field = mail_cache_header_fields[i];
-			if (mail_cache_lookup_field(view, buffer, seq, field)) {
-				/* remove terminating \0 */
-				buffer_set_used_size(buffer,
-					buffer_get_used_size(buffer)-1);
+	for (i = 0; i < MAIL_CACHE_HEADERS_COUNT; i++) {
+		if (mail_cache_header_fields[i] == field) {
+			/* it's header - save it into header field */
+			size32 = buffer_get_used_size(ctx->header);
+			if (size32 > 0) {
+				/* remove old terminating \0 */
+				buffer_set_used_size(ctx->header, size32-1);
 			}
+			buffer_append(ctx->header, data, data_size);
+			return 1;
 		}
-		buffer_append(buffer, null4, 1);
-
-		size32 = buffer_get_used_size(buffer) - pos;
-		if ((size32 & 3) != 0)
-			buffer_append(buffer, null4, 4 - (size32 & 3));
-		buffer_write(buffer, pos, &size32, sizeof(size32));
-
-		cached_fields |= MAIL_CACHE_HEADERS1;
 	}
 
-	cache_rec.fields = cached_fields;
-	cache_rec.size = buffer_get_used_size(buffer);
-	buffer_write(buffer, 0, &cache_rec, sizeof(cache_rec));
+	buffer_append(ctx->buffer, &field, sizeof(field));
 
-	data = buffer_get_data(buffer, &size);
-	*size_r = size;
-	return data;
+	if (mail_cache_field_sizes[field] == (unsigned int)-1) {
+		size32 = (uint32_t)data_size;
+		buffer_append(ctx->buffer, &size32, sizeof(size32));
+	}
+
+	buffer_append(ctx->buffer, data, data_size);
+	if ((data_size & 3) != 0)
+		buffer_append(ctx->buffer, null4, 4 - (data_size & 3));
+	return 1;
 }
 
 static int
 mail_cache_copy(struct mail_cache *cache, struct mail_index_view *view, int fd)
 {
+        struct mail_cache_copy_context ctx;
 	struct mail_cache_view *cache_view;
 	struct mail_index_transaction *t;
 	const struct mail_index_header *idx_hdr;
-	const struct mail_cache_record *cache_rec;
 	struct mail_cache_header hdr;
+	struct mail_cache_record cache_rec;
+        enum mail_cache_field field;
 	struct ostream *output;
-	enum mail_cache_field keep_fields, temp_fields;
-	enum mail_cache_field cached_fields, new_fields;
 	const char *str;
-	uint32_t size, size32, message_count, seq, first_new_seq;
+	uint32_t size32, message_count, seq, first_new_seq, old_offset;
 	uoff_t offset;
-	int i, header_idx, ret;
+	int i, ret, header_idx;
 
 	/* get sequence of first message which doesn't need it's temp fields
 	   removed. */
@@ -133,15 +111,23 @@
 		memcpy(hdr.field_usage_last_used,
 		       cache->hdr->field_usage_last_used,
 		       sizeof(hdr.field_usage_last_used));
+	} else {
+		memcpy(hdr.field_usage_decision_type,
+		       cache->default_field_usage_decision_type,
+		       sizeof(hdr.field_usage_decision_type));
+	}
 
-		keep_fields = temp_fields = 0;
-		for (i = 0; i < 32; i++) {
-			if (cache->hdr->field_usage_decision_type[i] &
-			    MAIL_CACHE_DECISION_YES)
-				keep_fields |= 1 << i;
-			else if (cache->hdr->field_usage_decision_type[i] &
-				 MAIL_CACHE_DECISION_TEMP)
-				temp_fields |= 1 << i;
+	memset(&ctx, 0, sizeof(ctx));
+	ctx.buffer = buffer_create_dynamic(default_pool, 4096, (size_t)-1);
+	ctx.header = buffer_create_dynamic(default_pool, 4096, (size_t)-1);
+
+	for (i = 0; i < 32; i++) {
+		if (hdr.field_usage_decision_type[i] & MAIL_CACHE_DECISION_YES)
+			ctx.keep_fields[i] = TRUE;
+		else if (hdr.field_usage_decision_type[i] &
+			 MAIL_CACHE_DECISION_TEMP) {
+			ctx.temp_fields[i] = TRUE;
+			ctx.keep_fields[i] = TRUE;
 		}
 	}
 
@@ -173,45 +159,42 @@
 
 	ret = 0;
 	for (seq = 1; seq <= message_count; seq++) {
-		cache_rec = mail_cache_lookup(cache_view, seq);
-		if (cache_rec == NULL)
-			continue;
+		ctx.new_msg = seq >= first_new_seq;
+		buffer_set_used_size(ctx.buffer, 0);
+		buffer_set_used_size(ctx.header, 0);
+		memset(ctx.field_seen, 0, sizeof(ctx.field_seen));
+
+		memset(&cache_rec, 0, sizeof(cache_rec));
+		buffer_append(ctx.buffer, &cache_rec, sizeof(cache_rec));
+
+		mail_cache_foreach(cache_view, seq,
+				   mail_cache_compress_callback, &ctx);
 
-		cached_fields = mail_cache_get_fields(cache_view, seq);
-                new_fields = cached_fields & keep_fields;
-		if ((cached_fields & temp_fields) != 0 &&
-		    seq >= first_new_seq) {
-			/* new message, keep temp fields */
-			new_fields |= cached_fields & temp_fields;
+		size32 = buffer_get_used_size(ctx.header);
+		if (size32 > 0 && ctx.field_seen[header_idx]) {
+			field = MAIL_CACHE_HEADERS1;
+			buffer_append(ctx.buffer, &field, sizeof(field));
+			buffer_append(ctx.buffer, &size32, sizeof(size32));
+			buffer_append(ctx.buffer,
+				      buffer_get_data(ctx.header, NULL),
+				      size32);
+			if ((size32 & 3) != 0) {
+				buffer_append(ctx.buffer, null4,
+					      4 - (size32 & 3));
+			}
 		}
 
-		if (keep_fields == cached_fields &&
-		    cache_rec->prev_offset == 0) {
-			/* just one unmodified block, save it */
-			mail_index_update_cache(t, seq, hdr.file_seq,
-						output->offset, NULL);
-			o_stream_send(output, cache_rec, cache_rec->size);
+		if (buffer_get_used_size(ctx.buffer) == sizeof(cache_rec))
+			continue;
 
-			if ((cache_rec->size & 3) != 0) {
-				o_stream_send(output, null4,
-					      4 - (cache_rec->size & 3));
-			}
-		} else {
-			/* a) dropping fields
-			   b) multiple blocks, sort them into buffer */
-			mail_index_update_cache(t, seq, hdr.file_seq,
-						output->offset, NULL);
-
-			t_push();
-			cache_rec = mail_cache_compress_record(cache_view, seq,
-							       keep_fields,
-							       header_idx,
-							       &size);
-			o_stream_send(output, cache_rec, size);
-			t_pop();
-		}
+		mail_index_update_cache(t, seq, hdr.file_seq,
+					output->offset, &old_offset);
+		o_stream_send(output, buffer_get_data(ctx.buffer, NULL),
+			      buffer_get_used_size(ctx.buffer));
 	}
 	hdr.used_file_size = output->offset;
+	buffer_free(ctx.buffer);
+	buffer_free(ctx.header);
 
 	o_stream_seek(output, 0);
 	o_stream_send(output, &hdr, sizeof(hdr));
@@ -263,7 +246,7 @@
 		return -1;
 	}
 
-	// FIXME: check that cache file was just recreated
+	// FIXME: check that cache file wasn't just recreated
 
 	ret = 0;
 	if (mail_cache_copy(cache, view, fd) < 0) {
--- a/src/lib-index/mail-cache-decisions.c	Thu Jul 08 23:24:48 2004 +0300
+++ b/src/lib-index/mail-cache-decisions.c	Thu Jul 08 23:26:15 2004 +0300
@@ -73,7 +73,8 @@
 #include <stddef.h>
 
 static void
-mail_cache_set_decision_type(struct mail_cache *cache, uint32_t idx,
+mail_cache_set_decision_type(struct mail_cache *cache,
+			     enum mail_cache_field field,
 			     enum mail_cache_decision_type type)
 {
 	uint8_t value = type;
@@ -84,20 +85,18 @@
 	   will be corrected sometimes later, not too bad.. */
 	if (pwrite_full(cache->fd, &value, 1,
 			offsetof(struct mail_cache_header,
-				 field_usage_decision_type) + idx) < 0) {
+				 field_usage_decision_type) + field) < 0) {
 		mail_cache_set_syscall_error(cache, "pwrite_full()");
 	}
 }
 
-void mail_cache_handle_decisions(struct mail_cache_view *view, uint32_t seq,
-				 enum mail_cache_field field)
+void mail_cache_decision_lookup(struct mail_cache_view *view, uint32_t seq,
+				enum mail_cache_field field)
 {
 	const struct mail_index_header *hdr;
-	unsigned int idx;
 	uint32_t uid;
 
-	idx = mail_cache_field_index(field);
-	if (view->cache->hdr->field_usage_decision_type[idx] !=
+	if (view->cache->hdr->field_usage_decision_type[field] !=
 	    MAIL_CACHE_DECISION_TEMP) {
 		/* a) forced decision
 		   b) not cached, mail_cache_mark_missing() will handle this
@@ -110,7 +109,7 @@
 	    mail_index_get_header(view->view, &hdr) < 0)
 		return;
 
-	if (uid < view->cache->field_usage_uid_highwater[idx] ||
+	if (uid < view->cache->field_usage_uid_highwater[field] ||
 	    uid < hdr->day_first_uid[7]) {
 		/* a) nonordered access within this session. if client doesn't
 		      request messages in growing order, we assume it doesn't
@@ -119,24 +118,22 @@
 		      client with no local cache. if it was just a new client
 		      generating the local cache for the first time, we'll
 		      drop back to TEMP within few months. */
-		mail_cache_set_decision_type(view->cache, idx,
+		mail_cache_set_decision_type(view->cache, field,
 					     MAIL_CACHE_DECISION_YES);
 	} else {
-		view->cache->field_usage_uid_highwater[idx] = uid;
+		view->cache->field_usage_uid_highwater[field] = uid;
 	}
 }
 
-void mail_cache_mark_missing(struct mail_cache_view *view, uint32_t seq,
+void mail_cache_decision_add(struct mail_cache_view *view, uint32_t seq,
 			     enum mail_cache_field field)
 {
-	unsigned int idx;
 	uint32_t uid;
 
 	if (MAIL_CACHE_IS_UNUSABLE(view->cache))
 		return;
 
-	idx = mail_cache_field_index(field);
-	if (view->cache->hdr->field_usage_decision_type[idx] !=
+	if (view->cache->hdr->field_usage_decision_type[field] !=
 	    MAIL_CACHE_DECISION_NO) {
 		/* a) forced decision
 		   b) we're already caching it, so it just wasn't in cache */
@@ -144,9 +141,9 @@
 	}
 
 	/* field used the first time */
-	mail_cache_set_decision_type(view->cache, idx,
+	mail_cache_set_decision_type(view->cache, field,
 				     MAIL_CACHE_DECISION_TEMP);
 
 	if (mail_index_lookup_uid(view->view, seq, &uid) == 0)
-		view->cache->field_usage_uid_highwater[idx] = uid;
+		view->cache->field_usage_uid_highwater[field] = uid;
 }
--- a/src/lib-index/mail-cache-lookup.c	Thu Jul 08 23:24:48 2004 +0300
+++ b/src/lib-index/mail-cache-lookup.c	Thu Jul 08 23:26:15 2004 +0300
@@ -171,112 +171,163 @@
 	return 0;
 }
 
-struct mail_cache_record *
-mail_cache_lookup(struct mail_cache_view *view, uint32_t seq)
+int mail_cache_foreach(struct mail_cache_view *view, uint32_t seq,
+		       int (*callback)(struct mail_cache_view *view,
+				       enum mail_cache_field field,
+				       const void *data, size_t data_size,
+				       void *context), void *context)
 {
-	uint32_t offset;
-
-	// FIXME: check transactions too
+	const struct mail_cache_record *cache_rec;
+	size_t pos, next_pos, max_size, data_size;
+	uint32_t offset, field;
+	int ret;
 
         if (MAIL_CACHE_IS_UNUSABLE(view->cache))
-		return NULL;
+		return 0;
+
+	if ((ret = mail_cache_lookup_offset(view, seq, &offset)) <= 0)
+		return ret;
 
-	if (mail_cache_lookup_offset(view, seq, &offset) <= 0)
-		return NULL;
+	cache_rec = mail_cache_get_record(view->cache, offset);
+	while (cache_rec != NULL) {
+		max_size = cache_rec->size;
+		if (max_size < sizeof(*cache_rec) + sizeof(uint32_t)*2) {
+			mail_cache_set_corrupted(view->cache,
+				"record has invalid size");
+			return -1;
+		}
+		max_size -= sizeof(uint32_t);
 
-	return mail_cache_get_record(view->cache, offset);
-}
+		for (pos = sizeof(*cache_rec); pos < max_size; ) {
+			field = *((const uint32_t *)
+				  CONST_PTR_OFFSET(cache_rec, pos));
+			pos += sizeof(uint32_t);
 
-enum mail_cache_field
-mail_cache_get_fields(struct mail_cache_view *view, uint32_t seq)
-{
-	struct mail_cache_record *cache_rec;
-        enum mail_cache_field fields = 0;
+			data_size = mail_cache_field_sizes[field];
+			if (data_size == (unsigned int)-1) {
+				data_size = *((const uint32_t *)
+					      CONST_PTR_OFFSET(cache_rec, pos));
+				pos += sizeof(uint32_t);
+			}
 
-	cache_rec = mail_cache_lookup(view, seq);
-	while (cache_rec != NULL) {
-		fields |= cache_rec->fields;
+			next_pos = pos + ((data_size + 3) & ~3);
+			if (next_pos > cache_rec->size) {
+				mail_cache_set_corrupted(view->cache,
+					"Record continues outside it's "
+					"allocated size");
+				return -1;
+			}
+
+			ret = callback(view, field,
+				       CONST_PTR_OFFSET(cache_rec, pos),
+				       data_size, context);
+			if (ret <= 0)
+				return ret;
+
+			pos = next_pos;
+		}
 		cache_rec = mail_cache_get_record(view->cache,
 						  cache_rec->prev_offset);
 	}
 
-	return fields;
+	if (view->transaction != NULL) {
+		// FIXME: update
+	}
+	return 1;
+}
+
+static int mail_cache_seq_callback(struct mail_cache_view *view,
+				   enum mail_cache_field field,
+				   const void *data __attr_unused__,
+				   size_t data_size __attr_unused__,
+				   void *context __attr_unused__)
+{
+	view->cached_exists[field] = TRUE;
+	return 1;
+}
+
+static int mail_cache_seq(struct mail_cache_view *view, uint32_t seq)
+{
+	int ret;
+
+	view->cached_exists_seq = seq;
+	memset(view->cached_exists, 0, sizeof(view->cached_exists));
+
+	ret = mail_cache_foreach(view, seq, mail_cache_seq_callback, NULL);
+	return ret < 0 ? -1 : 0;
 }
 
-static int cache_get_field(struct mail_cache *cache,
-			   const struct mail_cache_record *cache_rec,
-			   enum mail_cache_field field, buffer_t *dest_buf)
+int mail_cache_field_exists(struct mail_cache_view *view, uint32_t seq,
+			    enum mail_cache_field field)
 {
-	unsigned int mask;
-	uint32_t data_size;
-	size_t offset, prev_offset;
-	int i;
+	i_assert(field < MAIL_CACHE_FIELD_COUNT);
 
-	offset = sizeof(*cache_rec);
+        if (MAIL_CACHE_IS_UNUSABLE(view->cache))
+		return 0;
 
-	for (i = 0, mask = 1; i < 31; i++, mask <<= 1) {
-		if ((cache_rec->fields & mask) == 0)
-			continue;
+	if (view->cached_exists_seq != seq) {
+		if (mail_cache_seq(view, seq) < 0)
+			return -1;
+	}
+	return view->cached_exists[field];
+}
 
-		/* all records are at least 32bit. we have to check this
-		   before getting data_size. */
-		if (offset + sizeof(uint32_t) > cache_rec->size) {
-			mail_cache_set_corrupted(cache,
-				"Record continues outside it's allocated size");
-			return FALSE;
-		}
+enum mail_cache_decision_type
+mail_cache_field_get_decision(struct mail_cache *cache,
+			      enum mail_cache_field field)
+{
+	i_assert(field < MAIL_CACHE_FIELD_COUNT);
 
-		if ((mask & MAIL_CACHE_FIXED_MASK) != 0)
-			data_size = mail_cache_field_sizes[i];
-		else {
-			memcpy(&data_size, CONST_PTR_OFFSET(cache_rec, offset),
-			       sizeof(data_size));
-			offset += sizeof(data_size);
-		}
+        if (MAIL_CACHE_IS_UNUSABLE(cache))
+		return cache->default_field_usage_decision_type[field];
 
-		prev_offset = offset + ((data_size + 3) & ~3);
-		if (prev_offset > cache_rec->size) {
-			mail_cache_set_corrupted(cache,
-				"Record continues outside it's allocated size");
-			return FALSE;
-		}
+	return cache->hdr->field_usage_decision_type[field];
+}
+
+struct mail_cache_lookup_context {
+	buffer_t *dest_buf;
+	enum mail_cache_field field;
+};
 
-		if (field == mask) {
-			if (data_size == 0) {
-				mail_cache_set_corrupted(cache,
-							 "Field size is 0");
-				return FALSE;
-			}
-			buffer_append(dest_buf,
-				      CONST_PTR_OFFSET(cache_rec, offset),
-				      data_size);
-			return TRUE;
-		}
-		offset = prev_offset;
-	}
+static int
+mail_cache_lookup_callback(struct mail_cache_view *view __attr_unused__,
+			   enum mail_cache_field field,
+			   const void *data, size_t data_size, void *context)
+{
+        struct mail_cache_lookup_context *ctx = context;
 
-	i_unreached();
-	return FALSE;
+	if (ctx->field != field)
+		return 1;
+
+	buffer_append(ctx->dest_buf, data, data_size);
+	return 0;
 }
 
 int mail_cache_lookup_field(struct mail_cache_view *view, buffer_t *dest_buf,
 			    uint32_t seq, enum mail_cache_field field)
 {
-	struct mail_cache_record *cache_rec;
+        struct mail_cache_lookup_context ctx;
 
-	mail_cache_handle_decisions(view, seq, field);
+	i_assert(field < MAIL_CACHE_FIELD_COUNT);
 
-	cache_rec = mail_cache_lookup(view, seq);
-	while (cache_rec != NULL) {
-		if ((cache_rec->fields & field) != 0) {
-			return cache_get_field(view->cache, cache_rec, field,
-					       dest_buf);
-		}
-		cache_rec = mail_cache_get_record(view->cache,
-						  cache_rec->prev_offset);
+        if (MAIL_CACHE_IS_UNUSABLE(view->cache))
+		return 0;
+
+	mail_cache_decision_lookup(view, seq, field);
+
+	if (view->cached_exists_seq != seq) {
+		if (mail_cache_seq(view, seq) < 0)
+			return -1;
 	}
 
-	return FALSE;
+	if (!view->cached_exists[field])
+		return 0;
+
+	/* should exist. find it. */
+	ctx.field = field;
+	ctx.dest_buf = dest_buf;
+	return mail_cache_foreach(view, seq, mail_cache_lookup_callback,
+				  &ctx) == 0;
 }
 
 int mail_cache_lookup_string_field(struct mail_cache_view *view, string_t *dest,
@@ -284,21 +335,24 @@
 {
 	size_t old_size, new_size;
 
-	i_assert((field & MAIL_CACHE_STRING_MASK) != 0);
+	i_assert(field < MAIL_CACHE_FIELD_COUNT);
+
+        if (MAIL_CACHE_IS_UNUSABLE(view->cache))
+		return 0;
 
 	old_size = str_len(dest);
 	if (!mail_cache_lookup_field(view, dest, seq, field))
-		return FALSE;
+		return 0;
 
 	new_size = str_len(dest);
 	if (old_size == new_size ||
 	    str_data(dest)[new_size-1] != '\0') {
 		mail_cache_set_corrupted(view->cache,
 			"String field %x doesn't end with NUL", field);
-		return FALSE;
+		return -1;
 	}
 	str_truncate(dest, new_size-1);
-	return TRUE;
+	return 1;
 }
 
 enum mail_cache_record_flag
--- a/src/lib-index/mail-cache-private.h	Thu Jul 08 23:24:48 2004 +0300
+++ b/src/lib-index/mail-cache-private.h	Thu Jul 08 23:26:15 2004 +0300
@@ -35,18 +35,6 @@
 #define MAIL_CACHE_IS_UNUSABLE(cache) \
 	((cache)->hdr == NULL)
 
-enum mail_cache_decision_type {
-	/* Not needed currently */
-	MAIL_CACHE_DECISION_NO		= 0x00,
-	/* Needed only for new mails. Drop when compressing. */
-	MAIL_CACHE_DECISION_TEMP	= 0x01,
-	/* Needed. */
-	MAIL_CACHE_DECISION_YES		= 0x02,
-
-	/* This decision has been forced manually, don't change it. */
-	MAIL_CACHE_DECISION_FORCED	= 0x80
-};
-
 struct mail_cache_header {
 	/* version is increased only when you can't have backwards
 	   compatibility. */
@@ -69,9 +57,9 @@
 };
 
 struct mail_cache_record {
-	uint32_t fields; /* enum mail_cache_field */
 	uint32_t prev_offset;
 	uint32_t size; /* full record size, including this header */
+	/* array of { uint32_t field; [ uint32_t size; ] { .. } } */
 };
 
 struct mail_cache_hole_header {
@@ -101,9 +89,7 @@
 	uint32_t split_offsets[MAIL_CACHE_HEADERS_COUNT];
 	const char *const *split_headers[MAIL_CACHE_HEADERS_COUNT];
 
-	enum mail_cache_field default_cache_fields;
-	enum mail_cache_field never_cache_fields;
-
+	uint8_t default_field_usage_decision_type[32];
 	uint32_t field_usage_uid_highwater[32];
 
 	unsigned int locked:1;
@@ -115,7 +101,9 @@
 	struct mail_cache *cache;
 	struct mail_index_view *view;
 
-	unsigned int broken:1;
+	struct mail_cache_transaction_ctx *transaction;
+	char cached_exists[32];
+	uint32_t cached_exists_seq;
 };
 
 extern unsigned int mail_cache_field_sizes[32];
@@ -123,7 +111,6 @@
 
 uint32_t mail_cache_uint32_to_offset(uint32_t offset);
 uint32_t mail_cache_offset_to_uint32(uint32_t offset);
-unsigned int mail_cache_field_index(enum mail_cache_field field);
 
 /* Explicitly lock the cache file. Returns -1 if error, 1 if ok, 0 if we
    couldn't lock */
@@ -138,8 +125,11 @@
 struct mail_cache_record *
 mail_cache_get_record(struct mail_cache *cache, uint32_t offset);
 
-struct mail_cache_record *
-mail_cache_lookup(struct mail_cache_view *view, uint32_t seq);
+int mail_cache_foreach(struct mail_cache_view *view, uint32_t seq,
+		       int (*callback)(struct mail_cache_view *view,
+				       enum mail_cache_field field,
+				       const void *data, size_t data_size,
+				       void *context), void *context);
 
 int mail_cache_transaction_commit(struct mail_cache_transaction_ctx *ctx);
 void mail_cache_transaction_rollback(struct mail_cache_transaction_ctx *ctx);
@@ -154,8 +144,10 @@
 /* Mark record in given offset to be deleted. */
 int mail_cache_delete(struct mail_cache *cache, uint32_t offset);
 
-void mail_cache_handle_decisions(struct mail_cache_view *view, uint32_t seq,
-				 enum mail_cache_field field);
+void mail_cache_decision_lookup(struct mail_cache_view *view, uint32_t seq,
+				enum mail_cache_field field);
+void mail_cache_decision_add(struct mail_cache_view *view, uint32_t seq,
+			     enum mail_cache_field field);
 
 void mail_cache_set_syscall_error(struct mail_cache *cache,
 				  const char *function);
--- a/src/lib-index/mail-cache-transaction.c	Thu Jul 08 23:24:48 2004 +0300
+++ b/src/lib-index/mail-cache-transaction.c	Thu Jul 08 23:26:15 2004 +0300
@@ -28,7 +28,7 @@
 	uint32_t last_grow_size;
 
 	uint32_t first_seq, last_seq;
-	enum mail_cache_field fields;
+	enum mail_cache_field fields[32];
 
 	unsigned int changes:1;
 };
@@ -55,12 +55,17 @@
 	ctx->reservations =
 		buffer_create_dynamic(system_pool, 256, (size_t)-1);
 
+	i_assert(view->transaction == NULL);
+	view->transaction = ctx;
+
 	t->cache_trans_ctx = ctx;
 	return ctx;
 }
 
 static void mail_cache_transaction_free(struct mail_cache_transaction_ctx *ctx)
 {
+	ctx->view->transaction = NULL;
+
 	buffer_free(ctx->cache_data);
 	buffer_free(ctx->cache_data_seq);
 	buffer_free(ctx->reservations);
@@ -317,7 +322,7 @@
 	struct mail_cache *cache = ctx->cache;
 	const struct mail_cache_record *rec, *tmp_rec;
 	const uint32_t *seq;
-	uint32_t write_offset, old_offset, rec_offset;
+	uint32_t write_offset, old_offset, rec_pos;
 	size_t size, max_size, seq_idx, seq_limit, seq_count;
 	int commit;
 
@@ -332,9 +337,10 @@
 
 	seq = buffer_get_data(ctx->cache_data_seq, &seq_count);
 	seq_count /= sizeof(*seq);
+	seq_limit = 0;
 
-	for (seq_idx = 0, rec_offset = 0; rec_offset < ctx->prev_pos;) {
-		max_size = ctx->prev_pos - rec_offset;
+	for (seq_idx = 0, rec_pos = 0; rec_pos < ctx->prev_pos;) {
+		max_size = ctx->prev_pos - rec_pos;
 		write_offset = mail_cache_transaction_get_space(ctx, rec->size,
 								max_size,
 								&max_size,
@@ -344,7 +350,7 @@
 			return ctx->prev_pos == 0 ? 0 : -1;
 		}
 
-		if (max_size < ctx->prev_pos) {
+		if (rec_pos + max_size < ctx->prev_pos) {
 			/* see how much we can really write there */
 			tmp_rec = rec;
 			for (size = 0; size + tmp_rec->size <= max_size; ) {
@@ -383,7 +389,7 @@
 			}
 
 			write_offset += rec->size;
-			rec_offset += rec->size;
+			rec_pos += rec->size;
 			rec = CONST_PTR_OFFSET(rec, rec->size);
 		}
 	}
@@ -391,7 +397,10 @@
 	/* drop the written data from buffer */
 	buffer_copy(ctx->cache_data, 0,
 		    ctx->cache_data, ctx->prev_pos, (size_t)-1);
-	buffer_set_used_size(ctx->cache_data, size - ctx->prev_pos);
+	buffer_set_used_size(ctx->cache_data,
+			     buffer_get_used_size(ctx->cache_data) -
+			     ctx->prev_pos);
+	ctx->prev_pos = 0;
 
 	buffer_set_used_size(ctx->cache_data_seq, 0);
 	return 0;
@@ -409,6 +418,7 @@
 		data = buffer_get_modifyable_data(ctx->cache_data, &size);
 		rec = PTR_OFFSET(data, ctx->prev_pos);
 		rec->size = size - ctx->prev_pos;
+		i_assert(rec->size != 0);
 
 		buffer_append(ctx->cache_data_seq, &ctx->prev_seq,
 			      sizeof(ctx->prev_seq));
@@ -588,62 +598,23 @@
 	return offset > 0;
 }
 
-static size_t
-mail_cache_transaction_get_insert_pos(struct mail_cache_transaction_ctx *ctx,
-				      enum mail_cache_field field)
-{
-	const struct mail_cache_record *cache_rec;
-	const void *data;
-	unsigned int mask;
-	uint32_t data_size;
-	size_t pos;
-	int i;
-
-	data = buffer_get_data(ctx->cache_data, NULL);
-	cache_rec = CONST_PTR_OFFSET(data, ctx->prev_pos);
-
-	pos = ctx->prev_pos + sizeof(*cache_rec);
-	for (i = 0, mask = 1; i < 31; i++, mask <<= 1) {
-		if ((field & mask) != 0)
-			return pos;
-
-		if ((cache_rec->fields & mask) != 0) {
-			if ((mask & MAIL_CACHE_FIXED_MASK) != 0)
-				data_size = mail_cache_field_sizes[i];
-			else {
-				memcpy(&data_size, CONST_PTR_OFFSET(data, pos),
-				       sizeof(data_size));
-				pos += sizeof(data_size);
-			}
-			pos += (data_size + 3) & ~3;
-		}
-	}
-
-	i_unreached();
-	return pos;
-}
-
 void mail_cache_add(struct mail_cache_transaction_ctx *ctx, uint32_t seq,
 		    enum mail_cache_field field,
 		    const void *data, size_t data_size)
 {
-	struct mail_cache_record *cache_rec;
-	unsigned char *buf;
-	size_t full_size, pos;
-	uint32_t data_size32;
-	unsigned int field_idx;
+	uint32_t fixed_size, data_size32;
+	size_t full_size;
 
+	i_assert(field < MAIL_CACHE_FIELD_COUNT);
 	i_assert(data_size > 0);
 	i_assert(data_size < (uint32_t)-1);
 
-	data_size32 = (uint32_t)data_size;
+	mail_cache_decision_add(ctx->view, seq, field);
 
-	if ((field & MAIL_CACHE_FIXED_MASK) != 0) {
-		field_idx = mail_cache_field_index(field);
-		i_assert(mail_cache_field_sizes[field_idx] == data_size);
-	} else if ((field & MAIL_CACHE_STRING_MASK) != 0) {
-		i_assert(((char *) data)[data_size-1] == '\0');
-	}
+	fixed_size = mail_cache_field_sizes[field];
+	i_assert(fixed_size == (unsigned int)-1 || fixed_size == data_size);
+
+	data_size32 = (uint32_t)data_size;
 
 	if (ctx->prev_seq != seq) {
 		mail_cache_transaction_switch_seq(ctx);
@@ -655,11 +626,12 @@
 			ctx->first_seq = seq;
 		if (seq > ctx->last_seq)
 			ctx->last_seq = seq;
-		ctx->fields |= field;
+		ctx->view->cached_exists[field] = TRUE;
+		ctx->fields[field] = TRUE;
 	}
 
 	full_size = (data_size + 3) & ~3;
-	if ((field & MAIL_CACHE_FIXED_MASK) == 0)
+	if (fixed_size == (unsigned int)-1)
 		full_size += sizeof(data_size32);
 
 	if (buffer_get_used_size(ctx->cache_data) + full_size >
@@ -669,24 +641,15 @@
 			return;
 	}
 
-	/* fields must be ordered. find where to insert it. */
-	pos = mail_cache_transaction_get_insert_pos(ctx, field);
-	buffer_copy(ctx->cache_data, pos + full_size,
-		    ctx->cache_data, pos, (size_t)-1);
-
-	cache_rec = buffer_get_space_unsafe(ctx->cache_data, ctx->prev_pos,
-					    sizeof(*cache_rec));
-	cache_rec->fields |= field;
+	buffer_append(ctx->cache_data, &field, sizeof(field));
+	if (fixed_size == (unsigned int)-1) {
+		buffer_append(ctx->cache_data, &data_size32,
+			      sizeof(data_size32));
+	}
 
-	/* @UNSAFE */
-	buf = buffer_get_space_unsafe(ctx->cache_data, pos, full_size);
-	if ((field & MAIL_CACHE_FIXED_MASK) == 0) {
-		memcpy(buf, &data_size32, sizeof(data_size32));
-		buf += sizeof(data_size32);
-	}
-	memcpy(buf, data, data_size); buf += data_size;
+	buffer_append(ctx->cache_data, data, data_size);
 	if ((data_size & 3) != 0)
-		memset(buf, 0, 4 - (data_size & 3));
+                buffer_append(ctx->cache_data, null4, 4 - (data_size & 3));
 }
 
 int mail_cache_update_record_flags(struct mail_cache_view *view, uint32_t seq,
--- a/src/lib-index/mail-cache.c	Thu Jul 08 23:24:48 2004 +0300
+++ b/src/lib-index/mail-cache.c	Thu Jul 08 23:26:15 2004 +0300
@@ -15,13 +15,13 @@
 	sizeof(time_t),
 	sizeof(uoff_t),
 
-	0, 0, 0, 0, 0, 0, 0, 0,
-
 	/* variable sized */
 	(unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1,
 	(unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1,
 	(unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1,
 	(unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1,
+	(unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1,
+	(unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1,
 	(unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1
 };
 
@@ -60,17 +60,6 @@
 		(((uint32_t)buf[0] & 0x7f) << 23);
 }
 
-unsigned int mail_cache_field_index(enum mail_cache_field field)
-{
-	unsigned int i, num;
-
-	for (i = 0, num = 1; i < 32; i++, num <<= 1) {
-		if (field == num)
-			return i;
-	}
-	i_unreached();
-}
-
 void mail_cache_set_syscall_error(struct mail_cache *cache,
 				  const char *function)
 {
@@ -284,11 +273,10 @@
 }
 
 void mail_cache_set_defaults(struct mail_cache *cache,
-			     enum mail_cache_field default_cache_fields,
-			     enum mail_cache_field never_cache_fields)
+			     const enum mail_cache_decision_type dec[32])
 {
-	cache->default_cache_fields = default_cache_fields;
-	cache->never_cache_fields = never_cache_fields;
+	memcpy(cache->default_field_usage_decision_type, dec,
+	       sizeof(cache->default_field_usage_decision_type));
 }
 
 int mail_cache_lock(struct mail_cache *cache)
--- a/src/lib-index/mail-cache.h	Thu Jul 08 23:24:48 2004 +0300
+++ b/src/lib-index/mail-cache.h	Thu Jul 08 23:26:15 2004 +0300
@@ -11,6 +11,18 @@
 struct mail_cache_view;
 struct mail_cache_transaction_ctx;
 
+enum mail_cache_decision_type {
+	/* Not needed currently */
+	MAIL_CACHE_DECISION_NO		= 0x00,
+	/* Needed only for new mails. Drop when compressing. */
+	MAIL_CACHE_DECISION_TEMP	= 0x01,
+	/* Needed. */
+	MAIL_CACHE_DECISION_YES		= 0x02,
+
+	/* This decision has been forced manually, don't change it. */
+	MAIL_CACHE_DECISION_FORCED	= 0x80
+};
+
 enum mail_cache_record_flag {
 	/* If binary flags are set, it's not checked whether mail is
 	   missing CRs. So this flag may be set as an optimization for
@@ -28,40 +40,23 @@
 /* when modifying, remember to update mail_cache_field_sizes[] too */
 enum mail_cache_field {
 	/* fixed size fields */
-	MAIL_CACHE_INDEX_FLAGS		= 0x00000001,
-	MAIL_CACHE_SENT_DATE		= 0x00000002,
-	MAIL_CACHE_RECEIVED_DATE	= 0x00000004,
-	MAIL_CACHE_VIRTUAL_FULL_SIZE	= 0x00000008,
+	MAIL_CACHE_INDEX_FLAGS = 0,
+	MAIL_CACHE_SENT_DATE,
+	MAIL_CACHE_RECEIVED_DATE,
+	MAIL_CACHE_VIRTUAL_FULL_SIZE,
 
 	/* variable sized field */
-	MAIL_CACHE_HEADERS1		= 0x40000000,
-	MAIL_CACHE_HEADERS2		= 0x20000000,
-	MAIL_CACHE_HEADERS3		= 0x10000000,
-	MAIL_CACHE_HEADERS4		= 0x08000000,
-	MAIL_CACHE_LOCATION		= 0x04000000,
-	MAIL_CACHE_BODY			= 0x02000000,
-	MAIL_CACHE_BODYSTRUCTURE	= 0x01000000,
-	MAIL_CACHE_ENVELOPE		= 0x00800000,
-	MAIL_CACHE_MESSAGEPART		= 0x00400000,
-	MAIL_CACHE_UID_STRING		= 0x00200000,
+	MAIL_CACHE_HEADERS1,
+	MAIL_CACHE_HEADERS2,
+	MAIL_CACHE_HEADERS3,
+	MAIL_CACHE_HEADERS4,
+	MAIL_CACHE_BODY,
+	MAIL_CACHE_BODYSTRUCTURE,
+	MAIL_CACHE_ENVELOPE,
+	MAIL_CACHE_MESSAGEPART,
+	MAIL_CACHE_UID_STRING,
 
-	MAIL_CACHE_FIXED_MASK		= MAIL_CACHE_INDEX_FLAGS |
-					  MAIL_CACHE_SENT_DATE |
-					  MAIL_CACHE_RECEIVED_DATE |
-					  MAIL_CACHE_VIRTUAL_FULL_SIZE,
-	MAIL_CACHE_HEADERS_MASK		= MAIL_CACHE_HEADERS1 |
-					  MAIL_CACHE_HEADERS2 |
-					  MAIL_CACHE_HEADERS3 |
-					  MAIL_CACHE_HEADERS4,
-	MAIL_CACHE_STRING_MASK		= MAIL_CACHE_HEADERS_MASK |
-					  MAIL_CACHE_LOCATION |
-					  MAIL_CACHE_BODY |
-					  MAIL_CACHE_BODYSTRUCTURE |
-					  MAIL_CACHE_ENVELOPE |
-					  MAIL_CACHE_UID_STRING,
-	MAIL_CACHE_BODYSTRUCTURE_MASK	= MAIL_CACHE_BODY |
-					  MAIL_CACHE_BODYSTRUCTURE |
-                                          MAIL_CACHE_MESSAGEPART
+	MAIL_CACHE_FIELD_COUNT
 };
 
 struct mail_sent_date {
@@ -75,8 +70,7 @@
 void mail_cache_free(struct mail_cache *cache);
 
 void mail_cache_set_defaults(struct mail_cache *cache,
-			     enum mail_cache_field default_cache_fields,
-			     enum mail_cache_field never_cache_fields);
+			     const enum mail_cache_decision_type dec[32]);
 
 /* Returns TRUE if cache should be compressed. */
 int mail_cache_need_compress(struct mail_cache *cache);
@@ -106,9 +100,13 @@
 		    enum mail_cache_field field,
 		    const void *data, size_t data_size);
 
-/* Return all fields that are currently cached for record. */
-enum mail_cache_field
-mail_cache_get_fields(struct mail_cache_view *view, uint32_t seq);
+/* Retursn TRUE if field exists. */
+int mail_cache_field_exists(struct mail_cache_view *view, uint32_t seq,
+			    enum mail_cache_field field);
+/* Returns current caching decision for given field. */
+enum mail_cache_decision_type
+mail_cache_field_get_decision(struct mail_cache *cache,
+			      enum mail_cache_field field);
 
 /* Set data_r and size_r to point to wanted field in cache file.
    Returns TRUE if field was found. If field contains multiple fields,
@@ -120,10 +118,6 @@
 int mail_cache_lookup_string_field(struct mail_cache_view *view, string_t *dest,
 				   uint32_t seq, enum mail_cache_field field);
 
-/* Mark given field as missing, ie. it should be cached when possible. */
-void mail_cache_mark_missing(struct mail_cache_view *view, uint32_t uid,
-			     enum mail_cache_field field);
-
 /* Return record flags. */
 enum mail_cache_record_flag
 mail_cache_get_record_flags(struct mail_cache_view *view, uint32_t seq);
--- a/src/lib-storage/index/index-fetch.c	Thu Jul 08 23:24:48 2004 +0300
+++ b/src/lib-storage/index/index-fetch.c	Thu Jul 08 23:26:15 2004 +0300
@@ -10,23 +10,13 @@
 {
 	struct index_transaction_context *t =
 		(struct index_transaction_context *)_t;
-        const struct mail_index_record *rec;
-
-	if (mail_index_lookup(t->trans_view, seq, &rec) < 0) {
-		mail_storage_set_index_error(t->ibox);
-		return NULL;
-	}
-
-	if (rec == NULL)
-		return NULL;
 
 	if (t->fetch_mail.pool != NULL)
 		index_mail_deinit(&t->fetch_mail);
 
 	index_mail_init(t, &t->fetch_mail, wanted_fields, NULL);
-	if (index_mail_next(&t->fetch_mail, rec, seq, FALSE) <= 0)
+	if (index_mail_next(&t->fetch_mail, seq) < 0)
 		return NULL;
-
 	return &t->fetch_mail.mail;
 }
 
--- a/src/lib-storage/index/index-mail-headers.c	Thu Jul 08 23:24:48 2004 +0300
+++ b/src/lib-storage/index/index-mail-headers.c	Thu Jul 08 23:26:15 2004 +0300
@@ -191,8 +191,9 @@
 		return -1;
 
 	for (; idx < MAIL_CACHE_HEADERS_COUNT; idx++) {
-		if ((mail->data.cached_fields &
-		     mail_cache_header_fields[idx]) != 0)
+		if (mail_cache_field_exists(mail->trans->cache_view,
+					    mail->data.seq,
+					    mail_cache_header_fields[idx]) > 0)
 			return idx;
 	}
 
@@ -320,9 +321,9 @@
 			data->save_sent_date = FALSE;
 		}
 		if (data->sent_date.time != (time_t)-1) {
-			index_mail_cache_add(mail, MAIL_CACHE_SENT_DATE,
-					     &data->sent_date,
-					     sizeof(data->sent_date));
+                        mail_cache_add(mail->trans->cache_trans, data->seq,
+				       MAIL_CACHE_SENT_DATE, &data->sent_date,
+				       sizeof(data->sent_date));
 		}
 
 		cached_headers_mark_fully_saved(mail);
@@ -387,8 +388,11 @@
 
 static int index_mail_can_cache_headers(struct index_mail *mail)
 {
-	if ((mail->data.cached_fields &
-	     mail_cache_header_fields[MAIL_CACHE_HEADERS_COUNT-1]) != 0)
+	enum mail_cache_field field;
+
+	field = mail_cache_header_fields[MAIL_CACHE_HEADERS_COUNT-1];
+	if (mail_cache_field_exists(mail->trans->cache_view, mail->data.seq,
+				    field) != 0)
 		return FALSE; /* all headers used */
 
 	/* FIXME: add some smart checks here. we don't necessarily want to
@@ -493,9 +497,6 @@
 	if (mail->data.header_data == NULL)
 		mail->data.header_data = str_new(mail->pool, 4096);
 
-	/* can_cache_headers() locks the cache file. it must be done before
-	   we can expect cached headers to stay the same. it's not a good idea
-	   to cache some headers twice because of race conditions.. */
 	if (!data->header_fully_parsed && index_mail_can_cache_headers(mail)) {
 		if (data->header_data_cached_partial) {
 			/* too difficult to handle efficiently, trash it */
@@ -722,8 +723,10 @@
 	if (idx != -2) {
 		if (idx >= 0) {
 			for (; idx < MAIL_CACHE_HEADERS_COUNT; idx++) {
-				if ((data->cached_fields &
-				     mail_cache_header_fields[idx]) != 0)
+				if (mail_cache_field_exists(
+					mail->trans->cache_view,
+					data->seq,
+					mail_cache_header_fields[idx]) > 0)
 					break;
 			}
 		}
--- a/src/lib-storage/index/index-mail.c	Thu Jul 08 23:24:48 2004 +0300
+++ b/src/lib-storage/index/index-mail.c	Thu Jul 08 23:26:15 2004 +0300
@@ -13,7 +13,7 @@
 #include "index-storage.h"
 #include "index-mail.h"
 
-static void index_mail_parse_body(struct index_mail *mail);
+static void index_mail_parse_body(struct index_mail *mail, int need_parts);
 
 static struct message_part *get_cached_parts(struct index_mail *mail)
 {
@@ -21,17 +21,13 @@
 	buffer_t *part_buf;
 	const char *error;
 
-	if ((mail->data.cached_fields & MAIL_CACHE_MESSAGEPART) == 0) {
-		mail_cache_mark_missing(mail->trans->cache_view, mail->data.seq,
-					MAIL_CACHE_MESSAGEPART);
-		return NULL;
-	}
-
+	t_push();
 	part_buf = buffer_create_dynamic(pool_datastack_create(),
 					 128, (size_t)-1);
-	if (!mail_cache_lookup_field(mail->trans->cache_view, part_buf,
-				     mail->data.seq, MAIL_CACHE_MESSAGEPART)) {
-		/* unexpected - must be an error */
+	if (mail_cache_lookup_field(mail->trans->cache_view, part_buf,
+				    mail->data.seq,
+				    MAIL_CACHE_MESSAGEPART) <= 0) {
+		t_pop();
 		return NULL;
 	}
 
@@ -39,6 +35,8 @@
 					buffer_get_data(part_buf, NULL),
 					buffer_get_used_size(part_buf),
 					&error);
+	t_pop();
+
 	if (part == NULL) {
 		mail_cache_set_corrupted(mail->ibox->cache,
 			"Corrupted cached message_part data (%s)", error);
@@ -62,17 +60,13 @@
 {
 	string_t *str;
 
-	if ((mail->data.cached_fields & field) == 0) {
-		mail_cache_mark_missing(mail->trans->cache_view,
-					mail->data.seq, field);
+	str = str_new(mail->pool, 32);
+	if (mail_cache_lookup_string_field(mail->trans->cache_view, str,
+					   mail->data.seq, field) <= 0) {
+		p_free(mail->pool, str);
 		return NULL;
 	}
 
-	str = str_new(mail->pool, 32);
-	if (!mail_cache_lookup_string_field(mail->trans->cache_view, str,
-					    mail->data.seq, field))
-		return NULL;
-
 	return str_c(str);
 }
 
@@ -85,10 +79,8 @@
 
 	t_push();
 	buf = buffer_create_data(pool_datastack_create(), data, data_size);
-	if (!mail_cache_lookup_field(mail->trans->cache_view, buf,
-				     mail->data.seq, field)) {
-		mail_cache_mark_missing(mail->trans->cache_view,
-					mail->data.seq, field);
+	if (mail_cache_lookup_field(mail->trans->cache_view, buf,
+				    mail->data.seq, field) <= 0) {
 		ret = FALSE;
 	} else {
 		i_assert(buffer_get_used_size(buf) == data_size);
@@ -144,35 +136,6 @@
 	}
 }
 
-static int index_mail_cache_can_add(struct index_mail *mail,
-				    enum mail_cache_field field)
-{
-	if ((mail->data.cached_fields & field) != 0)
-		return FALSE;
-
-	// FIXME: check if we really want to cache this
-
-	index_mail_cache_transaction_begin(mail);
-
-	/* cached_fields may have changed, recheck */
-	if ((mail->data.cached_fields & field) != 0)
-		return FALSE;
-
-	return TRUE;
-}
-
-void index_mail_cache_add(struct index_mail *mail, enum mail_cache_field field,
-			  const void *data, size_t size)
-{
-        if (!index_mail_cache_can_add(mail, field))
-		return;
-
-	mail_cache_add(mail->trans->cache_trans, mail->data.seq,
-		       field, data, size);
-
-	mail->data.cached_fields |= field;
-}
-
 const struct mail_full_flags *index_mail_get_flags(struct mail *_mail)
 {
 	struct index_mail *mail = (struct index_mail *) _mail;
@@ -194,17 +157,15 @@
 	if (data->parts != NULL)
 		return data->parts;
 
-	if ((mail->wanted_fields & MAIL_FETCH_MESSAGE_PARTS) == 0) {
-		data->parts = get_cached_parts(mail);
-		if (data->parts != NULL)
-			return data->parts;
-	}
+	data->parts = get_cached_parts(mail);
+	if (data->parts != NULL)
+		return data->parts;
 
 	if (data->parser_ctx == NULL) {
 		if (!index_mail_parse_headers(mail))
 			return NULL;
 	}
-	index_mail_parse_body(mail);
+	index_mail_parse_body(mail, TRUE);
 
 	return data->parts;
 }
@@ -251,9 +212,9 @@
 				tz = 0;
 			}
                         data->sent_date.timezone = tz;
-			index_mail_cache_add(mail, MAIL_CACHE_SENT_DATE,
-					     &data->sent_date,
-					     sizeof(data->sent_date));
+			mail_cache_add(mail->trans->cache_trans, mail->data.seq,
+				       MAIL_CACHE_SENT_DATE, &data->sent_date,
+				       sizeof(data->sent_date));
 		}
 	}
 
@@ -266,12 +227,8 @@
 {
 	struct index_mail_data *data = &mail->data;
 
-	if (data->parts == NULL) {
-		if ((mail->wanted_fields & MAIL_FETCH_MESSAGE_PARTS) != 0)
-			(void)index_mail_get_parts(&mail->mail);
-		else
-			data->parts = get_cached_parts(mail);
-	}
+	if (data->parts == NULL)
+		(void)index_mail_get_parts(&mail->mail);
 
 	if (data->parts != NULL) {
 		data->hdr_size = data->parts->header_size;
@@ -294,11 +251,9 @@
 	if (data->size != (uoff_t)-1)
 		return data->size;
 
-	if ((mail->wanted_fields & MAIL_FETCH_SIZE) == 0) {
-		data->size = index_mail_get_cached_virtual_size(mail);
-		if (data->size != (uoff_t)-1)
-			return data->size;
-	}
+	data->size = index_mail_get_cached_virtual_size(mail);
+	if (data->size != (uoff_t)-1)
+		return data->size;
 
 	if (get_msgpart_sizes(mail))
 		return data->size;
@@ -318,10 +273,11 @@
 	imap_bodystructure_parse_header(pool, part, hdr);
 }
 
-static void index_mail_parse_body(struct index_mail *mail)
+static void index_mail_parse_body(struct index_mail *mail, int need_parts)
 {
 	struct index_mail_data *data = &mail->data;
         enum mail_cache_record_flag cache_flags;
+	enum mail_cache_decision_type decision;
 	buffer_t *buffer;
 	const void *buf_data;
 	size_t buf_size;
@@ -332,6 +288,8 @@
 	i_stream_seek(data->stream, data->hdr_size.physical_size);
 
 	if (data->bodystructure_header_parsed) {
+		/* bodystructure header is parsed, we want the body's mime
+		   headers too */
 		message_parser_parse_body(data->parser_ctx,
 					  parse_bodystructure_header,
 					  NULL, mail->pool);
@@ -344,41 +302,50 @@
 	data->body_size = data->parts->body_size;
 	data->body_size_set = TRUE;
 
-	if (mail->mail.has_nuls || mail->mail.has_no_nuls)
-		return;
-
-	/* we know the NULs now, update them */
-	if ((data->parts->flags & MESSAGE_PART_FLAG_HAS_NULS) != 0) {
-		mail->mail.has_nuls = TRUE;
-		mail->mail.has_no_nuls = FALSE;
-	} else {
-		mail->mail.has_nuls = FALSE;
-		mail->mail.has_no_nuls = TRUE;
-	}
-
 	index_mail_cache_transaction_begin(mail);
 
-	/* update cache_flags */
-	cache_flags = mail_cache_get_record_flags(mail->trans->cache_view,
-						  mail->data.seq);
-	if (mail->mail.has_nuls)
-		cache_flags |= MAIL_INDEX_FLAG_HAS_NULS;
-	else
-		cache_flags |= MAIL_INDEX_FLAG_HAS_NO_NULS;
+	if (!mail->mail.has_nuls && !mail->mail.has_no_nuls) {
+		/* we know the NULs now, update them */
+		if ((data->parts->flags & MESSAGE_PART_FLAG_HAS_NULS) != 0) {
+			mail->mail.has_nuls = TRUE;
+			mail->mail.has_no_nuls = FALSE;
+		} else {
+			mail->mail.has_nuls = FALSE;
+			mail->mail.has_no_nuls = TRUE;
+		}
 
-	if (!mail_cache_update_record_flags(mail->trans->cache_view,
-					    mail->data.seq, cache_flags))
+		/* update cache_flags */
+		cache_flags =
+			mail_cache_get_record_flags(mail->trans->cache_view,
+						    mail->data.seq);
+		if (mail->mail.has_nuls)
+			cache_flags |= MAIL_INDEX_FLAG_HAS_NULS;
+		else
+			cache_flags |= MAIL_INDEX_FLAG_HAS_NO_NULS;
+
+		(void)mail_cache_update_record_flags(mail->trans->cache_view,
+						     mail->data.seq,
+						     cache_flags);
+	}
+
+	/* see if we want to cache the message part */
+	if (mail_cache_field_exists(mail->trans->cache_view, mail->data.seq,
+				    MAIL_CACHE_MESSAGEPART) != 0)
 		return;
 
-	if (index_mail_cache_can_add(mail, MAIL_CACHE_MESSAGEPART)) {
+	decision = mail_cache_field_get_decision(mail->ibox->cache,
+						 MAIL_CACHE_MESSAGEPART);
+	if (decision != (MAIL_CACHE_DECISION_NO | MAIL_CACHE_DECISION_FORCED) &&
+	    (decision != MAIL_CACHE_DECISION_NO || need_parts ||
+	     (mail->wanted_fields & MAIL_FETCH_MESSAGE_PARTS) != 0)) {
 		t_push();
 		buffer = buffer_create_dynamic(pool_datastack_create(),
 					       1024, (size_t)-1);
 		message_part_serialize(mail->data.parts, buffer);
 
 		buf_data = buffer_get_data(buffer, &buf_size);
-		index_mail_cache_add(mail, MAIL_CACHE_MESSAGEPART,
-				     buf_data, buf_size);
+		mail_cache_add(mail->trans->cache_trans, mail->data.seq,
+			       MAIL_CACHE_MESSAGEPART, buf_data, buf_size);
 		t_pop();
 	}
 }
@@ -404,7 +371,7 @@
 
 	if (body_size != NULL) {
 		if (!data->body_size_set)
-			index_mail_parse_body(mail);
+			index_mail_parse_body(mail, FALSE);
 
 		*body_size = data->body_size;
 	}
@@ -418,83 +385,127 @@
 	return data->stream;
 }
 
+static void index_mail_parse_bodystructure(struct index_mail *mail,
+					   enum mail_cache_field field)
+{
+	struct index_mail_data *data = &mail->data;
+	enum mail_cache_decision_type dec;
+	string_t *str;
+	int bodystructure_cached = FALSE;
+
+	if (!data->bodystructure_header_parsed) {
+		data->bodystructure_header_want = TRUE;
+		if (!index_mail_parse_headers(mail))
+			return;
+	}
+
+	if (data->parts != NULL) {
+		i_assert(data->parts->next == NULL);
+		message_parse_from_parts(data->parts->children, data->stream,
+					 parse_bodystructure_header,
+					 mail->pool);
+	} else {
+		index_mail_parse_body(mail, FALSE);
+	}
+
+	dec = mail_cache_field_get_decision(mail->ibox->cache,
+					    MAIL_CACHE_BODYSTRUCTURE);
+	if (field == MAIL_CACHE_BODYSTRUCTURE ||
+	    ((dec & ~MAIL_CACHE_DECISION_FORCED) != MAIL_CACHE_DECISION_NO &&
+	     mail_cache_field_exists(mail->trans->cache_view, data->seq,
+				      MAIL_CACHE_BODYSTRUCTURE)) == 0) {
+		str = str_new(mail->pool, 128);
+		imap_bodystructure_write(data->parts, str, TRUE);
+		data->bodystructure = str_c(str);
+
+		if (dec !=
+		    (MAIL_CACHE_DECISION_NO | MAIL_CACHE_DECISION_FORCED)) {
+			mail_cache_add(mail->trans->cache_trans, data->seq,
+				       MAIL_CACHE_BODYSTRUCTURE,
+				       str_c(str), str_len(str)+1);
+			bodystructure_cached = TRUE;
+		}
+	}
+
+	dec = mail_cache_field_get_decision(mail->ibox->cache, MAIL_CACHE_BODY);
+	if (field == MAIL_CACHE_BODY ||
+	    ((dec & ~MAIL_CACHE_DECISION_FORCED) != MAIL_CACHE_DECISION_NO &&
+	     mail_cache_field_exists(mail->trans->cache_view, data->seq,
+				     MAIL_CACHE_BODY)) == 0) {
+		str = str_new(mail->pool, 128);
+		imap_bodystructure_write(data->parts, str, FALSE);
+		data->body = str_c(str);
+
+		if (!bodystructure_cached && dec !=
+		    (MAIL_CACHE_DECISION_NO | MAIL_CACHE_DECISION_FORCED)) {
+			mail_cache_add(mail->trans->cache_trans, data->seq,
+				       MAIL_CACHE_BODY,
+				       str_c(str), str_len(str)+1);
+		}
+	}
+}
+
 const char *index_mail_get_special(struct mail *_mail,
 				   enum mail_fetch_field field)
 {
 	struct index_mail *mail = (struct index_mail *) _mail;
 	struct index_mail_data *data = &mail->data;
-	struct mail_cache *cache = mail->ibox->cache;
-	enum mail_cache_field cache_field;
-	char *str;
+	string_t *str;
 
 	switch (field) {
 	case MAIL_FETCH_IMAP_BODY:
-		if ((data->cached_fields & MAIL_CACHE_BODY) &&
-		    data->body == NULL) {
-			data->body = index_mail_get_cached_string(mail,
-					MAIL_CACHE_BODY);
-		}
 		if (data->body != NULL)
 			return data->body;
-		/* fall through */
-	case MAIL_FETCH_IMAP_BODYSTRUCTURE:
-		if ((data->cached_fields & MAIL_CACHE_BODYSTRUCTURE) &&
-		    data->bodystructure == NULL) {
-			data->bodystructure = index_mail_get_cached_string(mail,
-						MAIL_CACHE_BODYSTRUCTURE);
-		}
 
-		if (data->bodystructure != NULL) {
-			if (field == MAIL_FETCH_IMAP_BODYSTRUCTURE)
-				return data->bodystructure;
+		/* 1) get BODY if it exists
+		   2) get it using BODYSTRUCTURE if it exists
+		   3) parse body structure, and save BODY/BODYSTRUCTURE
+		      depending on what we want cached */
 
-			/* create BODY from cached BODYSTRUCTURE */
-			t_push();
-			data->body = p_strdup(mail->pool,
-				imap_body_parse_from_bodystructure(
-							data->bodystructure));
-			t_pop();
-
-			if (data->body == NULL) {
-				mail_cache_set_corrupted(cache,
-					"Corrupted BODYSTRUCTURE");
-			}
+		str = str_new(mail->pool, 128);
+		if (mail_cache_lookup_string_field(mail->trans->cache_view,
+						   str, mail->data.seq,
+						   MAIL_CACHE_BODY) > 0) {
+			data->body = str_c(str);
 			return data->body;
 		}
-
-		if (!data->bodystructure_header_parsed) {
-			data->bodystructure_header_want = TRUE;
-			if (!index_mail_parse_headers(mail))
-				return NULL;
-		}
+		if (mail_cache_lookup_string_field(mail->trans->cache_view,
+					str, mail->data.seq,
+					MAIL_CACHE_BODYSTRUCTURE) > 0) {
+			data->bodystructure = str_c(str);
+			str_truncate(str, 0);
 
-		if (data->parts != NULL) {
-			i_assert(data->parts->next == NULL);
-			message_parse_from_parts(data->parts->children,
-						 data->stream,
-						 parse_bodystructure_header,
-						 mail->pool);
-		} else {
-			index_mail_parse_body(mail);
-		}
+			if (imap_body_parse_from_bodystructure(
+						data->bodystructure, str)) {
+				data->body = str_c(str);
+				return data->body;
+			}
 
-		t_push();
-                str = p_strdup(mail->pool, imap_bodystructure_parse_finish(
-			data->parts, field == MAIL_FETCH_IMAP_BODYSTRUCTURE));
-		t_pop();
-
-		/* should never fail */
-		i_assert(str != NULL);
+			/* broken, continue.. */
+			data->bodystructure = NULL;
+			mail_cache_set_corrupted(mail->ibox->cache,
+				"Corrupted BODYSTRUCTURE for mail %u",
+				mail->mail.uid);
+		}
+		p_free(mail->pool, str);
 
-		cache_field = field == MAIL_FETCH_IMAP_BODYSTRUCTURE ?
-			MAIL_CACHE_BODYSTRUCTURE : MAIL_CACHE_BODY;
-		index_mail_cache_add(mail, cache_field, str, strlen(str)+1);
+		index_mail_parse_bodystructure(mail, MAIL_CACHE_BODY);
+		return data->body;
+	case MAIL_FETCH_IMAP_BODYSTRUCTURE:
+		if (data->bodystructure != NULL)
+			return data->bodystructure;
 
-		if (field == MAIL_FETCH_IMAP_BODYSTRUCTURE)
-			data->bodystructure = str;
-		else
-			data->body = str;
-		return str;
+		str = str_new(mail->pool, 128);
+		if (mail_cache_lookup_string_field(mail->trans->cache_view,
+					str, mail->data.seq,
+					MAIL_CACHE_BODYSTRUCTURE) > 0) {
+			data->bodystructure = str_c(str);
+			return data->bodystructure;
+		}
+		p_free(mail->pool, str);
+
+		index_mail_parse_bodystructure(mail, MAIL_CACHE_BODYSTRUCTURE);
+		return data->bodystructure;
 	case MAIL_FETCH_IMAP_ENVELOPE:
 		if (data->envelope != NULL)
 			return data->envelope;
@@ -550,13 +561,16 @@
 	index_mail_headers_close(mail);
 }
 
-int index_mail_next(struct index_mail *mail,
-		    const struct mail_index_record *rec,
-		    uint32_t seq, int delay_open)
+int index_mail_next(struct index_mail *mail, uint32_t seq)
 {
 	struct index_mail_data *data = &mail->data;
+        const struct mail_index_record *rec;
         enum mail_cache_record_flag cache_flags;
-	int ret, open_mail;
+
+	if (mail_index_lookup(mail->trans->trans_view, seq, &rec) < 0) {
+		mail_storage_set_index_error(mail->ibox);
+		return -1;
+	}
 
 	t_push();
 
@@ -564,10 +578,7 @@
 	memset(data, 0, sizeof(*data));
 	p_clear(mail->pool);
 
-	data->cached_fields =
-		mail_cache_get_fields(mail->trans->cache_view, seq);
-	cache_flags = (data->cached_fields & MAIL_CACHE_INDEX_FLAGS) == 0 ? 0 :
-		mail_cache_get_record_flags(mail->trans->cache_view, seq);
+	cache_flags = mail_cache_get_record_flags(mail->trans->cache_view, seq);
 
 	mail->mail.seq = seq;
 	mail->mail.uid = rec->uid;
@@ -599,7 +610,6 @@
 		get_cached_sent_date(mail, &data->sent_date);
 
 	/* see if we have to parse the message */
-	open_mail = FALSE;
 	if ((mail->wanted_fields & MAIL_FETCH_MESSAGE_PARTS) &&
 	    data->parts == NULL)
 		data->parse_header = TRUE;
@@ -607,36 +617,22 @@
 		 data->bodystructure == NULL) {
 		if (data->parts == NULL)
 			data->parts = get_cached_parts(mail);
-		open_mail = TRUE;
+		data->open_mail = TRUE;
 		data->parse_header = data->parts == NULL;
-                data->bodystructure_header_want = TRUE;
+		data->bodystructure_header_want = TRUE;
 	} else if ((mail->wanted_fields & MAIL_FETCH_IMAP_BODY) &&
 		   data->body == NULL && data->bodystructure == NULL) {
 		if (data->parts == NULL)
 			data->parts = get_cached_parts(mail);
-		open_mail = TRUE;
+		data->open_mail = TRUE;
 		data->parse_header = data->parts == NULL;
                 data->bodystructure_header_want = TRUE;
 	} else if (mail->wanted_fields & (MAIL_FETCH_STREAM_HEADER |
 					  MAIL_FETCH_STREAM_BODY))
-		open_mail = TRUE;
+		data->open_mail = TRUE;
 
         index_mail_headers_init_next(mail);
 
-	if ((open_mail || data->parse_header) && !delay_open) {
-		if (mail->mail.get_stream(&mail->mail, NULL, NULL) == NULL)
-			ret = data->deleted ? 0 : -1;
-		else
-			ret = 1;
-	} else {
-		if (mail->wanted_fields & MAIL_FETCH_RECEIVED_DATE) {
-			/* check this only after open_mail() */
-			data->received_date =
-				index_mail_get_cached_received_date(mail);
-		}
-		ret = 1;
-	}
-
 	if ((mail->wanted_fields & MAIL_FETCH_DATE) &&
 	    data->sent_date.time == (time_t)-1)
 		data->save_sent_date = TRUE;
@@ -645,7 +641,7 @@
 		data->save_envelope = TRUE;
 
 	t_pop();
-	return ret;
+	return 0;
 }
 
 void index_mail_deinit(struct index_mail *mail)
--- a/src/lib-storage/index/index-mail.h	Thu Jul 08 23:24:48 2004 +0300
+++ b/src/lib-storage/index/index-mail.h	Thu Jul 08 23:26:15 2004 +0300
@@ -12,7 +12,6 @@
 	time_t date, received_date;
 	uoff_t size;
 
-	enum mail_cache_field cached_fields;
 	struct mail_sent_date sent_date;
 
 	buffer_t *headers;
@@ -46,6 +45,7 @@
 	unsigned int header_data_cached_partial:1;
 	unsigned int header_fully_parsed:1;
 	unsigned int header_save:1;
+	unsigned int open_mail:1;
 };
 
 struct index_mail {
@@ -68,9 +68,7 @@
 		     struct index_mail *mail,
 		     enum mail_fetch_field wanted_fields,
 		     const char *const wanted_headers[]);
-int index_mail_next(struct index_mail *mail,
-		    const struct mail_index_record *rec,
-		    uint32_t seq, int delay_open);
+int index_mail_next(struct index_mail *mail, uint32_t seq);
 void index_mail_deinit(struct index_mail *mail);
 
 void index_mail_parse_header_init(struct index_mail *mail,
@@ -80,9 +78,6 @@
 			    struct index_mail *mail);
 
 void index_mail_cache_transaction_begin(struct index_mail *mail);
-void index_mail_cache_add(struct index_mail *mail, enum mail_cache_field field,
-			  const void *data, size_t size);
-
 int index_mail_parse_headers(struct index_mail *mail);
 
 void index_mail_headers_init(struct index_mail *mail);
--- a/src/lib-storage/index/index-search.c	Thu Jul 08 23:24:48 2004 +0300
+++ b/src/lib-storage/index/index-search.c	Thu Jul 08 23:26:15 2004 +0300
@@ -810,21 +810,15 @@
 struct mail *index_storage_search_next(struct mail_search_context *_ctx)
 {
         struct index_search_context *ctx = (struct index_search_context *)_ctx;
-	const struct mail_index_record *rec;
 	int ret;
 
 	ret = 0;
 	while (ctx->seq1 <= ctx->seq2) {
-		if (mail_index_lookup(ctx->view, ctx->seq1, &rec) < 0) {
+		if (index_mail_next(&ctx->imail, ctx->seq1++) < 0) {
 			ctx->failed = TRUE;
-			mail_storage_set_index_error(ctx->ibox);
 			return NULL;
 		}
 
-		ret = index_mail_next(&ctx->imail, rec, ctx->seq1++, TRUE);
-		if (ret < 0)
-			break;
-
 		t_push();
 		ret = search_match_next(ctx);
 		t_pop();
--- a/src/lib-storage/index/index-storage.c	Thu Jul 08 23:24:48 2004 +0300
+++ b/src/lib-storage/index/index-storage.c	Thu Jul 08 23:26:15 2004 +0300
@@ -1,6 +1,7 @@
 /* Copyright (C) 2002-2003 Timo Sirainen */
 
 #include "lib.h"
+#include "buffer.h"
 #include "ioloop.h"
 #include "mail-index.h"
 #include "index-storage.h"
@@ -172,15 +173,17 @@
 	destroy_unrefed(TRUE);
 }
 
-static enum mail_cache_field get_cache_fields(const char *fields)
+static void set_cache_fields(const char *fields,
+			     enum mail_cache_decision_type dest[32],
+			     enum mail_cache_decision_type dec)
 {
-	static enum mail_cache_field field_masks[] = {
+	static enum mail_cache_field field_enums[] = {
 		MAIL_CACHE_SENT_DATE,
 		MAIL_CACHE_RECEIVED_DATE,
 		MAIL_CACHE_VIRTUAL_FULL_SIZE,
 		MAIL_CACHE_BODY,
 		MAIL_CACHE_BODYSTRUCTURE,
-		MAIL_CACHE_MESSAGEPART,
+		MAIL_CACHE_MESSAGEPART
 	};
 	static const char *field_names[] = {
 		"sent_date",
@@ -193,17 +196,15 @@
 	};
 
 	const char *const *arr;
-	enum mail_cache_field ret;
 	int i;
 
 	if (fields == NULL || *fields == '\0')
-		return 0;
+		return;
 
-	ret = 0;
 	for (arr = t_strsplit_spaces(fields, " ,"); *arr != NULL; arr++) {
 		for (i = 0; field_names[i] != NULL; i++) {
 			if (strcasecmp(field_names[i], *arr) == 0) {
-				ret |= field_masks[i];
+				dest[field_enums[i]] = dec;
 				break;
 			}
 		}
@@ -212,34 +213,22 @@
 				*arr);
 		}
 	}
-
-	return ret;
 }
 
-static enum mail_cache_field get_default_cache_fields(void)
+static const enum mail_cache_decision_type *get_default_cache_decisions(void)
 {
-	static enum mail_cache_field ret = 0;
-	static int ret_set = FALSE;
-
-	if (ret_set)
-		return ret;
+	static enum mail_cache_decision_type dec[32];
+	static int dec_set = FALSE;
 
-	ret = get_cache_fields(getenv("MAIL_CACHE_FIELDS"));
-	ret_set = TRUE;
-	return ret;
-}
+	if (dec_set)
+		return dec;
 
-static enum mail_cache_field get_never_cache_fields(void)
-{
-	static enum mail_cache_field ret = 0;
-	static int ret_set = FALSE;
-
-	if (ret_set)
-		return ret;
-
-	ret = get_cache_fields(getenv("MAIL_NEVER_CACHE_FIELDS"));
-	ret_set = TRUE;
-	return ret;
+	memset(dec, 0, sizeof(dec));
+	set_cache_fields(getenv("MAIL_CACHE_FIELDS"), dec,
+			 MAIL_CACHE_DECISION_TEMP);
+	set_cache_fields(getenv("MAIL_NEVER_CACHE_FIELDS"), dec,
+			 MAIL_CACHE_DECISION_NO | MAIL_CACHE_DECISION_FORCED);
+	return dec;
 }
 
 void index_storage_lock_notify(struct index_mailbox *ibox,
@@ -339,9 +328,7 @@
 
 		ibox->cache = mail_index_get_cache(index);
 		mail_cache_set_defaults(ibox->cache,
-					get_default_cache_fields(),
-					get_never_cache_fields());
-
+					get_default_cache_decisions());
 		ibox->view = mail_index_view_open(index);
 		return ibox;
 	} while (0);
--- a/src/lib-storage/index/maildir/maildir-mail.c	Thu Jul 08 23:24:48 2004 +0300
+++ b/src/lib-storage/index/maildir/maildir-mail.c	Thu Jul 08 23:26:15 2004 +0300
@@ -87,6 +87,11 @@
 	if (data->received_date != (time_t)-1)
 		return data->received_date;
 
+	if (data->open_mail && data->stream == NULL) {
+		/* we're going to open the mail anyway */
+		(void)_mail->get_stream(_mail, NULL, NULL);
+	}
+
 	if (data->stream != NULL) {
 		fd = i_stream_get_fd(data->stream);
 		i_assert(fd != -1);
@@ -103,8 +108,9 @@
 	}
 
 	data->received_date = st.st_mtime;
-	index_mail_cache_add(mail, MAIL_CACHE_RECEIVED_DATE,
-			     &data->received_date, sizeof(data->received_date));
+	mail_cache_add(mail->trans->cache_trans, mail->data.seq,
+		       MAIL_CACHE_RECEIVED_DATE,
+		       &data->received_date, sizeof(data->received_date));
 	return data->received_date;
 }
 
@@ -141,9 +147,9 @@
 		}
 
 		if (*p == ':' || *p == ',' || *p == '\0') {
-			index_mail_cache_add(mail, MAIL_CACHE_VIRTUAL_FULL_SIZE,
-					     &virtual_size,
-					     sizeof(virtual_size));
+			mail_cache_add(mail->trans->cache_trans, mail->data.seq,
+				       MAIL_CACHE_VIRTUAL_FULL_SIZE,
+				       &virtual_size, sizeof(virtual_size));
 			return virtual_size;
 		}
 	}
@@ -162,8 +168,10 @@
 	if (data->stream == NULL) {
 		data->stream = maildir_open_mail(mail->ibox, mail->mail.uid,
 						 &deleted);
-		if (data->stream == NULL)
+		if (data->stream == NULL) {
+			data->deleted = deleted;
 			return NULL;
+		}
 	}
 
 	return index_mail_init_stream(mail, hdr_size, body_size);
--- a/src/lib-storage/index/maildir/maildir-save.c	Thu Jul 08 23:24:48 2004 +0300
+++ b/src/lib-storage/index/maildir/maildir-save.c	Thu Jul 08 23:26:15 2004 +0300
@@ -201,11 +201,7 @@
 	t_pop();
 
 	if (mail_r != NULL) {
-		const struct mail_index_record *rec;
-
-		if (mail_index_lookup(t->ictx.trans_view, seq, &rec) < 0)
-			return -1;
-		if (index_mail_next(&ctx->mail, rec, seq, FALSE) <= 0)
+		if (index_mail_next(&ctx->mail, seq) < 0)
 			return -1;
 		*mail_r = &ctx->mail.mail;
 	}
--- a/src/lib-storage/index/mbox/mbox-mail.c	Thu Jul 08 23:24:48 2004 +0300
+++ b/src/lib-storage/index/mbox/mbox-mail.c	Thu Jul 08 23:26:15 2004 +0300
@@ -20,6 +20,9 @@
 	uint64_t offset;
 	int ret;
 
+	if (mail->data.deleted)
+		return 0;
+
 	if (ibox->mbox_lock_type == F_UNLCK) {
 		if (mbox_sync(ibox, FALSE, FALSE, TRUE) < 0)
 			return -1;
@@ -36,7 +39,9 @@
 	if (ret <= 0) {
 		if (ret < 0)
 			mail_storage_set_index_error(ibox);
-		return -1;
+		else
+			mail->data.deleted = TRUE;
+		return ret;
 	}
 
 	offset = *((const uint64_t *)data);
@@ -47,7 +52,7 @@
 		mail_index_mark_corrupted(ibox->index);
 		return -1;
 	}
-	return 0;
+	return 1;
 }
 
 static const struct mail_full_flags *mbox_mail_get_flags(struct mail *_mail)
@@ -74,7 +79,7 @@
 	if (data->received_date != (time_t)-1)
 		return data->received_date;
 
-	if (mbox_mail_seek(mail) < 0)
+	if (mbox_mail_seek(mail) <= 0)
 		return (time_t)-1;
 	data->received_date =
 		istream_raw_mbox_get_received_time(mail->ibox->mbox_stream);
@@ -84,9 +89,9 @@
 		data->received_date = 0;
 	}
 
-	index_mail_cache_add(mail, MAIL_CACHE_RECEIVED_DATE,
-			     &data->received_date,
-			     sizeof(data->received_date));
+	mail_cache_add(mail->trans->cache_trans, mail->data.seq,
+		       MAIL_CACHE_RECEIVED_DATE,
+		       &data->received_date, sizeof(data->received_date));
 	return data->received_date;
 }
 
@@ -96,7 +101,7 @@
 	struct index_mail *mail = (struct index_mail *)_mail;
 
 	if (field == MAIL_FETCH_FROM_ENVELOPE) {
-		if (mbox_mail_seek(mail) < 0)
+		if (mbox_mail_seek(mail) <= 0)
 			return NULL;
 
 		return istream_raw_mbox_get_sender(mail->ibox->mbox_stream);
@@ -116,10 +121,9 @@
 	uoff_t offset;
 
 	if (data->stream == NULL) {
-		if (mbox_mail_seek(mail) < 0)
+		if (mbox_mail_seek(mail) <= 0)
 			return NULL;
 
-		// FIXME: need to hide the headers
 		raw_stream = mail->ibox->mbox_stream;
 		offset = istream_raw_mbox_get_header_offset(raw_stream);
 		raw_stream = i_stream_create_limit(default_pool, raw_stream,
--- a/src/lib-storage/index/mbox/mbox-save.c	Thu Jul 08 23:24:48 2004 +0300
+++ b/src/lib-storage/index/mbox/mbox-save.c	Thu Jul 08 23:26:15 2004 +0300
@@ -344,11 +344,7 @@
 	t_pop();
 
 	if (mail_r != NULL) {
-		const struct mail_index_record *rec;
-
-		if (mail_index_lookup(t->ictx.trans_view, seq, &rec) < 0)
-			return -1;
-		if (index_mail_next(&ctx->mail, rec, seq, FALSE) <= 0)
+		if (index_mail_next(&ctx->mail, seq) < 0)
 			return -1;
 		*mail_r = &ctx->mail.mail;
 	}