diff --git a/lib/encryption/key_manager.dart b/lib/encryption/key_manager.dart index 64e407c1..2aefd18d 100644 --- a/lib/encryption/key_manager.dart +++ b/lib/encryption/key_manager.dart @@ -69,11 +69,14 @@ class KeyManager { if (lastEvent != null && lastEvent.type == EventTypes.Encrypted && lastEvent.content['can_request_session'] == true) { - try { - maybeAutoRequest(room.id, lastEvent.content['session_id'] as String, - lastEvent.content['sender_key'] as String?); - } catch (_) { - // dispose + final sessionId = lastEvent.content.tryGet('session_id'); + final senderKey = lastEvent.content.tryGet('sender_key'); + if (sessionId != null && senderKey != null) { + maybeAutoRequest( + room.id, + sessionId, + senderKey, + ); } } } @@ -647,16 +650,16 @@ class KeyManager { } catch (e, s) { Logs().e('[LibOlm] Error decrypting room key', e, s); } - if (decrypted != null) { + final senderKey = decrypted?.tryGet('sender_key'); + if (decrypted != null && senderKey != null) { decrypted['session_id'] = sessionId; decrypted['room_id'] = roomId; await setInboundGroupSession( - roomId, sessionId, decrypted['sender_key'] as String, decrypted, + roomId, sessionId, senderKey, decrypted, forwarded: true, - senderClaimedKeys: decrypted['sender_claimed_keys'] - is Map - ? (decrypted['sender_claimed_keys'] as Map) - : {}, + senderClaimedKeys: decrypted + .tryGetMap('sender_claimed_keys') ?? + {}, uploaded: true); } } @@ -833,14 +836,24 @@ class KeyManager { Logs().i( '[KeyManager] Received key sharing request from ${event.sender}:${event.content['requesting_device_id']}...'); if (!event.content.containsKey('body')) { - Logs().i('[KeyManager] No body, doing nothing'); + Logs().w('[KeyManager] No body, doing nothing'); return; // no body } - final body = event.content['body'] as Map; + final body = event.content.tryGetMap('body'); + if (body == null) { + Logs().w('[KeyManager] Wrong type for body, doing nothing'); + return; // wrong type for body + } + final roomId = body.tryGet('room_id'); + if (roomId == null) { + Logs().w( + '[KeyManager] Wrong type for room_id or no room_id, doing nothing'); + return; // wrong type for roomId or no roomId found + } final device = client.userDeviceKeys[event.sender] ?.deviceKeys[event.content['requesting_device_id']]; if (device == null) { - Logs().i('[KeyManager] Device not found, doing nothing'); + Logs().w('[KeyManager] Device not found, doing nothing'); return; // device not found } if (device.userId == client.userID && @@ -848,16 +861,15 @@ class KeyManager { Logs().i('[KeyManager] Request is by ourself, ignoring'); return; // ignore requests by ourself } - if (body['room_id'] is! String) { - return; // wrong type for room_id - } - final room = client.getRoomById(body['room_id'] as String); + final room = client.getRoomById(roomId); if (room == null) { Logs().i('[KeyManager] Unknown room, ignoring'); return; // unknown room } - final sessionId = body['session_id']; - if (sessionId is! String) { + final sessionId = body.tryGet('session_id'); + if (sessionId == null) { + Logs().w( + '[KeyManager] Wrong type for session_id or no session_id, doing nothing'); return; // wrong type for session_id } // okay, let's see if we have this session at all @@ -867,10 +879,12 @@ class KeyManager { return; // we don't have this session anyways } if (event.content['request_id'] is! String) { + Logs().w( + '[KeyManager] Wrong type for request_id or no request_id, doing nothing'); return; // wrong type for request_id } final request = KeyManagerKeyShareRequest( - requestId: event.content['request_id'] as String, + requestId: event.content.tryGet('request_id')!, devices: [device], room: room, sessionId: sessionId, @@ -890,7 +904,7 @@ class KeyManager { await roomKeyRequest.forwardKey(); } else if (device.encryptToDevice && session.allowedAtIndex - .tryGet>(device.userId) + .tryGet>(device.userId) ?.tryGet(device.curve25519Key!) != null) { // if we know the user may see the message, then we can just forward the key. @@ -946,6 +960,7 @@ class KeyManager { (event.content['forwarding_curve25519_key_chain'] as List) .add(encryptedContent['sender_key']); if (event.content['sender_claimed_ed25519_key'] is! String) { + Logs().w('sender_claimed_ed255519_key has wrong type'); return; // wrong type } // TODO: verify that the keys work to decrypt a message @@ -987,8 +1002,13 @@ 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'] as String; - final String sessionId = event.content['session_id'] as String; + final roomId = event.content.tryGet('room_id'); + final sessionId = event.content.tryGet('session_id'); + if (roomId == null || sessionId == null) { + Logs().w( + 'Either room_id or session_id are not the expected type or missing'); + return; + } final sender_ed25519 = client.userDeviceKeys[event.sender] ?.deviceKeys[event.content['requesting_device_id']]?.ed25519Key; if (sender_ed25519 != null) { diff --git a/lib/encryption/olm_manager.dart b/lib/encryption/olm_manager.dart index 5d53441a..054aa850 100644 --- a/lib/encryption/olm_manager.dart +++ b/lib/encryption/olm_manager.dart @@ -598,35 +598,34 @@ class OlmManager { final identityKey = client.userDeviceKeys[userId]!.deviceKeys[deviceId]!.curve25519Key; for (final deviceKey in deviceKeysEntry.value.values) { - if (deviceKey is Map) { - if (fingerprintKey == null || - identityKey == null || - !deviceKey.checkJsonSignature( - fingerprintKey, userId, deviceId)) { - Logs().w( - 'Skipping invalid device key from $userId:$deviceId', - deviceKey, - ); - continue; - } - Logs().v('[OlmManager] Starting session with $userId:$deviceId'); - final session = olm.Session(); - try { - session.create_outbound( - _olmAccount!, identityKey, deviceKey['key'] as String); - await storeOlmSession(OlmSession( - key: client.userID!, - identityKey: identityKey, - sessionId: session.session_id(), - session: session, - lastReceived: - DateTime.now(), // we want to use a newly created session - )); - } catch (e, s) { - session.free(); - Logs().e( - '[LibOlm] Could not create new outbound olm session', e, s); - } + if (fingerprintKey == null || + identityKey == null || + deviceKey is! Map || + !deviceKey.checkJsonSignature(fingerprintKey, userId, deviceId) || + deviceKey['key'] is! String) { + Logs().w( + 'Skipping invalid device key from $userId:$deviceId', + deviceKey, + ); + continue; + } + Logs().v('[OlmManager] Starting session with $userId:$deviceId'); + final session = olm.Session(); + try { + session.create_outbound( + _olmAccount!, identityKey, deviceKey.tryGet('key')!); + await storeOlmSession(OlmSession( + key: client.userID!, + identityKey: identityKey, + sessionId: session.session_id(), + session: session, + lastReceived: + DateTime.now(), // we want to use a newly created session + )); + } catch (e, s) { + session.free(); + Logs() + .e('[LibOlm] Could not create new outbound olm session', e, s); } } } diff --git a/lib/encryption/ssss.dart b/lib/encryption/ssss.dart index 1a20abb0..e6dab387 100644 --- a/lib/encryption/ssss.dart +++ b/lib/encryption/ssss.dart @@ -280,8 +280,7 @@ class SSSS { } bool isSecret(String type) => - client.accountData[type] != null && - client.accountData[type]!.content['encrypted'] is Map; + client.accountData[type]?.content['encrypted'] is Map; Future getCached(String type) async { if (client.database == null) { @@ -295,9 +294,11 @@ class SSSS { bool isValid(SSSSCache dbEntry) => keys.contains(dbEntry.keyId) && dbEntry.ciphertext != null && - ((client.accountData[type]?.content['encrypted'] - as Map)[dbEntry.keyId] - as Map)['ciphertext'] == + dbEntry.keyId != null && + client.accountData[type]?.content + .tryGetMap('encrypted') + ?.tryGetMap(dbEntry.keyId!) + ?.tryGet('ciphertext') == dbEntry.ciphertext; final fromCache = _cache[type]; @@ -320,25 +321,31 @@ class SSSS { if (secretInfo == null) { throw Exception('Not found'); } - if (secretInfo.content['encrypted'] is! Map) { + final encryptedContent = + secretInfo.content.tryGetMap('encrypted'); + if (encryptedContent == null) { throw Exception('Content is not encrypted'); } - if ((secretInfo.content['encrypted'] as Map)[keyId] - is! Map) { + final enc = encryptedContent.tryGetMap(keyId); + if (enc == null) { throw Exception('Wrong / unknown key'); } - final enc = (secretInfo.content['encrypted'] as Map)[keyId] - as Map; + final ciphertext = enc.tryGet('ciphertext'); + final iv = enc.tryGet('iv'); + final mac = enc.tryGet('mac'); + if (ciphertext == null || iv == null || mac == null) { + throw Exception('Wrong types for encrypted content or missing keys.'); + } final encryptInfo = EncryptedContent( - iv: enc['iv'] as String, - ciphertext: enc['ciphertext'] as String, - mac: enc['mac'] as String); + iv: iv, + ciphertext: ciphertext, + mac: mac, + ); 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'] as String, decrypted); + await db.storeSSSSCache(type, keyId, ciphertext, decrypted); onSecretStored.add(keyId); if (_cacheCallbacks.containsKey(type) && await getCached(type) == null) { _cacheCallbacks[type]!(decrypted); @@ -388,11 +395,14 @@ class SSSS { if (content == null) { throw InvalidPassphraseException('Key has no content!'); } - final contentEncrypted = content['encrypted'] as Map; + final encryptedContent = content.tryGetMap('encrypted'); + if (encryptedContent == null) { + throw Exception('Wrong type for encrypted content!'); + } final otherKeys = - Set.from(contentEncrypted.keys.where((k) => k != keyId)); - contentEncrypted.removeWhere((k, v) => otherKeys.contains(k)); + Set.from(encryptedContent.keys.where((k) => k != keyId)); + encryptedContent.removeWhere((k, v) => otherKeys.contains(k)); // yes, we are paranoid... if (await getStored(type, keyId, key) != secret) { throw Exception('Secrets do not match up!'); @@ -401,8 +411,12 @@ class SSSS { await client.setAccountData(client.userID!, type, content); if (cacheTypes.contains(type)) { // cache the thing - final ciphertext = (contentEncrypted[keyId] - as Map)['ciphertext'] as String; + final ciphertext = encryptedContent + .tryGetMap(keyId) + ?.tryGet('ciphertext'); + if (ciphertext == null) { + throw Exception('Wrong type for ciphertext!'); + } await client.database?.storeSSSSCache(type, keyId, ciphertext, secret); onSecretStored.add(keyId); } @@ -509,8 +523,12 @@ class SSSS { return; // nope....unknown or untrusted device } // 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 as String); + final type = event.content.tryGet('name'); + if (type == null) { + Logs().i('[SSSS] Wrong data type for type param, ignoring'); + return; + } + final secret = await getCached(type); if (secret == null) { Logs() .i('[SSSS] We don\'t have the secret for $type ourself, ignoring'); @@ -544,11 +562,11 @@ class SSSS { Logs().i('[SSSS] Someone else replied?'); return; // someone replied whom we didn't send the share request to } - if (event.content['secret'] is! String) { + final secret = event.content.tryGet('secret'); + if (secret == null) { 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))) { @@ -565,9 +583,14 @@ class SSSS { if (db != null) { final keyId = keyIdFromType(request.type); if (keyId != null) { - final ciphertext = ((client.accountData[request.type]! - .content['encrypted'] as Map)[keyId] - as Map)['ciphertext'] as String; + final ciphertext = (client.accountData[request.type]!.content + .tryGetMap('encrypted')) + ?.tryGetMap(keyId) + ?.tryGet('ciphertext'); + if (ciphertext == null) { + Logs().i('[SSSS] Ciphertext is empty or not a String'); + return; + } await db.storeSSSSCache(request.type, keyId, ciphertext, secret); if (_cacheCallbacks.containsKey(request.type)) { _cacheCallbacks[request.type]!(secret); @@ -583,8 +606,10 @@ class SSSS { if (data == null) { return null; } - if (data.content['encrypted'] is Map) { - return (data.content['encrypted'] as Map).keys.toSet(); + final contentEncrypted = + data.content.tryGetMap('encrypted'); + if (contentEncrypted != null) { + return contentEncrypted.keys.toSet(); } return null; } diff --git a/lib/encryption/utils/bootstrap.dart b/lib/encryption/utils/bootstrap.dart index 34d366c0..dc728b63 100644 --- a/lib/encryption/utils/bootstrap.dart +++ b/lib/encryption/utils/bootstrap.dart @@ -104,13 +104,14 @@ class Bootstrap { for (final entry in client.accountData.entries) { final type = entry.key; final event = entry.value; - if (event.content['encrypted'] is! Map) { + final encryptedContent = + event.content.tryGetMap('encrypted'); + if (encryptedContent == null) { continue; } final validKeys = {}; final invalidKeys = {}; - for (final keyEntry - in (event.content['encrypted'] as Map).entries) { + for (final keyEntry in encryptedContent.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 4e709e7b..1f4b262a 100644 --- a/lib/msc_extensions/msc_3814_dehydrated_devices/api.dart +++ b/lib/msc_extensions/msc_3814_dehydrated_devices/api.dart @@ -48,7 +48,8 @@ extension DehydratedDeviceMatrixApi on MatrixApi { }, ); return Map.from( - response['one_time_key_counts'] as Map); + response.tryGetMap('one_time_key_counts') ?? + {}); } /// uploads a dehydrated device. diff --git a/lib/src/client.dart b/lib/src/client.dart index 28582646..e21458a3 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -298,7 +298,7 @@ class Client extends MatrixApi { final ruleset = TryGetPushRule.tryFromJson( _accountData[EventTypes.PushRules] ?.content - .tryGetMap('global') ?? + .tryGetMap('global') ?? {}); _pushruleEvaluator = PushruleEvaluator.fromRuleset(ruleset); } @@ -1068,7 +1068,7 @@ class Client extends MatrixApi { PushRuleSet? get globalPushRules { final pushrules = _accountData['m.push_rules'] ?.content - .tryGetMap('global'); + .tryGetMap('global'); return pushrules != null ? TryGetPushRule.tryFromJson(pushrules) : null; } @@ -1076,7 +1076,7 @@ class Client extends MatrixApi { PushRuleSet? get devicePushRules { final pushrules = _accountData['m.push_rules'] ?.content - .tryGetMap('device'); + .tryGetMap('device'); return pushrules != null ? TryGetPushRule.tryFromJson(pushrules) : null; } @@ -2732,15 +2732,14 @@ class Client extends MatrixApi { /// 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'] - is Map? - ? _accountData[EventTypes.PushRules]?.content['global'] - as Map? - : null; + _accountData[EventTypes.PushRules] + ?.content + .tryGetMap('global'); if (globalPushRules == null) return false; - if (globalPushRules['override'] is List) { - for (final pushRule in globalPushRules['override'] as List) { + final globalPushRulesOverride = globalPushRules.tryGetList('override'); + if (globalPushRulesOverride != null) { + for (final pushRule in globalPushRulesOverride) { if (pushRule['rule_id'] == '.m.rule.master') { return pushRule['enabled']; } @@ -2817,13 +2816,12 @@ class Client extends MatrixApi { } /// A list of mxids of users who are ignored. - 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'] as Map) - .keys) - : []; + List get ignoredUsers => + List.from(_accountData['m.ignored_user_list'] + ?.content + .tryGetMap('ignored_users') + ?.keys ?? + []); /// Ignore another user. This will clear the local cached messages to /// hide all previous messages from this user. diff --git a/lib/src/event.dart b/lib/src/event.dart index 14b563b4..c7e6f3e4 100644 --- a/lib/src/event.dart +++ b/lib/src/event.dart @@ -259,9 +259,7 @@ class Event extends MatrixEvent { String get messageType => type == EventTypes.Sticker ? MessageTypes.Sticker - : (content['msgtype'] is String - ? content['msgtype'] as String - : MessageTypes.Text); + : (content.tryGet('msgtype') ?? MessageTypes.Text); void setRedactionEvent(Event redactedBecause) { unsigned = { @@ -303,12 +301,10 @@ class Event extends MatrixEvent { } /// Returns the body of this event if it has a body. - String get text => content['body'] is String ? content['body'] as String : ''; + String get text => content.tryGet('body') ?? ''; /// Returns the formatted boy of this event if it has a formatted body. - String get formattedText => content['formatted_body'] is String - ? content['formatted_body'] as String - : ''; + String get formattedText => content.tryGet('formatted_body') ?? ''; /// Use this to get the body. String get body { @@ -401,16 +397,11 @@ 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 ?? transactionId, + txid: txid ?? unsigned?.tryGet('transaction_id') ?? eventId, ); } @@ -440,15 +431,19 @@ class Event extends MatrixEvent { content['can_request_session'] != true) { throw ('Session key not requestable'); } - await room.requestSessionKey( - content['session_id'] as String, content['sender_key'] as String); + + final sessionId = content.tryGet('session_id'); + final senderKey = content.tryGet('sender_key'); + if (sessionId == null || senderKey == null) { + throw ('Unknown session_id or sender_key'); + } + await room.requestSessionKey(sessionId, senderKey); return; } /// Gets the info map of file events, or a blank map if none present - Map get infoMap => content['info'] is Map - ? content['info'] as Map - : {}; + Map get infoMap => + content.tryGetMap('info') ?? {}; /// Gets the thumbnail info map of file events, or a blank map if nonepresent Map get thumbnailInfoMap => infoMap['thumbnail_info'] is Map @@ -471,10 +466,10 @@ 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'] as Map)['mimetype'] is String - ? (content['file'] as Map)['mimetype'] - : ''); + : (content + .tryGetMap('file') + ?.tryGet('mimetype') ?? + ''); /// Gets the mimetype of the thumbnail of a file event, or a blank string if not present String get thumbnailMimetype => thumbnailInfoMap['mimetype'] is String @@ -487,7 +482,7 @@ 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'] as Map)['url'] + ? (content.tryGetMap('file')?['url']) : content['url']; return url is String ? Uri.tryParse(url) : null; } @@ -772,22 +767,17 @@ class Event extends MatrixEvent { // if we need to strip the reply fallback. var htmlMessage = content['format'] != 'org.matrix.custom.html'; // If we have an edit, we want to operate on the new content + final newContent = content.tryGetMap('m.new_content'); if (hideEdit && relationshipType == RelationshipTypes.edit && - content.tryGet>('m.new_content') != null) { - if (plaintextBody && - (content['m.new_content'] as Map)['format'] == - 'org.matrix.custom.html') { + newContent != null) { + if (plaintextBody && newContent['format'] == 'org.matrix.custom.html') { htmlMessage = true; body = HtmlToText.convert( - (content['m.new_content'] as Map) - .tryGet('formatted_body') ?? - formattedText); + newContent.tryGet('formatted_body') ?? formattedText); } else { htmlMessage = false; - body = (content['m.new_content'] as Map) - .tryGet('body') ?? - body; + body = newContent.tryGet('body') ?? body; } } // Hide reply fallback @@ -829,33 +819,27 @@ class Event extends MatrixEvent { /// Get the relationship type of an event. `null` if there is none String? get relationshipType { - if (content.tryGet>('m.relates_to') == null) { + final mRelatesTo = content.tryGetMap('m.relates_to'); + if (mRelatesTo == null) { return null; } - if ((content['m.relates_to'] as Map) - .containsKey('rel_type')) { - if (content - .tryGet>('m.relates_to') - ?.tryGet('rel_type') == - RelationshipTypes.thread) { - return RelationshipTypes.thread; - } + final relType = mRelatesTo.tryGet('rel_type'); + if (relType == RelationshipTypes.thread) { + return RelationshipTypes.thread; } - if ((content['m.relates_to'] as Map) - .containsKey('m.in_reply_to')) { + + if (mRelatesTo.containsKey('m.in_reply_to')) { return RelationshipTypes.reply; } - return content - .tryGet>('m.relates_to') - ?.tryGet('rel_type'); + return relType; } /// Get the event ID that this relationship will reference. `null` if there is none String? get relationshipEventId { - final relatesToMap = content.tryGetMap('m.relates_to'); + final relatesToMap = content.tryGetMap('m.relates_to'); return relatesToMap?.tryGet('event_id') ?? relatesToMap - ?.tryGetMap('m.in_reply_to') + ?.tryGetMap('m.in_reply_to') ?.tryGet('event_id'); } diff --git a/lib/src/room.dart b/lib/src/room.dart index a40f7404..c29cad81 100644 --- a/lib/src/room.dart +++ b/lib/src/room.dart @@ -267,9 +267,10 @@ class Room { } if (membership == Membership.leave) { final invitation = getState(EventTypes.RoomMember, client.userID!); - if (invitation != null && invitation.unsigned?['prev_sender'] != null) { + if (invitation != null && + invitation.unsigned?.tryGet('prev_sender') != null) { final name = unsafeGetUserFromMemoryOrFallback( - invitation.unsigned?['prev_sender'] as String) + invitation.unsigned!.tryGet('prev_sender')!) .calcDisplayname(i18n: i18n); return i18n.wasDirectChatDisplayName(name); } @@ -1748,7 +1749,7 @@ class Room { return getState(EventTypes.RoomCreate)?.senderId == userId ? 100 : 0; } return powerLevelMap - .tryGetMap('users') + .tryGetMap('users') ?.tryGet(userId) ?? powerLevelMap.tryGet('users_default') ?? 0; @@ -1803,7 +1804,7 @@ class Room { final powerLevelMap = getState(EventTypes.RoomPowerLevels)?.content; if (powerLevelMap == null) return 0; return powerLevelMap - .tryGetMap('events') + .tryGetMap('events') ?.tryGet(action) ?? powerLevelMap.tryGet('state_default') ?? 50; @@ -1834,9 +1835,8 @@ class Room { final currentPowerLevelsMap = getState(EventTypes.RoomPowerLevels)?.content; if (currentPowerLevelsMap != null) { final newPowerLevelMap = currentPowerLevelsMap; - final eventsMap = newPowerLevelMap['events'] is Map - ? newPowerLevelMap['events'] as Map - : {}; + final eventsMap = newPowerLevelMap.tryGetMap('events') ?? + {}; eventsMap.addAll({ EventTypes.GroupCallPrefix: getDefaultPowerLevel(currentPowerLevelsMap), EventTypes.GroupCallMemberPrefix: @@ -1904,7 +1904,7 @@ class Room { final powerLevelsMap = getState(EventTypes.RoomPowerLevels)?.content; if (powerLevelsMap == null) return 0 <= ownPowerLevel; final pl = powerLevelsMap - .tryGetMap('events') + .tryGetMap('events') ?.tryGet(eventType) ?? powerLevelsMap.tryGet('events_default') ?? 0; @@ -1916,7 +1916,7 @@ class Room { final userLevel = getPowerLevelByUserId(userid); final notificationLevel = getState(EventTypes.RoomPowerLevels) ?.content - .tryGetMap('notifications') + .tryGetMap('notifications') ?.tryGet(notificationType) ?? 50; diff --git a/lib/src/timeline.dart b/lib/src/timeline.dart index 5964582b..7ea015bf 100644 --- a/lib/src/timeline.dart +++ b/lib/src/timeline.dart @@ -347,16 +347,16 @@ class Timeline { if (event.type == EventTypes.Encrypted && event.messageType == MessageTypes.BadEncrypted && event.content['can_request_session'] == true) { - try { + final sessionId = event.content.tryGet('session_id'); + final senderKey = event.content.tryGet('sender_key'); + if (sessionId != null && senderKey != null) { room.client.encryption?.keyManager.maybeAutoRequest( room.id, - event.content['session_id'] as String, - event.content['sender_key'] as String, + sessionId, + senderKey, tryOnlineBackup: tryOnlineBackup, onlineKeyBackupOnly: onlineKeyBackupOnly, ); - } catch (_) { - // dispose } } } @@ -387,9 +387,9 @@ class Timeline { for (i = 0; i < events.length; i++) { final searchHaystack = {events[i].eventId}; - final txnid = events[i].unsigned?['transaction_id']; + final txnid = events[i].unsigned?.tryGet('transaction_id'); if (txnid != null) { - searchHaystack.add(txnid as String); + searchHaystack.add(txnid); } if (searchNeedle.intersection(searchHaystack).isNotEmpty) { break; @@ -401,11 +401,9 @@ class Timeline { void _removeEventFromSet(Set eventSet, Event event) { eventSet.removeWhere((e) => e.matchesEventOrTransactionId(event.eventId) || - (event.unsigned != null && + event.unsigned != null && e.matchesEventOrTransactionId( - (event.unsigned?['transaction_id'] is String) - ? (event.unsigned?['transaction_id'] as String) - : null))); + event.unsigned?.tryGet('transaction_id'))); } void addAggregatedEvent(Event event) { diff --git a/lib/src/utils/device_keys_list.dart b/lib/src/utils/device_keys_list.dart index 190a6010..44456a26 100644 --- a/lib/src/utils/device_keys_list.dart +++ b/lib/src/utils/device_keys_list.dart @@ -275,7 +275,7 @@ abstract class SignableKey extends MatrixSignableKey { var haveValidSignature = false; var gotSignatureFromCache = false; final fullKeyIdBool = validSignatures - ?.tryGetMap(otherUserId) + ?.tryGetMap(otherUserId) ?.tryGet(fullKeyId); if (fullKeyIdBool == true) { haveValidSignature = true; @@ -426,14 +426,15 @@ class DeviceKeys extends SignableKey { late DateTime lastActive; String? get curve25519Key => keys['curve25519:$deviceId']; - String? get deviceDisplayName => unsigned?['device_display_name'] as String; + String? get deviceDisplayName => + unsigned?.tryGet('device_display_name'); bool? _validSelfSignature; bool get selfSigned => _validSelfSignature ?? (_validSelfSignature = (deviceId != null && signatures - ?.tryGetMap(userId) + ?.tryGetMap(userId) ?.tryGet('ed25519:$deviceId') == null ? false diff --git a/lib/src/utils/event_localizations.dart b/lib/src/utils/event_localizations.dart index 4e754e3b..857418ba 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'] as String), + event.content.tryGet('name') ?? ''), EventTypes.RoomTopic: (event, i18n, body) => i18n.changedTheChatDescriptionTo( event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), - event.content['topic'] as String), + event.content.tryGet('topic') ?? ''), EventTypes.RoomAvatar: (event, i18n, body) => i18n.changedTheChatAvatar( event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)), EventTypes.GuestAccess: (event, i18n, body) { @@ -260,7 +260,7 @@ abstract class EventLocalizations { EventTypes.Reaction: (event, i18n, body) => i18n.sentReaction( event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n), event.content - .tryGetMap('m.relates_to') + .tryGetMap('m.relates_to') ?.tryGet('key') ?? body, ), diff --git a/lib/src/utils/image_pack_extension.dart b/lib/src/utils/image_pack_extension.dart index b36453f4..d2db19e3 100644 --- a/lib/src/utils/image_pack_extension.dart +++ b/lib/src/utils/image_pack_extension.dart @@ -60,14 +60,14 @@ extension ImagePackRoomExtension on Room { addImagePack(client.accountData['im.ponies.user_emotes'], slug: 'user'); // 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'] as Map).entries) { + final rooms = packRooms?.content.tryGetMap('rooms'); + if (packRooms != null && rooms != null) { + for (final roomEntry in rooms.entries) { final roomId = roomEntry.key; final room = client.getRoomById(roomId); - if (room != null && roomEntry.value is Map) { - for (final stateKeyEntry - in (roomEntry.value as Map).entries) { + final roomEntryValue = roomEntry.value; + if (room != null && roomEntryValue is Map) { + for (final stateKeyEntry in roomEntryValue.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 dbef3808..9331043e 100644 --- a/lib/src/voip/voip.dart +++ b/lib/src/voip/voip.dart @@ -704,18 +704,20 @@ class VoIP { final groupCallId = event.stateKey; - final callType = content['m.type']; + final callType = content.tryGet('m.type'); - if (callType != GroupCallType.Video && callType != GroupCallType.Voice) { + if (callType == null || + callType != GroupCallType.Video && callType != GroupCallType.Voice) { Logs().w('Received invalid group call type $callType for room $roomId.'); return null; } - final callIntent = content['m.intent']; + final callIntent = content.tryGet('m.intent'); - if (callIntent != GroupCallIntent.Prompt && - callIntent != GroupCallIntent.Room && - callIntent != GroupCallIntent.Ring) { + if (callIntent == null || + callIntent != GroupCallIntent.Prompt && + callIntent != GroupCallIntent.Room && + callIntent != GroupCallIntent.Ring) { Logs() .w('Received invalid group call intent $callType for room $roomId.'); return null; @@ -726,8 +728,8 @@ class VoIP { voip: this, room: room, groupCallId: groupCallId, - type: callType as String, - intent: callIntent as String, + type: callType, + intent: callIntent, ); groupCalls[groupCallId!] = groupCall;