refactor: make client nullsafe
This commit is contained in:
parent
17fd1f22b3
commit
fb0a84d7b2
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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].
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue