diff --git a/lib/src/client.dart b/lib/src/client.dart index 036d457..b6ef73f 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -706,7 +706,7 @@ class Client { final String olmSessionPickleString = await storeAPI.getItem("/clients/$userID/olm-sessions"); if (olmSessionPickleString != null) { - final Map> pickleMap = + final Map pickleMap = json.decode(olmSessionPickleString); for (var entry in pickleMap.entries) { for (String pickle in entry.value) { @@ -1362,56 +1362,61 @@ class Client { } Future _updateUserDeviceKeys() async { - Set trackedUserIds = await _getUserIdsInEncryptedRooms(); - trackedUserIds.add(this.userID); + try { + if (!this.isLogged()) return; + Set trackedUserIds = await _getUserIdsInEncryptedRooms(); + trackedUserIds.add(this.userID); - // Remove all userIds we no longer need to track the devices of. - _userDeviceKeys - .removeWhere((String userId, v) => !trackedUserIds.contains(userId)); + // Remove all userIds we no longer need to track the devices of. + _userDeviceKeys + .removeWhere((String userId, v) => !trackedUserIds.contains(userId)); - // Check if there are outdated device key lists. Add it to the set. - Map outdatedLists = {}; - for (String userId in trackedUserIds) { - if (!userDeviceKeys.containsKey(userId)) { - _userDeviceKeys[userId] = DeviceKeysList(userId); - } - DeviceKeysList deviceKeysList = userDeviceKeys[userId]; - if (deviceKeysList.outdated) { - outdatedLists[userId] = []; - } - } - - // Request the missing device key lists. - if (outdatedLists.isNotEmpty) { - final Map response = await this.jsonRequest( - type: HTTPType.POST, - action: "/client/r0/keys/query", - data: {"timeout": 10000, "device_keys": outdatedLists}); - final Map oldUserDeviceKeys = - Map.from(_userDeviceKeys); - for (final rawDeviceKeyListEntry in response["device_keys"].entries) { - final String userId = rawDeviceKeyListEntry.key; - _userDeviceKeys[userId].deviceKeys = {}; - for (final rawDeviceKeyEntry in rawDeviceKeyListEntry.value.entries) { - final String deviceId = rawDeviceKeyEntry.key; - _userDeviceKeys[userId].deviceKeys[deviceId] = - DeviceKeys.fromJson(rawDeviceKeyEntry.value); - if (oldUserDeviceKeys.containsKey(userId) && - _userDeviceKeys[userId].deviceKeys.containsKey(deviceId)) { - _userDeviceKeys[userId].deviceKeys[deviceId].verified = - _userDeviceKeys[userId].deviceKeys[deviceId].verified; - _userDeviceKeys[userId].deviceKeys[deviceId].blocked = - _userDeviceKeys[userId].deviceKeys[deviceId].blocked; - } else if (deviceId == this.deviceID && - _userDeviceKeys[userId].deviceKeys[deviceId].ed25519Key == - this.fingerprintKey) { - _userDeviceKeys[userId].deviceKeys[deviceId].verified = true; - } + // Check if there are outdated device key lists. Add it to the set. + Map outdatedLists = {}; + for (String userId in trackedUserIds) { + if (!userDeviceKeys.containsKey(userId)) { + _userDeviceKeys[userId] = DeviceKeysList(userId); + } + DeviceKeysList deviceKeysList = userDeviceKeys[userId]; + if (deviceKeysList.outdated) { + outdatedLists[userId] = []; } - _userDeviceKeys[userId].outdated = false; } + + // Request the missing device key lists. + if (outdatedLists.isNotEmpty) { + final Map response = await this.jsonRequest( + type: HTTPType.POST, + action: "/client/r0/keys/query", + data: {"timeout": 10000, "device_keys": outdatedLists}); + final Map oldUserDeviceKeys = + Map.from(_userDeviceKeys); + for (final rawDeviceKeyListEntry in response["device_keys"].entries) { + final String userId = rawDeviceKeyListEntry.key; + _userDeviceKeys[userId].deviceKeys = {}; + for (final rawDeviceKeyEntry in rawDeviceKeyListEntry.value.entries) { + final String deviceId = rawDeviceKeyEntry.key; + _userDeviceKeys[userId].deviceKeys[deviceId] = + DeviceKeys.fromJson(rawDeviceKeyEntry.value); + if (oldUserDeviceKeys.containsKey(userId) && + _userDeviceKeys[userId].deviceKeys.containsKey(deviceId)) { + _userDeviceKeys[userId].deviceKeys[deviceId].verified = + _userDeviceKeys[userId].deviceKeys[deviceId].verified; + _userDeviceKeys[userId].deviceKeys[deviceId].blocked = + _userDeviceKeys[userId].deviceKeys[deviceId].blocked; + } else if (deviceId == this.deviceID && + _userDeviceKeys[userId].deviceKeys[deviceId].ed25519Key == + this.fingerprintKey) { + _userDeviceKeys[userId].deviceKeys[deviceId].verified = true; + } + } + _userDeviceKeys[userId].outdated = false; + } + } + await this.storeAPI?.storeUserDeviceKeys(userDeviceKeys); + } catch (e) { + print("[LibOlm] Unable to update user device keys: " + e.toString()); } - await this.storeAPI?.storeUserDeviceKeys(userDeviceKeys); } String get fingerprintKey => encryptionEnabled diff --git a/lib/src/room.dart b/lib/src/room.dart index 86f4ee2..dfaafbf 100644 --- a/lib/src/room.dart +++ b/lib/src/room.dart @@ -123,6 +123,7 @@ class Room { } Future _storeOutboundGroupSession() async { + if (_outboundGroupSession == null) return; await client.storeAPI?.setItem( "/clients/${client.deviceID}/rooms/${this.id}/outbound_group_session", _outboundGroupSession.pickle(client.userID)); @@ -170,10 +171,11 @@ class Room { indexes: {}, key: client.userID, ); - - client.storeAPI?.setItem( - "/clients/${client.deviceID}/rooms/${this.id}/session_keys", - json.encode(sessionKeys)); + if (_fullyRestored) { + client.storeAPI?.setItem( + "/clients/${client.deviceID}/rooms/${this.id}/session_keys", + json.encode(sessionKeys)); + } } /// Returns the [Event] for the given [typeKey] and optional [stateKey]. @@ -842,8 +844,11 @@ class Room { } } } + _fullyRestored = true; } + bool _fullyRestored = false; + /// Returns a Room from a json String which comes normally from the store. If the /// state are also given, the method will await them. static Future getRoomFromTableRow( diff --git a/test_driver/famedlysdk_test.dart b/test_driver/famedlysdk_test.dart index f0d104e..555907c 100644 --- a/test_driver/famedlysdk_test.dart +++ b/test_driver/famedlysdk_test.dart @@ -31,20 +31,25 @@ void test() async { if (room.canonicalAlias?.isNotEmpty ?? false) { break; } - await room.leave(); - await room.forget(); + try { + await room.leave(); + await room.forget(); + } catch (e) { + print(e); + } } print("++++ ($testUserB) Leave all rooms ++++"); - if (testClientB.rooms.isNotEmpty) { - Room room = testClientB.rooms.first; - await room.leave(); - await room.forget(); - } - if (testClientB.rooms.isNotEmpty) { - Room room = testClientB.rooms.first; - await room.leave(); - await room.forget(); + for (int i = 0; i < 3; i++) { + if (testClientB.rooms.isNotEmpty) { + Room room = testClientB.rooms.first; + try { + await room.leave(); + await room.forget(); + } catch (e) { + print(e); + } + } } print("++++ ($testUserA) Create room and invite $testUserB ++++"); @@ -80,6 +85,8 @@ void test() async { assert(room.sessionKeys.containsKey(room.outboundGroupSession.session_id())); assert(testClientA.olmSessions[testClientB.identityKey].length == 1); assert(testClientB.olmSessions[testClientA.identityKey].length == 1); + assert(testClientA.olmSessions[testClientB.identityKey].first.session_id() == + testClientB.olmSessions[testClientA.identityKey].first.session_id()); assert(inviteRoom.sessionKeys .containsKey(room.outboundGroupSession.session_id())); assert(room.lastMessage == testMessage); @@ -92,6 +99,9 @@ void test() async { await Future.delayed(Duration(seconds: 5)); assert(testClientA.olmSessions[testClientB.identityKey].length == 1); assert(testClientB.olmSessions[testClientA.identityKey].length == 1); + assert(testClientA.olmSessions[testClientB.identityKey].first.session_id() == + testClientB.olmSessions[testClientA.identityKey].first.session_id()); + assert(room.outboundGroupSession.session_id() == currentSessionIdA); assert(inviteRoom.sessionKeys .containsKey(room.outboundGroupSession.session_id())); @@ -116,6 +126,40 @@ void test() async { print( "++++ ($testUserA) Received decrypted message: '${room.lastMessage}' ++++"); + print("++++ ($testUserA) Restore user ++++"); + FakeStore clientAStore = testClientA.storeAPI; + testClientA = null; + testClientA = Client("TestClient", debug: false); + testClientA.storeAPI = FakeStore(testClientA, clientAStore.storeMap); + await Future.delayed(Duration(seconds: 3)); + Room restoredRoom = testClientA.rooms.first; + assert(room != null); + assert(restoredRoom.id == room.id); + assert(restoredRoom.outboundGroupSession.session_id() == + room.outboundGroupSession.session_id()); + assert(restoredRoom.sessionKeys.length == 2); + assert(restoredRoom.sessionKeys.keys.first == room.sessionKeys.keys.first); + assert(restoredRoom.sessionKeys.keys.last == room.sessionKeys.keys.last); + assert(testClientA.olmSessions[testClientB.identityKey].length == 1); + assert(testClientB.olmSessions[testClientA.identityKey].length == 1); + assert(testClientA.olmSessions[testClientB.identityKey].first.session_id() == + testClientB.olmSessions[testClientA.identityKey].first.session_id()); + + print("++++ ($testUserA) Send again encrypted message: '$testMessage2' ++++"); + await restoredRoom.sendTextEvent(testMessage2); + await Future.delayed(Duration(seconds: 5)); + assert(testClientA.olmSessions[testClientB.identityKey].length == 1); + assert(testClientB.olmSessions[testClientA.identityKey].length == 1); + assert(testClientA.olmSessions[testClientB.identityKey].first.session_id() == + testClientB.olmSessions[testClientA.identityKey].first.session_id()); + assert(restoredRoom.outboundGroupSession.session_id() == currentSessionIdA); + assert(inviteRoom.sessionKeys + .containsKey(restoredRoom.outboundGroupSession.session_id())); + assert(restoredRoom.lastMessage == testMessage2); + assert(inviteRoom.lastMessage == testMessage2); + print( + "++++ ($testUserB) Received decrypted message: '${inviteRoom.lastMessage}' ++++"); + print("++++ Logout $testUserA and $testUserB ++++"); await room.leave(); await room.forget();