changeset 22177:3001873cd962

lib-imap-client: Fix hang when imapc_client_get_capabilities() is called without connection
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Thu, 08 Jun 2017 20:39:47 +0300
parents fc301d74f851
children e97e52bd980f
files src/lib-imap-client/imapc-client-private.h src/lib-imap-client/imapc-client.c src/lib-imap-client/imapc-connection.c src/lib-imap-client/test-imapc-client.c
diffstat 4 files changed, 108 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- 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 {
--- 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;
 
--- 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;
--- 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
 	};