diff --git a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapStore.kt b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapStore.kt index 7f3e07ab2..5f349fd05 100644 --- a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapStore.kt +++ b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapStore.kt @@ -143,8 +143,13 @@ internal open class RealImapStore( } if (RealImapFolder.INBOX.equals(serverId, ignoreCase = true)) { + // We always add our own inbox entry to the returned list. continue } else if (listResponse.hasAttribute("\\NoSelect")) { + // RFC 3501, section 7.2.2: It is not possible to use this name as a selectable mailbox. + continue + } else if (listResponse.hasAttribute("\\NonExistent")) { + // RFC 5258, section 3: The "\NonExistent" attribute implies "\NoSelect". continue } diff --git a/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/RealImapStoreTest.kt b/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/RealImapStoreTest.kt index 42485bc9a..3ea6cf138 100644 --- a/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/RealImapStoreTest.kt +++ b/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/RealImapStoreTest.kt @@ -117,12 +117,33 @@ class RealImapStoreTest { } @Test - fun `getFolders() with subscribedFoldersOnly = false`() { + fun `getFolders() should ignore NoSelect entries`() { val imapStore = createTestImapStore(isSubscribedFoldersOnly = false) val imapConnection = createMockConnection().stub { on { executeSimpleCommand("""LIST "" "*"""") } doReturn listOf( + createImapResponse("""* LIST () "." "INBOX""""), + createImapResponse("""* LIST (\Noselect) "." "Folder""""), + createImapResponse("""* LIST () "." "Folder.SubFolder""""), + createImapResponse("6 OK Success"), + ) + } + imapStore.enqueueImapConnection(imapConnection) + + val folders = imapStore.getFolders() + + assertThat(folders).isNotNull() + assertThat(folders.map { it.serverId }).containsExactly("INBOX", "Folder.SubFolder") + } + + @Test + fun `getFolders() should ignore NonExistent entries`() { + val imapStore = createTestImapStore(isSubscribedFoldersOnly = false) + val imapConnection = createMockConnection().stub { + on { hasCapability(Capabilities.LIST_EXTENDED) } doReturn true + on { hasCapability(Capabilities.SPECIAL_USE) } doReturn true + on { executeSimpleCommand("""LIST "" "*" RETURN (SPECIAL-USE)""") } doReturn listOf( createImapResponse("""* LIST (\HasNoChildren) "." "INBOX""""), - createImapResponse("""* LIST (\Noselect \HasChildren) "." "Folder""""), + createImapResponse("""* LIST (\NonExistent \HasChildren) "." "Folder""""), createImapResponse("""* LIST (\HasNoChildren) "." "Folder.SubFolder""""), createImapResponse("6 OK Success"), ) @@ -135,6 +156,24 @@ class RealImapStoreTest { assertThat(folders.map { it.serverId }).containsExactly("INBOX", "Folder.SubFolder") } + @Test + fun `getFolders() with subscribedFoldersOnly = false`() { + val imapStore = createTestImapStore(isSubscribedFoldersOnly = false) + val imapConnection = createMockConnection().stub { + on { executeSimpleCommand("""LIST "" "*"""") } doReturn listOf( + createImapResponse("""* LIST (\HasNoChildren) "." "INBOX""""), + createImapResponse("""* LIST (\HasNoChildren) "." "Folder""""), + createImapResponse("6 OK Success"), + ) + } + imapStore.enqueueImapConnection(imapConnection) + + val folders = imapStore.getFolders() + + assertThat(folders).isNotNull() + assertThat(folders.map { it.serverId }).containsExactly("INBOX", "Folder") + } + @Test fun `getFolders() with subscribedFoldersOnly = true should only return existing subscribed folders`() { val imapStore = createTestImapStore(isSubscribedFoldersOnly = true)