Merge pull request #7722 from thunderbird/expunge_handling

IMAP: Change EXPUNGE behavior after moving a message
This commit is contained in:
cketti 2024-03-26 11:34:19 +01:00 committed by GitHub
commit 8ef13f5dcd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 51 additions and 5 deletions

View file

@ -2,6 +2,7 @@ package com.fsck.k9.backends
import android.content.Context
import com.fsck.k9.Account
import com.fsck.k9.Account.Expunge
import com.fsck.k9.backend.BackendFactory
import com.fsck.k9.backend.api.Backend
import com.fsck.k9.backend.imap.ImapBackend
@ -74,6 +75,8 @@ class ImapBackendFactory(
override fun isSubscribedFoldersOnly() = account.isSubscribedFoldersOnly
override fun isExpungeImmediately() = account.expungePolicy == Expunge.EXPUNGE_IMMEDIATELY
override fun clientId() = ImapClientId(appName = clientIdAppName, appVersion = clientIdAppVersion)
}
}

View file

@ -41,6 +41,7 @@ class ImapFolderFetcher internal constructor(
val config = object : ImapStoreConfig {
override val logLabel = "folder-fetcher"
override fun isSubscribedFoldersOnly() = false
override fun isExpungeImmediately() = false
override fun clientId() = ImapClientId(appName = clientIdAppName, appVersion = clientIdAppVersion)
}
val oAuth2TokenProvider = createOAuth2TokenProviderOrNull(authStateStorage)

View file

@ -32,6 +32,7 @@ class ImapServerSettingsValidator(
val config = object : ImapStoreConfig {
override val logLabel = "check"
override fun isSubscribedFoldersOnly() = false
override fun isExpungeImmediately() = false
override fun clientId() = ImapClientId(appName = clientIdAppName, appVersion = clientIdAppVersion)
}
val oAuth2TokenProvider = createOAuth2TokenProviderOrNull(authStateStorage)

View file

@ -3,5 +3,6 @@ package com.fsck.k9.mail.store.imap
interface ImapStoreConfig {
val logLabel: String
fun isSubscribedFoldersOnly(): Boolean
fun isExpungeImmediately(): Boolean
fun clientId(): ImapClientId
}

View file

@ -4,6 +4,7 @@ import com.fsck.k9.mail.Flag
internal interface InternalImapStore {
val logLabel: String
val config: ImapStoreConfig
fun getCombinedPrefix(): String

View file

@ -354,7 +354,7 @@ internal class RealImapFolder(
val uidMapping = copyMessages(messages, folder)
setFlags(messages, setOf(Flag.DELETED), true)
expungeUidsOnly(uids)
expungeUidsAfterDelete(uids)
return uidMapping
}
@ -1117,8 +1117,8 @@ internal class RealImapFolder(
expungeUids(uids, fullExpungeFallback = true)
}
private fun expungeUidsOnly(uids: List<String>) {
expungeUids(uids, fullExpungeFallback = false)
private fun expungeUidsAfterDelete(uids: List<String>) {
expungeUids(uids, fullExpungeFallback = internalImapStore.config.isExpungeImmediately())
}
private fun expungeUids(uids: List<String>, fullExpungeFallback: Boolean) {

View file

@ -20,7 +20,7 @@ import java.util.LinkedList
internal open class RealImapStore(
private val serverSettings: ServerSettings,
private val config: ImapStoreConfig,
override val config: ImapStoreConfig,
private val trustedSocketFactory: TrustedSocketFactory,
private val oauthTokenProvider: OAuth2TokenProvider?,
) : ImapStore, ImapConnectionManager, InternalImapStore {

View file

@ -0,0 +1,17 @@
package com.fsck.k9.mail.store.imap
class FakeImapStoreConfig : ImapStoreConfig {
var expungeImmediately = true
override var logLabel: String = "irrelevant"
override fun isSubscribedFoldersOnly(): Boolean {
throw UnsupportedOperationException("not implemented")
}
override fun isExpungeImmediately(): Boolean = expungeImmediately
override fun clientId(): ImapClientId {
throw UnsupportedOperationException("not implemented")
}
}

View file

@ -54,8 +54,10 @@ import org.mockito.kotlin.stub
import org.mockito.kotlin.whenever
class RealImapFolderTest {
private val imapStoreConfig = FakeImapStoreConfig()
private val internalImapStore = object : InternalImapStore {
override val logLabel = "Account"
override val config = imapStoreConfig
override fun getCombinedPrefix() = ""
override fun getPermanentFlagsIndex() = mutableSetOf<Flag>()
}
@ -355,7 +357,26 @@ class RealImapFolderTest {
}
@Test
fun `moveMessages() should delete messages from source folder but not issue EXPUNGE command`() {
fun `moveMessages() with expungeImmediately = true should delete and expunge`() {
imapStoreConfig.expungeImmediately = true
val sourceFolder = createFolder("Folder")
prepareImapFolderForOpen(OpenMode.READ_WRITE)
imapConnection.stub {
on { isUidPlusCapable } doReturn false
}
val destinationFolder = createFolder("Destination")
val messages = listOf(createImapMessage("1"))
sourceFolder.open(OpenMode.READ_WRITE)
sourceFolder.moveMessages(messages, destinationFolder)
assertCommandWithIdsIssued("UID STORE 1 +FLAGS.SILENT (\\Deleted)")
assertCommandIssued("EXPUNGE")
}
@Test
fun `moveMessages() with expungeImmediately = false should delete but not expunge`() {
imapStoreConfig.expungeImmediately = false
val sourceFolder = createFolder("Folder")
prepareImapFolderForOpen(OpenMode.READ_WRITE)
imapConnection.stub {

View file

@ -408,6 +408,7 @@ class RealImapStoreTest {
return object : ImapStoreConfig {
override val logLabel: String = "irrelevant"
override fun isSubscribedFoldersOnly(): Boolean = isSubscribedFoldersOnly
override fun isExpungeImmediately(): Boolean = true
override fun clientId(): ImapClientId = ImapClientId(appName = "irrelevant", appVersion = "irrelevant")
}
}