Mercurial > dovecot > core-2.2
changeset 21764:04edf83cff79
imapc: Use LOGOUT to cleanly disconnect from server.
This makes it clearer in the remote server's logs whether the disconnection
was intentional or not.
Use a hardcoded 5 second timeout for LOGOUT. It should be enough time for
the server to finish sending the tagged reply.
author | Timo Sirainen <timo.sirainen@dovecot.fi> |
---|---|
date | Sun, 29 Jan 2017 01:03:00 +0200 |
parents | 5a5e046c42ec |
children | 91b94ecaa39d |
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-storage/index/imapc/imapc-list.c src/lib-storage/index/imapc/imapc-storage.c |
diffstat | 7 files changed, 80 insertions(+), 6 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lib-imap-client/imapc-client-private.h Thu Mar 16 18:46:28 2017 +0200 +++ b/src/lib-imap-client/imapc-client-private.h Sun Jan 29 01:03:00 2017 +0200 @@ -24,6 +24,7 @@ void *state_change_context; ARRAY(struct imapc_client_connection *) conns; + bool logging_out; struct ioloop *ioloop; };
--- a/src/lib-imap-client/imapc-client.c Thu Mar 16 18:46:28 2017 +0200 +++ b/src/lib-imap-client/imapc-client.c Sun Jan 29 01:03:00 2017 +0200 @@ -265,6 +265,51 @@ imapc_connection_connect(conn->conn, callback, context); } +struct imapc_logout_ctx { + struct imapc_client *client; + unsigned int logout_count; +}; + +static void +imapc_client_logout_callback(const struct imapc_command_reply *reply ATTR_UNUSED, + void *context) +{ + struct imapc_logout_ctx *ctx = context; + + i_assert(ctx->logout_count > 0); + + if (--ctx->logout_count == 0) + imapc_client_stop(ctx->client); +} + +void imapc_client_logout(struct imapc_client *client) +{ + struct imapc_logout_ctx ctx = { .client = client }; + struct imapc_client_connection *const *connp; + struct imapc_command *cmd; + + client->logging_out = TRUE; + + /* send LOGOUT to all connections */ + array_foreach(&client->conns, connp) { + imapc_connection_set_no_reconnect((*connp)->conn); + ctx.logout_count++; + cmd = imapc_connection_cmd((*connp)->conn, + imapc_client_logout_callback, &ctx); + imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_PRELOGIN | + IMAPC_COMMAND_FLAG_LOGOUT); + imapc_command_send(cmd, "LOGOUT"); + } + + /* wait for LOGOUT to finish */ + while (ctx.logout_count > 0) + imapc_client_run(client); + + /* we should have disconnected all clients already, but if there were + any timeouts there may be some clients left. */ + imapc_client_disconnect(client); +} + struct imapc_client_mailbox * imapc_client_mailbox_open(struct imapc_client *client, void *untagged_box_context)
--- a/src/lib-imap-client/imapc-client.h Thu Mar 16 18:46:28 2017 +0200 +++ b/src/lib-imap-client/imapc-client.h Sun Jan 29 01:03:00 2017 +0200 @@ -45,7 +45,9 @@ IMAPC_COMMAND_FLAG_PRELOGIN = 0x02, /* Allow command to be automatically retried if disconnected before it finishes. */ - IMAPC_COMMAND_FLAG_RETRIABLE = 0x04 + IMAPC_COMMAND_FLAG_RETRIABLE = 0x04, + /* This is the LOGOUT command. Use a small timeout for it. */ + IMAPC_COMMAND_FLAG_LOGOUT = 0x08 }; enum imapc_client_ssl_mode { @@ -169,6 +171,8 @@ /* Explicitly login to server (also done automatically). */ void imapc_client_login(struct imapc_client *client, imapc_command_callback_t *callback, void *context); +/* Send a LOGOUT and wait for disconnection. */ +void imapc_client_logout(struct imapc_client *client); struct imapc_command * imapc_client_cmd(struct imapc_client *client,
--- a/src/lib-imap-client/imapc-connection.c Thu Mar 16 18:46:28 2017 +0200 +++ b/src/lib-imap-client/imapc-connection.c Sun Jan 29 01:03:00 2017 +0200 @@ -25,6 +25,8 @@ #define IMAPC_COMMAND_STATE_AUTHENTICATE_CONTINUE 10000 #define IMAPC_MAX_INLINE_LITERAL_SIZE (1024*32) #define IMAPC_RECONNECT_MIN_RETRY_SECS 10 +/* If LOGOUT reply takes longer than this, disconnect. */ +#define IMAPC_LOGOUT_TIMEOUT_MSECS 5000 enum imapc_input_state { IMAPC_INPUT_STATE_NONE = 0, @@ -453,6 +455,11 @@ imapc_connection_abort_commands(conn, NULL, reconnecting); } +void imapc_connection_set_no_reconnect(struct imapc_connection *conn) +{ + conn->reconnect_ok = FALSE; +} + void imapc_connection_disconnect(struct imapc_connection *conn) { imapc_connection_disconnect_full(conn, FALSE); @@ -466,6 +473,8 @@ static bool imapc_connection_can_reconnect(struct imapc_connection *conn) { + if (conn->client->logging_out) + return FALSE; if (conn->selected_box != NULL) return imapc_client_mailbox_can_reconnect(conn->selected_box); else { @@ -1473,7 +1482,10 @@ while (conn->input != NULL && (ret = i_stream_read(conn->input)) > 0) imapc_connection_input_pending(conn); - if (ret < 0) { + if (ret < 0 && conn->client->logging_out && + conn->disconnect_reason != NULL) { + /* expected disconnection */ + } else if (ret < 0) { /* disconnected or buffer full */ str = t_str_new(128); if (conn->disconnect_reason != NULL) { @@ -2033,6 +2045,11 @@ /* wait until we're fully connected */ return; } + if ((cmd->flags & IMAPC_COMMAND_FLAG_LOGOUT) != 0 && + array_count(&conn->cmd_wait_list) > 0) { + /* wait until existing commands have finished */ + return; + } if (cmd->wait_for_literal) { /* wait until we received '+' */ return; @@ -2064,7 +2081,13 @@ /* add timeout for commands if there's not one yet (pre-login has its own timeout) */ - if (conn->to == NULL) { + if ((cmd->flags & IMAPC_COMMAND_FLAG_LOGOUT) != 0) { + /* LOGOUT has a shorter timeout */ + if (conn->to != NULL) + timeout_remove(&conn->to); + conn->to = timeout_add(IMAPC_LOGOUT_TIMEOUT_MSECS, + imapc_command_timeout, conn); + } else if (conn->to == NULL) { conn->to = timeout_add(conn->client->set.cmd_timeout_msecs, imapc_command_timeout, conn); }
--- a/src/lib-imap-client/imapc-connection.h Thu Mar 16 18:46:28 2017 +0200 +++ b/src/lib-imap-client/imapc-connection.h Sun Jan 29 01:03:00 2017 +0200 @@ -32,6 +32,7 @@ void imapc_connection_connect(struct imapc_connection *conn, imapc_command_callback_t *login_callback, void *login_context) ATTR_NULL(2, 3); +void imapc_connection_set_no_reconnect(struct imapc_connection *conn); void imapc_connection_disconnect(struct imapc_connection *conn); void imapc_connection_disconnect_full(struct imapc_connection *conn, bool reconnecting);
--- a/src/lib-storage/index/imapc/imapc-list.c Thu Mar 16 18:46:28 2017 +0200 +++ b/src/lib-storage/index/imapc/imapc-list.c Sun Jan 29 01:03:00 2017 +0200 @@ -91,7 +91,7 @@ deinitialized */ if (list->client != NULL) { list->client->destroying = TRUE; - imapc_client_disconnect(list->client->client); + imapc_client_logout(list->client->client); imapc_storage_client_unref(&list->client); } if (list->index_list != NULL)
--- a/src/lib-storage/index/imapc/imapc-storage.c Thu Mar 16 18:46:28 2017 +0200 +++ b/src/lib-storage/index/imapc/imapc-storage.c Sun Jan 29 01:03:00 2017 +0200 @@ -124,7 +124,7 @@ void imapc_simple_run(struct imapc_simple_context *sctx) { if (sctx->client->auth_failed) { - imapc_client_disconnect(sctx->client->client); + imapc_client_logout(sctx->client->client); sctx->ret = -1; } while (sctx->ret == -2) @@ -402,7 +402,7 @@ /* make sure all pending commands are aborted before anything is deinitialized */ - imapc_client_disconnect(storage->client->client); + imapc_client_logout(storage->client->client); imapc_storage_client_unref(&storage->client); index_storage_destroy(_storage);