Skip to content

Commit

Permalink
Merge pull request #7349 from thunderbird/folder_fetcher
Browse files Browse the repository at this point in the history
Add `FolderFetcher` interface
  • Loading branch information
wmontwe authored Nov 16, 2023
2 parents 8f06c24 + 1de41d9 commit e556211
Show file tree
Hide file tree
Showing 9 changed files with 392 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.fsck.k9.mail.folders

import com.fsck.k9.mail.ServerSettings
import com.fsck.k9.mail.oauth.AuthStateStorage

/**
* Fetches the list of folders from a server.
*
* @throws FolderFetcherException in case of an error
*/
fun interface FolderFetcher {
fun getFolders(
serverSettings: ServerSettings,
authStateStorage: AuthStateStorage?,
): List<RemoteFolder>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.fsck.k9.mail.folders

/**
* Thrown by [FolderFetcher] in case of an error.
*/
class FolderFetcherException(
cause: Throwable,
val messageFromServer: String? = null,
) : RuntimeException(cause.message, cause)
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.fsck.k9.mail.folders

@JvmInline
value class FolderServerId(val serverId: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.fsck.k9.mail.folders

import com.fsck.k9.mail.FolderType

data class RemoteFolder(
val serverId: FolderServerId,
val displayName: String,
val type: FolderType,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.fsck.k9.mail.store.imap

import com.fsck.k9.mail.AuthenticationFailedException
import com.fsck.k9.mail.ServerSettings
import com.fsck.k9.mail.folders.FolderFetcher
import com.fsck.k9.mail.folders.FolderFetcherException
import com.fsck.k9.mail.folders.FolderServerId
import com.fsck.k9.mail.folders.RemoteFolder
import com.fsck.k9.mail.oauth.AuthStateStorage
import com.fsck.k9.mail.oauth.OAuth2TokenProvider
import com.fsck.k9.mail.oauth.OAuth2TokenProviderFactory
import com.fsck.k9.mail.ssl.TrustedSocketFactory

/**
* Fetches the list of folders from an IMAP server.
*/
class ImapFolderFetcher internal constructor(
private val trustedSocketFactory: TrustedSocketFactory,
private val oAuth2TokenProviderFactory: OAuth2TokenProviderFactory?,
private val clientIdAppName: String,
private val clientIdAppVersion: String,
private val imapStoreFactory: ImapStoreFactory,
) : FolderFetcher {
constructor(
trustedSocketFactory: TrustedSocketFactory,
oAuth2TokenProviderFactory: OAuth2TokenProviderFactory?,
clientIdAppName: String,
clientIdAppVersion: String,
) : this(
trustedSocketFactory,
oAuth2TokenProviderFactory,
clientIdAppName,
clientIdAppVersion,
imapStoreFactory = ImapStore.Companion,
)

@Suppress("TooGenericExceptionCaught")
override fun getFolders(serverSettings: ServerSettings, authStateStorage: AuthStateStorage?): List<RemoteFolder> {
require(serverSettings.type == "imap")

val config = object : ImapStoreConfig {
override val logLabel = "folder-fetcher"
override fun isSubscribedFoldersOnly() = false
override fun clientId() = ImapClientId(appName = clientIdAppName, appVersion = clientIdAppVersion)
}
val oAuth2TokenProvider = createOAuth2TokenProviderOrNull(authStateStorage)
val store = imapStoreFactory.create(serverSettings, config, trustedSocketFactory, oAuth2TokenProvider)

return try {
store.getFolders()
.asSequence()
.filterNot { it.oldServerId == null }
.map { folder ->
RemoteFolder(
serverId = FolderServerId(folder.oldServerId!!),
displayName = folder.name,
type = folder.type,
)
}
.toList()
} catch (e: AuthenticationFailedException) {
throw FolderFetcherException(messageFromServer = e.messageFromServer, cause = e)
} catch (e: NegativeImapResponseException) {
throw FolderFetcherException(messageFromServer = e.responseText, cause = e)
} catch (e: Exception) {
throw FolderFetcherException(cause = e)
} finally {
store.closeAllConnections()
}
}

private fun createOAuth2TokenProviderOrNull(authStateStorage: AuthStateStorage?): OAuth2TokenProvider? {
return authStateStorage?.let {
oAuth2TokenProviderFactory?.create(it)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ interface ImapStore {

fun closeAllConnections()

companion object {
fun create(
companion object : ImapStoreFactory {
override fun create(
serverSettings: ServerSettings,
config: ImapStoreConfig,
trustedSocketFactory: TrustedSocketFactory,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.fsck.k9.mail.store.imap

import com.fsck.k9.mail.ServerSettings
import com.fsck.k9.mail.oauth.OAuth2TokenProvider
import com.fsck.k9.mail.ssl.TrustedSocketFactory

internal fun interface ImapStoreFactory {
fun create(
serverSettings: ServerSettings,
config: ImapStoreConfig,
trustedSocketFactory: TrustedSocketFactory,
oauthTokenProvider: OAuth2TokenProvider?,
): ImapStore
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.fsck.k9.mail.store.imap

import kotlin.test.fail

class FakeImapStore : ImapStore {
private var openConnectionCount = 0

var getFoldersAction: () -> List<FolderListItem> = { fail("getFoldersAction not set") }
val hasOpenConnections: Boolean
get() = openConnectionCount != 0

override fun checkSettings() {
throw UnsupportedOperationException("not implemented")
}

override fun getFolder(name: String): ImapFolder {
throw UnsupportedOperationException("not implemented")
}

override fun getFolders(): List<FolderListItem> {
openConnectionCount++
return getFoldersAction()
}

override fun closeAllConnections() {
openConnectionCount = 0
}
}
Loading

0 comments on commit e556211

Please sign in to comment.