diff --git a/CHANGES.md b/CHANGES.md index a482877971..06df3cebcc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,24 @@ +## Changes in 0.26.0 (2023-03-07) + +🙌 Improvements + +- CryptoV2: Control CryptoSDK via feature flag ([#1719](https://github.com/matrix-org/matrix-ios-sdk/pull/1719)) +- Update MatrixSDKCrypto ([#1725](https://github.com/matrix-org/matrix-ios-sdk/pull/1725)) +- Use correct next users with keys query ([#1726](https://github.com/matrix-org/matrix-ios-sdk/pull/1726)) +- Creating a direct room with a third party will now use their email as the m.direct ID and their obfuscated email as the room title. ([#1727](https://github.com/matrix-org/matrix-ios-sdk/pull/1727)) + +🐛 Bugfixes + +- Fixed incorrect filtering of "unread rooms" in the all chats list. ([#1723](https://github.com/matrix-org/matrix-ios-sdk/pull/1723)) +- Unread rooms: Move the storage file to a better location. ([#1730](https://github.com/matrix-org/matrix-ios-sdk/pull/1730)) +- Fixed a crash when roomSummary is nil. ([#1731](https://github.com/matrix-org/matrix-ios-sdk/pull/1731)) +- Fix room list last message when the key comes late. ([#6848](https://github.com/vector-im/element-ios/issues/6848)) + +⚠️ API Changes + +- MXRoomSummary: displayname has been renamed to displayName ([#1731](https://github.com/matrix-org/matrix-ios-sdk/pull/1731)) + + ## Changes in 0.25.2 (2023-02-21) 🙌 Improvements diff --git a/MatrixSDK.podspec b/MatrixSDK.podspec index 901190e95e..5c9c2ab4ae 100644 --- a/MatrixSDK.podspec +++ b/MatrixSDK.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "MatrixSDK" - s.version = "0.25.2" + s.version = "0.26.0" s.summary = "The iOS SDK to build apps compatible with Matrix (https://www.matrix.org)" s.description = <<-DESC @@ -45,7 +45,7 @@ Pod::Spec.new do |s| ss.dependency 'OLMKit', '~> 3.2.5' ss.dependency 'Realm', '10.27.0' ss.dependency 'libbase58', '~> 0.1.4' - ss.dependency 'MatrixSDKCrypto', '0.2.0', :configurations => ["DEBUG", "RELEASE"], :inhibit_warnings => true + ss.dependency 'MatrixSDKCrypto', '0.2.1', :configurations => ["DEBUG", "RELEASE"], :inhibit_warnings => true end s.subspec 'JingleCallStack' do |ss| diff --git a/MatrixSDK.xcodeproj/project.pbxproj b/MatrixSDK.xcodeproj/project.pbxproj index b8880a7f4d..605bd26367 100644 --- a/MatrixSDK.xcodeproj/project.pbxproj +++ b/MatrixSDK.xcodeproj/project.pbxproj @@ -2025,6 +2025,8 @@ EDC8C4092968A993003792C5 /* MXKeysQueryScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC8C4072968A993003792C5 /* MXKeysQueryScheduler.swift */; }; EDC8C40D2968C37E003792C5 /* MXKeysQuerySchedulerUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC8C40A2968A9F7003792C5 /* MXKeysQuerySchedulerUnitTests.swift */; }; EDC8C40E2968C37F003792C5 /* MXKeysQuerySchedulerUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC8C40A2968A9F7003792C5 /* MXKeysQuerySchedulerUnitTests.swift */; }; + EDCAD251299BF7F40088B4DA /* MXCryptoV2Feature.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDCAD250299BF7F40088B4DA /* MXCryptoV2Feature.swift */; }; + EDCAD252299BF7F40088B4DA /* MXCryptoV2Feature.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDCAD250299BF7F40088B4DA /* MXCryptoV2Feature.swift */; }; EDCB65E22912AB0C00F55D4D /* MXRoomEventDecryption.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDCB65E12912AB0C00F55D4D /* MXRoomEventDecryption.swift */; }; EDCB65E32912AB0C00F55D4D /* MXRoomEventDecryption.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDCB65E12912AB0C00F55D4D /* MXRoomEventDecryption.swift */; }; EDCF802D2941FF220059E774 /* MXCryptoMigrationV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDCF802C2941FF220059E774 /* MXCryptoMigrationV2.swift */; }; @@ -3171,6 +3173,7 @@ EDC2A0E528369E740039F3D6 /* CryptoTests.xctestplan */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CryptoTests.xctestplan; sourceTree = ""; }; EDC8C4072968A993003792C5 /* MXKeysQueryScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXKeysQueryScheduler.swift; sourceTree = ""; }; EDC8C40A2968A9F7003792C5 /* MXKeysQuerySchedulerUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXKeysQuerySchedulerUnitTests.swift; sourceTree = ""; }; + EDCAD250299BF7F40088B4DA /* MXCryptoV2Feature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXCryptoV2Feature.swift; sourceTree = ""; }; EDCB65E12912AB0C00F55D4D /* MXRoomEventDecryption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXRoomEventDecryption.swift; sourceTree = ""; }; EDCF802C2941FF220059E774 /* MXCryptoMigrationV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXCryptoMigrationV2.swift; sourceTree = ""; }; EDD4197D28DCAA5F007F3757 /* MXNativeKeyBackupEngine.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXNativeKeyBackupEngine.h; sourceTree = ""; }; @@ -3525,6 +3528,7 @@ 322A51B41D9AB15900C8536D /* MXCrypto.h */, 322A51B51D9AB15900C8536D /* MXCrypto.m */, ED47CB6C28523995004FD755 /* MXCryptoV2.swift */, + EDCAD250299BF7F40088B4DA /* MXCryptoV2Feature.swift */, ED5EF151297AB33E00A5ADDA /* MXCryptoV2Factory.swift */, 325D1C251DFECE0D0070B8BF /* MXCrypto_Private.h */, 322A51C51D9BBD3C00C8536D /* MXOlmDevice.h */, @@ -7142,6 +7146,7 @@ EC2EACFF266625170038B61F /* MXRoomLastMessage.m in Sources */, EDF1B6902876CD2C00BBBCEE /* MXTaskQueue.swift in Sources */, EC8A539525B1BC77004E0802 /* MXUserModel.m in Sources */, + EDCAD251299BF7F40088B4DA /* MXCryptoV2Feature.swift in Sources */, ED5EF14F297AB29F00A5ADDA /* MXEventDecryptionResult+DecryptedEvent.swift in Sources */, 3252DCAF224BE5D40032264F /* MXKeyVerificationManager.m in Sources */, 323E0C5C1A306D7A00A31D73 /* MXEvent.m in Sources */, @@ -7809,6 +7814,7 @@ EC8A53D925B1BCC6004E0802 /* MXThirdPartyProtocolInstance.m in Sources */, EDF1B6912876CD2C00BBBCEE /* MXTaskQueue.swift in Sources */, B14EF2422397E90400758AF0 /* MXDeviceInfo.m in Sources */, + EDCAD252299BF7F40088B4DA /* MXCryptoV2Feature.swift in Sources */, ED5EF150297AB29F00A5ADDA /* MXEventDecryptionResult+DecryptedEvent.swift in Sources */, B14EF2432397E90400758AF0 /* MXIncomingSASTransaction.m in Sources */, B14EF2442397E90400758AF0 /* NSObject+sortedKeys.m in Sources */, diff --git a/MatrixSDK/Background/MXBackgroundSyncService.swift b/MatrixSDK/Background/MXBackgroundSyncService.swift index ac7e9303e9..5fe4e5abfb 100644 --- a/MatrixSDK/Background/MXBackgroundSyncService.swift +++ b/MatrixSDK/Background/MXBackgroundSyncService.swift @@ -65,7 +65,12 @@ public enum MXBackgroundSyncServiceError: Error { /// Initializer /// - Parameter credentials: account credentials - public init(withCredentials credentials: MXCredentials, persistTokenDataHandler: MXRestClientPersistTokenDataHandler? = nil, unauthenticatedHandler: MXRestClientUnauthenticatedHandler? = nil) { + public init( + withCredentials credentials: MXCredentials, + isCryptoSDKEnabled: Bool = false, + persistTokenDataHandler: MXRestClientPersistTokenDataHandler? = nil, + unauthenticatedHandler: MXRestClientUnauthenticatedHandler? = nil + ) { processingQueue = DispatchQueue(label: "MXBackgroundSyncServiceQueue-" + MXTools.generateSecret()) self.credentials = credentials @@ -88,14 +93,13 @@ public enum MXBackgroundSyncServiceError: Error { // We can flush any crypto data if our sync response store is empty let resetBackgroundCryptoStore = syncResponseStoreManager.syncToken() == nil - crypto = { - if MXSDKOptions.sharedInstance().isCryptoSDKAvailable && MXSDKOptions.sharedInstance().enableCryptoSDK { - return MXBackgroundCryptoV2(credentials: credentials, restClient: restClient) - } - - MXLog.debug("[MXBackgroundSyncService] init: constructing legacy crypto \(MXSDKOptions.sharedInstance().isCryptoSDKAvailable) \(MXSDKOptions.sharedInstance().enableCryptoSDK)") - return MXLegacyBackgroundCrypto(credentials: credentials, resetBackgroundCryptoStore: resetBackgroundCryptoStore) - }() + if isCryptoSDKEnabled { + MXLog.debug("[MXBackgroundSyncService] init: constructing crypto v2") + crypto = MXBackgroundCryptoV2(credentials: credentials, restClient: restClient) + } else { + MXLog.debug("[MXBackgroundSyncService] init: constructing legacy crypto") + crypto = MXLegacyBackgroundCrypto(credentials: credentials, resetBackgroundCryptoStore: resetBackgroundCryptoStore) + } pushRulesManager = MXBackgroundPushRulesManager(withCredentials: credentials) MXLog.debug("[MXBackgroundSyncService] init complete") diff --git a/MatrixSDK/Background/Store/MXSyncResponseStoreManager.swift b/MatrixSDK/Background/Store/MXSyncResponseStoreManager.swift index 04dca45995..fb7f8334f9 100644 --- a/MatrixSDK/Background/Store/MXSyncResponseStoreManager.swift +++ b/MatrixSDK/Background/Store/MXSyncResponseStoreManager.swift @@ -327,18 +327,18 @@ public class MXSyncResponseStoreManager: NSObject { for event in eventsToProcess { switch event.eventType { case .roomAliases: - if summary.displayname == nil { - summary.displayname = (event.content["aliases"] as? [String])?.first + if summary.displayName == nil { + summary.displayName = (event.content["aliases"] as? [String])?.first } case .roomCanonicalAlias: - if summary.displayname == nil { - summary.displayname = event.content["alias"] as? String - if summary.displayname == nil { - summary.displayname = (event.content["alt_aliases"] as? [String])?.first + if summary.displayName == nil { + summary.displayName = event.content["alias"] as? String + if summary.displayName == nil { + summary.displayName = (event.content["alt_aliases"] as? [String])?.first } } case .roomName: - summary.displayname = event.content["name"] as? String + summary.displayName = event.content["name"] as? String default: break } diff --git a/MatrixSDK/Crypto/Algorithms/RoomEvent/MXRoomEventDecryption.swift b/MatrixSDK/Crypto/Algorithms/RoomEvent/MXRoomEventDecryption.swift index ee9193e10b..1221b8aef1 100644 --- a/MatrixSDK/Crypto/Algorithms/RoomEvent/MXRoomEventDecryption.swift +++ b/MatrixSDK/Crypto/Algorithms/RoomEvent/MXRoomEventDecryption.swift @@ -52,6 +52,7 @@ actor MXRoomEventDecryption: MXRoomEventDecrypting { } func decrypt(events: [MXEvent]) -> [MXEventDecryptionResult] { + log.debug("Decrypting \(events.count) event(s)") let results = events.map(decrypt(event:)) let undecrypted = results.filter { @@ -63,8 +64,8 @@ actor MXRoomEventDecryption: MXRoomEventDecrypting { "total": events.count, "undecrypted": undecrypted.count ]) - } else { - log.debug("Decrypted all \(events.count) event(s)") + } else if events.count > 1 { + log.debug("Decrypted all \(events.count) events") } return results @@ -120,7 +121,7 @@ actor MXRoomEventDecryption: MXRoomEventDecrypting { do { let decryptedEvent = try handler.decryptRoomEvent(event) let result = try MXEventDecryptionResult(event: decryptedEvent) - log.debug("Successfully decrypted event `\(result.clearEvent["type"] ?? "unknown")` eventId `\(eventId)`") + log.debug("Decrypted event `\(result.clearEvent["type"] ?? "unknown")` eventId `\(eventId)`") return result } catch let error as DecryptionError { diff --git a/MatrixSDK/Crypto/Algorithms/RoomEvent/MXRoomEventEncryption.swift b/MatrixSDK/Crypto/Algorithms/RoomEvent/MXRoomEventEncryption.swift index 80c21592f1..8475e44e7d 100644 --- a/MatrixSDK/Crypto/Algorithms/RoomEvent/MXRoomEventEncryption.swift +++ b/MatrixSDK/Crypto/Algorithms/RoomEvent/MXRoomEventEncryption.swift @@ -127,7 +127,6 @@ struct MXRoomEventEncryption: MXRoomEventEncrypting { for: room, historyVisibility: state.historyVisibility ) - log.debug("Collected \(users.count) eligible users") let settings = try encryptionSettings(for: state) try await handler.shareRoomKeysIfNecessary( @@ -135,21 +134,23 @@ struct MXRoomEventEncryption: MXRoomEventEncrypting { users: users, settings: settings ) - - log.debug("Encryption and room keys up to date") } /// Make sure that we recognize (and store if necessary) the claimed room encryption algorithm private func ensureRoomEncryption(roomId: String, algorithm: String?) throws { let existingAlgorithm = legacyStore.algorithm(forRoom: roomId) if existingAlgorithm != nil && existingAlgorithm == algorithm { - log.debug("Encryption in room is already set to the correct algorithm") + // Encryption in room is already set to the correct algorithm return } guard let algorithm = algorithm else { - log.error("Resetting encryption is not allowed") - throw Error.invalidEncryptionAlgorithm + if existingAlgorithm != nil { + log.error("Resetting encryption is not allowed") + return + } else { + throw Error.invalidEncryptionAlgorithm + } } let supportedAlgorithms = Set([kMXCryptoMegolmAlgorithm]) diff --git a/MatrixSDK/Crypto/CryptoMachine/MXCryptoMachine.swift b/MatrixSDK/Crypto/CryptoMachine/MXCryptoMachine.swift index 695ab00441..38563d7cf7 100644 --- a/MatrixSDK/Crypto/CryptoMachine/MXCryptoMachine.swift +++ b/MatrixSDK/Crypto/CryptoMachine/MXCryptoMachine.swift @@ -220,7 +220,7 @@ extension MXCryptoMachine: MXCryptoSyncing { // MARK: - Private private func handleRequest(_ request: Request) async throws { - log.debug("Handling request \(request)") + log.debug("Handling `\(request.type)` request") switch request { case .toDevice(let requestId, let eventType, let body): @@ -240,9 +240,14 @@ extension MXCryptoMachine: MXCryptoSyncing { try markRequestAsSent(requestId: requestId, requestType: .keysQuery, response: response.jsonString()) case .keysClaim(let requestId, let oneTimeKeys): + log.debug("Claiming keys \(oneTimeKeys)") + let response = try await requests.claimKeys( request: .init(oneTimeKeys: oneTimeKeys) ) + + let dictionary = response.jsonDictionary() as? [String: Any] ?? [:] + log.debug("Keys claimed\n\(dictionary)") try markRequestAsSent(requestId: requestId, requestType: .keysClaim, response: response.jsonString()) case .keysBackup(let requestId, let version, let rooms): @@ -276,8 +281,6 @@ extension MXCryptoMachine: MXCryptoSyncing { } private func handleOutgoingRequests() async throws { - log.debug("->") - let requests = try machine.outgoingRequests() try await withThrowingTaskGroup(of: Void.self) { [weak self] group in @@ -386,19 +389,14 @@ extension MXCryptoMachine: MXCryptoRoomEventEncrypting { log.debug("Checking room keys in room \(roomId)") try await sessionsQueue.sync { [weak self] in - self?.log.debug("Collecting missing sessions") try await self?.getMissingSessions(users: users) } - log.debug("Acquiring lock for room \(roomId)") let roomQueue = await roomQueues.getQueue(for: roomId) try await roomQueue.sync { [weak self] in - self?.log.debug("Sharing room keys") try await self?.shareRoomKey(roomId: roomId, users: users, settings: settings) } - - log.debug("All room keys have been shared") } func encryptRoomEvent( @@ -425,13 +423,13 @@ extension MXCryptoMachine: MXCryptoRoomEventEncrypting { // MARK: - Private private func getMissingSessions(users: [String]) async throws { - log.debug("Getting missing sessions for \(users.count) user(s)") + log.debug("Checking missing olm sessions for \(users.count) user(s): \(users)") guard let request = try machine.getMissingSessions(users: users), case .keysClaim = request else { - log.debug("No sessions are missing") + log.debug("No olm sessions are missing") return } @@ -440,6 +438,8 @@ extension MXCryptoMachine: MXCryptoRoomEventEncrypting { } private func shareRoomKey(roomId: String, users: [String], settings: EncryptionSettings) async throws { + log.debug("Checking unshared room keys") + let requests = try machine.shareRoomKey(roomId: roomId, users: users, settings: settings) guard !requests.isEmpty else { log.debug("There are no new keys to share") @@ -781,3 +781,24 @@ extension MXCryptoMachine: MXCryptoBackup { isComputingRoomKeyCounts = false } } + +extension Request { + var type: RequestType { + switch self { + case .toDevice: + return .toDevice + case .keysUpload: + return .keysUpload + case .keysQuery: + return .keysQuery + case .keysClaim: + return .keysClaim + case .keysBackup: + return .keysBackup + case .roomMessage: + return .roomMessage + case .signatureUpload: + return .signatureUpload + } + } +} diff --git a/MatrixSDK/Crypto/CryptoMachine/MXCryptoSDKLogger.swift b/MatrixSDK/Crypto/CryptoMachine/MXCryptoSDKLogger.swift index 99c8b83833..9c84ce7d78 100644 --- a/MatrixSDK/Crypto/CryptoMachine/MXCryptoSDKLogger.swift +++ b/MatrixSDK/Crypto/CryptoMachine/MXCryptoSDKLogger.swift @@ -30,7 +30,11 @@ class MXCryptoSDKLogger: Logger { // This will be changed in rust-sdk directly let ignored = [ "::uniffi_api:", - "::backup_recovery_key: decrypt_v1" + "::backup_recovery_key: decrypt_v1", + "matrix_sdk_crypto_ffi::machine: backup_enabled", + "matrix_sdk_crypto_ffi::machine: room_key_counts", + "matrix_sdk_crypto_ffi::machine: user_id", + "matrix_sdk_crypto_ffi::machine: identity_keys" ] for ignore in ignored { diff --git a/MatrixSDK/Crypto/CryptoMachine/MXKeysQueryScheduler.swift b/MatrixSDK/Crypto/CryptoMachine/MXKeysQueryScheduler.swift index d2bf3117dd..f20b11ac31 100644 --- a/MatrixSDK/Crypto/CryptoMachine/MXKeysQueryScheduler.swift +++ b/MatrixSDK/Crypto/CryptoMachine/MXKeysQueryScheduler.swift @@ -56,12 +56,12 @@ public actor MXKeysQueryScheduler { private func currentOrNextQuery(users: Set) -> Task { if let currentQuery = currentQuery { if currentQuery.contains(users: users) { - log("... query already running") + log("... query with the same user(s) already running") return currentQuery.task } else { - log("... queueing users for the next query") + log("... queueing user(s) for the next query") nextUsers = nextUsers.union(users) @@ -69,9 +69,9 @@ public actor MXKeysQueryScheduler { // Next task needs to await to completion of the currently running task let _ = await currentQuery.task.result - // Extract and reset next users - let users = nextUsers - nextUsers = [] + // At this point the previous query has already changed `self.currentQuery` + // to `next`, so we can extract its users to execute + let users = self.currentQuery?.users ?? [] // Only then we can execute the actual work return try await executeQuery(users: users) @@ -81,28 +81,29 @@ public actor MXKeysQueryScheduler { } } else { - log("... query starting") - let task = Task { // Since we do not have any task running we can execute work right away try await executeQuery(users: users) } - currentQuery = .init(users: users, task: task) + currentQuery = Query(users: users, task: task) return task } } private func executeQuery(users: Set) async throws -> Response { defer { - if let next = nextTask { - log("... query completed, starting next pending query.") - currentQuery = .init(users: users, task: next) + if let nextTask = nextTask { + log("... query for \(users) completed, starting next pending query.") + currentQuery = Query(users: nextUsers, task: nextTask) } else { - log("... query completed, no other queries scheduled.") + log("... query for \(users) completed, no other queries scheduled.") currentQuery = nil } nextTask = nil + nextUsers = [] } + + log("... query starting for \(users)") return try await queryAction(Array(users)) } diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m index 4f66792fa8..3f5c836fff 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m @@ -203,7 +203,7 @@ - (void)maybeSendKeyBackup } else { - MXLogDebug(@"[MXKeyBackup] maybeSendKeyBackup: Skip it because state: %@", @(_state)); + MXLogDebug(@"[MXKeyBackup] maybeSendKeyBackup: Skip it because state: %@", [self descriptionForState:_state]); // If not already done, check for a valid backup version on the homeserver. // If one, maybeSendKeyBackup will be called again. @@ -231,7 +231,7 @@ - (void)sendKeyBackup // Sanity check if (!self.enabled || !_keyBackupVersion) { - MXLogDebug(@"[MXKeyBackup] sendKeyBackup: Invalid state: %@", @(_state)); + MXLogDebug(@"[MXKeyBackup] sendKeyBackup: Invalid state: %@", [self descriptionForState:_state]); if (backupAllGroupSessionsFailure) { NSError *error = [NSError errorWithDomain:MXKeyBackupErrorDomain @@ -522,7 +522,7 @@ - (MXHTTPOperation*)forceRefresh:(nullable void (^)(BOOL valid))success { if (_state == MXKeyBackupStateUnknown || _state == MXKeyBackupStateCheckingBackUpOnHomeserver) { - MXLogDebug(@"[MXKeyBackup] forceRefresh: Invalid state (%@) to force the refresh", @(_state)); + MXLogDebug(@"[MXKeyBackup] forceRefresh: Invalid state (%@) to force the refresh", [self descriptionForState:_state]); if (failure) { NSError *error = [NSError errorWithDomain:MXKeyBackupErrorDomain diff --git a/MatrixSDK/Crypto/MXCrypto.m b/MatrixSDK/Crypto/MXCrypto.m index d8a155bd62..6066b74c7f 100644 --- a/MatrixSDK/Crypto/MXCrypto.m +++ b/MatrixSDK/Crypto/MXCrypto.m @@ -150,25 +150,13 @@ @implementation MXLegacyCrypto @synthesize keyVerificationManager = _keyVerificationManager; @synthesize recoveryService = _recoveryService; -+ (MXCryptoV2Factory *)sharedCryptoV2Factory -{ - static MXCryptoV2Factory *factory = nil; - static dispatch_once_t onceToken; - - dispatch_once(&onceToken, ^{ - factory = [[MXCryptoV2Factory alloc] init]; - }); - - return factory; -} - + (id)createCryptoWithMatrixSession:(MXSession *)mxSession error:(NSError **)error { __block id crypto; #ifdef MX_CRYPTO - if (MXSDKOptions.sharedInstance.isCryptoSDKAvailable && MXSDKOptions.sharedInstance.enableCryptoSDK) + if (MXSDKOptions.sharedInstance.enableCryptoSDK) { MXLogFailure(@"[MXCrypto] createCryptoWithMatrixSession: Crypto V2 should not be created directly, use initializeCryptoWithMatrixSession instead"); return nil; @@ -192,15 +180,19 @@ + (void)initializeCryptoWithMatrixSession:(MXSession *)mxSession complete:(void (^)(id crypto, NSError *error))complete { #ifdef MX_CRYPTO - if (MXSDKOptions.sharedInstance.isCryptoSDKAvailable && MXSDKOptions.sharedInstance.enableCryptoSDK) - { - MXCryptoV2Factory *factory = [MXLegacyCrypto sharedCryptoV2Factory]; - [factory buildCryptoWithSession:mxSession migrationProgress:migrationProgress - success:^(id crypto) { - complete(crypto, nil); - } failure:^(NSError *error) { - complete(nil, error); - }]; + + // Each time we construct the crypto module (app launch, login etc) we have a chance to try to enable + // the newer SDK crypto module, if it is available for this particular user. + [MXSDKOptions.sharedInstance.cryptoSDKFeature enableIfAvailableForUserId:mxSession.myUserId]; + if (MXSDKOptions.sharedInstance.enableCryptoSDK) + { + [MXCryptoV2Factory.shared buildCryptoWithSession:mxSession + migrationProgress:migrationProgress + success:^(id crypto) { + complete(crypto, nil); } + failure:^(NSError *error) { + complete(nil, error); + }]; return; } diff --git a/MatrixSDK/Crypto/MXCryptoV2.swift b/MatrixSDK/Crypto/MXCryptoV2.swift index 4b17f0b05e..0d2e574429 100644 --- a/MatrixSDK/Crypto/MXCryptoV2.swift +++ b/MatrixSDK/Crypto/MXCryptoV2.swift @@ -267,14 +267,13 @@ class MXCryptoV2: NSObject, MXCrypto { onComplete: (([MXEventDecryptionResult]) -> Void)? ) { guard session?.isEventStreamInitialised == true else { - log.debug("Ignoring \(events.count) encrypted event(s) during initial sync in timeline \(timeline ?? "") (we most likely do not have the keys yet)") + log.debug("Ignoring \(events.count) encrypted event(s) during initial sync (we most likely do not have the keys yet)") let results = events.map { _ in MXEventDecryptionResult() } onComplete?(results) return } Task { - log.debug("Decrypting \(events.count) event(s) in timeline \(timeline ?? "")") let results = await decryptor.decrypt(events: events) await MainActor.run { onComplete?(results) @@ -287,12 +286,9 @@ class MXCryptoV2: NSObject, MXCrypto { success: (() -> Void)?, failure: ((Swift.Error) -> Void)? ) -> MXHTTPOperation? { - log.debug("->") - Task { do { try await encryptor.ensureRoomKeysShared(roomId: roomId) - log.debug("Room keys shared when necessary") await MainActor.run { success?() } diff --git a/MatrixSDK/Crypto/MXCryptoV2Factory.swift b/MatrixSDK/Crypto/MXCryptoV2Factory.swift index 14d8c7b0fa..2226aabef5 100644 --- a/MatrixSDK/Crypto/MXCryptoV2Factory.swift +++ b/MatrixSDK/Crypto/MXCryptoV2Factory.swift @@ -22,6 +22,7 @@ import Foundation case storeNotAvailable } + @objc public static let shared = MXCryptoV2Factory() private let log = MXNamedLog(name: "MXCryptoV2Factory") @objc public func buildCrypto( diff --git a/MatrixSDK/Crypto/MXCryptoV2Feature.swift b/MatrixSDK/Crypto/MXCryptoV2Feature.swift new file mode 100644 index 0000000000..0af26c28a1 --- /dev/null +++ b/MatrixSDK/Crypto/MXCryptoV2Feature.swift @@ -0,0 +1,45 @@ +// +// Copyright 2023 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 + +/// Feature representing the availability of the external rust-based Crypto SDK +/// whilst it is not fully available to everyone and / or is an optional feature. +@objc public protocol MXCryptoV2Feature { + + /// Is Crypto SDK currently enabled + /// + /// By default this value is `false`. Once enabled, it can only be disabled by logging out, + /// as there is no way to migrate from from Crypto SDK back to legacy crypto. + var isEnabled: Bool { get } + + /// Manually enable the feature + /// + /// This is typically triggered by some user settings / Labs as an experimental feature. Once called + /// it should restart the session to re-initialize the crypto module. + func enable() + + /// Try to enable the feature for a given user + /// + /// This method should only be called when initializing a crypto module (e.g. during app launch or login), + /// as it is not possible to swap out crypto modules whilst a session is active. + /// + /// The availability conditions are implementation details, typically consisting of + /// various feature flags. + /// + /// If available, this method will set `isEnabled` permanently to `true`. + func enableIfAvailable(forUserId userId: String!) +} diff --git a/MatrixSDK/Data/MXRoom.m b/MatrixSDK/Data/MXRoom.m index e4ae043eab..020af712d3 100644 --- a/MatrixSDK/Data/MXRoom.m +++ b/MatrixSDK/Data/MXRoom.m @@ -3870,7 +3870,7 @@ - (void)membersTrustLevelSummaryWithForceDownload:(BOOL)forceDownload success:(v - (NSString *)description { - return [NSString stringWithFormat:@" %@: %@ - %@", self, self.roomId, self.summary.displayname, self.summary.topic]; + return [NSString stringWithFormat:@" %@: %@ - %@", self, self.roomId, self.summary.displayName, self.summary.topic]; } - (NSComparisonResult)compareLastMessageEventOriginServerTs:(MXRoom *)otherRoom diff --git a/MatrixSDK/Data/MXRoomLastMessage.h b/MatrixSDK/Data/MXRoomLastMessage.h index 7825afce6c..71978d0c82 100644 --- a/MatrixSDK/Data/MXRoomLastMessage.h +++ b/MatrixSDK/Data/MXRoomLastMessage.h @@ -51,6 +51,11 @@ FOUNDATION_EXPORT NSString *const MXRoomLastMessageDataType; */ @property (nonatomic, assign, readonly) BOOL isEncrypted; +/** + Indicates if the last message failed to be decrypted. + */ +@property (nonatomic, assign, readonly) BOOL hasDecryptionError; + /** Sender of the last message. */ diff --git a/MatrixSDK/Data/MXRoomLastMessage.m b/MatrixSDK/Data/MXRoomLastMessage.m index e0e83f02df..01c9f52edf 100644 --- a/MatrixSDK/Data/MXRoomLastMessage.m +++ b/MatrixSDK/Data/MXRoomLastMessage.m @@ -30,6 +30,7 @@ NSString *const kCodingKeyEventId = @"eventId"; NSString *const kCodingKeyOriginServerTs = @"originServerTs"; NSString *const kCodingKeyIsEncrypted = @"isEncrypted"; +NSString *const kCodingKeyHasDecryptionError = @"hasDecryptionError"; NSString *const kCodingKeySender = @"sender"; NSString *const kCodingKeyData = @"data"; NSString *const kCodingKeyEncryptedData = @"encryptedData"; @@ -54,6 +55,7 @@ - (instancetype)initWithEvent:(MXEvent *)event _eventId = event.eventId; _originServerTs = event.originServerTs; _isEncrypted = event.isEncrypted; + _hasDecryptionError = event.decryptionError != nil; _sender = event.sender; } return self; @@ -108,6 +110,7 @@ - (instancetype)initWithManagedObject:(MXRoomLastMessageMO *)model _eventId = model.s_eventId; _originServerTs = model.s_originServerTs; _isEncrypted = model.s_isEncrypted; + _hasDecryptionError = model.s_hasDecryptionError; _sender = model.s_sender; NSData* archivedSensitiveData; @@ -142,6 +145,7 @@ - (instancetype)initWithCoder:(NSCoder *)coder _eventId = [coder decodeObjectForKey:kCodingKeyEventId]; _originServerTs = [coder decodeInt64ForKey:kCodingKeyOriginServerTs]; _isEncrypted = [coder decodeBoolForKey:kCodingKeyIsEncrypted]; + _hasDecryptionError = [coder decodeBoolForKey:kCodingKeyHasDecryptionError]; _sender = [coder decodeObjectForKey:kCodingKeySender]; NSDictionary *lastMessageDictionary; @@ -172,6 +176,7 @@ - (void)encodeWithCoder:(NSCoder *)coder [coder encodeObject:_eventId forKey:kCodingKeyEventId]; [coder encodeInt64:_originServerTs forKey:kCodingKeyOriginServerTs]; [coder encodeBool:_isEncrypted forKey:kCodingKeyIsEncrypted]; + [coder encodeBool:_hasDecryptionError forKey:kCodingKeyHasDecryptionError]; [coder encodeObject:_sender forKey:kCodingKeySender]; // Build last message sensitive data diff --git a/MatrixSDK/Data/MXRoomSummary.h b/MatrixSDK/Data/MXRoomSummary.h index 70437d7754..63147cd2dc 100644 --- a/MatrixSDK/Data/MXRoomSummary.h +++ b/MatrixSDK/Data/MXRoomSummary.h @@ -169,7 +169,7 @@ FOUNDATION_EXPORT NSUInteger const MXRoomSummaryPaginationChunkSize; /** The computed display name of the room. */ -@property (nonatomic) NSString *displayname; +@property (nonatomic) NSString *displayName; /** The topic of the room. diff --git a/MatrixSDK/Data/MXRoomSummary.m b/MatrixSDK/Data/MXRoomSummary.m index 7c8429fc1d..0839f38085 100644 --- a/MatrixSDK/Data/MXRoomSummary.m +++ b/MatrixSDK/Data/MXRoomSummary.m @@ -243,7 +243,7 @@ - (void)updateWith:(id)summary _roomTypeString = summary.roomTypeString; _roomType = summary.roomType; _avatar = summary.avatar; - _displayname = summary.displayname; + _displayName = summary.displayName; _topic = summary.topic; _creatorUserId = summary.creatorUserId; _aliases = summary.aliases; @@ -286,7 +286,7 @@ - (void)resetRoomStateData MXRoom *room = self.room; _avatar = nil; - _displayname = nil; + _displayName = nil; _topic = nil; _aliases = nil; @@ -296,7 +296,7 @@ - (void)resetRoomStateData BOOL updated = [self.mxSession.roomSummaryUpdateDelegate session:self.mxSession updateRoomSummary:self withStateEvents:roomState.stateEvents roomState:roomState]; - if (self.displayname == nil || self.avatar == nil) + if (self.displayName == nil || self.avatar == nil) { // Avatar and displayname may not be recomputed from the state event list if // the latter does not contain any `name` or `avatar` event. So, in this case, @@ -971,7 +971,7 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder _roomTypeString = [aDecoder decodeObjectForKey:@"roomTypeString"]; _roomType = [aDecoder decodeIntegerForKey:@"roomType"]; _avatar = [aDecoder decodeObjectForKey:@"avatar"]; - _displayname = [aDecoder decodeObjectForKey:@"displayname"]; + _displayName = [aDecoder decodeObjectForKey:@"displayname"]; _topic = [aDecoder decodeObjectForKey:@"topic"]; _creatorUserId = [aDecoder decodeObjectForKey:@"creatorUserId"]; _aliases = [aDecoder decodeObjectForKey:@"aliases"]; @@ -1021,7 +1021,7 @@ - (void)encodeWithCoder:(NSCoder *)aCoder [aCoder encodeObject:_roomTypeString forKey:@"roomTypeString"]; [aCoder encodeInteger:_roomType forKey:@"roomType"]; [aCoder encodeObject:_avatar forKey:@"avatar"]; - [aCoder encodeObject:_displayname forKey:@"displayname"]; + [aCoder encodeObject:_displayName forKey:@"displayname"]; [aCoder encodeObject:_topic forKey:@"topic"]; [aCoder encodeObject:_creatorUserId forKey:@"creatorUserId"]; [aCoder encodeObject:_aliases forKey:@"aliases"]; @@ -1059,7 +1059,7 @@ - (void)encodeWithCoder:(NSCoder *)aCoder - (NSString *)description { - return [NSString stringWithFormat:@"%@ %@: %@ - %@", super.description, _roomId, _displayname, _lastMessage.eventId]; + return [NSString stringWithFormat:@"%@ %@: %@ - %@", super.description, _roomId, _displayName, _lastMessage.eventId]; } - (NSUInteger)hash @@ -1070,7 +1070,7 @@ - (NSUInteger)hash result = prime * result + [_roomId hash]; result = prime * result + [_roomTypeString hash]; result = prime * result + [_avatar hash]; - result = prime * result + [_displayname hash]; + result = prime * result + [_displayName hash]; result = prime * result + [_topic hash]; result = prime * result + [_creatorUserId hash]; result = prime * result + [_aliases hash]; @@ -1133,7 +1133,7 @@ - (MXRoomSummaryDataTypes)calculateDataTypes { result |= MXRoomSummaryDataTypesConferenceUser; } - if (self.hasAnyNotification) + if (self.hasAnyUnread) { result |= MXRoomSummaryDataTypesUnread; } diff --git a/MatrixSDK/Data/MXRoomSummaryProtocol.h b/MatrixSDK/Data/MXRoomSummaryProtocol.h index 7c1cee6864..d5701f2c8d 100644 --- a/MatrixSDK/Data/MXRoomSummaryProtocol.h +++ b/MatrixSDK/Data/MXRoomSummaryProtocol.h @@ -48,7 +48,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) NSString * _Nullable avatar; /// The computed display name of the room. -@property (nonatomic, readonly) NSString * _Nullable displayname; +@property (nonatomic, readonly) NSString * _Nullable displayName; /// The topic of the room. @property (nonatomic, readonly) NSString * _Nullable topic; diff --git a/MatrixSDK/Data/MXRoomSummaryUpdater.m b/MatrixSDK/Data/MXRoomSummaryUpdater.m index 9bac650970..f494abe568 100644 --- a/MatrixSDK/Data/MXRoomSummaryUpdater.m +++ b/MatrixSDK/Data/MXRoomSummaryUpdater.m @@ -153,7 +153,7 @@ - (BOOL)session:(MXSession *)session updateRoomSummary:(MXRoomSummary *)summary switch (event.eventType) { case MXEventTypeRoomName: - summary.displayname = roomState.name; + summary.displayName = roomState.name; updated = YES; break; @@ -186,7 +186,7 @@ - (BOOL)session:(MXSession *)session updateRoomSummary:(MXRoomSummary *)summary // If m.room.canonical_alias is set, use it if there is no m.room.name if (!roomState.name && roomState.canonicalAlias) { - summary.displayname = roomState.canonicalAlias; + summary.displayName = roomState.canonicalAlias; updated = YES; } // If canonicalAlias is set, add it to the aliases array @@ -300,11 +300,11 @@ - (BOOL)session:(MXSession *)session updateRoomSummary:(MXRoomSummary *)summary NSUInteger memberCount = roomState.membersCount.members; if (memberCount > 1 - && (!summary.displayname || [summary.displayname isEqualToString:_roomNameStringLocalizer.emptyRoom])) + && (!summary.displayName || [summary.displayName isEqualToString:_roomNameStringLocalizer.emptyRoom])) { // Data are missing to compute the display name MXLogDebug(@"[MXRoomSummaryUpdater] updateRoomSummary: Computed an unexpected \"Empty Room\" name. memberCount: %@", @(memberCount)); - summary.displayname = [self fixUnexpectedEmptyRoomDisplayname:memberCount + summary.displayName = [self fixUnexpectedEmptyRoomDisplayname:memberCount session:session roomState:roomState]; updated = YES; @@ -516,8 +516,7 @@ - (BOOL)updateSummaryDisplayname:(MXRoomSummary *)summary session:(MXSession *)s } } - if (memberCount > 1 - && (!displayName || [displayName isEqualToString:_roomNameStringLocalizer.emptyRoom])) + if (!displayName || [displayName isEqualToString:_roomNameStringLocalizer.emptyRoom]) { // Data are missing to compute the display name MXLogDebug(@"[MXRoomSummaryUpdater] updateSummaryDisplayname: Warning: Computed an unexpected \"Empty Room\" name. memberCount: %@", @(memberCount)); @@ -525,9 +524,9 @@ - (BOOL)updateSummaryDisplayname:(MXRoomSummary *)summary session:(MXSession *)s } } - if (displayName != summary.displayname || ![displayName isEqualToString:summary.displayname]) + if (displayName != summary.displayName || ![displayName isEqualToString:summary.displayName]) { - summary.displayname = displayName; + summary.displayName = displayName; return YES; } @@ -565,10 +564,23 @@ - (NSString*)fixUnexpectedEmptyRoomDisplayname:(NSUInteger)memberCount session:( switch (memberNames.count) { case 0: - MXLogDebug(@"[MXRoomSummaryUpdater] fixUnexpectedEmptyRoomDisplayname: No luck"); + { displayname = _roomNameStringLocalizer.emptyRoom; + NSString *directUserId = [session roomWithRoomId: roomState.roomId].directUserId; + if (directUserId != nil && [MXTools isEmailAddress:directUserId]) + { + displayname = directUserId; + } + else if (roomState.thirdPartyInvites.firstObject.displayname != nil) + { + displayname = roomState.thirdPartyInvites.firstObject.displayname; + } + else + { + MXLogDebug(@"[MXRoomSummaryUpdater] fixUnexpectedEmptyRoomDisplayname: No luck"); + } break; - + } case 1: if (memberCount == 2) { diff --git a/MatrixSDK/Data/RoomList/Common/MXBreadcrumbsRoomListDataFetcher.swift b/MatrixSDK/Data/RoomList/Common/MXBreadcrumbsRoomListDataFetcher.swift index d4cd931ef5..507242e666 100644 --- a/MatrixSDK/Data/RoomList/Common/MXBreadcrumbsRoomListDataFetcher.swift +++ b/MatrixSDK/Data/RoomList/Common/MXBreadcrumbsRoomListDataFetcher.swift @@ -91,10 +91,10 @@ internal class MXBreadcrumbsRoomListDataFetcher: NSObject, MXRoomListDataFetcher if let query = fetchOptions.filterOptions.query?.lowercased(), !query.isEmpty { recentRoomIds = recentRoomIds.filter({ roomId in - guard let displayname = session?.roomSummary(withRoomId: roomId).displayname else { + guard let displayName = session?.roomSummary(withRoomId: roomId)?.displayName else { return false } - return displayname.lowercased().contains(query) + return displayName.lowercased().contains(query) }) } diff --git a/MatrixSDK/Data/RoomList/Common/MXRoomListDataFilterable.swift b/MatrixSDK/Data/RoomList/Common/MXRoomListDataFilterable.swift index d5ea63d0bb..425f0c2d0a 100644 --- a/MatrixSDK/Data/RoomList/Common/MXRoomListDataFilterable.swift +++ b/MatrixSDK/Data/RoomList/Common/MXRoomListDataFilterable.swift @@ -122,7 +122,7 @@ extension MXRoomListDataFilterable { if let query = filterOptions.query, !query.isEmpty { let predicate1 = NSPredicate(format: "%K CONTAINS[cd] %@", - #keyPath(MXRoomSummaryProtocol.displayname), + #keyPath(MXRoomSummaryProtocol.displayName), query) let predicate2 = NSPredicate(format: "%K CONTAINS[cd] %@", #keyPath(MXRoomSummaryProtocol.spaceChildInfo.displayName), diff --git a/MatrixSDK/Data/RoomList/Common/MXRoomListDataSortable.swift b/MatrixSDK/Data/RoomList/Common/MXRoomListDataSortable.swift index 65a1f4a10b..2aced9df00 100644 --- a/MatrixSDK/Data/RoomList/Common/MXRoomListDataSortable.swift +++ b/MatrixSDK/Data/RoomList/Common/MXRoomListDataSortable.swift @@ -52,7 +52,7 @@ extension MXRoomListDataSortable { // } if sortOptions.alphabetical { - result.append(NSSortDescriptor(keyPath: \MXRoomSummaryProtocol.displayname, ascending: true)) + result.append(NSSortDescriptor(keyPath: \MXRoomSummaryProtocol.displayName, ascending: true)) } if sortOptions.invitesFirst { diff --git a/MatrixSDK/Data/RoomList/CoreData/MXCoreDataRoomListDataFetcher.swift b/MatrixSDK/Data/RoomList/CoreData/MXCoreDataRoomListDataFetcher.swift index 0a3edf50ae..84c521bf96 100644 --- a/MatrixSDK/Data/RoomList/CoreData/MXCoreDataRoomListDataFetcher.swift +++ b/MatrixSDK/Data/RoomList/CoreData/MXCoreDataRoomListDataFetcher.swift @@ -410,7 +410,7 @@ private class RoomSummaryForTotalCounts: NSObject, MXRoomSummaryProtocol { var roomTypeString: String? var roomType: MXRoomType = .room var avatar: String? - var displayname: String? + var displayName: String? var topic: String? var creatorUserId: String = "" var aliases: [String] = [] @@ -465,7 +465,7 @@ private class RoomSummaryForTotalCounts: NSObject, MXRoomSummaryProtocol { } override var description: String { - return "" + return "" } } diff --git a/MatrixSDK/Data/RoomSummaryStore/CoreData/MXCoreDataRoomSummaryStore.xcdatamodeld/MXRoomSummaryCoreDataStore.xcdatamodel/contents b/MatrixSDK/Data/RoomSummaryStore/CoreData/MXCoreDataRoomSummaryStore.xcdatamodeld/MXRoomSummaryCoreDataStore.xcdatamodel/contents index a1badaad18..044eb6e4b5 100644 --- a/MatrixSDK/Data/RoomSummaryStore/CoreData/MXCoreDataRoomSummaryStore.xcdatamodeld/MXRoomSummaryCoreDataStore.xcdatamodel/contents +++ b/MatrixSDK/Data/RoomSummaryStore/CoreData/MXCoreDataRoomSummaryStore.xcdatamodeld/MXRoomSummaryCoreDataStore.xcdatamodel/contents @@ -3,6 +3,7 @@ + diff --git a/MatrixSDK/Data/RoomSummaryStore/CoreData/MXCoreDataRoomSummaryStore.xcdatamodeld/MXRoomSummaryCoreDataStore2.xcdatamodel/contents b/MatrixSDK/Data/RoomSummaryStore/CoreData/MXCoreDataRoomSummaryStore.xcdatamodeld/MXRoomSummaryCoreDataStore2.xcdatamodel/contents index 21dd543330..17567edca8 100644 --- a/MatrixSDK/Data/RoomSummaryStore/CoreData/MXCoreDataRoomSummaryStore.xcdatamodeld/MXRoomSummaryCoreDataStore2.xcdatamodel/contents +++ b/MatrixSDK/Data/RoomSummaryStore/CoreData/MXCoreDataRoomSummaryStore.xcdatamodeld/MXRoomSummaryCoreDataStore2.xcdatamodel/contents @@ -2,6 +2,7 @@ + diff --git a/MatrixSDK/Data/RoomSummaryStore/CoreData/Models/MXRoomLastMessageMO.swift b/MatrixSDK/Data/RoomSummaryStore/CoreData/Models/MXRoomLastMessageMO.swift index 06e96d8916..bec595cfae 100644 --- a/MatrixSDK/Data/RoomSummaryStore/CoreData/Models/MXRoomLastMessageMO.swift +++ b/MatrixSDK/Data/RoomSummaryStore/CoreData/Models/MXRoomLastMessageMO.swift @@ -27,6 +27,7 @@ public class MXRoomLastMessageMO: NSManagedObject { @NSManaged public var s_eventId: String @NSManaged public var s_originServerTs: UInt64 @NSManaged public var s_isEncrypted: Bool + @NSManaged public var s_hasDecryptionError: Bool @NSManaged public var s_sender: String @NSManaged public var s_sensitiveData: Data? @@ -44,6 +45,7 @@ public class MXRoomLastMessageMO: NSManagedObject { s_eventId = lastMessage.eventId s_originServerTs = lastMessage.originServerTs s_isEncrypted = lastMessage.isEncrypted + s_hasDecryptionError = lastMessage.hasDecryptionError s_sender = lastMessage.sender s_sensitiveData = lastMessage.sensitiveData() } diff --git a/MatrixSDK/Data/RoomSummaryStore/CoreData/Models/MXRoomSummaryMO.swift b/MatrixSDK/Data/RoomSummaryStore/CoreData/Models/MXRoomSummaryMO.swift index c886d433a9..6c3730ecb9 100644 --- a/MatrixSDK/Data/RoomSummaryStore/CoreData/Models/MXRoomSummaryMO.swift +++ b/MatrixSDK/Data/RoomSummaryStore/CoreData/Models/MXRoomSummaryMO.swift @@ -83,7 +83,7 @@ public class MXRoomSummaryMO: NSManagedObject { s_typeString = summary.roomTypeString s_typeInt = Int16(summary.roomType.rawValue) s_avatar = summary.avatar - s_displayName = summary.displayname + s_displayName = summary.displayName s_topic = summary.topic s_creatorUserId = summary.creatorUserId s_aliases = summary.aliases.joined(separator: StringArrayDelimiter) @@ -186,7 +186,7 @@ extension MXRoomSummaryMO: MXRoomSummaryProtocol { return s_avatar } - public var displayname: String? { + public var displayName: String? { return s_displayName } diff --git a/MatrixSDK/Data/Store/MXFileStore/MXFileStore.m b/MatrixSDK/Data/Store/MXFileStore/MXFileStore.m index 0284b1efc4..e2bfc932f1 100644 --- a/MatrixSDK/Data/Store/MXFileStore/MXFileStore.m +++ b/MatrixSDK/Data/Store/MXFileStore/MXFileStore.m @@ -29,7 +29,7 @@ #import "MatrixSDKSwiftHeader.h" #import "MXFileRoomSummaryStore.h" -static NSUInteger const kMXFileVersion = 82; +static NSUInteger const kMXFileVersion = 82; // Check getUnreadRoomFromStore if you update this value. Delete this comment after static NSString *const kMXFileStoreFolder = @"MXFileStore"; static NSString *const kMXFileStoreMedaDataFile = @"MXFileStore"; @@ -1004,7 +1004,7 @@ -(void)saveUnreadRooms { NSArray* rooms = [roomUnreaded allObjects]; - NSString *roomsFile = [self unreadFileForRoomsForBackup:NO]; + NSString *roomsFile = [self unreadRoomsFile]; [self saveObject:rooms toFile:roomsFile]; } @@ -1015,8 +1015,19 @@ -(void)loadUnreadRooms - (NSArray*)getUnreadRoomFromStore { - NSString *roomsFile = [self unreadFileForRoomsForBackup:NO]; - NSArray *result = [self loadUnreadRoomsStoreFromFileAt:roomsFile]; + NSString *unreadRoomsFile = [self unreadRoomsFile]; + + // TODO: Remove this migration code on the next kMXFileVersion update (>82) + // The `unreadFileForRoomsForBackup()` method should be removed. + // Likewise for the associated test `testUnreadRoomsFileMigration()` + NSString *oldUnreadRoomsFile = [self unreadFileForRoomsForBackup:NO]; + if ([[NSFileManager defaultManager] fileExistsAtPath:oldUnreadRoomsFile]) + { + MXLogDebug(@"[MXFileStore] getUnreadRoomFromStore: Migrate the unread rooms file") + [[NSFileManager defaultManager] moveItemAtPath:oldUnreadRoomsFile toPath:unreadRoomsFile error:nil]; + } + + NSArray *result = [self loadUnreadRoomsStoreFromFileAt:unreadRoomsFile]; return result; } @@ -1187,6 +1198,13 @@ - (NSString*)readReceiptsFileForRoom:(NSString*)roomId forBackup:(BOOL)backup { return [[self folderForRoom:roomId forBackup:backup] stringByAppendingPathComponent:kMXFileStoreRoomReadReceiptsFile]; } + +- (NSString*)unreadRoomsFile +{ + return [storePath stringByAppendingPathComponent:kMXFileStoreRoomUnreadRoomsFile]; +} + +// TODO: To delete when kMXFileVersion > 82 - (NSString*)unreadFileForRoomsForBackup:(BOOL)backup { return [[self folderForRoomsforBackup:backup] stringByAppendingPathComponent:kMXFileStoreRoomUnreadRoomsFile]; diff --git a/MatrixSDK/MXSDKOptions.h b/MatrixSDK/MXSDKOptions.h index 23c4f8ce76..1d59459e0b 100644 --- a/MatrixSDK/MXSDKOptions.h +++ b/MatrixSDK/MXSDKOptions.h @@ -48,7 +48,7 @@ typedef NS_ENUM(NSUInteger, MXCallTransferType) NS_ASSUME_NONNULL_BEGIN -@protocol MXBackgroundModeHandler; +@protocol MXBackgroundModeHandler, MXCryptoV2Feature; /** SDK options that can be set at the launch time. @@ -204,23 +204,18 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) BOOL enableRoomSharedHistoryOnInvite; /** - The state of the rust-based `MatrixCryptoSDK` which replaces `MatrixSDK`'s internal crypto module, - and whether it is available to a user as an option. + An object which controls the availabilty of the rust-based `MatrixCryptoSDK`. - To control which crypto module is actually used, set `enableCryptoSDK`. - - @remark NO by default. + @remark nil by default. */ -@property (nonatomic) BOOL isCryptoSDKAvailable; +@property (nonatomic, nullable) id cryptoSDKFeature; /** Use the rust-based `MatrixCryptoSDK` instead of `MatrixSDK`'s internal crypto module. - This option should only be enabled if `isCryptoSDKAvailable` is set to YES. - - @remark NO by default. + @remark this property is a convenience getter for `cryptoSDKFeature.isEnabled` */ -@property (nonatomic) BOOL enableCryptoSDK; +@property (nonatomic, readonly) BOOL enableCryptoSDK; /** Enable symmetric room key backups @@ -240,9 +235,9 @@ NS_ASSUME_NONNULL_BEGIN Enable the calculating of progress during session startup, incl counting the number of attempts to sync with the server and percentage of response data processed. - @remark NO by default + @remark the value currently depends on `enableCryptoSDK` being `YES` */ -@property (nonatomic) BOOL enableStartupProgress; +@property (nonatomic, readonly) BOOL enableStartupProgress; @end diff --git a/MatrixSDK/MXSDKOptions.m b/MatrixSDK/MXSDKOptions.m index 820cd5f632..69d7df7c3b 100644 --- a/MatrixSDK/MXSDKOptions.m +++ b/MatrixSDK/MXSDKOptions.m @@ -54,18 +54,31 @@ - (instancetype)init _authEnableRefreshTokens = NO; _enableThreads = NO; _enableRoomSharedHistoryOnInvite = NO; - - _isCryptoSDKAvailable = YES; - _enableCryptoSDK = NO; - _enableSymmetricBackup = NO; _enableNewClientInformationFeature = NO; - _enableStartupProgress = NO; } return self; } +- (BOOL)enableCryptoSDK +{ + if (!self.cryptoSDKFeature) + { + MXLogError(@"[MXSDKOptions] enableCryptoSDK: Crypto SDK feature is not configured"); + return NO; + } + return self.cryptoSDKFeature.isEnabled; +} + +- (BOOL)enableStartupProgress +{ + // The value of `enableStartupProgress` depends on `enableCryptoSDK` as the latter provides some new UX elements + // such as initial data migration. It is a good opportunity to enable startup progress as well, before it becomes + // default to all. + return self.enableCryptoSDK; +} + - (void)setRoomListDataManagerClass:(Class)roomListDataManagerClass { // Sanity check diff --git a/MatrixSDK/MXSession.h b/MatrixSDK/MXSession.h index c283598c8c..a9ce329a02 100644 --- a/MatrixSDK/MXSession.h +++ b/MatrixSDK/MXSession.h @@ -1087,7 +1087,7 @@ typedef void (^MXOnBackgroundSyncFail)(NSError *error); @return the MXRoomSummary instance. */ -- (MXRoomSummary *)roomSummaryWithRoomId:(NSString*)roomId; +- (nullable MXRoomSummary *)roomSummaryWithRoomId:(NSString*)roomId; /** Recompute all room summaries last message. diff --git a/MatrixSDK/MXSession.m b/MatrixSDK/MXSession.m index 9aa58b49d1..faccbdf74f 100644 --- a/MatrixSDK/MXSession.m +++ b/MatrixSDK/MXSession.m @@ -2509,8 +2509,12 @@ - (MXHTTPOperation*)createRoomWithParameters:(MXRoomCreationParameters*)paramete { // When the flag isDirect is turned on, only one user id is expected in the inviteArray. // The room is considered as direct only for the first mentioned user in case of several user ids. - // Note: It is not possible FTM to mark as direct a room with an invited third party. NSString *directUserId = (parameters.inviteArray.count ? parameters.inviteArray.firstObject : nil); + // Fall back on the first invite3PID address. + if (!directUserId && parameters.invite3PIDArray != nil && parameters.invite3PIDArray.count > 0) + { + directUserId = parameters.invite3PIDArray.firstObject.address; + } [self onCreatedDirectChat:response withUserId:directUserId success:success]; } else @@ -3316,7 +3320,7 @@ - (MXHTTPOperation*)eventWithEventId:(NSString*)eventId #pragma mark - Rooms summaries -- (MXRoomSummary *)roomSummaryWithRoomId:(NSString*)roomId +- (nullable MXRoomSummary *)roomSummaryWithRoomId:(NSString*)roomId { MXRoomSummary *roomSummary; @@ -3332,6 +3336,15 @@ - (MXRoomSummary *)roomSummaryWithRoomId:(NSString*)roomId { roomSummary = [[MXRoomSummary alloc] initWithSummaryModel:roomSummaryProtocol]; [roomSummary setMatrixSession:self]; + + if (roomSummary.lastMessage.hasDecryptionError) + { + // Try to decrypt it again. + // It will also trigger the mechanism to automatically retry and refresh the last message if we + // receive the key later + [self eventWithEventId:roomSummary.lastMessage.eventId inRoom:roomSummary.roomId success:nil failure:nil]; + } + roomSummaries[roomId] = roomSummary; } } @@ -3421,7 +3434,7 @@ - (void)fixRoomsSummariesLastMessageWithMaxServerPaginationCount:(NSUInteger)max MXLogDebug(@"[MXSession] fixRoomsSummariesLastMessage: Fixing last message for room %@", summary.roomId); [summary resetLastMessageWithMaxServerPaginationCount:maxServerPaginationCount onComplete:^{ - MXLogDebug(@"[MXSession] fixRoomsSummariesLastMessage:Fixing last message operation for room %@ has complete. lastMessageEventId: %@", summary.roomId, summary.lastMessage.eventId); + MXLogDebug(@"[MXSession] fixRoomsSummariesLastMessage: Fixing last message operation for room %@ has complete. lastMessageEventId: %@", summary.roomId, summary.lastMessage.eventId); dispatch_group_leave_with_progress(dispatchGroup); } failure:^(NSError *error) { MXLogDebug(@"[MXSession] fixRoomsSummariesLastMessage: Cannot fix last message for room %@ with maxServerPaginationCount: %@", summary.roomId, @(maxServerPaginationCount)); @@ -3441,7 +3454,7 @@ - (void)fixRoomsSummariesLastMessageWithMaxServerPaginationCount:(NSUInteger)max MXLogDebug(@"[MXSession] fixRoomsSummariesLastMessage: Fixing last message for room %@", summary.roomId); [summary resetLastMessageWithMaxServerPaginationCount:maxServerPaginationCount onComplete:^{ - MXLogDebug(@"[MXSession] fixRoomsSummariesLastMessage:Fixing last message operation for room %@ has complete. lastMessageEventId: %@", summary.roomId, summary.lastMessage.eventId); + MXLogDebug(@"[MXSession] fixRoomsSummariesLastMessage: Fixing last message operation for room %@ has complete. lastMessageEventId: %@", summary.roomId, summary.lastMessage.eventId); dispatch_group_leave_with_progress(dispatchGroup); } failure:^(NSError *error) { MXLogDebug(@"[MXSession] fixRoomsSummariesLastMessage: Cannot fix last message for room %@ with maxServerPaginationCount: %@", summary.roomId, @(maxServerPaginationCount)); @@ -3467,7 +3480,7 @@ - (void)fixRoomsSummariesLastMessageWithMaxServerPaginationCount:(NSUInteger)max MXLogDebug(@"[MXSession] fixRoomsSummariesLastMessage: Fixing last message for room %@", summary.roomId); [summary resetLastMessageWithMaxServerPaginationCount:maxServerPaginationCount onComplete:^{ - MXLogDebug(@"[MXSession] fixRoomsSummariesLastMessage:Fixing last message operation for room %@ has complete. lastMessageEventId: %@", summary.roomId, summary.lastMessage.eventId); + MXLogDebug(@"[MXSession] fixRoomsSummariesLastMessage: Fixing last message operation for room %@ has complete. lastMessageEventId: %@", summary.roomId, summary.lastMessage.eventId); dispatch_group_leave_with_progress(dispatchGroup); } failure:^(NSError *error) { MXLogDebug(@"[MXSession] fixRoomsSummariesLastMessage: Cannot fix last message for room %@ with maxServerPaginationCount: %@", summary.roomId, @(maxServerPaginationCount)); diff --git a/MatrixSDK/MatrixSDKVersion.m b/MatrixSDK/MatrixSDKVersion.m index 61c81973cd..da3518e635 100644 --- a/MatrixSDK/MatrixSDKVersion.m +++ b/MatrixSDK/MatrixSDKVersion.m @@ -16,4 +16,4 @@ #import -NSString *const MatrixSDKVersion = @"0.25.2"; +NSString *const MatrixSDKVersion = @"0.26.0"; diff --git a/MatrixSDK/Space/MXSpaceService.swift b/MatrixSDK/Space/MXSpaceService.swift index 15011f7dc8..ad8b7b71bd 100644 --- a/MatrixSDK/Space/MXSpaceService.swift +++ b/MatrixSDK/Space/MXSpaceService.swift @@ -783,7 +783,7 @@ public class MXSpaceService: NSObject { membersCount.members = joinedMembersCount roomSummary.membersCount = membersCount - roomSummary.displayname = spaceChildSummaryResponse.name + roomSummary.displayName = spaceChildSummaryResponse.name roomSummary.topic = spaceChildSummaryResponse.topic roomSummary.avatar = spaceChildSummaryResponse.avatarUrl roomSummary.isEncrypted = false diff --git a/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m b/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m index a549f46bee..e4cfb2516c 100644 --- a/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m +++ b/MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m @@ -401,7 +401,7 @@ - (void)contactIdentifierForCall:(MXCall *)call onComplete:(void (^)(NSString *c { if (call.isConferenceCall) { - onComplete(call.room.summary.displayname); + onComplete(call.room.summary.displayName); } else { diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index 227512f7b8..7affafe075 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -1117,7 +1117,7 @@ - (void)handleCallInvite:(MXEvent *)event } else { - _callerName = nativeRoom.summary.displayname; + _callerName = nativeRoom.summary.displayName; } } calleeId = callManager.mxSession.myUserId; diff --git a/MatrixSDKTests/Crypto/CryptoMachine/MXKeysQuerySchedulerUnitTests.swift b/MatrixSDKTests/Crypto/CryptoMachine/MXKeysQuerySchedulerUnitTests.swift index 17f91ef4d0..ee7d3b350d 100644 --- a/MatrixSDKTests/Crypto/CryptoMachine/MXKeysQuerySchedulerUnitTests.swift +++ b/MatrixSDKTests/Crypto/CryptoMachine/MXKeysQuerySchedulerUnitTests.swift @@ -88,6 +88,8 @@ class MXKeysQuerySchedulerUnitTests: XCTestCase { // MARK: - Tests + // We assert a query like so: + // |-------------| (Alice) func test_queryAlice() async { let exp = expectation(description: "exp") @@ -102,6 +104,8 @@ class MXKeysQuerySchedulerUnitTests: XCTestCase { XCTAssertQueriesCount(1) } + // We assert a query like so: + // |-----------------| (Alice, Bob) func test_queryAliceAndBob() async { let exp = expectation(description: "exp") @@ -117,6 +121,9 @@ class MXKeysQuerySchedulerUnitTests: XCTestCase { XCTAssertQueriesCount(1) } + // We assert consecutive queries like so: + // |-------------| (Alice) + // |.....--------| (Bob) func test_queryBobAfterAlice() async { let exp = expectation(description: "exp") exp.expectedFulfillmentCount = 2 @@ -139,6 +146,10 @@ class MXKeysQuerySchedulerUnitTests: XCTestCase { XCTAssertQueriesCount(2) } + // We assert parallel queries like so: + // |-------------| (Alice) + // |---------| (Alice) + // |----| (Alice) func test_executeMultipleAliceQueriesOnce() async { queryStartSpy = { self.stubbedResult = .success([ @@ -178,6 +189,10 @@ class MXKeysQuerySchedulerUnitTests: XCTestCase { XCTAssertQueriesCount(1) } + // We assert consecutive queries like so: + // |-------------| (Alice) + // |-----------| (Alice) + // |---------| (Alice) func test_executeEachAliceQuerySeparately() async { queryStartSpy = { self.stubbedResult = .success([ @@ -218,6 +233,9 @@ class MXKeysQuerySchedulerUnitTests: XCTestCase { XCTAssertQueriesCount(3) } + // We assert parallel queries like so: + // |-------------| (Alice, Bob) + // |---------| (Bob) func test_executeMultipleBobQueriesOnce() async { queryStartSpy = { self.stubbedResult = .success([ @@ -252,6 +270,9 @@ class MXKeysQuerySchedulerUnitTests: XCTestCase { XCTAssertQueriesCount(1) } + // We assert consecutive queries like so: + // |-------------| (Alice, Bob) + // |.....--------| (Bob, Carol) func test_executeSecondBobQuerySeparately() async { queryStartSpy = { self.stubbedResult = .success([ @@ -287,6 +308,11 @@ class MXKeysQuerySchedulerUnitTests: XCTestCase { XCTAssertQueriesCount(2) } + // We assert consecutive queries like so: + // |-------------| (Alice) + // |.........--------| + Bob + // |.....--------| + Carol + // |..--------| + David func test_nextQueryAggregatesPendingUsers() async { let exp = expectation(description: "exp") exp.expectedFulfillmentCount = 4 @@ -331,6 +357,11 @@ class MXKeysQuerySchedulerUnitTests: XCTestCase { XCTAssertQueriesCount(2) } + // We assert consecutive queries like so: + // |-------------| (Alice) + // |.........--------| + Bob + // |.....--------| + Carol + // |--------| (David) func test_pendingUsersResetAfterQuery() async { var exp = expectation(description: "exp") exp.expectedFulfillmentCount = 3 @@ -385,6 +416,45 @@ class MXKeysQuerySchedulerUnitTests: XCTestCase { XCTAssertQueriesCount(4) } + // We assert consecutive queries like so: + // |-------------| (Alice) + // |.........--------| (Bob) + // |----| (Bob) + func test_alreadyRunningQueriesGetUpdated() async { + let exp = expectation(description: "exp") + exp.expectedFulfillmentCount = 3 + + await query(users: ["alice"]) { response in + XCTAssertEqual(response, [ + "alice": ["A"], + ]) + exp.fulfill() + + // We need to trigger another Bob request after Alice has completed + Task.detached { + await self.query(users: ["bob"]) { response in + XCTAssertEqual(response, [ + "bob": ["B"], + ]) + exp.fulfill() + } + } + } + + await query(users: ["bob"]) { response in + XCTAssertEqual(response, [ + "bob": ["B"], + ]) + exp.fulfill() + } + + await waitForExpectations(timeout: 1) + + // At the end of making 3 queries we expect to have executed only + // one per each user + XCTAssertQueriesCount(2) + } + func test_queryFail() async { scheduler = MXKeysQueryScheduler { _ in try! await Task.sleep(nanoseconds: 1_000_000) diff --git a/MatrixSDKTests/Crypto/MXCryptoV2FactoryTests.swift b/MatrixSDKTests/Crypto/MXCryptoV2FactoryTests.swift index e38908d567..fb5057ff58 100644 --- a/MatrixSDKTests/Crypto/MXCryptoV2FactoryTests.swift +++ b/MatrixSDKTests/Crypto/MXCryptoV2FactoryTests.swift @@ -76,6 +76,8 @@ class MXCryptoV2FactoryTests: XCTestCase { let legacyStore = MXRealmCryptoStore.init(credentials: session.credentials) XCTAssertNotNil(legacyStore) XCTAssertEqual(legacyStore?.cryptoVersion, .versionLegacyDeprecated) + + await env.close() } func test_migratesExistingUser() async throws { @@ -95,6 +97,8 @@ class MXCryptoV2FactoryTests: XCTestCase { // Assert we still have legacy store but it is now marked as deprecated XCTAssertNotNil(legacyStore) XCTAssertEqual(legacyStore?.cryptoVersion, .versionLegacyDeprecated) + + await env.close() } func test_doesNotMigrateDeprecatedStore() async throws { @@ -114,5 +118,7 @@ class MXCryptoV2FactoryTests: XCTestCase { // Assert we still have legacy store which is still marked as deprecated XCTAssertNotNil(legacyStore) XCTAssertEqual(legacyStore?.cryptoVersion, .versionLegacyDeprecated) + + await env.close() } } diff --git a/MatrixSDKTests/Crypto/Migration/MXCryptoMigrationV2Tests.swift b/MatrixSDKTests/Crypto/Migration/MXCryptoMigrationV2Tests.swift index 9ea9f15c6d..e20e41541a 100644 --- a/MatrixSDKTests/Crypto/Migration/MXCryptoMigrationV2Tests.swift +++ b/MatrixSDKTests/Crypto/Migration/MXCryptoMigrationV2Tests.swift @@ -74,35 +74,36 @@ class MXCryptoMigrationV2Tests: XCTestCase { func test_migratesAccountDetails() async throws { let env = try await e2eData.startE2ETest() let legacySession = env.session - + let machine = try self.migratedOlmMachine(session: env.session) - + XCTAssertEqual(machine.userId, legacySession.myUserId) XCTAssertEqual(machine.deviceId, legacySession.myDeviceId) XCTAssertEqual(machine.deviceCurve25519Key, legacySession.crypto.deviceCurve25519Key) XCTAssertEqual(machine.deviceEd25519Key, legacySession.crypto.deviceEd25519Key) + + await env.close() } - // Temporary disable test that fails to run on CI - func xtest_canDecryptMegolmMessageAfterMigration() async throws { + func test_canDecryptMegolmMessageAfterMigration() async throws { let env = try await e2eData.startE2ETest() guard let room = env.session.room(withRoomId: env.roomId) else { throw Error.missingDependencies } - + // Send a new message in encrypted room let event = try await room.sendTextMessage("Hi bob") - + // Erase cleartext and make sure the event was indeed encrypted event.setClearData(nil) XCTAssertTrue(event.isEncrypted) XCTAssertEqual(event.content["algorithm"] as? String, kMXCryptoMegolmAlgorithm) XCTAssertNotNil(event.content["ciphertext"]) - + // Migrate the session to crypto v2 let machine = try self.migratedOlmMachine(session: env.session) - + // Decrypt the event using crypto v2 let decrypted = try machine.decryptRoomEvent(event) let result = try MXEventDecryptionResult(event: decrypted) @@ -111,6 +112,8 @@ class MXCryptoMigrationV2Tests: XCTestCase { // At this point we should be able to read back the original message after // having decrypted the event with room keys migrated earlier XCTAssertEqual(content?["body"] as? String, "Hi bob") + + await env.close() } func test_notCrossSignedAfterMigration() async throws { @@ -129,6 +132,8 @@ class MXCryptoMigrationV2Tests: XCTestCase { // As expected we cannot cross sign in v2 either XCTAssertFalse(crossSigningV2.canCrossSign) XCTAssertFalse(crossSigningV2.hasAllPrivateKeys) + + await env.close() } func test_migratesCrossSigningStatus() async throws { @@ -148,6 +153,8 @@ class MXCryptoMigrationV2Tests: XCTestCase { // And confirm that cross signing is ready XCTAssertTrue(crossSigningV2.canCrossSign) XCTAssertTrue(crossSigningV2.hasAllPrivateKeys) + + await env.close() } } diff --git a/MatrixSDKTests/MXBackgroundSyncServiceTests.swift b/MatrixSDKTests/MXBackgroundSyncServiceTests.swift index f64f9e57cb..736de954ab 100644 --- a/MatrixSDKTests/MXBackgroundSyncServiceTests.swift +++ b/MatrixSDKTests/MXBackgroundSyncServiceTests.swift @@ -391,7 +391,7 @@ class MXBackgroundSyncServiceTests: XCTestCase { case .success: let roomSummary = self.bgSyncService?.roomSummary(forRoomId: roomId) XCTAssertNotNil(roomSummary, "Room summary should be fetched") - XCTAssertEqual(roomSummary?.displayname, newName, "Room name change should be reflected") + XCTAssertEqual(roomSummary?.displayName, newName, "Room name change should be reflected") expectation?.fulfill() case .failure(let error): XCTFail("Cannot fetch the event from background sync service - error: \(error)") diff --git a/MatrixSDKTests/MXCoreDataRoomListDataManagerUnitTests.swift b/MatrixSDKTests/MXCoreDataRoomListDataManagerUnitTests.swift index fbe1ddc51e..ab883f92fc 100644 --- a/MatrixSDKTests/MXCoreDataRoomListDataManagerUnitTests.swift +++ b/MatrixSDKTests/MXCoreDataRoomListDataManagerUnitTests.swift @@ -214,7 +214,7 @@ class MXCoreDataRoomListDataManagerUnitTests: XCTestCase { // rename rooms by index for (index, summary) in result.enumerated() { - summary.displayname = "Room \(index + 1)" + summary.displayName = "Room \(index + 1)" if let event = MXEvent(fromJSON: [ "event_id": MXTools.generateTransactionId() as Any, "room_id": summary.roomId, diff --git a/MatrixSDKTests/MXLazyLoadingTests.m b/MatrixSDKTests/MXLazyLoadingTests.m index b8dffdc4ad..2bda1299b3 100644 --- a/MatrixSDKTests/MXLazyLoadingTests.m +++ b/MatrixSDKTests/MXLazyLoadingTests.m @@ -1041,8 +1041,8 @@ - (void)checkRoomSummaryDisplayNameFromHeroesWithLazyLoading:(BOOL)lazyLoading MXRoomSummary *roomSummary = [aliceSession roomSummaryWithRoomId:roomId]; - XCTAssertTrue(roomSummary.displayname.length - && ![roomSummary.displayname isEqualToString:@"Empty room"], @"Unexpected null room name: %@", roomSummary.displayname); + XCTAssertTrue(roomSummary.displayName.length + && ![roomSummary.displayName isEqualToString:@"Empty room"], @"Unexpected null room name: %@", roomSummary.displayName); [expectation fulfill]; }]; @@ -1079,8 +1079,8 @@ - (void)checkRoomSummaryDisplayNameWhenNoMoreNameWithLazyLoading:(BOOL)lazyLoadi // -> The room name should be "Bob & 2 others" MXRoomSummary *roomSummary = [aliceSession roomSummaryWithRoomId:roomId]; - XCTAssertTrue(roomSummary.displayname.length - && ![roomSummary.displayname isEqualToString:@"Empty room"], @"Unexpected null room name: %@", roomSummary.displayname); + XCTAssertTrue(roomSummary.displayName.length + && ![roomSummary.displayName isEqualToString:@"Empty room"], @"Unexpected null room name: %@", roomSummary.displayName); // Make alice do an initial sync @@ -1100,8 +1100,8 @@ - (void)checkRoomSummaryDisplayNameWhenNoMoreNameWithLazyLoading:(BOOL)lazyLoadi // -> The room name should be "Bob & 2 others" MXRoomSummary *roomSummary2 = [aliceSession2 roomSummaryWithRoomId:roomId]; - XCTAssertTrue(roomSummary2.displayname.length - && ![roomSummary2.displayname isEqualToString:@"Empty room"], @"Unexpected null room name: %@", roomSummary2.displayname); + XCTAssertTrue(roomSummary2.displayName.length + && ![roomSummary2.displayName isEqualToString:@"Empty room"], @"Unexpected null room name: %@", roomSummary2.displayName); [expectation fulfill]; @@ -1145,10 +1145,10 @@ - (void)checkRoomSummaryDisplayNameInDirectChatWithLazyLoading:(BOOL)lazyLoading [self createDirectChatScenarioWithLazyLoading:lazyLoading readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { MXRoomSummary *roomSummary = [aliceSession roomSummaryWithRoomId:roomId]; - XCTAssertTrue(roomSummary.displayname.length - && ![roomSummary.displayname isEqualToString:@"Empty room"], @"Unexpected null room name: %@", roomSummary.displayname); + XCTAssertTrue(roomSummary.displayName.length + && ![roomSummary.displayName isEqualToString:@"Empty room"], @"Unexpected null room name: %@", roomSummary.displayName); - NSString *roomDisplayName = roomSummary.displayname; + NSString *roomDisplayName = roomSummary.displayName; [aliceSession pause]; @@ -1160,7 +1160,7 @@ - (void)checkRoomSummaryDisplayNameInDirectChatWithLazyLoading:(BOOL)lazyLoading // -> The room name should be still the same MXRoomSummary *roomSummary2 = [aliceSession roomSummaryWithRoomId:roomId]; - XCTAssertEqualObjects(roomSummary2.displayname, roomDisplayName); + XCTAssertEqualObjects(roomSummary2.displayName, roomDisplayName); [expectation fulfill]; }]; diff --git a/MatrixSDKTests/MXPeekingRoomTests.m b/MatrixSDKTests/MXPeekingRoomTests.m index 66d8820f70..d9022d56ab 100644 --- a/MatrixSDKTests/MXPeekingRoomTests.m +++ b/MatrixSDKTests/MXPeekingRoomTests.m @@ -104,7 +104,7 @@ - (void)testPeekingSummary XCTAssertNotNil(peekingRoom.summary); - XCTAssertNotNil(peekingRoom.summary.displayname); + XCTAssertNotNil(peekingRoom.summary.displayName); XCTAssertNotNil(peekingRoom.summary.topic); XCTAssertEqual(peekingRoom.summary.membersCount.members, 1, @"The MXPeekingRoom state must be known now"); diff --git a/MatrixSDKTests/MXRoomListDataManagerTests.swift b/MatrixSDKTests/MXRoomListDataManagerTests.swift index eb847f4cb5..cdc93650d4 100644 --- a/MatrixSDKTests/MXRoomListDataManagerTests.swift +++ b/MatrixSDKTests/MXRoomListDataManagerTests.swift @@ -17,10 +17,12 @@ import XCTest import MatrixSDK +import OHHTTPStubs class MXRoomListDataManagerTests: XCTestCase { private var testData: MatrixSDKTestsData! + private var e2eTestData: MatrixSDKTestsE2EData! private enum Constants { static let messageText: String = "Hello there!" @@ -29,14 +31,16 @@ class MXRoomListDataManagerTests: XCTestCase { override func setUp() { MXSDKOptions.sharedInstance().roomListDataManagerClass = MXStoreRoomListDataManager.self testData = MatrixSDKTestsData() + e2eTestData = MatrixSDKTestsE2EData(matrixSDKTestsData: testData) } override func tearDown() { testData = nil + e2eTestData = nil } private var basicFetchOptions: MXRoomListDataFetchOptions { - let filterOptions = MXRoomListDataFilterOptions(showAllRoomsInHomeSpace: false) + let filterOptions = MXRoomListDataFilterOptions(showAllRoomsInHomeSpace: true) let sortOptions = MXRoomListDataSortOptions(missedNotificationsFirst: false, unreadMessagesFirst: false) return MXRoomListDataFetchOptions(filterOptions: filterOptions, sortOptions: sortOptions, @@ -53,6 +57,7 @@ class MXRoomListDataManagerTests: XCTestCase { createBasicFetcherWithBob { bobSession, initialRoom, fetcher, expectation in guard let summary = fetcher.data?.rooms.first else { XCTFail("Initial room must be available") + expectation.fulfill() return } XCTAssertEqual(summary.roomId, initialRoom.roomId, "Initial room must be fetched") @@ -249,6 +254,158 @@ class MXRoomListDataManagerTests: XCTestCase { } } + /// Test: Expect an e2ee room is added to fetcher's data + /// - Create a Bob and Alice session with an encrypted room + /// - Create a basic fetcher + /// - Alice: Send a message + /// - Expect Bob to see the last message end-to-end encrypted + func testRoomUpdateWhenReceivingEncryptedEvent() { + createBasicFetcherWithE2EBobAndAlice { aliceSession, bobSession, fetcher, expectation in + + guard let roomSummary = fetcher.data?.rooms.first else { + XCTFail("Failed to setup test conditions for Bob and Alice") + expectation.fulfill() + return + } + + var localEcho: MXEvent? + aliceSession.room(withRoomId: roomSummary.roomId).sendTextMessage(Constants.messageText, localEcho: &localEcho) { sendMessageResponse in + switch sendMessageResponse { + case .success(let eventId): + self.waitForOneSync(for: bobSession) { + + guard let lastMessage = fetcher.data?.rooms.first?.lastMessage else { + XCTFail("Failed to setup test conditions for Bob and Alice") + expectation.fulfill() + return + } + XCTAssertEqual(lastMessage.eventId, eventId, "Room's last message should point to new event") + XCTAssertTrue(lastMessage.isEncrypted, "The last message should be encrypted") + XCTAssertFalse(lastMessage.hasDecryptionError, "The last message should be readable") + + expectation.fulfill() + } + case .failure(let error): + XCTFail("Failed to setup test conditions: \(error)") + expectation.fulfill() + } + } + } + } + + /// Test: Expect a last message to report a UTD error if there is one + /// - Create a scenario where Bob is getting a UTD from Alice + /// - Expect Bob to see the last message unable to decrypt + func testRoomUpdateWithUTD() { + createFetcherWithBobAndAliceWithUTD { aliceSession, bobSession, fetcher, roomId, eventId, toDevicePayload, expectation in + guard let lastMessage = fetcher.data?.rooms.first?.lastMessage else { + XCTFail("Failed to setup test conditions for Bob and Alice") + expectation.fulfill() + return + } + + XCTAssertEqual(lastMessage.eventId, eventId, "Room's last message should point to new event") + XCTAssertTrue(lastMessage.isEncrypted, "The last message should be encrypted") + XCTAssertTrue(lastMessage.hasDecryptionError, "We should have a UTD") + + expectation.fulfill() + } + } + + /// Test: Expect a last message to recover after being a UTD + /// - Create a scenario where Bob is getting a UTD from Alice + /// - Make alice send the blocked to_device event (this simulates a late room key + /// - Expect Bob to be able to decrypt the last message + func testRoomUpdateWithLateRoomKeyFix() { + createFetcherWithBobAndAliceWithUTD { [self] aliceSession, bobSession, fetcher, roomId, eventId, toDevicePayload, expectation in + + aliceSession.matrixRestClient.sendDirectToDevice(payload: toDevicePayload) { response in + self.waitForOneSync(for: bobSession) { + guard let lastMessage = fetcher.data?.rooms.first?.lastMessage else { + XCTFail("Failed to setup test conditions for Bob and Alice") + expectation.fulfill() + return + } + + XCTAssertEqual(lastMessage.eventId, eventId) + XCTAssertFalse(lastMessage.hasDecryptionError, "The last message should be readable now") + + expectation.fulfill() + } + } + + } + } + + /// Test: Expect a last message to recover after being a UTD + /// - Create a scenario where Bob is getting a UTD from Alice + /// - Restart Bob session + /// - Make alice send the blocked to_device event (this simulates a late room key + /// - Expect Bob to be able to decrypt the last message + func testRoomUpdateWithLateRoomKeyFixAfterBobRestart() { + createFetcherWithBobAndAliceWithUTD { [self] aliceSession, bobSession, fetcher, roomId, eventId, toDevicePayload, expectation in + + guard let bobCredentials = bobSession.credentials else { + XCTFail("Cannot set up initial test conditions") + expectation.fulfill() + return + } + + // Restart Bob session + bobSession.close() + let restClient = MXRestClient(credentials: bobCredentials, unrecognizedCertificateHandler: nil) + guard let bobSession2 = MXSession(matrixRestClient: restClient) else { + XCTFail("The request should not fail"); + expectation.fulfill() + return + } + self.testData.retain(bobSession2) + bobSession2.setStore(MXFileStore(), completion: { _ in + bobSession2.start(completion: { (_) in + + guard let manager2 = bobSession2.roomListDataManager else { + XCTFail("Manager must be created before") + return + } + + let fetcher2 = manager2.fetcher(withOptions: self.basicFetchOptions) + fetcher2.paginate() + + guard let lastMessage = fetcher2.data?.rooms.first?.lastMessage else { + XCTFail("Failed to setup test conditions for Bob and Alice") + expectation.fulfill() + return + } + + // Intermediate checks: we should have a UTD + XCTAssertEqual(lastMessage.eventId, eventId, "Room's last message should point to new event") + XCTAssertTrue(lastMessage.isEncrypted, "The last message should be encrypted") + XCTAssertTrue(lastMessage.hasDecryptionError, "We should have a UTD") + + aliceSession.matrixRestClient.sendDirectToDevice(payload: toDevicePayload) { response in + self.waitForOneSync(for: bobSession2) { + // Add some latency because actions to fix the last message behind the scene happen in an unpredicatble order but they happen + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + guard let lastMessage = fetcher2.data?.rooms.first?.lastMessage else { + XCTFail("Failed to setup test conditions for Bob and Alice") + expectation.fulfill() + return + } + + // No more UTD + XCTAssertEqual(lastMessage.eventId, eventId) + XCTAssertFalse(lastMessage.hasDecryptionError, "The last message should be readable now") + + expectation.fulfill() + } + } + } + }) + }) + } + } + + // MARK: - Private private func createBasicFetcherWithBob(_ completion: @escaping (MXSession, MXRoom, MXRoomListDataFetcher, XCTestExpectation) -> Void) { @@ -290,6 +447,78 @@ class MXRoomListDataManagerTests: XCTestCase { } } + private func createBasicFetcherWithE2EBobAndAlice(_ completion: @escaping (MXSession, MXSession, MXRoomListDataFetcher, XCTestExpectation) -> Void) { + e2eTestData.doE2ETestWithAliceAndBob(inARoom: self, cryptedBob: true, warnOnUnknowDevices: false, aliceStore: MXMemoryStore(), bobStore: MXFileStore()) { aliceSession, bobSession, roomId, expectation in + guard let bobSession = bobSession, + let aliceSession = aliceSession, + let expectation = expectation else { + XCTFail("Failed to setup test conditions for Bob and Alice") + return + } + guard let manager = bobSession.roomListDataManager else { + XCTFail("Manager must be created before") + return + } + + let fetcher = manager.fetcher(withOptions: self.basicFetchOptions) + fetcher.paginate() + completion(aliceSession, bobSession, fetcher, expectation) + } + } + + /// Create a scenario with an UTD (Unable To Decrypt) for the last message + /// - Parameter closure called when scenario has been created. Among all parameters, it provides the to_device message that has been blocked, creating the UTD. + private func createFetcherWithBobAndAliceWithUTD(_ completion: @escaping (MXSession, MXSession, MXRoomListDataFetcher, String, String, MXToDevicePayload, XCTestExpectation) -> Void) { + createBasicFetcherWithE2EBobAndAlice { [self] aliceSession, bobSession, fetcher, expectation in + + guard let roomSummary = fetcher.data?.rooms.first else { + XCTFail("Failed to setup test conditions for Bob and Alice") + expectation.fulfill() + return + } + + // Prevent Alice to send the to_device message that contains the room key + var toDevicePayload: MXToDevicePayload? + HTTPStubs.stubRequests { request in + if request.url?.absoluteString.contains("sendToDevice") ?? false { + guard let httpBodyStream = request.httpBodyStream, + let body = try? JSONSerialization.jsonObject(with: Data(reading: httpBodyStream), options: []) as? [String: Any], + let map = body ["messages"] as? [String : [String : NSDictionary]] else { + return false + } + + toDevicePayload = MXToDevicePayload(eventType: "m.room.encrypted", contentMap: MXUsersDevicesMap(map: map)) + return true + } + return request.url?.absoluteString.contains("sendToDevice") ?? false + } withStubResponse: { request in + return HTTPStubsResponse(data: Data(), statusCode: 200, headers: nil) + } + + var localEcho: MXEvent? + aliceSession.room(withRoomId: roomSummary.roomId).sendTextMessage(Constants.messageText, localEcho: &localEcho) { sendMessageResponse in + HTTPStubs.removeAllStubs() + + switch sendMessageResponse { + case .success(let eventId): + self.waitForOneSync(for: bobSession) { + + guard let eventId = eventId, let toDevicePayload = toDevicePayload else { + XCTFail("Failed to setup test conditions") + expectation.fulfill() + return + } + + completion(aliceSession, bobSession, fetcher, roomSummary.roomId, eventId, toDevicePayload, expectation) + } + case .failure(let error): + XCTFail("Failed to setup test conditions: \(error)") + expectation.fulfill() + } + } + } + } + private func waitForOneSync(for session: MXSession, completion: @escaping () -> Void) { var observer: NSObjectProtocol? observer = NotificationCenter.default.addObserver(forName: .mxSessionDidSync, object: session, queue: .main) { [weak self] _ in @@ -301,3 +530,22 @@ class MXRoomListDataManagerTests: XCTestCase { } } } + +// MARK: - Data extension + +extension Data { + init(reading input: InputStream) { + self.init() + input.open() + + let bufferSize = 1024 + let buffer = UnsafeMutablePointer.allocate(capacity: bufferSize) + while input.hasBytesAvailable { + let read = input.read(buffer, maxLength: bufferSize) + self.append(buffer, count: read) + } + buffer.deallocate() + + input.close() + } +} diff --git a/MatrixSDKTests/MXRoomSummaryTests.m b/MatrixSDKTests/MXRoomSummaryTests.m index 3b5f37a3aa..0644aa9e14 100644 --- a/MatrixSDKTests/MXRoomSummaryTests.m +++ b/MatrixSDKTests/MXRoomSummaryTests.m @@ -458,7 +458,7 @@ - (void)testDisplaynameUpdate observer = [[NSNotificationCenter defaultCenter] addObserverForName:kMXRoomSummaryDidChangeNotification object:summary queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { - XCTAssertEqualObjects(summary.displayname, displayName, @"Room summary must be updated"); + XCTAssertEqualObjects(summary.displayName, displayName, @"Room summary must be updated"); [expectation fulfill]; }]; @@ -479,13 +479,13 @@ - (void)testRoomDisplaynameExcludingUsers [room state:^(MXRoomState *roomState) { // Given a room with two users. - XCTAssertEqualObjects(summary.displayname, @"mxAlice", @"A room with one other user should be given the name of that user."); + XCTAssertEqualObjects(summary.displayName, @"mxAlice", @"A room with one other user should be given the name of that user."); // When excluding the other user during a display name update. [updater updateSummaryDisplayname:summary session:bobSession withServerRoomSummary:nil roomState:roomState excludingUserIDs: @[aliceRestClient.credentials.userId]]; // Then the name of the room should no longer include the other user. - XCTAssertEqualObjects(summary.displayname, @"Empty room", @"The name of the room should not include the other user when they are excluded."); + XCTAssertEqualObjects(summary.displayName, @"Empty room", @"The name of the room should not include the other user when they are excluded."); [expectation fulfill]; }]; }]; @@ -1107,7 +1107,7 @@ - (void)testStatePassedToMXRoomSummaryUpdating [room state:^(MXRoomState *roomState) { XCTAssertEqualObjects(roomState.name, displayName); - XCTAssertEqualObjects(summary.displayname, displayName, @"Room summary must be updated"); + XCTAssertEqualObjects(summary.displayName, displayName, @"Room summary must be updated"); [expectation fulfill]; }]; diff --git a/MatrixSDKTests/MXRoomTests.m b/MatrixSDKTests/MXRoomTests.m index b461bd9a9b..3c9e04c243 100644 --- a/MatrixSDKTests/MXRoomTests.m +++ b/MatrixSDKTests/MXRoomTests.m @@ -27,6 +27,12 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-retain-cycles" +@interface MXFileStore () +- (NSString*)unreadRoomsFile; +- (NSString*)unreadFileForRoomsForBackup:(BOOL)backup; +@end + + @interface MXRoomTests : XCTestCase { MatrixSDKTestsData *matrixSDKTestsData; @@ -975,6 +981,47 @@ - (void)testMarkUnreadWithMessage }]; } +// Test migration of the MXFileStore unread rooms file that was at a bad location. +// - Have a Bob session with an unread room +// - Close that session +// - Reopen a Bob session (it simulates an app restart) +// -> The room should be still unread, meaning the migration went as expected +// TODO: Delete this test when removing the unreadFileForRoomsForBackup() method +- (void)testUnreadRoomsFileMigration +{ + // - Have a Bob session with an unread room + MXFileStore *store = [MXFileStore new]; + [matrixSDKTestsData doMXSessionTestWithBobAndARoom:self andStore:store readyToTest:^(MXSession *bobSession, MXRoom *room, XCTestExpectation *expectation) { + [room setUnread]; + + // - Close that session + NSString *roomId = room.roomId; + MXRestClient *bobRestClient = bobSession.matrixRestClient; + NSString *wrongFile = [store unreadFileForRoomsForBackup:NO]; + NSString *goodFile = [store unreadRoomsFile]; + [bobSession close]; + + // - Move the unread rooms file to the previous wrong location + [[NSFileManager defaultManager] moveItemAtPath:goodFile toPath:wrongFile error:nil]; + + // - Reopen a Bob session (it simulates an app restart) + MXSession *bobSession2 = [[MXSession alloc] initWithMatrixRestClient:bobRestClient]; + [self->matrixSDKTestsData retain:bobSession2]; + [bobSession2 setStore:[[MXFileStore alloc] init] success:^{ + + // -> The room should be still unread, meaning the migration went as expected + MXRoom *room = [bobSession2 roomWithRoomId:roomId]; + XCTAssert([room isMarkedAsUnread], @"the room should still be marked as unread"); + + [expectation fulfill]; + + } failure:^(NSError *error) { + XCTFail(@"Cannot set up intial test conditions - error: %@", error); + [expectation fulfill]; + }]; + }]; +} + @end #pragma clang diagnostic pop diff --git a/MatrixSDKTests/MXSpaceServiceTest.swift b/MatrixSDKTests/MXSpaceServiceTest.swift index 93a25d46bd..9921a002bf 100644 --- a/MatrixSDKTests/MXSpaceServiceTest.swift +++ b/MatrixSDKTests/MXSpaceServiceTest.swift @@ -196,7 +196,7 @@ class MXSpaceServiceTest: XCTestCase { XCTAssertTrue(summary.roomType == .space) XCTAssert(summary.membersCount.members == 1, "Bob must be the only one") - XCTAssertTrue(summary.displayname == expectedSpaceName) + XCTAssertTrue(summary.displayName == expectedSpaceName) XCTAssertTrue(summary.topic == expectedSpaceTopic) guard let room = space.room else { @@ -381,18 +381,18 @@ class MXSpaceServiceTest: XCTestCase { switch response { case .success(let spaceChildrenSummary): - XCTAssert(spaceChildrenSummary.spaceInfo?.displayName == spaceB.summary?.displayname) + XCTAssert(spaceChildrenSummary.spaceInfo?.displayName == spaceB.summary?.displayName) let childInfos = spaceChildrenSummary.childInfos XCTAssert(childInfos.count == 2) let childInfoSpaceC = childInfos.first { (childInfo) -> Bool in - childInfo.name == spaceC.summary?.displayname + childInfo.name == spaceC.summary?.displayName } let childInfoSpaceD = childInfos.first { (childInfo) -> Bool in - childInfo.name == spaceD.summary?.displayname + childInfo.name == spaceD.summary?.displayName } XCTAssertNotNil(childInfoSpaceC) diff --git a/MatrixSDKTests/MXStoreRoomListDataManagerUnitTests.swift b/MatrixSDKTests/MXStoreRoomListDataManagerUnitTests.swift index 562530b042..e68d6778dd 100644 --- a/MatrixSDKTests/MXStoreRoomListDataManagerUnitTests.swift +++ b/MatrixSDKTests/MXStoreRoomListDataManagerUnitTests.swift @@ -300,7 +300,7 @@ class MXStoreRoomListDataManagerUnitTests: XCTestCase { // rename rooms by index for (index, summary) in result.enumerated() { - summary.displayname = "Room \(index + 1)" + summary.displayName = "Room \(index + 1)" if let event = MXEvent(fromJSON: [ "event_id": MXTools.generateTransactionId() as Any, "room_id": summary.roomId, diff --git a/MatrixSDKTests/MXStoreTests.m b/MatrixSDKTests/MXStoreTests.m index 58db85b98f..771c237750 100644 --- a/MatrixSDKTests/MXStoreTests.m +++ b/MatrixSDKTests/MXStoreTests.m @@ -1686,7 +1686,7 @@ - (void)checkRoomSummary:(Class)mxStoreClass XCTAssert(summary); - XCTAssertEqualObjects(summary.displayname, @"A name"); + XCTAssertEqualObjects(summary.displayName, @"A name"); XCTAssertEqualObjects(summary.topic, @"A topic"); [expectation fulfill]; diff --git a/MatrixSDKTests/MatrixSDKTestsE2EData.swift b/MatrixSDKTests/MatrixSDKTestsE2EData.swift index 63b414eef0..33e217ae87 100644 --- a/MatrixSDKTests/MatrixSDKTestsE2EData.swift +++ b/MatrixSDKTests/MatrixSDKTestsE2EData.swift @@ -30,15 +30,15 @@ extension MatrixSDKTestsE2EData { self.roomId = roomId } - deinit { - let session = session - DispatchQueue.main.async { - session.close() - } + @MainActor + func close() { + session.close() } } + @MainActor func startE2ETest() async throws -> Environment { + return try await withCheckedThrowingContinuation { continuation in doE2ETestWithAlice(inARoom: nil) { session, roomId, _ in guard let session = session, let roomId = roomId else { diff --git a/MatrixSDKTests/Mocks/MockRoomSummary.swift b/MatrixSDKTests/Mocks/MockRoomSummary.swift index 3a8f3f4c5f..8d213984c9 100644 --- a/MatrixSDKTests/Mocks/MockRoomSummary.swift +++ b/MatrixSDKTests/Mocks/MockRoomSummary.swift @@ -25,7 +25,7 @@ internal class MockRoomSummary: NSObject, MXRoomSummaryProtocol { var avatar: String? - var displayname: String? + var displayName: String? var topic: String? @@ -124,6 +124,6 @@ internal class MockRoomSummary: NSObject, MXRoomSummaryProtocol { } override var description: String { - return "" + return "" } } diff --git a/Podfile b/Podfile index 3dc917aee0..6a5f5ca52e 100644 --- a/Podfile +++ b/Podfile @@ -16,7 +16,7 @@ abstract_target 'MatrixSDK' do pod 'Realm', '10.27.0' pod 'libbase58', '~> 0.1.4' - pod 'MatrixSDKCrypto', "0.2.0", :inhibit_warnings => true + pod 'MatrixSDKCrypto', "0.2.1", :inhibit_warnings => true target 'MatrixSDK-iOS' do platform :ios, '11.0' diff --git a/Podfile.lock b/Podfile.lock index d78807365e..d34cb15956 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -16,7 +16,7 @@ PODS: - AFNetworking/NSURLSession - GZIP (1.3.0) - libbase58 (0.1.4) - - MatrixSDKCrypto (0.2.0) + - MatrixSDKCrypto (0.2.1) - OHHTTPStubs (9.1.0): - OHHTTPStubs/Default (= 9.1.0) - OHHTTPStubs/Core (9.1.0) @@ -44,7 +44,7 @@ DEPENDENCIES: - AFNetworking (~> 4.0.0) - GZIP (~> 1.3.0) - libbase58 (~> 0.1.4) - - MatrixSDKCrypto (= 0.2.0) + - MatrixSDKCrypto (= 0.2.1) - OHHTTPStubs (~> 9.1.0) - OLMKit (~> 3.2.5) - Realm (= 10.27.0) @@ -65,12 +65,12 @@ SPEC CHECKSUMS: AFNetworking: 3bd23d814e976cd148d7d44c3ab78017b744cd58 GZIP: 416858efbe66b41b206895ac6dfd5493200d95b3 libbase58: 7c040313537b8c44b6e2d15586af8e21f7354efd - MatrixSDKCrypto: e1ef22aae76b5a6f030ace21a47be83864f4ff44 + MatrixSDKCrypto: 477d818bf2cc37b6cf702a290eb647bc8cf3cb1b OHHTTPStubs: 90eac6d8f2c18317baeca36698523dc67c513831 OLMKit: da115f16582e47626616874e20f7bb92222c7a51 Realm: 9ca328bd7e700cc19703799785e37f77d1a130f2 SwiftyBeaver: 84069991dd5dca07d7069100985badaca7f0ce82 -PODFILE CHECKSUM: 22dbe406b768ebe51e6de5fe02348d35a4d3d9f6 +PODFILE CHECKSUM: e6ea6492ce460203d61def97cf6ceb27f8827d70 COCOAPODS: 1.11.3