]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
imapc: Automatically enable IMAP4rev2 if server has the capability
authorMarkus Valentin <markus.valentin@open-xchange.com>
Tue, 13 May 2025 11:36:28 +0000 (13:36 +0200)
committerMarkus Valentin <markus.valentin@open-xchange.com>
Mon, 2 Jun 2025 08:27:46 +0000 (10:27 +0200)
src/lib-imap-client/imapc-connection.c
src/lib-imap-client/test-imapc-client.c

index a9a49a52a0c5d04e85cf45d0ff9ab7ade1531e69..7b43a65d80bb8a2ef7b8f59912972e6152668a6e 100644 (file)
@@ -156,6 +156,7 @@ struct imapc_connection {
        bool idling:1;
        bool idle_stopping:1;
        bool idle_plus_waiting:1;
+       bool imap4rev2_enabled:1;
        bool select_waiting_reply:1;
        /* TRUE if IMAP server is in SELECTED state. select_box may be NULL
           though, if we already closed the mailbox from client point of
@@ -753,6 +754,30 @@ imapc_connection_read_line(struct imapc_connection *conn,
        return ret;
 }
 
+static void
+imapc_connection_imap4rev2_enable_callback(const struct imapc_command_reply *reply ATTR_UNUSED,
+                                          void *context)
+{
+       struct imapc_connection *conn = context;
+
+       if (reply->state == IMAPC_COMMAND_STATE_OK)
+               conn->imap4rev2_enabled = TRUE;
+}
+
+static void imapc_connection_send_enable_imap4rev2(struct imapc_connection *conn)
+{
+       struct imapc_command *cmd;
+
+       if ((conn->capabilities & IMAPC_CAPABILITY_IMAP4REV2) == 0 ||
+            conn->imap4rev2_enabled)
+               return;
+
+       cmd = imapc_connection_cmd(conn,
+                                  imapc_connection_imap4rev2_enable_callback,
+                                  conn);
+       imapc_command_send(cmd, "ENABLE IMAP4REV2");
+}
+
 static int
 imapc_connection_parse_capability(struct imapc_connection *conn,
                                  const char *value)
@@ -907,6 +932,9 @@ imapc_connection_auth_finish(struct imapc_connection *conn,
        imapc_connection_set_state(conn, IMAPC_CONNECTION_STATE_DONE);
        imapc_login_callback(conn, reply);
 
+       /* Enable IMAP4REV2 post login, if available */
+       imapc_connection_send_enable_imap4rev2(conn);
+
        imapc_command_send_more(conn);
 }
 
index 2ddb88b87b7f35a8823c0100806403b93511aeaf..e8ca2be8bd13a2c66b2a28a303953a473e22b30c 100644 (file)
@@ -162,7 +162,7 @@ static bool test_imapc_server_expect(const char *expected_line)
 }
 
 static void
-test_server_wait_connection(struct test_server *server, bool send_banner)
+test_server_wait_connection_common(struct test_server *server)
 {
        if (debug)
                i_debug("Waiting for connection");
@@ -177,13 +177,31 @@ test_server_wait_connection(struct test_server *server, bool send_banner)
        server->input = i_stream_create_fd(server->fd, SIZE_MAX);
        server->output = o_stream_create_fd(server->fd, SIZE_MAX);
        o_stream_set_no_error_handling(server->output, TRUE);
+}
 
+static void
+test_server_wait_connection(struct test_server *server, bool send_banner)
+{
+       test_server_wait_connection_common(server);
        if (send_banner) {
                o_stream_nsend_str(server->output,
                        "* OK [CAPABILITY IMAP4rev1 UNSELECT QUOTA] ready\r\n");
        }
 }
 
+#ifdef EXPERIMENTAL_IMAP4REV2
+static void
+test_server_wait_connection_imap4rev2(struct test_server *server, bool send_banner)
+{
+       test_server_wait_connection_common(server);
+
+       if (send_banner) {
+               o_stream_nsend_str(server->output,
+                       "* OK [CAPABILITY IMAP4rev1 IMAP4rev2 UNSELECT QUOTA QRESYNC CONDSTORE] ready\r\n");
+       }
+}
+#endif
+
 static void test_server_disconnect(struct test_server *server)
 {
        if (debug)
@@ -847,6 +865,51 @@ static void test_imapc_client_get_capabilities_disconnected(void)
        test_end();
 }
 
+#ifdef EXPERIMENTAL_IMAP4REV2
+/*
+ * imapc_client_get_capabilities_imap4rev2()
+ */
+
+static void test_imapc_client_get_capabilities_imap4rev2_client(void)
+{
+       enum imapc_capability capabilities;
+
+       test_assert(imapc_client_get_capabilities(imapc_client, &capabilities) == 0);
+       test_assert(capabilities == (IMAPC_CAPABILITY_IMAP4REV1 |
+                                    IMAPC_CAPABILITY_IMAP4REV2 |
+                                    IMAPC_CAPABILITY_QRESYNC |
+                                    IMAPC_CAPABILITY_CONDSTORE |
+                                    IMAPC_CAPABILITY_UNSELECT |
+                                    IMAPC_CAPABILITY_QUOTA));
+}
+
+static void test_imapc_client_get_capabilities_imap4rev2_server(void)
+{
+       test_server_wait_connection_imap4rev2(&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 ENABLE IMAP4REV2"));
+       o_stream_nsend_str(server.output, "2 OK ENABLED\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_imap4rev2(void)
+{
+       test_begin("imapc_client_get_capabilities_imap4rev2()");
+       test_run_client_server(test_imapc_client_get_capabilities_imap4rev2_client,
+                              test_imapc_client_get_capabilities_imap4rev2_server,
+                              FALSE);
+       test_end();
+}
+#endif
+
 /*
  * Main
  */
@@ -878,6 +941,9 @@ int main(int argc ATTR_UNUSED, char *argv[])
                test_imapc_client_get_capabilities,
                test_imapc_client_get_capabilities_reconnected,
                test_imapc_client_get_capabilities_disconnected,
+#ifdef EXPERIMENTAL_IMAP4REV2
+               test_imapc_client_get_capabilities_imap4rev2,
+#endif
                NULL
        };