]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
imapc: Use LOGOUT to cleanly disconnect from server.
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Sat, 28 Jan 2017 23:03:00 +0000 (01:03 +0200)
committerTimo Sirainen <timo.sirainen@dovecot.fi>
Tue, 21 Mar 2017 09:51:18 +0000 (11:51 +0200)
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.

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

index 3d8f347e4e7185500b33369a3e3425660dac4851..89b183f3c044f76060844b61529c5dd4828cb05a 100644 (file)
@@ -24,6 +24,7 @@ struct imapc_client {
        void *state_change_context;
 
        ARRAY(struct imapc_client_connection *) conns;
+       bool logging_out;
 
        struct ioloop *ioloop;
 };
index b4d7de73eab8383ae3ab676630bc4c0451657783..d5ded341d30e1b4f9584c43290df38d79daab646 100644 (file)
@@ -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)
index 04f49b258f1dfe3380205c7ea24625fe2b438f08..8bdbbb2b5a018fc48c1ca3515851307e01065bb5 100644 (file)
@@ -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,
index 81970def6979141e8808906ca13089ab32ebffac..ead05307c8b91b97ed37469fbdd829360c955693 100644 (file)
@@ -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);
        }
index 21ffd0af3d7117d5d32d9a7275ba9bdfb48debee..611d0d5ce74245d650adc661ee5e1d585fb45396 100644 (file)
@@ -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);
index aaa583ffdd2acacc9eba728d9f1167468980dff3..82094504fc1f74be700d4814df6bd16442a945e4 100644 (file)
@@ -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)
index b2df83daf8504fa876427026313344a78a600519..58d3531681c52ffc33a869ed86963f0e93467abe 100644 (file)
@@ -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);