diff --git a/build.gradle b/build.gradle index 777755f16..0bde9ec3d 100644 --- a/build.gradle +++ b/build.gradle @@ -45,8 +45,8 @@ android { defaultConfig { minSdkVersion 21 targetSdkVersion 28 - versionCode 52 - versionName "0.17.0" + versionCode 53 + versionName "0.17.1" applicationId "com.criptext.mail" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" multiDexEnabled true diff --git a/src/androidTest/kotlin/com/criptext/mail/scenes/mailbox/data/ResendEmailWorkerTest.kt b/src/androidTest/kotlin/com/criptext/mail/scenes/mailbox/data/ResendEmailWorkerTest.kt index b84a468a3..0933c452e 100644 --- a/src/androidTest/kotlin/com/criptext/mail/scenes/mailbox/data/ResendEmailWorkerTest.kt +++ b/src/androidTest/kotlin/com/criptext/mail/scenes/mailbox/data/ResendEmailWorkerTest.kt @@ -99,6 +99,7 @@ class ResendEmailWorkerTest { val bodyJSON = JSONObject(bodyString) val firstCriptextEmail = bodyJSON.getJSONArray("criptextEmails").getJSONObject(0) + .getJSONArray("emails").getJSONObject(0) val encryptedText = firstCriptextEmail.getString("body") return recipient.decrypt("tester", 1, diff --git a/src/androidTest/kotlin/com/criptext/mail/scenes/mailbox/data/SendEmailWorkerTest.kt b/src/androidTest/kotlin/com/criptext/mail/scenes/mailbox/data/SendEmailWorkerTest.kt index e472860db..57eebe57e 100644 --- a/src/androidTest/kotlin/com/criptext/mail/scenes/mailbox/data/SendEmailWorkerTest.kt +++ b/src/androidTest/kotlin/com/criptext/mail/scenes/mailbox/data/SendEmailWorkerTest.kt @@ -104,6 +104,7 @@ class SendEmailWorkerTest { val bodyJSON = JSONObject(bodyString) val firstCriptextEmail = bodyJSON.getJSONArray("criptextEmails").getJSONObject(0) + .getJSONArray("emails").getJSONObject(0) val encryptedText = firstCriptextEmail.getString("body") return recipient.decrypt("tester", 1, diff --git a/src/main/kotlin/com/criptext/mail/api/HttpClient.kt b/src/main/kotlin/com/criptext/mail/api/HttpClient.kt index 61cc28a6a..7d44d978d 100644 --- a/src/main/kotlin/com/criptext/mail/api/HttpClient.kt +++ b/src/main/kotlin/com/criptext/mail/api/HttpClient.kt @@ -269,7 +269,7 @@ interface HttpClient { } companion object { - const val API_VERSION = "6.0.0" + const val API_VERSION = "7.0.0" } } } diff --git a/src/main/kotlin/com/criptext/mail/push/PushController.kt b/src/main/kotlin/com/criptext/mail/push/PushController.kt index 0ce7ff999..d8890caaa 100644 --- a/src/main/kotlin/com/criptext/mail/push/PushController.kt +++ b/src/main/kotlin/com/criptext/mail/push/PushController.kt @@ -29,6 +29,7 @@ class PushController(private val dataSource: PushDataSource, private val host: M private val dataSourceListener = { result: PushResult -> when (result) { is PushResult.UpdateMailbox -> onUpdateMailbox(result) + is PushResult.NewEmail -> onNewEmail(result) } } @@ -97,7 +98,7 @@ class PushController(private val dataSource: PushDataSource, private val host: M fun parsePushPayload(pushData: Map, shouldPostNotification: Boolean) { if(shouldPostNotification) - dataSource.submitRequest(PushRequest.UpdateMailbox(Label.defaultItems.inbox, null, + dataSource.submitRequest(PushRequest.NewEmail(Label.defaultItems.inbox, pushData, shouldPostNotification)) } @@ -137,13 +138,20 @@ class PushController(private val dataSource: PushDataSource, private val host: M private fun onUpdateMailbox(result: PushResult.UpdateMailbox){ when(result){ - is PushResult.UpdateMailbox.Success -> { - createAndNotifyPush(result.pushData, result.shouldPostNotification, true, result.senderImage) - } is PushResult.UpdateMailbox.SuccessAndRepeat -> { parsePushPayload(result.pushData, result.shouldPostNotification) } - is PushResult.UpdateMailbox.Failure -> { + } + } + + private fun onNewEmail(result: PushResult.NewEmail){ + when(result){ + is PushResult.NewEmail.Success -> { + createAndNotifyPush(result.pushData, result.shouldPostNotification, true, result.senderImage) + dataSource.submitRequest(PushRequest.UpdateMailbox(Label.defaultItems.inbox, null, + result.pushData, result.shouldPostNotification)) + } + is PushResult.NewEmail.Failure -> { createAndNotifyPush(result.pushData, result.shouldPostNotification, false, null) } } diff --git a/src/main/kotlin/com/criptext/mail/push/data/PushDataSource.kt b/src/main/kotlin/com/criptext/mail/push/data/PushDataSource.kt index 5c42db4e8..0ee5cb39f 100644 --- a/src/main/kotlin/com/criptext/mail/push/data/PushDataSource.kt +++ b/src/main/kotlin/com/criptext/mail/push/data/PushDataSource.kt @@ -7,8 +7,9 @@ import com.criptext.mail.bgworker.WorkRunner import com.criptext.mail.db.AppDatabase import com.criptext.mail.db.EventLocalDB import com.criptext.mail.db.KeyValueStorage -import com.criptext.mail.db.models.Account import com.criptext.mail.db.models.ActiveAccount +import com.criptext.mail.push.workers.GetPushEmailWorker +import com.criptext.mail.push.workers.UpdateMailboxWorker import com.criptext.mail.signal.SignalClient import com.criptext.mail.signal.SignalStoreCriptext import java.io.File @@ -27,6 +28,17 @@ class PushDataSource( flushResults: (PushResult) -> Unit) : BackgroundWorker<*> { return when (params) { + is PushRequest.NewEmail -> GetPushEmailWorker( + signalClient = SignalClient.Default(SignalStoreCriptext(db)), + dbEvents = EventLocalDB(db, filesDir, cacheDir), + httpClient = httpClient, + activeAccount = activeAccount, + label = params.label, + pushData = params.pushData, + shouldPostNotification = params.shouldPostNotification, + publishFn = { result -> + flushResults(result) + }) is PushRequest.UpdateMailbox -> UpdateMailboxWorker( signalClient = SignalClient.Default(SignalStoreCriptext(db)), dbEvents = EventLocalDB(db, filesDir, cacheDir), diff --git a/src/main/kotlin/com/criptext/mail/push/data/PushRequest.kt b/src/main/kotlin/com/criptext/mail/push/data/PushRequest.kt index daf1c6122..ce3402f72 100644 --- a/src/main/kotlin/com/criptext/mail/push/data/PushRequest.kt +++ b/src/main/kotlin/com/criptext/mail/push/data/PushRequest.kt @@ -11,6 +11,11 @@ sealed class PushRequest{ val pushData: Map, val shouldPostNotification: Boolean): PushRequest() + data class NewEmail( + val label: Label, + val pushData: Map, + val shouldPostNotification: Boolean): PushRequest() + data class LinkAccept(val randomId: String, val notificationId: Int): PushRequest() data class LinkDenied(val randomId: String, val notificationId: Int): PushRequest() diff --git a/src/main/kotlin/com/criptext/mail/push/data/PushResult.kt b/src/main/kotlin/com/criptext/mail/push/data/PushResult.kt index 00a172e72..da50a26c6 100644 --- a/src/main/kotlin/com/criptext/mail/push/data/PushResult.kt +++ b/src/main/kotlin/com/criptext/mail/push/data/PushResult.kt @@ -48,6 +48,22 @@ sealed class PushResult { } } + sealed class NewEmail : PushResult() { + data class Success( + val mailboxLabel: Label, + val isManual: Boolean, + val pushData: Map, + val shouldPostNotification: Boolean, + val senderImage: Bitmap?): NewEmail() + + data class Failure( + val mailboxLabel: Label, + val message: UIMessage, + val exception: Exception?, + val pushData: Map, + val shouldPostNotification: Boolean): NewEmail() + } + sealed class LinkAccept: PushResult() { data class Success(val notificationId: Int): LinkAccept() data class Failure(val message: UIMessage): LinkAccept() diff --git a/src/main/kotlin/com/criptext/mail/push/workers/GetPushEmailWorker.kt b/src/main/kotlin/com/criptext/mail/push/workers/GetPushEmailWorker.kt new file mode 100644 index 000000000..8082d51e0 --- /dev/null +++ b/src/main/kotlin/com/criptext/mail/push/workers/GetPushEmailWorker.kt @@ -0,0 +1,211 @@ +package com.criptext.mail.push.workers + +import android.content.res.Resources +import com.criptext.mail.R +import com.criptext.mail.api.EmailInsertionAPIClient +import com.criptext.mail.api.Hosts +import com.criptext.mail.api.HttpClient +import com.criptext.mail.api.models.EmailMetadata +import com.criptext.mail.api.models.Event +import com.criptext.mail.bgworker.BackgroundWorker +import com.criptext.mail.bgworker.ProgressReporter +import com.criptext.mail.db.EventLocalDB +import com.criptext.mail.db.models.ActiveAccount +import com.criptext.mail.db.models.Label +import com.criptext.mail.push.data.PushResult +import com.criptext.mail.scenes.mailbox.data.MailboxAPIClient +import com.criptext.mail.signal.SignalClient +import com.criptext.mail.utils.EmailAddressUtils +import com.criptext.mail.utils.EventHelper +import com.criptext.mail.utils.EventLoader +import com.criptext.mail.utils.UIMessage +import com.github.kittinunf.result.Result +import com.github.kittinunf.result.flatMap +import com.squareup.picasso.Picasso +import org.whispersystems.libsignal.DuplicateMessageException +import java.io.IOException + + +class GetPushEmailWorker( + private val signalClient: SignalClient, + private val dbEvents: EventLocalDB, + private val activeAccount: ActiveAccount, + private val label: Label, + private val pushData: Map, + private val shouldPostNotification: Boolean, + httpClient: HttpClient, + override val publishFn: ( + PushResult.NewEmail) -> Unit) + : BackgroundWorker { + + + override val canBeParallelized = false + + private val apiClient = MailboxAPIClient(httpClient, activeAccount.jwt) + private val emailInsertionApiClient = EmailInsertionAPIClient(httpClient, activeAccount.jwt) + private val eventsToAcknowldege = mutableListOf() + + override fun catchException(ex: Exception): PushResult.NewEmail { + val message = createErrorMessage(ex) + return PushResult.NewEmail.Failure(label, message, ex, pushData, shouldPostNotification) + } + + private fun processFailure(failure: Result.Failure): PushResult.NewEmail { + return if (failure.error is EventHelper.NothingNewException) + PushResult.NewEmail.Success( + mailboxLabel = label, + isManual = true, + shouldPostNotification = shouldPostNotification, + pushData = pushData, + senderImage = null) + else + PushResult.NewEmail.Failure( + mailboxLabel = label, + message = createErrorMessage(failure.error), + exception = failure.error, + pushData = pushData, + shouldPostNotification = shouldPostNotification) + } + + override fun work(reporter: ProgressReporter) + : PushResult.NewEmail? { + + val rowId = pushData["rowId"]?.toInt() ?: return PushResult.NewEmail.Failure( + mailboxLabel = label, + message = createErrorMessage(EventHelper.NothingNewException()), + exception = EventHelper.NothingNewException(), + pushData = pushData, + shouldPostNotification = shouldPostNotification) + + val requestEvents = EventLoader.getEvent(apiClient, rowId) + val operationResult = requestEvents + .flatMap(processEvent) + + val newData = mutableMapOf() + newData.putAll(pushData) + + + return when(operationResult) { + is Result.Success -> { + val metadataKey = newData["metadataKey"]?.toLong() + if(metadataKey != null) { + val email = dbEvents.getEmailByMetadataKey(metadataKey) + if(email != null){ + val files = dbEvents.getFullEmailById(emailId = email.id)!!.files + newData["preview"] = email.preview + newData["subject"] = email.subject + newData["hasInlineImages"] = (files.firstOrNull { it.cid != null } != null).toString() + newData["name"] = dbEvents.getFromContactByEmailId(email.id)[0].name + newData["email"] = dbEvents.getFromContactByEmailId(email.id)[0].email + val emailAddress = newData["email"] + val bm = try { + if(emailAddress != null && EmailAddressUtils.isFromCriptextDomain(emailAddress)) + Picasso.get().load(Hosts.restApiBaseUrl + .plus("/user/avatar/${EmailAddressUtils.extractRecipientIdFromCriptextAddress(emailAddress)}")).get() + else + null + } catch (ex: Exception){ + null + } + PushResult.NewEmail.Success( + mailboxLabel = label, + isManual = true, + pushData = newData, + shouldPostNotification = shouldPostNotification, + senderImage = bm + ) + }else{ + PushResult.NewEmail.Failure( + mailboxLabel = label, + message = createErrorMessage(Resources.NotFoundException()), + exception = Resources.NotFoundException(), + pushData = pushData, + shouldPostNotification = shouldPostNotification) + } + }else { + PushResult.NewEmail.Failure( + mailboxLabel = label, + message = createErrorMessage(Resources.NotFoundException()), + exception = Resources.NotFoundException(), + pushData = pushData, + shouldPostNotification = shouldPostNotification) + } + } + + is Result.Failure -> processFailure(operationResult) + } + } + + val processEvent: (Event) -> Result = { event -> + Result.of { + val shouldReload = processNewEmails(event) + shouldReload.or(acknowledgeEventsIgnoringErrors(eventsToAcknowldege)) + } + } + + private fun processNewEmails(event: Event): Boolean { + val toIdAndMetadataPair: (Event) -> Pair = + { Pair( it.rowid, EmailMetadata.fromJSON(it.params)) } + val emailInsertedSuccessfully: (Pair) -> Boolean = + { (_, metadata) -> + try { + insertIncomingEmailTransaction(metadata) + // insertion success, try to acknowledge it + true + } catch (ex: DuplicateMessageException) { + // duplicated, try to acknowledge it + true + } + catch (ex: Exception) { + // Unknown exception, probably network related, skip acknowledge + if(ex is DuplicateMessageException){ + updateExistingEmailTransaction(metadata) + } + ex is DuplicateMessageException + } + } + val toEventId: (Pair) -> Long = + { (eventId, _) -> eventId } + + val eventIdsToAcknowledge = listOf(event) + .map(toIdAndMetadataPair) + .filter(emailInsertedSuccessfully) + .map(toEventId) + + if (eventIdsToAcknowledge.isNotEmpty()) + eventsToAcknowldege.addAll(eventIdsToAcknowledge) + + return eventIdsToAcknowledge.isNotEmpty() + } + + override fun cancel() { + TODO("CANCEL IS NOT IMPLEMENTED") + } + + private fun insertIncomingEmailTransaction(metadata: EmailMetadata) = + dbEvents.insertIncomingEmail(signalClient, emailInsertionApiClient, metadata, activeAccount) + + private fun updateExistingEmailTransaction(metadata: EmailMetadata) = + dbEvents.updateExistingEmail(metadata, activeAccount) + + private fun acknowledgeEventsIgnoringErrors(eventIdsToAcknowledge: List): Boolean { + try { + if(eventIdsToAcknowledge.isNotEmpty()) + apiClient.acknowledgeEvents(eventIdsToAcknowledge) + } catch (ex: IOException) { + // if this request fails, just ignore it, we can acknowledge again later + } + return eventIdsToAcknowledge.isNotEmpty() + } + + private val createErrorMessage: (ex: Exception) -> UIMessage = { ex -> + when(ex) { + is DuplicateMessageException -> + UIMessage(resId = R.string.email_already_decrypted) + else -> { + UIMessage(resId = R.string.failed_getting_emails) + } + } + } +} diff --git a/src/main/kotlin/com/criptext/mail/push/data/UpdateMailboxWorker.kt b/src/main/kotlin/com/criptext/mail/push/workers/UpdateMailboxWorker.kt similarity index 97% rename from src/main/kotlin/com/criptext/mail/push/data/UpdateMailboxWorker.kt rename to src/main/kotlin/com/criptext/mail/push/workers/UpdateMailboxWorker.kt index 21746a927..0b854ad67 100644 --- a/src/main/kotlin/com/criptext/mail/push/data/UpdateMailboxWorker.kt +++ b/src/main/kotlin/com/criptext/mail/push/workers/UpdateMailboxWorker.kt @@ -1,9 +1,8 @@ -package com.criptext.mail.push.data +package com.criptext.mail.push.workers import android.content.res.Resources import com.criptext.mail.R import com.criptext.mail.api.HttpClient -import com.criptext.mail.api.models.DeviceInfo import com.criptext.mail.bgworker.BackgroundWorker import com.criptext.mail.bgworker.ProgressReporter import com.criptext.mail.db.EventLocalDB @@ -12,15 +11,14 @@ import com.criptext.mail.db.models.ActiveAccount import com.criptext.mail.db.models.Label import com.criptext.mail.email_preview.EmailPreview import com.criptext.mail.scenes.mailbox.data.MailboxAPIClient -import com.criptext.mail.scenes.mailbox.data.UpdateBannerData import com.criptext.mail.signal.SignalClient import com.github.kittinunf.result.Result import com.github.kittinunf.result.flatMap import org.whispersystems.libsignal.DuplicateMessageException -import java.io.File import com.squareup.picasso.Picasso import android.graphics.Bitmap import com.criptext.mail.api.Hosts +import com.criptext.mail.push.data.PushResult import com.criptext.mail.utils.* diff --git a/src/main/kotlin/com/criptext/mail/scenes/composer/data/PostEmailBody.kt b/src/main/kotlin/com/criptext/mail/scenes/composer/data/PostEmailBody.kt index a00b7e6b4..434aeb189 100644 --- a/src/main/kotlin/com/criptext/mail/scenes/composer/data/PostEmailBody.kt +++ b/src/main/kotlin/com/criptext/mail/scenes/composer/data/PostEmailBody.kt @@ -45,21 +45,39 @@ class PostEmailBody(val threadId: String?, val subject: String, } } - data class CriptextEmail(val recipientId: String, val deviceId: Int, - val messageType: SignalEncryptedData.Type, - val type: RecipientTypes, val body: String, val fileKey: String?, - val fileKeys: List?): JSONData { + abstract class CriptextEmail: JSONData + + data class CompleteCriptextEmail(val recipientId: String, val deviceId: Int, + val messageType: SignalEncryptedData.Type, + val type: RecipientTypes, val body: String, val fileKey: String?, + val fileKeys: List?, val preview: String, + val previewMessageType: SignalEncryptedData.Type): CriptextEmail() { override fun toJSON(): JSONObject { val json = JSONObject() + val jsonOuter = JSONObject() json.put("recipientId", recipientId) json.put("deviceId", deviceId) json.put("type", type.toString()) json.put("body", body) + json.put("preview", preview) + json.put("previewMessageType", previewMessageType.toInt()) json.put("messageType", messageType.toInt()) json.put("fileKey", fileKey) json.put("fileKeys", JSONArray(fileKeys)) - return json + jsonOuter.put("username", recipientId) + jsonOuter.put("emails", JSONArray().put(json)) + return jsonOuter + } + } + + data class EmptyCriptextEmail(val recipientId: String): CriptextEmail() { + + override fun toJSON(): JSONObject { + val jsonOuter = JSONObject() + jsonOuter.put("username", recipientId) + jsonOuter.put("emails", JSONArray()) + return jsonOuter } } diff --git a/src/main/kotlin/com/criptext/mail/scenes/mailbox/MailboxSceneController.kt b/src/main/kotlin/com/criptext/mail/scenes/mailbox/MailboxSceneController.kt index 8579c2b8b..2a603a510 100644 --- a/src/main/kotlin/com/criptext/mail/scenes/mailbox/MailboxSceneController.kt +++ b/src/main/kotlin/com/criptext/mail/scenes/mailbox/MailboxSceneController.kt @@ -547,6 +547,7 @@ class MailboxSceneController(private val scene: MailboxScene, dataSource.submitRequest(MailboxRequest.UpdateUnreadStatus( threadIds, !unreadStatus, model.selectedLabel)) changeMode(multiSelectON = false, silent = false) + threadListController.changeThreadReadStatus(threadIds, !unreadStatus) } private fun showMultiModeBar() { @@ -875,7 +876,7 @@ class MailboxSceneController(private val scene: MailboxScene, fun onUpdateUnreadStatus(result: MailboxResult){ when (result) { is MailboxResult.UpdateUnreadStatus.Success -> { - threadListController.changeThreadReadStatus(result.threadId, result.unreadStatus) + //threadListController.changeThreadReadStatus(result.threadId, result.unreadStatus) generalDataSource.submitRequest(GeneralRequest.TotalUnreadEmails(model.selectedLabel.text)) dataSource.submitRequest(MailboxRequest.GetMenuInformation()) } diff --git a/src/main/kotlin/com/criptext/mail/scenes/mailbox/data/MailboxAPIClient.kt b/src/main/kotlin/com/criptext/mail/scenes/mailbox/data/MailboxAPIClient.kt index b28c0ccad..59ab47a18 100644 --- a/src/main/kotlin/com/criptext/mail/scenes/mailbox/data/MailboxAPIClient.kt +++ b/src/main/kotlin/com/criptext/mail/scenes/mailbox/data/MailboxAPIClient.kt @@ -19,6 +19,12 @@ class MailboxAPIClient(private val httpClient: HttpClient, var token: String): C path = "/event") } + fun getPendingEvent(rowId: Int): HttpResponseData { + return httpClient.get( + authToken = token, + path = "/event/$rowId") + } + fun insertPreKeys(preKeys: Map, excludedKeys: List): HttpResponseData { val jsonObject = JSONObject() val preKeyArray = JSONArray() diff --git a/src/main/kotlin/com/criptext/mail/scenes/mailbox/workers/ResendEmailsWorker.kt b/src/main/kotlin/com/criptext/mail/scenes/mailbox/workers/ResendEmailsWorker.kt index 59e96a91a..f09f6500b 100644 --- a/src/main/kotlin/com/criptext/mail/scenes/mailbox/workers/ResendEmailsWorker.kt +++ b/src/main/kotlin/com/criptext/mail/scenes/mailbox/workers/ResendEmailsWorker.kt @@ -24,10 +24,8 @@ import com.criptext.mail.utils.* import com.github.kittinunf.result.Result import com.github.kittinunf.result.flatMap import com.github.kittinunf.result.mapError -import org.json.JSONArray import org.json.JSONObject import org.whispersystems.libsignal.DuplicateMessageException -import org.whispersystems.libsignal.SignalProtocolAddress import java.io.File class ResendEmailsWorker( @@ -236,17 +234,29 @@ class ResendEmailsWorker( if (devices == null || devices.isEmpty()) { if (type == PostEmailBody.RecipientTypes.peer) return emptyList() - throw IllegalArgumentException("Signal address for '$recipientId' does not exist in the store") + return listOf(PostEmailBody.EmptyCriptextEmail(recipientId)) } devices.filter { deviceId -> type != PostEmailBody.RecipientTypes.peer || deviceId != activeAccount.deviceId }.map { deviceId -> - val encryptedData = signalClient.encryptMessage(recipientId, deviceId, fullEmail.email.content) - PostEmailBody.CriptextEmail(recipientId = recipientId, deviceId = deviceId, - type = type, body = encryptedData.encryptedB64, - messageType = encryptedData.type, fileKey = if(getFileKey(fullEmail.fileKey, fullEmail.files) != null) - signalClient.encryptMessage(recipientId, deviceId, getFileKey(fullEmail.fileKey, fullEmail.files)!!).encryptedB64 - else null, fileKeys = getEncryptedFileKeys(fullEmail, recipientId, deviceId)) + val encryptOperation = Result.of { + val encryptedData = signalClient.encryptMessage(recipientId, deviceId, fullEmail.email.content) + val encryptedPreview = signalClient.encryptMessage(recipientId, deviceId, fullEmail.email.preview) + Pair(encryptedData, encryptedPreview) + } + when(encryptOperation){ + is Result.Success -> { + PostEmailBody.CompleteCriptextEmail(recipientId = recipientId, deviceId = deviceId, + type = type, body = encryptOperation.value.first.encryptedB64, + messageType = encryptOperation.value.first.type, fileKey = if(getFileKey(fullEmail.fileKey, fullEmail.files) != null) + signalClient.encryptMessage(recipientId, deviceId, getFileKey(fullEmail.fileKey, fullEmail.files)!!).encryptedB64 + else null, fileKeys = getEncryptedFileKeys(fullEmail, recipientId, deviceId), + preview = encryptOperation.value.second.encryptedB64, previewMessageType = encryptOperation.value.second.type) + } + is Result.Failure -> { + PostEmailBody.EmptyCriptextEmail(recipientId) + } + } } }.flatten() } diff --git a/src/main/kotlin/com/criptext/mail/scenes/mailbox/workers/SendMailWorker.kt b/src/main/kotlin/com/criptext/mail/scenes/mailbox/workers/SendMailWorker.kt index 8b6badbd0..bf619f27f 100644 --- a/src/main/kotlin/com/criptext/mail/scenes/mailbox/workers/SendMailWorker.kt +++ b/src/main/kotlin/com/criptext/mail/scenes/mailbox/workers/SendMailWorker.kt @@ -114,17 +114,33 @@ class SendMailWorker(private val signalClient: SignalClient, if (devices == null || devices.isEmpty()) { if (type == PostEmailBody.RecipientTypes.peer) return emptyList() - throw IllegalArgumentException("Signal address for '$recipientId' does not exist in the store") + return listOf(PostEmailBody.EmptyCriptextEmail(recipientId)) } devices.filter { deviceId -> type != PostEmailBody.RecipientTypes.peer || deviceId != activeAccount.deviceId }.map { deviceId -> - val encryptedData = signalClient.encryptMessage(recipientId, deviceId, composerInputData.body) - PostEmailBody.CriptextEmail(recipientId = recipientId, deviceId = deviceId, - type = type, body = encryptedData.encryptedB64, - messageType = encryptedData.type, fileKey = if(getFileKey() != null) - signalClient.encryptMessage(recipientId, deviceId, getFileKey()!!).encryptedB64 - else null, fileKeys = getEncryptedFileKeys(recipientId, deviceId)) + val encryptOperation = Result.of { + val encryptedData = signalClient.encryptMessage(recipientId, deviceId, composerInputData.body) + val email = db.getEmailById(emailId)!! + val encryptedPreview = signalClient.encryptMessage(recipientId, deviceId, email.preview) + Pair(encryptedData, encryptedPreview) + } + when(encryptOperation){ + is Result.Success -> { + PostEmailBody.CompleteCriptextEmail(recipientId = recipientId, deviceId = deviceId, + type = type, body = encryptOperation.value.first.encryptedB64, + messageType = encryptOperation.value.first.type, fileKey = if(getFileKey() != null) + signalClient.encryptMessage(recipientId, deviceId, getFileKey()!!).encryptedB64 + else null, fileKeys = getEncryptedFileKeys(recipientId, deviceId), + preview = encryptOperation.value.second.encryptedB64, + previewMessageType = encryptOperation.value.second.type) + } + is Result.Failure -> { + PostEmailBody.EmptyCriptextEmail(recipientId) + } + } + + } }.flatten() } diff --git a/src/main/kotlin/com/criptext/mail/scenes/signin/SignInSceneController.kt b/src/main/kotlin/com/criptext/mail/scenes/signin/SignInSceneController.kt index c15208a5c..6600fa405 100644 --- a/src/main/kotlin/com/criptext/mail/scenes/signin/SignInSceneController.kt +++ b/src/main/kotlin/com/criptext/mail/scenes/signin/SignInSceneController.kt @@ -115,7 +115,7 @@ class SignInSceneController( scene.drawInputError(UIMessage(R.string.username_doesnt_exist)) } } - is SignInResult.CheckUsernameAvailability.Failure -> scene.showError(UIMessage(R.string.error_checking_availability)) + is SignInResult.CheckUsernameAvailability.Failure -> scene.showError(UIMessage(R.string.forgot_password_error)) } scene.setSubmitButtonState(ProgressButtonState.enabled) } diff --git a/src/main/kotlin/com/criptext/mail/utils/EventLoader.kt b/src/main/kotlin/com/criptext/mail/utils/EventLoader.kt index bd7ffe3e6..fbfb62587 100644 --- a/src/main/kotlin/com/criptext/mail/utils/EventLoader.kt +++ b/src/main/kotlin/com/criptext/mail/utils/EventLoader.kt @@ -6,6 +6,7 @@ import com.criptext.mail.scenes.mailbox.data.MailboxAPIClient import com.github.kittinunf.result.Result import com.github.kittinunf.result.flatMap import org.json.JSONArray +import org.json.JSONObject object EventLoader{ @@ -14,6 +15,18 @@ object EventLoader{ .flatMap(parseEvents) } + fun getEvent(apiClient: MailboxAPIClient, rowId: Int): Result{ + return fetchPendingEvent(apiClient, rowId) + .flatMap(parseEvent) + } + + private fun fetchPendingEvent(apiClient: MailboxAPIClient, rowId: Int): Result { + return Result.of { + val responseText = apiClient.getPendingEvent(rowId) + if (responseText.body.isEmpty()) HttpResponseData(ServerCodes.Success, "[]") else responseText + } + } + private fun fetchPendingEvents(apiClient: MailboxAPIClient): Result { return Result.of { val responseText = apiClient.getPendingEvents() @@ -21,6 +34,15 @@ object EventLoader{ } } + private val parseEvent: (HttpResponseData) -> Result = { responseData -> + Result.of { + val eventJSONString = JSONObject(responseData.body).toString() + if (eventJSONString.isNotEmpty()) { + Event.fromJSON(eventJSONString) + } else throw Exception() + } + } + private val parseEvents: (HttpResponseData) -> Result, Boolean>, Exception> = { responseData -> Result.of { val eventsJSONArray = JSONArray(responseData.body) diff --git a/src/main/kotlin/com/criptext/mail/utils/generaldatasource/workers/ResendEmailWorker.kt b/src/main/kotlin/com/criptext/mail/utils/generaldatasource/workers/ResendEmailWorker.kt index c39b38f8f..84809326a 100644 --- a/src/main/kotlin/com/criptext/mail/utils/generaldatasource/workers/ResendEmailWorker.kt +++ b/src/main/kotlin/com/criptext/mail/utils/generaldatasource/workers/ResendEmailWorker.kt @@ -15,7 +15,6 @@ import com.criptext.mail.db.dao.signal.RawSessionDao import com.criptext.mail.db.models.* import com.criptext.mail.scenes.composer.data.ComposerAPIClient import com.criptext.mail.scenes.composer.data.PostEmailBody -import com.criptext.mail.scenes.mailbox.data.MailboxResult import com.criptext.mail.scenes.mailbox.data.SentMailData import com.criptext.mail.signal.PreKeyBundleShareData import com.criptext.mail.signal.SignalClient @@ -25,10 +24,8 @@ import com.criptext.mail.utils.generaldatasource.data.GeneralResult import com.github.kittinunf.result.Result import com.github.kittinunf.result.flatMap import com.github.kittinunf.result.mapError -import org.json.JSONArray import org.json.JSONObject import org.whispersystems.libsignal.DuplicateMessageException -import org.whispersystems.libsignal.SignalProtocolAddress import java.io.File class ResendEmailWorker( @@ -240,17 +237,29 @@ class ResendEmailWorker( if (devices == null || devices.isEmpty()) { if (type == PostEmailBody.RecipientTypes.peer) return emptyList() - throw IllegalArgumentException("Signal address for '$recipientId' does not exist in the store") + return listOf(PostEmailBody.EmptyCriptextEmail(recipientId)) } devices.filter { deviceId -> type != PostEmailBody.RecipientTypes.peer || deviceId != activeAccount.deviceId }.map { deviceId -> - val encryptedData = signalClient.encryptMessage(recipientId, deviceId, fullEmail.email.content) - PostEmailBody.CriptextEmail(recipientId = recipientId, deviceId = deviceId, - type = type, body = encryptedData.encryptedB64, - messageType = encryptedData.type, fileKey = if(getFileKey(fullEmail.fileKey, fullEmail.files) != null) - signalClient.encryptMessage(recipientId, deviceId, getFileKey(fullEmail.fileKey, fullEmail.files)!!).encryptedB64 - else null, fileKeys = getEncryptedFileKeys(fullEmail, recipientId, deviceId)) + val encryptOperation = Result.of { + val encryptedData = signalClient.encryptMessage(recipientId, deviceId, fullEmail.email.content) + val encryptedPreview = signalClient.encryptMessage(recipientId, deviceId, fullEmail.email.preview) + Pair(encryptedData, encryptedPreview) + } + when(encryptOperation){ + is Result.Success -> { + PostEmailBody.CompleteCriptextEmail(recipientId = recipientId, deviceId = deviceId, + type = type, body = encryptOperation.value.first.encryptedB64, + messageType = encryptOperation.value.first.type, fileKey = if(getFileKey(fullEmail.fileKey, fullEmail.files) != null) + signalClient.encryptMessage(recipientId, deviceId, getFileKey(fullEmail.fileKey, fullEmail.files)!!).encryptedB64 + else null, fileKeys = getEncryptedFileKeys(fullEmail, recipientId, deviceId), + preview = encryptOperation.value.second.encryptedB64, previewMessageType = encryptOperation.value.second.type) + } + is Result.Failure -> { + PostEmailBody.EmptyCriptextEmail(recipientId) + } + } } }.flatten() }