changeset 4044:da1d65e064f8 HEAD

mailbox_save_init() supports now returning failure. Quota plugin now checks if mail is too large in mailbox_save_init() hook. APPEND fails before giving "+ OK" reply if mailbox_save_init() failed. Also fixed some APPEND failure cases where the next command was eaten away.
author Timo Sirainen <timo.sirainen@movial.fi>
date Wed, 22 Feb 2006 18:02:20 +0200
parents ec4061662609
children 5819e0190400
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/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/quota/quota-dict.c src/plugins/quota/quota-dirsize.c src/plugins/quota/quota-fs.c src/plugins/quota/quota-private.h src/plugins/quota/quota-storage.c src/plugins/quota/quota.c src/plugins/quota/quota.h
diffstat 18 files changed, 192 insertions(+), 105 deletions(-) [+]
line wrap: on
line diff
--- a/src/imap/cmd-append.c	Wed Feb 22 17:48:33 2006 +0200
+++ b/src/imap/cmd-append.c	Wed Feb 22 18:02:20 2006 +0200
@@ -110,8 +110,6 @@
 
 static void cmd_append_finish(struct cmd_append_context *ctx)
 {
-	ctx->client->input_skip_line = TRUE;
-
 	io_remove(&ctx->client->io);
 
         imap_parser_destroy(&ctx->save_parser);
@@ -203,6 +201,9 @@
 		/* last message */
 		enum mailbox_sync_flags sync_flags;
 
+		/* eat away the trailing CRLF */
+		client->input_skip_line = TRUE;
+
 		if (ctx->box == NULL) {
 			/* we failed earlier, error message is sent */
 			cmd_append_finish(ctx);
@@ -263,25 +264,31 @@
 		return TRUE;
 	}
 
-	if (!nonsync) {
-		o_stream_send(client->output, "+ OK\r\n", 6);
-		o_stream_flush(client->output);
-		o_stream_uncork(client->output);
+	/* save the mail */
+	ctx->input = i_stream_create_limit(default_pool, client->input,
+					   client->input->v_offset,
+					   ctx->msg_size);
+	ret = mailbox_save_init(ctx->t, flags, keywords,
+				internal_date, timezone_offset, NULL,
+				ctx->input, FALSE, &ctx->save_ctx);
+
+	if (keywords != NULL)
+		mailbox_keywords_free(ctx->t, &keywords);
+
+	if (ret < 0) {
+		/* save initialization failed */
+		client_send_storage_error(cmd, ctx->storage);
+		return cmd_append_cancel(ctx, nonsync);
 	}
 
 	/* after literal comes CRLF, if we fail make sure we eat it away */
 	client->input_skip_line = TRUE;
 
-	/* save the mail */
-	ctx->input = i_stream_create_limit(default_pool, client->input,
-					   client->input->v_offset,
-					   ctx->msg_size);
-	ctx->save_ctx = mailbox_save_init(ctx->t, flags, keywords,
-					  internal_date, timezone_offset, NULL,
-					  ctx->input, FALSE);
-
-	if (keywords != NULL)
-		mailbox_keywords_free(ctx->t, &keywords);
+	if (!nonsync) {
+		o_stream_send(client->output, "+ OK\r\n", 6);
+		o_stream_flush(client->output);
+		o_stream_uncork(client->output);
+	}
 
 	client->command_pending = TRUE;
 	cmd->func = cmd_append_continue_message;
--- a/src/lib-storage/index/dbox/dbox-save.c	Wed Feb 22 17:48:33 2006 +0200
+++ b/src/lib-storage/index/dbox/dbox-save.c	Wed Feb 22 18:02:20 2006 +0200
@@ -32,7 +32,8 @@
 	uint64_t hdr_offset;
 	uint64_t mail_offset;
 
-	bool failed;
+	unsigned int failed:1;
+	unsigned int finished:1;
 };
 
 static int
@@ -93,12 +94,12 @@
 	return ret;
 }
 
-struct mail_save_context *
-dbox_save_init(struct mailbox_transaction_context *_t,
-	       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__)
+int dbox_save_init(struct mailbox_transaction_context *_t,
+		   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 mail_save_context **ctx_r)
 {
 	struct dbox_transaction_context *t =
 		(struct dbox_transaction_context *)_t;
@@ -125,12 +126,12 @@
 
 		if ((ret = dbox_sync_if_changed(mbox)) < 0) {
 			ctx->failed = TRUE;
-			return &ctx->ctx;
+			return -1;
 		}
 		if (ret > 0) {
 			if (dbox_sync(mbox, FALSE) < 0) {
 				ctx->failed = TRUE;
-				return &ctx->ctx;
+				return -1;
 			}
 		}
 	}
@@ -138,7 +139,7 @@
 
 	if (dbox_uidlist_append_locked(ctx->append_ctx, &ctx->file) < 0) {
 		ctx->failed = TRUE;
-		return &ctx->ctx;
+		return -1;
 	}
 	ctx->hdr_offset = ctx->file->output->offset;
 
@@ -150,7 +151,7 @@
 		if (dbox_save_add_keywords(ctx, keywords, file_keywords) < 0) {
 			ctx->failed = TRUE;
 			t_pop();
-			return &ctx->ctx;
+			return -1;
 		}
 		o_stream_seek(ctx->file->output, ctx->hdr_offset);
 	}
@@ -214,7 +215,9 @@
 	if (ctx->first_append_seq == 0)
 		ctx->first_append_seq = ctx->seq;
 	t_pop();
-	return &ctx->ctx;
+
+	*ctx_r = &ctx->ctx;
+	return ctx->failed ? -1 : 0;
 }
 
 int dbox_save_continue(struct mail_save_context *_ctx)
@@ -244,6 +247,7 @@
 	struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx;
 	struct dbox_mail_header hdr;
 
+	ctx->finished = TRUE;
 	if (!ctx->failed) {
 		/* write mail size to header */
 		DEC2HEX(hdr.mail_size_hex,
@@ -295,6 +299,8 @@
 	uoff_t offset;
 	int ret;
 
+	i_assert(ctx->finished);
+
 	/* we want the index file to be locked from here until the appends
 	   have been written to transaction log. this is so that the
 	   transaction log gets locked before uidlist, not after */
@@ -367,6 +373,9 @@
 
 void dbox_transaction_save_rollback(struct dbox_save_context *ctx)
 {
+	if (!ctx->finished)
+		dbox_save_cancel(&ctx->ctx);
+
 	if (ctx->index_sync_ctx != NULL)
 		mail_index_sync_rollback(&ctx->index_sync_ctx);
 
--- a/src/lib-storage/index/dbox/dbox-storage.h	Wed Feb 22 17:48:33 2006 +0200
+++ b/src/lib-storage/index/dbox/dbox-storage.h	Wed Feb 22 18:02:20 2006 +0200
@@ -145,12 +145,11 @@
 			    enum mailbox_sync_flags flags);
 void dbox_transaction_rollback(struct mailbox_transaction_context *t);
 
-struct mail_save_context *
-dbox_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);
+int dbox_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);
 int dbox_save_continue(struct mail_save_context *ctx);
 int dbox_save_finish(struct mail_save_context *ctx, struct mail *dest_mail);
 void dbox_save_cancel(struct mail_save_context *ctx);
--- a/src/lib-storage/index/maildir/maildir-save.c	Wed Feb 22 17:48:33 2006 +0200
+++ b/src/lib-storage/index/maildir/maildir-save.c	Wed Feb 22 18:02:20 2006 +0200
@@ -49,6 +49,7 @@
 
 	unsigned int synced:1;
 	unsigned int failed:1;
+	unsigned int finished:1;
 };
 
 static int maildir_file_move(struct maildir_save_context *ctx,
@@ -115,12 +116,12 @@
 	return ctx;
 }
 
-struct mail_save_context *
-maildir_save_init(struct mailbox_transaction_context *_t,
-		  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)
+int maildir_save_init(struct mailbox_transaction_context *_t,
+		      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 mail_save_context **ctx_r)
 {
 	struct maildir_transaction_context *t =
 		(struct maildir_transaction_context *)_t;
@@ -144,7 +145,7 @@
 	if (ctx->fd == -1) {
 		ctx->failed = TRUE;
 		t_pop();
-		return &ctx->ctx;
+		return -1;
 	}
 
 	fname = strrchr(path, '/');
@@ -205,8 +206,8 @@
 	}
 	t_pop();
 
-	ctx->failed = FALSE;
-	return &ctx->ctx;
+	*ctx_r = &ctx->ctx;
+	return ctx->failed ? -1 : 0;
 }
 
 int maildir_save_continue(struct mail_save_context *_ctx)
@@ -234,6 +235,7 @@
 	const char *path;
 	int output_errno;
 
+	ctx->finished = TRUE;
 	if (ctx->failed && ctx->fd == -1) {
 		/* tmp file creation failed */
 		return -1;
@@ -358,6 +360,7 @@
 	int ret;
 
 	i_assert(ctx->output == NULL);
+	i_assert(ctx->finished);
 
 	/* Start syncing so that keywords_sync_ctx gets set.. */
 	ctx->sync_ctx = maildir_sync_index_begin(ctx->mbox);
@@ -429,6 +432,9 @@
 
 	i_assert(ctx->output == NULL);
 
+	if (!ctx->finished)
+		maildir_save_cancel(&ctx->ctx);
+
 	t_push();
 	str = t_str_new(1024);
 
--- a/src/lib-storage/index/maildir/maildir-storage.h	Wed Feb 22 17:48:33 2006 +0200
+++ b/src/lib-storage/index/maildir/maildir-storage.h	Wed Feb 22 18:02:20 2006 +0200
@@ -112,12 +112,11 @@
 			       enum mailbox_sync_flags flags);
 void maildir_transaction_rollback(struct mailbox_transaction_context *t);
 
-struct mail_save_context *
-maildir_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);
+int maildir_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);
 int maildir_save_continue(struct mail_save_context *ctx);
 int maildir_save_finish(struct mail_save_context *ctx, struct mail *dest_mail);
 void maildir_save_cancel(struct mail_save_context *ctx);
--- a/src/lib-storage/index/mbox/mbox-save.c	Wed Feb 22 17:48:33 2006 +0200
+++ b/src/lib-storage/index/mbox/mbox-save.c	Wed Feb 22 18:02:20 2006 +0200
@@ -47,6 +47,7 @@
 
 	unsigned int synced:1;
 	unsigned int failed:1;
+	unsigned int finished:1;
 };
 
 static char my_hostdomain[256] = "";
@@ -339,11 +340,11 @@
 	}
 }
 
-struct mail_save_context *
-mbox_save_init(struct mailbox_transaction_context *_t,
-	       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)
+int mbox_save_init(struct mailbox_transaction_context *_t,
+		   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 mbox_transaction_context *t =
 		(struct mbox_transaction_context *)_t;
@@ -373,7 +374,7 @@
 
 	if (mbox_save_init_file(ctx, t, want_mail) < 0) {
 		ctx->failed = TRUE;
-		return &ctx->ctx;
+		return -1;
 	}
 
 	save_flags = (flags & ~MAIL_RECENT) | MAIL_RECENT;
@@ -435,7 +436,8 @@
 			ctx->mbox_md5_ctx = mbox_md5_init();
 	}
 
-	return &ctx->ctx;
+	*ctx_r = &ctx->ctx;
+	return ctx->failed ? -1 : 0;
 }
 
 int mbox_save_continue(struct mail_save_context *_ctx)
@@ -518,6 +520,7 @@
 {
 	struct mbox_save_context *ctx = (struct mbox_save_context *)_ctx;
 
+	ctx->finished = TRUE;
 	if (!ctx->failed) {
 		if (mbox_write_content_length(ctx) < 0 ||
 		    mbox_append_lf(ctx) < 0)
@@ -573,6 +576,8 @@
 {
 	int ret = 0;
 
+	i_assert(ctx->finished);
+
 	if (ctx->synced) {
 		mail_index_update_header(ctx->trans,
 			offsetof(struct mail_index_header, next_uid),
@@ -595,6 +600,9 @@
 {
 	struct mbox_mailbox *mbox = ctx->mbox;
 
+	if (!ctx->finished)
+		mbox_save_cancel(&ctx->ctx);
+
 	if (ctx->append_offset != (uoff_t)-1 && mbox->mbox_fd != -1) {
 		i_assert(mbox->mbox_lock_type == F_WRLCK);
 
--- a/src/lib-storage/index/mbox/mbox-storage.h	Wed Feb 22 17:48:33 2006 +0200
+++ b/src/lib-storage/index/mbox/mbox-storage.h	Wed Feb 22 18:02:20 2006 +0200
@@ -79,12 +79,11 @@
 struct mailbox_sync_context *
 mbox_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags);
 
-struct mail_save_context *
-mbox_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);
+int mbox_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);
 int mbox_save_continue(struct mail_save_context *ctx);
 int mbox_save_finish(struct mail_save_context *ctx, struct mail *dest_mail);
 void mbox_save_cancel(struct mail_save_context *ctx);
--- a/src/lib-storage/mail-copy.c	Wed Feb 22 17:48:33 2006 +0200
+++ b/src/lib-storage/mail-copy.c	Wed Feb 22 18:02:20 2006 +0200
@@ -19,9 +19,11 @@
 
 	from_envelope = mail_get_special(mail, MAIL_FETCH_FROM_ENVELOPE);
 
-	ctx = mailbox_save_init(t, flags, keywords,
-				mail_get_received_date(mail),
-				0, from_envelope, input, dest_mail != NULL);
+	if (mailbox_save_init(t, flags, keywords,
+			      mail_get_received_date(mail),
+			      0, from_envelope, input, dest_mail != NULL,
+			      &ctx) < 0)
+		return -1;
 
 	while (i_stream_read(input) != -1) {
 		if (mailbox_save_continue(ctx) < 0)
--- a/src/lib-storage/mail-storage-private.h	Wed Feb 22 17:48:33 2006 +0200
+++ b/src/lib-storage/mail-storage-private.h	Wed Feb 22 18:02:20 2006 +0200
@@ -140,13 +140,12 @@
 	int (*search_deinit)(struct mail_search_context *ctx);
 	int (*search_next)(struct mail_search_context *ctx, struct mail *mail);
 
-	struct mail_save_context *
-		(*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);
+	int (*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);
 	int (*save_continue)(struct mail_save_context *ctx);
 	int (*save_finish)(struct mail_save_context *ctx,
 			   struct mail *dest_mail);
--- a/src/lib-storage/mail-storage.c	Wed Feb 22 17:48:33 2006 +0200
+++ b/src/lib-storage/mail-storage.c	Wed Feb 22 18:02:20 2006 +0200
@@ -495,16 +495,15 @@
 	t->box->v.transaction_rollback(t);
 }
 
-struct mail_save_context *
-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)
+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)
 {
 	return t->box->v.save_init(t, flags, keywords,
 				   received_date, timezone_offset,
-				   from_envelope, input, want_mail);
+				   from_envelope, input, want_mail, ctx_r);
 }
 
 int mailbox_save_continue(struct mail_save_context *ctx)
--- a/src/lib-storage/mail-storage.h	Wed Feb 22 17:48:33 2006 +0200
+++ b/src/lib-storage/mail-storage.h	Wed Feb 22 18:02:20 2006 +0200
@@ -152,6 +152,7 @@
 struct mail_storage;
 struct mail_search_arg;
 struct mail_keywords;
+struct mail_save_context;
 struct mailbox;
 struct mailbox_list_context;
 struct mailbox_transaction_context;
@@ -398,12 +399,11 @@
    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. */
-struct mail_save_context *
-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);
+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);
 int mailbox_save_continue(struct mail_save_context *ctx);
 int mailbox_save_finish(struct mail_save_context **ctx, struct mail *dest_mail);
 void mailbox_save_cancel(struct mail_save_context **ctx);
--- a/src/plugins/quota/quota-dict.c	Wed Feb 22 17:48:33 2006 +0200
+++ b/src/plugins/quota/quota-dict.c	Wed Feb 22 18:02:20 2006 +0200
@@ -225,6 +225,19 @@
 }
 
 static int
+dict_quota_try_alloc_bytes(struct quota_transaction_context *ctx,
+			   uoff_t size, bool *too_large_r)
+{
+	*too_large_r = size > ctx->storage_limit;
+
+	if (ctx->storage_current + ctx->bytes_diff + size > ctx->storage_limit)
+		return 0;
+
+	ctx->bytes_diff += size;
+	return 1;
+}
+
+static int
 dict_quota_try_alloc(struct quota_transaction_context *ctx,
 		     struct mail *mail, bool *too_large_r)
 {
@@ -234,13 +247,7 @@
 	if (size == (uoff_t)-1)
 		return -1;
 
-	*too_large_r = size > ctx->storage_limit;
-
-	if (ctx->storage_current + ctx->bytes_diff + size > ctx->storage_limit)
-		return 0;
-
-	ctx->bytes_diff += size;
-	return 1;
+	return dict_quota_try_alloc_bytes(ctx, size, too_large_r);
 }
 
 static void
@@ -294,6 +301,7 @@
 	dict_quota_transaction_rollback,
 
 	dict_quota_try_alloc,
+	dict_quota_try_alloc_bytes,
 	dict_quota_alloc,
 	dict_quota_free,
 
--- a/src/plugins/quota/quota-dirsize.c	Wed Feb 22 17:48:33 2006 +0200
+++ b/src/plugins/quota/quota-dirsize.c	Wed Feb 22 18:02:20 2006 +0200
@@ -260,6 +260,22 @@
 }
 
 static int
+dirsize_quota_try_alloc_bytes(struct quota_transaction_context *ctx,
+			      uoff_t size, bool *too_large_r)
+{
+	if (ctx->storage_current == (uoff_t)-1)
+		return -1;
+
+	*too_large_r = size > ctx->storage_limit;
+
+	if (ctx->storage_current + ctx->bytes_diff + size > ctx->storage_limit)
+		return 0;
+
+	ctx->bytes_diff += size;
+	return 1;
+}
+
+static int
 dirsize_quota_try_alloc(struct quota_transaction_context *ctx,
 			struct mail *mail, bool *too_large_r)
 {
@@ -272,13 +288,7 @@
 	if (size == (uoff_t)-1)
 		return -1;
 
-	*too_large_r = size > ctx->storage_limit;
-
-	if (ctx->storage_current + ctx->bytes_diff + size > ctx->storage_limit)
-		return 0;
-
-	ctx->bytes_diff += size;
-	return 1;
+	return dirsize_quota_try_alloc_bytes(ctx, size, too_large_r);
 }
 
 static void
@@ -332,6 +342,7 @@
 	dirsize_quota_transaction_rollback,
 
 	dirsize_quota_try_alloc,
+	dirsize_quota_try_alloc_bytes,
 	dirsize_quota_alloc,
 	dirsize_quota_free,
 
--- a/src/plugins/quota/quota-fs.c	Wed Feb 22 17:48:33 2006 +0200
+++ b/src/plugins/quota/quota-fs.c	Wed Feb 22 18:02:20 2006 +0200
@@ -257,6 +257,15 @@
 	return 1;
 }
 
+static int
+fs_quota_try_alloc_bytes(struct quota_transaction_context *ctx __attr_unused__,
+			 uoff_t size __attr_unused__,
+			 bool *too_large_r __attr_unused__)
+{
+	/* no-op */
+	return 1;
+}
+
 static void
 fs_quota_alloc(struct quota_transaction_context *ctx __attr_unused__,
 		struct mail *mail __attr_unused__)
@@ -302,6 +311,7 @@
 	fs_quota_transaction_rollback,
 
 	fs_quota_try_alloc,
+	fs_quota_try_alloc_bytes,
 	fs_quota_alloc,
 	fs_quota_free,
 
--- a/src/plugins/quota/quota-private.h	Wed Feb 22 17:48:33 2006 +0200
+++ b/src/plugins/quota/quota-private.h	Wed Feb 22 18:02:20 2006 +0200
@@ -39,6 +39,8 @@
 
 	int (*try_alloc)(struct quota_transaction_context *ctx,
 			 struct mail *mail, bool *too_large_r);
+	int (*try_alloc_bytes)(struct quota_transaction_context *ctx,
+			       uoff_t size, bool *too_large_r);
 	void (*alloc)(struct quota_transaction_context *ctx, struct mail *mail);
 	void (*free)(struct quota_transaction_context *ctx, struct mail *mail);
 
--- a/src/plugins/quota/quota-storage.c	Wed Feb 22 17:48:33 2006 +0200
+++ b/src/plugins/quota/quota-storage.c	Wed Feb 22 18:02:20 2006 +0200
@@ -152,27 +152,48 @@
 	return ret;
 }
 
-static struct mail_save_context *
+static int
 quota_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 __attr_unused__)
+		bool want_mail __attr_unused__,
+		struct mail_save_context **ctx_r)
 {
+	struct quota_transaction_context *qt = QUOTA_CONTEXT(t);
 	struct quota_mailbox *qbox = QUOTA_CONTEXT(t->box);
 	const struct stat *st;
+	int ret;
 
 	st = i_stream_stat(input, TRUE);
 	if (st != NULL && st->st_size != -1) {
-		/* FIXME: input size is known, check for quota.
-		   the API needs changing however to do this, we'd need to
-		   return failure before "+ OK".. */
+		/* Input size is known, check for quota immediately. This
+		   check isn't perfect, especially because input stream's
+		   linefeeds may contain CR+LFs while physical message would
+		   only contain LFs. With mbox some headers might be skipped
+		   entirely.
+
+		   I think these don't really matter though compared to the
+		   benefit of giving "out of quota" error before sending the
+		   full mail. */
+		bool too_large;
+
+		ret = quota_try_alloc_bytes(qt, st->st_size, &too_large);
+		if (ret == 0) {
+			mail_storage_set_error(t->box->storage,
+					       "Quota exceeded");
+			return -1;
+		} else if (ret < 0) {
+			mail_storage_set_error(t->box->storage,  "%s",
+					       quota_last_error(quota));
+			return -1;
+		}
 	}
 
 	/* note that we set want_mail = TRUE in here. */
 	return qbox->super.save_init(t, flags, keywords, received_date,
 				     timezone_offset, from_envelope,
-				     input, TRUE);
+				     input, TRUE, ctx_r);
 }
 
 static int quota_save_finish(struct mail_save_context *ctx,
--- a/src/plugins/quota/quota.c	Wed Feb 22 17:48:33 2006 +0200
+++ b/src/plugins/quota/quota.c	Wed Feb 22 18:02:20 2006 +0200
@@ -131,6 +131,12 @@
 	return ctx->quota->try_alloc(ctx, mail, too_large_r);
 }
 
+int quota_try_alloc_bytes(struct quota_transaction_context *ctx,
+			  uoff_t size, bool *too_large_r)
+{
+	return ctx->quota->try_alloc_bytes(ctx, size, too_large_r);
+}
+
 void quota_alloc(struct quota_transaction_context *ctx, struct mail *mail)
 {
 	ctx->quota->alloc(ctx, mail);
--- a/src/plugins/quota/quota.h	Wed Feb 22 17:48:33 2006 +0200
+++ b/src/plugins/quota/quota.h	Wed Feb 22 18:02:20 2006 +0200
@@ -54,6 +54,8 @@
    too_large_r is set to TRUE. */
 int quota_try_alloc(struct quota_transaction_context *ctx,
 		    struct mail *mail, bool *too_large_r);
+int quota_try_alloc_bytes(struct quota_transaction_context *ctx,
+			  uoff_t size, bool *too_large_r);
 /* Update quota by allocating/freeing space used by mail. */
 void quota_alloc(struct quota_transaction_context *ctx, struct mail *mail);
 void quota_free(struct quota_transaction_context *ctx, struct mail *mail);