diff src/lib-storage/index/mbox/mbox-save.c @ 2190:755ec9442a58 HEAD

mailbox_save() and mailbox_copy() functions can now return the saved mail so it can be immediately queried. Implemented UIDPLUS extension using it. Maildir implementation missing, so it crashes with it for now.. APPEND with mbox now doesn't require resyncing the mailbox since it updates indexes directly.
author Timo Sirainen <tss@iki.fi>
date Sun, 20 Jun 2004 06:25:32 +0300
parents 1039d3650f5c
children 6033af323a61
line wrap: on
line diff
--- a/src/lib-storage/index/mbox/mbox-save.c	Sun Jun 20 05:06:13 2004 +0300
+++ b/src/lib-storage/index/mbox/mbox-save.c	Sun Jun 20 06:25:32 2004 +0300
@@ -1,6 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
+#include "ioloop.h"
 #include "hostpid.h"
 #include "ostream.h"
 #include "str.h"
@@ -9,8 +10,10 @@
 #include "mbox-file.h"
 #include "mbox-from.h"
 #include "mbox-lock.h"
+#include "mbox-sync-private.h"
 #include "mail-save.h"
 
+#include <stddef.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <fcntl.h>
@@ -19,12 +22,20 @@
 
 struct mbox_save_context {
 	struct index_mailbox *ibox;
+	struct mail_index_transaction *trans;
 	uoff_t append_offset;
 
+	string_t *headers;
+	size_t space_end_idx;
+	uint32_t next_uid;
+
 	struct ostream *output;
-	uoff_t sync_offset, content_length_offset, eoh_offset;
+	uoff_t extra_hdr_offset, eoh_offset;
+
+	struct index_mail mail;
 
 	const struct mail_full_flags *flags;
+	unsigned int synced:1;
 };
 
 static char my_hostdomain[256] = "";
@@ -111,89 +122,50 @@
 	return 0;
 }
 
-static const char *get_system_flags(enum mail_flags flags)
+static int mbox_write_content_length(struct mbox_save_context *ctx)
 {
-	string_t *str;
+	uoff_t end_offset;
+	const char *str;
+	size_t len;
+	int ret = 0;
 
-	if (flags == 0)
-		return "";
-
-	str = t_str_new(32);
-	if (flags & MAIL_SEEN)
-		str_append(str, "Status: R\n");
+	end_offset = ctx->output->offset;
 
-	if (flags & (MAIL_ANSWERED|MAIL_DRAFT|MAIL_FLAGGED|MAIL_DELETED)) {
-		str_append(str, "X-Status: ");
+	/* write Content-Length headers */
+	t_push();
+	str = t_strdup_printf("\nContent-Length: %s",
+			      dec2str(end_offset - (ctx->eoh_offset + 1)));
+	len = strlen(str);
 
-		if ((flags & MAIL_ANSWERED) != 0)
-			str_append_c(str, 'A');
-		if ((flags & MAIL_DELETED) != 0)
-			str_append_c(str, 'D');
-		if ((flags & MAIL_FLAGGED) != 0)
-			str_append_c(str, 'F');
-		if ((flags & MAIL_DRAFT) != 0)
-			str_append_c(str, 'T');
-		str_append_c(str, '\n');
+	if (o_stream_seek(ctx->output, ctx->extra_hdr_offset +
+			  ctx->space_end_idx - len) < 0) {
+		mbox_set_syscall_error(ctx->ibox, "o_stream_seek()");
+		ret = -1;
+	} else if (o_stream_send(ctx->output, str, len) < 0) {
+		write_error(ctx);
+		ret = -1;
+	} else {
+		if (o_stream_seek(ctx->output, end_offset) < 0) {
+			mbox_set_syscall_error(ctx->ibox, "o_stream_seek()");
+			ret = -1;
+		}
 	}
 
-	return str_c(str);
-}
-
-static const char *get_keywords(const struct mail_full_flags *flags)
-{
-	string_t *str;
-	unsigned int i;
-
-	if (flags->keywords_count == 0)
-		return "";
-
-	str = t_str_new(256);
-	for (i = 0; i < flags->keywords_count; i++) {
-		if (str_len(str) > 0)
-			str_append_c(str, ' ');
-		str_append(str, flags->keywords[i]);
-	}
-	return str_c(str);
+	t_pop();
+	return ret;
 }
 
 static int save_header_callback(const char *name, write_func_t *write_func,
 				void *context)
 {
-	static const char *content_length = "Content-Length: ";
 	struct mbox_save_context *ctx = context;
-	const char *str;
-	char *buf;
-	size_t space;
 
 	if (name == NULL) {
-		/* write system flags */
-		str = get_system_flags(ctx->flags->flags);
-		if (write_func(ctx->output, str, strlen(str)) < 0)
-			return -1;
-
-		/* write beginning of content-length header */
-		if (write_func(ctx->output, content_length,
-			       strlen(content_length)) < 0) {
-			write_error(ctx);
+		/* write our extra headers */
+		ctx->extra_hdr_offset = ctx->output->offset;
+		if (write_func(ctx->output, str_data(ctx->headers),
+			       str_len(ctx->headers)) < 0)
 			return -1;
-		}
-		ctx->content_length_offset = ctx->output->offset;
-
-		/* calculate how much space keywords and content-length
-		   value needs, then write that amount of spaces. */
-		space = strlen(get_keywords(ctx->flags));
-		space += sizeof("X-Keywords: ");
-		space += MBOX_HEADER_EXTRA_SPACE + MAX_INT_STRLEN + 1;
-
-		/* @UNSAFE */
-		buf = t_malloc(space);
-		memset(buf, ' ', space-1);
-		buf[space-1] = '\n';
-
-		if (write_func(ctx->output, buf, space) < 0) {
-			write_error(ctx);
-			return -1;
-		}
 		ctx->eoh_offset = ctx->output->offset;
 		return 1;
 	}
@@ -217,6 +189,8 @@
 			return 0;
 		if (strcasecmp(name, "X-Keywords") == 0)
 			return 0;
+		if (strcasecmp(name, "X-IMAP") == 0)
+			return 0;
 		if (strcasecmp(name, "X-IMAPbase") == 0)
 			return 0;
 		break;
@@ -225,51 +199,95 @@
 	return 1;
 }
 
-static int mbox_fix_header(struct mbox_save_context *ctx)
+static int mbox_save_init_sync(struct mbox_transaction_context *t)
 {
-	uoff_t old_offset;
-	const char *str;
-	int crlf = getenv("MAIL_SAVE_CRLF") != NULL;
+	struct mbox_save_context *ctx = t->save_ctx;
+	const struct mail_index_header *hdr;
+
+	if (mail_index_get_header(ctx->ibox->view, &hdr) < 0) {
+		mail_storage_set_index_error(ctx->ibox);
+		return -1;
+	}
+	ctx->next_uid = hdr->next_uid;
+	ctx->synced = TRUE;
+        t->mbox_modified = TRUE;
 
-	old_offset = ctx->output->offset;
-	if (o_stream_seek(ctx->output, ctx->content_length_offset) < 0)
-                return mbox_set_syscall_error(ctx->ibox, "o_stream_seek()");
+	index_mail_init(&t->ictx, &ctx->mail, 0, NULL);
+	return 0;
+}
+
+static void status_flags_append(string_t *str, enum mail_flags flags,
+				const struct mbox_flag_type *flags_list)
+{
+	int i;
+
+	for (i = 0; flags_list[i].chr != 0; i++) {
+		if ((flags & flags_list[i].flag) != 0)
+			str_append_c(str, flags_list[i].chr);
+	}
+}
 
-	/* write value for Content-Length */
-	str = dec2str(old_offset - (ctx->eoh_offset + 1 + crlf));
-	if (o_stream_send_str(ctx->output, str) < 0)
-		return write_error(ctx);
+static void mbox_save_append_flag_headers(string_t *str, enum mail_flags flags)
+{
+	if ((flags & STATUS_FLAGS_MASK) != 0) {
+		str_append(str, "Status: ");
+		status_flags_append(str, flags, mbox_status_flags);
+		str_append_c(str, '\n');
+	}
+
+	if ((flags & XSTATUS_FLAGS_MASK) != 0) {
+		str_append(str, "X-Status: ");
+		status_flags_append(str, flags, mbox_xstatus_flags);
+		str_append_c(str, '\n');
+	}
+}
 
-	/* [CR]LF X-Keywords: */
-	str = crlf ? "\r\nX-Keywords:" : "\nX-Keywords:";
-	if (o_stream_send_str(ctx->output, str) < 0)
-		return write_error(ctx);
+static void mbox_save_append_keyword_headers(struct mbox_save_context *ctx,
+					     const char *const *keywords,
+					     unsigned int count)
+{
+	unsigned char space[MBOX_HEADER_EXTRA_SPACE+1 +
+			    sizeof("Content-Length: \n")-1 + MAX_INT_STRLEN];
+	unsigned int i;
 
-	/* write keywords into X-Keywords */
-	str = get_keywords(ctx->flags);
-	if (o_stream_send_str(ctx->output, str) < 0)
-		return write_error(ctx);
+	str_append(ctx->headers, "X-Keywords:");
+	for (i = 0; i < count; i++) {
+		str_append_c(ctx->headers, ' ');
+		str_append(ctx->headers, keywords[i]);
+	}
 
-	if (o_stream_seek(ctx->output, old_offset) < 0)
-		return mbox_set_syscall_error(ctx->ibox, "o_stream_seek()");
-	return 0;
+	memset(space, ' ', sizeof(space));
+	str_append_n(ctx->headers, space, sizeof(space));
+	ctx->space_end_idx = str_len(ctx->headers);
+	str_append_c(ctx->headers, '\n');
 }
 
 int mbox_save(struct mailbox_transaction_context *_t,
 	      const struct mail_full_flags *flags,
 	      time_t received_date, int timezone_offset __attr_unused__,
-	      const char *from_envelope, struct istream *data)
+	      const char *from_envelope, struct istream *data,
+	      struct mail **mail_r)
 {
 	struct mbox_transaction_context *t =
 		(struct mbox_transaction_context *)_t;
 	struct index_mailbox *ibox = t->ictx.ibox;
 	struct mbox_save_context *ctx = t->save_ctx;
+	enum mail_flags save_flags;
+	keywords_mask_t keywords;
+	uint64_t offset;
+	uint32_t seq = 0;
 	int ret;
 
+	/* FIXME: we could write timezone_offset to From-line.. */
+	if (received_date == (time_t)-1)
+		received_date = ioloop_time;
+
 	if (ctx == NULL) {
 		ctx = t->save_ctx = i_new(struct mbox_save_context, 1);
 		ctx->ibox = ibox;
+		ctx->trans = t->ictx.trans;
 		ctx->append_offset = (uoff_t)-1;
+		ctx->headers = str_new(default_pool, 512);
 	}
 	ctx->flags = flags;
 
@@ -284,6 +302,17 @@
 				return -1;
 		}
 
+		if (mail_r == NULL) {
+			/* assign UIDs only if mbox doesn't require syncing */
+			ret = mbox_sync_has_changed(ibox);
+			if (ret < 0)
+				return -1;
+			if (ret == 0) {
+				if (mbox_save_init_sync(t) < 0)
+					return -1;
+			}
+		}
+
 		if (mbox_seek_to_end(ctx, &ctx->append_offset) < 0)
 			return -1;
 
@@ -292,27 +321,72 @@
 		o_stream_set_blocking(ctx->output, 60000, NULL, NULL);
 	}
 
+	if (!ctx->synced && mail_r != NULL) {
+		/* we'll need to assign UID for the mail immediately. */
+		if (mbox_sync(ibox, FALSE, FALSE, FALSE) < 0)
+			return -1;
+		if (mbox_save_init_sync(t) < 0)
+			return -1;
+	}
+
+	save_flags = (flags->flags & ~MAIL_RECENT) | MAIL_RECENT;
+	str_truncate(ctx->headers, 0);
+	if (ctx->synced) {
+		str_printfa(ctx->headers, "X-UID: %u\n", ctx->next_uid);
+		if (!ibox->keep_recent)
+			save_flags &= ~MAIL_RECENT;
+
+		memset(keywords, 0, INDEX_KEYWORDS_BYTE_COUNT);
+		// FIXME: set keywords
+		mail_index_append(ctx->trans, ctx->next_uid, &seq);
+		mail_index_update_flags(ctx->trans, seq, MODIFY_REPLACE,
+					save_flags, keywords);
+
+		offset = ctx->output->offset - 1;
+		mail_index_update_extra_rec(ctx->trans, seq,
+					    ibox->mbox_extra_idx, &offset);
+		ctx->next_uid++;
+	}
+	mbox_save_append_flag_headers(ctx->headers,
+				      save_flags ^ MBOX_NONRECENT);
+	mbox_save_append_keyword_headers(ctx, flags->keywords,
+					 flags->keywords_count);
+
 	i_assert(ibox->mbox_lock_type == F_WRLCK);
 
 	t_push();
 	if (write_from_line(ctx, received_date, from_envelope) < 0 ||
 	    mail_storage_save(ibox->box.storage, ibox->path, data, ctx->output,
-			      getenv("MAIL_SAVE_CRLF") != NULL,
+			      FALSE, getenv("MAIL_SAVE_CRLF") != NULL,
 			      save_header_callback, ctx) < 0 ||
-	    mbox_fix_header(ctx) < 0 ||
+	    mbox_write_content_length(ctx) < 0 ||
 	    mbox_append_lf(ctx) < 0) {
 		ret = -1;
 	} else {
 		ret = 0;
 	}
 	t_pop();
+
+	if (mail_r != NULL) {
+		const struct mail_index_record *rec;
+
+		rec = mail_index_lookup_append(ctx->trans, seq);
+		if (index_mail_next(&ctx->mail, rec, seq, FALSE) <= 0)
+			return -1;
+		*mail_r = &ctx->mail.mail;
+	}
+
 	return ret;
 }
 
 static void mbox_save_deinit(struct mbox_save_context *ctx)
 {
+	if (ctx->mail.pool != NULL)
+		index_mail_deinit(&ctx->mail);
+
 	if (ctx->output != NULL)
 		o_stream_unref(ctx->output);
+	str_free(ctx->headers);
 	i_free(ctx);
 }
 
@@ -320,7 +394,13 @@
 {
 	int ret = 0;
 
-	if (ctx->ibox->mbox_fd != -1) {
+	if (ctx->synced) {
+		mail_index_update_header(ctx->trans,
+			offsetof(struct mail_index_header, next_uid),
+			&ctx->next_uid, sizeof(ctx->next_uid));
+	}
+
+	if (!ctx->synced && ctx->ibox->mbox_fd != -1) {
 		if (fdatasync(ctx->ibox->mbox_fd) < 0) {
 			mbox_set_syscall_error(ctx->ibox, "fsync()");
 			ret = -1;