forked from GitHub-Mirror/riotX-android
merge develop
This commit is contained in:
parent
4521ea14ee
commit
e50dd265d4
@ -20,12 +20,11 @@ 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.util.JsonDict
|
import im.vector.matrix.android.api.util.JsonDict
|
||||||
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
|
import im.vector.matrix.android.internal.crypto.algorithms.MXDecryptionResult
|
||||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
|
||||||
import kotlin.collections.HashMap
|
|
||||||
|
|
||||||
typealias Content = JsonDict
|
typealias Content = JsonDict
|
||||||
|
|
||||||
@ -79,6 +78,10 @@ data class Event(
|
|||||||
@Json(name = "redacts") val redacts: String? = null
|
@Json(name = "redacts") val redacts: String? = null
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
||||||
|
var mxDecryptionResult: MXDecryptionResult? = null
|
||||||
|
var mCryptoError: MXCryptoError? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if event is a state event.
|
* Check if event is a state event.
|
||||||
* @return true if event is state event.
|
* @return true if event is state event.
|
||||||
@ -91,41 +94,41 @@ data class Event(
|
|||||||
// Crypto
|
// Crypto
|
||||||
//==============================================================================================================
|
//==============================================================================================================
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* For encrypted events, the plaintext payload for the event.
|
// * For encrypted events, the plaintext payload for the event.
|
||||||
* This is a small MXEvent instance with typically value for `type` and 'content' fields.
|
// * This is a small MXEvent instance with typically value for `type` and 'content' fields.
|
||||||
*/
|
// */
|
||||||
@Transient
|
// @Transient
|
||||||
var mClearEvent: Event? = null
|
// var mClearEvent: Event? = null
|
||||||
private set
|
// private set
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* Curve25519 key which we believe belongs to the sender of the event.
|
// * Curve25519 key which we believe belongs to the sender of the event.
|
||||||
* See `senderKey` property.
|
// * See `senderKey` property.
|
||||||
*/
|
// */
|
||||||
@Transient
|
// @Transient
|
||||||
private var mSenderCurve25519Key: String? = null
|
// 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.
|
// * 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.
|
// * See `claimedEd25519Key` property.
|
||||||
*/
|
// */
|
||||||
@Transient
|
// @Transient
|
||||||
private var mClaimedEd25519Key: String? = null
|
// private var mClaimedEd25519Key: String? = null
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* Curve25519 keys of devices involved in telling us about the senderCurve25519Key and claimedEd25519Key.
|
// * Curve25519 keys of devices involved in telling us about the senderCurve25519Key and claimedEd25519Key.
|
||||||
* See `forwardingCurve25519KeyChain` property.
|
// * See `forwardingCurve25519KeyChain` property.
|
||||||
*/
|
// */
|
||||||
@Transient
|
// @Transient
|
||||||
private var mForwardingCurve25519KeyChain: List<String> = ArrayList()
|
// private var mForwardingCurve25519KeyChain: List<String> = ArrayList()
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* Decryption error
|
// * Decryption error
|
||||||
*/
|
// */
|
||||||
@Transient
|
// @Transient
|
||||||
var mCryptoError: MXCryptoError? = null
|
// var mCryptoError: MXCryptoError? = null
|
||||||
private set
|
// private set
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if this event is encrypted.
|
* @return true if this event is encrypted.
|
||||||
@ -140,88 +143,96 @@ data class Event(
|
|||||||
*
|
*
|
||||||
* @param decryptionResult the decryption result, including the plaintext and some key info.
|
* @param decryptionResult the decryption result, including the plaintext and some key info.
|
||||||
*/
|
*/
|
||||||
internal fun setClearData(decryptionResult: MXEventDecryptionResult) {
|
// internal fun setClearData(decryptionResult: MXEventDecryptionResult?) {
|
||||||
mClearEvent = null
|
// mClearEvent = null
|
||||||
mCryptoError = null
|
// if (decryptionResult != null) {
|
||||||
|
// if (decryptionResult.clearEvent != null) {
|
||||||
val adapter = MoshiProvider.providesMoshi().adapter(Event::class.java)
|
// val adapter = MoshiProvider.providesMoshi().adapter(Event::class.java)
|
||||||
mClearEvent = adapter.fromJsonValue(decryptionResult.clearEvent)
|
// mClearEvent = adapter.fromJsonValue(decryptionResult.clearEvent)
|
||||||
|
//
|
||||||
if (mClearEvent != null) {
|
// if (mClearEvent != null) {
|
||||||
mSenderCurve25519Key = decryptionResult.senderCurve25519Key
|
// mSenderCurve25519Key = decryptionResult.senderCurve25519Key
|
||||||
mClaimedEd25519Key = decryptionResult.claimedEd25519Key
|
// mClaimedEd25519Key = decryptionResult.claimedEd25519Key
|
||||||
mForwardingCurve25519KeyChain = decryptionResult.forwardingCurve25519KeyChain
|
// mForwardingCurve25519KeyChain = decryptionResult.forwardingCurve25519KeyChain
|
||||||
|
//
|
||||||
// For encrypted events with relation, the m.relates_to is kept in clear, so we need to put it back
|
// // For encrypted events with relation, the m.relates_to is kept in clear, so we need to put it back
|
||||||
// in the clear event
|
// // in the clear event
|
||||||
try {
|
// try {
|
||||||
content?.get("m.relates_to")?.let { clearRelates ->
|
// content?.get("m.relates_to")?.let { clearRelates ->
|
||||||
mClearEvent = mClearEvent?.copy(
|
// mClearEvent = mClearEvent?.copy(
|
||||||
content = HashMap(mClearEvent!!.content).apply {
|
// content = HashMap(mClearEvent!!.content).apply {
|
||||||
this["m.relates_to"] = clearRelates
|
// this["m.relates_to"] = clearRelates
|
||||||
}
|
// }
|
||||||
)
|
// )
|
||||||
}
|
// }
|
||||||
} catch (e: Exception) {
|
// } catch (e: Exception) {
|
||||||
Timber.e(e, "Unable to restore 'm.relates_to' the clear event")
|
// 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 mClearEvent?.mSenderCurve25519Key ?: mSenderCurve25519Key
|
return mxDecryptionResult?.senderKey
|
||||||
|
// return mClearEvent?.mSenderCurve25519Key ?: mSenderCurve25519Key
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The additional keys the sender of this encrypted event claims to possess.
|
* @return The additional keys the sender of this encrypted event claims to possess.
|
||||||
*/
|
*/
|
||||||
fun getKeysClaimed(): Map<String, String> {
|
fun getKeysClaimed(): Map<String, String> {
|
||||||
val res = HashMap<String, String>()
|
return mxDecryptionResult?.keysClaimed ?: HashMap()
|
||||||
|
// val res = HashMap<String, String>()
|
||||||
val claimedEd25519Key = if (null != mClearEvent) mClearEvent!!.mClaimedEd25519Key else mClaimedEd25519Key
|
//
|
||||||
|
// val claimedEd25519Key = if (null != mClearEvent) mClearEvent!!.mClaimedEd25519Key else mClaimedEd25519Key
|
||||||
if (null != claimedEd25519Key) {
|
//
|
||||||
res["ed25519"] = claimedEd25519Key
|
// if (null != claimedEd25519Key) {
|
||||||
}
|
// res["ed25519"] = claimedEd25519Key
|
||||||
|
// }
|
||||||
return res
|
//
|
||||||
|
// return res
|
||||||
}
|
}
|
||||||
|
//
|
||||||
/**
|
/**
|
||||||
* @return the event type
|
* @return the event type
|
||||||
*/
|
*/
|
||||||
fun getClearType(): String {
|
fun getClearType(): String {
|
||||||
return mClearEvent?.type ?: type
|
return mxDecryptionResult?.payload?.get("type")?.toString()
|
||||||
|
?: type//get("type")?.toString() ?: type
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the event content
|
* @return the event content
|
||||||
*/
|
*/
|
||||||
fun getClearContent(): Content? {
|
fun getClearContent(): Content? {
|
||||||
return mClearEvent?.content ?: content
|
return mxDecryptionResult?.payload?.get("content") as? Content ?: content
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* @return the linked crypto error
|
// * @return the linked crypto error
|
||||||
*/
|
// */
|
||||||
fun getCryptoError(): MXCryptoError? {
|
// fun getCryptoError(): MXCryptoError? {
|
||||||
return mCryptoError
|
// return mCryptoError
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* Update the linked crypto error
|
// * Update the linked crypto error
|
||||||
*
|
// *
|
||||||
* @param error the new crypto error.
|
// * @param error the new crypto error.
|
||||||
*/
|
// */
|
||||||
fun setCryptoError(error: MXCryptoError?) {
|
// fun setCryptoError(error: MXCryptoError?) {
|
||||||
mCryptoError = error
|
// mCryptoError = error
|
||||||
if (null != error) {
|
// if (null != error) {
|
||||||
mClearEvent = null
|
// mClearEvent = null
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells if the event is redacted
|
* Tells if the event is redacted
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.api.session.room.timeline
|
package im.vector.matrix.android.api.session.room.timeline
|
||||||
|
|
||||||
|
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.api.session.events.model.EventType
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
|
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
|
||||||
@ -34,7 +35,7 @@ data class TimelineEvent(
|
|||||||
val isUniqueDisplayName: Boolean,
|
val isUniqueDisplayName: Boolean,
|
||||||
val senderAvatar: String?,
|
val senderAvatar: String?,
|
||||||
val sendState: SendState,
|
val sendState: SendState,
|
||||||
val hasClearEventFlag: Boolean = false,
|
// val hasClearEventFlag: Boolean = false,
|
||||||
val annotations: EventAnnotationsSummary? = null
|
val annotations: EventAnnotationsSummary? = null
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
@ -17,10 +17,13 @@
|
|||||||
package im.vector.matrix.android.internal.database.mapper
|
package im.vector.matrix.android.internal.database.mapper
|
||||||
|
|
||||||
import com.squareup.moshi.JsonDataException
|
import com.squareup.moshi.JsonDataException
|
||||||
|
import im.vector.matrix.android.api.session.crypto.MXCryptoError
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.events.model.UnsignedData
|
import im.vector.matrix.android.api.session.events.model.UnsignedData
|
||||||
|
import im.vector.matrix.android.internal.crypto.algorithms.MXDecryptionResult
|
||||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||||
|
import timber.log.Timber
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
internal object EventMapper {
|
internal object EventMapper {
|
||||||
@ -46,7 +49,6 @@ internal object EventMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun map(eventEntity: EventEntity): Event {
|
fun map(eventEntity: EventEntity): Event {
|
||||||
//TODO proxy the event to only parse unsigned data when accessed?
|
|
||||||
val ud = if (eventEntity.unsignedData.isNullOrBlank()) {
|
val ud = if (eventEntity.unsignedData.isNullOrBlank()) {
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
@ -68,7 +70,17 @@ internal object EventMapper {
|
|||||||
roomId = eventEntity.roomId,
|
roomId = eventEntity.roomId,
|
||||||
unsignedData = ud,
|
unsignedData = ud,
|
||||||
redacts = eventEntity.redacts
|
redacts = eventEntity.redacts
|
||||||
)
|
).also {
|
||||||
|
eventEntity.decryptionResultJson?.let { json ->
|
||||||
|
try {
|
||||||
|
it.mxDecryptionResult = MoshiProvider.providesMoshi().adapter(MXDecryptionResult::class.java).fromJson(json)
|
||||||
|
} catch (t: JsonDataException) {
|
||||||
|
Timber.e(t, "Failed to parse decryption result")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//TODO get the full crypto error object
|
||||||
|
it.mCryptoError = eventEntity.decryptionErrorCode?.let { MXCryptoError(it, it) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -16,10 +16,12 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.database.mapper
|
package im.vector.matrix.android.internal.database.mapper
|
||||||
|
|
||||||
|
import com.squareup.moshi.Types
|
||||||
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.send.SendState
|
import im.vector.matrix.android.api.session.room.send.SendState
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
||||||
|
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||||
|
|
||||||
internal object TimelineEventMapper {
|
internal object TimelineEventMapper {
|
||||||
|
|
||||||
@ -33,17 +35,18 @@ internal object TimelineEventMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun map(timelineEventEntity: TimelineEventEntity): TimelineEvent {
|
fun map(timelineEventEntity: TimelineEventEntity): TimelineEvent {
|
||||||
|
val listMapper = MoshiProvider.providesMoshi().adapter<List<String>>(Types.newParameterizedType(List::class.java, String::class.java))
|
||||||
|
|
||||||
return TimelineEvent(
|
return TimelineEvent(
|
||||||
root = timelineEventEntity.root?.asDomain()
|
root = timelineEventEntity.root?.asDomain()
|
||||||
?: Event("", timelineEventEntity.eventId),
|
?: Event("", timelineEventEntity.eventId),
|
||||||
annotations = timelineEventEntity.annotations?.asDomain(),
|
annotations = timelineEventEntity.annotations?.asDomain(),
|
||||||
localId = timelineEventEntity.localId,
|
localId = timelineEventEntity.localId,
|
||||||
displayIndex = timelineEventEntity.root?.displayIndex ?: 0,
|
displayIndex = timelineEventEntity.root?.displayIndex ?: 0,
|
||||||
senderName = timelineEventEntity.senderName,
|
senderName = timelineEventEntity.senderName,
|
||||||
isUniqueDisplayName = timelineEventEntity.isUniqueDisplayName,
|
isUniqueDisplayName = timelineEventEntity.isUniqueDisplayName,
|
||||||
senderAvatar = timelineEventEntity.senderAvatar,
|
senderAvatar = timelineEventEntity.senderAvatar,
|
||||||
sendState = timelineEventEntity.root?.sendState ?: SendState.UNKNOWN,
|
sendState = timelineEventEntity.root?.sendState ?: SendState.UNKNOWN
|
||||||
hasClearEventFlag = false
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,9 @@
|
|||||||
package im.vector.matrix.android.internal.database.model
|
package im.vector.matrix.android.internal.database.model
|
||||||
|
|
||||||
import im.vector.matrix.android.api.session.room.send.SendState
|
import im.vector.matrix.android.api.session.room.send.SendState
|
||||||
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
|
||||||
|
import im.vector.matrix.android.internal.crypto.algorithms.MXDecryptionResult
|
||||||
|
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||||
import io.realm.RealmObject
|
import io.realm.RealmObject
|
||||||
import io.realm.RealmResults
|
import io.realm.RealmResults
|
||||||
import io.realm.annotations.Index
|
import io.realm.annotations.Index
|
||||||
@ -39,7 +41,9 @@ internal open class EventEntity(@PrimaryKey var localId: String = UUID.randomUUI
|
|||||||
var redacts: String? = null,
|
var redacts: String? = null,
|
||||||
@Index var stateIndex: Int = 0,
|
@Index var stateIndex: Int = 0,
|
||||||
@Index var displayIndex: Int = 0,
|
@Index var displayIndex: Int = 0,
|
||||||
@Index var isUnlinked: Boolean = false
|
@Index var isUnlinked: Boolean = false,
|
||||||
|
var decryptionResultJson: String? = null,
|
||||||
|
var decryptionErrorCode: String? = null
|
||||||
) : RealmObject() {
|
) : RealmObject() {
|
||||||
|
|
||||||
enum class LinkFilterMode {
|
enum class LinkFilterMode {
|
||||||
@ -68,4 +72,14 @@ internal open class EventEntity(@PrimaryKey var localId: String = UUID.randomUUI
|
|||||||
val timelineEventEntity: RealmResults<TimelineEventEntity>? = null
|
val timelineEventEntity: RealmResults<TimelineEventEntity>? = null
|
||||||
|
|
||||||
|
|
||||||
|
fun setDecryptionResult(result: MXEventDecryptionResult) {
|
||||||
|
val decryptionResult = MXDecryptionResult(
|
||||||
|
payload = result.clearEvent,
|
||||||
|
senderKey = result.senderCurve25519Key,
|
||||||
|
keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) },
|
||||||
|
forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain
|
||||||
|
)
|
||||||
|
val adapter = MoshiProvider.providesMoshi().adapter<MXDecryptionResult>(MXDecryptionResult::class.java)
|
||||||
|
decryptionResultJson = adapter.toJson(decryptionResult)
|
||||||
|
}
|
||||||
}
|
}
|
@ -16,6 +16,10 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.database.model
|
package im.vector.matrix.android.internal.database.model
|
||||||
|
|
||||||
|
import com.squareup.moshi.Types
|
||||||
|
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
|
||||||
|
import im.vector.matrix.android.internal.database.mapper.ContentMapper
|
||||||
|
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||||
import io.realm.RealmObject
|
import io.realm.RealmObject
|
||||||
import io.realm.RealmResults
|
import io.realm.RealmResults
|
||||||
import io.realm.annotations.Index
|
import io.realm.annotations.Index
|
||||||
|
@ -19,19 +19,15 @@ package im.vector.matrix.android.internal.session.room.timeline
|
|||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||||
import im.vector.matrix.android.api.session.events.model.EventType
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
import im.vector.matrix.android.api.session.events.model.toModel
|
|
||||||
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
import im.vector.matrix.android.api.util.CancelableBag
|
import im.vector.matrix.android.api.util.CancelableBag
|
||||||
|
import im.vector.matrix.android.api.util.addTo
|
||||||
import im.vector.matrix.android.internal.crypto.NewSessionListener
|
import im.vector.matrix.android.internal.crypto.NewSessionListener
|
||||||
import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
|
import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
|
||||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||||
import im.vector.matrix.android.internal.database.model.*
|
import im.vector.matrix.android.internal.database.model.*
|
||||||
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
|
||||||
import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntity
|
|
||||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||||
import im.vector.matrix.android.internal.database.model.RoomEntity
|
|
||||||
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
|
||||||
import im.vector.matrix.android.internal.database.query.findIncludingEvent
|
import im.vector.matrix.android.internal.database.query.findIncludingEvent
|
||||||
import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom
|
import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
@ -41,13 +37,7 @@ import im.vector.matrix.android.internal.task.configureWith
|
|||||||
import im.vector.matrix.android.internal.util.Debouncer
|
import im.vector.matrix.android.internal.util.Debouncer
|
||||||
import im.vector.matrix.android.internal.util.createBackgroundHandler
|
import im.vector.matrix.android.internal.util.createBackgroundHandler
|
||||||
import im.vector.matrix.android.internal.util.createUIHandler
|
import im.vector.matrix.android.internal.util.createUIHandler
|
||||||
import io.realm.OrderedCollectionChangeSet
|
import io.realm.*
|
||||||
import io.realm.OrderedRealmCollectionChangeListener
|
|
||||||
import io.realm.Realm
|
|
||||||
import io.realm.RealmConfiguration
|
|
||||||
import io.realm.RealmQuery
|
|
||||||
import io.realm.RealmResults
|
|
||||||
import io.realm.Sort
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
@ -67,7 +57,7 @@ internal class DefaultTimeline(
|
|||||||
private val taskExecutor: TaskExecutor,
|
private val taskExecutor: TaskExecutor,
|
||||||
private val contextOfEventTask: GetContextOfEventTask,
|
private val contextOfEventTask: GetContextOfEventTask,
|
||||||
private val paginationTask: PaginationTask,
|
private val paginationTask: PaginationTask,
|
||||||
private val cryptoService: CryptoService,
|
cryptoService: CryptoService,
|
||||||
private val allowedTypes: List<String>?
|
private val allowedTypes: List<String>?
|
||||||
) : Timeline {
|
) : Timeline {
|
||||||
|
|
||||||
@ -102,8 +92,12 @@ internal class DefaultTimeline(
|
|||||||
private val forwardsPaginationState = AtomicReference(PaginationState())
|
private val forwardsPaginationState = AtomicReference(PaginationState())
|
||||||
|
|
||||||
|
|
||||||
|
private val timelineID = UUID.randomUUID().toString()
|
||||||
|
|
||||||
private lateinit var eventRelations: RealmResults<EventAnnotationsSummaryEntity>
|
private lateinit var eventRelations: RealmResults<EventAnnotationsSummaryEntity>
|
||||||
|
|
||||||
|
private val eventDecryptor = TimelineEventDecryptor(realmConfiguration, timelineID, cryptoService)
|
||||||
|
|
||||||
private val eventsChangeListener = OrderedRealmCollectionChangeListener<RealmResults<TimelineEventEntity>> { results, changeSet ->
|
private val eventsChangeListener = OrderedRealmCollectionChangeListener<RealmResults<TimelineEventEntity>> { results, changeSet ->
|
||||||
if (changeSet.state == OrderedCollectionChangeSet.State.INITIAL) {
|
if (changeSet.state == OrderedCollectionChangeSet.State.INITIAL) {
|
||||||
handleInitialLoad()
|
handleInitialLoad()
|
||||||
@ -172,32 +166,32 @@ internal class DefaultTimeline(
|
|||||||
postSnapshot()
|
postSnapshot()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val newSessionListener = object : NewSessionListener {
|
// private val newSessionListener = object : NewSessionListener {
|
||||||
override fun onNewSession(roomId: String?, senderKey: String, sessionId: String) {
|
// override fun onNewSession(roomId: String?, senderKey: String, sessionId: String) {
|
||||||
if (roomId == this@DefaultTimeline.roomId) {
|
// if (roomId == this@DefaultTimeline.roomId) {
|
||||||
Timber.v("New session id detected for this room")
|
// Timber.v("New session id detected for this room")
|
||||||
BACKGROUND_HANDLER.post {
|
// BACKGROUND_HANDLER.post {
|
||||||
val realm = backgroundRealm.get()
|
// val realm = backgroundRealm.get()
|
||||||
var hasChange = false
|
// var hasChange = false
|
||||||
builtEvents.forEachIndexed { index, timelineEvent ->
|
// builtEvents.forEachIndexed { index, timelineEvent ->
|
||||||
if (timelineEvent.isEncrypted()) {
|
// if (timelineEvent.isEncrypted()) {
|
||||||
val eventContent = timelineEvent.root.content.toModel<EncryptedEventContent>()
|
// val eventContent = timelineEvent.root.content.toModel<EncryptedEventContent>()
|
||||||
if (eventContent?.sessionId == sessionId
|
// if (eventContent?.sessionId == sessionId
|
||||||
&& (timelineEvent.root.mClearEvent == null || timelineEvent.root.mCryptoError != null)) {
|
// && (timelineEvent.root.mClearEvent == null || timelineEvent.root.mCryptoError != null)) {
|
||||||
//we need to rebuild this event
|
// //we need to rebuild this event
|
||||||
EventEntity.where(realm, eventId = timelineEvent.root.eventId!!).findFirst()?.let {
|
// EventEntity.where(realm, eventId = timelineEvent.root.eventId!!).findFirst()?.let {
|
||||||
//builtEvents[index] = timelineEventFactory.create(it, realm)
|
// //builtEvents[index] = timelineEventFactory.create(it, realm)
|
||||||
hasChange = true
|
// hasChange = true
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
if (hasChange) postSnapshot()
|
// if (hasChange) postSnapshot()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Public methods ******************************************************************************
|
// Public methods ******************************************************************************
|
||||||
|
|
||||||
@ -219,7 +213,7 @@ internal class DefaultTimeline(
|
|||||||
override fun start() {
|
override fun start() {
|
||||||
if (isStarted.compareAndSet(false, true)) {
|
if (isStarted.compareAndSet(false, true)) {
|
||||||
Timber.v("Start timeline for roomId: $roomId and eventId: $initialEventId")
|
Timber.v("Start timeline for roomId: $roomId and eventId: $initialEventId")
|
||||||
cryptoService.addNewSessionListener(newSessionListener)
|
eventDecryptor.start()
|
||||||
BACKGROUND_HANDLER.post {
|
BACKGROUND_HANDLER.post {
|
||||||
val realm = Realm.getInstance(realmConfiguration)
|
val realm = Realm.getInstance(realmConfiguration)
|
||||||
backgroundRealm.set(realm)
|
backgroundRealm.set(realm)
|
||||||
@ -247,7 +241,7 @@ internal class DefaultTimeline(
|
|||||||
|
|
||||||
override fun dispose() {
|
override fun dispose() {
|
||||||
if (isStarted.compareAndSet(true, false)) {
|
if (isStarted.compareAndSet(true, false)) {
|
||||||
cryptoService.removeSessionListener(newSessionListener)
|
eventDecryptor.destroy()
|
||||||
Timber.v("Dispose timeline for roomId: $roomId and eventId: $initialEventId")
|
Timber.v("Dispose timeline for roomId: $roomId and eventId: $initialEventId")
|
||||||
BACKGROUND_HANDLER.post {
|
BACKGROUND_HANDLER.post {
|
||||||
cancelableBag.cancel()
|
cancelableBag.cancel()
|
||||||
@ -387,9 +381,9 @@ internal class DefaultTimeline(
|
|||||||
private fun executePaginationTask(direction: Timeline.Direction, limit: Int) {
|
private fun executePaginationTask(direction: Timeline.Direction, limit: Int) {
|
||||||
val token = getTokenLive(direction) ?: return
|
val token = getTokenLive(direction) ?: return
|
||||||
val params = PaginationTask.Params(roomId = roomId,
|
val params = PaginationTask.Params(roomId = roomId,
|
||||||
from = token,
|
from = token,
|
||||||
direction = direction.toPaginationDirection(),
|
direction = direction.toPaginationDirection(),
|
||||||
limit = limit)
|
limit = limit)
|
||||||
|
|
||||||
Timber.v("Should fetch $limit items $direction")
|
Timber.v("Should fetch $limit items $direction")
|
||||||
cancelableBag += paginationTask.configureWith(params)
|
cancelableBag += paginationTask.configureWith(params)
|
||||||
@ -453,6 +447,12 @@ internal class DefaultTimeline(
|
|||||||
}
|
}
|
||||||
offsetResults.forEach { eventEntity ->
|
offsetResults.forEach { eventEntity ->
|
||||||
val timelineEvent = eventEntity.asDomain()
|
val timelineEvent = eventEntity.asDomain()
|
||||||
|
|
||||||
|
if (timelineEvent.isEncrypted()
|
||||||
|
&& timelineEvent.root.mxDecryptionResult == null) {
|
||||||
|
timelineEvent.root.eventId?.let { eventDecryptor.requestDecryption(it) }
|
||||||
|
}
|
||||||
|
|
||||||
val position = if (direction == Timeline.Direction.FORWARDS) 0 else builtEvents.size
|
val position = if (direction == Timeline.Direction.FORWARDS) 0 else builtEvents.size
|
||||||
builtEvents.add(position, timelineEvent)
|
builtEvents.add(position, timelineEvent)
|
||||||
//Need to shift :/
|
//Need to shift :/
|
||||||
|
@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package im.vector.matrix.android.internal.session.room.timeline
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||||
|
import im.vector.matrix.android.api.session.crypto.MXCryptoError
|
||||||
|
import im.vector.matrix.android.api.session.events.model.toModel
|
||||||
|
import im.vector.matrix.android.internal.crypto.MXDecryptionException
|
||||||
|
import im.vector.matrix.android.internal.crypto.NewSessionListener
|
||||||
|
import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
|
||||||
|
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||||
|
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||||
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
|
import io.realm.Realm
|
||||||
|
import io.realm.RealmConfiguration
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
|
|
||||||
|
internal class TimelineEventDecryptor(
|
||||||
|
private val realmConfiguration: RealmConfiguration,
|
||||||
|
private val timelineId: String,
|
||||||
|
private val cryptoService: CryptoService
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val newSessionListener = object : NewSessionListener {
|
||||||
|
override fun onNewSession(roomId: String?, senderKey: String, sessionId: String) {
|
||||||
|
synchronized(unknownSessionsFailure) {
|
||||||
|
unknownSessionsFailure[sessionId]?.let { eventIds ->
|
||||||
|
eventIds.forEach {
|
||||||
|
requestDecryption(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unknownSessionsFailure[sessionId]?.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private val executor = Executors.newSingleThreadExecutor()
|
||||||
|
|
||||||
|
private val existingRequests = HashSet<String>()
|
||||||
|
private val unknownSessionsFailure = HashMap<String, MutableList<String>>()
|
||||||
|
|
||||||
|
fun start() {
|
||||||
|
cryptoService.addNewSessionListener(newSessionListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun destroy() {
|
||||||
|
cryptoService.removeSessionListener(newSessionListener)
|
||||||
|
executor.shutdownNow()
|
||||||
|
unknownSessionsFailure.clear()
|
||||||
|
existingRequests.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun requestDecryption(eventId: String) {
|
||||||
|
synchronized(existingRequests) {
|
||||||
|
if (existingRequests.contains(eventId)) {
|
||||||
|
return Unit.also {
|
||||||
|
Timber.d("Skip Decryption request for event ${eventId}, already requested")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
existingRequests.add(eventId)
|
||||||
|
}
|
||||||
|
synchronized(unknownSessionsFailure) {
|
||||||
|
unknownSessionsFailure.values.forEach {
|
||||||
|
if (it.contains(eventId)) return@synchronized Unit.also {
|
||||||
|
Timber.d("Skip Decryption request for event ${eventId}, unknown session")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
executor.execute {
|
||||||
|
Realm.getInstance(realmConfiguration).use { realm ->
|
||||||
|
realm.executeTransaction {
|
||||||
|
processDecryptRequest(eventId, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processDecryptRequest(eventId: String, realm: Realm) {
|
||||||
|
Timber.v("Decryption request for event ${eventId}")
|
||||||
|
val eventEntity = EventEntity.where(realm, eventId = eventId).findFirst()
|
||||||
|
?: return Unit.also {
|
||||||
|
Timber.d("Decryption request for unknown message")
|
||||||
|
}
|
||||||
|
val event = eventEntity.asDomain()
|
||||||
|
try {
|
||||||
|
val result = cryptoService.decryptEvent(event, timelineId)
|
||||||
|
if (result == null) {
|
||||||
|
Timber.e("Null decryption result for event ${eventId}")
|
||||||
|
} else {
|
||||||
|
Timber.v("Successfully decrypted event ${eventId}")
|
||||||
|
eventEntity.setDecryptionResult(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e: MXDecryptionException) {
|
||||||
|
if (e.cryptoError?.code == MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE) {
|
||||||
|
//Keep track of unknown sessions to automatically try to decrypt on new session
|
||||||
|
event.content?.toModel<EncryptedEventContent>()?.let { content ->
|
||||||
|
content.sessionId?.let { sessionId ->
|
||||||
|
synchronized(unknownSessionsFailure) {
|
||||||
|
val list = unknownSessionsFailure[sessionId]
|
||||||
|
?: ArrayList<String>().also {
|
||||||
|
unknownSessionsFailure[sessionId] = it
|
||||||
|
}
|
||||||
|
list.add(eventId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
Timber.e(t, "Failed to decrypt event $eventId")
|
||||||
|
} finally {
|
||||||
|
synchronized(existingRequests) {
|
||||||
|
existingRequests.remove(eventId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -24,6 +24,7 @@ import im.vector.matrix.android.api.session.events.model.toModel
|
|||||||
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.internal.crypto.CryptoManager
|
import im.vector.matrix.android.internal.crypto.CryptoManager
|
||||||
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
|
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
|
||||||
|
import im.vector.matrix.android.internal.crypto.algorithms.MXDecryptionResult
|
||||||
import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService
|
import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService
|
||||||
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.session.sync.model.ToDeviceSyncResponse
|
import im.vector.matrix.android.internal.session.sync.model.ToDeviceSyncResponse
|
||||||
@ -39,7 +40,7 @@ internal class CryptoSyncHandler @Inject constructor(private val cryptoManager:
|
|||||||
// Decrypt event if necessary
|
// Decrypt event if necessary
|
||||||
decryptEvent(event, null)
|
decryptEvent(event, null)
|
||||||
if (TextUtils.equals(event.getClearType(), EventType.MESSAGE)
|
if (TextUtils.equals(event.getClearType(), EventType.MESSAGE)
|
||||||
&& event.mClearEvent?.content?.toModel<MessageContent>()?.type == "m.bad.encrypted") {
|
&& event.getClearContent()?.toModel<MessageContent>()?.type == "m.bad.encrypted") {
|
||||||
Timber.e("## handleToDeviceEvent() : Warning: Unable to decrypt to-device event : " + event.content)
|
Timber.e("## handleToDeviceEvent() : Warning: Unable to decrypt to-device event : " + event.content)
|
||||||
} else {
|
} else {
|
||||||
sasVerificationService.onToDeviceEvent(event)
|
sasVerificationService.onToDeviceEvent(event)
|
||||||
@ -70,7 +71,18 @@ internal class CryptoSyncHandler @Inject constructor(private val cryptoManager:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (null != result) {
|
if (null != result) {
|
||||||
event.setClearData(result)
|
// event.mxDecryptionResult = MXDecryptionResult(
|
||||||
|
// payload = result.clearEvent,
|
||||||
|
// keysClaimed = map
|
||||||
|
// )
|
||||||
|
//TODO persist that?
|
||||||
|
event.mxDecryptionResult = MXDecryptionResult(
|
||||||
|
payload = result.clearEvent,
|
||||||
|
senderKey = result.senderCurve25519Key,
|
||||||
|
keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) },
|
||||||
|
forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain
|
||||||
|
)
|
||||||
|
// event.setClearData(result)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,7 +176,7 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M
|
|||||||
|
|
||||||
this.add(SimpleAction(VIEW_SOURCE, R.string.view_source, R.drawable.ic_view_source, JSONObject(event.root.toContent()).toString(4)))
|
this.add(SimpleAction(VIEW_SOURCE, R.string.view_source, R.drawable.ic_view_source, JSONObject(event.root.toContent()).toString(4)))
|
||||||
if (event.isEncrypted()) {
|
if (event.isEncrypted()) {
|
||||||
val decryptedContent = event.root.mClearEvent.toContent()?.let {
|
val decryptedContent = event.root.getClearContent()?.let {
|
||||||
JSONObject(it).toString(4)
|
JSONObject(it).toString(4)
|
||||||
} ?: stringProvider.getString(R.string.encryption_information_decryption_error)
|
} ?: stringProvider.getString(R.string.encryption_information_decryption_error)
|
||||||
this.add(SimpleAction(VIEW_DECRYPTED_SOURCE, R.string.view_decrypted_source, R.drawable.ic_view_source, decryptedContent))
|
this.add(SimpleAction(VIEW_DECRYPTED_SOURCE, R.string.view_decrypted_source, R.drawable.ic_view_source, decryptedContent))
|
||||||
|
Loading…
Reference in New Issue
Block a user