changeset 7353:33304b5f6070 HEAD

Syncing supports now calling a callback after sync instead of just sending a tagged reply. Use it with EXPUNGE to retry EXPUNGE in case the syncing had seen new \Deleted flags (Outlook workaround).
author Timo Sirainen <tss@iki.fi>
date Wed, 05 Mar 2008 02:54:33 +0200
parents 4f63124e756e
children de47a37ca0c1
files src/imap/client.h src/imap/cmd-expunge.c src/imap/imap-sync.c src/imap/imap-sync.h
diffstat 4 files changed, 90 insertions(+), 30 deletions(-) [+]
line wrap: on
line diff
--- a/src/imap/client.h	Wed Mar 05 02:53:05 2008 +0200
+++ b/src/imap/client.h	Wed Mar 05 02:54:33 2008 +0200
@@ -48,12 +48,7 @@
 	struct imap_parser *parser;
 	enum client_command_state state;
 
-	/* if multiple commands are in progress, we may need to wait for them
-	   to finish before syncing mailbox. */
-	unsigned int sync_counter;
-	enum mailbox_sync_flags sync_flags;
-	enum imap_sync_flags sync_imap_flags;
-	const char *sync_tagline;
+	struct client_sync_context *sync;
 
 	unsigned int uid:1; /* used UID command */
 	unsigned int cancel:1; /* command is wanted to be cancelled */
@@ -89,6 +84,9 @@
 	struct client_command_context *input_lock;
 	struct client_command_context *output_lock;
 
+	/* syncing marks this TRUE when it sees \Deleted flags. this is by
+	   EXPUNGE for Outlook-workaround. */
+	unsigned int sync_seen_deletes:1;
 	unsigned int disconnected:1;
 	unsigned int destroyed:1;
 	unsigned int handling_input:1;
--- a/src/imap/cmd-expunge.c	Wed Mar 05 02:53:05 2008 +0200
+++ b/src/imap/cmd-expunge.c	Wed Mar 05 02:54:33 2008 +0200
@@ -38,6 +38,19 @@
 	}
 }
 
+static bool cmd_expunge_callback(struct client_command_context *cmd)
+{
+	if (cmd->client->sync_seen_deletes) {
+		/* Outlook workaround: session 1 set \Deleted flag and
+		   session 2 tried to expunge without having seen it yet.
+		   expunge again. */
+		return cmd_expunge(cmd);
+	}
+
+	client_send_tagline(cmd, "OK Expunge completed.");
+	return TRUE;
+}
+
 bool cmd_expunge(struct client_command_context *cmd)
 {
 	struct client *client = cmd->client;
@@ -45,9 +58,10 @@
 	if (!client_verify_open_mailbox(cmd))
 		return TRUE;
 
+	cmd->client->sync_seen_deletes = FALSE;
 	if (imap_expunge(client->mailbox, NULL)) {
-		return cmd_sync(cmd, 0, IMAP_SYNC_FLAG_SAFE,
-				"OK Expunge completed.");
+		return cmd_sync_callback(cmd, 0, IMAP_SYNC_FLAG_SAFE,
+					 cmd_expunge_callback);
 	} else {
 		client_send_storage_error(cmd,
 					  mailbox_get_storage(client->mailbox));
--- a/src/imap/imap-sync.c	Wed Mar 05 02:53:05 2008 +0200
+++ b/src/imap/imap-sync.c	Wed Mar 05 02:54:33 2008 +0200
@@ -8,6 +8,16 @@
 #include "imap-sync.h"
 #include "commands.h"
 
+struct client_sync_context {
+	/* if multiple commands are in progress, we may need to wait for them
+	   to finish before syncing mailbox. */
+	unsigned int counter;
+	enum mailbox_sync_flags flags;
+	enum imap_sync_flags imap_flags;
+	const char *tagline;
+	imap_sync_callback_t *callback;
+};
+
 struct imap_sync_context {
 	struct client *client;
 	struct mailbox *box;
@@ -103,6 +113,9 @@
 	keywords = client_get_keyword_names(ctx->client, &ctx->tmp_keywords,
 			mail_get_keyword_indexes(ctx->mail));
 
+	if ((flags & MAIL_DELETED) != 0)
+		ctx->client->sync_seen_deletes = TRUE;
+
 	str_truncate(str, 0);
 	str_printfa(str, "* %u FETCH (", ctx->seq);
 	if (ctx->imap_flags & IMAP_SYNC_FLAG_SEND_UID)
@@ -185,6 +198,16 @@
 	return ret;
 }
 
+static bool cmd_finish_sync(struct client_command_context *cmd)
+{
+	if (cmd->sync->callback != NULL)
+		return cmd->sync->callback(cmd);
+	else {
+		client_send_tagline(cmd, cmd->sync->tagline);
+		return TRUE;
+	}
+}
+
 static bool cmd_sync_continue(struct client_command_context *sync_cmd)
 {
 	struct client_command_context *cmd;
@@ -210,13 +233,12 @@
 	for (cmd = client->command_queue; cmd != NULL; cmd = cmd->next) {
 		if (cmd->state == CLIENT_COMMAND_STATE_WAIT_SYNC &&
 		    cmd != sync_cmd &&
-		    cmd->sync_counter+1 == client->sync_counter) {
-			client_send_tagline(cmd, cmd->sync_tagline);
-			client_command_free(cmd);
+		    cmd->sync->counter+1 == client->sync_counter) {
+			if (cmd_finish_sync(cmd))
+				client_command_free(cmd);
 		}
 	}
-	client_send_tagline(sync_cmd, sync_cmd->sync_tagline);
-	return TRUE;
+	return cmd_finish_sync(sync_cmd);
 }
 
 static void get_common_sync_flags(struct client *client,
@@ -230,14 +252,14 @@
 	*imap_flags_r = 0;
 
 	for (cmd = client->command_queue; cmd != NULL; cmd = cmd->next) {
-		if (cmd->sync_tagline != NULL &&
-		    cmd->sync_counter == client->sync_counter) {
-			if ((cmd->sync_flags & MAILBOX_SYNC_FLAG_FAST) != 0)
+		if (cmd->sync != NULL &&
+		    cmd->sync->counter == client->sync_counter) {
+			if ((cmd->sync->flags & MAILBOX_SYNC_FLAG_FAST) != 0)
 				fast_count++;
-			if (cmd->sync_flags & MAILBOX_SYNC_FLAG_NO_EXPUNGES)
+			if (cmd->sync->flags & MAILBOX_SYNC_FLAG_NO_EXPUNGES)
 				noexpunges_count++;
-			*flags_r |= cmd->sync_flags;
-			*imap_flags_r |= cmd->sync_imap_flags;
+			*flags_r |= cmd->sync->flags;
+			*imap_flags_r |= cmd->sync->imap_flags;
 			count++;
 		}
 	}
@@ -290,8 +312,10 @@
 	return TRUE;
 }
 
-bool cmd_sync(struct client_command_context *cmd, enum mailbox_sync_flags flags,
-	      enum imap_sync_flags imap_flags, const char *tagline)
+static bool
+cmd_sync_full(struct client_command_context *cmd, enum mailbox_sync_flags flags,
+	      enum imap_sync_flags imap_flags, const char *tagline,
+	      imap_sync_callback_t *callback)
 {
 	struct client *client = cmd->client;
 
@@ -302,14 +326,17 @@
 
 	if (client->mailbox == NULL) {
 		/* no mailbox selected, no point in delaying the sync */
+		i_assert(callback == NULL);
 		client_send_tagline(cmd, tagline);
 		return TRUE;
 	}
 
-	cmd->sync_counter = client->sync_counter;
-	cmd->sync_flags = flags;
-	cmd->sync_imap_flags = imap_flags;
-	cmd->sync_tagline = p_strdup(cmd->pool, tagline);
+	cmd->sync = p_new(cmd->pool, struct client_sync_context, 1);
+	cmd->sync->counter = client->sync_counter;
+	cmd->sync->flags = flags;
+	cmd->sync->imap_flags = imap_flags;
+	cmd->sync->tagline = p_strdup(cmd->pool, tagline);
+	cmd->sync->callback = callback;
 	cmd->state = CLIENT_COMMAND_STATE_WAIT_SYNC;
 
 	cmd->func = NULL;
@@ -321,6 +348,20 @@
 	return FALSE;
 }
 
+bool cmd_sync(struct client_command_context *cmd, enum mailbox_sync_flags flags,
+	      enum imap_sync_flags imap_flags, const char *tagline)
+{
+	return cmd_sync_full(cmd, flags, imap_flags, tagline, NULL);
+}
+
+bool cmd_sync_callback(struct client_command_context *cmd,
+		       enum mailbox_sync_flags flags,
+		       enum imap_sync_flags imap_flags,
+		       imap_sync_callback_t *callback)
+{
+	return cmd_sync_full(cmd, flags, imap_flags, NULL, callback);
+}
+
 static bool cmd_sync_drop_fast(struct client *client)
 {
 	struct client_command_context *cmd, *next;
@@ -330,10 +371,11 @@
 		next = cmd->next;
 
 		if (cmd->state == CLIENT_COMMAND_STATE_WAIT_SYNC &&
-		    (cmd->sync_flags & MAILBOX_SYNC_FLAG_FAST) != 0) {
-			client_send_tagline(cmd, cmd->sync_tagline);
-			client_command_free(cmd);
-			ret = TRUE;
+		    (cmd->sync->flags & MAILBOX_SYNC_FLAG_FAST) != 0) {
+			if (cmd_finish_sync(cmd)) {
+				client_command_free(cmd);
+				ret = TRUE;
+			}
 		}
 	}
 	return ret;
@@ -358,7 +400,7 @@
 	/* find a command that we can sync */
 	for (cmd = client->command_queue; cmd != NULL; cmd = cmd->next) {
 		if (cmd->state == CLIENT_COMMAND_STATE_WAIT_SYNC) {
-			if (cmd->sync_counter == client->sync_counter)
+			if (cmd->sync->counter == client->sync_counter)
 				break;
 		}
 	}
--- a/src/imap/imap-sync.h	Wed Mar 05 02:53:05 2008 +0200
+++ b/src/imap/imap-sync.h	Wed Mar 05 02:54:33 2008 +0200
@@ -6,6 +6,8 @@
 	IMAP_SYNC_FLAG_SAFE	= 0x02
 };
 
+typedef bool imap_sync_callback_t(struct client_command_context *cmd);
+
 struct client;
 
 struct imap_sync_context *
@@ -16,6 +18,10 @@
 
 bool cmd_sync(struct client_command_context *cmd, enum mailbox_sync_flags flags,
 	      enum imap_sync_flags imap_flags, const char *tagline);
+bool cmd_sync_callback(struct client_command_context *cmd,
+		       enum mailbox_sync_flags flags,
+		       enum imap_sync_flags imap_flags,
+		       imap_sync_callback_t *callback);
 bool cmd_sync_delayed(struct client *client);
 
 #endif