forked from GitHub-Mirror/riotX-android
Merge pull request #460 from vector-im/feature/fix_cancellations
Feature/fix cancellations
This commit is contained in:
commit
dae8b5c196
@ -8,6 +8,8 @@ Improvements:
|
||||
- UI for pending edits (#193)
|
||||
- UX image preview screen transition (#393)
|
||||
- Basic support for resending failed messages (retry/remove)
|
||||
- Enable proper cancellation of suspending functions (including db transaction)
|
||||
- Enhances network connectivity checks in SDK
|
||||
|
||||
Other changes:
|
||||
-
|
||||
@ -17,6 +19,7 @@ Bugfix:
|
||||
- Close detail room screen when the room is left with another client (#256)
|
||||
- Clear notification for a room left on another client
|
||||
- Fix messages with empty `in_reply_to` not rendering (#447)
|
||||
- Fix clear cache (#408) and Logout (#205 )
|
||||
|
||||
Translations:
|
||||
-
|
||||
|
@ -28,7 +28,7 @@ internal class MatrixCallbackCompletable<T>(private val completableEmitter: Comp
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
completableEmitter.onError(failure)
|
||||
completableEmitter.tryOnError(failure)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ internal class MatrixCallbackSingle<T>(private val singleEmitter: SingleEmitter<
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
singleEmitter.onError(failure)
|
||||
singleEmitter.tryOnError(failure)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,7 @@ class RxRoom(private val room: Room) {
|
||||
return room.liveTimeLineEvent(eventId).asObservable()
|
||||
}
|
||||
|
||||
fun loadRoomMembersIfNeeded(): Single<Boolean> = Single.create {
|
||||
fun loadRoomMembersIfNeeded(): Single<Unit> = Single.create {
|
||||
room.loadRoomMembersIfNeeded(MatrixCallbackSingle(it)).toSingle(it)
|
||||
}
|
||||
|
||||
|
@ -110,7 +110,7 @@ dependencies {
|
||||
implementation 'com.squareup.retrofit2:converter-moshi:2.4.0'
|
||||
implementation 'com.squareup.okhttp3:okhttp:3.14.1'
|
||||
implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
|
||||
implementation 'com.novoda:merlin:1.1.6'
|
||||
implementation 'com.novoda:merlin:1.2.0'
|
||||
implementation "com.squareup.moshi:moshi-adapters:$moshi_version"
|
||||
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version"
|
||||
|
||||
@ -126,9 +126,6 @@ dependencies {
|
||||
// FP
|
||||
implementation "io.arrow-kt:arrow-core:$arrow_version"
|
||||
implementation "io.arrow-kt:arrow-instances-core:$arrow_version"
|
||||
implementation "io.arrow-kt:arrow-effects:$arrow_version"
|
||||
implementation "io.arrow-kt:arrow-effects-instances:$arrow_version"
|
||||
implementation "io.arrow-kt:arrow-integration-retrofit-adapter:$arrow_version"
|
||||
|
||||
// olm lib is now hosted by jitpack: https://jitpack.io/#org.matrix.gitlab.matrix-org/olm
|
||||
implementation 'org.matrix.gitlab.matrix-org:olm:3.1.2'
|
||||
|
@ -16,10 +16,10 @@
|
||||
|
||||
package im.vector.matrix.android;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
@ -19,11 +19,7 @@ package im.vector.matrix.android.session.room.timeline
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.InstrumentedTest
|
||||
import im.vector.matrix.android.internal.database.helper.add
|
||||
import im.vector.matrix.android.internal.database.helper.addAll
|
||||
import im.vector.matrix.android.internal.database.helper.isUnlinked
|
||||
import im.vector.matrix.android.internal.database.helper.lastStateIndex
|
||||
import im.vector.matrix.android.internal.database.helper.merge
|
||||
import im.vector.matrix.android.internal.database.helper.*
|
||||
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
||||
import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
|
||||
import im.vector.matrix.android.session.room.timeline.RoomDataHelper.createFakeListOfEvents
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
package im.vector.matrix.android.session.room.timeline
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.internal.session.room.timeline.GetContextOfEventTask
|
||||
import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
|
||||
import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEventPersistor
|
||||
@ -24,7 +23,7 @@ import kotlin.random.Random
|
||||
|
||||
internal class FakeGetContextOfEventTask constructor(private val tokenChunkEventPersistor: TokenChunkEventPersistor) : GetContextOfEventTask {
|
||||
|
||||
override suspend fun execute(params: GetContextOfEventTask.Params): Try<TokenChunkEventPersistor.Result> {
|
||||
override suspend fun execute(params: GetContextOfEventTask.Params): TokenChunkEventPersistor.Result {
|
||||
val fakeEvents = RoomDataHelper.createFakeListOfEvents(30)
|
||||
val tokenChunkEvent = FakeTokenChunkEvent(
|
||||
Random.nextLong(System.currentTimeMillis()).toString(),
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
package im.vector.matrix.android.session.room.timeline
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.internal.session.room.timeline.PaginationTask
|
||||
import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEventPersistor
|
||||
import javax.inject.Inject
|
||||
@ -24,7 +23,7 @@ import kotlin.random.Random
|
||||
|
||||
internal class FakePaginationTask @Inject constructor(private val tokenChunkEventPersistor: TokenChunkEventPersistor) : PaginationTask {
|
||||
|
||||
override suspend fun execute(params: PaginationTask.Params): Try<TokenChunkEventPersistor.Result> {
|
||||
override suspend fun execute(params: PaginationTask.Params): TokenChunkEventPersistor.Result {
|
||||
val fakeEvents = RoomDataHelper.createFakeListOfEvents(30)
|
||||
val tokenChunkEvent = FakeTokenChunkEvent(params.from, Random.nextLong(System.currentTimeMillis()).toString(), fakeEvents)
|
||||
return tokenChunkEventPersistor.insertInDb(tokenChunkEvent, params.roomId, params.direction)
|
||||
|
@ -18,6 +18,7 @@ package im.vector.matrix.android.api.permalinks
|
||||
|
||||
import android.text.style.ClickableSpan
|
||||
import android.view.View
|
||||
import im.vector.matrix.android.api.permalinks.MatrixPermalinkSpan.Callback
|
||||
|
||||
/**
|
||||
* This MatrixPermalinkSpan is a clickable span which use a [Callback] to communicate back.
|
||||
|
@ -26,14 +26,12 @@ import im.vector.matrix.android.api.session.events.model.Content
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
|
||||
import im.vector.matrix.android.internal.crypto.NewSessionListener
|
||||
import im.vector.matrix.android.internal.crypto.attachments.ElementToDecrypt
|
||||
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
|
||||
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
|
||||
import im.vector.matrix.android.internal.crypto.model.MXEncryptEventContentResult
|
||||
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
|
||||
import java.io.File
|
||||
|
||||
interface CryptoService {
|
||||
|
||||
|
@ -30,7 +30,7 @@ interface MembershipService {
|
||||
* This methods load all room members if it was done yet.
|
||||
* @return a [Cancelable]
|
||||
*/
|
||||
fun loadRoomMembersIfNeeded(matrixCallback: MatrixCallback<Boolean>): Cancelable
|
||||
fun loadRoomMembersIfNeeded(matrixCallback: MatrixCallback<Unit>): Cancelable
|
||||
|
||||
/**
|
||||
* Return the roomMember with userId or null.
|
||||
|
@ -22,7 +22,6 @@ import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.isReply
|
||||
import im.vector.matrix.android.api.session.room.send.SendState
|
||||
import im.vector.matrix.android.api.util.ContentUtils.extractUsefulTextFromReply
|
||||
import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
|
||||
|
||||
|
@ -19,5 +19,6 @@ package im.vector.matrix.android.api.util
|
||||
class CancelableBag : Cancelable, MutableList<Cancelable> by ArrayList() {
|
||||
override fun cancel() {
|
||||
forEach { it.cancel() }
|
||||
clear()
|
||||
}
|
||||
}
|
||||
|
@ -68,7 +68,9 @@ internal class DefaultAuthenticator @Inject constructor(@Unauthenticated
|
||||
callback: MatrixCallback<Session>): Cancelable {
|
||||
|
||||
val job = GlobalScope.launch(coroutineDispatchers.main) {
|
||||
val sessionOrFailure = authenticate(homeServerConnectionConfig, login, password)
|
||||
val sessionOrFailure = runCatching {
|
||||
authenticate(homeServerConnectionConfig, login, password)
|
||||
}
|
||||
sessionOrFailure.foldToCallback(callback)
|
||||
}
|
||||
return CancelableCoroutine(job)
|
||||
@ -85,16 +87,12 @@ internal class DefaultAuthenticator @Inject constructor(@Unauthenticated
|
||||
} else {
|
||||
PasswordLoginParams.userIdentifier(login, password, "Mobile")
|
||||
}
|
||||
executeRequest<Credentials> {
|
||||
val credentials = executeRequest<Credentials> {
|
||||
apiCall = authAPI.login(loginParams)
|
||||
}.map {
|
||||
val sessionParams = SessionParams(it, homeServerConnectionConfig)
|
||||
sessionParamsStore.save(sessionParams)
|
||||
sessionParams
|
||||
}.map {
|
||||
sessionManager.getOrCreateSession(it)
|
||||
}
|
||||
|
||||
val sessionParams = SessionParams(credentials, homeServerConnectionConfig)
|
||||
sessionParamsStore.save(sessionParams)
|
||||
sessionManager.getOrCreateSession(sessionParams)
|
||||
}
|
||||
|
||||
private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI {
|
||||
|
@ -20,7 +20,6 @@ import com.squareup.moshi.Moshi
|
||||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||
import im.vector.matrix.android.internal.di.MatrixScope
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class SessionParamsMapper @Inject constructor(moshi: Moshi) {
|
||||
|
@ -72,7 +72,6 @@ import im.vector.matrix.android.internal.session.room.membership.RoomMembers
|
||||
import im.vector.matrix.android.internal.session.sync.model.SyncResponse
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.task.configureWith
|
||||
import im.vector.matrix.android.internal.task.toConfigurableTask
|
||||
import im.vector.matrix.android.internal.util.JsonCanonicalizer
|
||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||
import im.vector.matrix.android.internal.util.fetchCopied
|
||||
@ -167,22 +166,25 @@ internal class CryptoManager @Inject constructor(
|
||||
|
||||
override fun setDeviceName(deviceId: String, deviceName: String, callback: MatrixCallback<Unit>) {
|
||||
setDeviceNameTask
|
||||
.configureWith(SetDeviceNameTask.Params(deviceId, deviceName))
|
||||
.dispatchTo(callback)
|
||||
.configureWith(SetDeviceNameTask.Params(deviceId, deviceName)) {
|
||||
this.callback = callback
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
override fun deleteDevice(deviceId: String, callback: MatrixCallback<Unit>) {
|
||||
deleteDeviceTask
|
||||
.configureWith(DeleteDeviceTask.Params(deviceId))
|
||||
.dispatchTo(callback)
|
||||
.configureWith(DeleteDeviceTask.Params(deviceId)) {
|
||||
this.callback = callback
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
override fun deleteDeviceWithUserPassword(deviceId: String, authSession: String?, password: String, callback: MatrixCallback<Unit>) {
|
||||
deleteDeviceWithUserPasswordTask
|
||||
.configureWith(DeleteDeviceWithUserPasswordTask.Params(deviceId, authSession, password))
|
||||
.dispatchTo(callback)
|
||||
.configureWith(DeleteDeviceWithUserPasswordTask.Params(deviceId, authSession, password)) {
|
||||
this.callback = callback
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
@ -196,8 +198,9 @@ internal class CryptoManager @Inject constructor(
|
||||
|
||||
override fun getDevicesList(callback: MatrixCallback<DevicesListResponse>) {
|
||||
getDevicesTask
|
||||
.toConfigurableTask()
|
||||
.dispatchTo(callback)
|
||||
.configureWith {
|
||||
this.callback = callback
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
@ -254,35 +257,36 @@ internal class CryptoManager @Inject constructor(
|
||||
private suspend fun internalStart(isInitialSync: Boolean) {
|
||||
// Open the store
|
||||
cryptoStore.open()
|
||||
uploadDeviceKeys()
|
||||
.flatMap { oneTimeKeysUploader.maybeUploadOneTimeKeys() }
|
||||
.fold(
|
||||
{
|
||||
Timber.e("Start failed: $it")
|
||||
delay(1000)
|
||||
isStarting.set(false)
|
||||
internalStart(isInitialSync)
|
||||
},
|
||||
{
|
||||
outgoingRoomKeyRequestManager.start()
|
||||
keysBackup.checkAndStartKeysBackup()
|
||||
if (isInitialSync) {
|
||||
// refresh the devices list for each known room members
|
||||
deviceListManager.invalidateAllDeviceLists()
|
||||
deviceListManager.refreshOutdatedDeviceLists()
|
||||
} else {
|
||||
incomingRoomKeyRequestManager.processReceivedRoomKeyRequests()
|
||||
}
|
||||
isStarting.set(false)
|
||||
isStarted.set(true)
|
||||
}
|
||||
)
|
||||
runCatching {
|
||||
uploadDeviceKeys()
|
||||
oneTimeKeysUploader.maybeUploadOneTimeKeys()
|
||||
outgoingRoomKeyRequestManager.start()
|
||||
keysBackup.checkAndStartKeysBackup()
|
||||
if (isInitialSync) {
|
||||
// refresh the devices list for each known room members
|
||||
deviceListManager.invalidateAllDeviceLists()
|
||||
deviceListManager.refreshOutdatedDeviceLists()
|
||||
} else {
|
||||
incomingRoomKeyRequestManager.processReceivedRoomKeyRequests()
|
||||
}
|
||||
}.fold(
|
||||
{
|
||||
isStarting.set(false)
|
||||
isStarted.set(true)
|
||||
},
|
||||
{
|
||||
Timber.e("Start failed: $it")
|
||||
delay(1000)
|
||||
isStarting.set(false)
|
||||
internalStart(isInitialSync)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the crypto
|
||||
*/
|
||||
fun close() {
|
||||
fun close() = runBlocking(coroutineDispatchers.crypto) {
|
||||
olmDevice.release()
|
||||
cryptoStore.close()
|
||||
outgoingRoomKeyRequestManager.stop()
|
||||
@ -556,13 +560,16 @@ internal class CryptoManager @Inject constructor(
|
||||
if (safeAlgorithm != null) {
|
||||
val t0 = System.currentTimeMillis()
|
||||
Timber.v("## encryptEventContent() starts")
|
||||
safeAlgorithm.encryptEventContent(eventContent, eventType, userIds)
|
||||
runCatching {
|
||||
safeAlgorithm.encryptEventContent(eventContent, eventType, userIds)
|
||||
}
|
||||
.fold(
|
||||
{ callback.onFailure(it) },
|
||||
{
|
||||
Timber.v("## encryptEventContent() : succeeds after ${System.currentTimeMillis() - t0} ms")
|
||||
callback.onSuccess(MXEncryptEventContentResult(it, EventType.ENCRYPTED))
|
||||
}
|
||||
},
|
||||
{ callback.onFailure(it) }
|
||||
|
||||
)
|
||||
} else {
|
||||
val algorithm = getEncryptionAlgorithm(roomId)
|
||||
@ -584,10 +591,7 @@ internal class CryptoManager @Inject constructor(
|
||||
@Throws(MXCryptoError::class)
|
||||
override fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult {
|
||||
return runBlocking {
|
||||
internalDecryptEvent(event, timeline).fold(
|
||||
{ throw it },
|
||||
{ it }
|
||||
)
|
||||
internalDecryptEvent(event, timeline)
|
||||
}
|
||||
}
|
||||
|
||||
@ -600,8 +604,10 @@ internal class CryptoManager @Inject constructor(
|
||||
*/
|
||||
override fun decryptEventAsync(event: Event, timeline: String, callback: MatrixCallback<MXEventDecryptionResult>) {
|
||||
GlobalScope.launch {
|
||||
val result = withContext(coroutineDispatchers.crypto) {
|
||||
internalDecryptEvent(event, timeline)
|
||||
val result = runCatching {
|
||||
withContext(coroutineDispatchers.crypto) {
|
||||
internalDecryptEvent(event, timeline)
|
||||
}
|
||||
}
|
||||
result.foldToCallback(callback)
|
||||
}
|
||||
@ -612,22 +618,22 @@ internal class CryptoManager @Inject constructor(
|
||||
*
|
||||
* @param event the raw event.
|
||||
* @param timeline the id of the timeline where the event is decrypted. It is used to prevent replay attack.
|
||||
* @return the MXEventDecryptionResult data, or null in case of error wrapped into [Try]
|
||||
* @return the MXEventDecryptionResult data, or null in case of error
|
||||
*/
|
||||
private suspend fun internalDecryptEvent(event: Event, timeline: String): Try<MXEventDecryptionResult> {
|
||||
private suspend fun internalDecryptEvent(event: Event, timeline: String): MXEventDecryptionResult {
|
||||
val eventContent = event.content
|
||||
return if (eventContent == null) {
|
||||
if (eventContent == null) {
|
||||
Timber.e("## decryptEvent : empty event content")
|
||||
Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON))
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)
|
||||
} else {
|
||||
val algorithm = eventContent["algorithm"]?.toString()
|
||||
val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(event.roomId, algorithm)
|
||||
if (alg == null) {
|
||||
val reason = String.format(MXCryptoError.UNABLE_TO_DECRYPT_REASON, event.eventId, algorithm)
|
||||
Timber.e("## decryptEvent() : $reason")
|
||||
Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT, reason))
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT, reason)
|
||||
} else {
|
||||
alg.decryptEvent(event, timeline)
|
||||
return alg.decryptEvent(event, timeline)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -689,12 +695,13 @@ internal class CryptoManager @Inject constructor(
|
||||
private fun onRoomEncryptionEvent(roomId: String, event: Event) {
|
||||
GlobalScope.launch(coroutineDispatchers.crypto) {
|
||||
val params = LoadRoomMembersTask.Params(roomId)
|
||||
loadRoomMembersTask
|
||||
.execute(params)
|
||||
.map { _ ->
|
||||
val userIds = getRoomUserIds(roomId)
|
||||
setEncryptionInRoom(roomId, event.content?.get("algorithm")?.toString(), true, userIds)
|
||||
}
|
||||
try {
|
||||
loadRoomMembersTask.execute(params)
|
||||
val userIds = getRoomUserIds(roomId)
|
||||
setEncryptionInRoom(roomId, event.content?.get("algorithm")?.toString(), true, userIds)
|
||||
} catch (throwable: Throwable) {
|
||||
Timber.e(throwable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -761,7 +768,7 @@ internal class CryptoManager @Inject constructor(
|
||||
/**
|
||||
* Upload my user's device keys.
|
||||
*/
|
||||
private suspend fun uploadDeviceKeys(): Try<KeysUploadResponse> {
|
||||
private suspend fun uploadDeviceKeys(): KeysUploadResponse {
|
||||
// Prepare the device keys data to send
|
||||
// Sign it
|
||||
val canonicalJson = JsonCanonicalizer.getCanonicalJson(Map::class.java, getMyDevice().signalableJSONDictionary())
|
||||
@ -868,10 +875,8 @@ internal class CryptoManager @Inject constructor(
|
||||
fun checkUnknownDevices(userIds: List<String>, callback: MatrixCallback<Unit>) {
|
||||
// force the refresh to ensure that the devices list is up-to-date
|
||||
GlobalScope.launch(coroutineDispatchers.crypto) {
|
||||
deviceListManager
|
||||
.downloadKeys(userIds, true)
|
||||
runCatching { deviceListManager.downloadKeys(userIds, true) }
|
||||
.fold(
|
||||
{ callback.onFailure(it) },
|
||||
{
|
||||
val unknownDevices = getUnknownDevices(it)
|
||||
if (unknownDevices.map.isEmpty()) {
|
||||
@ -880,7 +885,8 @@ internal class CryptoManager @Inject constructor(
|
||||
// trigger an an unknown devices exception
|
||||
callback.onFailure(Failure.CryptoError(MXCryptoError.UnknownDevice(unknownDevices)))
|
||||
}
|
||||
}
|
||||
},
|
||||
{ callback.onFailure(it) }
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -1035,16 +1041,17 @@ internal class CryptoManager @Inject constructor(
|
||||
|
||||
override fun downloadKeys(userIds: List<String>, forceDownload: Boolean, callback: MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>>) {
|
||||
GlobalScope.launch(coroutineDispatchers.crypto) {
|
||||
deviceListManager
|
||||
.downloadKeys(userIds, forceDownload)
|
||||
.foldToCallback(callback)
|
||||
runCatching {
|
||||
deviceListManager.downloadKeys(userIds, forceDownload)
|
||||
}.foldToCallback(callback)
|
||||
}
|
||||
}
|
||||
|
||||
override fun clearCryptoCache(callback: MatrixCallback<Unit>) {
|
||||
clearCryptoDataTask
|
||||
.toConfigurableTask()
|
||||
.dispatchTo(callback)
|
||||
.configureWith {
|
||||
this.callback = callback
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
|
@ -18,14 +18,12 @@
|
||||
package im.vector.matrix.android.internal.crypto
|
||||
|
||||
import android.text.TextUtils
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.api.MatrixPatterns
|
||||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
|
||||
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||
import im.vector.matrix.android.internal.crypto.tasks.DownloadKeysForUsersTask
|
||||
import im.vector.matrix.android.internal.extensions.onError
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.session.sync.SyncTokenStore
|
||||
import timber.log.Timber
|
||||
@ -237,7 +235,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
||||
* @param forceDownload Always download the keys even if cached.
|
||||
* @param callback the asynchronous callback
|
||||
*/
|
||||
suspend fun downloadKeys(userIds: List<String>?, forceDownload: Boolean): Try<MXUsersDevicesMap<MXDeviceInfo>> {
|
||||
suspend fun downloadKeys(userIds: List<String>?, forceDownload: Boolean): MXUsersDevicesMap<MXDeviceInfo> {
|
||||
Timber.v("## downloadKeys() : forceDownload $forceDownload : $userIds")
|
||||
// Map from userId -> deviceId -> MXDeviceInfo
|
||||
val stored = MXUsersDevicesMap<MXDeviceInfo>()
|
||||
@ -268,16 +266,15 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
||||
}
|
||||
return if (downloadUsers.isEmpty()) {
|
||||
Timber.v("## downloadKeys() : no new user device")
|
||||
Try.just(stored)
|
||||
stored
|
||||
} else {
|
||||
Timber.v("## downloadKeys() : starts")
|
||||
val t0 = System.currentTimeMillis()
|
||||
doKeyDownloadForUsers(downloadUsers)
|
||||
.map {
|
||||
Timber.v("## downloadKeys() : doKeyDownloadForUsers succeeds after " + (System.currentTimeMillis() - t0) + " ms")
|
||||
it.addEntriesFromMap(stored)
|
||||
it
|
||||
}
|
||||
val result = doKeyDownloadForUsers(downloadUsers)
|
||||
Timber.v("## downloadKeys() : doKeyDownloadForUsers succeeds after " + (System.currentTimeMillis() - t0) + " ms")
|
||||
result.also {
|
||||
it.addEntriesFromMap(stored)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -286,60 +283,60 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
||||
*
|
||||
* @param downloadUsers the user ids list
|
||||
*/
|
||||
private suspend fun doKeyDownloadForUsers(downloadUsers: MutableList<String>): Try<MXUsersDevicesMap<MXDeviceInfo>> {
|
||||
private suspend fun doKeyDownloadForUsers(downloadUsers: MutableList<String>): MXUsersDevicesMap<MXDeviceInfo> {
|
||||
Timber.v("## doKeyDownloadForUsers() : doKeyDownloadForUsers $downloadUsers")
|
||||
// get the user ids which did not already trigger a keys download
|
||||
val filteredUsers = downloadUsers.filter { MatrixPatterns.isUserId(it) }
|
||||
if (filteredUsers.isEmpty()) {
|
||||
// trigger nothing
|
||||
return Try.just(MXUsersDevicesMap())
|
||||
return MXUsersDevicesMap()
|
||||
}
|
||||
val params = DownloadKeysForUsersTask.Params(filteredUsers, syncTokenStore.getLastToken())
|
||||
return downloadKeysForUsersTask.execute(params)
|
||||
.map { response ->
|
||||
Timber.v("## doKeyDownloadForUsers() : Got keys for " + filteredUsers.size + " users")
|
||||
for (userId in filteredUsers) {
|
||||
val devices = response.deviceKeys?.get(userId)
|
||||
Timber.v("## doKeyDownloadForUsers() : Got keys for $userId : $devices")
|
||||
if (devices != null) {
|
||||
val mutableDevices = HashMap(devices)
|
||||
val deviceIds = ArrayList(mutableDevices.keys)
|
||||
for (deviceId in deviceIds) {
|
||||
// Get the potential previously store device keys for this device
|
||||
val previouslyStoredDeviceKeys = cryptoStore.getUserDevice(deviceId, userId)
|
||||
val deviceInfo = mutableDevices[deviceId]
|
||||
val response = try {
|
||||
downloadKeysForUsersTask.execute(params)
|
||||
} catch (throwable: Throwable) {
|
||||
Timber.e(throwable, "##doKeyDownloadForUsers(): error")
|
||||
onKeysDownloadFailed(filteredUsers)
|
||||
throw throwable
|
||||
}
|
||||
Timber.v("## doKeyDownloadForUsers() : Got keys for " + filteredUsers.size + " users")
|
||||
for (userId in filteredUsers) {
|
||||
val devices = response.deviceKeys?.get(userId)
|
||||
Timber.v("## doKeyDownloadForUsers() : Got keys for $userId : $devices")
|
||||
if (devices != null) {
|
||||
val mutableDevices = HashMap(devices)
|
||||
val deviceIds = ArrayList(mutableDevices.keys)
|
||||
for (deviceId in deviceIds) {
|
||||
// Get the potential previously store device keys for this device
|
||||
val previouslyStoredDeviceKeys = cryptoStore.getUserDevice(deviceId, userId)
|
||||
val deviceInfo = mutableDevices[deviceId]
|
||||
|
||||
// in some race conditions (like unit tests)
|
||||
// the self device must be seen as verified
|
||||
if (TextUtils.equals(deviceInfo!!.deviceId, credentials.deviceId) && TextUtils.equals(userId, credentials.userId)) {
|
||||
deviceInfo.verified = MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED
|
||||
}
|
||||
// Validate received keys
|
||||
if (!validateDeviceKeys(deviceInfo, userId, deviceId, previouslyStoredDeviceKeys)) {
|
||||
// New device keys are not valid. Do not store them
|
||||
mutableDevices.remove(deviceId)
|
||||
if (null != previouslyStoredDeviceKeys) {
|
||||
// But keep old validated ones if any
|
||||
mutableDevices[deviceId] = previouslyStoredDeviceKeys
|
||||
}
|
||||
} else if (null != previouslyStoredDeviceKeys) {
|
||||
// The verified status is not sync'ed with hs.
|
||||
// This is a client side information, valid only for this client.
|
||||
// So, transfer its previous value
|
||||
mutableDevices[deviceId]!!.verified = previouslyStoredDeviceKeys.verified
|
||||
}
|
||||
}
|
||||
// Update the store
|
||||
// Note that devices which aren't in the response will be removed from the stores
|
||||
cryptoStore.storeUserDevices(userId, mutableDevices)
|
||||
}
|
||||
// in some race conditions (like unit tests)
|
||||
// the self device must be seen as verified
|
||||
if (TextUtils.equals(deviceInfo!!.deviceId, credentials.deviceId) && TextUtils.equals(userId, credentials.userId)) {
|
||||
deviceInfo.verified = MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED
|
||||
}
|
||||
// Validate received keys
|
||||
if (!validateDeviceKeys(deviceInfo, userId, deviceId, previouslyStoredDeviceKeys)) {
|
||||
// New device keys are not valid. Do not store them
|
||||
mutableDevices.remove(deviceId)
|
||||
if (null != previouslyStoredDeviceKeys) {
|
||||
// But keep old validated ones if any
|
||||
mutableDevices[deviceId] = previouslyStoredDeviceKeys
|
||||
}
|
||||
} else if (null != previouslyStoredDeviceKeys) {
|
||||
// The verified status is not sync'ed with hs.
|
||||
// This is a client side information, valid only for this client.
|
||||
// So, transfer its previous value
|
||||
mutableDevices[deviceId]!!.verified = previouslyStoredDeviceKeys.verified
|
||||
}
|
||||
onKeysDownloadSucceed(filteredUsers, response.failures)
|
||||
}
|
||||
.onError {
|
||||
Timber.e(it, "##doKeyDownloadForUsers(): error")
|
||||
onKeysDownloadFailed(filteredUsers)
|
||||
}
|
||||
// Update the store
|
||||
// Note that devices which aren't in the response will be removed from the stores
|
||||
cryptoStore.storeUserDevices(userId, mutableDevices)
|
||||
}
|
||||
}
|
||||
return onKeysDownloadSucceed(filteredUsers, response.failures)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -465,15 +462,16 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
||||
}
|
||||
|
||||
cryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses)
|
||||
doKeyDownloadForUsers(users)
|
||||
.fold(
|
||||
{
|
||||
Timber.e(it, "## refreshOutdatedDeviceLists() : ERROR updating device keys for users $users")
|
||||
},
|
||||
{
|
||||
Timber.v("## refreshOutdatedDeviceLists() : done")
|
||||
}
|
||||
)
|
||||
runCatching {
|
||||
doKeyDownloadForUsers(users)
|
||||
}.fold(
|
||||
{
|
||||
Timber.v("## refreshOutdatedDeviceLists() : done")
|
||||
},
|
||||
{
|
||||
Timber.e(it, "## refreshOutdatedDeviceLists() : ERROR updating device keys for users $users")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -18,7 +18,6 @@
|
||||
package im.vector.matrix.android.internal.crypto
|
||||
|
||||
import android.text.TextUtils
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.api.session.crypto.MXCryptoError
|
||||
import im.vector.matrix.android.api.util.JSON_DICT_PARAMETERIZED_TYPE
|
||||
import im.vector.matrix.android.api.util.JsonDict
|
||||
@ -506,25 +505,25 @@ internal class MXOlmDevice @Inject constructor(
|
||||
keysClaimed: Map<String, String>,
|
||||
exportFormat: Boolean): Boolean {
|
||||
val session = OlmInboundGroupSessionWrapper(sessionKey, exportFormat)
|
||||
runCatching { getInboundGroupSession(sessionId, senderKey, roomId) }
|
||||
.fold(
|
||||
{
|
||||
// If we already have this session, consider updating it
|
||||
Timber.e("## addInboundGroupSession() : Update for megolm session $senderKey/$sessionId")
|
||||
|
||||
getInboundGroupSession(sessionId, senderKey, roomId).fold(
|
||||
{
|
||||
// Nothing to do in case of error
|
||||
},
|
||||
{
|
||||
// If we already have this session, consider updating it
|
||||
Timber.e("## addInboundGroupSession() : Update for megolm session $senderKey/$sessionId")
|
||||
val existingFirstKnown = it.firstKnownIndex!!
|
||||
val newKnownFirstIndex = session.firstKnownIndex
|
||||
|
||||
val existingFirstKnown = it.firstKnownIndex!!
|
||||
val newKnownFirstIndex = session.firstKnownIndex
|
||||
|
||||
//If our existing session is better we keep it
|
||||
if (newKnownFirstIndex != null && existingFirstKnown <= newKnownFirstIndex) {
|
||||
session.olmInboundGroupSession?.releaseSession()
|
||||
return false
|
||||
}
|
||||
}
|
||||
)
|
||||
//If our existing session is better we keep it
|
||||
if (newKnownFirstIndex != null && existingFirstKnown <= newKnownFirstIndex) {
|
||||
session.olmInboundGroupSession?.releaseSession()
|
||||
return false
|
||||
}
|
||||
},
|
||||
{
|
||||
// Nothing to do in case of error
|
||||
}
|
||||
)
|
||||
|
||||
// sanity check
|
||||
if (null == session.olmInboundGroupSession) {
|
||||
@ -595,12 +594,8 @@ internal class MXOlmDevice @Inject constructor(
|
||||
continue
|
||||
}
|
||||
|
||||
getInboundGroupSession(sessionId, senderKey, roomId)
|
||||
runCatching { getInboundGroupSession(sessionId, senderKey, roomId) }
|
||||
.fold(
|
||||
{
|
||||
// Session does not already exist, add it
|
||||
sessions.add(session)
|
||||
},
|
||||
{
|
||||
// If we already have this session, consider updating it
|
||||
Timber.e("## importInboundGroupSession() : Update for megolm session $senderKey/$sessionId")
|
||||
@ -613,7 +608,12 @@ internal class MXOlmDevice @Inject constructor(
|
||||
sessions.add(session)
|
||||
}
|
||||
Unit
|
||||
},
|
||||
{
|
||||
// Session does not already exist, add it
|
||||
sessions.add(session)
|
||||
}
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
@ -648,61 +648,57 @@ internal class MXOlmDevice @Inject constructor(
|
||||
roomId: String,
|
||||
timeline: String?,
|
||||
sessionId: String,
|
||||
senderKey: String): Try<OlmDecryptionResult> {
|
||||
return getInboundGroupSession(sessionId, senderKey, roomId)
|
||||
.flatMap { session ->
|
||||
// Check that the room id matches the original one for the session. This stops
|
||||
// the HS pretending a message was targeting a different room.
|
||||
if (roomId == session.roomId) {
|
||||
var decryptResult: OlmInboundGroupSession.DecryptMessageResult? = null
|
||||
try {
|
||||
decryptResult = session.olmInboundGroupSession!!.decryptMessage(body)
|
||||
} catch (e: OlmException) {
|
||||
Timber.e(e, "## decryptGroupMessage () : decryptMessage failed")
|
||||
return@flatMap Try.Failure(MXCryptoError.OlmError(e))
|
||||
}
|
||||
senderKey: String): OlmDecryptionResult {
|
||||
val session = getInboundGroupSession(sessionId, senderKey, roomId)
|
||||
// Check that the room id matches the original one for the session. This stops
|
||||
// the HS pretending a message was targeting a different room.
|
||||
if (roomId == session.roomId) {
|
||||
var decryptResult: OlmInboundGroupSession.DecryptMessageResult? = null
|
||||
try {
|
||||
decryptResult = session.olmInboundGroupSession!!.decryptMessage(body)
|
||||
} catch (e: OlmException) {
|
||||
Timber.e(e, "## decryptGroupMessage () : decryptMessage failed")
|
||||
throw MXCryptoError.OlmError(e)
|
||||
}
|
||||
|
||||
if (null != timeline) {
|
||||
if (!inboundGroupSessionMessageIndexes.containsKey(timeline)) {
|
||||
inboundGroupSessionMessageIndexes[timeline] = HashMap()
|
||||
}
|
||||
|
||||
val messageIndexKey = senderKey + "|" + sessionId + "|" + decryptResult.mIndex
|
||||
|
||||
if (inboundGroupSessionMessageIndexes[timeline]?.get(messageIndexKey) != null) {
|
||||
val reason = String.format(MXCryptoError.DUPLICATE_MESSAGE_INDEX_REASON, decryptResult.mIndex)
|
||||
Timber.e("## decryptGroupMessage() : $reason")
|
||||
return@flatMap Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.DUPLICATED_MESSAGE_INDEX, reason))
|
||||
}
|
||||
|
||||
inboundGroupSessionMessageIndexes[timeline]!!.put(messageIndexKey, true)
|
||||
}
|
||||
|
||||
store.storeInboundGroupSessions(listOf(session))
|
||||
val payload = try {
|
||||
val adapter = MoshiProvider.providesMoshi().adapter<JsonDict>(JSON_DICT_PARAMETERIZED_TYPE)
|
||||
val payloadString = convertFromUTF8(decryptResult.mDecryptedMessage)
|
||||
adapter.fromJson(payloadString)
|
||||
} catch (e: Exception) {
|
||||
Timber.e("## decryptGroupMessage() : fails to parse the payload")
|
||||
return@flatMap Try.Failure(
|
||||
MXCryptoError.Base(MXCryptoError.ErrorType.BAD_DECRYPTED_FORMAT, MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON))
|
||||
}
|
||||
|
||||
return@flatMap Try.just(
|
||||
OlmDecryptionResult(
|
||||
payload,
|
||||
session.keysClaimed,
|
||||
senderKey,
|
||||
session.forwardingCurve25519KeyChain
|
||||
)
|
||||
)
|
||||
} else {
|
||||
val reason = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, session.roomId)
|
||||
Timber.e("## decryptGroupMessage() : $reason")
|
||||
return@flatMap Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.INBOUND_SESSION_MISMATCH_ROOM_ID, reason))
|
||||
}
|
||||
if (null != timeline) {
|
||||
if (!inboundGroupSessionMessageIndexes.containsKey(timeline)) {
|
||||
inboundGroupSessionMessageIndexes[timeline] = HashMap()
|
||||
}
|
||||
|
||||
val messageIndexKey = senderKey + "|" + sessionId + "|" + decryptResult.mIndex
|
||||
|
||||
if (inboundGroupSessionMessageIndexes[timeline]?.get(messageIndexKey) != null) {
|
||||
val reason = String.format(MXCryptoError.DUPLICATE_MESSAGE_INDEX_REASON, decryptResult.mIndex)
|
||||
Timber.e("## decryptGroupMessage() : $reason")
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.DUPLICATED_MESSAGE_INDEX, reason)
|
||||
}
|
||||
|
||||
inboundGroupSessionMessageIndexes[timeline]!!.put(messageIndexKey, true)
|
||||
}
|
||||
|
||||
store.storeInboundGroupSessions(listOf(session))
|
||||
val payload = try {
|
||||
val adapter = MoshiProvider.providesMoshi().adapter<JsonDict>(JSON_DICT_PARAMETERIZED_TYPE)
|
||||
val payloadString = convertFromUTF8(decryptResult.mDecryptedMessage)
|
||||
adapter.fromJson(payloadString)
|
||||
} catch (e: Exception) {
|
||||
Timber.e("## decryptGroupMessage() : fails to parse the payload")
|
||||
throw
|
||||
MXCryptoError.Base(MXCryptoError.ErrorType.BAD_DECRYPTED_FORMAT, MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON)
|
||||
}
|
||||
|
||||
return OlmDecryptionResult(
|
||||
payload,
|
||||
session.keysClaimed,
|
||||
senderKey,
|
||||
session.forwardingCurve25519KeyChain
|
||||
)
|
||||
} else {
|
||||
val reason = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, session.roomId)
|
||||
Timber.e("## decryptGroupMessage() : $reason")
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.INBOUND_SESSION_MISMATCH_ROOM_ID, reason)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -766,26 +762,26 @@ internal class MXOlmDevice @Inject constructor(
|
||||
* @param senderKey the base64-encoded curve25519 key of the sender.
|
||||
* @return the inbound group session.
|
||||
*/
|
||||
fun getInboundGroupSession(sessionId: String?, senderKey: String?, roomId: String?): Try<OlmInboundGroupSessionWrapper> {
|
||||
fun getInboundGroupSession(sessionId: String?, senderKey: String?, roomId: String?): OlmInboundGroupSessionWrapper {
|
||||
if (sessionId.isNullOrBlank() || senderKey.isNullOrBlank()) {
|
||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_SENDER_KEY, MXCryptoError.ERROR_MISSING_PROPERTY_REASON))
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_SENDER_KEY, MXCryptoError.ERROR_MISSING_PROPERTY_REASON)
|
||||
}
|
||||
|
||||
val session = store.getInboundGroupSession(sessionId, senderKey)
|
||||
|
||||
return if (null != session) {
|
||||
if (session != null) {
|
||||
// Check that the room id matches the original one for the session. This stops
|
||||
// the HS pretending a message was targeting a different room.
|
||||
if (!TextUtils.equals(roomId, session.roomId)) {
|
||||
val errorDescription = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, session.roomId)
|
||||
Timber.e("## getInboundGroupSession() : $errorDescription")
|
||||
Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.INBOUND_SESSION_MISMATCH_ROOM_ID, errorDescription))
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.INBOUND_SESSION_MISMATCH_ROOM_ID, errorDescription)
|
||||
} else {
|
||||
Try.just(session)
|
||||
return session
|
||||
}
|
||||
} else {
|
||||
Timber.e("## getInboundGroupSession() : Cannot retrieve inbound group session $sessionId")
|
||||
Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID, MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_REASON))
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID, MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_REASON)
|
||||
}
|
||||
}
|
||||
|
||||
@ -798,6 +794,6 @@ internal class MXOlmDevice @Inject constructor(
|
||||
* @return true if the unbound session keys are known.
|
||||
*/
|
||||
fun hasInboundSessionKeys(roomId: String, senderKey: String, sessionId: String): Boolean {
|
||||
return getInboundGroupSession(sessionId, senderKey, roomId).isSuccess()
|
||||
return runCatching { getInboundGroupSession(sessionId, senderKey, roomId) }.isSuccess
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
package im.vector.matrix.android.internal.crypto
|
||||
|
||||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -16,8 +16,6 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto
|
||||
|
||||
import arrow.core.Try
|
||||
import arrow.instances.`try`.applicativeError.handleError
|
||||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
import im.vector.matrix.android.internal.crypto.model.MXKey
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse
|
||||
@ -59,13 +57,13 @@ internal class OneTimeKeysUploader @Inject constructor(
|
||||
/**
|
||||
* Check if the OTK must be uploaded.
|
||||
*/
|
||||
suspend fun maybeUploadOneTimeKeys(): Try<Unit> {
|
||||
suspend fun maybeUploadOneTimeKeys() {
|
||||
if (oneTimeKeyCheckInProgress) {
|
||||
return Try.just(Unit)
|
||||
return
|
||||
}
|
||||
if (System.currentTimeMillis() - lastOneTimeKeyCheck < ONE_TIME_KEY_UPLOAD_PERIOD) {
|
||||
// we've done a key upload recently.
|
||||
return Try.just(Unit)
|
||||
return
|
||||
}
|
||||
|
||||
lastOneTimeKeyCheck = System.currentTimeMillis()
|
||||
@ -81,41 +79,31 @@ internal class OneTimeKeysUploader @Inject constructor(
|
||||
// discard the oldest private keys first. This will eventually clean
|
||||
// out stale private keys that won't receive a message.
|
||||
val keyLimit = Math.floor(maxOneTimeKeys / 2.0).toInt()
|
||||
val result = if (oneTimeKeyCount != null) {
|
||||
if (oneTimeKeyCount != null) {
|
||||
uploadOTK(oneTimeKeyCount!!, keyLimit)
|
||||
} else {
|
||||
// ask the server how many keys we have
|
||||
val uploadKeysParams = UploadKeysTask.Params(null, null, credentials.deviceId!!)
|
||||
uploadKeysTask.execute(uploadKeysParams)
|
||||
.flatMap {
|
||||
// We need to keep a pool of one time public keys on the server so that
|
||||
// other devices can start conversations with us. But we can only store
|
||||
// a finite number of private keys in the olm Account object.
|
||||
// To complicate things further then can be a delay between a device
|
||||
// claiming a public one time key from the server and it sending us a
|
||||
// message. We need to keep the corresponding private key locally until
|
||||
// we receive the message.
|
||||
// But that message might never arrive leaving us stuck with duff
|
||||
// private keys clogging up our local storage.
|
||||
// So we need some kind of engineering compromise to balance all of
|
||||
// these factors.
|
||||
// TODO Why we do not set oneTimeKeyCount here?
|
||||
// TODO This is not needed anymore, see https://github.com/matrix-org/matrix-js-sdk/pull/493 (TODO on iOS also)
|
||||
val keyCount = it.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)
|
||||
uploadOTK(keyCount, keyLimit)
|
||||
}
|
||||
val response = uploadKeysTask.execute(uploadKeysParams)
|
||||
// We need to keep a pool of one time public keys on the server so that
|
||||
// other devices can start conversations with us. But we can only store
|
||||
// a finite number of private keys in the olm Account object.
|
||||
// To complicate things further then can be a delay between a device
|
||||
// claiming a public one time key from the server and it sending us a
|
||||
// message. We need to keep the corresponding private key locally until
|
||||
// we receive the message.
|
||||
// But that message might never arrive leaving us stuck with duff
|
||||
// private keys clogging up our local storage.
|
||||
// So we need some kind of engineering compromise to balance all of
|
||||
// these factors.
|
||||
// TODO Why we do not set oneTimeKeyCount here?
|
||||
// TODO This is not needed anymore, see https://github.com/matrix-org/matrix-js-sdk/pull/493 (TODO on iOS also)
|
||||
val keyCount = response.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)
|
||||
uploadOTK(keyCount, keyLimit)
|
||||
}
|
||||
return result
|
||||
.map {
|
||||
Timber.v("## uploadKeys() : success")
|
||||
oneTimeKeyCount = null
|
||||
oneTimeKeyCheckInProgress = false
|
||||
}
|
||||
.handleError {
|
||||
Timber.e(it, "## uploadKeys() : failed")
|
||||
oneTimeKeyCount = null
|
||||
oneTimeKeyCheckInProgress = false
|
||||
}
|
||||
Timber.v("## uploadKeys() : success")
|
||||
oneTimeKeyCount = null
|
||||
oneTimeKeyCheckInProgress = false
|
||||
}
|
||||
|
||||
/**
|
||||
@ -124,29 +112,26 @@ internal class OneTimeKeysUploader @Inject constructor(
|
||||
* @param keyCount the key count
|
||||
* @param keyLimit the limit
|
||||
*/
|
||||
private suspend fun uploadOTK(keyCount: Int, keyLimit: Int): Try<Unit> {
|
||||
private suspend fun uploadOTK(keyCount: Int, keyLimit: Int) {
|
||||
if (keyLimit <= keyCount) {
|
||||
// If we don't need to generate any more keys then we are done.
|
||||
return Try.just(Unit)
|
||||
return
|
||||
}
|
||||
|
||||
val keysThisLoop = Math.min(keyLimit - keyCount, ONE_TIME_KEY_GENERATION_MAX_NUMBER)
|
||||
olmDevice.generateOneTimeKeys(keysThisLoop)
|
||||
return uploadOneTimeKeys()
|
||||
.flatMap {
|
||||
if (it.hasOneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)) {
|
||||
uploadOTK(it.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE), keyLimit)
|
||||
} else {
|
||||
Timber.e("## uploadLoop() : response for uploading keys does not contain one_time_key_counts.signed_curve25519")
|
||||
Try.raise(Exception("response for uploading keys does not contain one_time_key_counts.signed_curve25519"))
|
||||
}
|
||||
}
|
||||
val response = uploadOneTimeKeys()
|
||||
if (response.hasOneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)) {
|
||||
uploadOTK(response.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE), keyLimit)
|
||||
} else {
|
||||
Timber.e("## uploadLoop() : response for uploading keys does not contain one_time_key_counts.signed_curve25519")
|
||||
throw Exception("response for uploading keys does not contain one_time_key_counts.signed_curve25519")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload my user's one time keys.
|
||||
*/
|
||||
private suspend fun uploadOneTimeKeys(): Try<KeysUploadResponse> {
|
||||
private suspend fun uploadOneTimeKeys(): KeysUploadResponse {
|
||||
val oneTimeKeys = olmDevice.getOneTimeKeys()
|
||||
val oneTimeJson = HashMap<String, Any>()
|
||||
|
||||
@ -169,13 +154,10 @@ internal class OneTimeKeysUploader @Inject constructor(
|
||||
// For now, we set the device id explicitly, as we may not be using the
|
||||
// same one as used in login.
|
||||
val uploadParams = UploadKeysTask.Params(null, oneTimeJson, credentials.deviceId!!)
|
||||
return uploadKeysTask
|
||||
.execute(uploadParams)
|
||||
.map {
|
||||
lastPublishedOneTimeKeys = oneTimeKeys
|
||||
olmDevice.markKeysAsPublished()
|
||||
it
|
||||
}
|
||||
val response = uploadKeysTask.execute(uploadParams)
|
||||
lastPublishedOneTimeKeys = oneTimeKeys
|
||||
olmDevice.markKeysAsPublished()
|
||||
return response
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -299,10 +299,12 @@ internal class OutgoingRoomKeyRequestManager @Inject constructor(
|
||||
// TODO Change this two hard coded key to something better
|
||||
contentMap.setObject(recipient["userId"], recipient["deviceId"], message)
|
||||
}
|
||||
sendToDeviceTask.configureWith(SendToDeviceTask.Params(EventType.ROOM_KEY_REQUEST, contentMap, transactionId))
|
||||
.dispatchTo(callback)
|
||||
.executeOn(TaskThread.CALLER)
|
||||
.callbackOn(TaskThread.CALLER)
|
||||
sendToDeviceTask
|
||||
.configureWith(SendToDeviceTask.Params(EventType.ROOM_KEY_REQUEST, contentMap, transactionId)) {
|
||||
this.callback = callback
|
||||
this.callbackThread = TaskThread.CALLER
|
||||
this.executionThread = TaskThread.CALLER
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,6 @@
|
||||
package im.vector.matrix.android.internal.crypto.actions
|
||||
|
||||
import android.text.TextUtils
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.internal.crypto.MXOlmDevice
|
||||
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
|
||||
import im.vector.matrix.android.internal.crypto.model.MXKey
|
||||
@ -32,7 +31,7 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(private val
|
||||
private val oneTimeKeysForUsersDeviceTask: ClaimOneTimeKeysForUsersDeviceTask) {
|
||||
|
||||
|
||||
suspend fun handle(devicesByUser: Map<String, List<MXDeviceInfo>>): Try<MXUsersDevicesMap<MXOlmSessionResult>> {
|
||||
suspend fun handle(devicesByUser: Map<String, List<MXDeviceInfo>>): MXUsersDevicesMap<MXOlmSessionResult> {
|
||||
val devicesWithoutSession = ArrayList<MXDeviceInfo>()
|
||||
|
||||
val results = MXUsersDevicesMap<MXOlmSessionResult>()
|
||||
@ -58,7 +57,7 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(private val
|
||||
}
|
||||
|
||||
if (devicesWithoutSession.size == 0) {
|
||||
return Try.just(results)
|
||||
return results
|
||||
}
|
||||
|
||||
// Prepare the request for claiming one-time keys
|
||||
@ -79,39 +78,36 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(private val
|
||||
Timber.v("## claimOneTimeKeysForUsersDevices() : $usersDevicesToClaim")
|
||||
|
||||
val claimParams = ClaimOneTimeKeysForUsersDeviceTask.Params(usersDevicesToClaim)
|
||||
return oneTimeKeysForUsersDeviceTask
|
||||
.execute(claimParams)
|
||||
.map {
|
||||
Timber.v("## claimOneTimeKeysForUsersDevices() : keysClaimResponse.oneTimeKeys: $it")
|
||||
for (userId in userIds) {
|
||||
val deviceInfos = devicesByUser[userId]
|
||||
for (deviceInfo in deviceInfos!!) {
|
||||
var oneTimeKey: MXKey? = null
|
||||
val deviceIds = it.getUserDeviceIds(userId)
|
||||
if (null != deviceIds) {
|
||||
for (deviceId in deviceIds) {
|
||||
val olmSessionResult = results.getObject(userId, deviceId)
|
||||
if (olmSessionResult!!.sessionId != null) {
|
||||
// We already have a result for this device
|
||||
continue
|
||||
}
|
||||
val key = it.getObject(userId, deviceId)
|
||||
if (key?.type == oneTimeKeyAlgorithm) {
|
||||
oneTimeKey = key
|
||||
}
|
||||
if (oneTimeKey == null) {
|
||||
Timber.v("## ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm
|
||||
+ " for device " + userId + " : " + deviceId)
|
||||
continue
|
||||
}
|
||||
// Update the result for this device in results
|
||||
olmSessionResult.sessionId = verifyKeyAndStartSession(oneTimeKey, userId, deviceInfo)
|
||||
}
|
||||
}
|
||||
val oneTimeKeys = oneTimeKeysForUsersDeviceTask.execute(claimParams)
|
||||
Timber.v("## claimOneTimeKeysForUsersDevices() : keysClaimResponse.oneTimeKeys: $oneTimeKeys")
|
||||
for (userId in userIds) {
|
||||
val deviceInfos = devicesByUser[userId]
|
||||
for (deviceInfo in deviceInfos!!) {
|
||||
var oneTimeKey: MXKey? = null
|
||||
val deviceIds = oneTimeKeys.getUserDeviceIds(userId)
|
||||
if (null != deviceIds) {
|
||||
for (deviceId in deviceIds) {
|
||||
val olmSessionResult = results.getObject(userId, deviceId)
|
||||
if (olmSessionResult!!.sessionId != null) {
|
||||
// We already have a result for this device
|
||||
continue
|
||||
}
|
||||
val key = oneTimeKeys.getObject(userId, deviceId)
|
||||
if (key?.type == oneTimeKeyAlgorithm) {
|
||||
oneTimeKey = key
|
||||
}
|
||||
if (oneTimeKey == null) {
|
||||
Timber.v("## ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm
|
||||
+ " for device " + userId + " : " + deviceId)
|
||||
continue
|
||||
}
|
||||
// Update the result for this device in results
|
||||
olmSessionResult.sessionId = verifyKeyAndStartSession(oneTimeKey, userId, deviceInfo)
|
||||
}
|
||||
results
|
||||
}
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
private fun verifyKeyAndStartSession(oneTimeKey: MXKey, userId: String, deviceInfo: MXDeviceInfo): String? {
|
||||
|
@ -17,14 +17,11 @@
|
||||
package im.vector.matrix.android.internal.crypto.actions
|
||||
|
||||
import android.text.TextUtils
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.internal.crypto.MXOlmDevice
|
||||
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
|
||||
import im.vector.matrix.android.internal.crypto.model.MXOlmSessionResult
|
||||
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import timber.log.Timber
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
@ -37,7 +34,7 @@ internal class EnsureOlmSessionsForUsersAction @Inject constructor(private val o
|
||||
* Try to make sure we have established olm sessions for the given users.
|
||||
* @param users a list of user ids.
|
||||
*/
|
||||
suspend fun handle(users: List<String>) : Try<MXUsersDevicesMap<MXOlmSessionResult>> {
|
||||
suspend fun handle(users: List<String>) : MXUsersDevicesMap<MXOlmSessionResult> {
|
||||
Timber.v("## ensureOlmSessionsForUsers() : ensureOlmSessionsForUsers $users")
|
||||
val devicesByUser = HashMap<String /* userId */, MutableList<MXDeviceInfo>>()
|
||||
|
||||
|
@ -26,7 +26,6 @@ import im.vector.matrix.android.internal.crypto.RoomDecryptorProvider
|
||||
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
|
||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -19,7 +19,6 @@ package im.vector.matrix.android.internal.crypto.actions
|
||||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup
|
||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.algorithms
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest
|
||||
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
|
||||
@ -35,7 +34,7 @@ internal interface IMXDecrypting {
|
||||
* @param timeline the id of the timeline where the event is decrypted. It is used to prevent replay attack.
|
||||
* @return the decryption information, or an error
|
||||
*/
|
||||
suspend fun decryptEvent(event: Event, timeline: String): Try<MXEventDecryptionResult>
|
||||
suspend fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult
|
||||
|
||||
/**
|
||||
* Handle a key event.
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.algorithms
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.api.session.events.model.Content
|
||||
|
||||
/**
|
||||
@ -31,7 +30,7 @@ internal interface IMXEncrypting {
|
||||
* @param eventContent the content of the event.
|
||||
* @param eventType the type of the event.
|
||||
* @param userIds the room members the event will be sent to.
|
||||
* @return the encrypted content wrapped by [Try]
|
||||
* @return the encrypted content
|
||||
*/
|
||||
suspend fun encryptEventContent(eventContent: Content, eventType: String, userIds: List<String>): Try<Content>
|
||||
suspend fun encryptEventContent(eventContent: Content, eventType: String, userIds: List<String>): Content
|
||||
}
|
||||
|
@ -18,7 +18,6 @@
|
||||
package im.vector.matrix.android.internal.crypto.algorithms.megolm
|
||||
|
||||
import android.text.TextUtils
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
import im.vector.matrix.android.api.session.crypto.MXCryptoError
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
@ -40,7 +39,6 @@ import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import kotlin.collections.HashMap
|
||||
|
||||
internal class MXMegolmDecryption(private val credentials: Credentials,
|
||||
private val olmDevice: MXOlmDevice,
|
||||
@ -61,30 +59,46 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
|
||||
*/
|
||||
private var pendingEvents: MutableMap<String /* senderKey|sessionId */, MutableMap<String /* timelineId */, MutableList<Event>>> = HashMap()
|
||||
|
||||
override suspend fun decryptEvent(event: Event, timeline: String): Try<MXEventDecryptionResult> {
|
||||
override suspend fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult {
|
||||
return decryptEvent(event, timeline, true)
|
||||
}
|
||||
|
||||
private fun decryptEvent(event: Event, timeline: String, requestKeysOnFail: Boolean): Try<MXEventDecryptionResult> {
|
||||
private suspend fun decryptEvent(event: Event, timeline: String, requestKeysOnFail: Boolean): MXEventDecryptionResult {
|
||||
if (event.roomId.isNullOrBlank()) {
|
||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON))
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)
|
||||
}
|
||||
|
||||
val encryptedEventContent = event.content.toModel<EncryptedEventContent>()
|
||||
?: return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON))
|
||||
?: throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)
|
||||
|
||||
if (encryptedEventContent.senderKey.isNullOrBlank()
|
||||
|| encryptedEventContent.sessionId.isNullOrBlank()
|
||||
|| encryptedEventContent.ciphertext.isNullOrBlank()) {
|
||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON))
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)
|
||||
}
|
||||
|
||||
return olmDevice.decryptGroupMessage(encryptedEventContent.ciphertext,
|
||||
event.roomId,
|
||||
timeline,
|
||||
encryptedEventContent.sessionId,
|
||||
encryptedEventContent.senderKey)
|
||||
return runCatching {
|
||||
olmDevice.decryptGroupMessage(encryptedEventContent.ciphertext,
|
||||
event.roomId,
|
||||
timeline,
|
||||
encryptedEventContent.sessionId,
|
||||
encryptedEventContent.senderKey)
|
||||
}
|
||||
.fold(
|
||||
{ olmDecryptionResult ->
|
||||
// the decryption succeeds
|
||||
if (olmDecryptionResult.payload != null) {
|
||||
MXEventDecryptionResult(
|
||||
clearEvent = olmDecryptionResult.payload,
|
||||
senderCurve25519Key = olmDecryptionResult.senderKey,
|
||||
claimedEd25519Key = olmDecryptionResult.keysClaimed?.get("ed25519"),
|
||||
forwardingCurve25519KeyChain = olmDecryptionResult.forwardingCurve25519KeyChain
|
||||
?: emptyList()
|
||||
)
|
||||
} else {
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)
|
||||
}
|
||||
},
|
||||
{ throwable ->
|
||||
if (throwable is MXCryptoError.OlmError) {
|
||||
// TODO Check the value of .message
|
||||
@ -98,10 +112,10 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
|
||||
val reason = String.format(MXCryptoError.OLM_REASON, throwable.olmException.message)
|
||||
val detailedReason = String.format(MXCryptoError.DETAILED_OLM_REASON, encryptedEventContent.ciphertext, reason)
|
||||
|
||||
Try.Failure(MXCryptoError.Base(
|
||||
throw MXCryptoError.Base(
|
||||
MXCryptoError.ErrorType.OLM,
|
||||
reason,
|
||||
detailedReason))
|
||||
detailedReason)
|
||||
}
|
||||
if (throwable is MXCryptoError.Base) {
|
||||
if (throwable.errorType == MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID) {
|
||||
@ -111,23 +125,7 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Try.Failure(throwable)
|
||||
},
|
||||
{ olmDecryptionResult ->
|
||||
// the decryption succeeds
|
||||
if (olmDecryptionResult.payload != null) {
|
||||
Try.just(
|
||||
MXEventDecryptionResult(
|
||||
clearEvent = olmDecryptionResult.payload,
|
||||
senderCurve25519Key = olmDecryptionResult.senderKey,
|
||||
claimedEd25519Key = olmDecryptionResult.keysClaimed?.get("ed25519"),
|
||||
forwardingCurve25519KeyChain = olmDecryptionResult.forwardingCurve25519KeyChain ?: emptyList()
|
||||
)
|
||||
)
|
||||
} else {
|
||||
Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON))
|
||||
}
|
||||
throw throwable
|
||||
}
|
||||
)
|
||||
}
|
||||
@ -311,51 +309,48 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
|
||||
}
|
||||
val userId = request.userId ?: return
|
||||
GlobalScope.launch(coroutineDispatchers.crypto) {
|
||||
deviceListManager
|
||||
.downloadKeys(listOf(userId), false)
|
||||
.flatMap {
|
||||
runCatching { deviceListManager.downloadKeys(listOf(userId), false) }
|
||||
.mapCatching {
|
||||
val deviceId = request.deviceId
|
||||
val deviceInfo = cryptoStore.getUserDevice(deviceId ?: "", userId)
|
||||
if (deviceInfo == null) {
|
||||
throw RuntimeException()
|
||||
} else {
|
||||
val devicesByUser = mapOf(userId to listOf(deviceInfo))
|
||||
ensureOlmSessionsForDevicesAction
|
||||
.handle(devicesByUser)
|
||||
.flatMap {
|
||||
val body = request.requestBody
|
||||
val olmSessionResult = it.getObject(userId, deviceId)
|
||||
if (olmSessionResult?.sessionId == null) {
|
||||
// no session with this device, probably because there
|
||||
// were no one-time keys.
|
||||
Try.just(Unit)
|
||||
}
|
||||
Timber.v("## shareKeysWithDevice() : sharing keys for session" +
|
||||
" ${body?.senderKey}|${body?.sessionId} with device $userId:$deviceId")
|
||||
val usersDeviceMap = ensureOlmSessionsForDevicesAction.handle(devicesByUser)
|
||||
val body = request.requestBody
|
||||
val olmSessionResult = usersDeviceMap.getObject(userId, deviceId)
|
||||
if (olmSessionResult?.sessionId == null) {
|
||||
// no session with this device, probably because there
|
||||
// were no one-time keys.
|
||||
return@mapCatching
|
||||
}
|
||||
Timber.v("## shareKeysWithDevice() : sharing keys for session" +
|
||||
" ${body?.senderKey}|${body?.sessionId} with device $userId:$deviceId")
|
||||
|
||||
val payloadJson = mutableMapOf<String, Any>("type" to EventType.FORWARDED_ROOM_KEY)
|
||||
val payloadJson = mutableMapOf<String, Any>("type" to EventType.FORWARDED_ROOM_KEY)
|
||||
runCatching { olmDevice.getInboundGroupSession(body?.sessionId, body?.senderKey, body?.roomId) }
|
||||
.fold(
|
||||
{
|
||||
// TODO
|
||||
payloadJson["content"] = it.exportKeys()
|
||||
?: ""
|
||||
},
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
olmDevice.getInboundGroupSession(body?.sessionId, body?.senderKey, body?.roomId)
|
||||
.fold(
|
||||
{
|
||||
// TODO
|
||||
},
|
||||
{
|
||||
// TODO
|
||||
payloadJson["content"] = it.exportKeys() ?: ""
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo))
|
||||
val sendToDeviceMap = MXUsersDevicesMap<Any>()
|
||||
sendToDeviceMap.setObject(userId, deviceId, encodedPayload)
|
||||
Timber.v("## shareKeysWithDevice() : sending to $userId:$deviceId")
|
||||
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap)
|
||||
sendToDeviceTask.execute(sendToDeviceParams)
|
||||
}
|
||||
val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo))
|
||||
val sendToDeviceMap = MXUsersDevicesMap<Any>()
|
||||
sendToDeviceMap.setObject(userId, deviceId, encodedPayload)
|
||||
Timber.v("## shareKeysWithDevice() : sending to $userId:$deviceId")
|
||||
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap)
|
||||
sendToDeviceTask.execute(sendToDeviceParams)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,6 @@ import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevi
|
||||
import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter
|
||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -19,7 +19,6 @@
|
||||
package im.vector.matrix.android.internal.crypto.algorithms.megolm
|
||||
|
||||
import android.text.TextUtils
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
import im.vector.matrix.android.api.session.crypto.MXCryptoError
|
||||
import im.vector.matrix.android.api.session.events.model.Content
|
||||
@ -69,12 +68,10 @@ internal class MXMegolmEncryption(
|
||||
|
||||
override suspend fun encryptEventContent(eventContent: Content,
|
||||
eventType: String,
|
||||
userIds: List<String>): Try<Content> {
|
||||
return getDevicesInRoom(userIds)
|
||||
.flatMap { ensureOutboundSession(it) }
|
||||
.flatMap {
|
||||
encryptContent(it, eventType, eventContent)
|
||||
}
|
||||
userIds: List<String>): Content {
|
||||
val devices = getDevicesInRoom(userIds)
|
||||
val outboundSession = ensureOutboundSession(devices)
|
||||
return encryptContent(outboundSession, eventType, eventContent)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -101,7 +98,7 @@ internal class MXMegolmEncryption(
|
||||
*
|
||||
* @param devicesInRoom the devices list
|
||||
*/
|
||||
private suspend fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap<MXDeviceInfo>): Try<MXOutboundSessionInfo> {
|
||||
private suspend fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap<MXDeviceInfo>): MXOutboundSessionInfo {
|
||||
var session = outboundSession
|
||||
if (session == null
|
||||
// Need to make a brand new session?
|
||||
@ -126,7 +123,8 @@ internal class MXMegolmEncryption(
|
||||
}
|
||||
}
|
||||
}
|
||||
return shareKey(safeSession, shareMap).map { safeSession!! }
|
||||
shareKey(safeSession, shareMap)
|
||||
return safeSession
|
||||
}
|
||||
|
||||
/**
|
||||
@ -136,11 +134,11 @@ internal class MXMegolmEncryption(
|
||||
* @param devicesByUsers the devices map
|
||||
*/
|
||||
private suspend fun shareKey(session: MXOutboundSessionInfo,
|
||||
devicesByUsers: Map<String, List<MXDeviceInfo>>): Try<Unit> {
|
||||
devicesByUsers: Map<String, List<MXDeviceInfo>>) {
|
||||
// nothing to send, the task is done
|
||||
if (devicesByUsers.isEmpty()) {
|
||||
Timber.v("## shareKey() : nothing more to do")
|
||||
return Try.just(Unit)
|
||||
return
|
||||
}
|
||||
// reduce the map size to avoid request timeout when there are too many devices (Users size * devices per user)
|
||||
val subMap = HashMap<String, List<MXDeviceInfo>>()
|
||||
@ -157,11 +155,9 @@ internal class MXMegolmEncryption(
|
||||
}
|
||||
}
|
||||
Timber.v("## shareKey() ; userId $userIds")
|
||||
return shareUserDevicesKey(session, subMap)
|
||||
.flatMap {
|
||||
val remainingDevices = devicesByUsers.filterKeys { userIds.contains(it).not() }
|
||||
shareKey(session, remainingDevices)
|
||||
}
|
||||
shareUserDevicesKey(session, subMap)
|
||||
val remainingDevices = devicesByUsers.filterKeys { userIds.contains(it).not() }
|
||||
shareKey(session, remainingDevices)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -172,7 +168,7 @@ internal class MXMegolmEncryption(
|
||||
* @param callback the asynchronous callback
|
||||
*/
|
||||
private suspend fun shareUserDevicesKey(session: MXOutboundSessionInfo,
|
||||
devicesByUser: Map<String, List<MXDeviceInfo>>): Try<Unit> {
|
||||
devicesByUser: Map<String, List<MXDeviceInfo>>) {
|
||||
val sessionKey = olmDevice.getSessionKey(session.sessionId)
|
||||
val chainIndex = olmDevice.getMessageIndex(session.sessionId)
|
||||
|
||||
@ -190,94 +186,86 @@ internal class MXMegolmEncryption(
|
||||
var t0 = System.currentTimeMillis()
|
||||
Timber.v("## shareUserDevicesKey() : starts")
|
||||
|
||||
return ensureOlmSessionsForDevicesAction.handle(devicesByUser)
|
||||
.flatMap {
|
||||
Timber.v("## shareUserDevicesKey() : ensureOlmSessionsForDevices succeeds after "
|
||||
+ (System.currentTimeMillis() - t0) + " ms")
|
||||
val contentMap = MXUsersDevicesMap<Any>()
|
||||
var haveTargets = false
|
||||
val userIds = it.userIds
|
||||
for (userId in userIds) {
|
||||
val devicesToShareWith = devicesByUser[userId]
|
||||
for ((deviceID) in devicesToShareWith!!) {
|
||||
val sessionResult = it.getObject(userId, deviceID)
|
||||
if (sessionResult?.sessionId == null) {
|
||||
// no session with this device, probably because there
|
||||
// were no one-time keys.
|
||||
//
|
||||
// we could send them a to_device message anyway, as a
|
||||
// signal that they have missed out on the key sharing
|
||||
// message because of the lack of keys, but there's not
|
||||
// much point in that really; it will mostly serve to clog
|
||||
// up to_device inboxes.
|
||||
//
|
||||
// ensureOlmSessionsForUsers has already done the logging,
|
||||
// so just skip it.
|
||||
continue
|
||||
}
|
||||
Timber.v("## shareUserDevicesKey() : Sharing keys with device $userId:$deviceID")
|
||||
//noinspection ArraysAsListWithZeroOrOneArgument,ArraysAsListWithZeroOrOneArgument
|
||||
contentMap.setObject(userId, deviceID, messageEncrypter.encryptMessage(payload, Arrays.asList(sessionResult.deviceInfo)))
|
||||
haveTargets = true
|
||||
}
|
||||
}
|
||||
if (haveTargets) {
|
||||
t0 = System.currentTimeMillis()
|
||||
Timber.v("## shareUserDevicesKey() : has target")
|
||||
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, contentMap)
|
||||
sendToDeviceTask.execute(sendToDeviceParams)
|
||||
.map {
|
||||
Timber.v("## shareUserDevicesKey() : sendToDevice succeeds after "
|
||||
+ (System.currentTimeMillis() - t0) + " ms")
|
||||
|
||||
// Add the devices we have shared with to session.sharedWithDevices.
|
||||
// we deliberately iterate over devicesByUser (ie, the devices we
|
||||
// attempted to share with) rather than the contentMap (those we did
|
||||
// share with), because we don't want to try to claim a one-time-key
|
||||
// for dead devices on every message.
|
||||
for (userId in devicesByUser.keys) {
|
||||
val devicesToShareWith = devicesByUser[userId]
|
||||
for ((deviceId) in devicesToShareWith!!) {
|
||||
session.sharedWithDevices.setObject(userId, deviceId, chainIndex)
|
||||
}
|
||||
}
|
||||
Unit
|
||||
}
|
||||
} else {
|
||||
Timber.v("## shareUserDevicesKey() : no need to sharekey")
|
||||
Try.just(Unit)
|
||||
}
|
||||
val results = ensureOlmSessionsForDevicesAction.handle(devicesByUser)
|
||||
Timber.v("## shareUserDevicesKey() : ensureOlmSessionsForDevices succeeds after "
|
||||
+ (System.currentTimeMillis() - t0) + " ms")
|
||||
val contentMap = MXUsersDevicesMap<Any>()
|
||||
var haveTargets = false
|
||||
val userIds = results.userIds
|
||||
for (userId in userIds) {
|
||||
val devicesToShareWith = devicesByUser[userId]
|
||||
for ((deviceID) in devicesToShareWith!!) {
|
||||
val sessionResult = results.getObject(userId, deviceID)
|
||||
if (sessionResult?.sessionId == null) {
|
||||
// no session with this device, probably because there
|
||||
// were no one-time keys.
|
||||
//
|
||||
// we could send them a to_device message anyway, as a
|
||||
// signal that they have missed out on the key sharing
|
||||
// message because of the lack of keys, but there's not
|
||||
// much point in that really; it will mostly serve to clog
|
||||
// up to_device inboxes.
|
||||
//
|
||||
// ensureOlmSessionsForUsers has already done the logging,
|
||||
// so just skip it.
|
||||
continue
|
||||
}
|
||||
Timber.v("## shareUserDevicesKey() : Sharing keys with device $userId:$deviceID")
|
||||
//noinspection ArraysAsListWithZeroOrOneArgument,ArraysAsListWithZeroOrOneArgument
|
||||
contentMap.setObject(userId, deviceID, messageEncrypter.encryptMessage(payload, Arrays.asList(sessionResult.deviceInfo)))
|
||||
haveTargets = true
|
||||
}
|
||||
}
|
||||
if (haveTargets) {
|
||||
t0 = System.currentTimeMillis()
|
||||
Timber.v("## shareUserDevicesKey() : has target")
|
||||
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, contentMap)
|
||||
sendToDeviceTask.execute(sendToDeviceParams)
|
||||
Timber.v("## shareUserDevicesKey() : sendToDevice succeeds after "
|
||||
+ (System.currentTimeMillis() - t0) + " ms")
|
||||
|
||||
// Add the devices we have shared with to session.sharedWithDevices.
|
||||
// we deliberately iterate over devicesByUser (ie, the devices we
|
||||
// attempted to share with) rather than the contentMap (those we did
|
||||
// share with), because we don't want to try to claim a one-time-key
|
||||
// for dead devices on every message.
|
||||
for (userId in devicesByUser.keys) {
|
||||
val devicesToShareWith = devicesByUser[userId]
|
||||
for ((deviceId) in devicesToShareWith!!) {
|
||||
session.sharedWithDevices.setObject(userId, deviceId, chainIndex)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Timber.v("## shareUserDevicesKey() : no need to sharekey")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* process the pending encryptions
|
||||
*/
|
||||
private fun encryptContent(session: MXOutboundSessionInfo, eventType: String, eventContent: Content): Try<Content> {
|
||||
return Try<Content> {
|
||||
// Everything is in place, encrypt all pending events
|
||||
val payloadJson = HashMap<String, Any>()
|
||||
payloadJson["room_id"] = roomId
|
||||
payloadJson["type"] = eventType
|
||||
payloadJson["content"] = eventContent
|
||||
private fun encryptContent(session: MXOutboundSessionInfo, eventType: String, eventContent: Content): Content {
|
||||
// Everything is in place, encrypt all pending events
|
||||
val payloadJson = HashMap<String, Any>()
|
||||
payloadJson["room_id"] = roomId
|
||||
payloadJson["type"] = eventType
|
||||
payloadJson["content"] = eventContent
|
||||
|
||||
// Get canonical Json from
|
||||
// Get canonical Json from
|
||||
|
||||
val payloadString = convertToUTF8(JsonCanonicalizer.getCanonicalJson(Map::class.java, payloadJson))
|
||||
val ciphertext = olmDevice.encryptGroupMessage(session.sessionId, payloadString!!)
|
||||
val payloadString = convertToUTF8(JsonCanonicalizer.getCanonicalJson(Map::class.java, payloadJson))
|
||||
val ciphertext = olmDevice.encryptGroupMessage(session.sessionId, payloadString!!)
|
||||
|
||||
val map = HashMap<String, Any>()
|
||||
map["algorithm"] = MXCRYPTO_ALGORITHM_MEGOLM
|
||||
map["sender_key"] = olmDevice.deviceCurve25519Key!!
|
||||
map["ciphertext"] = ciphertext!!
|
||||
map["session_id"] = session.sessionId
|
||||
val map = HashMap<String, Any>()
|
||||
map["algorithm"] = MXCRYPTO_ALGORITHM_MEGOLM
|
||||
map["sender_key"] = olmDevice.deviceCurve25519Key!!
|
||||
map["ciphertext"] = ciphertext!!
|
||||
map["session_id"] = session.sessionId
|
||||
|
||||
// Include our device ID so that recipients can send us a
|
||||
// m.new_device message if they don't have our session key.
|
||||
map["device_id"] = credentials.deviceId!!
|
||||
session.useCount++
|
||||
map
|
||||
}
|
||||
// Include our device ID so that recipients can send us a
|
||||
// m.new_device message if they don't have our session key.
|
||||
map["device_id"] = credentials.deviceId!!
|
||||
session.useCount++
|
||||
return map
|
||||
}
|
||||
|
||||
/**
|
||||
@ -287,50 +275,47 @@ internal class MXMegolmEncryption(
|
||||
* @param userIds the user ids whose devices must be checked.
|
||||
* @param callback the asynchronous callback
|
||||
*/
|
||||
private suspend fun getDevicesInRoom(userIds: List<String>): Try<MXUsersDevicesMap<MXDeviceInfo>> {
|
||||
private suspend fun getDevicesInRoom(userIds: List<String>): MXUsersDevicesMap<MXDeviceInfo> {
|
||||
// We are happy to use a cached version here: we assume that if we already
|
||||
// have a list of the user's devices, then we already share an e2e room
|
||||
// with them, which means that they will have announced any new devices via
|
||||
// an m.new_device.
|
||||
return deviceListManager
|
||||
.downloadKeys(userIds, false)
|
||||
.flatMap {
|
||||
val encryptToVerifiedDevicesOnly = cryptoStore.getGlobalBlacklistUnverifiedDevices()
|
||||
|| cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId)
|
||||
val keys = deviceListManager.downloadKeys(userIds, false)
|
||||
val encryptToVerifiedDevicesOnly = cryptoStore.getGlobalBlacklistUnverifiedDevices()
|
||||
|| cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId)
|
||||
|
||||
val devicesInRoom = MXUsersDevicesMap<MXDeviceInfo>()
|
||||
val unknownDevices = MXUsersDevicesMap<MXDeviceInfo>()
|
||||
val devicesInRoom = MXUsersDevicesMap<MXDeviceInfo>()
|
||||
val unknownDevices = MXUsersDevicesMap<MXDeviceInfo>()
|
||||
|
||||
for (userId in it.userIds) {
|
||||
val deviceIds = it.getUserDeviceIds(userId) ?: continue
|
||||
for (deviceId in deviceIds) {
|
||||
val deviceInfo = it.getObject(userId, deviceId) ?: continue
|
||||
if (warnOnUnknownDevicesRepository.warnOnUnknownDevices() && deviceInfo.isUnknown) {
|
||||
// The device is not yet known by the user
|
||||
unknownDevices.setObject(userId, deviceId, deviceInfo)
|
||||
continue
|
||||
}
|
||||
if (deviceInfo.isBlocked) {
|
||||
// Remove any blocked devices
|
||||
continue
|
||||
}
|
||||
|
||||
if (!deviceInfo.isVerified && encryptToVerifiedDevicesOnly) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (TextUtils.equals(deviceInfo.identityKey(), olmDevice.deviceCurve25519Key)) {
|
||||
// Don't bother sending to ourself
|
||||
continue
|
||||
}
|
||||
devicesInRoom.setObject(userId, deviceId, deviceInfo)
|
||||
}
|
||||
}
|
||||
if (unknownDevices.isEmpty) {
|
||||
Try.just(devicesInRoom)
|
||||
} else {
|
||||
Try.Failure(MXCryptoError.UnknownDevice(unknownDevices))
|
||||
}
|
||||
for (userId in keys.userIds) {
|
||||
val deviceIds = keys.getUserDeviceIds(userId) ?: continue
|
||||
for (deviceId in deviceIds) {
|
||||
val deviceInfo = keys.getObject(userId, deviceId) ?: continue
|
||||
if (warnOnUnknownDevicesRepository.warnOnUnknownDevices() && deviceInfo.isUnknown) {
|
||||
// The device is not yet known by the user
|
||||
unknownDevices.setObject(userId, deviceId, deviceInfo)
|
||||
continue
|
||||
}
|
||||
if (deviceInfo.isBlocked) {
|
||||
// Remove any blocked devices
|
||||
continue
|
||||
}
|
||||
|
||||
if (!deviceInfo.isVerified && encryptToVerifiedDevicesOnly) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (TextUtils.equals(deviceInfo.identityKey(), olmDevice.deviceCurve25519Key)) {
|
||||
// Don't bother sending to ourself
|
||||
continue
|
||||
}
|
||||
devicesInRoom.setObject(userId, deviceId, deviceInfo)
|
||||
}
|
||||
}
|
||||
if (unknownDevices.isEmpty) {
|
||||
return devicesInRoom
|
||||
} else {
|
||||
throw MXCryptoError.UnknownDevice(unknownDevices)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.algorithms.olm
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
import im.vector.matrix.android.api.session.crypto.MXCryptoError
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
@ -41,29 +40,28 @@ internal class MXOlmDecryption(
|
||||
private val credentials: Credentials)
|
||||
: IMXDecrypting {
|
||||
|
||||
override suspend fun decryptEvent(event: Event, timeline: String): Try<MXEventDecryptionResult> {
|
||||
override suspend fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult {
|
||||
val olmEventContent = event.content.toModel<OlmEventContent>() ?: run {
|
||||
Timber.e("## decryptEvent() : bad event format")
|
||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_EVENT_FORMAT,
|
||||
MXCryptoError.BAD_EVENT_FORMAT_TEXT_REASON))
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_EVENT_FORMAT,
|
||||
MXCryptoError.BAD_EVENT_FORMAT_TEXT_REASON)
|
||||
}
|
||||
|
||||
val cipherText = olmEventContent.ciphertext ?: run {
|
||||
Timber.e("## decryptEvent() : missing cipher text")
|
||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_CIPHER_TEXT,
|
||||
MXCryptoError.MISSING_CIPHER_TEXT_REASON))
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_CIPHER_TEXT,
|
||||
MXCryptoError.MISSING_CIPHER_TEXT_REASON)
|
||||
}
|
||||
|
||||
val senderKey = olmEventContent.senderKey ?: run {
|
||||
Timber.e("## decryptEvent() : missing sender key")
|
||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_SENDER_KEY,
|
||||
MXCryptoError.MISSING_SENDER_KEY_TEXT_REASON))
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_SENDER_KEY,
|
||||
MXCryptoError.MISSING_SENDER_KEY_TEXT_REASON)
|
||||
}
|
||||
|
||||
val messageAny = cipherText[olmDevice.deviceCurve25519Key] ?: run {
|
||||
Timber.e("## decryptEvent() : our device ${olmDevice.deviceCurve25519Key} is not included in recipients")
|
||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.NOT_INCLUDE_IN_RECIPIENTS,
|
||||
MXCryptoError.NOT_INCLUDED_IN_RECIPIENT_REASON))
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.NOT_INCLUDE_IN_RECIPIENTS, MXCryptoError.NOT_INCLUDED_IN_RECIPIENT_REASON)
|
||||
}
|
||||
|
||||
// The message for myUser
|
||||
@ -73,14 +71,12 @@ internal class MXOlmDecryption(
|
||||
|
||||
if (decryptedPayload == null) {
|
||||
Timber.e("## decryptEvent() Failed to decrypt Olm event (id= ${event.eventId} from $senderKey")
|
||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE,
|
||||
MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON))
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)
|
||||
}
|
||||
val payloadString = convertFromUTF8(decryptedPayload)
|
||||
if (payloadString == null) {
|
||||
Timber.e("## decryptEvent() Failed to decrypt Olm event (id= ${event.eventId} from $senderKey")
|
||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE,
|
||||
MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON))
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)
|
||||
}
|
||||
|
||||
val adapter = MoshiProvider.providesMoshi().adapter<JsonDict>(JSON_DICT_PARAMETERIZED_TYPE)
|
||||
@ -88,73 +84,70 @@ internal class MXOlmDecryption(
|
||||
|
||||
if (payload == null) {
|
||||
Timber.e("## decryptEvent failed : null payload")
|
||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT,
|
||||
MXCryptoError.MISSING_CIPHER_TEXT_REASON))
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT, MXCryptoError.MISSING_CIPHER_TEXT_REASON)
|
||||
}
|
||||
|
||||
val olmPayloadContent = OlmPayloadContent.fromJsonString(payloadString) ?: run {
|
||||
Timber.e("## decryptEvent() : bad olmPayloadContent format")
|
||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_DECRYPTED_FORMAT,
|
||||
MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON))
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_DECRYPTED_FORMAT, MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON)
|
||||
}
|
||||
|
||||
if (olmPayloadContent.recipient.isNullOrBlank()) {
|
||||
val reason = String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "recipient")
|
||||
Timber.e("## decryptEvent() : $reason")
|
||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY,
|
||||
reason))
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY, reason)
|
||||
}
|
||||
|
||||
if (olmPayloadContent.recipient != credentials.userId) {
|
||||
Timber.e("## decryptEvent() : Event ${event.eventId}:" +
|
||||
" Intended recipient ${olmPayloadContent.recipient} does not match our id ${credentials.userId}")
|
||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_RECIPIENT,
|
||||
String.format(MXCryptoError.BAD_RECIPIENT_REASON, olmPayloadContent.recipient)))
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_RECIPIENT,
|
||||
String.format(MXCryptoError.BAD_RECIPIENT_REASON, olmPayloadContent.recipient))
|
||||
}
|
||||
|
||||
val recipientKeys = olmPayloadContent.recipient_keys ?: run {
|
||||
Timber.e("## decryptEvent() : Olm event (id=${event.eventId}) contains no 'recipient_keys' property; cannot prevent unknown-key attack")
|
||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY,
|
||||
String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "recipient_keys")))
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY,
|
||||
String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "recipient_keys"))
|
||||
}
|
||||
|
||||
val ed25519 = recipientKeys["ed25519"]
|
||||
|
||||
if (ed25519 != olmDevice.deviceEd25519Key) {
|
||||
Timber.e("## decryptEvent() : Event ${event.eventId}: Intended recipient ed25519 key $ed25519 did not match ours")
|
||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_RECIPIENT_KEY,
|
||||
MXCryptoError.BAD_RECIPIENT_KEY_REASON))
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_RECIPIENT_KEY,
|
||||
MXCryptoError.BAD_RECIPIENT_KEY_REASON)
|
||||
}
|
||||
|
||||
if (olmPayloadContent.sender.isNullOrBlank()) {
|
||||
Timber.e("## decryptEvent() : Olm event (id=${event.eventId}) contains no 'sender' property; cannot prevent unknown-key attack")
|
||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY,
|
||||
String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "sender")))
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY,
|
||||
String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "sender"))
|
||||
}
|
||||
|
||||
if (olmPayloadContent.sender != event.senderId) {
|
||||
Timber.e("Event ${event.eventId}: original sender ${olmPayloadContent.sender} does not match reported sender ${event.senderId}")
|
||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.FORWARDED_MESSAGE,
|
||||
String.format(MXCryptoError.FORWARDED_MESSAGE_REASON, olmPayloadContent.sender)))
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.FORWARDED_MESSAGE,
|
||||
String.format(MXCryptoError.FORWARDED_MESSAGE_REASON, olmPayloadContent.sender))
|
||||
}
|
||||
|
||||
if (olmPayloadContent.room_id != event.roomId) {
|
||||
Timber.e("## decryptEvent() : Event ${event.eventId}: original room ${olmPayloadContent.room_id} does not match reported room ${event.roomId}")
|
||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ROOM,
|
||||
String.format(MXCryptoError.BAD_ROOM_REASON, olmPayloadContent.room_id)))
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ROOM,
|
||||
String.format(MXCryptoError.BAD_ROOM_REASON, olmPayloadContent.room_id))
|
||||
}
|
||||
|
||||
val keys = olmPayloadContent.keys ?: run {
|
||||
Timber.e("## decryptEvent failed : null keys")
|
||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT,
|
||||
MXCryptoError.MISSING_CIPHER_TEXT_REASON))
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT,
|
||||
MXCryptoError.MISSING_CIPHER_TEXT_REASON)
|
||||
}
|
||||
|
||||
return Try.just(MXEventDecryptionResult(
|
||||
return MXEventDecryptionResult(
|
||||
clearEvent = payload,
|
||||
senderCurve25519Key = senderKey,
|
||||
claimedEd25519Key = keys["ed25519"]
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -18,7 +18,6 @@ package im.vector.matrix.android.internal.crypto.algorithms.olm
|
||||
|
||||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
import im.vector.matrix.android.internal.crypto.MXOlmDevice
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class MXOlmDecryptionFactory @Inject constructor(private val olmDevice: MXOlmDevice,
|
||||
|
@ -19,7 +19,6 @@
|
||||
package im.vector.matrix.android.internal.crypto.algorithms.olm
|
||||
|
||||
import android.text.TextUtils
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.api.session.events.model.Content
|
||||
import im.vector.matrix.android.api.session.events.model.toContent
|
||||
import im.vector.matrix.android.internal.crypto.DeviceListManager
|
||||
@ -40,37 +39,35 @@ internal class MXOlmEncryption(
|
||||
private val ensureOlmSessionsForUsersAction: EnsureOlmSessionsForUsersAction)
|
||||
: IMXEncrypting {
|
||||
|
||||
override suspend fun encryptEventContent(eventContent: Content, eventType: String, userIds: List<String>): Try<Content> {
|
||||
override suspend fun encryptEventContent(eventContent: Content, eventType: String, userIds: List<String>): Content {
|
||||
// pick the list of recipients based on the membership list.
|
||||
//
|
||||
// TODO: there is a race condition here! What if a new user turns up
|
||||
return ensureSession(userIds)
|
||||
.map {
|
||||
val deviceInfos = ArrayList<MXDeviceInfo>()
|
||||
for (userId in userIds) {
|
||||
val devices = cryptoStore.getUserDevices(userId)?.values ?: emptyList()
|
||||
for (device in devices) {
|
||||
val key = device.identityKey()
|
||||
if (TextUtils.equals(key, olmDevice.deviceCurve25519Key)) {
|
||||
// Don't bother setting up session to ourself
|
||||
continue
|
||||
}
|
||||
if (device.isBlocked) {
|
||||
// Don't bother setting up sessions with blocked users
|
||||
continue
|
||||
}
|
||||
deviceInfos.add(device)
|
||||
}
|
||||
}
|
||||
|
||||
val messageMap = HashMap<String, Any>()
|
||||
messageMap["room_id"] = roomId
|
||||
messageMap["type"] = eventType
|
||||
messageMap["content"] = eventContent
|
||||
|
||||
messageEncrypter.encryptMessage(messageMap, deviceInfos)
|
||||
messageMap.toContent()!!
|
||||
ensureSession(userIds)
|
||||
val deviceInfos = ArrayList<MXDeviceInfo>()
|
||||
for (userId in userIds) {
|
||||
val devices = cryptoStore.getUserDevices(userId)?.values ?: emptyList()
|
||||
for (device in devices) {
|
||||
val key = device.identityKey()
|
||||
if (TextUtils.equals(key, olmDevice.deviceCurve25519Key)) {
|
||||
// Don't bother setting up session to ourself
|
||||
continue
|
||||
}
|
||||
if (device.isBlocked) {
|
||||
// Don't bother setting up sessions with blocked users
|
||||
continue
|
||||
}
|
||||
deviceInfos.add(device)
|
||||
}
|
||||
}
|
||||
|
||||
val messageMap = HashMap<String, Any>()
|
||||
messageMap["room_id"] = roomId
|
||||
messageMap["type"] = eventType
|
||||
messageMap["content"] = eventContent
|
||||
|
||||
messageEncrypter.encryptMessage(messageMap, deviceInfos)
|
||||
return messageMap.toContent()!!
|
||||
}
|
||||
|
||||
|
||||
@ -78,13 +75,9 @@ internal class MXOlmEncryption(
|
||||
* Ensure that the session
|
||||
*
|
||||
* @param users the user ids list
|
||||
* @param callback the asynchronous callback
|
||||
*/
|
||||
private suspend fun ensureSession(users: List<String>): Try<Unit> {
|
||||
return deviceListManager
|
||||
.downloadKeys(users, false)
|
||||
.flatMap { ensureOlmSessionsForUsersAction.handle(users) }
|
||||
.map { Unit }
|
||||
|
||||
private suspend fun ensureSession(users: List<String>) {
|
||||
deviceListManager.downloadKeys(users, false)
|
||||
ensureOlmSessionsForUsersAction.handle(users)
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ import im.vector.matrix.android.internal.crypto.MXOlmDevice
|
||||
import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForUsersAction
|
||||
import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter
|
||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -18,8 +18,6 @@ package im.vector.matrix.android.internal.crypto.api
|
||||
|
||||
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.*
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadBody
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.SendToDeviceBody
|
||||
import im.vector.matrix.android.internal.network.NetworkConstants
|
||||
import retrofit2.Call
|
||||
import retrofit2.http.*
|
||||
|
@ -51,7 +51,6 @@ import im.vector.matrix.android.internal.crypto.store.db.model.KeysBackupDataEnt
|
||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||
import im.vector.matrix.android.internal.extensions.foldToCallback
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.task.*
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.task.TaskThread
|
||||
@ -204,31 +203,32 @@ internal class KeysBackup @Inject constructor(
|
||||
keysBackupStateManager.state = KeysBackupState.Enabling
|
||||
|
||||
createKeysBackupVersionTask
|
||||
.configureWith(createKeysBackupVersionBody)
|
||||
.dispatchTo(object : MatrixCallback<KeysVersion> {
|
||||
override fun onSuccess(info: KeysVersion) {
|
||||
// Reset backup markers.
|
||||
cryptoStore.resetBackupMarkers()
|
||||
.configureWith(createKeysBackupVersionBody) {
|
||||
this.callback = object : MatrixCallback<KeysVersion> {
|
||||
override fun onSuccess(info: KeysVersion) {
|
||||
// Reset backup markers.
|
||||
cryptoStore.resetBackupMarkers()
|
||||
|
||||
val keyBackupVersion = KeysVersionResult()
|
||||
keyBackupVersion.algorithm = createKeysBackupVersionBody.algorithm
|
||||
keyBackupVersion.authData = createKeysBackupVersionBody.authData
|
||||
keyBackupVersion.version = info.version
|
||||
val keyBackupVersion = KeysVersionResult()
|
||||
keyBackupVersion.algorithm = createKeysBackupVersionBody.algorithm
|
||||
keyBackupVersion.authData = createKeysBackupVersionBody.authData
|
||||
keyBackupVersion.version = info.version
|
||||
|
||||
// We can consider that the server does not have keys yet
|
||||
keyBackupVersion.count = 0
|
||||
keyBackupVersion.hash = null
|
||||
// We can consider that the server does not have keys yet
|
||||
keyBackupVersion.count = 0
|
||||
keyBackupVersion.hash = null
|
||||
|
||||
enableKeysBackup(keyBackupVersion)
|
||||
enableKeysBackup(keyBackupVersion)
|
||||
|
||||
callback.onSuccess(info)
|
||||
callback.onSuccess(info)
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
keysBackupStateManager.state = KeysBackupState.Disabled
|
||||
callback.onFailure(failure)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
keysBackupStateManager.state = KeysBackupState.Disabled
|
||||
callback.onFailure(failure)
|
||||
}
|
||||
})
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
@ -243,27 +243,29 @@ internal class KeysBackup @Inject constructor(
|
||||
keysBackupStateManager.state = KeysBackupState.Unknown
|
||||
}
|
||||
|
||||
deleteBackupTask.configureWith(DeleteBackupTask.Params(version))
|
||||
.dispatchTo(object : MatrixCallback<Unit> {
|
||||
private fun eventuallyRestartBackup() {
|
||||
// Do not stay in KeysBackupState.Unknown but check what is available on the homeserver
|
||||
if (state == KeysBackupState.Unknown) {
|
||||
checkAndStartKeysBackup()
|
||||
deleteBackupTask
|
||||
.configureWith(DeleteBackupTask.Params(version)) {
|
||||
this.callback = object : MatrixCallback<Unit> {
|
||||
private fun eventuallyRestartBackup() {
|
||||
// Do not stay in KeysBackupState.Unknown but check what is available on the homeserver
|
||||
if (state == KeysBackupState.Unknown) {
|
||||
checkAndStartKeysBackup()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSuccess(data: Unit) {
|
||||
eventuallyRestartBackup()
|
||||
|
||||
uiHandler.post { callback?.onSuccess(Unit) }
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
eventuallyRestartBackup()
|
||||
|
||||
uiHandler.post { callback?.onFailure(failure) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSuccess(data: Unit) {
|
||||
eventuallyRestartBackup()
|
||||
|
||||
uiHandler.post { callback?.onSuccess(Unit) }
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
eventuallyRestartBackup()
|
||||
|
||||
uiHandler.post { callback?.onFailure(failure) }
|
||||
}
|
||||
})
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
}
|
||||
@ -355,15 +357,14 @@ internal class KeysBackup @Inject constructor(
|
||||
callback: MatrixCallback<KeysBackupVersionTrust>) {
|
||||
// TODO Validate with François that this is correct
|
||||
object : Task<KeysVersionResult, KeysBackupVersionTrust> {
|
||||
override suspend fun execute(params: KeysVersionResult): Try<KeysBackupVersionTrust> {
|
||||
return Try {
|
||||
getKeysBackupTrustBg(params)
|
||||
}
|
||||
override suspend fun execute(params: KeysVersionResult): KeysBackupVersionTrust {
|
||||
return getKeysBackupTrustBg(params)
|
||||
}
|
||||
}
|
||||
.configureWith(keysBackupVersion)
|
||||
.dispatchTo(callback)
|
||||
.executeOn(TaskThread.COMPUTATION)
|
||||
.configureWith(keysBackupVersion) {
|
||||
this.callback = callback
|
||||
this.executionThread = TaskThread.COMPUTATION
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
@ -454,7 +455,8 @@ internal class KeysBackup @Inject constructor(
|
||||
val myUserId = credentials.userId
|
||||
|
||||
// Get current signatures, or create an empty set
|
||||
val myUserSignatures = authData.signatures?.get(myUserId)?.toMutableMap() ?: HashMap()
|
||||
val myUserSignatures = authData.signatures?.get(myUserId)?.toMutableMap()
|
||||
?: HashMap()
|
||||
|
||||
if (trust) {
|
||||
// Add current device signature
|
||||
@ -492,27 +494,28 @@ internal class KeysBackup @Inject constructor(
|
||||
|
||||
// And send it to the homeserver
|
||||
updateKeysBackupVersionTask
|
||||
.configureWith(UpdateKeysBackupVersionTask.Params(keysBackupVersion.version!!, updateKeysBackupVersionBody))
|
||||
.dispatchTo(object : MatrixCallback<Unit> {
|
||||
override fun onSuccess(data: Unit) {
|
||||
// Relaunch the state machine on this updated backup version
|
||||
val newKeysBackupVersion = KeysVersionResult()
|
||||
.configureWith(UpdateKeysBackupVersionTask.Params(keysBackupVersion.version!!, updateKeysBackupVersionBody)) {
|
||||
this.callback = object : MatrixCallback<Unit> {
|
||||
override fun onSuccess(data: Unit) {
|
||||
// Relaunch the state machine on this updated backup version
|
||||
val newKeysBackupVersion = KeysVersionResult()
|
||||
|
||||
newKeysBackupVersion.version = keysBackupVersion.version
|
||||
newKeysBackupVersion.algorithm = keysBackupVersion.algorithm
|
||||
newKeysBackupVersion.count = keysBackupVersion.count
|
||||
newKeysBackupVersion.hash = keysBackupVersion.hash
|
||||
newKeysBackupVersion.authData = updateKeysBackupVersionBody.authData
|
||||
newKeysBackupVersion.version = keysBackupVersion.version
|
||||
newKeysBackupVersion.algorithm = keysBackupVersion.algorithm
|
||||
newKeysBackupVersion.count = keysBackupVersion.count
|
||||
newKeysBackupVersion.hash = keysBackupVersion.hash
|
||||
newKeysBackupVersion.authData = updateKeysBackupVersionBody.authData
|
||||
|
||||
checkAndStartWithKeysBackupVersion(newKeysBackupVersion)
|
||||
checkAndStartWithKeysBackupVersion(newKeysBackupVersion)
|
||||
|
||||
callback.onSuccess(data)
|
||||
callback.onSuccess(data)
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
callback.onFailure(failure)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
callback.onFailure(failure)
|
||||
}
|
||||
})
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
}
|
||||
@ -758,49 +761,52 @@ internal class KeysBackup @Inject constructor(
|
||||
if (roomId != null && sessionId != null) {
|
||||
// Get key for the room and for the session
|
||||
getRoomSessionDataTask
|
||||
.configureWith(GetRoomSessionDataTask.Params(roomId, sessionId, version))
|
||||
.dispatchTo(object : MatrixCallback<KeyBackupData> {
|
||||
override fun onSuccess(data: KeyBackupData) {
|
||||
// Convert to KeysBackupData
|
||||
val keysBackupData = KeysBackupData()
|
||||
keysBackupData.roomIdToRoomKeysBackupData = HashMap()
|
||||
val roomKeysBackupData = RoomKeysBackupData()
|
||||
roomKeysBackupData.sessionIdToKeyBackupData = HashMap()
|
||||
roomKeysBackupData.sessionIdToKeyBackupData[sessionId] = data
|
||||
keysBackupData.roomIdToRoomKeysBackupData[roomId] = roomKeysBackupData
|
||||
.configureWith(GetRoomSessionDataTask.Params(roomId, sessionId, version)) {
|
||||
this.callback = object : MatrixCallback<KeyBackupData> {
|
||||
override fun onSuccess(data: KeyBackupData) {
|
||||
// Convert to KeysBackupData
|
||||
val keysBackupData = KeysBackupData()
|
||||
keysBackupData.roomIdToRoomKeysBackupData = HashMap()
|
||||
val roomKeysBackupData = RoomKeysBackupData()
|
||||
roomKeysBackupData.sessionIdToKeyBackupData = HashMap()
|
||||
roomKeysBackupData.sessionIdToKeyBackupData[sessionId] = data
|
||||
keysBackupData.roomIdToRoomKeysBackupData[roomId] = roomKeysBackupData
|
||||
|
||||
callback.onSuccess(keysBackupData)
|
||||
}
|
||||
callback.onSuccess(keysBackupData)
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
callback.onFailure(failure)
|
||||
override fun onFailure(failure: Throwable) {
|
||||
callback.onFailure(failure)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
} else if (roomId != null) {
|
||||
// Get all keys for the room
|
||||
getRoomSessionsDataTask
|
||||
.configureWith(GetRoomSessionsDataTask.Params(roomId, version))
|
||||
.dispatchTo(object : MatrixCallback<RoomKeysBackupData> {
|
||||
override fun onSuccess(data: RoomKeysBackupData) {
|
||||
// Convert to KeysBackupData
|
||||
val keysBackupData = KeysBackupData()
|
||||
keysBackupData.roomIdToRoomKeysBackupData = HashMap()
|
||||
keysBackupData.roomIdToRoomKeysBackupData[roomId] = data
|
||||
.configureWith(GetRoomSessionsDataTask.Params(roomId, version)) {
|
||||
this.callback = object : MatrixCallback<RoomKeysBackupData> {
|
||||
override fun onSuccess(data: RoomKeysBackupData) {
|
||||
// Convert to KeysBackupData
|
||||
val keysBackupData = KeysBackupData()
|
||||
keysBackupData.roomIdToRoomKeysBackupData = HashMap()
|
||||
keysBackupData.roomIdToRoomKeysBackupData[roomId] = data
|
||||
|
||||
callback.onSuccess(keysBackupData)
|
||||
}
|
||||
callback.onSuccess(keysBackupData)
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
callback.onFailure(failure)
|
||||
override fun onFailure(failure: Throwable) {
|
||||
callback.onFailure(failure)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
} else {
|
||||
// Get all keys
|
||||
getSessionsDataTask
|
||||
.configureWith(GetSessionsDataTask.Params(version))
|
||||
.dispatchTo(callback)
|
||||
.configureWith(GetSessionsDataTask.Params(version)) {
|
||||
this.callback = callback
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
}
|
||||
@ -855,45 +861,47 @@ internal class KeysBackup @Inject constructor(
|
||||
override fun getVersion(version: String,
|
||||
callback: MatrixCallback<KeysVersionResult?>) {
|
||||
getKeysBackupVersionTask
|
||||
.configureWith(version)
|
||||
.dispatchTo(object : MatrixCallback<KeysVersionResult> {
|
||||
override fun onSuccess(data: KeysVersionResult) {
|
||||
callback.onSuccess(data)
|
||||
}
|
||||
.configureWith(version) {
|
||||
this.callback = object : MatrixCallback<KeysVersionResult> {
|
||||
override fun onSuccess(data: KeysVersionResult) {
|
||||
callback.onSuccess(data)
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
if (failure is Failure.ServerError
|
||||
&& failure.error.code == MatrixError.NOT_FOUND) {
|
||||
// Workaround because the homeserver currently returns M_NOT_FOUND when there is no key backup
|
||||
callback.onSuccess(null)
|
||||
} else {
|
||||
// Transmit the error
|
||||
callback.onFailure(failure)
|
||||
override fun onFailure(failure: Throwable) {
|
||||
if (failure is Failure.ServerError
|
||||
&& failure.error.code == MatrixError.NOT_FOUND) {
|
||||
// Workaround because the homeserver currently returns M_NOT_FOUND when there is no key backup
|
||||
callback.onSuccess(null)
|
||||
} else {
|
||||
// Transmit the error
|
||||
callback.onFailure(failure)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
override fun getCurrentVersion(callback: MatrixCallback<KeysVersionResult?>) {
|
||||
getKeysBackupLastVersionTask
|
||||
.toConfigurableTask()
|
||||
.dispatchTo(object : MatrixCallback<KeysVersionResult> {
|
||||
override fun onSuccess(data: KeysVersionResult) {
|
||||
callback.onSuccess(data)
|
||||
}
|
||||
.configureWith {
|
||||
this.callback = object : MatrixCallback<KeysVersionResult> {
|
||||
override fun onSuccess(data: KeysVersionResult) {
|
||||
callback.onSuccess(data)
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
if (failure is Failure.ServerError
|
||||
&& failure.error.code == MatrixError.NOT_FOUND) {
|
||||
// Workaround because the homeserver currently returns M_NOT_FOUND when there is no key backup
|
||||
callback.onSuccess(null)
|
||||
} else {
|
||||
// Transmit the error
|
||||
callback.onFailure(failure)
|
||||
override fun onFailure(failure: Throwable) {
|
||||
if (failure is Failure.ServerError
|
||||
&& failure.error.code == MatrixError.NOT_FOUND) {
|
||||
// Workaround because the homeserver currently returns M_NOT_FOUND when there is no key backup
|
||||
callback.onSuccess(null)
|
||||
} else {
|
||||
// Transmit the error
|
||||
callback.onFailure(failure)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
@ -1236,69 +1244,72 @@ internal class KeysBackup @Inject constructor(
|
||||
|
||||
Timber.v("backupKeys: 4 - Sending request")
|
||||
|
||||
// Make the request
|
||||
storeSessionDataTask
|
||||
.configureWith(StoreSessionsDataTask.Params(keysBackupVersion!!.version!!, keysBackupData))
|
||||
.dispatchTo(object : MatrixCallback<BackupKeysResult> {
|
||||
override fun onSuccess(data: BackupKeysResult) {
|
||||
uiHandler.post {
|
||||
Timber.v("backupKeys: 5a - Request complete")
|
||||
val sendingRequestCallback = object : MatrixCallback<BackupKeysResult> {
|
||||
override fun onSuccess(data: BackupKeysResult) {
|
||||
uiHandler.post {
|
||||
Timber.v("backupKeys: 5a - Request complete")
|
||||
|
||||
// Mark keys as backed up
|
||||
cryptoStore.markBackupDoneForInboundGroupSessions(olmInboundGroupSessionWrappers)
|
||||
// Mark keys as backed up
|
||||
cryptoStore.markBackupDoneForInboundGroupSessions(olmInboundGroupSessionWrappers)
|
||||
|
||||
if (olmInboundGroupSessionWrappers.size < KEY_BACKUP_SEND_KEYS_MAX_COUNT) {
|
||||
Timber.v("backupKeys: All keys have been backed up")
|
||||
onServerDataRetrieved(data.count, data.hash)
|
||||
if (olmInboundGroupSessionWrappers.size < KEY_BACKUP_SEND_KEYS_MAX_COUNT) {
|
||||
Timber.v("backupKeys: All keys have been backed up")
|
||||
onServerDataRetrieved(data.count, data.hash)
|
||||
|
||||
// Note: Changing state will trigger the call to backupAllGroupSessionsCallback.onSuccess()
|
||||
keysBackupStateManager.state = KeysBackupState.ReadyToBackUp
|
||||
} else {
|
||||
Timber.v("backupKeys: Continue to back up keys")
|
||||
keysBackupStateManager.state = KeysBackupState.WillBackUp
|
||||
// Note: Changing state will trigger the call to backupAllGroupSessionsCallback.onSuccess()
|
||||
keysBackupStateManager.state = KeysBackupState.ReadyToBackUp
|
||||
} else {
|
||||
Timber.v("backupKeys: Continue to back up keys")
|
||||
keysBackupStateManager.state = KeysBackupState.WillBackUp
|
||||
|
||||
backupKeys()
|
||||
}
|
||||
}
|
||||
backupKeys()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
if (failure is Failure.ServerError) {
|
||||
uiHandler.post {
|
||||
Timber.e(failure, "backupKeys: backupKeys failed.")
|
||||
override fun onFailure(failure: Throwable) {
|
||||
if (failure is Failure.ServerError) {
|
||||
uiHandler.post {
|
||||
Timber.e(failure, "backupKeys: backupKeys failed.")
|
||||
|
||||
when (failure.error.code) {
|
||||
MatrixError.NOT_FOUND,
|
||||
MatrixError.WRONG_ROOM_KEYS_VERSION -> {
|
||||
// Backup has been deleted on the server, or we are not using the last backup version
|
||||
keysBackupStateManager.state = KeysBackupState.WrongBackUpVersion
|
||||
backupAllGroupSessionsCallback?.onFailure(failure)
|
||||
resetBackupAllGroupSessionsListeners()
|
||||
resetKeysBackupData()
|
||||
keysBackupVersion = null
|
||||
|
||||
// Do not stay in KeysBackupState.WrongBackUpVersion but check what is available on the homeserver
|
||||
checkAndStartKeysBackup()
|
||||
}
|
||||
else ->
|
||||
// Come back to the ready state so that we will retry on the next received key
|
||||
keysBackupStateManager.state = KeysBackupState.ReadyToBackUp
|
||||
}
|
||||
}
|
||||
} else {
|
||||
uiHandler.post {
|
||||
when (failure.error.code) {
|
||||
MatrixError.NOT_FOUND,
|
||||
MatrixError.WRONG_ROOM_KEYS_VERSION -> {
|
||||
// Backup has been deleted on the server, or we are not using the last backup version
|
||||
keysBackupStateManager.state = KeysBackupState.WrongBackUpVersion
|
||||
backupAllGroupSessionsCallback?.onFailure(failure)
|
||||
resetBackupAllGroupSessionsListeners()
|
||||
resetKeysBackupData()
|
||||
keysBackupVersion = null
|
||||
|
||||
Timber.e("backupKeys: backupKeys failed.")
|
||||
|
||||
// Retry a bit later
|
||||
keysBackupStateManager.state = KeysBackupState.ReadyToBackUp
|
||||
maybeBackupKeys()
|
||||
// Do not stay in KeysBackupState.WrongBackUpVersion but check what is available on the homeserver
|
||||
checkAndStartKeysBackup()
|
||||
}
|
||||
else ->
|
||||
// Come back to the ready state so that we will retry on the next received key
|
||||
keysBackupStateManager.state = KeysBackupState.ReadyToBackUp
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
uiHandler.post {
|
||||
backupAllGroupSessionsCallback?.onFailure(failure)
|
||||
resetBackupAllGroupSessionsListeners()
|
||||
|
||||
Timber.e("backupKeys: backupKeys failed.")
|
||||
|
||||
// Retry a bit later
|
||||
keysBackupStateManager.state = KeysBackupState.ReadyToBackUp
|
||||
maybeBackupKeys()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make the request
|
||||
storeSessionDataTask
|
||||
.configureWith(StoreSessionsDataTask.Params(keysBackupVersion!!.version!!, keysBackupData)){
|
||||
this.callback = sendingRequestCallback
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.keysbackup.tasks
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.CreateKeysBackupVersionBody
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersion
|
||||
@ -30,7 +29,7 @@ internal class DefaultCreateKeysBackupVersionTask @Inject constructor(private va
|
||||
: CreateKeysBackupVersionTask {
|
||||
|
||||
|
||||
override suspend fun execute(params: CreateKeysBackupVersionBody): Try<KeysVersion> {
|
||||
override suspend fun execute(params: CreateKeysBackupVersionBody): KeysVersion {
|
||||
return executeRequest {
|
||||
apiCall = roomKeysApi.createKeysBackupVersion(params)
|
||||
}
|
||||
|
@ -16,10 +16,8 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.keysbackup.tasks
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -33,10 +31,9 @@ internal interface DeleteBackupTask : Task<DeleteBackupTask.Params, Unit> {
|
||||
internal class DefaultDeleteBackupTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
||||
: DeleteBackupTask {
|
||||
|
||||
override suspend fun execute(params: DeleteBackupTask.Params): Try<Unit> {
|
||||
override suspend fun execute(params: DeleteBackupTask.Params) {
|
||||
return executeRequest {
|
||||
apiCall = roomKeysApi.deleteBackup(
|
||||
params.version)
|
||||
apiCall = roomKeysApi.deleteBackup(params.version)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,10 +16,8 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.keysbackup.tasks
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -34,7 +32,7 @@ internal interface DeleteRoomSessionDataTask : Task<DeleteRoomSessionDataTask.Pa
|
||||
internal class DefaultDeleteRoomSessionDataTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
||||
: DeleteRoomSessionDataTask {
|
||||
|
||||
override suspend fun execute(params: DeleteRoomSessionDataTask.Params): Try<Unit> {
|
||||
override suspend fun execute(params: DeleteRoomSessionDataTask.Params) {
|
||||
return executeRequest {
|
||||
apiCall = roomKeysApi.deleteRoomSessionData(
|
||||
params.roomId,
|
||||
|
@ -16,10 +16,8 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.keysbackup.tasks
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -33,7 +31,7 @@ internal interface DeleteRoomSessionsDataTask : Task<DeleteRoomSessionsDataTask.
|
||||
internal class DefaultDeleteRoomSessionsDataTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
||||
: DeleteRoomSessionsDataTask {
|
||||
|
||||
override suspend fun execute(params: DeleteRoomSessionsDataTask.Params): Try<Unit> {
|
||||
override suspend fun execute(params: DeleteRoomSessionsDataTask.Params) {
|
||||
return executeRequest {
|
||||
apiCall = roomKeysApi.deleteRoomSessionsData(
|
||||
params.roomId,
|
||||
|
@ -16,10 +16,8 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.keysbackup.tasks
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -32,10 +30,9 @@ internal interface DeleteSessionsDataTask : Task<DeleteSessionsDataTask.Params,
|
||||
internal class DefaultDeleteSessionsDataTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
||||
: DeleteSessionsDataTask {
|
||||
|
||||
override suspend fun execute(params: DeleteSessionsDataTask.Params): Try<Unit> {
|
||||
override suspend fun execute(params: DeleteSessionsDataTask.Params) {
|
||||
return executeRequest {
|
||||
apiCall = roomKeysApi.deleteSessionsData(
|
||||
params.version)
|
||||
apiCall = roomKeysApi.deleteSessionsData(params.version)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.keysbackup.tasks
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
@ -29,7 +28,7 @@ internal class DefaultGetKeysBackupLastVersionTask @Inject constructor(private v
|
||||
: GetKeysBackupLastVersionTask {
|
||||
|
||||
|
||||
override suspend fun execute(params: Unit): Try<KeysVersionResult> {
|
||||
override suspend fun execute(params: Unit): KeysVersionResult {
|
||||
return executeRequest {
|
||||
apiCall = roomKeysApi.getKeysBackupLastVersion()
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.keysbackup.tasks
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
@ -29,7 +28,7 @@ internal class DefaultGetKeysBackupVersionTask @Inject constructor(private val r
|
||||
: GetKeysBackupVersionTask {
|
||||
|
||||
|
||||
override suspend fun execute(params: String): Try<KeysVersionResult> {
|
||||
override suspend fun execute(params: String): KeysVersionResult {
|
||||
return executeRequest {
|
||||
apiCall = roomKeysApi.getKeysBackupVersion(params)
|
||||
}
|
||||
|
@ -16,11 +16,9 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.keysbackup.tasks
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeyBackupData
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -35,7 +33,7 @@ internal interface GetRoomSessionDataTask : Task<GetRoomSessionDataTask.Params,
|
||||
internal class DefaultGetRoomSessionDataTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
||||
: GetRoomSessionDataTask {
|
||||
|
||||
override suspend fun execute(params: GetRoomSessionDataTask.Params): Try<KeyBackupData> {
|
||||
override suspend fun execute(params: GetRoomSessionDataTask.Params): KeyBackupData {
|
||||
return executeRequest {
|
||||
apiCall = roomKeysApi.getRoomSessionData(
|
||||
params.roomId,
|
||||
|
@ -16,11 +16,9 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.keysbackup.tasks
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.RoomKeysBackupData
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -35,7 +33,7 @@ internal interface GetRoomSessionsDataTask : Task<GetRoomSessionsDataTask.Params
|
||||
internal class DefaultGetRoomSessionsDataTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
||||
: GetRoomSessionsDataTask {
|
||||
|
||||
override suspend fun execute(params: GetRoomSessionsDataTask.Params): Try<RoomKeysBackupData> {
|
||||
override suspend fun execute(params: GetRoomSessionsDataTask.Params): RoomKeysBackupData {
|
||||
return executeRequest {
|
||||
apiCall = roomKeysApi.getRoomSessionsData(
|
||||
params.roomId,
|
||||
|
@ -16,11 +16,9 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.keysbackup.tasks
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysBackupData
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -33,10 +31,9 @@ internal interface GetSessionsDataTask : Task<GetSessionsDataTask.Params, KeysBa
|
||||
internal class DefaultGetSessionsDataTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
||||
: GetSessionsDataTask {
|
||||
|
||||
override suspend fun execute(params: GetSessionsDataTask.Params): Try<KeysBackupData> {
|
||||
override suspend fun execute(params: GetSessionsDataTask.Params): KeysBackupData {
|
||||
return executeRequest {
|
||||
apiCall = roomKeysApi.getSessionsData(
|
||||
params.version)
|
||||
apiCall = roomKeysApi.getSessionsData(params.version)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,12 +16,10 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.keysbackup.tasks
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeyBackupData
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.BackupKeysResult
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeyBackupData
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -37,7 +35,7 @@ internal interface StoreRoomSessionDataTask : Task<StoreRoomSessionDataTask.Para
|
||||
internal class DefaultStoreRoomSessionDataTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
||||
: StoreRoomSessionDataTask {
|
||||
|
||||
override suspend fun execute(params: StoreRoomSessionDataTask.Params): Try<BackupKeysResult> {
|
||||
override suspend fun execute(params: StoreRoomSessionDataTask.Params): BackupKeysResult {
|
||||
return executeRequest {
|
||||
apiCall = roomKeysApi.storeRoomSessionData(
|
||||
params.roomId,
|
||||
|
@ -16,12 +16,10 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.keysbackup.tasks
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.RoomKeysBackupData
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.BackupKeysResult
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.RoomKeysBackupData
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -36,7 +34,7 @@ internal interface StoreRoomSessionsDataTask : Task<StoreRoomSessionsDataTask.Pa
|
||||
internal class DefaultStoreRoomSessionsDataTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
||||
: StoreRoomSessionsDataTask {
|
||||
|
||||
override suspend fun execute(params: StoreRoomSessionsDataTask.Params): Try<BackupKeysResult> {
|
||||
override suspend fun execute(params: StoreRoomSessionsDataTask.Params): BackupKeysResult {
|
||||
return executeRequest {
|
||||
apiCall = roomKeysApi.storeRoomSessionsData(
|
||||
params.roomId,
|
||||
|
@ -16,12 +16,10 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.keysbackup.tasks
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysBackupData
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.BackupKeysResult
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysBackupData
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -35,7 +33,7 @@ internal interface StoreSessionsDataTask : Task<StoreSessionsDataTask.Params, Ba
|
||||
internal class DefaultStoreSessionsDataTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
||||
: StoreSessionsDataTask {
|
||||
|
||||
override suspend fun execute(params: StoreSessionsDataTask.Params): Try<BackupKeysResult> {
|
||||
override suspend fun execute(params: StoreSessionsDataTask.Params): BackupKeysResult {
|
||||
return executeRequest {
|
||||
apiCall = roomKeysApi.storeSessionsData(
|
||||
params.version,
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.keysbackup.tasks
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.UpdateKeysBackupVersionBody
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
@ -34,7 +33,7 @@ internal class DefaultUpdateKeysBackupVersionTask @Inject constructor(private va
|
||||
: UpdateKeysBackupVersionTask {
|
||||
|
||||
|
||||
override suspend fun execute(params: UpdateKeysBackupVersionTask.Params): Try<Unit> {
|
||||
override suspend fun execute(params: UpdateKeysBackupVersionTask.Params) {
|
||||
return executeRequest {
|
||||
apiCall = roomKeysApi.updateKeysBackupVersion(params.version, params.keysBackupVersionBody)
|
||||
}
|
||||
|
@ -16,8 +16,8 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.store.db
|
||||
|
||||
import io.realm.annotations.RealmModule
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.*
|
||||
import io.realm.annotations.RealmModule
|
||||
|
||||
/**
|
||||
* Realm module for Crypto store classes
|
||||
|
@ -16,10 +16,10 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.store.db.model
|
||||
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.PrimaryKey
|
||||
import im.vector.matrix.android.internal.crypto.store.db.deserializeFromRealm
|
||||
import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.PrimaryKey
|
||||
import org.matrix.olm.OlmAccount
|
||||
|
||||
internal open class CryptoMetadataEntity(
|
||||
|
@ -16,9 +16,9 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.store.db.model
|
||||
|
||||
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
|
||||
import im.vector.matrix.android.internal.crypto.store.db.deserializeFromRealm
|
||||
import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm
|
||||
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
|
||||
import io.realm.RealmObject
|
||||
import io.realm.RealmResults
|
||||
import io.realm.annotations.LinkingObjects
|
||||
|
@ -16,11 +16,11 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.store.db.model
|
||||
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.PrimaryKey
|
||||
import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper
|
||||
import im.vector.matrix.android.internal.crypto.store.db.deserializeFromRealm
|
||||
import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm
|
||||
import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.PrimaryKey
|
||||
|
||||
internal fun OlmInboundGroupSessionEntity.Companion.createPrimaryKey(sessionId: String?, senderKey: String?) = "$sessionId|$senderKey"
|
||||
|
||||
|
@ -16,10 +16,10 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.store.db.model
|
||||
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.PrimaryKey
|
||||
import im.vector.matrix.android.internal.crypto.store.db.deserializeFromRealm
|
||||
import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.PrimaryKey
|
||||
import org.matrix.olm.OlmSession
|
||||
|
||||
internal fun OlmSessionEntity.Companion.createPrimaryKey(sessionId: String, deviceKey: String) = "$sessionId|$deviceKey"
|
||||
|
@ -17,9 +17,9 @@
|
||||
package im.vector.matrix.android.internal.crypto.store.db.model
|
||||
|
||||
import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
|
||||
import im.vector.matrix.android.internal.crypto.store.db.deserializeFromRealm
|
||||
import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.PrimaryKey
|
||||
|
||||
|
@ -16,11 +16,11 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.store.db.query
|
||||
|
||||
import io.realm.Realm
|
||||
import io.realm.kotlin.where
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.DeviceInfoEntity
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.DeviceInfoEntityFields
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.createPrimaryKey
|
||||
import io.realm.Realm
|
||||
import io.realm.kotlin.where
|
||||
|
||||
/**
|
||||
* Get or create a device info
|
||||
|
@ -16,10 +16,10 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.store.db.query
|
||||
|
||||
import io.realm.Realm
|
||||
import io.realm.kotlin.where
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.UserEntity
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.UserEntityFields
|
||||
import io.realm.Realm
|
||||
import io.realm.kotlin.where
|
||||
|
||||
/**
|
||||
* Get or create a user
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.tasks
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.internal.crypto.api.CryptoApi
|
||||
import im.vector.matrix.android.internal.crypto.model.MXKey
|
||||
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
||||
@ -37,35 +36,30 @@ internal interface ClaimOneTimeKeysForUsersDeviceTask : Task<ClaimOneTimeKeysFor
|
||||
internal class DefaultClaimOneTimeKeysForUsersDevice @Inject constructor(private val cryptoApi: CryptoApi)
|
||||
: ClaimOneTimeKeysForUsersDeviceTask {
|
||||
|
||||
override suspend fun execute(params: ClaimOneTimeKeysForUsersDeviceTask.Params): Try<MXUsersDevicesMap<MXKey>> {
|
||||
override suspend fun execute(params: ClaimOneTimeKeysForUsersDeviceTask.Params): MXUsersDevicesMap<MXKey> {
|
||||
val body = KeysClaimBody(oneTimeKeys = params.usersDevicesKeyTypesMap.map)
|
||||
|
||||
return executeRequest<KeysClaimResponse> {
|
||||
val keysClaimResponse = executeRequest<KeysClaimResponse> {
|
||||
apiCall = cryptoApi.claimOneTimeKeysForUsersDevices(body)
|
||||
}.flatMap { keysClaimResponse ->
|
||||
Try {
|
||||
val map = MXUsersDevicesMap<MXKey>()
|
||||
}
|
||||
val map = MXUsersDevicesMap<MXKey>()
|
||||
keysClaimResponse.oneTimeKeys?.let { oneTimeKeys ->
|
||||
for (userId in oneTimeKeys.keys) {
|
||||
val mapByUserId = oneTimeKeys[userId]
|
||||
|
||||
keysClaimResponse.oneTimeKeys?.let { oneTimeKeys ->
|
||||
for (userId in oneTimeKeys.keys) {
|
||||
val mapByUserId = oneTimeKeys[userId]
|
||||
if (mapByUserId != null) {
|
||||
for (deviceId in mapByUserId.keys) {
|
||||
val mxKey = MXKey.from(mapByUserId[deviceId])
|
||||
|
||||
if (mapByUserId != null) {
|
||||
for (deviceId in mapByUserId.keys) {
|
||||
val mxKey = MXKey.from(mapByUserId[deviceId])
|
||||
|
||||
if (mxKey != null) {
|
||||
map.setObject(userId, deviceId, mxKey)
|
||||
} else {
|
||||
Timber.e("## claimOneTimeKeysForUsersDevices : fail to create a MXKey")
|
||||
}
|
||||
}
|
||||
if (mxKey != null) {
|
||||
map.setObject(userId, deviceId, mxKey)
|
||||
} else {
|
||||
Timber.e("## claimOneTimeKeysForUsersDevices : fail to create a MXKey")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
map
|
||||
}
|
||||
}
|
||||
return map
|
||||
}
|
||||
}
|
||||
|
@ -16,16 +16,12 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.tasks
|
||||
|
||||
import arrow.core.Try
|
||||
import arrow.core.failure
|
||||
import arrow.core.recoverWith
|
||||
import im.vector.matrix.android.api.failure.Failure
|
||||
import im.vector.matrix.android.internal.auth.registration.RegistrationFlowResponse
|
||||
import im.vector.matrix.android.internal.crypto.api.CryptoApi
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.DeleteDeviceParams
|
||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -38,10 +34,12 @@ internal interface DeleteDeviceTask : Task<DeleteDeviceTask.Params, Unit> {
|
||||
internal class DefaultDeleteDeviceTask @Inject constructor(private val cryptoApi: CryptoApi)
|
||||
: DeleteDeviceTask {
|
||||
|
||||
override suspend fun execute(params: DeleteDeviceTask.Params): Try<Unit> {
|
||||
return executeRequest<Unit> {
|
||||
apiCall = cryptoApi.deleteDevice(params.deviceId, DeleteDeviceParams())
|
||||
}.recoverWith { throwable ->
|
||||
override suspend fun execute(params: DeleteDeviceTask.Params) {
|
||||
try {
|
||||
executeRequest<Unit> {
|
||||
apiCall = cryptoApi.deleteDevice(params.deviceId, DeleteDeviceParams())
|
||||
}
|
||||
} catch (throwable: Throwable) {
|
||||
if (throwable is Failure.OtherServerError && throwable.httpCode == 401) {
|
||||
// Parse to get a RegistrationFlowResponse
|
||||
val registrationFlowResponse = try {
|
||||
@ -51,17 +49,16 @@ internal class DefaultDeleteDeviceTask @Inject constructor(private val cryptoApi
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
// check if the server response can be casted
|
||||
if (registrationFlowResponse != null) {
|
||||
Failure.RegistrationFlowError(registrationFlowResponse).failure()
|
||||
throw Failure.RegistrationFlowError(registrationFlowResponse)
|
||||
} else {
|
||||
throwable.failure()
|
||||
throw throwable
|
||||
}
|
||||
|
||||
} else {
|
||||
// Other error
|
||||
throwable.failure()
|
||||
throw throwable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.tasks
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
import im.vector.matrix.android.internal.auth.data.LoginFlowTypes
|
||||
import im.vector.matrix.android.internal.crypto.api.CryptoApi
|
||||
@ -38,7 +37,7 @@ internal class DefaultDeleteDeviceWithUserPasswordTask @Inject constructor(priva
|
||||
private val credentials: Credentials)
|
||||
: DeleteDeviceWithUserPasswordTask {
|
||||
|
||||
override suspend fun execute(params: DeleteDeviceWithUserPasswordTask.Params): Try<Unit> {
|
||||
override suspend fun execute(params: DeleteDeviceWithUserPasswordTask.Params) {
|
||||
return executeRequest {
|
||||
apiCall = cryptoApi.deleteDevice(params.deviceId, DeleteDeviceParams()
|
||||
.apply {
|
||||
|
@ -17,12 +17,10 @@
|
||||
package im.vector.matrix.android.internal.crypto.tasks
|
||||
|
||||
import android.text.TextUtils
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.internal.crypto.api.CryptoApi
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.KeysQueryBody
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.KeysQueryResponse
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
@ -38,7 +36,7 @@ internal interface DownloadKeysForUsersTask : Task<DownloadKeysForUsersTask.Para
|
||||
internal class DefaultDownloadKeysForUsers @Inject constructor(private val cryptoApi: CryptoApi)
|
||||
: DownloadKeysForUsersTask {
|
||||
|
||||
override suspend fun execute(params: DownloadKeysForUsersTask.Params): Try<KeysQueryResponse> {
|
||||
override suspend fun execute(params: DownloadKeysForUsersTask.Params): KeysQueryResponse {
|
||||
val downloadQuery = HashMap<String, Map<String, Any>>()
|
||||
|
||||
if (null != params.userIds) {
|
||||
|
@ -16,11 +16,9 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.tasks
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.internal.crypto.api.CryptoApi
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -29,7 +27,7 @@ internal interface GetDevicesTask : Task<Unit, DevicesListResponse>
|
||||
internal class DefaultGetDevicesTask @Inject constructor(private val cryptoApi: CryptoApi)
|
||||
: GetDevicesTask {
|
||||
|
||||
override suspend fun execute(params: Unit): Try<DevicesListResponse> {
|
||||
override suspend fun execute(params: Unit): DevicesListResponse {
|
||||
return executeRequest {
|
||||
apiCall = cryptoApi.getDevices()
|
||||
}
|
||||
|
@ -16,11 +16,9 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.tasks
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.internal.crypto.api.CryptoApi
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.KeyChangesResponse
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -36,10 +34,9 @@ internal interface GetKeyChangesTask : Task<GetKeyChangesTask.Params, KeyChanges
|
||||
internal class DefaultGetKeyChangesTask @Inject constructor(private val cryptoApi: CryptoApi)
|
||||
: GetKeyChangesTask {
|
||||
|
||||
override suspend fun execute(params: GetKeyChangesTask.Params): Try<KeyChangesResponse> {
|
||||
override suspend fun execute(params: GetKeyChangesTask.Params): KeyChangesResponse {
|
||||
return executeRequest {
|
||||
apiCall = cryptoApi.getKeyChanges(params.from,
|
||||
params.to)
|
||||
apiCall = cryptoApi.getKeyChanges(params.from, params.to)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,12 +16,10 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.tasks
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.internal.crypto.api.CryptoApi
|
||||
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.SendToDeviceBody
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
@ -40,7 +38,7 @@ internal interface SendToDeviceTask : Task<SendToDeviceTask.Params, Unit> {
|
||||
internal class DefaultSendToDeviceTask @Inject constructor(private val cryptoApi: CryptoApi)
|
||||
: SendToDeviceTask {
|
||||
|
||||
override suspend fun execute(params: SendToDeviceTask.Params): Try<Unit> {
|
||||
override suspend fun execute(params: SendToDeviceTask.Params) {
|
||||
val sendToDeviceBody = SendToDeviceBody()
|
||||
sendToDeviceBody.messages = params.contentMap.map
|
||||
|
||||
|
@ -17,11 +17,9 @@
|
||||
package im.vector.matrix.android.internal.crypto.tasks
|
||||
|
||||
import android.text.TextUtils
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.internal.crypto.api.CryptoApi
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.UpdateDeviceInfoBody
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -37,11 +35,10 @@ internal interface SetDeviceNameTask : Task<SetDeviceNameTask.Params, Unit> {
|
||||
internal class DefaultSetDeviceNameTask @Inject constructor(private val cryptoApi: CryptoApi)
|
||||
: SetDeviceNameTask {
|
||||
|
||||
override suspend fun execute(params: SetDeviceNameTask.Params): Try<Unit> {
|
||||
override suspend fun execute(params: SetDeviceNameTask.Params) {
|
||||
val body = UpdateDeviceInfoBody(
|
||||
displayName = if (TextUtils.isEmpty(params.deviceName)) "" else params.deviceName
|
||||
)
|
||||
|
||||
return executeRequest {
|
||||
apiCall = cryptoApi.updateDeviceInfo(params.deviceId, body)
|
||||
}
|
||||
|
@ -16,14 +16,12 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.tasks
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.api.util.JsonDict
|
||||
import im.vector.matrix.android.internal.crypto.api.CryptoApi
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.DeviceKeys
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadBody
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import im.vector.matrix.android.internal.util.convertToUTF8
|
||||
import javax.inject.Inject
|
||||
@ -41,7 +39,7 @@ internal interface UploadKeysTask : Task<UploadKeysTask.Params, KeysUploadRespon
|
||||
internal class DefaultUploadKeysTask @Inject constructor(private val cryptoApi: CryptoApi)
|
||||
: UploadKeysTask {
|
||||
|
||||
override suspend fun execute(params: UploadKeysTask.Params): Try<KeysUploadResponse> {
|
||||
override suspend fun execute(params: UploadKeysTask.Params): KeysUploadResponse {
|
||||
val encodedDeviceId = convertToUTF8(params.deviceId)
|
||||
|
||||
val body = KeysUploadBody()
|
||||
|
@ -37,6 +37,7 @@ import im.vector.matrix.android.internal.crypto.model.rest.*
|
||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.task.TaskConstraints
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.task.configureWith
|
||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||
@ -219,17 +220,20 @@ internal class DefaultSasVerificationService @Inject constructor(private val cre
|
||||
startReq: KeyVerificationStart,
|
||||
success: (MXUsersDevicesMap<MXDeviceInfo>) -> Unit,
|
||||
error: () -> Unit) {
|
||||
deviceListManager.downloadKeys(listOf(otherUserId), true)
|
||||
.fold(
|
||||
{ error() },
|
||||
{
|
||||
if (it.getUserDeviceIds(otherUserId)?.contains(startReq.fromDevice) == true) {
|
||||
success(it)
|
||||
} else {
|
||||
error()
|
||||
}
|
||||
}
|
||||
)
|
||||
runCatching {
|
||||
deviceListManager.downloadKeys(listOf(otherUserId), true)
|
||||
}.fold(
|
||||
{
|
||||
if (it.getUserDeviceIds(otherUserId)?.contains(startReq.fromDevice) == true) {
|
||||
success(it)
|
||||
} else {
|
||||
error()
|
||||
}
|
||||
},
|
||||
{
|
||||
error()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun onCancelReceived(event: Event) {
|
||||
@ -412,16 +416,18 @@ internal class DefaultSasVerificationService @Inject constructor(private val cre
|
||||
val contentMap = MXUsersDevicesMap<Any>()
|
||||
contentMap.setObject(userId, userDevice, cancelMessage)
|
||||
|
||||
sendToDeviceTask.configureWith(SendToDeviceTask.Params(EventType.KEY_VERIFICATION_CANCEL, contentMap, transactionId))
|
||||
.dispatchTo(object : MatrixCallback<Unit> {
|
||||
override fun onSuccess(data: Unit) {
|
||||
Timber.v("## SAS verification [$transactionId] canceled for reason ${code.value}")
|
||||
}
|
||||
sendToDeviceTask
|
||||
.configureWith(SendToDeviceTask.Params(EventType.KEY_VERIFICATION_CANCEL, contentMap, transactionId)) {
|
||||
this.callback = object : MatrixCallback<Unit> {
|
||||
override fun onSuccess(data: Unit) {
|
||||
Timber.v("## SAS verification [$transactionId] canceled for reason ${code.value}")
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
Timber.e(failure, "## SAS verification [$transactionId] failed to cancel.")
|
||||
override fun onFailure(failure: Throwable) {
|
||||
Timber.e(failure, "## SAS verification [$transactionId] failed to cancel.")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
}
|
@ -287,23 +287,25 @@ internal abstract class SASVerificationTransaction(
|
||||
val contentMap = MXUsersDevicesMap<Any>()
|
||||
contentMap.setObject(otherUserId, otherDeviceId, keyToDevice)
|
||||
|
||||
sendToDeviceTask.configureWith(SendToDeviceTask.Params(type, contentMap, transactionId))
|
||||
.dispatchTo(object : MatrixCallback<Unit> {
|
||||
override fun onSuccess(data: Unit) {
|
||||
Timber.v("## SAS verification [$transactionId] toDevice type '$type' success.")
|
||||
if (onDone != null) {
|
||||
onDone()
|
||||
} else {
|
||||
state = nextState
|
||||
sendToDeviceTask
|
||||
.configureWith(SendToDeviceTask.Params(type, contentMap, transactionId)) {
|
||||
this.callback = object : MatrixCallback<Unit> {
|
||||
override fun onSuccess(data: Unit) {
|
||||
Timber.v("## SAS verification [$transactionId] toDevice type '$type' success.")
|
||||
if (onDone != null) {
|
||||
onDone()
|
||||
} else {
|
||||
state = nextState
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
Timber.e("## SAS verification [$transactionId] failed to send toDevice in state : $state")
|
||||
|
||||
cancel(onErrorReason)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
Timber.e("## SAS verification [$transactionId] failed to send toDevice in state : $state")
|
||||
|
||||
cancel(onErrorReason)
|
||||
}
|
||||
})
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2019 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package im.vector.matrix.android.internal.database
|
||||
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmConfiguration
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
|
||||
suspend fun awaitTransaction(config: RealmConfiguration, transaction: suspend (realm: Realm) -> Unit) = withContext(Dispatchers.IO) {
|
||||
Realm.getInstance(config).use { bgRealm ->
|
||||
bgRealm.beginTransaction()
|
||||
try {
|
||||
transaction(bgRealm)
|
||||
if (isActive) {
|
||||
bgRealm.commitTransaction()
|
||||
}
|
||||
} finally {
|
||||
if (bgRealm.isInTransaction) {
|
||||
bgRealm.cancelTransaction()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -18,12 +18,7 @@ package im.vector.matrix.android.internal.database
|
||||
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.internal.util.createBackgroundHandler
|
||||
import io.realm.OrderedCollectionChangeSet
|
||||
import io.realm.OrderedRealmCollectionChangeListener
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmConfiguration
|
||||
import io.realm.RealmObject
|
||||
import io.realm.RealmResults
|
||||
import io.realm.*
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
|
@ -20,11 +20,7 @@ import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||
import im.vector.matrix.android.internal.database.mapper.ContentMapper
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
import im.vector.matrix.android.internal.database.model.EventEntityFields
|
||||
import im.vector.matrix.android.internal.database.model.RoomEntity
|
||||
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
||||
import im.vector.matrix.android.internal.database.model.TimelineEventEntityFields
|
||||
import im.vector.matrix.android.internal.database.model.*
|
||||
import im.vector.matrix.android.internal.database.query.next
|
||||
import im.vector.matrix.android.internal.database.query.prev
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
|
@ -20,13 +20,10 @@ import com.squareup.moshi.JsonDataException
|
||||
import im.vector.matrix.android.api.session.crypto.MXCryptoError
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.events.model.UnsignedData
|
||||
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
|
||||
import im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmDecryption
|
||||
import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||
import timber.log.Timber
|
||||
import java.util.*
|
||||
|
||||
internal object EventMapper {
|
||||
|
||||
|
@ -17,7 +17,6 @@
|
||||
package im.vector.matrix.android.internal.database.mapper
|
||||
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.room.send.SendState
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
||||
|
||||
|
@ -24,8 +24,6 @@ import io.realm.RealmObject
|
||||
import io.realm.RealmResults
|
||||
import io.realm.annotations.Index
|
||||
import io.realm.annotations.LinkingObjects
|
||||
import io.realm.annotations.PrimaryKey
|
||||
import java.util.*
|
||||
|
||||
internal open class EventEntity(@Index var eventId: String = "",
|
||||
@Index var roomId: String = "",
|
||||
|
@ -16,12 +16,8 @@
|
||||
|
||||
package im.vector.matrix.android.internal.database.query
|
||||
|
||||
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
import im.vector.matrix.android.internal.database.model.*
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity.LinkFilterMode.*
|
||||
import im.vector.matrix.android.internal.database.model.RoomEntity
|
||||
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
||||
import im.vector.matrix.android.internal.database.model.TimelineEventEntityFields
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmList
|
||||
import io.realm.RealmQuery
|
||||
|
@ -27,13 +27,11 @@ import im.vector.matrix.android.internal.SessionManager
|
||||
import im.vector.matrix.android.internal.auth.AuthModule
|
||||
import im.vector.matrix.android.internal.auth.SessionParamsStore
|
||||
import im.vector.matrix.android.internal.network.NetworkConnectivityChecker
|
||||
import im.vector.matrix.android.internal.network.RetrofitFactory
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.util.BackgroundDetectionObserver
|
||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||
import okhttp3.OkHttpClient
|
||||
import org.matrix.olm.OlmManager
|
||||
import retrofit2.Retrofit
|
||||
|
||||
|
||||
@Component(modules = [MatrixModule::class, NetworkModule::class, AuthModule::class])
|
||||
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
|
||||
* Copyright 2019 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
|
||||
*/
|
||||
package im.vector.matrix.android.internal.extensions
|
||||
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
|
||||
fun <A> Result<A>.foldToCallback(callback: MatrixCallback<A>): Unit = fold(
|
||||
{ callback.onSuccess(it) },
|
||||
{ callback.onFailure(it) }
|
||||
)
|
@ -19,35 +19,64 @@ package im.vector.matrix.android.internal.network
|
||||
import android.content.Context
|
||||
import com.novoda.merlin.Merlin
|
||||
import com.novoda.merlin.MerlinsBeard
|
||||
import com.novoda.merlin.registerable.connection.Connectable
|
||||
import im.vector.matrix.android.internal.di.MatrixScope
|
||||
import timber.log.Timber
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
@MatrixScope
|
||||
internal class NetworkConnectivityChecker @Inject constructor(context: Context) {
|
||||
|
||||
private val merlin = Merlin.Builder().withConnectableCallbacks().build(context)
|
||||
private val merlinsBeard = MerlinsBeard.from(context)
|
||||
private val merlin = Merlin.Builder()
|
||||
.withConnectableCallbacks()
|
||||
.withDisconnectableCallbacks()
|
||||
.build(context)
|
||||
|
||||
private val listeners = ArrayList<Listener>()
|
||||
private val merlinsBeard = MerlinsBeard.Builder().build(context)
|
||||
private val listeners = Collections.synchronizedList(ArrayList<Listener>())
|
||||
|
||||
fun register(listener: Listener) {
|
||||
if (listeners.isEmpty()) {
|
||||
merlin.bind()
|
||||
}
|
||||
listeners.add(listener)
|
||||
val connectable = Connectable {
|
||||
if (listeners.contains(listener)) {
|
||||
listener.onConnect()
|
||||
init {
|
||||
merlin.bind()
|
||||
merlin.registerDisconnectable {
|
||||
Timber.v("On Disconnect")
|
||||
val localListeners = listeners.toList()
|
||||
localListeners.forEach {
|
||||
it.onDisconnect()
|
||||
}
|
||||
}
|
||||
merlin.registerConnectable(connectable)
|
||||
merlin.registerConnectable {
|
||||
Timber.v("On Connect")
|
||||
val localListeners = listeners.toList()
|
||||
localListeners.forEach {
|
||||
it.onConnect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun waitUntilConnected() {
|
||||
if (isConnected()) {
|
||||
return
|
||||
} else {
|
||||
suspendCoroutine<Unit> { continuation ->
|
||||
register(object : Listener {
|
||||
override fun onConnect() {
|
||||
unregister(this)
|
||||
continuation.resume(Unit)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun register(listener: Listener) {
|
||||
listeners.add(listener)
|
||||
}
|
||||
|
||||
fun unregister(listener: Listener) {
|
||||
if (listeners.remove(listener) && listeners.isEmpty()) {
|
||||
merlin.unbind()
|
||||
}
|
||||
listeners.remove(listener)
|
||||
}
|
||||
|
||||
fun isConnected(): Boolean {
|
||||
@ -55,7 +84,13 @@ internal class NetworkConnectivityChecker @Inject constructor(context: Context)
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
fun onConnect()
|
||||
fun onConnect() {
|
||||
|
||||
}
|
||||
|
||||
fun onDisconnect() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -18,11 +18,7 @@ package im.vector.matrix.android.internal.network
|
||||
|
||||
import okhttp3.MediaType
|
||||
import okhttp3.RequestBody
|
||||
import okio.Buffer
|
||||
import okio.BufferedSink
|
||||
import okio.ForwardingSink
|
||||
import okio.Okio
|
||||
import okio.Sink
|
||||
import okio.*
|
||||
import java.io.IOException
|
||||
|
||||
internal class ProgressRequestBody(private val delegate: RequestBody,
|
||||
|
@ -16,9 +16,6 @@
|
||||
|
||||
package im.vector.matrix.android.internal.network
|
||||
|
||||
import arrow.core.Try
|
||||
import arrow.core.failure
|
||||
import arrow.core.recoverWith
|
||||
import com.squareup.moshi.JsonDataException
|
||||
import com.squareup.moshi.Moshi
|
||||
import im.vector.matrix.android.api.failure.Failure
|
||||
@ -36,8 +33,8 @@ internal class Request<DATA> {
|
||||
private val moshi: Moshi = MoshiProvider.providesMoshi()
|
||||
lateinit var apiCall: Call<DATA>
|
||||
|
||||
suspend fun execute(): Try<DATA> {
|
||||
return Try {
|
||||
suspend fun execute(): DATA {
|
||||
return try {
|
||||
val response = apiCall.awaitResponse()
|
||||
if (response.isSuccessful) {
|
||||
response.body()
|
||||
@ -45,13 +42,13 @@ internal class Request<DATA> {
|
||||
} else {
|
||||
throw manageFailure(response.errorBody(), response.code())
|
||||
}
|
||||
}.recoverWith {
|
||||
when (it) {
|
||||
is IOException -> Failure.NetworkConnection(it)
|
||||
} catch (exception: Throwable) {
|
||||
throw when (exception) {
|
||||
is IOException -> Failure.NetworkConnection(exception)
|
||||
is Failure.ServerError,
|
||||
is Failure.OtherServerError -> it
|
||||
else -> Failure.Unknown(it)
|
||||
}.failure()
|
||||
is Failure.OtherServerError -> exception
|
||||
else -> Failure.Unknown(exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,9 @@
|
||||
package im.vector.matrix.android.internal.network
|
||||
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import retrofit2.*
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.resumeWithException
|
||||
|
||||
|
@ -17,7 +17,6 @@
|
||||
package im.vector.matrix.android.internal.network
|
||||
|
||||
import com.squareup.moshi.Moshi
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||
|
@ -47,6 +47,7 @@ import im.vector.matrix.android.internal.session.sync.job.SyncWorker
|
||||
import im.vector.matrix.android.internal.worker.WorkManagerUtil
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
|
||||
@SessionScope
|
||||
internal class DefaultSession @Inject constructor(override val sessionParams: SessionParams,
|
||||
@ -64,7 +65,7 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se
|
||||
private val pushersService: Lazy<PushersService>,
|
||||
private val cryptoService: Lazy<CryptoManager>,
|
||||
private val fileService: Lazy<FileService>,
|
||||
private val syncThread: SyncThread,
|
||||
private val syncThreadProvider: Provider<SyncThread>,
|
||||
private val contentUrlResolver: ContentUrlResolver,
|
||||
private val contentUploadProgressTracker: ContentUploadStateTracker,
|
||||
private val initialSyncProgressService: Lazy<InitialSyncProgressService>)
|
||||
@ -84,6 +85,7 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se
|
||||
|
||||
private var isOpen = false
|
||||
|
||||
private var syncThread: SyncThread? = null
|
||||
|
||||
@MainThread
|
||||
override fun open() {
|
||||
@ -105,21 +107,23 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se
|
||||
SyncWorker.stopAnyBackgroundSync(context)
|
||||
}
|
||||
|
||||
override fun startSync(fromForeground : Boolean) {
|
||||
override fun startSync(fromForeground: Boolean) {
|
||||
Timber.i("Starting sync thread")
|
||||
assert(isOpen)
|
||||
syncThread.setInitialForeground(fromForeground)
|
||||
if (!syncThread.isAlive) {
|
||||
syncThread.start()
|
||||
val localSyncThread = getSyncThread()
|
||||
localSyncThread.setInitialForeground(fromForeground)
|
||||
if (!localSyncThread.isAlive) {
|
||||
localSyncThread.start()
|
||||
} else {
|
||||
syncThread.restart()
|
||||
localSyncThread.restart()
|
||||
Timber.w("Attempt to start an already started thread")
|
||||
}
|
||||
}
|
||||
|
||||
override fun stopSync() {
|
||||
assert(isOpen)
|
||||
syncThread.kill()
|
||||
syncThread?.kill()
|
||||
syncThread = null
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
@ -131,7 +135,13 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se
|
||||
}
|
||||
|
||||
override fun syncState(): LiveData<SyncState> {
|
||||
return syncThread.liveState()
|
||||
return getSyncThread().liveState()
|
||||
}
|
||||
|
||||
private fun getSyncThread(): SyncThread {
|
||||
return syncThread ?: syncThreadProvider.get().also {
|
||||
syncThread = it
|
||||
}
|
||||
}
|
||||
|
||||
@MainThread
|
||||
@ -139,21 +149,20 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se
|
||||
Timber.w("SIGN_OUT: start")
|
||||
|
||||
assert(isOpen)
|
||||
//Timber.w("SIGN_OUT: kill sync thread")
|
||||
//syncThread.kill()
|
||||
|
||||
Timber.w("SIGN_OUT: call webservice")
|
||||
return signOutService.get().signOut(object : MatrixCallback<Unit> {
|
||||
override fun onSuccess(data: Unit) {
|
||||
Timber.w("SIGN_OUT: call webservice -> SUCCESS: clear cache")
|
||||
|
||||
stopSync()
|
||||
stopAnyBackgroundSync()
|
||||
// Clear the cache
|
||||
cacheService.get().clearCache(object : MatrixCallback<Unit> {
|
||||
override fun onSuccess(data: Unit) {
|
||||
Timber.w("SIGN_OUT: clear cache -> SUCCESS: clear crypto cache")
|
||||
cryptoService.get().clearCryptoCache(MatrixCallbackDelegate(callback))
|
||||
|
||||
WorkManagerUtil.cancelAllWorks(context)
|
||||
callback.onSuccess(Unit)
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
@ -172,6 +181,22 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se
|
||||
})
|
||||
}
|
||||
|
||||
override fun clearCache(callback: MatrixCallback<Unit>) {
|
||||
stopSync()
|
||||
stopAnyBackgroundSync()
|
||||
cacheService.get().clearCache(object : MatrixCallback<Unit> {
|
||||
override fun onSuccess(data: Unit) {
|
||||
startSync(true)
|
||||
callback.onSuccess(data)
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
startSync(true)
|
||||
callback.onFailure(failure)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun contentUrlResolver() = contentUrlResolver
|
||||
|
||||
override fun contentUploadProgressTracker() = contentUploadProgressTracker
|
||||
|
@ -23,7 +23,6 @@ import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.internal.crypto.CryptoModule
|
||||
import im.vector.matrix.android.internal.di.MatrixComponent
|
||||
import im.vector.matrix.android.internal.network.NetworkConnectivityChecker
|
||||
import im.vector.matrix.android.internal.session.user.accountdata.AccountDataModule
|
||||
import im.vector.matrix.android.internal.session.cache.CacheModule
|
||||
import im.vector.matrix.android.internal.session.content.ContentModule
|
||||
import im.vector.matrix.android.internal.session.content.UploadContentWorker
|
||||
@ -43,6 +42,7 @@ import im.vector.matrix.android.internal.session.sync.SyncTask
|
||||
import im.vector.matrix.android.internal.session.sync.SyncTokenStore
|
||||
import im.vector.matrix.android.internal.session.sync.job.SyncWorker
|
||||
import im.vector.matrix.android.internal.session.user.UserModule
|
||||
import im.vector.matrix.android.internal.session.user.accountdata.AccountDataModule
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
|
||||
@Component(dependencies = [MatrixComponent::class],
|
||||
|
@ -38,8 +38,8 @@ import im.vector.matrix.android.internal.network.RetrofitFactory
|
||||
import im.vector.matrix.android.internal.session.group.GroupSummaryUpdater
|
||||
import im.vector.matrix.android.internal.session.room.EventRelationsAggregationUpdater
|
||||
import im.vector.matrix.android.internal.session.room.create.RoomCreateEventLiveObserver
|
||||
import im.vector.matrix.android.internal.session.room.tombstone.RoomTombstoneEventLiveObserver
|
||||
import im.vector.matrix.android.internal.session.room.prune.EventsPruner
|
||||
import im.vector.matrix.android.internal.session.room.tombstone.RoomTombstoneEventLiveObserver
|
||||
import im.vector.matrix.android.internal.util.md5
|
||||
import io.realm.RealmConfiguration
|
||||
import okhttp3.OkHttpClient
|
||||
|
@ -21,7 +21,6 @@ import dagger.Module
|
||||
import dagger.Provides
|
||||
import im.vector.matrix.android.api.session.cache.CacheService
|
||||
import im.vector.matrix.android.internal.di.SessionDatabase
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import io.realm.RealmConfiguration
|
||||
|
||||
@Module
|
||||
|
@ -16,27 +16,18 @@
|
||||
|
||||
package im.vector.matrix.android.internal.session.cache
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.database.awaitTransaction
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmConfiguration
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
||||
internal interface ClearCacheTask : Task<Unit, Unit>
|
||||
|
||||
internal class RealmClearCacheTask @Inject constructor(private val realmConfiguration: RealmConfiguration) : ClearCacheTask {
|
||||
|
||||
override suspend fun execute(params: Unit): Try<Unit> {
|
||||
return Try {
|
||||
val realm = Realm.getInstance(realmConfiguration)
|
||||
|
||||
realm.executeTransaction {
|
||||
it.deleteAll()
|
||||
}
|
||||
|
||||
realm.close()
|
||||
override suspend fun execute(params: Unit) {
|
||||
awaitTransaction(realmConfiguration) {
|
||||
it.deleteAll()
|
||||
}
|
||||
}
|
||||
}
|
@ -20,16 +20,19 @@ import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.cache.CacheService
|
||||
import im.vector.matrix.android.internal.di.SessionDatabase
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.task.toConfigurableTask
|
||||
import im.vector.matrix.android.internal.task.configureWith
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class DefaultCacheService @Inject constructor(@SessionDatabase private val clearCacheTask: ClearCacheTask,
|
||||
internal class DefaultCacheService @Inject constructor(@SessionDatabase
|
||||
private val clearCacheTask: ClearCacheTask,
|
||||
private val taskExecutor: TaskExecutor) : CacheService {
|
||||
|
||||
override fun clearCache(callback: MatrixCallback<Unit>) {
|
||||
taskExecutor.cancelAll()
|
||||
clearCacheTask
|
||||
.toConfigurableTask()
|
||||
.dispatchTo(callback)
|
||||
.configureWith {
|
||||
this.callback = callback
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
}
|
@ -16,7 +16,6 @@
|
||||
|
||||
package im.vector.matrix.android.internal.session.filter
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
@ -39,15 +38,12 @@ internal class DefaultSaveFilterTask @Inject constructor(private val sessionPara
|
||||
private val filterRepository: FilterRepository
|
||||
) : SaveFilterTask {
|
||||
|
||||
override suspend fun execute(params: SaveFilterTask.Params): Try<Unit> {
|
||||
return executeRequest<FilterResponse> {
|
||||
override suspend fun execute(params: SaveFilterTask.Params) {
|
||||
val filterResponse = executeRequest<FilterResponse> {
|
||||
// TODO auto retry
|
||||
apiCall = filterAPI.uploadFilter(sessionParams.credentials.userId, params.filter)
|
||||
}.flatMap { filterResponse ->
|
||||
Try {
|
||||
filterRepository.storeFilterId(params.filter, filterResponse.filterId)
|
||||
}
|
||||
}
|
||||
filterRepository.storeFilterId(params.filter, filterResponse.filterId)
|
||||
}
|
||||
|
||||
}
|
@ -16,10 +16,6 @@
|
||||
|
||||
package im.vector.matrix.android.internal.session.group
|
||||
|
||||
import arrow.core.Try
|
||||
import arrow.core.fix
|
||||
import arrow.instances.`try`.monad.monad
|
||||
import arrow.typeclasses.binding
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.internal.database.model.GroupSummaryEntity
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
@ -28,8 +24,6 @@ import im.vector.matrix.android.internal.session.group.model.GroupRooms
|
||||
import im.vector.matrix.android.internal.session.group.model.GroupSummaryResponse
|
||||
import im.vector.matrix.android.internal.session.group.model.GroupUsers
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import im.vector.matrix.android.internal.util.tryTransactionSync
|
||||
import io.realm.kotlin.createObject
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface GetGroupDataTask : Task<GetGroupDataTask.Params, Unit> {
|
||||
@ -43,7 +37,7 @@ internal class DefaultGetGroupDataTask @Inject constructor(
|
||||
private val monarchy: Monarchy
|
||||
) : GetGroupDataTask {
|
||||
|
||||
override suspend fun execute(params: GetGroupDataTask.Params): Try<Unit> {
|
||||
override suspend fun execute(params: GetGroupDataTask.Params) {
|
||||
val groupId = params.groupId
|
||||
val groupSummary = executeRequest<GroupSummaryResponse> {
|
||||
apiCall = groupAPI.getSummary(groupId)
|
||||
@ -54,20 +48,18 @@ internal class DefaultGetGroupDataTask @Inject constructor(
|
||||
val groupUsers = executeRequest<GroupUsers> {
|
||||
apiCall = groupAPI.getUsers(groupId)
|
||||
}
|
||||
return Try.monad().binding {
|
||||
insertInDb(groupSummary.bind(), groupRooms.bind(), groupUsers.bind(), groupId).bind()
|
||||
}.fix()
|
||||
insertInDb(groupSummary, groupRooms, groupUsers, groupId)
|
||||
}
|
||||
|
||||
|
||||
private fun insertInDb(groupSummary: GroupSummaryResponse,
|
||||
groupRooms: GroupRooms,
|
||||
groupUsers: GroupUsers,
|
||||
groupId: String): Try<Unit> {
|
||||
return monarchy
|
||||
.tryTransactionSync { realm ->
|
||||
groupId: String) {
|
||||
monarchy
|
||||
.writeAsync { realm ->
|
||||
val groupSummaryEntity = GroupSummaryEntity.where(realm, groupId).findFirst()
|
||||
?: realm.createObject(groupId)
|
||||
?: realm.createObject(GroupSummaryEntity::class.java, groupId)
|
||||
|
||||
groupSummaryEntity.avatarUrl = groupSummary.profile?.avatarUrl ?: ""
|
||||
val name = groupSummary.profile?.name
|
||||
@ -82,7 +74,6 @@ internal class DefaultGetGroupDataTask @Inject constructor(
|
||||
val userIds = groupUsers.users.map { it.userId }
|
||||
groupSummaryEntity.userIds.clear()
|
||||
groupSummaryEntity.userIds.addAll(userIds)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,6 @@ import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||
import im.vector.matrix.android.internal.database.model.GroupSummaryEntity
|
||||
import im.vector.matrix.android.internal.database.model.GroupSummaryEntityFields
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class DefaultGroupService @Inject constructor(private val monarchy: Monarchy) : GroupService {
|
||||
|
@ -19,7 +19,6 @@ package im.vector.matrix.android.internal.session.group
|
||||
import android.content.Context
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.WorkerParameters
|
||||
import arrow.core.Try
|
||||
import com.squareup.moshi.JsonClass
|
||||
import im.vector.matrix.android.internal.worker.SessionWorkerParams
|
||||
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
||||
@ -43,16 +42,15 @@ internal class GetGroupDataWorker(context: Context, params: WorkerParameters) :
|
||||
|
||||
val sessionComponent = getSessionComponent(params.userId) ?: return Result.success()
|
||||
sessionComponent.inject(this)
|
||||
|
||||
val results = params.groupIds.map { groupId ->
|
||||
fetchGroupData(groupId)
|
||||
runCatching { fetchGroupData(groupId) }
|
||||
}
|
||||
val isSuccessful = results.none { it.isFailure() }
|
||||
val isSuccessful = results.none { it.isFailure }
|
||||
return if (isSuccessful) Result.success() else Result.retry()
|
||||
}
|
||||
|
||||
private suspend fun fetchGroupData(groupId: String): Try<Unit> {
|
||||
return getGroupDataTask.execute(GetGroupDataTask.Params(groupId))
|
||||
private suspend fun fetchGroupData(groupId: String) {
|
||||
getGroupDataTask.execute(GetGroupDataTask.Params(groupId))
|
||||
}
|
||||
|
||||
}
|
@ -22,20 +22,15 @@ import androidx.work.WorkManager
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
import im.vector.matrix.android.internal.database.RealmLiveEntityObserver
|
||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
import im.vector.matrix.android.internal.database.model.GroupEntity
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.di.SessionDatabase
|
||||
import im.vector.matrix.android.internal.session.room.prune.PruneEventTask
|
||||
import im.vector.matrix.android.internal.task.configureWith
|
||||
import im.vector.matrix.android.internal.worker.WorkManagerUtil
|
||||
import im.vector.matrix.android.internal.worker.WorkManagerUtil.matrixOneTimeWorkRequestBuilder
|
||||
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
||||
import io.realm.OrderedCollectionChangeSet
|
||||
import io.realm.RealmConfiguration
|
||||
import io.realm.RealmResults
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val GET_GROUP_DATA_WORKER = "GET_GROUP_DATA_WORKER"
|
||||
|
@ -83,9 +83,10 @@ internal class DefaultPushRuleService @Inject constructor(
|
||||
|
||||
override fun updatePushRuleEnableStatus(kind: String, pushRule: PushRule, enabled: Boolean, callback: MatrixCallback<Unit>): Cancelable {
|
||||
return updatePushRuleEnableStatusTask
|
||||
.configureWith(UpdatePushRuleEnableStatusTask.Params(kind, pushRule, enabled))
|
||||
.configureWith(UpdatePushRuleEnableStatusTask.Params(kind, pushRule, enabled)) {
|
||||
this.callback = callback
|
||||
}
|
||||
// TODO Fetch the rules
|
||||
.dispatchTo(callback)
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
package im.vector.matrix.android.internal.session.notification
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
@ -41,48 +40,45 @@ internal class DefaultProcessEventForPushTask @Inject constructor(
|
||||
private val sessionParams: SessionParams
|
||||
) : ProcessEventForPushTask {
|
||||
|
||||
|
||||
override suspend fun execute(params: ProcessEventForPushTask.Params): Try<Unit> {
|
||||
return Try {
|
||||
// Handle left rooms
|
||||
params.syncResponse.leave.keys.forEach {
|
||||
defaultPushRuleService.dispatchRoomLeft(it)
|
||||
}
|
||||
val newJoinEvents = params.syncResponse.join
|
||||
.map { entries ->
|
||||
entries.value.timeline?.events?.map { it.copy(roomId = entries.key) }
|
||||
}
|
||||
.fold(emptyList<Event>(), { acc, next ->
|
||||
acc + (next ?: emptyList())
|
||||
})
|
||||
val inviteEvents = params.syncResponse.invite
|
||||
.map { entries ->
|
||||
entries.value.inviteState?.events?.map { it.copy(roomId = entries.key) }
|
||||
}
|
||||
.fold(emptyList<Event>(), { acc, next ->
|
||||
acc + (next ?: emptyList())
|
||||
})
|
||||
val allEvents = (newJoinEvents + inviteEvents).filter { event ->
|
||||
when (event.type) {
|
||||
EventType.MESSAGE,
|
||||
EventType.REDACTION,
|
||||
EventType.ENCRYPTED,
|
||||
EventType.STATE_ROOM_MEMBER -> true
|
||||
else -> false
|
||||
}
|
||||
}.filter {
|
||||
it.senderId != sessionParams.credentials.userId
|
||||
}
|
||||
Timber.v("[PushRules] Found ${allEvents.size} out of ${(newJoinEvents + inviteEvents).size}" +
|
||||
" to check for push rules with ${params.rules.size} rules")
|
||||
allEvents.forEach { event ->
|
||||
fulfilledBingRule(event, params.rules)?.let {
|
||||
Timber.v("[PushRules] Rule $it match for event ${event.eventId}")
|
||||
defaultPushRuleService.dispatchBing(event, it)
|
||||
}
|
||||
}
|
||||
defaultPushRuleService.dispatchFinish()
|
||||
override suspend fun execute(params: ProcessEventForPushTask.Params) {
|
||||
// Handle left rooms
|
||||
params.syncResponse.leave.keys.forEach {
|
||||
defaultPushRuleService.dispatchRoomLeft(it)
|
||||
}
|
||||
val newJoinEvents = params.syncResponse.join
|
||||
.map { entries ->
|
||||
entries.value.timeline?.events?.map { it.copy(roomId = entries.key) }
|
||||
}
|
||||
.fold(emptyList<Event>(), { acc, next ->
|
||||
acc + (next ?: emptyList())
|
||||
})
|
||||
val inviteEvents = params.syncResponse.invite
|
||||
.map { entries ->
|
||||
entries.value.inviteState?.events?.map { it.copy(roomId = entries.key) }
|
||||
}
|
||||
.fold(emptyList<Event>(), { acc, next ->
|
||||
acc + (next ?: emptyList())
|
||||
})
|
||||
val allEvents = (newJoinEvents + inviteEvents).filter { event ->
|
||||
when (event.type) {
|
||||
EventType.MESSAGE,
|
||||
EventType.REDACTION,
|
||||
EventType.ENCRYPTED,
|
||||
EventType.STATE_ROOM_MEMBER -> true
|
||||
else -> false
|
||||
}
|
||||
}.filter {
|
||||
it.senderId != sessionParams.credentials.userId
|
||||
}
|
||||
Timber.v("[PushRules] Found ${allEvents.size} out of ${(newJoinEvents + inviteEvents).size}" +
|
||||
" to check for push rules with ${params.rules.size} rules")
|
||||
allEvents.forEach { event ->
|
||||
fulfilledBingRule(event, params.rules)?.let {
|
||||
Timber.v("[PushRules] Rule $it match for event ${event.eventId}")
|
||||
defaultPushRuleService.dispatchBing(event, it)
|
||||
}
|
||||
}
|
||||
defaultPushRuleService.dispatchFinish()
|
||||
}
|
||||
|
||||
private fun fulfilledBingRule(event: Event, rules: List<PushRule>): PushRule? {
|
||||
|
@ -26,6 +26,7 @@ import im.vector.matrix.android.internal.database.mapper.toEntity
|
||||
import im.vector.matrix.android.internal.database.model.PusherEntity
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.util.awaitTransaction
|
||||
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
||||
import im.vector.matrix.android.internal.worker.getSessionComponent
|
||||
import javax.inject.Inject
|
||||
@ -55,15 +56,14 @@ internal class AddHttpPusherWorker(context: Context, params: WorkerParameters)
|
||||
if (pusher.pushKey.isBlank()) {
|
||||
return Result.failure()
|
||||
}
|
||||
|
||||
val result = executeRequest<Unit> {
|
||||
apiCall = pushersAPI.setPusher(pusher)
|
||||
}
|
||||
return result.fold({
|
||||
when (it) {
|
||||
return try {
|
||||
setPusher(pusher, params.userId)
|
||||
Result.success()
|
||||
} catch (exception: Throwable) {
|
||||
when (exception) {
|
||||
is Failure.NetworkConnection -> Result.retry()
|
||||
else -> {
|
||||
monarchy.runTransactionSync { realm ->
|
||||
monarchy.awaitTransaction { realm ->
|
||||
PusherEntity.where(realm, params.userId, pusher.pushKey).findFirst()?.let {
|
||||
//update it
|
||||
it.state = PusherState.FAILED_TO_REGISTER
|
||||
@ -73,28 +73,31 @@ internal class AddHttpPusherWorker(context: Context, params: WorkerParameters)
|
||||
Result.failure()
|
||||
}
|
||||
}
|
||||
}, {
|
||||
monarchy.runTransactionSync { realm ->
|
||||
val echo = PusherEntity.where(realm, params.userId, pusher.pushKey).findFirst()
|
||||
if (echo != null) {
|
||||
//update it
|
||||
echo.appDisplayName = pusher.appDisplayName
|
||||
echo.appId = pusher.appId
|
||||
echo.kind = pusher.kind
|
||||
echo.lang = pusher.lang
|
||||
echo.profileTag = pusher.profileTag
|
||||
echo.data?.format = pusher.data?.format
|
||||
echo.data?.url = pusher.data?.url
|
||||
echo.state = PusherState.REGISTERED
|
||||
} else {
|
||||
pusher.toEntity(params.userId).also {
|
||||
it.state = PusherState.REGISTERED
|
||||
realm.insertOrUpdate(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun setPusher(pusher: JsonPusher, userId: String) {
|
||||
executeRequest<Unit> {
|
||||
apiCall = pushersAPI.setPusher(pusher)
|
||||
}
|
||||
monarchy.awaitTransaction { realm ->
|
||||
val echo = PusherEntity.where(realm, userId, pusher.pushKey).findFirst()
|
||||
if (echo != null) {
|
||||
//update it
|
||||
echo.appDisplayName = pusher.appDisplayName
|
||||
echo.appId = pusher.appId
|
||||
echo.kind = pusher.kind
|
||||
echo.lang = pusher.lang
|
||||
echo.profileTag = pusher.profileTag
|
||||
echo.data?.format = pusher.data?.format
|
||||
echo.data?.url = pusher.data?.url
|
||||
echo.state = PusherState.REGISTERED
|
||||
} else {
|
||||
pusher.toEntity(userId).also {
|
||||
it.state = PusherState.REGISTERED
|
||||
realm.insertOrUpdate(it)
|
||||
}
|
||||
}
|
||||
Result.success()
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@ -29,7 +29,6 @@ import im.vector.matrix.android.internal.database.model.PusherEntity
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.task.configureWith
|
||||
import im.vector.matrix.android.internal.task.toConfigurableTask
|
||||
import im.vector.matrix.android.internal.worker.WorkManagerUtil
|
||||
import im.vector.matrix.android.internal.worker.WorkManagerUtil.matrixOneTimeWorkRequestBuilder
|
||||
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
||||
@ -50,7 +49,7 @@ internal class DefaultPusherService @Inject constructor(
|
||||
|
||||
override fun refreshPushers() {
|
||||
getPusherTask
|
||||
.toConfigurableTask()
|
||||
.configureWith()
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
@ -85,8 +84,9 @@ internal class DefaultPusherService @Inject constructor(
|
||||
override fun removeHttpPusher(pushkey: String, appId: String, callback: MatrixCallback<Unit>) {
|
||||
val params = RemovePusherTask.Params(sessionParam.credentials.userId, pushkey, appId)
|
||||
removePusherTask
|
||||
.configureWith(params)
|
||||
.dispatchTo(callback)
|
||||
.configureWith(params) {
|
||||
this.callback = callback
|
||||
}
|
||||
//.enableRetry() ??
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
@ -15,7 +15,6 @@
|
||||
*/
|
||||
package im.vector.matrix.android.internal.session.pushers
|
||||
|
||||
import arrow.core.Try
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||
import im.vector.matrix.android.api.pushrules.rest.GetPushRulesResponse
|
||||
@ -24,7 +23,7 @@ import im.vector.matrix.android.internal.database.model.PushRulesEntity
|
||||
import im.vector.matrix.android.internal.database.model.PusherEntityFields
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import im.vector.matrix.android.internal.util.tryTransactionSync
|
||||
import im.vector.matrix.android.internal.util.awaitTransaction
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
@ -39,58 +38,57 @@ internal class DefaultGetPushRulesTask @Inject constructor(private val pushRules
|
||||
private val monarchy: Monarchy,
|
||||
private val sessionParams: SessionParams) : GetPushRulesTask {
|
||||
|
||||
override suspend fun execute(params: GetPushRulesTask.Params): Try<Unit> {
|
||||
return executeRequest<GetPushRulesResponse> {
|
||||
override suspend fun execute(params: GetPushRulesTask.Params) {
|
||||
val response = executeRequest<GetPushRulesResponse> {
|
||||
apiCall = pushRulesApi.getAllRules()
|
||||
}.flatMap { response ->
|
||||
val scope = params.scope
|
||||
return monarchy.tryTransactionSync { realm ->
|
||||
//clear existings?
|
||||
//TODO
|
||||
realm.where(PushRulesEntity::class.java)
|
||||
.equalTo(PusherEntityFields.USER_ID, sessionParams.credentials.userId)
|
||||
.findAll().deleteAllFromRealm()
|
||||
}
|
||||
val scope = params.scope
|
||||
monarchy.awaitTransaction { realm ->
|
||||
//clear existings?
|
||||
//TODO
|
||||
realm.where(PushRulesEntity::class.java)
|
||||
.equalTo(PusherEntityFields.USER_ID, sessionParams.credentials.userId)
|
||||
.findAll().deleteAllFromRealm()
|
||||
|
||||
val content = PushRulesEntity(sessionParams.credentials.userId, scope, "content")
|
||||
response.global.content?.forEach { rule ->
|
||||
PushRulesMapper.map(rule).also {
|
||||
content.pushRules.add(it)
|
||||
}
|
||||
val content = PushRulesEntity(sessionParams.credentials.userId, scope, "content")
|
||||
response.global.content?.forEach { rule ->
|
||||
PushRulesMapper.map(rule).also {
|
||||
content.pushRules.add(it)
|
||||
}
|
||||
realm.insertOrUpdate(content)
|
||||
|
||||
val override = PushRulesEntity(sessionParams.credentials.userId, scope, "override")
|
||||
response.global.override?.forEach { rule ->
|
||||
PushRulesMapper.map(rule).also {
|
||||
override.pushRules.add(it)
|
||||
}
|
||||
}
|
||||
realm.insertOrUpdate(override)
|
||||
|
||||
val rooms = PushRulesEntity(sessionParams.credentials.userId, scope, "room")
|
||||
response.global.room?.forEach { rule ->
|
||||
PushRulesMapper.map(rule).also {
|
||||
rooms.pushRules.add(it)
|
||||
}
|
||||
}
|
||||
realm.insertOrUpdate(rooms)
|
||||
|
||||
val senders = PushRulesEntity(sessionParams.credentials.userId, scope, "sender")
|
||||
response.global.sender?.forEach { rule ->
|
||||
PushRulesMapper.map(rule).also {
|
||||
senders.pushRules.add(it)
|
||||
}
|
||||
}
|
||||
realm.insertOrUpdate(senders)
|
||||
|
||||
val underrides = PushRulesEntity(sessionParams.credentials.userId, scope, "underride")
|
||||
response.global.underride?.forEach { rule ->
|
||||
PushRulesMapper.map(rule).also {
|
||||
underrides.pushRules.add(it)
|
||||
}
|
||||
}
|
||||
realm.insertOrUpdate(underrides)
|
||||
}
|
||||
realm.insertOrUpdate(content)
|
||||
|
||||
val override = PushRulesEntity(sessionParams.credentials.userId, scope, "override")
|
||||
response.global.override?.forEach { rule ->
|
||||
PushRulesMapper.map(rule).also {
|
||||
override.pushRules.add(it)
|
||||
}
|
||||
}
|
||||
realm.insertOrUpdate(override)
|
||||
|
||||
val rooms = PushRulesEntity(sessionParams.credentials.userId, scope, "room")
|
||||
response.global.room?.forEach { rule ->
|
||||
PushRulesMapper.map(rule).also {
|
||||
rooms.pushRules.add(it)
|
||||
}
|
||||
}
|
||||
realm.insertOrUpdate(rooms)
|
||||
|
||||
val senders = PushRulesEntity(sessionParams.credentials.userId, scope, "sender")
|
||||
response.global.sender?.forEach { rule ->
|
||||
PushRulesMapper.map(rule).also {
|
||||
senders.pushRules.add(it)
|
||||
}
|
||||
}
|
||||
realm.insertOrUpdate(senders)
|
||||
|
||||
val underrides = PushRulesEntity(sessionParams.credentials.userId, scope, "underride")
|
||||
response.global.underride?.forEach { rule ->
|
||||
PushRulesMapper.map(rule).also {
|
||||
underrides.pushRules.add(it)
|
||||
}
|
||||
}
|
||||
realm.insertOrUpdate(underrides)
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user