forked from GitHub-Mirror/riotX-android
Merge branch 'release/0.3.0'
This commit is contained in:
commit
9cd69d1e33
33
CHANGES.md
33
CHANGES.md
@ -1,3 +1,34 @@
|
|||||||
|
Changes in RiotX 0.3.0 (2019-08-08)
|
||||||
|
===================================================
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- Create Direct Room flow
|
||||||
|
- Handle `/markdown` command
|
||||||
|
|
||||||
|
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
|
||||||
|
- Add "View Edit History" item in the message bottom sheet (#401)
|
||||||
|
- Cancel sync request on pause and timeout to 0 after pause (#404)
|
||||||
|
|
||||||
|
Other changes:
|
||||||
|
- Show sync progress also in room detail screen (#403)
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Edited message: link confusion when (edited) appears in body (#398)
|
||||||
|
- 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)
|
||||||
|
- Fix `(edited)` link can be copied to clipboard (#402)
|
||||||
|
|
||||||
|
Build:
|
||||||
|
- Split APK: generate one APK per arch, to reduce APK size of about 30%
|
||||||
|
|
||||||
|
|
||||||
Changes in RiotX 0.2.0 (2019-07-18)
|
Changes in RiotX 0.2.0 (2019-07-18)
|
||||||
===================================================
|
===================================================
|
||||||
|
|
||||||
@ -36,7 +67,7 @@ Mode details here: https://medium.com/@RiotChat/introducing-the-riotx-beta-for-a
|
|||||||
=======================================================
|
=======================================================
|
||||||
|
|
||||||
|
|
||||||
Changes in RiotX 0.XX (2019-XX-XX)
|
Changes in RiotX 0.0.0 (2019-XX-XX)
|
||||||
===================================================
|
===================================================
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
|
@ -33,11 +33,12 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
|
||||||
implementation project(":matrix-sdk-android")
|
implementation project(":matrix-sdk-android")
|
||||||
implementation 'androidx.appcompat:appcompat:1.1.0-beta01'
|
implementation 'androidx.appcompat:appcompat:1.1.0-beta01'
|
||||||
implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0'
|
implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0'
|
||||||
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
|
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
|
||||||
|
// Paging
|
||||||
|
implementation "androidx.paging:paging-runtime-ktx:2.1.0"
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
androidTestImplementation 'androidx.test:runner:1.2.0'
|
androidTestImplementation 'androidx.test:runner:1.2.0'
|
||||||
|
@ -20,6 +20,8 @@ import androidx.lifecycle.LiveData
|
|||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
import io.reactivex.android.MainThreadDisposable
|
import io.reactivex.android.MainThreadDisposable
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
|
import io.reactivex.schedulers.Schedulers
|
||||||
|
|
||||||
private class LiveDataObservable<T>(
|
private class LiveDataObservable<T>(
|
||||||
private val liveData: LiveData<T>,
|
private val liveData: LiveData<T>,
|
||||||
@ -57,5 +59,5 @@ private class LiveDataObservable<T>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun <T> LiveData<T>.asObservable(): Observable<T> {
|
fun <T> LiveData<T>.asObservable(): Observable<T> {
|
||||||
return LiveDataObservable(this)
|
return LiveDataObservable(this).observeOn(Schedulers.computation())
|
||||||
}
|
}
|
@ -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.rx
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
|
import io.reactivex.CompletableEmitter
|
||||||
|
import io.reactivex.SingleEmitter
|
||||||
|
|
||||||
|
internal class MatrixCallbackCompletable<T>(private val completableEmitter: CompletableEmitter) : MatrixCallback<T> {
|
||||||
|
|
||||||
|
override fun onSuccess(data: T) {
|
||||||
|
completableEmitter.onComplete()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(failure: Throwable) {
|
||||||
|
completableEmitter.tryOnError(failure)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Cancelable.toCompletable(completableEmitter: CompletableEmitter) {
|
||||||
|
completableEmitter.setCancellable {
|
||||||
|
this.cancel()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* 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.rx
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
|
import io.reactivex.SingleEmitter
|
||||||
|
|
||||||
|
internal class MatrixCallbackSingle<T>(private val singleEmitter: SingleEmitter<T>) : MatrixCallback<T> {
|
||||||
|
|
||||||
|
override fun onSuccess(data: T) {
|
||||||
|
singleEmitter.onSuccess(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(failure: Throwable) {
|
||||||
|
singleEmitter.tryOnError(failure)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> Cancelable.toSingle(singleEmitter: SingleEmitter<T>) {
|
||||||
|
singleEmitter.setCancellable {
|
||||||
|
this.cancel()
|
||||||
|
}
|
||||||
|
}
|
@ -21,24 +21,32 @@ import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
|
|||||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.Single
|
||||||
|
|
||||||
class RxRoom(private val room: Room) {
|
class RxRoom(private val room: Room) {
|
||||||
|
|
||||||
fun liveRoomSummary(): Observable<RoomSummary> {
|
fun liveRoomSummary(): Observable<RoomSummary> {
|
||||||
return room.liveRoomSummary().asObservable().observeOn(Schedulers.computation())
|
return room.liveRoomSummary().asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun liveRoomMemberIds(): Observable<List<String>> {
|
fun liveRoomMemberIds(): Observable<List<String>> {
|
||||||
return room.getRoomMemberIdsLive().asObservable().observeOn(Schedulers.computation())
|
return room.getRoomMemberIdsLive().asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun liveAnnotationSummary(eventId: String): Observable<EventAnnotationsSummary> {
|
fun liveAnnotationSummary(eventId: String): Observable<EventAnnotationsSummary> {
|
||||||
return room.getEventSummaryLive(eventId).asObservable().observeOn(Schedulers.computation())
|
return room.getEventSummaryLive(eventId).asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun liveTimelineEvent(eventId: String): Observable<TimelineEvent> {
|
fun liveTimelineEvent(eventId: String): Observable<TimelineEvent> {
|
||||||
return room.liveTimeLineEvent(eventId).asObservable().observeOn(Schedulers.computation())
|
return room.liveTimeLineEvent(eventId).asObservable()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadRoomMembersIfNeeded(): Single<Unit> = Single.create {
|
||||||
|
room.loadRoomMembersIfNeeded(MatrixCallbackSingle(it)).toSingle(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun joinRoom(viaServers: List<String> = emptyList()): Single<Unit> = Single.create {
|
||||||
|
room.join(viaServers, MatrixCallbackSingle(it)).toSingle(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -16,30 +16,55 @@
|
|||||||
|
|
||||||
package im.vector.matrix.rx
|
package im.vector.matrix.rx
|
||||||
|
|
||||||
|
import androidx.paging.PagedList
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.api.session.group.model.GroupSummary
|
import im.vector.matrix.android.api.session.group.model.GroupSummary
|
||||||
import im.vector.matrix.android.api.session.pushers.Pusher
|
import im.vector.matrix.android.api.session.pushers.Pusher
|
||||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
|
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
|
||||||
import im.vector.matrix.android.api.session.sync.SyncState
|
import im.vector.matrix.android.api.session.sync.SyncState
|
||||||
|
import im.vector.matrix.android.api.session.user.model.User
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.Single
|
||||||
|
|
||||||
class RxSession(private val session: Session) {
|
class RxSession(private val session: Session) {
|
||||||
|
|
||||||
fun liveRoomSummaries(): Observable<List<RoomSummary>> {
|
fun liveRoomSummaries(): Observable<List<RoomSummary>> {
|
||||||
return session.liveRoomSummaries().asObservable().observeOn(Schedulers.computation())
|
return session.liveRoomSummaries().asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun liveGroupSummaries(): Observable<List<GroupSummary>> {
|
fun liveGroupSummaries(): Observable<List<GroupSummary>> {
|
||||||
return session.liveGroupSummaries().asObservable().observeOn(Schedulers.computation())
|
return session.liveGroupSummaries().asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun liveSyncState(): Observable<SyncState> {
|
fun liveSyncState(): Observable<SyncState> {
|
||||||
return session.syncState().asObservable().observeOn(Schedulers.computation())
|
return session.syncState().asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun livePushers(): Observable<List<Pusher>> {
|
fun livePushers(): Observable<List<Pusher>> {
|
||||||
return session.livePushers().asObservable().observeOn(Schedulers.computation())
|
return session.livePushers().asObservable()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun liveUsers(): Observable<List<User>> {
|
||||||
|
return session.liveUsers().asObservable()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun livePagedUsers(filter: String? = null): Observable<PagedList<User>> {
|
||||||
|
return session.livePagedUsers(filter).asObservable()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createRoom(roomParams: CreateRoomParams): Single<String> = Single.create {
|
||||||
|
session.createRoom(roomParams, MatrixCallbackSingle(it)).toSingle(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun searchUsersDirectory(search: String,
|
||||||
|
limit: Int,
|
||||||
|
excludedUserIds: Set<String>): Single<List<User>> = Single.create {
|
||||||
|
session.searchUsersDirectory(search, limit, excludedUserIds, MatrixCallbackSingle(it)).toSingle(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun joinRoom(roomId: String, viaServers: List<String> = emptyList()): Single<Unit> = Single.create {
|
||||||
|
session.joinRoom(roomId, viaServers, MatrixCallbackSingle(it)).toSingle(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,6 @@ dependencies {
|
|||||||
def markwon_version = '3.0.0'
|
def markwon_version = '3.0.0'
|
||||||
def daggerVersion = '2.23.1'
|
def daggerVersion = '2.23.1'
|
||||||
|
|
||||||
implementation fileTree(dir: 'libs', include: ['*.aar'])
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
|
||||||
@ -110,7 +109,7 @@ dependencies {
|
|||||||
implementation 'com.squareup.retrofit2:converter-moshi:2.4.0'
|
implementation 'com.squareup.retrofit2:converter-moshi:2.4.0'
|
||||||
implementation 'com.squareup.okhttp3:okhttp:3.14.1'
|
implementation 'com.squareup.okhttp3:okhttp:3.14.1'
|
||||||
implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
|
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"
|
implementation "com.squareup.moshi:moshi-adapters:$moshi_version"
|
||||||
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version"
|
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version"
|
||||||
|
|
||||||
@ -126,9 +125,6 @@ dependencies {
|
|||||||
// FP
|
// FP
|
||||||
implementation "io.arrow-kt:arrow-core:$arrow_version"
|
implementation "io.arrow-kt:arrow-core:$arrow_version"
|
||||||
implementation "io.arrow-kt:arrow-instances-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
|
// 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'
|
implementation 'org.matrix.gitlab.matrix-org:olm:3.1.2'
|
||||||
|
Binary file not shown.
@ -16,10 +16,10 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android;
|
package im.vector.matrix.android;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
import androidx.lifecycle.Observer;
|
import androidx.lifecycle.Observer;
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -19,11 +19,7 @@ package im.vector.matrix.android.session.room.timeline
|
|||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import im.vector.matrix.android.InstrumentedTest
|
import im.vector.matrix.android.InstrumentedTest
|
||||||
import im.vector.matrix.android.internal.database.helper.add
|
import im.vector.matrix.android.internal.database.helper.*
|
||||||
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.model.ChunkEntity
|
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
||||||
import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
|
import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
|
||||||
import im.vector.matrix.android.session.room.timeline.RoomDataHelper.createFakeListOfEvents
|
import im.vector.matrix.android.session.room.timeline.RoomDataHelper.createFakeListOfEvents
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.session.room.timeline
|
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.GetContextOfEventTask
|
||||||
import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
|
import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
|
||||||
import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEventPersistor
|
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 {
|
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 fakeEvents = RoomDataHelper.createFakeListOfEvents(30)
|
||||||
val tokenChunkEvent = FakeTokenChunkEvent(
|
val tokenChunkEvent = FakeTokenChunkEvent(
|
||||||
Random.nextLong(System.currentTimeMillis()).toString(),
|
Random.nextLong(System.currentTimeMillis()).toString(),
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.session.room.timeline
|
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.PaginationTask
|
||||||
import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEventPersistor
|
import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEventPersistor
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -24,7 +23,7 @@ import kotlin.random.Random
|
|||||||
|
|
||||||
internal class FakePaginationTask @Inject constructor(private val tokenChunkEventPersistor: TokenChunkEventPersistor) : PaginationTask {
|
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 fakeEvents = RoomDataHelper.createFakeListOfEvents(30)
|
||||||
val tokenChunkEvent = FakeTokenChunkEvent(params.from, Random.nextLong(System.currentTimeMillis()).toString(), fakeEvents)
|
val tokenChunkEvent = FakeTokenChunkEvent(params.from, Random.nextLong(System.currentTimeMillis()).toString(), fakeEvents)
|
||||||
return tokenChunkEventPersistor.insertInDb(tokenChunkEvent, params.roomId, params.direction)
|
return tokenChunkEventPersistor.insertInDb(tokenChunkEvent, params.roomId, params.direction)
|
||||||
|
@ -28,7 +28,7 @@ object MatrixPatterns {
|
|||||||
// regex pattern to find matrix user ids in a string.
|
// regex pattern to find matrix user ids in a string.
|
||||||
// See https://matrix.org/speculator/spec/HEAD/appendices.html#historical-user-ids
|
// See https://matrix.org/speculator/spec/HEAD/appendices.html#historical-user-ids
|
||||||
private const val MATRIX_USER_IDENTIFIER_REGEX = "@[A-Z0-9\\x21-\\x39\\x3B-\\x7F]+$DOMAIN_REGEX"
|
private const val MATRIX_USER_IDENTIFIER_REGEX = "@[A-Z0-9\\x21-\\x39\\x3B-\\x7F]+$DOMAIN_REGEX"
|
||||||
private val PATTERN_CONTAIN_MATRIX_USER_IDENTIFIER = MATRIX_USER_IDENTIFIER_REGEX.toRegex(RegexOption.IGNORE_CASE)
|
val PATTERN_CONTAIN_MATRIX_USER_IDENTIFIER = MATRIX_USER_IDENTIFIER_REGEX.toRegex(RegexOption.IGNORE_CASE)
|
||||||
|
|
||||||
// regex pattern to find room ids in a string.
|
// regex pattern to find room ids in a string.
|
||||||
private const val MATRIX_ROOM_IDENTIFIER_REGEX = "![A-Z0-9]+$DOMAIN_REGEX"
|
private const val MATRIX_ROOM_IDENTIFIER_REGEX = "![A-Z0-9]+$DOMAIN_REGEX"
|
||||||
@ -137,4 +137,23 @@ object MatrixPatterns {
|
|||||||
fun isGroupId(str: String?): Boolean {
|
fun isGroupId(str: String?): Boolean {
|
||||||
return str != null && str matches PATTERN_CONTAIN_MATRIX_GROUP_IDENTIFIER
|
return str != null && str matches PATTERN_CONTAIN_MATRIX_GROUP_IDENTIFIER
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract server name from a matrix id
|
||||||
|
*
|
||||||
|
* @param matrixId
|
||||||
|
* @return null if not found or if matrixId is null
|
||||||
|
*/
|
||||||
|
fun extractServerNameFromId(matrixId: String?): String? {
|
||||||
|
if (matrixId == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val index = matrixId.indexOf(":")
|
||||||
|
|
||||||
|
return if (index == -1) {
|
||||||
|
null
|
||||||
|
} else matrixId.substring(index + 1)
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ import okhttp3.TlsVersion
|
|||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class HomeServerConnectionConfig(
|
data class HomeServerConnectionConfig(
|
||||||
val homeServerUri: Uri,
|
val homeServerUri: Uri,
|
||||||
val identityServerUri: Uri,
|
val identityServerUri: Uri? = null,
|
||||||
val antiVirusServerUri: Uri? = null,
|
val antiVirusServerUri: Uri? = null,
|
||||||
val allowedFingerprints: MutableList<Fingerprint> = ArrayList(),
|
val allowedFingerprints: MutableList<Fingerprint> = ArrayList(),
|
||||||
val shouldPin: Boolean = false,
|
val shouldPin: Boolean = false,
|
||||||
@ -48,7 +48,7 @@ data class HomeServerConnectionConfig(
|
|||||||
class Builder {
|
class Builder {
|
||||||
|
|
||||||
private lateinit var homeServerUri: Uri
|
private lateinit var homeServerUri: Uri
|
||||||
private lateinit var identityServerUri: Uri
|
private var identityServerUri: Uri? = null
|
||||||
private var antiVirusServerUri: Uri? = null
|
private var antiVirusServerUri: Uri? = null
|
||||||
private val allowedFingerprints: MutableList<Fingerprint> = ArrayList()
|
private val allowedFingerprints: MutableList<Fingerprint> = ArrayList()
|
||||||
private var shouldPin: Boolean = false
|
private var shouldPin: Boolean = false
|
||||||
|
@ -26,8 +26,13 @@ import com.squareup.moshi.JsonClass
|
|||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class MatrixError(
|
data class MatrixError(
|
||||||
@Json(name = "errcode") val code: String,
|
@Json(name = "errcode") val code: String,
|
||||||
@Json(name = "error") val message: String
|
@Json(name = "error") val message: String,
|
||||||
) {
|
|
||||||
|
@Json(name = "consent_uri") val consentUri: String? = null,
|
||||||
|
// RESOURCE_LIMIT_EXCEEDED data
|
||||||
|
@Json(name = "limit_type") val limitType: String? = null,
|
||||||
|
@Json(name = "admin_contact") val adminUri: String? = null) {
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val FORBIDDEN = "M_FORBIDDEN"
|
const val FORBIDDEN = "M_FORBIDDEN"
|
||||||
@ -55,5 +60,8 @@ data class MatrixError(
|
|||||||
const val M_CONSENT_NOT_GIVEN = "M_CONSENT_NOT_GIVEN"
|
const val M_CONSENT_NOT_GIVEN = "M_CONSENT_NOT_GIVEN"
|
||||||
const val RESOURCE_LIMIT_EXCEEDED = "M_RESOURCE_LIMIT_EXCEEDED"
|
const val RESOURCE_LIMIT_EXCEEDED = "M_RESOURCE_LIMIT_EXCEEDED"
|
||||||
const val WRONG_ROOM_KEYS_VERSION = "M_WRONG_ROOM_KEYS_VERSION"
|
const val WRONG_ROOM_KEYS_VERSION = "M_WRONG_ROOM_KEYS_VERSION"
|
||||||
|
|
||||||
|
// Possible value for "limit_type"
|
||||||
|
const val LIMIT_TYPE_MAU = "monthly_active_user"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -18,6 +18,7 @@ package im.vector.matrix.android.api.permalinks
|
|||||||
|
|
||||||
import android.text.style.ClickableSpan
|
import android.text.style.ClickableSpan
|
||||||
import android.view.View
|
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.
|
* This MatrixPermalinkSpan is a clickable span which use a [Callback] to communicate back.
|
||||||
|
@ -18,6 +18,7 @@ package im.vector.matrix.android.api.pushrules
|
|||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
|
|
||||||
interface PushRuleService {
|
interface PushRuleService {
|
||||||
|
|
||||||
@ -31,7 +32,7 @@ interface PushRuleService {
|
|||||||
|
|
||||||
//TODO update rule
|
//TODO update rule
|
||||||
|
|
||||||
fun updatePushRuleEnableStatus(kind: String, pushRule: PushRule, enabled: Boolean, callback: MatrixCallback<Unit>)
|
fun updatePushRuleEnableStatus(kind: String, pushRule: PushRule, enabled: Boolean, callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
fun addPushRuleListener(listener: PushRuleListener)
|
fun addPushRuleListener(listener: PushRuleListener)
|
||||||
|
|
||||||
@ -41,6 +42,7 @@ interface PushRuleService {
|
|||||||
|
|
||||||
interface PushRuleListener {
|
interface PushRuleListener {
|
||||||
fun onMatchRule(event: Event, actions: List<Action>)
|
fun onMatchRule(event: Event, actions: List<Action>)
|
||||||
|
fun onRoomLeft(roomId: String)
|
||||||
fun batchFinish()
|
fun batchFinish()
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
|
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
|
||||||
import im.vector.matrix.android.internal.crypto.NewSessionListener
|
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.ImportRoomKeysResult
|
||||||
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
|
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.MXEncryptEventContentResult
|
||||||
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
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.DevicesListResponse
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
|
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
interface CryptoService {
|
interface CryptoService {
|
||||||
|
|
||||||
|
@ -20,6 +20,9 @@ import android.text.TextUtils
|
|||||||
import com.squareup.moshi.Json
|
import com.squareup.moshi.Json
|
||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
import im.vector.matrix.android.api.session.crypto.MXCryptoError
|
import im.vector.matrix.android.api.session.crypto.MXCryptoError
|
||||||
|
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
||||||
|
import im.vector.matrix.android.api.session.room.model.message.MessageType
|
||||||
|
import im.vector.matrix.android.api.session.room.send.SendState
|
||||||
import im.vector.matrix.android.api.util.JsonDict
|
import im.vector.matrix.android.api.util.JsonDict
|
||||||
import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
|
import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
|
||||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||||
@ -79,9 +82,15 @@ data class Event(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
||||||
|
@Transient
|
||||||
var mxDecryptionResult: OlmDecryptionResult? = null
|
var mxDecryptionResult: OlmDecryptionResult? = null
|
||||||
|
|
||||||
|
@Transient
|
||||||
var mCryptoError: MXCryptoError.ErrorType? = null
|
var mCryptoError: MXCryptoError.ErrorType? = null
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
var sendState: SendState = SendState.UNKNOWN
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if event is a state event.
|
* Check if event is a state event.
|
||||||
@ -95,42 +104,6 @@ data class Event(
|
|||||||
// Crypto
|
// Crypto
|
||||||
//==============================================================================================================
|
//==============================================================================================================
|
||||||
|
|
||||||
// /**
|
|
||||||
// * For encrypted events, the plaintext payload for the event.
|
|
||||||
// * This is a small MXEvent instance with typically value for `type` and 'content' fields.
|
|
||||||
// */
|
|
||||||
// @Transient
|
|
||||||
// var mClearEvent: Event? = null
|
|
||||||
// private set
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Curve25519 key which we believe belongs to the sender of the event.
|
|
||||||
// * See `senderKey` property.
|
|
||||||
// */
|
|
||||||
// @Transient
|
|
||||||
// private var mSenderCurve25519Key: String? = null
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Ed25519 key which the sender of this event (for olm) or the creator of the megolm session (for megolm) claims to own.
|
|
||||||
// * See `claimedEd25519Key` property.
|
|
||||||
// */
|
|
||||||
// @Transient
|
|
||||||
// private var mClaimedEd25519Key: String? = null
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Curve25519 keys of devices involved in telling us about the senderCurve25519Key and claimedEd25519Key.
|
|
||||||
// * See `forwardingCurve25519KeyChain` property.
|
|
||||||
// */
|
|
||||||
// @Transient
|
|
||||||
// private var mForwardingCurve25519KeyChain: List<String> = ArrayList()
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Decryption error
|
|
||||||
// */
|
|
||||||
// @Transient
|
|
||||||
// var mCryptoError: MXCryptoError? = null
|
|
||||||
// private set
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if this event is encrypted.
|
* @return true if this event is encrypted.
|
||||||
*/
|
*/
|
||||||
@ -138,51 +111,11 @@ data class Event(
|
|||||||
return TextUtils.equals(type, EventType.ENCRYPTED)
|
return TextUtils.equals(type, EventType.ENCRYPTED)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the clear data on this event.
|
|
||||||
* This is used after decrypting an event; it should not be used by applications.
|
|
||||||
*
|
|
||||||
* @param decryptionResult the decryption result, including the plaintext and some key info.
|
|
||||||
*/
|
|
||||||
// internal fun setClearData(decryptionResult: MXEventDecryptionResult?) {
|
|
||||||
// mClearEvent = null
|
|
||||||
// if (decryptionResult != null) {
|
|
||||||
// if (decryptionResult.clearEvent != null) {
|
|
||||||
// val adapter = MoshiProvider.providesMoshi().adapter(Event::class.java)
|
|
||||||
// mClearEvent = adapter.fromJsonValue(decryptionResult.clearEvent)
|
|
||||||
//
|
|
||||||
// if (mClearEvent != null) {
|
|
||||||
// mSenderCurve25519Key = decryptionResult.senderCurve25519Key
|
|
||||||
// mClaimedEd25519Key = decryptionResult.claimedEd25519Key
|
|
||||||
// mForwardingCurve25519KeyChain = decryptionResult.forwardingCurve25519KeyChain
|
|
||||||
//
|
|
||||||
// // For encrypted events with relation, the m.relates_to is kept in clear, so we need to put it back
|
|
||||||
// // in the clear event
|
|
||||||
// try {
|
|
||||||
// content?.get("m.relates_to")?.let { clearRelates ->
|
|
||||||
// mClearEvent = mClearEvent?.copy(
|
|
||||||
// content = HashMap(mClearEvent!!.content).apply {
|
|
||||||
// this["m.relates_to"] = clearRelates
|
|
||||||
// }
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// } catch (e: Exception) {
|
|
||||||
// Timber.e(e, "Unable to restore 'm.relates_to' the clear event")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// mCryptoError = null
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The curve25519 key that sent this event.
|
* @return The curve25519 key that sent this event.
|
||||||
*/
|
*/
|
||||||
fun getSenderKey(): String? {
|
fun getSenderKey(): String? {
|
||||||
return mxDecryptionResult?.senderKey
|
return mxDecryptionResult?.senderKey
|
||||||
// return mClearEvent?.mSenderCurve25519Key ?: mSenderCurve25519Key
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -190,23 +123,13 @@ data class Event(
|
|||||||
*/
|
*/
|
||||||
fun getKeysClaimed(): Map<String, String> {
|
fun getKeysClaimed(): Map<String, String> {
|
||||||
return mxDecryptionResult?.keysClaimed ?: HashMap()
|
return mxDecryptionResult?.keysClaimed ?: HashMap()
|
||||||
// val res = HashMap<String, String>()
|
|
||||||
//
|
|
||||||
// val claimedEd25519Key = if (null != mClearEvent) mClearEvent!!.mClaimedEd25519Key else mClaimedEd25519Key
|
|
||||||
//
|
|
||||||
// if (null != claimedEd25519Key) {
|
|
||||||
// res["ed25519"] = claimedEd25519Key
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return res
|
|
||||||
}
|
}
|
||||||
//
|
|
||||||
/**
|
/**
|
||||||
* @return the event type
|
* @return the event type
|
||||||
*/
|
*/
|
||||||
fun getClearType(): String {
|
fun getClearType(): String {
|
||||||
return mxDecryptionResult?.payload?.get("type")?.toString()
|
return mxDecryptionResult?.payload?.get("type")?.toString() ?: type
|
||||||
?: type//get("type")?.toString() ?: type
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -216,30 +139,8 @@ data class Event(
|
|||||||
return mxDecryptionResult?.payload?.get("content") as? Content ?: content
|
return mxDecryptionResult?.payload?.get("content") as? Content ?: content
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
|
||||||
// * @return the linked crypto error
|
|
||||||
// */
|
|
||||||
// fun getCryptoError(): MXCryptoError? {
|
|
||||||
// return mCryptoError
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Update the linked crypto error
|
|
||||||
// *
|
|
||||||
// * @param error the new crypto error.
|
|
||||||
// */
|
|
||||||
// fun setCryptoError(error: MXCryptoError?) {
|
|
||||||
// mCryptoError = error
|
|
||||||
// if (null != error) {
|
|
||||||
// mClearEvent = null
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
fun toContentStringWithIndent(): String {
|
fun toContentStringWithIndent(): String {
|
||||||
val contentMap = this.toContent()?.toMutableMap() ?: HashMap()
|
val contentMap = toContent()?.toMutableMap() ?: HashMap()
|
||||||
contentMap.remove("mxDecryptionResult")
|
|
||||||
contentMap.remove("mCryptoError")
|
|
||||||
return JSONObject(contentMap).toString(4)
|
return JSONObject(contentMap).toString(4)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,6 +173,7 @@ data class Event(
|
|||||||
if (redacts != other.redacts) return false
|
if (redacts != other.redacts) return false
|
||||||
if (mxDecryptionResult != other.mxDecryptionResult) return false
|
if (mxDecryptionResult != other.mxDecryptionResult) return false
|
||||||
if (mCryptoError != other.mCryptoError) return false
|
if (mCryptoError != other.mCryptoError) return false
|
||||||
|
if (sendState != other.sendState) return false
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -289,6 +191,27 @@ data class Event(
|
|||||||
result = 31 * result + (redacts?.hashCode() ?: 0)
|
result = 31 * result + (redacts?.hashCode() ?: 0)
|
||||||
result = 31 * result + (mxDecryptionResult?.hashCode() ?: 0)
|
result = 31 * result + (mxDecryptionResult?.hashCode() ?: 0)
|
||||||
result = 31 * result + (mCryptoError?.hashCode() ?: 0)
|
result = 31 * result + (mCryptoError?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + sendState.hashCode()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun Event.isTextMessage(): Boolean {
|
||||||
|
return getClearType() == EventType.MESSAGE
|
||||||
|
&& when (getClearContent()?.toModel<MessageContent>()?.type) {
|
||||||
|
MessageType.MSGTYPE_TEXT,
|
||||||
|
MessageType.MSGTYPE_EMOTE,
|
||||||
|
MessageType.MSGTYPE_NOTICE -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Event.isImageMessage(): Boolean {
|
||||||
|
return getClearType() == EventType.MESSAGE
|
||||||
|
&& when (getClearContent()?.toModel<MessageContent>()?.type) {
|
||||||
|
MessageType.MSGTYPE_IMAGE -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
}
|
}
|
@ -30,20 +30,17 @@ interface RoomDirectoryService {
|
|||||||
/**
|
/**
|
||||||
* Get rooms from directory
|
* Get rooms from directory
|
||||||
*/
|
*/
|
||||||
fun getPublicRooms(server: String?,
|
fun getPublicRooms(server: String?, publicRoomsParams: PublicRoomsParams, callback: MatrixCallback<PublicRoomsResponse>): Cancelable
|
||||||
publicRoomsParams: PublicRoomsParams,
|
|
||||||
callback: MatrixCallback<PublicRoomsResponse>): Cancelable
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Join a room by id
|
* Join a room by id
|
||||||
*/
|
*/
|
||||||
fun joinRoom(roomId: String,
|
fun joinRoom(roomId: String, callback: MatrixCallback<Unit>): Cancelable
|
||||||
callback: MatrixCallback<Unit>)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches the overall metadata about protocols supported by the homeserver.
|
* Fetches the overall metadata about protocols supported by the homeserver.
|
||||||
* Includes both the available protocols and all fields required for queries against each protocol.
|
* Includes both the available protocols and all fields required for queries against each protocol.
|
||||||
*/
|
*/
|
||||||
fun getThirdPartyProtocol(callback: MatrixCallback<Map<String, ThirdPartyProtocol>>)
|
fun getThirdPartyProtocol(callback: MatrixCallback<Map<String, ThirdPartyProtocol>>): Cancelable
|
||||||
|
|
||||||
}
|
}
|
@ -20,6 +20,7 @@ import androidx.lifecycle.LiveData
|
|||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
|
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
|
||||||
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This interface defines methods to get rooms. It's implemented at the session level.
|
* This interface defines methods to get rooms. It's implemented at the session level.
|
||||||
@ -27,10 +28,18 @@ import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
|
|||||||
interface RoomService {
|
interface RoomService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a room
|
* Create a room asynchronously
|
||||||
*/
|
*/
|
||||||
fun createRoom(createRoomParams: CreateRoomParams,
|
fun createRoom(createRoomParams: CreateRoomParams, callback: MatrixCallback<String>): Cancelable
|
||||||
callback: MatrixCallback<String>)
|
|
||||||
|
/**
|
||||||
|
* Join a room by id
|
||||||
|
* @param roomId the roomId of the room to join
|
||||||
|
* @param viaServers the servers to attempt to join the room through. One of the servers must be participating in the room.
|
||||||
|
*/
|
||||||
|
fun joinRoom(roomId: String,
|
||||||
|
viaServers: List<String> = emptyList(),
|
||||||
|
callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a room from a roomId
|
* Get a room from a roomId
|
||||||
|
@ -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.api.session.room.failure
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.failure.Failure
|
||||||
|
|
||||||
|
sealed class CreateRoomFailure : Failure.FeatureFailure() {
|
||||||
|
|
||||||
|
object CreatedWithTimeout: CreateRoomFailure()
|
||||||
|
|
||||||
|
}
|
@ -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.api.session.room.failure
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.failure.Failure
|
||||||
|
|
||||||
|
sealed class JoinRoomFailure : Failure.FeatureFailure() {
|
||||||
|
|
||||||
|
object JoinedWithTimeout : JoinRoomFailure()
|
||||||
|
|
||||||
|
}
|
@ -30,7 +30,7 @@ interface MembershipService {
|
|||||||
* This methods load all room members if it was done yet.
|
* This methods load all room members if it was done yet.
|
||||||
* @return a [Cancelable]
|
* @return a [Cancelable]
|
||||||
*/
|
*/
|
||||||
fun loadRoomMembersIfNeeded(): Cancelable
|
fun loadRoomMembersIfNeeded(matrixCallback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the roomMember with userId or null.
|
* Return the roomMember with userId or null.
|
||||||
@ -52,16 +52,17 @@ interface MembershipService {
|
|||||||
/**
|
/**
|
||||||
* Invite a user in the room
|
* Invite a user in the room
|
||||||
*/
|
*/
|
||||||
fun invite(userId: String, callback: MatrixCallback<Unit>)
|
fun invite(userId: String, callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Join the room, or accept an invitation.
|
* Join the room, or accept an invitation.
|
||||||
*/
|
*/
|
||||||
fun join(callback: MatrixCallback<Unit>)
|
|
||||||
|
fun join(viaServers: List<String> = emptyList(), callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Leave the room, or reject an invitation.
|
* Leave the room, or reject an invitation.
|
||||||
*/
|
*/
|
||||||
fun leave(callback: MatrixCallback<Unit>)
|
fun leave(callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
}
|
}
|
@ -34,5 +34,10 @@ data class RoomSummary(
|
|||||||
val notificationCount: Int = 0,
|
val notificationCount: Int = 0,
|
||||||
val highlightCount: Int = 0,
|
val highlightCount: Int = 0,
|
||||||
val tags: List<RoomTag> = emptyList(),
|
val tags: List<RoomTag> = emptyList(),
|
||||||
val membership: Membership = Membership.NONE
|
val membership: Membership = Membership.NONE,
|
||||||
)
|
val versioningState: VersioningState = VersioningState.NONE
|
||||||
|
) {
|
||||||
|
|
||||||
|
val isVersioned: Boolean
|
||||||
|
get() = versioningState != VersioningState.NONE
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* 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.api.session.room.model
|
||||||
|
|
||||||
|
enum class VersioningState {
|
||||||
|
NONE,
|
||||||
|
UPGRADED_ROOM_NOT_JOINED,
|
||||||
|
UPGRADED_ROOM_JOINED
|
||||||
|
}
|
@ -223,11 +223,10 @@ class CreateRoomParams {
|
|||||||
credentials: Credentials,
|
credentials: Credentials,
|
||||||
ids: List<String>) {
|
ids: List<String>) {
|
||||||
for (id in ids) {
|
for (id in ids) {
|
||||||
if (Patterns.EMAIL_ADDRESS.matcher(id).matches()) {
|
if (Patterns.EMAIL_ADDRESS.matcher(id).matches() && hsConfig.identityServerUri != null) {
|
||||||
if (null == invite3pids) {
|
if (null == invite3pids) {
|
||||||
invite3pids = ArrayList()
|
invite3pids = ArrayList()
|
||||||
}
|
}
|
||||||
|
|
||||||
val pid = Invite3Pid(idServer = hsConfig.identityServerUri.host!!,
|
val pid = Invite3Pid(idServer = hsConfig.identityServerUri.host!!,
|
||||||
medium = ThreePidMedium.EMAIL,
|
medium = ThreePidMedium.EMAIL,
|
||||||
address = id)
|
address = id)
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* 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.api.session.room.model.create
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A link to an old room in case of room versioning
|
||||||
|
*/
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class Predecessor(
|
||||||
|
@Json(name = "room_id") val roomId: String? = null,
|
||||||
|
@Json(name = "event_id") val eventId: String? = null
|
||||||
|
)
|
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* 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.api.session.room.model.create
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Content of a m.room.create type event
|
||||||
|
*/
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class RoomCreateContent(
|
||||||
|
@Json(name = "creator") val creator: String? = null,
|
||||||
|
@Json(name = "room_version") val roomVersion: String? = null,
|
||||||
|
@Json(name = "predecessor") val predecessor: Predecessor? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
|
@ -29,5 +29,5 @@ interface MessageContent {
|
|||||||
|
|
||||||
|
|
||||||
fun MessageContent?.isReply(): Boolean {
|
fun MessageContent?.isReply(): Boolean {
|
||||||
return this?.relatesTo?.inReplyTo != null
|
return this?.relatesTo?.inReplyTo?.eventId != null
|
||||||
}
|
}
|
@ -21,5 +21,5 @@ import com.squareup.moshi.JsonClass
|
|||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class ReplyToContent(
|
data class ReplyToContent(
|
||||||
@Json(name = "event_id") val eventId: String
|
@Json(name = "event_id") val eventId: String? = null
|
||||||
)
|
)
|
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* 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.api.session.room.model.tombstone
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class to contains Tombstone information
|
||||||
|
*/
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class RoomTombstoneContent(
|
||||||
|
@Json(name = "body") val body: String? = null,
|
||||||
|
@Json(name = "replacement_room") val replacementRoom: String?
|
||||||
|
)
|
@ -19,6 +19,7 @@ package im.vector.matrix.android.api.session.room.send
|
|||||||
import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.room.model.message.MessageType
|
import im.vector.matrix.android.api.session.room.model.message.MessageType
|
||||||
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
|
|
||||||
|
|
||||||
@ -65,4 +66,31 @@ interface SendService {
|
|||||||
*/
|
*/
|
||||||
fun redactEvent(event: Event, reason: String?): Cancelable
|
fun redactEvent(event: Event, reason: String?): Cancelable
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule this message to be resent
|
||||||
|
* @param localEcho the unsent local echo
|
||||||
|
*/
|
||||||
|
fun resendTextMessage(localEcho: TimelineEvent): Cancelable?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule this message to be resent
|
||||||
|
* @param localEcho the unsent local echo
|
||||||
|
*/
|
||||||
|
fun resendMediaMessage(localEcho: TimelineEvent): Cancelable?
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove this failed message from the timeline
|
||||||
|
* @param localEcho the unsent local echo
|
||||||
|
*/
|
||||||
|
fun deleteFailedEcho(localEcho: TimelineEvent)
|
||||||
|
|
||||||
|
fun clearSendingQueue()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resend all failed messages one by one (and keep order)
|
||||||
|
*/
|
||||||
|
fun resendAllFailedMessages()
|
||||||
|
|
||||||
}
|
}
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.api.session.room.send
|
package im.vector.matrix.android.api.session.room.send
|
||||||
|
|
||||||
|
|
||||||
enum class SendState {
|
enum class SendState {
|
||||||
UNKNOWN,
|
UNKNOWN,
|
||||||
// the event has not been sent
|
// the event has not been sent
|
||||||
@ -33,12 +34,19 @@ enum class SendState {
|
|||||||
// the event failed to be sent because some unknown devices have been found while encrypting it
|
// the event failed to be sent because some unknown devices have been found while encrypting it
|
||||||
FAILED_UNKNOWN_DEVICES;
|
FAILED_UNKNOWN_DEVICES;
|
||||||
|
|
||||||
fun isSent(): Boolean {
|
internal companion object {
|
||||||
return this == SENT || this == SYNCED
|
val HAS_FAILED_STATES = listOf(UNDELIVERED, FAILED_UNKNOWN_DEVICES)
|
||||||
|
val IS_SENT_STATES = listOf(SENT, SYNCED)
|
||||||
|
val IS_SENDING_STATES = listOf(UNSENT, ENCRYPTING, SENDING)
|
||||||
|
val PENDING_STATES = IS_SENDING_STATES + HAS_FAILED_STATES
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hasFailed(): Boolean {
|
fun isSent() = IS_SENT_STATES.contains(this)
|
||||||
return this == UNDELIVERED || this == FAILED_UNKNOWN_DEVICES
|
|
||||||
}
|
fun hasFailed() = HAS_FAILED_STATES.contains(this)
|
||||||
|
|
||||||
|
fun isSending() = IS_SENDING_STATES.contains(this)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package im.vector.matrix.android.api.session.room.state
|
package im.vector.matrix.android.api.session.room.state
|
||||||
|
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
|
|
||||||
interface StateService {
|
interface StateService {
|
||||||
|
|
||||||
@ -25,4 +26,6 @@ interface StateService {
|
|||||||
*/
|
*/
|
||||||
fun updateTopic(topic: String, callback: MatrixCallback<Unit>)
|
fun updateTopic(topic: String, callback: MatrixCallback<Unit>)
|
||||||
|
|
||||||
|
fun getStateEvent(eventType: String): Event?
|
||||||
|
|
||||||
}
|
}
|
@ -56,6 +56,9 @@ interface Timeline {
|
|||||||
*/
|
*/
|
||||||
fun paginate(direction: Direction, count: Int)
|
fun paginate(direction: Direction, count: Int)
|
||||||
|
|
||||||
|
fun pendingEventCount() : Int
|
||||||
|
|
||||||
|
fun failedToDeliverEventCount() : Int
|
||||||
|
|
||||||
interface Listener {
|
interface Listener {
|
||||||
/**
|
/**
|
||||||
|
@ -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.EventAnnotationsSummary
|
||||||
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
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.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.api.util.ContentUtils.extractUsefulTextFromReply
|
||||||
import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
|
import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
|
||||||
|
|
||||||
@ -38,7 +37,6 @@ data class TimelineEvent(
|
|||||||
val senderName: String?,
|
val senderName: String?,
|
||||||
val isUniqueDisplayName: Boolean,
|
val isUniqueDisplayName: Boolean,
|
||||||
val senderAvatar: String?,
|
val senderAvatar: String?,
|
||||||
val sendState: SendState,
|
|
||||||
val annotations: EventAnnotationsSummary? = null
|
val annotations: EventAnnotationsSummary? = null
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -86,6 +84,12 @@ data class TimelineEvent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells if the event has been edited
|
||||||
|
*/
|
||||||
|
fun TimelineEvent.hasBeenEdited() = annotations?.editSummary != null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get last MessageContent, after a possible edition
|
* Get last MessageContent, after a possible edition
|
||||||
*/
|
*/
|
||||||
|
@ -17,7 +17,10 @@
|
|||||||
package im.vector.matrix.android.api.session.user
|
package im.vector.matrix.android.api.session.user
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.paging.PagedList
|
||||||
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.session.user.model.User
|
import im.vector.matrix.android.api.session.user.model.User
|
||||||
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This interface defines methods to get users. It's implemented at the session level.
|
* This interface defines methods to get users. It's implemented at the session level.
|
||||||
@ -31,11 +34,34 @@ interface UserService {
|
|||||||
*/
|
*/
|
||||||
fun getUser(userId: String): User?
|
fun getUser(userId: String): User?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search list of users on server directory.
|
||||||
|
* @param search the searched term
|
||||||
|
* @param limit the max number of users to return
|
||||||
|
* @param excludedUserIds the user ids to filter from the search
|
||||||
|
* @param callback the async callback
|
||||||
|
* @return Cancelable
|
||||||
|
*/
|
||||||
|
fun searchUsersDirectory(search: String, limit: Int, excludedUserIds: Set<String>, callback: MatrixCallback<List<User>>): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Observe a live user from a userId
|
* Observe a live user from a userId
|
||||||
* @param userId the userId to look for.
|
* @param userId the userId to look for.
|
||||||
* @return a Livedata of user with userId
|
* @return a Livedata of user with userId
|
||||||
*/
|
*/
|
||||||
fun observeUser(userId: String): LiveData<User?>
|
fun liveUser(userId: String): LiveData<User?>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observe a live list of users sorted alphabetically
|
||||||
|
* @return a Livedata of users
|
||||||
|
*/
|
||||||
|
fun liveUsers(): LiveData<List<User>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observe a live [PagedList] of users sorted alphabetically. You can filter the users.
|
||||||
|
* @param filter the filter. It will look into userId and displayName.
|
||||||
|
* @return a Livedata of users
|
||||||
|
*/
|
||||||
|
fun livePagedUsers(filter: String? = null): LiveData<PagedList<User>>
|
||||||
|
|
||||||
}
|
}
|
@ -19,5 +19,6 @@ package im.vector.matrix.android.api.util
|
|||||||
class CancelableBag : Cancelable, MutableList<Cancelable> by ArrayList() {
|
class CancelableBag : Cancelable, MutableList<Cancelable> by ArrayList() {
|
||||||
override fun cancel() {
|
override fun cancel() {
|
||||||
forEach { it.cancel() }
|
forEach { it.cancel() }
|
||||||
|
clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,9 @@ internal class DefaultAuthenticator @Inject constructor(@Unauthenticated
|
|||||||
callback: MatrixCallback<Session>): Cancelable {
|
callback: MatrixCallback<Session>): Cancelable {
|
||||||
|
|
||||||
val job = GlobalScope.launch(coroutineDispatchers.main) {
|
val job = GlobalScope.launch(coroutineDispatchers.main) {
|
||||||
val sessionOrFailure = authenticate(homeServerConnectionConfig, login, password)
|
val sessionOrFailure = runCatching {
|
||||||
|
authenticate(homeServerConnectionConfig, login, password)
|
||||||
|
}
|
||||||
sessionOrFailure.foldToCallback(callback)
|
sessionOrFailure.foldToCallback(callback)
|
||||||
}
|
}
|
||||||
return CancelableCoroutine(job)
|
return CancelableCoroutine(job)
|
||||||
@ -85,16 +87,12 @@ internal class DefaultAuthenticator @Inject constructor(@Unauthenticated
|
|||||||
} else {
|
} else {
|
||||||
PasswordLoginParams.userIdentifier(login, password, "Mobile")
|
PasswordLoginParams.userIdentifier(login, password, "Mobile")
|
||||||
}
|
}
|
||||||
executeRequest<Credentials> {
|
val credentials = executeRequest<Credentials> {
|
||||||
apiCall = authAPI.login(loginParams)
|
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 {
|
private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI {
|
||||||
|
@ -27,7 +27,7 @@ internal interface SessionParamsStore {
|
|||||||
|
|
||||||
fun getAll(): List<SessionParams>
|
fun getAll(): List<SessionParams>
|
||||||
|
|
||||||
fun save(sessionParams: SessionParams): Try<SessionParams>
|
fun save(sessionParams: SessionParams): Try<Unit>
|
||||||
|
|
||||||
fun delete(userId: String): Try<Unit>
|
fun delete(userId: String): Try<Unit>
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ internal class RealmSessionParamsStore @Inject constructor(private val mapper: S
|
|||||||
return sessionParams
|
return sessionParams
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun save(sessionParams: SessionParams): Try<SessionParams> {
|
override fun save(sessionParams: SessionParams): Try<Unit> {
|
||||||
return Try {
|
return Try {
|
||||||
val entity = mapper.map(sessionParams)
|
val entity = mapper.map(sessionParams)
|
||||||
if (entity != null) {
|
if (entity != null) {
|
||||||
@ -72,7 +72,6 @@ internal class RealmSessionParamsStore @Inject constructor(private val mapper: S
|
|||||||
}
|
}
|
||||||
realm.close()
|
realm.close()
|
||||||
}
|
}
|
||||||
sessionParams
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.Credentials
|
||||||
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
||||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||||
import im.vector.matrix.android.internal.di.MatrixScope
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class SessionParamsMapper @Inject constructor(moshi: Moshi) {
|
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.session.sync.model.SyncResponse
|
||||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||||
import im.vector.matrix.android.internal.task.configureWith
|
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.JsonCanonicalizer
|
||||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||||
import im.vector.matrix.android.internal.util.fetchCopied
|
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>) {
|
override fun setDeviceName(deviceId: String, deviceName: String, callback: MatrixCallback<Unit>) {
|
||||||
setDeviceNameTask
|
setDeviceNameTask
|
||||||
.configureWith(SetDeviceNameTask.Params(deviceId, deviceName))
|
.configureWith(SetDeviceNameTask.Params(deviceId, deviceName)) {
|
||||||
.dispatchTo(callback)
|
this.callback = callback
|
||||||
|
}
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deleteDevice(deviceId: String, callback: MatrixCallback<Unit>) {
|
override fun deleteDevice(deviceId: String, callback: MatrixCallback<Unit>) {
|
||||||
deleteDeviceTask
|
deleteDeviceTask
|
||||||
.configureWith(DeleteDeviceTask.Params(deviceId))
|
.configureWith(DeleteDeviceTask.Params(deviceId)) {
|
||||||
.dispatchTo(callback)
|
this.callback = callback
|
||||||
|
}
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deleteDeviceWithUserPassword(deviceId: String, authSession: String?, password: String, callback: MatrixCallback<Unit>) {
|
override fun deleteDeviceWithUserPassword(deviceId: String, authSession: String?, password: String, callback: MatrixCallback<Unit>) {
|
||||||
deleteDeviceWithUserPasswordTask
|
deleteDeviceWithUserPasswordTask
|
||||||
.configureWith(DeleteDeviceWithUserPasswordTask.Params(deviceId, authSession, password))
|
.configureWith(DeleteDeviceWithUserPasswordTask.Params(deviceId, authSession, password)) {
|
||||||
.dispatchTo(callback)
|
this.callback = callback
|
||||||
|
}
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,8 +198,9 @@ internal class CryptoManager @Inject constructor(
|
|||||||
|
|
||||||
override fun getDevicesList(callback: MatrixCallback<DevicesListResponse>) {
|
override fun getDevicesList(callback: MatrixCallback<DevicesListResponse>) {
|
||||||
getDevicesTask
|
getDevicesTask
|
||||||
.toConfigurableTask()
|
.configureWith {
|
||||||
.dispatchTo(callback)
|
this.callback = callback
|
||||||
|
}
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,16 +257,9 @@ internal class CryptoManager @Inject constructor(
|
|||||||
private suspend fun internalStart(isInitialSync: Boolean) {
|
private suspend fun internalStart(isInitialSync: Boolean) {
|
||||||
// Open the store
|
// Open the store
|
||||||
cryptoStore.open()
|
cryptoStore.open()
|
||||||
|
runCatching {
|
||||||
uploadDeviceKeys()
|
uploadDeviceKeys()
|
||||||
.flatMap { oneTimeKeysUploader.maybeUploadOneTimeKeys() }
|
oneTimeKeysUploader.maybeUploadOneTimeKeys()
|
||||||
.fold(
|
|
||||||
{
|
|
||||||
Timber.e("Start failed: $it")
|
|
||||||
delay(1000)
|
|
||||||
isStarting.set(false)
|
|
||||||
internalStart(isInitialSync)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
outgoingRoomKeyRequestManager.start()
|
outgoingRoomKeyRequestManager.start()
|
||||||
keysBackup.checkAndStartKeysBackup()
|
keysBackup.checkAndStartKeysBackup()
|
||||||
if (isInitialSync) {
|
if (isInitialSync) {
|
||||||
@ -273,8 +269,16 @@ internal class CryptoManager @Inject constructor(
|
|||||||
} else {
|
} else {
|
||||||
incomingRoomKeyRequestManager.processReceivedRoomKeyRequests()
|
incomingRoomKeyRequestManager.processReceivedRoomKeyRequests()
|
||||||
}
|
}
|
||||||
|
}.fold(
|
||||||
|
{
|
||||||
isStarting.set(false)
|
isStarting.set(false)
|
||||||
isStarted.set(true)
|
isStarted.set(true)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Timber.e("Start failed: $it")
|
||||||
|
delay(1000)
|
||||||
|
isStarting.set(false)
|
||||||
|
internalStart(isInitialSync)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -282,7 +286,7 @@ internal class CryptoManager @Inject constructor(
|
|||||||
/**
|
/**
|
||||||
* Close the crypto
|
* Close the crypto
|
||||||
*/
|
*/
|
||||||
fun close() {
|
fun close() = runBlocking(coroutineDispatchers.crypto) {
|
||||||
olmDevice.release()
|
olmDevice.release()
|
||||||
cryptoStore.close()
|
cryptoStore.close()
|
||||||
outgoingRoomKeyRequestManager.stop()
|
outgoingRoomKeyRequestManager.stop()
|
||||||
@ -556,13 +560,16 @@ internal class CryptoManager @Inject constructor(
|
|||||||
if (safeAlgorithm != null) {
|
if (safeAlgorithm != null) {
|
||||||
val t0 = System.currentTimeMillis()
|
val t0 = System.currentTimeMillis()
|
||||||
Timber.v("## encryptEventContent() starts")
|
Timber.v("## encryptEventContent() starts")
|
||||||
|
runCatching {
|
||||||
safeAlgorithm.encryptEventContent(eventContent, eventType, userIds)
|
safeAlgorithm.encryptEventContent(eventContent, eventType, userIds)
|
||||||
|
}
|
||||||
.fold(
|
.fold(
|
||||||
{ callback.onFailure(it) },
|
|
||||||
{
|
{
|
||||||
Timber.v("## encryptEventContent() : succeeds after ${System.currentTimeMillis() - t0} ms")
|
Timber.v("## encryptEventContent() : succeeds after ${System.currentTimeMillis() - t0} ms")
|
||||||
callback.onSuccess(MXEncryptEventContentResult(it, EventType.ENCRYPTED))
|
callback.onSuccess(MXEncryptEventContentResult(it, EventType.ENCRYPTED))
|
||||||
}
|
},
|
||||||
|
{ callback.onFailure(it) }
|
||||||
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
val algorithm = getEncryptionAlgorithm(roomId)
|
val algorithm = getEncryptionAlgorithm(roomId)
|
||||||
@ -584,10 +591,7 @@ internal class CryptoManager @Inject constructor(
|
|||||||
@Throws(MXCryptoError::class)
|
@Throws(MXCryptoError::class)
|
||||||
override fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult {
|
override fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult {
|
||||||
return runBlocking {
|
return runBlocking {
|
||||||
internalDecryptEvent(event, timeline).fold(
|
internalDecryptEvent(event, timeline)
|
||||||
{ throw it },
|
|
||||||
{ it }
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -600,9 +604,11 @@ internal class CryptoManager @Inject constructor(
|
|||||||
*/
|
*/
|
||||||
override fun decryptEventAsync(event: Event, timeline: String, callback: MatrixCallback<MXEventDecryptionResult>) {
|
override fun decryptEventAsync(event: Event, timeline: String, callback: MatrixCallback<MXEventDecryptionResult>) {
|
||||||
GlobalScope.launch {
|
GlobalScope.launch {
|
||||||
val result = withContext(coroutineDispatchers.crypto) {
|
val result = runCatching {
|
||||||
|
withContext(coroutineDispatchers.crypto) {
|
||||||
internalDecryptEvent(event, timeline)
|
internalDecryptEvent(event, timeline)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
result.foldToCallback(callback)
|
result.foldToCallback(callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -612,22 +618,22 @@ internal class CryptoManager @Inject constructor(
|
|||||||
*
|
*
|
||||||
* @param event the raw event.
|
* @param event the raw event.
|
||||||
* @param timeline the id of the timeline where the event is decrypted. It is used to prevent replay attack.
|
* @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
|
val eventContent = event.content
|
||||||
return if (eventContent == null) {
|
if (eventContent == null) {
|
||||||
Timber.e("## decryptEvent : empty event content")
|
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 {
|
} else {
|
||||||
val algorithm = eventContent["algorithm"]?.toString()
|
val algorithm = eventContent["algorithm"]?.toString()
|
||||||
val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(event.roomId, algorithm)
|
val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(event.roomId, algorithm)
|
||||||
if (alg == null) {
|
if (alg == null) {
|
||||||
val reason = String.format(MXCryptoError.UNABLE_TO_DECRYPT_REASON, event.eventId, algorithm)
|
val reason = String.format(MXCryptoError.UNABLE_TO_DECRYPT_REASON, event.eventId, algorithm)
|
||||||
Timber.e("## decryptEvent() : $reason")
|
Timber.e("## decryptEvent() : $reason")
|
||||||
Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT, reason))
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT, reason)
|
||||||
} else {
|
} else {
|
||||||
alg.decryptEvent(event, timeline)
|
return alg.decryptEvent(event, timeline)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -689,11 +695,12 @@ internal class CryptoManager @Inject constructor(
|
|||||||
private fun onRoomEncryptionEvent(roomId: String, event: Event) {
|
private fun onRoomEncryptionEvent(roomId: String, event: Event) {
|
||||||
GlobalScope.launch(coroutineDispatchers.crypto) {
|
GlobalScope.launch(coroutineDispatchers.crypto) {
|
||||||
val params = LoadRoomMembersTask.Params(roomId)
|
val params = LoadRoomMembersTask.Params(roomId)
|
||||||
loadRoomMembersTask
|
try {
|
||||||
.execute(params)
|
loadRoomMembersTask.execute(params)
|
||||||
.map { _ ->
|
|
||||||
val userIds = getRoomUserIds(roomId)
|
val userIds = getRoomUserIds(roomId)
|
||||||
setEncryptionInRoom(roomId, event.content?.get("algorithm")?.toString(), true, userIds)
|
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.
|
* Upload my user's device keys.
|
||||||
*/
|
*/
|
||||||
private suspend fun uploadDeviceKeys(): Try<KeysUploadResponse> {
|
private suspend fun uploadDeviceKeys(): KeysUploadResponse {
|
||||||
// Prepare the device keys data to send
|
// Prepare the device keys data to send
|
||||||
// Sign it
|
// Sign it
|
||||||
val canonicalJson = JsonCanonicalizer.getCanonicalJson(Map::class.java, getMyDevice().signalableJSONDictionary())
|
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>) {
|
fun checkUnknownDevices(userIds: List<String>, callback: MatrixCallback<Unit>) {
|
||||||
// force the refresh to ensure that the devices list is up-to-date
|
// force the refresh to ensure that the devices list is up-to-date
|
||||||
GlobalScope.launch(coroutineDispatchers.crypto) {
|
GlobalScope.launch(coroutineDispatchers.crypto) {
|
||||||
deviceListManager
|
runCatching { deviceListManager.downloadKeys(userIds, true) }
|
||||||
.downloadKeys(userIds, true)
|
|
||||||
.fold(
|
.fold(
|
||||||
{ callback.onFailure(it) },
|
|
||||||
{
|
{
|
||||||
val unknownDevices = getUnknownDevices(it)
|
val unknownDevices = getUnknownDevices(it)
|
||||||
if (unknownDevices.map.isEmpty()) {
|
if (unknownDevices.map.isEmpty()) {
|
||||||
@ -880,7 +885,8 @@ internal class CryptoManager @Inject constructor(
|
|||||||
// trigger an an unknown devices exception
|
// trigger an an unknown devices exception
|
||||||
callback.onFailure(Failure.CryptoError(MXCryptoError.UnknownDevice(unknownDevices)))
|
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>>) {
|
override fun downloadKeys(userIds: List<String>, forceDownload: Boolean, callback: MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>>) {
|
||||||
GlobalScope.launch(coroutineDispatchers.crypto) {
|
GlobalScope.launch(coroutineDispatchers.crypto) {
|
||||||
deviceListManager
|
runCatching {
|
||||||
.downloadKeys(userIds, forceDownload)
|
deviceListManager.downloadKeys(userIds, forceDownload)
|
||||||
.foldToCallback(callback)
|
}.foldToCallback(callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun clearCryptoCache(callback: MatrixCallback<Unit>) {
|
override fun clearCryptoCache(callback: MatrixCallback<Unit>) {
|
||||||
clearCryptoDataTask
|
clearCryptoDataTask
|
||||||
.toConfigurableTask()
|
.configureWith {
|
||||||
.dispatchTo(callback)
|
this.callback = callback
|
||||||
|
}
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,14 +18,12 @@
|
|||||||
package im.vector.matrix.android.internal.crypto
|
package im.vector.matrix.android.internal.crypto
|
||||||
|
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import arrow.core.Try
|
|
||||||
import im.vector.matrix.android.api.MatrixPatterns
|
import im.vector.matrix.android.api.MatrixPatterns
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
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.MXDeviceInfo
|
||||||
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
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.store.IMXCryptoStore
|
||||||
import im.vector.matrix.android.internal.crypto.tasks.DownloadKeysForUsersTask
|
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.SessionScope
|
||||||
import im.vector.matrix.android.internal.session.sync.SyncTokenStore
|
import im.vector.matrix.android.internal.session.sync.SyncTokenStore
|
||||||
import timber.log.Timber
|
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 forceDownload Always download the keys even if cached.
|
||||||
* @param callback the asynchronous callback
|
* @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")
|
Timber.v("## downloadKeys() : forceDownload $forceDownload : $userIds")
|
||||||
// Map from userId -> deviceId -> MXDeviceInfo
|
// Map from userId -> deviceId -> MXDeviceInfo
|
||||||
val stored = MXUsersDevicesMap<MXDeviceInfo>()
|
val stored = MXUsersDevicesMap<MXDeviceInfo>()
|
||||||
@ -268,15 +266,14 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
|||||||
}
|
}
|
||||||
return if (downloadUsers.isEmpty()) {
|
return if (downloadUsers.isEmpty()) {
|
||||||
Timber.v("## downloadKeys() : no new user device")
|
Timber.v("## downloadKeys() : no new user device")
|
||||||
Try.just(stored)
|
stored
|
||||||
} else {
|
} else {
|
||||||
Timber.v("## downloadKeys() : starts")
|
Timber.v("## downloadKeys() : starts")
|
||||||
val t0 = System.currentTimeMillis()
|
val t0 = System.currentTimeMillis()
|
||||||
doKeyDownloadForUsers(downloadUsers)
|
val result = doKeyDownloadForUsers(downloadUsers)
|
||||||
.map {
|
|
||||||
Timber.v("## downloadKeys() : doKeyDownloadForUsers succeeds after " + (System.currentTimeMillis() - t0) + " ms")
|
Timber.v("## downloadKeys() : doKeyDownloadForUsers succeeds after " + (System.currentTimeMillis() - t0) + " ms")
|
||||||
|
result.also {
|
||||||
it.addEntriesFromMap(stored)
|
it.addEntriesFromMap(stored)
|
||||||
it
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -286,17 +283,22 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
|||||||
*
|
*
|
||||||
* @param downloadUsers the user ids list
|
* @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")
|
Timber.v("## doKeyDownloadForUsers() : doKeyDownloadForUsers $downloadUsers")
|
||||||
// get the user ids which did not already trigger a keys download
|
// get the user ids which did not already trigger a keys download
|
||||||
val filteredUsers = downloadUsers.filter { MatrixPatterns.isUserId(it) }
|
val filteredUsers = downloadUsers.filter { MatrixPatterns.isUserId(it) }
|
||||||
if (filteredUsers.isEmpty()) {
|
if (filteredUsers.isEmpty()) {
|
||||||
// trigger nothing
|
// trigger nothing
|
||||||
return Try.just(MXUsersDevicesMap())
|
return MXUsersDevicesMap()
|
||||||
}
|
}
|
||||||
val params = DownloadKeysForUsersTask.Params(filteredUsers, syncTokenStore.getLastToken())
|
val params = DownloadKeysForUsersTask.Params(filteredUsers, syncTokenStore.getLastToken())
|
||||||
return downloadKeysForUsersTask.execute(params)
|
val response = try {
|
||||||
.map { response ->
|
downloadKeysForUsersTask.execute(params)
|
||||||
|
} catch (throwable: Throwable) {
|
||||||
|
Timber.e(throwable, "##doKeyDownloadForUsers(): error")
|
||||||
|
onKeysDownloadFailed(filteredUsers)
|
||||||
|
throw throwable
|
||||||
|
}
|
||||||
Timber.v("## doKeyDownloadForUsers() : Got keys for " + filteredUsers.size + " users")
|
Timber.v("## doKeyDownloadForUsers() : Got keys for " + filteredUsers.size + " users")
|
||||||
for (userId in filteredUsers) {
|
for (userId in filteredUsers) {
|
||||||
val devices = response.deviceKeys?.get(userId)
|
val devices = response.deviceKeys?.get(userId)
|
||||||
@ -334,12 +336,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
|||||||
cryptoStore.storeUserDevices(userId, mutableDevices)
|
cryptoStore.storeUserDevices(userId, mutableDevices)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onKeysDownloadSucceed(filteredUsers, response.failures)
|
return onKeysDownloadSucceed(filteredUsers, response.failures)
|
||||||
}
|
|
||||||
.onError {
|
|
||||||
Timber.e(it, "##doKeyDownloadForUsers(): error")
|
|
||||||
onKeysDownloadFailed(filteredUsers)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -465,13 +462,14 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
|||||||
}
|
}
|
||||||
|
|
||||||
cryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses)
|
cryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses)
|
||||||
|
runCatching {
|
||||||
doKeyDownloadForUsers(users)
|
doKeyDownloadForUsers(users)
|
||||||
.fold(
|
}.fold(
|
||||||
{
|
|
||||||
Timber.e(it, "## refreshOutdatedDeviceLists() : ERROR updating device keys for users $users")
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Timber.v("## refreshOutdatedDeviceLists() : done")
|
Timber.v("## refreshOutdatedDeviceLists() : done")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Timber.e(it, "## refreshOutdatedDeviceLists() : ERROR updating device keys for users $users")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
package im.vector.matrix.android.internal.crypto
|
package im.vector.matrix.android.internal.crypto
|
||||||
|
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import arrow.core.Try
|
|
||||||
import im.vector.matrix.android.api.session.crypto.MXCryptoError
|
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.JSON_DICT_PARAMETERIZED_TYPE
|
||||||
import im.vector.matrix.android.api.util.JsonDict
|
import im.vector.matrix.android.api.util.JsonDict
|
||||||
@ -80,8 +79,7 @@ internal class MXOlmDevice @Inject constructor(
|
|||||||
//
|
//
|
||||||
// The first level keys are timeline ids.
|
// The first level keys are timeline ids.
|
||||||
// The second level keys are strings of form "<senderKey>|<session_id>|<message_index>"
|
// The second level keys are strings of form "<senderKey>|<session_id>|<message_index>"
|
||||||
// Values are true.
|
private val inboundGroupSessionMessageIndexes: MutableMap<String, MutableSet<String>> = HashMap()
|
||||||
private val inboundGroupSessionMessageIndexes: MutableMap<String, MutableMap<String, Boolean>> = HashMap()
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// Retrieve the account from the store
|
// Retrieve the account from the store
|
||||||
@ -506,11 +504,8 @@ internal class MXOlmDevice @Inject constructor(
|
|||||||
keysClaimed: Map<String, String>,
|
keysClaimed: Map<String, String>,
|
||||||
exportFormat: Boolean): Boolean {
|
exportFormat: Boolean): Boolean {
|
||||||
val session = OlmInboundGroupSessionWrapper(sessionKey, exportFormat)
|
val session = OlmInboundGroupSessionWrapper(sessionKey, exportFormat)
|
||||||
|
runCatching { getInboundGroupSession(sessionId, senderKey, roomId) }
|
||||||
getInboundGroupSession(sessionId, senderKey, roomId).fold(
|
.fold(
|
||||||
{
|
|
||||||
// Nothing to do in case of error
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
// If we already have this session, consider updating it
|
// If we already have this session, consider updating it
|
||||||
Timber.e("## addInboundGroupSession() : Update for megolm session $senderKey/$sessionId")
|
Timber.e("## addInboundGroupSession() : Update for megolm session $senderKey/$sessionId")
|
||||||
@ -523,6 +518,9 @@ internal class MXOlmDevice @Inject constructor(
|
|||||||
session.olmInboundGroupSession?.releaseSession()
|
session.olmInboundGroupSession?.releaseSession()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Nothing to do in case of error
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -595,12 +593,8 @@ internal class MXOlmDevice @Inject constructor(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
getInboundGroupSession(sessionId, senderKey, roomId)
|
runCatching { getInboundGroupSession(sessionId, senderKey, roomId) }
|
||||||
.fold(
|
.fold(
|
||||||
{
|
|
||||||
// Session does not already exist, add it
|
|
||||||
sessions.add(session)
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
// If we already have this session, consider updating it
|
// If we already have this session, consider updating it
|
||||||
Timber.e("## importInboundGroupSession() : Update for megolm session $senderKey/$sessionId")
|
Timber.e("## importInboundGroupSession() : Update for megolm session $senderKey/$sessionId")
|
||||||
@ -613,7 +607,12 @@ internal class MXOlmDevice @Inject constructor(
|
|||||||
sessions.add(session)
|
sessions.add(session)
|
||||||
}
|
}
|
||||||
Unit
|
Unit
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Session does not already exist, add it
|
||||||
|
sessions.add(session)
|
||||||
}
|
}
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -648,9 +647,8 @@ internal class MXOlmDevice @Inject constructor(
|
|||||||
roomId: String,
|
roomId: String,
|
||||||
timeline: String?,
|
timeline: String?,
|
||||||
sessionId: String,
|
sessionId: String,
|
||||||
senderKey: String): Try<OlmDecryptionResult> {
|
senderKey: String): OlmDecryptionResult {
|
||||||
return getInboundGroupSession(sessionId, senderKey, roomId)
|
val session = getInboundGroupSession(sessionId, senderKey, roomId)
|
||||||
.flatMap { session ->
|
|
||||||
// Check that the room id matches the original one for the session. This stops
|
// Check that the room id matches the original one for the session. This stops
|
||||||
// the HS pretending a message was targeting a different room.
|
// the HS pretending a message was targeting a different room.
|
||||||
if (roomId == session.roomId) {
|
if (roomId == session.roomId) {
|
||||||
@ -659,23 +657,21 @@ internal class MXOlmDevice @Inject constructor(
|
|||||||
decryptResult = session.olmInboundGroupSession!!.decryptMessage(body)
|
decryptResult = session.olmInboundGroupSession!!.decryptMessage(body)
|
||||||
} catch (e: OlmException) {
|
} catch (e: OlmException) {
|
||||||
Timber.e(e, "## decryptGroupMessage () : decryptMessage failed")
|
Timber.e(e, "## decryptGroupMessage () : decryptMessage failed")
|
||||||
return@flatMap Try.Failure(MXCryptoError.OlmError(e))
|
throw MXCryptoError.OlmError(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null != timeline) {
|
if (null != timeline) {
|
||||||
if (!inboundGroupSessionMessageIndexes.containsKey(timeline)) {
|
val timelineSet = inboundGroupSessionMessageIndexes.getOrPut(timeline) { mutableSetOf() }
|
||||||
inboundGroupSessionMessageIndexes[timeline] = HashMap()
|
|
||||||
}
|
|
||||||
|
|
||||||
val messageIndexKey = senderKey + "|" + sessionId + "|" + decryptResult.mIndex
|
val messageIndexKey = senderKey + "|" + sessionId + "|" + decryptResult.mIndex
|
||||||
|
|
||||||
if (inboundGroupSessionMessageIndexes[timeline]?.get(messageIndexKey) != null) {
|
if (timelineSet.contains(messageIndexKey)) {
|
||||||
val reason = String.format(MXCryptoError.DUPLICATE_MESSAGE_INDEX_REASON, decryptResult.mIndex)
|
val reason = String.format(MXCryptoError.DUPLICATE_MESSAGE_INDEX_REASON, decryptResult.mIndex)
|
||||||
Timber.e("## decryptGroupMessage() : $reason")
|
Timber.e("## decryptGroupMessage() : $reason")
|
||||||
return@flatMap Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.DUPLICATED_MESSAGE_INDEX, reason))
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.DUPLICATED_MESSAGE_INDEX, reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
inboundGroupSessionMessageIndexes[timeline]!!.put(messageIndexKey, true)
|
timelineSet.add(messageIndexKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
store.storeInboundGroupSessions(listOf(session))
|
store.storeInboundGroupSessions(listOf(session))
|
||||||
@ -685,23 +681,20 @@ internal class MXOlmDevice @Inject constructor(
|
|||||||
adapter.fromJson(payloadString)
|
adapter.fromJson(payloadString)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e("## decryptGroupMessage() : fails to parse the payload")
|
Timber.e("## decryptGroupMessage() : fails to parse the payload")
|
||||||
return@flatMap Try.Failure(
|
throw
|
||||||
MXCryptoError.Base(MXCryptoError.ErrorType.BAD_DECRYPTED_FORMAT, MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON))
|
MXCryptoError.Base(MXCryptoError.ErrorType.BAD_DECRYPTED_FORMAT, MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON)
|
||||||
}
|
}
|
||||||
|
|
||||||
return@flatMap Try.just(
|
return OlmDecryptionResult(
|
||||||
OlmDecryptionResult(
|
|
||||||
payload,
|
payload,
|
||||||
session.keysClaimed,
|
session.keysClaimed,
|
||||||
senderKey,
|
senderKey,
|
||||||
session.forwardingCurve25519KeyChain
|
session.forwardingCurve25519KeyChain
|
||||||
)
|
)
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
val reason = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, session.roomId)
|
val reason = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, session.roomId)
|
||||||
Timber.e("## decryptGroupMessage() : $reason")
|
Timber.e("## decryptGroupMessage() : $reason")
|
||||||
return@flatMap Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.INBOUND_SESSION_MISMATCH_ROOM_ID, reason))
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.INBOUND_SESSION_MISMATCH_ROOM_ID, reason)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -766,26 +759,26 @@ internal class MXOlmDevice @Inject constructor(
|
|||||||
* @param senderKey the base64-encoded curve25519 key of the sender.
|
* @param senderKey the base64-encoded curve25519 key of the sender.
|
||||||
* @return the inbound group session.
|
* @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()) {
|
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)
|
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
|
// Check that the room id matches the original one for the session. This stops
|
||||||
// the HS pretending a message was targeting a different room.
|
// the HS pretending a message was targeting a different room.
|
||||||
if (!TextUtils.equals(roomId, session.roomId)) {
|
if (!TextUtils.equals(roomId, session.roomId)) {
|
||||||
val errorDescription = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, session.roomId)
|
val errorDescription = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, session.roomId)
|
||||||
Timber.e("## getInboundGroupSession() : $errorDescription")
|
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 {
|
} else {
|
||||||
Try.just(session)
|
return session
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Timber.e("## getInboundGroupSession() : Cannot retrieve inbound group session $sessionId")
|
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 +791,6 @@ internal class MXOlmDevice @Inject constructor(
|
|||||||
* @return true if the unbound session keys are known.
|
* @return true if the unbound session keys are known.
|
||||||
*/
|
*/
|
||||||
fun hasInboundSessionKeys(roomId: String, senderKey: String, sessionId: String): Boolean {
|
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
|
package im.vector.matrix.android.internal.crypto
|
||||||
|
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
import im.vector.matrix.android.api.auth.data.Credentials
|
||||||
import im.vector.matrix.android.internal.session.SessionScope
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto
|
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.api.auth.data.Credentials
|
||||||
import im.vector.matrix.android.internal.crypto.model.MXKey
|
import im.vector.matrix.android.internal.crypto.model.MXKey
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse
|
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.
|
* Check if the OTK must be uploaded.
|
||||||
*/
|
*/
|
||||||
suspend fun maybeUploadOneTimeKeys(): Try<Unit> {
|
suspend fun maybeUploadOneTimeKeys() {
|
||||||
if (oneTimeKeyCheckInProgress) {
|
if (oneTimeKeyCheckInProgress) {
|
||||||
return Try.just(Unit)
|
return
|
||||||
}
|
}
|
||||||
if (System.currentTimeMillis() - lastOneTimeKeyCheck < ONE_TIME_KEY_UPLOAD_PERIOD) {
|
if (System.currentTimeMillis() - lastOneTimeKeyCheck < ONE_TIME_KEY_UPLOAD_PERIOD) {
|
||||||
// we've done a key upload recently.
|
// we've done a key upload recently.
|
||||||
return Try.just(Unit)
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
lastOneTimeKeyCheck = System.currentTimeMillis()
|
lastOneTimeKeyCheck = System.currentTimeMillis()
|
||||||
@ -81,13 +79,12 @@ internal class OneTimeKeysUploader @Inject constructor(
|
|||||||
// discard the oldest private keys first. This will eventually clean
|
// discard the oldest private keys first. This will eventually clean
|
||||||
// out stale private keys that won't receive a message.
|
// out stale private keys that won't receive a message.
|
||||||
val keyLimit = Math.floor(maxOneTimeKeys / 2.0).toInt()
|
val keyLimit = Math.floor(maxOneTimeKeys / 2.0).toInt()
|
||||||
val result = if (oneTimeKeyCount != null) {
|
if (oneTimeKeyCount != null) {
|
||||||
uploadOTK(oneTimeKeyCount!!, keyLimit)
|
uploadOTK(oneTimeKeyCount!!, keyLimit)
|
||||||
} else {
|
} else {
|
||||||
// ask the server how many keys we have
|
// ask the server how many keys we have
|
||||||
val uploadKeysParams = UploadKeysTask.Params(null, null, credentials.deviceId!!)
|
val uploadKeysParams = UploadKeysTask.Params(null, null, credentials.deviceId!!)
|
||||||
uploadKeysTask.execute(uploadKeysParams)
|
val response = uploadKeysTask.execute(uploadKeysParams)
|
||||||
.flatMap {
|
|
||||||
// We need to keep a pool of one time public keys on the server so that
|
// 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
|
// other devices can start conversations with us. But we can only store
|
||||||
// a finite number of private keys in the olm Account object.
|
// a finite number of private keys in the olm Account object.
|
||||||
@ -101,22 +98,13 @@ internal class OneTimeKeysUploader @Inject constructor(
|
|||||||
// these factors.
|
// these factors.
|
||||||
// TODO Why we do not set oneTimeKeyCount here?
|
// 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)
|
// 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)
|
val keyCount = response.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)
|
||||||
uploadOTK(keyCount, keyLimit)
|
uploadOTK(keyCount, keyLimit)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return result
|
|
||||||
.map {
|
|
||||||
Timber.v("## uploadKeys() : success")
|
Timber.v("## uploadKeys() : success")
|
||||||
oneTimeKeyCount = null
|
oneTimeKeyCount = null
|
||||||
oneTimeKeyCheckInProgress = false
|
oneTimeKeyCheckInProgress = false
|
||||||
}
|
}
|
||||||
.handleError {
|
|
||||||
Timber.e(it, "## uploadKeys() : failed")
|
|
||||||
oneTimeKeyCount = null
|
|
||||||
oneTimeKeyCheckInProgress = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upload some the OTKs.
|
* Upload some the OTKs.
|
||||||
@ -124,29 +112,26 @@ internal class OneTimeKeysUploader @Inject constructor(
|
|||||||
* @param keyCount the key count
|
* @param keyCount the key count
|
||||||
* @param keyLimit the limit
|
* @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 (keyLimit <= keyCount) {
|
||||||
// If we don't need to generate any more keys then we are done.
|
// 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)
|
val keysThisLoop = Math.min(keyLimit - keyCount, ONE_TIME_KEY_GENERATION_MAX_NUMBER)
|
||||||
olmDevice.generateOneTimeKeys(keysThisLoop)
|
olmDevice.generateOneTimeKeys(keysThisLoop)
|
||||||
return uploadOneTimeKeys()
|
val response = uploadOneTimeKeys()
|
||||||
.flatMap {
|
if (response.hasOneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)) {
|
||||||
if (it.hasOneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)) {
|
uploadOTK(response.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE), keyLimit)
|
||||||
uploadOTK(it.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE), keyLimit)
|
|
||||||
} else {
|
} else {
|
||||||
Timber.e("## uploadLoop() : response for uploading keys does not contain one_time_key_counts.signed_curve25519")
|
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"))
|
throw Exception("response for uploading keys does not contain one_time_key_counts.signed_curve25519")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upload my user's one time keys.
|
* Upload my user's one time keys.
|
||||||
*/
|
*/
|
||||||
private suspend fun uploadOneTimeKeys(): Try<KeysUploadResponse> {
|
private suspend fun uploadOneTimeKeys(): KeysUploadResponse {
|
||||||
val oneTimeKeys = olmDevice.getOneTimeKeys()
|
val oneTimeKeys = olmDevice.getOneTimeKeys()
|
||||||
val oneTimeJson = HashMap<String, Any>()
|
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
|
// For now, we set the device id explicitly, as we may not be using the
|
||||||
// same one as used in login.
|
// same one as used in login.
|
||||||
val uploadParams = UploadKeysTask.Params(null, oneTimeJson, credentials.deviceId!!)
|
val uploadParams = UploadKeysTask.Params(null, oneTimeJson, credentials.deviceId!!)
|
||||||
return uploadKeysTask
|
val response = uploadKeysTask.execute(uploadParams)
|
||||||
.execute(uploadParams)
|
|
||||||
.map {
|
|
||||||
lastPublishedOneTimeKeys = oneTimeKeys
|
lastPublishedOneTimeKeys = oneTimeKeys
|
||||||
olmDevice.markKeysAsPublished()
|
olmDevice.markKeysAsPublished()
|
||||||
it
|
return response
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -299,10 +299,12 @@ internal class OutgoingRoomKeyRequestManager @Inject constructor(
|
|||||||
// TODO Change this two hard coded key to something better
|
// TODO Change this two hard coded key to something better
|
||||||
contentMap.setObject(recipient["userId"], recipient["deviceId"], message)
|
contentMap.setObject(recipient["userId"], recipient["deviceId"], message)
|
||||||
}
|
}
|
||||||
sendToDeviceTask.configureWith(SendToDeviceTask.Params(EventType.ROOM_KEY_REQUEST, contentMap, transactionId))
|
sendToDeviceTask
|
||||||
.dispatchTo(callback)
|
.configureWith(SendToDeviceTask.Params(EventType.ROOM_KEY_REQUEST, contentMap, transactionId)) {
|
||||||
.executeOn(TaskThread.CALLER)
|
this.callback = callback
|
||||||
.callbackOn(TaskThread.CALLER)
|
this.callbackThread = TaskThread.CALLER
|
||||||
|
this.executionThread = TaskThread.CALLER
|
||||||
|
}
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
package im.vector.matrix.android.internal.crypto.actions
|
package im.vector.matrix.android.internal.crypto.actions
|
||||||
|
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import arrow.core.Try
|
|
||||||
import im.vector.matrix.android.internal.crypto.MXOlmDevice
|
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.MXDeviceInfo
|
||||||
import im.vector.matrix.android.internal.crypto.model.MXKey
|
import im.vector.matrix.android.internal.crypto.model.MXKey
|
||||||
@ -32,7 +31,7 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(private val
|
|||||||
private val oneTimeKeysForUsersDeviceTask: ClaimOneTimeKeysForUsersDeviceTask) {
|
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 devicesWithoutSession = ArrayList<MXDeviceInfo>()
|
||||||
|
|
||||||
val results = MXUsersDevicesMap<MXOlmSessionResult>()
|
val results = MXUsersDevicesMap<MXOlmSessionResult>()
|
||||||
@ -58,7 +57,7 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(private val
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (devicesWithoutSession.size == 0) {
|
if (devicesWithoutSession.size == 0) {
|
||||||
return Try.just(results)
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare the request for claiming one-time keys
|
// Prepare the request for claiming one-time keys
|
||||||
@ -79,15 +78,13 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(private val
|
|||||||
Timber.v("## claimOneTimeKeysForUsersDevices() : $usersDevicesToClaim")
|
Timber.v("## claimOneTimeKeysForUsersDevices() : $usersDevicesToClaim")
|
||||||
|
|
||||||
val claimParams = ClaimOneTimeKeysForUsersDeviceTask.Params(usersDevicesToClaim)
|
val claimParams = ClaimOneTimeKeysForUsersDeviceTask.Params(usersDevicesToClaim)
|
||||||
return oneTimeKeysForUsersDeviceTask
|
val oneTimeKeys = oneTimeKeysForUsersDeviceTask.execute(claimParams)
|
||||||
.execute(claimParams)
|
Timber.v("## claimOneTimeKeysForUsersDevices() : keysClaimResponse.oneTimeKeys: $oneTimeKeys")
|
||||||
.map {
|
|
||||||
Timber.v("## claimOneTimeKeysForUsersDevices() : keysClaimResponse.oneTimeKeys: $it")
|
|
||||||
for (userId in userIds) {
|
for (userId in userIds) {
|
||||||
val deviceInfos = devicesByUser[userId]
|
val deviceInfos = devicesByUser[userId]
|
||||||
for (deviceInfo in deviceInfos!!) {
|
for (deviceInfo in deviceInfos!!) {
|
||||||
var oneTimeKey: MXKey? = null
|
var oneTimeKey: MXKey? = null
|
||||||
val deviceIds = it.getUserDeviceIds(userId)
|
val deviceIds = oneTimeKeys.getUserDeviceIds(userId)
|
||||||
if (null != deviceIds) {
|
if (null != deviceIds) {
|
||||||
for (deviceId in deviceIds) {
|
for (deviceId in deviceIds) {
|
||||||
val olmSessionResult = results.getObject(userId, deviceId)
|
val olmSessionResult = results.getObject(userId, deviceId)
|
||||||
@ -95,7 +92,7 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(private val
|
|||||||
// We already have a result for this device
|
// We already have a result for this device
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
val key = it.getObject(userId, deviceId)
|
val key = oneTimeKeys.getObject(userId, deviceId)
|
||||||
if (key?.type == oneTimeKeyAlgorithm) {
|
if (key?.type == oneTimeKeyAlgorithm) {
|
||||||
oneTimeKey = key
|
oneTimeKey = key
|
||||||
}
|
}
|
||||||
@ -110,8 +107,7 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(private val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
results
|
return results
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun verifyKeyAndStartSession(oneTimeKey: MXKey, userId: String, deviceInfo: MXDeviceInfo): String? {
|
private fun verifyKeyAndStartSession(oneTimeKey: MXKey, userId: String, deviceInfo: MXDeviceInfo): String? {
|
||||||
|
@ -17,14 +17,11 @@
|
|||||||
package im.vector.matrix.android.internal.crypto.actions
|
package im.vector.matrix.android.internal.crypto.actions
|
||||||
|
|
||||||
import android.text.TextUtils
|
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.MXOlmDevice
|
||||||
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
|
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.MXOlmSessionResult
|
||||||
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
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.store.IMXCryptoStore
|
||||||
import im.vector.matrix.android.internal.session.SessionScope
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.inject.Inject
|
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.
|
* Try to make sure we have established olm sessions for the given users.
|
||||||
* @param users a list of user ids.
|
* @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")
|
Timber.v("## ensureOlmSessionsForUsers() : ensureOlmSessionsForUsers $users")
|
||||||
val devicesByUser = HashMap<String /* userId */, MutableList<MXDeviceInfo>>()
|
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.ImportRoomKeysResult
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
|
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.crypto.store.IMXCryptoStore
|
||||||
import im.vector.matrix.android.internal.session.SessionScope
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
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.api.auth.data.Credentials
|
||||||
import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup
|
import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup
|
||||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||||
import im.vector.matrix.android.internal.session.SessionScope
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.algorithms
|
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.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest
|
import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest
|
||||||
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
|
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.
|
* @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
|
* @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.
|
* Handle a key event.
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.algorithms
|
package im.vector.matrix.android.internal.crypto.algorithms
|
||||||
|
|
||||||
import arrow.core.Try
|
|
||||||
import im.vector.matrix.android.api.session.events.model.Content
|
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 eventContent the content of the event.
|
||||||
* @param eventType the type of the event.
|
* @param eventType the type of the event.
|
||||||
* @param userIds the room members the event will be sent to.
|
* @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
|
package im.vector.matrix.android.internal.crypto.algorithms.megolm
|
||||||
|
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import arrow.core.Try
|
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
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.crypto.MXCryptoError
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
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.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import kotlin.collections.HashMap
|
|
||||||
|
|
||||||
internal class MXMegolmDecryption(private val credentials: Credentials,
|
internal class MXMegolmDecryption(private val credentials: Credentials,
|
||||||
private val olmDevice: MXOlmDevice,
|
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()
|
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)
|
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()) {
|
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>()
|
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()
|
if (encryptedEventContent.senderKey.isNullOrBlank()
|
||||||
|| encryptedEventContent.sessionId.isNullOrBlank()
|
|| encryptedEventContent.sessionId.isNullOrBlank()
|
||||||
|| encryptedEventContent.ciphertext.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,
|
return runCatching {
|
||||||
|
olmDevice.decryptGroupMessage(encryptedEventContent.ciphertext,
|
||||||
event.roomId,
|
event.roomId,
|
||||||
timeline,
|
timeline,
|
||||||
encryptedEventContent.sessionId,
|
encryptedEventContent.sessionId,
|
||||||
encryptedEventContent.senderKey)
|
encryptedEventContent.senderKey)
|
||||||
|
}
|
||||||
.fold(
|
.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 ->
|
{ throwable ->
|
||||||
if (throwable is MXCryptoError.OlmError) {
|
if (throwable is MXCryptoError.OlmError) {
|
||||||
// TODO Check the value of .message
|
// 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 reason = String.format(MXCryptoError.OLM_REASON, throwable.olmException.message)
|
||||||
val detailedReason = String.format(MXCryptoError.DETAILED_OLM_REASON, encryptedEventContent.ciphertext, reason)
|
val detailedReason = String.format(MXCryptoError.DETAILED_OLM_REASON, encryptedEventContent.ciphertext, reason)
|
||||||
|
|
||||||
Try.Failure(MXCryptoError.Base(
|
throw MXCryptoError.Base(
|
||||||
MXCryptoError.ErrorType.OLM,
|
MXCryptoError.ErrorType.OLM,
|
||||||
reason,
|
reason,
|
||||||
detailedReason))
|
detailedReason)
|
||||||
}
|
}
|
||||||
if (throwable is MXCryptoError.Base) {
|
if (throwable is MXCryptoError.Base) {
|
||||||
if (throwable.errorType == MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID) {
|
if (throwable.errorType == MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID) {
|
||||||
@ -111,23 +125,7 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
throw throwable
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -311,39 +309,37 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
|
|||||||
}
|
}
|
||||||
val userId = request.userId ?: return
|
val userId = request.userId ?: return
|
||||||
GlobalScope.launch(coroutineDispatchers.crypto) {
|
GlobalScope.launch(coroutineDispatchers.crypto) {
|
||||||
deviceListManager
|
runCatching { deviceListManager.downloadKeys(listOf(userId), false) }
|
||||||
.downloadKeys(listOf(userId), false)
|
.mapCatching {
|
||||||
.flatMap {
|
|
||||||
val deviceId = request.deviceId
|
val deviceId = request.deviceId
|
||||||
val deviceInfo = cryptoStore.getUserDevice(deviceId ?: "", userId)
|
val deviceInfo = cryptoStore.getUserDevice(deviceId ?: "", userId)
|
||||||
if (deviceInfo == null) {
|
if (deviceInfo == null) {
|
||||||
throw RuntimeException()
|
throw RuntimeException()
|
||||||
} else {
|
} else {
|
||||||
val devicesByUser = mapOf(userId to listOf(deviceInfo))
|
val devicesByUser = mapOf(userId to listOf(deviceInfo))
|
||||||
ensureOlmSessionsForDevicesAction
|
val usersDeviceMap = ensureOlmSessionsForDevicesAction.handle(devicesByUser)
|
||||||
.handle(devicesByUser)
|
|
||||||
.flatMap {
|
|
||||||
val body = request.requestBody
|
val body = request.requestBody
|
||||||
val olmSessionResult = it.getObject(userId, deviceId)
|
val olmSessionResult = usersDeviceMap.getObject(userId, deviceId)
|
||||||
if (olmSessionResult?.sessionId == null) {
|
if (olmSessionResult?.sessionId == null) {
|
||||||
// no session with this device, probably because there
|
// no session with this device, probably because there
|
||||||
// were no one-time keys.
|
// were no one-time keys.
|
||||||
Try.just(Unit)
|
return@mapCatching
|
||||||
}
|
}
|
||||||
Timber.v("## shareKeysWithDevice() : sharing keys for session" +
|
Timber.v("## shareKeysWithDevice() : sharing keys for session" +
|
||||||
" ${body?.senderKey}|${body?.sessionId} with device $userId:$deviceId")
|
" ${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) }
|
||||||
olmDevice.getInboundGroupSession(body?.sessionId, body?.senderKey, body?.roomId)
|
|
||||||
.fold(
|
.fold(
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO
|
||||||
|
payloadJson["content"] = it.exportKeys()
|
||||||
|
?: ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO
|
||||||
payloadJson["content"] = it.exportKeys() ?: ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo))
|
val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo))
|
||||||
@ -356,6 +352,5 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.actions.MessageEncrypter
|
||||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||||
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
|
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 im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
package im.vector.matrix.android.internal.crypto.algorithms.megolm
|
package im.vector.matrix.android.internal.crypto.algorithms.megolm
|
||||||
|
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import arrow.core.Try
|
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
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.crypto.MXCryptoError
|
||||||
import im.vector.matrix.android.api.session.events.model.Content
|
import im.vector.matrix.android.api.session.events.model.Content
|
||||||
@ -69,12 +68,10 @@ internal class MXMegolmEncryption(
|
|||||||
|
|
||||||
override suspend fun encryptEventContent(eventContent: Content,
|
override suspend fun encryptEventContent(eventContent: Content,
|
||||||
eventType: String,
|
eventType: String,
|
||||||
userIds: List<String>): Try<Content> {
|
userIds: List<String>): Content {
|
||||||
return getDevicesInRoom(userIds)
|
val devices = getDevicesInRoom(userIds)
|
||||||
.flatMap { ensureOutboundSession(it) }
|
val outboundSession = ensureOutboundSession(devices)
|
||||||
.flatMap {
|
return encryptContent(outboundSession, eventType, eventContent)
|
||||||
encryptContent(it, eventType, eventContent)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -101,7 +98,7 @@ internal class MXMegolmEncryption(
|
|||||||
*
|
*
|
||||||
* @param devicesInRoom the devices list
|
* @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
|
var session = outboundSession
|
||||||
if (session == null
|
if (session == null
|
||||||
// Need to make a brand new session?
|
// 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
|
* @param devicesByUsers the devices map
|
||||||
*/
|
*/
|
||||||
private suspend fun shareKey(session: MXOutboundSessionInfo,
|
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
|
// nothing to send, the task is done
|
||||||
if (devicesByUsers.isEmpty()) {
|
if (devicesByUsers.isEmpty()) {
|
||||||
Timber.v("## shareKey() : nothing more to do")
|
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)
|
// 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>>()
|
val subMap = HashMap<String, List<MXDeviceInfo>>()
|
||||||
@ -157,12 +155,10 @@ internal class MXMegolmEncryption(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Timber.v("## shareKey() ; userId $userIds")
|
Timber.v("## shareKey() ; userId $userIds")
|
||||||
return shareUserDevicesKey(session, subMap)
|
shareUserDevicesKey(session, subMap)
|
||||||
.flatMap {
|
|
||||||
val remainingDevices = devicesByUsers.filterKeys { userIds.contains(it).not() }
|
val remainingDevices = devicesByUsers.filterKeys { userIds.contains(it).not() }
|
||||||
shareKey(session, remainingDevices)
|
shareKey(session, remainingDevices)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Share the device keys of a an user
|
* Share the device keys of a an user
|
||||||
@ -172,7 +168,7 @@ internal class MXMegolmEncryption(
|
|||||||
* @param callback the asynchronous callback
|
* @param callback the asynchronous callback
|
||||||
*/
|
*/
|
||||||
private suspend fun shareUserDevicesKey(session: MXOutboundSessionInfo,
|
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 sessionKey = olmDevice.getSessionKey(session.sessionId)
|
||||||
val chainIndex = olmDevice.getMessageIndex(session.sessionId)
|
val chainIndex = olmDevice.getMessageIndex(session.sessionId)
|
||||||
|
|
||||||
@ -190,17 +186,16 @@ internal class MXMegolmEncryption(
|
|||||||
var t0 = System.currentTimeMillis()
|
var t0 = System.currentTimeMillis()
|
||||||
Timber.v("## shareUserDevicesKey() : starts")
|
Timber.v("## shareUserDevicesKey() : starts")
|
||||||
|
|
||||||
return ensureOlmSessionsForDevicesAction.handle(devicesByUser)
|
val results = ensureOlmSessionsForDevicesAction.handle(devicesByUser)
|
||||||
.flatMap {
|
|
||||||
Timber.v("## shareUserDevicesKey() : ensureOlmSessionsForDevices succeeds after "
|
Timber.v("## shareUserDevicesKey() : ensureOlmSessionsForDevices succeeds after "
|
||||||
+ (System.currentTimeMillis() - t0) + " ms")
|
+ (System.currentTimeMillis() - t0) + " ms")
|
||||||
val contentMap = MXUsersDevicesMap<Any>()
|
val contentMap = MXUsersDevicesMap<Any>()
|
||||||
var haveTargets = false
|
var haveTargets = false
|
||||||
val userIds = it.userIds
|
val userIds = results.userIds
|
||||||
for (userId in userIds) {
|
for (userId in userIds) {
|
||||||
val devicesToShareWith = devicesByUser[userId]
|
val devicesToShareWith = devicesByUser[userId]
|
||||||
for ((deviceID) in devicesToShareWith!!) {
|
for ((deviceID) in devicesToShareWith!!) {
|
||||||
val sessionResult = it.getObject(userId, deviceID)
|
val sessionResult = results.getObject(userId, deviceID)
|
||||||
if (sessionResult?.sessionId == null) {
|
if (sessionResult?.sessionId == null) {
|
||||||
// no session with this device, probably because there
|
// no session with this device, probably because there
|
||||||
// were no one-time keys.
|
// were no one-time keys.
|
||||||
@ -226,7 +221,6 @@ internal class MXMegolmEncryption(
|
|||||||
Timber.v("## shareUserDevicesKey() : has target")
|
Timber.v("## shareUserDevicesKey() : has target")
|
||||||
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, contentMap)
|
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, contentMap)
|
||||||
sendToDeviceTask.execute(sendToDeviceParams)
|
sendToDeviceTask.execute(sendToDeviceParams)
|
||||||
.map {
|
|
||||||
Timber.v("## shareUserDevicesKey() : sendToDevice succeeds after "
|
Timber.v("## shareUserDevicesKey() : sendToDevice succeeds after "
|
||||||
+ (System.currentTimeMillis() - t0) + " ms")
|
+ (System.currentTimeMillis() - t0) + " ms")
|
||||||
|
|
||||||
@ -241,20 +235,15 @@ internal class MXMegolmEncryption(
|
|||||||
session.sharedWithDevices.setObject(userId, deviceId, chainIndex)
|
session.sharedWithDevices.setObject(userId, deviceId, chainIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Unit
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Timber.v("## shareUserDevicesKey() : no need to sharekey")
|
Timber.v("## shareUserDevicesKey() : no need to sharekey")
|
||||||
Try.just(Unit)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* process the pending encryptions
|
* process the pending encryptions
|
||||||
*/
|
*/
|
||||||
private fun encryptContent(session: MXOutboundSessionInfo, eventType: String, eventContent: Content): Try<Content> {
|
private fun encryptContent(session: MXOutboundSessionInfo, eventType: String, eventContent: Content): Content {
|
||||||
return Try<Content> {
|
|
||||||
// Everything is in place, encrypt all pending events
|
// Everything is in place, encrypt all pending events
|
||||||
val payloadJson = HashMap<String, Any>()
|
val payloadJson = HashMap<String, Any>()
|
||||||
payloadJson["room_id"] = roomId
|
payloadJson["room_id"] = roomId
|
||||||
@ -276,8 +265,7 @@ internal class MXMegolmEncryption(
|
|||||||
// m.new_device message if they don't have our session key.
|
// m.new_device message if they don't have our session key.
|
||||||
map["device_id"] = credentials.deviceId!!
|
map["device_id"] = credentials.deviceId!!
|
||||||
session.useCount++
|
session.useCount++
|
||||||
map
|
return map
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -287,24 +275,22 @@ internal class MXMegolmEncryption(
|
|||||||
* @param userIds the user ids whose devices must be checked.
|
* @param userIds the user ids whose devices must be checked.
|
||||||
* @param callback the asynchronous callback
|
* @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
|
// 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
|
// 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
|
// with them, which means that they will have announced any new devices via
|
||||||
// an m.new_device.
|
// an m.new_device.
|
||||||
return deviceListManager
|
val keys = deviceListManager.downloadKeys(userIds, false)
|
||||||
.downloadKeys(userIds, false)
|
|
||||||
.flatMap {
|
|
||||||
val encryptToVerifiedDevicesOnly = cryptoStore.getGlobalBlacklistUnverifiedDevices()
|
val encryptToVerifiedDevicesOnly = cryptoStore.getGlobalBlacklistUnverifiedDevices()
|
||||||
|| cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId)
|
|| cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId)
|
||||||
|
|
||||||
val devicesInRoom = MXUsersDevicesMap<MXDeviceInfo>()
|
val devicesInRoom = MXUsersDevicesMap<MXDeviceInfo>()
|
||||||
val unknownDevices = MXUsersDevicesMap<MXDeviceInfo>()
|
val unknownDevices = MXUsersDevicesMap<MXDeviceInfo>()
|
||||||
|
|
||||||
for (userId in it.userIds) {
|
for (userId in keys.userIds) {
|
||||||
val deviceIds = it.getUserDeviceIds(userId) ?: continue
|
val deviceIds = keys.getUserDeviceIds(userId) ?: continue
|
||||||
for (deviceId in deviceIds) {
|
for (deviceId in deviceIds) {
|
||||||
val deviceInfo = it.getObject(userId, deviceId) ?: continue
|
val deviceInfo = keys.getObject(userId, deviceId) ?: continue
|
||||||
if (warnOnUnknownDevicesRepository.warnOnUnknownDevices() && deviceInfo.isUnknown) {
|
if (warnOnUnknownDevicesRepository.warnOnUnknownDevices() && deviceInfo.isUnknown) {
|
||||||
// The device is not yet known by the user
|
// The device is not yet known by the user
|
||||||
unknownDevices.setObject(userId, deviceId, deviceInfo)
|
unknownDevices.setObject(userId, deviceId, deviceInfo)
|
||||||
@ -327,10 +313,9 @@ internal class MXMegolmEncryption(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (unknownDevices.isEmpty) {
|
if (unknownDevices.isEmpty) {
|
||||||
Try.just(devicesInRoom)
|
return devicesInRoom
|
||||||
} else {
|
} else {
|
||||||
Try.Failure(MXCryptoError.UnknownDevice(unknownDevices))
|
throw MXCryptoError.UnknownDevice(unknownDevices)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.algorithms.olm
|
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.auth.data.Credentials
|
||||||
import im.vector.matrix.android.api.session.crypto.MXCryptoError
|
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.Event
|
||||||
@ -41,29 +40,28 @@ internal class MXOlmDecryption(
|
|||||||
private val credentials: Credentials)
|
private val credentials: Credentials)
|
||||||
: IMXDecrypting {
|
: 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 {
|
val olmEventContent = event.content.toModel<OlmEventContent>() ?: run {
|
||||||
Timber.e("## decryptEvent() : bad event format")
|
Timber.e("## decryptEvent() : bad event format")
|
||||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_EVENT_FORMAT,
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_EVENT_FORMAT,
|
||||||
MXCryptoError.BAD_EVENT_FORMAT_TEXT_REASON))
|
MXCryptoError.BAD_EVENT_FORMAT_TEXT_REASON)
|
||||||
}
|
}
|
||||||
|
|
||||||
val cipherText = olmEventContent.ciphertext ?: run {
|
val cipherText = olmEventContent.ciphertext ?: run {
|
||||||
Timber.e("## decryptEvent() : missing cipher text")
|
Timber.e("## decryptEvent() : missing cipher text")
|
||||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_CIPHER_TEXT,
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_CIPHER_TEXT,
|
||||||
MXCryptoError.MISSING_CIPHER_TEXT_REASON))
|
MXCryptoError.MISSING_CIPHER_TEXT_REASON)
|
||||||
}
|
}
|
||||||
|
|
||||||
val senderKey = olmEventContent.senderKey ?: run {
|
val senderKey = olmEventContent.senderKey ?: run {
|
||||||
Timber.e("## decryptEvent() : missing sender key")
|
Timber.e("## decryptEvent() : missing sender key")
|
||||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_SENDER_KEY,
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_SENDER_KEY,
|
||||||
MXCryptoError.MISSING_SENDER_KEY_TEXT_REASON))
|
MXCryptoError.MISSING_SENDER_KEY_TEXT_REASON)
|
||||||
}
|
}
|
||||||
|
|
||||||
val messageAny = cipherText[olmDevice.deviceCurve25519Key] ?: run {
|
val messageAny = cipherText[olmDevice.deviceCurve25519Key] ?: run {
|
||||||
Timber.e("## decryptEvent() : our device ${olmDevice.deviceCurve25519Key} is not included in recipients")
|
Timber.e("## decryptEvent() : our device ${olmDevice.deviceCurve25519Key} is not included in recipients")
|
||||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.NOT_INCLUDE_IN_RECIPIENTS,
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.NOT_INCLUDE_IN_RECIPIENTS, MXCryptoError.NOT_INCLUDED_IN_RECIPIENT_REASON)
|
||||||
MXCryptoError.NOT_INCLUDED_IN_RECIPIENT_REASON))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The message for myUser
|
// The message for myUser
|
||||||
@ -73,14 +71,12 @@ internal class MXOlmDecryption(
|
|||||||
|
|
||||||
if (decryptedPayload == null) {
|
if (decryptedPayload == null) {
|
||||||
Timber.e("## decryptEvent() Failed to decrypt Olm event (id= ${event.eventId} from $senderKey")
|
Timber.e("## decryptEvent() Failed to decrypt Olm event (id= ${event.eventId} from $senderKey")
|
||||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE,
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)
|
||||||
MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON))
|
|
||||||
}
|
}
|
||||||
val payloadString = convertFromUTF8(decryptedPayload)
|
val payloadString = convertFromUTF8(decryptedPayload)
|
||||||
if (payloadString == null) {
|
if (payloadString == null) {
|
||||||
Timber.e("## decryptEvent() Failed to decrypt Olm event (id= ${event.eventId} from $senderKey")
|
Timber.e("## decryptEvent() Failed to decrypt Olm event (id= ${event.eventId} from $senderKey")
|
||||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE,
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)
|
||||||
MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val adapter = MoshiProvider.providesMoshi().adapter<JsonDict>(JSON_DICT_PARAMETERIZED_TYPE)
|
val adapter = MoshiProvider.providesMoshi().adapter<JsonDict>(JSON_DICT_PARAMETERIZED_TYPE)
|
||||||
@ -88,73 +84,70 @@ internal class MXOlmDecryption(
|
|||||||
|
|
||||||
if (payload == null) {
|
if (payload == null) {
|
||||||
Timber.e("## decryptEvent failed : null payload")
|
Timber.e("## decryptEvent failed : null payload")
|
||||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT,
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT, MXCryptoError.MISSING_CIPHER_TEXT_REASON)
|
||||||
MXCryptoError.MISSING_CIPHER_TEXT_REASON))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val olmPayloadContent = OlmPayloadContent.fromJsonString(payloadString) ?: run {
|
val olmPayloadContent = OlmPayloadContent.fromJsonString(payloadString) ?: run {
|
||||||
Timber.e("## decryptEvent() : bad olmPayloadContent format")
|
Timber.e("## decryptEvent() : bad olmPayloadContent format")
|
||||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_DECRYPTED_FORMAT,
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_DECRYPTED_FORMAT, MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON)
|
||||||
MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (olmPayloadContent.recipient.isNullOrBlank()) {
|
if (olmPayloadContent.recipient.isNullOrBlank()) {
|
||||||
val reason = String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "recipient")
|
val reason = String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "recipient")
|
||||||
Timber.e("## decryptEvent() : $reason")
|
Timber.e("## decryptEvent() : $reason")
|
||||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY,
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY, reason)
|
||||||
reason))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (olmPayloadContent.recipient != credentials.userId) {
|
if (olmPayloadContent.recipient != credentials.userId) {
|
||||||
Timber.e("## decryptEvent() : Event ${event.eventId}:" +
|
Timber.e("## decryptEvent() : Event ${event.eventId}:" +
|
||||||
" Intended recipient ${olmPayloadContent.recipient} does not match our id ${credentials.userId}")
|
" Intended recipient ${olmPayloadContent.recipient} does not match our id ${credentials.userId}")
|
||||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_RECIPIENT,
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_RECIPIENT,
|
||||||
String.format(MXCryptoError.BAD_RECIPIENT_REASON, olmPayloadContent.recipient)))
|
String.format(MXCryptoError.BAD_RECIPIENT_REASON, olmPayloadContent.recipient))
|
||||||
}
|
}
|
||||||
|
|
||||||
val recipientKeys = olmPayloadContent.recipient_keys ?: run {
|
val recipientKeys = olmPayloadContent.recipient_keys ?: run {
|
||||||
Timber.e("## decryptEvent() : Olm event (id=${event.eventId}) contains no 'recipient_keys' property; cannot prevent unknown-key attack")
|
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,
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY,
|
||||||
String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "recipient_keys")))
|
String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "recipient_keys"))
|
||||||
}
|
}
|
||||||
|
|
||||||
val ed25519 = recipientKeys["ed25519"]
|
val ed25519 = recipientKeys["ed25519"]
|
||||||
|
|
||||||
if (ed25519 != olmDevice.deviceEd25519Key) {
|
if (ed25519 != olmDevice.deviceEd25519Key) {
|
||||||
Timber.e("## decryptEvent() : Event ${event.eventId}: Intended recipient ed25519 key $ed25519 did not match ours")
|
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,
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_RECIPIENT_KEY,
|
||||||
MXCryptoError.BAD_RECIPIENT_KEY_REASON))
|
MXCryptoError.BAD_RECIPIENT_KEY_REASON)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (olmPayloadContent.sender.isNullOrBlank()) {
|
if (olmPayloadContent.sender.isNullOrBlank()) {
|
||||||
Timber.e("## decryptEvent() : Olm event (id=${event.eventId}) contains no 'sender' property; cannot prevent unknown-key attack")
|
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,
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY,
|
||||||
String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "sender")))
|
String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "sender"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (olmPayloadContent.sender != event.senderId) {
|
if (olmPayloadContent.sender != event.senderId) {
|
||||||
Timber.e("Event ${event.eventId}: original sender ${olmPayloadContent.sender} does not match reported 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,
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.FORWARDED_MESSAGE,
|
||||||
String.format(MXCryptoError.FORWARDED_MESSAGE_REASON, olmPayloadContent.sender)))
|
String.format(MXCryptoError.FORWARDED_MESSAGE_REASON, olmPayloadContent.sender))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (olmPayloadContent.room_id != event.roomId) {
|
if (olmPayloadContent.room_id != event.roomId) {
|
||||||
Timber.e("## decryptEvent() : Event ${event.eventId}: original room ${olmPayloadContent.room_id} does not match reported room ${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,
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ROOM,
|
||||||
String.format(MXCryptoError.BAD_ROOM_REASON, olmPayloadContent.room_id)))
|
String.format(MXCryptoError.BAD_ROOM_REASON, olmPayloadContent.room_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
val keys = olmPayloadContent.keys ?: run {
|
val keys = olmPayloadContent.keys ?: run {
|
||||||
Timber.e("## decryptEvent failed : null keys")
|
Timber.e("## decryptEvent failed : null keys")
|
||||||
return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT,
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT,
|
||||||
MXCryptoError.MISSING_CIPHER_TEXT_REASON))
|
MXCryptoError.MISSING_CIPHER_TEXT_REASON)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Try.just(MXEventDecryptionResult(
|
return MXEventDecryptionResult(
|
||||||
clearEvent = payload,
|
clearEvent = payload,
|
||||||
senderCurve25519Key = senderKey,
|
senderCurve25519Key = senderKey,
|
||||||
claimedEd25519Key = keys["ed25519"]
|
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.api.auth.data.Credentials
|
||||||
import im.vector.matrix.android.internal.crypto.MXOlmDevice
|
import im.vector.matrix.android.internal.crypto.MXOlmDevice
|
||||||
import im.vector.matrix.android.internal.session.SessionScope
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class MXOlmDecryptionFactory @Inject constructor(private val olmDevice: MXOlmDevice,
|
internal class MXOlmDecryptionFactory @Inject constructor(private val olmDevice: MXOlmDevice,
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
package im.vector.matrix.android.internal.crypto.algorithms.olm
|
package im.vector.matrix.android.internal.crypto.algorithms.olm
|
||||||
|
|
||||||
import android.text.TextUtils
|
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.Content
|
||||||
import im.vector.matrix.android.api.session.events.model.toContent
|
import im.vector.matrix.android.api.session.events.model.toContent
|
||||||
import im.vector.matrix.android.internal.crypto.DeviceListManager
|
import im.vector.matrix.android.internal.crypto.DeviceListManager
|
||||||
@ -40,12 +39,11 @@ internal class MXOlmEncryption(
|
|||||||
private val ensureOlmSessionsForUsersAction: EnsureOlmSessionsForUsersAction)
|
private val ensureOlmSessionsForUsersAction: EnsureOlmSessionsForUsersAction)
|
||||||
: IMXEncrypting {
|
: 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.
|
// pick the list of recipients based on the membership list.
|
||||||
//
|
//
|
||||||
// TODO: there is a race condition here! What if a new user turns up
|
// TODO: there is a race condition here! What if a new user turns up
|
||||||
return ensureSession(userIds)
|
ensureSession(userIds)
|
||||||
.map {
|
|
||||||
val deviceInfos = ArrayList<MXDeviceInfo>()
|
val deviceInfos = ArrayList<MXDeviceInfo>()
|
||||||
for (userId in userIds) {
|
for (userId in userIds) {
|
||||||
val devices = cryptoStore.getUserDevices(userId)?.values ?: emptyList()
|
val devices = cryptoStore.getUserDevices(userId)?.values ?: emptyList()
|
||||||
@ -69,8 +67,7 @@ internal class MXOlmEncryption(
|
|||||||
messageMap["content"] = eventContent
|
messageMap["content"] = eventContent
|
||||||
|
|
||||||
messageEncrypter.encryptMessage(messageMap, deviceInfos)
|
messageEncrypter.encryptMessage(messageMap, deviceInfos)
|
||||||
messageMap.toContent()!!
|
return messageMap.toContent()!!
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -78,13 +75,9 @@ internal class MXOlmEncryption(
|
|||||||
* Ensure that the session
|
* Ensure that the session
|
||||||
*
|
*
|
||||||
* @param users the user ids list
|
* @param users the user ids list
|
||||||
* @param callback the asynchronous callback
|
|
||||||
*/
|
*/
|
||||||
private suspend fun ensureSession(users: List<String>): Try<Unit> {
|
private suspend fun ensureSession(users: List<String>) {
|
||||||
return deviceListManager
|
deviceListManager.downloadKeys(users, false)
|
||||||
.downloadKeys(users, false)
|
ensureOlmSessionsForUsersAction.handle(users)
|
||||||
.flatMap { ensureOlmSessionsForUsersAction.handle(users) }
|
|
||||||
.map { Unit }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.EnsureOlmSessionsForUsersAction
|
||||||
import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter
|
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.store.IMXCryptoStore
|
||||||
import im.vector.matrix.android.internal.session.SessionScope
|
|
||||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||||
import javax.inject.Inject
|
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.*
|
||||||
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 im.vector.matrix.android.internal.network.NetworkConstants
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.http.*
|
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.di.MoshiProvider
|
||||||
import im.vector.matrix.android.internal.extensions.foldToCallback
|
import im.vector.matrix.android.internal.extensions.foldToCallback
|
||||||
import im.vector.matrix.android.internal.session.SessionScope
|
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.Task
|
||||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||||
import im.vector.matrix.android.internal.task.TaskThread
|
import im.vector.matrix.android.internal.task.TaskThread
|
||||||
@ -204,8 +203,8 @@ internal class KeysBackup @Inject constructor(
|
|||||||
keysBackupStateManager.state = KeysBackupState.Enabling
|
keysBackupStateManager.state = KeysBackupState.Enabling
|
||||||
|
|
||||||
createKeysBackupVersionTask
|
createKeysBackupVersionTask
|
||||||
.configureWith(createKeysBackupVersionBody)
|
.configureWith(createKeysBackupVersionBody) {
|
||||||
.dispatchTo(object : MatrixCallback<KeysVersion> {
|
this.callback = object : MatrixCallback<KeysVersion> {
|
||||||
override fun onSuccess(info: KeysVersion) {
|
override fun onSuccess(info: KeysVersion) {
|
||||||
// Reset backup markers.
|
// Reset backup markers.
|
||||||
cryptoStore.resetBackupMarkers()
|
cryptoStore.resetBackupMarkers()
|
||||||
@ -228,7 +227,8 @@ internal class KeysBackup @Inject constructor(
|
|||||||
keysBackupStateManager.state = KeysBackupState.Disabled
|
keysBackupStateManager.state = KeysBackupState.Disabled
|
||||||
callback.onFailure(failure)
|
callback.onFailure(failure)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
}
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,8 +243,9 @@ internal class KeysBackup @Inject constructor(
|
|||||||
keysBackupStateManager.state = KeysBackupState.Unknown
|
keysBackupStateManager.state = KeysBackupState.Unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteBackupTask.configureWith(DeleteBackupTask.Params(version))
|
deleteBackupTask
|
||||||
.dispatchTo(object : MatrixCallback<Unit> {
|
.configureWith(DeleteBackupTask.Params(version)) {
|
||||||
|
this.callback = object : MatrixCallback<Unit> {
|
||||||
private fun eventuallyRestartBackup() {
|
private fun eventuallyRestartBackup() {
|
||||||
// Do not stay in KeysBackupState.Unknown but check what is available on the homeserver
|
// Do not stay in KeysBackupState.Unknown but check what is available on the homeserver
|
||||||
if (state == KeysBackupState.Unknown) {
|
if (state == KeysBackupState.Unknown) {
|
||||||
@ -263,7 +264,8 @@ internal class KeysBackup @Inject constructor(
|
|||||||
|
|
||||||
uiHandler.post { callback?.onFailure(failure) }
|
uiHandler.post { callback?.onFailure(failure) }
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
}
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -355,15 +357,14 @@ internal class KeysBackup @Inject constructor(
|
|||||||
callback: MatrixCallback<KeysBackupVersionTrust>) {
|
callback: MatrixCallback<KeysBackupVersionTrust>) {
|
||||||
// TODO Validate with François that this is correct
|
// TODO Validate with François that this is correct
|
||||||
object : Task<KeysVersionResult, KeysBackupVersionTrust> {
|
object : Task<KeysVersionResult, KeysBackupVersionTrust> {
|
||||||
override suspend fun execute(params: KeysVersionResult): Try<KeysBackupVersionTrust> {
|
override suspend fun execute(params: KeysVersionResult): KeysBackupVersionTrust {
|
||||||
return Try {
|
return getKeysBackupTrustBg(params)
|
||||||
getKeysBackupTrustBg(params)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.configureWith(keysBackupVersion) {
|
||||||
|
this.callback = callback
|
||||||
|
this.executionThread = TaskThread.COMPUTATION
|
||||||
}
|
}
|
||||||
.configureWith(keysBackupVersion)
|
|
||||||
.dispatchTo(callback)
|
|
||||||
.executeOn(TaskThread.COMPUTATION)
|
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -454,7 +455,8 @@ internal class KeysBackup @Inject constructor(
|
|||||||
val myUserId = credentials.userId
|
val myUserId = credentials.userId
|
||||||
|
|
||||||
// Get current signatures, or create an empty set
|
// 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) {
|
if (trust) {
|
||||||
// Add current device signature
|
// Add current device signature
|
||||||
@ -492,8 +494,8 @@ internal class KeysBackup @Inject constructor(
|
|||||||
|
|
||||||
// And send it to the homeserver
|
// And send it to the homeserver
|
||||||
updateKeysBackupVersionTask
|
updateKeysBackupVersionTask
|
||||||
.configureWith(UpdateKeysBackupVersionTask.Params(keysBackupVersion.version!!, updateKeysBackupVersionBody))
|
.configureWith(UpdateKeysBackupVersionTask.Params(keysBackupVersion.version!!, updateKeysBackupVersionBody)) {
|
||||||
.dispatchTo(object : MatrixCallback<Unit> {
|
this.callback = object : MatrixCallback<Unit> {
|
||||||
override fun onSuccess(data: Unit) {
|
override fun onSuccess(data: Unit) {
|
||||||
// Relaunch the state machine on this updated backup version
|
// Relaunch the state machine on this updated backup version
|
||||||
val newKeysBackupVersion = KeysVersionResult()
|
val newKeysBackupVersion = KeysVersionResult()
|
||||||
@ -512,7 +514,8 @@ internal class KeysBackup @Inject constructor(
|
|||||||
override fun onFailure(failure: Throwable) {
|
override fun onFailure(failure: Throwable) {
|
||||||
callback.onFailure(failure)
|
callback.onFailure(failure)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
}
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -758,8 +761,8 @@ internal class KeysBackup @Inject constructor(
|
|||||||
if (roomId != null && sessionId != null) {
|
if (roomId != null && sessionId != null) {
|
||||||
// Get key for the room and for the session
|
// Get key for the room and for the session
|
||||||
getRoomSessionDataTask
|
getRoomSessionDataTask
|
||||||
.configureWith(GetRoomSessionDataTask.Params(roomId, sessionId, version))
|
.configureWith(GetRoomSessionDataTask.Params(roomId, sessionId, version)) {
|
||||||
.dispatchTo(object : MatrixCallback<KeyBackupData> {
|
this.callback = object : MatrixCallback<KeyBackupData> {
|
||||||
override fun onSuccess(data: KeyBackupData) {
|
override fun onSuccess(data: KeyBackupData) {
|
||||||
// Convert to KeysBackupData
|
// Convert to KeysBackupData
|
||||||
val keysBackupData = KeysBackupData()
|
val keysBackupData = KeysBackupData()
|
||||||
@ -775,13 +778,14 @@ internal class KeysBackup @Inject constructor(
|
|||||||
override fun onFailure(failure: Throwable) {
|
override fun onFailure(failure: Throwable) {
|
||||||
callback.onFailure(failure)
|
callback.onFailure(failure)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
}
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
} else if (roomId != null) {
|
} else if (roomId != null) {
|
||||||
// Get all keys for the room
|
// Get all keys for the room
|
||||||
getRoomSessionsDataTask
|
getRoomSessionsDataTask
|
||||||
.configureWith(GetRoomSessionsDataTask.Params(roomId, version))
|
.configureWith(GetRoomSessionsDataTask.Params(roomId, version)) {
|
||||||
.dispatchTo(object : MatrixCallback<RoomKeysBackupData> {
|
this.callback = object : MatrixCallback<RoomKeysBackupData> {
|
||||||
override fun onSuccess(data: RoomKeysBackupData) {
|
override fun onSuccess(data: RoomKeysBackupData) {
|
||||||
// Convert to KeysBackupData
|
// Convert to KeysBackupData
|
||||||
val keysBackupData = KeysBackupData()
|
val keysBackupData = KeysBackupData()
|
||||||
@ -794,13 +798,15 @@ internal class KeysBackup @Inject constructor(
|
|||||||
override fun onFailure(failure: Throwable) {
|
override fun onFailure(failure: Throwable) {
|
||||||
callback.onFailure(failure)
|
callback.onFailure(failure)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
}
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
} else {
|
} else {
|
||||||
// Get all keys
|
// Get all keys
|
||||||
getSessionsDataTask
|
getSessionsDataTask
|
||||||
.configureWith(GetSessionsDataTask.Params(version))
|
.configureWith(GetSessionsDataTask.Params(version)) {
|
||||||
.dispatchTo(callback)
|
this.callback = callback
|
||||||
|
}
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -855,8 +861,8 @@ internal class KeysBackup @Inject constructor(
|
|||||||
override fun getVersion(version: String,
|
override fun getVersion(version: String,
|
||||||
callback: MatrixCallback<KeysVersionResult?>) {
|
callback: MatrixCallback<KeysVersionResult?>) {
|
||||||
getKeysBackupVersionTask
|
getKeysBackupVersionTask
|
||||||
.configureWith(version)
|
.configureWith(version) {
|
||||||
.dispatchTo(object : MatrixCallback<KeysVersionResult> {
|
this.callback = object : MatrixCallback<KeysVersionResult> {
|
||||||
override fun onSuccess(data: KeysVersionResult) {
|
override fun onSuccess(data: KeysVersionResult) {
|
||||||
callback.onSuccess(data)
|
callback.onSuccess(data)
|
||||||
}
|
}
|
||||||
@ -871,14 +877,15 @@ internal class KeysBackup @Inject constructor(
|
|||||||
callback.onFailure(failure)
|
callback.onFailure(failure)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
}
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getCurrentVersion(callback: MatrixCallback<KeysVersionResult?>) {
|
override fun getCurrentVersion(callback: MatrixCallback<KeysVersionResult?>) {
|
||||||
getKeysBackupLastVersionTask
|
getKeysBackupLastVersionTask
|
||||||
.toConfigurableTask()
|
.configureWith {
|
||||||
.dispatchTo(object : MatrixCallback<KeysVersionResult> {
|
this.callback = object : MatrixCallback<KeysVersionResult> {
|
||||||
override fun onSuccess(data: KeysVersionResult) {
|
override fun onSuccess(data: KeysVersionResult) {
|
||||||
callback.onSuccess(data)
|
callback.onSuccess(data)
|
||||||
}
|
}
|
||||||
@ -893,7 +900,8 @@ internal class KeysBackup @Inject constructor(
|
|||||||
callback.onFailure(failure)
|
callback.onFailure(failure)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
}
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1236,10 +1244,7 @@ internal class KeysBackup @Inject constructor(
|
|||||||
|
|
||||||
Timber.v("backupKeys: 4 - Sending request")
|
Timber.v("backupKeys: 4 - Sending request")
|
||||||
|
|
||||||
// Make the request
|
val sendingRequestCallback = object : MatrixCallback<BackupKeysResult> {
|
||||||
storeSessionDataTask
|
|
||||||
.configureWith(StoreSessionsDataTask.Params(keysBackupVersion!!.version!!, keysBackupData))
|
|
||||||
.dispatchTo(object : MatrixCallback<BackupKeysResult> {
|
|
||||||
override fun onSuccess(data: BackupKeysResult) {
|
override fun onSuccess(data: BackupKeysResult) {
|
||||||
uiHandler.post {
|
uiHandler.post {
|
||||||
Timber.v("backupKeys: 5a - Request complete")
|
Timber.v("backupKeys: 5a - Request complete")
|
||||||
@ -1298,7 +1303,13 @@ internal class KeysBackup @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
|
// Make the request
|
||||||
|
storeSessionDataTask
|
||||||
|
.configureWith(StoreSessionsDataTask.Params(keysBackupVersion!!.version!!, keysBackupData)){
|
||||||
|
this.callback = sendingRequestCallback
|
||||||
|
}
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -142,12 +142,11 @@ private fun deriveKey(password: String,
|
|||||||
* Generate a 32 chars salt
|
* Generate a 32 chars salt
|
||||||
*/
|
*/
|
||||||
private fun generateSalt(): String {
|
private fun generateSalt(): String {
|
||||||
var salt = ""
|
val salt = buildString {
|
||||||
|
|
||||||
do {
|
do {
|
||||||
salt += UUID.randomUUID().toString()
|
append(UUID.randomUUID().toString())
|
||||||
} while (salt.length < SALT_LENGTH)
|
} while (length < SALT_LENGTH)
|
||||||
|
}
|
||||||
|
|
||||||
return salt.substring(0, SALT_LENGTH)
|
return salt.substring(0, SALT_LENGTH)
|
||||||
}
|
}
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.keysbackup.tasks
|
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.api.RoomKeysApi
|
||||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.CreateKeysBackupVersionBody
|
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.CreateKeysBackupVersionBody
|
||||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersion
|
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersion
|
||||||
@ -30,7 +29,7 @@ internal class DefaultCreateKeysBackupVersionTask @Inject constructor(private va
|
|||||||
: CreateKeysBackupVersionTask {
|
: CreateKeysBackupVersionTask {
|
||||||
|
|
||||||
|
|
||||||
override suspend fun execute(params: CreateKeysBackupVersionBody): Try<KeysVersion> {
|
override suspend fun execute(params: CreateKeysBackupVersionBody): KeysVersion {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomKeysApi.createKeysBackupVersion(params)
|
apiCall = roomKeysApi.createKeysBackupVersion(params)
|
||||||
}
|
}
|
||||||
|
@ -16,10 +16,8 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.keysbackup.tasks
|
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.api.RoomKeysApi
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
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.task.Task
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -33,10 +31,9 @@ internal interface DeleteBackupTask : Task<DeleteBackupTask.Params, Unit> {
|
|||||||
internal class DefaultDeleteBackupTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
internal class DefaultDeleteBackupTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
||||||
: DeleteBackupTask {
|
: DeleteBackupTask {
|
||||||
|
|
||||||
override suspend fun execute(params: DeleteBackupTask.Params): Try<Unit> {
|
override suspend fun execute(params: DeleteBackupTask.Params) {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomKeysApi.deleteBackup(
|
apiCall = roomKeysApi.deleteBackup(params.version)
|
||||||
params.version)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,10 +16,8 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.keysbackup.tasks
|
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.api.RoomKeysApi
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
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.task.Task
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -34,7 +32,7 @@ internal interface DeleteRoomSessionDataTask : Task<DeleteRoomSessionDataTask.Pa
|
|||||||
internal class DefaultDeleteRoomSessionDataTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
internal class DefaultDeleteRoomSessionDataTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
||||||
: DeleteRoomSessionDataTask {
|
: DeleteRoomSessionDataTask {
|
||||||
|
|
||||||
override suspend fun execute(params: DeleteRoomSessionDataTask.Params): Try<Unit> {
|
override suspend fun execute(params: DeleteRoomSessionDataTask.Params) {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomKeysApi.deleteRoomSessionData(
|
apiCall = roomKeysApi.deleteRoomSessionData(
|
||||||
params.roomId,
|
params.roomId,
|
||||||
|
@ -16,10 +16,8 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.keysbackup.tasks
|
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.api.RoomKeysApi
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
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.task.Task
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -33,7 +31,7 @@ internal interface DeleteRoomSessionsDataTask : Task<DeleteRoomSessionsDataTask.
|
|||||||
internal class DefaultDeleteRoomSessionsDataTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
internal class DefaultDeleteRoomSessionsDataTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
||||||
: DeleteRoomSessionsDataTask {
|
: DeleteRoomSessionsDataTask {
|
||||||
|
|
||||||
override suspend fun execute(params: DeleteRoomSessionsDataTask.Params): Try<Unit> {
|
override suspend fun execute(params: DeleteRoomSessionsDataTask.Params) {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomKeysApi.deleteRoomSessionsData(
|
apiCall = roomKeysApi.deleteRoomSessionsData(
|
||||||
params.roomId,
|
params.roomId,
|
||||||
|
@ -16,10 +16,8 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.keysbackup.tasks
|
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.api.RoomKeysApi
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
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.task.Task
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -32,10 +30,9 @@ internal interface DeleteSessionsDataTask : Task<DeleteSessionsDataTask.Params,
|
|||||||
internal class DefaultDeleteSessionsDataTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
internal class DefaultDeleteSessionsDataTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
||||||
: DeleteSessionsDataTask {
|
: DeleteSessionsDataTask {
|
||||||
|
|
||||||
override suspend fun execute(params: DeleteSessionsDataTask.Params): Try<Unit> {
|
override suspend fun execute(params: DeleteSessionsDataTask.Params) {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomKeysApi.deleteSessionsData(
|
apiCall = roomKeysApi.deleteSessionsData(params.version)
|
||||||
params.version)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.keysbackup.tasks
|
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.api.RoomKeysApi
|
||||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult
|
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
import im.vector.matrix.android.internal.network.executeRequest
|
||||||
@ -29,7 +28,7 @@ internal class DefaultGetKeysBackupLastVersionTask @Inject constructor(private v
|
|||||||
: GetKeysBackupLastVersionTask {
|
: GetKeysBackupLastVersionTask {
|
||||||
|
|
||||||
|
|
||||||
override suspend fun execute(params: Unit): Try<KeysVersionResult> {
|
override suspend fun execute(params: Unit): KeysVersionResult {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomKeysApi.getKeysBackupLastVersion()
|
apiCall = roomKeysApi.getKeysBackupLastVersion()
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.keysbackup.tasks
|
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.api.RoomKeysApi
|
||||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult
|
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
import im.vector.matrix.android.internal.network.executeRequest
|
||||||
@ -29,7 +28,7 @@ internal class DefaultGetKeysBackupVersionTask @Inject constructor(private val r
|
|||||||
: GetKeysBackupVersionTask {
|
: GetKeysBackupVersionTask {
|
||||||
|
|
||||||
|
|
||||||
override suspend fun execute(params: String): Try<KeysVersionResult> {
|
override suspend fun execute(params: String): KeysVersionResult {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomKeysApi.getKeysBackupVersion(params)
|
apiCall = roomKeysApi.getKeysBackupVersion(params)
|
||||||
}
|
}
|
||||||
|
@ -16,11 +16,9 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.keysbackup.tasks
|
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.api.RoomKeysApi
|
||||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeyBackupData
|
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.network.executeRequest
|
||||||
import im.vector.matrix.android.internal.session.SessionScope
|
|
||||||
import im.vector.matrix.android.internal.task.Task
|
import im.vector.matrix.android.internal.task.Task
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -35,7 +33,7 @@ internal interface GetRoomSessionDataTask : Task<GetRoomSessionDataTask.Params,
|
|||||||
internal class DefaultGetRoomSessionDataTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
internal class DefaultGetRoomSessionDataTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
||||||
: GetRoomSessionDataTask {
|
: GetRoomSessionDataTask {
|
||||||
|
|
||||||
override suspend fun execute(params: GetRoomSessionDataTask.Params): Try<KeyBackupData> {
|
override suspend fun execute(params: GetRoomSessionDataTask.Params): KeyBackupData {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomKeysApi.getRoomSessionData(
|
apiCall = roomKeysApi.getRoomSessionData(
|
||||||
params.roomId,
|
params.roomId,
|
||||||
|
@ -16,11 +16,9 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.keysbackup.tasks
|
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.api.RoomKeysApi
|
||||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.RoomKeysBackupData
|
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.network.executeRequest
|
||||||
import im.vector.matrix.android.internal.session.SessionScope
|
|
||||||
import im.vector.matrix.android.internal.task.Task
|
import im.vector.matrix.android.internal.task.Task
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -35,7 +33,7 @@ internal interface GetRoomSessionsDataTask : Task<GetRoomSessionsDataTask.Params
|
|||||||
internal class DefaultGetRoomSessionsDataTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
internal class DefaultGetRoomSessionsDataTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
||||||
: GetRoomSessionsDataTask {
|
: GetRoomSessionsDataTask {
|
||||||
|
|
||||||
override suspend fun execute(params: GetRoomSessionsDataTask.Params): Try<RoomKeysBackupData> {
|
override suspend fun execute(params: GetRoomSessionsDataTask.Params): RoomKeysBackupData {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomKeysApi.getRoomSessionsData(
|
apiCall = roomKeysApi.getRoomSessionsData(
|
||||||
params.roomId,
|
params.roomId,
|
||||||
|
@ -16,11 +16,9 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.keysbackup.tasks
|
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.api.RoomKeysApi
|
||||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysBackupData
|
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.network.executeRequest
|
||||||
import im.vector.matrix.android.internal.session.SessionScope
|
|
||||||
import im.vector.matrix.android.internal.task.Task
|
import im.vector.matrix.android.internal.task.Task
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -33,10 +31,9 @@ internal interface GetSessionsDataTask : Task<GetSessionsDataTask.Params, KeysBa
|
|||||||
internal class DefaultGetSessionsDataTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
internal class DefaultGetSessionsDataTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
||||||
: GetSessionsDataTask {
|
: GetSessionsDataTask {
|
||||||
|
|
||||||
override suspend fun execute(params: GetSessionsDataTask.Params): Try<KeysBackupData> {
|
override suspend fun execute(params: GetSessionsDataTask.Params): KeysBackupData {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomKeysApi.getSessionsData(
|
apiCall = roomKeysApi.getSessionsData(params.version)
|
||||||
params.version)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,12 +16,10 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.keysbackup.tasks
|
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.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.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.network.executeRequest
|
||||||
import im.vector.matrix.android.internal.session.SessionScope
|
|
||||||
import im.vector.matrix.android.internal.task.Task
|
import im.vector.matrix.android.internal.task.Task
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -37,7 +35,7 @@ internal interface StoreRoomSessionDataTask : Task<StoreRoomSessionDataTask.Para
|
|||||||
internal class DefaultStoreRoomSessionDataTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
internal class DefaultStoreRoomSessionDataTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
||||||
: StoreRoomSessionDataTask {
|
: StoreRoomSessionDataTask {
|
||||||
|
|
||||||
override suspend fun execute(params: StoreRoomSessionDataTask.Params): Try<BackupKeysResult> {
|
override suspend fun execute(params: StoreRoomSessionDataTask.Params): BackupKeysResult {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomKeysApi.storeRoomSessionData(
|
apiCall = roomKeysApi.storeRoomSessionData(
|
||||||
params.roomId,
|
params.roomId,
|
||||||
|
@ -16,12 +16,10 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.keysbackup.tasks
|
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.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.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.network.executeRequest
|
||||||
import im.vector.matrix.android.internal.session.SessionScope
|
|
||||||
import im.vector.matrix.android.internal.task.Task
|
import im.vector.matrix.android.internal.task.Task
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -36,7 +34,7 @@ internal interface StoreRoomSessionsDataTask : Task<StoreRoomSessionsDataTask.Pa
|
|||||||
internal class DefaultStoreRoomSessionsDataTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
internal class DefaultStoreRoomSessionsDataTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
||||||
: StoreRoomSessionsDataTask {
|
: StoreRoomSessionsDataTask {
|
||||||
|
|
||||||
override suspend fun execute(params: StoreRoomSessionsDataTask.Params): Try<BackupKeysResult> {
|
override suspend fun execute(params: StoreRoomSessionsDataTask.Params): BackupKeysResult {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomKeysApi.storeRoomSessionsData(
|
apiCall = roomKeysApi.storeRoomSessionsData(
|
||||||
params.roomId,
|
params.roomId,
|
||||||
|
@ -16,12 +16,10 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.keysbackup.tasks
|
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.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.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.network.executeRequest
|
||||||
import im.vector.matrix.android.internal.session.SessionScope
|
|
||||||
import im.vector.matrix.android.internal.task.Task
|
import im.vector.matrix.android.internal.task.Task
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -35,7 +33,7 @@ internal interface StoreSessionsDataTask : Task<StoreSessionsDataTask.Params, Ba
|
|||||||
internal class DefaultStoreSessionsDataTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
internal class DefaultStoreSessionsDataTask @Inject constructor(private val roomKeysApi: RoomKeysApi)
|
||||||
: StoreSessionsDataTask {
|
: StoreSessionsDataTask {
|
||||||
|
|
||||||
override suspend fun execute(params: StoreSessionsDataTask.Params): Try<BackupKeysResult> {
|
override suspend fun execute(params: StoreSessionsDataTask.Params): BackupKeysResult {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomKeysApi.storeSessionsData(
|
apiCall = roomKeysApi.storeSessionsData(
|
||||||
params.version,
|
params.version,
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.keysbackup.tasks
|
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.api.RoomKeysApi
|
||||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.UpdateKeysBackupVersionBody
|
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.UpdateKeysBackupVersionBody
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
import im.vector.matrix.android.internal.network.executeRequest
|
||||||
@ -34,7 +33,7 @@ internal class DefaultUpdateKeysBackupVersionTask @Inject constructor(private va
|
|||||||
: UpdateKeysBackupVersionTask {
|
: UpdateKeysBackupVersionTask {
|
||||||
|
|
||||||
|
|
||||||
override suspend fun execute(params: UpdateKeysBackupVersionTask.Params): Try<Unit> {
|
override suspend fun execute(params: UpdateKeysBackupVersionTask.Params) {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomKeysApi.updateKeysBackupVersion(params.version, params.keysBackupVersionBody)
|
apiCall = roomKeysApi.updateKeysBackupVersion(params.version, params.keysBackupVersionBody)
|
||||||
}
|
}
|
||||||
|
@ -63,10 +63,8 @@ data class MXKey(
|
|||||||
fun signatureForUserId(userId: String, signkey: String): String? {
|
fun signatureForUserId(userId: String, signkey: String): String? {
|
||||||
// sanity checks
|
// sanity checks
|
||||||
if (userId.isNotBlank() && signkey.isNotBlank()) {
|
if (userId.isNotBlank() && signkey.isNotBlank()) {
|
||||||
if (signatures.containsKey(userId)) {
|
|
||||||
return signatures[userId]?.get(signkey)
|
return signatures[userId]?.get(signkey)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,8 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.store.db
|
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 im.vector.matrix.android.internal.crypto.store.db.model.*
|
||||||
|
import io.realm.annotations.RealmModule
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Realm module for Crypto store classes
|
* Realm module for Crypto store classes
|
||||||
|
@ -16,10 +16,10 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.store.db.model
|
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.deserializeFromRealm
|
||||||
import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm
|
import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm
|
||||||
|
import io.realm.RealmObject
|
||||||
|
import io.realm.annotations.PrimaryKey
|
||||||
import org.matrix.olm.OlmAccount
|
import org.matrix.olm.OlmAccount
|
||||||
|
|
||||||
internal open class CryptoMetadataEntity(
|
internal open class CryptoMetadataEntity(
|
||||||
|
@ -16,9 +16,9 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.store.db.model
|
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.deserializeFromRealm
|
||||||
import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm
|
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.RealmObject
|
||||||
import io.realm.RealmResults
|
import io.realm.RealmResults
|
||||||
import io.realm.annotations.LinkingObjects
|
import io.realm.annotations.LinkingObjects
|
||||||
|
@ -16,11 +16,11 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.store.db.model
|
package im.vector.matrix.android.internal.crypto.store.db.model
|
||||||
|
|
||||||
import io.realm.RealmObject
|
import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper
|
||||||
import io.realm.annotations.PrimaryKey
|
|
||||||
import im.vector.matrix.android.internal.crypto.store.db.deserializeFromRealm
|
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.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"
|
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
|
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.deserializeFromRealm
|
||||||
import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm
|
import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm
|
||||||
|
import io.realm.RealmObject
|
||||||
|
import io.realm.annotations.PrimaryKey
|
||||||
import org.matrix.olm.OlmSession
|
import org.matrix.olm.OlmSession
|
||||||
|
|
||||||
internal fun OlmSessionEntity.Companion.createPrimaryKey(sessionId: String, deviceKey: String) = "$sessionId|$deviceKey"
|
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
|
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.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.deserializeFromRealm
|
||||||
import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm
|
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.RealmObject
|
||||||
import io.realm.annotations.PrimaryKey
|
import io.realm.annotations.PrimaryKey
|
||||||
|
|
||||||
|
@ -16,11 +16,11 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.store.db.query
|
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.DeviceInfoEntity
|
||||||
import im.vector.matrix.android.internal.crypto.store.db.model.DeviceInfoEntityFields
|
import im.vector.matrix.android.internal.crypto.store.db.model.DeviceInfoEntityFields
|
||||||
import im.vector.matrix.android.internal.crypto.store.db.model.createPrimaryKey
|
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
|
* Get or create a device info
|
||||||
|
@ -16,10 +16,10 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.store.db.query
|
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.UserEntity
|
||||||
import im.vector.matrix.android.internal.crypto.store.db.model.UserEntityFields
|
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
|
* Get or create a user
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.tasks
|
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.api.CryptoApi
|
||||||
import im.vector.matrix.android.internal.crypto.model.MXKey
|
import im.vector.matrix.android.internal.crypto.model.MXKey
|
||||||
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
||||||
@ -37,15 +36,13 @@ internal interface ClaimOneTimeKeysForUsersDeviceTask : Task<ClaimOneTimeKeysFor
|
|||||||
internal class DefaultClaimOneTimeKeysForUsersDevice @Inject constructor(private val cryptoApi: CryptoApi)
|
internal class DefaultClaimOneTimeKeysForUsersDevice @Inject constructor(private val cryptoApi: CryptoApi)
|
||||||
: ClaimOneTimeKeysForUsersDeviceTask {
|
: 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)
|
val body = KeysClaimBody(oneTimeKeys = params.usersDevicesKeyTypesMap.map)
|
||||||
|
|
||||||
return executeRequest<KeysClaimResponse> {
|
val keysClaimResponse = executeRequest<KeysClaimResponse> {
|
||||||
apiCall = cryptoApi.claimOneTimeKeysForUsersDevices(body)
|
apiCall = cryptoApi.claimOneTimeKeysForUsersDevices(body)
|
||||||
}.flatMap { keysClaimResponse ->
|
}
|
||||||
Try {
|
|
||||||
val map = MXUsersDevicesMap<MXKey>()
|
val map = MXUsersDevicesMap<MXKey>()
|
||||||
|
|
||||||
keysClaimResponse.oneTimeKeys?.let { oneTimeKeys ->
|
keysClaimResponse.oneTimeKeys?.let { oneTimeKeys ->
|
||||||
for (userId in oneTimeKeys.keys) {
|
for (userId in oneTimeKeys.keys) {
|
||||||
val mapByUserId = oneTimeKeys[userId]
|
val mapByUserId = oneTimeKeys[userId]
|
||||||
@ -63,9 +60,6 @@ internal class DefaultClaimOneTimeKeysForUsersDevice @Inject constructor(private
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return map
|
||||||
map
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,16 +16,12 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.tasks
|
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.api.failure.Failure
|
||||||
import im.vector.matrix.android.internal.auth.registration.RegistrationFlowResponse
|
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.api.CryptoApi
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.DeleteDeviceParams
|
import im.vector.matrix.android.internal.crypto.model.rest.DeleteDeviceParams
|
||||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
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.task.Task
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -38,10 +34,12 @@ internal interface DeleteDeviceTask : Task<DeleteDeviceTask.Params, Unit> {
|
|||||||
internal class DefaultDeleteDeviceTask @Inject constructor(private val cryptoApi: CryptoApi)
|
internal class DefaultDeleteDeviceTask @Inject constructor(private val cryptoApi: CryptoApi)
|
||||||
: DeleteDeviceTask {
|
: DeleteDeviceTask {
|
||||||
|
|
||||||
override suspend fun execute(params: DeleteDeviceTask.Params): Try<Unit> {
|
override suspend fun execute(params: DeleteDeviceTask.Params) {
|
||||||
return executeRequest<Unit> {
|
try {
|
||||||
|
executeRequest<Unit> {
|
||||||
apiCall = cryptoApi.deleteDevice(params.deviceId, DeleteDeviceParams())
|
apiCall = cryptoApi.deleteDevice(params.deviceId, DeleteDeviceParams())
|
||||||
}.recoverWith { throwable ->
|
}
|
||||||
|
} catch (throwable: Throwable) {
|
||||||
if (throwable is Failure.OtherServerError && throwable.httpCode == 401) {
|
if (throwable is Failure.OtherServerError && throwable.httpCode == 401) {
|
||||||
// Parse to get a RegistrationFlowResponse
|
// Parse to get a RegistrationFlowResponse
|
||||||
val registrationFlowResponse = try {
|
val registrationFlowResponse = try {
|
||||||
@ -51,17 +49,16 @@ internal class DefaultDeleteDeviceTask @Inject constructor(private val cryptoApi
|
|||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if the server response can be casted
|
// check if the server response can be casted
|
||||||
if (registrationFlowResponse != null) {
|
if (registrationFlowResponse != null) {
|
||||||
Failure.RegistrationFlowError(registrationFlowResponse).failure()
|
throw Failure.RegistrationFlowError(registrationFlowResponse)
|
||||||
} else {
|
} else {
|
||||||
throwable.failure()
|
throw throwable
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Other error
|
// Other error
|
||||||
throwable.failure()
|
throw throwable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.tasks
|
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.api.auth.data.Credentials
|
||||||
import im.vector.matrix.android.internal.auth.data.LoginFlowTypes
|
import im.vector.matrix.android.internal.auth.data.LoginFlowTypes
|
||||||
import im.vector.matrix.android.internal.crypto.api.CryptoApi
|
import im.vector.matrix.android.internal.crypto.api.CryptoApi
|
||||||
@ -38,7 +37,7 @@ internal class DefaultDeleteDeviceWithUserPasswordTask @Inject constructor(priva
|
|||||||
private val credentials: Credentials)
|
private val credentials: Credentials)
|
||||||
: DeleteDeviceWithUserPasswordTask {
|
: DeleteDeviceWithUserPasswordTask {
|
||||||
|
|
||||||
override suspend fun execute(params: DeleteDeviceWithUserPasswordTask.Params): Try<Unit> {
|
override suspend fun execute(params: DeleteDeviceWithUserPasswordTask.Params) {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = cryptoApi.deleteDevice(params.deviceId, DeleteDeviceParams()
|
apiCall = cryptoApi.deleteDevice(params.deviceId, DeleteDeviceParams()
|
||||||
.apply {
|
.apply {
|
||||||
|
@ -17,12 +17,10 @@
|
|||||||
package im.vector.matrix.android.internal.crypto.tasks
|
package im.vector.matrix.android.internal.crypto.tasks
|
||||||
|
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import arrow.core.Try
|
|
||||||
import im.vector.matrix.android.internal.crypto.api.CryptoApi
|
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.KeysQueryBody
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.KeysQueryResponse
|
import im.vector.matrix.android.internal.crypto.model.rest.KeysQueryResponse
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
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.task.Task
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -38,7 +36,7 @@ internal interface DownloadKeysForUsersTask : Task<DownloadKeysForUsersTask.Para
|
|||||||
internal class DefaultDownloadKeysForUsers @Inject constructor(private val cryptoApi: CryptoApi)
|
internal class DefaultDownloadKeysForUsers @Inject constructor(private val cryptoApi: CryptoApi)
|
||||||
: DownloadKeysForUsersTask {
|
: 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>>()
|
val downloadQuery = HashMap<String, Map<String, Any>>()
|
||||||
|
|
||||||
if (null != params.userIds) {
|
if (null != params.userIds) {
|
||||||
|
@ -16,11 +16,9 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.tasks
|
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.api.CryptoApi
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse
|
import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
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.task.Task
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -29,7 +27,7 @@ internal interface GetDevicesTask : Task<Unit, DevicesListResponse>
|
|||||||
internal class DefaultGetDevicesTask @Inject constructor(private val cryptoApi: CryptoApi)
|
internal class DefaultGetDevicesTask @Inject constructor(private val cryptoApi: CryptoApi)
|
||||||
: GetDevicesTask {
|
: GetDevicesTask {
|
||||||
|
|
||||||
override suspend fun execute(params: Unit): Try<DevicesListResponse> {
|
override suspend fun execute(params: Unit): DevicesListResponse {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = cryptoApi.getDevices()
|
apiCall = cryptoApi.getDevices()
|
||||||
}
|
}
|
||||||
|
@ -16,11 +16,9 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.tasks
|
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.api.CryptoApi
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.KeyChangesResponse
|
import im.vector.matrix.android.internal.crypto.model.rest.KeyChangesResponse
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
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.task.Task
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -36,10 +34,9 @@ internal interface GetKeyChangesTask : Task<GetKeyChangesTask.Params, KeyChanges
|
|||||||
internal class DefaultGetKeyChangesTask @Inject constructor(private val cryptoApi: CryptoApi)
|
internal class DefaultGetKeyChangesTask @Inject constructor(private val cryptoApi: CryptoApi)
|
||||||
: GetKeyChangesTask {
|
: GetKeyChangesTask {
|
||||||
|
|
||||||
override suspend fun execute(params: GetKeyChangesTask.Params): Try<KeyChangesResponse> {
|
override suspend fun execute(params: GetKeyChangesTask.Params): KeyChangesResponse {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = cryptoApi.getKeyChanges(params.from,
|
apiCall = cryptoApi.getKeyChanges(params.from, params.to)
|
||||||
params.to)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,12 +16,10 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.tasks
|
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.api.CryptoApi
|
||||||
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
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.crypto.model.rest.SendToDeviceBody
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
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.task.Task
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -40,7 +38,7 @@ internal interface SendToDeviceTask : Task<SendToDeviceTask.Params, Unit> {
|
|||||||
internal class DefaultSendToDeviceTask @Inject constructor(private val cryptoApi: CryptoApi)
|
internal class DefaultSendToDeviceTask @Inject constructor(private val cryptoApi: CryptoApi)
|
||||||
: SendToDeviceTask {
|
: SendToDeviceTask {
|
||||||
|
|
||||||
override suspend fun execute(params: SendToDeviceTask.Params): Try<Unit> {
|
override suspend fun execute(params: SendToDeviceTask.Params) {
|
||||||
val sendToDeviceBody = SendToDeviceBody()
|
val sendToDeviceBody = SendToDeviceBody()
|
||||||
sendToDeviceBody.messages = params.contentMap.map
|
sendToDeviceBody.messages = params.contentMap.map
|
||||||
|
|
||||||
|
@ -17,11 +17,9 @@
|
|||||||
package im.vector.matrix.android.internal.crypto.tasks
|
package im.vector.matrix.android.internal.crypto.tasks
|
||||||
|
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import arrow.core.Try
|
|
||||||
import im.vector.matrix.android.internal.crypto.api.CryptoApi
|
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.crypto.model.rest.UpdateDeviceInfoBody
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
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.task.Task
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -37,11 +35,10 @@ internal interface SetDeviceNameTask : Task<SetDeviceNameTask.Params, Unit> {
|
|||||||
internal class DefaultSetDeviceNameTask @Inject constructor(private val cryptoApi: CryptoApi)
|
internal class DefaultSetDeviceNameTask @Inject constructor(private val cryptoApi: CryptoApi)
|
||||||
: SetDeviceNameTask {
|
: SetDeviceNameTask {
|
||||||
|
|
||||||
override suspend fun execute(params: SetDeviceNameTask.Params): Try<Unit> {
|
override suspend fun execute(params: SetDeviceNameTask.Params) {
|
||||||
val body = UpdateDeviceInfoBody(
|
val body = UpdateDeviceInfoBody(
|
||||||
displayName = if (TextUtils.isEmpty(params.deviceName)) "" else params.deviceName
|
displayName = if (TextUtils.isEmpty(params.deviceName)) "" else params.deviceName
|
||||||
)
|
)
|
||||||
|
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = cryptoApi.updateDeviceInfo(params.deviceId, body)
|
apiCall = cryptoApi.updateDeviceInfo(params.deviceId, body)
|
||||||
}
|
}
|
||||||
|
@ -16,14 +16,12 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto.tasks
|
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.api.util.JsonDict
|
||||||
import im.vector.matrix.android.internal.crypto.api.CryptoApi
|
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.DeviceKeys
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadBody
|
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.network.executeRequest
|
||||||
import im.vector.matrix.android.internal.session.SessionScope
|
|
||||||
import im.vector.matrix.android.internal.task.Task
|
import im.vector.matrix.android.internal.task.Task
|
||||||
import im.vector.matrix.android.internal.util.convertToUTF8
|
import im.vector.matrix.android.internal.util.convertToUTF8
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -41,7 +39,7 @@ internal interface UploadKeysTask : Task<UploadKeysTask.Params, KeysUploadRespon
|
|||||||
internal class DefaultUploadKeysTask @Inject constructor(private val cryptoApi: CryptoApi)
|
internal class DefaultUploadKeysTask @Inject constructor(private val cryptoApi: CryptoApi)
|
||||||
: UploadKeysTask {
|
: UploadKeysTask {
|
||||||
|
|
||||||
override suspend fun execute(params: UploadKeysTask.Params): Try<KeysUploadResponse> {
|
override suspend fun execute(params: UploadKeysTask.Params): KeysUploadResponse {
|
||||||
val encodedDeviceId = convertToUTF8(params.deviceId)
|
val encodedDeviceId = convertToUTF8(params.deviceId)
|
||||||
|
|
||||||
val body = KeysUploadBody()
|
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.store.IMXCryptoStore
|
||||||
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
|
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
|
||||||
import im.vector.matrix.android.internal.session.SessionScope
|
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.TaskExecutor
|
||||||
import im.vector.matrix.android.internal.task.configureWith
|
import im.vector.matrix.android.internal.task.configureWith
|
||||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||||
@ -219,15 +220,18 @@ internal class DefaultSasVerificationService @Inject constructor(private val cre
|
|||||||
startReq: KeyVerificationStart,
|
startReq: KeyVerificationStart,
|
||||||
success: (MXUsersDevicesMap<MXDeviceInfo>) -> Unit,
|
success: (MXUsersDevicesMap<MXDeviceInfo>) -> Unit,
|
||||||
error: () -> Unit) {
|
error: () -> Unit) {
|
||||||
|
runCatching {
|
||||||
deviceListManager.downloadKeys(listOf(otherUserId), true)
|
deviceListManager.downloadKeys(listOf(otherUserId), true)
|
||||||
.fold(
|
}.fold(
|
||||||
{ error() },
|
|
||||||
{
|
{
|
||||||
if (it.getUserDeviceIds(otherUserId)?.contains(startReq.fromDevice) == true) {
|
if (it.getUserDeviceIds(otherUserId)?.contains(startReq.fromDevice) == true) {
|
||||||
success(it)
|
success(it)
|
||||||
} else {
|
} else {
|
||||||
error()
|
error()
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
error()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -412,8 +416,9 @@ internal class DefaultSasVerificationService @Inject constructor(private val cre
|
|||||||
val contentMap = MXUsersDevicesMap<Any>()
|
val contentMap = MXUsersDevicesMap<Any>()
|
||||||
contentMap.setObject(userId, userDevice, cancelMessage)
|
contentMap.setObject(userId, userDevice, cancelMessage)
|
||||||
|
|
||||||
sendToDeviceTask.configureWith(SendToDeviceTask.Params(EventType.KEY_VERIFICATION_CANCEL, contentMap, transactionId))
|
sendToDeviceTask
|
||||||
.dispatchTo(object : MatrixCallback<Unit> {
|
.configureWith(SendToDeviceTask.Params(EventType.KEY_VERIFICATION_CANCEL, contentMap, transactionId)) {
|
||||||
|
this.callback = object : MatrixCallback<Unit> {
|
||||||
override fun onSuccess(data: Unit) {
|
override fun onSuccess(data: Unit) {
|
||||||
Timber.v("## SAS verification [$transactionId] canceled for reason ${code.value}")
|
Timber.v("## SAS verification [$transactionId] canceled for reason ${code.value}")
|
||||||
}
|
}
|
||||||
@ -421,7 +426,8 @@ internal class DefaultSasVerificationService @Inject constructor(private val cre
|
|||||||
override fun onFailure(failure: Throwable) {
|
override fun onFailure(failure: Throwable) {
|
||||||
Timber.e(failure, "## SAS verification [$transactionId] failed to cancel.")
|
Timber.e(failure, "## SAS verification [$transactionId] failed to cancel.")
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
}
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -287,8 +287,9 @@ internal abstract class SASVerificationTransaction(
|
|||||||
val contentMap = MXUsersDevicesMap<Any>()
|
val contentMap = MXUsersDevicesMap<Any>()
|
||||||
contentMap.setObject(otherUserId, otherDeviceId, keyToDevice)
|
contentMap.setObject(otherUserId, otherDeviceId, keyToDevice)
|
||||||
|
|
||||||
sendToDeviceTask.configureWith(SendToDeviceTask.Params(type, contentMap, transactionId))
|
sendToDeviceTask
|
||||||
.dispatchTo(object : MatrixCallback<Unit> {
|
.configureWith(SendToDeviceTask.Params(type, contentMap, transactionId)) {
|
||||||
|
this.callback = object : MatrixCallback<Unit> {
|
||||||
override fun onSuccess(data: Unit) {
|
override fun onSuccess(data: Unit) {
|
||||||
Timber.v("## SAS verification [$transactionId] toDevice type '$type' success.")
|
Timber.v("## SAS verification [$transactionId] toDevice type '$type' success.")
|
||||||
if (onDone != null) {
|
if (onDone != null) {
|
||||||
@ -303,7 +304,8 @@ internal abstract class SASVerificationTransaction(
|
|||||||
|
|
||||||
cancel(onErrorReason)
|
cancel(onErrorReason)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
}
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user