[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