From 5943576b1bffe8067d1af943e23b03a03008c299 Mon Sep 17 00:00:00 2001 From: Malin Errenst Date: Fri, 9 Jun 2023 14:41:34 +0200 Subject: [PATCH] refactor: Added type casts to match refactored matrix_api_lite --- lib/encryption/key_manager.dart | 41 +++++++++++------ lib/encryption/ssss.dart | 39 +++++++++------- lib/encryption/utils/bootstrap.dart | 3 +- .../msc_3814_dehydrated_devices/api.dart | 3 +- lib/src/client.dart | 15 ++++--- lib/src/event.dart | 44 +++++++++++++------ lib/src/room.dart | 11 +++-- lib/src/timeline.dart | 11 +++-- lib/src/utils/device_keys_list.dart | 2 +- lib/src/utils/event_localizations.dart | 4 +- lib/src/utils/image_pack_extension.dart | 6 ++- lib/src/voip/voip.dart | 4 +- test/room_test.dart | 6 ++- 13 files changed, 122 insertions(+), 67 deletions(-) diff --git a/lib/encryption/key_manager.dart b/lib/encryption/key_manager.dart index 76147406..64e407c1 100644 --- a/lib/encryption/key_manager.dart +++ b/lib/encryption/key_manager.dart @@ -70,8 +70,8 @@ class KeyManager { lastEvent.type == EventTypes.Encrypted && lastEvent.content['can_request_session'] == true) { try { - maybeAutoRequest(room.id, lastEvent.content['session_id'], - lastEvent.content['sender_key']); + maybeAutoRequest(room.id, lastEvent.content['session_id'] as String, + lastEvent.content['sender_key'] as String?); } catch (_) { // dispose } @@ -638,7 +638,7 @@ class KeyManager { final sessionId = sessionEntry.key; final session = sessionEntry.value; final sessionData = session.sessionData; - Map? decrypted; + Map? decrypted; try { decrypted = json.decode(decryption.decrypt( sessionData['ephemeral'] as String, @@ -651,11 +651,11 @@ class KeyManager { decrypted['session_id'] = sessionId; decrypted['room_id'] = roomId; await setInboundGroupSession( - roomId, sessionId, decrypted['sender_key'], decrypted, + roomId, sessionId, decrypted['sender_key'] as String, decrypted, forwarded: true, - senderClaimedKeys: decrypted['sender_claimed_keys'] != null - ? Map.from( - decrypted['sender_claimed_keys']!) + senderClaimedKeys: decrypted['sender_claimed_keys'] + is Map + ? (decrypted['sender_claimed_keys'] as Map) : {}, uploaded: true); } @@ -836,6 +836,7 @@ class KeyManager { Logs().i('[KeyManager] No body, doing nothing'); return; // no body } + final body = event.content['body'] as Map; final device = client.userDeviceKeys[event.sender] ?.deviceKeys[event.content['requesting_device_id']]; if (device == null) { @@ -847,20 +848,29 @@ class KeyManager { Logs().i('[KeyManager] Request is by ourself, ignoring'); return; // ignore requests by ourself } - final room = client.getRoomById(event.content['body']['room_id']); + if (body['room_id'] is! String) { + return; // wrong type for room_id + } + final room = client.getRoomById(body['room_id'] as String); if (room == null) { Logs().i('[KeyManager] Unknown room, ignoring'); return; // unknown room } - final sessionId = event.content['body']['session_id']; + final sessionId = body['session_id']; + if (sessionId is! String) { + return; // wrong type for session_id + } // okay, let's see if we have this session at all final session = await loadInboundGroupSession(room.id, sessionId); if (session == null) { Logs().i('[KeyManager] Unknown session, ignoring'); return; // we don't have this session anyways } + if (event.content['request_id'] is! String) { + return; // wrong type for request_id + } final request = KeyManagerKeyShareRequest( - requestId: event.content['request_id'], + requestId: event.content['request_id'] as String, devices: [device], room: room, sessionId: sessionId, @@ -933,15 +943,18 @@ class KeyManager { if (event.content['forwarding_curve25519_key_chain'] is! List) { event.content['forwarding_curve25519_key_chain'] = []; } - event.content['forwarding_curve25519_key_chain'] + (event.content['forwarding_curve25519_key_chain'] as List) .add(encryptedContent['sender_key']); + if (event.content['sender_claimed_ed25519_key'] is! String) { + return; // wrong type + } // TODO: verify that the keys work to decrypt a message // alright, all checks out, let's go ahead and store this session await setInboundGroupSession(request.room.id, request.sessionId, device.curve25519Key!, event.content, forwarded: true, senderClaimedKeys: { - 'ed25519': event.content['sender_claimed_ed25519_key'], + 'ed25519': event.content['sender_claimed_ed25519_key'] as String, }); request.devices.removeWhere( (k) => k.userId == device.userId && k.deviceId == device.deviceId); @@ -974,8 +987,8 @@ class KeyManager { Logs().v('[KeyManager] not encrypted, ignoring...'); return; // the event wasn't encrypted, this is a security risk; } - final String roomId = event.content['room_id']; - final String sessionId = event.content['session_id']; + final String roomId = event.content['room_id'] as String; + final String sessionId = event.content['session_id'] as String; final sender_ed25519 = client.userDeviceKeys[event.sender] ?.deviceKeys[event.content['requesting_device_id']]?.ed25519Key; if (sender_ed25519 != null) { diff --git a/lib/encryption/ssss.dart b/lib/encryption/ssss.dart index 65b14f47..1a20abb0 100644 --- a/lib/encryption/ssss.dart +++ b/lib/encryption/ssss.dart @@ -295,8 +295,9 @@ class SSSS { bool isValid(SSSSCache dbEntry) => keys.contains(dbEntry.keyId) && dbEntry.ciphertext != null && - client.accountData[type]?.content['encrypted'][dbEntry.keyId] - ['ciphertext'] == + ((client.accountData[type]?.content['encrypted'] + as Map)[dbEntry.keyId] + as Map)['ciphertext'] == dbEntry.ciphertext; final fromCache = _cache[type]; @@ -322,17 +323,22 @@ class SSSS { if (secretInfo.content['encrypted'] is! Map) { throw Exception('Content is not encrypted'); } - if (secretInfo.content['encrypted'][keyId] is! Map) { + if ((secretInfo.content['encrypted'] as Map)[keyId] + is! Map) { throw Exception('Wrong / unknown key'); } - final enc = secretInfo.content['encrypted'][keyId]; + final enc = (secretInfo.content['encrypted'] as Map)[keyId] + as Map; final encryptInfo = EncryptedContent( - iv: enc['iv'], ciphertext: enc['ciphertext'], mac: enc['mac']); + iv: enc['iv'] as String, + ciphertext: enc['ciphertext'] as String, + mac: enc['mac'] as String); final decrypted = await decryptAes(encryptInfo, key, type); final db = client.database; if (cacheTypes.contains(type) && db != null) { // cache the thing - await db.storeSSSSCache(type, keyId, enc['ciphertext'], decrypted); + await db.storeSSSSCache( + type, keyId, enc['ciphertext'] as String, decrypted); onSecretStored.add(keyId); if (_cacheCallbacks.containsKey(type) && await getCached(type) == null) { _cacheCallbacks[type]!(decrypted); @@ -382,10 +388,11 @@ class SSSS { if (content == null) { throw InvalidPassphraseException('Key has no content!'); } + final contentEncrypted = content['encrypted'] as Map; final otherKeys = - Set.from(content['encrypted'].keys.where((k) => k != keyId)); - content['encrypted'].removeWhere((k, v) => otherKeys.contains(k)); + Set.from(contentEncrypted.keys.where((k) => k != keyId)); + contentEncrypted.removeWhere((k, v) => otherKeys.contains(k)); // yes, we are paranoid... if (await getStored(type, keyId, key) != secret) { throw Exception('Secrets do not match up!'); @@ -394,8 +401,9 @@ class SSSS { await client.setAccountData(client.userID!, type, content); if (cacheTypes.contains(type)) { // cache the thing - await client.database?.storeSSSSCache( - type, keyId, content['encrypted'][keyId]['ciphertext'], secret); + final ciphertext = (contentEncrypted[keyId] + as Map)['ciphertext'] as String; + await client.database?.storeSSSSCache(type, keyId, ciphertext, secret); onSecretStored.add(keyId); } } @@ -502,7 +510,7 @@ class SSSS { } // alright, all seems fine...let's check if we actually have the secret they are asking for final type = event.content['name']; - final secret = await getCached(type); + final secret = await getCached(type as String); if (secret == null) { Logs() .i('[SSSS] We don\'t have the secret for $type ourself, ignoring'); @@ -536,11 +544,11 @@ class SSSS { Logs().i('[SSSS] Someone else replied?'); return; // someone replied whom we didn't send the share request to } - final secret = event.content['secret']; if (event.content['secret'] is! String) { Logs().i('[SSSS] Secret wasn\'t a string'); return; // the secret wasn't a string....wut? } + final secret = event.content['secret'] as String; // let's validate if the secret is, well, valid if (_validators.containsKey(request.type) && !(await _validators[request.type]!(secret))) { @@ -557,8 +565,9 @@ class SSSS { if (db != null) { final keyId = keyIdFromType(request.type); if (keyId != null) { - final ciphertext = client.accountData[request.type]! - .content['encrypted'][keyId]['ciphertext']; + final ciphertext = ((client.accountData[request.type]! + .content['encrypted'] as Map)[keyId] + as Map)['ciphertext'] as String; await db.storeSSSSCache(request.type, keyId, ciphertext, secret); if (_cacheCallbacks.containsKey(request.type)) { _cacheCallbacks[request.type]!(secret); @@ -575,7 +584,7 @@ class SSSS { return null; } if (data.content['encrypted'] is Map) { - return data.content['encrypted'].keys.toSet(); + return (data.content['encrypted'] as Map).keys.toSet(); } return null; } diff --git a/lib/encryption/utils/bootstrap.dart b/lib/encryption/utils/bootstrap.dart index c6c91a24..34d366c0 100644 --- a/lib/encryption/utils/bootstrap.dart +++ b/lib/encryption/utils/bootstrap.dart @@ -109,7 +109,8 @@ class Bootstrap { } final validKeys = {}; final invalidKeys = {}; - for (final keyEntry in event.content['encrypted'].entries) { + for (final keyEntry + in (event.content['encrypted'] as Map).entries) { final key = keyEntry.key; final value = keyEntry.value; if (value is! Map) { diff --git a/lib/msc_extensions/msc_3814_dehydrated_devices/api.dart b/lib/msc_extensions/msc_3814_dehydrated_devices/api.dart index e815bf45..4e709e7b 100644 --- a/lib/msc_extensions/msc_3814_dehydrated_devices/api.dart +++ b/lib/msc_extensions/msc_3814_dehydrated_devices/api.dart @@ -47,7 +47,8 @@ extension DehydratedDeviceMatrixApi on MatrixApi { }, }, ); - return Map.from(response['one_time_key_counts']); + return Map.from( + response['one_time_key_counts'] as Map); } /// uploads a dehydrated device. diff --git a/lib/src/client.dart b/lib/src/client.dart index 4f2df19c..28582646 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -2731,12 +2731,16 @@ class Client extends MatrixApi { /// Whether all push notifications are muted using the [.m.rule.master] /// rule of the push rules: https://matrix.org/docs/spec/client_server/r0.6.0#m-rule-master bool get allPushNotificationsMuted { - final Map? globalPushRules = - _accountData[EventTypes.PushRules]?.content['global']; + final Map? globalPushRules = + _accountData[EventTypes.PushRules]?.content['global'] + is Map? + ? _accountData[EventTypes.PushRules]?.content['global'] + as Map? + : null; if (globalPushRules == null) return false; if (globalPushRules['override'] is List) { - for (final pushRule in globalPushRules['override']) { + for (final pushRule in globalPushRules['override'] as List) { if (pushRule['rule_id'] == '.m.rule.master') { return pushRule['enabled']; } @@ -2816,8 +2820,9 @@ class Client extends MatrixApi { List get ignoredUsers => (_accountData .containsKey('m.ignored_user_list') && _accountData['m.ignored_user_list']?.content['ignored_users'] is Map) - ? List.from( - _accountData['m.ignored_user_list']?.content['ignored_users'].keys) + ? List.from((_accountData['m.ignored_user_list'] + ?.content['ignored_users'] as Map) + .keys) : []; /// Ignore another user. This will clear the local cached messages to diff --git a/lib/src/event.dart b/lib/src/event.dart index d2090154..14b563b4 100644 --- a/lib/src/event.dart +++ b/lib/src/event.dart @@ -259,7 +259,9 @@ class Event extends MatrixEvent { String get messageType => type == EventTypes.Sticker ? MessageTypes.Sticker - : (content['msgtype'] is String ? content['msgtype'] : MessageTypes.Text); + : (content['msgtype'] is String + ? content['msgtype'] as String + : MessageTypes.Text); void setRedactionEvent(Event redactedBecause) { unsigned = { @@ -301,11 +303,12 @@ class Event extends MatrixEvent { } /// Returns the body of this event if it has a body. - String get text => content['body'] is String ? content['body'] : ''; + String get text => content['body'] is String ? content['body'] as String : ''; /// Returns the formatted boy of this event if it has a formatted body. - String get formattedText => - content['formatted_body'] is String ? content['formatted_body'] : ''; + String get formattedText => content['formatted_body'] is String + ? content['formatted_body'] as String + : ''; /// Use this to get the body. String get body { @@ -398,11 +401,16 @@ class Event extends MatrixEvent { ); } + String transactionId = eventId; + if (unsigned?['transaction_id'] is String) { + transactionId = unsigned?['transaction_id'] as String; + } + // we do not remove the event here. It will automatically be updated // in the `sendEvent` method to transition -1 -> 0 -> 1 -> 2 return await room.sendEvent( content, - txid: txid ?? unsigned?['transaction_id'] ?? eventId, + txid: txid ?? transactionId, ); } @@ -432,13 +440,15 @@ class Event extends MatrixEvent { content['can_request_session'] != true) { throw ('Session key not requestable'); } - await room.requestSessionKey(content['session_id'], content['sender_key']); + await room.requestSessionKey( + content['session_id'] as String, content['sender_key'] as String); return; } /// Gets the info map of file events, or a blank map if none present - Map get infoMap => - content['info'] is Map ? content['info'] : {}; + Map get infoMap => content['info'] is Map + ? content['info'] as Map + : {}; /// Gets the thumbnail info map of file events, or a blank map if nonepresent Map get thumbnailInfoMap => infoMap['thumbnail_info'] is Map @@ -461,8 +471,9 @@ class Event extends MatrixEvent { /// Gets the mimetype of the attachment of a file event, or a blank string if not present String get attachmentMimetype => infoMap['mimetype'] is String ? infoMap['mimetype'].toLowerCase() - : (content['file'] is Map && content['file']['mimetype'] is String - ? content['file']['mimetype'] + : (content['file'] is Map && + (content['file'] as Map)['mimetype'] is String + ? (content['file'] as Map)['mimetype'] : ''); /// Gets the mimetype of the thumbnail of a file event, or a blank string if not present @@ -475,7 +486,9 @@ class Event extends MatrixEvent { /// Gets the underlying mxc url of an attachment of a file event, or null if not present Uri? get attachmentMxcUrl { - final url = isAttachmentEncrypted ? content['file']['url'] : content['url']; + final url = isAttachmentEncrypted + ? (content['file'] as Map)['url'] + : content['url']; return url is String ? Uri.tryParse(url) : null; } @@ -763,7 +776,8 @@ class Event extends MatrixEvent { relationshipType == RelationshipTypes.edit && content.tryGet>('m.new_content') != null) { if (plaintextBody && - content['m.new_content']['format'] == 'org.matrix.custom.html') { + (content['m.new_content'] as Map)['format'] == + 'org.matrix.custom.html') { htmlMessage = true; body = HtmlToText.convert( (content['m.new_content'] as Map) @@ -818,7 +832,8 @@ class Event extends MatrixEvent { if (content.tryGet>('m.relates_to') == null) { return null; } - if (content['m.relates_to'].containsKey('rel_type')) { + if ((content['m.relates_to'] as Map) + .containsKey('rel_type')) { if (content .tryGet>('m.relates_to') ?.tryGet('rel_type') == @@ -826,7 +841,8 @@ class Event extends MatrixEvent { return RelationshipTypes.thread; } } - if (content['m.relates_to'].containsKey('m.in_reply_to')) { + if ((content['m.relates_to'] as Map) + .containsKey('m.in_reply_to')) { return RelationshipTypes.reply; } return content diff --git a/lib/src/room.dart b/lib/src/room.dart index 16977858..a40f7404 100644 --- a/lib/src/room.dart +++ b/lib/src/room.dart @@ -269,7 +269,7 @@ class Room { final invitation = getState(EventTypes.RoomMember, client.userID!); if (invitation != null && invitation.unsigned?['prev_sender'] != null) { final name = unsafeGetUserFromMemoryOrFallback( - invitation.unsigned?['prev_sender']) + invitation.unsigned?['prev_sender'] as String) .calcDisplayname(i18n: i18n); return i18n.wasDirectChatDisplayName(name); } @@ -1315,8 +1315,9 @@ class Room { Future removeFromDirectChat() async { final directChats = client.directChats.copy(); for (final k in directChats.keys) { - if (directChats[k] is List && directChats[k].contains(id)) { - directChats[k].remove(id); + final directChat = directChats[k]; + if (directChat is List && directChat.contains(id)) { + directChat.remove(id); } } @@ -1833,7 +1834,9 @@ class Room { final currentPowerLevelsMap = getState(EventTypes.RoomPowerLevels)?.content; if (currentPowerLevelsMap != null) { final newPowerLevelMap = currentPowerLevelsMap; - final eventsMap = newPowerLevelMap['events'] ?? {}; + final eventsMap = newPowerLevelMap['events'] is Map + ? newPowerLevelMap['events'] as Map + : {}; eventsMap.addAll({ EventTypes.GroupCallPrefix: getDefaultPowerLevel(currentPowerLevelsMap), EventTypes.GroupCallMemberPrefix: diff --git a/lib/src/timeline.dart b/lib/src/timeline.dart index 2da0d001..5964582b 100644 --- a/lib/src/timeline.dart +++ b/lib/src/timeline.dart @@ -350,8 +350,8 @@ class Timeline { try { room.client.encryption?.keyManager.maybeAutoRequest( room.id, - event.content['session_id'], - event.content['sender_key'], + event.content['session_id'] as String, + event.content['sender_key'] as String, tryOnlineBackup: tryOnlineBackup, onlineKeyBackupOnly: onlineKeyBackupOnly, ); @@ -389,7 +389,7 @@ class Timeline { final txnid = events[i].unsigned?['transaction_id']; if (txnid != null) { - searchHaystack.add(txnid); + searchHaystack.add(txnid as String); } if (searchNeedle.intersection(searchHaystack).isNotEmpty) { break; @@ -402,7 +402,10 @@ class Timeline { eventSet.removeWhere((e) => e.matchesEventOrTransactionId(event.eventId) || (event.unsigned != null && - e.matchesEventOrTransactionId(event.unsigned?['transaction_id']))); + e.matchesEventOrTransactionId( + (event.unsigned?['transaction_id'] is String) + ? (event.unsigned?['transaction_id'] as String) + : null))); } void addAggregatedEvent(Event event) { diff --git a/lib/src/utils/device_keys_list.dart b/lib/src/utils/device_keys_list.dart index 7efd2bdc..190a6010 100644 --- a/lib/src/utils/device_keys_list.dart +++ b/lib/src/utils/device_keys_list.dart @@ -426,7 +426,7 @@ class DeviceKeys extends SignableKey { late DateTime lastActive; String? get curve25519Key => keys['curve25519:$deviceId']; - String? get deviceDisplayName => unsigned?['device_display_name']; + String? get deviceDisplayName => unsigned?['device_display_name'] as String; bool? _validSelfSignature; bool get selfSigned => diff --git a/lib/src/utils/event_localizations.dart b/lib/src/utils/event_localizations.dart index c39287d7..4e754e3b 100644 --- a/lib/src/utils/event_localizations.dart +++ b/lib/src/utils/event_localizations.dart @@ -204,11 +204,11 @@ abstract class EventLocalizations { event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)), EventTypes.RoomName: (event, i18n, body) => i18n.changedTheChatNameTo( event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), - event.content['name']), + event.content['name'] as String), EventTypes.RoomTopic: (event, i18n, body) => i18n.changedTheChatDescriptionTo( event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), - event.content['topic']), + event.content['topic'] as String), EventTypes.RoomAvatar: (event, i18n, body) => i18n.changedTheChatAvatar( event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)), EventTypes.GuestAccess: (event, i18n, body) { diff --git a/lib/src/utils/image_pack_extension.dart b/lib/src/utils/image_pack_extension.dart index 00b97430..b36453f4 100644 --- a/lib/src/utils/image_pack_extension.dart +++ b/lib/src/utils/image_pack_extension.dart @@ -61,11 +61,13 @@ extension ImagePackRoomExtension on Room { // next we add all the external image packs final packRooms = client.accountData['im.ponies.emote_rooms']; if (packRooms != null && packRooms.content['rooms'] is Map) { - for (final roomEntry in packRooms.content['rooms'].entries) { + for (final roomEntry + in (packRooms.content['rooms'] as Map).entries) { final roomId = roomEntry.key; final room = client.getRoomById(roomId); if (room != null && roomEntry.value is Map) { - for (final stateKeyEntry in roomEntry.value.entries) { + for (final stateKeyEntry + in (roomEntry.value as Map).entries) { final stateKey = stateKeyEntry.key; final fallbackSlug = '${room.getLocalizedDisplayname()}-${stateKey.isNotEmpty ? '$stateKey-' : ''}${room.id}'; diff --git a/lib/src/voip/voip.dart b/lib/src/voip/voip.dart index 3bd19c23..dbef3808 100644 --- a/lib/src/voip/voip.dart +++ b/lib/src/voip/voip.dart @@ -726,8 +726,8 @@ class VoIP { voip: this, room: room, groupCallId: groupCallId, - type: callType, - intent: callIntent, + type: callType as String, + intent: callIntent as String, ); groupCalls[groupCallId!] = groupCall; diff --git a/test/room_test.dart b/test/room_test.dart index 04ef1813..1496d0d4 100644 --- a/test/room_test.dart +++ b/test/room_test.dart @@ -1042,8 +1042,10 @@ void main() { test('pushRuleState', () async { expect(room.pushRuleState, PushRuleState.mentionsOnly); - matrix.accountData['m.push_rules']?.content['global']['override'].add( - matrix.accountData['m.push_rules']?.content['global']['room'][0]); + ((matrix.accountData['m.push_rules']?.content['global'] + as Map)['override'] as List) + .add(((matrix.accountData['m.push_rules']?.content['global'] + as Map)['room'] as List)[0]); expect(room.pushRuleState, PushRuleState.dontNotify); });