]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-imap-client: Avoid "Unknown tag" errors for aborted commands
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Wed, 17 Jan 2018 13:03:06 +0000 (15:03 +0200)
committerVille Savolainen <ville.savolainen@dovecot.fi>
Thu, 18 Jan 2018 10:07:02 +0000 (12:07 +0200)
If mailbox is closed before all command replies were received, the commands
were aborted but they'll still receive the replies from server. Remember
the aborted commands' tag numbers so they can be ignored.

src/lib-imap-client/imapc-connection.c

index 4dbb69637bdae19f6fb558147b58fc67d4083a86..8bd54197ed69e3b6a69a1bf578355dd30d1be7db 100644 (file)
@@ -64,6 +64,8 @@ struct imapc_command {
        unsigned int idle:1;
        /* Waiting for '+' literal reply before we can continue */
        unsigned int wait_for_literal:1;
+       /* Command is fully sent to server */
+       unsigned int sent:1;
 };
 ARRAY_DEFINE_TYPE(imapc_command, struct imapc_command *);
 
@@ -114,6 +116,9 @@ struct imapc_connection {
        ARRAY_TYPE(imapc_command) cmd_send_queue;
        /* commands that have been sent, waiting for their tagged reply */
        ARRAY_TYPE(imapc_command) cmd_wait_list;
+       /* commands that were already sent, but were aborted since (due to
+          unselecting mailbox). */
+       ARRAY_TYPE(seq_range) aborted_cmd_tags;
        unsigned int reconnect_command_count;
 
        unsigned int ips_count, prev_connect_idx;
@@ -202,6 +207,7 @@ imapc_connection_init(struct imapc_client *client,
        i_array_init(&conn->cmd_send_queue, 8);
        i_array_init(&conn->cmd_wait_list, 32);
        i_array_init(&conn->literal_files, 4);
+       i_array_init(&conn->aborted_cmd_tags, 8);
 
        if (client->set.debug)
                i_debug("imapc(%s): Created new connection", conn->name);
@@ -234,6 +240,7 @@ static void imapc_connection_unref(struct imapc_connection **_conn)
        array_free(&conn->cmd_send_queue);
        array_free(&conn->cmd_wait_list);
        array_free(&conn->literal_files);
+       array_free(&conn->aborted_cmd_tags);
        imapc_client_unref(&conn->client);
        i_free(conn->ips);
        i_free(conn->name);
@@ -348,6 +355,11 @@ void imapc_connection_abort_commands(struct imapc_connection *conn,
        array_foreach(&tmp_array, cmdp) {
                cmd = *cmdp;
 
+               if (cmd->sent && conn->state == IMAPC_CONNECTION_STATE_DONE) {
+                       /* We're not disconnected, so the reply will still
+                          come. Remember that it needs to be ignored. */
+                       seq_range_array_add(&conn->aborted_cmd_tags, cmd->tag);
+               }
                cmd->callback(&reply, cmd->context);
                imapc_command_free(cmd);
        }
@@ -386,6 +398,7 @@ static void imapc_connection_set_state(struct imapc_connection *conn,
                        i_free(conn->ips);
                        conn->ips_count = 0;
                }
+               array_clear(&conn->aborted_cmd_tags);
                conn->idling = FALSE;
                conn->idle_plus_waiting = FALSE;
                conn->idle_stopping = FALSE;
@@ -1435,6 +1448,13 @@ static int imapc_connection_input_tagged(struct imapc_connection *conn)
                timeout_remove(&conn->to);
 
        if (cmd == NULL) {
+               if (seq_range_exists(&conn->aborted_cmd_tags, conn->cur_tag)) {
+                       /* sent command was already aborted - ignore it */
+                       seq_range_array_remove(&conn->aborted_cmd_tags,
+                                              conn->cur_tag);
+                       imapc_connection_input_reset(conn);
+                       return 1;
+               }
                imapc_connection_input_error(conn,
                        "Unknown tag in a reply: %u %s %s",
                        conn->cur_tag, line, reply.text_full);
@@ -1988,6 +2008,7 @@ static void imapc_command_send_finished(struct imapc_connection *conn,
 
        if (cmd->idle)
                conn->idle_plus_waiting = TRUE;
+       cmd->sent = TRUE;
 
        /* everything sent. move command to wait list. */
        cmdp = array_idx(&conn->cmd_send_queue, 0);