Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 13:56:56 +01:00
parent 75dc487a7a
commit 39c29d175b
6317 changed files with 388324 additions and 2 deletions

View file

@ -0,0 +1,12 @@
plugins {
id(ThunderbirdPlugins.Library.jvm)
alias(libs.plugins.android.lint)
}
dependencies {
implementation(projects.core.common)
implementation(projects.core.outcome)
implementation(projects.feature.mail.account.api)
implementation(projects.feature.mail.folder.api)
api(projects.mail.common)
}

View file

@ -0,0 +1,94 @@
package com.fsck.k9.backend.api
import com.fsck.k9.mail.BodyFactory
import com.fsck.k9.mail.Flag
import com.fsck.k9.mail.Message
import com.fsck.k9.mail.Part
import net.thunderbird.core.common.exception.MessagingException
import net.thunderbird.feature.mail.folder.api.FolderPathDelimiter
interface Backend {
val supportsFlags: Boolean
val supportsExpunge: Boolean
val supportsMove: Boolean
val supportsCopy: Boolean
val supportsUpload: Boolean
val supportsTrashFolder: Boolean
val supportsSearchByDate: Boolean
val supportsFolderSubscriptions: Boolean
val isPushCapable: Boolean
@Throws(MessagingException::class)
fun refreshFolderList(): FolderPathDelimiter?
// TODO: Add a way to cancel the sync process
fun sync(folderServerId: String, syncConfig: SyncConfig, listener: SyncListener)
@Throws(MessagingException::class)
fun downloadMessage(syncConfig: SyncConfig, folderServerId: String, messageServerId: String)
@Throws(MessagingException::class)
fun downloadMessageStructure(folderServerId: String, messageServerId: String)
@Throws(MessagingException::class)
fun downloadCompleteMessage(folderServerId: String, messageServerId: String)
@Throws(MessagingException::class)
fun setFlag(folderServerId: String, messageServerIds: List<String>, flag: Flag, newState: Boolean)
@Throws(MessagingException::class)
fun markAllAsRead(folderServerId: String)
@Throws(MessagingException::class)
fun expunge(folderServerId: String)
@Throws(MessagingException::class)
fun deleteMessages(folderServerId: String, messageServerIds: List<String>)
@Throws(MessagingException::class)
fun deleteAllMessages(folderServerId: String)
@Throws(MessagingException::class)
fun moveMessages(
sourceFolderServerId: String,
targetFolderServerId: String,
messageServerIds: List<String>,
): Map<String, String>?
@Throws(MessagingException::class)
fun moveMessagesAndMarkAsRead(
sourceFolderServerId: String,
targetFolderServerId: String,
messageServerIds: List<String>,
): Map<String, String>?
@Throws(MessagingException::class)
fun copyMessages(
sourceFolderServerId: String,
targetFolderServerId: String,
messageServerIds: List<String>,
): Map<String, String>?
@Throws(MessagingException::class)
fun search(
folderServerId: String,
query: String?,
requiredFlags: Set<Flag>?,
forbiddenFlags: Set<Flag>?,
performFullTextSearch: Boolean,
): List<String>
@Throws(MessagingException::class)
fun fetchPart(folderServerId: String, messageServerId: String, part: Part, bodyFactory: BodyFactory)
@Throws(MessagingException::class)
fun findByMessageId(folderServerId: String, messageId: String): String?
@Throws(MessagingException::class)
fun uploadMessage(folderServerId: String, message: Message): String?
@Throws(MessagingException::class)
fun sendMessage(message: Message)
fun createPusher(callback: BackendPusherCallback): BackendPusher
}

View file

@ -0,0 +1,36 @@
package com.fsck.k9.backend.api
import com.fsck.k9.mail.Flag
import com.fsck.k9.mail.Message
import com.fsck.k9.mail.MessageDownloadState
import java.util.Date
// FIXME: add documentation
interface BackendFolder {
val name: String
val visibleLimit: Int
fun getMessageServerIds(): Set<String>
fun getAllMessagesAndEffectiveDates(): Map<String, Long?>
fun destroyMessages(messageServerIds: List<String>)
fun clearAllMessages()
fun getMoreMessages(): MoreMessages
fun setMoreMessages(moreMessages: MoreMessages)
fun setLastChecked(timestamp: Long)
fun setStatus(status: String?)
fun isMessagePresent(messageServerId: String): Boolean
fun getMessageFlags(messageServerId: String): Set<Flag>
fun setMessageFlag(messageServerId: String, flag: Flag, value: Boolean)
fun saveMessage(message: Message, downloadState: MessageDownloadState)
fun getOldestMessageDate(): Date?
fun getFolderExtraString(name: String): String?
fun setFolderExtraString(name: String, value: String?)
fun getFolderExtraNumber(name: String): Long?
fun setFolderExtraNumber(name: String, value: Long)
enum class MoreMessages {
UNKNOWN,
FALSE,
TRUE,
}
}

View file

@ -0,0 +1,8 @@
package com.fsck.k9.backend.api
interface BackendPusher {
fun start()
fun updateFolders(folderServerIds: Collection<String>)
fun stop()
fun reconnect()
}

View file

@ -0,0 +1,7 @@
package com.fsck.k9.backend.api
interface BackendPusherCallback {
fun onPushEvent(folderServerId: String)
fun onPushError(exception: Exception)
fun onPushNotSupported()
}

View file

@ -0,0 +1,33 @@
package com.fsck.k9.backend.api
import com.fsck.k9.mail.FolderType
import java.io.Closeable
import net.thunderbird.core.common.exception.MessagingException
interface BackendStorage {
fun getFolder(folderServerId: String): BackendFolder
fun getFolderServerIds(): List<String>
fun createFolderUpdater(): BackendFolderUpdater
fun getExtraString(name: String): String?
fun setExtraString(name: String, value: String)
fun getExtraNumber(name: String): Long?
fun setExtraNumber(name: String, value: Long)
}
interface BackendFolderUpdater : Closeable {
@Throws(MessagingException::class)
fun createFolders(folders: List<FolderInfo>): Set<Long>
fun deleteFolders(folderServerIds: List<String>)
@Throws(MessagingException::class)
fun changeFolder(folderServerId: String, name: String, type: FolderType)
}
fun BackendFolderUpdater.createFolder(folder: FolderInfo): Long? = createFolders(listOf(folder)).firstOrNull()
inline fun <T> BackendStorage.updateFolders(block: BackendFolderUpdater.() -> T): T {
return createFolderUpdater().use { it.block() }
}

View file

@ -0,0 +1,11 @@
package com.fsck.k9.backend.api
import com.fsck.k9.mail.FolderType
import net.thunderbird.feature.mail.folder.api.FolderPathDelimiter
data class FolderInfo(
val serverId: String,
val name: String,
val type: FolderType,
val folderPathDelimiter: FolderPathDelimiter? = null,
)

View file

@ -0,0 +1,19 @@
package com.fsck.k9.backend.api
import com.fsck.k9.mail.Flag
import java.util.Date
data class SyncConfig(
val expungePolicy: ExpungePolicy,
val earliestPollDate: Date?,
val syncRemoteDeletions: Boolean,
val maximumAutoDownloadMessageSize: Int,
val defaultVisibleLimit: Int,
val syncFlags: Set<Flag>,
) {
enum class ExpungePolicy {
IMMEDIATELY,
MANUALLY,
ON_POLL,
}
}

View file

@ -0,0 +1,21 @@
package com.fsck.k9.backend.api
interface SyncListener {
fun syncStarted(folderServerId: String)
fun syncAuthenticationSuccess()
fun syncHeadersStarted(folderServerId: String)
fun syncHeadersProgress(folderServerId: String, completed: Int, total: Int)
fun syncHeadersFinished(folderServerId: String, totalMessagesInMailbox: Int, numNewMessages: Int)
fun syncProgress(folderServerId: String, completed: Int, total: Int)
fun syncNewMessage(folderServerId: String, messageServerId: String, isOldMessage: Boolean)
fun syncRemovedMessage(folderServerId: String, messageServerId: String)
fun syncFlagChanged(folderServerId: String, messageServerId: String)
fun syncFinished(folderServerId: String)
fun syncFailed(folderServerId: String, message: String, exception: Exception?)
fun folderStatusChanged(folderServerId: String)
}

View file

@ -0,0 +1,8 @@
package net.thunderbird.backend.api
import com.fsck.k9.backend.api.Backend
import net.thunderbird.feature.mail.account.api.BaseAccount
interface BackendFactory<TAccount : BaseAccount> {
fun createBackend(account: TAccount): Backend
}

View file

@ -0,0 +1,8 @@
package net.thunderbird.backend.api
import com.fsck.k9.backend.api.BackendStorage
import net.thunderbird.feature.mail.account.api.BaseAccount
interface BackendStorageFactory<in TAccount : BaseAccount> {
fun createBackendStorage(account: TAccount): BackendStorage
}

View file

@ -0,0 +1,68 @@
package net.thunderbird.backend.api.folder
import com.fsck.k9.mail.FolderType
import com.fsck.k9.mail.folders.FolderServerId
import net.thunderbird.core.outcome.Outcome
import net.thunderbird.feature.mail.account.api.BaseAccount
interface RemoteFolderCreator {
/**
* Creates a folder on the remote server. If the folder already exists and [mustCreate] is `false`,
* the operation will succeed returning [RemoteFolderCreationOutcome.Success.AlreadyExists].
*
* @param folderServerId The folder server ID.
* @param mustCreate If `true`, the folder must be created returning
* [RemoteFolderCreationOutcome.Error.FailedToCreateRemoteFolder]. If `false`, the folder will be created
* only if it doesn't exist.
* @param folderType The folder type. This requires special handling for some servers. Default [FolderType.REGULAR].
* @return The result of the operation.
* @see RemoteFolderCreationOutcome.Success
* @see RemoteFolderCreationOutcome.Error
*/
suspend fun create(
folderServerId: FolderServerId,
mustCreate: Boolean,
folderType: FolderType = FolderType.REGULAR,
): Outcome<RemoteFolderCreationOutcome.Success, RemoteFolderCreationOutcome.Error>
interface Factory {
fun create(account: BaseAccount): RemoteFolderCreator
}
}
sealed interface RemoteFolderCreationOutcome {
sealed interface Success : RemoteFolderCreationOutcome {
/**
* Used to flag that the folder was created successfully.
*/
data object Created : Success
/**
* Used to flag that the folder creation was skipped because the folder already exists and
* the creation is NOT mandatory.
*/
data object AlreadyExists : Success
}
sealed interface Error : RemoteFolderCreationOutcome {
/**
* Used to flag that the folder creation has failed because the folder already exists and
* the creation is mandatory.
*/
data object AlreadyExists : Error
/**
* Used to flag that the folder creation failed on the remote server.
* @param reason The reason why the folder creation failed.
*/
data class FailedToCreateRemoteFolder(
val reason: String,
) : Error
/**
* Used to flag that the Create Folder operation is not supported by the server.
* E.g. POP3 servers don't support creating archive folders.
*/
data object NotSupportedOperation : Error
}
}