Merge branch 'nico/nullsafe' into 'main'

Make SDK nullsafe

Closes #201

See merge request famedly/company/frontend/famedlysdk!867
This commit is contained in:
Nicolas Werner 2021-10-28 16:25:25 +00:00
commit 6ee5771334
65 changed files with 2182 additions and 2350 deletions

View File

@ -11,10 +11,6 @@ linter:
analyzer: analyzer:
errors: errors:
todo: ignore todo: ignore
import_of_legacy_library_into_null_safe: ignore
# ignore those until we are completely nullsafe
invalid_null_aware_operator: ignore
unnecessary_null_comparison: ignore
exclude: exclude:
- example/main.dart - example/main.dart
# needed until crypto packages upgrade # needed until crypto packages upgrade

View File

@ -90,9 +90,11 @@ class CrossSigning {
final masterPrivateKey = final masterPrivateKey =
base64.decode(await handle.getStored(EventTypes.CrossSigningMasterKey)); base64.decode(await handle.getStored(EventTypes.CrossSigningMasterKey));
final keyObj = olm.PkSigning(); final keyObj = olm.PkSigning();
String masterPubkey; String? masterPubkey;
try { try {
masterPubkey = keyObj.init_with_seed(masterPrivateKey); masterPubkey = keyObj.init_with_seed(masterPrivateKey);
} catch (e) {
masterPubkey = null;
} finally { } finally {
keyObj.free(); keyObj.free();
} }
@ -131,9 +133,6 @@ class CrossSigning {
final addSignature = final addSignature =
(SignableKey key, SignableKey signedWith, String signature) { (SignableKey key, SignableKey signedWith, String signature) {
if (key == null || signedWith == null || signature == null) {
return;
}
final signedKey = key.cloneForSigning(); final signedKey = key.cloneForSigning();
((signedKey.signatures ??= ((signedKey.signatures ??=
<String, Map<String, String>>{})[signedWith.userId] ??= <String, Map<String, String>>{})[signedWith.userId] ??=

View File

@ -62,7 +62,8 @@ class Encryption {
crossSigning = CrossSigning(this); 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); await olmManager.init(olmAccount);
_backgroundTasksRunning = true; _backgroundTasksRunning = true;
_backgroundTasks(); // start the background tasks _backgroundTasks(); // start the background tasks
@ -86,7 +87,7 @@ class Encryption {
); );
void handleDeviceOneTimeKeysCount( void handleDeviceOneTimeKeysCount(
Map<String, int> countJson, List<String> unusedFallbackKeyTypes) { Map<String, int>? countJson, List<String>? unusedFallbackKeyTypes) {
runInRoot(() => olmManager.handleDeviceOneTimeKeysCount( runInRoot(() => olmManager.handleDeviceOneTimeKeysCount(
countJson, unusedFallbackKeyTypes)); countJson, unusedFallbackKeyTypes));
} }
@ -308,9 +309,10 @@ class Encryption {
await client.database?.storeEventUpdate( await client.database?.storeEventUpdate(
EventUpdate( EventUpdate(
content: event.toJson(), content: event.toJson(),
roomID: event.roomId, roomID: roomId,
type: updateType, type: updateType,
), ),
client,
); );
} }
return event; return event;
@ -374,12 +376,13 @@ class Encryption {
Future<void> autovalidateMasterOwnKey() async { Future<void> autovalidateMasterOwnKey() async {
// check if we can set our own master key as verified, if it isn't yet // 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 && if (client.database != null &&
masterKey != null && masterKey != null &&
userId != null &&
!masterKey.directVerified && !masterKey.directVerified &&
masterKey masterKey.hasValidSignatureChain(onlyValidateUserIds: {userId})) {
.hasValidSignatureChain(onlyValidateUserIds: {client.userID})) {
await masterKey.setVerified(true); await masterKey.setVerified(true);
} }
} }

View File

@ -63,7 +63,8 @@ class KeyManager {
_requestedSessionIds.clear(); _requestedSessionIds.clear();
for (final room in client.rooms) { for (final room in client.rooms) {
final lastEvent = room.lastEvent; final lastEvent = room.lastEvent;
if (lastEvent.type == EventTypes.Encrypted && if (lastEvent != null &&
lastEvent.type == EventTypes.Encrypted &&
lastEvent.content['can_request_session'] == true) { lastEvent.content['can_request_session'] == true) {
try { try {
maybeAutoRequest(room.id, lastEvent.content['session_id'], maybeAutoRequest(room.id, lastEvent.content['session_id'],
@ -95,6 +96,8 @@ class KeyManager {
}) { }) {
final senderClaimedKeys_ = senderClaimedKeys ?? <String, String>{}; final senderClaimedKeys_ = senderClaimedKeys ?? <String, String>{};
final allowedAtIndex_ = allowedAtIndex ?? <String, Map<String, int>>{}; final allowedAtIndex_ = allowedAtIndex ?? <String, Map<String, int>>{};
final userId = client.userID;
if (userId == null) return;
if (!senderClaimedKeys_.containsKey('ed25519')) { if (!senderClaimedKeys_.containsKey('ed25519')) {
final device = client.getUserDeviceKeysByCurve25519Key(senderKey); final device = client.getUserDeviceKeysByCurve25519Key(senderKey);
@ -126,7 +129,7 @@ class KeyManager {
indexes: {}, indexes: {},
roomId: roomId, roomId: roomId,
sessionId: sessionId, sessionId: sessionId,
key: client.userID, key: userId,
senderKey: senderKey, senderKey: senderKey,
senderClaimedKeys: senderClaimedKeys_, senderClaimedKeys: senderClaimedKeys_,
allowedAtIndex: allowedAtIndex_, allowedAtIndex: allowedAtIndex_,
@ -157,7 +160,7 @@ class KeyManager {
?.storeInboundGroupSession( ?.storeInboundGroupSession(
roomId, roomId,
sessionId, sessionId,
inboundGroupSession.pickle(client.userID), inboundGroupSession.pickle(userId),
json.encode(content), json.encode(content),
json.encode({}), json.encode({}),
json.encode(allowedAtIndex_), json.encode(allowedAtIndex_),
@ -169,7 +172,7 @@ class KeyManager {
return; return;
} }
if (uploaded) { if (uploaded) {
client.database.markInboundGroupSessionAsUploaded(roomId, sessionId); client.database?.markInboundGroupSessionAsUploaded(roomId, sessionId);
} else { } else {
_haveKeysToUpload = true; _haveKeysToUpload = true;
} }
@ -239,10 +242,10 @@ class KeyManager {
} }
final session = final session =
await client.database?.getInboundGroupSession(roomId, sessionId); await client.database?.getInboundGroupSession(roomId, sessionId);
if (session == null) { if (session == null) return null;
return null; final userID = client.userID;
} if (userID == null) return null;
final dbSess = SessionKey.fromDb(session, client.userID); final dbSess = SessionKey.fromDb(session, userID);
final roomInboundGroupSessions = final roomInboundGroupSessions =
_inboundGroupSessions[roomId] ??= <String, SessionKey>{}; _inboundGroupSessions[roomId] ??= <String, SessionKey>{};
if (!dbSess.isValid || if (!dbSess.isValid ||
@ -394,12 +397,10 @@ class KeyManager {
sess.outboundGroupSession!.message_index(); sess.outboundGroupSession!.message_index();
} }
} }
if (client.database != null) { await client.database?.updateInboundGroupSessionAllowedAtIndex(
await client.database.updateInboundGroupSessionAllowedAtIndex( json.encode(inboundSess!.allowedAtIndex),
json.encode(inboundSess!.allowedAtIndex), room.id,
room.id, sess.outboundGroupSession!.session_id());
sess.outboundGroupSession!.session_id());
}
// send out the key // send out the key
await client.sendToDeviceEncryptedChunked( await client.sendToDeviceEncryptedChunked(
devicesToReceive, EventTypes.RoomKey, rawSession); devicesToReceive, EventTypes.RoomKey, rawSession);
@ -422,9 +423,11 @@ class KeyManager {
/// Store an outbound group session in the database /// Store an outbound group session in the database
Future<void> storeOutboundGroupSession( Future<void> storeOutboundGroupSession(
String roomId, OutboundGroupSession sess) async { String roomId, OutboundGroupSession sess) async {
final userID = client.userID;
if (userID == null) return;
await client.database?.storeOutboundGroupSession( await client.database?.storeOutboundGroupSession(
roomId, roomId,
sess.outboundGroupSession!.pickle(client.userID), sess.outboundGroupSession!.pickle(userID),
json.encode(sess.devices), json.encode(sess.devices),
sess.creationTime.millisecondsSinceEpoch); sess.creationTime.millisecondsSinceEpoch);
} }
@ -464,6 +467,12 @@ class KeyManager {
throw Exception( throw Exception(
'Tried to create a megolm session in a non-existing room ($roomId)!'); '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 deviceKeys = await room.getUserDeviceKeys();
final deviceKeyIds = _getDeviceKeyIdMap(deviceKeys); final deviceKeyIds = _getDeviceKeyIdMap(deviceKeys);
deviceKeys.removeWhere((k) => !k.encryptToDevice); deviceKeys.removeWhere((k) => !k.encryptToDevice);
@ -498,7 +507,7 @@ class KeyManager {
devices: deviceKeyIds, devices: deviceKeyIds,
creationTime: DateTime.now(), creationTime: DateTime.now(),
outboundGroupSession: outboundGroupSession, outboundGroupSession: outboundGroupSession,
key: client.userID, key: userID,
); );
try { try {
await client.sendToDeviceEncryptedChunked( await client.sendToDeviceEncryptedChunked(
@ -523,15 +532,18 @@ class KeyManager {
/// Load an outbound group session from database /// Load an outbound group session from database
Future<void> loadOutboundGroupSession(String roomId) async { Future<void> loadOutboundGroupSession(String roomId) async {
final database = client.database;
final userID = client.userID;
if (_loadedOutboundGroupSessions.contains(roomId) || if (_loadedOutboundGroupSessions.contains(roomId) ||
_outboundGroupSessions.containsKey(roomId) || _outboundGroupSessions.containsKey(roomId) ||
client.database == null) { database == null ||
userID == null) {
return; // nothing to do return; // nothing to do
} }
_loadedOutboundGroupSessions.add(roomId); _loadedOutboundGroupSessions.add(roomId);
final sess = await client.database.getOutboundGroupSession( final sess = await database.getOutboundGroupSession(
roomId, roomId,
client.userID, userID,
); );
if (sess == null || !sess.isValid) { if (sess == null || !sess.isValid) {
return; return;
@ -699,7 +711,9 @@ class KeyManager {
bool _isUploadingKeys = false; bool _isUploadingKeys = false;
bool _haveKeysToUpload = true; bool _haveKeysToUpload = true;
Future<void> backgroundTasks() async { Future<void> backgroundTasks() async {
if (_isUploadingKeys || client.database == null) { final database = client.database;
final userID = client.userID;
if (_isUploadingKeys || database == null || userID == null) {
return; return;
} }
_isUploadingKeys = true; _isUploadingKeys = true;
@ -707,8 +721,7 @@ class KeyManager {
if (!_haveKeysToUpload || !(await isCached())) { if (!_haveKeysToUpload || !(await isCached())) {
return; // we can't backup anyways return; // we can't backup anyways
} }
final dbSessions = final dbSessions = await database.getInboundGroupSessionsToUpload();
await client.database.getInboundGroupSessionsToUpload();
if (dbSessions.isEmpty) { if (dbSessions.isEmpty) {
_haveKeysToUpload = false; _haveKeysToUpload = false;
return; // nothing to do return; // nothing to do
@ -730,7 +743,7 @@ class KeyManager {
final args = _GenerateUploadKeysArgs( final args = _GenerateUploadKeysArgs(
pubkey: backupPubKey, pubkey: backupPubKey,
dbSessions: <_DbInboundGroupSessionBundle>[], dbSessions: <_DbInboundGroupSessionBundle>[],
userId: client.userID, userId: userID,
); );
// we need to calculate verified beforehand, as else we pass a closure to an isolate // 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 // with 500 keys they do, however, noticably block the UI, which is why we give brief async suspentions in here
@ -741,7 +754,7 @@ class KeyManager {
client.getUserDeviceKeysByCurve25519Key(dbSession.senderKey); client.getUserDeviceKeysByCurve25519Key(dbSession.senderKey);
args.dbSessions.add(_DbInboundGroupSessionBundle( args.dbSessions.add(_DbInboundGroupSessionBundle(
dbSession: dbSession, dbSession: dbSession,
verified: device.verified, verified: device?.verified ?? false,
)); ));
i++; i++;
if (i > 10) { if (i > 10) {
@ -758,7 +771,7 @@ class KeyManager {
// and now finally mark all the keys as uploaded // 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 // 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) { for (final dbSession in dbSessions) {
await client.database.markInboundGroupSessionAsUploaded( await database.markInboundGroupSessionAsUploaded(
dbSession.roomId, dbSession.sessionId); dbSession.roomId, dbSession.sessionId);
} }
} finally { } finally {

View File

@ -36,7 +36,7 @@ class OlmManager {
/// Returns the base64 encoded keys to store them in a store. /// Returns the base64 encoded keys to store them in a store.
/// This String should **never** leave the device! /// This String should **never** leave the device!
String? get pickledOlmAccount => String? get pickledOlmAccount =>
enabled ? _olmAccount!.pickle(client.userID) : null; enabled ? _olmAccount!.pickle(client.userID!) : null;
String? get fingerprintKey => String? get fingerprintKey =>
enabled ? json.decode(_olmAccount!.identity_keys())['ed25519'] : null; enabled ? json.decode(_olmAccount!.identity_keys())['ed25519'] : null;
String? get identityKey => String? get identityKey =>
@ -57,8 +57,7 @@ class OlmManager {
await olm.init(); await olm.init();
_olmAccount = olm.Account(); _olmAccount = olm.Account();
_olmAccount!.create(); _olmAccount!.create();
if (await uploadKeys(uploadDeviceKeys: true, updateDatabase: false) == if (!await uploadKeys(uploadDeviceKeys: true, updateDatabase: false)) {
false) {
throw ('Upload key failed'); throw ('Upload key failed');
} }
} catch (_) { } catch (_) {
@ -70,7 +69,7 @@ class OlmManager {
try { try {
await olm.init(); await olm.init();
_olmAccount = olm.Account(); _olmAccount = olm.Account();
_olmAccount!.unpickle(client.userID, olmAccount); _olmAccount!.unpickle(client.userID!, olmAccount);
} catch (_) { } catch (_) {
_olmAccount?.free(); _olmAccount?.free();
_olmAccount = null; _olmAccount = null;
@ -279,7 +278,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. // 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()) { if (keyCount > _olmAccount!.max_number_of_one_time_keys()) {
final requestingKeysFrom = { final requestingKeysFrom = {
client.userID: {client.deviceID: 'signed_curve25519'} client.userID!: {client.deviceID!: 'signed_curve25519'}
}; };
client.claimKeys(requestingKeysFrom, timeout: 10000); client.claimKeys(requestingKeysFrom, timeout: 10000);
} }
@ -311,10 +310,7 @@ class OlmManager {
// update an existing session // update an existing session
_olmSessions[session.identityKey]![ix] = session; _olmSessions[session.identityKey]![ix] = session;
} }
if (client.database == null) { await client.database?.storeOlmSession(
return;
}
await client.database.storeOlmSession(
session.identityKey, session.identityKey,
session.sessionId!, session.sessionId!,
session.pickledSession!, session.pickledSession!,
@ -362,7 +358,7 @@ class OlmManager {
if (session.session == null) { if (session.session == null) {
continue; continue;
} }
if (type == 0 && session.session!.matches_inbound(body) == true) { if (type == 0 && session.session!.matches_inbound(body)) {
try { try {
plaintext = session.session!.decrypt(type, body); plaintext = session.session!.decrypt(type, body);
} catch (e) { } catch (e) {
@ -395,7 +391,7 @@ class OlmManager {
client.database?.updateClientKeys(pickledOlmAccount!); client.database?.updateClientKeys(pickledOlmAccount!);
plaintext = newSession.decrypt(type, body); plaintext = newSession.decrypt(type, body);
runInRoot(() => storeOlmSession(OlmSession( runInRoot(() => storeOlmSession(OlmSession(
key: client.userID, key: client.userID!,
identityKey: senderKey, identityKey: senderKey,
sessionId: newSession.session_id(), sessionId: newSession.session_id(),
session: newSession, session: newSession,
@ -428,25 +424,19 @@ class OlmManager {
} }
Future<List<OlmSession>> getOlmSessionsFromDatabase(String senderKey) async { Future<List<OlmSession>> getOlmSessionsFromDatabase(String senderKey) async {
if (client.database == null) {
return [];
}
final olmSessions = final olmSessions =
await client.database.getOlmSessions(senderKey, client.userID); await client.database?.getOlmSessions(senderKey, client.userID!);
return olmSessions.where((sess) => sess.isValid).toList(); return olmSessions?.where((sess) => sess.isValid).toList() ?? [];
} }
Future<void> getOlmSessionsForDevicesFromDatabase( Future<void> getOlmSessionsForDevicesFromDatabase(
List<String> senderKeys) async { List<String> senderKeys) async {
if (client.database == null) { final rows = await client.database?.getOlmSessionsForDevices(
return;
}
final rows = await client.database.getOlmSessionsForDevices(
senderKeys, senderKeys,
client.userID, client.userID!,
); );
final res = <String, List<OlmSession>>{}; final res = <String, List<OlmSession>>{};
for (final sess in rows) { for (final sess in rows ?? []) {
res[sess.identityKey] ??= <OlmSession>[]; res[sess.identityKey] ??= <OlmSession>[];
if (sess.isValid) { if (sess.isValid) {
res[sess.identityKey]!.add(sess); res[sess.identityKey]!.add(sess);
@ -565,7 +555,7 @@ class OlmManager {
session.create_outbound( session.create_outbound(
_olmAccount!, identityKey, deviceKey['key']); _olmAccount!, identityKey, deviceKey['key']);
await storeOlmSession(OlmSession( await storeOlmSession(OlmSession(
key: client.userID, key: client.userID!,
identityKey: identityKey, identityKey: identityKey,
sessionId: session.session_id(), sessionId: session.session_id(),
session: session, session: session,
@ -602,7 +592,7 @@ class OlmManager {
await storeOlmSession(sess.first); await storeOlmSession(sess.first);
if (client.database != null) { if (client.database != null) {
// ignore: unawaited_futures // ignore: unawaited_futures
runInRoot(() => client.database.setLastSentMessageUserDeviceKey( runInRoot(() => client.database?.setLastSentMessageUserDeviceKey(
json.encode({ json.encode({
'type': type, 'type': type,
'content': payload, 'content': payload,
@ -668,8 +658,10 @@ class OlmManager {
Logs().v( Logs().v(
'[OlmManager] Device ${device.userId}:${device.deviceId} generated a new olm session, replaying last sent message...'); '[OlmManager] Device ${device.userId}:${device.deviceId} generated a new olm session, replaying last sent message...');
final lastSentMessageRes = await client.database final lastSentMessageRes = await client.database
.getLastSentMessageUserDeviceKey(device.userId, device.deviceId!); ?.getLastSentMessageUserDeviceKey(device.userId, device.deviceId!);
if (lastSentMessageRes.isEmpty || (lastSentMessageRes.first.isEmpty)) { if (lastSentMessageRes == null ||
lastSentMessageRes.isEmpty ||
lastSentMessageRes.first.isEmpty) {
return; return;
} }
final lastSentMessage = json.decode(lastSentMessageRes.first); final lastSentMessage = json.decode(lastSentMessageRes.first);

View File

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

View File

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

View File

@ -24,8 +24,10 @@ import '../../matrix.dart';
extension JsonSignatureCheckExtension on Map<String, dynamic> { extension JsonSignatureCheckExtension on Map<String, dynamic> {
/// Checks the signature of a signed json object. /// Checks the signature of a signed json object.
bool checkJsonSignature(String key, String userId, String deviceId) { bool checkJsonSignature(String key, String userId, String deviceId) {
final Map<String, dynamic> signatures = this['signatures']; final signatures = this['signatures'];
if (signatures == null || !signatures.containsKey(userId)) return false; if (signatures == null ||
!(signatures is Map<String, dynamic>) ||
!signatures.containsKey(userId)) return false;
remove('unsigned'); remove('unsigned');
remove('signatures'); remove('signatures');
if (!signatures[userId].containsKey('ed25519:$deviceId')) return false; if (!signatures[userId].containsKey('ed25519:$deviceId')) return false;

View File

@ -356,10 +356,8 @@ class KeyVerification {
if (_nextAction == 'request') { if (_nextAction == 'request') {
sendStart(); sendStart();
} else if (_nextAction == 'done') { } else if (_nextAction == 'done') {
if (_verifiedDevices != null) { // and now let's sign them all in the background
// and now let's sign them all in the background encryption.crossSigning.sign(_verifiedDevices);
encryption.crossSigning.sign(_verifiedDevices);
}
setState(KeyVerificationState.done); setState(KeyVerificationState.done);
} }
}; };
@ -530,8 +528,7 @@ class KeyVerification {
} }
Future<bool> verifyActivity() async { Future<bool> verifyActivity() async {
if (lastActivity != null && if (lastActivity.add(Duration(minutes: 10)).isAfter(DateTime.now())) {
lastActivity.add(Duration(minutes: 10)).isAfter(DateTime.now())) {
lastActivity = DateTime.now(); lastActivity = DateTime.now();
return true; return true;
} }
@ -876,7 +873,7 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod {
: theirInfo + ourInfo) + : theirInfo + ourInfo) +
request.transactionId!; request.transactionId!;
} else if (keyAgreementProtocol == 'curve25519') { } else if (keyAgreementProtocol == 'curve25519') {
final ourInfo = client.userID + client.deviceID; final ourInfo = client.userID! + client.deviceID!;
final theirInfo = request.userId + request.deviceId!; final theirInfo = request.userId + request.deviceId!;
sasInfo = 'MATRIX_KEY_VERIFICATION_SAS' + sasInfo = 'MATRIX_KEY_VERIFICATION_SAS' +
(request.startedVerification (request.startedVerification
@ -891,8 +888,8 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod {
Future<void> _sendMac() async { Future<void> _sendMac() async {
final baseInfo = 'MATRIX_KEY_VERIFICATION_MAC' + final baseInfo = 'MATRIX_KEY_VERIFICATION_MAC' +
client.userID + client.userID! +
client.deviceID + client.deviceID! +
request.userId + request.userId +
request.deviceId! + request.deviceId! +
request.transactionId!; request.transactionId!;
@ -929,8 +926,8 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod {
final baseInfo = 'MATRIX_KEY_VERIFICATION_MAC' + final baseInfo = 'MATRIX_KEY_VERIFICATION_MAC' +
request.userId + request.userId +
request.deviceId! + request.deviceId! +
client.userID + client.userID! +
client.deviceID + client.deviceID! +
request.transactionId!; request.transactionId!;
final keyList = payload['mac'].keys.toList(); final keyList = payload['mac'].keys.toList();

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -507,6 +507,7 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
Future<List<Room>> getRoomList(Client client) => Future<List<Room>> getRoomList(Client client) =>
runBenchmarked<List<Room>>('Get room list from hive', () async { runBenchmarked<List<Room>>('Get room list from hive', () async {
final rooms = <String, Room>{}; final rooms = <String, Room>{};
final userID = client.userID;
final importantRoomStates = client.importantStateEvents; final importantRoomStates = client.importantStateEvents;
for (final key in _roomsBox.keys) { for (final key in _roomsBox.keys) {
// Get the room // Get the room
@ -515,12 +516,15 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
// let's see if we need any m.room.member events // let's see if we need any m.room.member events
// We always need the member event for ourself // 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 the room is a direct chat, those IDs should be there too
if (room.isDirectChat) membersToPostload.add(room.directChatMatrixID); 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 // the lastEvent message preview might have an author we need to fetch, if it is a group chat
if (room.getState(EventTypes.Message) != null && !room.isDirectChat) { final lastEvent = room.getState(EventTypes.Message);
membersToPostload.add(room.getState(EventTypes.Message).senderId); if (lastEvent != null && !room.isDirectChat) {
membersToPostload.add(lastEvent.senderId);
} }
// if the room has no name and no canonical alias, its name is calculated // if the room has no name and no canonical alias, its name is calculated
// based on the heroes of the room // based on the heroes of the room
@ -528,7 +532,7 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
room.getState(EventTypes.RoomCanonicalAlias) == null) { room.getState(EventTypes.RoomCanonicalAlias) == null) {
// we don't have a name and no canonical alias, so we'll need to // we don't have a name and no canonical alias, so we'll need to
// post-load the heroes // post-load the heroes
membersToPostload.addAll(room.summary?.mHeroes ?? []); membersToPostload.addAll(room.summary.mHeroes ?? []);
} }
// Load members // Load members
for (final userId in membersToPostload) { for (final userId in membersToPostload) {
@ -671,10 +675,10 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
String homeserverUrl, String homeserverUrl,
String token, String token,
String userId, String userId,
String deviceId, String? deviceId,
String deviceName, String? deviceName,
String prevBatch, String? prevBatch,
String olmAccount) async { String? olmAccount) async {
await _clientBox.put('homeserver_url', homeserverUrl); await _clientBox.put('homeserver_url', homeserverUrl);
await _clientBox.put('token', token); await _clientBox.put('token', token);
await _clientBox.put('user_id', userId); await _clientBox.put('user_id', userId);
@ -816,10 +820,11 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
} }
@override @override
Future<void> setRoomPrevBatch(String prevBatch, String roomId) async { Future<void> setRoomPrevBatch(
String prevBatch, String roomId, Client client) async {
final raw = await _roomsBox.get(roomId.toHiveKey); final raw = await _roomsBox.get(roomId.toHiveKey);
if (raw == null) return; if (raw == null) return;
final room = Room.fromJson(convertToJson(raw)); final room = Room.fromJson(convertToJson(raw), client);
room.prev_batch = prevBatch; room.prev_batch = prevBatch;
await _roomsBox.put(roomId.toHiveKey, room.toJson()); await _roomsBox.put(roomId.toHiveKey, room.toJson());
return; return;
@ -860,13 +865,13 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
} }
@override @override
Future<void> storeEventUpdate(EventUpdate eventUpdate) async { Future<void> storeEventUpdate(EventUpdate eventUpdate, Client client) async {
// Ephemerals should not be stored // Ephemerals should not be stored
if (eventUpdate.type == EventUpdateType.ephemeral) return; if (eventUpdate.type == EventUpdateType.ephemeral) return;
// In case of this is a redaction event // In case of this is a redaction event
if (eventUpdate.content['type'] == EventTypes.Redaction) { if (eventUpdate.content['type'] == EventTypes.Redaction) {
final tmpRoom = Room(id: eventUpdate.roomID); final tmpRoom = Room(id: eventUpdate.roomID, client: client);
final event = await getEventById(eventUpdate.content['redacts'], tmpRoom); final event = await getEventById(eventUpdate.content['redacts'], tmpRoom);
if (event != null) { if (event != null) {
event.setRedactionEvent(Event.fromJson(eventUpdate.content, tmpRoom)); event.setRedactionEvent(Event.fromJson(eventUpdate.content, tmpRoom));
@ -906,13 +911,12 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
return; return;
} }
final status = final status = newStatus.isError || prevEvent == null
newStatus.isError || prevEvent == null || prevEvent.status != null ? newStatus
? newStatus : latestEventStatus(
: latestEventStatus( prevEvent.status,
prevEvent.status, newStatus,
newStatus, );
);
// Add the status and the sort order to the content so it get stored // Add the status and the sort order to the content so it get stored
eventUpdate.content['unsigned'] ??= <String, dynamic>{}; eventUpdate.content['unsigned'] ??= <String, dynamic>{};
@ -1076,8 +1080,8 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
} }
@override @override
Future<void> storeRoomUpdate(String roomId, SyncRoomUpdate roomUpdate, Future<void> storeRoomUpdate(
[dynamic _]) async { String roomId, SyncRoomUpdate roomUpdate, Client client) async {
// Leave room if membership is leave // Leave room if membership is leave
if (roomUpdate is LeftRoomUpdate) { if (roomUpdate is LeftRoomUpdate) {
await forgetRoom(roomId); await forgetRoom(roomId);
@ -1094,26 +1098,31 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
roomId.toHiveKey, roomId.toHiveKey,
roomUpdate is JoinedRoomUpdate roomUpdate is JoinedRoomUpdate
? Room( ? Room(
client: client,
id: roomId, id: roomId,
membership: membership, membership: membership,
highlightCount: highlightCount:
roomUpdate.unreadNotifications?.highlightCount?.toInt(), roomUpdate.unreadNotifications?.highlightCount?.toInt() ??
0,
notificationCount: roomUpdate notificationCount: roomUpdate
.unreadNotifications?.notificationCount .unreadNotifications?.notificationCount
?.toInt(), ?.toInt() ??
0,
prev_batch: roomUpdate.timeline?.prevBatch, prev_batch: roomUpdate.timeline?.prevBatch,
summary: roomUpdate.summary, summary: roomUpdate.summary,
).toJson() ).toJson()
: Room( : Room(
client: client,
id: roomId, id: roomId,
membership: membership, membership: membership,
).toJson()); ).toJson());
} else if (roomUpdate is JoinedRoomUpdate) { } else if (roomUpdate is JoinedRoomUpdate) {
final currentRawRoom = await _roomsBox.get(roomId.toHiveKey); final currentRawRoom = await _roomsBox.get(roomId.toHiveKey);
final currentRoom = Room.fromJson(convertToJson(currentRawRoom)); final currentRoom = Room.fromJson(convertToJson(currentRawRoom), client);
await _roomsBox.put( await _roomsBox.put(
roomId.toHiveKey, roomId.toHiveKey,
Room( Room(
client: client,
id: roomId, id: roomId,
membership: membership, membership: membership,
highlightCount: highlightCount:
@ -1250,10 +1259,10 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
String homeserverUrl, String homeserverUrl,
String token, String token,
String userId, String userId,
String deviceId, String? deviceId,
String deviceName, String? deviceName,
String prevBatch, String? prevBatch,
String olmAccount, String? olmAccount,
) async { ) async {
await _clientBox.put('homeserver_url', homeserverUrl); await _clientBox.put('homeserver_url', homeserverUrl);
await _clientBox.put('token', token); await _clientBox.put('token', token);

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2019, 2020, 2021 Famedly GmbH * Copyright (C) 2019, 2020, 2021 Famedly GmbH
@ -39,7 +38,13 @@ abstract class RelationshipTypes {
/// All data exchanged over Matrix is expressed as an "event". Typically each client action (e.g. sending a message) correlates with exactly one event. /// All data exchanged over Matrix is expressed as an "event". Typically each client action (e.g. sending a message) correlates with exactly one event.
class Event extends MatrixEvent { class Event extends MatrixEvent {
User get sender => room.getUserByMXIDSync(senderId ?? '@unknown:unknown'); User get sender =>
room?.getUserByMXIDSync(senderId) ??
User.fromState(
stateKey: senderId,
typeKey: EventTypes.RoomMember,
originServerTs: DateTime.now(),
);
@Deprecated('Use [originServerTs] instead') @Deprecated('Use [originServerTs] instead')
DateTime get time => originServerTs; DateTime get time => originServerTs;
@ -48,10 +53,10 @@ class Event extends MatrixEvent {
String get typeKey => type; String get typeKey => type;
@Deprecated('Use [sender.calcDisplayname()] instead') @Deprecated('Use [sender.calcDisplayname()] instead')
String get senderName => sender.calcDisplayname(); String? get senderName => sender.calcDisplayname();
/// The room this event belongs to. May be null. /// The room this event belongs to. May be null.
final Room room; final Room? room;
/// The status of this event. /// The status of this event.
EventStatus status; EventStatus status;
@ -59,33 +64,39 @@ class Event extends MatrixEvent {
static const EventStatus defaultStatus = EventStatus.synced; static const EventStatus defaultStatus = EventStatus.synced;
/// Optional. The event that redacted this event, if any. Otherwise null. /// Optional. The event that redacted this event, if any. Otherwise null.
Event get redactedBecause => Event? get redactedBecause {
unsigned != null && unsigned['redacted_because'] is Map final redacted_because = unsigned?['redacted_because'];
? Event.fromJson(unsigned['redacted_because'], room) final room = this.room;
: null; return (redacted_because is Map<String, dynamic>)
? Event.fromJson(redacted_because, room)
: null;
}
bool get redacted => redactedBecause != null; bool get redacted => redactedBecause != null;
User get stateKeyUser => room.getUserByMXIDSync(stateKey); User? get stateKeyUser => room?.getUserByMXIDSync(stateKey!);
Event({ Event({
this.status = defaultStatus, this.status = defaultStatus,
Map<String, dynamic> content, required Map<String, dynamic> content,
String type, required String type,
String eventId, required String eventId,
String roomId, String? roomId,
String senderId, required String senderId,
DateTime originServerTs, required DateTime originServerTs,
Map<String, dynamic> unsigned, Map<String, dynamic>? unsigned,
Map<String, dynamic> prevContent, Map<String, dynamic>? prevContent,
String stateKey, String? stateKey,
this.room, this.room,
}) { }) : super(
this.content = content; content: content,
this.type = type; type: type,
eventId: eventId,
senderId: senderId,
originServerTs: originServerTs,
roomId: roomId ?? room?.id,
) {
this.eventId = eventId; this.eventId = eventId;
this.roomId = roomId ?? room?.id;
this.senderId = senderId;
this.unsigned = unsigned; this.unsigned = unsigned;
// synapse unfortunately isn't following the spec and tosses the prev_content // synapse unfortunately isn't following the spec and tosses the prev_content
// into the unsigned block. // into the unsigned block.
@ -103,17 +114,17 @@ class Event extends MatrixEvent {
// A strange bug in dart web makes this crash // A strange bug in dart web makes this crash
} }
this.stateKey = stateKey; this.stateKey = stateKey;
this.originServerTs = originServerTs;
// Mark event as failed to send if status is `sending` and event is older // Mark event as failed to send if status is `sending` and event is older
// than the timeout. This should not happen with the deprecated Moor // than the timeout. This should not happen with the deprecated Moor
// database! // database!
if (status.isSending && room?.client?.database != null) { if (status.isSending && room?.client.database != null) {
// Age of this event in milliseconds // Age of this event in milliseconds
final age = DateTime.now().millisecondsSinceEpoch - final age = DateTime.now().millisecondsSinceEpoch -
originServerTs.millisecondsSinceEpoch; originServerTs.millisecondsSinceEpoch;
if (age > room.client.sendMessageTimeoutSeconds * 1000) { final room = this.room;
if (room != null && age > room.client.sendMessageTimeoutSeconds * 1000) {
// Update this event in database and open timelines // Update this event in database and open timelines
final json = toJson(); final json = toJson();
json['unsigned'] ??= <String, dynamic>{}; json['unsigned'] ??= <String, dynamic>{};
@ -143,7 +154,7 @@ class Event extends MatrixEvent {
factory Event.fromMatrixEvent( factory Event.fromMatrixEvent(
MatrixEvent matrixEvent, MatrixEvent matrixEvent,
Room room, { Room room, {
EventStatus status, EventStatus status = defaultStatus,
}) => }) =>
Event( Event(
status: status, status: status,
@ -162,7 +173,7 @@ class Event extends MatrixEvent {
/// Get a State event from a table row or from the event stream. /// Get a State event from a table row or from the event stream.
factory Event.fromJson( factory Event.fromJson(
Map<String, dynamic> jsonPayload, Map<String, dynamic> jsonPayload,
Room room, Room? room,
) { ) {
final content = Event.getMapFromPayload(jsonPayload['content']); final content = Event.getMapFromPayload(jsonPayload['content']);
final unsigned = Event.getMapFromPayload(jsonPayload['unsigned']); final unsigned = Event.getMapFromPayload(jsonPayload['unsigned']);
@ -175,7 +186,7 @@ class Event extends MatrixEvent {
prevContent: prevContent, prevContent: prevContent,
content: content, content: content,
type: jsonPayload['type'], type: jsonPayload['type'],
eventId: jsonPayload['event_id'], eventId: jsonPayload['event_id'] ?? '',
roomId: jsonPayload['room_id'], roomId: jsonPayload['room_id'],
senderId: jsonPayload['sender'], senderId: jsonPayload['sender'],
originServerTs: jsonPayload.containsKey('origin_server_ts') originServerTs: jsonPayload.containsKey('origin_server_ts')
@ -190,7 +201,7 @@ class Event extends MatrixEvent {
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final data = <String, dynamic>{}; final data = <String, dynamic>{};
if (stateKey != null) data['state_key'] = stateKey; if (stateKey != null) data['state_key'] = stateKey;
if (prevContent != null && prevContent.isNotEmpty) { if (prevContent?.isNotEmpty == true) {
data['prev_content'] = prevContent; data['prev_content'] = prevContent;
} }
data['content'] = content; data['content'] = content;
@ -199,14 +210,15 @@ class Event extends MatrixEvent {
data['room_id'] = roomId; data['room_id'] = roomId;
data['sender'] = senderId; data['sender'] = senderId;
data['origin_server_ts'] = originServerTs.millisecondsSinceEpoch; data['origin_server_ts'] = originServerTs.millisecondsSinceEpoch;
if (unsigned != null && unsigned.isNotEmpty) { if (unsigned?.isNotEmpty == true) {
data['unsigned'] = unsigned; data['unsigned'] = unsigned;
} }
return data; return data;
} }
User get asUser => User.fromState( User get asUser => User.fromState(
stateKey: stateKey, // state key should always be set for member events
stateKey: stateKey!,
prevContent: prevContent, prevContent: prevContent,
content: content, content: content,
typeKey: type, typeKey: type,
@ -282,8 +294,10 @@ class Event extends MatrixEvent {
/// Returns a list of [Receipt] instances for this event. /// Returns a list of [Receipt] instances for this event.
List<Receipt> get receipts { List<Receipt> get receipts {
if (!(room.roomAccountData.containsKey('m.receipt'))) return []; final room = this.room;
return room.roomAccountData['m.receipt'].content.entries final receipt = room?.roomAccountData['m.receipt'];
if (receipt == null || room == null) return [];
return receipt.content.entries
.where((entry) => entry.value['event_id'] == eventId) .where((entry) => entry.value['event_id'] == eventId)
.map((entry) => Receipt(room.getUserByMXIDSync(entry.key), .map((entry) => Receipt(room.getUserByMXIDSync(entry.key),
DateTime.fromMillisecondsSinceEpoch(entry.value['ts']))) DateTime.fromMillisecondsSinceEpoch(entry.value['ts'])))
@ -294,6 +308,11 @@ class Event extends MatrixEvent {
/// This event will just be removed from the database and the timelines. /// This event will just be removed from the database and the timelines.
/// Returns [false] if not removed. /// Returns [false] if not removed.
Future<bool> remove() async { Future<bool> remove() async {
final room = this.room;
if (room == null) {
return false;
}
if (!status.isSent) { if (!status.isSent) {
await room.client.database?.removeEvent(eventId, room.id); await room.client.database?.removeEvent(eventId, room.id);
@ -311,29 +330,33 @@ class Event extends MatrixEvent {
return false; return false;
} }
/// Try to send this event again. Only works with events of `EventStatus.isError`. /// Try to send this event again. Only works with events of status -1.
Future<String> sendAgain({String txid}) async { Future<String?> sendAgain({String? txid}) async {
if (!status.isError) return null; if (!status.isError) return null;
// we do not remove the event here. It will automatically be updated // we do not remove the event here. It will automatically be updated
// in the `sendEvent` method to transition -1 -> 0 -> 1 -> 2 // in the `sendEvent` method to transition -1 -> 0 -> 1 -> 2
final newEventId = await room.sendEvent( final newEventId = await room?.sendEvent(
content, content,
txid: txid ?? unsigned['transaction_id'] ?? eventId, txid: txid ?? unsigned?['transaction_id'] ?? eventId,
); );
return newEventId; return newEventId;
} }
/// Whether the client is allowed to redact this event. /// Whether the client is allowed to redact this event.
bool get canRedact => senderId == room.client.userID || room.canRedact; bool get canRedact =>
senderId == room?.client.userID || (room?.canRedact ?? false);
/// Redacts this event. Throws `ErrorResponse` on error. /// Redacts this event. Throws `ErrorResponse` on error.
Future<dynamic> redactEvent({String reason, String txid}) => Future<String?> redactEvent({String? reason, String? txid}) async =>
room.redactEvent(eventId, reason: reason, txid: txid); await room?.redactEvent(eventId, reason: reason, txid: txid);
/// Searches for the reply event in the given timeline. /// Searches for the reply event in the given timeline.
Future<Event> getReplyEvent(Timeline timeline) async { Future<Event?> getReplyEvent(Timeline timeline) async {
if (relationshipType != RelationshipTypes.reply) return null; if (relationshipType != RelationshipTypes.reply) return null;
return await timeline.getEventById(relationshipEventId); final relationshipEventId = this.relationshipEventId;
return relationshipEventId == null
? null
: await timeline.getEventById(relationshipEventId);
} }
/// If this event is encrypted and the decryption was not successful because /// If this event is encrypted and the decryption was not successful because
@ -346,7 +369,7 @@ class Event extends MatrixEvent {
content['can_request_session'] != true) { content['can_request_session'] != true) {
throw ('Session key not requestable'); throw ('Session key not requestable');
} }
await room.requestSessionKey(content['session_id'], content['sender_key']); await room?.requestSessionKey(content['session_id'], content['sender_key']);
return; return;
} }
@ -388,16 +411,21 @@ class Event extends MatrixEvent {
: ''); : '');
/// Gets the underlying mxc url of an attachment of a file event, or null if not present /// Gets the underlying mxc url of an attachment of a file event, or null if not present
Uri get attachmentMxcUrl => Uri.parse( Uri? get attachmentMxcUrl {
isAttachmentEncrypted ? content['file']['url'] : content['url']); final url = isAttachmentEncrypted ? content['file']['url'] : content['url'];
return url is String ? Uri.tryParse(url) : null;
}
/// Gets the underlying mxc url of a thumbnail of a file event, or null if not present /// Gets the underlying mxc url of a thumbnail of a file event, or null if not present
Uri get thumbnailMxcUrl => Uri.parse(isThumbnailEncrypted Uri? get thumbnailMxcUrl {
? infoMap['thumbnail_file']['url'] final url = isThumbnailEncrypted
: infoMap['thumbnail_url']); ? infoMap['thumbnail_file']['url']
: infoMap['thumbnail_url'];
return url is String ? Uri.tryParse(url) : null;
}
/// Gets the mxc url of an attachment/thumbnail of a file event, taking sizes into account, or null if not present /// Gets the mxc url of an attachment/thumbnail of a file event, taking sizes into account, or null if not present
Uri attachmentOrThumbnailMxcUrl({bool getThumbnail = false}) { Uri? attachmentOrThumbnailMxcUrl({bool getThumbnail = false}) {
if (getThumbnail && if (getThumbnail &&
infoMap['size'] is int && infoMap['size'] is int &&
thumbnailInfoMap['size'] is int && thumbnailInfoMap['size'] is int &&
@ -420,7 +448,7 @@ class Event extends MatrixEvent {
/// [minNoThumbSize] is the minimum size that an original image may be to not fetch its thumbnail, defaults to 80k /// [minNoThumbSize] is the minimum size that an original image may be to not fetch its thumbnail, defaults to 80k
/// [useThumbnailMxcUrl] says weather to use the mxc url of the thumbnail, rather than the original attachment. /// [useThumbnailMxcUrl] says weather to use the mxc url of the thumbnail, rather than the original attachment.
/// [animated] says weather the thumbnail is animated /// [animated] says weather the thumbnail is animated
Uri getAttachmentUrl( Uri? getAttachmentUrl(
{bool getThumbnail = false, {bool getThumbnail = false,
bool useThumbnailMxcUrl = false, bool useThumbnailMxcUrl = false,
double width = 800.0, double width = 800.0,
@ -428,6 +456,10 @@ class Event extends MatrixEvent {
ThumbnailMethod method = ThumbnailMethod.scale, ThumbnailMethod method = ThumbnailMethod.scale,
int minNoThumbSize = _minNoThumbSize, int minNoThumbSize = _minNoThumbSize,
bool animated = false}) { bool animated = false}) {
final client = room?.client;
if (client == null) {
return null;
}
if (![EventTypes.Message, EventTypes.Sticker].contains(type) || if (![EventTypes.Message, EventTypes.Sticker].contains(type) ||
!hasAttachment || !hasAttachment ||
isAttachmentEncrypted) { isAttachmentEncrypted) {
@ -449,14 +481,14 @@ class Event extends MatrixEvent {
// now generate the actual URLs // now generate the actual URLs
if (getThumbnail) { if (getThumbnail) {
return Uri.parse(thisMxcUrl).getThumbnail( return Uri.parse(thisMxcUrl).getThumbnail(
room.client, client,
width: width, width: width,
height: height, height: height,
method: method, method: method,
animated: animated, animated: animated,
); );
} else { } else {
return Uri.parse(thisMxcUrl).getDownloadLink(room.client); return Uri.parse(thisMxcUrl).getDownloadLink(client);
} }
} }
@ -472,13 +504,17 @@ class Event extends MatrixEvent {
getThumbnail = mxcUrl != attachmentMxcUrl; getThumbnail = mxcUrl != attachmentMxcUrl;
// Is this file storeable? // Is this file storeable?
final thisInfoMap = getThumbnail ? thumbnailInfoMap : infoMap; final thisInfoMap = getThumbnail ? thumbnailInfoMap : infoMap;
final storeable = room.client.database != null && final database = room?.client.database;
thisInfoMap['size'] is int && if (database == null) {
thisInfoMap['size'] <= room.client.database.maxFileSize; return false;
}
Uint8List uint8list; final storeable = thisInfoMap['size'] is int &&
thisInfoMap['size'] <= database.maxFileSize;
Uint8List? uint8list;
if (storeable) { if (storeable) {
uint8list = await room.client.database.getFile(mxcUrl); uint8list = await database.getFile(mxcUrl);
} }
return uint8list != null; return uint8list != null;
} }
@ -487,12 +523,17 @@ class Event extends MatrixEvent {
/// event and returns it as a [MatrixFile]. If this event doesn't /// event and returns it as a [MatrixFile]. If this event doesn't
/// contain an attachment, this throws an error. Set [getThumbnail] to /// contain an attachment, this throws an error. Set [getThumbnail] to
/// true to download the thumbnail instead. /// true to download the thumbnail instead.
Future<MatrixFile> downloadAndDecryptAttachment( Future<MatrixFile?> downloadAndDecryptAttachment(
{bool getThumbnail = false, {bool getThumbnail = false,
Future<Uint8List> Function(Uri) downloadCallback}) async { Future<Uint8List> Function(Uri)? downloadCallback}) async {
if (![EventTypes.Message, EventTypes.Sticker].contains(type)) { if (![EventTypes.Message, EventTypes.Sticker].contains(type)) {
throw ("This event has the type '$type' and so it can't contain an attachment."); throw ("This event has the type '$type' and so it can't contain an attachment.");
} }
final client = room?.client;
final database = room?.client.database;
if (client == null) {
throw 'This event has no valid client.';
}
final mxcUrl = attachmentOrThumbnailMxcUrl(getThumbnail: getThumbnail); final mxcUrl = attachmentOrThumbnailMxcUrl(getThumbnail: getThumbnail);
if (mxcUrl == null) { if (mxcUrl == null) {
throw "This event hasn't any attachment or thumbnail."; throw "This event hasn't any attachment or thumbnail.";
@ -500,33 +541,30 @@ class Event extends MatrixEvent {
getThumbnail = mxcUrl != attachmentMxcUrl; getThumbnail = mxcUrl != attachmentMxcUrl;
final isEncrypted = final isEncrypted =
getThumbnail ? isThumbnailEncrypted : isAttachmentEncrypted; getThumbnail ? isThumbnailEncrypted : isAttachmentEncrypted;
if (isEncrypted && !client.encryptionEnabled) {
if (isEncrypted && !room.client.encryptionEnabled) {
throw ('Encryption is not enabled in your Client.'); throw ('Encryption is not enabled in your Client.');
} }
Uint8List uint8list;
// Is this file storeable? // Is this file storeable?
final thisInfoMap = getThumbnail ? thumbnailInfoMap : infoMap; final thisInfoMap = getThumbnail ? thumbnailInfoMap : infoMap;
var storeable = room.client.database != null && var storeable = database != null &&
thisInfoMap['size'] is int && thisInfoMap['size'] is int &&
thisInfoMap['size'] <= room.client.database.maxFileSize; thisInfoMap['size'] <= database.maxFileSize;
Uint8List? uint8list;
if (storeable) { if (storeable) {
uint8list = await room.client.database.getFile(mxcUrl); uint8list = await client.database?.getFile(mxcUrl);
} }
// Download the file // Download the file
if (uint8list == null) { if (uint8list == null) {
downloadCallback ??= (Uri url) async { downloadCallback ??= (Uri url) async => (await http.get(url)).bodyBytes;
return (await http.get(url)).bodyBytes; uint8list = await downloadCallback(mxcUrl.getDownloadLink(client));
}; storeable = database != null &&
uint8list = await downloadCallback(mxcUrl.getDownloadLink(room.client)); storeable &&
storeable = storeable && uint8list.lengthInBytes < database.maxFileSize;
uint8list.lengthInBytes < room.client.database.maxFileSize;
if (storeable) { if (storeable) {
await room.client.database.storeFile( await database.storeFile(
mxcUrl, uint8list, DateTime.now().millisecondsSinceEpoch); mxcUrl, uint8list, DateTime.now().millisecondsSinceEpoch);
} }
} }
@ -544,9 +582,10 @@ class Event extends MatrixEvent {
k: fileMap['key']['k'], k: fileMap['key']['k'],
sha256: fileMap['hashes']['sha256'], sha256: fileMap['hashes']['sha256'],
); );
uint8list = await room.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. /// Returns if this is a known event type.
@ -565,7 +604,7 @@ class Event extends MatrixEvent {
bool plaintextBody = false, bool plaintextBody = false,
}) { }) {
if (redacted) { if (redacted) {
return i18n.removedBy(redactedBecause.sender.calcDisplayname()); return i18n.removedBy(redactedBecause?.sender.calcDisplayname() ?? '');
} }
var body = plaintextBody ? this.plaintextBody : this.body; var body = plaintextBody ? this.plaintextBody : this.body;
@ -607,8 +646,9 @@ class Event extends MatrixEvent {
if (withSenderNamePrefix && if (withSenderNamePrefix &&
type == EventTypes.Message && type == EventTypes.Message &&
textOnlyMessageTypes.contains(messageType)) { textOnlyMessageTypes.contains(messageType)) {
final senderNameOrYou = final senderNameOrYou = senderId == room?.client.userID
senderId == room.client.userID ? i18n.you : sender.calcDisplayname(); ? i18n.you
: (sender.calcDisplayname());
localizedBody = '$senderNameOrYou: $localizedBody'; localizedBody = '$senderNameOrYou: $localizedBody';
} }
@ -623,19 +663,19 @@ class Event extends MatrixEvent {
}; };
/// returns if this event matches the passed event or transaction id /// returns if this event matches the passed event or transaction id
bool matchesEventOrTransactionId(String search) { bool matchesEventOrTransactionId(String? search) {
if (search == null) { if (search == null) {
return false; return false;
} }
if (eventId == search) { if (eventId == search) {
return true; return true;
} }
return unsigned != null && unsigned['transaction_id'] == search; return unsigned?['transaction_id'] == search;
} }
/// Get the relationship type of an event. `null` if there is none /// Get the relationship type of an event. `null` if there is none
String get relationshipType { String? get relationshipType {
if (content?.tryGet<Map<String, dynamic>>('m.relates_to') == null) { if (content.tryGet<Map<String, dynamic>>('m.relates_to') == null) {
return null; return null;
} }
if (content['m.relates_to'].containsKey('m.in_reply_to')) { if (content['m.relates_to'].containsKey('m.in_reply_to')) {
@ -643,12 +683,12 @@ class Event extends MatrixEvent {
} }
return content return content
.tryGet<Map<String, dynamic>>('m.relates_to') .tryGet<Map<String, dynamic>>('m.relates_to')
.tryGet<String>('rel_type'); ?.tryGet<String>('rel_type');
} }
/// Get the event ID that this relationship will reference. `null` if there is none /// Get the event ID that this relationship will reference. `null` if there is none
String get relationshipEventId { String? get relationshipEventId {
if (content == null || !(content['m.relates_to'] is Map)) { if (!(content['m.relates_to'] is Map)) {
return null; return null;
} }
if (content['m.relates_to'].containsKey('event_id')) { if (content['m.relates_to'].containsKey('event_id')) {
@ -664,15 +704,12 @@ class Event extends MatrixEvent {
/// Get whether this event has aggregated events from a certain [type] /// Get whether this event has aggregated events from a certain [type]
/// To be able to do that you need to pass a [timeline] /// To be able to do that you need to pass a [timeline]
bool hasAggregatedEvents(Timeline timeline, String type) => bool hasAggregatedEvents(Timeline timeline, String type) =>
timeline.aggregatedEvents.containsKey(eventId) && timeline.aggregatedEvents[eventId]?.containsKey(type) == true;
timeline.aggregatedEvents[eventId].containsKey(type);
/// Get all the aggregated event objects for a given [type]. To be able to do this /// Get all the aggregated event objects for a given [type]. To be able to do this
/// you have to pass a [timeline] /// you have to pass a [timeline]
Set<Event> aggregatedEvents(Timeline timeline, String type) => Set<Event> aggregatedEvents(Timeline timeline, String type) =>
hasAggregatedEvents(timeline, type) timeline.aggregatedEvents[eventId]?[type] ?? <Event>{};
? timeline.aggregatedEvents[eventId][type]
: <Event>{};
/// Fetches the event to be rendered, taking into account all the edits and the like. /// Fetches the event to be rendered, taking into account all the edits and the like.
/// It needs a [timeline] for that. /// It needs a [timeline] for that.

File diff suppressed because it is too large Load Diff

View File

@ -132,15 +132,16 @@ class Timeline {
void _sessionKeyReceived(String sessionId) async { void _sessionKeyReceived(String sessionId) async {
var decryptAtLeastOneEvent = false; var decryptAtLeastOneEvent = false;
final decryptFn = () async { final decryptFn = () async {
if (!room.client.encryptionEnabled) { final encryption = room.client.encryption;
if (!room.client.encryptionEnabled || encryption == null) {
return; return;
} }
for (var i = 0; i < events.length; i++) { for (var i = 0; i < events.length; i++) {
if (events[i].type == EventTypes.Encrypted && if (events[i].type == EventTypes.Encrypted &&
events[i].messageType == MessageTypes.BadEncrypted && events[i].messageType == MessageTypes.BadEncrypted &&
events[i].content['session_id'] == sessionId) { events[i].content['session_id'] == sessionId) {
events[i] = await room.client.encryption events[i] = await encryption.decryptRoomEvent(room.id, events[i],
.decryptRoomEvent(room.id, events[i], store: true); store: true);
if (events[i].type != EventTypes.Encrypted) { if (events[i].type != EventTypes.Encrypted) {
decryptAtLeastOneEvent = true; decryptAtLeastOneEvent = true;
} }
@ -148,7 +149,7 @@ class Timeline {
} }
}; };
if (room.client.database != null) { if (room.client.database != null) {
await room.client.database.transaction(decryptFn); await room.client.database?.transaction(decryptFn);
} else { } else {
await decryptFn(); await decryptFn();
} }
@ -162,7 +163,7 @@ class Timeline {
event.messageType == MessageTypes.BadEncrypted && event.messageType == MessageTypes.BadEncrypted &&
event.content['can_request_session'] == true) { event.content['can_request_session'] == true) {
try { try {
room.client.encryption.keyManager.maybeAutoRequest(room.id, room.client.encryption?.keyManager.maybeAutoRequest(room.id,
event.content['session_id'], event.content['sender_key']); event.content['session_id'], event.content['sender_key']);
} catch (_) { } catch (_) {
// dispose // dispose
@ -186,13 +187,11 @@ class Timeline {
} }
int i; int i;
for (i = 0; i < events.length; i++) { for (i = 0; i < events.length; i++) {
final searchHaystack = <String>{}; final searchHaystack = <String>{events[i].eventId};
if (events[i].eventId != null) {
searchHaystack.add(events[i].eventId); final txnid = events[i].unsigned?['transaction_id'];
} if (txnid != null) {
if (events[i].unsigned != null && searchHaystack.add(txnid);
events[i].unsigned['transaction_id'] != null) {
searchHaystack.add(events[i].unsigned['transaction_id']);
} }
if (searchNeedle.intersection(searchHaystack).isNotEmpty) { if (searchNeedle.intersection(searchHaystack).isNotEmpty) {
break; break;
@ -205,19 +204,18 @@ class Timeline {
eventSet.removeWhere((e) => eventSet.removeWhere((e) =>
e.matchesEventOrTransactionId(event.eventId) || e.matchesEventOrTransactionId(event.eventId) ||
(event.unsigned != null && (event.unsigned != null &&
e.matchesEventOrTransactionId(event.unsigned['transaction_id']))); e.matchesEventOrTransactionId(event.unsigned?['transaction_id'])));
} }
void addAggregatedEvent(Event event) { void addAggregatedEvent(Event event) {
// we want to add an event to the aggregation tree // we want to add an event to the aggregation tree
if (event.relationshipType == null || event.relationshipEventId == null) { final relationshipType = event.relationshipType;
final relationshipEventId = event.relationshipEventId;
if (relationshipType == null || relationshipEventId == null) {
return; // nothing to do return; // nothing to do
} }
if (!aggregatedEvents.containsKey(event.relationshipEventId)) { final events = (aggregatedEvents[relationshipEventId] ??=
aggregatedEvents[event.relationshipEventId] = <String, Set<Event>>{}; <String, Set<Event>>{})[relationshipType] ??= <Event>{};
}
final events = (aggregatedEvents[event.relationshipEventId] ??=
<String, Set<Event>>{})[event.relationshipType] ??= <Event>{};
// remove a potential old event // remove a potential old event
_removeEventFromSet(events, event); _removeEventFromSet(events, event);
// add the new one // add the new one
@ -227,7 +225,7 @@ class Timeline {
void removeAggregatedEvent(Event event) { void removeAggregatedEvent(Event event) {
aggregatedEvents.remove(event.eventId); aggregatedEvents.remove(event.eventId);
if (event.unsigned != null) { if (event.unsigned != null) {
aggregatedEvents.remove(event.unsigned['transaction_id']); aggregatedEvents.remove(event.unsigned?['transaction_id']);
} }
for (final types in aggregatedEvents.values) { for (final types in aggregatedEvents.values) {
for (final events in types.values) { for (final events in types.values) {

View File

@ -49,9 +49,9 @@ class User extends Event {
required String stateKey, required String stateKey,
dynamic content, dynamic content,
required String typeKey, required String typeKey,
String? eventId, String eventId = 'fakevent',
String? roomId, String? roomId,
String? senderId, String senderId = 'fakesender',
required DateTime originServerTs, required DateTime originServerTs,
dynamic unsigned, dynamic unsigned,
Room? room}) Room? room})
@ -68,11 +68,11 @@ class User extends Event {
room: room); room: room);
/// The full qualified Matrix ID in the format @username:server.abc. /// The full qualified Matrix ID in the format @username:server.abc.
String get id => stateKey; String get id => stateKey ?? '@unknown:unknown';
/// The displayname of the user if the user has set one. /// The displayname of the user if the user has set one.
String? get displayName => String? get displayName =>
content?.tryGet<String>('displayname') ?? content.tryGet<String>('displayname') ??
prevContent?.tryGet<String>('displayname'); prevContent?.tryGet<String>('displayname');
/// Returns the power level of this user. /// Returns the power level of this user.
@ -91,13 +91,16 @@ class User extends Event {
}, orElse: () => Membership.join); }, orElse: () => Membership.join);
/// The avatar if the user has one. /// The avatar if the user has one.
Uri? get avatarUrl => content != null && content.containsKey('avatar_url') Uri? get avatarUrl {
? (content['avatar_url'] is String final prevContent = this.prevContent;
? Uri.tryParse(content['avatar_url']) return content.containsKey('avatar_url')
: null) ? (content['avatar_url'] is String
: (prevContent != null && prevContent['avatar_url'] is String ? Uri.tryParse(content['avatar_url'])
? Uri.tryParse(prevContent['avatar_url']) : null)
: null); : (prevContent != null && prevContent['avatar_url'] is String
? Uri.tryParse(prevContent['avatar_url'])
: null);
}
/// Returns the displayname or the local part of the Matrix ID if the user /// Returns the displayname or the local part of the Matrix ID if the user
/// has no displayname. If [formatLocalpart] is true, then the localpart will /// has no displayname. If [formatLocalpart] is true, then the localpart will
@ -109,8 +112,8 @@ class User extends Event {
bool? formatLocalpart, bool? formatLocalpart,
bool? mxidLocalPartFallback, bool? mxidLocalPartFallback,
}) { }) {
formatLocalpart ??= room?.client?.formatLocalpart ?? true; formatLocalpart ??= room?.client.formatLocalpart ?? true;
mxidLocalPartFallback ??= room?.client?.mxidLocalPartFallback ?? true; mxidLocalPartFallback ??= room?.client.mxidLocalPartFallback ?? true;
final displayName = this.displayName; final displayName = this.displayName;
if (displayName != null && displayName.isNotEmpty) { if (displayName != null && displayName.isNotEmpty) {
return displayName; return displayName;
@ -132,37 +135,40 @@ class User extends Event {
} }
/// Call the Matrix API to kick this user from this room. /// Call the Matrix API to kick this user from this room.
Future<void> kick() => room.kick(id); Future<void> kick() async => await room?.kick(id);
/// Call the Matrix API to ban this user from this room. /// Call the Matrix API to ban this user from this room.
Future<void> ban() => room.ban(id); Future<void> ban() async => await room?.ban(id);
/// Call the Matrix API to unban this banned user from this room. /// Call the Matrix API to unban this banned user from this room.
Future<void> unban() => room.unban(id); Future<void> unban() async => await room?.unban(id);
/// Call the Matrix API to change the power level of this user. /// Call the Matrix API to change the power level of this user.
Future<void> setPower(int power) => room.setPower(id, power); Future<void> setPower(int power) async => await room?.setPower(id, power);
/// Returns an existing direct chat ID with this user or creates a new one. /// Returns an existing direct chat ID with this user or creates a new one.
/// Returns null on error. /// Returns null on error.
Future<String?> startDirectChat() => room.client.startDirectChat(id); Future<String?> startDirectChat() async => room?.client.startDirectChat(id);
/// The newest presence of this user if there is any and null if not. /// The newest presence of this user if there is any and null if not.
Presence? get presence => room.client.presences[id]; Presence? get presence => room?.client.presences[id];
/// Whether the client is able to ban/unban this user. /// Whether the client is able to ban/unban this user.
bool get canBan => room.canBan && powerLevel < room.ownPowerLevel; bool get canBan =>
(room?.canBan ?? false) &&
powerLevel < (room?.ownPowerLevel ?? powerLevel);
/// Whether the client is able to kick this user. /// Whether the client is able to kick this user.
bool get canKick => bool get canKick =>
[Membership.join, Membership.invite].contains(membership) && [Membership.join, Membership.invite].contains(membership) &&
room.canKick && (room?.canKick ?? false) &&
powerLevel < room.ownPowerLevel; powerLevel < (room?.ownPowerLevel ?? powerLevel);
/// Whether the client is allowed to change the power level of this user. /// Whether the client is allowed to change the power level of this user.
/// Please be aware that you can only set the power level to at least your own! /// Please be aware that you can only set the power level to at least your own!
bool get canChangePowerLevel => bool get canChangePowerLevel =>
room.canChangePowerLevel && powerLevel < room.ownPowerLevel; (room?.canChangePowerLevel ?? false) &&
powerLevel < (room?.ownPowerLevel ?? powerLevel);
@override @override
bool operator ==(dynamic other) => (other is User && bool operator ==(dynamic other) => (other is User &&
@ -190,7 +196,7 @@ class User extends Event {
: '[$displayName]'); : '[$displayName]');
// get all the users with the same display name // get all the users with the same display name
final allUsersWithSameDisplayname = room.getParticipants(); final allUsersWithSameDisplayname = room?.getParticipants() ?? [];
allUsersWithSameDisplayname.removeWhere((user) => allUsersWithSameDisplayname.removeWhere((user) =>
user.id == id || user.id == id ||
(user.displayName?.isEmpty ?? true) || (user.displayName?.isEmpty ?? true) ||

View File

@ -171,33 +171,35 @@ extension CommandsClientExtension on Client {
}); });
addCommand('myroomnick', (CommandArgs args) async { addCommand('myroomnick', (CommandArgs args) async {
final currentEventJson = args.room final currentEventJson = args.room
.getState(EventTypes.RoomMember, args.room.client.userID) .getState(EventTypes.RoomMember, args.room.client.userID!)
.content ?.content
.copy(); .copy() ??
{};
currentEventJson['displayname'] = args.msg; currentEventJson['displayname'] = args.msg;
return await args.room.client.setRoomStateWithKey( return await args.room.client.setRoomStateWithKey(
args.room.id, args.room.id,
EventTypes.RoomMember, EventTypes.RoomMember,
args.room.client.userID, args.room.client.userID!,
currentEventJson, currentEventJson,
); );
}); });
addCommand('myroomavatar', (CommandArgs args) async { addCommand('myroomavatar', (CommandArgs args) async {
final currentEventJson = args.room final currentEventJson = args.room
.getState(EventTypes.RoomMember, args.room.client.userID) .getState(EventTypes.RoomMember, args.room.client.userID!)
.content ?.content
.copy(); .copy() ??
{};
currentEventJson['avatar_url'] = args.msg; currentEventJson['avatar_url'] = args.msg;
return await args.room.client.setRoomStateWithKey( return await args.room.client.setRoomStateWithKey(
args.room.id, args.room.id,
EventTypes.RoomMember, EventTypes.RoomMember,
args.room.client.userID, args.room.client.userID!,
currentEventJson, currentEventJson,
); );
}); });
addCommand('discardsession', (CommandArgs args) async { addCommand('discardsession', (CommandArgs args) async {
await encryption?.keyManager await encryption?.keyManager
?.clearOrUseOutboundGroupSession(args.room.id, wipe: true); .clearOrUseOutboundGroupSession(args.room.id, wipe: true);
return ''; return '';
}); });
} }

View File

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

View File

@ -104,12 +104,11 @@ abstract class EventLocalizations {
}, },
EventTypes.RoomMember: (event, i18n, body) { EventTypes.RoomMember: (event, i18n, body) {
var text = 'Failed to parse member event'; var text = 'Failed to parse member event';
final targetName = event.stateKeyUser.calcDisplayname(); final targetName = event.stateKeyUser?.calcDisplayname() ?? '';
// Has the membership changed? // Has the membership changed?
final newMembership = event.content['membership'] ?? ''; final newMembership = event.content['membership'] ?? '';
final oldMembership = event.prevContent != null final oldMembership = event.prevContent?['membership'] ?? '';
? event.prevContent['membership'] ?? ''
: '';
if (newMembership != oldMembership) { if (newMembership != oldMembership) {
if (oldMembership == 'invite' && newMembership == 'join') { if (oldMembership == 'invite' && newMembership == 'join') {
text = i18n.acceptedTheInvitation(targetName); text = i18n.acceptedTheInvitation(targetName);
@ -146,22 +145,19 @@ abstract class EventLocalizations {
} }
} else if (newMembership == 'join') { } else if (newMembership == 'join') {
final newAvatar = event.content['avatar_url'] ?? ''; final newAvatar = event.content['avatar_url'] ?? '';
final oldAvatar = event.prevContent != null final oldAvatar = event.prevContent?['avatar_url'] ?? '';
? event.prevContent['avatar_url'] ?? ''
: '';
final newDisplayname = event.content['displayname'] ?? ''; final newDisplayname = event.content['displayname'] ?? '';
final oldDisplayname = event.prevContent != null final oldDisplayname = event.prevContent?['displayname'] ?? '';
? event.prevContent['displayname'] ?? '' final stateKey = event.stateKey;
: '';
// Has the user avatar changed? // Has the user avatar changed?
if (newAvatar != oldAvatar) { if (newAvatar != oldAvatar) {
text = i18n.changedTheProfileAvatar(targetName); text = i18n.changedTheProfileAvatar(targetName);
} }
// Has the user avatar changed? // Has the user displayname changed?
else if (newDisplayname != oldDisplayname) { else if (newDisplayname != oldDisplayname && stateKey != null) {
text = i18n.changedTheDisplaynameTo(event.stateKey, newDisplayname); text = i18n.changedTheDisplaynameTo(stateKey, newDisplayname);
} }
} }
return text; return text;
@ -201,7 +197,7 @@ abstract class EventLocalizations {
EventTypes.Encryption: (event, i18n, body) { EventTypes.Encryption: (event, i18n, body) {
var localizedBody = var localizedBody =
i18n.activatedEndToEndEncryption(event.sender.calcDisplayname()); i18n.activatedEndToEndEncryption(event.sender.calcDisplayname());
if (!event.room.client.encryptionEnabled) { if (event.room?.client.encryptionEnabled == false) {
localizedBody += '. ' + i18n.needPantalaimonWarning; localizedBody += '. ' + i18n.needPantalaimonWarning;
} }
return localizedBody; return localizedBody;

View File

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

View File

@ -79,7 +79,9 @@ extension ImagePackRoomExtension on Room {
for (final entry in allRoomEmotes.entries) { for (final entry in allRoomEmotes.entries) {
addImagePack(entry.value, addImagePack(entry.value,
room: this, room: this,
slug: entry.value.stateKey.isEmpty ? 'room' : entry.value.stateKey); slug: (entry.value.stateKey?.isNotEmpty == true)
? entry.value.stateKey
: 'room');
} }
} }
return packs; return packs;

View File

@ -183,7 +183,7 @@ class PillSyntax extends InlineSyntax {
} }
class MentionSyntax extends InlineSyntax { class MentionSyntax extends InlineSyntax {
final String Function(String)? getMention; final String? Function(String)? getMention;
MentionSyntax(this.getMention) : super(r'(@(?:\[[^\]:]+\]|\w+)(?:#\w+)?)'); MentionSyntax(this.getMention) : super(r'(@(?:\[[^\]:]+\]|\w+)(?:#\w+)?)');
@override @override
@ -206,7 +206,7 @@ class MentionSyntax extends InlineSyntax {
String markdown( String markdown(
String text, { String text, {
Map<String, Map<String, String>> Function()? getEmotePacks, Map<String, Map<String, String>> Function()? getEmotePacks,
String Function(String)? getMention, String? Function(String)? getMention,
}) { }) {
var ret = markdownToHtml( var ret = markdownToHtml(
text, text,

View File

@ -21,7 +21,7 @@ import 'package:matrix_api_lite/matrix_api_lite.dart';
import '../event.dart'; import '../event.dart';
class SpaceChild { class SpaceChild {
final String roomId; final String? roomId;
final List<String>? via; final List<String>? via;
final String order; final String order;
final bool? suggested; final bool? suggested;
@ -35,7 +35,7 @@ class SpaceChild {
} }
class SpaceParent { class SpaceParent {
final String roomId; final String? roomId;
final List<String>? via; final List<String>? via;
final bool? canonical; final bool? canonical;

View File

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

View File

@ -84,8 +84,8 @@ class CallCapabilities {
transferee: json['m.call.transferee'] as bool? ?? false, transferee: json['m.call.transferee'] as bool? ?? false,
); );
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
if (transferee != null) 'm.call.transferee': transferee, 'm.call.transferee': transferee,
if (dtmf != null) 'm.call.dtmf': dtmf, 'm.call.dtmf': dtmf,
}; };
} }
@ -118,8 +118,8 @@ class SDPStreamPurpose {
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
'purpose': purpose, 'purpose': purpose,
if (audio_muted != null) 'audio_muted': audio_muted, 'audio_muted': audio_muted,
if (video_muted != null) 'video_muted': video_muted, 'video_muted': video_muted,
}; };
} }

View File

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

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2019, 2020 Famedly GmbH * Copyright (C) 2019, 2020 Famedly GmbH
@ -35,7 +34,7 @@ import 'fake_database.dart';
import 'fake_matrix_api.dart'; import 'fake_matrix_api.dart';
void main() { void main() {
Client matrix; late Client matrix;
Future<List<EventUpdate>> eventUpdateListFuture; Future<List<EventUpdate>> eventUpdateListFuture;
Future<List<ToDeviceEvent>> toDeviceUpdateListFuture; Future<List<ToDeviceEvent>> toDeviceUpdateListFuture;
@ -87,7 +86,7 @@ void main() {
try { try {
await matrix.checkHomeserver('https://fakeserver.wrongaddress'); await matrix.checkHomeserver('https://fakeserver.wrongaddress');
} catch (exception) { } catch (exception) {
expect(exception != null, true); expect(exception.toString().isNotEmpty, true);
} }
await matrix.checkHomeserver('https://fakeserver.notexisting', await matrix.checkHomeserver('https://fakeserver.notexisting',
checkWellKnown: false); checkWellKnown: false);
@ -128,7 +127,7 @@ void main() {
expect(matrix.getDirectChatFromUserId('@bob:example.com'), expect(matrix.getDirectChatFromUserId('@bob:example.com'),
'!726s6s6q:example.com'); '!726s6s6q:example.com');
expect(matrix.rooms[1].directChatMatrixID, '@bob:example.com'); expect(matrix.rooms[1].directChatMatrixID, '@bob:example.com');
expect(matrix.directChats, matrix.accountData['m.direct'].content); expect(matrix.directChats, matrix.accountData['m.direct']?.content);
expect(matrix.presences.length, 1); expect(matrix.presences.length, 1);
expect(matrix.rooms[1].ephemerals.length, 2); expect(matrix.rooms[1].ephemerals.length, 2);
expect(matrix.rooms[1].typingUsers.length, 1); expect(matrix.rooms[1].typingUsers.length, 1);
@ -139,29 +138,30 @@ void main() {
Client.supportedGroupEncryptionAlgorithms.first); Client.supportedGroupEncryptionAlgorithms.first);
expect( expect(
matrix.rooms[1].roomAccountData['m.receipt'] matrix.rooms[1].roomAccountData['m.receipt']
.content['@alice:example.com']['ts'], ?.content['@alice:example.com']['ts'],
1436451550453); 1436451550453);
expect( expect(
matrix.rooms[1].roomAccountData['m.receipt'] matrix.rooms[1].roomAccountData['m.receipt']
.content['@alice:example.com']['event_id'], ?.content['@alice:example.com']['event_id'],
'7365636s6r6432:example.com'); '7365636s6r6432:example.com');
expect(matrix.rooms.length, 2); expect(matrix.rooms.length, 2);
expect(matrix.rooms[1].canonicalAlias, expect(matrix.rooms[1].canonicalAlias,
"#famedlyContactDiscovery:${matrix.userID.split(":")[1]}"); "#famedlyContactDiscovery:${matrix.userID!.split(":")[1]}");
expect(matrix.presences['@alice:example.com'].presence.presence, expect(matrix.presences['@alice:example.com']?.presence.presence,
PresenceType.online); PresenceType.online);
expect(presenceCounter, 1); expect(presenceCounter, 1);
expect(accountDataCounter, 9); expect(accountDataCounter, 9);
await Future.delayed(Duration(milliseconds: 50)); await Future.delayed(Duration(milliseconds: 50));
expect(matrix.userDeviceKeys.length, 4); expect(matrix.userDeviceKeys.length, 4);
expect(matrix.userDeviceKeys['@alice:example.com'].outdated, false); expect(matrix.userDeviceKeys['@alice:example.com']?.outdated, false);
expect(matrix.userDeviceKeys['@alice:example.com'].deviceKeys.length, 2); expect(matrix.userDeviceKeys['@alice:example.com']?.deviceKeys.length, 2);
expect( expect(
matrix.userDeviceKeys['@alice:example.com'].deviceKeys['JLAFKJWSCS'] matrix.userDeviceKeys['@alice:example.com']?.deviceKeys['JLAFKJWSCS']
.verified, ?.verified,
false); false);
await matrix.handleSync(SyncUpdate.fromJson({ await matrix.handleSync(SyncUpdate.fromJson({
'next_batch': 'fakesync',
'device_lists': { 'device_lists': {
'changed': [ 'changed': [
'@alice:example.com', '@alice:example.com',
@ -173,9 +173,10 @@ void main() {
})); }));
await Future.delayed(Duration(milliseconds: 50)); await Future.delayed(Duration(milliseconds: 50));
expect(matrix.userDeviceKeys.length, 3); expect(matrix.userDeviceKeys.length, 3);
expect(matrix.userDeviceKeys['@alice:example.com'].outdated, true); expect(matrix.userDeviceKeys['@alice:example.com']?.outdated, true);
await matrix.handleSync(SyncUpdate.fromJson({ await matrix.handleSync(SyncUpdate.fromJson({
'next_batch': 'fakesync',
'rooms': { 'rooms': {
'join': { 'join': {
'!726s6s6q:example.com': { '!726s6s6q:example.com': {
@ -199,7 +200,7 @@ void main() {
expect( expect(
matrix.getRoomByAlias( matrix.getRoomByAlias(
"#famedlyContactDiscovery:${matrix.userID.split(":")[1]}"), "#famedlyContactDiscovery:${matrix.userID!.split(":")[1]}"),
null); null);
}); });
@ -310,7 +311,7 @@ void main() {
identifier: AuthenticationUserIdentifier(user: 'test'), identifier: AuthenticationUserIdentifier(user: 'test'),
password: '1234'); password: '1234');
expect(loginResp != null, true); expect(loginResp.userId != null, true);
}); });
test('setAvatar', () async { test('setAvatar', () async {
@ -340,7 +341,7 @@ void main() {
expect(archive[0].id, '!5345234234:example.com'); expect(archive[0].id, '!5345234234:example.com');
expect(archive[0].membership, Membership.leave); expect(archive[0].membership, Membership.leave);
expect(archive[0].name, 'The room name'); expect(archive[0].name, 'The room name');
expect(archive[0].lastEvent.body, 'This is an example text message'); expect(archive[0].lastEvent?.body, 'This is an example text message');
expect(archive[0].roomAccountData.length, 1); expect(archive[0].roomAccountData.length, 1);
expect(archive[1].id, '!5345234235:example.com'); expect(archive[1].id, '!5345234235:example.com');
expect(archive[1].membership, Membership.leave); expect(archive[1].membership, Membership.leave);
@ -364,7 +365,7 @@ void main() {
} }
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await matrix.sendToDeviceEncrypted( await matrix.sendToDeviceEncrypted(
matrix.userDeviceKeys['@alice:example.com'].deviceKeys.values matrix.userDeviceKeys['@alice:example.com']!.deviceKeys.values
.toList(), .toList(),
'm.message', 'm.message',
{ {
@ -382,7 +383,7 @@ void main() {
} }
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await matrix.sendToDeviceEncryptedChunked( await matrix.sendToDeviceEncryptedChunked(
matrix.userDeviceKeys['@alice:example.com'].deviceKeys.values matrix.userDeviceKeys['@alice:example.com']!.deviceKeys.values
.toList(), .toList(),
'm.message', 'm.message',
{ {
@ -483,11 +484,11 @@ void main() {
expect( expect(
json.decode(FakeMatrixApi json.decode(FakeMatrixApi
.calledEndpoints['/client/r0/sendToDevice/foxies/floof_txnid'] .calledEndpoints['/client/r0/sendToDevice/foxies/floof_txnid']
[0])['messages'], ?[0])['messages'],
foxContent); foxContent);
expect( expect(
json.decode(FakeMatrixApi.calledEndpoints[ json.decode(FakeMatrixApi.calledEndpoints[
'/client/r0/sendToDevice/raccoon/raccoon_txnid'][0])['messages'], '/client/r0/sendToDevice/raccoon/raccoon_txnid']?[0])['messages'],
raccoonContent); raccoonContent);
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client.sendToDevice('bunny', 'bunny_txnid', bunnyContent); await client.sendToDevice('bunny', 'bunny_txnid', bunnyContent);
@ -502,7 +503,7 @@ void main() {
expect( expect(
json.decode(FakeMatrixApi json.decode(FakeMatrixApi
.calledEndpoints['/client/r0/sendToDevice/bunny/bunny_txnid'] .calledEndpoints['/client/r0/sendToDevice/bunny/bunny_txnid']
[0])['messages'], ?[0])['messages'],
bunnyContent); bunnyContent);
await client.dispose(closeDatabase: true); await client.dispose(closeDatabase: true);
}); });
@ -546,16 +547,16 @@ void main() {
expect( expect(
json.decode(FakeMatrixApi json.decode(FakeMatrixApi
.calledEndpoints['/client/r0/sendToDevice/foxies/floof_txnid'] .calledEndpoints['/client/r0/sendToDevice/foxies/floof_txnid']
[0])['messages'], ?[0])['messages'],
foxContent); foxContent);
expect( expect(
json.decode(FakeMatrixApi.calledEndpoints[ json.decode(FakeMatrixApi.calledEndpoints[
'/client/r0/sendToDevice/raccoon/raccoon_txnid'][0])['messages'], '/client/r0/sendToDevice/raccoon/raccoon_txnid']?[0])['messages'],
raccoonContent); raccoonContent);
expect( expect(
json.decode(FakeMatrixApi json.decode(FakeMatrixApi
.calledEndpoints['/client/r0/sendToDevice/bunny/bunny_txnid'] .calledEndpoints['/client/r0/sendToDevice/bunny/bunny_txnid']
[0])['messages'], ?[0])['messages'],
bunnyContent); bunnyContent);
await client.dispose(closeDatabase: true); await client.dispose(closeDatabase: true);
}); });
@ -596,10 +597,12 @@ void main() {
expect(client2.homeserver, client1.homeserver); expect(client2.homeserver, client1.homeserver);
expect(client2.deviceID, client1.deviceID); expect(client2.deviceID, client1.deviceID);
expect(client2.deviceName, client1.deviceName); expect(client2.deviceName, client1.deviceName);
expect(client2.rooms.length, 2);
if (client2.encryptionEnabled) { if (client2.encryptionEnabled) {
expect(client2.encryption.fingerprintKey, expect(client2.encryption?.fingerprintKey,
client1.encryption.fingerprintKey); client1.encryption?.fingerprintKey);
expect(client2.encryption.identityKey, client1.encryption.identityKey); expect(
client2.encryption?.identityKey, client1.encryption?.identityKey);
expect(client2.rooms[1].id, client1.rooms[1].id); expect(client2.rooms[1].id, client1.rooms[1].id);
} }
@ -628,16 +631,18 @@ void main() {
final response = final response =
await client.uploadContent(Uint8List(0), filename: 'file.jpeg'); await client.uploadContent(Uint8List(0), filename: 'file.jpeg');
expect(response.toString(), 'mxc://example.com/AQwafuaFswefuhsfAFAgsw'); expect(response.toString(), 'mxc://example.com/AQwafuaFswefuhsfAFAgsw');
expect(await client.database.getFile(response) != null, expect(await client.database?.getFile(response) != null,
client.database.supportsFileStoring); client.database?.supportsFileStoring);
await client.dispose(closeDatabase: true); await client.dispose(closeDatabase: true);
}); });
test('object equality', () async { test('object equality', () async {
final time1 = DateTime.fromMillisecondsSinceEpoch(1); final time1 = DateTime.fromMillisecondsSinceEpoch(1);
final time2 = DateTime.fromMillisecondsSinceEpoch(0); final time2 = DateTime.fromMillisecondsSinceEpoch(0);
final user1 = User('@user1:example.org', room: Room(id: '!room1')); final user1 =
final user2 = User('@user2:example.org', room: Room(id: '!room1')); User('@user1:example.org', room: Room(id: '!room1', client: matrix));
final user2 =
User('@user2:example.org', room: Room(id: '!room1', client: matrix));
// receipts // receipts
expect(Receipt(user1, time1) == Receipt(user1, time1), true); expect(Receipt(user1, time1) == Receipt(user1, time1), true);
expect(Receipt(user1, time1) == Receipt(user1, time2), false); expect(Receipt(user1, time1) == Receipt(user1, time2), false);
@ -648,19 +653,29 @@ void main() {
expect(user1 == user1, true); expect(user1 == user1, true);
expect(user1 == user2, false); expect(user1 == user2, false);
expect( expect(
user1 == User('@user1:example.org', room: Room(id: '!room2')), false); user1 ==
User('@user1:example.org',
room: Room(id: '!room2', client: matrix)),
false);
expect( expect(
user1 == user1 ==
User('@user1:example.org', User('@user1:example.org',
room: Room(id: '!room1'), membership: 'leave'), room: Room(id: '!room1', client: matrix),
membership: 'leave'),
false); false);
// ignore: unrelated_type_equality_checks // ignore: unrelated_type_equality_checks
expect(user1 == 'beep', false); expect(user1 == 'beep', false);
// rooms // rooms
expect(Room(id: '!room1') == Room(id: '!room1'), true); expect(
expect(Room(id: '!room1') == Room(id: '!room2'), false); Room(id: '!room1', client: matrix) ==
Room(id: '!room1', client: matrix),
true);
expect(
Room(id: '!room1', client: matrix) ==
Room(id: '!room2', client: matrix),
false);
// ignore: unrelated_type_equality_checks // ignore: unrelated_type_equality_checks
expect(Room(id: '!room1') == 'beep', false); expect(Room(id: '!room1', client: matrix) == 'beep', false);
}); });
test('clearCache', () async { test('clearCache', () async {

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2021 Famedly GmbH * Copyright (C) 2021 Famedly GmbH
@ -27,16 +26,16 @@ import 'fake_matrix_api.dart';
void main() { void main() {
group('Commands', () { group('Commands', () {
Client client; late Client client;
Room room; late Room room;
var olmEnabled = true; var olmEnabled = true;
final getLastMessagePayload = final getLastMessagePayload =
([String type = 'm.room.message', String stateKey]) { ([String type = 'm.room.message', String? stateKey]) {
final state = stateKey != null; final state = stateKey != null;
return json.decode(FakeMatrixApi.calledEndpoints.entries return json.decode(FakeMatrixApi.calledEndpoints.entries
.firstWhere((e) => e.key.startsWith( .firstWhere((e) => e.key.startsWith(
'/client/r0/rooms/${Uri.encodeComponent(room.id)}/${state ? 'state' : 'send'}/${Uri.encodeComponent(type)}${state && stateKey.isNotEmpty ? '/' + Uri.encodeComponent(stateKey) : ''}')) '/client/r0/rooms/${Uri.encodeComponent(room.id)}/${state ? 'state' : 'send'}/${Uri.encodeComponent(type)}${state && stateKey?.isNotEmpty == true ? '/' + Uri.encodeComponent(stateKey!) : ''}'))
.value .value
.first); .first);
}; };
@ -55,12 +54,18 @@ void main() {
content: {}, content: {},
room: room, room: room,
stateKey: '', stateKey: '',
eventId: '\$fakeeventid',
originServerTs: DateTime.now(),
senderId: '\@fakeuser:fakeServer.notExisting',
)); ));
room.setState(Event( room.setState(Event(
type: 'm.room.member', type: 'm.room.member',
content: {'membership': 'join'}, content: {'membership': 'join'},
room: room, room: room,
stateKey: client.userID, stateKey: client.userID,
eventId: '\$fakeeventid',
originServerTs: DateTime.now(),
senderId: '\@fakeuser:fakeServer.notExisting',
)); ));
}); });
@ -135,7 +140,18 @@ void main() {
test('react', () async { test('react', () async {
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await room.sendTextEvent('/react 🦊', await room.sendTextEvent('/react 🦊',
inReplyTo: Event(eventId: '\$event')); inReplyTo: Event(
eventId: '\$event',
type: 'm.room.message',
content: {
'msgtype': 'm.text',
'body': '<b>yay</b>',
'format': 'org.matrix.custom.html',
'formatted_body': '<b>yay</b>',
},
originServerTs: DateTime.now(),
senderId: client.userID!,
));
final sent = getLastMessagePayload('m.reaction'); final sent = getLastMessagePayload('m.reaction');
expect(sent, { expect(sent, {
'm.relates_to': { 'm.relates_to': {
@ -152,7 +168,7 @@ void main() {
expect( expect(
FakeMatrixApi FakeMatrixApi
.calledEndpoints['/client/r0/join/!newroom%3Aexample.com'] .calledEndpoints['/client/r0/join/!newroom%3Aexample.com']
.first != ?.first !=
null, null,
true); true);
}); });
@ -164,7 +180,7 @@ void main() {
FakeMatrixApi FakeMatrixApi
.calledEndpoints[ .calledEndpoints[
'/client/r0/rooms/!1234%3AfakeServer.notExisting/leave'] '/client/r0/rooms/!1234%3AfakeServer.notExisting/leave']
.first != ?.first !=
null, null,
true); true);
}); });
@ -192,7 +208,7 @@ void main() {
json.decode(FakeMatrixApi json.decode(FakeMatrixApi
.calledEndpoints[ .calledEndpoints[
'/client/r0/rooms/!1234%3AfakeServer.notExisting/kick'] '/client/r0/rooms/!1234%3AfakeServer.notExisting/kick']
.first), ?.first),
{ {
'user_id': '@baduser:example.org', 'user_id': '@baduser:example.org',
}); });
@ -205,7 +221,7 @@ void main() {
json.decode(FakeMatrixApi json.decode(FakeMatrixApi
.calledEndpoints[ .calledEndpoints[
'/client/r0/rooms/!1234%3AfakeServer.notExisting/ban'] '/client/r0/rooms/!1234%3AfakeServer.notExisting/ban']
.first), ?.first),
{ {
'user_id': '@baduser:example.org', 'user_id': '@baduser:example.org',
}); });
@ -218,7 +234,7 @@ void main() {
json.decode(FakeMatrixApi json.decode(FakeMatrixApi
.calledEndpoints[ .calledEndpoints[
'/client/r0/rooms/!1234%3AfakeServer.notExisting/unban'] '/client/r0/rooms/!1234%3AfakeServer.notExisting/unban']
.first), ?.first),
{ {
'user_id': '@baduser:example.org', 'user_id': '@baduser:example.org',
}); });
@ -231,7 +247,7 @@ void main() {
json.decode(FakeMatrixApi json.decode(FakeMatrixApi
.calledEndpoints[ .calledEndpoints[
'/client/r0/rooms/!1234%3AfakeServer.notExisting/invite'] '/client/r0/rooms/!1234%3AfakeServer.notExisting/invite']
.first), ?.first),
{ {
'user_id': '@baduser:example.org', 'user_id': '@baduser:example.org',
}); });
@ -259,14 +275,14 @@ void main() {
test('discardsession', () async { test('discardsession', () async {
if (olmEnabled) { if (olmEnabled) {
await client.encryption.keyManager.createOutboundGroupSession(room.id); await client.encryption?.keyManager.createOutboundGroupSession(room.id);
expect( expect(
client.encryption.keyManager.getOutboundGroupSession(room.id) != client.encryption?.keyManager.getOutboundGroupSession(room.id) !=
null, null,
true); true);
await room.sendTextEvent('/discardsession'); await room.sendTextEvent('/discardsession');
expect( expect(
client.encryption.keyManager.getOutboundGroupSession(room.id) != client.encryption?.keyManager.getOutboundGroupSession(room.id) !=
null, null,
false); false);
} }

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2019, 2020 Famedly GmbH * Copyright (C) 2019, 2020 Famedly GmbH
@ -49,8 +48,8 @@ Future<bool> olmEnabled() async {
void testDatabase( void testDatabase(
Future<DatabaseApi> futureDatabase, Future<DatabaseApi> futureDatabase,
) { ) {
DatabaseApi database; late DatabaseApi database;
int toDeviceQueueIndex; late int toDeviceQueueIndex;
test('Open', () async { test('Open', () async {
database = await futureDatabase; database = await futureDatabase;
}); });
@ -115,8 +114,9 @@ void testDatabase(
'limited_timeline': false, 'limited_timeline': false,
'membership': Membership.join, 'membership': Membership.join,
}); });
await database.storeRoomUpdate('!testroom', roomUpdate); final client = Client('testclient');
final rooms = await database.getRoomList(Client('testclient')); await database.storeRoomUpdate('!testroom', roomUpdate, client);
final rooms = await database.getRoomList(client);
expect(rooms.single.id, '!testroom'); expect(rooms.single.id, '!testroom');
}); });
test('getRoomList', () async { test('getRoomList', () async {
@ -124,8 +124,9 @@ void testDatabase(
expect(list.single.id, '!testroom'); expect(list.single.id, '!testroom');
}); });
test('setRoomPrevBatch', () async { test('setRoomPrevBatch', () async {
await database.setRoomPrevBatch('1234', '!testroom'); final client = Client('testclient');
final rooms = await database.getRoomList(Client('testclient')); await database.setRoomPrevBatch('1234', '!testroom', client);
final rooms = await database.getRoomList(client);
expect(rooms.single.prev_batch, '1234'); expect(rooms.single.prev_batch, '1234');
}); });
test('forgetRoom', () async { test('forgetRoom', () async {
@ -149,7 +150,7 @@ void testDatabase(
); );
final client = await database.getClient('name'); final client = await database.getClient('name');
expect(client['token'], 'token'); expect(client?['token'], 'token');
}); });
test('updateClient', () async { test('updateClient', () async {
await database.updateClient( await database.updateClient(
@ -162,21 +163,21 @@ void testDatabase(
'olmAccount', 'olmAccount',
); );
final client = await database.getClient('name'); final client = await database.getClient('name');
expect(client['token'], 'token_different'); expect(client?['token'], 'token_different');
}); });
test('updateClientKeys', () async { test('updateClientKeys', () async {
await database.updateClientKeys( await database.updateClientKeys(
'olmAccount2', 'olmAccount2',
); );
final client = await database.getClient('name'); final client = await database.getClient('name');
expect(client['olm_account'], 'olmAccount2'); expect(client?['olm_account'], 'olmAccount2');
}); });
test('storeSyncFilterId', () async { test('storeSyncFilterId', () async {
await database.storeSyncFilterId( await database.storeSyncFilterId(
'1234', '1234',
); );
final client = await database.getClient('name'); final client = await database.getClient('name');
expect(client['sync_filter_id'], '1234'); expect(client?['sync_filter_id'], '1234');
}); });
test('getAccountData', () async { test('getAccountData', () async {
await database.getAccountData(); await database.getAccountData();
@ -192,52 +193,53 @@ void testDatabase(
}); });
test('storeEventUpdate', () async { test('storeEventUpdate', () async {
await database.storeEventUpdate( await database.storeEventUpdate(
EventUpdate( EventUpdate(
roomID: '!testroom:example.com', roomID: '!testroom:example.com',
type: EventUpdateType.timeline, type: EventUpdateType.timeline,
content: { content: {
'type': EventTypes.Message, 'type': EventTypes.Message,
'content': { 'content': {
'body': '* edit 3', 'body': '* edit 3',
'msgtype': 'm.text',
'm.new_content': {
'body': 'edit 3',
'msgtype': 'm.text', 'msgtype': 'm.text',
'm.new_content': {
'body': 'edit 3',
'msgtype': 'm.text',
},
'm.relates_to': {
'event_id': '\$source',
'rel_type': RelationshipTypes.edit,
},
}, },
'm.relates_to': { 'event_id': '\$event:example.com',
'event_id': '\$source', 'sender': '@bob:example.org',
'rel_type': RelationshipTypes.edit,
},
}, },
'event_id': '\$event:example.com', ),
'sender': '@bob:example.org', Client('testclient'));
},
),
);
}); });
test('getEventById', () async { test('getEventById', () async {
final event = await database.getEventById( final event = await database.getEventById('\$event:example.com',
'\$event:example.com', Room(id: '!testroom:example.com')); Room(id: '!testroom:example.com', client: Client('testclient')));
expect(event.type, EventTypes.Message); expect(event?.type, EventTypes.Message);
}); });
test('getEventList', () async { test('getEventList', () async {
final events = final events = await database.getEventList(
await database.getEventList(Room(id: '!testroom:example.com')); Room(id: '!testroom:example.com', client: Client('testclient')));
expect(events.single.type, EventTypes.Message); expect(events.single.type, EventTypes.Message);
}); });
test('getUser', () async { test('getUser', () async {
final user = await database.getUser( final user = await database.getUser('@bob:example.org',
'@bob:example.org', Room(id: '!testroom:example.com')); Room(id: '!testroom:example.com', client: Client('testclient')));
expect(user, null); expect(user, null);
}); });
test('getUsers', () async { test('getUsers', () async {
final users = await database.getUsers(Room(id: '!testroom:example.com')); final users = await database.getUsers(
Room(id: '!testroom:example.com', client: Client('testclient')));
expect(users.isEmpty, true); expect(users.isEmpty, true);
}); });
test('removeEvent', () async { test('removeEvent', () async {
await database.removeEvent('\$event:example.com', '!testroom:example.com'); await database.removeEvent('\$event:example.com', '!testroom:example.com');
final event = await database.getEventById( final event = await database.getEventById('\$event:example.com',
'\$event:example.com', Room(id: '!testroom:example.com')); Room(id: '!testroom:example.com', client: Client('testclient')));
expect(event, null); expect(event, null);
}); });
test('getAllInboundGroupSessions', () async { test('getAllInboundGroupSessions', () async {
@ -265,7 +267,7 @@ void testDatabase(
'!testroom:example.com', '!testroom:example.com',
'sessionId', 'sessionId',
); );
expect(jsonDecode(session.content)['foo'], 'bar'); expect(jsonDecode(session!.content)['foo'], 'bar');
}); });
test('markInboundGroupSessionAsUploaded', () async { test('markInboundGroupSessionAsUploaded', () async {
await database.markInboundGroupSessionAsUploaded( await database.markInboundGroupSessionAsUploaded(
@ -294,7 +296,7 @@ void testDatabase(
}); });
test('storeSSSSCache', () async { test('storeSSSSCache', () async {
await database.storeSSSSCache('type', 'keyId', 'ciphertext', '{}'); await database.storeSSSSCache('type', 'keyId', 'ciphertext', '{}');
final cache = await database.getSSSSCache('type'); final cache = (await database.getSSSSCache('type'))!;
expect(cache.type, 'type'); expect(cache.type, 'type');
expect(cache.keyId, 'keyId'); expect(cache.keyId, 'keyId');
expect(cache.ciphertext, 'ciphertext'); expect(cache.ciphertext, 'ciphertext');
@ -347,7 +349,7 @@ void testDatabase(
'!testroom:example.com', '!testroom:example.com',
'@alice:example.com', '@alice:example.com',
); );
expect(session.devices.isEmpty, true); expect(session?.devices.isEmpty, true);
}); });
test('getLastSentMessageUserDeviceKey', () async { test('getLastSentMessageUserDeviceKey', () async {
final list = await database.getLastSentMessageUserDeviceKey( final list = await database.getLastSentMessageUserDeviceKey(
@ -359,7 +361,7 @@ void testDatabase(
test('getUnimportantRoomEventStatesForRoom', () async { test('getUnimportantRoomEventStatesForRoom', () async {
final events = await database.getUnimportantRoomEventStatesForRoom( final events = await database.getUnimportantRoomEventStatesForRoom(
['events'], ['events'],
Room(id: '!mep'), Room(id: '!mep', client: Client('testclient')),
); );
expect(events.isEmpty, true); expect(events.isEmpty, true);
}); });

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2019, 2020 Famedly GmbH * Copyright (C) 2019, 2020 Famedly GmbH
@ -34,7 +33,7 @@ void main() {
var olmEnabled = true; var olmEnabled = true;
Client client; late Client client;
test('setupClient', () async { test('setupClient', () async {
try { try {
@ -135,8 +134,8 @@ void main() {
test('set blocked / verified', () async { test('set blocked / verified', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
final key = final key =
client.userDeviceKeys[client.userID].deviceKeys['OTHERDEVICE']; client.userDeviceKeys[client.userID]!.deviceKeys['OTHERDEVICE']!;
client.userDeviceKeys[client.userID].deviceKeys['UNSIGNEDDEVICE'] = client.userDeviceKeys[client.userID]?.deviceKeys['UNSIGNEDDEVICE'] =
DeviceKeys.fromJson({ DeviceKeys.fromJson({
'user_id': '@test:fakeServer.notExisting', 'user_id': '@test:fakeServer.notExisting',
'device_id': 'UNSIGNEDDEVICE', 'device_id': 'UNSIGNEDDEVICE',
@ -157,10 +156,10 @@ void main() {
}, },
}, },
}, client); }, client);
final masterKey = client.userDeviceKeys[client.userID].masterKey; final masterKey = client.userDeviceKeys[client.userID]!.masterKey!;
masterKey.setDirectVerified(true); masterKey.setDirectVerified(true);
// we need to populate the ssss cache to be able to test signing easily // we need to populate the ssss cache to be able to test signing easily
final handle = client.encryption.ssss.open(); final handle = client.encryption!.ssss.open();
await handle.unlock(recoveryKey: ssssKey); await handle.unlock(recoveryKey: ssssKey);
await handle.maybeCacheAll(); await handle.maybeCacheAll();
@ -174,16 +173,16 @@ void main() {
expect(key.verified, true); // still verified via cross-sgining expect(key.verified, true); // still verified via cross-sgining
expect(key.encryptToDevice, true); expect(key.encryptToDevice, true);
expect( expect(
client.userDeviceKeys[client.userID].deviceKeys['UNSIGNEDDEVICE'] client.userDeviceKeys[client.userID]?.deviceKeys['UNSIGNEDDEVICE']
.encryptToDevice, ?.encryptToDevice,
false); false);
expect(masterKey.verified, true); expect(masterKey.verified, true);
await masterKey.setBlocked(true); await masterKey.setBlocked(true);
expect(masterKey.verified, false); expect(masterKey.verified, false);
expect( expect(
client.userDeviceKeys[client.userID].deviceKeys['UNSIGNEDDEVICE'] client.userDeviceKeys[client.userID]?.deviceKeys['UNSIGNEDDEVICE']
.encryptToDevice, ?.encryptToDevice,
true); true);
await masterKey.setBlocked(false); await masterKey.setBlocked(false);
expect(masterKey.verified, true); expect(masterKey.verified, true);
@ -205,57 +204,57 @@ void main() {
.any((k) => k == '/client/r0/keys/signatures/upload'), .any((k) => k == '/client/r0/keys/signatures/upload'),
false); false);
expect(key.directVerified, false); expect(key.directVerified, false);
client.userDeviceKeys[client.userID].deviceKeys.remove('UNSIGNEDDEVICE'); client.userDeviceKeys[client.userID]?.deviceKeys.remove('UNSIGNEDDEVICE');
}); });
test('verification based on signatures', () async { test('verification based on signatures', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
final user = client.userDeviceKeys[client.userID]; final user = client.userDeviceKeys[client.userID]!;
user.masterKey.setDirectVerified(true); user.masterKey?.setDirectVerified(true);
expect(user.deviceKeys['GHTYAJCE'].crossVerified, true); expect(user.deviceKeys['GHTYAJCE']?.crossVerified, true);
expect(user.deviceKeys['GHTYAJCE'].signed, true); expect(user.deviceKeys['GHTYAJCE']?.signed, true);
expect(user.getKey('GHTYAJCE').crossVerified, true); expect(user.getKey('GHTYAJCE')?.crossVerified, true);
expect(user.deviceKeys['OTHERDEVICE'].crossVerified, true); expect(user.deviceKeys['OTHERDEVICE']?.crossVerified, true);
expect(user.selfSigningKey.crossVerified, true); expect(user.selfSigningKey?.crossVerified, true);
expect( expect(
user user
.getKey('F9ypFzgbISXCzxQhhSnXMkc1vq12Luna3Nw5rqViOJY') .getKey('F9ypFzgbISXCzxQhhSnXMkc1vq12Luna3Nw5rqViOJY')
.crossVerified, ?.crossVerified,
true); true);
expect(user.userSigningKey.crossVerified, true); expect(user.userSigningKey?.crossVerified, true);
expect(user.verified, UserVerifiedStatus.verified); expect(user.verified, UserVerifiedStatus.verified);
user.masterKey.setDirectVerified(false); user.masterKey?.setDirectVerified(false);
expect(user.deviceKeys['GHTYAJCE'].crossVerified, false); expect(user.deviceKeys['GHTYAJCE']?.crossVerified, false);
expect(user.deviceKeys['OTHERDEVICE'].crossVerified, false); expect(user.deviceKeys['OTHERDEVICE']?.crossVerified, false);
expect(user.verified, UserVerifiedStatus.unknown); expect(user.verified, UserVerifiedStatus.unknown);
user.deviceKeys['OTHERDEVICE'].setDirectVerified(true); user.deviceKeys['OTHERDEVICE']?.setDirectVerified(true);
expect(user.verified, UserVerifiedStatus.verified); expect(user.verified, UserVerifiedStatus.verified);
user.deviceKeys['OTHERDEVICE'].setDirectVerified(false); user.deviceKeys['OTHERDEVICE']?.setDirectVerified(false);
user.masterKey.setDirectVerified(true); user.masterKey?.setDirectVerified(true);
user.deviceKeys['GHTYAJCE'].signatures[client.userID] user.deviceKeys['GHTYAJCE']?.signatures?[client.userID]
.removeWhere((k, v) => k != 'ed25519:GHTYAJCE'); ?.removeWhere((k, v) => k != 'ed25519:GHTYAJCE');
expect(user.deviceKeys['GHTYAJCE'].verified, expect(user.deviceKeys['GHTYAJCE']?.verified,
true); // it's our own device, should be direct verified true); // it's our own device, should be direct verified
expect( expect(user.deviceKeys['GHTYAJCE']?.signed,
user.deviceKeys['GHTYAJCE'].signed, false); // not verified for others false); // not verified for others
user.deviceKeys['OTHERDEVICE'].signatures.clear(); user.deviceKeys['OTHERDEVICE']?.signatures?.clear();
expect(user.verified, UserVerifiedStatus.unknownDevice); expect(user.verified, UserVerifiedStatus.unknownDevice);
}); });
test('start verification', () async { test('start verification', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
var req = client var req = client
.userDeviceKeys['@alice:example.com'].deviceKeys['JLAFKJWSCS'] .userDeviceKeys['@alice:example.com']?.deviceKeys['JLAFKJWSCS']
.startVerification(); ?.startVerification();
expect(req != null, true); expect(req != null, true);
expect(req.room != null, false); expect(req?.room != null, false);
req = req = await client.userDeviceKeys['@alice:example.com']
await client.userDeviceKeys['@alice:example.com'].startVerification(); ?.startVerification();
expect(req != null, true); expect(req != null, true);
expect(req.room != null, true); expect(req?.room != null, true);
}); });
test('dispose client', () async { test('dispose client', () async {

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2020 Famedly GmbH * Copyright (C) 2020 Famedly GmbH
@ -33,9 +32,9 @@ void main() {
Logs().level = Level.error; Logs().level = Level.error;
var olmEnabled = true; var olmEnabled = true;
Client client; late Client client;
Map<String, dynamic> oldSecret; late Map<String, dynamic> oldSecret;
String origKeyId; late String origKeyId;
test('setupClient', () async { test('setupClient', () async {
client = await getClient(); client = await getClient();
@ -53,8 +52,8 @@ void main() {
Logs().i('[LibOlm] Enabled: $olmEnabled'); Logs().i('[LibOlm] Enabled: $olmEnabled');
if (!olmEnabled) return; if (!olmEnabled) return;
Bootstrap bootstrap; Bootstrap? bootstrap;
bootstrap = client.encryption.bootstrap( bootstrap = client.encryption!.bootstrap(
onUpdate: () async { onUpdate: () async {
while (bootstrap == null) { while (bootstrap == null) {
await Future.delayed(Duration(milliseconds: 5)); await Future.delayed(Duration(milliseconds: 5));
@ -82,7 +81,7 @@ void main() {
while (bootstrap.state != BootstrapState.done) { while (bootstrap.state != BootstrapState.done) {
await Future.delayed(Duration(milliseconds: 50)); await Future.delayed(Duration(milliseconds: 50));
} }
final defaultKey = client.encryption.ssss.open(); final defaultKey = client.encryption!.ssss.open();
await defaultKey.unlock(passphrase: 'foxies'); await defaultKey.unlock(passphrase: 'foxies');
// test all the x-signing keys match up // test all the x-signing keys match up
@ -95,8 +94,8 @@ void main() {
expect( expect(
pubKey, pubKey,
client.userDeviceKeys[client.userID] client.userDeviceKeys[client.userID]
.getCrossSigningKey(keyType) ?.getCrossSigningKey(keyType)
.publicKey); ?.publicKey);
} finally { } finally {
keyObj.free(); keyObj.free();
} }
@ -104,14 +103,15 @@ void main() {
await defaultKey.store('foxes', 'floof'); await defaultKey.store('foxes', 'floof');
await Future.delayed(Duration(milliseconds: 50)); await Future.delayed(Duration(milliseconds: 50));
oldSecret = json.decode(json.encode(client.accountData['foxes'].content)); oldSecret =
json.decode(json.encode(client.accountData['foxes']!.content));
origKeyId = defaultKey.keyId; origKeyId = defaultKey.keyId;
}, timeout: Timeout(Duration(minutes: 2))); }, timeout: Timeout(Duration(minutes: 2)));
test('change recovery passphrase', () async { test('change recovery passphrase', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
Bootstrap bootstrap; Bootstrap? bootstrap;
bootstrap = client.encryption.bootstrap( bootstrap = client.encryption!.bootstrap(
onUpdate: () async { onUpdate: () async {
while (bootstrap == null) { while (bootstrap == null) {
await Future.delayed(Duration(milliseconds: 5)); await Future.delayed(Duration(milliseconds: 5));
@ -121,7 +121,7 @@ void main() {
} else if (bootstrap.state == BootstrapState.askUseExistingSsss) { } else if (bootstrap.state == BootstrapState.askUseExistingSsss) {
bootstrap.useExistingSsss(false); bootstrap.useExistingSsss(false);
} else if (bootstrap.state == BootstrapState.askUnlockSsss) { } else if (bootstrap.state == BootstrapState.askUnlockSsss) {
await bootstrap.oldSsssKeys[client.encryption.ssss.defaultKeyId] await bootstrap.oldSsssKeys![client.encryption!.ssss.defaultKeyId]!
.unlock(passphrase: 'foxies'); .unlock(passphrase: 'foxies');
bootstrap.unlockedSsss(); bootstrap.unlockedSsss();
} else if (bootstrap.state == BootstrapState.askNewSsss) { } else if (bootstrap.state == BootstrapState.askNewSsss) {
@ -136,7 +136,7 @@ void main() {
while (bootstrap.state != BootstrapState.done) { while (bootstrap.state != BootstrapState.done) {
await Future.delayed(Duration(milliseconds: 50)); await Future.delayed(Duration(milliseconds: 50));
} }
final defaultKey = client.encryption.ssss.open(); final defaultKey = client.encryption!.ssss.open();
await defaultKey.unlock(passphrase: 'newfoxies'); await defaultKey.unlock(passphrase: 'newfoxies');
// test all the x-signing keys match up // test all the x-signing keys match up
@ -149,8 +149,8 @@ void main() {
expect( expect(
pubKey, pubKey,
client.userDeviceKeys[client.userID] client.userDeviceKeys[client.userID]
.getCrossSigningKey(keyType) ?.getCrossSigningKey(keyType)
.publicKey); ?.publicKey);
} finally { } finally {
keyObj.free(); keyObj.free();
} }
@ -161,11 +161,11 @@ void main() {
test('change passphrase with multiple keys', () async { test('change passphrase with multiple keys', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
await client.setAccountData(client.userID, 'foxes', oldSecret); await client.setAccountData(client.userID!, 'foxes', oldSecret);
await Future.delayed(Duration(milliseconds: 50)); await Future.delayed(Duration(milliseconds: 50));
Bootstrap bootstrap; Bootstrap? bootstrap;
bootstrap = client.encryption.bootstrap( bootstrap = client.encryption!.bootstrap(
onUpdate: () async { onUpdate: () async {
while (bootstrap == null) { while (bootstrap == null) {
await Future.delayed(Duration(milliseconds: 5)); await Future.delayed(Duration(milliseconds: 5));
@ -175,9 +175,10 @@ void main() {
} else if (bootstrap.state == BootstrapState.askUseExistingSsss) { } else if (bootstrap.state == BootstrapState.askUseExistingSsss) {
bootstrap.useExistingSsss(false); bootstrap.useExistingSsss(false);
} else if (bootstrap.state == BootstrapState.askUnlockSsss) { } else if (bootstrap.state == BootstrapState.askUnlockSsss) {
await bootstrap.oldSsssKeys[client.encryption.ssss.defaultKeyId] await bootstrap.oldSsssKeys![client.encryption!.ssss.defaultKeyId]!
.unlock(passphrase: 'newfoxies'); .unlock(passphrase: 'newfoxies');
await bootstrap.oldSsssKeys[origKeyId].unlock(passphrase: 'foxies'); await bootstrap.oldSsssKeys![origKeyId]!
.unlock(passphrase: 'foxies');
bootstrap.unlockedSsss(); bootstrap.unlockedSsss();
} else if (bootstrap.state == BootstrapState.askNewSsss) { } else if (bootstrap.state == BootstrapState.askNewSsss) {
await bootstrap.newSsss('supernewfoxies'); await bootstrap.newSsss('supernewfoxies');
@ -191,7 +192,7 @@ void main() {
while (bootstrap.state != BootstrapState.done) { while (bootstrap.state != BootstrapState.done) {
await Future.delayed(Duration(milliseconds: 50)); await Future.delayed(Duration(milliseconds: 50));
} }
final defaultKey = client.encryption.ssss.open(); final defaultKey = client.encryption!.ssss.open();
await defaultKey.unlock(passphrase: 'supernewfoxies'); await defaultKey.unlock(passphrase: 'supernewfoxies');
// test all the x-signing keys match up // test all the x-signing keys match up
@ -204,8 +205,8 @@ void main() {
expect( expect(
pubKey, pubKey,
client.userDeviceKeys[client.userID] client.userDeviceKeys[client.userID]
.getCrossSigningKey(keyType) ?.getCrossSigningKey(keyType)
.publicKey); ?.publicKey);
} finally { } finally {
keyObj.free(); keyObj.free();
} }
@ -217,8 +218,8 @@ void main() {
test('setup new ssss', () async { test('setup new ssss', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
client.accountData.clear(); client.accountData.clear();
Bootstrap bootstrap; Bootstrap? bootstrap;
bootstrap = client.encryption.bootstrap( bootstrap = client.encryption!.bootstrap(
onUpdate: () async { onUpdate: () async {
while (bootstrap == null) { while (bootstrap == null) {
await Future.delayed(Duration(milliseconds: 5)); await Future.delayed(Duration(milliseconds: 5));
@ -236,18 +237,18 @@ void main() {
while (bootstrap.state != BootstrapState.done) { while (bootstrap.state != BootstrapState.done) {
await Future.delayed(Duration(milliseconds: 50)); await Future.delayed(Duration(milliseconds: 50));
} }
final defaultKey = client.encryption.ssss.open(); final defaultKey = client.encryption!.ssss.open();
await defaultKey.unlock(passphrase: 'thenewestfoxies'); await defaultKey.unlock(passphrase: 'thenewestfoxies');
}, timeout: Timeout(Duration(minutes: 2))); }, timeout: Timeout(Duration(minutes: 2)));
test('bad ssss', () async { test('bad ssss', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
client.accountData.clear(); client.accountData.clear();
await client.setAccountData(client.userID, 'foxes', oldSecret); await client.setAccountData(client.userID!, 'foxes', oldSecret);
await Future.delayed(Duration(milliseconds: 50)); await Future.delayed(Duration(milliseconds: 50));
var askedBadSsss = false; var askedBadSsss = false;
Bootstrap bootstrap; Bootstrap? bootstrap;
bootstrap = client.encryption.bootstrap( bootstrap = client.encryption!.bootstrap(
onUpdate: () async { onUpdate: () async {
while (bootstrap == null) { while (bootstrap == null) {
await Future.delayed(Duration(milliseconds: 5)); await Future.delayed(Duration(milliseconds: 5));

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2020 Famedly GmbH * Copyright (C) 2020 Famedly GmbH
@ -32,7 +31,7 @@ void main() {
Logs().level = Level.error; Logs().level = Level.error;
var olmEnabled = true; var olmEnabled = true;
Client client; late Client client;
test('setupClient', () async { test('setupClient', () async {
try { try {
@ -50,41 +49,43 @@ void main() {
test('basic things', () async { test('basic things', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
expect(client.encryption.crossSigning.enabled, true); expect(client.encryption?.crossSigning.enabled, true);
}); });
test('selfSign', () async { test('selfSign', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
final key = client.userDeviceKeys[client.userID].masterKey; final key = client.userDeviceKeys[client.userID]!.masterKey!;
key.setDirectVerified(false); key.setDirectVerified(false);
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client.encryption.crossSigning.selfSign(recoveryKey: ssssKey); await client.encryption!.crossSigning.selfSign(recoveryKey: ssssKey);
expect(key.directVerified, true); expect(key.directVerified, true);
expect( expect(
FakeMatrixApi.calledEndpoints FakeMatrixApi.calledEndpoints
.containsKey('/client/r0/keys/signatures/upload'), .containsKey('/client/r0/keys/signatures/upload'),
true); true);
expect(await client.encryption.crossSigning.isCached(), true); expect(await client.encryption!.crossSigning.isCached(), true);
}); });
test('signable', () async { test('signable', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
expect( expect(
client.encryption.crossSigning client.encryption!.crossSigning
.signable([client.userDeviceKeys[client.userID].masterKey]), .signable([client.userDeviceKeys[client.userID!]!.masterKey!]),
true); true);
expect( expect(
client.encryption.crossSigning.signable([ client.encryption!.crossSigning.signable([
client.userDeviceKeys[client.userID].deviceKeys[client.deviceID] client.userDeviceKeys[client.userID!]!.deviceKeys[client.deviceID!]!
]), ]),
false); false);
expect( expect(
client.encryption.crossSigning.signable( client.encryption!.crossSigning.signable([
[client.userDeviceKeys[client.userID].deviceKeys['OTHERDEVICE']]), client.userDeviceKeys[client.userID!]!.deviceKeys['OTHERDEVICE']!
]),
true); true);
expect( expect(
client.encryption.crossSigning.signable([ client.encryption!.crossSigning.signable([
client.userDeviceKeys['@alice:example.com'].deviceKeys['JLAFKJWSCS'] client
.userDeviceKeys['@alice:example.com']!.deviceKeys['JLAFKJWSCS']!
]), ]),
false); false);
}); });
@ -92,24 +93,24 @@ void main() {
test('sign', () async { test('sign', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client.encryption.crossSigning.sign([ await client.encryption!.crossSigning.sign([
client.userDeviceKeys[client.userID].masterKey, client.userDeviceKeys[client.userID!]!.masterKey!,
client.userDeviceKeys[client.userID].deviceKeys['OTHERDEVICE'], client.userDeviceKeys[client.userID!]!.deviceKeys['OTHERDEVICE']!,
client.userDeviceKeys['@othertest:fakeServer.notExisting'].masterKey client.userDeviceKeys['@othertest:fakeServer.notExisting']!.masterKey!
]); ]);
final body = json.decode(FakeMatrixApi final body = json.decode(FakeMatrixApi
.calledEndpoints['/client/r0/keys/signatures/upload'].first); .calledEndpoints['/client/r0/keys/signatures/upload']!.first);
expect(body['@test:fakeServer.notExisting']?.containsKey('OTHERDEVICE'), expect(body['@test:fakeServer.notExisting']?.containsKey('OTHERDEVICE'),
true); true);
expect( expect(
body['@test:fakeServer.notExisting'].containsKey( body['@test:fakeServer.notExisting'].containsKey(
client.userDeviceKeys[client.userID].masterKey.publicKey), client.userDeviceKeys[client.userID]!.masterKey!.publicKey),
true); true);
expect( expect(
body['@othertest:fakeServer.notExisting'].containsKey(client body['@othertest:fakeServer.notExisting'].containsKey(client
.userDeviceKeys['@othertest:fakeServer.notExisting'] .userDeviceKeys['@othertest:fakeServer.notExisting']
.masterKey ?.masterKey
.publicKey), ?.publicKey),
true); true);
}); });

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2020 Famedly GmbH * Copyright (C) 2020 Famedly GmbH
@ -29,10 +28,10 @@ void main() {
Logs().level = Level.error; Logs().level = Level.error;
var olmEnabled = true; var olmEnabled = true;
Client client; late Client client;
final roomId = '!726s6s6q:example.com'; final roomId = '!726s6s6q:example.com';
Room room; late Room room;
Map<String, dynamic> payload; late Map<String, dynamic> payload;
final now = DateTime.now(); final now = DateTime.now();
test('setupClient', () async { test('setupClient', () async {
@ -47,12 +46,12 @@ void main() {
if (!olmEnabled) return; if (!olmEnabled) return;
client = await getClient(); client = await getClient();
room = client.getRoomById(roomId); room = client.getRoomById(roomId)!;
}); });
test('encrypt payload', () async { test('encrypt payload', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
payload = await client.encryption.encryptGroupMessagePayload(roomId, { payload = await client.encryption!.encryptGroupMessagePayload(roomId, {
'msgtype': 'm.text', 'msgtype': 'm.text',
'text': 'Hello foxies!', 'text': 'Hello foxies!',
}); });
@ -72,9 +71,10 @@ void main() {
room: room, room: room,
originServerTs: now, originServerTs: now,
eventId: '\$event', eventId: '\$event',
senderId: client.userID!,
); );
final decryptedEvent = final decryptedEvent =
await client.encryption.decryptRoomEvent(roomId, encryptedEvent); await client.encryption!.decryptRoomEvent(roomId, encryptedEvent);
expect(decryptedEvent.type, 'm.room.message'); expect(decryptedEvent.type, 'm.room.message');
expect(decryptedEvent.content['msgtype'], 'm.text'); expect(decryptedEvent.content['msgtype'], 'm.text');
expect(decryptedEvent.content['text'], 'Hello foxies!'); expect(decryptedEvent.content['text'], 'Hello foxies!');
@ -82,7 +82,7 @@ void main() {
test('decrypt payload nocache', () async { test('decrypt payload nocache', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
client.encryption.keyManager.clearInboundGroupSessions(); client.encryption!.keyManager.clearInboundGroupSessions();
final encryptedEvent = Event( final encryptedEvent = Event(
type: EventTypes.Encrypted, type: EventTypes.Encrypted,
content: payload, content: payload,
@ -93,11 +93,11 @@ void main() {
senderId: '@alice:example.com', senderId: '@alice:example.com',
); );
final decryptedEvent = final decryptedEvent =
await client.encryption.decryptRoomEvent(roomId, encryptedEvent); await client.encryption!.decryptRoomEvent(roomId, encryptedEvent);
expect(decryptedEvent.type, 'm.room.message'); expect(decryptedEvent.type, 'm.room.message');
expect(decryptedEvent.content['msgtype'], 'm.text'); expect(decryptedEvent.content['msgtype'], 'm.text');
expect(decryptedEvent.content['text'], 'Hello foxies!'); expect(decryptedEvent.content['text'], 'Hello foxies!');
await client.encryption await client.encryption!
.decryptRoomEvent(roomId, encryptedEvent, store: true); .decryptRoomEvent(roomId, encryptedEvent, store: true);
}); });

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2020 Famedly GmbH * Copyright (C) 2020 Famedly GmbH
@ -35,11 +34,11 @@ void main() {
Logs().level = Level.error; Logs().level = Level.error;
var olmEnabled = true; var olmEnabled = true;
Client client; late Client client;
final otherClient = Client('othertestclient', final otherClient = Client('othertestclient',
httpClient: FakeMatrixApi(), databaseBuilder: getDatabase); httpClient: FakeMatrixApi(), databaseBuilder: getDatabase);
DeviceKeys device; late DeviceKeys device;
Map<String, dynamic> payload; late Map<String, dynamic> payload;
test('setupClient', () async { test('setupClient', () async {
try { try {
@ -83,7 +82,7 @@ void main() {
test('encryptToDeviceMessage', () async { test('encryptToDeviceMessage', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
payload = await otherClient.encryption payload = await otherClient.encryption!
.encryptToDeviceMessage([device], 'm.to_device', {'hello': 'foxies'}); .encryptToDeviceMessage([device], 'm.to_device', {'hello': 'foxies'});
}); });
@ -95,15 +94,15 @@ void main() {
content: payload[client.userID][client.deviceID], content: payload[client.userID][client.deviceID],
); );
final decryptedEvent = final decryptedEvent =
await client.encryption.decryptToDeviceEvent(encryptedEvent); await client.encryption!.decryptToDeviceEvent(encryptedEvent);
expect(decryptedEvent.type, 'm.to_device'); expect(decryptedEvent.type, 'm.to_device');
expect(decryptedEvent.content['hello'], 'foxies'); expect(decryptedEvent.content['hello'], 'foxies');
}); });
test('decryptToDeviceEvent nocache', () async { test('decryptToDeviceEvent nocache', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
client.encryption.olmManager.olmSessions.clear(); client.encryption!.olmManager.olmSessions.clear();
payload = await otherClient.encryption.encryptToDeviceMessage( payload = await otherClient.encryption!.encryptToDeviceMessage(
[device], 'm.to_device', {'hello': 'superfoxies'}); [device], 'm.to_device', {'hello': 'superfoxies'});
final encryptedEvent = ToDeviceEvent( final encryptedEvent = ToDeviceEvent(
sender: '@othertest:fakeServer.notExisting', sender: '@othertest:fakeServer.notExisting',
@ -111,7 +110,7 @@ void main() {
content: payload[client.userID][client.deviceID], content: payload[client.userID][client.deviceID],
); );
final decryptedEvent = final decryptedEvent =
await client.encryption.decryptToDeviceEvent(encryptedEvent); await client.encryption!.decryptToDeviceEvent(encryptedEvent);
expect(decryptedEvent.type, 'm.to_device'); expect(decryptedEvent.type, 'm.to_device');
expect(decryptedEvent.content['hello'], 'superfoxies'); expect(decryptedEvent.content['hello'], 'superfoxies');
}); });

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2020 Famedly GmbH * Copyright (C) 2020 Famedly GmbH
@ -32,7 +31,7 @@ void main() {
Logs().level = Level.error; Logs().level = Level.error;
var olmEnabled = true; var olmEnabled = true;
Client client; late Client client;
test('setupClient', () async { test('setupClient', () async {
try { try {
@ -55,7 +54,7 @@ void main() {
final sessionKey = final sessionKey =
'AgAAAAAQcQ6XrFJk6Prm8FikZDqfry/NbDz8Xw7T6e+/9Yf/q3YHIPEQlzv7IZMNcYb51ifkRzFejVvtphS7wwG2FaXIp4XS2obla14iKISR0X74ugB2vyb1AydIHE/zbBQ1ic5s3kgjMFlWpu/S3FQCnCrv+DPFGEt3ERGWxIl3Bl5X53IjPyVkz65oljz2TZESwz0GH/QFvyOOm8ci0q/gceaF3S7Dmafg3dwTKYwcA5xkcc+BLyrLRzB6Hn+oMAqSNSscnm4mTeT5zYibIhrzqyUTMWr32spFtI9dNR/RFSzfCw'; 'AgAAAAAQcQ6XrFJk6Prm8FikZDqfry/NbDz8Xw7T6e+/9Yf/q3YHIPEQlzv7IZMNcYb51ifkRzFejVvtphS7wwG2FaXIp4XS2obla14iKISR0X74ugB2vyb1AydIHE/zbBQ1ic5s3kgjMFlWpu/S3FQCnCrv+DPFGEt3ERGWxIl3Bl5X53IjPyVkz65oljz2TZESwz0GH/QFvyOOm8ci0q/gceaF3S7Dmafg3dwTKYwcA5xkcc+BLyrLRzB6Hn+oMAqSNSscnm4mTeT5zYibIhrzqyUTMWr32spFtI9dNR/RFSzfCw';
client.encryption.keyManager.clearInboundGroupSessions(); client.encryption!.keyManager.clearInboundGroupSessions();
var event = ToDeviceEvent( var event = ToDeviceEvent(
sender: '@alice:example.com', sender: '@alice:example.com',
type: 'm.room_key', type: 'm.room_key',
@ -68,9 +67,9 @@ void main() {
encryptedContent: { encryptedContent: {
'sender_key': validSenderKey, 'sender_key': validSenderKey,
}); });
await client.encryption.keyManager.handleToDeviceEvent(event); await client.encryption!.keyManager.handleToDeviceEvent(event);
expect( expect(
client.encryption.keyManager.getInboundGroupSession( client.encryption!.keyManager.getInboundGroupSession(
'!726s6s6q:example.com', validSessionId, validSenderKey) != '!726s6s6q:example.com', validSessionId, validSenderKey) !=
null, null,
true); true);
@ -78,7 +77,7 @@ void main() {
// now test a few invalid scenarios // now test a few invalid scenarios
// not encrypted // not encrypted
client.encryption.keyManager.clearInboundGroupSessions(); client.encryption!.keyManager.clearInboundGroupSessions();
event = ToDeviceEvent( event = ToDeviceEvent(
sender: '@alice:example.com', sender: '@alice:example.com',
type: 'm.room_key', type: 'm.room_key',
@ -88,9 +87,9 @@ void main() {
'session_id': validSessionId, 'session_id': validSessionId,
'session_key': sessionKey, 'session_key': sessionKey,
}); });
await client.encryption.keyManager.handleToDeviceEvent(event); await client.encryption!.keyManager.handleToDeviceEvent(event);
expect( expect(
client.encryption.keyManager.getInboundGroupSession( client.encryption!.keyManager.getInboundGroupSession(
'!726s6s6q:example.com', validSessionId, validSenderKey) != '!726s6s6q:example.com', validSessionId, validSenderKey) !=
null, null,
false); false);
@ -100,99 +99,104 @@ void main() {
if (!olmEnabled) return; if (!olmEnabled) return;
final roomId = '!726s6s6q:example.com'; final roomId = '!726s6s6q:example.com';
expect( expect(
client.encryption.keyManager.getOutboundGroupSession(roomId) != null, client.encryption!.keyManager.getOutboundGroupSession(roomId) != null,
false); false);
var sess = var sess = await client.encryption!.keyManager
await client.encryption.keyManager.createOutboundGroupSession(roomId); .createOutboundGroupSession(roomId);
expect( expect(
client.encryption.keyManager.getOutboundGroupSession(roomId) != null, client.encryption!.keyManager.getOutboundGroupSession(roomId) != null,
true); true);
await client.encryption.keyManager.clearOrUseOutboundGroupSession(roomId); await client.encryption!.keyManager
.clearOrUseOutboundGroupSession(roomId);
expect( expect(
client.encryption.keyManager.getOutboundGroupSession(roomId) != null, client.encryption!.keyManager.getOutboundGroupSession(roomId) != null,
true); true);
var inbound = client.encryption.keyManager.getInboundGroupSession( var inbound = client.encryption!.keyManager.getInboundGroupSession(
roomId, sess.outboundGroupSession.session_id(), client.identityKey); roomId, sess.outboundGroupSession!.session_id(), client.identityKey);
expect(inbound != null, true); expect(inbound != null, true);
expect( expect(
inbound.allowedAtIndex['@alice:example.com'] inbound!.allowedAtIndex['@alice:example.com']
['L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8'], ?['L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8'],
0); 0);
expect( expect(
inbound.allowedAtIndex['@alice:example.com'] inbound.allowedAtIndex['@alice:example.com']
['wMIDhiQl5jEXQrTB03ePOSQfR8sA/KMrW0CIfFfXKEE'], ?['wMIDhiQl5jEXQrTB03ePOSQfR8sA/KMrW0CIfFfXKEE'],
0); 0);
// rotate after too many messages // rotate after too many messages
Iterable.generate(300).forEach((_) { Iterable.generate(300).forEach((_) {
sess.outboundGroupSession.encrypt('some string'); sess.outboundGroupSession!.encrypt('some string');
}); });
await client.encryption.keyManager.clearOrUseOutboundGroupSession(roomId); await client.encryption!.keyManager
.clearOrUseOutboundGroupSession(roomId);
expect( expect(
client.encryption.keyManager.getOutboundGroupSession(roomId) != null, client.encryption!.keyManager.getOutboundGroupSession(roomId) != null,
false); false);
// rotate if device is blocked // rotate if device is blocked
sess = sess = await client.encryption!.keyManager
await client.encryption.keyManager.createOutboundGroupSession(roomId); .createOutboundGroupSession(roomId);
client.userDeviceKeys['@alice:example.com'].deviceKeys['JLAFKJWSCS'] client.userDeviceKeys['@alice:example.com']!.deviceKeys['JLAFKJWSCS']!
.blocked = true; .blocked = true;
await client.encryption.keyManager.clearOrUseOutboundGroupSession(roomId); await client.encryption!.keyManager
.clearOrUseOutboundGroupSession(roomId);
expect( expect(
client.encryption.keyManager.getOutboundGroupSession(roomId) != null, client.encryption!.keyManager.getOutboundGroupSession(roomId) != null,
false); false);
client.userDeviceKeys['@alice:example.com'].deviceKeys['JLAFKJWSCS'] client.userDeviceKeys['@alice:example.com']!.deviceKeys['JLAFKJWSCS']!
.blocked = false; .blocked = false;
// lazy-create if it would rotate // lazy-create if it would rotate
sess = sess = await client.encryption!.keyManager
await client.encryption.keyManager.createOutboundGroupSession(roomId); .createOutboundGroupSession(roomId);
final oldSessKey = sess.outboundGroupSession.session_key(); final oldSessKey = sess.outboundGroupSession!.session_key();
client.userDeviceKeys['@alice:example.com'].deviceKeys['JLAFKJWSCS'] client.userDeviceKeys['@alice:example.com']!.deviceKeys['JLAFKJWSCS']!
.blocked = true; .blocked = true;
await client.encryption.keyManager.prepareOutboundGroupSession(roomId); await client.encryption!.keyManager.prepareOutboundGroupSession(roomId);
expect( expect(
client.encryption.keyManager.getOutboundGroupSession(roomId) != null, client.encryption!.keyManager.getOutboundGroupSession(roomId) != null,
true); true);
expect( expect(
client.encryption.keyManager client.encryption!.keyManager
.getOutboundGroupSession(roomId) .getOutboundGroupSession(roomId)!
.outboundGroupSession .outboundGroupSession!
.session_key() != .session_key() !=
oldSessKey, oldSessKey,
true); true);
client.userDeviceKeys['@alice:example.com'].deviceKeys['JLAFKJWSCS'] client.userDeviceKeys['@alice:example.com']!.deviceKeys['JLAFKJWSCS']!
.blocked = false; .blocked = false;
// rotate if too far in the past // rotate if too far in the past
sess = sess = await client.encryption!.keyManager
await client.encryption.keyManager.createOutboundGroupSession(roomId); .createOutboundGroupSession(roomId);
sess.creationTime = DateTime.now().subtract(Duration(days: 30)); sess.creationTime = DateTime.now().subtract(Duration(days: 30));
await client.encryption.keyManager.clearOrUseOutboundGroupSession(roomId); await client.encryption!.keyManager
.clearOrUseOutboundGroupSession(roomId);
expect( expect(
client.encryption.keyManager.getOutboundGroupSession(roomId) != null, client.encryption!.keyManager.getOutboundGroupSession(roomId) != null,
false); false);
// rotate if user leaves // rotate if user leaves
sess = sess = await client.encryption!.keyManager
await client.encryption.keyManager.createOutboundGroupSession(roomId); .createOutboundGroupSession(roomId);
final room = client.getRoomById(roomId); final room = client.getRoomById(roomId)!;
final member = room.getState('m.room.member', '@alice:example.com'); final member = room.getState('m.room.member', '@alice:example.com');
member.content['membership'] = 'leave'; member!.content['membership'] = 'leave';
room.summary.mJoinedMemberCount--; room.summary.mJoinedMemberCount = room.summary.mJoinedMemberCount! - 1;
await client.encryption.keyManager.clearOrUseOutboundGroupSession(roomId); await client.encryption!.keyManager
.clearOrUseOutboundGroupSession(roomId);
expect( expect(
client.encryption.keyManager.getOutboundGroupSession(roomId) != null, client.encryption!.keyManager.getOutboundGroupSession(roomId) != null,
false); false);
member.content['membership'] = 'join'; member.content['membership'] = 'join';
room.summary.mJoinedMemberCount++; room.summary.mJoinedMemberCount = room.summary.mJoinedMemberCount! + 1;
// do not rotate if new device is added // do not rotate if new device is added
sess = sess = await client.encryption!.keyManager
await client.encryption.keyManager.createOutboundGroupSession(roomId); .createOutboundGroupSession(roomId);
sess.outboundGroupSession.encrypt( sess.outboundGroupSession!.encrypt(
'foxies'); // so that the new device will have a different index 'foxies'); // so that the new device will have a different index
client.userDeviceKeys['@alice:example.com'].deviceKeys['NEWDEVICE'] = client.userDeviceKeys['@alice:example.com']?.deviceKeys['NEWDEVICE'] =
DeviceKeys.fromJson({ DeviceKeys.fromJson({
'user_id': '@alice:example.com', 'user_id': '@alice:example.com',
'device_id': 'NEWDEVICE', 'device_id': 'NEWDEVICE',
@ -211,56 +215,58 @@ void main() {
} }
} }
}, client); }, client);
await client.encryption.keyManager.clearOrUseOutboundGroupSession(roomId); await client.encryption!.keyManager
.clearOrUseOutboundGroupSession(roomId);
expect( expect(
client.encryption.keyManager.getOutboundGroupSession(roomId) != null, client.encryption!.keyManager.getOutboundGroupSession(roomId) != null,
true); true);
inbound = client.encryption.keyManager.getInboundGroupSession( inbound = client.encryption!.keyManager.getInboundGroupSession(
roomId, sess.outboundGroupSession.session_id(), client.identityKey); roomId, sess.outboundGroupSession!.session_id(), client.identityKey);
expect( expect(
inbound.allowedAtIndex['@alice:example.com'] inbound!.allowedAtIndex['@alice:example.com']
['L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8'], ?['L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8'],
0); 0);
expect( expect(
inbound.allowedAtIndex['@alice:example.com'] inbound.allowedAtIndex['@alice:example.com']
['wMIDhiQl5jEXQrTB03ePOSQfR8sA/KMrW0CIfFfXKEE'], ?['wMIDhiQl5jEXQrTB03ePOSQfR8sA/KMrW0CIfFfXKEE'],
0); 0);
expect( expect(
inbound.allowedAtIndex['@alice:example.com'] inbound.allowedAtIndex['@alice:example.com']
['bnKQp6pPW0l9cGoIgHpBoK5OUi4h0gylJ7upc4asFV8'], ?['bnKQp6pPW0l9cGoIgHpBoK5OUi4h0gylJ7upc4asFV8'],
1); 1);
// do not rotate if new user is added // do not rotate if new user is added
member.content['membership'] = 'leave'; member.content['membership'] = 'leave';
room.summary.mJoinedMemberCount--; room.summary.mJoinedMemberCount = room.summary.mJoinedMemberCount! - 1;
sess = sess = await client.encryption!.keyManager
await client.encryption.keyManager.createOutboundGroupSession(roomId); .createOutboundGroupSession(roomId);
member.content['membership'] = 'join'; member.content['membership'] = 'join';
room.summary.mJoinedMemberCount++; room.summary.mJoinedMemberCount = room.summary.mJoinedMemberCount! + 1;
await client.encryption.keyManager.clearOrUseOutboundGroupSession(roomId); await client.encryption!.keyManager
.clearOrUseOutboundGroupSession(roomId);
expect( expect(
client.encryption.keyManager.getOutboundGroupSession(roomId) != null, client.encryption!.keyManager.getOutboundGroupSession(roomId) != null,
true); true);
// force wipe // force wipe
sess = sess = await client.encryption!.keyManager
await client.encryption.keyManager.createOutboundGroupSession(roomId); .createOutboundGroupSession(roomId);
await client.encryption.keyManager await client.encryption!.keyManager
.clearOrUseOutboundGroupSession(roomId, wipe: true); .clearOrUseOutboundGroupSession(roomId, wipe: true);
expect( expect(
client.encryption.keyManager.getOutboundGroupSession(roomId) != null, client.encryption!.keyManager.getOutboundGroupSession(roomId) != null,
false); false);
// load from database // load from database
sess = sess = await client.encryption!.keyManager
await client.encryption.keyManager.createOutboundGroupSession(roomId); .createOutboundGroupSession(roomId);
client.encryption.keyManager.clearOutboundGroupSessions(); client.encryption!.keyManager.clearOutboundGroupSessions();
expect( expect(
client.encryption.keyManager.getOutboundGroupSession(roomId) != null, client.encryption!.keyManager.getOutboundGroupSession(roomId) != null,
false); false);
await client.encryption.keyManager.loadOutboundGroupSession(roomId); await client.encryption!.keyManager.loadOutboundGroupSession(roomId);
expect( expect(
client.encryption.keyManager.getOutboundGroupSession(roomId) != null, client.encryption!.keyManager.getOutboundGroupSession(roomId) != null,
true); true);
}); });
@ -276,71 +282,71 @@ void main() {
'session_key': 'session_key':
'AgAAAAAQcQ6XrFJk6Prm8FikZDqfry/NbDz8Xw7T6e+/9Yf/q3YHIPEQlzv7IZMNcYb51ifkRzFejVvtphS7wwG2FaXIp4XS2obla14iKISR0X74ugB2vyb1AydIHE/zbBQ1ic5s3kgjMFlWpu/S3FQCnCrv+DPFGEt3ERGWxIl3Bl5X53IjPyVkz65oljz2TZESwz0GH/QFvyOOm8ci0q/gceaF3S7Dmafg3dwTKYwcA5xkcc+BLyrLRzB6Hn+oMAqSNSscnm4mTeT5zYibIhrzqyUTMWr32spFtI9dNR/RFSzfCw' 'AgAAAAAQcQ6XrFJk6Prm8FikZDqfry/NbDz8Xw7T6e+/9Yf/q3YHIPEQlzv7IZMNcYb51ifkRzFejVvtphS7wwG2FaXIp4XS2obla14iKISR0X74ugB2vyb1AydIHE/zbBQ1ic5s3kgjMFlWpu/S3FQCnCrv+DPFGEt3ERGWxIl3Bl5X53IjPyVkz65oljz2TZESwz0GH/QFvyOOm8ci0q/gceaF3S7Dmafg3dwTKYwcA5xkcc+BLyrLRzB6Hn+oMAqSNSscnm4mTeT5zYibIhrzqyUTMWr32spFtI9dNR/RFSzfCw'
}; };
client.encryption.keyManager.clearInboundGroupSessions(); client.encryption!.keyManager.clearInboundGroupSessions();
expect( expect(
client.encryption.keyManager client.encryption!.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey) != .getInboundGroupSession(roomId, sessionId, senderKey) !=
null, null,
false); false);
client.encryption.keyManager client.encryption!.keyManager
.setInboundGroupSession(roomId, sessionId, senderKey, sessionContent); .setInboundGroupSession(roomId, sessionId, senderKey, sessionContent);
await Future.delayed(Duration(milliseconds: 10)); await Future.delayed(Duration(milliseconds: 10));
expect( expect(
client.encryption.keyManager client.encryption!.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey) != .getInboundGroupSession(roomId, sessionId, senderKey) !=
null, null,
true); true);
expect( expect(
client.encryption.keyManager client.encryption!.keyManager
.getInboundGroupSession(roomId, sessionId, 'invalid') != .getInboundGroupSession(roomId, sessionId, 'invalid') !=
null, null,
false); false);
expect( expect(
client.encryption.keyManager client.encryption!.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey) != .getInboundGroupSession(roomId, sessionId, senderKey) !=
null, null,
true); true);
expect( expect(
client.encryption.keyManager client.encryption!.keyManager
.getInboundGroupSession('otherroom', sessionId, senderKey) != .getInboundGroupSession('otherroom', sessionId, senderKey) !=
null, null,
true); true);
expect( expect(
client.encryption.keyManager client.encryption!.keyManager
.getInboundGroupSession('otherroom', sessionId, 'invalid') != .getInboundGroupSession('otherroom', sessionId, 'invalid') !=
null, null,
false); false);
expect( expect(
client.encryption.keyManager client.encryption!.keyManager
.getInboundGroupSession('otherroom', 'invalid', senderKey) != .getInboundGroupSession('otherroom', 'invalid', senderKey) !=
null, null,
false); false);
client.encryption.keyManager.clearInboundGroupSessions(); client.encryption!.keyManager.clearInboundGroupSessions();
expect( expect(
client.encryption.keyManager client.encryption!.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey) != .getInboundGroupSession(roomId, sessionId, senderKey) !=
null, null,
false); false);
await client.encryption.keyManager await client.encryption!.keyManager
.loadInboundGroupSession(roomId, sessionId, senderKey); .loadInboundGroupSession(roomId, sessionId, senderKey);
expect( expect(
client.encryption.keyManager client.encryption!.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey) != .getInboundGroupSession(roomId, sessionId, senderKey) !=
null, null,
true); true);
client.encryption.keyManager.clearInboundGroupSessions(); client.encryption!.keyManager.clearInboundGroupSessions();
expect( expect(
client.encryption.keyManager client.encryption!.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey) != .getInboundGroupSession(roomId, sessionId, senderKey) !=
null, null,
false); false);
await client.encryption.keyManager await client.encryption!.keyManager
.loadInboundGroupSession(roomId, sessionId, 'invalid'); .loadInboundGroupSession(roomId, sessionId, 'invalid');
expect( expect(
client.encryption.keyManager client.encryption!.keyManager
.getInboundGroupSession(roomId, sessionId, 'invalid') != .getInboundGroupSession(roomId, sessionId, 'invalid') !=
null, null,
false); false);
@ -379,7 +385,7 @@ void main() {
stateKey: '', stateKey: '',
), ),
); );
expect(room.lastEvent.type, 'm.room.encrypted'); expect(room.lastEvent?.type, 'm.room.encrypted');
// set a payload... // set a payload...
var sessionPayload = <String, dynamic>{ var sessionPayload = <String, dynamic>{
'algorithm': AlgorithmTypes.megolmV1AesSha2, 'algorithm': AlgorithmTypes.megolmV1AesSha2,
@ -390,19 +396,19 @@ void main() {
'sender_key': senderKey, 'sender_key': senderKey,
'sender_claimed_ed25519_key': client.fingerprintKey, 'sender_claimed_ed25519_key': client.fingerprintKey,
}; };
client.encryption.keyManager.setInboundGroupSession( client.encryption!.keyManager.setInboundGroupSession(
roomId, sessionId, senderKey, sessionPayload, roomId, sessionId, senderKey, sessionPayload,
forwarded: true); forwarded: true);
expect( expect(
client.encryption.keyManager client.encryption!.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey) .getInboundGroupSession(roomId, sessionId, senderKey)
.inboundGroupSession ?.inboundGroupSession
.first_known_index(), ?.first_known_index(),
1); 1);
expect( expect(
client.encryption.keyManager client.encryption!.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey) .getInboundGroupSession(roomId, sessionId, senderKey)
.forwardingCurve25519KeyChain ?.forwardingCurve25519KeyChain
.length, .length,
1); 1);
@ -416,19 +422,19 @@ void main() {
'sender_key': senderKey, 'sender_key': senderKey,
'sender_claimed_ed25519_key': client.fingerprintKey, 'sender_claimed_ed25519_key': client.fingerprintKey,
}; };
client.encryption.keyManager.setInboundGroupSession( client.encryption!.keyManager.setInboundGroupSession(
roomId, sessionId, senderKey, sessionPayload, roomId, sessionId, senderKey, sessionPayload,
forwarded: true); forwarded: true);
expect( expect(
client.encryption.keyManager client.encryption!.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey) .getInboundGroupSession(roomId, sessionId, senderKey)
.inboundGroupSession ?.inboundGroupSession
.first_known_index(), ?.first_known_index(),
1); 1);
expect( expect(
client.encryption.keyManager client.encryption!.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey) .getInboundGroupSession(roomId, sessionId, senderKey)
.forwardingCurve25519KeyChain ?.forwardingCurve25519KeyChain
.length, .length,
1); 1);
@ -442,19 +448,19 @@ void main() {
'sender_key': senderKey, 'sender_key': senderKey,
'sender_claimed_ed25519_key': client.fingerprintKey, 'sender_claimed_ed25519_key': client.fingerprintKey,
}; };
client.encryption.keyManager.setInboundGroupSession( client.encryption!.keyManager.setInboundGroupSession(
roomId, sessionId, senderKey, sessionPayload, roomId, sessionId, senderKey, sessionPayload,
forwarded: true); forwarded: true);
expect( expect(
client.encryption.keyManager client.encryption!.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey) .getInboundGroupSession(roomId, sessionId, senderKey)
.inboundGroupSession ?.inboundGroupSession
.first_known_index(), ?.first_known_index(),
0); 0);
expect( expect(
client.encryption.keyManager client.encryption!.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey) .getInboundGroupSession(roomId, sessionId, senderKey)
.forwardingCurve25519KeyChain ?.forwardingCurve25519KeyChain
.length, .length,
1); 1);
@ -468,19 +474,19 @@ void main() {
'sender_key': senderKey, 'sender_key': senderKey,
'sender_claimed_ed25519_key': client.fingerprintKey, 'sender_claimed_ed25519_key': client.fingerprintKey,
}; };
client.encryption.keyManager.setInboundGroupSession( client.encryption!.keyManager.setInboundGroupSession(
roomId, sessionId, senderKey, sessionPayload, roomId, sessionId, senderKey, sessionPayload,
forwarded: true); forwarded: true);
expect( expect(
client.encryption.keyManager client.encryption!.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey) .getInboundGroupSession(roomId, sessionId, senderKey)
.inboundGroupSession ?.inboundGroupSession
.first_known_index(), ?.first_known_index(),
0); 0);
expect( expect(
client.encryption.keyManager client.encryption!.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey) .getInboundGroupSession(roomId, sessionId, senderKey)
.forwardingCurve25519KeyChain ?.forwardingCurve25519KeyChain
.length, .length,
1); 1);
@ -494,25 +500,25 @@ void main() {
'sender_key': senderKey, 'sender_key': senderKey,
'sender_claimed_ed25519_key': client.fingerprintKey, 'sender_claimed_ed25519_key': client.fingerprintKey,
}; };
client.encryption.keyManager.setInboundGroupSession( client.encryption!.keyManager.setInboundGroupSession(
roomId, sessionId, senderKey, sessionPayload, roomId, sessionId, senderKey, sessionPayload,
forwarded: true); forwarded: true);
expect( expect(
client.encryption.keyManager client.encryption!.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey) .getInboundGroupSession(roomId, sessionId, senderKey)
.inboundGroupSession ?.inboundGroupSession
.first_known_index(), ?.first_known_index(),
0); 0);
expect( expect(
client.encryption.keyManager client.encryption!.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey) .getInboundGroupSession(roomId, sessionId, senderKey)
.forwardingCurve25519KeyChain ?.forwardingCurve25519KeyChain
.length, .length,
0); 0);
// test that it decrypted the last event // test that it decrypted the last event
expect(room.lastEvent.type, 'm.room.message'); expect(room.lastEvent?.type, 'm.room.message');
expect(room.lastEvent.content['body'], 'foxies'); expect(room.lastEvent?.content['body'], 'foxies');
inbound.free(); inbound.free();
session.free(); session.free();
@ -521,22 +527,21 @@ void main() {
test('Reused deviceID attack', () async { test('Reused deviceID attack', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
Logs().level = Level.warning; Logs().level = Level.warning;
client ??= await getClient();
// Ensure the device came from sync // Ensure the device came from sync
expect( expect(
client.userDeviceKeys['@alice:example.com'] client.userDeviceKeys['@alice:example.com']
.deviceKeys['JLAFKJWSCS'] != ?.deviceKeys['JLAFKJWSCS'] !=
null, null,
true); true);
// Alice removes her device // Alice removes her device
client.userDeviceKeys['@alice:example.com'].deviceKeys client.userDeviceKeys['@alice:example.com']?.deviceKeys
.remove('JLAFKJWSCS'); .remove('JLAFKJWSCS');
// Alice adds her device with same device ID but different keys // Alice adds her device with same device ID but different keys
final oldResp = FakeMatrixApi.api['POST']['/client/r0/keys/query'](null); final oldResp = FakeMatrixApi.api['POST']?['/client/r0/keys/query'](null);
FakeMatrixApi.api['POST']['/client/r0/keys/query'] = (_) { FakeMatrixApi.api['POST']?['/client/r0/keys/query'] = (_) {
oldResp['device_keys']['@alice:example.com']['JLAFKJWSCS'] = { oldResp['device_keys']['@alice:example.com']['JLAFKJWSCS'] = {
'user_id': '@alice:example.com', 'user_id': '@alice:example.com',
'device_id': 'JLAFKJWSCS', 'device_id': 'JLAFKJWSCS',
@ -558,10 +563,10 @@ void main() {
}; };
return oldResp; return oldResp;
}; };
client.userDeviceKeys['@alice:example.com'].outdated = true; client.userDeviceKeys['@alice:example.com']!.outdated = true;
await client.updateUserDeviceKeys(); await client.updateUserDeviceKeys();
expect( expect(
client.userDeviceKeys['@alice:example.com'].deviceKeys['JLAFKJWSCS'], client.userDeviceKeys['@alice:example.com']?.deviceKeys['JLAFKJWSCS'],
null); null);
}); });

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2020 Famedly GmbH * Copyright (C) 2020 Famedly GmbH
@ -58,8 +57,8 @@ void main() {
if (!olmEnabled) return; if (!olmEnabled) return;
final matrix = await getClient(); final matrix = await getClient();
final requestRoom = matrix.getRoomById('!726s6s6q:example.com'); final requestRoom = matrix.getRoomById('!726s6s6q:example.com')!;
await matrix.encryption.keyManager.request( await matrix.encryption!.keyManager.request(
requestRoom, 'sessionId', validSenderKey, requestRoom, 'sessionId', validSenderKey,
tryOnlineBackup: false); tryOnlineBackup: false);
var foundEvent = false; var foundEvent = false;
@ -89,12 +88,12 @@ void main() {
matrix.setUserId('@alice:example.com'); // we need to pretend to be alice matrix.setUserId('@alice:example.com'); // we need to pretend to be alice
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await matrix await matrix
.userDeviceKeys['@alice:example.com'].deviceKeys['OTHERDEVICE'] .userDeviceKeys['@alice:example.com']!.deviceKeys['OTHERDEVICE']!
.setBlocked(false); .setBlocked(false);
await matrix await matrix
.userDeviceKeys['@alice:example.com'].deviceKeys['OTHERDEVICE'] .userDeviceKeys['@alice:example.com']!.deviceKeys['OTHERDEVICE']!
.setVerified(true); .setVerified(true);
final session = await matrix.encryption.keyManager final session = await matrix.encryption!.keyManager
.loadInboundGroupSession( .loadInboundGroupSession(
'!726s6s6q:example.com', validSessionId, validSenderKey); '!726s6s6q:example.com', validSessionId, validSenderKey);
// test a successful share // test a successful share
@ -112,7 +111,7 @@ void main() {
'request_id': 'request_1', 'request_id': 'request_1',
'requesting_device_id': 'OTHERDEVICE', 'requesting_device_id': 'OTHERDEVICE',
}); });
await matrix.encryption.keyManager.handleToDeviceEvent(event); await matrix.encryption!.keyManager.handleToDeviceEvent(event);
Logs().i(FakeMatrixApi.calledEndpoints.keys.toString()); Logs().i(FakeMatrixApi.calledEndpoints.keys.toString());
expect( expect(
FakeMatrixApi.calledEndpoints.keys.any( FakeMatrixApi.calledEndpoints.keys.any(
@ -121,9 +120,9 @@ void main() {
// test a successful foreign share // test a successful foreign share
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
session.allowedAtIndex['@test:fakeServer.notExisting'] = <String, int>{ session!.allowedAtIndex['@test:fakeServer.notExisting'] = <String, int>{
matrix.userDeviceKeys['@test:fakeServer.notExisting'] matrix.userDeviceKeys['@test:fakeServer.notExisting']!
.deviceKeys['OTHERDEVICE'].curve25519Key: 0, .deviceKeys['OTHERDEVICE']!.curve25519Key!: 0,
}; };
event = ToDeviceEvent( event = ToDeviceEvent(
sender: '@test:fakeServer.notExisting', sender: '@test:fakeServer.notExisting',
@ -139,7 +138,7 @@ void main() {
'request_id': 'request_a1', 'request_id': 'request_a1',
'requesting_device_id': 'OTHERDEVICE', 'requesting_device_id': 'OTHERDEVICE',
}); });
await matrix.encryption.keyManager.handleToDeviceEvent(event); await matrix.encryption!.keyManager.handleToDeviceEvent(event);
Logs().i(FakeMatrixApi.calledEndpoints.keys.toString()); Logs().i(FakeMatrixApi.calledEndpoints.keys.toString());
expect( expect(
FakeMatrixApi.calledEndpoints.keys.any( FakeMatrixApi.calledEndpoints.keys.any(
@ -165,7 +164,7 @@ void main() {
'request_id': 'request_a2', 'request_id': 'request_a2',
'requesting_device_id': 'OTHERDEVICE', 'requesting_device_id': 'OTHERDEVICE',
}); });
await matrix.encryption.keyManager.handleToDeviceEvent(event); await matrix.encryption!.keyManager.handleToDeviceEvent(event);
Logs().i(FakeMatrixApi.calledEndpoints.keys.toString()); Logs().i(FakeMatrixApi.calledEndpoints.keys.toString());
expect( expect(
FakeMatrixApi.calledEndpoints.keys.any( FakeMatrixApi.calledEndpoints.keys.any(
@ -182,7 +181,7 @@ void main() {
'request_id': 'request_2', 'request_id': 'request_2',
'requesting_device_id': 'OTHERDEVICE', 'requesting_device_id': 'OTHERDEVICE',
}); });
await matrix.encryption.keyManager.handleToDeviceEvent(event); await matrix.encryption!.keyManager.handleToDeviceEvent(event);
expect( expect(
FakeMatrixApi.calledEndpoints.keys.any( FakeMatrixApi.calledEndpoints.keys.any(
(k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), (k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')),
@ -204,7 +203,7 @@ void main() {
'request_id': 'request_3', 'request_id': 'request_3',
'requesting_device_id': 'JLAFKJWSCS', 'requesting_device_id': 'JLAFKJWSCS',
}); });
await matrix.encryption.keyManager.handleToDeviceEvent(event); await matrix.encryption!.keyManager.handleToDeviceEvent(event);
expect( expect(
FakeMatrixApi.calledEndpoints.keys.any( FakeMatrixApi.calledEndpoints.keys.any(
(k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), (k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')),
@ -226,7 +225,7 @@ void main() {
'request_id': 'request_4', 'request_id': 'request_4',
'requesting_device_id': 'blubb', 'requesting_device_id': 'blubb',
}); });
await matrix.encryption.keyManager.handleToDeviceEvent(event); await matrix.encryption!.keyManager.handleToDeviceEvent(event);
expect( expect(
FakeMatrixApi.calledEndpoints.keys.any( FakeMatrixApi.calledEndpoints.keys.any(
(k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), (k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')),
@ -248,7 +247,7 @@ void main() {
'request_id': 'request_5', 'request_id': 'request_5',
'requesting_device_id': 'OTHERDEVICE', 'requesting_device_id': 'OTHERDEVICE',
}); });
await matrix.encryption.keyManager.handleToDeviceEvent(event); await matrix.encryption!.keyManager.handleToDeviceEvent(event);
expect( expect(
FakeMatrixApi.calledEndpoints.keys.any( FakeMatrixApi.calledEndpoints.keys.any(
(k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), (k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')),
@ -270,7 +269,7 @@ void main() {
'request_id': 'request_6', 'request_id': 'request_6',
'requesting_device_id': 'OTHERDEVICE', 'requesting_device_id': 'OTHERDEVICE',
}); });
await matrix.encryption.keyManager.handleToDeviceEvent(event); await matrix.encryption!.keyManager.handleToDeviceEvent(event);
expect( expect(
FakeMatrixApi.calledEndpoints.keys.any( FakeMatrixApi.calledEndpoints.keys.any(
(k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), (k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')),
@ -282,17 +281,17 @@ void main() {
test('Receive shared keys', () async { test('Receive shared keys', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
final matrix = await getClient(); final matrix = await getClient();
final requestRoom = matrix.getRoomById('!726s6s6q:example.com'); final requestRoom = matrix.getRoomById('!726s6s6q:example.com')!;
await matrix.encryption.keyManager.request( await matrix.encryption!.keyManager.request(
requestRoom, validSessionId, validSenderKey, requestRoom, validSessionId, validSenderKey,
tryOnlineBackup: false); tryOnlineBackup: false);
final session = await matrix.encryption.keyManager final session = (await matrix.encryption!.keyManager
.loadInboundGroupSession( .loadInboundGroupSession(
requestRoom.id, validSessionId, validSenderKey); requestRoom.id, validSessionId, validSenderKey))!;
final sessionKey = session.inboundGroupSession final sessionKey = session.inboundGroupSession!
.export_session(session.inboundGroupSession.first_known_index()); .export_session(session.inboundGroupSession!.first_known_index());
matrix.encryption.keyManager.clearInboundGroupSessions(); matrix.encryption!.keyManager.clearInboundGroupSessions();
var event = ToDeviceEvent( var event = ToDeviceEvent(
sender: '@alice:example.com', sender: '@alice:example.com',
type: 'm.forwarded_room_key', type: 'm.forwarded_room_key',
@ -303,13 +302,15 @@ void main() {
'session_key': sessionKey, 'session_key': sessionKey,
'sender_key': validSenderKey, 'sender_key': validSenderKey,
'forwarding_curve25519_key_chain': [], 'forwarding_curve25519_key_chain': [],
'sender_claimed_ed25519_key':
'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8',
}, },
encryptedContent: { encryptedContent: {
'sender_key': 'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8', 'sender_key': 'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8',
}); });
await matrix.encryption.keyManager.handleToDeviceEvent(event); await matrix.encryption!.keyManager.handleToDeviceEvent(event);
expect( expect(
matrix.encryption.keyManager.getInboundGroupSession( matrix.encryption!.keyManager.getInboundGroupSession(
requestRoom.id, validSessionId, validSenderKey) != requestRoom.id, validSessionId, validSenderKey) !=
null, null,
true); true);
@ -317,7 +318,7 @@ void main() {
// now test a few invalid scenarios // now test a few invalid scenarios
// request not found // request not found
matrix.encryption.keyManager.clearInboundGroupSessions(); matrix.encryption!.keyManager.clearInboundGroupSessions();
event = ToDeviceEvent( event = ToDeviceEvent(
sender: '@alice:example.com', sender: '@alice:example.com',
type: 'm.forwarded_room_key', type: 'm.forwarded_room_key',
@ -328,22 +329,24 @@ void main() {
'session_key': sessionKey, 'session_key': sessionKey,
'sender_key': validSenderKey, 'sender_key': validSenderKey,
'forwarding_curve25519_key_chain': [], 'forwarding_curve25519_key_chain': [],
'sender_claimed_ed25519_key':
'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8',
}, },
encryptedContent: { encryptedContent: {
'sender_key': 'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8', 'sender_key': 'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8',
}); });
await matrix.encryption.keyManager.handleToDeviceEvent(event); await matrix.encryption!.keyManager.handleToDeviceEvent(event);
expect( expect(
matrix.encryption.keyManager.getInboundGroupSession( matrix.encryption!.keyManager.getInboundGroupSession(
requestRoom.id, validSessionId, validSenderKey) != requestRoom.id, validSessionId, validSenderKey) !=
null, null,
false); false);
// unknown device // unknown device
await matrix.encryption.keyManager.request( await matrix.encryption!.keyManager.request(
requestRoom, validSessionId, validSenderKey, requestRoom, validSessionId, validSenderKey,
tryOnlineBackup: false); tryOnlineBackup: false);
matrix.encryption.keyManager.clearInboundGroupSessions(); matrix.encryption!.keyManager.clearInboundGroupSessions();
event = ToDeviceEvent( event = ToDeviceEvent(
sender: '@alice:example.com', sender: '@alice:example.com',
type: 'm.forwarded_room_key', type: 'm.forwarded_room_key',
@ -354,22 +357,24 @@ void main() {
'session_key': sessionKey, 'session_key': sessionKey,
'sender_key': validSenderKey, 'sender_key': validSenderKey,
'forwarding_curve25519_key_chain': [], 'forwarding_curve25519_key_chain': [],
'sender_claimed_ed25519_key':
'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8',
}, },
encryptedContent: { encryptedContent: {
'sender_key': 'invalid', 'sender_key': 'invalid',
}); });
await matrix.encryption.keyManager.handleToDeviceEvent(event); await matrix.encryption!.keyManager.handleToDeviceEvent(event);
expect( expect(
matrix.encryption.keyManager.getInboundGroupSession( matrix.encryption!.keyManager.getInboundGroupSession(
requestRoom.id, validSessionId, validSenderKey) != requestRoom.id, validSessionId, validSenderKey) !=
null, null,
false); false);
// no encrypted content // no encrypted content
await matrix.encryption.keyManager.request( await matrix.encryption!.keyManager.request(
requestRoom, validSessionId, validSenderKey, requestRoom, validSessionId, validSenderKey,
tryOnlineBackup: false); tryOnlineBackup: false);
matrix.encryption.keyManager.clearInboundGroupSessions(); matrix.encryption!.keyManager.clearInboundGroupSessions();
event = ToDeviceEvent( event = ToDeviceEvent(
sender: '@alice:example.com', sender: '@alice:example.com',
type: 'm.forwarded_room_key', type: 'm.forwarded_room_key',
@ -380,10 +385,12 @@ void main() {
'session_key': sessionKey, 'session_key': sessionKey,
'sender_key': validSenderKey, 'sender_key': validSenderKey,
'forwarding_curve25519_key_chain': [], 'forwarding_curve25519_key_chain': [],
'sender_claimed_ed25519_key':
'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8',
}); });
await matrix.encryption.keyManager.handleToDeviceEvent(event); await matrix.encryption!.keyManager.handleToDeviceEvent(event);
expect( expect(
matrix.encryption.keyManager.getInboundGroupSession( matrix.encryption!.keyManager.getInboundGroupSession(
requestRoom.id, validSessionId, validSenderKey) != requestRoom.id, validSessionId, validSenderKey) !=
null, null,
false); false);

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2020 Famedly GmbH * Copyright (C) 2020 Famedly GmbH
@ -34,7 +33,7 @@ class MockSSSS extends SSSS {
bool requestedSecrets = false; bool requestedSecrets = false;
@override @override
Future<void> maybeRequestAll([List<DeviceKeys> devices]) async { Future<void> maybeRequestAll([List<DeviceKeys>? devices]) async {
requestedSecrets = true; requestedSecrets = true;
final handle = open(); final handle = open();
await handle.unlock(recoveryKey: ssssKey); await handle.unlock(recoveryKey: ssssKey);
@ -56,7 +55,7 @@ EventUpdate getLastSentEvent(KeyVerification req) {
'sender': req.client.userID, 'sender': req.client.userID,
}, },
type: EventUpdateType.timeline, type: EventUpdateType.timeline,
roomID: req.room.id, roomID: req.room!.id,
); );
} }
@ -70,8 +69,8 @@ void main() {
const otherPickledOlmAccount = const otherPickledOlmAccount =
'VWhVApbkcilKAEGppsPDf9nNVjaK8/IxT3asSR0sYg0S5KgbfE8vXEPwoiKBX2cEvwX3OessOBOkk+ZE7TTbjlrh/KEd31p8Wo+47qj0AP+Ky+pabnhi+/rTBvZy+gfzTqUfCxZrkzfXI9Op4JnP6gYmy7dVX2lMYIIs9WCO1jcmIXiXum5jnfXu1WLfc7PZtO2hH+k9CDKosOFaXRBmsu8k/BGXPSoWqUpvu6WpEG9t5STk4FeAzA'; 'VWhVApbkcilKAEGppsPDf9nNVjaK8/IxT3asSR0sYg0S5KgbfE8vXEPwoiKBX2cEvwX3OessOBOkk+ZE7TTbjlrh/KEd31p8Wo+47qj0AP+Ky+pabnhi+/rTBvZy+gfzTqUfCxZrkzfXI9Op4JnP6gYmy7dVX2lMYIIs9WCO1jcmIXiXum5jnfXu1WLfc7PZtO2hH+k9CDKosOFaXRBmsu8k/BGXPSoWqUpvu6WpEG9t5STk4FeAzA';
Client client1; late Client client1;
Client client2; late Client client2;
test('setupClient', () async { test('setupClient', () async {
try { try {
@ -117,24 +116,24 @@ void main() {
// because then we can easily intercept the payloads and inject in the other client // because then we can easily intercept the payloads and inject in the other client
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
// make sure our master key is *not* verified to not triger SSSS for now // make sure our master key is *not* verified to not triger SSSS for now
client1.userDeviceKeys[client1.userID].masterKey.setDirectVerified(false); client1.userDeviceKeys[client1.userID]!.masterKey!
.setDirectVerified(false);
final req1 = final req1 =
await client1.userDeviceKeys[client2.userID].startVerification(); await client1.userDeviceKeys[client2.userID]!.startVerification();
var evt = getLastSentEvent(req1); var evt = getLastSentEvent(req1);
expect(req1.state, KeyVerificationState.waitingAccept); expect(req1.state, KeyVerificationState.waitingAccept);
KeyVerification req2; late KeyVerification req2;
final sub = client2.onKeyVerificationRequest.stream.listen((req) { final sub = client2.onKeyVerificationRequest.stream.listen((req) {
req2 = req; req2 = req;
}); });
await client2.encryption.keyVerificationManager.handleEventUpdate(evt); await client2.encryption!.keyVerificationManager.handleEventUpdate(evt);
await Future.delayed(Duration(milliseconds: 10)); await Future.delayed(Duration(milliseconds: 10));
await sub.cancel(); await sub.cancel();
expect(req2 != null, true);
expect( expect(
client2.encryption.keyVerificationManager client2.encryption!.keyVerificationManager
.getRequest(req2.transactionId), .getRequest(req2.transactionId!),
req2); req2);
// send ready // send ready
@ -145,27 +144,27 @@ void main() {
// send start // send start
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client1.encryption.keyVerificationManager.handleEventUpdate(evt); await client1.encryption!.keyVerificationManager.handleEventUpdate(evt);
evt = getLastSentEvent(req1); evt = getLastSentEvent(req1);
// send accept // send accept
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client2.encryption.keyVerificationManager.handleEventUpdate(evt); await client2.encryption!.keyVerificationManager.handleEventUpdate(evt);
evt = getLastSentEvent(req2); evt = getLastSentEvent(req2);
// send key // send key
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client1.encryption.keyVerificationManager.handleEventUpdate(evt); await client1.encryption!.keyVerificationManager.handleEventUpdate(evt);
evt = getLastSentEvent(req1); evt = getLastSentEvent(req1);
// send key // send key
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client2.encryption.keyVerificationManager.handleEventUpdate(evt); await client2.encryption!.keyVerificationManager.handleEventUpdate(evt);
evt = getLastSentEvent(req2); evt = getLastSentEvent(req2);
// receive last key // receive last key
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client1.encryption.keyVerificationManager.handleEventUpdate(evt); await client1.encryption!.keyVerificationManager.handleEventUpdate(evt);
// compare emoji // compare emoji
expect(req1.state, KeyVerificationState.askSas); expect(req1.state, KeyVerificationState.askSas);
@ -194,64 +193,66 @@ void main() {
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await req1.acceptSas(); await req1.acceptSas();
evt = getLastSentEvent(req1); evt = getLastSentEvent(req1);
await client2.encryption.keyVerificationManager.handleEventUpdate(evt); await client2.encryption!.keyVerificationManager.handleEventUpdate(evt);
expect(req1.state, KeyVerificationState.waitingSas); expect(req1.state, KeyVerificationState.waitingSas);
// send mac // send mac
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await req2.acceptSas(); await req2.acceptSas();
evt = getLastSentEvent(req2); evt = getLastSentEvent(req2);
await client1.encryption.keyVerificationManager.handleEventUpdate(evt); await client1.encryption!.keyVerificationManager.handleEventUpdate(evt);
expect(req1.state, KeyVerificationState.done); expect(req1.state, KeyVerificationState.done);
expect(req2.state, KeyVerificationState.done); expect(req2.state, KeyVerificationState.done);
expect( expect(
client1.userDeviceKeys[client2.userID].deviceKeys[client2.deviceID] client1.userDeviceKeys[client2.userID]?.deviceKeys[client2.deviceID]
.directVerified, ?.directVerified,
true); true);
expect( expect(
client2.userDeviceKeys[client1.userID].deviceKeys[client1.deviceID] client2.userDeviceKeys[client1.userID]?.deviceKeys[client1.deviceID]
.directVerified, ?.directVerified,
true); true);
await client1.encryption.keyVerificationManager.cleanup(); await client1.encryption!.keyVerificationManager.cleanup();
await client2.encryption.keyVerificationManager.cleanup(); await client2.encryption!.keyVerificationManager.cleanup();
}); });
test('ask SSSS start', () async { test('ask SSSS start', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
client1.userDeviceKeys[client1.userID].masterKey.setDirectVerified(true); client1.userDeviceKeys[client1.userID]!.masterKey!
await client1.encryption.ssss.clearCache(); .setDirectVerified(true);
await client1.encryption!.ssss.clearCache();
final req1 = final req1 =
await client1.userDeviceKeys[client2.userID].startVerification(); await client1.userDeviceKeys[client2.userID]!.startVerification();
expect(req1.state, KeyVerificationState.askSSSS); expect(req1.state, KeyVerificationState.askSSSS);
await req1.openSSSS(recoveryKey: ssssKey); await req1.openSSSS(recoveryKey: ssssKey);
await Future.delayed(Duration(seconds: 1)); await Future.delayed(Duration(seconds: 1));
expect(req1.state, KeyVerificationState.waitingAccept); expect(req1.state, KeyVerificationState.waitingAccept);
await req1.cancel(); await req1.cancel();
await client1.encryption.keyVerificationManager.cleanup(); await client1.encryption!.keyVerificationManager.cleanup();
}); });
test('ask SSSS end', () async { test('ask SSSS end', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
// make sure our master key is *not* verified to not triger SSSS for now // make sure our master key is *not* verified to not triger SSSS for now
client1.userDeviceKeys[client1.userID].masterKey.setDirectVerified(false); client1.userDeviceKeys[client1.userID]!.masterKey!
.setDirectVerified(false);
// the other one has to have their master key verified to trigger asking for ssss // the other one has to have their master key verified to trigger asking for ssss
client2.userDeviceKeys[client2.userID].masterKey.setDirectVerified(true); client2.userDeviceKeys[client2.userID]!.masterKey!
.setDirectVerified(true);
final req1 = final req1 =
await client1.userDeviceKeys[client2.userID].startVerification(); await client1.userDeviceKeys[client2.userID]!.startVerification();
var evt = getLastSentEvent(req1); var evt = getLastSentEvent(req1);
expect(req1.state, KeyVerificationState.waitingAccept); expect(req1.state, KeyVerificationState.waitingAccept);
KeyVerification req2; late KeyVerification req2;
final sub = client2.onKeyVerificationRequest.stream.listen((req) { final sub = client2.onKeyVerificationRequest.stream.listen((req) {
req2 = req; req2 = req;
}); });
await client2.encryption.keyVerificationManager.handleEventUpdate(evt); await client2.encryption!.keyVerificationManager.handleEventUpdate(evt);
await Future.delayed(Duration(milliseconds: 10)); await Future.delayed(Duration(milliseconds: 10));
await sub.cancel(); await sub.cancel();
expect(req2 != null, true);
// send ready // send ready
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
@ -261,27 +262,27 @@ void main() {
// send start // send start
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client1.encryption.keyVerificationManager.handleEventUpdate(evt); await client1.encryption!.keyVerificationManager.handleEventUpdate(evt);
evt = getLastSentEvent(req1); evt = getLastSentEvent(req1);
// send accept // send accept
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client2.encryption.keyVerificationManager.handleEventUpdate(evt); await client2.encryption!.keyVerificationManager.handleEventUpdate(evt);
evt = getLastSentEvent(req2); evt = getLastSentEvent(req2);
// send key // send key
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client1.encryption.keyVerificationManager.handleEventUpdate(evt); await client1.encryption!.keyVerificationManager.handleEventUpdate(evt);
evt = getLastSentEvent(req1); evt = getLastSentEvent(req1);
// send key // send key
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client2.encryption.keyVerificationManager.handleEventUpdate(evt); await client2.encryption!.keyVerificationManager.handleEventUpdate(evt);
evt = getLastSentEvent(req2); evt = getLastSentEvent(req2);
// receive last key // receive last key
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client1.encryption.keyVerificationManager.handleEventUpdate(evt); await client1.encryption!.keyVerificationManager.handleEventUpdate(evt);
// compare emoji // compare emoji
expect(req1.state, KeyVerificationState.askSas); expect(req1.state, KeyVerificationState.askSas);
@ -301,21 +302,22 @@ void main() {
} }
// alright, they match // alright, they match
client1.userDeviceKeys[client1.userID].masterKey.setDirectVerified(true); client1.userDeviceKeys[client1.userID]!.masterKey!
await client1.encryption.ssss.clearCache(); .setDirectVerified(true);
await client1.encryption!.ssss.clearCache();
// send mac // send mac
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await req1.acceptSas(); await req1.acceptSas();
evt = getLastSentEvent(req1); evt = getLastSentEvent(req1);
await client2.encryption.keyVerificationManager.handleEventUpdate(evt); await client2.encryption!.keyVerificationManager.handleEventUpdate(evt);
expect(req1.state, KeyVerificationState.waitingSas); expect(req1.state, KeyVerificationState.waitingSas);
// send mac // send mac
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await req2.acceptSas(); await req2.acceptSas();
evt = getLastSentEvent(req2); evt = getLastSentEvent(req2);
await client1.encryption.keyVerificationManager.handleEventUpdate(evt); await client1.encryption!.keyVerificationManager.handleEventUpdate(evt);
expect(req1.state, KeyVerificationState.askSSSS); expect(req1.state, KeyVerificationState.askSSSS);
expect(req2.state, KeyVerificationState.done); expect(req2.state, KeyVerificationState.done);
@ -324,66 +326,67 @@ void main() {
await Future.delayed(Duration(milliseconds: 10)); await Future.delayed(Duration(milliseconds: 10));
expect(req1.state, KeyVerificationState.done); expect(req1.state, KeyVerificationState.done);
client1.encryption.ssss = MockSSSS(client1.encryption); client1.encryption!.ssss = MockSSSS(client1.encryption!);
(client1.encryption.ssss as MockSSSS).requestedSecrets = false; (client1.encryption!.ssss as MockSSSS).requestedSecrets = false;
await client1.encryption.ssss.clearCache(); await client1.encryption!.ssss.clearCache();
await req1.maybeRequestSSSSSecrets(); await req1.maybeRequestSSSSSecrets();
await Future.delayed(Duration(milliseconds: 10)); await Future.delayed(Duration(milliseconds: 10));
expect((client1.encryption.ssss as MockSSSS).requestedSecrets, true); expect((client1.encryption!.ssss as MockSSSS).requestedSecrets, true);
// delay for 12 seconds to be sure no other tests clear the ssss cache // delay for 12 seconds to be sure no other tests clear the ssss cache
await Future.delayed(Duration(seconds: 12)); await Future.delayed(Duration(seconds: 12));
await client1.encryption.keyVerificationManager.cleanup(); await client1.encryption!.keyVerificationManager.cleanup();
await client2.encryption.keyVerificationManager.cleanup(); await client2.encryption!.keyVerificationManager.cleanup();
}); });
test('reject verification', () async { test('reject verification', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
// make sure our master key is *not* verified to not triger SSSS for now // make sure our master key is *not* verified to not triger SSSS for now
client1.userDeviceKeys[client1.userID].masterKey.setDirectVerified(false); client1.userDeviceKeys[client1.userID]!.masterKey!
.setDirectVerified(false);
final req1 = final req1 =
await client1.userDeviceKeys[client2.userID].startVerification(); await client1.userDeviceKeys[client2.userID]!.startVerification();
var evt = getLastSentEvent(req1); var evt = getLastSentEvent(req1);
expect(req1.state, KeyVerificationState.waitingAccept); expect(req1.state, KeyVerificationState.waitingAccept);
KeyVerification req2; late KeyVerification req2;
final sub = client2.onKeyVerificationRequest.stream.listen((req) { final sub = client2.onKeyVerificationRequest.stream.listen((req) {
req2 = req; req2 = req;
}); });
await client2.encryption.keyVerificationManager.handleEventUpdate(evt); await client2.encryption!.keyVerificationManager.handleEventUpdate(evt);
await Future.delayed(Duration(milliseconds: 10)); await Future.delayed(Duration(milliseconds: 10));
await sub.cancel(); await sub.cancel();
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await req2.rejectVerification(); await req2.rejectVerification();
evt = getLastSentEvent(req2); evt = getLastSentEvent(req2);
await client1.encryption.keyVerificationManager.handleEventUpdate(evt); await client1.encryption!.keyVerificationManager.handleEventUpdate(evt);
expect(req1.state, KeyVerificationState.error); expect(req1.state, KeyVerificationState.error);
expect(req2.state, KeyVerificationState.error); expect(req2.state, KeyVerificationState.error);
await client1.encryption.keyVerificationManager.cleanup(); await client1.encryption!.keyVerificationManager.cleanup();
await client2.encryption.keyVerificationManager.cleanup(); await client2.encryption!.keyVerificationManager.cleanup();
}); });
test('reject sas', () async { test('reject sas', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
// make sure our master key is *not* verified to not triger SSSS for now // make sure our master key is *not* verified to not triger SSSS for now
client1.userDeviceKeys[client1.userID].masterKey.setDirectVerified(false); client1.userDeviceKeys[client1.userID]!.masterKey!
.setDirectVerified(false);
final req1 = final req1 =
await client1.userDeviceKeys[client2.userID].startVerification(); await client1.userDeviceKeys[client2.userID]!.startVerification();
var evt = getLastSentEvent(req1); var evt = getLastSentEvent(req1);
expect(req1.state, KeyVerificationState.waitingAccept); expect(req1.state, KeyVerificationState.waitingAccept);
KeyVerification req2; late KeyVerification req2;
final sub = client2.onKeyVerificationRequest.stream.listen((req) { final sub = client2.onKeyVerificationRequest.stream.listen((req) {
req2 = req; req2 = req;
}); });
await client2.encryption.keyVerificationManager.handleEventUpdate(evt); await client2.encryption!.keyVerificationManager.handleEventUpdate(evt);
await Future.delayed(Duration(milliseconds: 10)); await Future.delayed(Duration(milliseconds: 10));
await sub.cancel(); await sub.cancel();
expect(req2 != null, true);
// send ready // send ready
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
@ -393,60 +396,60 @@ void main() {
// send start // send start
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client1.encryption.keyVerificationManager.handleEventUpdate(evt); await client1.encryption!.keyVerificationManager.handleEventUpdate(evt);
evt = getLastSentEvent(req1); evt = getLastSentEvent(req1);
// send accept // send accept
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client2.encryption.keyVerificationManager.handleEventUpdate(evt); await client2.encryption!.keyVerificationManager.handleEventUpdate(evt);
evt = getLastSentEvent(req2); evt = getLastSentEvent(req2);
// send key // send key
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client1.encryption.keyVerificationManager.handleEventUpdate(evt); await client1.encryption!.keyVerificationManager.handleEventUpdate(evt);
evt = getLastSentEvent(req1); evt = getLastSentEvent(req1);
// send key // send key
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client2.encryption.keyVerificationManager.handleEventUpdate(evt); await client2.encryption!.keyVerificationManager.handleEventUpdate(evt);
evt = getLastSentEvent(req2); evt = getLastSentEvent(req2);
// receive last key // receive last key
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client1.encryption.keyVerificationManager.handleEventUpdate(evt); await client1.encryption!.keyVerificationManager.handleEventUpdate(evt);
await req1.acceptSas(); await req1.acceptSas();
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await req2.rejectSas(); await req2.rejectSas();
evt = getLastSentEvent(req2); evt = getLastSentEvent(req2);
await client1.encryption.keyVerificationManager.handleEventUpdate(evt); await client1.encryption!.keyVerificationManager.handleEventUpdate(evt);
expect(req1.state, KeyVerificationState.error); expect(req1.state, KeyVerificationState.error);
expect(req2.state, KeyVerificationState.error); expect(req2.state, KeyVerificationState.error);
await client1.encryption.keyVerificationManager.cleanup(); await client1.encryption!.keyVerificationManager.cleanup();
await client2.encryption.keyVerificationManager.cleanup(); await client2.encryption!.keyVerificationManager.cleanup();
}); });
test('other device accepted', () async { test('other device accepted', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
// make sure our master key is *not* verified to not triger SSSS for now // make sure our master key is *not* verified to not triger SSSS for now
client1.userDeviceKeys[client1.userID].masterKey.setDirectVerified(false); client1.userDeviceKeys[client1.userID]!.masterKey!
.setDirectVerified(false);
final req1 = final req1 =
await client1.userDeviceKeys[client2.userID].startVerification(); await client1.userDeviceKeys[client2.userID]!.startVerification();
final evt = getLastSentEvent(req1); final evt = getLastSentEvent(req1);
expect(req1.state, KeyVerificationState.waitingAccept); expect(req1.state, KeyVerificationState.waitingAccept);
KeyVerification req2; late KeyVerification req2;
final sub = client2.onKeyVerificationRequest.stream.listen((req) { final sub = client2.onKeyVerificationRequest.stream.listen((req) {
req2 = req; req2 = req;
}); });
await client2.encryption.keyVerificationManager.handleEventUpdate(evt); await client2.encryption!.keyVerificationManager.handleEventUpdate(evt);
await Future.delayed(Duration(milliseconds: 10)); await Future.delayed(Duration(milliseconds: 10));
await sub.cancel(); await sub.cancel();
expect(req2 != null, true);
await client2.encryption.keyVerificationManager await client2.encryption!.keyVerificationManager
.handleEventUpdate(EventUpdate( .handleEventUpdate(EventUpdate(
content: { content: {
'event_id': req2.transactionId, 'event_id': req2.transactionId,
@ -463,13 +466,13 @@ void main() {
'sender': client2.userID, 'sender': client2.userID,
}, },
type: EventUpdateType.timeline, type: EventUpdateType.timeline,
roomID: req2.room.id, roomID: req2.room!.id,
)); ));
expect(req2.state, KeyVerificationState.error); expect(req2.state, KeyVerificationState.error);
await req2.cancel(); await req2.cancel();
await client1.encryption.keyVerificationManager.cleanup(); await client1.encryption!.keyVerificationManager.cleanup();
await client2.encryption.keyVerificationManager.cleanup(); await client2.encryption!.keyVerificationManager.cleanup();
}); });
test('dispose client', () async { test('dispose client', () async {

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2020, 2021 Famedly GmbH * Copyright (C) 2020, 2021 Famedly GmbH
@ -32,7 +31,7 @@ void main() {
Logs().level = Level.error; Logs().level = Level.error;
var olmEnabled = true; var olmEnabled = true;
Client client; late Client client;
test('setupClient', () async { test('setupClient', () async {
try { try {
@ -53,37 +52,37 @@ void main() {
final payload = <String, dynamic>{ final payload = <String, dynamic>{
'fox': 'floof', 'fox': 'floof',
}; };
final signedPayload = client.encryption.olmManager.signJson(payload); final signedPayload = client.encryption!.olmManager.signJson(payload);
expect( expect(
signedPayload.checkJsonSignature( signedPayload.checkJsonSignature(
client.fingerprintKey, client.userID, client.deviceID), client.fingerprintKey, client.userID!, client.deviceID!),
true); true);
}); });
test('uploadKeys', () async { test('uploadKeys', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
final res = final res = await client.encryption!.olmManager
await client.encryption.olmManager.uploadKeys(uploadDeviceKeys: true); .uploadKeys(uploadDeviceKeys: true);
expect(res, true); expect(res, true);
var sent = json.decode( var sent = json.decode(
FakeMatrixApi.calledEndpoints['/client/r0/keys/upload'].first); FakeMatrixApi.calledEndpoints['/client/r0/keys/upload']!.first);
expect(sent['device_keys'] != null, true); expect(sent['device_keys'] != null, true);
expect(sent['one_time_keys'] != null, true); expect(sent['one_time_keys'] != null, true);
expect(sent['one_time_keys'].keys.length, 66); expect(sent['one_time_keys'].keys.length, 66);
expect(sent['fallback_keys'] != null, true); expect(sent['fallback_keys'] != null, true);
expect(sent['fallback_keys'].keys.length, 1); expect(sent['fallback_keys'].keys.length, 1);
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client.encryption.olmManager.uploadKeys(); await client.encryption!.olmManager.uploadKeys();
sent = json.decode( sent = json.decode(
FakeMatrixApi.calledEndpoints['/client/r0/keys/upload'].first); FakeMatrixApi.calledEndpoints['/client/r0/keys/upload']!.first);
expect(sent['device_keys'] != null, false); expect(sent['device_keys'] != null, false);
expect(sent['fallback_keys'].keys.length, 1); expect(sent['fallback_keys'].keys.length, 1);
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client.encryption.olmManager await client.encryption!.olmManager
.uploadKeys(oldKeyCount: 20, unusedFallbackKey: true); .uploadKeys(oldKeyCount: 20, unusedFallbackKey: true);
sent = json.decode( sent = json.decode(
FakeMatrixApi.calledEndpoints['/client/r0/keys/upload'].first); FakeMatrixApi.calledEndpoints['/client/r0/keys/upload']!.first);
expect(sent['one_time_keys'].keys.length, 46); expect(sent['one_time_keys'].keys.length, 46);
expect(sent['fallback_keys'].keys.length, 0); expect(sent['fallback_keys'].keys.length, 0);
}); });
@ -91,7 +90,7 @@ void main() {
test('handleDeviceOneTimeKeysCount', () async { test('handleDeviceOneTimeKeysCount', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
client.encryption.olmManager client.encryption!.olmManager
.handleDeviceOneTimeKeysCount({'signed_curve25519': 20}, null); .handleDeviceOneTimeKeysCount({'signed_curve25519': 20}, null);
await Future.delayed(Duration(milliseconds: 50)); await Future.delayed(Duration(milliseconds: 50));
expect( expect(
@ -99,7 +98,7 @@ void main() {
true); true);
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
client.encryption.olmManager client.encryption!.olmManager
.handleDeviceOneTimeKeysCount({'signed_curve25519': 70}, null); .handleDeviceOneTimeKeysCount({'signed_curve25519': 70}, null);
await Future.delayed(Duration(milliseconds: 50)); await Future.delayed(Duration(milliseconds: 50));
expect( expect(
@ -107,7 +106,7 @@ void main() {
false); false);
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
client.encryption.olmManager.handleDeviceOneTimeKeysCount(null, []); client.encryption!.olmManager.handleDeviceOneTimeKeysCount(null, []);
await Future.delayed(Duration(milliseconds: 50)); await Future.delayed(Duration(milliseconds: 50));
expect( expect(
FakeMatrixApi.calledEndpoints.containsKey('/client/r0/keys/upload'), FakeMatrixApi.calledEndpoints.containsKey('/client/r0/keys/upload'),
@ -115,7 +114,7 @@ void main() {
// this will upload keys because we assume the key count is 0, if the server doesn't send one // this will upload keys because we assume the key count is 0, if the server doesn't send one
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
client.encryption.olmManager client.encryption!.olmManager
.handleDeviceOneTimeKeysCount(null, ['signed_curve25519']); .handleDeviceOneTimeKeysCount(null, ['signed_curve25519']);
await Future.delayed(Duration(milliseconds: 50)); await Future.delayed(Duration(milliseconds: 50));
expect( expect(
@ -125,30 +124,31 @@ void main() {
test('restoreOlmSession', () async { test('restoreOlmSession', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
client.encryption.olmManager.olmSessions.clear(); client.encryption!.olmManager.olmSessions.clear();
await client.encryption.olmManager await client.encryption!.olmManager
.restoreOlmSession(client.userID, client.identityKey); .restoreOlmSession(client.userID!, client.identityKey);
expect(client.encryption.olmManager.olmSessions.length, 1); expect(client.encryption!.olmManager.olmSessions.length, 1);
client.encryption.olmManager.olmSessions.clear(); client.encryption!.olmManager.olmSessions.clear();
await client.encryption.olmManager await client.encryption!.olmManager
.restoreOlmSession(client.userID, 'invalid'); .restoreOlmSession(client.userID!, 'invalid');
expect(client.encryption.olmManager.olmSessions.length, 0); expect(client.encryption!.olmManager.olmSessions.length, 0);
client.encryption.olmManager.olmSessions.clear(); client.encryption!.olmManager.olmSessions.clear();
await client.encryption.olmManager await client.encryption!.olmManager
.restoreOlmSession('invalid', client.identityKey); .restoreOlmSession('invalid', client.identityKey);
expect(client.encryption.olmManager.olmSessions.length, 0); expect(client.encryption!.olmManager.olmSessions.length, 0);
}); });
test('startOutgoingOlmSessions', () async { test('startOutgoingOlmSessions', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
// start an olm session.....with ourself! // start an olm session.....with ourself!
client.encryption.olmManager.olmSessions.clear(); client.encryption!.olmManager.olmSessions.clear();
await client.encryption.olmManager.startOutgoingOlmSessions( await client.encryption!.olmManager.startOutgoingOlmSessions([
[client.userDeviceKeys[client.userID].deviceKeys[client.deviceID]]); client.userDeviceKeys[client.userID!]!.deviceKeys[client.deviceID]!
]);
expect( expect(
client.encryption.olmManager.olmSessions client.encryption!.olmManager.olmSessions
.containsKey(client.identityKey), .containsKey(client.identityKey),
true); true);
}); });
@ -159,7 +159,7 @@ void main() {
final deviceId = 'JLAFKJWSCS'; final deviceId = 'JLAFKJWSCS';
final senderKey = 'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8'; final senderKey = 'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8';
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client.database.setLastSentMessageUserDeviceKey( await client.database!.setLastSentMessageUserDeviceKey(
json.encode({ json.encode({
'type': 'm.foxies', 'type': 'm.foxies',
'content': { 'content': {
@ -176,7 +176,7 @@ void main() {
'sender_key': senderKey, 'sender_key': senderKey,
}, },
); );
await client.encryption.olmManager.handleToDeviceEvent(event); await client.encryption!.olmManager.handleToDeviceEvent(event);
expect( expect(
FakeMatrixApi.calledEndpoints.keys.any( FakeMatrixApi.calledEndpoints.keys.any(
(k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), (k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')),
@ -186,7 +186,7 @@ void main() {
// not encrypted // not encrypted
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client.database.setLastSentMessageUserDeviceKey( await client.database!.setLastSentMessageUserDeviceKey(
json.encode({ json.encode({
'type': 'm.foxies', 'type': 'm.foxies',
'content': { 'content': {
@ -201,7 +201,7 @@ void main() {
content: {}, content: {},
encryptedContent: null, encryptedContent: null,
); );
await client.encryption.olmManager.handleToDeviceEvent(event); await client.encryption!.olmManager.handleToDeviceEvent(event);
expect( expect(
FakeMatrixApi.calledEndpoints.keys.any( FakeMatrixApi.calledEndpoints.keys.any(
(k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), (k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')),
@ -209,7 +209,7 @@ void main() {
// device not found // device not found
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client.database.setLastSentMessageUserDeviceKey( await client.database!.setLastSentMessageUserDeviceKey(
json.encode({ json.encode({
'type': 'm.foxies', 'type': 'm.foxies',
'content': { 'content': {
@ -226,7 +226,7 @@ void main() {
'sender_key': 'invalid', 'sender_key': 'invalid',
}, },
); );
await client.encryption.olmManager.handleToDeviceEvent(event); await client.encryption!.olmManager.handleToDeviceEvent(event);
expect( expect(
FakeMatrixApi.calledEndpoints.keys.any( FakeMatrixApi.calledEndpoints.keys.any(
(k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), (k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')),
@ -234,7 +234,7 @@ void main() {
// don't replay if the last event is m.dummy itself // don't replay if the last event is m.dummy itself
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client.database.setLastSentMessageUserDeviceKey( await client.database!.setLastSentMessageUserDeviceKey(
json.encode({ json.encode({
'type': 'm.dummy', 'type': 'm.dummy',
'content': {}, 'content': {},
@ -249,7 +249,7 @@ void main() {
'sender_key': senderKey, 'sender_key': senderKey,
}, },
); );
await client.encryption.olmManager.handleToDeviceEvent(event); await client.encryption!.olmManager.handleToDeviceEvent(event);
expect( expect(
FakeMatrixApi.calledEndpoints.keys.any( FakeMatrixApi.calledEndpoints.keys.any(
(k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), (k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')),

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2020 Famedly GmbH * Copyright (C) 2020 Famedly GmbH
@ -32,7 +31,7 @@ void main() {
Logs().level = Level.error; Logs().level = Level.error;
var olmEnabled = true; var olmEnabled = true;
Client client; late Client client;
final roomId = '!726s6s6q:example.com'; final roomId = '!726s6s6q:example.com';
final sessionId = 'ciM/JWTPrmiWPPZNkRLDPQYf9AW/I46bxyLSr+Bx5oU'; final sessionId = 'ciM/JWTPrmiWPPZNkRLDPQYf9AW/I46bxyLSr+Bx5oU';
@ -54,21 +53,21 @@ void main() {
test('basic things', () async { test('basic things', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
expect(client.encryption.keyManager.enabled, true); expect(client.encryption!.keyManager.enabled, true);
expect(await client.encryption.keyManager.isCached(), false); expect(await client.encryption!.keyManager.isCached(), false);
final handle = client.encryption.ssss.open(); final handle = client.encryption!.ssss.open();
await handle.unlock(recoveryKey: ssssKey); await handle.unlock(recoveryKey: ssssKey);
await handle.maybeCacheAll(); await handle.maybeCacheAll();
expect(await client.encryption.keyManager.isCached(), true); expect(await client.encryption!.keyManager.isCached(), true);
}); });
test('load key', () async { test('load key', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
client.encryption.keyManager.clearInboundGroupSessions(); client.encryption!.keyManager.clearInboundGroupSessions();
await client.encryption.keyManager await client.encryption!.keyManager
.request(client.getRoomById(roomId), sessionId, senderKey); .request(client.getRoomById(roomId)!, sessionId, senderKey);
expect( expect(
client.encryption.keyManager client.encryption!.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey) != .getInboundGroupSession(roomId, sessionId, senderKey) !=
null, null,
true); true);
@ -94,25 +93,25 @@ void main() {
'sender_claimed_ed25519_key': client.fingerprintKey, 'sender_claimed_ed25519_key': client.fingerprintKey,
}; };
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
client.encryption.keyManager.setInboundGroupSession( client.encryption!.keyManager.setInboundGroupSession(
roomId, sessionId, senderKey, sessionPayload, roomId, sessionId, senderKey, sessionPayload,
forwarded: true); forwarded: true);
await Future.delayed(Duration(milliseconds: 500)); await Future.delayed(Duration(milliseconds: 500));
var dbSessions = await client.database.getInboundGroupSessionsToUpload(); var dbSessions = await client.database!.getInboundGroupSessionsToUpload();
expect(dbSessions.isNotEmpty, true); expect(dbSessions.isNotEmpty, true);
await client.encryption.keyManager.backgroundTasks(); await client.encryption!.keyManager.backgroundTasks();
final payload = FakeMatrixApi final payload = FakeMatrixApi
.calledEndpoints['/client/unstable/room_keys/keys?version=5'].first; .calledEndpoints['/client/unstable/room_keys/keys?version=5']!.first;
dbSessions = await client.database.getInboundGroupSessionsToUpload(); dbSessions = await client.database!.getInboundGroupSessionsToUpload();
expect(dbSessions.isEmpty, true); expect(dbSessions.isEmpty, true);
final onlineKeys = RoomKeys.fromJson(json.decode(payload)); final onlineKeys = RoomKeys.fromJson(json.decode(payload));
client.encryption.keyManager.clearInboundGroupSessions(); client.encryption!.keyManager.clearInboundGroupSessions();
var ret = client.encryption.keyManager var ret = client.encryption!.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey); .getInboundGroupSession(roomId, sessionId, senderKey);
expect(ret, null); expect(ret, null);
await client.encryption.keyManager.loadFromResponse(onlineKeys); await client.encryption!.keyManager.loadFromResponse(onlineKeys);
ret = client.encryption.keyManager ret = client.encryption!.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey); .getInboundGroupSession(roomId, sessionId, senderKey);
expect(ret != null, true); expect(ret != null, true);
}); });

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2020 Famedly GmbH * Copyright (C) 2020 Famedly GmbH
@ -42,7 +41,7 @@ class MockSSSS extends SSSS {
bool requestedSecrets = false; bool requestedSecrets = false;
@override @override
Future<void> maybeRequestAll([List<DeviceKeys> devices]) async { Future<void> maybeRequestAll([List<DeviceKeys>? devices]) async {
requestedSecrets = true; requestedSecrets = true;
final handle = open(); final handle = open();
await handle.unlock(recoveryKey: ssssKey); await handle.unlock(recoveryKey: ssssKey);
@ -55,7 +54,7 @@ void main() {
Logs().level = Level.error; Logs().level = Level.error;
var olmEnabled = true; var olmEnabled = true;
Client client; late Client client;
test('setupClient', () async { test('setupClient', () async {
try { try {
@ -73,7 +72,7 @@ void main() {
test('basic things', () async { test('basic things', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
expect(client.encryption.ssss.defaultKeyId, expect(client.encryption!.ssss.defaultKeyId,
'0FajDWYaM6wQ4O60OZnLvwZfsBNu4Bu3'); '0FajDWYaM6wQ4O60OZnLvwZfsBNu4Bu3');
}); });
@ -88,7 +87,7 @@ void main() {
test('store', () async { test('store', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
final handle = client.encryption.ssss.open(); final handle = client.encryption!.ssss.open();
var failed = false; var failed = false;
try { try {
await handle.unlock(passphrase: 'invalid'); await handle.unlock(passphrase: 'invalid');
@ -114,7 +113,7 @@ void main() {
// account_data for this test // account_data for this test
final content = FakeMatrixApi final content = FakeMatrixApi
.calledEndpoints[ .calledEndpoints[
'/client/r0/user/%40test%3AfakeServer.notExisting/account_data/best%20animal'] '/client/r0/user/%40test%3AfakeServer.notExisting/account_data/best%20animal']!
.first; .first;
client.accountData['best animal'] = BasicEvent.fromJson({ client.accountData['best animal'] = BasicEvent.fromJson({
'type': 'best animal', 'type': 'best animal',
@ -130,77 +129,78 @@ void main() {
final decoded = SSSS.decodeRecoveryKey(encoded); final decoded = SSSS.decodeRecoveryKey(encoded);
expect(key, decoded); expect(key, decoded);
final handle = client.encryption.ssss.open(); final handle = client.encryption!.ssss.open();
await handle.unlock(recoveryKey: ssssKey); await handle.unlock(recoveryKey: ssssKey);
expect(handle.recoveryKey, ssssKey); expect(handle.recoveryKey, ssssKey);
}); });
test('cache', () async { test('cache', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
await client.encryption.ssss.clearCache(); await client.encryption!.ssss.clearCache();
final handle = final handle =
client.encryption.ssss.open(EventTypes.CrossSigningSelfSigning); client.encryption!.ssss.open(EventTypes.CrossSigningSelfSigning);
await handle.unlock(recoveryKey: ssssKey, postUnlock: false); await handle.unlock(recoveryKey: ssssKey, postUnlock: false);
expect( expect(
(await client.encryption.ssss (await client.encryption!.ssss
.getCached(EventTypes.CrossSigningSelfSigning)) != .getCached(EventTypes.CrossSigningSelfSigning)) !=
null, null,
false); false);
expect( expect(
(await client.encryption.ssss (await client.encryption!.ssss
.getCached(EventTypes.CrossSigningUserSigning)) != .getCached(EventTypes.CrossSigningUserSigning)) !=
null, null,
false); false);
await handle.getStored(EventTypes.CrossSigningSelfSigning); await handle.getStored(EventTypes.CrossSigningSelfSigning);
expect( expect(
(await client.encryption.ssss (await client.encryption!.ssss
.getCached(EventTypes.CrossSigningSelfSigning)) != .getCached(EventTypes.CrossSigningSelfSigning)) !=
null, null,
true); true);
await handle.maybeCacheAll(); await handle.maybeCacheAll();
expect( expect(
(await client.encryption.ssss (await client.encryption!.ssss
.getCached(EventTypes.CrossSigningUserSigning)) != .getCached(EventTypes.CrossSigningUserSigning)) !=
null, null,
true); true);
expect( expect(
(await client.encryption.ssss.getCached(EventTypes.MegolmBackup)) != (await client.encryption!.ssss.getCached(EventTypes.MegolmBackup)) !=
null, null,
true); true);
}); });
test('postUnlock', () async { test('postUnlock', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
await client.encryption.ssss.clearCache(); await client.encryption!.ssss.clearCache();
client.userDeviceKeys[client.userID].masterKey.setDirectVerified(false); client.userDeviceKeys[client.userID!]!.masterKey!
.setDirectVerified(false);
final handle = final handle =
client.encryption.ssss.open(EventTypes.CrossSigningSelfSigning); client.encryption!.ssss.open(EventTypes.CrossSigningSelfSigning);
await handle.unlock(recoveryKey: ssssKey); await handle.unlock(recoveryKey: ssssKey);
expect( expect(
(await client.encryption.ssss (await client.encryption!.ssss
.getCached(EventTypes.CrossSigningSelfSigning)) != .getCached(EventTypes.CrossSigningSelfSigning)) !=
null, null,
true); true);
expect( expect(
(await client.encryption.ssss (await client.encryption!.ssss
.getCached(EventTypes.CrossSigningUserSigning)) != .getCached(EventTypes.CrossSigningUserSigning)) !=
null, null,
true); true);
expect( expect(
(await client.encryption.ssss.getCached(EventTypes.MegolmBackup)) != (await client.encryption!.ssss.getCached(EventTypes.MegolmBackup)) !=
null, null,
true); true);
expect( expect(client.userDeviceKeys[client.userID!]!.masterKey!.directVerified,
client.userDeviceKeys[client.userID].masterKey.directVerified, true); true);
}); });
test('make share requests', () async { test('make share requests', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
final key = final key =
client.userDeviceKeys[client.userID].deviceKeys['OTHERDEVICE']; client.userDeviceKeys[client.userID!]!.deviceKeys['OTHERDEVICE']!;
key.setDirectVerified(true); key.setDirectVerified(true);
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client.encryption.ssss.request('some.type', [key]); await client.encryption!.ssss.request('some.type', [key]);
expect( expect(
FakeMatrixApi.calledEndpoints.keys.any( FakeMatrixApi.calledEndpoints.keys.any(
(k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), (k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')),
@ -210,7 +210,7 @@ void main() {
test('answer to share requests', () async { test('answer to share requests', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
var event = ToDeviceEvent( var event = ToDeviceEvent(
sender: client.userID, sender: client.userID!,
type: 'm.secret.request', type: 'm.secret.request',
content: { content: {
'action': 'request', 'action': 'request',
@ -220,7 +220,7 @@ void main() {
}, },
); );
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client.encryption.ssss.handleToDeviceEvent(event); await client.encryption!.ssss.handleToDeviceEvent(event);
expect( expect(
FakeMatrixApi.calledEndpoints.keys.any( FakeMatrixApi.calledEndpoints.keys.any(
(k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), (k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')),
@ -240,7 +240,7 @@ void main() {
}, },
); );
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client.encryption.ssss.handleToDeviceEvent(event); await client.encryption!.ssss.handleToDeviceEvent(event);
expect( expect(
FakeMatrixApi.calledEndpoints.keys.any( FakeMatrixApi.calledEndpoints.keys.any(
(k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), (k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')),
@ -248,7 +248,7 @@ void main() {
// secret not cached // secret not cached
event = ToDeviceEvent( event = ToDeviceEvent(
sender: client.userID, sender: client.userID!,
type: 'm.secret.request', type: 'm.secret.request',
content: { content: {
'action': 'request', 'action': 'request',
@ -258,7 +258,7 @@ void main() {
}, },
); );
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client.encryption.ssss.handleToDeviceEvent(event); await client.encryption!.ssss.handleToDeviceEvent(event);
expect( expect(
FakeMatrixApi.calledEndpoints.keys.any( FakeMatrixApi.calledEndpoints.keys.any(
(k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), (k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')),
@ -266,7 +266,7 @@ void main() {
// is a cancelation // is a cancelation
event = ToDeviceEvent( event = ToDeviceEvent(
sender: client.userID, sender: client.userID!,
type: 'm.secret.request', type: 'm.secret.request',
content: { content: {
'action': 'request_cancellation', 'action': 'request_cancellation',
@ -276,7 +276,7 @@ void main() {
}, },
); );
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client.encryption.ssss.handleToDeviceEvent(event); await client.encryption!.ssss.handleToDeviceEvent(event);
expect( expect(
FakeMatrixApi.calledEndpoints.keys.any( FakeMatrixApi.calledEndpoints.keys.any(
(k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), (k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')),
@ -284,11 +284,12 @@ void main() {
// device not verified // device not verified
final key = final key =
client.userDeviceKeys[client.userID].deviceKeys['OTHERDEVICE']; client.userDeviceKeys[client.userID!]!.deviceKeys['OTHERDEVICE']!;
key.setDirectVerified(false); key.setDirectVerified(false);
client.userDeviceKeys[client.userID].masterKey.setDirectVerified(false); client.userDeviceKeys[client.userID!]!.masterKey!
.setDirectVerified(false);
event = ToDeviceEvent( event = ToDeviceEvent(
sender: client.userID, sender: client.userID!,
type: 'm.secret.request', type: 'm.secret.request',
content: { content: {
'action': 'request', 'action': 'request',
@ -298,7 +299,7 @@ void main() {
}, },
); );
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await client.encryption.ssss.handleToDeviceEvent(event); await client.encryption!.ssss.handleToDeviceEvent(event);
expect( expect(
FakeMatrixApi.calledEndpoints.keys.any( FakeMatrixApi.calledEndpoints.keys.any(
(k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')), (k) => k.startsWith('/client/r0/sendToDevice/m.room.encrypted')),
@ -309,28 +310,28 @@ void main() {
test('receive share requests', () async { test('receive share requests', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
final key = final key =
client.userDeviceKeys[client.userID].deviceKeys['OTHERDEVICE']; client.userDeviceKeys[client.userID!]!.deviceKeys['OTHERDEVICE']!;
key.setDirectVerified(true); key.setDirectVerified(true);
final handle = final handle =
client.encryption.ssss.open(EventTypes.CrossSigningSelfSigning); client.encryption!.ssss.open(EventTypes.CrossSigningSelfSigning);
await handle.unlock(recoveryKey: ssssKey); await handle.unlock(recoveryKey: ssssKey);
await client.encryption.ssss.clearCache(); await client.encryption!.ssss.clearCache();
client.encryption.ssss.pendingShareRequests.clear(); client.encryption!.ssss.pendingShareRequests.clear();
await client.encryption.ssss.request('best animal', [key]); await client.encryption!.ssss.request('best animal', [key]);
var event = ToDeviceEvent( var event = ToDeviceEvent(
sender: client.userID, sender: client.userID!,
type: 'm.secret.send', type: 'm.secret.send',
content: { content: {
'request_id': client.encryption.ssss.pendingShareRequests.keys.first, 'request_id': client.encryption!.ssss.pendingShareRequests.keys.first,
'secret': 'foxies!', 'secret': 'foxies!',
}, },
encryptedContent: { encryptedContent: {
'sender_key': key.curve25519Key, 'sender_key': key.curve25519Key,
}, },
); );
await client.encryption.ssss.handleToDeviceEvent(event); await client.encryption!.ssss.handleToDeviceEvent(event);
expect(await client.encryption.ssss.getCached('best animal'), 'foxies!'); expect(await client.encryption!.ssss.getCached('best animal'), 'foxies!');
// test the different validators // test the different validators
for (final type in [ for (final type in [
@ -339,48 +340,48 @@ void main() {
EventTypes.MegolmBackup EventTypes.MegolmBackup
]) { ]) {
final secret = await handle.getStored(type); final secret = await handle.getStored(type);
await client.encryption.ssss.clearCache(); await client.encryption!.ssss.clearCache();
client.encryption.ssss.pendingShareRequests.clear(); client.encryption!.ssss.pendingShareRequests.clear();
await client.encryption.ssss.request(type, [key]); await client.encryption!.ssss.request(type, [key]);
event = ToDeviceEvent( event = ToDeviceEvent(
sender: client.userID, sender: client.userID!,
type: 'm.secret.send', type: 'm.secret.send',
content: { content: {
'request_id': 'request_id':
client.encryption.ssss.pendingShareRequests.keys.first, client.encryption!.ssss.pendingShareRequests.keys.first,
'secret': secret, 'secret': secret,
}, },
encryptedContent: { encryptedContent: {
'sender_key': key.curve25519Key, 'sender_key': key.curve25519Key,
}, },
); );
await client.encryption.ssss.handleToDeviceEvent(event); await client.encryption!.ssss.handleToDeviceEvent(event);
expect(await client.encryption.ssss.getCached(type), secret); expect(await client.encryption!.ssss.getCached(type), secret);
} }
// test different fail scenarios // test different fail scenarios
// not encrypted // not encrypted
await client.encryption.ssss.clearCache(); await client.encryption!.ssss.clearCache();
client.encryption.ssss.pendingShareRequests.clear(); client.encryption!.ssss.pendingShareRequests.clear();
await client.encryption.ssss.request('best animal', [key]); await client.encryption!.ssss.request('best animal', [key]);
event = ToDeviceEvent( event = ToDeviceEvent(
sender: client.userID, sender: client.userID!,
type: 'm.secret.send', type: 'm.secret.send',
content: { content: {
'request_id': client.encryption.ssss.pendingShareRequests.keys.first, 'request_id': client.encryption!.ssss.pendingShareRequests.keys.first,
'secret': 'foxies!', 'secret': 'foxies!',
}, },
); );
await client.encryption.ssss.handleToDeviceEvent(event); await client.encryption!.ssss.handleToDeviceEvent(event);
expect(await client.encryption.ssss.getCached('best animal'), null); expect(await client.encryption!.ssss.getCached('best animal'), null);
// unknown request id // unknown request id
await client.encryption.ssss.clearCache(); await client.encryption!.ssss.clearCache();
client.encryption.ssss.pendingShareRequests.clear(); client.encryption!.ssss.pendingShareRequests.clear();
await client.encryption.ssss.request('best animal', [key]); await client.encryption!.ssss.request('best animal', [key]);
event = ToDeviceEvent( event = ToDeviceEvent(
sender: client.userID, sender: client.userID!,
type: 'm.secret.send', type: 'm.secret.send',
content: { content: {
'request_id': 'invalid', 'request_id': 'invalid',
@ -390,103 +391,103 @@ void main() {
'sender_key': key.curve25519Key, 'sender_key': key.curve25519Key,
}, },
); );
await client.encryption.ssss.handleToDeviceEvent(event); await client.encryption!.ssss.handleToDeviceEvent(event);
expect(await client.encryption.ssss.getCached('best animal'), null); expect(await client.encryption!.ssss.getCached('best animal'), null);
// not from a device we sent the request to // not from a device we sent the request to
await client.encryption.ssss.clearCache(); await client.encryption!.ssss.clearCache();
client.encryption.ssss.pendingShareRequests.clear(); client.encryption!.ssss.pendingShareRequests.clear();
await client.encryption.ssss.request('best animal', [key]); await client.encryption!.ssss.request('best animal', [key]);
event = ToDeviceEvent( event = ToDeviceEvent(
sender: client.userID, sender: client.userID!,
type: 'm.secret.send', type: 'm.secret.send',
content: { content: {
'request_id': client.encryption.ssss.pendingShareRequests.keys.first, 'request_id': client.encryption!.ssss.pendingShareRequests.keys.first,
'secret': 'foxies!', 'secret': 'foxies!',
}, },
encryptedContent: { encryptedContent: {
'sender_key': 'invalid', 'sender_key': 'invalid',
}, },
); );
await client.encryption.ssss.handleToDeviceEvent(event); await client.encryption!.ssss.handleToDeviceEvent(event);
expect(await client.encryption.ssss.getCached('best animal'), null); expect(await client.encryption!.ssss.getCached('best animal'), null);
// secret not a string // secret not a string
await client.encryption.ssss.clearCache(); await client.encryption!.ssss.clearCache();
client.encryption.ssss.pendingShareRequests.clear(); client.encryption!.ssss.pendingShareRequests.clear();
await client.encryption.ssss.request('best animal', [key]); await client.encryption!.ssss.request('best animal', [key]);
event = ToDeviceEvent( event = ToDeviceEvent(
sender: client.userID, sender: client.userID!,
type: 'm.secret.send', type: 'm.secret.send',
content: { content: {
'request_id': client.encryption.ssss.pendingShareRequests.keys.first, 'request_id': client.encryption!.ssss.pendingShareRequests.keys.first,
'secret': 42, 'secret': 42,
}, },
encryptedContent: { encryptedContent: {
'sender_key': key.curve25519Key, 'sender_key': key.curve25519Key,
}, },
); );
await client.encryption.ssss.handleToDeviceEvent(event); await client.encryption!.ssss.handleToDeviceEvent(event);
expect(await client.encryption.ssss.getCached('best animal'), null); expect(await client.encryption!.ssss.getCached('best animal'), null);
// validator doesn't check out // validator doesn't check out
await client.encryption.ssss.clearCache(); await client.encryption!.ssss.clearCache();
client.encryption.ssss.pendingShareRequests.clear(); client.encryption!.ssss.pendingShareRequests.clear();
await client.encryption.ssss.request(EventTypes.MegolmBackup, [key]); await client.encryption!.ssss.request(EventTypes.MegolmBackup, [key]);
event = ToDeviceEvent( event = ToDeviceEvent(
sender: client.userID, sender: client.userID!,
type: 'm.secret.send', type: 'm.secret.send',
content: { content: {
'request_id': client.encryption.ssss.pendingShareRequests.keys.first, 'request_id': client.encryption!.ssss.pendingShareRequests.keys.first,
'secret': 'foxies!', 'secret': 'foxies!',
}, },
encryptedContent: { encryptedContent: {
'sender_key': key.curve25519Key, 'sender_key': key.curve25519Key,
}, },
); );
await client.encryption.ssss.handleToDeviceEvent(event); await client.encryption!.ssss.handleToDeviceEvent(event);
expect(await client.encryption.ssss.getCached(EventTypes.MegolmBackup), expect(await client.encryption!.ssss.getCached(EventTypes.MegolmBackup),
null); null);
}); });
test('request all', () async { test('request all', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
final key = final key =
client.userDeviceKeys[client.userID].deviceKeys['OTHERDEVICE']; client.userDeviceKeys[client.userID!]!.deviceKeys['OTHERDEVICE']!;
key.setDirectVerified(true); key.setDirectVerified(true);
await client.encryption.ssss.clearCache(); await client.encryption!.ssss.clearCache();
client.encryption.ssss.pendingShareRequests.clear(); client.encryption!.ssss.pendingShareRequests.clear();
await client.encryption.ssss.maybeRequestAll([key]); await client.encryption!.ssss.maybeRequestAll([key]);
expect(client.encryption.ssss.pendingShareRequests.length, 3); expect(client.encryption!.ssss.pendingShareRequests.length, 3);
}); });
test('periodicallyRequestMissingCache', () async { test('periodicallyRequestMissingCache', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
client.userDeviceKeys[client.userID].masterKey.setDirectVerified(true); client.userDeviceKeys[client.userID!]!.masterKey!.setDirectVerified(true);
client.encryption.ssss = MockSSSS(client.encryption); client.encryption!.ssss = MockSSSS(client.encryption!);
(client.encryption.ssss as MockSSSS).requestedSecrets = false; (client.encryption!.ssss as MockSSSS).requestedSecrets = false;
await client.encryption.ssss.periodicallyRequestMissingCache(); await client.encryption!.ssss.periodicallyRequestMissingCache();
expect((client.encryption.ssss as MockSSSS).requestedSecrets, true); expect((client.encryption!.ssss as MockSSSS).requestedSecrets, true);
// it should only retry once every 15 min // it should only retry once every 15 min
(client.encryption.ssss as MockSSSS).requestedSecrets = false; (client.encryption!.ssss as MockSSSS).requestedSecrets = false;
await client.encryption.ssss.periodicallyRequestMissingCache(); await client.encryption!.ssss.periodicallyRequestMissingCache();
expect((client.encryption.ssss as MockSSSS).requestedSecrets, false); expect((client.encryption!.ssss as MockSSSS).requestedSecrets, false);
}); });
test('createKey', () async { test('createKey', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
// with passphrase // with passphrase
var newKey = await client.encryption.ssss.createKey('test'); var newKey = await client.encryption!.ssss.createKey('test');
expect(client.encryption.ssss.isKeyValid(newKey.keyId), true); expect(client.encryption!.ssss.isKeyValid(newKey.keyId), true);
var testKey = client.encryption.ssss.open(newKey.keyId); var testKey = client.encryption!.ssss.open(newKey.keyId);
await testKey.unlock(passphrase: 'test'); await testKey.unlock(passphrase: 'test');
await testKey.setPrivateKey(newKey.privateKey); await testKey.setPrivateKey(newKey.privateKey!);
// without passphrase // without passphrase
newKey = await client.encryption.ssss.createKey(); newKey = await client.encryption!.ssss.createKey();
expect(client.encryption.ssss.isKeyValid(newKey.keyId), true); expect(client.encryption!.ssss.isKeyValid(newKey.keyId), true);
testKey = client.encryption.ssss.open(newKey.keyId); testKey = client.encryption!.ssss.open(newKey.keyId);
await testKey.setPrivateKey(newKey.privateKey); await testKey.setPrivateKey(newKey.privateKey!);
}); });
test('dispose client', () async { test('dispose client', () async {

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2019, 2020 Famedly GmbH * Copyright (C) 2019, 2020 Famedly GmbH
@ -29,7 +28,7 @@ import 'package:test/test.dart';
import 'fake_client.dart'; import 'fake_client.dart';
import 'fake_matrix_api.dart'; import 'fake_matrix_api.dart';
import 'fake_matrix_localizations.dart'; import 'matrix_default_localizations.dart';
void main() { void main() {
/// All Tests related to the Event /// All Tests related to the Event
@ -249,10 +248,10 @@ void main() {
final event = Event.fromJson(redactJsonObj, room); final event = Event.fromJson(redactJsonObj, room);
event.setRedactionEvent(redactedBecause); event.setRedactionEvent(redactedBecause);
expect(event.redacted, true); expect(event.redacted, true);
expect(event.redactedBecause.toJson(), redactedBecause.toJson()); expect(event.redactedBecause?.toJson(), redactedBecause.toJson());
expect(event.content.isEmpty, true); expect(event.content.isEmpty, true);
redactionEventJson.remove('redacts'); redactionEventJson.remove('redacts');
expect(event.unsigned['redacted_because'], redactionEventJson); expect(event.unsigned?['redacted_because'], redactionEventJson);
} }
}); });
@ -280,7 +279,7 @@ void main() {
event.status = EventStatus.error; event.status = EventStatus.error;
final resp2 = await event.sendAgain(txid: '1234'); final resp2 = await event.sendAgain(txid: '1234');
expect(resp1, null); expect(resp1, null);
expect(resp2.startsWith('\$event'), true); expect(resp2?.startsWith('\$event'), true);
await matrix.dispose(closeDatabase: true); await matrix.dispose(closeDatabase: true);
}); });
@ -295,7 +294,7 @@ void main() {
final event = Event.fromJson( final event = Event.fromJson(
jsonObj, Room(id: '!1234:example.com', client: matrix)); jsonObj, Room(id: '!1234:example.com', client: matrix));
String exception; String? exception;
try { try {
await event.requestKey(); await event.requestKey();
} catch (e) { } catch (e) {
@ -330,7 +329,7 @@ void main() {
jsonObj['state_key'] = '@alice:example.com'; jsonObj['state_key'] = '@alice:example.com';
final event = Event.fromJson( final event = Event.fromJson(
jsonObj, Room(id: '!localpart:server.abc', client: client)); jsonObj, Room(id: '!localpart:server.abc', client: client));
expect(event.stateKeyUser.id, '@alice:example.com'); expect(event.stateKeyUser?.id, '@alice:example.com');
}); });
test('canRedact', () async { test('canRedact', () async {
expect(event.canRedact, true); expect(event.canRedact, true);
@ -364,7 +363,8 @@ void main() {
} }
} }
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), null); expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Removed by Example');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
event = Event.fromJson({ event = Event.fromJson({
@ -392,7 +392,8 @@ void main() {
'type': 'm.sticker', 'type': 'm.sticker',
'unsigned': {'age': 1234} 'unsigned': {'age': 1234}
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), null); expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Example sent a sticker');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
event = Event.fromJson({ event = Event.fromJson({
@ -405,7 +406,8 @@ void main() {
'type': 'm.room.redaction', 'type': 'm.room.redaction',
'unsigned': {'age': 1234} 'unsigned': {'age': 1234}
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), null); expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Example redacted an event');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
event = Event.fromJson({ event = Event.fromJson({
@ -420,7 +422,8 @@ void main() {
'type': 'm.room.aliases', 'type': 'm.room.aliases',
'unsigned': {'age': 1234} 'unsigned': {'age': 1234}
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), null); expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Example changed the room aliases');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
event = Event.fromJson({ event = Event.fromJson({
@ -435,7 +438,8 @@ void main() {
'type': 'm.room.aliases', 'type': 'm.room.aliases',
'unsigned': {'age': 1234} 'unsigned': {'age': 1234}
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), null); expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Example changed the room aliases');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
event = Event.fromJson({ event = Event.fromJson({
@ -448,7 +452,8 @@ void main() {
'type': 'm.room.canonical_alias', 'type': 'm.room.canonical_alias',
'unsigned': {'age': 1234} 'unsigned': {'age': 1234}
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), null); expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Example changed the room invitation link');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
event = Event.fromJson({ event = Event.fromJson({
@ -469,7 +474,8 @@ void main() {
'type': 'm.room.create', 'type': 'm.room.create',
'unsigned': {'age': 1234} 'unsigned': {'age': 1234}
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), null); expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Example created the chat');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
event = Event.fromJson({ event = Event.fromJson({
@ -485,7 +491,8 @@ void main() {
'type': 'm.room.tombstone', 'type': 'm.room.tombstone',
'unsigned': {'age': 1234} 'unsigned': {'age': 1234}
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), null); expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Room has been upgraded');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
event = Event.fromJson({ event = Event.fromJson({
@ -498,7 +505,8 @@ void main() {
'type': 'm.room.join_rules', 'type': 'm.room.join_rules',
'unsigned': {'age': 1234} 'unsigned': {'age': 1234}
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), null); expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Example changed the join rules to Anyone can join');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
event = Event.fromJson({ event = Event.fromJson({
@ -515,7 +523,8 @@ void main() {
'type': 'm.room.member', 'type': 'm.room.member',
'unsigned': {'age': 1234} 'unsigned': {'age': 1234}
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), null); expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Alice joined the chat');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
event = Event.fromJson({ event = Event.fromJson({
@ -527,7 +536,8 @@ void main() {
'state_key': '@alice:example.org', 'state_key': '@alice:example.org',
'type': 'm.room.member' 'type': 'm.room.member'
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), null); expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Example has invited Alice');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
event = Event.fromJson({ event = Event.fromJson({
@ -542,7 +552,8 @@ void main() {
'prev_content': {'membership': 'join'}, 'prev_content': {'membership': 'join'},
} }
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), null); expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Example kicked Alice');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
event = Event.fromJson({ event = Event.fromJson({
@ -557,7 +568,8 @@ void main() {
'prev_content': {'membership': 'join'}, 'prev_content': {'membership': 'join'},
} }
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), null); expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Example banned Alice');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
event = Event.fromJson({ event = Event.fromJson({
@ -572,7 +584,8 @@ void main() {
'prev_content': {'membership': 'invite'}, 'prev_content': {'membership': 'invite'},
} }
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), null); expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Alice accepted the invitation');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
event = Event.fromJson({ event = Event.fromJson({
@ -587,7 +600,8 @@ void main() {
'prev_content': {'membership': 'join'}, 'prev_content': {'membership': 'join'},
} }
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), null); expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Example has invited Alice');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
event = Event.fromJson({ event = Event.fromJson({
@ -602,7 +616,8 @@ void main() {
'prev_content': {'membership': 'invite'}, 'prev_content': {'membership': 'invite'},
} }
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), null); expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Example has withdrawn the invitation for Alice');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
event = Event.fromJson({ event = Event.fromJson({
@ -617,7 +632,8 @@ void main() {
'prev_content': {'membership': 'invite'}, 'prev_content': {'membership': 'invite'},
} }
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), null); expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Alice rejected the invitation');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
event = Event.fromJson({ event = Event.fromJson({
@ -641,7 +657,8 @@ void main() {
'type': 'm.room.power_levels', 'type': 'm.room.power_levels',
'unsigned': {'age': 1234} 'unsigned': {'age': 1234}
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), null); expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Example changed the chat permissions');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
event = Event.fromJson({ event = Event.fromJson({
@ -654,7 +671,8 @@ void main() {
'type': 'm.room.name', 'type': 'm.room.name',
'unsigned': {'age': 1234} 'unsigned': {'age': 1234}
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), null); expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Example changed the chat name to The room name');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
event = Event.fromJson({ event = Event.fromJson({
@ -667,7 +685,8 @@ void main() {
'type': 'm.room.topic', 'type': 'm.room.topic',
'unsigned': {'age': 1234} 'unsigned': {'age': 1234}
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), null); expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Example changed the chat description to A room topic');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
event = Event.fromJson({ event = Event.fromJson({
@ -683,7 +702,8 @@ void main() {
'type': 'm.room.avatar', 'type': 'm.room.avatar',
'unsigned': {'age': 1234} 'unsigned': {'age': 1234}
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), null); expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Example changed the chat avatar');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
event = Event.fromJson({ event = Event.fromJson({
@ -696,7 +716,8 @@ void main() {
'type': 'm.room.history_visibility', 'type': 'm.room.history_visibility',
'unsigned': {'age': 1234} 'unsigned': {'age': 1234}
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), null); expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Example changed the history visibility to Visible for all participants');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
event = Event.fromJson({ event = Event.fromJson({
@ -713,8 +734,8 @@ void main() {
'type': 'm.room.encryption', 'type': 'm.room.encryption',
'unsigned': {'age': 1234} 'unsigned': {'age': 1234}
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Example activatedEndToEndEncryption. needPantalaimonWarning'); 'Example activated end to end encryption. Need pantalaimon');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
event = Event.fromJson({ event = Event.fromJson({
@ -731,7 +752,7 @@ void main() {
'type': 'm.room.message', 'type': 'm.room.message',
'unsigned': {'age': 1234} 'unsigned': {'age': 1234}
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'This is an example text message'); 'This is an example text message');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
@ -749,7 +770,7 @@ void main() {
'type': 'm.room.message', 'type': 'm.room.message',
'unsigned': {'age': 1234} 'unsigned': {'age': 1234}
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'* thinks this is an example emote'); '* thinks this is an example emote');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
@ -767,7 +788,7 @@ void main() {
'type': 'm.room.message', 'type': 'm.room.message',
'unsigned': {'age': 1234} 'unsigned': {'age': 1234}
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'This is an example notice'); 'This is an example notice');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
@ -785,7 +806,8 @@ void main() {
'type': 'm.room.message', 'type': 'm.room.message',
'unsigned': {'age': 1234} 'unsigned': {'age': 1234}
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), null); expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Example sent a picture');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
event = Event.fromJson({ event = Event.fromJson({
@ -803,7 +825,8 @@ void main() {
'type': 'm.room.message', 'type': 'm.room.message',
'unsigned': {'age': 1234} 'unsigned': {'age': 1234}
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), null); expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Example sent a file');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
event = Event.fromJson({ event = Event.fromJson({
@ -824,7 +847,8 @@ void main() {
'type': 'm.room.message', 'type': 'm.room.message',
'unsigned': {'age': 1234} 'unsigned': {'age': 1234}
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), null); expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Example sent an audio');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
event = Event.fromJson({ event = Event.fromJson({
@ -849,7 +873,8 @@ void main() {
'type': 'm.room.message', 'type': 'm.room.message',
'unsigned': {'age': 1234} 'unsigned': {'age': 1234}
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), null); expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Example shared the location');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
event = Event.fromJson({ event = Event.fromJson({
@ -879,7 +904,8 @@ void main() {
'type': 'm.room.message', 'type': 'm.room.message',
'unsigned': {'age': 1234} 'unsigned': {'age': 1234}
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), null); expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Example sent a video');
expect(event.isEventTypeKnown, true); expect(event.isEventTypeKnown, true);
event = Event.fromJson({ event = Event.fromJson({
@ -891,7 +917,8 @@ void main() {
'type': 'unknown.event.type', 'type': 'unknown.event.type',
'unsigned': {'age': 1234} 'unsigned': {'age': 1234}
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations()), null); expect(event.getLocalizedBody(MatrixDefaultLocalizations()),
'Unknown event unknown.event.type');
expect(event.isEventTypeKnown, false); expect(event.isEventTypeKnown, false);
}); });
@ -913,7 +940,7 @@ void main() {
'unsigned': {'age': 1234} 'unsigned': {'age': 1234}
}, room); }, room);
expect( expect(
event.getLocalizedBody(FakeMatrixLocalizations(), event.getLocalizedBody(MatrixDefaultLocalizations(),
plaintextBody: true), plaintextBody: true),
'**This is an example text message**'); '**This is an example text message**');
@ -941,10 +968,11 @@ void main() {
'type': 'm.room.message', 'type': 'm.room.message',
'unsigned': {'age': 1234} 'unsigned': {'age': 1234}
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations(), hideEdit: true), expect(
event.getLocalizedBody(MatrixDefaultLocalizations(), hideEdit: true),
'This is an example text message'); 'This is an example text message');
expect( expect(
event.getLocalizedBody(FakeMatrixLocalizations(), event.getLocalizedBody(MatrixDefaultLocalizations(),
hideEdit: true, plaintextBody: true), hideEdit: true, plaintextBody: true),
'**This is an example text message**'); '**This is an example text message**');
@ -962,10 +990,11 @@ void main() {
'type': 'm.room.message', 'type': 'm.room.message',
'unsigned': {'age': 1234} 'unsigned': {'age': 1234}
}, room); }, room);
expect(event.getLocalizedBody(FakeMatrixLocalizations(), hideReply: true), expect(
event.getLocalizedBody(MatrixDefaultLocalizations(), hideReply: true),
'hmm, fox'); 'hmm, fox');
expect( expect(
event.getLocalizedBody(FakeMatrixLocalizations(), event.getLocalizedBody(MatrixDefaultLocalizations(),
hideReply: true, plaintextBody: true), hideReply: true, plaintextBody: true),
'hmm, *fox*'); 'hmm, *fox*');
}); });
@ -976,6 +1005,8 @@ void main() {
'body': 'blah', 'body': 'blah',
'msgtype': 'm.text', 'msgtype': 'm.text',
}, },
'type': 'm.room.message',
'sender': '@example:example.org',
'event_id': '\$source', 'event_id': '\$source',
}, null); }, null);
final edit1 = Event.fromJson({ final edit1 = Event.fromJson({
@ -987,6 +1018,8 @@ void main() {
'rel_type': RelationshipTypes.edit, 'rel_type': RelationshipTypes.edit,
}, },
}, },
'type': 'm.room.message',
'sender': '@example:example.org',
'event_id': '\$edit1', 'event_id': '\$edit1',
}, null); }, null);
final edit2 = Event.fromJson({ final edit2 = Event.fromJson({
@ -998,9 +1031,11 @@ void main() {
'rel_type': RelationshipTypes.edit, 'rel_type': RelationshipTypes.edit,
}, },
}, },
'type': 'm.room.message',
'sender': '@example:example.org',
'event_id': '\$edit2', 'event_id': '\$edit2',
}, null); }, null);
final room = Room(client: client); final room = Room(client: client, id: '!id:fakeserver.nonexisting');
final timeline = final timeline =
Timeline(events: <Event>[event, edit1, edit2], room: room); Timeline(events: <Event>[event, edit1, edit2], room: room);
expect(event.hasAggregatedEvents(timeline, RelationshipTypes.edit), true); expect(event.hasAggregatedEvents(timeline, RelationshipTypes.edit), true);
@ -1096,7 +1131,7 @@ void main() {
'event_id': '\$edit3', 'event_id': '\$edit3',
'sender': '@bob:example.org', 'sender': '@bob:example.org',
}, null); }, null);
final room = Room(client: client); final room = Room(id: '!localpart:server.abc', client: client);
// no edits // no edits
var displayEvent = var displayEvent =
event.getDisplayEvent(Timeline(events: <Event>[event], room: room)); event.getDisplayEvent(Timeline(events: <Event>[event], room: room));
@ -1127,8 +1162,9 @@ void main() {
'sender': '@alice:example.org', 'sender': '@alice:example.org',
'unsigned': { 'unsigned': {
'redacted_because': { 'redacted_because': {
'evnet_id': '\$redact', 'event_id': '\$redact',
'sender': '@alice:example.org', 'sender': '@alice:example.org',
'type': 'm.room.redaction',
}, },
}, },
}, null); }, null);
@ -1143,7 +1179,7 @@ void main() {
return { return {
'/_matrix/media/r0/download/example.org/file': FILE_BUFF, '/_matrix/media/r0/download/example.org/file': FILE_BUFF,
'/_matrix/media/r0/download/example.org/thumb': THUMBNAIL_BUFF, '/_matrix/media/r0/download/example.org/thumb': THUMBNAIL_BUFF,
}[uri.path]; }[uri.path]!;
}; };
await client.checkHomeserver('https://fakeserver.notexisting', await client.checkHomeserver('https://fakeserver.notexisting',
checkWellKnown: false); checkWellKnown: false);
@ -1160,7 +1196,7 @@ void main() {
}, room); }, room);
var buffer = await event.downloadAndDecryptAttachment( var buffer = await event.downloadAndDecryptAttachment(
downloadCallback: downloadCallback); downloadCallback: downloadCallback);
expect(buffer.bytes, FILE_BUFF); expect(buffer?.bytes, FILE_BUFF);
expect(event.attachmentOrThumbnailMxcUrl().toString(), expect(event.attachmentOrThumbnailMxcUrl().toString(),
'mxc://example.org/file'); 'mxc://example.org/file');
expect(event.attachmentOrThumbnailMxcUrl(getThumbnail: true).toString(), expect(event.attachmentOrThumbnailMxcUrl(getThumbnail: true).toString(),
@ -1215,11 +1251,11 @@ void main() {
buffer = await event.downloadAndDecryptAttachment( buffer = await event.downloadAndDecryptAttachment(
downloadCallback: downloadCallback); downloadCallback: downloadCallback);
expect(buffer.bytes, FILE_BUFF); expect(buffer?.bytes, FILE_BUFF);
buffer = await event.downloadAndDecryptAttachment( buffer = await event.downloadAndDecryptAttachment(
getThumbnail: true, downloadCallback: downloadCallback); getThumbnail: true, downloadCallback: downloadCallback);
expect(buffer.bytes, THUMBNAIL_BUFF); expect(buffer?.bytes, THUMBNAIL_BUFF);
}); });
test('encrypted attachments', () async { test('encrypted attachments', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
@ -1234,7 +1270,7 @@ void main() {
return { return {
'/_matrix/media/r0/download/example.com/file': FILE_BUFF_ENC, '/_matrix/media/r0/download/example.com/file': FILE_BUFF_ENC,
'/_matrix/media/r0/download/example.com/thumb': THUMB_BUFF_ENC, '/_matrix/media/r0/download/example.com/thumb': THUMB_BUFF_ENC,
}[uri.path]; }[uri.path]!;
}; };
final room = Room(id: '!localpart:server.abc', client: await getClient()); final room = Room(id: '!localpart:server.abc', client: await getClient());
var event = Event.fromJson({ var event = Event.fromJson({
@ -1262,7 +1298,7 @@ void main() {
}, room); }, room);
var buffer = await event.downloadAndDecryptAttachment( var buffer = await event.downloadAndDecryptAttachment(
downloadCallback: downloadCallback); downloadCallback: downloadCallback);
expect(buffer.bytes, FILE_BUFF_DEC); expect(buffer?.bytes, FILE_BUFF_DEC);
event = Event.fromJson({ event = Event.fromJson({
'type': EventTypes.Message, 'type': EventTypes.Message,
@ -1315,11 +1351,11 @@ void main() {
expect(event.thumbnailMxcUrl.toString(), 'mxc://example.com/thumb'); expect(event.thumbnailMxcUrl.toString(), 'mxc://example.com/thumb');
buffer = await event.downloadAndDecryptAttachment( buffer = await event.downloadAndDecryptAttachment(
downloadCallback: downloadCallback); downloadCallback: downloadCallback);
expect(buffer.bytes, FILE_BUFF_DEC); expect(buffer?.bytes, FILE_BUFF_DEC);
buffer = await event.downloadAndDecryptAttachment( buffer = await event.downloadAndDecryptAttachment(
getThumbnail: true, downloadCallback: downloadCallback); getThumbnail: true, downloadCallback: downloadCallback);
expect(buffer.bytes, THUMB_BUFF_DEC); expect(buffer?.bytes, THUMB_BUFF_DEC);
await room.client.dispose(closeDatabase: true); await room.client.dispose(closeDatabase: true);
}); });
@ -1330,7 +1366,7 @@ void main() {
serverHits++; serverHits++;
return { return {
'/_matrix/media/r0/download/example.org/newfile': FILE_BUFF, '/_matrix/media/r0/download/example.org/newfile': FILE_BUFF,
}[uri.path]; }[uri.path]!;
}; };
await client.checkHomeserver('https://fakeserver.notexisting', await client.checkHomeserver('https://fakeserver.notexisting',
checkWellKnown: false); checkWellKnown: false);
@ -1352,14 +1388,14 @@ void main() {
var buffer = await event.downloadAndDecryptAttachment( var buffer = await event.downloadAndDecryptAttachment(
downloadCallback: downloadCallback); downloadCallback: downloadCallback);
expect(await event.isAttachmentInLocalStore(), expect(await event.isAttachmentInLocalStore(),
event.room.client.database.supportsFileStoring); event.room?.client.database?.supportsFileStoring);
expect(buffer.bytes, FILE_BUFF); expect(buffer?.bytes, FILE_BUFF);
expect(serverHits, 1); expect(serverHits, 1);
buffer = await event.downloadAndDecryptAttachment( buffer = await event.downloadAndDecryptAttachment(
downloadCallback: downloadCallback); downloadCallback: downloadCallback);
expect(buffer.bytes, FILE_BUFF); expect(buffer?.bytes, FILE_BUFF);
expect( expect(
serverHits, event.room.client.database.supportsFileStoring ? 1 : 2); serverHits, event.room!.client.database!.supportsFileStoring ? 1 : 2);
await room.client.dispose(closeDatabase: true); await room.client.dispose(closeDatabase: true);
}); });

View File

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

View File

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

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2019, 2020 Famedly GmbH * Copyright (C) 2019, 2020 Famedly GmbH
@ -39,7 +38,7 @@ Map<String, dynamic> decodeJson(dynamic data) {
class FakeMatrixApi extends MockClient { class FakeMatrixApi extends MockClient {
static final calledEndpoints = <String, List<dynamic>>{}; static final calledEndpoints = <String, List<dynamic>>{};
static int eventCounter = 0; static int eventCounter = 0;
static sdk.Client client; static sdk.Client? client;
static bool failToDevice = false; static bool failToDevice = false;
FakeMatrixApi() FakeMatrixApi()
@ -85,9 +84,10 @@ class FakeMatrixApi extends MockClient {
if (!calledEndpoints.containsKey(action)) { if (!calledEndpoints.containsKey(action)) {
calledEndpoints[action] = <dynamic>[]; calledEndpoints[action] = <dynamic>[];
} }
calledEndpoints[action].add(data); calledEndpoints[action]?.add(data);
if (api.containsKey(method) && api[method].containsKey(action)) { final act = api[method]?[action];
res = api[method][action](data); if (act != null) {
res = act(data);
if (res is Map && res.containsKey('errcode')) { if (res is Map && res.containsKey('errcode')) {
if (res['errcode'] == 'M_NOT_FOUND') { if (res['errcode'] == 'M_NOT_FOUND') {
statusCode = 404; statusCode = 404;
@ -126,12 +126,12 @@ class FakeMatrixApi extends MockClient {
..accountData = [ ..accountData = [
sdk.BasicEvent(content: decodeJson(data), type: type) sdk.BasicEvent(content: decodeJson(data), type: type)
]; ];
if (client.database != null) { if (client?.database != null) {
await client.database.transaction(() async { await client?.database?.transaction(() async {
await client.handleSync(syncUpdate); await client?.handleSync(syncUpdate);
}); });
} else { } else {
await client.handleSync(syncUpdate); await client?.handleSync(syncUpdate);
} }
res = {}; res = {};
} else { } else {
@ -2101,15 +2101,15 @@ class FakeMatrixApi extends MockClient {
}) { }) {
if (jsonBody[keyType] != null) { if (jsonBody[keyType] != null) {
final key = final key =
sdk.CrossSigningKey.fromJson(jsonBody[keyType], client); sdk.CrossSigningKey.fromJson(jsonBody[keyType], client!);
client.userDeviceKeys[client.userID].crossSigningKeys client!.userDeviceKeys[client!.userID!]?.crossSigningKeys
.removeWhere((k, v) => v.usage.contains(key.usage.first)); .removeWhere((k, v) => v.usage.contains(key.usage.first));
client.userDeviceKeys[client.userID] client!.userDeviceKeys[client!.userID!]
.crossSigningKeys[key.publicKey] = key; ?.crossSigningKeys[key.publicKey!] = key;
} }
} }
// and generate a fake sync // and generate a fake sync
client.handleSync(sdk.SyncUpdate(nextBatch: '')); client!.handleSync(sdk.SyncUpdate(nextBatch: ''));
} }
return {}; return {};
}, },
@ -2124,35 +2124,35 @@ class FakeMatrixApi extends MockClient {
'/client/r0/pushrules/global/content/nocake/enabled': (var req) => {}, '/client/r0/pushrules/global/content/nocake/enabled': (var req) => {},
'/client/r0/pushrules/global/content/nocake/actions': (var req) => {}, '/client/r0/pushrules/global/content/nocake/actions': (var req) => {},
'/client/r0/rooms/!localpart%3Aserver.abc/state/m.room.history_visibility': '/client/r0/rooms/!localpart%3Aserver.abc/state/m.room.history_visibility':
(var req) => {}, (var req) => {'event_id': '1234'},
'/client/r0/rooms/!localpart%3Aserver.abc/state/m.room.join_rules': '/client/r0/rooms/!localpart%3Aserver.abc/state/m.room.join_rules':
(var req) => {}, (var req) => {'event_id': '1234'},
'/client/r0/rooms/!localpart%3Aserver.abc/state/m.room.guest_access': '/client/r0/rooms/!localpart%3Aserver.abc/state/m.room.guest_access':
(var req) => {}, (var req) => {'event_id': '1234'},
'/client/r0/rooms/!localpart%3Aserver.abc/send/m.call.invite/1234': '/client/r0/rooms/!localpart%3Aserver.abc/send/m.call.invite/1234':
(var req) => {}, (var req) => {'event_id': '1234'},
'/client/r0/rooms/!localpart%3Aserver.abc/send/m.call.answer/1234': '/client/r0/rooms/!localpart%3Aserver.abc/send/m.call.answer/1234':
(var req) => {}, (var req) => {'event_id': '1234'},
'/client/r0/rooms/!localpart%3Aserver.abc/send/m.call.select_answer/1234': '/client/r0/rooms/!localpart%3Aserver.abc/send/m.call.select_answer/1234':
(var req) => {}, (var req) => {'event_id': '1234'},
'/client/r0/rooms/!localpart%3Aserver.abc/send/m.call.reject/1234': '/client/r0/rooms/!localpart%3Aserver.abc/send/m.call.reject/1234':
(var req) => {}, (var req) => {'event_id': '1234'},
'/client/r0/rooms/!localpart%3Aserver.abc/send/m.call.negotiate/1234': '/client/r0/rooms/!localpart%3Aserver.abc/send/m.call.negotiate/1234':
(var req) => {}, (var req) => {'event_id': '1234'},
'/client/r0/rooms/!localpart%3Aserver.abc/send/m.call.candidates/1234': '/client/r0/rooms/!localpart%3Aserver.abc/send/m.call.candidates/1234':
(var req) => {}, (var req) => {'event_id': '1234'},
'/client/r0/rooms/!localpart%3Aserver.abc/send/m.call.hangup/1234': '/client/r0/rooms/!localpart%3Aserver.abc/send/m.call.hangup/1234':
(var req) => {}, (var req) => {'event_id': '1234'},
'/client/r0/rooms/!localpart%3Aserver.abc/send/m.call.replaces/1234': '/client/r0/rooms/!localpart%3Aserver.abc/send/m.call.replaces/1234':
(var req) => {}, (var req) => {'event_id': '1234'},
'/client/r0/rooms/!localpart%3Aserver.abc/send/m.call.asserted_identity/1234': '/client/r0/rooms/!localpart%3Aserver.abc/send/m.call.asserted_identity/1234':
(var req) => {}, (var req) => {'event_id': '1234'},
'/client/r0/rooms/!localpart%3Aserver.abc/send/m.call.sdp_stream_metadata_changed/1234': '/client/r0/rooms/!localpart%3Aserver.abc/send/m.call.sdp_stream_metadata_changed/1234':
(var req) => {}, (var req) => {'event_id': '1234'},
'/client/r0/rooms/!localpart%3Aserver.abc/send/org.matrix.call.sdp_stream_metadata_changed/1234': '/client/r0/rooms/!localpart%3Aserver.abc/send/org.matrix.call.sdp_stream_metadata_changed/1234':
(var req) => {}, (var req) => {'event_id': '1234'},
'/client/r0/rooms/!localpart%3Aserver.abc/send/org.matrix.call.asserted_identity/1234': '/client/r0/rooms/!localpart%3Aserver.abc/send/org.matrix.call.asserted_identity/1234':
(var req) => {}, (var req) => {'event_id': '1234'},
'/client/r0/rooms/!1234%3Aexample.com/redact/1143273582443PhrSn%3Aexample.org/1234': '/client/r0/rooms/!1234%3Aexample.com/redact/1143273582443PhrSn%3Aexample.org/1234':
(var req) => {'event_id': '1234'}, (var req) => {'event_id': '1234'},
'/client/r0/pushrules/global/room/!localpart%3Aserver.abc': (var req) => '/client/r0/pushrules/global/room/!localpart%3Aserver.abc': (var req) =>

View File

@ -1,334 +0,0 @@
// @dart=2.9
/*
* Famedly Matrix SDK
* Copyright (C) 2019, 2020 Famedly GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import 'package:matrix/matrix.dart';
class FakeMatrixLocalizations extends MatrixLocalizations {
@override
String acceptedTheInvitation(String targetName) {
// TODO: implement acceptedTheInvitation
return null;
}
@override
String activatedEndToEndEncryption(String senderName) {
// TODO: implement activatedEndToEndEncryption
return '$senderName activatedEndToEndEncryption';
}
@override
// TODO: implement anyoneCanJoin
String get anyoneCanJoin => null;
@override
String bannedUser(String senderName, String targetName) {
// TODO: implement bannedUser
return null;
}
@override
String changedTheChatAvatar(String senderName) {
// TODO: implement changedTheChatAvatar
return null;
}
@override
String changedTheChatDescriptionTo(String senderName, String content) {
// TODO: implement changedTheChatDescriptionTo
return null;
}
@override
String changedTheChatNameTo(String senderName, String content) {
// TODO: implement changedTheChatNameTo
return null;
}
@override
String changedTheChatPermissions(String senderName) {
// TODO: implement changedTheChatPermissions
return null;
}
@override
String changedTheDisplaynameTo(String targetName, String newDisplayname) {
// TODO: implement changedTheDisplaynameTo
return null;
}
@override
String changedTheGuestAccessRules(String senderName) {
// TODO: implement changedTheGuestAccessRules
return null;
}
@override
String changedTheGuestAccessRulesTo(
String senderName, String localizedString) {
// TODO: implement changedTheGuestAccessRulesTo
return null;
}
@override
String changedTheHistoryVisibility(String senderName) {
// TODO: implement changedTheHistoryVisibility
return null;
}
@override
String changedTheHistoryVisibilityTo(
String senderName, String localizedString) {
// TODO: implement changedTheHistoryVisibilityTo
return null;
}
@override
String changedTheJoinRules(String senderName) {
// TODO: implement changedTheJoinRules
return null;
}
@override
String changedTheJoinRulesTo(String senderName, String localizedString) {
// TODO: implement changedTheJoinRulesTo
return null;
}
@override
String changedTheProfileAvatar(String targetName) {
// TODO: implement changedTheProfileAvatar
return null;
}
@override
String changedTheRoomAliases(String senderName) {
// TODO: implement changedTheRoomAliases
return null;
}
@override
String changedTheRoomInvitationLink(String senderName) {
// TODO: implement changedTheRoomInvitationLink
return null;
}
@override
// TODO: implement channelCorruptedDecryptError
String get channelCorruptedDecryptError => null;
@override
String couldNotDecryptMessage(String errorText) {
// TODO: implement couldNotDecryptMessage
return null;
}
@override
String createdTheChat(String senderName) {
// TODO: implement createdTheChat
return null;
}
@override
// TODO: implement emptyChat
String get emptyChat => null;
@override
// TODO: implement encryptionNotEnabled
String get encryptionNotEnabled => null;
@override
// TODO: implement fromJoining
String get fromJoining => null;
@override
// TODO: implement fromTheInvitation
String get fromTheInvitation => null;
@override
String groupWith(String displayname) {
// TODO: implement groupWith
return null;
}
@override
// TODO: implement guestsAreForbidden
String get guestsAreForbidden => null;
@override
// TODO: implement guestsCanJoin
String get guestsCanJoin => null;
@override
String hasWithdrawnTheInvitationFor(String senderName, String targetName) {
// TODO: implement hasWithdrawnTheInvitationFor
return null;
}
@override
String invitedUser(String senderName, String targetName) {
// TODO: implement invitedUser
return null;
}
@override
// TODO: implement invitedUsersOnly
String get invitedUsersOnly => null;
@override
String joinedTheChat(String targetName) {
// TODO: implement joinedTheChat
return null;
}
@override
String kicked(String senderName, String targetName) {
// TODO: implement kicked
return null;
}
@override
String kickedAndBanned(String senderName, String targetName) {
// TODO: implement kickedAndBanned
return null;
}
@override
// TODO: implement needPantalaimonWarning
String get needPantalaimonWarning => 'needPantalaimonWarning';
@override
// TODO: implement noPermission
String get noPermission => 'noPermission';
@override
String redactedAnEvent(String senderName) {
// TODO: implement redactedAnEvent
return null;
}
@override
String rejectedTheInvitation(String targetName) {
// TODO: implement rejectedTheInvitation
return null;
}
@override
String removedBy(String calcDisplayname) {
// TODO: implement removedBy
return null;
}
@override
// TODO: implement roomHasBeenUpgraded
String get roomHasBeenUpgraded => null;
@override
String sentAFile(String senderName) {
// TODO: implement sentAFile
return null;
}
@override
String sentAPicture(String senderName) {
// TODO: implement sentAPicture
return null;
}
@override
String sentASticker(String senderName) {
// TODO: implement sentASticker
return null;
}
@override
String sentAVideo(String senderName) {
// TODO: implement sentAVideo
return null;
}
@override
String sentAnAudio(String senderName) {
// TODO: implement sentAnAudio
return null;
}
@override
String sharedTheLocation(String senderName) {
// TODO: implement sharedTheLocation
return null;
}
@override
String unbannedUser(String senderName, String targetName) {
// TODO: implement unbannedUser
return null;
}
@override
// TODO: implement unknownEncryptionAlgorithm
String get unknownEncryptionAlgorithm => null;
@override
String unknownEvent(String typeKey) {
// TODO: implement unknownEvent
return null;
}
@override
String userLeftTheChat(String targetName) {
// TODO: implement userLeftTheChat
return null;
}
@override
// TODO: implement visibleForAllParticipants
String get visibleForAllParticipants => null;
@override
// TODO: implement visibleForEveryone
String get visibleForEveryone => null;
@override
// TODO: implement you
String get you => null;
@override
String answeredTheCall(String senderName) {
// TODO: implement answeredTheCall
return null;
}
@override
String endedTheCall(String senderName) {
// TODO: implement endedTheCall
return null;
}
@override
String sentCallInformations(String senderName) {
// TODO: implement sentCallInformations
return null;
}
@override
String startedACall(String senderName) {
// TODO: implement startedACall
return null;
}
}

View File

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

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2021 Famedly GmbH * Copyright (C) 2021 Famedly GmbH
@ -23,9 +22,9 @@ import 'fake_client.dart';
void main() { void main() {
group('Image Pack', () { group('Image Pack', () {
Client client; late Client client;
Room room; late Room room;
Room room2; late Room room2;
test('setupClient', () async { test('setupClient', () async {
client = await getClient(); client = await getClient();
@ -36,24 +35,36 @@ void main() {
content: {}, content: {},
room: room, room: room,
stateKey: '', stateKey: '',
senderId: client.userID!,
eventId: '\$fakeid1:fakeServer.notExisting',
originServerTs: DateTime.now(),
)); ));
room.setState(Event( room.setState(Event(
type: 'm.room.member', type: 'm.room.member',
content: {'membership': 'join'}, content: {'membership': 'join'},
room: room, room: room,
stateKey: client.userID, stateKey: client.userID,
senderId: '\@fakeuser:fakeServer.notExisting',
eventId: '\$fakeid2:fakeServer.notExisting',
originServerTs: DateTime.now(),
)); ));
room2.setState(Event( room2.setState(Event(
type: 'm.room.power_levels', type: 'm.room.power_levels',
content: {}, content: {},
room: room, room: room,
stateKey: '', stateKey: '',
senderId: client.userID!,
eventId: '\$fakeid3:fakeServer.notExisting',
originServerTs: DateTime.now(),
)); ));
room2.setState(Event( room2.setState(Event(
type: 'm.room.member', type: 'm.room.member',
content: {'membership': 'join'}, content: {'membership': 'join'},
room: room, room: room,
stateKey: client.userID, stateKey: client.userID,
senderId: '\@fakeuser:fakeServer.notExisting',
eventId: '\$fakeid4:fakeServer.notExisting',
originServerTs: DateTime.now(),
)); ));
client.rooms.add(room); client.rooms.add(room);
client.rooms.add(room2); client.rooms.add(room2);
@ -69,11 +80,14 @@ void main() {
}, },
room: room, room: room,
stateKey: '', stateKey: '',
senderId: '\@fakeuser:fakeServer.notExisting',
eventId: '\$fakeid5:fakeServer.notExisting',
originServerTs: DateTime.now(),
)); ));
final packs = room.getImagePacks(); final packs = room.getImagePacks();
expect(packs.length, 1); expect(packs.length, 1);
expect(packs['room'].images.length, 1); expect(packs['room']?.images.length, 1);
expect(packs['room'].images['room_plain'].url.toString(), expect(packs['room']?.images['room_plain']?.url.toString(),
'mxc://room_plain'); 'mxc://room_plain');
var packsFlat = room.getImagePacksFlat(); var packsFlat = room.getImagePacksFlat();
expect(packsFlat, { expect(packsFlat, {
@ -95,6 +109,9 @@ void main() {
}, },
room: room, room: room,
stateKey: '', stateKey: '',
senderId: '\@fakeuser:fakeServer.notExisting',
eventId: '\$fakeid6:fakeServer.notExisting',
originServerTs: DateTime.now(),
)); ));
packsFlat = room.getImagePacksFlat(ImagePackUsage.emoticon); packsFlat = room.getImagePacksFlat(ImagePackUsage.emoticon);
expect(packsFlat, { expect(packsFlat, {
@ -117,6 +134,9 @@ void main() {
}, },
room: room, room: room,
stateKey: '', stateKey: '',
senderId: '\@fakeuser:fakeServer.notExisting',
eventId: '\$fakeid7:fakeServer.notExisting',
originServerTs: DateTime.now(),
)); ));
packsFlat = room.getImagePacksFlat(ImagePackUsage.emoticon); packsFlat = room.getImagePacksFlat(ImagePackUsage.emoticon);
expect(packsFlat, { expect(packsFlat, {
@ -137,6 +157,9 @@ void main() {
}, },
room: room, room: room,
stateKey: 'fox', stateKey: 'fox',
senderId: '\@fakeuser:fakeServer.notExisting',
eventId: '\$fakeid8:fakeServer.notExisting',
originServerTs: DateTime.now(),
)); ));
packsFlat = room.getImagePacksFlat(ImagePackUsage.emoticon); packsFlat = room.getImagePacksFlat(ImagePackUsage.emoticon);
expect(packsFlat, { expect(packsFlat, {
@ -177,6 +200,9 @@ void main() {
}, },
room: room2, room: room2,
stateKey: '', stateKey: '',
senderId: '\@fakeuser:fakeServer.notExisting',
eventId: '\$fakeid9:fakeServer.notExisting',
originServerTs: DateTime.now(),
)); ));
client.accountData['im.ponies.emote_rooms'] = BasicEvent.fromJson({ client.accountData['im.ponies.emote_rooms'] = BasicEvent.fromJson({
'type': 'im.ponies.emote_rooms', 'type': 'im.ponies.emote_rooms',
@ -207,6 +233,9 @@ void main() {
}, },
room: room2, room: room2,
stateKey: 'fox', stateKey: 'fox',
senderId: '\@fakeuser:fakeServer.notExisting',
eventId: '\$fakeid10:fakeServer.notExisting',
originServerTs: DateTime.now(),
)); ));
client.accountData['im.ponies.emote_rooms'] = BasicEvent.fromJson({ client.accountData['im.ponies.emote_rooms'] = BasicEvent.fromJson({
'type': 'im.ponies.emote_rooms', 'type': 'im.ponies.emote_rooms',

View File

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

View File

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

View File

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

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2020 Famedly GmbH * Copyright (C) 2020 Famedly GmbH
@ -27,7 +26,7 @@ import 'fake_database.dart';
void main() { void main() {
group('Databse', () { group('Databse', () {
Logs().level = Level.error; Logs().level = Level.error;
final room = Room(id: '!room:blubb'); final room = Room(id: '!room:blubb', client: Client('testclient'));
test('setupDatabase', () async { test('setupDatabase', () async {
final database = await getDatabase(null); final database = await getDatabase(null);
await database.insertClient( await database.insertClient(
@ -43,7 +42,8 @@ void main() {
}); });
test('storeEventUpdate', () async { test('storeEventUpdate', () async {
final database = await getDatabase(null); final client = Client('testclient');
final database = await getDatabase(client);
// store a simple update // store a simple update
var update = EventUpdate( var update = EventUpdate(
type: EventUpdateType.timeline, type: EventUpdateType.timeline,
@ -56,9 +56,9 @@ void main() {
'sender': '@blah:blubb', 'sender': '@blah:blubb',
}, },
); );
await database.storeEventUpdate(update); await database.storeEventUpdate(update, client);
var event = await database.getEventById('\$event-1', room); var event = await database.getEventById('\$event-1', room);
expect(event.eventId, '\$event-1'); expect(event?.eventId, '\$event-1');
// insert a transaction id // insert a transaction id
update = EventUpdate( update = EventUpdate(
@ -73,9 +73,9 @@ void main() {
'status': EventStatus.sending.intValue, 'status': EventStatus.sending.intValue,
}, },
); );
await database.storeEventUpdate(update); await database.storeEventUpdate(update, client);
event = await database.getEventById('transaction-1', room); event = await database.getEventById('transaction-1', room);
expect(event.eventId, 'transaction-1'); expect(event?.eventId, 'transaction-1');
update = EventUpdate( update = EventUpdate(
type: EventUpdateType.timeline, type: EventUpdateType.timeline,
roomID: room.id, roomID: room.id,
@ -91,7 +91,7 @@ void main() {
'status': EventStatus.sent.intValue, 'status': EventStatus.sent.intValue,
}, },
); );
await database.storeEventUpdate(update); await database.storeEventUpdate(update, client);
event = await database.getEventById('transaction-1', room); event = await database.getEventById('transaction-1', room);
expect(event, null); expect(event, null);
event = await database.getEventById('\$event-2', room); event = await database.getEventById('\$event-2', room);
@ -109,9 +109,9 @@ void main() {
'status': EventStatus.sending.intValue, 'status': EventStatus.sending.intValue,
}, },
); );
await database.storeEventUpdate(update); await database.storeEventUpdate(update, client);
event = await database.getEventById('\$event-3', room); event = await database.getEventById('\$event-3', room);
expect(event.eventId, '\$event-3'); expect(event?.eventId, '\$event-3');
update = EventUpdate( update = EventUpdate(
type: EventUpdateType.timeline, type: EventUpdateType.timeline,
roomID: room.id, roomID: room.id,
@ -127,10 +127,10 @@ void main() {
}, },
}, },
); );
await database.storeEventUpdate(update); await database.storeEventUpdate(update, client);
event = await database.getEventById('\$event-3', room); event = await database.getEventById('\$event-3', room);
expect(event.eventId, '\$event-3'); expect(event?.eventId, '\$event-3');
expect(event.status, EventStatus.sent); expect(event?.status, EventStatus.sent);
event = await database.getEventById('transaction-2', room); event = await database.getEventById('transaction-2', room);
expect(event, null); expect(event, null);
@ -147,9 +147,9 @@ void main() {
'status': EventStatus.synced.intValue, 'status': EventStatus.synced.intValue,
}, },
); );
await database.storeEventUpdate(update); await database.storeEventUpdate(update, client);
event = await database.getEventById('\$event-4', room); event = await database.getEventById('\$event-4', room);
expect(event.eventId, '\$event-4'); expect(event?.eventId, '\$event-4');
update = EventUpdate( update = EventUpdate(
type: EventUpdateType.timeline, type: EventUpdateType.timeline,
roomID: room.id, roomID: room.id,
@ -165,10 +165,10 @@ void main() {
}, },
}, },
); );
await database.storeEventUpdate(update); await database.storeEventUpdate(update, client);
event = await database.getEventById('\$event-4', room); event = await database.getEventById('\$event-4', room);
expect(event.eventId, '\$event-4'); expect(event?.eventId, '\$event-4');
expect(event.status, EventStatus.synced); expect(event?.status, EventStatus.synced);
event = await database.getEventById('transaction-3', room); event = await database.getEventById('transaction-3', room);
expect(event, null); expect(event, null);
}); });

View File

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

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2019, 2020 Famedly GmbH * Copyright (C) 2019, 2020 Famedly GmbH
@ -34,11 +33,11 @@ void main() {
); );
expect(matrixException.errcode, 'M_FORBIDDEN'); expect(matrixException.errcode, 'M_FORBIDDEN');
final flows = matrixException.authenticationFlows; final flows = matrixException.authenticationFlows;
expect(flows.length, 1); expect(flows?.length, 1);
expect(flows.first.stages.length, 1); expect(flows?.first.stages.length, 1);
expect(flows.first.stages.first, 'example.type.foo'); expect(flows?.first.stages.first, 'example.type.foo');
expect( expect(
matrixException.authenticationParams['example.type.baz'], matrixException.authenticationParams?['example.type.baz'],
{'example_key': 'foobar'}, {'example_key': 'foobar'},
); );
expect(matrixException.completedAuthenticationFlows.length, 1); expect(matrixException.completedAuthenticationFlows.length, 1);

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2019, 2020 Famedly GmbH * Copyright (C) 2019, 2020 Famedly GmbH
@ -43,7 +42,7 @@ void main() {
} }
if (olmEnabled) { if (olmEnabled) {
final encryptedFile = await file.encrypt(); final encryptedFile = await file.encrypt();
expect(encryptedFile != null, true); expect(encryptedFile.data.isNotEmpty, true);
} }
}); });
}); });

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2019, 2020 Famedly GmbH * Copyright (C) 2019, 2020 Famedly GmbH
@ -48,77 +47,73 @@ void main() {
expect('@user:domain:8448'.domain, 'domain:8448'); expect('@user:domain:8448'.domain, 'domain:8448');
}); });
test('parseIdentifierIntoParts', () { test('parseIdentifierIntoParts', () {
var res = '#alias:beep'.parseIdentifierIntoParts(); var res = '#alias:beep'.parseIdentifierIntoParts()!;
expect(res.primaryIdentifier, '#alias:beep'); expect(res.primaryIdentifier, '#alias:beep');
expect(res.secondaryIdentifier, null); expect(res.secondaryIdentifier, null);
expect(res.queryString, null); expect(res.queryString, null);
res = 'blha'.parseIdentifierIntoParts(); expect('blha'.parseIdentifierIntoParts(), null);
expect(res, null); res = '#alias:beep/\$event'.parseIdentifierIntoParts()!;
res = '#alias:beep/\$event'.parseIdentifierIntoParts();
expect(res.primaryIdentifier, '#alias:beep'); expect(res.primaryIdentifier, '#alias:beep');
expect(res.secondaryIdentifier, '\$event'); expect(res.secondaryIdentifier, '\$event');
expect(res.queryString, null); expect(res.queryString, null);
res = '#alias:beep?blubb'.parseIdentifierIntoParts(); res = '#alias:beep?blubb'.parseIdentifierIntoParts()!;
expect(res.primaryIdentifier, '#alias:beep'); expect(res.primaryIdentifier, '#alias:beep');
expect(res.secondaryIdentifier, null); expect(res.secondaryIdentifier, null);
expect(res.queryString, 'blubb'); expect(res.queryString, 'blubb');
res = '#alias:beep/\$event?blubb'.parseIdentifierIntoParts(); res = '#alias:beep/\$event?blubb'.parseIdentifierIntoParts()!;
expect(res.primaryIdentifier, '#alias:beep'); expect(res.primaryIdentifier, '#alias:beep');
expect(res.secondaryIdentifier, '\$event'); expect(res.secondaryIdentifier, '\$event');
expect(res.queryString, 'blubb'); expect(res.queryString, 'blubb');
res = '#/\$?:beep/\$event?blubb?b'.parseIdentifierIntoParts(); res = '#/\$?:beep/\$event?blubb?b'.parseIdentifierIntoParts()!;
expect(res.primaryIdentifier, '#/\$?:beep'); expect(res.primaryIdentifier, '#/\$?:beep');
expect(res.secondaryIdentifier, '\$event'); expect(res.secondaryIdentifier, '\$event');
expect(res.queryString, 'blubb?b'); expect(res.queryString, 'blubb?b');
res = 'https://matrix.to/#/#alias:beep'.parseIdentifierIntoParts(); res = 'https://matrix.to/#/#alias:beep'.parseIdentifierIntoParts()!;
expect(res.primaryIdentifier, '#alias:beep'); expect(res.primaryIdentifier, '#alias:beep');
expect(res.secondaryIdentifier, null); expect(res.secondaryIdentifier, null);
expect(res.queryString, null); expect(res.queryString, null);
res = 'https://matrix.to/#/#🦊:beep'.parseIdentifierIntoParts(); res = 'https://matrix.to/#/#🦊:beep'.parseIdentifierIntoParts()!;
expect(res.primaryIdentifier, '#🦊:beep'); expect(res.primaryIdentifier, '#🦊:beep');
expect(res.secondaryIdentifier, null); expect(res.secondaryIdentifier, null);
expect(res.queryString, null); expect(res.queryString, null);
res = 'https://matrix.to/#/%23alias%3abeep'.parseIdentifierIntoParts(); res = 'https://matrix.to/#/%23alias%3abeep'.parseIdentifierIntoParts()!;
expect(res.primaryIdentifier, '#alias:beep'); expect(res.primaryIdentifier, '#alias:beep');
expect(res.secondaryIdentifier, null); expect(res.secondaryIdentifier, null);
expect(res.queryString, null); expect(res.queryString, null);
res = 'https://matrix.to/#/%23alias%3abeep?boop%F0%9F%A7%A1%F0%9F%A6%8A' res = 'https://matrix.to/#/%23alias%3abeep?boop%F0%9F%A7%A1%F0%9F%A6%8A'
.parseIdentifierIntoParts(); .parseIdentifierIntoParts()!;
expect(res.primaryIdentifier, '#alias:beep'); expect(res.primaryIdentifier, '#alias:beep');
expect(res.secondaryIdentifier, null); expect(res.secondaryIdentifier, null);
expect(res.queryString, 'boop%F0%9F%A7%A1%F0%9F%A6%8A'); expect(res.queryString, 'boop%F0%9F%A7%A1%F0%9F%A6%8A');
res = 'https://matrix.to/#/#alias:beep?via=fox.com&via=fox.org' res = 'https://matrix.to/#/#alias:beep?via=fox.com&via=fox.org'
.parseIdentifierIntoParts(); .parseIdentifierIntoParts()!;
expect(res.via, <String>{'fox.com', 'fox.org'}); expect(res.via, <String>{'fox.com', 'fox.org'});
res = 'matrix:u/her:example.org'.parseIdentifierIntoParts(); res = 'matrix:u/her:example.org'.parseIdentifierIntoParts()!;
expect(res.primaryIdentifier, '@her:example.org'); expect(res.primaryIdentifier, '@her:example.org');
expect(res.secondaryIdentifier, null); expect(res.secondaryIdentifier, null);
res = 'matrix:u/bad'.parseIdentifierIntoParts(); expect('matrix:u/bad'.parseIdentifierIntoParts(), null);
expect(res, null); res = 'matrix:roomid/rid:example.org'.parseIdentifierIntoParts()!;
res = 'matrix:roomid/rid:example.org'.parseIdentifierIntoParts();
expect(res.primaryIdentifier, '!rid:example.org'); expect(res.primaryIdentifier, '!rid:example.org');
expect(res.secondaryIdentifier, null); expect(res.secondaryIdentifier, null);
expect(res.action, null); expect(res.action, null);
res = 'matrix:r/us:example.org?action=chat'.parseIdentifierIntoParts(); res = 'matrix:r/us:example.org?action=chat'.parseIdentifierIntoParts()!;
expect(res.primaryIdentifier, '#us:example.org'); expect(res.primaryIdentifier, '#us:example.org');
expect(res.secondaryIdentifier, null); expect(res.secondaryIdentifier, null);
expect(res.action, 'chat'); expect(res.action, 'chat');
res = 'matrix:r/us:example.org/e/lol823y4bcp3qo4' res = 'matrix:r/us:example.org/e/lol823y4bcp3qo4'
.parseIdentifierIntoParts(); .parseIdentifierIntoParts()!;
expect(res.primaryIdentifier, '#us:example.org'); expect(res.primaryIdentifier, '#us:example.org');
expect(res.secondaryIdentifier, '\$lol823y4bcp3qo4'); expect(res.secondaryIdentifier, '\$lol823y4bcp3qo4');
res = 'matrix:roomid/rid:example.org?via=fox.com&via=fox.org' res = 'matrix:roomid/rid:example.org?via=fox.com&via=fox.org'
.parseIdentifierIntoParts(); .parseIdentifierIntoParts()!;
expect(res.primaryIdentifier, '!rid:example.org'); expect(res.primaryIdentifier, '!rid:example.org');
expect(res.secondaryIdentifier, null); expect(res.secondaryIdentifier, null);
expect(res.via, <String>{'fox.com', 'fox.org'}); expect(res.via, <String>{'fox.com', 'fox.org'});
res = 'matrix:beep/boop:example.org'.parseIdentifierIntoParts(); expect('matrix:beep/boop:example.org'.parseIdentifierIntoParts(), null);
expect(res, null); expect('matrix:boop'.parseIdentifierIntoParts(), null);
res = 'matrix:boop'.parseIdentifierIntoParts();
expect(res, null);
}); });
}); });
} }

View File

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

View File

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

View File

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

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2019, 2020 Famedly GmbH * Copyright (C) 2019, 2020 Famedly GmbH
@ -33,8 +32,8 @@ import 'fake_client.dart';
import 'fake_matrix_api.dart'; import 'fake_matrix_api.dart';
void main() { void main() {
Client matrix; late Client matrix;
Room room; late Room room;
/// All Tests related to the Event /// All Tests related to the Event
group('Room', () { group('Room', () {
@ -96,8 +95,9 @@ void main() {
expect(room.summary.mInvitedMemberCount, notificationCount); expect(room.summary.mInvitedMemberCount, notificationCount);
expect(room.summary.mHeroes, heroes); expect(room.summary.mHeroes, heroes);
expect(room.displayname, 'Alice, Bob, Charley'); expect(room.displayname, 'Alice, Bob, Charley');
expect(room.getState('m.room.join_rules').content['join_rule'], 'public'); expect(
expect(room.roomAccountData['com.test.foo'].content['foo'], 'bar'); room.getState('m.room.join_rules')?.content['join_rule'], 'public');
expect(room.roomAccountData['com.test.foo']?.content['foo'], 'bar');
room.setState( room.setState(
Event( Event(
@ -107,6 +107,7 @@ void main() {
room: room, room: room,
eventId: '123', eventId: '123',
content: {'alias': '#testalias:example.com'}, content: {'alias': '#testalias:example.com'},
originServerTs: DateTime.now(),
stateKey: ''), stateKey: ''),
); );
expect(room.displayname, 'testalias'); expect(room.displayname, 'testalias');
@ -120,6 +121,7 @@ void main() {
room: room, room: room,
eventId: '123', eventId: '123',
content: {'name': 'testname'}, content: {'name': 'testname'},
originServerTs: DateTime.now(),
stateKey: ''), stateKey: ''),
); );
expect(room.displayname, 'testname'); expect(room.displayname, 'testname');
@ -133,6 +135,7 @@ void main() {
room: room, room: room,
eventId: '123', eventId: '123',
content: {'topic': 'testtopic'}, content: {'topic': 'testtopic'},
originServerTs: DateTime.now(),
stateKey: ''), stateKey: ''),
); );
expect(room.topic, 'testtopic'); expect(room.topic, 'testtopic');
@ -146,6 +149,7 @@ void main() {
room: room, room: room,
eventId: '123', eventId: '123',
content: {'url': 'mxc://testurl'}, content: {'url': 'mxc://testurl'},
originServerTs: DateTime.now(),
stateKey: ''), stateKey: ''),
); );
expect(room.avatar.toString(), 'mxc://testurl'); expect(room.avatar.toString(), 'mxc://testurl');
@ -161,6 +165,7 @@ void main() {
content: { content: {
'pinned': ['1234'] 'pinned': ['1234']
}, },
originServerTs: DateTime.now(),
stateKey: ''), stateKey: ''),
); );
expect(room.pinnedEventIds.first, '1234'); expect(room.pinnedEventIds.first, '1234');
@ -176,9 +181,9 @@ void main() {
stateKey: '', stateKey: '',
), ),
); );
expect(room.lastEvent.eventId, '12345'); expect(room.lastEvent?.eventId, '12345');
expect(room.lastEvent.body, 'abc'); expect(room.lastEvent?.body, 'abc');
expect(room.timeCreated, room.lastEvent.originServerTs); expect(room.timeCreated, room.lastEvent?.originServerTs);
}); });
test('lastEvent is set properly', () { test('lastEvent is set properly', () {
@ -194,7 +199,7 @@ void main() {
stateKey: '', stateKey: '',
), ),
); );
expect(room.lastEvent.body, 'cd'); expect(room.lastEvent?.body, 'cd');
room.setState( room.setState(
Event( Event(
senderId: '@test:example.com', senderId: '@test:example.com',
@ -207,7 +212,7 @@ void main() {
stateKey: '', stateKey: '',
), ),
); );
expect(room.lastEvent.body, 'cdc'); expect(room.lastEvent?.body, 'cdc');
room.setState( room.setState(
Event( Event(
senderId: '@test:example.com', senderId: '@test:example.com',
@ -225,7 +230,7 @@ void main() {
stateKey: '', stateKey: '',
), ),
); );
expect(room.lastEvent.body, 'cdc'); // because we edited the "cd" message expect(room.lastEvent?.body, 'cdc'); // because we edited the "cd" message
room.setState( room.setState(
Event( Event(
senderId: '@test:example.com', senderId: '@test:example.com',
@ -243,7 +248,7 @@ void main() {
stateKey: '', stateKey: '',
), ),
); );
expect(room.lastEvent.body, 'edited cdc'); expect(room.lastEvent?.body, 'edited cdc');
}); });
test('lastEvent when reply parent edited', () async { test('lastEvent when reply parent edited', () async {
room.setState( room.setState(
@ -258,7 +263,7 @@ void main() {
stateKey: '', stateKey: '',
), ),
); );
expect(room.lastEvent.body, 'A'); expect(room.lastEvent?.body, 'A');
room.setState( room.setState(
Event( Event(
@ -276,7 +281,7 @@ void main() {
stateKey: '', stateKey: '',
), ),
); );
expect(room.lastEvent.body, 'B'); expect(room.lastEvent?.body, 'B');
room.setState( room.setState(
Event( Event(
senderId: '@test:example.com', senderId: '@test:example.com',
@ -294,7 +299,7 @@ void main() {
stateKey: '', stateKey: '',
), ),
); );
expect(room.lastEvent.body, 'B'); expect(room.lastEvent?.body, 'B');
}); });
test('sendReadMarker', () async { test('sendReadMarker', () async {
await room.setReadMarker('§1234:fakeServer.notExisting'); await room.setReadMarker('§1234:fakeServer.notExisting');
@ -308,12 +313,12 @@ void main() {
expect(user.displayName, 'Alice Margatroid'); expect(user.displayName, 'Alice Margatroid');
expect(user.membership, Membership.join); expect(user.membership, Membership.join);
expect(user.avatarUrl.toString(), 'mxc://example.org/SEsfnsuifSDFSSEF'); expect(user.avatarUrl.toString(), 'mxc://example.org/SEsfnsuifSDFSSEF');
expect(user.room.id, '!localpart:server.abc'); expect(user.room?.id, '!localpart:server.abc');
}); });
test('getEventByID', () async { test('getEventByID', () async {
final event = await room.getEventById('1234'); final event = await room.getEventById('1234');
expect(event.eventId, '143273582443PhrSn:example.org'); expect(event?.eventId, '143273582443PhrSn:example.org');
}); });
test('setName', () async { test('setName', () async {
@ -358,10 +363,11 @@ void main() {
'users': {'@test:fakeServer.notExisting': 100}, 'users': {'@test:fakeServer.notExisting': 100},
'users_default': 10 'users_default': 10
}, },
originServerTs: DateTime.now(),
stateKey: ''), stateKey: ''),
); );
expect(room.ownPowerLevel, 100); expect(room.ownPowerLevel, 100);
expect(room.getPowerLevelByUserId(matrix.userID), room.ownPowerLevel); expect(room.getPowerLevelByUserId(matrix.userID!), room.ownPowerLevel);
expect(room.getPowerLevelByUserId('@nouser:example.com'), 10); expect(room.getPowerLevelByUserId('@nouser:example.com'), 10);
expect(room.ownPowerLevel, 100); expect(room.ownPowerLevel, 100);
expect(room.canBan, true); expect(room.canBan, true);
@ -375,7 +381,7 @@ void main() {
expect(room.canSendEvent('m.room.power_levels'), true); expect(room.canSendEvent('m.room.power_levels'), true);
expect(room.canSendEvent('m.room.member'), true); expect(room.canSendEvent('m.room.member'), true);
expect(room.powerLevels, expect(room.powerLevels,
room.getState('m.room.power_levels').content['users']); room.getState('m.room.power_levels')?.content['users']);
room.setState( room.setState(
Event( Event(
@ -396,6 +402,7 @@ void main() {
'users': {}, 'users': {},
'users_default': 0 'users_default': 0
}, },
originServerTs: DateTime.now(),
stateKey: '', stateKey: '',
), ),
); );
@ -447,12 +454,12 @@ void main() {
}); });
test('getUserByMXID', () async { test('getUserByMXID', () async {
User user; User? user;
try { try {
user = await room.requestUser('@getme:example.com'); user = await room.requestUser('@getme:example.com');
} catch (_) {} } catch (_) {}
expect(user.stateKey, '@getme:example.com'); expect(user?.stateKey, '@getme:example.com');
expect(user.calcDisplayname(), 'Getme'); expect(user?.calcDisplayname(), 'Getme');
}); });
test('setAvatar', () async { test('setAvatar', () async {
@ -465,14 +472,14 @@ void main() {
final dynamic resp = await room.sendEvent( final dynamic resp = await room.sendEvent(
{'msgtype': 'm.text', 'body': 'hello world'}, {'msgtype': 'm.text', 'body': 'hello world'},
txid: 'testtxid'); txid: 'testtxid');
expect(resp.startsWith('\$event'), true); expect(resp?.startsWith('\$event'), true);
}); });
test('sendEvent', () async { test('sendEvent', () async {
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
final dynamic resp = final dynamic resp =
await room.sendTextEvent('Hello world', txid: 'testtxid'); await room.sendTextEvent('Hello world', txid: 'testtxid');
expect(resp.startsWith('\$event'), true); expect(resp?.startsWith('\$event'), true);
final entry = FakeMatrixApi.calledEndpoints.entries final entry = FakeMatrixApi.calledEndpoints.entries
.firstWhere((p) => p.key.contains('/send/m.room.message/')); .firstWhere((p) => p.key.contains('/send/m.room.message/'));
final content = json.decode(entry.value.first); final content = json.decode(entry.value.first);
@ -486,7 +493,7 @@ void main() {
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
final dynamic resp = await room.sendTextEvent('Hello world', final dynamic resp = await room.sendTextEvent('Hello world',
txid: 'testtxid', editEventId: '\$otherEvent'); txid: 'testtxid', editEventId: '\$otherEvent');
expect(resp.startsWith('\$event'), true); expect(resp?.startsWith('\$event'), true);
final entry = FakeMatrixApi.calledEndpoints.entries final entry = FakeMatrixApi.calledEndpoints.entries
.firstWhere((p) => p.key.contains('/send/m.room.message/')); .firstWhere((p) => p.key.contains('/send/m.room.message/'));
final content = json.decode(entry.value.first); final content = json.decode(entry.value.first);
@ -517,7 +524,7 @@ void main() {
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
var resp = await room.sendTextEvent('Hello world', var resp = await room.sendTextEvent('Hello world',
txid: 'testtxid', inReplyTo: event); txid: 'testtxid', inReplyTo: event);
expect(resp.startsWith('\$event'), true); expect(resp?.startsWith('\$event'), true);
var entry = FakeMatrixApi.calledEndpoints.entries var entry = FakeMatrixApi.calledEndpoints.entries
.firstWhere((p) => p.key.contains('/send/m.room.message/')); .firstWhere((p) => p.key.contains('/send/m.room.message/'));
var content = json.decode(entry.value.first); var content = json.decode(entry.value.first);
@ -546,7 +553,7 @@ void main() {
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
resp = await room.sendTextEvent('Hello world\nfox', resp = await room.sendTextEvent('Hello world\nfox',
txid: 'testtxid', inReplyTo: event); txid: 'testtxid', inReplyTo: event);
expect(resp.startsWith('\$event'), true); expect(resp?.startsWith('\$event'), true);
entry = FakeMatrixApi.calledEndpoints.entries entry = FakeMatrixApi.calledEndpoints.entries
.firstWhere((p) => p.key.contains('/send/m.room.message/')); .firstWhere((p) => p.key.contains('/send/m.room.message/'));
content = json.decode(entry.value.first); content = json.decode(entry.value.first);
@ -578,7 +585,7 @@ void main() {
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
resp = await room.sendTextEvent('Hello world', resp = await room.sendTextEvent('Hello world',
txid: 'testtxid', inReplyTo: event); txid: 'testtxid', inReplyTo: event);
expect(resp.startsWith('\$event'), true); expect(resp?.startsWith('\$event'), true);
entry = FakeMatrixApi.calledEndpoints.entries entry = FakeMatrixApi.calledEndpoints.entries
.firstWhere((p) => p.key.contains('/send/m.room.message/')); .firstWhere((p) => p.key.contains('/send/m.room.message/'));
content = json.decode(entry.value.first); content = json.decode(entry.value.first);
@ -607,7 +614,7 @@ void main() {
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
resp = await room.sendTextEvent('Hello world', resp = await room.sendTextEvent('Hello world',
txid: 'testtxid', inReplyTo: event); txid: 'testtxid', inReplyTo: event);
expect(resp.startsWith('\$event'), true); expect(resp?.startsWith('\$event'), true);
entry = FakeMatrixApi.calledEndpoints.entries entry = FakeMatrixApi.calledEndpoints.entries
.firstWhere((p) => p.key.contains('/send/m.room.message/')); .firstWhere((p) => p.key.contains('/send/m.room.message/'));
content = json.decode(entry.value.first); content = json.decode(entry.value.first);
@ -629,7 +636,7 @@ void main() {
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
final dynamic resp = final dynamic resp =
await room.sendReaction('\$otherEvent', '🦊', txid: 'testtxid'); await room.sendReaction('\$otherEvent', '🦊', txid: 'testtxid');
expect(resp.startsWith('\$event'), true); expect(resp?.startsWith('\$event'), true);
final entry = FakeMatrixApi.calledEndpoints.entries final entry = FakeMatrixApi.calledEndpoints.entries
.firstWhere((p) => p.key.contains('/send/m.reaction/')); .firstWhere((p) => p.key.contains('/send/m.reaction/'));
final content = json.decode(entry.value.first); final content = json.decode(entry.value.first);
@ -649,7 +656,7 @@ void main() {
final geoUri = 'geo:0.0,0.0'; final geoUri = 'geo:0.0,0.0';
final dynamic resp = final dynamic resp =
await room.sendLocation(body, geoUri, txid: 'testtxid'); await room.sendLocation(body, geoUri, txid: 'testtxid');
expect(resp.startsWith('\$event'), true); expect(resp?.startsWith('\$event'), true);
final entry = FakeMatrixApi.calledEndpoints.entries final entry = FakeMatrixApi.calledEndpoints.entries
.firstWhere((p) => p.key.contains('/send/m.room.message/')); .firstWhere((p) => p.key.contains('/send/m.room.message/'));
@ -677,8 +684,8 @@ void main() {
test('pushRuleState', () async { test('pushRuleState', () async {
expect(room.pushRuleState, PushRuleState.mentionsOnly); expect(room.pushRuleState, PushRuleState.mentionsOnly);
matrix.accountData['m.push_rules'].content['global']['override'] matrix.accountData['m.push_rules']?.content['global']['override'].add(
.add(matrix.accountData['m.push_rules'].content['global']['room'][0]); matrix.accountData['m.push_rules']?.content['global']['room'][0]);
expect(room.pushRuleState, PushRuleState.dontNotify); expect(room.pushRuleState, PushRuleState.dontNotify);
}); });
@ -749,7 +756,7 @@ void main() {
'type': 'm.tag' 'type': 'm.tag'
}); });
expect(room.tags.length, 1); expect(room.tags.length, 1);
expect(room.tags[TagType.favourite].order, 0.1); expect(room.tags[TagType.favourite]?.order, 0.1);
expect(room.isFavourite, true); expect(room.isFavourite, true);
await room.setFavourite(false); await room.setFavourite(false);
}); });

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2020 Famedly GmbH * Copyright (C) 2020 Famedly GmbH
@ -119,6 +118,7 @@ const updates = {
'events': [ 'events': [
{ {
'type': 'beep', 'type': 'beep',
'sender': '@example:localhost',
'content': { 'content': {
'blah': 'blubb', 'blah': 'blubb',
}, },

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2019, 2020 Famedly GmbH * Copyright (C) 2019, 2020 Famedly GmbH
@ -37,9 +36,9 @@ void main() {
final insertList = <int>[]; final insertList = <int>[];
var olmEnabled = true; var olmEnabled = true;
Client client; late Client client;
Room room; late Room room;
Timeline timeline; late Timeline timeline;
test('create stuff', () async { test('create stuff', () async {
try { try {
await olm.init(); await olm.init();
@ -233,18 +232,18 @@ void main() {
test('getEventById', () async { test('getEventById', () async {
var event = await timeline.getEventById('abc'); var event = await timeline.getEventById('abc');
expect(event.content, {'msgtype': 'm.text', 'body': 'Testcase'}); expect(event?.content, {'msgtype': 'm.text', 'body': 'Testcase'});
event = await timeline.getEventById('not_found'); event = await timeline.getEventById('not_found');
expect(event, null); expect(event, null);
event = await timeline.getEventById('unencrypted_event'); event = await timeline.getEventById('unencrypted_event');
expect(event.body, 'This is an example text message'); expect(event?.body, 'This is an example text message');
if (olmEnabled) { if (olmEnabled) {
event = await timeline.getEventById('encrypted_event'); event = await timeline.getEventById('encrypted_event');
// the event is invalid but should have traces of attempting to decrypt // the event is invalid but should have traces of attempting to decrypt
expect(event.messageType, MessageTypes.BadEncrypted); expect(event?.messageType, MessageTypes.BadEncrypted);
} }
}); });

View File

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

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2019, 2020 Famedly GmbH * Copyright (C) 2019, 2020 Famedly GmbH
@ -123,6 +122,7 @@ void main() {
}); });
test('getPresence', () async { test('getPresence', () async {
await client.handleSync(SyncUpdate.fromJson({ await client.handleSync(SyncUpdate.fromJson({
'next_batch': 'fake',
'presence': { 'presence': {
'events': [ 'events': [
{ {
@ -133,7 +133,7 @@ void main() {
] ]
} }
})); }));
expect(user1.presence.presence.presence, PresenceType.online); expect(user1.presence?.presence.presence, PresenceType.online);
}); });
test('canBan', () async { test('canBan', () async {
expect(user1.canBan, false); expect(user1.canBan, false);

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2019, 2020, 2021 Famedly GmbH * Copyright (C) 2019, 2020, 2021 Famedly GmbH
@ -30,7 +29,7 @@ const String testMessage5 = 'Hello earth';
const String testMessage6 = 'Hello mars'; const String testMessage6 = 'Hello mars';
void test() async { void test() async {
Client testClientA, testClientB; Client? testClientA, testClientB;
try { try {
await olm.init(); await olm.init();
@ -56,7 +55,7 @@ void test() async {
Logs().i('++++ (Alice) Leave all rooms ++++'); Logs().i('++++ (Alice) Leave all rooms ++++');
while (testClientA.rooms.isNotEmpty) { while (testClientA.rooms.isNotEmpty) {
final room = testClientA.rooms.first; final room = testClientA.rooms.first;
if (room.canonicalAlias?.isNotEmpty ?? false) { if (room.canonicalAlias.isNotEmpty) {
break; break;
} }
try { try {
@ -78,29 +77,28 @@ void test() async {
Logs().i('++++ Check if own olm device is verified by default ++++'); Logs().i('++++ Check if own olm device is verified by default ++++');
assert(testClientA.userDeviceKeys.containsKey(TestUser.username)); assert(testClientA.userDeviceKeys.containsKey(TestUser.username));
assert(testClientA.userDeviceKeys[TestUser.username].deviceKeys assert(testClientA.userDeviceKeys[TestUser.username]!.deviceKeys
.containsKey(testClientA.deviceID)); .containsKey(testClientA.deviceID));
assert(testClientA.userDeviceKeys[TestUser.username] assert(testClientA.userDeviceKeys[TestUser.username]!
.deviceKeys[testClientA.deviceID].verified); .deviceKeys[testClientA.deviceID!]!.verified);
assert(!testClientA.userDeviceKeys[TestUser.username] assert(!testClientA.userDeviceKeys[TestUser.username]!
.deviceKeys[testClientA.deviceID].blocked); .deviceKeys[testClientA.deviceID!]!.blocked);
assert(testClientB.userDeviceKeys.containsKey(TestUser.username2)); assert(testClientB.userDeviceKeys.containsKey(TestUser.username2));
assert(testClientB.userDeviceKeys[TestUser.username2].deviceKeys assert(testClientB.userDeviceKeys[TestUser.username2]!.deviceKeys
.containsKey(testClientB.deviceID)); .containsKey(testClientB.deviceID));
assert(testClientB.userDeviceKeys[TestUser.username2] assert(testClientB.userDeviceKeys[TestUser.username2]!
.deviceKeys[testClientB.deviceID].verified); .deviceKeys[testClientB.deviceID!]!.verified);
assert(!testClientB.userDeviceKeys[TestUser.username2] assert(!testClientB.userDeviceKeys[TestUser.username2]!
.deviceKeys[testClientB.deviceID].blocked); .deviceKeys[testClientB.deviceID!]!.blocked);
Logs().i('++++ (Alice) Create room and invite Bob ++++'); Logs().i('++++ (Alice) Create room and invite Bob ++++');
await testClientA.createRoom(invite: [TestUser.username2]); await testClientA.createRoom(invite: [TestUser.username2]);
await Future.delayed(Duration(seconds: 1)); await Future.delayed(Duration(seconds: 1));
final room = testClientA.rooms.first; final room = testClientA.rooms.first;
assert(room != null);
final roomId = room.id; final roomId = room.id;
Logs().i('++++ (Bob) Join room ++++'); Logs().i('++++ (Bob) Join room ++++');
final inviteRoom = testClientB.getRoomById(roomId); final inviteRoom = testClientB.getRoomById(roomId)!;
await inviteRoom.join(); await inviteRoom.join();
await Future.delayed(Duration(seconds: 1)); await Future.delayed(Duration(seconds: 1));
assert(inviteRoom.membership == Membership.join); assert(inviteRoom.membership == Membership.join);
@ -110,116 +108,118 @@ void test() async {
await room.enableEncryption(); await room.enableEncryption();
await Future.delayed(Duration(seconds: 5)); await Future.delayed(Duration(seconds: 5));
assert(room.encrypted == true); assert(room.encrypted == true);
assert(room.client.encryption.keyManager.getOutboundGroupSession(room.id) == assert(
null); room.client.encryption!.keyManager.getOutboundGroupSession(room.id) ==
null);
Logs().i('++++ (Alice) Check known olm devices ++++'); Logs().i('++++ (Alice) Check known olm devices ++++');
assert(testClientA.userDeviceKeys.containsKey(TestUser.username2)); assert(testClientA.userDeviceKeys.containsKey(TestUser.username2));
assert(testClientA.userDeviceKeys[TestUser.username2].deviceKeys assert(testClientA.userDeviceKeys[TestUser.username2]!.deviceKeys
.containsKey(testClientB.deviceID)); .containsKey(testClientB.deviceID));
assert(!testClientA.userDeviceKeys[TestUser.username2] assert(!testClientA.userDeviceKeys[TestUser.username2]!
.deviceKeys[testClientB.deviceID].verified); .deviceKeys[testClientB.deviceID!]!.verified);
assert(!testClientA.userDeviceKeys[TestUser.username2] assert(!testClientA.userDeviceKeys[TestUser.username2]!
.deviceKeys[testClientB.deviceID].blocked); .deviceKeys[testClientB.deviceID!]!.blocked);
assert(testClientB.userDeviceKeys.containsKey(TestUser.username)); assert(testClientB.userDeviceKeys.containsKey(TestUser.username));
assert(testClientB.userDeviceKeys[TestUser.username].deviceKeys assert(testClientB.userDeviceKeys[TestUser.username]!.deviceKeys
.containsKey(testClientA.deviceID)); .containsKey(testClientA.deviceID));
assert(!testClientB.userDeviceKeys[TestUser.username] assert(!testClientB.userDeviceKeys[TestUser.username]!
.deviceKeys[testClientA.deviceID].verified); .deviceKeys[testClientA.deviceID!]!.verified);
assert(!testClientB.userDeviceKeys[TestUser.username] assert(!testClientB.userDeviceKeys[TestUser.username]!
.deviceKeys[testClientA.deviceID].blocked); .deviceKeys[testClientA.deviceID!]!.blocked);
await testClientA await testClientA
.userDeviceKeys[TestUser.username2].deviceKeys[testClientB.deviceID] .userDeviceKeys[TestUser.username2]!.deviceKeys[testClientB.deviceID!]!
.setVerified(true); .setVerified(true);
Logs().i('++++ Check if own olm device is verified by default ++++'); Logs().i('++++ Check if own olm device is verified by default ++++');
assert(testClientA.userDeviceKeys.containsKey(TestUser.username)); assert(testClientA.userDeviceKeys.containsKey(TestUser.username));
assert(testClientA.userDeviceKeys[TestUser.username].deviceKeys assert(testClientA.userDeviceKeys[TestUser.username]!.deviceKeys
.containsKey(testClientA.deviceID)); .containsKey(testClientA.deviceID));
assert(testClientA.userDeviceKeys[TestUser.username] assert(testClientA.userDeviceKeys[TestUser.username]!
.deviceKeys[testClientA.deviceID].verified); .deviceKeys[testClientA.deviceID!]!.verified);
assert(testClientB.userDeviceKeys.containsKey(TestUser.username2)); assert(testClientB.userDeviceKeys.containsKey(TestUser.username2));
assert(testClientB.userDeviceKeys[TestUser.username2].deviceKeys assert(testClientB.userDeviceKeys[TestUser.username2]!.deviceKeys
.containsKey(testClientB.deviceID)); .containsKey(testClientB.deviceID));
assert(testClientB.userDeviceKeys[TestUser.username2] assert(testClientB.userDeviceKeys[TestUser.username2]!
.deviceKeys[testClientB.deviceID].verified); .deviceKeys[testClientB.deviceID!]!.verified);
Logs().i("++++ (Alice) Send encrypted message: '$testMessage' ++++"); Logs().i("++++ (Alice) Send encrypted message: '$testMessage' ++++");
await room.sendTextEvent(testMessage); await room.sendTextEvent(testMessage);
await Future.delayed(Duration(seconds: 5)); await Future.delayed(Duration(seconds: 5));
assert(room.client.encryption.keyManager.getOutboundGroupSession(room.id) != assert(
null); room.client.encryption!.keyManager.getOutboundGroupSession(room.id) !=
var currentSessionIdA = room.client.encryption.keyManager null);
.getOutboundGroupSession(room.id) var currentSessionIdA = room.client.encryption!.keyManager
.outboundGroupSession .getOutboundGroupSession(room.id)!
.outboundGroupSession!
.session_id(); .session_id();
/*assert(room.client.encryption.keyManager /*assert(room.client.encryption.keyManager
.getInboundGroupSession(room.id, currentSessionIdA, '') != .getInboundGroupSession(room.id, currentSessionIdA, '') !=
null);*/ null);*/
assert(testClientA.encryption.olmManager assert(testClientA.encryption!.olmManager
.olmSessions[testClientB.identityKey].length == .olmSessions[testClientB.identityKey]!.length ==
1); 1);
assert(testClientB.encryption.olmManager assert(testClientB.encryption!.olmManager
.olmSessions[testClientA.identityKey].length == .olmSessions[testClientA.identityKey]!.length ==
1); 1);
assert(testClientA.encryption.olmManager assert(testClientA.encryption!.olmManager
.olmSessions[testClientB.identityKey].first.sessionId == .olmSessions[testClientB.identityKey]!.first.sessionId ==
testClientB.encryption.olmManager.olmSessions[testClientA.identityKey] testClientB.encryption!.olmManager.olmSessions[testClientA.identityKey]!
.first.sessionId); .first.sessionId);
/*assert(inviteRoom.client.encryption.keyManager /*assert(inviteRoom.client.encryption.keyManager
.getInboundGroupSession(inviteRoom.id, currentSessionIdA, '') != .getInboundGroupSession(inviteRoom.id, currentSessionIdA, '') !=
null);*/ null);*/
assert(room.lastEvent.body == testMessage); assert(room.lastEvent!.body == testMessage);
assert(inviteRoom.lastEvent.body == testMessage); assert(inviteRoom.lastEvent!.body == testMessage);
Logs().i( Logs().i(
"++++ (Bob) Received decrypted message: '${inviteRoom.lastEvent.body}' ++++"); "++++ (Bob) Received decrypted message: '${inviteRoom.lastEvent!.body}' ++++");
Logs().i("++++ (Alice) Send again encrypted message: '$testMessage2' ++++"); Logs().i("++++ (Alice) Send again encrypted message: '$testMessage2' ++++");
await room.sendTextEvent(testMessage2); await room.sendTextEvent(testMessage2);
await Future.delayed(Duration(seconds: 5)); await Future.delayed(Duration(seconds: 5));
assert(testClientA.encryption.olmManager assert(testClientA.encryption!.olmManager
.olmSessions[testClientB.identityKey].length == .olmSessions[testClientB.identityKey]!.length ==
1); 1);
assert(testClientB.encryption.olmManager assert(testClientB.encryption!.olmManager
.olmSessions[testClientA.identityKey].length == .olmSessions[testClientA.identityKey]!.length ==
1); 1);
assert(testClientA.encryption.olmManager assert(testClientA.encryption!.olmManager
.olmSessions[testClientB.identityKey].first.sessionId == .olmSessions[testClientB.identityKey]!.first.sessionId ==
testClientB.encryption.olmManager.olmSessions[testClientA.identityKey] testClientB.encryption!.olmManager.olmSessions[testClientA.identityKey]!
.first.sessionId); .first.sessionId);
assert(room.client.encryption.keyManager assert(room.client.encryption!.keyManager
.getOutboundGroupSession(room.id) .getOutboundGroupSession(room.id)!
.outboundGroupSession .outboundGroupSession!
.session_id() == .session_id() ==
currentSessionIdA); currentSessionIdA);
/*assert(room.client.encryption.keyManager /*assert(room.client.encryption.keyManager
.getInboundGroupSession(room.id, currentSessionIdA, '') != .getInboundGroupSession(room.id, currentSessionIdA, '') !=
null);*/ null);*/
assert(room.lastEvent.body == testMessage2); assert(room.lastEvent!.body == testMessage2);
assert(inviteRoom.lastEvent.body == testMessage2); assert(inviteRoom.lastEvent!.body == testMessage2);
Logs().i( Logs().i(
"++++ (Bob) Received decrypted message: '${inviteRoom.lastEvent.body}' ++++"); "++++ (Bob) Received decrypted message: '${inviteRoom.lastEvent!.body}' ++++");
Logs().i("++++ (Bob) Send again encrypted message: '$testMessage3' ++++"); Logs().i("++++ (Bob) Send again encrypted message: '$testMessage3' ++++");
await inviteRoom.sendTextEvent(testMessage3); await inviteRoom.sendTextEvent(testMessage3);
await Future.delayed(Duration(seconds: 5)); await Future.delayed(Duration(seconds: 5));
assert(testClientA.encryption.olmManager assert(testClientA.encryption!.olmManager
.olmSessions[testClientB.identityKey].length == .olmSessions[testClientB.identityKey]!.length ==
1); 1);
assert(testClientB.encryption.olmManager assert(testClientB.encryption!.olmManager
.olmSessions[testClientA.identityKey].length == .olmSessions[testClientA.identityKey]!.length ==
1); 1);
assert(room.client.encryption.keyManager assert(room.client.encryption!.keyManager
.getOutboundGroupSession(room.id) .getOutboundGroupSession(room.id)!
.outboundGroupSession .outboundGroupSession!
.session_id() == .session_id() ==
currentSessionIdA); currentSessionIdA);
final inviteRoomOutboundGroupSession = inviteRoom final inviteRoomOutboundGroupSession = inviteRoom
.client.encryption.keyManager .client.encryption!.keyManager
.getOutboundGroupSession(inviteRoom.id); .getOutboundGroupSession(inviteRoom.id)!;
assert(inviteRoomOutboundGroupSession != null); assert(inviteRoomOutboundGroupSession.isValid);
/*assert(inviteRoom.client.encryption.keyManager.getInboundGroupSession( /*assert(inviteRoom.client.encryption.keyManager.getInboundGroupSession(
inviteRoom.id, inviteRoom.id,
inviteRoomOutboundGroupSession.outboundGroupSession.session_id(), inviteRoomOutboundGroupSession.outboundGroupSession.session_id(),
@ -230,13 +230,13 @@ void test() async {
inviteRoomOutboundGroupSession.outboundGroupSession.session_id(), inviteRoomOutboundGroupSession.outboundGroupSession.session_id(),
'') != '') !=
null);*/ null);*/
assert(inviteRoom.lastEvent.body == testMessage3); assert(inviteRoom.lastEvent!.body == testMessage3);
assert(room.lastEvent.body == testMessage3); assert(room.lastEvent!.body == testMessage3);
Logs().i( Logs().i(
"++++ (Alice) Received decrypted message: '${room.lastEvent.body}' ++++"); "++++ (Alice) Received decrypted message: '${room.lastEvent!.body}' ++++");
Logs().i('++++ Login Bob in another client ++++'); Logs().i('++++ Login Bob in another client ++++');
var testClientC = Client('TestClientC', databaseBuilder: getDatabase); final testClientC = Client('TestClientC', databaseBuilder: getDatabase);
await testClientC.checkHomeserver(TestUser.homeserver); await testClientC.checkHomeserver(TestUser.homeserver);
await testClientC.login(LoginType.mLoginPassword, await testClientC.login(LoginType.mLoginPassword,
identifier: AuthenticationUserIdentifier(user: TestUser.username2), identifier: AuthenticationUserIdentifier(user: TestUser.username2),
@ -246,78 +246,77 @@ void test() async {
Logs().i("++++ (Alice) Send again encrypted message: '$testMessage4' ++++"); Logs().i("++++ (Alice) Send again encrypted message: '$testMessage4' ++++");
await room.sendTextEvent(testMessage4); await room.sendTextEvent(testMessage4);
await Future.delayed(Duration(seconds: 5)); await Future.delayed(Duration(seconds: 5));
assert(testClientA.encryption.olmManager assert(testClientA.encryption!.olmManager
.olmSessions[testClientB.identityKey].length == .olmSessions[testClientB.identityKey]!.length ==
1); 1);
assert(testClientB.encryption.olmManager assert(testClientB.encryption!.olmManager
.olmSessions[testClientA.identityKey].length == .olmSessions[testClientA.identityKey]!.length ==
1); 1);
assert(testClientA.encryption.olmManager assert(testClientA.encryption!.olmManager
.olmSessions[testClientB.identityKey].first.sessionId == .olmSessions[testClientB.identityKey]!.first.sessionId ==
testClientB.encryption.olmManager.olmSessions[testClientA.identityKey] testClientB.encryption!.olmManager.olmSessions[testClientA.identityKey]!
.first.sessionId); .first.sessionId);
assert(testClientA.encryption.olmManager assert(testClientA.encryption!.olmManager
.olmSessions[testClientC.identityKey].length == .olmSessions[testClientC.identityKey]!.length ==
1); 1);
assert(testClientC.encryption.olmManager assert(testClientC.encryption!.olmManager
.olmSessions[testClientA.identityKey].length == .olmSessions[testClientA.identityKey]!.length ==
1); 1);
assert(testClientA.encryption.olmManager assert(testClientA.encryption!.olmManager
.olmSessions[testClientC.identityKey].first.sessionId == .olmSessions[testClientC.identityKey]!.first.sessionId ==
testClientC.encryption.olmManager.olmSessions[testClientA.identityKey] testClientC.encryption!.olmManager.olmSessions[testClientA.identityKey]!
.first.sessionId); .first.sessionId);
assert(room.client.encryption.keyManager assert(room.client.encryption!.keyManager
.getOutboundGroupSession(room.id) .getOutboundGroupSession(room.id)!
.outboundGroupSession .outboundGroupSession!
.session_id() != .session_id() !=
currentSessionIdA); currentSessionIdA);
currentSessionIdA = room.client.encryption.keyManager currentSessionIdA = room.client.encryption!.keyManager
.getOutboundGroupSession(room.id) .getOutboundGroupSession(room.id)!
.outboundGroupSession .outboundGroupSession!
.session_id(); .session_id();
/*assert(inviteRoom.client.encryption.keyManager /*assert(inviteRoom.client.encryption.keyManager
.getInboundGroupSession(inviteRoom.id, currentSessionIdA, '') != .getInboundGroupSession(inviteRoom.id, currentSessionIdA, '') !=
null);*/ null);*/
assert(room.lastEvent.body == testMessage4); assert(room.lastEvent!.body == testMessage4);
assert(inviteRoom.lastEvent.body == testMessage4); assert(inviteRoom.lastEvent!.body == testMessage4);
Logs().i( Logs().i(
"++++ (Bob) Received decrypted message: '${inviteRoom.lastEvent.body}' ++++"); "++++ (Bob) Received decrypted message: '${inviteRoom.lastEvent!.body}' ++++");
Logs().i('++++ Logout Bob another client ++++'); Logs().i('++++ Logout Bob another client ++++');
await testClientC.dispose(closeDatabase: false); await testClientC.dispose(closeDatabase: false);
await testClientC.logout(); await testClientC.logout();
testClientC = null;
await Future.delayed(Duration(seconds: 5)); await Future.delayed(Duration(seconds: 5));
Logs().i("++++ (Alice) Send again encrypted message: '$testMessage6' ++++"); Logs().i("++++ (Alice) Send again encrypted message: '$testMessage6' ++++");
await room.sendTextEvent(testMessage6); await room.sendTextEvent(testMessage6);
await Future.delayed(Duration(seconds: 5)); await Future.delayed(Duration(seconds: 5));
assert(testClientA.encryption.olmManager assert(testClientA.encryption!.olmManager
.olmSessions[testClientB.identityKey].length == .olmSessions[testClientB.identityKey]!.length ==
1); 1);
assert(testClientB.encryption.olmManager assert(testClientB.encryption!.olmManager
.olmSessions[testClientA.identityKey].length == .olmSessions[testClientA.identityKey]!.length ==
1); 1);
assert(testClientA.encryption.olmManager assert(testClientA.encryption!.olmManager
.olmSessions[testClientB.identityKey].first.sessionId == .olmSessions[testClientB.identityKey]!.first.sessionId ==
testClientB.encryption.olmManager.olmSessions[testClientA.identityKey] testClientB.encryption!.olmManager.olmSessions[testClientA.identityKey]!
.first.sessionId); .first.sessionId);
assert(room.client.encryption.keyManager assert(room.client.encryption!.keyManager
.getOutboundGroupSession(room.id) .getOutboundGroupSession(room.id)!
.outboundGroupSession .outboundGroupSession!
.session_id() != .session_id() !=
currentSessionIdA); currentSessionIdA);
currentSessionIdA = room.client.encryption.keyManager currentSessionIdA = room.client.encryption!.keyManager
.getOutboundGroupSession(room.id) .getOutboundGroupSession(room.id)!
.outboundGroupSession .outboundGroupSession!
.session_id(); .session_id();
/*assert(inviteRoom.client.encryption.keyManager /*assert(inviteRoom.client.encryption.keyManager
.getInboundGroupSession(inviteRoom.id, currentSessionIdA, '') != .getInboundGroupSession(inviteRoom.id, currentSessionIdA, '') !=
null);*/ null);*/
assert(room.lastEvent.body == testMessage6); assert(room.lastEvent!.body == testMessage6);
assert(inviteRoom.lastEvent.body == testMessage6); assert(inviteRoom.lastEvent!.body == testMessage6);
Logs().i( Logs().i(
"++++ (Bob) Received decrypted message: '${inviteRoom.lastEvent.body}' ++++"); "++++ (Bob) Received decrypted message: '${inviteRoom.lastEvent!.body}' ++++");
await room.leave(); await room.leave();
await room.forget(); await room.forget();
@ -329,8 +328,8 @@ void test() async {
rethrow; rethrow;
} finally { } finally {
Logs().i('++++ Logout Alice and Bob ++++'); Logs().i('++++ Logout Alice and Bob ++++');
if (testClientA?.isLogged() ?? false) await testClientA.logoutAll(); if (testClientA?.isLogged() ?? false) await testClientA!.logoutAll();
if (testClientA?.isLogged() ?? false) await testClientB.logoutAll(); if (testClientA?.isLogged() ?? false) await testClientB!.logoutAll();
await testClientA?.dispose(closeDatabase: false); await testClientA?.dispose(closeDatabase: false);
await testClientB?.dispose(closeDatabase: false); await testClientB?.dispose(closeDatabase: false);
testClientA = null; testClientA = null;