Skip to content

Commit

Permalink
Merge pull request #7403 from thunderbird/move_copyuid_response
Browse files Browse the repository at this point in the history
IMAP: Add support for untagged COPYUID responses
  • Loading branch information
cketti authored Dec 4, 2023
2 parents 48f82f6 + 462736e commit 891294f
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 203 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ internal class RealImapFolder(
uids,
)

UidCopyResponse.parse(imapResponses)?.uidMapping
UidCopyResponse.parse(imapResponses, allowUntaggedResponse = true)?.uidMapping
} catch (ioe: IOException) {
throw ioExceptionHandler(connection, ioe)
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.fsck.k9.mail.store.imap

internal class UidCopyResponse private constructor(val uidMapping: Map<String, String>) {

companion object {
fun parse(imapResponses: List<ImapResponse>, allowUntaggedResponse: Boolean = false): UidCopyResponse? {
val uidMapping = mutableMapOf<String, String>()
for (imapResponse in imapResponses) {
parseUidCopyResponse(imapResponse, allowUntaggedResponse, uidMapping)
}

return if (uidMapping.isNotEmpty()) {
UidCopyResponse(uidMapping)
} else {
null
}
}

@Suppress("ReturnCount", "ComplexCondition", "MagicNumber")
private fun parseUidCopyResponse(
response: ImapResponse,
allowUntaggedResponse: Boolean,
uidMappingOutput: MutableMap<String, String>,
) {
if (!(allowUntaggedResponse || response.isTagged) || response.size < 2 ||
!ImapResponseParser.equalsIgnoreCase(response[0], Responses.OK) || !response.isList(1)
) {
return
}

val responseTextList = response.getList(1)
if (responseTextList.size < 4 ||
!ImapResponseParser.equalsIgnoreCase(responseTextList[0], Responses.COPYUID) ||
!responseTextList.isString(1) || !responseTextList.isString(2) || !responseTextList.isString(3)
) {
return
}

val sourceUids = ImapUtility.getImapSequenceValues(responseTextList.getString(2))
val destinationUids = ImapUtility.getImapSequenceValues(responseTextList.getString(3))

val size = sourceUids.size
if (size == 0 || size != destinationUids.size) {
return
}

for (i in 0 until size) {
val sourceUid = sourceUids[i]
val destinationUid = destinationUids[i]
uidMappingOutput[sourceUid] = destinationUid
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.fsck.k9.mail.Part
import com.fsck.k9.mail.internet.BinaryTempFileBody
import com.fsck.k9.mail.internet.MimeHeader
import com.fsck.k9.mail.store.imap.ImapResponseHelper.createImapResponse
import com.fsck.k9.mail.store.imap.ImapResponseHelper.createImapResponseList
import java.io.File
import java.io.IOException
import java.nio.file.Files
Expand Down Expand Up @@ -300,15 +301,37 @@ class RealImapFolderTest {
}

@Test
fun `moveMessages() with MOVE extension`() {
fun `moveMessages() with MOVE extension and tagged COPYUID response`() {
val sourceFolder = createFolder("Folder")
prepareImapFolderForOpen(OpenMode.READ_WRITE)
imapConnection.stub {
on { hasCapability(Capabilities.MOVE) } doReturn true
}
val destinationFolder = createFolder("Destination")
val messages = listOf(createImapMessage("1"))
setupMoveResponse("x OK [COPYUID 23 1 101] Success")
setupMoveResponses("x OK [COPYUID 23 1 101] Success")
sourceFolder.open(OpenMode.READ_WRITE)

val uidMapping = sourceFolder.moveMessages(messages, destinationFolder)

assertCommandWithIdsIssued("UID MOVE 1 \"Destination\"")
assertThat(uidMapping).isNotNull().containsOnly("1" to "101")
}

@Test
fun `moveMessages() with MOVE extension and untagged COPYUID response`() {
val sourceFolder = createFolder("Folder")
prepareImapFolderForOpen(OpenMode.READ_WRITE)
imapConnection.stub {
on { hasCapability(Capabilities.MOVE) } doReturn true
}
val destinationFolder = createFolder("Destination")
val messages = listOf(createImapMessage("1"))
setupMoveResponses(
"* OK [COPYUID 23 1 101]",
"* 1 EXPUNGE",
"x OK MOVE completed",
)
sourceFolder.open(OpenMode.READ_WRITE)

val uidMapping = sourceFolder.moveMessages(messages, destinationFolder)
Expand Down Expand Up @@ -1223,8 +1246,8 @@ class RealImapFolderTest {
}

@Suppress("SameParameterValue")
private fun setupMoveResponse(response: String) {
val imapResponses = listOf(createImapResponse(response))
private fun setupMoveResponses(vararg responses: String) {
val imapResponses = createImapResponseList(*responses)
whenever(imapConnection.executeCommandWithIdSet(eq(Commands.UID_MOVE), anyString(), anySet()))
.thenReturn(imapResponses)
}
Expand Down

This file was deleted.

Loading

0 comments on commit 891294f

Please sign in to comment.