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:
errors:
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:
- example/main.dart
# needed until crypto packages upgrade

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,3 @@
// @dart=2.9
/*
* Famedly Matrix SDK
* 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.
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')
DateTime get time => originServerTs;
@ -48,10 +53,10 @@ class Event extends MatrixEvent {
String get typeKey => type;
@Deprecated('Use [sender.calcDisplayname()] instead')
String get senderName => sender.calcDisplayname();
String? get senderName => sender.calcDisplayname();
/// The room this event belongs to. May be null.
final Room room;
final Room? room;
/// The status of this event.
EventStatus status;
@ -59,33 +64,39 @@ class Event extends MatrixEvent {
static const EventStatus defaultStatus = EventStatus.synced;
/// Optional. The event that redacted this event, if any. Otherwise null.
Event get redactedBecause =>
unsigned != null && unsigned['redacted_because'] is Map
? Event.fromJson(unsigned['redacted_because'], room)
: null;
Event? get redactedBecause {
final redacted_because = unsigned?['redacted_because'];
final room = this.room;
return (redacted_because is Map<String, dynamic>)
? Event.fromJson(redacted_because, room)
: null;
}
bool get redacted => redactedBecause != null;
User get stateKeyUser => room.getUserByMXIDSync(stateKey);
User? get stateKeyUser => room?.getUserByMXIDSync(stateKey!);
Event({
this.status = defaultStatus,
Map<String, dynamic> content,
String type,
String eventId,
String roomId,
String senderId,
DateTime originServerTs,
Map<String, dynamic> unsigned,
Map<String, dynamic> prevContent,
String stateKey,
required Map<String, dynamic> content,
required String type,
required String eventId,
String? roomId,
required String senderId,
required DateTime originServerTs,
Map<String, dynamic>? unsigned,
Map<String, dynamic>? prevContent,
String? stateKey,
this.room,
}) {
this.content = content;
this.type = type;
}) : super(
content: content,
type: type,
eventId: eventId,
senderId: senderId,
originServerTs: originServerTs,
roomId: roomId ?? room?.id,
) {
this.eventId = eventId;
this.roomId = roomId ?? room?.id;
this.senderId = senderId;
this.unsigned = unsigned;
// synapse unfortunately isn't following the spec and tosses the prev_content
// into the unsigned block.
@ -103,17 +114,17 @@ class Event extends MatrixEvent {
// A strange bug in dart web makes this crash
}
this.stateKey = stateKey;
this.originServerTs = originServerTs;
// 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
// database!
if (status.isSending && room?.client?.database != null) {
if (status.isSending && room?.client.database != null) {
// Age of this event in milliseconds
final age = DateTime.now().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
final json = toJson();
json['unsigned'] ??= <String, dynamic>{};
@ -143,7 +154,7 @@ class Event extends MatrixEvent {
factory Event.fromMatrixEvent(
MatrixEvent matrixEvent,
Room room, {
EventStatus status,
EventStatus status = defaultStatus,
}) =>
Event(
status: status,
@ -162,7 +173,7 @@ class Event extends MatrixEvent {
/// Get a State event from a table row or from the event stream.
factory Event.fromJson(
Map<String, dynamic> jsonPayload,
Room room,
Room? room,
) {
final content = Event.getMapFromPayload(jsonPayload['content']);
final unsigned = Event.getMapFromPayload(jsonPayload['unsigned']);
@ -175,7 +186,7 @@ class Event extends MatrixEvent {
prevContent: prevContent,
content: content,
type: jsonPayload['type'],
eventId: jsonPayload['event_id'],
eventId: jsonPayload['event_id'] ?? '',
roomId: jsonPayload['room_id'],
senderId: jsonPayload['sender'],
originServerTs: jsonPayload.containsKey('origin_server_ts')
@ -190,7 +201,7 @@ class Event extends MatrixEvent {
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
if (stateKey != null) data['state_key'] = stateKey;
if (prevContent != null && prevContent.isNotEmpty) {
if (prevContent?.isNotEmpty == true) {
data['prev_content'] = prevContent;
}
data['content'] = content;
@ -199,14 +210,15 @@ class Event extends MatrixEvent {
data['room_id'] = roomId;
data['sender'] = senderId;
data['origin_server_ts'] = originServerTs.millisecondsSinceEpoch;
if (unsigned != null && unsigned.isNotEmpty) {
if (unsigned?.isNotEmpty == true) {
data['unsigned'] = unsigned;
}
return data;
}
User get asUser => User.fromState(
stateKey: stateKey,
// state key should always be set for member events
stateKey: stateKey!,
prevContent: prevContent,
content: content,
typeKey: type,
@ -282,8 +294,10 @@ class Event extends MatrixEvent {
/// Returns a list of [Receipt] instances for this event.
List<Receipt> get receipts {
if (!(room.roomAccountData.containsKey('m.receipt'))) return [];
return room.roomAccountData['m.receipt'].content.entries
final room = this.room;
final receipt = room?.roomAccountData['m.receipt'];
if (receipt == null || room == null) return [];
return receipt.content.entries
.where((entry) => entry.value['event_id'] == eventId)
.map((entry) => Receipt(room.getUserByMXIDSync(entry.key),
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.
/// Returns [false] if not removed.
Future<bool> remove() async {
final room = this.room;
if (room == null) {
return false;
}
if (!status.isSent) {
await room.client.database?.removeEvent(eventId, room.id);
@ -311,29 +330,33 @@ class Event extends MatrixEvent {
return false;
}
/// Try to send this event again. Only works with events of `EventStatus.isError`.
Future<String> sendAgain({String txid}) async {
/// Try to send this event again. Only works with events of status -1.
Future<String?> sendAgain({String? txid}) async {
if (!status.isError) return null;
// we do not remove the event here. It will automatically be updated
// in the `sendEvent` method to transition -1 -> 0 -> 1 -> 2
final newEventId = await room.sendEvent(
final newEventId = await room?.sendEvent(
content,
txid: txid ?? unsigned['transaction_id'] ?? eventId,
txid: txid ?? unsigned?['transaction_id'] ?? eventId,
);
return newEventId;
}
/// 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.
Future<dynamic> redactEvent({String reason, String txid}) =>
room.redactEvent(eventId, reason: reason, txid: txid);
Future<String?> redactEvent({String? reason, String? txid}) async =>
await room?.redactEvent(eventId, reason: reason, txid: txid);
/// 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;
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
@ -346,7 +369,7 @@ class Event extends MatrixEvent {
content['can_request_session'] != true) {
throw ('Session key not requestable');
}
await room.requestSessionKey(content['session_id'], content['sender_key']);
await room?.requestSessionKey(content['session_id'], content['sender_key']);
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
Uri get attachmentMxcUrl => Uri.parse(
isAttachmentEncrypted ? content['file']['url'] : content['url']);
Uri? get attachmentMxcUrl {
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
Uri get thumbnailMxcUrl => Uri.parse(isThumbnailEncrypted
? infoMap['thumbnail_file']['url']
: infoMap['thumbnail_url']);
Uri? get thumbnailMxcUrl {
final url = isThumbnailEncrypted
? 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
Uri attachmentOrThumbnailMxcUrl({bool getThumbnail = false}) {
Uri? attachmentOrThumbnailMxcUrl({bool getThumbnail = false}) {
if (getThumbnail &&
infoMap['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
/// [useThumbnailMxcUrl] says weather to use the mxc url of the thumbnail, rather than the original attachment.
/// [animated] says weather the thumbnail is animated
Uri getAttachmentUrl(
Uri? getAttachmentUrl(
{bool getThumbnail = false,
bool useThumbnailMxcUrl = false,
double width = 800.0,
@ -428,6 +456,10 @@ class Event extends MatrixEvent {
ThumbnailMethod method = ThumbnailMethod.scale,
int minNoThumbSize = _minNoThumbSize,
bool animated = false}) {
final client = room?.client;
if (client == null) {
return null;
}
if (![EventTypes.Message, EventTypes.Sticker].contains(type) ||
!hasAttachment ||
isAttachmentEncrypted) {
@ -449,14 +481,14 @@ class Event extends MatrixEvent {
// now generate the actual URLs
if (getThumbnail) {
return Uri.parse(thisMxcUrl).getThumbnail(
room.client,
client,
width: width,
height: height,
method: method,
animated: animated,
);
} 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;
// Is this file storeable?
final thisInfoMap = getThumbnail ? thumbnailInfoMap : infoMap;
final storeable = room.client.database != null &&
thisInfoMap['size'] is int &&
thisInfoMap['size'] <= room.client.database.maxFileSize;
final database = room?.client.database;
if (database == null) {
return false;
}
Uint8List uint8list;
final storeable = thisInfoMap['size'] is int &&
thisInfoMap['size'] <= database.maxFileSize;
Uint8List? uint8list;
if (storeable) {
uint8list = await room.client.database.getFile(mxcUrl);
uint8list = await database.getFile(mxcUrl);
}
return uint8list != null;
}
@ -487,12 +523,17 @@ class Event extends MatrixEvent {
/// event and returns it as a [MatrixFile]. If this event doesn't
/// contain an attachment, this throws an error. Set [getThumbnail] to
/// true to download the thumbnail instead.
Future<MatrixFile> downloadAndDecryptAttachment(
Future<MatrixFile?> downloadAndDecryptAttachment(
{bool getThumbnail = false,
Future<Uint8List> Function(Uri) downloadCallback}) async {
Future<Uint8List> Function(Uri)? downloadCallback}) async {
if (![EventTypes.Message, EventTypes.Sticker].contains(type)) {
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);
if (mxcUrl == null) {
throw "This event hasn't any attachment or thumbnail.";
@ -500,33 +541,30 @@ class Event extends MatrixEvent {
getThumbnail = mxcUrl != attachmentMxcUrl;
final isEncrypted =
getThumbnail ? isThumbnailEncrypted : isAttachmentEncrypted;
if (isEncrypted && !room.client.encryptionEnabled) {
if (isEncrypted && !client.encryptionEnabled) {
throw ('Encryption is not enabled in your Client.');
}
Uint8List uint8list;
// Is this file storeable?
final thisInfoMap = getThumbnail ? thumbnailInfoMap : infoMap;
var storeable = room.client.database != null &&
var storeable = database != null &&
thisInfoMap['size'] is int &&
thisInfoMap['size'] <= room.client.database.maxFileSize;
thisInfoMap['size'] <= database.maxFileSize;
Uint8List? uint8list;
if (storeable) {
uint8list = await room.client.database.getFile(mxcUrl);
uint8list = await client.database?.getFile(mxcUrl);
}
// Download the file
if (uint8list == null) {
downloadCallback ??= (Uri url) async {
return (await http.get(url)).bodyBytes;
};
uint8list = await downloadCallback(mxcUrl.getDownloadLink(room.client));
storeable = storeable &&
uint8list.lengthInBytes < room.client.database.maxFileSize;
downloadCallback ??= (Uri url) async => (await http.get(url)).bodyBytes;
uint8list = await downloadCallback(mxcUrl.getDownloadLink(client));
storeable = database != null &&
storeable &&
uint8list.lengthInBytes < database.maxFileSize;
if (storeable) {
await room.client.database.storeFile(
await database.storeFile(
mxcUrl, uint8list, DateTime.now().millisecondsSinceEpoch);
}
}
@ -544,9 +582,10 @@ class Event extends MatrixEvent {
k: fileMap['key']['k'],
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.
@ -565,7 +604,7 @@ class Event extends MatrixEvent {
bool plaintextBody = false,
}) {
if (redacted) {
return i18n.removedBy(redactedBecause.sender.calcDisplayname());
return i18n.removedBy(redactedBecause?.sender.calcDisplayname() ?? '');
}
var body = plaintextBody ? this.plaintextBody : this.body;
@ -607,8 +646,9 @@ class Event extends MatrixEvent {
if (withSenderNamePrefix &&
type == EventTypes.Message &&
textOnlyMessageTypes.contains(messageType)) {
final senderNameOrYou =
senderId == room.client.userID ? i18n.you : sender.calcDisplayname();
final senderNameOrYou = senderId == room?.client.userID
? i18n.you
: (sender.calcDisplayname());
localizedBody = '$senderNameOrYou: $localizedBody';
}
@ -623,19 +663,19 @@ class Event extends MatrixEvent {
};
/// returns if this event matches the passed event or transaction id
bool matchesEventOrTransactionId(String search) {
bool matchesEventOrTransactionId(String? search) {
if (search == null) {
return false;
}
if (eventId == search) {
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
String get relationshipType {
if (content?.tryGet<Map<String, dynamic>>('m.relates_to') == null) {
String? get relationshipType {
if (content.tryGet<Map<String, dynamic>>('m.relates_to') == null) {
return null;
}
if (content['m.relates_to'].containsKey('m.in_reply_to')) {
@ -643,12 +683,12 @@ class Event extends MatrixEvent {
}
return content
.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
String get relationshipEventId {
if (content == null || !(content['m.relates_to'] is Map)) {
String? get relationshipEventId {
if (!(content['m.relates_to'] is Map)) {
return null;
}
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]
/// To be able to do that you need to pass a [timeline]
bool hasAggregatedEvents(Timeline timeline, String type) =>
timeline.aggregatedEvents.containsKey(eventId) &&
timeline.aggregatedEvents[eventId].containsKey(type);
timeline.aggregatedEvents[eventId]?.containsKey(type) == true;
/// Get all the aggregated event objects for a given [type]. To be able to do this
/// you have to pass a [timeline]
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.
/// 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 {
var decryptAtLeastOneEvent = false;
final decryptFn = () async {
if (!room.client.encryptionEnabled) {
final encryption = room.client.encryption;
if (!room.client.encryptionEnabled || encryption == null) {
return;
}
for (var i = 0; i < events.length; i++) {
if (events[i].type == EventTypes.Encrypted &&
events[i].messageType == MessageTypes.BadEncrypted &&
events[i].content['session_id'] == sessionId) {
events[i] = await room.client.encryption
.decryptRoomEvent(room.id, events[i], store: true);
events[i] = await encryption.decryptRoomEvent(room.id, events[i],
store: true);
if (events[i].type != EventTypes.Encrypted) {
decryptAtLeastOneEvent = true;
}
@ -148,7 +149,7 @@ class Timeline {
}
};
if (room.client.database != null) {
await room.client.database.transaction(decryptFn);
await room.client.database?.transaction(decryptFn);
} else {
await decryptFn();
}
@ -162,7 +163,7 @@ class Timeline {
event.messageType == MessageTypes.BadEncrypted &&
event.content['can_request_session'] == true) {
try {
room.client.encryption.keyManager.maybeAutoRequest(room.id,
room.client.encryption?.keyManager.maybeAutoRequest(room.id,
event.content['session_id'], event.content['sender_key']);
} catch (_) {
// dispose
@ -186,13 +187,11 @@ class Timeline {
}
int i;
for (i = 0; i < events.length; i++) {
final searchHaystack = <String>{};
if (events[i].eventId != null) {
searchHaystack.add(events[i].eventId);
}
if (events[i].unsigned != null &&
events[i].unsigned['transaction_id'] != null) {
searchHaystack.add(events[i].unsigned['transaction_id']);
final searchHaystack = <String>{events[i].eventId};
final txnid = events[i].unsigned?['transaction_id'];
if (txnid != null) {
searchHaystack.add(txnid);
}
if (searchNeedle.intersection(searchHaystack).isNotEmpty) {
break;
@ -205,19 +204,18 @@ class Timeline {
eventSet.removeWhere((e) =>
e.matchesEventOrTransactionId(event.eventId) ||
(event.unsigned != null &&
e.matchesEventOrTransactionId(event.unsigned['transaction_id'])));
e.matchesEventOrTransactionId(event.unsigned?['transaction_id'])));
}
void addAggregatedEvent(Event event) {
// 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
}
if (!aggregatedEvents.containsKey(event.relationshipEventId)) {
aggregatedEvents[event.relationshipEventId] = <String, Set<Event>>{};
}
final events = (aggregatedEvents[event.relationshipEventId] ??=
<String, Set<Event>>{})[event.relationshipType] ??= <Event>{};
final events = (aggregatedEvents[relationshipEventId] ??=
<String, Set<Event>>{})[relationshipType] ??= <Event>{};
// remove a potential old event
_removeEventFromSet(events, event);
// add the new one
@ -227,7 +225,7 @@ class Timeline {
void removeAggregatedEvent(Event event) {
aggregatedEvents.remove(event.eventId);
if (event.unsigned != null) {
aggregatedEvents.remove(event.unsigned['transaction_id']);
aggregatedEvents.remove(event.unsigned?['transaction_id']);
}
for (final types in aggregatedEvents.values) {
for (final events in types.values) {

View File

@ -49,9 +49,9 @@ class User extends Event {
required String stateKey,
dynamic content,
required String typeKey,
String? eventId,
String eventId = 'fakevent',
String? roomId,
String? senderId,
String senderId = 'fakesender',
required DateTime originServerTs,
dynamic unsigned,
Room? room})
@ -68,11 +68,11 @@ class User extends Event {
room: room);
/// 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.
String? get displayName =>
content?.tryGet<String>('displayname') ??
content.tryGet<String>('displayname') ??
prevContent?.tryGet<String>('displayname');
/// Returns the power level of this user.
@ -91,13 +91,16 @@ class User extends Event {
}, orElse: () => Membership.join);
/// The avatar if the user has one.
Uri? get avatarUrl => content != null && content.containsKey('avatar_url')
? (content['avatar_url'] is String
? Uri.tryParse(content['avatar_url'])
: null)
: (prevContent != null && prevContent['avatar_url'] is String
? Uri.tryParse(prevContent['avatar_url'])
: null);
Uri? get avatarUrl {
final prevContent = this.prevContent;
return content.containsKey('avatar_url')
? (content['avatar_url'] is String
? Uri.tryParse(content['avatar_url'])
: 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
/// has no displayname. If [formatLocalpart] is true, then the localpart will
@ -109,8 +112,8 @@ class User extends Event {
bool? formatLocalpart,
bool? mxidLocalPartFallback,
}) {
formatLocalpart ??= room?.client?.formatLocalpart ?? true;
mxidLocalPartFallback ??= room?.client?.mxidLocalPartFallback ?? true;
formatLocalpart ??= room?.client.formatLocalpart ?? true;
mxidLocalPartFallback ??= room?.client.mxidLocalPartFallback ?? true;
final displayName = this.displayName;
if (displayName != null && displayName.isNotEmpty) {
return displayName;
@ -132,37 +135,40 @@ class User extends Event {
}
/// 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.
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.
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.
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 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.
Presence? get presence => room.client.presences[id];
Presence? get presence => room?.client.presences[id];
/// 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.
bool get canKick =>
[Membership.join, Membership.invite].contains(membership) &&
room.canKick &&
powerLevel < room.ownPowerLevel;
(room?.canKick ?? false) &&
powerLevel < (room?.ownPowerLevel ?? powerLevel);
/// 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!
bool get canChangePowerLevel =>
room.canChangePowerLevel && powerLevel < room.ownPowerLevel;
(room?.canChangePowerLevel ?? false) &&
powerLevel < (room?.ownPowerLevel ?? powerLevel);
@override
bool operator ==(dynamic other) => (other is User &&
@ -190,7 +196,7 @@ class User extends Event {
: '[$displayName]');
// get all the users with the same display name
final allUsersWithSameDisplayname = room.getParticipants();
final allUsersWithSameDisplayname = room?.getParticipants() ?? [];
allUsersWithSameDisplayname.removeWhere((user) =>
user.id == id ||
(user.displayName?.isEmpty ?? true) ||

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,3 @@
// @dart=2.9
/*
* Famedly Matrix SDK
* Copyright (C) 2019, 2020 Famedly GmbH
@ -39,7 +38,7 @@ Map<String, dynamic> decodeJson(dynamic data) {
class FakeMatrixApi extends MockClient {
static final calledEndpoints = <String, List<dynamic>>{};
static int eventCounter = 0;
static sdk.Client client;
static sdk.Client? client;
static bool failToDevice = false;
FakeMatrixApi()
@ -85,9 +84,10 @@ class FakeMatrixApi extends MockClient {
if (!calledEndpoints.containsKey(action)) {
calledEndpoints[action] = <dynamic>[];
}
calledEndpoints[action].add(data);
if (api.containsKey(method) && api[method].containsKey(action)) {
res = api[method][action](data);
calledEndpoints[action]?.add(data);
final act = api[method]?[action];
if (act != null) {
res = act(data);
if (res is Map && res.containsKey('errcode')) {
if (res['errcode'] == 'M_NOT_FOUND') {
statusCode = 404;
@ -126,12 +126,12 @@ class FakeMatrixApi extends MockClient {
..accountData = [
sdk.BasicEvent(content: decodeJson(data), type: type)
];
if (client.database != null) {
await client.database.transaction(() async {
await client.handleSync(syncUpdate);
if (client?.database != null) {
await client?.database?.transaction(() async {
await client?.handleSync(syncUpdate);
});
} else {
await client.handleSync(syncUpdate);
await client?.handleSync(syncUpdate);
}
res = {};
} else {
@ -2101,15 +2101,15 @@ class FakeMatrixApi extends MockClient {
}) {
if (jsonBody[keyType] != null) {
final key =
sdk.CrossSigningKey.fromJson(jsonBody[keyType], client);
client.userDeviceKeys[client.userID].crossSigningKeys
sdk.CrossSigningKey.fromJson(jsonBody[keyType], client!);
client!.userDeviceKeys[client!.userID!]?.crossSigningKeys
.removeWhere((k, v) => v.usage.contains(key.usage.first));
client.userDeviceKeys[client.userID]
.crossSigningKeys[key.publicKey] = key;
client!.userDeviceKeys[client!.userID!]
?.crossSigningKeys[key.publicKey!] = key;
}
}
// and generate a fake sync
client.handleSync(sdk.SyncUpdate(nextBatch: ''));
client!.handleSync(sdk.SyncUpdate(nextBatch: ''));
}
return {};
},
@ -2124,35 +2124,35 @@ class FakeMatrixApi extends MockClient {
'/client/r0/pushrules/global/content/nocake/enabled': (var req) => {},
'/client/r0/pushrules/global/content/nocake/actions': (var req) => {},
'/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':
(var req) => {},
(var req) => {'event_id': '1234'},
'/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':
(var req) => {},
(var req) => {'event_id': '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':
(var req) => {},
(var req) => {'event_id': '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':
(var req) => {},
(var req) => {'event_id': '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':
(var req) => {},
(var req) => {'event_id': '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':
(var req) => {},
(var req) => {'event_id': '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':
(var req) => {},
(var req) => {'event_id': '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':
(var req) => {'event_id': '1234'},
'/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
* Copyright (C) 2021 Famedly GmbH

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,3 @@
// @dart=2.9
/*
* Famedly Matrix SDK
* Copyright (C) 2019, 2020 Famedly GmbH
@ -37,9 +36,9 @@ void main() {
final insertList = <int>[];
var olmEnabled = true;
Client client;
Room room;
Timeline timeline;
late Client client;
late Room room;
late Timeline timeline;
test('create stuff', () async {
try {
await olm.init();
@ -233,18 +232,18 @@ void main() {
test('getEventById', () async {
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');
expect(event, null);
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) {
event = await timeline.getEventById('encrypted_event');
// 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
* Copyright (C) 2020 Famedly GmbH

View File

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

View File

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