changeset 4941:f612d2086448 HEAD

If running commands in parallel would cause ambiguity, run them sequentially.
author Timo Sirainen <tss@iki.fi>
date Wed, 20 Dec 2006 22:47:02 +0200
parents 76aa8a360c2f
children b382b21409b5
files src/imap/client.c src/imap/client.h src/imap/cmd-uid.c src/imap/commands.c src/imap/commands.h
diffstat 5 files changed, 117 insertions(+), 45 deletions(-) [+]
line wrap: on
line diff
--- a/src/imap/client.c	Wed Dec 20 22:13:07 2006 +0200
+++ b/src/imap/client.c	Wed Dec 20 22:47:02 2006 +0200
@@ -280,6 +280,47 @@
 }
 
 static struct client_command_context *
+client_command_find_with_flags(struct client_command_context *new_cmd,
+			       enum command_flags flags)
+{
+	struct client_command_context *cmd;
+
+	cmd = new_cmd->client->command_queue;
+	for (; cmd != NULL; cmd = cmd->next) {
+		if (cmd != new_cmd && (cmd->cmd_flags & flags) != 0)
+			return cmd;
+	}
+	return NULL;
+}
+
+static bool client_command_check_ambiguity(struct client_command_context *cmd)
+{
+	enum command_flags flags;
+	bool broken_client = FALSE;
+
+	if ((cmd->cmd_flags & COMMAND_FLAG_USES_SEQS) != 0) {
+		/* no existing command must be breaking sequences */
+		flags = COMMAND_FLAG_BREAKS_SEQS;
+		broken_client = TRUE;
+	} else if ((cmd->cmd_flags & COMMAND_FLAG_BREAKS_SEQS) != 0) {
+		/* if existing command uses sequences, we'll have to block */
+		flags = COMMAND_FLAG_USES_SEQS;
+	} else {
+		return FALSE;
+	}
+
+	if (client_command_find_with_flags(cmd, flags) == NULL)
+		return FALSE;
+
+	if (broken_client) {
+		client_send_line(cmd->client,
+			"* BAD Command pipelining results in ambiguity.");
+	}
+
+	return TRUE;
+}
+
+static struct client_command_context *
 client_command_new(struct client *client)
 {
 	struct client_command_context *cmd;
@@ -356,7 +397,10 @@
 		p_clear(client->command_pool);
 	}
 
-	if (client->input_lock == NULL && !client->disconnected) {
+	if (!client->disconnected &&
+	    (client->input_lock == NULL ||
+	     (client->input_lock->waiting_unambiguity &&
+	      !client_command_check_ambiguity(client->input_lock)))) {
 		if (client->io == NULL) {
 			i_assert(i_stream_get_fd(client->input) >= 0);
 			client->io = io_add(i_stream_get_fd(client->input),
@@ -425,14 +469,27 @@
 		cmd->name = p_strdup(cmd->pool, cmd->name);
 	}
 
+	client->input_skip_line = TRUE;
+
 	if (cmd->name == '\0') {
 		/* command not given - cmd_func is already NULL. */
 	} else {
 		/* find the command function */
-		cmd->func = command_find(cmd->name);
+		struct command *command = command_find(cmd->name);
+
+		if (command != NULL) {
+			cmd->func = command->func;
+			cmd->cmd_flags = command->flags;
+			if (client_command_check_ambiguity(cmd)) {
+				/* do nothing until existing commands are
+				   finished */
+				cmd->waiting_unambiguity = TRUE;
+				io_remove(&client->io);
+				return FALSE;
+			}
+		}
 	}
 
-	client->input_skip_line = TRUE;
 	if (cmd->func == NULL) {
 		/* unknown command */
 		client_send_command_error(cmd, "Unknown command.");
--- a/src/imap/client.h	Wed Dec 20 22:13:07 2006 +0200
+++ b/src/imap/client.h	Wed Dec 20 22:47:02 2006 +0200
@@ -23,6 +23,7 @@
 	pool_t pool;
 	const char *tag;
 	const char *name;
+	enum command_flags cmd_flags;
 
 	command_func_t *func;
 	void *context;
@@ -33,6 +34,7 @@
 	unsigned int cancel:1; /* command is wanted to be cancelled */
 	unsigned int param_error:1;
 	unsigned int output_pending:1;
+	unsigned int waiting_unambiguity:1;
 };
 
 struct client {
--- a/src/imap/cmd-uid.c	Wed Dec 20 22:13:07 2006 +0200
+++ b/src/imap/cmd-uid.c	Wed Dec 20 22:47:02 2006 +0200
@@ -5,6 +5,7 @@
 
 bool cmd_uid(struct client_command_context *cmd)
 {
+	struct command *command;
 	const char *cmd_name;
 
 	/* UID <command> <args> */
@@ -12,7 +13,9 @@
 	if (cmd_name == NULL)
 		return FALSE;
 
-	cmd->func = command_find(t_strconcat("UID ", cmd_name, NULL));
+	command = command_find(t_strconcat("UID ", cmd_name, NULL));
+	cmd->cmd_flags = command->flags;
+	cmd->func = command->func;
 
 	if (cmd->func != NULL) {
 		cmd->uid = TRUE;
--- a/src/imap/commands.c	Wed Dec 20 22:13:07 2006 +0200
+++ b/src/imap/commands.c	Wed Dec 20 22:47:02 2006 +0200
@@ -8,48 +8,49 @@
 #include <stdlib.h>
 
 const struct command imap4rev1_commands[] = {
-	{ "CAPABILITY",		cmd_capability },
-	{ "LOGOUT",		cmd_logout },
-	{ "NOOP",		cmd_noop },
+	{ "CAPABILITY",		cmd_capability,  0 },
+	{ "LOGOUT",		cmd_logout,      0 },
+	{ "NOOP",		cmd_noop,        COMMAND_FLAG_BREAKS_SEQS },
 
-	{ "APPEND",		cmd_append },
-	{ "EXAMINE",		cmd_examine },
-	{ "CREATE",		cmd_create },
-	{ "DELETE",		cmd_delete },
-	{ "RENAME",		cmd_rename },
-	{ "LIST",		cmd_list },
-	{ "LSUB",		cmd_lsub },
-	{ "SELECT",		cmd_select },
-	{ "STATUS",		cmd_status },
-	{ "SUBSCRIBE",		cmd_subscribe },
-	{ "UNSUBSCRIBE",	cmd_unsubscribe },
+	{ "APPEND",		cmd_append,      COMMAND_FLAG_BREAKS_SEQS },
+	{ "EXAMINE",		cmd_examine,     COMMAND_FLAG_BREAKS_MAILBOX },
+	{ "CREATE",		cmd_create,      0 },
+	{ "DELETE",		cmd_delete,      0 },
+	{ "RENAME",		cmd_rename,      0 },
+	{ "LIST",		cmd_list,        0 },
+	{ "LSUB",		cmd_lsub,        0 },
+	{ "SELECT",		cmd_select,      COMMAND_FLAG_BREAKS_MAILBOX },
+	{ "STATUS",		cmd_status,      0 },
+	{ "SUBSCRIBE",		cmd_subscribe,   0 },
+	{ "UNSUBSCRIBE",	cmd_unsubscribe, 0 },
 
-	{ "CHECK",		cmd_check },
-	{ "CLOSE",		cmd_close },
-	{ "COPY",		cmd_copy },
-	{ "EXPUNGE",		cmd_expunge },
-	{ "FETCH",		cmd_fetch },
-	{ "SEARCH",		cmd_search },
-	{ "STORE",		cmd_store },
-	{ "UID",		cmd_uid },
-	{ "UID COPY",		cmd_copy },
-	{ "UID FETCH",		cmd_fetch },
-	{ "UID SEARCH",		cmd_search },
-	{ "UID STORE",		cmd_store }
+	{ "CHECK",		cmd_check,       COMMAND_FLAG_BREAKS_SEQS },
+	{ "CLOSE",		cmd_close,       COMMAND_FLAG_BREAKS_MAILBOX },
+	{ "COPY",		cmd_copy,        COMMAND_FLAG_USES_SEQS |
+						 COMMAND_FLAG_BREAKS_SEQS },
+	{ "EXPUNGE",		cmd_expunge,     COMMAND_FLAG_BREAKS_SEQS },
+	{ "FETCH",		cmd_fetch,       COMMAND_FLAG_USES_SEQS },
+	{ "SEARCH",		cmd_search,      COMMAND_FLAG_USES_SEQS },
+	{ "STORE",		cmd_store,       COMMAND_FLAG_USES_SEQS },
+	{ "UID",		cmd_uid,         0 },
+	{ "UID COPY",		cmd_copy,        COMMAND_FLAG_BREAKS_SEQS },
+	{ "UID FETCH",		cmd_fetch,       COMMAND_FLAG_BREAKS_SEQS },
+	{ "UID SEARCH",		cmd_search,      COMMAND_FLAG_BREAKS_SEQS },
+	{ "UID STORE",		cmd_store,       COMMAND_FLAG_BREAKS_SEQS }
 };
 #define IMAP4REV1_COMMANDS_COUNT \
 	(sizeof(imap4rev1_commands) / sizeof(imap4rev1_commands[0]))
 
 const struct command imap_ext_commands[] = {
-	{ "IDLE",		cmd_idle },
-	{ "NAMESPACE",		cmd_namespace },
-	{ "SORT",		cmd_sort },
-	{ "THREAD",		cmd_thread },
-	{ "UID EXPUNGE",	cmd_uid_expunge },
-	{ "UID SORT",		cmd_sort },
-	{ "UID THREAD",		cmd_thread },
-	{ "UNSELECT",		cmd_unselect },
-	{ "X-CANCEL",		cmd_x_cancel }
+	{ "IDLE",		cmd_idle,        COMMAND_FLAG_BREAKS_SEQS },
+	{ "NAMESPACE",		cmd_namespace,   0 },
+	{ "SORT",		cmd_sort,        COMMAND_FLAG_USES_SEQS },
+	{ "THREAD",		cmd_thread,      COMMAND_FLAG_USES_SEQS },
+	{ "UID EXPUNGE",	cmd_uid_expunge, COMMAND_FLAG_BREAKS_SEQS },
+	{ "UID SORT",		cmd_sort,        COMMAND_FLAG_BREAKS_SEQS },
+	{ "UID THREAD",		cmd_thread,      COMMAND_FLAG_BREAKS_SEQS },
+	{ "UNSELECT",		cmd_unselect,    COMMAND_FLAG_BREAKS_MAILBOX },
+	{ "X-CANCEL",		cmd_x_cancel,    0 }
 };
 #define IMAP_EXT_COMMANDS_COUNT \
 	(sizeof(imap_ext_commands) / sizeof(imap_ext_commands[0]))
@@ -112,9 +113,8 @@
 	return strcasecmp(name, cmd->name);
 }
 
-command_func_t *command_find(const char *name)
+struct command *command_find(const char *name)
 {
-	const struct command *cmd;
 	void *base;
 	unsigned int count;
 
@@ -124,9 +124,8 @@
                 commands_unsorted = FALSE;
 	}
 
-	cmd = bsearch(name, base, count, sizeof(struct command),
-		      command_bsearch);
-	return cmd == NULL ? NULL : cmd->func;
+	return bsearch(name, base, count, sizeof(struct command),
+		       command_bsearch);
 }
 
 void commands_init(void)
--- a/src/imap/commands.h	Wed Dec 20 22:13:07 2006 +0200
+++ b/src/imap/commands.h	Wed Dec 20 22:47:02 2006 +0200
@@ -10,9 +10,20 @@
 
 typedef bool command_func_t(struct client_command_context *cmd);
 
+enum command_flags {
+	/* Command uses sequences as its input parameters */
+	COMMAND_FLAG_USES_SEQS		= 0x01,
+	/* Command may reply with EXPUNGE, causing sequences to break */
+	COMMAND_FLAG_BREAKS_SEQS	= 0x02,
+	/* Command changes the mailbox */
+	COMMAND_FLAG_BREAKS_MAILBOX	= 0x04 | COMMAND_FLAG_BREAKS_SEQS
+};
+
 struct command {
 	const char *name;
 	command_func_t *func;
+
+	enum command_flags flags;
 };
 
 /* Register command. Given name parameter must be permanently stored until
@@ -24,7 +35,7 @@
 void command_register_array(const struct command *cmdarr, unsigned int count);
 void command_unregister_array(const struct command *cmdarr, unsigned int count);
 
-command_func_t *command_find(const char *name);
+struct command *command_find(const char *name);
 
 void commands_init(void);
 void commands_deinit(void);