diff src/imap/imap-fetch.c @ 7629:bad3a811a148 HEAD

Added QRESYNC support.
author Timo Sirainen <tss@iki.fi>
date Sat, 15 Mar 2008 16:24:26 +0200
parents 4b8c1c164d8f
children 03146f02403f
line wrap: on
line diff
--- a/src/imap/imap-fetch.c	Sat Mar 15 15:24:45 2008 +0200
+++ b/src/imap/imap-fetch.c	Sat Mar 15 16:24:26 2008 +0200
@@ -9,6 +9,8 @@
 #include "message-send.h"
 #include "message-size.h"
 #include "imap-date.h"
+#include "mail-search.h"
+#include "mail-search-build.h"
 #include "commands.h"
 #include "imap-fetch.h"
 #include "imap-util.h"
@@ -81,7 +83,8 @@
 	return handler->init(ctx, name, args);
 }
 
-struct imap_fetch_context *imap_fetch_init(struct client_command_context *cmd)
+struct imap_fetch_context *
+imap_fetch_init(struct client_command_context *cmd, struct mailbox *box)
 {
 	struct client *client = cmd->client;
 	struct imap_fetch_context *ctx;
@@ -94,7 +97,7 @@
 	ctx = p_new(cmd->pool, struct imap_fetch_context, 1);
 	ctx->client = client;
 	ctx->cmd = cmd;
-	ctx->box = client->mailbox;
+	ctx->box = box;
 
 	ctx->cur_str = str_new(default_pool, 8192);
 	ctx->all_headers_buf = buffer_create_dynamic(cmd->pool, 128);
@@ -105,6 +108,23 @@
 	return ctx;
 }
 
+bool imap_fetch_add_unchanged_since(struct imap_fetch_context *ctx,
+				    uint64_t modseq)
+{
+	struct mail_search_arg *search_arg;
+
+	search_arg = p_new(ctx->cmd->pool, struct mail_search_arg, 1);
+	search_arg->type = SEARCH_MODSEQ;
+	search_arg->value.modseq =
+		p_new(ctx->cmd->pool, struct mail_search_modseq, 1);
+	search_arg->value.modseq->modseq = modseq;
+
+	search_arg->next = ctx->search_args->next;
+	ctx->search_args->next = search_arg;
+
+	return imap_fetch_init_handler(ctx, "MODSEQ", NULL);
+}
+
 #undef imap_fetch_add_handler
 void imap_fetch_add_handler(struct imap_fetch_context *ctx,
 			    bool buffered, bool want_deinit,
@@ -153,11 +173,152 @@
 	}
 }
 
-void imap_fetch_begin(struct imap_fetch_context *ctx)
+static void
+expunges_drop_known(struct imap_fetch_context *ctx, struct mail *mail,
+		    ARRAY_TYPE(seq_range) *expunges)
+{
+	const uint32_t *seqs, *uids;
+	unsigned int i, count;
+
+	seqs = array_get(ctx->qresync_sample_seqset, &count);
+	uids = array_idx(ctx->qresync_sample_uidset, 0);
+	i_assert(array_count(ctx->qresync_sample_uidset) == count);
+	i_assert(count > 0);
+
+	/* FIXME: we could do removals from the middle as well */
+	for (i = 0; i < count; i++) {
+		mail_set_seq(mail, seqs[i]);
+		if (uids[i] != mail->uid)
+			break;
+	}
+	if (i > 0)
+		seq_range_array_remove_range(expunges, 1, uids[i-1]);
+}
+
+static int get_expunges_fallback(struct imap_fetch_context *ctx,
+				 const ARRAY_TYPE(seq_range) *uids,
+				 ARRAY_TYPE(seq_range) *expunges)
+{
+	struct mailbox_transaction_context *trans;
+	struct mail_search_arg search_arg;
+	struct mail_search_context *search_ctx;
+	struct mail *mail;
+	const struct seq_range *uid_range;
+	struct mailbox_status status;
+	unsigned int i, count;
+	uint32_t next_uid;
+	int ret = 0;
+
+	uid_range = array_get(uids, &count);
+	i_assert(count > 0);
+	i = 0;
+	next_uid = uid_range[0].seq1;
+
+	/* search UIDs in given range */
+	memset(&search_arg, 0, sizeof(search_arg));
+	search_arg.type = SEARCH_UIDSET;
+	i_array_init(&search_arg.value.seqset, array_count(uids));
+	array_append_array(&search_arg.value.seqset, uids);
+
+	trans = mailbox_transaction_begin(ctx->box, 0);
+	mail = mail_alloc(trans, 0, NULL);
+	search_ctx = mailbox_search_init(trans, NULL, &search_arg, NULL);
+	while (mailbox_search_next(search_ctx, mail) > 0) {
+		if (mail->uid == next_uid) {
+			if (next_uid < uid_range[i].seq2)
+				next_uid++;
+			else if (++i < count)
+				next_uid = uid_range[++i].seq1;
+			else
+				break;
+		} else {
+			/* next_uid .. mail->uid-1 are expunged */
+			i_assert(mail->uid > next_uid);
+			while (mail->uid > uid_range[i].seq2) {
+				seq_range_array_add_range(expunges, next_uid,
+							  uid_range[i].seq2);
+				i++;
+				i_assert(i < count);
+				next_uid = uid_range[i].seq1;
+			}
+			if (next_uid != mail->uid) {
+				seq_range_array_add_range(expunges, next_uid,
+							  mail->uid - 1);
+			}
+			if (uid_range[i].seq2 == mail->uid)
+				next_uid = uid_range[++i].seq1;
+			else
+				next_uid = mail->uid + 1;
+		}
+	}
+	if (i < count) {
+		i_assert(next_uid <= uid_range[i].seq2);
+		seq_range_array_add_range(expunges, next_uid,
+					  uid_range[i].seq2);
+		i++;
+	}
+	for (; i < count; i++) {
+		seq_range_array_add_range(expunges, uid_range[i].seq1,
+					  uid_range[i].seq2);
+	}
+
+	mailbox_get_status(ctx->box, STATUS_UIDNEXT, &status);
+	seq_range_array_remove_range(expunges, status.uidnext, (uint32_t)-1);
+
+	if (mailbox_search_deinit(&search_ctx) < 0)
+		ret = -1;
+
+	if (ret == 0 && ctx->qresync_sample_seqset != NULL)
+		expunges_drop_known(ctx, mail, expunges);
+
+	mail_free(&mail);
+	(void)mailbox_transaction_commit(&trans);
+	return ret;
+}
+
+static int
+imap_fetch_send_vanished(struct imap_fetch_context *ctx)
+{
+	const struct mail_search_arg *uidarg = ctx->search_args;
+	const struct mail_search_arg *modseqarg = uidarg->next;
+	const ARRAY_TYPE(seq_range) *uids = &uidarg->value.seqset;
+	uint64_t modseq = modseqarg->value.modseq->modseq;
+	ARRAY_TYPE(seq_range) expunges;
+	string_t *str;
+	int ret = 0;
+
+	i_array_init(&expunges, array_count(uids));
+	if (!mailbox_get_expunged_uids(ctx->box, modseq, uids, &expunges)) {
+		/* return all expunged UIDs */
+		if (get_expunges_fallback(ctx, uids, &expunges) < 0) {
+			array_clear(&expunges);
+			ret = -1;
+		}
+	}
+	if (array_count(&expunges) > 0) {
+		str = str_new(default_pool, 128);
+		str_append(str, "* VANISHED (EARLIER) ");
+		imap_write_seq_range(str, &expunges);
+		str_append(str, "\r\n");
+		o_stream_send(ctx->client->output, str_data(str), str_len(str));
+		str_free(&str);
+	}
+	array_free(&expunges);
+	return ret;
+}
+
+int imap_fetch_begin(struct imap_fetch_context *ctx)
 {
 	const void *null = NULL;
 	const void *data;
 
+	if (ctx->send_vanished) {
+		if (imap_fetch_send_vanished(ctx) < 0) {
+			ctx->failed = TRUE;
+			return -1;
+		}
+	}
+
 	if (ctx->flags_update_seen) {
 		if (mailbox_is_readonly(ctx->box))
 			ctx->flags_update_seen = FALSE;
@@ -186,8 +347,12 @@
 	ctx->select_counter = ctx->client->select_counter;
 	ctx->mail = mail_alloc(ctx->trans, ctx->fetch_data,
 			       ctx->all_headers_ctx);
+
+	/* Delayed uidset -> seqset conversion. VANISHED needs the uidset. */
+	mail_search_args_init(ctx->search_args, ctx->box, TRUE);
 	ctx->search_ctx =
 		mailbox_search_init(ctx->trans, NULL, ctx->search_args, NULL);
+	return 0;
 }
 
 static int imap_fetch_flush_buffer(struct imap_fetch_context *ctx)
@@ -233,7 +398,7 @@
 	return 0;
 }
 
-static int imap_fetch_more(struct imap_fetch_context *ctx)
+static int imap_fetch_more_int(struct imap_fetch_context *ctx)
 {
 	struct client *client = ctx->client;
 	const struct imap_fetch_context_handler *handlers;
@@ -350,14 +515,16 @@
 	return 1;
 }
 
-int imap_fetch(struct imap_fetch_context *ctx)
+int imap_fetch_more(struct imap_fetch_context *ctx)
 {
 	int ret;
 
 	i_assert(ctx->client->output_lock == NULL ||
 		 ctx->client->output_lock == ctx->cmd);
 
-	ret = imap_fetch_more(ctx);
+	ret = imap_fetch_more_int(ctx);
+	if (ret < 0)
+		ctx->failed = TRUE;
 	if (ctx->line_partial) {
 		/* nothing can be sent until FETCH is finished */
 		ctx->client->output_lock = ctx->cmd;