Skip to content

Commit

Permalink
Merge branch 'release/0.24.3/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
gulekismail committed Nov 15, 2022
2 parents 8b23c97 + 1cfbb06 commit 895fbd2
Show file tree
Hide file tree
Showing 61 changed files with 2,774 additions and 561 deletions.
27 changes: 27 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
## Changes in 0.24.3 (2022-11-15)

✨ Features

- Threads: added support to read receipts (MSC3771) ([#6663](https://github.com/vector-im/element-ios/issues/6663))
- Threads: added support to notifications count (MSC3773) ([#6664](https://github.com/vector-im/element-ios/issues/6664))
- Threads: added support to labs flag for read receipts ([#7029](https://github.com/vector-im/element-ios/issues/7029))
- Threads: notification count in main timeline including un participated threads ([#7038](https://github.com/vector-im/element-ios/issues/7038))

🙌 Improvements

- CryptoV2: Room event decryption ([#1627](https://github.com/matrix-org/matrix-ios-sdk/pull/1627))
- CryptoV2: Bugfixes ([#1630](https://github.com/matrix-org/matrix-ios-sdk/pull/1630))
- CryptoV2: Log decryption errors separately ([#1632](https://github.com/matrix-org/matrix-ios-sdk/pull/1632))
- Adds the sending of read receipts for poll start/end events ([#1633](https://github.com/matrix-org/matrix-ios-sdk/pull/1633))

🐛 Bugfixes

- Tests: Fix or disable flakey integration tests ([#1628](https://github.com/matrix-org/matrix-ios-sdk/pull/1628))
- Threads: removed "unread_thread_notifications" from sync filters for server that doesn't support MSC3773 ([#7066](https://github.com/vector-im/element-ios/issues/7066))
- Threads: Display number of unread messages above threads button ([#7076](https://github.com/vector-im/element-ios/issues/7076))

📄 Documentation

- Doc: Update the synapse installation section with poetry usage ([#1625](https://github.com/matrix-org/matrix-ios-sdk/pull/1625))


## Changes in 0.24.2 (2022-11-01)

🙌 Improvements
Expand Down
2 changes: 1 addition & 1 deletion MatrixSDK.podspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|

s.name = "MatrixSDK"
s.version = "0.24.2"
s.version = "0.24.3"
s.summary = "The iOS SDK to build apps compatible with Matrix (https://www.matrix.org)"

s.description = <<-DESC
Expand Down
52 changes: 52 additions & 0 deletions MatrixSDK.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

12 changes: 10 additions & 2 deletions MatrixSDK/Background/MXBackgroundStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ class MXBackgroundStore: NSObject, MXStore {
return nil
}

func getEventReceipts(_ roomId: String, eventId: String, sorted sort: Bool, completion: @escaping ([MXReceiptData]) -> Void) {
func getEventReceipts(_ roomId: String, eventId: String, threadId: String, sorted sort: Bool, completion: @escaping ([MXReceiptData]) -> Void) {
DispatchQueue.main.async {
completion([])
}
Expand All @@ -218,10 +218,14 @@ class MXBackgroundStore: NSObject, MXStore {
return false
}

func getReceiptInRoom(_ roomId: String, forUserId userId: String) -> MXReceiptData? {
func getReceiptInRoom(_ roomId: String, threadId: String, forUserId userId: String) -> MXReceiptData? {
return nil
}

func getReceiptsInRoom(_ roomId: String, forUserId userId: String) -> [String: MXReceiptData] {
return [:]
}

func loadReceipts(forRoom roomId: String, completion: (() -> Void)? = nil) {
DispatchQueue.main.async {
completion?()
Expand All @@ -231,6 +235,10 @@ class MXBackgroundStore: NSObject, MXStore {
func localUnreadEventCount(_ roomId: String, threadId: String?, withTypeIn types: [Any]?) -> UInt {
return 0
}

func localUnreadEventCountPerThread(_ roomId: String, withTypeIn types: [Any]?) -> [String : NSNumber]! {
return [:]
}

func newIncomingEvents(inRoom roomId: String, threadId: String?, withTypeIn types: [String]?) -> [MXEvent] {
return []
Expand Down
5 changes: 3 additions & 2 deletions MatrixSDK/Contrib/Swift/MXRestClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1730,13 +1730,14 @@ public extension MXRestClient {
- parameters:
- roomId: the id of the room.
- eventId: the id of the event.
- threadId: the id of the thread (`nil` for unthreaded RR)
- completion: A block object called when the operation completes.
- response: Indicates whether the operation was successful.
- returns: a `MXHTTPOperation` instance.
*/
@nonobjc @discardableResult func sendReadReceipt(toRoom roomId: String, forEvent eventId: String, completion: @escaping (_ response: MXResponse<Void>) -> Void) -> MXHTTPOperation {
return __sendReadReceipt(roomId, eventId: eventId, success: currySuccess(completion), failure: curryFailure(completion))
@nonobjc @discardableResult func sendReadReceipt(toRoom roomId: String, forEvent eventId: String, threadId: String?, completion: @escaping (_ response: MXResponse<Void>) -> Void) -> MXHTTPOperation {
return __sendReadReceipt(roomId, eventId: eventId, threadId: threadId, success: currySuccess(completion), failure: curryFailure(completion))
}


Expand Down
271 changes: 271 additions & 0 deletions MatrixSDK/Crypto/Algorithms/RoomEvent/MXRoomEventDecryption.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
//
// Copyright 2022 The Matrix.org Foundation C.I.C
//
// 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.
//

import Foundation

#if DEBUG
import MatrixSDKCrypto

/// Object responsible for decrypting room events and dealing with undecryptable events
protocol MXRoomEventDecrypting: Actor {

/// Decrypt a list of events
func decrypt(events: [MXEvent]) -> [MXEventDecryptionResult]

/// Process an event that may contain room key and retry decryption if it does
///
/// Note: room key could be contained in `m.room_key` or `m.forwarded_room_key`
func handlePossibleRoomKeyEvent(_ event: MXEvent)

/// Retry decrypting events with specific session ids
///
/// Note: this may be useful if we have just imported keys from backup / file
func retryUndecryptedEvents(sessionIds: [String])

/// Reset the store of undecrypted events
func resetUndecryptedEvents()
}

/// Implementation of `MXRoomEventDecrypting` as an Actor
actor MXRoomEventDecryption: MXRoomEventDecrypting {
typealias SessionId = String
typealias EventId = String

// `Megolm` error does not currently expose the type of "missing keys" error, so have to match against
// hardcoded non-localized error message. Will be changed in future PR
private static let MissingKeysMessage = "decryption failed because the room key is missing"

private let handler: MXCryptoRoomEventDecrypting
private var undecryptedEvents: [SessionId: [EventId: MXEvent]]
private let log = MXNamedLog(name: "MXRoomEventDecryption")

init(handler: MXCryptoRoomEventDecrypting) {
self.handler = handler
self.undecryptedEvents = [:]
}

func decrypt(events: [MXEvent]) -> [MXEventDecryptionResult] {
let results = events.map(decrypt(event:))

let undecrypted = results.filter {
$0.clearEvent == nil || $0.error != nil
}

if !undecrypted.isEmpty {
log.error("Unable to decrypt some event(s)", context: [
"total": events.count,
"undecrypted": undecrypted.count
])
} else {
log.debug("Decrypted all \(events.count) event(s)")
}

return results
}

func handlePossibleRoomKeyEvent(_ event: MXEvent) {
guard let sessionId = roomKeySessionId(for: event) else {
return
}

log.debug("Recieved a new room key as `\(event.type ?? "")` for session \(sessionId)")
let events = undecryptedEvents[sessionId]?.map(\.value) ?? []
retryDecryption(events: events)
}

func retryUndecryptedEvents(sessionIds: [String]) {
let events = sessionIds
.flatMap {
undecryptedEvents[$0]?.map {
$0.value
} ?? []
}
retryDecryption(events: events)
}

func resetUndecryptedEvents() {
undecryptedEvents = [:]
}

// MARK: - Private

private func decrypt(event: MXEvent) -> MXEventDecryptionResult {
guard
event.isEncrypted && event.clear == nil,
event.content?["algorithm"] as? String == kMXCryptoMegolmAlgorithm,
let sessionId = sessionId(for: event)
else {
log.debug("Ignoring unencrypted or non-room event")

let result = MXEventDecryptionResult()
result.clearEvent = event.clear?.jsonDictionary()
return result
}

do {
let decryptedEvent = try handler.decryptRoomEvent(event)
let result = try MXEventDecryptionResult(event: decryptedEvent)
log.debug("Successfully decrypted event `\(result.clearEvent["type"] ?? "unknown")`")
return result

} catch let error as DecryptionError {
return handleDecryptionError(for: event, sessionId: sessionId, error: error)
} catch {
return handleGenericError(for: event, sessionId: sessionId, error: error)
}
}

private func addUndecryptedEvent(_ event: MXEvent) {
guard let sessionId = sessionId(for: event) else {
return
}

var events = undecryptedEvents[sessionId] ?? [:]
events[event.eventId] = event
undecryptedEvents[sessionId] = events
}

private func removeUndecryptedEvent(_ event: MXEvent) {
guard let sessionId = sessionId(for: event) else {
return
}
undecryptedEvents[sessionId]?[event.eventId] = nil
}

private func retryDecryption(events: [MXEvent]) {
guard !events.isEmpty else {
return
}

log.debug("Re-decrypting \(events.count) event(s)")

var results = [(MXEvent, MXEventDecryptionResult)]()
for event in events {
guard event.clear == nil else {
removeUndecryptedEvent(event)
continue
}

let result = decrypt(event: event)
guard result.clearEvent != nil else {
log.error("Event still not decryptable", context: [
"event_id": event.eventId ?? "unknown",
"session_id": sessionId(for: event),
"error": result.error.localizedDescription
])
continue
}

removeUndecryptedEvent(event)
results.append((event, result))
}

Task { [results] in
await MainActor.run {
for (event, result) in results {
event.setClearData(result)
}
}
}
}

private func roomKeySessionId(for event: MXEvent) -> String? {
if event.eventType == .roomKey, let content = MXRoomKeyEventContent(fromJSON: event.content) {
return content.sessionId
} else if event.eventType == .roomForwardedKey, let content = MXForwardedRoomKeyEventContent(fromJSON: event.content) {
return content.sessionId
} else {
return nil
}
}

private func sessionId(for event: MXEvent) -> String? {
let sessionId = event.content["session_id"] ?? event.wireContent["session_id"]
guard let sessionId = sessionId as? String else {
log.failure("Event is missing session id")
return nil
}
return sessionId
}

// MARK: - Error handling

private func handleDecryptionError(for event: MXEvent, sessionId: String, error: DecryptionError) -> MXEventDecryptionResult {
switch error {
case .Identifier(let message):
log.error("Failed to decrypt event due to identifier", context: [
"session_id": sessionId,
"message": message,
"error": error
])
return trackedDecryptionResult(for: event, error: error)

case .Serialization(let message):
log.error("Failed to decrypt event due to serialization", context: [
"session_id": sessionId,
"message": message,
"error": error
])
return trackedDecryptionResult(for: event, error: error)

case .Megolm(let message):
if message == Self.MissingKeysMessage {
if undecryptedEvents[sessionId] == nil {
log.error("Failed to decrypt event(s) due to missing room keys", context: [
"session_id": sessionId,
"message": message,
"error": error,
"details": "further errors for the same key will be supressed",
])
}

let keysError = NSError(
domain: MXDecryptingErrorDomain,
code: Int(MXDecryptingErrorUnknownInboundSessionIdCode.rawValue),
userInfo: [
NSLocalizedDescriptionKey: MXDecryptingErrorUnknownInboundSessionIdReason
]
)
return trackedDecryptionResult(for: event, error: keysError)
} else {
log.error("Failed to decrypt event due to megolm error", context: [
"session_id": sessionId,
"message": message,
"error": error
])
return trackedDecryptionResult(for: event, error: error)
}
}
}

private func handleGenericError(for event: MXEvent, sessionId: String, error: Error) -> MXEventDecryptionResult {
log.error("Failed to decrypt event", context: [
"session_id": sessionId,
"error": error
])
return trackedDecryptionResult(for: event, error: error)
}

private func trackedDecryptionResult(for event: MXEvent, error: Error) -> MXEventDecryptionResult {
addUndecryptedEvent(event)

let result = MXEventDecryptionResult()
result.error = error
return result
}
}

#endif
6 changes: 3 additions & 3 deletions MatrixSDK/Crypto/CrossSigning/MXCrossSigningV2.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ class MXCrossSigningV2: NSObject, MXCrossSigning {

Task {
do {
try await crossSigning.updateTrackedUsers(users: [crossSigning.userId])
try await crossSigning.downloadKeys(users: [crossSigning.userId])
myUserCrossSigningKeys = infoSource.crossSigningInfo(userId: crossSigning.userId)

log.debug("Cross signing state refreshed")
Expand All @@ -146,7 +146,7 @@ class MXCrossSigningV2: NSObject, MXCrossSigning {

Task {
do {
try await crossSigning.manuallyVerifyDevice(userId: crossSigning.userId, deviceId: deviceId)
try await crossSigning.verifyDevice(userId: crossSigning.userId, deviceId: deviceId)

log.debug("Successfully cross-signed a device")
await MainActor.run {
Expand All @@ -170,7 +170,7 @@ class MXCrossSigningV2: NSObject, MXCrossSigning {

Task {
do {
try await crossSigning.manuallyVerifyUser(userId: userId)
try await crossSigning.verifyUser(userId: userId)
log.debug("Successfully cross-signed a user")

await MainActor.run {
Expand Down
Loading

0 comments on commit 895fbd2

Please sign in to comment.