refactor: null safety for all utils

This commit is contained in:
Lukas Lihotzki 2021-09-23 18:24:43 +02:00
parent 687a6341f1
commit a7818bbd0f
10 changed files with 89 additions and 101 deletions

View File

@ -864,7 +864,8 @@ class KeyManager {
}
} else if (event.type == EventTypes.ForwardedRoomKey) {
// we *received* an incoming key request
if (event.encryptedContent == null) {
final encryptedContent = event.encryptedContent;
if (encryptedContent == null) {
return; // event wasn't encrypted, this is a security risk
}
final request = outgoingShareRequests.values.firstWhereOrNull((r) =>
@ -876,7 +877,7 @@ class KeyManager {
}
final device = request.devices.firstWhereOrNull((d) =>
d.userId == event.sender &&
d.curve25519Key == event.encryptedContent['sender_key']);
d.curve25519Key == encryptedContent['sender_key']);
if (device == null) {
return; // someone we didn't send our request to replied....better ignore this
}
@ -885,7 +886,7 @@ class KeyManager {
event.content['forwarding_curve25519_key_chain'] = <String>[];
}
event.content['forwarding_curve25519_key_chain']
.add(event.encryptedContent['sender_key']);
.add(encryptedContent['sender_key']);
// TODO: verify that the keys work to decrypt a message
// alright, all checks out, let's go ahead and store this session
setInboundGroupSession(
@ -920,7 +921,8 @@ class KeyManager {
} else if (event.type == EventTypes.RoomKey) {
Logs().v(
'[KeyManager] Received room key with session ${event.content['session_id']}');
if (event.encryptedContent == null) {
final encryptedContent = event.encryptedContent;
if (encryptedContent == null) {
Logs().v('[KeyManager] not encrypted, ignoring...');
return; // the event wasn't encrypted, this is a security risk;
}
@ -932,8 +934,8 @@ class KeyManager {
event.content['sender_claimed_ed25519_key'] = sender_ed25519;
}
Logs().v('[KeyManager] Keeping room key');
setInboundGroupSession(roomId, sessionId,
event.encryptedContent['sender_key'], event.content,
setInboundGroupSession(
roomId, sessionId, encryptedContent['sender_key'], event.content,
forwarded: false);
}
}
@ -972,11 +974,11 @@ class RoomKeyRequest extends ToDeviceEvent {
KeyManager keyManager;
KeyManagerKeyShareRequest request;
RoomKeyRequest.fromToDeviceEvent(
ToDeviceEvent toDeviceEvent, this.keyManager, this.request) {
sender = toDeviceEvent.sender;
content = toDeviceEvent.content;
type = toDeviceEvent.type;
}
ToDeviceEvent toDeviceEvent, this.keyManager, this.request)
: super(
sender: toDeviceEvent.sender,
content: toDeviceEvent.content,
type: toDeviceEvent.type);
Room get room => request.room;

View File

@ -653,11 +653,12 @@ class OlmManager {
if (event.type == EventTypes.Dummy) {
// We receive dan encrypted m.dummy. This means that the other end was not able to
// decrypt our last message. So, we re-send it.
if (event.encryptedContent == null || client.database == null) {
final encryptedContent = event.encryptedContent;
if (encryptedContent == null || client.database == null) {
return;
}
final device = client.getUserDeviceKeysByCurve25519Key(
event.encryptedContent.tryGet<String>('sender_key') ?? '');
encryptedContent.tryGet<String>('sender_key') ?? '');
if (device == null) {
return; // device not found
}

View File

@ -509,9 +509,10 @@ class SSSS {
} else if (event.type == EventTypes.SecretSend) {
// receiving a secret we asked for
Logs().i('[SSSS] Received shared secret...');
final encryptedContent = event.encryptedContent;
if (event.sender != client.userID ||
!pendingShareRequests.containsKey(event.content['request_id']) ||
event.encryptedContent == null) {
encryptedContent == null) {
Logs().i('[SSSS] Not by us or unknown request');
return; // we have no idea what we just received
}
@ -519,7 +520,7 @@ class SSSS {
// alright, as we received a known request id, let's check if the sender is valid
final device = request.devices.firstWhereOrNull((d) =>
d.userId == event.sender &&
d.curve25519Key == event.encryptedContent['sender_key']);
d.curve25519Key == encryptedContent['sender_key']);
if (device == null) {
Logs().i('[SSSS] Someone else replied?');
return; // someone replied whom we didn't send the share request to

View File

@ -1,4 +1,3 @@
// @dart=2.9
/*
* Famedly Matrix SDK
* Copyright (C) 2019, 2020, 2021 Famedly GmbH

View File

@ -1,4 +1,3 @@
// @dart=2.9
/*
* Famedly Matrix SDK
* Copyright (C) 2019, 2020, 2021 Famedly GmbH
@ -17,6 +16,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import 'package:collection/collection.dart';
import '../../encryption.dart';
import '../../matrix.dart';
import '../event.dart';
@ -77,7 +78,7 @@ abstract class EventLocalizations {
// This map holds how to localize event types, and thus which event types exist.
// If an event exists but it does not have a localized body, set its callback to null
static final Map<String,
String Function(Event event, MatrixLocalizations i18n, String body)>
String Function(Event event, MatrixLocalizations i18n, String body)?>
localizationsMap = {
EventTypes.Sticker: (event, i18n, body) =>
i18n.sentASticker(event.sender.calcDisplayname()),
@ -91,11 +92,9 @@ abstract class EventLocalizations {
i18n.createdTheChat(event.sender.calcDisplayname()),
EventTypes.RoomTombstone: (event, i18n, body) => i18n.roomHasBeenUpgraded,
EventTypes.RoomJoinRules: (event, i18n, body) {
final joinRules = JoinRules.values.firstWhere(
(r) =>
r.toString().replaceAll('JoinRules.', '') ==
event.content['join_rule'],
orElse: () => null);
final joinRules = JoinRules.values.firstWhereOrNull((r) =>
r.toString().replaceAll('JoinRules.', '') ==
event.content['join_rule']);
if (joinRules == null) {
return i18n.changedTheJoinRules(event.sender.calcDisplayname());
} else {
@ -177,11 +176,9 @@ abstract class EventLocalizations {
EventTypes.RoomAvatar: (event, i18n, body) =>
i18n.changedTheChatAvatar(event.sender.calcDisplayname()),
EventTypes.GuestAccess: (event, i18n, body) {
final guestAccess = GuestAccess.values.firstWhere(
(r) =>
r.toString().replaceAll('GuestAccess.', '') ==
event.content['guest_access'],
orElse: () => null);
final guestAccess = GuestAccess.values.firstWhereOrNull((r) =>
r.toString().replaceAll('GuestAccess.', '') ==
event.content['guest_access']);
if (guestAccess == null) {
return i18n.changedTheGuestAccessRules(event.sender.calcDisplayname());
} else {
@ -190,11 +187,9 @@ abstract class EventLocalizations {
}
},
EventTypes.HistoryVisibility: (event, i18n, body) {
final historyVisibility = HistoryVisibility.values.firstWhere(
(r) =>
r.toString().replaceAll('HistoryVisibility.', '') ==
event.content['history_visibility'],
orElse: () => null);
final historyVisibility = HistoryVisibility.values.firstWhereOrNull((r) =>
r.toString().replaceAll('HistoryVisibility.', '') ==
event.content['history_visibility']);
if (historyVisibility == null) {
return i18n.changedTheHistoryVisibility(event.sender.calcDisplayname());
} else {

View File

@ -1,4 +1,3 @@
// @dart=2.9
/*
* Famedly Matrix SDK
* Copyright (C) 2021 Famedly GmbH
@ -17,6 +16,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import 'package:collection/collection.dart';
import 'package:html/parser.dart';
import 'package:html/dom.dart';
import 'package:html_unescape/html_unescape.dart';
@ -71,9 +72,9 @@ class HtmlToText {
}
final language =
RegExp(r'language-(\w+)', multiLine: false, caseSensitive: false)
.firstMatch(match.group(1));
.firstMatch(match.group(1)!);
if (language != null) {
text = language.group(1) + text;
text = language.group(1)! + text;
}
return text;
}
@ -116,17 +117,16 @@ class HtmlToText {
opts.listDepth++;
final entries = _listChildNodes(opts, node, {'li'});
opts.listDepth--;
var entry = 1;
if (node.attributes['start'] is String &&
RegExp(r'^[0-9]+$', multiLine: false)
.hasMatch(node.attributes['start'])) {
entry = int.parse(node.attributes['start']);
}
final startStr = node.attributes['start'];
final start = (startStr is String &&
RegExp(r'^[0-9]+$', multiLine: false).hasMatch(startStr))
? int.parse(startStr)
: 1;
return entries
.map((s) =>
.mapIndexed((index, s) =>
(' ' * opts.listDepth) +
'${entry++}. ' +
'${start + index}. ' +
s.replaceAll('\n', '\n' + (' ' * opts.listDepth) + ' '))
.join('\n');
}
@ -134,14 +134,14 @@ class HtmlToText {
static const _listBulletPoints = <String>['', '', '', ''];
static List<String> _listChildNodes(_ConvertOpts opts, Element node,
[Iterable<String> types]) {
[Iterable<String>? types]) {
final replies = <String>[];
for (final child in node.nodes) {
if (types != null &&
types.isNotEmpty &&
((child is Text) ||
((child is Element) &&
!types.contains(child.localName.toLowerCase())))) {
!types.contains(child.localName!.toLowerCase())))) {
continue;
}
replies.add(_walkNode(opts, child));
@ -166,7 +166,7 @@ class HtmlToText {
var reply = '';
var lastTag = '';
for (final child in node.nodes) {
final thisTag = child is Element ? child.localName.toLowerCase() : '';
final thisTag = child is Element ? child.localName!.toLowerCase() : '';
if (thisTag == 'p' && lastTag == 'p') {
reply += '\n\n';
} else if (_blockTags.contains(thisTag) &&
@ -187,7 +187,7 @@ class HtmlToText {
// ignore \n between single nodes
return node.text == '\n' ? '' : node.text;
} else if (node is Element) {
final tag = node.localName.toLowerCase();
final tag = node.localName!.toLowerCase();
switch (tag) {
case 'em':
case 'i':

View File

@ -1,4 +1,3 @@
// @dart=2.9
/*
* Famedly Matrix SDK
* Copyright (C) 2020, 2021 Famedly GmbH
@ -24,10 +23,10 @@ import '../room.dart';
extension ImagePackRoomExtension on Room {
/// Get all the active image packs for the specified [usage], mapped by their slug
Map<String, ImagePackContent> getImagePacks([ImagePackUsage usage]) {
Map<String, ImagePackContent> getImagePacks([ImagePackUsage? usage]) {
final allMxcs = <Uri>{}; // used for easy deduplication
final packs = <String, ImagePackContent>{};
final addImagePack = (BasicEvent event, {Room room, String slug}) {
final addImagePack = (BasicEvent? event, {Room? room, String? slug}) {
if (event == null) return;
final imagePack = event.parsedImagePackContent;
final finalSlug = slugify(slug ?? 'pack');
@ -42,17 +41,16 @@ extension ImagePackRoomExtension on Room {
!imageUsage.contains(usage)) {
continue;
}
if (!packs.containsKey(finalSlug)) {
packs[finalSlug] = ImagePackContent.fromJson(<String, dynamic>{});
packs[finalSlug].pack.displayName = imagePack.pack.displayName ??
room?.displayname ??
finalSlug ??
'';
packs[finalSlug].pack.avatarUrl =
imagePack.pack.avatarUrl ?? room?.avatar;
packs[finalSlug].pack.attribution = imagePack.pack.attribution;
}
packs[finalSlug].images[entry.key] = image;
packs
.putIfAbsent(
finalSlug,
() => ImagePackContent.fromJson({})
..pack.displayName = imagePack.pack.displayName ??
room?.displayname ??
finalSlug
..pack.avatarUrl = imagePack.pack.avatarUrl ?? room?.avatar
..pack.attribution = imagePack.pack.attribution)
.images[entry.key] = image;
allMxcs.add(image.url);
}
};
@ -89,7 +87,7 @@ extension ImagePackRoomExtension on Room {
/// Get a flat view of all the image packs of a specified [usage], that is a map of all
/// slugs to a map of the image code to their mxc url
Map<String, Map<String, String>> getImagePacksFlat([ImagePackUsage usage]) =>
Map<String, Map<String, String>> getImagePacksFlat([ImagePackUsage? usage]) =>
getImagePacks(usage).map((k, v) =>
MapEntry(k, v.images.map((k, v) => MapEntry(k, v.url.toString()))));
}

View File

@ -1,4 +1,3 @@
// @dart=2.9
/*
* Famedly Matrix SDK
* Copyright (C) 2020, 2021 Famedly GmbH

View File

@ -1,4 +1,3 @@
// @dart=2.9
/*
* Famedly Matrix SDK
* Copyright (C) 2020, 2021 Famedly GmbH
@ -37,7 +36,7 @@ class SpoilerSyntax extends TagSyntax {
@override
Node close(InlineParser parser, Delimiter opener, Delimiter closer,
{List<Node> Function() getChildren}) {
{required List<Node> Function() getChildren}) {
final children = getChildren();
final newChildren = <Node>[];
var searchingForReason = true;
@ -72,16 +71,16 @@ class SpoilerSyntax extends TagSyntax {
}
class EmoteSyntax extends InlineSyntax {
final Map<String, Map<String, String>> Function() getEmotePacks;
Map<String, Map<String, String>> emotePacks;
final Map<String, Map<String, String>> Function()? getEmotePacks;
Map<String, Map<String, String>>? emotePacks;
EmoteSyntax(this.getEmotePacks) : super(r':(?:([-\w]+)~)?([-\w]+):');
@override
bool onMatch(InlineParser parser, Match match) {
emotePacks ??= getEmotePacks?.call() ?? <String, Map<String, String>>{};
final emotePacks = this.emotePacks ??= getEmotePacks?.call() ?? {};
final pack = match[1] ?? '';
final emote = match[2];
String mxc;
String? mxc;
if (pack.isEmpty) {
// search all packs
for (final emotePack in emotePacks.values) {
@ -91,11 +90,11 @@ class EmoteSyntax extends InlineSyntax {
}
}
} else {
mxc = emotePacks[pack] != null ? emotePacks[pack][emote] : null;
mxc = emotePacks[pack]?[emote];
}
if (mxc == null) {
// emote not found. Insert the whole thing as plain text
parser.addNode(Text(match[0]));
parser.addNode(Text(match[0]!));
return true;
}
final element = Element.empty('img');
@ -116,8 +115,8 @@ class InlineLatexSyntax extends TagSyntax {
@override
bool onMatch(InlineParser parser, Match match) {
final element =
Element('span', [Element.text('code', htmlEscape.convert(match[1]))]);
element.attributes['data-mx-maths'] = htmlAttrEscape.convert(match[1]);
Element('span', [Element.text('code', htmlEscape.convert(match[1]!))]);
element.attributes['data-mx-maths'] = htmlAttrEscape.convert(match[1]!);
parser.addNode(element);
return true;
}
@ -136,11 +135,11 @@ class BlockLatexSyntax extends BlockSyntax {
var first = true;
while (!parser.isDone) {
final match = endPattern.firstMatch(parser.current);
if (match == null || (first && match.group(1).trim().isEmpty)) {
if (match == null || (first && match[1]!.trim().isEmpty)) {
childLines.add(parser.current);
parser.advance();
} else {
childLines.add(match.group(1));
childLines.add(match[1]!);
parser.advance();
break;
}
@ -171,10 +170,10 @@ class PillSyntax extends InlineSyntax {
bool onMatch(InlineParser parser, Match match) {
if (match.start > 0 &&
!RegExp(r'[\s.!?:;\(]').hasMatch(match.input[match.start - 1])) {
parser.addNode(Text(match[0]));
parser.addNode(Text(match[0]!));
return true;
}
final identifier = match[1];
final identifier = match[1]!;
final element = Element.text('a', htmlEscape.convert(identifier));
element.attributes['href'] =
htmlAttrEscape.convert('https://matrix.to/#/$identifier');
@ -184,19 +183,19 @@ class PillSyntax extends InlineSyntax {
}
class MentionSyntax extends InlineSyntax {
final String Function(String) getMention;
final String Function(String)? getMention;
MentionSyntax(this.getMention) : super(r'(@(?:\[[^\]:]+\]|\w+)(?:#\w+)?)');
@override
bool onMatch(InlineParser parser, Match match) {
final mention = getMention?.call(match[1]);
final mention = getMention?.call(match[1]!);
if ((match.start > 0 &&
!RegExp(r'[\s.!?:;\(]').hasMatch(match.input[match.start - 1])) ||
mention == null) {
parser.addNode(Text(match[0]));
parser.addNode(Text(match[0]!));
return true;
}
final element = Element.text('a', htmlEscape.convert(match[1]));
final element = Element.text('a', htmlEscape.convert(match[1]!));
element.attributes['href'] =
htmlAttrEscape.convert('https://matrix.to/#/$mention');
parser.addNode(element);
@ -206,8 +205,8 @@ class MentionSyntax extends InlineSyntax {
String markdown(
String text, {
Map<String, Map<String, String>> Function() getEmotePacks,
String Function(String) getMention,
Map<String, Map<String, String>> Function()? getEmotePacks,
String Function(String)? getMention,
}) {
var ret = markdownToHtml(
text,

View File

@ -1,4 +1,3 @@
// @dart=2.9
/*
* Famedly Matrix SDK
* Copyright (C) 2020, 2021 Famedly GmbH
@ -20,36 +19,31 @@
import '../../matrix.dart';
class ToDeviceEvent extends BasicEventWithSender {
Map<String, dynamic> encryptedContent;
Map<String, dynamic>? encryptedContent;
String get sender => senderId;
set sender(String sender) => senderId = sender;
ToDeviceEvent({
String sender,
String type,
Map<String, dynamic> content,
required String sender,
required String type,
required Map<String, dynamic> content,
this.encryptedContent,
}) {
senderId = sender;
this.type = type;
this.content = content;
}
}) : super(senderId: sender, type: type, content: content);
ToDeviceEvent.fromJson(Map<String, dynamic> json) {
factory ToDeviceEvent.fromJson(Map<String, dynamic> json) {
final event = BasicEventWithSender.fromJson(json);
senderId = event.senderId;
type = event.type;
content = event.content;
return ToDeviceEvent(
sender: event.senderId, type: event.type, content: event.content);
}
}
class ToDeviceEventDecryptionError extends ToDeviceEvent {
Exception exception;
StackTrace stackTrace;
StackTrace? stackTrace;
ToDeviceEventDecryptionError({
ToDeviceEvent toDeviceEvent,
this.exception,
required ToDeviceEvent toDeviceEvent,
required this.exception,
this.stackTrace,
}) : super(
sender: toDeviceEvent.senderId,