IMAP: Add support for the \NonExistent LIST response attribute

This commit is contained in:
cketti 2024-03-28 15:10:16 +01:00
parent 1414dde598
commit 8436b07153
2 changed files with 46 additions and 2 deletions

View file

@ -143,8 +143,13 @@ internal open class RealImapStore(
} }
if (RealImapFolder.INBOX.equals(serverId, ignoreCase = true)) { if (RealImapFolder.INBOX.equals(serverId, ignoreCase = true)) {
// We always add our own inbox entry to the returned list.
continue continue
} else if (listResponse.hasAttribute("\\NoSelect")) { } 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 continue
} }

View file

@ -117,12 +117,33 @@ class RealImapStoreTest {
} }
@Test @Test
fun `getFolders() with subscribedFoldersOnly = false`() { fun `getFolders() should ignore NoSelect entries`() {
val imapStore = createTestImapStore(isSubscribedFoldersOnly = false) val imapStore = createTestImapStore(isSubscribedFoldersOnly = false)
val imapConnection = createMockConnection().stub { val imapConnection = createMockConnection().stub {
on { executeSimpleCommand("""LIST "" "*"""") } doReturn listOf( 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 (\HasNoChildren) "." "INBOX""""),
createImapResponse("""* LIST (\Noselect \HasChildren) "." "Folder""""), createImapResponse("""* LIST (\NonExistent \HasChildren) "." "Folder""""),
createImapResponse("""* LIST (\HasNoChildren) "." "Folder.SubFolder""""), createImapResponse("""* LIST (\HasNoChildren) "." "Folder.SubFolder""""),
createImapResponse("6 OK Success"), createImapResponse("6 OK Success"),
) )
@ -135,6 +156,24 @@ class RealImapStoreTest {
assertThat(folders.map { it.serverId }).containsExactly("INBOX", "Folder.SubFolder") 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 @Test
fun `getFolders() with subscribedFoldersOnly = true should only return existing subscribed folders`() { fun `getFolders() with subscribedFoldersOnly = true should only return existing subscribed folders`() {
val imapStore = createTestImapStore(isSubscribedFoldersOnly = true) val imapStore = createTestImapStore(isSubscribedFoldersOnly = true)