changeset 4268:7112aad504ae HEAD

Changed mailbox_save_*() API a bit: Moved the struct mail *dest_mail to save_init() instead of being in save_finish(). This way you can request wanted fields from the mail while it's being saved. With maildir the message is being parsed at the same time as it's being saved, and the results are stored into cache file.
author Timo Sirainen <timo.sirainen@movial.fi>
date Tue, 09 May 2006 14:57:36 +0300
parents bd99e8f5e3ac
children b8fd29a53d47
files src/imap/cmd-append.c src/lib-storage/index/dbox/dbox-save.c src/lib-storage/index/dbox/dbox-storage.h 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/maildir/maildir-copy.c src/lib-storage/index/maildir/maildir-save.c src/lib-storage/index/maildir/maildir-storage.h src/lib-storage/index/mbox/mbox-save.c src/lib-storage/index/mbox/mbox-storage.h src/lib-storage/mail-copy.c src/lib-storage/mail-storage-private.h src/lib-storage/mail-storage.c src/lib-storage/mail-storage.h src/plugins/acl/acl-mailbox.c src/plugins/quota/quota-private.h src/plugins/quota/quota-storage.c
diffstat 18 files changed, 227 insertions(+), 133 deletions(-) [+]
line wrap: on
line diff
--- a/src/imap/cmd-append.c	Tue May 09 14:55:04 2006 +0300
+++ b/src/imap/cmd-append.c	Tue May 09 14:57:36 2006 +0300
@@ -273,7 +273,7 @@
 					   ctx->msg_size);
 	ret = mailbox_save_init(ctx->t, flags, keywords,
 				internal_date, timezone_offset, NULL,
-				ctx->input, FALSE, &ctx->save_ctx);
+				ctx->input, NULL, &ctx->save_ctx);
 
 	if (keywords != NULL)
 		mailbox_keywords_free(ctx->t, &keywords);
@@ -335,7 +335,7 @@
 			   whole message. */
 			failed = TRUE;
 			mailbox_save_cancel(&ctx->save_ctx);
-		} else if (mailbox_save_finish(&ctx->save_ctx, NULL) < 0) {
+		} else if (mailbox_save_finish(&ctx->save_ctx) < 0) {
 			failed = TRUE;
 			client_send_storage_error(cmd, ctx->storage);
 		} else {
--- a/src/lib-storage/index/dbox/dbox-save.c	Tue May 09 14:55:04 2006 +0300
+++ b/src/lib-storage/index/dbox/dbox-save.c	Tue May 09 14:57:36 2006 +0300
@@ -7,6 +7,7 @@
 #include "write-full.h"
 #include "ostream.h"
 #include "seq-range-array.h"
+#include "index-mail.h"
 #include "dbox-uidlist.h"
 #include "dbox-keywords.h"
 #include "dbox-sync.h"
@@ -28,7 +29,8 @@
 	uint32_t seq;
 	struct istream *input;
 	struct ostream *output;
-        struct dbox_file *file;
+	struct dbox_file *file;
+	struct mail *mail;
 	uint64_t hdr_offset;
 	uint64_t mail_offset;
 
@@ -98,7 +100,7 @@
 		   enum mail_flags flags, struct mail_keywords *keywords,
 		   time_t received_date, int timezone_offset __attr_unused__,
 		   const char *from_envelope __attr_unused__,
-		   struct istream *input, bool want_mail __attr_unused__,
+		   struct istream *input, struct mail *dest_mail,
 		   struct mail_save_context **ctx_r)
 {
 	struct dbox_transaction_context *t =
@@ -212,6 +214,14 @@
 			      mbox->dbox_offset_ext_idx, &ctx->hdr_offset,
 			      NULL);
 
+	if (dest_mail == NULL) {
+		if (ctx->mail == NULL)
+			ctx->mail = index_mail_alloc(_t, 0, NULL);
+		dest_mail = ctx->mail;
+	}
+	if (mail_set_seq(dest_mail, ctx->seq) < 0)
+		i_unreached();
+
 	if (ctx->first_append_seq == 0)
 		ctx->first_append_seq = ctx->seq;
 	t_pop();
@@ -242,7 +252,7 @@
 	return 0;
 }
 
-int dbox_save_finish(struct mail_save_context *_ctx, struct mail *dest_mail)
+int dbox_save_finish(struct mail_save_context *_ctx)
 {
 	struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx;
 	struct dbox_mail_header hdr;
@@ -282,13 +292,6 @@
 		return -1;
 
 	dbox_uidlist_append_finish_mail(ctx->append_ctx, ctx->file);
-
-	if (dest_mail != NULL) {
-		i_assert(ctx->seq != 0);
-
-		if (mail_set_seq(dest_mail, ctx->seq) < 0)
-			return -1;
-	}
 	return 0;
 }
 
@@ -297,7 +300,7 @@
 	struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx;
 
 	ctx->failed = TRUE;
-	(void)dbox_save_finish(_ctx, NULL);
+	(void)dbox_save_finish(_ctx);
 }
 
 int dbox_transaction_save_commit_pre(struct dbox_save_context *ctx)
@@ -382,6 +385,8 @@
 void dbox_transaction_save_commit_post(struct dbox_save_context *ctx)
 {
 	mail_index_sync_rollback(&ctx->index_sync_ctx);
+	if (ctx->mail != NULL)
+		index_mail_free(ctx->mail);
 	i_free(ctx);
 }
 
@@ -394,5 +399,7 @@
 		mail_index_sync_rollback(&ctx->index_sync_ctx);
 
         dbox_uidlist_append_rollback(ctx->append_ctx);
+	if (ctx->mail != NULL)
+		index_mail_free(ctx->mail);
 	i_free(ctx);
 }
--- a/src/lib-storage/index/dbox/dbox-storage.h	Tue May 09 14:55:04 2006 +0300
+++ b/src/lib-storage/index/dbox/dbox-storage.h	Tue May 09 14:57:36 2006 +0300
@@ -93,9 +93,9 @@
 		   enum mail_flags flags, struct mail_keywords *keywords,
 		   time_t received_date, int timezone_offset,
 		   const char *from_envelope, struct istream *input,
-		   bool want_mail, struct mail_save_context **ctx_r);
+		   struct mail *dest_mail, struct mail_save_context **ctx_r);
 int dbox_save_continue(struct mail_save_context *ctx);
-int dbox_save_finish(struct mail_save_context *ctx, struct mail *dest_mail);
+int dbox_save_finish(struct mail_save_context *ctx);
 void dbox_save_cancel(struct mail_save_context *ctx);
 
 int dbox_transaction_save_commit_pre(struct dbox_save_context *ctx);
--- a/src/lib-storage/index/index-mail-headers.c	Tue May 09 14:55:04 2006 +0300
+++ b/src/lib-storage/index/index-mail-headers.c	Tue May 09 14:57:36 2006 +0300
@@ -1,4 +1,4 @@
-/* Copyright (C) 2003 Timo Sirainen */
+/* Copyright (C) 2003-2006 Timo Sirainen */
 
 #include "lib.h"
 #include "istream.h"
@@ -247,7 +247,6 @@
 			     struct index_mail *mail)
 {
 	struct index_mail_data *data = &mail->data;
-	enum mail_cache_decision_type decision;
 	const char *cache_field_name;
 	unsigned int field_idx, count;
 	uint8_t *match;
@@ -295,17 +294,9 @@
 	}
 
 	if (!hdr->continued) {
-		decision = mail_cache_field_get_decision(mail->ibox->cache,
-							 field_idx);
 		data->parse_line.cache =
-			(decision & ~MAIL_CACHE_DECISION_FORCED) !=
-			MAIL_CACHE_DECISION_NO;
-		if (data->parse_line.cache &&
-		    mail_cache_field_exists(mail->trans->cache_view,
-					    data->seq, field_idx) > 0) {
-			/* already cached */
-			data->parse_line.cache = FALSE;
-		}
+			mail_cache_field_want_add(mail->trans->cache_trans,
+						  data->seq, field_idx);
 	}
 
 	match = array_get_modifyable(&mail->header_match, &count);
--- a/src/lib-storage/index/index-mail.c	Tue May 09 14:55:04 2006 +0300
+++ b/src/lib-storage/index/index-mail.c	Tue May 09 14:57:36 2006 +0300
@@ -1,4 +1,4 @@
-/* Copyright (C) 2002-2003 Timo Sirainen */
+/* Copyright (C) 2002-2006 Timo Sirainen */
 
 #include "lib.h"
 #include "array.h"
@@ -501,11 +501,11 @@
 	else if (field == MAIL_CACHE_IMAP_BODY) {
 		cache_body =
 			mail_cache_field_can_add(mail->trans->cache_trans,
-				data->seq, cache_field_bodystructure);
+				data->seq, cache_field_body);
 	} else {
 		cache_body =
 			mail_cache_field_want_add(mail->trans->cache_trans,
-				data->seq, cache_field_bodystructure);
+				data->seq, cache_field_body);
 	}
 
 	if (field == MAIL_CACHE_IMAP_BODY || cache_body) {
@@ -520,17 +520,38 @@
 	}
 }
 
+static void
+index_mail_body_parsed_cache_virtual_size(struct index_mail *mail)
+{
+	unsigned int cache_field =
+		mail->ibox->cache_fields[MAIL_CACHE_VIRTUAL_FULL_SIZE].idx;
+
+	if (mail_cache_field_want_add(mail->trans->cache_trans,
+				      mail->data.seq, cache_field)) {
+		index_mail_cache_add(mail, cache_field,
+				     &mail->data.virtual_size,
+				     sizeof(mail->data.virtual_size));
+	}
+}
+
 static void index_mail_parse_body_finish(struct index_mail *mail,
-					 enum index_cache_field field)
+					 enum index_cache_field field,
+					 bool appended_mail)
 {
 	mail->data.parts = message_parser_deinit(&mail->data.parser_ctx);
 
-	mail->data.body_size = mail->data.parts->body_size;
-	mail->data.body_size_set = TRUE;
+	if (appended_mail) {
+		bool use_crlf = (mail->ibox->box.storage->flags &
+				 MAIL_STORAGE_FLAG_SAVE_CRLF) != 0;
+		message_parser_set_crlfs(mail->data.parts, use_crlf);
+	}
+
+	(void)get_cached_msgpart_sizes(mail);
 
 	index_mail_body_parsed_cache_flags(mail);
 	index_mail_body_parsed_cache_message_parts(mail);
 	index_mail_body_parsed_cache_bodystructure(mail, field);
+	index_mail_body_parsed_cache_virtual_size(mail);
 }
 
 static void index_mail_parse_body(struct index_mail *mail,
@@ -555,7 +576,7 @@
 	} else {
 		message_parser_parse_body(data->parser_ctx, NULL, NULL);
 	}
-	index_mail_parse_body_finish(mail, field);
+	index_mail_parse_body_finish(mail, field, FALSE);
 }
 
 struct istream *index_mail_init_stream(struct index_mail *_mail,
@@ -981,6 +1002,47 @@
 	pool_unref(mail->mail.pool);
 }
 
+void index_mail_cache_parse_init(struct mail *_mail, struct istream *input)
+{
+	struct index_mail *mail = (struct index_mail *)_mail;
+
+	i_assert(mail->data.parser_ctx == NULL);
+
+	index_mail_parse_header_init(mail, NULL);
+	mail->data.parser_ctx = message_parser_init(mail->data_pool, input);
+}
+
+void index_mail_cache_parse_continue(struct mail *_mail)
+{
+	struct index_mail *mail = (struct index_mail *)_mail;
+	struct message_block block;
+	int ret;
+
+	while ((ret = message_parser_parse_next_block(mail->data.parser_ctx,
+						      &block)) > 0) {
+		if (block.size != 0)
+			continue;
+
+		if (!mail->data.header_parsed) {
+			index_mail_parse_header(block.part, block.hdr, mail);
+			if (block.hdr == NULL)
+				mail->data.header_parsed = TRUE;
+		} else {
+			imap_bodystructure_parse_header(mail->data_pool,
+							block.part, block.hdr);
+		}
+	}
+}
+
+void index_mail_cache_parse_deinit(struct mail *_mail)
+{
+	struct index_mail *mail = (struct index_mail *)_mail;
+
+	mail->data.save_bodystructure_body = FALSE;
+	mail->data.parsed_bodystructure = TRUE;
+	index_mail_parse_body_finish(mail, 0, TRUE);
+}
+
 int index_mail_update_flags(struct mail *mail, enum modify_type modify_type,
 			    enum mail_flags flags)
 {
--- a/src/lib-storage/index/index-mail.h	Tue May 09 14:55:04 2006 +0300
+++ b/src/lib-storage/index/index-mail.h	Tue May 09 14:57:36 2006 +0300
@@ -101,6 +101,7 @@
 	unsigned int hdr_size_set:1;
 	unsigned int body_size_set:1;
 	unsigned int messageparts_saved_to_cache:1;
+	unsigned int header_parsed:1;
 };
 
 struct index_mail {
@@ -174,4 +175,8 @@
 void index_mail_cache_add(struct index_mail *mail, unsigned int field,
 			  const void *data, size_t data_size);
 
+void index_mail_cache_parse_init(struct mail *mail, struct istream *input);
+void index_mail_cache_parse_continue(struct mail *mail);
+void index_mail_cache_parse_deinit(struct mail *mail);
+
 #endif
--- a/src/lib-storage/index/maildir/maildir-copy.c	Tue May 09 14:55:04 2006 +0300
+++ b/src/lib-storage/index/maildir/maildir-copy.c	Tue May 09 14:57:36 2006 +0300
@@ -108,18 +108,11 @@
 		/* hardlinked to destination, set hardlinked-flag */
 		seq = maildir_save_add(t, dest_fname,
 				       flags | MAILDIR_SAVE_FLAG_HARDLINK, NULL,
-				       dest_mail != NULL);
+				       dest_mail);
 	} else {
 		/* hardlinked to tmp/, treat as normal copied mail */
 		seq = maildir_save_add(t, dest_fname, flags, keywords,
-				       dest_mail != NULL);
-	}
-
-	if (dest_mail != NULL) {
-		i_assert(seq != 0);
-
-		if (mail_set_seq(dest_mail, seq) < 0)
-			return -1;
+				       dest_mail);
 	}
 	return 1;
 }
--- a/src/lib-storage/index/maildir/maildir-save.c	Tue May 09 14:55:04 2006 +0300
+++ b/src/lib-storage/index/maildir/maildir-save.c	Tue May 09 14:57:36 2006 +0300
@@ -4,9 +4,12 @@
 #include "ioloop.h"
 #include "array.h"
 #include "buffer.h"
+#include "istream.h"
+#include "istream-tee.h"
 #include "ostream.h"
 #include "ostream-crlf.h"
 #include "str.h"
+#include "index-mail.h"
 #include "maildir-storage.h"
 #include "maildir-uidlist.h"
 
@@ -34,6 +37,7 @@
 	struct mail_index_transaction *trans;
 	struct maildir_uidlist_sync_ctx *uidlist_sync_ctx;
 	struct maildir_index_sync_context *sync_ctx;
+	struct mail *mail, *cur_dest_mail;
 
 	const char *tmpdir, *newdir, *curdir;
 	struct maildir_filename *files, **files_tail;
@@ -41,7 +45,7 @@
 	buffer_t *keywords_buffer;
 	array_t ARRAY_DEFINE(keywords_array, unsigned int);
 
-	struct istream *input;
+	struct istream *input, *input2;
 	struct ostream *output;
 	int fd;
 	time_t received_date;
@@ -125,12 +129,14 @@
 
 uint32_t maildir_save_add(struct maildir_transaction_context *t,
 			  const char *base_fname, enum mail_flags flags,
-			  struct mail_keywords *keywords, bool want_mail)
+			  struct mail_keywords *keywords,
+			  struct mail *dest_mail)
 {
 	struct maildir_save_context *ctx = t->save_ctx;
 	struct maildir_filename *mf;
+	struct tee_istream *tee;
 
-	if (want_mail)
+	if (dest_mail != NULL)
 		ctx->want_mails = TRUE;
 
 	/* now, we want to be able to rollback the whole append session,
@@ -171,8 +177,28 @@
 			ctx->first_seq = ctx->seq;
 			i_assert(ctx->files->next == NULL);
 		}
+
+		if (dest_mail == NULL) {
+			if (ctx->mail == NULL) {
+				struct mailbox_transaction_context *_t =
+					&t->ictx.mailbox_ctx;
+
+				ctx->mail = index_mail_alloc(_t, 0, NULL);
+			}
+			dest_mail = ctx->mail;
+		}
+		if (mail_set_seq(dest_mail, ctx->seq) < 0)
+			i_unreached();
+
+		tee = tee_i_stream_create(ctx->input, default_pool);
+		ctx->input = tee_i_stream_create_child(tee, default_pool);
+		ctx->input2 = tee_i_stream_create_child(tee, default_pool);
+
+		index_mail_cache_parse_init(dest_mail, ctx->input2);
+		ctx->cur_dest_mail = dest_mail;
 	} else {
 		ctx->seq = 0;
+		ctx->cur_dest_mail = NULL;
 	}
 
 	return ctx->seq;
@@ -241,7 +267,7 @@
 		      enum mail_flags flags, struct mail_keywords *keywords,
 		      time_t received_date, int timezone_offset __attr_unused__,
 		      const char *from_envelope __attr_unused__,
-		      struct istream *input, bool want_mail,
+		      struct istream *input, struct mail *dest_mail,
 		      struct mail_save_context **ctx_r)
 {
 	struct maildir_transaction_context *t =
@@ -286,7 +312,7 @@
 	if (mbox->ibox.keep_recent)
 		flags |= MAIL_RECENT;
 
-	maildir_save_add(t, fname, flags, keywords, want_mail);
+	maildir_save_add(t, fname, flags, keywords, dest_mail);
 
 	t_pop();
 	*ctx_r = &ctx->ctx;
@@ -300,6 +326,9 @@
 	if (ctx->failed)
 		return -1;
 
+	if (ctx->cur_dest_mail != NULL)
+		index_mail_cache_parse_continue(ctx->cur_dest_mail);
+
 	if (o_stream_send_istream(ctx->output, ctx->input) < 0) {
 		mail_storage_set_critical(STORAGE(ctx->mbox->storage),
 			"o_stream_send_istream(%s) failed: %m",
@@ -311,13 +340,19 @@
 	return 0;
 }
 
-int maildir_save_finish(struct mail_save_context *_ctx, struct mail *dest_mail)
+int maildir_save_finish(struct mail_save_context *_ctx)
 {
 	struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx;
 	struct utimbuf buf;
 	const char *path;
 	int output_errno;
 
+	if (ctx->cur_dest_mail != NULL) {
+		index_mail_cache_parse_deinit(ctx->cur_dest_mail);
+		i_stream_unref(&ctx->input);
+		i_stream_unref(&ctx->input2);
+	}
+
 	ctx->finished = TRUE;
 	if (ctx->failed && ctx->fd == -1) {
 		/* tmp file creation failed */
@@ -378,13 +413,6 @@
 	}
 	t_pop();
 
-	if (dest_mail != NULL) {
-		i_assert(ctx->seq != 0);
-
-		if (mail_set_seq(dest_mail, ctx->seq) < 0)
-			return -1;
-	}
-
 	return 0;
 }
 
@@ -393,7 +421,7 @@
 	struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx;
 
 	ctx->failed = TRUE;
-	(void)maildir_save_finish(_ctx, NULL);
+	(void)maildir_save_finish(_ctx);
 }
 
 static void
@@ -499,6 +527,9 @@
 	/* uidlist locks the syncing. don't release it until save's transaction
 	   has been written to disk. */
 	(void)maildir_uidlist_sync_deinit(&ctx->uidlist_sync_ctx);
+
+	if (ctx->mail != NULL)
+		index_mail_free(ctx->mail);
 	pool_unref(ctx->pool);
 }
 
@@ -543,5 +574,7 @@
 
 	t_pop();
 
+	if (ctx->mail != NULL)
+		index_mail_free(ctx->mail);
 	pool_unref(ctx->pool);
 }
--- a/src/lib-storage/index/maildir/maildir-storage.h	Tue May 09 14:55:04 2006 +0300
+++ b/src/lib-storage/index/maildir/maildir-storage.h	Tue May 09 14:57:36 2006 +0300
@@ -122,16 +122,17 @@
 		      enum mail_flags flags, struct mail_keywords *keywords,
 		      time_t received_date, int timezone_offset,
 		      const char *from_envelope, struct istream *input,
-		      bool want_mail, struct mail_save_context **ctx_r);
+		      struct mail *dest_mail, struct mail_save_context **ctx_r);
 int maildir_save_continue(struct mail_save_context *ctx);
-int maildir_save_finish(struct mail_save_context *ctx, struct mail *dest_mail);
+int maildir_save_finish(struct mail_save_context *ctx);
 void maildir_save_cancel(struct mail_save_context *ctx);
 
 struct maildir_save_context *
 maildir_save_transaction_init(struct maildir_transaction_context *t);
 uint32_t maildir_save_add(struct maildir_transaction_context *t,
 			  const char *base_fname, enum mail_flags flags,
-			  struct mail_keywords *keywords, bool want_mail);
+			  struct mail_keywords *keywords,
+			  struct mail *dest_mail);
 const char *maildir_save_file_get_path(struct mailbox_transaction_context *t,
 				       uint32_t seq);
 
--- a/src/lib-storage/index/mbox/mbox-save.c	Tue May 09 14:55:04 2006 +0300
+++ b/src/lib-storage/index/mbox/mbox-save.c	Tue May 09 14:57:36 2006 +0300
@@ -344,7 +344,7 @@
 		   enum mail_flags flags, struct mail_keywords *keywords,
 		   time_t received_date, int timezone_offset __attr_unused__,
 		   const char *from_envelope, struct istream *input,
-		   bool want_mail, struct mail_save_context **ctx_r)
+		   struct mail *dest_mail, struct mail_save_context **ctx_r)
 {
 	struct mbox_transaction_context *t =
 		(struct mbox_transaction_context *)_t;
@@ -372,7 +372,7 @@
 	ctx->failed = FALSE;
 	ctx->seq = 0;
 
-	if (mbox_save_init_file(ctx, t, want_mail) < 0) {
+	if (mbox_save_init_file(ctx, t, dest_mail != NULL) < 0) {
 		ctx->failed = TRUE;
 		return -1;
 	}
@@ -399,10 +399,14 @@
 		ctx->next_uid++;
 
 		/* parse and cache the mail headers as we read it */
-		if (ctx->mail == NULL)
-			ctx->mail = index_mail_alloc(_t, 0, NULL);
-		mail_set_seq(ctx->mail, ctx->seq);
-		index_mail_parse_header_init((struct index_mail *)ctx->mail,
+		if (dest_mail == NULL) {
+			if (ctx->mail == NULL)
+				ctx->mail = index_mail_alloc(_t, 0, NULL);
+			dest_mail = ctx->mail;
+		}
+		if (mail_set_seq(dest_mail, ctx->seq) < 0)
+			i_unreached();
+		index_mail_parse_header_init((struct index_mail *)dest_mail,
 					     NULL);
 	}
 	mbox_save_append_flag_headers(ctx->headers, save_flags);
@@ -516,7 +520,7 @@
 	return ctx->input->eof && size == 0 ? 0 : mbox_save_continue(_ctx);
 }
 
-int mbox_save_finish(struct mail_save_context *_ctx, struct mail *dest_mail)
+int mbox_save_finish(struct mail_save_context *_ctx)
 {
 	struct mbox_save_context *ctx = (struct mbox_save_context *)_ctx;
 
@@ -539,17 +543,7 @@
 		ctx->mail_offset = (uoff_t)-1;
 	}
 
-	if (ctx->failed)
-		return -1;
-
-	if (dest_mail != NULL) {
-		i_assert(ctx->seq != 0);
-
-		if (mail_set_seq(dest_mail, ctx->seq) < 0)
-			return -1;
-	}
-
-	return 0;
+	return ctx->failed ? -1 : 0;
 }
 
 void mbox_save_cancel(struct mail_save_context *_ctx)
@@ -557,7 +551,7 @@
 	struct mbox_save_context *ctx = (struct mbox_save_context *)_ctx;
 
 	ctx->failed = TRUE;
-	(void)mbox_save_finish(_ctx, NULL);
+	(void)mbox_save_finish(_ctx);
 }
 
 static void mbox_transaction_save_deinit(struct mbox_save_context *ctx)
--- a/src/lib-storage/index/mbox/mbox-storage.h	Tue May 09 14:55:04 2006 +0300
+++ b/src/lib-storage/index/mbox/mbox-storage.h	Tue May 09 14:57:36 2006 +0300
@@ -84,9 +84,9 @@
 		   enum mail_flags flags, struct mail_keywords *keywords,
 		   time_t received_date, int timezone_offset,
 		   const char *from_envelope, struct istream *input,
-		   bool want_mail, struct mail_save_context **ctx_r);
+		   struct mail *dest_mail, struct mail_save_context **ctx_r);
 int mbox_save_continue(struct mail_save_context *ctx);
-int mbox_save_finish(struct mail_save_context *ctx, struct mail *dest_mail);
+int mbox_save_finish(struct mail_save_context *ctx);
 void mbox_save_cancel(struct mail_save_context *ctx);
 
 int mbox_transaction_save_commit(struct mbox_save_context *ctx);
--- a/src/lib-storage/mail-copy.c	Tue May 09 14:55:04 2006 +0300
+++ b/src/lib-storage/mail-copy.c	Tue May 09 14:57:36 2006 +0300
@@ -21,8 +21,7 @@
 
 	if (mailbox_save_init(t, flags, keywords,
 			      mail_get_received_date(mail),
-			      0, from_envelope, input, dest_mail != NULL,
-			      &ctx) < 0)
+			      0, from_envelope, input, dest_mail, &ctx) < 0)
 		return -1;
 
 	while (i_stream_read(input) != -1) {
@@ -35,5 +34,5 @@
 		return -1;
 	}
 
-	return mailbox_save_finish(&ctx, dest_mail);
+	return mailbox_save_finish(&ctx);
 }
--- a/src/lib-storage/mail-storage-private.h	Tue May 09 14:55:04 2006 +0300
+++ b/src/lib-storage/mail-storage-private.h	Tue May 09 14:57:36 2006 +0300
@@ -147,10 +147,10 @@
 			 struct mail_keywords *keywords,
 			 time_t received_date, int timezone_offset,
 			 const char *from_envelope, struct istream *input,
-			 bool want_mail, struct mail_save_context **ctx_r);
+			 struct mail *dest_mail,
+			 struct mail_save_context **ctx_r);
 	int (*save_continue)(struct mail_save_context *ctx);
-	int (*save_finish)(struct mail_save_context *ctx,
-			   struct mail *dest_mail);
+	int (*save_finish)(struct mail_save_context *ctx);
 	void (*save_cancel)(struct mail_save_context *ctx);
 
 	int (*copy)(struct mailbox_transaction_context *t, struct mail *mail,
@@ -231,6 +231,7 @@
 
 struct mail_save_context {
 	struct mailbox_transaction_context *transaction;
+	struct mail *dest_mail;
 };
 
 struct mailbox_sync_context {
--- a/src/lib-storage/mail-storage.c	Tue May 09 14:55:04 2006 +0300
+++ b/src/lib-storage/mail-storage.c	Tue May 09 14:57:36 2006 +0300
@@ -499,11 +499,15 @@
 		      enum mail_flags flags, struct mail_keywords *keywords,
 		      time_t received_date, int timezone_offset,
 		      const char *from_envelope, struct istream *input,
-		      bool want_mail, struct mail_save_context **ctx_r)
+		      struct mail *dest_mail, struct mail_save_context **ctx_r)
 {
-	return t->box->v.save_init(t, flags, keywords,
-				   received_date, timezone_offset,
-				   from_envelope, input, want_mail, ctx_r);
+	if (t->box->v.save_init(t, flags, keywords,
+				received_date, timezone_offset,
+				from_envelope, input, dest_mail, ctx_r) < 0)
+		return -1;
+
+	(*ctx_r)->dest_mail = dest_mail;
+	return 0;
 }
 
 int mailbox_save_continue(struct mail_save_context *ctx)
@@ -511,12 +515,12 @@
 	return ctx->transaction->box->v.save_continue(ctx);
 }
 
-int mailbox_save_finish(struct mail_save_context **_ctx, struct mail *dest_mail)
+int mailbox_save_finish(struct mail_save_context **_ctx)
 {
 	struct mail_save_context *ctx = *_ctx;
 
 	*_ctx = NULL;
-	return ctx->transaction->box->v.save_finish(ctx, dest_mail);
+	return ctx->transaction->box->v.save_finish(ctx);
 }
 
 void mailbox_save_cancel(struct mail_save_context **_ctx)
--- a/src/lib-storage/mail-storage.h	Tue May 09 14:55:04 2006 +0300
+++ b/src/lib-storage/mail-storage.h	Tue May 09 14:57:36 2006 +0300
@@ -397,16 +397,22 @@
    minutes in which received_date was originally given with. To use
    current time, set received_date to (time_t)-1.
 
-   If want_mail is TRUE, mail_r will be set in mailbox_save_finish() and
-   the saved message can be accessed using it. Note that setting it may
-   require mailbox syncing, so don't set it unless you need it. */
+   If dest_mail is set, the saved message can be accessed using it. Note that
+   setting it may require mailbox syncing, so don't set it unless you need
+   it. Also you shouldn't try to access it before mailbox_save_finish() is
+   called.
+
+   The given input stream is never read in these functions, only the data
+   inside it is used. So you should call i_stream_read() yourself and then
+   call mailbox_save_continue() whenever more data is read.
+*/
 int mailbox_save_init(struct mailbox_transaction_context *t,
 		      enum mail_flags flags, struct mail_keywords *keywords,
 		      time_t received_date, int timezone_offset,
 		      const char *from_envelope, struct istream *input,
-		      bool want_mail, struct mail_save_context **ctx_r);
+		      struct mail *dest_mail, struct mail_save_context **ctx_r);
 int mailbox_save_continue(struct mail_save_context *ctx);
-int mailbox_save_finish(struct mail_save_context **ctx, struct mail *dest_mail);
+int mailbox_save_finish(struct mail_save_context **ctx);
 void mailbox_save_cancel(struct mail_save_context **ctx);
 
 /* Copy given message. If dest_mail is non-NULL, the copied message can be
--- a/src/plugins/acl/acl-mailbox.c	Tue May 09 14:55:04 2006 +0300
+++ b/src/plugins/acl/acl-mailbox.c	Tue May 09 14:57:36 2006 +0300
@@ -179,7 +179,7 @@
 	      enum mail_flags flags, struct mail_keywords *keywords,
 	      time_t received_date, int timezone_offset,
 	      const char *from_envelope, struct istream *input,
-	      bool want_mail, struct mail_save_context **ctx_r)
+	      struct mail *dest_mail, struct mail_save_context **ctx_r)
 {
 	struct acl_mailbox *abox = ACL_CONTEXT(t->box);
 
@@ -188,7 +188,7 @@
 
 	return abox->super.save_init(t, flags, keywords, received_date,
 				     timezone_offset, from_envelope,
-				     input, want_mail, ctx_r);
+				     input, dest_mail, ctx_r);
 }
 
 static int
--- a/src/plugins/quota/quota-private.h	Tue May 09 14:55:04 2006 +0300
+++ b/src/plugins/quota/quota-private.h	Tue May 09 14:57:36 2006 +0300
@@ -88,6 +88,7 @@
 
 	array_t ARRAY_DEFINE(root_transactions,
 			     struct quota_root_transaction_context *);
+	struct mail *mail;
 };
 
 struct quota_root_transaction_context {
--- a/src/plugins/quota/quota-storage.c	Tue May 09 14:55:04 2006 +0300
+++ b/src/plugins/quota/quota-storage.c	Tue May 09 14:57:36 2006 +0300
@@ -75,6 +75,8 @@
 		return -1;
 	} else {
 		(void)quota_transaction_commit(qt);
+		if (qt->mail != NULL)
+			mail_free(&qt->mail);
 		return 0;
 	}
 }
@@ -86,6 +88,9 @@
 	struct quota_transaction_context *qt = QUOTA_CONTEXT(ctx);
 
 	qbox->super.transaction_rollback(ctx);
+
+	if (qt->mail != NULL)
+		mail_free(&qt->mail);
 	quota_transaction_rollback(qt);
 }
 
@@ -134,26 +139,25 @@
 	   enum mail_flags flags, struct mail_keywords *keywords,
 	   struct mail *dest_mail)
 {
+	struct quota_transaction_context *qt = QUOTA_CONTEXT(t);
 	struct quota_mailbox *qbox = QUOTA_CONTEXT(t->box);
-	struct mail *copy_dest_mail;
-	int ret;
 
-	if (dest_mail != NULL)
-		copy_dest_mail = dest_mail;
-	else
-                copy_dest_mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE, NULL);
+	if (dest_mail == NULL) {
+		/* we always want to know the mail size */
+		if (qt->mail == NULL) {
+			qt->mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE,
+					      NULL);
+		}
+		dest_mail = qt->mail;
+	}
 
 	qbox->save_hack = FALSE;
-	if (qbox->super.copy(t, mail, flags, keywords, copy_dest_mail) < 0)
+	if (qbox->super.copy(t, mail, flags, keywords, dest_mail) < 0)
 		return -1;
 
 	/* if copying used saving internally, we already checked the quota
 	   and set qbox->save_hack = TRUE. */
-	ret = qbox->save_hack ? 0 : quota_check(t, copy_dest_mail);
-
-	if (copy_dest_mail != dest_mail)
-		mail_free(&copy_dest_mail);
-	return ret;
+	return qbox->save_hack ? 0 : quota_check(t, dest_mail);
 }
 
 static int
@@ -161,8 +165,7 @@
 		enum mail_flags flags, struct mail_keywords *keywords,
 		time_t received_date, int timezone_offset,
 		const char *from_envelope, struct istream *input,
-		bool want_mail __attr_unused__,
-		struct mail_save_context **ctx_r)
+		struct mail *dest_mail, struct mail_save_context **ctx_r)
 {
 	struct quota_transaction_context *qt = QUOTA_CONTEXT(t);
 	struct quota_mailbox *qbox = QUOTA_CONTEXT(t->box);
@@ -194,35 +197,29 @@
 		}
 	}
 
-	/* note that we set want_mail = TRUE in here. */
+	if (dest_mail == NULL) {
+		/* we always want to know the mail size */
+		if (qt->mail == NULL) {
+			qt->mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE,
+					      NULL);
+		}
+		dest_mail = qt->mail;
+	}
+
 	return qbox->super.save_init(t, flags, keywords, received_date,
 				     timezone_offset, from_envelope,
-				     input, TRUE, ctx_r);
+				     input, dest_mail, ctx_r);
 }
 
-static int quota_save_finish(struct mail_save_context *ctx,
-			     struct mail *dest_mail)
+static int quota_save_finish(struct mail_save_context *ctx)
 {
 	struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->transaction->box);
-	struct mail *save_dest_mail;
-	int ret;
 
-	if (dest_mail != NULL)
-		save_dest_mail = dest_mail;
-	else {
-		save_dest_mail = mail_alloc(ctx->transaction,
-					    MAIL_FETCH_PHYSICAL_SIZE, NULL);
-	}
-
-	if (qbox->super.save_finish(ctx, save_dest_mail) < 0)
+	if (qbox->super.save_finish(ctx) < 0)
 		return -1;
 
 	qbox->save_hack = TRUE;
-	ret = quota_check(ctx->transaction, save_dest_mail);
-
-	if (save_dest_mail != dest_mail)
-		mail_free(&save_dest_mail);
-	return ret;
+	return quota_check(ctx->transaction, ctx->dest_mail);
 }
 
 static struct mailbox *