changeset 7632:6e2e4e5c52f3 HEAD

Fixed CLOSE HIGEHSTMODSEQ race condition. Added some checks to make sure mailbox isn't being closed/opened while the previous SELECT/CLOSE is still being processed.
author Timo Sirainen <tss@iki.fi>
date Sun, 16 Mar 2008 07:18:06 +0200
parents 5d3b6a766032
children 27a0de620b0c
files src/imap/client.c src/imap/client.h src/imap/cmd-close.c src/imap/cmd-expunge.c src/imap/cmd-select.c src/imap/cmd-unselect.c src/imap/imap-expunge.c src/imap/imap-expunge.h
diffstat 8 files changed, 69 insertions(+), 44 deletions(-) [+]
line wrap: on
line diff
--- a/src/imap/client.c	Sun Mar 16 06:53:58 2008 +0200
+++ b/src/imap/client.c	Sun Mar 16 07:18:06 2008 +0200
@@ -364,6 +364,11 @@
 			/* don't do anything until syncing is finished */
 			return TRUE;
 		}
+		if (cmd->client->changing_mailbox) {
+			/* don't do anything until mailbox is fully
+			   opened/closed */
+			return TRUE;
+		}
 		return FALSE;
 	}
 
--- a/src/imap/client.h	Sun Mar 16 06:53:58 2008 +0200
+++ b/src/imap/client.h	Sun Mar 16 07:18:06 2008 +0200
@@ -92,7 +92,7 @@
 	unsigned int destroyed:1;
 	unsigned int handling_input:1;
 	unsigned int syncing:1;
-	unsigned int selecting:1;
+	unsigned int changing_mailbox:1;
 	unsigned int input_skip_line:1; /* skip all the data until we've
 					   found a new line */
 };
--- a/src/imap/cmd-close.c	Sun Mar 16 06:53:58 2008 +0200
+++ b/src/imap/cmd-close.c	Sun Mar 16 07:18:06 2008 +0200
@@ -4,39 +4,60 @@
 #include "commands.h"
 #include "imap-expunge.h"
 
+static void cmd_close_finish(struct client *client)
+{
+	if (mailbox_close(&client->mailbox) < 0) {
+		client_send_untagged_storage_error(client,
+			mailbox_get_storage(client->mailbox));
+	}
+	client_update_mailbox_flags(client, NULL);
+	client->changing_mailbox = FALSE;
+}
+
+static bool cmd_close_callback(struct client_command_context *cmd)
+{
+	struct mailbox_status status;
+
+	mailbox_get_status(cmd->client->mailbox,
+			   STATUS_HIGHESTMODSEQ, &status);
+	cmd_close_finish(cmd->client);
+	client_send_tagline(cmd, t_strdup_printf(
+		"OK [HIGHESTMODSEQ %llu] Close completed.",
+		(unsigned long long)status.highest_modseq));
+	return TRUE;
+}
+
 bool cmd_close(struct client_command_context *cmd)
 {
 	struct client *client = cmd->client;
 	struct mailbox *mailbox = client->mailbox;
 	struct mail_storage *storage;
-	struct mailbox_status status;
-	bool show_highestmodseq;
+	int ret;
 
 	if (!client_verify_open_mailbox(cmd))
 		return TRUE;
 
-	storage = mailbox_get_storage(mailbox);
-	client->mailbox = NULL;
+	i_assert(!client->changing_mailbox);
+	client->changing_mailbox = TRUE;
 
-	show_highestmodseq =
-		(cmd->client->enabled_features & MAILBOX_FEATURE_QRESYNC) != 0;
-
-	if (!imap_expunge(mailbox, NULL))
-		client_send_untagged_storage_error(client, storage);
-	else if (mailbox_sync(mailbox, 0, !show_highestmodseq ? 0 :
-			      STATUS_HIGHESTMODSEQ, &status) < 0)
+	storage = mailbox_get_storage(mailbox);
+	if ((ret = imap_expunge(mailbox, NULL)) < 0)
 		client_send_untagged_storage_error(client, storage);
 
-	if (mailbox_close(&mailbox) < 0)
-                client_send_untagged_storage_error(client, storage);
-	client_update_mailbox_flags(client, NULL);
-
-	if (!show_highestmodseq)
+	if ((client->enabled_features & MAILBOX_FEATURE_QRESYNC) != 0 &&
+	    ret > 0) {
+		/* we expunged something. since we're sending updated
+		   HIGHESTMODSEQ make sure the client sees all changes up to
+		   it by syncing the mailbox one last time. We wouldn't need
+		   to include our own expunge in there, but it's too much
+		   trouble to hide it. */
+		return cmd_sync_callback(cmd, 0, IMAP_SYNC_FLAG_SAFE,
+					 cmd_close_callback);
+	} else {
+		if (mailbox_sync(mailbox, 0, 0, NULL) < 0)
+			client_send_untagged_storage_error(client, storage);
+		cmd_close_finish(client);
 		client_send_tagline(cmd, "OK Close completed.");
-	else {
-		client_send_tagline(cmd, t_strdup_printf(
-			"OK [HIGHESTMODSEQ %llu] Close completed.",
-			(unsigned long long)status.highest_modseq));
+		return TRUE;
 	}
-	return TRUE;
 }
--- a/src/imap/cmd-expunge.c	Sun Mar 16 06:53:58 2008 +0200
+++ b/src/imap/cmd-expunge.c	Sun Mar 16 07:18:06 2008 +0200
@@ -51,14 +51,13 @@
 	if (search_arg == NULL)
 		return TRUE;
 
-	if (imap_expunge(client->mailbox, search_arg)) {
-		return cmd_sync_callback(cmd, 0, IMAP_SYNC_FLAG_SAFE,
-					 cmd_expunge_callback);
-	} else {
+	if (imap_expunge(client->mailbox, search_arg) < 0) {
 		client_send_storage_error(cmd,
 					  mailbox_get_storage(client->mailbox));
 		return TRUE;
 	}
+	return cmd_sync_callback(cmd, 0, IMAP_SYNC_FLAG_SAFE,
+				 cmd_expunge_callback);
 }
 
 bool cmd_expunge(struct client_command_context *cmd)
@@ -69,12 +68,11 @@
 		return TRUE;
 
 	cmd->client->sync_seen_deletes = FALSE;
-	if (imap_expunge(client->mailbox, NULL)) {
-		return cmd_sync_callback(cmd, 0, IMAP_SYNC_FLAG_SAFE,
-					 cmd_expunge_callback);
-	} else {
+	if (imap_expunge(client->mailbox, NULL) < 0) {
 		client_send_storage_error(cmd,
 					  mailbox_get_storage(client->mailbox));
 		return TRUE;
 	}
+	return cmd_sync_callback(cmd, 0, IMAP_SYNC_FLAG_SAFE,
+				 cmd_expunge_callback);
 }
--- a/src/imap/cmd-select.c	Sun Mar 16 06:53:58 2008 +0200
+++ b/src/imap/cmd-select.c	Sun Mar 16 07:18:06 2008 +0200
@@ -194,7 +194,7 @@
 				    "OK [READ-ONLY] Select completed." :
 				    "OK [READ-WRITE] Select completed.");
 	}
-	ctx->cmd->client->selecting = FALSE;
+	ctx->cmd->client->changing_mailbox = FALSE;
 	select_context_free(ctx);
 }
 
@@ -352,11 +352,8 @@
 		}
 	}
 
-	if (client->selecting) {
-		client_send_tagline(cmd, "Mailbox is already being selected");
-		return TRUE;
-	}
-	client->selecting = TRUE;
+	i_assert(!client->changing_mailbox);
+	client->changing_mailbox = TRUE;
 
 	if (client->mailbox != NULL) {
 		box = client->mailbox;
--- a/src/imap/cmd-unselect.c	Sun Mar 16 06:53:58 2008 +0200
+++ b/src/imap/cmd-unselect.c	Sun Mar 16 07:18:06 2008 +0200
@@ -12,6 +12,7 @@
 	if (!client_verify_open_mailbox(cmd))
 		return TRUE;
 
+	i_assert(!client->changing_mailbox);
 	client->mailbox = NULL;
 
 	storage = mailbox_get_storage(mailbox);
--- a/src/imap/imap-expunge.c	Sun Mar 16 06:53:58 2008 +0200
+++ b/src/imap/imap-expunge.c	Sun Mar 16 07:18:06 2008 +0200
@@ -5,17 +5,17 @@
 #include "mail-search.h"
 #include "imap-expunge.h"
 
-bool imap_expunge(struct mailbox *box, struct mail_search_arg *next_search_arg)
+int imap_expunge(struct mailbox *box, struct mail_search_arg *next_search_arg)
 {
 	struct mail_search_context *ctx;
         struct mailbox_transaction_context *t;
 	struct mail *mail;
 	struct mail_search_arg search_arg;
-	bool failed;
+	bool expunges = FALSE;
 
 	if (mailbox_is_readonly(box)) {
 		/* silently ignore */
-		return TRUE;
+		return 0;
 	}
 
 	memset(&search_arg, 0, sizeof(search_arg));
@@ -27,16 +27,19 @@
 	ctx = mailbox_search_init(t, NULL, &search_arg, NULL);
 
 	mail = mail_alloc(t, 0, NULL);
-	while (mailbox_search_next(ctx, mail) > 0)
+	while (mailbox_search_next(ctx, mail) > 0) {
 		mail_expunge(mail);
+		expunges = TRUE;
+	}
 	mail_free(&mail);
 
 	if (mailbox_search_deinit(&ctx) < 0) {
-		failed = TRUE;
 		mailbox_transaction_rollback(&t);
+		return -1;
 	} else {
-		failed = mailbox_transaction_commit(&t) < 0;
+		if (mailbox_transaction_commit(&t) < 0)
+			return -1;
 	}
 
-	return !failed;
+	return expunges ? 1 : 0;
 }
--- a/src/imap/imap-expunge.h	Sun Mar 16 06:53:58 2008 +0200
+++ b/src/imap/imap-expunge.h	Sun Mar 16 07:18:06 2008 +0200
@@ -1,6 +1,6 @@
 #ifndef IMAP_EXPUNGE_H
 #define IMAP_EXPUNGE_H
 
-bool imap_expunge(struct mailbox *box, struct mail_search_arg *next_search_arg);
+int imap_expunge(struct mailbox *box, struct mail_search_arg *next_search_arg);
 
 #endif