# HG changeset patch # User Timo Sirainen # Date 1496943587 -10800 # Node ID 3001873cd962f87e36e5ed3917c0bfb70d7fcfda # Parent fc301d74f8519469c5178522b45791b8b0d60dc8 lib-imap-client: Fix hang when imapc_client_get_capabilities() is called without connection diff -r fc301d74f851 -r 3001873cd962 src/lib-imap-client/imapc-client-private.h --- a/src/lib-imap-client/imapc-client-private.h Wed Jun 07 18:10:10 2017 +0300 +++ b/src/lib-imap-client/imapc-client-private.h Thu Jun 08 20:39:47 2017 +0300 @@ -31,6 +31,7 @@ bool logging_out; struct ioloop *ioloop; + bool stop_on_state_finish; }; struct imapc_client_mailbox { diff -r fc301d74f851 -r 3001873cd962 src/lib-imap-client/imapc-client.c --- a/src/lib-imap-client/imapc-client.c Wed Jun 07 18:10:10 2017 +0300 +++ b/src/lib-imap-client/imapc-client.c Thu Jun 08 20:39:47 2017 +0300 @@ -530,7 +530,9 @@ (void)imapc_client_add_connection(client); /* wait for any of the connections to login */ + client->stop_on_state_finish = TRUE; imapc_client_run(client); + client->stop_on_state_finish = FALSE; if (imapc_client_get_any_capabilities(client, capabilities_r)) return 0; diff -r fc301d74f851 -r 3001873cd962 src/lib-imap-client/imapc-connection.c --- a/src/lib-imap-client/imapc-connection.c Wed Jun 07 18:10:10 2017 +0300 +++ b/src/lib-imap-client/imapc-connection.c Thu Jun 08 20:39:47 2017 +0300 @@ -386,6 +386,12 @@ conn->selecting_box = NULL; conn->selected_box = NULL; + /* fall through */ + case IMAPC_CONNECTION_STATE_DONE: + /* if we came from imapc_client_get_capabilities(), stop so + it can finish up and not just hang indefinitely. */ + if (conn->client->stop_on_state_finish && !conn->reconnecting) + imapc_client_stop(conn->client); break; default: break; diff -r fc301d74f851 -r 3001873cd962 src/lib-imap-client/test-imapc-client.c --- a/src/lib-imap-client/test-imapc-client.c Wed Jun 07 18:10:10 2017 +0300 +++ b/src/lib-imap-client/test-imapc-client.c Thu Jun 08 20:39:47 2017 +0300 @@ -81,7 +81,7 @@ if (send_banner) { o_stream_nsend_str(server->output, - "* OK [CAPABILITY IMAP4rev1] ready\r\n"); + "* OK [CAPABILITY IMAP4rev1 UNSELECT QUOTA] ready\r\n"); } } @@ -581,6 +581,101 @@ test_end(); } +static void test_imapc_client_get_capabilities_client(void) +{ + enum imapc_capability capabilities; + + test_assert(imapc_client_get_capabilities(imapc_client, &capabilities) == 0); + test_assert(capabilities == (IMAPC_CAPABILITY_IMAP4REV1 | + IMAPC_CAPABILITY_UNSELECT | + IMAPC_CAPABILITY_QUOTA)); +} + +static void test_imapc_client_get_capabilities_server(void) +{ + test_server_wait_connection(&server, TRUE); + test_assert(test_imapc_server_expect("1 LOGIN \"testuser\" \"testpass\"")); + o_stream_nsend_str(server.output, "1 OK \r\n"); + + test_assert(test_imapc_server_expect("2 LOGOUT")); + o_stream_nsend_str(server.output, "2 OK \r\n"); + + test_assert(i_stream_read_next_line(server.input) == NULL); +} + +static void test_imapc_client_get_capabilities(void) +{ + struct imapc_client_settings set = test_imapc_default_settings; + + test_begin("imapc_client_get_capabilities()"); + test_run_client_server(&set, test_imapc_client_get_capabilities_client, + test_imapc_client_get_capabilities_server); + test_end(); +} + +static void test_imapc_client_get_capabilities_reconnected_client(void) +{ + enum imapc_capability capabilities; + + test_expect_errors(2); + test_assert(imapc_client_get_capabilities(imapc_client, &capabilities) == 0); + test_assert(capabilities == (IMAPC_CAPABILITY_IMAP4REV1 | + IMAPC_CAPABILITY_UNSELECT | + IMAPC_CAPABILITY_QUOTA)); + test_expect_no_more_errors(); +} + +static void test_imapc_client_get_capabilities_reconnected_server(void) +{ + test_server_wait_connection(&server, TRUE); + test_server_disconnect_and_wait(TRUE); + + test_assert(test_imapc_server_expect("2 LOGIN \"testuser\" \"testpass\"")); + o_stream_nsend_str(server.output, "2 OK \r\n"); + + test_assert(test_imapc_server_expect("3 LOGOUT")); + o_stream_nsend_str(server.output, "3 OK \r\n"); + + test_assert(i_stream_read_next_line(server.input) == NULL); +} + +static void test_imapc_client_get_capabilities_reconnected(void) +{ + struct imapc_client_settings set = test_imapc_default_settings; + + test_begin("imapc_client_get_capabilities() reconnected"); + + test_run_client_server(&set, test_imapc_client_get_capabilities_reconnected_client, + test_imapc_client_get_capabilities_reconnected_server); + test_end(); +} + +static void test_imapc_client_get_capabilities_disconnected_client(void) +{ + enum imapc_capability capabilities; + + test_expect_errors(4); + test_assert(imapc_client_get_capabilities(imapc_client, &capabilities) < 0); + test_expect_no_more_errors(); +} + +static void test_imapc_client_get_capabilities_disconnected_server(void) +{ + test_server_wait_connection(&server, TRUE); + test_server_disconnect_and_wait(TRUE); +} + +static void test_imapc_client_get_capabilities_disconnected(void) +{ + struct imapc_client_settings set = test_imapc_default_settings; + + test_begin("imapc_client_get_capabilities() disconnected"); + + test_run_client_server(&set, test_imapc_client_get_capabilities_disconnected_client, + test_imapc_client_get_capabilities_disconnected_server); + test_end(); +} + int main(int argc ATTR_UNUSED, char *argv[]) { static void (*test_functions[])(void) = { @@ -591,6 +686,9 @@ test_imapc_reconnect_resend_commands, test_imapc_reconnect_resend_commands_failed, test_imapc_reconnect_mailbox, + test_imapc_client_get_capabilities, + test_imapc_client_get_capabilities_reconnected, + test_imapc_client_get_capabilities_disconnected, NULL };