changeset 12601:1413a0fa9ae7

imapc: Added support for changing flags and expunging.
author Timo Sirainen <tss@iki.fi>
date Tue, 25 Jan 2011 17:23:52 +0200
parents ba3c0ee558f5
children 3136e78751f7
files src/lib-storage/index/imapc/imapc-client.c src/lib-storage/index/imapc/imapc-client.h src/lib-storage/index/imapc/imapc-connection.c src/lib-storage/index/imapc/imapc-sync.c src/lib-storage/index/imapc/imapc-sync.h
diffstat 5 files changed, 229 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-storage/index/imapc/imapc-client.c	Tue Jan 25 17:15:54 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-client.c	Tue Jan 25 17:23:52 2011 +0200
@@ -19,6 +19,7 @@
 	{ "LITERAL+", IMAPC_CAPABILITY_LITERALPLUS },
 	{ "QRESYNC", IMAPC_CAPABILITY_QRESYNC },
 	{ "IDLE", IMAPC_CAPABILITY_IDLE },
+	{ "UIDPLUS", IMAPC_CAPABILITY_UIDPLUS },
 
 	{ "IMAP4REV1", IMAPC_CAPABILITY_IMAP4REV1 },
 	{ NULL, 0 }
@@ -195,6 +196,33 @@
 	i_free(ctx);
 }
 
+static struct imapc_client_command_context *
+imapc_client_mailbox_cmd_common(struct imapc_client_mailbox *box,
+				imapc_command_callback_t *callback,
+				void *context)
+{
+	struct imapc_client_command_context *ctx;
+
+	ctx = i_new(struct imapc_client_command_context, 1);
+	ctx->box = box;
+	ctx->callback = callback;
+	ctx->context = context;
+
+	box->pending_box_command_count++;
+	return ctx;
+}
+
+void imapc_client_mailbox_cmd(struct imapc_client_mailbox *box,
+			      const char *cmd,
+			      imapc_command_callback_t *callback,
+			      void *context)
+{
+	struct imapc_client_command_context *ctx;
+
+	ctx = imapc_client_mailbox_cmd_common(box, callback, context);
+	imapc_connection_cmd(box->conn, cmd, imapc_client_mailbox_cmd_cb, ctx);
+}
+
 void imapc_client_mailbox_cmdf(struct imapc_client_mailbox *box,
 			       imapc_command_callback_t *callback,
 			       void *context, const char *cmd_fmt, ...)
@@ -202,13 +230,7 @@
 	struct imapc_client_command_context *ctx;
 	va_list args;
 
-	ctx = i_new(struct imapc_client_command_context, 1);
-	ctx->box = box;
-	ctx->callback = callback;
-	ctx->context = context;
-
-	box->pending_box_command_count++;
-
+	ctx = imapc_client_mailbox_cmd_common(box, callback, context);
 	va_start(args, cmd_fmt);
 	imapc_connection_cmdvf(box->conn, imapc_client_mailbox_cmd_cb,
 			       ctx, cmd_fmt, args);
--- a/src/lib-storage/index/imapc/imapc-client.h	Tue Jan 25 17:15:54 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-client.h	Tue Jan 25 17:23:52 2011 +0200
@@ -13,6 +13,7 @@
 	IMAPC_CAPABILITY_LITERALPLUS	= 0x02,
 	IMAPC_CAPABILITY_QRESYNC	= 0x04,
 	IMAPC_CAPABILITY_IDLE		= 0x08,
+	IMAPC_CAPABILITY_UIDPLUS	= 0x10,
 
 	IMAPC_CAPABILITY_IMAP4REV1	= 0x400000000
 };
@@ -91,6 +92,10 @@
 			  imapc_command_callback_t *callback, void *context,
 			  void *untagged_box_context);
 void imapc_client_mailbox_close(struct imapc_client_mailbox **box);
+void imapc_client_mailbox_cmd(struct imapc_client_mailbox *box,
+			      const char *cmd,
+			      imapc_command_callback_t *callback,
+			      void *context);
 void imapc_client_mailbox_cmdf(struct imapc_client_mailbox *box,
 			       imapc_command_callback_t *callback,
 			       void *context, const char *cmd_fmt, ...)
--- a/src/lib-storage/index/imapc/imapc-connection.c	Tue Jan 25 17:15:54 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-connection.c	Tue Jan 25 17:23:52 2011 +0200
@@ -614,7 +614,7 @@
 	imapc_connection_input_reset(conn);
 	cmd->callback(&reply, cmd->context);
 	pool_unref(&cmd->pool);
-	return 0;
+	return 1;
 }
 
 static int imapc_connection_input_one(struct imapc_connection *conn)
--- a/src/lib-storage/index/imapc/imapc-sync.c	Tue Jan 25 17:15:54 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-sync.c	Tue Jan 25 17:23:52 2011 +0200
@@ -2,41 +2,211 @@
 
 #include "lib.h"
 #include "ioloop.h"
+#include "str.h"
+#include "imap-util.h"
 #include "index-sync-private.h"
 #include "imapc-storage.h"
 #include "imapc-client.h"
 #include "imapc-seqmap.h"
 #include "imapc-sync.h"
 
+static void imapc_sync_callback(const struct imapc_command_reply *reply,
+				void *context)
+{
+	struct imapc_sync_context *ctx = context;
+
+	i_assert(ctx->sync_command_count > 0);
+
+	if (reply->state == IMAPC_COMMAND_STATE_OK)
+		;
+	else if (reply->state == IMAPC_COMMAND_STATE_NO) {
+		/* maybe the message was expunged already.
+		   some servers fail STOREs with NO in such situation. */
+	} else {
+		mail_storage_set_critical(&ctx->mbox->storage->storage,
+			"imapc: Sync command failed: %s", reply->text_full);
+	}
+	
+	if (--ctx->sync_command_count == 0)
+		imapc_client_stop(ctx->mbox->storage->client);
+}
+
+static void imapc_sync_cmd(struct imapc_sync_context *ctx, const char *cmd)
+{
+	ctx->sync_command_count++;
+	imapc_client_mailbox_cmd(ctx->mbox->client_box, cmd,
+				 imapc_sync_callback, ctx);
+}
+
+static void
+imapc_sync_add_missing_deleted_flags(struct imapc_sync_context *ctx,
+				     uint32_t seq1, uint32_t seq2)
+{
+	const struct mail_index_record *rec;
+	uint32_t seq, uid1, uid2;
+	const char *cmd;
+
+	/* if any of them has a missing \Deleted flag,
+	   just add it to all of them. */
+	for (seq = seq1; seq <= seq2; seq++) {
+		rec = mail_index_lookup(ctx->sync_view, seq);
+		if ((rec->flags & MAIL_DELETED) == 0)
+			break;
+	}
+
+	if (seq <= seq2) {
+		mail_index_lookup_uid(ctx->sync_view, seq1, &uid1);
+		mail_index_lookup_uid(ctx->sync_view, seq2, &uid2);
+		cmd = t_strdup_printf("UID STORE %u:%u +FLAGS \\Deleted",
+				      uid1, uid2);
+		imapc_sync_cmd(ctx, cmd);
+	}
+}
+
+static void imapc_sync_index_flags(struct imapc_sync_context *ctx,
+				   const struct mail_index_sync_rec *sync_rec)
+{
+	string_t *str = t_str_new(128);
+
+	i_assert(sync_rec->type == MAIL_INDEX_SYNC_TYPE_FLAGS);
+
+	if (sync_rec->add_flags != 0) {
+		str_printfa(str, "UID STORE %u:%u +FLAGS (",
+			    sync_rec->uid1, sync_rec->uid2);
+		imap_write_flags(str, sync_rec->add_flags, NULL);
+		str_append_c(str, ')');
+		imapc_sync_cmd(ctx, str_c(str));
+	}
+
+	if (sync_rec->remove_flags != 0) {
+		str_truncate(str, 0);
+		str_printfa(str, "UID STORE %u:%u -FLAGS (",
+			    sync_rec->uid1, sync_rec->uid2);
+		imap_write_flags(str, sync_rec->remove_flags, NULL);
+		str_append_c(str, ')');
+		imapc_sync_cmd(ctx, str_c(str));
+	}
+}
+
+static void
+imapc_sync_index_keyword(struct imapc_sync_context *ctx,
+			 const struct mail_index_sync_rec *sync_rec)
+{
+	string_t *str = t_str_new(128);
+	const char *const *kw_p;
+	char change_char;
+
+	switch (sync_rec->type) {
+	case MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD:
+		change_char = '+';
+		break;
+	case MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE:
+		change_char = '-';
+		break;
+	default:
+		i_unreached();
+	}
+
+	str_printfa(str, "UID STORE %u:%u %cFLAGS (",
+		    sync_rec->uid1, sync_rec->uid2, change_char);
+
+	kw_p = array_idx(ctx->keywords, sync_rec->keyword_idx);
+	str_append(str, *kw_p);
+	str_append_c(str, ')');
+	imapc_sync_cmd(ctx, str_c(str));
+}
+
+static void
+imapc_sync_index_keyword_reset(struct imapc_sync_context *ctx,
+			       uint32_t seq1, uint32_t seq2)
+{
+	const struct mail_index_record *rec;
+	string_t *str = t_str_new(128);
+	uint32_t seq;
+
+	for (seq = seq1; seq <= seq2; seq++) {
+		rec = mail_index_lookup(ctx->sync_view, seq);
+
+		str_truncate(str, 0);
+		str_printfa(str, "UID STORE %u FLAGS (", rec->uid);
+		imap_write_flags(str, rec->flags, NULL);
+		str_append_c(str, ')');
+		imapc_sync_cmd(ctx, str_c(str));
+	}
+}
+
+static void imapc_sync_expunge_finish(struct imapc_sync_context *ctx)
+{
+	string_t *str;
+	enum imapc_capability caps;
+	const struct seq_range *range;
+	unsigned int i, count;
+
+	if (array_count(&ctx->expunged_uids) == 0)
+		return;
+
+	caps = imapc_client_get_capabilities(ctx->mbox->storage->client);
+	if ((caps & IMAPC_CAPABILITY_UIDPLUS) == 0) {
+		/* just expunge everything */
+		imapc_sync_cmd(ctx, "EXPUNGE");
+		return;
+	}
+
+	/* build a list of UIDs to expunge */
+	str = t_str_new(128);
+	str_append(str, "UID EXPUNGE");
+
+	range = array_get(&ctx->expunged_uids, &count);
+	for (i = 0; i < count; i++) {
+		str_printfa(str, " %u", range[i].seq1);
+		if (range[i].seq1 == range[i].seq2)
+			str_printfa(str, ":%u", range[i].seq2);
+	}
+	imapc_sync_cmd(ctx, str_c(str));
+}
+
 static void imapc_sync_index(struct imapc_sync_context *ctx)
 {
 	struct mailbox *box = &ctx->mbox->box;
 	struct mail_index_sync_rec sync_rec;
 	uint32_t seq1, seq2;
 
-	while (mail_index_sync_next(ctx->index_sync_ctx, &sync_rec)) {
+	i_array_init(&ctx->expunged_uids, 64);
+	ctx->keywords = mail_index_get_keywords(box->index);
+
+	while (mail_index_sync_next(ctx->index_sync_ctx, &sync_rec)) T_BEGIN {
 		if (!mail_index_lookup_seq_range(ctx->sync_view,
 						 sync_rec.uid1, sync_rec.uid2,
 						 &seq1, &seq2)) {
 			/* already expunged, nothing to do. */
-			continue;
-		}
-
-		switch (sync_rec.type) {
+		} else switch (sync_rec.type) {
 		case MAIL_INDEX_SYNC_TYPE_APPEND:
 			/* don't care */
 			break;
 		case MAIL_INDEX_SYNC_TYPE_EXPUNGE:
-			//imapc_sync_expunge(ctx, seq1, seq2);
+			imapc_sync_add_missing_deleted_flags(ctx, seq1, seq2);
+			seq_range_array_add_range(&ctx->expunged_uids,
+						  sync_rec.uid1, sync_rec.uid2);
 			break;
 		case MAIL_INDEX_SYNC_TYPE_FLAGS:
+			imapc_sync_index_flags(ctx, &sync_rec);
+			break;
 		case MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD:
 		case MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE:
+			imapc_sync_index_keyword(ctx, &sync_rec);
+			break;
 		case MAIL_INDEX_SYNC_TYPE_KEYWORD_RESET:
-			/* FIXME: should be bother calling sync_notify()? */
+			imapc_sync_index_keyword_reset(ctx, seq1, seq2);
 			break;
 		}
-	}
+	} T_END;
+
+	if (ctx->sync_command_count > 0)
+		imapc_client_run(ctx->mbox->storage->client);
+	imapc_sync_expunge_finish(ctx);
+	if (ctx->sync_command_count > 0)
+		imapc_client_run(ctx->mbox->storage->client);
+	array_free(&ctx->expunged_uids);
 
 	if (box->v.sync_notify != NULL)
 		box->v.sync_notify(box, 0, 0);
@@ -69,7 +239,15 @@
 		return ret;
 	}
 
+	i_assert(mbox->delayed_sync_trans == NULL);
+	mbox->delayed_sync_view = ctx->sync_view;
+	mbox->delayed_sync_trans = ctx->trans;
+
 	imapc_sync_index(ctx);
+
+	mbox->delayed_sync_view = NULL;
+	mbox->delayed_sync_trans = NULL;
+
 	*ctx_r = ctx;
 	return 0;
 }
@@ -147,14 +325,17 @@
 	struct index_mailbox_sync_context *ictx =
 		(struct index_mailbox_sync_context *)ctx;
 	struct imapc_mailbox *mbox = (struct imapc_mailbox *)ctx->box;
+	enum mailbox_sync_flags flags = ictx->flags;
 	struct imapc_seqmap *seqmap;
 	int ret;
 
 	ret = index_mailbox_sync_deinit(ctx, status_r);
+	ctx = NULL;
+
 	if (mbox->client_box == NULL)
 		return ret;
 
-	if ((ictx->flags & MAILBOX_SYNC_FLAG_NO_EXPUNGES) == 0 && ret == 0) {
+	if ((flags & MAILBOX_SYNC_FLAG_NO_EXPUNGES) == 0 && ret == 0) {
 		seqmap = imapc_client_mailbox_get_seqmap(mbox->client_box);
 		imapc_seqmap_reset(seqmap);
 	}
--- a/src/lib-storage/index/imapc/imapc-sync.h	Tue Jan 25 17:15:54 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-sync.h	Tue Jan 25 17:23:52 2011 +0200
@@ -8,6 +8,10 @@
         struct mail_index_sync_ctx *index_sync_ctx;
 	struct mail_index_view *sync_view;
 	struct mail_index_transaction *trans;
+
+	const ARRAY_TYPE(keywords) *keywords;
+	ARRAY_TYPE(seq_range) expunged_uids;
+	unsigned int sync_command_count;
 };
 
 struct mailbox_sync_context *