JMAP: Add support for uploading messages
This commit is contained in:
parent
c5cf5da746
commit
2a8d094343
6 changed files with 118 additions and 4 deletions
|
@ -23,7 +23,7 @@ dependencies {
|
|||
implementation "androidx.fragment:fragment:${versions.androidxFragment}"
|
||||
implementation "androidx.localbroadcastmanager:localbroadcastmanager:${versions.androidxLocalBroadcastManager}"
|
||||
implementation "org.jsoup:jsoup:1.11.2"
|
||||
implementation "com.squareup.moshi:moshi:1.9.2"
|
||||
implementation "com.squareup.moshi:moshi:${versions.moshi}"
|
||||
implementation "com.jakewharton.timber:timber:${versions.timber}"
|
||||
implementation "org.apache.james:apache-mime4j-core:${versions.mime4j}"
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'org.jetbrains.kotlin.android'
|
||||
apply plugin: 'org.jlleitschuh.gradle.ktlint'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
if (rootProject.testCoverage) {
|
||||
apply plugin: 'jacoco'
|
||||
|
@ -12,8 +13,10 @@ dependencies {
|
|||
api project(":backend:api")
|
||||
|
||||
api "com.squareup.okhttp3:okhttp:${versions.okhttp}"
|
||||
implementation "rs.ltt.jmap:jmap-client:0.3.0"
|
||||
implementation "rs.ltt.jmap:jmap-client:0.3.1"
|
||||
implementation "com.jakewharton.timber:timber:${versions.timber}"
|
||||
implementation "com.squareup.moshi:moshi:${versions.moshi}"
|
||||
kapt "com.squareup.moshi:moshi-kotlin-codegen:${versions.moshi}"
|
||||
|
||||
testImplementation project(":mail:testing")
|
||||
testImplementation "org.mockito:mockito-core:${versions.mockito}"
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
package com.fsck.k9.backend.jmap
|
||||
|
||||
import com.fsck.k9.mail.Message
|
||||
import com.fsck.k9.mail.MessagingException
|
||||
import com.squareup.moshi.Moshi
|
||||
import okhttp3.MediaType
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody
|
||||
import okio.BufferedSink
|
||||
import rs.ltt.jmap.client.JmapClient
|
||||
import rs.ltt.jmap.client.http.HttpAuthentication
|
||||
import rs.ltt.jmap.common.entity.EmailImport
|
||||
import rs.ltt.jmap.common.method.call.email.ImportEmailMethodCall
|
||||
import rs.ltt.jmap.common.method.response.email.ImportEmailMethodResponse
|
||||
import timber.log.Timber
|
||||
|
||||
class CommandUpload(
|
||||
private val jmapClient: JmapClient,
|
||||
private val okHttpClient: OkHttpClient,
|
||||
private val httpAuthentication: HttpAuthentication,
|
||||
private val accountId: String
|
||||
) {
|
||||
private val moshi = Moshi.Builder().build()
|
||||
|
||||
fun uploadMessage(folderServerId: String, message: Message): String? {
|
||||
Timber.d("Uploading message to $folderServerId")
|
||||
|
||||
val uploadResponse = uploadMessageAsBlob(message)
|
||||
return importEmailBlob(uploadResponse, folderServerId)
|
||||
}
|
||||
|
||||
private fun uploadMessageAsBlob(message: Message): JmapUploadResponse {
|
||||
val session = jmapClient.session.get()
|
||||
val uploadUrl = session.getUploadUrl(accountId)
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(uploadUrl)
|
||||
.post(MessageRequestBody(message))
|
||||
.apply {
|
||||
httpAuthentication.authenticate(this)
|
||||
}
|
||||
.build()
|
||||
|
||||
return okHttpClient.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) {
|
||||
throw MessagingException("Uploading message as blob failed")
|
||||
}
|
||||
|
||||
response.body!!.source().use { source ->
|
||||
val adapter = moshi.adapter(JmapUploadResponse::class.java)
|
||||
val uploadResponse = adapter.fromJson(source)
|
||||
uploadResponse ?: throw MessagingException("Error reading upload response")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun importEmailBlob(uploadResponse: JmapUploadResponse, folderServerId: String): String? {
|
||||
val importEmailRequest = ImportEmailMethodCall.builder()
|
||||
.accountId(accountId)
|
||||
.email(
|
||||
LOCAL_EMAIL_ID,
|
||||
EmailImport.builder()
|
||||
.blobId(uploadResponse.blobId)
|
||||
.keywords(mapOf("\$seen" to true))
|
||||
.mailboxIds(mapOf(folderServerId to true))
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
|
||||
val importEmailCall = jmapClient.call(importEmailRequest)
|
||||
val importEmailResponse = importEmailCall.getMainResponseBlocking<ImportEmailMethodResponse>()
|
||||
|
||||
return importEmailResponse.serverEmailId
|
||||
}
|
||||
|
||||
private val ImportEmailMethodResponse.serverEmailId
|
||||
get() = created?.get(LOCAL_EMAIL_ID)?.id
|
||||
|
||||
companion object {
|
||||
private const val LOCAL_EMAIL_ID = "t1"
|
||||
}
|
||||
}
|
||||
|
||||
private class MessageRequestBody(private val message: Message) : RequestBody() {
|
||||
override fun contentType(): MediaType? {
|
||||
return "message/rfc822".toMediaType()
|
||||
}
|
||||
|
||||
override fun contentLength(): Long {
|
||||
return message.calculateSize()
|
||||
}
|
||||
|
||||
override fun writeTo(sink: BufferedSink) {
|
||||
message.writeTo(sink.outputStream())
|
||||
}
|
||||
}
|
|
@ -31,6 +31,7 @@ class JmapBackend(
|
|||
private val commandSetFlag = CommandSetFlag(jmapClient, accountId)
|
||||
private val commandDelete = CommandDelete(jmapClient, accountId)
|
||||
private val commandMove = CommandMove(jmapClient, accountId)
|
||||
private val commandUpload = CommandUpload(jmapClient, okHttpClient, httpAuthentication, accountId)
|
||||
override val supportsSeenFlag = true
|
||||
override val supportsExpunge = false
|
||||
override val supportsMove = true
|
||||
|
@ -113,11 +114,11 @@ class JmapBackend(
|
|||
}
|
||||
|
||||
override fun findByMessageId(folderServerId: String, messageId: String): String? {
|
||||
throw UnsupportedOperationException("not implemented")
|
||||
return null
|
||||
}
|
||||
|
||||
override fun uploadMessage(folderServerId: String, message: Message): String? {
|
||||
throw UnsupportedOperationException("not implemented")
|
||||
return commandUpload.uploadMessage(folderServerId, message)
|
||||
}
|
||||
|
||||
override fun createPusher(receiver: PushReceiver): Pusher {
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package com.fsck.k9.backend.jmap
|
||||
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class JmapUploadResponse(
|
||||
val accountId: String,
|
||||
val blobId: String,
|
||||
val type: String,
|
||||
val size: Long
|
||||
)
|
|
@ -23,6 +23,7 @@ buildscript {
|
|||
'materialComponents': '1.1.0',
|
||||
'preferencesFix': '1.1.0',
|
||||
'okio': '2.4.3',
|
||||
'moshi': '1.9.2',
|
||||
'timber': '4.5.1',
|
||||
'koin': '2.0.1',
|
||||
'commonsIo': '2.6',
|
||||
|
|
Loading…
Reference in a new issue