From: Timo Sirainen Date: Sat, 28 Jan 2017 23:03:00 +0000 (+0200) Subject: imapc: Use LOGOUT to cleanly disconnect from server. X-Git-Tag: 2.2.29.rc1~144 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9fe890fdbf8e64b0fac30787ca1f370f8913e6ba;p=thirdparty%2Fdovecot%2Fcore.git 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. --- diff --git a/src/lib-imap-client/imapc-client-private.h b/src/lib-imap-client/imapc-client-private.h index 3d8f347e4e..89b183f3c0 100644 --- a/src/lib-imap-client/imapc-client-private.h +++ b/src/lib-imap-client/imapc-client-private.h @@ -24,6 +24,7 @@ struct imapc_client { void *state_change_context; ARRAY(struct imapc_client_connection *) conns; + bool logging_out; struct ioloop *ioloop; }; diff --git a/src/lib-imap-client/imapc-client.c b/src/lib-imap-client/imapc-client.c index b4d7de73ea..d5ded341d3 100644 --- a/src/lib-imap-client/imapc-client.c +++ b/src/lib-imap-client/imapc-client.c @@ -265,6 +265,51 @@ void imapc_client_login(struct imapc_client *client, 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) diff --git a/src/lib-imap-client/imapc-client.h b/src/lib-imap-client/imapc-client.h index 04f49b258f..8bdbbb2b5a 100644 --- a/src/lib-imap-client/imapc-client.h +++ b/src/lib-imap-client/imapc-client.h @@ -45,7 +45,9 @@ enum imapc_command_flags { 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 @@ void imapc_client_deinit(struct imapc_client **client); /* 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, diff --git a/src/lib-imap-client/imapc-connection.c b/src/lib-imap-client/imapc-connection.c index 81970def69..ead05307c8 100644 --- a/src/lib-imap-client/imapc-connection.c +++ b/src/lib-imap-client/imapc-connection.c @@ -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 @@ void imapc_connection_disconnect_full(struct imapc_connection *conn, 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 void imapc_connection_set_disconnected(struct imapc_connection *conn) 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 @@ static void imapc_connection_input(struct imapc_connection *conn) 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 @@ static void imapc_command_send_more(struct imapc_connection *conn) /* 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 @@ static void imapc_command_send_more(struct imapc_connection *conn) /* 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); } diff --git a/src/lib-imap-client/imapc-connection.h b/src/lib-imap-client/imapc-connection.h index 21ffd0af3d..611d0d5ce7 100644 --- a/src/lib-imap-client/imapc-connection.h +++ b/src/lib-imap-client/imapc-connection.h @@ -32,6 +32,7 @@ void imapc_connection_deinit(struct imapc_connection **conn); 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); diff --git a/src/lib-storage/index/imapc/imapc-list.c b/src/lib-storage/index/imapc/imapc-list.c index aaa583ffdd..82094504fc 100644 --- a/src/lib-storage/index/imapc/imapc-list.c +++ b/src/lib-storage/index/imapc/imapc-list.c @@ -91,7 +91,7 @@ static void imapc_list_deinit(struct mailbox_list *_list) 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) diff --git a/src/lib-storage/index/imapc/imapc-storage.c b/src/lib-storage/index/imapc/imapc-storage.c index b2df83daf8..58d3531681 100644 --- a/src/lib-storage/index/imapc/imapc-storage.c +++ b/src/lib-storage/index/imapc/imapc-storage.c @@ -124,7 +124,7 @@ void imapc_simple_context_init(struct imapc_simple_context *sctx, 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 @@ static void imapc_storage_destroy(struct mail_storage *_storage) /* 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);