changeset 2427:e1616067df5c HEAD

Syncing works now too without buffering everything. Also fixed handling input while command was being processed.
author Timo Sirainen <tss@iki.fi>
date Thu, 19 Aug 2004 02:53:39 +0300
parents d47a550fc23b
children abef2ac8843a
files src/imap/client.c src/imap/client.h src/imap/cmd-append.c src/imap/cmd-check.c src/imap/cmd-copy.c src/imap/cmd-expunge.c src/imap/cmd-fetch.c src/imap/cmd-idle.c src/imap/cmd-noop.c src/imap/cmd-search.c src/imap/cmd-select.c src/imap/cmd-sort.c src/imap/cmd-status.c src/imap/cmd-store.c src/imap/cmd-thread.c src/imap/commands-util.c src/imap/commands-util.h src/imap/commands.h src/imap/imap-sync.c src/imap/imap-sync.h
diffstat 20 files changed, 321 insertions(+), 205 deletions(-) [+]
line wrap: on
line diff
--- a/src/imap/client.c	Thu Aug 19 02:42:31 2004 +0300
+++ b/src/imap/client.c	Thu Aug 19 02:53:39 2004 +0300
@@ -67,9 +67,6 @@
 	if (client->io != NULL)
 		io_remove(client->io);
 
-	if (client->idle_to != NULL)
-		timeout_remove(client->idle_to);
-
 	i_stream_unref(client->input);
 	o_stream_unref(client->output);
 
@@ -235,7 +232,10 @@
 	client->cmd_uid = FALSE;
 
 	p_clear(client->cmd_pool);
-        imap_parser_reset(client->parser);
+	imap_parser_reset(client->parser);
+
+	if (client->input_pending)
+                client_input(client);
 }
 
 /* Skip incoming data until newline is found,
@@ -334,6 +334,7 @@
 		client->io = NULL;
 	}
 
+	client->input_pending = FALSE;
 	client->last_input = ioloop_time;
 
 	switch (i_stream_read(client->input)) {
@@ -357,6 +358,9 @@
 		;
 	o_stream_uncork(client->output);
 
+	if (client->command_pending)
+		client->input_pending = TRUE;
+
 	if (client->output->closed)
 		client_destroy(client);
 }
--- a/src/imap/client.h	Thu Aug 19 02:42:31 2004 +0300
+++ b/src/imap/client.h	Thu Aug 19 02:53:39 2004 +0300
@@ -37,10 +37,8 @@
 	command_func_t *cmd_func;
 	void *cmd_context;
 
-	struct timeout *idle_to;
-	unsigned int idle_expunge;
-
 	unsigned int command_pending:1;
+	unsigned int input_pending:1;
 	unsigned int cmd_uid:1; /* used UID command */
 	unsigned int rawlog:1;
 	unsigned int input_skip_line:1; /* skip all the data until we've
--- a/src/imap/cmd-append.c	Thu Aug 19 02:42:31 2004 +0300
+++ b/src/imap/cmd-append.c	Thu Aug 19 02:53:39 2004 +0300
@@ -58,6 +58,7 @@
 	struct istream *input;
 	time_t internal_date;
 	const char *mailbox, *internal_date_str;
+        enum mailbox_sync_flags sync_flags = 0;
 	uoff_t msg_size;
 	unsigned int count;
 	int ret, failed, timezone_offset, nonsync;
@@ -207,13 +208,11 @@
 		}
 	}
 
-	if (box != client->mailbox)
+	if (box != client->mailbox) {
+		sync_flags |= MAILBOX_SYNC_FLAG_FAST;
 		mailbox_close(box);
-
-	if (!failed) {
-		client_sync_full(client);
-		client_send_tagline(client, "OK Append completed.");
 	}
 
-	return TRUE;
+	return failed ? TRUE :
+		cmd_sync(client, sync_flags, "OK Append completed.");
 }
--- a/src/imap/cmd-check.c	Thu Aug 19 02:42:31 2004 +0300
+++ b/src/imap/cmd-check.c	Thu Aug 19 02:53:39 2004 +0300
@@ -8,8 +8,5 @@
 	if (!client_verify_open_mailbox(client))
 		return TRUE;
 
-	/* we don't need this command, but sync the mailbox anyway. */
-	client_sync_full(client);
-	client_send_tagline(client, "OK Check completed.");
-	return TRUE;
+	return cmd_sync(client, 0, "OK Check completed.");
 }
--- a/src/imap/cmd-copy.c	Thu Aug 19 02:42:31 2004 +0300
+++ b/src/imap/cmd-copy.c	Thu Aug 19 02:53:39 2004 +0300
@@ -55,6 +55,7 @@
 	struct mailbox_transaction_context *t;
         struct mail_search_arg *search_arg;
 	const char *messageset, *mailbox;
+        enum mailbox_sync_flags sync_flags = 0;
 	int ret;
 
 	/* <message set> <mailbox> */
@@ -100,22 +101,19 @@
 			ret = -1;
 	}
 
-	if (ret < 0)
-		client_send_storage_error(client, storage);
+	if (destbox != client->mailbox) {
+		sync_flags |= MAILBOX_SYNC_FLAG_FAST;
+		mailbox_close(destbox);
+	}
+
+	if (ret > 0)
+		return cmd_sync(client, sync_flags, "OK Copy completed.");
 	else if (ret == 0) {
 		/* some messages were expunged, sync them */
-		client_sync_full(client);
-		client_send_tagline(client,
+		return cmd_sync(client, 0,
 			"NO Some of the requested messages no longer exist.");
 	} else {
-		if (destbox == client->mailbox)
-			client_sync_full(client);
-		else
-			client_sync_full_fast(client);
-		client_send_tagline(client, "OK Copy completed.");
+		client_send_storage_error(client, storage);
+		return TRUE;
 	}
-
-	if (destbox != client->mailbox)
-		mailbox_close(destbox);
-	return TRUE;
 }
--- a/src/imap/cmd-expunge.c	Thu Aug 19 02:42:31 2004 +0300
+++ b/src/imap/cmd-expunge.c	Thu Aug 19 02:53:39 2004 +0300
@@ -27,15 +27,13 @@
 	if (search_arg == NULL)
 		return TRUE;
 
-	if (imap_expunge(client->mailbox, search_arg)) {
-		client_sync_full(client);
-		client_send_tagline(client, "OK Expunge completed.");
-	} else {
+	if (imap_expunge(client->mailbox, search_arg))
+		return cmd_sync(client, 0, "OK Expunge completed.");
+	else {
 		client_send_storage_error(client,
 					  mailbox_get_storage(client->mailbox));
+		return TRUE;
 	}
-
-	return TRUE;
 }
 
 int cmd_expunge(struct client *client)
@@ -43,13 +41,11 @@
 	if (!client_verify_open_mailbox(client))
 		return TRUE;
 
-	if (imap_expunge(client->mailbox, NULL)) {
-		client_sync_full(client);
-		client_send_tagline(client, "OK Expunge completed.");
-	} else {
+	if (imap_expunge(client->mailbox, NULL))
+		return cmd_sync(client, 0, "OK Expunge completed.");
+	else {
 		client_send_storage_error(client,
 					  mailbox_get_storage(client->mailbox));
+		return TRUE;
 	}
-
-	return TRUE;
 }
--- a/src/imap/cmd-fetch.c	Thu Aug 19 02:42:31 2004 +0300
+++ b/src/imap/cmd-fetch.c	Thu Aug 19 02:53:39 2004 +0300
@@ -61,19 +61,11 @@
 	return TRUE;
 }
 
-static void cmd_fetch_finish(struct client *client, int failed)
+static int cmd_fetch_finish(struct client *client, int failed)
 {
-	if (!failed) {
-		if ((client_workarounds &
-		     WORKAROUND_OE6_FETCH_NO_NEWMAIL) == 0) {
-			if (client->cmd_uid)
-				client_sync_full_fast(client);
-			else
-				client_sync_without_expunges(client);
-		}
+	static const char *ok_message = "OK Fetch completed.";
 
-		client_send_tagline(client, "OK Fetch completed.");
-	} else {
+	if (failed) {
 		struct mail_storage *storage;
 		const char *error;
 		int syntax;
@@ -92,6 +84,14 @@
 			/* user error, we'll reply with BAD */
 			client_send_storage_error(client, storage);
 		}
+		return TRUE;
+	} if ((client_workarounds & WORKAROUND_OE6_FETCH_NO_NEWMAIL) != 0) {
+		client_send_tagline(client, ok_message);
+		return TRUE;
+	} else {
+		return cmd_sync(client, MAILBOX_SYNC_FLAG_FAST |
+				(client->cmd_uid ? 0 :
+				 MAILBOX_SYNC_FLAG_NO_EXPUNGES), ok_message);
 	}
 }
 
@@ -106,8 +106,7 @@
 	}
 	if (imap_fetch_deinit(ctx) < 0)
 		ret = -1;
-	cmd_fetch_finish(client, ret < 0);
-	return TRUE;
+	return cmd_fetch_finish(client, ret < 0);
 }
 
 int cmd_fetch(struct client *client)
@@ -154,6 +153,5 @@
 	}
 	if (imap_fetch_deinit(ctx) < 0)
 		ret = -1;
-	cmd_fetch_finish(client, ret < 0);
-	return TRUE;
+	return cmd_fetch_finish(client, ret < 0);
 }
--- a/src/imap/cmd-idle.c	Thu Aug 19 02:42:31 2004 +0300
+++ b/src/imap/cmd-idle.c	Thu Aug 19 02:53:39 2004 +0300
@@ -11,18 +11,34 @@
 
 #define DEFAULT_IDLE_CHECK_INTERVAL 30
 
-static void idle_finish(struct client *client, int done_ok)
+struct cmd_idle_context {
+	struct client *client;
+
+	struct imap_sync_context *sync_ctx;
+	struct timeout *to;
+	uint32_t dummy_seq;
+
+	unsigned int idle_timeout:1;
+	unsigned int sync_pending:1;
+};
+
+static int cmd_idle_continue(struct client *client);
+
+static void idle_finish(struct cmd_idle_context *ctx, int done_ok)
 {
-	if (client->idle_to != NULL) {
-		timeout_remove(client->idle_to);
-		client->idle_to = NULL;
+	struct client *client = ctx->client;
+
+	if (ctx->to != NULL) {
+		timeout_remove(ctx->to);
+		ctx->to = NULL;
 	}
 
 	o_stream_cork(client->output);
 
-	if (client->idle_expunge != 0) {
+	if (ctx->dummy_seq != 0) {
+		/* outlook idle workaround */
 		client_send_line(client,
-			t_strdup_printf("* %u EXPUNGE", client->idle_expunge));
+			t_strdup_printf("* %u EXPUNGE", ctx->dummy_seq));
 	}
 
 	io_remove(client->io);
@@ -31,7 +47,6 @@
 	if (client->mailbox != NULL)
 		mailbox_notify_changes(client->mailbox, 0, NULL, NULL);
 
-	client_sync_full(client);
 	if (done_ok)
 		client_send_tagline(client, "OK Idle completed.");
 	else
@@ -45,7 +60,8 @@
 
 static void idle_client_input(void *context)
 {
-	struct client *client = context;
+        struct cmd_idle_context *ctx = context;
+	struct client *client = ctx->client;
 	char *line;
 
 	client->last_input = ioloop_time;
@@ -57,7 +73,7 @@
 		return;
 	case -2:
 		client->input_skip_line = TRUE;
-		idle_finish(client, FALSE);
+		idle_finish(ctx, FALSE);
 		return;
 	}
 
@@ -65,50 +81,95 @@
 		if (client->input_skip_line)
 			client->input_skip_line = FALSE;
 		else {
-			idle_finish(client, strcmp(line, "DONE") == 0);
+			idle_finish(ctx, strcmp(line, "DONE") == 0);
 			break;
 		}
 	}
 }
 
+static void idle_send_expunge(struct cmd_idle_context *ctx)
+{
+	struct client *client = ctx->client;
+
+	ctx->dummy_seq = client->messages_count+1;
+	client_send_line(client,
+			 t_strdup_printf("* %u EXISTS", ctx->dummy_seq));
+	mailbox_notify_changes(client->mailbox, 0, NULL, NULL);
+}
+
 static void idle_timeout(void *context)
 {
-	struct client *client = context;
+	struct cmd_idle_context *ctx = context;
 
 	/* outlook workaround - it hasn't sent anything for a long time and
 	   we're about to disconnect unless it does something. send a fake
 	   EXISTS to see if it responds. it's expunged later. */
 
-	timeout_remove(client->idle_to);
-	client->idle_to = NULL;
+	timeout_remove(ctx->to);
+	ctx->to = NULL;
 
-	client->idle_expunge = client->messages_count+1;
-	client_send_line(client,
-			 t_strdup_printf("* %u EXISTS", client->idle_expunge));
-	mailbox_notify_changes(client->mailbox, 0, NULL, NULL);
+	if (ctx->sync_ctx != NULL) {
+		/* we're already syncing.. do this after it's finished */
+		ctx->idle_timeout = TRUE;
+		return;
+	}
+
+	idle_send_expunge(ctx);
 }
 
 static void idle_callback(struct mailbox *box, void *context)
 {
-	struct client *client = context;
+        struct cmd_idle_context *ctx = context;
+
+	if (ctx->sync_ctx != NULL)
+		ctx->sync_pending = TRUE;
+	else {
+		ctx->sync_pending = FALSE;
+		ctx->sync_ctx = imap_sync_init(ctx->client, box, 0);
+		cmd_idle_continue(ctx->client);
+	}
+}
 
-	if (imap_sync(client, box, 0) < 0) {
+static int cmd_idle_continue(struct client *client)
+{
+	struct cmd_idle_context *ctx = client->cmd_context;
+
+	if (imap_sync_more(ctx->sync_ctx) == 0) {
+		/* unfinished */
+		return FALSE;
+	}
+
+	if (imap_sync_deinit(ctx->sync_ctx) < 0) {
 		client_send_untagged_storage_error(client,
 			mailbox_get_storage(client->mailbox));
 		mailbox_notify_changes(client->mailbox, 0, NULL, NULL);
 	}
+	ctx->sync_ctx = NULL;
+
+	if (ctx->idle_timeout) {
+		/* outlook workaround */
+		idle_send_expunge(ctx);
+	} else if (ctx->sync_pending) {
+		/* more changes occured while we were sending changes to
+		   client */
+                idle_callback(client->mailbox, client);
+	}
+	return FALSE;
 }
 
 int cmd_idle(struct client *client)
 {
+	struct cmd_idle_context *ctx;
 	const char *str;
 	unsigned int interval;
 
-        client->idle_expunge = 0;
+	ctx = p_new(client->cmd_pool, struct cmd_idle_context, 1);
+	ctx->client = client;
+
 	if ((client_workarounds & WORKAROUND_OUTLOOK_IDLE) != 0 &&
 	    client->mailbox != NULL) {
-		client->idle_to = timeout_add((CLIENT_IDLE_TIMEOUT - 60) * 1000,
-					      idle_timeout, client);
+		ctx->to = timeout_add((CLIENT_IDLE_TIMEOUT - 60) * 1000,
+				      idle_timeout, ctx);
 	}
 
 	str = getenv("MAILBOX_IDLE_CHECK_INTERVAL");
@@ -118,12 +179,16 @@
 
 	if (client->mailbox != NULL) {
 		mailbox_notify_changes(client->mailbox, interval,
-				       idle_callback, client);
+				       idle_callback, ctx);
 	}
 	client_send_line(client, "+ idling");
 
 	io_remove(client->io);
 	client->io = io_add(i_stream_get_fd(client->input),
-			    IO_READ, idle_client_input, client);
+			    IO_READ, idle_client_input, ctx);
+
+	client->command_pending = TRUE;
+	client->cmd_func = cmd_idle_continue;
+	client->cmd_context = ctx;
 	return FALSE;
 }
--- a/src/imap/cmd-noop.c	Thu Aug 19 02:42:31 2004 +0300
+++ b/src/imap/cmd-noop.c	Thu Aug 19 02:53:39 2004 +0300
@@ -5,7 +5,5 @@
 
 int cmd_noop(struct client *client)
 {
-	client_sync_full(client);
-	client_send_tagline(client, "OK NOOP completed.");
-	return TRUE;
+	return cmd_sync(client, 0, "OK NOOP completed.");
 }
--- a/src/imap/cmd-search.c	Thu Aug 19 02:42:31 2004 +0300
+++ b/src/imap/cmd-search.c	Thu Aug 19 02:53:39 2004 +0300
@@ -95,11 +95,10 @@
 		/* error in search arguments */
 		client_send_tagline(client, t_strconcat("NO ", error, NULL));
 	} else if (imap_search(client, charset, sargs)) {
-		if (client->cmd_uid)
-			client_sync_full_fast(client);
-		else
-			client_sync_without_expunges(client);
-		client_send_tagline(client, "OK Search completed.");
+		return cmd_sync(client, MAILBOX_SYNC_FLAG_FAST |
+				(client->cmd_uid ?
+				 0 : MAILBOX_SYNC_FLAG_NO_EXPUNGES),
+				"OK Search completed.");
 	} else {
 		client_send_storage_error(client,
 					  mailbox_get_storage(client->mailbox));
--- a/src/imap/cmd-select.c	Thu Aug 19 02:42:31 2004 +0300
+++ b/src/imap/cmd-select.c	Thu Aug 19 02:53:39 2004 +0300
@@ -35,7 +35,7 @@
 		return TRUE;
 	}
 
-	if (imap_sync(client, box, 0) < 0) {
+	if (imap_sync_nonselected(box, 0) < 0) {
 		client_send_storage_error(client, storage);
 		mailbox_close(box);
 		return TRUE;
--- a/src/imap/cmd-sort.c	Thu Aug 19 02:42:31 2004 +0300
+++ b/src/imap/cmd-sort.c	Thu Aug 19 02:53:39 2004 +0300
@@ -121,12 +121,11 @@
 		/* error in search arguments */
 		client_send_tagline(client, t_strconcat("NO ", error, NULL));
 	} else if (imap_sort(client, charset, sargs, sorting) == 0) {
-		/* NOTE: syncing is allowed when returning UIDs */
-		if (client->cmd_uid)
-			client_sync_full_fast(client);
-		else
-			client_sync_without_expunges(client);
-		client_send_tagline(client, "OK Sort completed.");
+		pool_unref(pool);
+		return cmd_sync(client, MAILBOX_SYNC_FLAG_FAST |
+				(client->cmd_uid ?
+				 0 : MAILBOX_SYNC_FLAG_NO_EXPUNGES),
+				"OK Sort completed.");
 	} else {
 		client_send_storage_error(client,
 					  mailbox_get_storage(client->mailbox));
--- a/src/imap/cmd-status.c	Thu Aug 19 02:42:31 2004 +0300
+++ b/src/imap/cmd-status.c	Thu Aug 19 02:53:39 2004 +0300
@@ -65,7 +65,7 @@
 			return FALSE;
 	}
 
-	if (imap_sync(client, box, 0) < 0)
+	if (imap_sync_nonselected(box, 0) < 0)
 		failed = TRUE;
 	else
 		failed = mailbox_get_status(box, items, status) < 0;
--- a/src/imap/cmd-store.c	Thu Aug 19 02:42:31 2004 +0300
+++ b/src/imap/cmd-store.c	Thu Aug 19 02:53:39 2004 +0300
@@ -105,14 +105,12 @@
 	}
 
 	if (!failed) {
-		if (client->cmd_uid)
-			client_sync_full_fast(client);
-		else
-			client_sync_without_expunges(client);
-		client_send_tagline(client, "OK Store completed.");
+		return cmd_sync(client, MAILBOX_SYNC_FLAG_FAST |
+				(client->cmd_uid ?
+				 0 : MAILBOX_SYNC_FLAG_NO_EXPUNGES),
+				"OK Store completed.");
 	} else {
 		client_send_storage_error(client, mailbox_get_storage(box));
+		return TRUE;
 	}
-
-	return TRUE;
 }
--- a/src/imap/cmd-thread.c	Thu Aug 19 02:42:31 2004 +0300
+++ b/src/imap/cmd-thread.c	Thu Aug 19 02:53:39 2004 +0300
@@ -63,12 +63,11 @@
 		/* error in search arguments */
 		client_send_tagline(client, t_strconcat("NO ", error, NULL));
 	} else if (imap_thread(client, charset, sargs, threading) == 0) {
-		/* NOTE: syncing is allowed when returning UIDs */
-		if (client->cmd_uid)
-			client_sync_full_fast(client);
-		else
-			client_sync_without_expunges(client);
-		client_send_tagline(client, "OK Search completed.");
+		pool_unref(pool);
+		return cmd_sync(client, MAILBOX_SYNC_FLAG_FAST |
+				(client->cmd_uid ?
+				 0 : MAILBOX_SYNC_FLAG_NO_EXPUNGES),
+				"OK Thread completed.");
 	} else {
 		client_send_storage_error(client,
 					  mailbox_get_storage(client->mailbox));
--- a/src/imap/commands-util.c	Thu Aug 19 02:42:31 2004 +0300
+++ b/src/imap/commands-util.c	Thu Aug 19 02:53:39 2004 +0300
@@ -112,40 +112,6 @@
 	}
 }
 
-void client_sync_full(struct client *client)
-{
-	if (client->mailbox == NULL)
-		return;
-
-	if (imap_sync(client, client->mailbox, 0) < 0) {
-		client_send_untagged_storage_error(client,
-			mailbox_get_storage(client->mailbox));
-	}
-}
-
-void client_sync_full_fast(struct client *client)
-{
-	if (client->mailbox == NULL)
-		return;
-
-	if (imap_sync(client, client->mailbox, MAILBOX_SYNC_FLAG_FAST) < 0) {
-		client_send_untagged_storage_error(client,
-			mailbox_get_storage(client->mailbox));
-	}
-}
-
-void client_sync_without_expunges(struct client *client)
-{
-	if (client->mailbox == NULL)
-		return;
-
-	if (imap_sync(client, client->mailbox, MAILBOX_SYNC_FLAG_FAST |
-		      MAILBOX_SYNC_FLAG_NO_EXPUNGES) < 0) {
-		client_send_untagged_storage_error(client,
-			mailbox_get_storage(client->mailbox));
-	}
-}
-
 void client_send_storage_error(struct client *client,
 			       struct mail_storage *storage)
 {
--- a/src/imap/commands-util.h	Thu Aug 19 02:42:31 2004 +0300
+++ b/src/imap/commands-util.h	Thu Aug 19 02:53:39 2004 +0300
@@ -26,16 +26,6 @@
    error message to client. */
 int client_verify_open_mailbox(struct client *client);
 
-/* Synchronize selected mailbox with client by sending EXPUNGE,
-   FETCH FLAGS, EXISTS and RECENT responses. */
-void client_sync_full(struct client *client);
-
-/* Synchronize fast. */
-void client_sync_full_fast(struct client *client);
-
-/* Synchronize all but expunges with client. */
-void client_sync_without_expunges(struct client *client);
-
 /* Send last mail storage error message to client. */
 void client_send_storage_error(struct client *client,
 			       struct mail_storage *storage);
--- a/src/imap/commands.h	Thu Aug 19 02:42:31 2004 +0300
+++ b/src/imap/commands.h	Thu Aug 19 02:53:39 2004 +0300
@@ -3,6 +3,7 @@
 
 #include "mail-storage.h"
 #include "imap-parser.h"
+#include "imap-sync.h"
 #include "commands-util.h"
 
 struct command {
--- a/src/imap/imap-sync.c	Thu Aug 19 02:42:31 2004 +0300
+++ b/src/imap/imap-sync.c	Thu Aug 19 02:53:39 2004 +0300
@@ -1,90 +1,193 @@
-/* Copyright (C) 2002 Timo Sirainen */
+/* Copyright (C) 2002-2004 Timo Sirainen */
 
 #include "common.h"
 #include "str.h"
 #include "imap-util.h"
 #include "mail-storage.h"
 #include "imap-sync.h"
+#include "commands.h"
 
-int imap_sync(struct client *client, struct mailbox *box,
-	      enum mailbox_sync_flags flags)
+struct cmd_sync_context {
+	const char *tagline;
+	struct imap_sync_context *sync_ctx;
+};
+
+struct imap_sync_context {
+	struct client *client;
+	struct mailbox *box;
+
+	struct mailbox_transaction_context *t;
+	struct mailbox_sync_context *sync_ctx;
+
+	struct mailbox_sync_rec sync_rec;
+	uint32_t seq;
+
+	unsigned int messages_count;
+
+	int failed;
+};
+
+struct imap_sync_context *
+imap_sync_init(struct client *client, struct mailbox *box,
+	       enum mailbox_sync_flags flags)
 {
-        struct mailbox_transaction_context *t;
-	struct mailbox_sync_context *ctx;
-        struct mailbox_sync_rec sync_rec;
+	struct imap_sync_context *ctx;
+
+	i_assert(client->mailbox == box);
+
+	ctx = i_new(struct imap_sync_context, 1);
+	ctx->client = client;
+	ctx->box = box;
+
+	ctx->t = mailbox_transaction_begin(box, FALSE);
+	ctx->sync_ctx = mailbox_sync_init(box, flags);
+	ctx->messages_count = client->messages_count;
+	return ctx;
+}
+
+int imap_sync_deinit(struct imap_sync_context *ctx)
+{
 	struct mailbox_status status;
+
+	if (mailbox_sync_deinit(ctx->sync_ctx, &status) < 0 || ctx->failed) {
+		mailbox_transaction_rollback(ctx->t);
+		i_free(ctx);
+		return -1;
+	}
+
+	mailbox_transaction_commit(ctx->t);
+
+	t_push();
+
+	ctx->client->messages_count = status.messages;
+	if (status.messages != ctx->messages_count) {
+		client_send_line(ctx->client,
+			t_strdup_printf("* %u EXISTS", status.messages));
+	}
+	if (status.recent != ctx->client->recent_count) {
+                ctx->client->recent_count = status.recent;
+		client_send_line(ctx->client,
+			t_strdup_printf("* %u RECENT", status.recent));
+	}
+
+	/*FIXME:client_save_keywords(&client->keywords, keywords, keywords_count);
+	client_send_mailbox_flags(client, mailbox, keywords, keywords_count);*/
+
+	t_pop();
+	i_free(ctx);
+	return 0;
+}
+
+int imap_sync_more(struct imap_sync_context *ctx)
+{
 	struct mail *mail;
         const struct mail_full_flags *mail_flags;
 	string_t *str;
-	uint32_t seq, messages_count;
-
-	if (client->mailbox != box) {
-		/* mailbox isn't selected - we only wish to sync the mailbox
-		   without sending anything to client */
-		ctx = mailbox_sync_init(box, flags);
-		while (mailbox_sync_next(ctx, &sync_rec) > 0)
-			;
-		return mailbox_sync_deinit(ctx, &status);
-	}
 
 	t_push();
 	str = t_str_new(256);
-	messages_count = client->messages_count;
+
+	for (;;) {
+		if (ctx->seq == 0) {
+			/* get next one */
+			if (mailbox_sync_next(ctx->sync_ctx,
+					      &ctx->sync_rec) <= 0)
+				break;
+		}
 
-	t = mailbox_transaction_begin(box, FALSE);
-	ctx = mailbox_sync_init(box, flags);
-	while (mailbox_sync_next(ctx, &sync_rec) > 0) {
-		switch (sync_rec.type) {
+		switch (ctx->sync_rec.type) {
 		case MAILBOX_SYNC_TYPE_FLAGS:
-			for (seq = sync_rec.seq1; seq <= sync_rec.seq2; seq++) {
-				mail = mailbox_fetch(t, seq, MAIL_FETCH_FLAGS);
+			if (ctx->seq == 0)
+				ctx->seq = ctx->sync_rec.seq1;
+
+			for (; ctx->seq <= ctx->sync_rec.seq2; ctx->seq++) {
+				mail = mailbox_fetch(ctx->t, ctx->seq,
+						     MAIL_FETCH_FLAGS);
 
 				mail_flags = mail->get_flags(mail);
 				if (mail_flags == NULL)
 					continue;
 
 				str_truncate(str, 0);
-				str_printfa(str, "* %u FETCH (FLAGS (", seq);
+				str_printfa(str, "* %u FETCH (FLAGS (",
+					    ctx->seq);
 				imap_write_flags(str, mail_flags);
 				str_append(str, "))");
-				client_send_line(client, str_c(str));
+				if (!client_send_line(ctx->client,
+						      str_c(str))) {
+					t_pop();
+					return 0;
+				}
 			}
 			break;
 		case MAILBOX_SYNC_TYPE_EXPUNGE:
-			messages_count -= sync_rec.seq2 - sync_rec.seq1 + 1;
-			for (seq = sync_rec.seq2; seq >= sync_rec.seq1; seq--) {
+			if (ctx->seq == 0) {
+				ctx->seq = ctx->sync_rec.seq2;
+				ctx->messages_count -=
+					ctx->sync_rec.seq2 -
+					ctx->sync_rec.seq1 + 1;
+			}
+			for (; ctx->seq >= ctx->sync_rec.seq1; ctx->seq--) {
 				str_truncate(str, 0);
-				str_printfa(str, "* %u EXPUNGE", seq);
-				client_send_line(client, str_c(str));
+				str_printfa(str, "* %u EXPUNGE", ctx->seq);
+				if (!client_send_line(ctx->client,
+						      str_c(str))) {
+					t_pop();
+					return 0;
+				}
 			}
 			break;
 		}
+		ctx->seq = 0;
 	}
+	t_pop();
+	return 1;
+}
+
+int imap_sync_nonselected(struct mailbox *box, enum mailbox_sync_flags flags)
+{
+	struct mailbox_sync_context *ctx;
+        struct mailbox_sync_rec sync_rec;
+	struct mailbox_status status;
 
-	if (mailbox_sync_deinit(ctx, &status) < 0) {
-		mailbox_transaction_rollback(t);
-		t_pop();
-		return -1;
+	ctx = mailbox_sync_init(box, flags);
+	while (mailbox_sync_next(ctx, &sync_rec) > 0)
+		;
+	return mailbox_sync_deinit(ctx, &status);
+}
+
+static int cmd_sync_continue(struct client *client)
+{
+	struct cmd_sync_context *ctx = client->cmd_context;
+
+	if (imap_sync_more(ctx->sync_ctx) == 0)
+		return FALSE;
+
+	if (imap_sync_deinit(ctx->sync_ctx) < 0) {
+		client_send_untagged_storage_error(client,
+			mailbox_get_storage(client->mailbox));
 	}
 
-	mailbox_transaction_commit(t);
+	client_send_tagline(client, ctx->tagline);
+	return TRUE;
+}
 
-	client->messages_count = status.messages;
-	if (status.messages != messages_count) {
-		str_truncate(str, 0);
-		str_printfa(str, "* %u EXISTS", status.messages);
-		client_send_line(client, str_c(str));
-	}
-	if (status.recent != client->recent_count) {
-                client->recent_count = status.recent;
-		str_truncate(str, 0);
-		str_printfa(str, "* %u RECENT", status.recent);
-		client_send_line(client, str_c(str));
+int cmd_sync(struct client *client, enum mailbox_sync_flags flags,
+	     const char *tagline)
+{
+        struct cmd_sync_context *ctx;
+
+	if (client->mailbox == NULL) {
+		client_send_tagline(client, tagline);
+		return TRUE;
 	}
 
-	/*FIXME:client_save_keywords(&client->keywords, keywords, keywords_count);
-	client_send_mailbox_flags(client, mailbox, keywords, keywords_count);*/
+	ctx = p_new(client->cmd_pool, struct cmd_sync_context, 1);
+	ctx->tagline = p_strdup(client->cmd_pool, tagline);
+	ctx->sync_ctx = imap_sync_init(client, client->mailbox, flags);
 
-	t_pop();
-	return 0;
+	client->cmd_func = cmd_sync_continue;
+	client->cmd_context = ctx;
+	client->command_pending = TRUE;
+	return cmd_sync_continue(client);
 }
--- a/src/imap/imap-sync.h	Thu Aug 19 02:42:31 2004 +0300
+++ b/src/imap/imap-sync.h	Thu Aug 19 02:53:39 2004 +0300
@@ -1,7 +1,15 @@
 #ifndef __IMAP_SYNC_H
 #define __IMAP_SYNC_H
 
-int imap_sync(struct client *client, struct mailbox *box,
-	      enum mailbox_sync_flags flags);
+struct imap_sync_context *
+imap_sync_init(struct client *client, struct mailbox *box,
+	       enum mailbox_sync_flags flags);
+int imap_sync_deinit(struct imap_sync_context *ctx);
+int imap_sync_more(struct imap_sync_context *ctx);
+
+int imap_sync_nonselected(struct mailbox *box, enum mailbox_sync_flags flags);
+
+int cmd_sync(struct client *client, enum mailbox_sync_flags flags,
+	     const char *tagline);
 
 #endif