Skip to content

Commit

Permalink
Implemented GetEvent on push noification. Now sending email preview e…
Browse files Browse the repository at this point in the history
…ncrypted, also some UI tweaks.
  • Loading branch information
jorgeblacio committed Mar 12, 2019
1 parent 3555ea5 commit a58cca0
Show file tree
Hide file tree
Showing 18 changed files with 380 additions and 46 deletions.
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/com/criptext/mail/api/HttpClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ interface HttpClient {
}

companion object {
const val API_VERSION = "6.0.0"
const val API_VERSION = "7.0.0"
}
}
}
18 changes: 13 additions & 5 deletions src/main/kotlin/com/criptext/mail/push/PushController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand Down Expand Up @@ -97,7 +98,7 @@ class PushController(private val dataSource: PushDataSource, private val host: M

fun parsePushPayload(pushData: Map<String, String>, shouldPostNotification: Boolean) {
if(shouldPostNotification)
dataSource.submitRequest(PushRequest.UpdateMailbox(Label.defaultItems.inbox, null,
dataSource.submitRequest(PushRequest.NewEmail(Label.defaultItems.inbox,
pushData, shouldPostNotification))
}

Expand Down Expand Up @@ -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)
}
}
Expand Down
14 changes: 13 additions & 1 deletion src/main/kotlin/com/criptext/mail/push/data/PushDataSource.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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),
Expand Down
5 changes: 5 additions & 0 deletions src/main/kotlin/com/criptext/mail/push/data/PushRequest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ sealed class PushRequest{
val pushData: Map<String, String>,
val shouldPostNotification: Boolean): PushRequest()

data class NewEmail(
val label: Label,
val pushData: Map<String, String>,
val shouldPostNotification: Boolean): PushRequest()

data class LinkAccept(val randomId: String, val notificationId: Int): PushRequest()
data class LinkDenied(val randomId: String, val notificationId: Int): PushRequest()

Expand Down
16 changes: 16 additions & 0 deletions src/main/kotlin/com/criptext/mail/push/data/PushResult.kt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,22 @@ sealed class PushResult {
}
}

sealed class NewEmail : PushResult() {
data class Success(
val mailboxLabel: Label,
val isManual: Boolean,
val pushData: Map<String, String>,
val shouldPostNotification: Boolean,
val senderImage: Bitmap?): NewEmail()

data class Failure(
val mailboxLabel: Label,
val message: UIMessage,
val exception: Exception?,
val pushData: Map<String, String>,
val shouldPostNotification: Boolean): NewEmail()
}

sealed class LinkAccept: PushResult() {
data class Success(val notificationId: Int): LinkAccept()
data class Failure(val message: UIMessage): LinkAccept()
Expand Down
211 changes: 211 additions & 0 deletions src/main/kotlin/com/criptext/mail/push/workers/GetPushEmailWorker.kt
Original file line number Diff line number Diff line change
@@ -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<String, String>,
private val shouldPostNotification: Boolean,
httpClient: HttpClient,
override val publishFn: (
PushResult.NewEmail) -> Unit)
: BackgroundWorker<PushResult.NewEmail> {


override val canBeParallelized = false

private val apiClient = MailboxAPIClient(httpClient, activeAccount.jwt)
private val emailInsertionApiClient = EmailInsertionAPIClient(httpClient, activeAccount.jwt)
private val eventsToAcknowldege = mutableListOf<Long>()

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<Boolean,
Exception>): 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>)
: 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<String, String>()
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<Boolean, Exception> = { event ->
Result.of {
val shouldReload = processNewEmails(event)
shouldReload.or(acknowledgeEventsIgnoringErrors(eventsToAcknowldege))
}
}

private fun processNewEmails(event: Event): Boolean {
val toIdAndMetadataPair: (Event) -> Pair<Long, EmailMetadata> =
{ Pair( it.rowid, EmailMetadata.fromJSON(it.params)) }
val emailInsertedSuccessfully: (Pair<Long, EmailMetadata>) -> 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, EmailMetadata>) -> 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<Long>): 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)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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.*


Expand Down
Loading

0 comments on commit a58cca0

Please sign in to comment.