Repository: thundernest/k-9
Issue: Fix ImapStore.listFolders() when SPECIAL-USE extension is present

`LIST (SPECIAL-USE) …` only lists special folders, not special folders and regular folders.

We probably want to switch back to using a regular LSUB/LIST, then querying the special folders and adding them to our list afterwards. That way we'll get to use a special folder, even if it wasn't included in the result list of the previous LIST command (because the user isn't subscribed to it or it's outside of the subtree specified by the IMAP path prefix).

Changed files:
mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/ImapConnectionTest.java
mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapConnection.java
mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapStore.java
mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/ImapStoreTest.java

diff --git a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapConnection.java b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapConnection.java
index 0758cf6ce..dde67c72f 100644
--- a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapConnection.java
+++ b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapConnection.java
@@ -694,11 +694,15 @@ class ImapConnection {
         return isListResponse && hierarchyDelimiterValid;
     }
 
-    protected boolean hasCapability(String capability) {
+    protected boolean hasCapability(String capability) throws IOException, MessagingException {
+        if (!open) {
+            open();
+        }
+
         return capabilities.contains(capability.toUpperCase(Locale.US));
     }
 
-    public boolean isCondstoreCapable()  {
+    public boolean isCondstoreCapable() throws IOException, MessagingException {
         return hasCapability(Capabilities.CONDSTORE);
     }
 
@@ -886,7 +890,7 @@ class ImapConnection {
         return response;
     }
 
-    int getLineLengthLimit() {
+    int getLineLengthLimit() throws IOException, MessagingException {
         return isCondstoreCapable() ? LENGTH_LIMIT_WITH_CONDSTORE : LENGTH_LIMIT_WITHOUT_CONDSTORE;
     }
 }
diff --git a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapStore.java b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapStore.java
index dd06807b6..645ced912 100644
--- a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapStore.java
+++ b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/ImapStore.java
@@ -162,19 +162,18 @@ public class ImapStore extends RemoteStore {
     private List<FolderListItem> listFolders(ImapConnection connection, boolean subscribedOnly) throws IOException,
             MessagingException {
 
-        String command;
+        String commandFormat;
         if (subscribedOnly) {
-            command = "LSUB";
+            commandFormat = "LSUB \\"\\" %s";
         } else if (connection.hasCapability(Capabilities.SPECIAL_USE) &&
                 connection.hasCapability(Capabilities.LIST_EXTENDED)) {
-            command = "LIST (SPECIAL-USE)";
+            commandFormat = "LIST \\"\\" %s RETURN (SPECIAL-USE)";
         } else {
-            command = "LIST";
+            commandFormat = "LIST \\"\\" %s";
         }
 
         String encodedListPrefix = ImapUtility.encodeString(getCombinedPrefix() + "*");
-        List<ImapResponse> responses = connection.executeSimpleCommand(
-                String.format("%s \\"\\" %s", command, encodedListPrefix));
+        List<ImapResponse> responses = connection.executeSimpleCommand(String.format(commandFormat, encodedListPrefix));
 
         List<ListResponse> listResponses = (subscribedOnly) ?
                 ListResponse.parseLsub(responses) :
diff --git a/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/ImapConnectionTest.java b/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/ImapConnectionTest.java
index 3d26c9cd3..6ace4f139 100644
--- a/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/ImapConnectionTest.java
+++ b/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/ImapConnectionTest.java
@@ -935,6 +935,19 @@ public class ImapConnectionTest {
         server.verifyInteractionCompleted();
     }
 
+    @Test
+    public void hasCapability_withNotYetOpenedConnection_shouldConnectAndFetchCapabilities() throws Exception {
+        MockImapServer server = new MockImapServer();
+        simpleOpenDialog(server, "X-SOMETHING");
+        ImapConnection imapConnection = startServerAndCreateImapConnection(server);
+
+        boolean capabilityPresent = imapConnection.hasCapability("X-SOMETHING");
+
+        assertTrue(capabilityPresent);
+        server.verifyConnectionStillOpen();
+        server.verifyInteractionCompleted();
+    }
+
     private ImapConnection createImapConnection(ImapSettings settings, TrustedSocketFactory socketFactory,
             ConnectivityManager connectivityManager, OAuth2TokenProvider oAuth2TokenProvider) {
         return new ImapConnection(settings, socketFactory, connectivityManager, oAuth2TokenProvider,
diff --git a/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/ImapStoreTest.java b/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/ImapStoreTest.java
index be5177b7b..d7079fa16 100644
--- a/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/ImapStoreTest.java
+++ b/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/ImapStoreTest.java
@@ -115,7 +115,7 @@ public class ImapStoreTest {
                 createImapResponse("* LIST (\\\\HasNoChildren \\\\Trash) \\"/\\" \\"[Gmail]/Trash\\""),
                 createImapResponse("5 OK Success")
         );
-        when(imapConnection.executeSimpleCommand("LIST (SPECIAL-USE) \\"\\" \\"*\\"")).thenReturn(imapResponses);
+        when(imapConnection.executeSimpleCommand("LIST \\"\\" \\"*\\" RETURN (SPECIAL-USE)")).thenReturn(imapResponses);
         imapStore.enqueueImapConnection(imapConnection);
 
         List<ImapFolder> folders = imapStore.getPersonalNamespaces();
@@ -138,7 +138,7 @@ public class ImapStoreTest {
 
         imapStore.getPersonalNamespaces();
 
-        verify(imapConnection, never()).executeSimpleCommand("LIST (SPECIAL-USE) \\"\\" \\"*\\"");
+        verify(imapConnection, never()).executeSimpleCommand("LIST \\"\\" \\"*\\" RETURN (SPECIAL-USE)");
         verify(imapConnection).executeSimpleCommand("LIST \\"\\" \\"*\\"");
     }
 
@@ -151,7 +151,7 @@ public class ImapStoreTest {
 
         imapStore.getPersonalNamespaces();
 
-        verify(imapConnection, never()).executeSimpleCommand("LIST (SPECIAL-USE) \\"\\" \\"*\\"");
+        verify(imapConnection, never()).executeSimpleCommand("LIST \\"\\" \\"*\\" RETURN (SPECIAL-USE)");
         verify(imapConnection).executeSimpleCommand("LIST \\"\\" \\"*\\"");
     }