[dovecot-cvs] dovecot/src/imap Makefile.am, 1.37, 1.38 client.c, 1.72, 1.73 client.h, 1.38, 1.39 cmd-append.c, 1.85, 1.86 cmd-fetch.c, 1.32, 1.33 cmd-idle.c, 1.34, 1.35 cmd-list.c, 1.59, 1.60 cmd-search.c, 1.29, 1.30 cmd-sort.c, 1.21, 1.22 cmd-thread.c, 1.12, 1.13 cmd-uid.c, 1.7, 1.8 commands.c, 1.18, 1.19 commands.h, 1.20, 1.21 imap-fetch.c, 1.50, 1.51 imap-fetch.h, 1.22, 1.23 imap-sync.c, 1.21, 1.22

tss at dovecot.org tss at dovecot.org
Wed Dec 20 19:23:54 UTC 2006


Update of /var/lib/cvs/dovecot/src/imap
In directory talvi:/tmp/cvs-serv31150/imap

Modified Files:
	Makefile.am client.c client.h cmd-append.c cmd-fetch.c 
	cmd-idle.c cmd-list.c cmd-search.c cmd-sort.c cmd-thread.c 
	cmd-uid.c commands.c commands.h imap-fetch.c imap-fetch.h 
	imap-sync.c 
Log Message:
Dovecot is now able to execute multiple commands at the same time.
Practically this means commands: FETCH, LIST, SEARCH and syncing output for
all commands. For example it's possible that doing two FETCH commands at the
same time makes their output mixed together.

Non-blocking SEARCH is done by doing search for 20 mails at a time, and then
checking if another command is pending.

Also added X-CANCEL <tag> command to cancel running commands.



Index: Makefile.am
===================================================================
RCS file: /var/lib/cvs/dovecot/src/imap/Makefile.am,v
retrieving revision 1.37
retrieving revision 1.38
diff -u -d -r1.37 -r1.38
--- Makefile.am	25 Nov 2006 22:17:40 -0000	1.37
+++ Makefile.am	20 Dec 2006 19:23:46 -0000	1.38
@@ -17,6 +17,7 @@
 # be a shared library so this wouldn't be needed..
 unused_objects = \
 	../lib/mountpoint.o \
+	../lib/unichar.o \
 	../lib-mail/message-decoder.o
 
 libs = \
@@ -66,7 +67,8 @@
 	cmd-thread.c \
 	cmd-uid.c \
 	cmd-unselect.c \
-	cmd-unsubscribe.c
+	cmd-unsubscribe.c \
+	cmd-x-cancel.c
 
 imap_SOURCES = \
 	$(cmds) \

Index: client.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/imap/client.c,v
retrieving revision 1.72
retrieving revision 1.73
diff -u -d -r1.72 -r1.73
--- client.c	15 Dec 2006 18:38:10 -0000	1.72
+++ client.c	20 Dec 2006 19:23:46 -0000	1.73
@@ -36,13 +36,9 @@
 	o_stream_set_flush_callback(client->output, _client_output, client);
 
 	client->io = io_add(fd_in, IO_READ, _client_input, client);
-	client->parser = imap_parser_create(client->input, client->output,
-					    imap_max_line_length);
         client->last_input = ioloop_time;
 
-	client->cmd.pool = pool_alloconly_create("command pool", 8192);
-	client->cmd.client = client;
-
+	client->command_pool = pool_alloconly_create("client command", 8192);
 	client->keywords.pool = pool_alloconly_create("mailbox_keywords", 512);
 	client->namespaces = namespaces;
 
@@ -60,10 +56,22 @@
 	return client;
 }
 
-void client_destroy(struct client *client, const char *reason)
+void client_command_cancel(struct client_command_context *cmd)
 {
-	int ret;
+	bool cmd_ret;
+
+	cmd->cancel = TRUE;
+	cmd_ret = cmd->func(cmd);
+	if (!cmd_ret) {
+		if (cmd->client->output->closed)
+			i_panic("command didn't cancel itself: %s", cmd->name);
+	} else {
+		client_command_free(cmd);
+	}
+}
 
+void client_destroy(struct client *client, const char *reason)
+{
 	i_assert(!client->destroyed);
 	client->destroyed = TRUE;
 
@@ -74,22 +82,23 @@
 		i_info("%s", reason);
 	}
 
-	if (client->command_pending) {
-		/* try to deinitialize the command */
-		i_assert(client->cmd.func != NULL);
-		i_stream_close(client->input);
-		o_stream_close(client->output);
-		client->input_pending = FALSE;
+	i_stream_close(client->input);
+	o_stream_close(client->output);
 
-		ret = client->cmd.func(&client->cmd);
-		i_assert(ret);
-	}
+	/* finish off all the queued commands. */
+	if (client->output_lock != NULL)
+		client_command_cancel(client->output_lock);
+	if (client->input_lock != NULL)
+		client_command_cancel(client->input_lock);
+	while (client->command_queue != NULL)
+		client_command_cancel(client->command_queue);
 
 	if (client->mailbox != NULL)
 		mailbox_close(&client->mailbox);
 	namespace_deinit(client->namespaces);
 
-	imap_parser_destroy(&client->parser);
+	if (client->free_parser != NULL)
+		imap_parser_destroy(&client->free_parser);
 	if (client->io != NULL)
 		io_remove(&client->io);
 
@@ -104,7 +113,7 @@
 	}
 
 	pool_unref(client->keywords.pool);
-	pool_unref(client->cmd.pool);
+	pool_unref(client->command_pool);
 	i_free(client);
 
 	/* quit the program */
@@ -161,7 +170,7 @@
 	struct client *client = cmd->client;
 	const char *tag = cmd->tag;
 
-	if (client->output->closed)
+	if (client->output->closed || cmd->cancel)
 		return;
 
 	if (tag == NULL || *tag == '\0')
@@ -181,7 +190,7 @@
 	bool fatal;
 
 	if (msg == NULL) {
-		msg = imap_parser_get_error(client->parser, &fatal);
+		msg = imap_parser_get_error(cmd->parser, &fatal);
 		if (fatal) {
 			client_disconnect_with_error(client, msg);
 			return;
@@ -218,9 +227,12 @@
 
 	i_assert(count <= INT_MAX);
 
-	ret = imap_parser_read_args(cmd->client->parser, count, flags, args);
+	ret = imap_parser_read_args(cmd->parser, count, flags, args);
 	if (ret >= (int)count) {
 		/* all parameters read successfully */
+		i_assert(cmd->client->input_lock == NULL ||
+			 cmd->client->input_lock == cmd);
+		cmd->client->input_lock = NULL;
 		return TRUE;
 	} else if (ret == -2) {
 		/* need more data */
@@ -267,38 +279,97 @@
 	return i == count;
 }
 
-void _client_reset_command(struct client *client)
+static struct client_command_context *
+client_command_new(struct client *client)
 {
-	pool_t pool;
+	struct client_command_context *cmd;
+
+	cmd = p_new(client->command_pool, struct client_command_context, 1);
+	cmd->client = client;
+	cmd->pool = client->command_pool;
+
+	if (client->free_parser != NULL) {
+		cmd->parser = client->free_parser;
+		client->free_parser = NULL;
+	} else {
+		cmd->parser = imap_parser_create(client->input, client->output,
+						 imap_max_line_length);
+	}
+
+	/* add to beginning of the queue */
+	if (client->command_queue != NULL) {
+		client->command_queue->prev = cmd;
+		cmd->next = client->command_queue;
+	}
+	client->command_queue = cmd;
+	client->command_queue_size++;
+
+	return cmd;
+}
+
+void client_command_free(struct client_command_context *cmd)
+{
+	struct client *client = cmd->client;
 	size_t size;
 
 	/* reset input idle time because command output might have taken a
 	   long time and we don't want to disconnect client immediately then */
 	client->last_input = ioloop_time;
 
-	client->command_pending = FALSE;
-	if (client->io == NULL && !client->disconnected) {
-		i_assert(i_stream_get_fd(client->input) >= 0);
-		client->io = io_add(i_stream_get_fd(client->input),
-				    IO_READ, _client_input, client);
+	if (cmd->cancel) {
+		cmd->cancel = FALSE;
+		client_send_tagline(cmd, "NO Command cancelled.");
 	}
-	o_stream_set_flush_callback(client->output, _client_output, client);
 
-	pool = client->cmd.pool;
-	memset(&client->cmd, 0, sizeof(client->cmd));
+	if (!cmd->param_error)
+		client->bad_counter = 0;
 
-	p_clear(pool);
-	client->cmd.pool = pool;
-	client->cmd.client = client;
+	if (client->input_lock == cmd) {
+		/* reset the input handler in case it was changed */
+		client->input_lock = NULL;
+	}
+	if (client->output_lock == cmd) {
+		/* reset the output handler in case it was changed */
+		o_stream_set_flush_callback(client->output,
+					    _client_output, client);
+		client->output_lock = NULL;
+	}
 
-	imap_parser_reset(client->parser);
+	if (client->free_parser != NULL)
+		imap_parser_destroy(&cmd->parser);
+	else {
+		imap_parser_reset(cmd->parser);
+		client->free_parser = cmd->parser;
+	}
 
-	/* if there's unread data in buffer, remember that there's input
-	   pending and we should get around to calling client_input() soon.
-	   This is mostly for APPEND/IDLE. */
-	(void)i_stream_get_data(client->input, &size);
-	if (size > 0 && !client->destroyed)
-		client->input_pending = TRUE;
+	client->command_queue_size--;
+	if (cmd->prev != NULL)
+		cmd->prev->next = cmd->next;
+	else
+		client->command_queue = cmd->next;
+	if (cmd->next != NULL)
+		cmd->next->prev = cmd->prev;
+	cmd = NULL;
+
+	if (client->command_queue == NULL) {
+		/* no commands left in the queue, we can clear the pool */
+		p_clear(client->command_pool);
+	}
+
+	if (client->input_lock == NULL && !client->disconnected) {
+		if (client->io == NULL) {
+			i_assert(i_stream_get_fd(client->input) >= 0);
+			client->io = io_add(i_stream_get_fd(client->input),
+					    IO_READ, _client_input, client);
+		}
+
+		/* if there's unread data in buffer, handle it. */
+		if (!client->handling_input) {
+			(void)i_stream_get_data(client->input, &size);
+			if (size > 0 && !client->destroyed)
+				_client_input(client);
+		}
+	}
 }
 
 /* Skip incoming data until newline is found,
@@ -322,7 +393,7 @@
 	return !client->input_skip_line;
 }
 
-static bool client_handle_input(struct client_command_context *cmd)
+static bool client_command_input(struct client_command_context *cmd)
 {
 	struct client *client = cmd->client;
 
@@ -330,33 +401,25 @@
 		/* command is being executed - continue it */
 		if (cmd->func(cmd) || cmd->param_error) {
 			/* command execution was finished */
-                        client->bad_counter = 0;
-			_client_reset_command(client);
+			client_command_free(cmd);
 			return TRUE;
 		}
-		return FALSE;
-	}
-
-	if (client->input_skip_line) {
-		/* we're just waiting for new line.. */
-		if (!client_skip_line(client))
-			return FALSE;
 
-		/* got the newline */
-		_client_reset_command(client);
-
-		/* pass through to parse next command */
+		/* unfinished */
+		if (cmd->output_pending)
+			o_stream_set_flush_pending(client->output, TRUE);
+		return FALSE;
 	}
 
 	if (cmd->tag == NULL) {
-                cmd->tag = imap_parser_read_word(client->parser);
+                cmd->tag = imap_parser_read_word(cmd->parser);
 		if (cmd->tag == NULL)
 			return FALSE; /* need more data */
 		cmd->tag = p_strdup(cmd->pool, cmd->tag);
 	}
 
 	if (cmd->name == NULL) {
-		cmd->name = imap_parser_read_word(client->parser);
+		cmd->name = imap_parser_read_word(cmd->parser);
 		if (cmd->name == NULL)
 			return FALSE; /* need more data */
 		cmd->name = p_strdup(cmd->pool, cmd->name);
@@ -369,44 +432,57 @@
 		cmd->func = command_find(cmd->name);
 	}
 
+	client->input_skip_line = TRUE;
 	if (cmd->func == NULL) {
 		/* unknown command */
 		client_send_command_error(cmd, "Unknown command.");
-		client->input_skip_line = TRUE;
-		_client_reset_command(client);
+		cmd->param_error = TRUE;
+		client_command_free(cmd);
+		return TRUE;
 	} else {
 		i_assert(!client->disconnected);
 
-		client->input_skip_line = TRUE;
-		if (cmd->func(cmd) || cmd->param_error) {
-			/* command execution was finished. */
-                        client->bad_counter = 0;
-			_client_reset_command(client);
-		} else {
-			/* unfinished */
-			if (client->command_pending) {
-				o_stream_set_flush_pending(client->output,
-							   TRUE);
-			}
+		return client_command_input(cmd);
+	}
+}
+
+static bool client_handle_next_command(struct client *client)
+{
+	size_t size;
+
+	if (client->input_lock != NULL)
+		return client_command_input(client->input_lock);
+
+	if (client->input_skip_line) {
+		/* first eat the previous command line */
+		if (!client_skip_line(client))
 			return FALSE;
-		}
+		client->input_skip_line = FALSE;
 	}
 
-	return TRUE;
+	/* don't bother creating a new client command before there's at least
+	   some input */
+	(void)i_stream_get_data(client->input, &size);
+	if (size == 0)
+		return FALSE;
+
+	/* beginning a new command */
+	if (client->command_queue_size >= CLIENT_COMMAND_QUEUE_MAX_SIZE ||
+	    client->output_lock != NULL) {
+		/* wait for some of the commands to finish */
+		io_remove(&client->io);
+		return FALSE;
+	}
+
+	client->input_lock = client_command_new(client);
+	return client_command_input(client->input_lock);
 }
 
 void _client_input(struct client *client)
 {
-	struct client_command_context *cmd = &client->cmd;
+	struct client_command_context *cmd;
 	int ret;
 
-	if (client->command_pending) {
-		/* already processing one command. wait. */
-		io_remove(&client->io);
-		return;
-	}
-
-	client->input_pending = FALSE;
 	client->last_input = ioloop_time;
 
 	switch (i_stream_read(client->input)) {
@@ -420,31 +496,49 @@
 		   until newline is found. */
 		client->input_skip_line = TRUE;
 
+		cmd = client->input_lock != NULL ? client->input_lock :
+			client_command_new(client);
+		cmd->param_error = TRUE;
 		client_send_command_error(cmd, "Too long argument.");
-		_client_reset_command(client);
-		break;
+		client_command_free(cmd);
+		return;
 	}
 
 	o_stream_cork(client->output);
+	client->handling_input = TRUE;
 	do {
 		t_push();
-		ret = client_handle_input(cmd);
+		ret = client_handle_next_command(client);
 		t_pop();
 	} while (ret);
+	client->handling_input = FALSE;
 	o_stream_uncork(client->output);
 
-	if (client->command_pending)
-		client->input_pending = TRUE;
-
 	if (client->output->closed)
 		client_destroy(client, NULL);
 }
 
+static void client_output_cmd(struct client_command_context *cmd)
+{
+	struct client *client = cmd->client;
+	bool finished;
+
+	/* continue processing command */
+	finished = cmd->func(cmd) || cmd->param_error;
+
+	if (!finished) {
+		if (cmd->output_pending)
+			o_stream_set_flush_pending(client->output, TRUE);
+	} else {
+		/* command execution was finished */
+		client_command_free(cmd);
+	}
+}
+
 int _client_output(struct client *client)
 {
-	struct client_command_context *cmd = &client->cmd;
+	struct client_command_context *cmd;
 	int ret;
-	bool finished;
 
 	client->last_output = ioloop_time;
 
@@ -453,30 +547,18 @@
 		return 1;
 	}
 
-	if (!client->command_pending)
-		return 1;
-
-	/* continue processing command */
 	o_stream_cork(client->output);
-	client->output_pending = TRUE;
-	finished = cmd->func(cmd) || cmd->param_error;
-
-	/* a bit kludgy check. normally we would want to get back to this
-	   output handler, but IDLE is a special case which has command
-	   pending but without necessarily anything to write. */
-	if (!finished && client->output_pending)
-		o_stream_set_flush_pending(client->output, TRUE);
-
-	o_stream_uncork(client->output);
-
-	if (finished) {
-		/* command execution was finished */
-		client->bad_counter = 0;
-		_client_reset_command(client);
-
-		if (client->input_pending)
-			_client_input(client);
+	if (client->output_lock != NULL)
+		client_output_cmd(client->output_lock);
+	if (client->output_lock == NULL) {
+		cmd = client->command_queue;
+		for (; cmd != NULL; cmd = cmd->next) {
+			client_output_cmd(cmd);
+			if (client->output_lock != NULL)
+				break;
+		}
 	}
+	o_stream_uncork(client->output);
 	return ret;
 }
 
@@ -490,15 +572,14 @@
 	idle_time = ioloop_time -
 		I_MAX(my_client->last_input, my_client->last_output);
 
-	if (my_client->command_pending &&
-	    o_stream_get_buffer_used_size(my_client->output) > 0 &&
+	if (o_stream_get_buffer_used_size(my_client->output) > 0 &&
 	    idle_time >= CLIENT_OUTPUT_TIMEOUT) {
 		/* client isn't reading our output */
 		client_destroy(my_client, "Disconnected for inactivity "
 			       "in reading our output");
 	} else if (idle_time >= CLIENT_IDLE_TIMEOUT) {
 		/* client isn't sending us anything */
-		if (!my_client->command_pending) {
+		if (my_client->output_lock == NULL) {
 			client_send_line(my_client,
 					 "* BYE Disconnected for inactivity.");
 		}

Index: client.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/imap/client.h,v
retrieving revision 1.38
retrieving revision 1.39
diff -u -d -r1.38 -r1.39
--- client.h	15 Dec 2006 18:38:10 -0000	1.38
+++ client.h	20 Dec 2006 19:23:46 -0000	1.39
@@ -3,6 +3,8 @@
 
 #include "commands.h"
 
+#define CLIENT_COMMAND_QUEUE_MAX_SIZE 4
+
 struct client;
 struct mail_storage;
 struct imap_parser;
@@ -15,6 +17,7 @@
 };
 
 struct client_command_context {
+	struct client_command_context *prev, *next;
 	struct client *client;
 
 	pool_t pool;
@@ -24,8 +27,12 @@
 	command_func_t *func;
 	void *context;
 
+	struct imap_parser *parser;
+
 	unsigned int uid:1; /* used UID command */
+	unsigned int cancel:1; /* command is wanted to be cancelled */
 	unsigned int param_error:1;
+	unsigned int output_pending:1;
 };
 
 struct client {
@@ -43,14 +50,20 @@
 	time_t last_input, last_output;
 	unsigned int bad_counter;
 
-	struct imap_parser *parser;
-	struct client_command_context cmd;
+	/* one parser is kept here to be used for new commands */
+	struct imap_parser *free_parser;
+	/* command_pool is cleared when the command queue gets empty */
+	pool_t command_pool;
+	struct client_command_context *command_queue;
+	unsigned int command_queue_size;
+
+	/* client input/output is locked by this command */
+	struct client_command_context *input_lock;
+	struct client_command_context *output_lock;
 
 	unsigned int disconnected:1;
 	unsigned int destroyed:1;
-	unsigned int command_pending:1;
-	unsigned int input_pending:1;
-	unsigned int output_pending:1;
+	unsigned int handling_input:1;
 	unsigned int rawlog:1;
 	unsigned int input_skip_line:1; /* skip all the data until we've
 					   found a new line */
@@ -88,7 +101,9 @@
 void clients_init(void);
 void clients_deinit(void);
 
-void _client_reset_command(struct client *client);
+void client_command_cancel(struct client_command_context *cmd);
+void client_command_free(struct client_command_context *cmd);
+
 void _client_input(struct client *client);
 int _client_output(struct client *client);
 

Index: cmd-append.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/imap/cmd-append.c,v
retrieving revision 1.85
retrieving revision 1.86
diff -u -d -r1.85 -r1.86
--- cmd-append.c	15 Dec 2006 18:38:10 -0000	1.85
+++ cmd-append.c	20 Dec 2006 19:23:46 -0000	1.86
@@ -24,14 +24,17 @@
 
 	struct imap_parser *save_parser;
 	struct mail_save_context *save_ctx;
+
+	unsigned int message_input:1;
 };
 
 static void cmd_append_finish(struct cmd_append_context *ctx);
 static bool cmd_append_continue_message(struct client_command_context *cmd);
 
-static void client_input(struct client *client)
+static void client_input(struct client_command_context *cmd)
 {
-	struct client_command_context *cmd = &client->cmd;
+	struct cmd_append_context *ctx = cmd->context;
+	struct client *client = cmd->client;
 
 	client->last_input = ioloop_time;
 
@@ -41,12 +44,12 @@
 		cmd_append_finish(cmd->context);
 		/* Reset command so that client_destroy() doesn't try to call
 		   cmd_append_continue_message() anymore. */
-		_client_reset_command(client);
+		client_command_free(cmd);
 		client_destroy(client, "Disconnected in APPEND");
 		return;
 	case -2:
 		cmd_append_finish(cmd->context);
-		if (client->command_pending) {
+		if (ctx->message_input) {
 			/* message data, this is handled internally by
 			   mailbox_save_continue() */
 			break;
@@ -58,22 +61,13 @@
 		client->input_skip_line = TRUE;
 
 		client_send_command_error(cmd, "Too long argument.");
-		_client_reset_command(client);
+		cmd->param_error = TRUE;
+		client_command_free(cmd);
 		return;
 	}
 
-	if (cmd->func(cmd)) {
-		/* command execution was finished. Note that if cmd_sync()
-		   didn't finish, we didn't get here but the input handler
-		   has already been moved. So don't do anything important
-		   here..
-
-		   reset command once again to reset cmd_sync()'s changes. */
-		_client_reset_command(client);
-
-		if (client->input_pending)
-			_client_input(client);
-	}
+	if (cmd->func(cmd))
+		client_command_free(cmd);
 }
 
 /* Returns -1 = error, 0 = need more data, 1 = successful. flags and
@@ -112,25 +106,18 @@
 
 static void cmd_append_finish(struct cmd_append_context *ctx)
 {
-	io_remove(&ctx->client->io);
-
         imap_parser_destroy(&ctx->save_parser);
 
+	i_assert(ctx->client->input_lock == ctx->cmd);
+
 	if (ctx->input != NULL)
 		i_stream_unref(&ctx->input);
-
 	if (ctx->save_ctx != NULL)
 		mailbox_save_cancel(&ctx->save_ctx);
-
 	if (ctx->t != NULL)
 		mailbox_transaction_rollback(&ctx->t);
-
 	if (ctx->box != ctx->cmd->client->mailbox && ctx->box != NULL)
 		mailbox_close(&ctx->box);
-
-	ctx->client->bad_counter = 0;
-	o_stream_set_flush_callback(ctx->client->output,
-				    _client_output, ctx->client);
 }
 
 static bool cmd_append_continue_cancel(struct client_command_context *cmd)
@@ -138,6 +125,11 @@
 	struct cmd_append_context *ctx = cmd->context;
 	size_t size;
 
+	if (cmd->cancel) {
+		cmd_append_finish(ctx);
+		return TRUE;
+	}
+
 	(void)i_stream_read(ctx->input);
 	(void)i_stream_get_data(ctx->input, &size);
 	i_stream_skip(ctx->input, size);
@@ -163,7 +155,7 @@
 					   ctx->client->input->v_offset,
 					   ctx->msg_size);
 
-	ctx->client->command_pending = TRUE;
+	ctx->message_input = TRUE;
 	ctx->cmd->func = cmd_append_continue_cancel;
 	ctx->cmd->context = ctx;
 	return cmd_append_continue_cancel(ctx->cmd);
@@ -183,6 +175,11 @@
 	int ret, timezone_offset;
 	bool nonsync;
 
+	if (cmd->cancel) {
+		cmd_append_finish(ctx);
+		return TRUE;
+	}
+
 	/* if error occurs, the CRLF is already read. */
 	client->input_skip_line = FALSE;
 
@@ -293,7 +290,7 @@
 		o_stream_uncork(client->output);
 	}
 
-	client->command_pending = TRUE;
+	ctx->message_input = TRUE;
 	cmd->func = cmd_append_continue_message;
 	return cmd_append_continue_message(cmd);
 }
@@ -305,6 +302,11 @@
 	size_t size;
 	bool failed;
 
+	if (cmd->cancel) {
+		cmd_append_finish(ctx);
+		return TRUE;
+	}
+
 	if (ctx->save_ctx != NULL) {
 		if (mailbox_save_continue(ctx->save_ctx) < 0) {
 			/* we still have to finish reading the message
@@ -349,7 +351,7 @@
 		}
 
 		/* prepare for next message */
-		client->command_pending = FALSE;
+		ctx->message_input = FALSE;
 		imap_parser_reset(ctx->save_parser);
 		cmd->func = cmd_append_continue_parsing;
 		return cmd_append_continue_parsing(cmd);
@@ -395,6 +397,9 @@
 	if (!client_read_string_args(cmd, 1, &mailbox))
 		return FALSE;
 
+	/* we keep the input locked all the time */
+	client->input_lock = cmd;
+
 	ctx = p_new(cmd->pool, struct cmd_append_context, 1);
 	ctx->cmd = cmd;
 	ctx->client = client;
@@ -417,7 +422,7 @@
 
 	io_remove(&client->io);
 	client->io = io_add(i_stream_get_fd(client->input), IO_READ,
-			    client_input, client);
+			    client_input, cmd);
 	/* append is special because we're only waiting on client input, not
 	   client output, so disable the standard output handler until we're
 	   finished */

Index: cmd-fetch.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/imap/cmd-fetch.c,v
retrieving revision 1.32
retrieving revision 1.33
diff -u -d -r1.32 -r1.33
--- cmd-fetch.c	7 Jun 2006 09:05:05 -0000	1.32
+++ cmd-fetch.c	20 Dec 2006 19:23:46 -0000	1.33
@@ -118,13 +118,9 @@
         struct imap_fetch_context *ctx = cmd->context;
 	int ret;
 
-	if (cmd->client->output->closed)
-		ret = -1;
-	else {
-		if ((ret = imap_fetch(ctx)) == 0) {
-			/* unfinished */
-			return FALSE;
-		}
+	if ((ret = imap_fetch(ctx)) == 0) {
+		/* unfinished */
+		return FALSE;
 	}
 	if (ret < 0)
 		ctx->failed = TRUE;
@@ -134,7 +130,6 @@
 
 bool cmd_fetch(struct client_command_context *cmd)
 {
-	struct client *client = cmd->client;
 	struct imap_fetch_context *ctx;
 	struct imap_arg *args;
 	struct mail_search_arg *search_arg;
@@ -170,7 +165,8 @@
 	imap_fetch_begin(ctx, search_arg);
 	if ((ret = imap_fetch(ctx)) == 0) {
 		/* unfinished */
-		client->command_pending = TRUE;
+		cmd->output_pending = TRUE;
+
 		cmd->func = cmd_fetch_continue;
 		cmd->context = ctx;
 		return FALSE;

Index: cmd-idle.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/imap/cmd-idle.c,v
retrieving revision 1.34
retrieving revision 1.35
diff -u -d -r1.34 -r1.35
--- cmd-idle.c	15 Dec 2006 18:38:10 -0000	1.34
+++ cmd-idle.c	20 Dec 2006 19:23:46 -0000	1.35
@@ -62,12 +62,7 @@
 		client_send_tagline(ctx->cmd, "BAD Expected DONE.");
 
 	o_stream_uncork(client->output);
-
-	client->bad_counter = 0;
-	_client_reset_command(client);
-
-	if (client->input_pending)
-		_client_input(client);
+	client_command_free(ctx->cmd);
 }
 
 static void idle_client_input(struct cmd_idle_context *ctx)
@@ -134,7 +129,7 @@
 
 static void keepalive_timeout(struct cmd_idle_context *ctx)
 {
-	if (ctx->client->output_pending) {
+	if (ctx->client->output_lock != NULL) {
 		/* it's busy sending output */
 		return;
 	}
@@ -166,6 +161,11 @@
 	struct client *client = cmd->client;
 	struct cmd_idle_context *ctx = cmd->context;
 
+	if (cmd->cancel) {
+		idle_finish(ctx, FALSE);
+		return TRUE;
+	}
+
 	if (ctx->manual_cork)  {
 		/* we're coming from idle_callback instead of a normal
 		   I/O handler, so we'll have to do corking manually */
@@ -179,6 +179,7 @@
 				ctx->manual_cork = FALSE;
 				o_stream_uncork(client->output);
 			}
+			cmd->output_pending = TRUE;
 			return FALSE;
 		}
 
@@ -201,7 +202,7 @@
 		   so we return here instead of doing everything twice. */
 		return FALSE;
 	}
-        client->output_pending = FALSE;
+        cmd->output_pending = FALSE;
 
 	if (ctx->manual_cork) {
 		ctx->manual_cork = FALSE;
@@ -255,7 +256,6 @@
 	client->io = io_add(i_stream_get_fd(client->input),
 			    IO_READ, idle_client_input, ctx);
 
-	client->command_pending = TRUE;
 	cmd->func = cmd_idle_continue;
 	cmd->context = ctx;
 

Index: cmd-list.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/imap/cmd-list.c,v
retrieving revision 1.59
retrieving revision 1.60
diff -u -d -r1.59 -r1.60
--- cmd-list.c	25 Nov 2006 22:17:40 -0000	1.59
+++ cmd-list.c	20 Dec 2006 19:23:46 -0000	1.60
@@ -389,6 +389,13 @@
         struct cmd_list_context *ctx = cmd->context;
 	int ret;
 
+	if (cmd->cancel) {
+		if (ctx->list_iter != NULL) {
+			if (mailbox_list_iter_deinit(&ctx->list_iter) < 0)
+				mail_storage_set_list_error(ctx->ns->storage);
+		}
+		return TRUE;
+	}
 	for (; ctx->ns != NULL; ctx->ns = ctx->ns->next) {
 		if (ctx->list_iter == NULL)
 			list_namespace_init(cmd, ctx);
@@ -497,7 +504,7 @@
 		cmd->context = ctx;
 		if (!cmd_list_continue(cmd)) {
 			/* unfinished */
-			client->command_pending = TRUE;
+			cmd->output_pending = TRUE;
 			cmd->func = cmd_list_continue;
 			return FALSE;
 		}

Index: cmd-search.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/imap/cmd-search.c,v
retrieving revision 1.29
retrieving revision 1.30
diff -u -d -r1.29 -r1.30
--- cmd-search.c	2 Feb 2006 19:37:59 -0000	1.29
+++ cmd-search.c	20 Dec 2006 19:23:46 -0000	1.30
@@ -6,61 +6,121 @@
 #include "commands.h"
 #include "imap-search.h"
 
-#define STRBUF_SIZE 1024
+#define OUTBUF_SIZE 65536
 
-static bool imap_search(struct client_command_context *cmd, const char *charset,
-			struct mail_search_arg *sargs)
-{
-	struct client *client = cmd->client;
-        struct mail_search_context *ctx;
+struct imap_search_context {
         struct mailbox_transaction_context *trans;
+        struct mail_search_context *search_ctx;
 	struct mail *mail;
-	string_t *str;
+
+	struct timeout *to;
+	string_t *output_buf;
+
+	unsigned int output_sent:1;
+};
+
+static struct imap_search_context *
+imap_search_init(struct client_command_context *cmd, const char *charset,
+		 struct mail_search_arg *sargs)
+{
+	struct imap_search_context *ctx;
+
+	ctx = p_new(cmd->pool, struct imap_search_context, 1);
+	ctx->trans = mailbox_transaction_begin(cmd->client->mailbox, 0);
+	ctx->search_ctx = mailbox_search_init(ctx->trans, charset, sargs, NULL);
+	ctx->mail = mail_alloc(ctx->trans, 0, NULL);
+
+	ctx->output_buf = str_new(default_pool, OUTBUF_SIZE);
+	str_append(ctx->output_buf, "* SEARCH");
+	return ctx;
+}
+
+static int imap_search_deinit(struct client_command_context *cmd,
+			      struct imap_search_context *ctx)
+{
 	int ret;
-	bool uid, first = TRUE;
 
-	str = t_str_new(STRBUF_SIZE);
-	uid = cmd->uid;
+	mail_free(&ctx->mail);
+	ret = mailbox_search_deinit(&ctx->search_ctx);
 
-	trans = mailbox_transaction_begin(client->mailbox, 0);
-	ctx = mailbox_search_init(trans, charset, sargs, NULL);
+	if (mailbox_transaction_commit(&ctx->trans, 0) < 0)
+		ret = -1;
 
-	str_append(str, "* SEARCH");
-	mail = mail_alloc(trans, 0, NULL);
-	while ((ret = mailbox_search_next(ctx, mail)) > 0) {
-		if (str_len(str) >= STRBUF_SIZE-MAX_INT_STRLEN) {
-			/* flush */
-			o_stream_send(client->output,
-				      str_data(str), str_len(str));
-			str_truncate(str, 0);
-			first = FALSE;
-		}
+	if (ctx->output_sent || (ret == 0 && !cmd->cancel)) {
+		str_append(ctx->output_buf, "\r\n");
+		o_stream_send(cmd->client->output,
+			      str_data(ctx->output_buf),
+			      str_len(ctx->output_buf));
+	}
+	if (ctx->to != NULL)
+		timeout_remove(&ctx->to);
+	str_free(&ctx->output_buf);
+	return ret;
+}
 
-		str_printfa(str, " %u", uid ? mail->uid : mail->seq);
+static bool cmd_search_more(struct client_command_context *cmd)
+{
+	struct imap_search_context *ctx = cmd->context;
+	bool tryagain;
+	int ret;
+
+	if (cmd->cancel) {
+		(void)imap_search_deinit(cmd, ctx);
+		return TRUE;
 	}
-	mail_free(&mail);
 
-	ret = mailbox_search_deinit(&ctx);
+	while ((ret = mailbox_search_next_nonblock(ctx->search_ctx, ctx->mail,
+						   &tryagain)) > 0) {
+		if (str_len(ctx->output_buf) >= OUTBUF_SIZE - MAX_INT_STRLEN) {
+			/* flush. this also causes us to lock the output. */
+			cmd->client->output_lock = cmd;
+			o_stream_send(cmd->client->output,
+				      str_data(ctx->output_buf),
+				      str_len(ctx->output_buf));
+			str_truncate(ctx->output_buf, 0);
+			ctx->output_sent = TRUE;
+		}
 
-	if (mailbox_transaction_commit(&trans, 0) < 0)
+		str_printfa(ctx->output_buf, " %u",
+			    cmd->uid ? ctx->mail->uid : ctx->mail->seq);
+	}
+	if (tryagain)
+		return FALSE;
+
+	if (imap_search_deinit(cmd, ctx) < 0)
 		ret = -1;
+	cmd->context = NULL;
 
-	if (!first || ret == 0) {
-		str_append(str, "\r\n");
-		o_stream_send(client->output, str_data(str), str_len(str));
+	if (ret < 0) {
+		client_send_storage_error(cmd,
+			mailbox_get_storage(cmd->client->mailbox));
+		return TRUE;
+	} else {
+		return cmd_sync(cmd, MAILBOX_SYNC_FLAG_FAST |
+				(cmd->uid ? 0 : MAILBOX_SYNC_FLAG_NO_EXPUNGES),
+				0, "OK Search completed.");
+	}
+}
+
+static void cmd_search_more_callback(struct client_command_context *cmd)
+{
+	if (cmd_search_more(cmd))
+		client_command_free(cmd);
+	else {
+		if (cmd->output_pending)
+			o_stream_set_flush_pending(cmd->client->output, TRUE);
 	}
-	return ret == 0;
 }
 
 bool cmd_search(struct client_command_context *cmd)
 {
-	struct client *client = cmd->client;
+	struct imap_search_context *ctx;
 	struct mail_search_arg *sargs;
 	struct imap_arg *args;
 	int args_count;
 	const char *error, *charset;
 
-	args_count = imap_parser_read_args(client->parser, 0, 0, &args);
+	args_count = imap_parser_read_args(cmd->parser, 0, 0, &args);
 	if (args_count < 1) {
 		if (args_count == -2)
 			return FALSE;
@@ -69,6 +129,7 @@
 					  "Missing SEARCH arguments.");
 		return TRUE;
 	}
+	cmd->client->input_lock = NULL;
 
 	if (!client_verify_open_mailbox(cmd))
 		return TRUE;
@@ -90,19 +151,21 @@
 		charset = NULL;
 	}
 
-	sargs = imap_search_args_build(cmd->pool, client->mailbox,
+	sargs = imap_search_args_build(cmd->pool, cmd->client->mailbox,
 				       args, &error);
 	if (sargs == NULL) {
 		/* error in search arguments */
 		client_send_tagline(cmd, t_strconcat("NO ", error, NULL));
-	} else if (imap_search(cmd, charset, sargs)) {
-		return cmd_sync(cmd, MAILBOX_SYNC_FLAG_FAST |
-				(cmd->uid ? 0 : MAILBOX_SYNC_FLAG_NO_EXPUNGES),
-				0, "OK Search completed.");
-	} else {
-		client_send_storage_error(cmd,
-					  mailbox_get_storage(client->mailbox));
+		return TRUE;
 	}
 
-	return TRUE;
+	ctx = imap_search_init(cmd, charset, sargs);
+	cmd->func = cmd_search_more;
+	cmd->context = ctx;
+
+	if (cmd_search_more(cmd))
+		return TRUE;
+
+	ctx->to = timeout_add(0, cmd_search_more_callback, cmd);
+	return FALSE;
 }

Index: cmd-sort.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/imap/cmd-sort.c,v
retrieving revision 1.21
retrieving revision 1.22
diff -u -d -r1.21 -r1.22
--- cmd-sort.c	8 Jun 2006 12:49:40 -0000	1.21
+++ cmd-sort.c	20 Dec 2006 19:23:46 -0000	1.22
@@ -91,9 +91,10 @@
 	pool_t pool;
 	const char *error, *charset;
 
-	args_count = imap_parser_read_args(client->parser, 0, 0, &args);
+	args_count = imap_parser_read_args(cmd->parser, 0, 0, &args);
 	if (args_count == -2)
 		return FALSE;
+	client->input_lock = NULL;
 
 	if (args_count < 3) {
 		client_send_command_error(cmd, args_count < 0 ? NULL :

Index: cmd-thread.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/imap/cmd-thread.c,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -d -r1.12 -r1.13
--- cmd-thread.c	20 Dec 2006 03:38:41 -0000	1.12
+++ cmd-thread.c	20 Dec 2006 19:23:46 -0000	1.13
@@ -16,9 +16,10 @@
 	pool_t pool;
 	const char *error, *charset, *str;
 
-	args_count = imap_parser_read_args(client->parser, 0, 0, &args);
+	args_count = imap_parser_read_args(cmd->parser, 0, 0, &args);
 	if (args_count == -2)
 		return FALSE;
+	client->input_lock = NULL;
 
 	if (args_count < 3) {
 		client_send_command_error(cmd, args_count < 0 ? NULL :

Index: cmd-uid.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/imap/cmd-uid.c,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -d -r1.7 -r1.8
--- cmd-uid.c	13 Jan 2006 20:25:59 -0000	1.7
+++ cmd-uid.c	20 Dec 2006 19:23:46 -0000	1.8
@@ -8,7 +8,7 @@
 	const char *cmd_name;
 
 	/* UID <command> <args> */
-	cmd_name = imap_parser_read_word(cmd->client->parser);
+	cmd_name = imap_parser_read_word(cmd->parser);
 	if (cmd_name == NULL)
 		return FALSE;
 

Index: commands.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/imap/commands.c,v
retrieving revision 1.18
retrieving revision 1.19
diff -u -d -r1.18 -r1.19
--- commands.c	23 Sep 2006 15:34:17 -0000	1.18
+++ commands.c	20 Dec 2006 19:23:46 -0000	1.19
@@ -48,7 +48,8 @@
 	{ "UID EXPUNGE",	cmd_uid_expunge },
 	{ "UID SORT",		cmd_sort },
 	{ "UID THREAD",		cmd_thread },
-	{ "UNSELECT",		cmd_unselect }
+	{ "UNSELECT",		cmd_unselect },
+	{ "X-CANCEL",		cmd_x_cancel }
 };
 #define IMAP_EXT_COMMANDS_COUNT \
 	(sizeof(imap_ext_commands) / sizeof(imap_ext_commands[0]))

Index: commands.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/imap/commands.h,v
retrieving revision 1.20
retrieving revision 1.21
diff -u -d -r1.20 -r1.21
--- commands.h	23 Sep 2006 15:34:17 -0000	1.20
+++ commands.h	20 Dec 2006 19:23:46 -0000	1.21
@@ -71,6 +71,7 @@
 bool cmd_thread(struct client_command_context *cmd);
 bool cmd_uid_expunge(struct client_command_context *cmd);
 bool cmd_unselect(struct client_command_context *cmd);
+bool cmd_x_cancel(struct client_command_context *cmd);
 
 /* private: */
 bool _cmd_list_full(struct client_command_context *cmd, bool lsub);

Index: imap-fetch.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/imap/imap-fetch.c,v
retrieving revision 1.50
retrieving revision 1.51
diff -u -d -r1.50 -r1.51
--- imap-fetch.c	15 Dec 2006 18:38:10 -0000	1.50
+++ imap-fetch.c	20 Dec 2006 19:23:46 -0000	1.51
@@ -205,6 +205,7 @@
 
 int imap_fetch(struct imap_fetch_context *ctx)
 {
+	struct client *client = ctx->client;
 	const struct imap_fetch_context_handler *handlers;
 	unsigned int size;
 	int ret;
@@ -229,16 +230,29 @@
                 ctx->cur_handler++;
 	}
 
+	/* assume initially that we're locking it */
+	i_assert(client->output_lock == NULL ||
+		 client->output_lock == ctx->cmd);
+	client->output_lock = ctx->cmd;
+
 	handlers = array_get(&ctx->handlers, &size);
 	for (;;) {
-		if (o_stream_get_buffer_used_size(ctx->client->output) >=
+		if (o_stream_get_buffer_used_size(client->output) >=
 		    CLIENT_OUTPUT_OPTIMAL_SIZE) {
-			ret = o_stream_flush(ctx->client->output);
-			if (ret <= 0)
+			ret = o_stream_flush(client->output);
+			if (ret <= 0) {
+				if (!ctx->line_partial) {
+					/* last line was fully sent */
+					client->output_lock = NULL;
+				}
 				return ret;
+			}
 		}
 
 		if (ctx->cur_mail == NULL) {
+			if (ctx->cmd->cancel)
+				return 1;
+
 			if (ctx->cur_input != NULL)
 				i_stream_unref(&ctx->cur_input);
 
@@ -258,6 +272,7 @@
 			    !handlers[ctx->cur_handler].buffered) {
 				/* first non-buffered handler.
 				   flush the buffer. */
+				ctx->line_partial = TRUE;
 				if (imap_fetch_flush_buffer(ctx) < 0)
 					return -1;
 			}
@@ -268,8 +283,13 @@
 					handlers[ctx->cur_handler].context);
 			t_pop();
 
-			if (ret == 0)
+			if (ret == 0) {
+				if (!ctx->line_partial) {
+					/* last line was fully sent */
+					client->output_lock = NULL;
+				}
 				return 0;
+			}
 
 			if (ret < 0) {
 				if (ctx->cur_mail->expunged) {
@@ -293,7 +313,8 @@
 		}
 
 		ctx->line_finished = TRUE;
-		if (o_stream_send(ctx->client->output, ")\r\n", 3) < 0)
+		ctx->line_partial = FALSE;
+		if (o_stream_send(client->output, ")\r\n", 3) < 0)
 			return -1;
 
 		ctx->cur_mail = NULL;

Index: imap-fetch.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/imap/imap-fetch.h,v
retrieving revision 1.22
retrieving revision 1.23
diff -u -d -r1.22 -r1.23
--- imap-fetch.h	15 Dec 2006 18:38:10 -0000	1.22
+++ imap-fetch.h	20 Dec 2006 19:23:46 -0000	1.23
@@ -57,6 +57,7 @@
 	unsigned int cur_have_eoh:1;
 	unsigned int cur_append_eoh:1;
 	unsigned int first:1;
+	unsigned int line_partial:1;
 	unsigned int line_finished:1;
 	unsigned int partial_fetch:1;
 	unsigned int failed:1;

Index: imap-sync.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/imap/imap-sync.c,v
retrieving revision 1.21
retrieving revision 1.22
diff -u -d -r1.21 -r1.22
--- imap-sync.c	25 Nov 2006 22:17:40 -0000	1.21
+++ imap-sync.c	20 Dec 2006 19:23:46 -0000	1.22
@@ -204,8 +204,12 @@
 	struct cmd_sync_context *ctx = cmd->context;
 	int ret;
 
-	if ((ret = imap_sync_more(ctx->sync_ctx)) == 0)
-		return FALSE;
+	if (cmd->cancel)
+		ret = 0;
+	else {
+		if ((ret = imap_sync_more(ctx->sync_ctx)) == 0)
+			return FALSE;
+	}
 
 	if (ret < 0)
 		ctx->sync_ctx->failed = TRUE;
@@ -215,16 +219,21 @@
 			mailbox_get_storage(cmd->client->mailbox));
 	}
 
-	client_send_tagline(cmd, ctx->tagline);
+	if (!cmd->cancel)
+		client_send_tagline(cmd, ctx->tagline);
 	return TRUE;
 }
 
 bool cmd_sync(struct client_command_context *cmd, enum mailbox_sync_flags flags,
 	      enum imap_sync_flags imap_flags, const char *tagline)
 {
-        struct cmd_sync_context *ctx;
+	struct client *client = cmd->client;
+	struct cmd_sync_context *ctx;
 
-	if (cmd->client->mailbox == NULL) {
+	i_assert(client->output_lock == cmd || client->output_lock == NULL);
+
+	if (client->mailbox == NULL ||
+	    mailbox_transaction_get_count(client->mailbox) > 0) {
 		client_send_tagline(cmd, tagline);
 		return TRUE;
 	}
@@ -241,11 +250,14 @@
 
 	ctx = p_new(cmd->pool, struct cmd_sync_context, 1);
 	ctx->tagline = p_strdup(cmd->pool, tagline);
-	ctx->sync_ctx = imap_sync_init(cmd->client, cmd->client->mailbox,
+	ctx->sync_ctx = imap_sync_init(client, client->mailbox,
 				       imap_flags, flags);
 
 	cmd->func = cmd_sync_continue;
 	cmd->context = ctx;
-	cmd->client->command_pending = TRUE;
+	cmd->output_pending = TRUE;
+	if (client->input_lock == cmd)
+		client->input_lock = NULL;
+	client->output_lock = NULL;
 	return cmd_sync_continue(cmd);
 }



More information about the dovecot-cvs mailing list