refactor: make client nullsafe

This commit is contained in:
Nicolas Werner 2021-10-15 16:35:22 +02:00
parent 17fd1f22b3
commit fb0a84d7b2
No known key found for this signature in database
GPG Key ID: C8D75E610773F2D9
18 changed files with 596 additions and 529 deletions

View File

@ -62,7 +62,8 @@ class Encryption {
crossSigning = CrossSigning(this);
}
Future<void> init(String olmAccount) async {
// initial login passes null to init a new olm account
Future<void> init(String? olmAccount) async {
await olmManager.init(olmAccount);
_backgroundTasksRunning = true;
_backgroundTasks(); // start the background tasks
@ -86,7 +87,7 @@ class Encryption {
);
void handleDeviceOneTimeKeysCount(
Map<String, int> countJson, List<String> unusedFallbackKeyTypes) {
Map<String, int>? countJson, List<String>? unusedFallbackKeyTypes) {
runInRoot(() => olmManager.handleDeviceOneTimeKeysCount(
countJson, unusedFallbackKeyTypes));
}
@ -374,12 +375,13 @@ class Encryption {
Future<void> autovalidateMasterOwnKey() async {
// check if we can set our own master key as verified, if it isn't yet
final masterKey = client.userDeviceKeys[client.userID]?.masterKey;
final userId = client.userID;
final masterKey = client.userDeviceKeys[userId]?.masterKey;
if (client.database != null &&
masterKey != null &&
userId != null &&
!masterKey.directVerified &&
masterKey
.hasValidSignatureChain(onlyValidateUserIds: {client.userID})) {
masterKey.hasValidSignatureChain(onlyValidateUserIds: {userId})) {
await masterKey.setVerified(true);
}
}

View File

@ -95,6 +95,8 @@ class KeyManager {
}) {
final senderClaimedKeys_ = senderClaimedKeys ?? <String, String>{};
final allowedAtIndex_ = allowedAtIndex ?? <String, Map<String, int>>{};
final userId = client.userID;
if (userId == null) return;
if (!senderClaimedKeys_.containsKey('ed25519')) {
final device = client.getUserDeviceKeysByCurve25519Key(senderKey);
@ -126,7 +128,7 @@ class KeyManager {
indexes: {},
roomId: roomId,
sessionId: sessionId,
key: client.userID,
key: userId,
senderKey: senderKey,
senderClaimedKeys: senderClaimedKeys_,
allowedAtIndex: allowedAtIndex_,
@ -157,7 +159,7 @@ class KeyManager {
?.storeInboundGroupSession(
roomId,
sessionId,
inboundGroupSession.pickle(client.userID),
inboundGroupSession.pickle(userId),
json.encode(content),
json.encode({}),
json.encode(allowedAtIndex_),
@ -169,7 +171,7 @@ class KeyManager {
return;
}
if (uploaded) {
client.database.markInboundGroupSessionAsUploaded(roomId, sessionId);
client.database?.markInboundGroupSessionAsUploaded(roomId, sessionId);
} else {
_haveKeysToUpload = true;
}
@ -239,10 +241,10 @@ class KeyManager {
}
final session =
await client.database?.getInboundGroupSession(roomId, sessionId);
if (session == null) {
return null;
}
final dbSess = SessionKey.fromDb(session, client.userID);
if (session == null) return null;
final userID = client.userID;
if (userID == null) return null;
final dbSess = SessionKey.fromDb(session, userID);
final roomInboundGroupSessions =
_inboundGroupSessions[roomId] ??= <String, SessionKey>{};
if (!dbSess.isValid ||
@ -394,12 +396,10 @@ class KeyManager {
sess.outboundGroupSession!.message_index();
}
}
if (client.database != null) {
await client.database.updateInboundGroupSessionAllowedAtIndex(
json.encode(inboundSess!.allowedAtIndex),
room.id,
sess.outboundGroupSession!.session_id());
}
await client.database?.updateInboundGroupSessionAllowedAtIndex(
json.encode(inboundSess!.allowedAtIndex),
room.id,
sess.outboundGroupSession!.session_id());
// send out the key
await client.sendToDeviceEncryptedChunked(
devicesToReceive, EventTypes.RoomKey, rawSession);
@ -422,9 +422,11 @@ class KeyManager {
/// Store an outbound group session in the database
Future<void> storeOutboundGroupSession(
String roomId, OutboundGroupSession sess) async {
final userID = client.userID;
if (userID == null) return;
await client.database?.storeOutboundGroupSession(
roomId,
sess.outboundGroupSession!.pickle(client.userID),
sess.outboundGroupSession!.pickle(userID),
json.encode(sess.devices),
sess.creationTime.millisecondsSinceEpoch);
}
@ -464,6 +466,12 @@ class KeyManager {
throw Exception(
'Tried to create a megolm session in a non-existing room ($roomId)!');
}
final userID = client.userID;
if (userID == null) {
throw Exception(
'Tried to create a megolm session without being logged in!');
}
final deviceKeys = await room.getUserDeviceKeys();
final deviceKeyIds = _getDeviceKeyIdMap(deviceKeys);
deviceKeys.removeWhere((k) => !k.encryptToDevice);
@ -498,7 +506,7 @@ class KeyManager {
devices: deviceKeyIds,
creationTime: DateTime.now(),
outboundGroupSession: outboundGroupSession,
key: client.userID,
key: userID,
);
try {
await client.sendToDeviceEncryptedChunked(
@ -523,15 +531,18 @@ class KeyManager {
/// Load an outbound group session from database
Future<void> loadOutboundGroupSession(String roomId) async {
final database = client.database;
final userID = client.userID;
if (_loadedOutboundGroupSessions.contains(roomId) ||
_outboundGroupSessions.containsKey(roomId) ||
client.database == null) {
database == null ||
userID == null) {
return; // nothing to do
}
_loadedOutboundGroupSessions.add(roomId);
final sess = await client.database.getOutboundGroupSession(
final sess = await database.getOutboundGroupSession(
roomId,
client.userID,
userID,
);
if (sess == null || !sess.isValid) {
return;
@ -699,7 +710,9 @@ class KeyManager {
bool _isUploadingKeys = false;
bool _haveKeysToUpload = true;
Future<void> backgroundTasks() async {
if (_isUploadingKeys || client.database == null) {
final database = client.database;
final userID = client.userID;
if (_isUploadingKeys || database == null || userID == null) {
return;
}
_isUploadingKeys = true;
@ -707,8 +720,7 @@ class KeyManager {
if (!_haveKeysToUpload || !(await isCached())) {
return; // we can't backup anyways
}
final dbSessions =
await client.database.getInboundGroupSessionsToUpload();
final dbSessions = await database.getInboundGroupSessionsToUpload();
if (dbSessions.isEmpty) {
_haveKeysToUpload = false;
return; // nothing to do
@ -730,7 +742,7 @@ class KeyManager {
final args = _GenerateUploadKeysArgs(
pubkey: backupPubKey,
dbSessions: <_DbInboundGroupSessionBundle>[],
userId: client.userID,
userId: userID,
);
// we need to calculate verified beforehand, as else we pass a closure to an isolate
// with 500 keys they do, however, noticably block the UI, which is why we give brief async suspentions in here
@ -741,7 +753,7 @@ class KeyManager {
client.getUserDeviceKeysByCurve25519Key(dbSession.senderKey);
args.dbSessions.add(_DbInboundGroupSessionBundle(
dbSession: dbSession,
verified: device.verified,
verified: device?.verified ?? false,
));
i++;
if (i > 10) {
@ -758,7 +770,7 @@ class KeyManager {
// and now finally mark all the keys as uploaded
// no need to optimze this, as we only run it so seldomly and almost never with many keys at once
for (final dbSession in dbSessions) {
await client.database.markInboundGroupSessionAsUploaded(
await database.markInboundGroupSessionAsUploaded(
dbSession.roomId, dbSession.sessionId);
}
} finally {

View File

@ -56,7 +56,7 @@ class KeyVerificationManager {
Future<void> handleToDeviceEvent(ToDeviceEvent event) async {
if (!event.type.startsWith('m.key.verification.') ||
client.verificationMethods.isEmpty) {
client.verificationMethods.isEmpty != false) {
return;
}
// we have key verification going on!
@ -95,7 +95,7 @@ class KeyVerificationManager {
: event['content']['msgtype'];
if (type == null ||
!type.startsWith('m.key.verification.') ||
client.verificationMethods.isEmpty) {
client.verificationMethods.isEmpty != false) {
return;
}
if (type == EventTypes.KeyVerificationRequest) {

View File

@ -36,7 +36,7 @@ class OlmManager {
/// Returns the base64 encoded keys to store them in a store.
/// This String should **never** leave the device!
String? get pickledOlmAccount =>
enabled ? _olmAccount!.pickle(client.userID) : null;
enabled ? _olmAccount!.pickle(client.userID!) : null;
String? get fingerprintKey =>
enabled ? json.decode(_olmAccount!.identity_keys())['ed25519'] : null;
String? get identityKey =>
@ -70,7 +70,7 @@ class OlmManager {
try {
await olm.init();
_olmAccount = olm.Account();
_olmAccount!.unpickle(client.userID, olmAccount);
_olmAccount!.unpickle(client.userID!, olmAccount);
} catch (_) {
_olmAccount?.free();
_olmAccount = null;
@ -279,7 +279,7 @@ class OlmManager {
// fixup accidental too many uploads. We delete only one of them so that the server has time to update the counts and because we will get rate limited anyway.
if (keyCount > _olmAccount!.max_number_of_one_time_keys()) {
final requestingKeysFrom = {
client.userID: {client.deviceID: 'signed_curve25519'}
client.userID!: {client.deviceID!: 'signed_curve25519'}
};
client.claimKeys(requestingKeysFrom, timeout: 10000);
}
@ -311,10 +311,7 @@ class OlmManager {
// update an existing session
_olmSessions[session.identityKey]![ix] = session;
}
if (client.database == null) {
return;
}
await client.database.storeOlmSession(
await client.database?.storeOlmSession(
session.identityKey,
session.sessionId!,
session.pickledSession!,
@ -395,7 +392,7 @@ class OlmManager {
client.database?.updateClientKeys(pickledOlmAccount!);
plaintext = newSession.decrypt(type, body);
runInRoot(() => storeOlmSession(OlmSession(
key: client.userID,
key: client.userID!,
identityKey: senderKey,
sessionId: newSession.session_id(),
session: newSession,
@ -428,25 +425,19 @@ class OlmManager {
}
Future<List<OlmSession>> getOlmSessionsFromDatabase(String senderKey) async {
if (client.database == null) {
return [];
}
final olmSessions =
await client.database.getOlmSessions(senderKey, client.userID);
return olmSessions.where((sess) => sess.isValid).toList();
await client.database?.getOlmSessions(senderKey, client.userID!);
return olmSessions?.where((sess) => sess.isValid).toList() ?? [];
}
Future<void> getOlmSessionsForDevicesFromDatabase(
List<String> senderKeys) async {
if (client.database == null) {
return;
}
final rows = await client.database.getOlmSessionsForDevices(
final rows = await client.database?.getOlmSessionsForDevices(
senderKeys,
client.userID,
client.userID!,
);
final res = <String, List<OlmSession>>{};
for (final sess in rows) {
for (final sess in rows ?? []) {
res[sess.identityKey] ??= <OlmSession>[];
if (sess.isValid) {
res[sess.identityKey]!.add(sess);
@ -565,7 +556,7 @@ class OlmManager {
session.create_outbound(
_olmAccount!, identityKey, deviceKey['key']);
await storeOlmSession(OlmSession(
key: client.userID,
key: client.userID!,
identityKey: identityKey,
sessionId: session.session_id(),
session: session,
@ -602,7 +593,7 @@ class OlmManager {
await storeOlmSession(sess.first);
if (client.database != null) {
// ignore: unawaited_futures
runInRoot(() => client.database.setLastSentMessageUserDeviceKey(
runInRoot(() => client.database?.setLastSentMessageUserDeviceKey(
json.encode({
'type': type,
'content': payload,
@ -668,8 +659,10 @@ class OlmManager {
Logs().v(
'[OlmManager] Device ${device.userId}:${device.deviceId} generated a new olm session, replaying last sent message...');
final lastSentMessageRes = await client.database
.getLastSentMessageUserDeviceKey(device.userId, device.deviceId!);
if (lastSentMessageRes.isEmpty || (lastSentMessageRes.first.isEmpty)) {
?.getLastSentMessageUserDeviceKey(device.userId, device.deviceId!);
if (lastSentMessageRes == null ||
lastSentMessageRes.isEmpty ||
lastSentMessageRes.first.isEmpty) {
return;
}
final lastSentMessage = json.decode(lastSentMessageRes.first);

View File

@ -187,7 +187,7 @@ class SSSS {
Future<void> setDefaultKeyId(String keyId) async {
await client.setAccountData(
client.userID,
client.userID!,
EventTypes.SecretStorageDefaultKey,
SecretStorageDefaultKeyContent(key: keyId).toJson(),
);
@ -250,7 +250,7 @@ class SSSS {
syncUpdate.accountData!
.any((accountData) => accountData.type == accountDataType));
await client.setAccountData(
client.userID, accountDataType, content.toJson());
client.userID!, accountDataType, content.toJson());
await waitForAccountData;
final key = open(keyId);
@ -295,7 +295,7 @@ class SSSS {
if (_cache.containsKey(type) && isValid(_cache[type])) {
return _cache[type]?.content;
}
final ret = await client.database.getSSSSCache(type);
final ret = await client.database?.getSSSSCache(type);
if (ret == null) {
return null;
}
@ -321,10 +321,10 @@ class SSSS {
final encryptInfo = _Encrypted(
iv: enc['iv'], ciphertext: enc['ciphertext'], mac: enc['mac']);
final decrypted = await decryptAes(encryptInfo, key, type);
if (cacheTypes.contains(type) && client.database != null) {
final db = client.database;
if (cacheTypes.contains(type) && db != null) {
// cache the thing
await client.database
.storeSSSSCache(type, keyId, enc['ciphertext'], decrypted);
await db.storeSSSSCache(type, keyId, enc['ciphertext'], decrypted);
if (_cacheCallbacks.containsKey(type) && await getCached(type) == null) {
_cacheCallbacks[type]!(decrypted);
}
@ -351,11 +351,11 @@ class SSSS {
'mac': encrypted.mac,
};
// store the thing in your account data
await client.setAccountData(client.userID, type, content);
if (cacheTypes.contains(type) && client.database != null) {
await client.setAccountData(client.userID!, type, content);
final db = client.database;
if (cacheTypes.contains(type) && db != null) {
// cache the thing
await client.database
.storeSSSSCache(type, keyId, encrypted.ciphertext, secret);
await db.storeSSSSCache(type, keyId, encrypted.ciphertext, secret);
if (_cacheCallbacks.containsKey(type) && await getCached(type) == null) {
_cacheCallbacks[type]!(secret);
}
@ -381,10 +381,10 @@ class SSSS {
throw Exception('Secrets do not match up!');
}
// store the thing in your account data
await client.setAccountData(client.userID, type, content);
if (cacheTypes.contains(type) && client.database != null) {
await client.setAccountData(client.userID!, type, content);
if (cacheTypes.contains(type)) {
// cache the thing
await client.database.storeSSSSCache(
await client.database?.storeSSSSCache(
type, keyId, content['encrypted'][keyId]['ciphertext'], secret);
}
}
@ -542,13 +542,13 @@ class SSSS {
return; // our request is more than 15min in the past...better not trust it anymore
}
Logs().i('[SSSS] Secret for type ${request.type} is ok, storing it');
if (client.database != null) {
final db = client.database;
if (db != null) {
final keyId = keyIdFromType(request.type);
if (keyId != null) {
final ciphertext = client.accountData[request.type]!
.content['encrypted'][keyId]['ciphertext'];
await client.database
.storeSSSSCache(request.type, keyId, ciphertext, secret);
await db.storeSSSSCache(request.type, keyId, ciphertext, secret);
if (_cacheCallbacks.containsKey(request.type)) {
_cacheCallbacks[request.type]!(secret);
}

View File

@ -357,6 +357,7 @@ class Bootstrap {
checkOnlineKeyBackup();
return;
}
final userID = client.userID!;
try {
Uint8List masterSigningKey;
final secretsToStore = <String, String>{};
@ -370,7 +371,7 @@ class Bootstrap {
masterSigningKey = master.generate_seed();
masterPub = master.init_with_seed(masterSigningKey);
final json = <String, dynamic>{
'user_id': client.userID,
'user_id': userID,
'usage': ['master'],
'keys': <String, dynamic>{
'ed25519:$masterPub': masterPub,
@ -414,7 +415,7 @@ class Bootstrap {
final selfSigningPriv = selfSigning.generate_seed();
final selfSigningPub = selfSigning.init_with_seed(selfSigningPriv);
final json = <String, dynamic>{
'user_id': client.userID,
'user_id': userID,
'usage': ['self_signing'],
'keys': <String, dynamic>{
'ed25519:$selfSigningPub': selfSigningPub,
@ -422,7 +423,7 @@ class Bootstrap {
};
final signature = _sign(json);
json['signatures'] = <String, dynamic>{
client.userID: <String, dynamic>{
userID: <String, dynamic>{
'ed25519:$masterPub': signature,
},
};
@ -439,7 +440,7 @@ class Bootstrap {
final userSigningPriv = userSigning.generate_seed();
final userSigningPub = userSigning.init_with_seed(userSigningPriv);
final json = <String, dynamic>{
'user_id': client.userID,
'user_id': userID,
'usage': ['user_signing'],
'keys': <String, dynamic>{
'ed25519:$userSigningPub': userSigningPub,
@ -447,7 +448,7 @@ class Bootstrap {
};
final signature = _sign(json);
json['signatures'] = <String, dynamic>{
client.userID: <String, dynamic>{
userID: <String, dynamic>{
'ed25519:$masterPub': signature,
},
};
@ -462,7 +463,7 @@ class Bootstrap {
state = BootstrapState.loading;
Logs().v('Upload device signing keys.');
await client.uiaRequestBackground(
(AuthenticationData auth) => client.uploadCrossSigningKeys(
(AuthenticationData? auth) => client.uploadCrossSigningKeys(
masterKey: masterKey,
selfSigningKey: selfSigningKey,
userSigningKey: userSigningKey,

View File

@ -152,8 +152,10 @@ class KeyVerification {
List<String> get knownVerificationMethods {
final methods = <String>[];
if (client.verificationMethods.contains(KeyVerificationMethod.numbers) ||
client.verificationMethods.contains(KeyVerificationMethod.emoji)) {
if (client.verificationMethods?.contains(KeyVerificationMethod.numbers) ==
true ||
client.verificationMethods?.contains(KeyVerificationMethod.emoji) ==
true) {
methods.add('m.sas.v1');
}
return methods;
@ -673,11 +675,13 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod {
List<String> get knownAuthentificationTypes {
final types = <String>[];
if (request.client.verificationMethods
.contains(KeyVerificationMethod.emoji)) {
?.contains(KeyVerificationMethod.emoji) ==
true) {
types.add('emoji');
}
if (request.client.verificationMethods
.contains(KeyVerificationMethod.numbers)) {
?.contains(KeyVerificationMethod.numbers) ==
true) {
types.add('decimal');
}
return types;
@ -876,7 +880,7 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod {
: theirInfo + ourInfo) +
request.transactionId!;
} else if (keyAgreementProtocol == 'curve25519') {
final ourInfo = client.userID + client.deviceID;
final ourInfo = client.userID! + client.deviceID!;
final theirInfo = request.userId + request.deviceId!;
sasInfo = 'MATRIX_KEY_VERIFICATION_SAS' +
(request.startedVerification
@ -891,8 +895,8 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod {
Future<void> _sendMac() async {
final baseInfo = 'MATRIX_KEY_VERIFICATION_MAC' +
client.userID +
client.deviceID +
client.userID! +
client.deviceID! +
request.userId +
request.deviceId! +
request.transactionId!;
@ -929,8 +933,8 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod {
final baseInfo = 'MATRIX_KEY_VERIFICATION_MAC' +
request.userId +
request.deviceId! +
client.userID +
client.deviceID +
client.userID! +
client.deviceID! +
request.transactionId!;
final keyList = payload['mac'].keys.toList();

File diff suppressed because it is too large Load Diff

View File

@ -35,10 +35,10 @@ abstract class DatabaseApi {
String homeserverUrl,
String token,
String userId,
String deviceId,
String deviceName,
String prevBatch,
String olmAccount,
String? deviceId,
String? deviceName,
String? prevBatch,
String? olmAccount,
);
Future insertClient(
@ -46,10 +46,10 @@ abstract class DatabaseApi {
String homeserverUrl,
String token,
String userId,
String deviceId,
String deviceName,
String prevBatch,
String olmAccount,
String? deviceId,
String? deviceName,
String? prevBatch,
String? olmAccount,
);
Future<List<Room>> getRoomList(Client client);
@ -58,8 +58,7 @@ abstract class DatabaseApi {
/// Stores a RoomUpdate object in the database. Must be called inside of
/// [transaction].
Future<void> storeRoomUpdate(String roomId, SyncRoomUpdate roomUpdate,
[Room oldRoom]);
Future<void> storeRoomUpdate(String roomId, SyncRoomUpdate roomUpdate);
/// Stores an EventUpdate object in the database. Must be called inside of
/// [transaction].

View File

@ -507,6 +507,7 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
Future<List<Room>> getRoomList(Client client) =>
runBenchmarked<List<Room>>('Get room list from hive', () async {
final rooms = <String, Room>{};
final userID = client.userID;
final importantRoomStates = client.importantStateEvents;
for (final key in _roomsBox.keys) {
// Get the room
@ -515,7 +516,7 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
// let's see if we need any m.room.member events
// We always need the member event for ourself
final membersToPostload = <String>{client.userID};
final membersToPostload = <String>{if (userID != null) userID};
// If the room is a direct chat, those IDs should be there too
if (room.isDirectChat) membersToPostload.add(room.directChatMatrixID);
// the lastEvent message preview might have an author we need to fetch, if it is a group chat
@ -671,10 +672,10 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
String homeserverUrl,
String token,
String userId,
String deviceId,
String deviceName,
String prevBatch,
String olmAccount) async {
String? deviceId,
String? deviceName,
String? prevBatch,
String? olmAccount) async {
await _clientBox.put('homeserver_url', homeserverUrl);
await _clientBox.put('token', token);
await _clientBox.put('user_id', userId);
@ -1076,8 +1077,7 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
}
@override
Future<void> storeRoomUpdate(String roomId, SyncRoomUpdate roomUpdate,
[dynamic _]) async {
Future<void> storeRoomUpdate(String roomId, SyncRoomUpdate roomUpdate) async {
// Leave room if membership is leave
if (roomUpdate is LeftRoomUpdate) {
await forgetRoom(roomId);
@ -1250,10 +1250,10 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
String homeserverUrl,
String token,
String userId,
String deviceId,
String deviceName,
String prevBatch,
String olmAccount,
String? deviceId,
String? deviceName,
String? prevBatch,
String? olmAccount,
) async {
await _clientBox.put('homeserver_url', homeserverUrl);
await _clientBox.put('token', token);

View File

@ -518,7 +518,7 @@ class Event extends MatrixEvent {
/// event and returns it as a [MatrixFile]. If this event doesn't
/// contain an attachment, this throws an error. Set [getThumbnail] to
/// true to download the thumbnail instead.
Future<MatrixFile> downloadAndDecryptAttachment(
Future<MatrixFile?> downloadAndDecryptAttachment(
{bool getThumbnail = false,
Future<Uint8List> Function(Uri)? downloadCallback}) async {
if (![EventTypes.Message, EventTypes.Sticker].contains(type)) {
@ -548,7 +548,7 @@ class Event extends MatrixEvent {
Uint8List? uint8list;
if (storeable) {
uint8list = await client.database.getFile(mxcUrl);
uint8list = await client.database?.getFile(mxcUrl);
}
// Download the file
@ -577,9 +577,10 @@ class Event extends MatrixEvent {
k: fileMap['key']['k'],
sha256: fileMap['hashes']['sha256'],
);
uint8list = await client.runInBackground(decryptFile, encryptedFile);
uint8list = await client.runInBackground<Uint8List?, EncryptedFile>(
decryptFile, encryptedFile);
}
return MatrixFile(bytes: uint8list, name: body);
return uint8list != null ? MatrixFile(bytes: uint8list, name: body) : null;
}
/// Returns if this is a known event type.

View File

@ -132,15 +132,16 @@ class Timeline {
void _sessionKeyReceived(String sessionId) async {
var decryptAtLeastOneEvent = false;
final decryptFn = () async {
if (!room.client.encryptionEnabled) {
final encryption = room.client.encryption;
if (!room.client.encryptionEnabled || encryption == null) {
return;
}
for (var i = 0; i < events.length; i++) {
if (events[i].type == EventTypes.Encrypted &&
events[i].messageType == MessageTypes.BadEncrypted &&
events[i].content['session_id'] == sessionId) {
events[i] = await room.client.encryption
.decryptRoomEvent(room.id, events[i], store: true);
events[i] = await encryption.decryptRoomEvent(room.id, events[i],
store: true);
if (events[i].type != EventTypes.Encrypted) {
decryptAtLeastOneEvent = true;
}
@ -148,7 +149,7 @@ class Timeline {
}
};
if (room.client.database != null) {
await room.client.database.transaction(decryptFn);
await room.client.database?.transaction(decryptFn);
} else {
await decryptFn();
}
@ -162,7 +163,7 @@ class Timeline {
event.messageType == MessageTypes.BadEncrypted &&
event.content['can_request_session'] == true) {
try {
room.client.encryption.keyManager.maybeAutoRequest(room.id,
room.client.encryption?.keyManager.maybeAutoRequest(room.id,
event.content['session_id'], event.content['sender_key']);
} catch (_) {
// dispose

View File

@ -178,7 +178,7 @@ extension CommandsClientExtension on Client {
return await args.room.client.setRoomStateWithKey(
args.room.id,
EventTypes.RoomMember,
args.room.client.userID,
args.room.client.userID!,
currentEventJson,
);
});
@ -191,7 +191,7 @@ extension CommandsClientExtension on Client {
return await args.room.client.setRoomStateWithKey(
args.room.id,
EventTypes.RoomMember,
args.room.client.userID,
args.room.client.userID!,
currentEventJson,
);
});

View File

@ -68,17 +68,21 @@ class DeviceKeysList {
}
Future<KeyVerification> startVerification() async {
final encryption = client.encryption;
if (encryption == null) {
throw Exception('Encryption not enabled');
}
if (userId != client.userID) {
// in-room verification with someone else
final roomId = await client.startDirectChat(userId);
if (roomId ==
null /* can be null as long as startDirectChat is not migrated */) {
if (roomId == null) {
throw Exception('Unable to start new room');
}
final room =
client.getRoomById(roomId) ?? Room(id: roomId, client: client);
final request = KeyVerification(
encryption: client.encryption, room: room, userId: userId);
final request =
KeyVerification(encryption: encryption, room: room, userId: userId);
await request.start();
// no need to add to the request client object. As we are doing a room
// verification request that'll happen automatically once we know the transaction id
@ -86,9 +90,9 @@ class DeviceKeysList {
} else {
// broadcast self-verification
final request = KeyVerification(
encryption: client.encryption, userId: userId, deviceId: '*');
encryption: encryption, userId: userId, deviceId: '*');
await request.start();
client.encryption.keyVerificationManager.addRequest(request);
encryption.keyVerificationManager.addRequest(request);
return request;
}
}
@ -314,13 +318,15 @@ abstract class SignableKey extends MatrixSignableKey {
Future<void> setVerified(bool newVerified, [bool sign = true]) async {
_verified = newVerified;
final encryption = client.encryption;
if (newVerified &&
sign &&
encryption != null &&
client.encryptionEnabled &&
client.encryption.crossSigning.signable([this])) {
encryption.crossSigning.signable([this])) {
// sign the key!
// ignore: unawaited_futures
client.encryption.crossSigning.sign([this]);
encryption.crossSigning.sign([this]);
}
}
@ -493,11 +499,16 @@ class DeviceKeys extends SignableKey {
if (!isValid) {
throw Exception('setVerification called on invalid key');
}
final encryption = client.encryption;
if (encryption == null) {
throw Exception('setVerification called with disabled encryption');
}
final request = KeyVerification(
encryption: client.encryption, userId: userId, deviceId: deviceId!);
encryption: encryption, userId: userId, deviceId: deviceId!);
request.start();
client.encryption.keyVerificationManager.addRequest(request);
encryption.keyVerificationManager.addRequest(request);
return request;
}
}

View File

@ -49,12 +49,14 @@ class EventUpdate {
});
Future<EventUpdate> decrypt(Room room, {bool store = false}) async {
final encryption = room.client.encryption;
if (content['type'] != EventTypes.Encrypted ||
!room.client.encryptionEnabled) {
!room.client.encryptionEnabled ||
encryption == null) {
return this;
}
try {
final decrpytedEvent = await room.client.encryption.decryptRoomEvent(
final decrpytedEvent = await encryption.decryptRoomEvent(
room.id, Event.fromJson(content, room),
store: store, updateType: type);
return EventUpdate(

View File

@ -24,7 +24,9 @@ extension MxcUriExtension on Uri {
/// Returns a download Link to this content.
Uri getDownloadLink(Client matrix) => isScheme('mxc')
? matrix.homeserver != null
? matrix.homeserver.resolve('_matrix/media/r0/download/$host$path')
? matrix.homeserver
?.resolve('_matrix/media/r0/download/$host$path') ??
Uri()
: Uri()
: this;
@ -39,14 +41,15 @@ extension MxcUriExtension on Uri {
ThumbnailMethod? method = ThumbnailMethod.crop,
bool? animated = false}) {
if (!isScheme('mxc')) return this;
if (matrix.homeserver == null) {
final homeserver = matrix.homeserver;
if (homeserver == null) {
return Uri();
}
return Uri(
scheme: matrix.homeserver.scheme,
host: matrix.homeserver.host,
scheme: homeserver.scheme,
host: homeserver.host,
path: '/_matrix/media/r0/thumbnail/$host$path',
port: matrix.homeserver.port,
port: homeserver.port,
queryParameters: {
if (width != null) 'width': width.round().toString(),
if (height != null) 'height': height.round().toString(),

View File

@ -596,6 +596,7 @@ void main() {
expect(client2.homeserver, client1.homeserver);
expect(client2.deviceID, client1.deviceID);
expect(client2.deviceName, client1.deviceName);
expect(client2.rooms.length, 2);
if (client2.encryptionEnabled) {
expect(client2.encryption.fingerprintKey,
client1.encryption.fingerprintKey);

View File

@ -1,4 +1,3 @@
// @dart=2.9
/*
* Famedly Matrix SDK
* Copyright (C) 2019, 2020 Famedly GmbH
@ -25,11 +24,11 @@ import 'package:matrix/src/database/hive_database.dart';
import 'package:file/memory.dart';
import 'package:hive/hive.dart';
Future<DatabaseApi> getDatabase(Client _) => getHiveDatabase(_);
Future<DatabaseApi> getDatabase(Client? _) => getHiveDatabase(_);
bool hiveInitialized = false;
Future<FamedlySdkHiveDatabase> getHiveDatabase(Client c) async {
Future<FamedlySdkHiveDatabase> getHiveDatabase(Client? c) async {
if (!hiveInitialized) {
final fileSystem = MemoryFileSystem();
final testHivePath =
@ -38,7 +37,7 @@ Future<FamedlySdkHiveDatabase> getHiveDatabase(Client c) async {
Hive.init(testHivePath);
hiveInitialized = true;
}
final db = FamedlySdkHiveDatabase('unit_test.${c.hashCode}');
final db = FamedlySdkHiveDatabase('unit_test.${c?.hashCode}');
await db.open();
return db;
}