Mercurial > dovecot > core-2.2
changeset 13621:8fdd22504aab
imapc: Initial support for automatically reconnecting to remote server.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 09 Oct 2011 16:41:17 +0300 |
parents | 7e0bdfa76f12 |
children | 305ca9c93dd7 |
files | src/lib-imap-client/imapc-client-private.h src/lib-imap-client/imapc-client.c src/lib-imap-client/imapc-client.h src/lib-imap-client/imapc-connection.c src/lib-imap-client/imapc-connection.h src/lib-imap-client/imapc-msgmap.c src/lib-imap-client/imapc-msgmap.h src/lib-storage/index/imapc/imapc-mail.c src/lib-storage/index/imapc/imapc-mailbox.c src/lib-storage/index/imapc/imapc-storage.c src/lib-storage/index/imapc/imapc-storage.h |
diffstat | 11 files changed, 200 insertions(+), 81 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lib-imap-client/imapc-client-private.h Sat Oct 08 20:31:22 2011 +0300 +++ b/src/lib-imap-client/imapc-client-private.h Sun Oct 09 16:41:17 2011 +0300 @@ -28,8 +28,13 @@ struct imapc_connection *conn; struct imapc_msgmap *msgmap; + void (*reopen_callback)(void *context); + void *reopen_context; + void *untagged_box_context; unsigned int pending_box_command_count; + + bool reconnect_ok; }; void imapc_client_ref(struct imapc_client *client);
--- a/src/lib-imap-client/imapc-client.c Sat Oct 08 20:31:22 2011 +0300 +++ b/src/lib-imap-client/imapc-client.c Sun Oct 09 16:41:17 2011 +0300 @@ -118,7 +118,7 @@ client->untagged_context = context; } -void imapc_client_run_pre(struct imapc_client *client) +static void imapc_client_run_pre(struct imapc_client *client) { struct imapc_client_connection *const *connp; struct ioloop *prev_ioloop = current_ioloop; @@ -138,7 +138,7 @@ current_ioloop = prev_ioloop; } -void imapc_client_run_post(struct imapc_client *client) +static void imapc_client_run_post(struct imapc_client *client) { struct imapc_client_connection *const *connp; struct ioloop *ioloop = client->ioloop; @@ -151,6 +151,12 @@ io_loop_destroy(&ioloop); } +void imapc_client_run(struct imapc_client *client) +{ + imapc_client_run_pre(client); + imapc_client_run_post(client); +} + void imapc_client_stop(struct imapc_client *client) { if (client->ioloop != NULL) @@ -238,10 +244,34 @@ return box; } -void imapc_client_mailbox_disconnect(struct imapc_client_mailbox *box) +void imapc_client_mailbox_set_reopen_cb(struct imapc_client_mailbox *box, + void (*callback)(void *context), + void *context) +{ + box->reopen_callback = callback; + box->reopen_context = context; +} + +static void +imapc_client_reconnect_cb(const struct imapc_command_reply *reply, + void *context) { - if (box->conn != NULL) - imapc_connection_disconnect(box->conn); + struct imapc_client_mailbox *box = context; + + if (reply->state == IMAPC_COMMAND_STATE_OK) { + /* reopen the mailbox */ + box->reopen_callback(box->reopen_context); + } +} + +void imapc_client_mailbox_reconnect(struct imapc_client_mailbox *box) +{ + imapc_connection_disconnect(box->conn); + if (box->reopen_callback != NULL && box->reconnect_ok) { + imapc_connection_connect(box->conn, + imapc_client_reconnect_cb, box); + } + box->reconnect_ok = FALSE; } void imapc_client_mailbox_close(struct imapc_client_mailbox **_box) @@ -249,6 +279,13 @@ struct imapc_client_mailbox *box = *_box; struct imapc_client_connection *const *connp; + /* cancel any pending commands */ + imapc_connection_unselect(box); + + /* set this only after unselect, which may cancel some commands that + reference this box */ + *_box = NULL; + array_foreach(&box->client->conns, connp) { if ((*connp)->box == box) { (*connp)->box = NULL; @@ -256,14 +293,8 @@ } } - if (box->conn != NULL) - imapc_connection_unselect(box); imapc_msgmap_deinit(&box->msgmap); i_free(box); - - /* set this only after unselect, which may cancel some commands that - reference this box */ - *_box = NULL; } struct imapc_command * @@ -285,24 +316,25 @@ void imapc_client_mailbox_idle(struct imapc_client_mailbox *box) { - if (imapc_client_mailbox_is_connected(box)) + if (imapc_client_mailbox_is_opened(box)) imapc_connection_idle(box->conn); + box->reconnect_ok = TRUE; } -bool imapc_client_mailbox_is_connected(struct imapc_client_mailbox *box) +bool imapc_client_mailbox_is_opened(struct imapc_client_mailbox *box) { struct imapc_client_mailbox *selected_box; - selected_box = box->conn == NULL ? NULL : - imapc_connection_get_mailbox(box->conn); - if (selected_box == box) - return TRUE; + if (imapc_connection_get_state(box->conn) != IMAPC_CONNECTION_STATE_DONE) + return FALSE; - if (selected_box != NULL) - i_error("imapc: Selected mailbox changed unexpectedly"); - - box->conn = NULL; - return FALSE; + selected_box = imapc_connection_get_mailbox(box->conn); + if (selected_box != box) { + if (selected_box != NULL) + i_error("imapc: Selected mailbox changed unexpectedly"); + return FALSE; + } + return TRUE; } enum imapc_capability
--- a/src/lib-imap-client/imapc-client.h Sat Oct 08 20:31:22 2011 +0300 +++ b/src/lib-imap-client/imapc-client.h Sun Oct 09 16:41:17 2011 +0300 @@ -27,7 +27,14 @@ enum imapc_command_flags { /* The command changes the selected mailbox (SELECT, EXAMINE) */ - IMAPC_COMMAND_FLAG_SELECT = 0x01 + IMAPC_COMMAND_FLAG_SELECT = 0x01, + /* The command is sent to server before login (or is the login + command itself). Non-prelogin commands will be queued until login + is successful. */ + IMAPC_COMMAND_FLAG_PRELOGIN = 0x02, + /* Allow command to be automatically retried if disconnected before it + finishes. */ + IMAPC_COMMAND_FLAG_RETRIABLE = 0x04 }; enum imapc_client_ssl_mode { @@ -129,16 +136,18 @@ imapc_untagged_callback_t *callback, void *context); -void imapc_client_run_pre(struct imapc_client *client); -void imapc_client_run_post(struct imapc_client *client); +void imapc_client_run(struct imapc_client *client); void imapc_client_stop(struct imapc_client *client); bool imapc_client_is_running(struct imapc_client *client); struct imapc_client_mailbox * imapc_client_mailbox_open(struct imapc_client *client, void *untagged_box_context); +void imapc_client_mailbox_set_reopen_cb(struct imapc_client_mailbox *box, + void (*callback)(void *context), + void *context); void imapc_client_mailbox_close(struct imapc_client_mailbox **box); -void imapc_client_mailbox_disconnect(struct imapc_client_mailbox *box); +void imapc_client_mailbox_reconnect(struct imapc_client_mailbox *box); struct imapc_command * imapc_client_mailbox_cmd(struct imapc_client_mailbox *box, imapc_command_callback_t *callback, void *context); @@ -146,7 +155,7 @@ imapc_client_mailbox_get_msgmap(struct imapc_client_mailbox *box); void imapc_client_mailbox_idle(struct imapc_client_mailbox *box); -bool imapc_client_mailbox_is_connected(struct imapc_client_mailbox *box); +bool imapc_client_mailbox_is_opened(struct imapc_client_mailbox *box); enum imapc_capability imapc_client_get_capabilities(struct imapc_client *client);
--- a/src/lib-imap-client/imapc-connection.c Sat Oct 08 20:31:22 2011 +0300 +++ b/src/lib-imap-client/imapc-connection.c Sun Oct 09 16:41:17 2011 +0300 @@ -122,8 +122,7 @@ static int imapc_connection_output(struct imapc_connection *conn); static int imapc_connection_ssl_init(struct imapc_connection *conn); static void imapc_command_free(struct imapc_command *cmd); -static void imapc_command_send_more(struct imapc_connection *conn, - struct imapc_command *cmd); +static void imapc_command_send_more(struct imapc_connection *conn); struct imapc_connection * imapc_connection_init(struct imapc_client *client) @@ -253,9 +252,12 @@ static void imapc_connection_set_state(struct imapc_connection *conn, enum imapc_connection_state state) { - if (state == IMAPC_CONNECTION_STATE_DISCONNECTED) { - struct imapc_command_reply reply; + struct imapc_command_reply reply; + conn->state = state; + + switch (state) { + case IMAPC_CONNECTION_STATE_DISCONNECTED: memset(&reply, 0, sizeof(reply)); reply.state = IMAPC_COMMAND_STATE_DISCONNECTED; reply.text_without_resp = reply.text_full = @@ -269,15 +271,13 @@ conn->selecting_box = NULL; conn->selected_box = NULL; + break; + case IMAPC_CONNECTION_STATE_DONE: + imapc_command_send_more(conn); + break; + default: + break; } - if (state == IMAPC_CONNECTION_STATE_DONE) { - if (array_count(&conn->cmd_send_queue) > 0) { - struct imapc_command *const *cmd_p = - array_idx(&conn->cmd_send_queue, 0); - imapc_command_send_more(conn, *cmd_p); - } - } - conn->state = state; } static void imapc_connection_lfiles_free(struct imapc_connection *conn) @@ -330,6 +330,14 @@ imapc_connection_set_state(conn, IMAPC_CONNECTION_STATE_DISCONNECTED); } +static void imapc_connection_reconnect(struct imapc_connection *conn) +{ + if (conn->selected_box != NULL) + imapc_client_mailbox_reconnect(conn->selected_box); + else + imapc_connection_disconnect(conn); +} + static void ATTR_FORMAT(2, 3) imapc_connection_input_error(struct imapc_connection *conn, const char *fmt, ...) @@ -339,7 +347,6 @@ va_start(va, fmt); i_error("imapc(%s): Server sent invalid input: %s", conn->name, t_strdup_vprintf(fmt, va)); - sleep(3600); imapc_connection_disconnect(conn); va_end(va); } @@ -636,6 +643,8 @@ timeout_remove(&conn->to); imapc_connection_set_state(conn, IMAPC_CONNECTION_STATE_DONE); imapc_login_callback(conn, reply); + + imapc_command_send_more(conn); } static const char * @@ -678,6 +687,7 @@ cmd = imapc_connection_cmd(conn, imapc_connection_login_cb, conn); + imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_PRELOGIN); if ((set->master_user == NULL && need_literal(set->username) && need_literal(set->password)) || @@ -727,6 +737,7 @@ } cmd = imapc_connection_cmd(conn, imapc_connection_starttls_cb, conn); + imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_PRELOGIN); imapc_command_send(cmd, "STARTTLS"); return; } @@ -769,6 +780,7 @@ /* capabilities weren't sent in the banner. ask for them. */ cmd = imapc_connection_cmd(conn, imapc_connection_capability_cb, conn); + imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_PRELOGIN); imapc_command_send(cmd, "CAPABILITY"); } else { imapc_connection_starttls(conn); @@ -864,7 +876,7 @@ } else if (cmds_count > 0 && cmds[0]->wait_for_literal) { /* reply for literal */ cmds[0]->wait_for_literal = FALSE; - imapc_command_send_more(conn, cmds[0]); + imapc_command_send_more(conn); } else { imapc_connection_input_error(conn, "Unexpected '+': %s", line); return -1; @@ -1042,7 +1054,7 @@ i_error("imapc(%s): Server disconnected: %s", conn->name, errstr != NULL ? errstr : ""); } - imapc_connection_disconnect(conn); + imapc_connection_reconnect(conn); } imapc_connection_unref(&conn); } @@ -1252,17 +1264,18 @@ } void imapc_connection_connect(struct imapc_connection *conn, - imapc_command_callback_t *callback, void *context) + imapc_command_callback_t *login_callback, + void *login_context) { struct dns_lookup_settings dns_set; - i_assert(conn->login_callback == NULL); if (conn->fd != -1) { - i_assert(callback == NULL); + i_assert(login_callback == NULL); return; } - conn->login_callback = callback; - conn->login_context = context; + i_assert(conn->login_callback == NULL); + conn->login_callback = login_callback; + conn->login_context = login_context; imapc_connection_input_reset(conn); @@ -1370,13 +1383,8 @@ array_delete(&conn->cmd_send_queue, 0, 1); array_append(&conn->cmd_wait_list, &cmd, 1); - if (array_count(&conn->cmd_send_queue) > 0 && - conn->state == IMAPC_CONNECTION_STATE_DONE) { - /* send the next command in queue */ - struct imapc_command *const *cmd2_p = - array_idx(&conn->cmd_send_queue, 0); - imapc_command_send_more(conn, *cmd2_p); - } + /* send the next command in queue */ + imapc_command_send_more(conn); } static struct imapc_command_stream * @@ -1440,15 +1448,29 @@ } } -static void imapc_command_send_more(struct imapc_connection *conn, - struct imapc_command *cmd) +static void imapc_command_send_more(struct imapc_connection *conn) { + struct imapc_command *const *cmds, *cmd; struct imapc_command_reply reply; const unsigned char *p, *data; - unsigned int seek_pos, start_pos, end_pos, size; + unsigned int count, seek_pos, start_pos, end_pos, size; int ret; - i_assert(!cmd->wait_for_literal); + cmds = array_get(&conn->cmd_send_queue, &count); + if (count == 0) + return; + cmd = cmds[0]; + + if ((cmd->flags & IMAPC_COMMAND_FLAG_PRELOGIN) == 0 && + conn->state != IMAPC_CONNECTION_STATE_DONE) { + /* wait until we're fully connected */ + return; + } + if (cmd->wait_for_literal) { + /* wait until we received '+' */ + return; + } + i_assert(cmd->send_pos < cmd->data->used); if (cmd->box == NULL) { @@ -1457,11 +1479,13 @@ (cmd->flags & IMAPC_COMMAND_FLAG_SELECT) != 0) { /* SELECT/EXAMINE command */ imapc_connection_set_selecting(cmd->box); - } else if (!imapc_client_mailbox_is_connected(cmd->box)) { + } else if (!imapc_client_mailbox_is_opened(cmd->box)) { /* shouldn't normally happen */ memset(&reply, 0, sizeof(reply)); reply.text_without_resp = reply.text_full = "Mailbox not open"; reply.state = IMAPC_COMMAND_STATE_BAD; + + array_delete(&conn->cmd_send_queue, 0, 1); imapc_command_reply_free(cmd, &reply); return; } @@ -1535,7 +1559,7 @@ switch (conn->state) { case IMAPC_CONNECTION_STATE_AUTHENTICATING: array_insert(&conn->cmd_send_queue, 0, &cmd, 1); - imapc_command_send_more(conn, cmd); + imapc_command_send_more(conn); break; case IMAPC_CONNECTION_STATE_DONE: if (cmd->idle) { @@ -1547,8 +1571,7 @@ } array_append(&conn->cmd_send_queue, &cmd, 1); - if (array_count(&conn->cmd_send_queue) == 1) - imapc_command_send_more(conn, cmd); + imapc_command_send_more(conn); break; default: array_append(&conn->cmd_send_queue, &cmd, 1); @@ -1575,7 +1598,7 @@ if (imapc_command_get_sending_stream(cmds[0]) != NULL && !cmds[0]->wait_for_literal) { /* we're sending a stream. send more. */ - imapc_command_send_more(conn, cmds[0]); + imapc_command_send_more(conn); } } o_stream_uncork(conn->output); @@ -1742,9 +1765,7 @@ } } - if (conn->selected_box == NULL && conn->selecting_box == NULL) { - i_assert(conn->state == IMAPC_CONNECTION_STATE_DISCONNECTED); - } else { + if (conn->selected_box != NULL || conn->selecting_box != NULL) { i_assert(conn->selected_box == box || conn->selecting_box == box);
--- a/src/lib-imap-client/imapc-connection.h Sat Oct 08 20:31:22 2011 +0300 +++ b/src/lib-imap-client/imapc-connection.h Sun Oct 09 16:41:17 2011 +0300 @@ -22,8 +22,8 @@ void imapc_connection_deinit(struct imapc_connection **conn); void imapc_connection_connect(struct imapc_connection *conn, - imapc_command_callback_t *callback, - void *context); + imapc_command_callback_t *login_callback, + void *login_context); void imapc_connection_disconnect(struct imapc_connection *conn); void imapc_connection_ioloop_changed(struct imapc_connection *conn); void imapc_connection_input_pending(struct imapc_connection *conn);
--- a/src/lib-imap-client/imapc-msgmap.c Sat Oct 08 20:31:22 2011 +0300 +++ b/src/lib-imap-client/imapc-msgmap.c Sun Oct 09 16:41:17 2011 +0300 @@ -86,3 +86,9 @@ array_delete(&msgmap->uids, rseq-1, 1); } + +void imapc_msgmap_reset(struct imapc_msgmap *msgmap) +{ + array_clear(&msgmap->uids); + msgmap->uid_next = 1; +}
--- a/src/lib-imap-client/imapc-msgmap.h Sat Oct 08 20:31:22 2011 +0300 +++ b/src/lib-imap-client/imapc-msgmap.h Sun Oct 09 16:41:17 2011 +0300 @@ -13,5 +13,6 @@ void imapc_msgmap_append(struct imapc_msgmap *msgmap, uint32_t rseq, uint32_t uid); void imapc_msgmap_expunge(struct imapc_msgmap *msgmap, uint32_t rseq); +void imapc_msgmap_reset(struct imapc_msgmap *msgmap); #endif
--- a/src/lib-storage/index/imapc/imapc-mail.c Sat Oct 08 20:31:22 2011 +0300 +++ b/src/lib-storage/index/imapc/imapc-mail.c Sun Oct 09 16:41:17 2011 +0300 @@ -62,7 +62,7 @@ if (mail->expunged || imapc_mail_is_expunged(mail)) mail_set_expunged(mail); - else if (!imapc_client_mailbox_is_connected(mbox->client_box)) { + else if (!imapc_client_mailbox_is_opened(mbox->client_box)) { /* we've already logged a disconnection error */ mail_storage_set_internal_error(mail->box->storage); } else {
--- a/src/lib-storage/index/imapc/imapc-mailbox.c Sat Oct 08 20:31:22 2011 +0300 +++ b/src/lib-storage/index/imapc/imapc-mailbox.c Sun Oct 09 16:41:17 2011 +0300 @@ -30,7 +30,7 @@ /* maybe the remote server is buggy and has become confused. try reconnecting. */ } - imapc_client_mailbox_disconnect(mbox->client_box); + imapc_client_mailbox_reconnect(mbox->client_box); } static struct mail_index_view *
--- a/src/lib-storage/index/imapc/imapc-storage.c Sat Oct 08 20:31:22 2011 +0300 +++ b/src/lib-storage/index/imapc/imapc-storage.c Sun Oct 09 16:41:17 2011 +0300 @@ -6,9 +6,10 @@ #include "imap-arg.h" #include "imap-resp-code.h" #include "mailbox-tree.h" -#include "imapc-mail.h" #include "imapc-client.h" #include "imapc-connection.h" +#include "imapc-msgmap.h" +#include "imapc-mail.h" #include "imapc-list.h" #include "imapc-sync.h" #include "imapc-settings.h" @@ -113,8 +114,9 @@ void imapc_storage_run(struct imapc_storage *storage) { - imapc_client_run_pre(storage->client); - imapc_client_run_post(storage->client); + do { + imapc_client_run(storage->client); + } while (storage->reopen_count > 0); } void imapc_simple_callback(const struct imapc_command_reply *reply, @@ -348,12 +350,58 @@ return 0; } +static bool imapc_mailbox_want_examine(struct imapc_mailbox *mbox) +{ + return (mbox->box.flags & MAILBOX_FLAG_DROP_RECENT) == 0 && + ((mbox->box.flags & MAILBOX_FLAG_READONLY) != 0 || + (mbox->box.flags & MAILBOX_FLAG_SAVEONLY) != 0); +} + +static void +imapc_mailbox_reopen_callback(const struct imapc_command_reply *reply, + void *context) +{ + struct imapc_mailbox *mbox = context; + + i_assert(mbox->storage->reopen_count > 0); + mbox->storage->reopen_count--; + mbox->selecting = FALSE; + if (reply->state != IMAPC_COMMAND_STATE_OK) { + mail_storage_set_critical(mbox->box.storage, + "imapc: Reopening mailbox '%s' failed: %s", + mbox->box.name, reply->text_full); + imapc_client_mailbox_reconnect(mbox->client_box); + } + imapc_client_stop(mbox->storage->client); +} + +static void imapc_mailbox_reopen(void *context) +{ + struct imapc_mailbox *mbox = context; + struct imapc_command *cmd; + + /* we're reconnecting and need to reopen the mailbox */ + mbox->initial_sync_done = FALSE; + mbox->selecting = TRUE; + imapc_msgmap_reset(imapc_client_mailbox_get_msgmap(mbox->client_box)); + + cmd = imapc_client_mailbox_cmd(mbox->client_box, + imapc_mailbox_reopen_callback, mbox); + imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_SELECT); + if (imapc_mailbox_want_examine(mbox)) + imapc_command_sendf(cmd, "EXAMINE %s", mbox->box.name); + else + imapc_command_sendf(cmd, "SELECT %s", mbox->box.name); + mbox->storage->reopen_count++; +} + static void imapc_mailbox_open_callback(const struct imapc_command_reply *reply, void *context) { struct imapc_open_context *ctx = context; + ctx->mbox->selecting = FALSE; if (reply->state == IMAPC_COMMAND_STATE_OK) ctx->ret = 0; else if (reply->state == IMAPC_COMMAND_STATE_NO) { @@ -373,16 +421,13 @@ { struct imapc_command *cmd; struct imapc_open_context ctx; - bool examine = TRUE; i_assert(mbox->client_box == NULL); - examine = (mbox->box.flags & MAILBOX_FLAG_DROP_RECENT) == 0 && - ((mbox->box.flags & MAILBOX_FLAG_READONLY) != 0 || - (mbox->box.flags & MAILBOX_FLAG_SAVEONLY) != 0); - mbox->client_box = imapc_client_mailbox_open(mbox->storage->client, mbox); + imapc_client_mailbox_set_reopen_cb(mbox->client_box, + imapc_mailbox_reopen, mbox); mbox->selecting = TRUE; ctx.mbox = mbox; @@ -390,14 +435,13 @@ cmd = imapc_client_mailbox_cmd(mbox->client_box, imapc_mailbox_open_callback, &ctx); imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_SELECT); - if (examine) + if (imapc_mailbox_want_examine(mbox)) imapc_command_sendf(cmd, "EXAMINE %s", mbox->box.name); else imapc_command_sendf(cmd, "SELECT %s", mbox->box.name); while (ctx.ret == -2) imapc_storage_run(mbox->storage); - mbox->selecting = FALSE; return ctx.ret; } @@ -670,7 +714,7 @@ if (mail_index_view_is_inconsistent(box->view)) return TRUE; - return !imapc_client_mailbox_is_connected(mbox->client_box); + return !imapc_client_mailbox_is_opened(mbox->client_box); } struct mail_storage imapc_storage = {
--- a/src/lib-storage/index/imapc/imapc-storage.h Sat Oct 08 20:31:22 2011 +0300 +++ b/src/lib-storage/index/imapc/imapc-storage.h Sun Oct 09 16:41:17 2011 +0300 @@ -37,6 +37,7 @@ struct imapc_mailbox *cur_status_box; struct mailbox_status *cur_status; + unsigned int reopen_count; ARRAY_DEFINE(untagged_callbacks, struct imapc_storage_event_callback); };