chore: matrix_api_lite 0.4.0

This commit is contained in:
Lukas Lihotzki 2021-06-07 17:18:23 +02:00
parent bc2dac2ecc
commit 8665f092f4
17 changed files with 185 additions and 166 deletions

View File

@ -44,7 +44,8 @@ class KeyManager {
final keyObj = olm.PkDecryption(); final keyObj = olm.PkDecryption();
try { try {
final info = await getRoomKeysBackupInfo(false); final info = await getRoomKeysBackupInfo(false);
if (info.algorithm != RoomKeysAlgorithmType.v1Curve25519AesSha2) { if (info.algorithm !=
BackupAlgorithm.mMegolmBackupV1Curve25519AesSha2) {
return false; return false;
} }
return keyObj.init_with_private_key(base64.decode(secret)) == return keyObj.init_with_private_key(base64.decode(secret)) ==
@ -523,9 +524,9 @@ class KeyManager {
return (await encryption.ssss.getCached(megolmKey)) != null; return (await encryption.ssss.getCached(megolmKey)) != null;
} }
RoomKeysVersionResponse _roomKeysVersionCache; GetRoomKeysVersionCurrentResponse _roomKeysVersionCache;
DateTime _roomKeysVersionCacheDate; DateTime _roomKeysVersionCacheDate;
Future<RoomKeysVersionResponse> getRoomKeysBackupInfo( Future<GetRoomKeysVersionCurrentResponse> getRoomKeysBackupInfo(
[bool useCache = true]) async { [bool useCache = true]) async {
if (_roomKeysVersionCache != null && if (_roomKeysVersionCache != null &&
_roomKeysVersionCacheDate != null && _roomKeysVersionCacheDate != null &&
@ -535,7 +536,7 @@ class KeyManager {
.isBefore(_roomKeysVersionCacheDate)) { .isBefore(_roomKeysVersionCacheDate)) {
return _roomKeysVersionCache; return _roomKeysVersionCache;
} }
_roomKeysVersionCache = await client.getRoomKeysBackup(); _roomKeysVersionCache = await client.getRoomKeysVersionCurrent();
_roomKeysVersionCacheDate = DateTime.now(); _roomKeysVersionCacheDate = DateTime.now();
return _roomKeysVersionCache; return _roomKeysVersionCache;
} }
@ -553,7 +554,7 @@ class KeyManager {
backupPubKey = decryption.init_with_private_key(privateKey); backupPubKey = decryption.init_with_private_key(privateKey);
if (backupPubKey == null || if (backupPubKey == null ||
info.algorithm != RoomKeysAlgorithmType.v1Curve25519AesSha2 || info.algorithm != BackupAlgorithm.mMegolmBackupV1Curve25519AesSha2 ||
info.authData['public_key'] != backupPubKey) { info.authData['public_key'] != backupPubKey) {
return; return;
} }
@ -706,7 +707,8 @@ class KeyManager {
backupPubKey = decryption.init_with_private_key(privateKey); backupPubKey = decryption.init_with_private_key(privateKey);
if (backupPubKey == null || if (backupPubKey == null ||
info.algorithm != RoomKeysAlgorithmType.v1Curve25519AesSha2 || info.algorithm !=
BackupAlgorithm.mMegolmBackupV1Curve25519AesSha2 ||
info.authData['public_key'] != backupPubKey) { info.authData['public_key'] != backupPubKey) {
return; return;
} }
@ -737,7 +739,7 @@ class KeyManager {
_generateUploadKeys, args); _generateUploadKeys, args);
Logs().i('[Key Manager] Uploading ${dbSessions.length} room keys...'); Logs().i('[Key Manager] Uploading ${dbSessions.length} room keys...');
// upload the payload... // upload the payload...
await client.storeRoomKeys(info.version, roomKeys); await client.postRoomKeysKey(info.version, roomKeys);
// and now finally mark all the keys as uploaded // and now finally mark all the keys as uploaded
// no need to optimze this, as we only run it so seldomly and almost never with many keys at once // no need to optimze this, as we only run it so seldomly and almost never with many keys at once
for (final dbSession in dbSessions) { for (final dbSession in dbSessions) {
@ -1011,7 +1013,7 @@ RoomKeys _generateUploadKeys(_GenerateUploadKeysArgs args) {
try { try {
enc.set_recipient_key(args.pubkey); enc.set_recipient_key(args.pubkey);
// first we generate the payload to upload all the session keys in this chunk // first we generate the payload to upload all the session keys in this chunk
final roomKeys = RoomKeys(); final roomKeys = RoomKeys(rooms: {});
for (final dbSession in args.dbSessions) { for (final dbSession in args.dbSessions) {
final sess = SessionKey.fromDb(dbSession.dbSession, args.userId); final sess = SessionKey.fromDb(dbSession.dbSession, args.userId);
if (!sess.isValid) { if (!sess.isValid) {
@ -1019,7 +1021,7 @@ RoomKeys _generateUploadKeys(_GenerateUploadKeysArgs args) {
} }
// create the room if it doesn't exist // create the room if it doesn't exist
if (!roomKeys.rooms.containsKey(sess.roomId)) { if (!roomKeys.rooms.containsKey(sess.roomId)) {
roomKeys.rooms[sess.roomId] = RoomKeysRoom(); roomKeys.rooms[sess.roomId] = RoomKeyBackup(sessions: {});
} }
// generate the encrypted content // generate the encrypted content
final payload = <String, dynamic>{ final payload = <String, dynamic>{
@ -1035,7 +1037,7 @@ RoomKeys _generateUploadKeys(_GenerateUploadKeysArgs args) {
// fetch the device, if available... // fetch the device, if available...
//final device = args.client.getUserDeviceKeysByCurve25519Key(sess.senderKey); //final device = args.client.getUserDeviceKeysByCurve25519Key(sess.senderKey);
// aaaand finally add the session key to our payload // aaaand finally add the session key to our payload
roomKeys.rooms[sess.roomId].sessions[sess.sessionId] = RoomKeysSingleKey( roomKeys.rooms[sess.roomId].sessions[sess.sessionId] = KeyBackupData(
firstMessageIndex: sess.inboundGroupSession.first_known_index(), firstMessageIndex: sess.inboundGroupSession.first_known_index(),
forwardedCount: sess.forwardingCurve25519KeyChain.length, forwardedCount: sess.forwardingCurve25519KeyChain.length,
isVerified: dbSession.verified, //device?.verified ?? false, isVerified: dbSession.verified, //device?.verified ?? false,

View File

@ -23,9 +23,9 @@ import 'package:matrix/matrix.dart';
import 'package:olm/olm.dart' as olm; import 'package:olm/olm.dart' as olm;
import '../encryption/utils/json_signature_check_extension.dart'; import '../encryption/utils/json_signature_check_extension.dart';
import '../src/utils/run_in_root.dart';
import 'encryption.dart'; import 'encryption.dart';
import 'utils/olm_session.dart'; import 'utils/olm_session.dart';
import '../src/utils/run_in_root.dart';
class OlmManager { class OlmManager {
final Encryption encryption; final Encryption encryption;
@ -264,7 +264,7 @@ class OlmManager {
// and generate and upload more if not. // and generate and upload more if not.
// If the server did not send us a count, assume it is 0 // If the server did not send us a count, assume it is 0
final keyCount = countJson?.tryGet<int>('signed_curve25519', 0) ?? 0; final keyCount = countJson?.tryGet<int>('signed_curve25519') ?? 0;
// If the server does not support fallback keys, it will not tell us about them. // If the server does not support fallback keys, it will not tell us about them.
// If the server supports them but has no key, upload a new one. // If the server supports them but has no key, upload a new one.
@ -644,7 +644,7 @@ class OlmManager {
return; return;
} }
final device = client.getUserDeviceKeysByCurve25519Key( final device = client.getUserDeviceKeysByCurve25519Key(
event.encryptedContent.tryGet<String>('sender_key', '')); event.encryptedContent.tryGet<String>('sender_key') ?? '');
if (device == null) { if (device == null) {
return; // device not found return; // device not found
} }

View File

@ -16,10 +16,10 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import 'dart:core';
import 'dart:convert';
import 'dart:typed_data';
import 'dart:async'; import 'dart:async';
import 'dart:convert';
import 'dart:core';
import 'dart:typed_data';
import 'package:base58check/base58.dart'; import 'package:base58check/base58.dart';
import 'package:crypto/crypto.dart'; import 'package:crypto/crypto.dart';
@ -51,11 +51,13 @@ const pbkdf2SaltLength = 64;
/// https://matrix.org/docs/guides/implementing-more-advanced-e-2-ee-features-such-as-cross-signing#3-implementing-ssss /// https://matrix.org/docs/guides/implementing-more-advanced-e-2-ee-features-such-as-cross-signing#3-implementing-ssss
class SSSS { class SSSS {
final Encryption encryption; final Encryption encryption;
Client get client => encryption.client; Client get client => encryption.client;
final pendingShareRequests = <String, _ShareRequest>{}; final pendingShareRequests = <String, _ShareRequest>{};
final _validators = <String, FutureOr<bool> Function(String)>{}; final _validators = <String, FutureOr<bool> Function(String)>{};
final _cacheCallbacks = <String, FutureOr<void> Function(String)>{}; final _cacheCallbacks = <String, FutureOr<void> Function(String)>{};
final Map<String, SSSSCache> _cache = <String, SSSSCache>{}; final Map<String, SSSSCache> _cache = <String, SSSSCache>{};
SSSS(this.encryption); SSSS(this.encryption);
// for testing // for testing
@ -174,7 +176,7 @@ class SSSS {
await client.setAccountData( await client.setAccountData(
client.userID, client.userID,
EventTypes.SecretStorageDefaultKey, EventTypes.SecretStorageDefaultKey,
(SecretStorageDefaultKeyContent()..key = keyId).toJson(), SecretStorageDefaultKeyContent(key: keyId).toJson(),
); );
} }
@ -193,13 +195,12 @@ class SSSS {
final content = SecretStorageKeyContent(); final content = SecretStorageKeyContent();
if (passphrase != null) { if (passphrase != null) {
// we need to derive the key off of the passphrase // we need to derive the key off of the passphrase
content.passphrase = PassphraseInfo(); content.passphrase = PassphraseInfo(
content.passphrase.algorithm = AlgorithmTypes.pbkdf2; iterations: pbkdf2DefaultIterations,
content.passphrase.salt = base64 salt: base64.encode(uc.secureRandomBytes(pbkdf2SaltLength)),
.encode(uc.secureRandomBytes(pbkdf2SaltLength)); // generate salt algorithm: AlgorithmTypes.pbkdf2,
content.passphrase.iterations = pbkdf2DefaultIterations; bits: ssssKeyLength * 8,
; );
content.passphrase.bits = ssssKeyLength * 8;
privateKey = await client privateKey = await client
.runInBackground( .runInBackground(
_keyFromPassphrase, _keyFromPassphrase,
@ -431,6 +432,7 @@ class SSSS {
DateTime _lastCacheRequest; DateTime _lastCacheRequest;
bool _isPeriodicallyRequestingMissingCache = false; bool _isPeriodicallyRequestingMissingCache = false;
Future<void> periodicallyRequestMissingCache() async { Future<void> periodicallyRequestMissingCache() async {
if (_isPeriodicallyRequestingMissingCache || if (_isPeriodicallyRequestingMissingCache ||
(_lastCacheRequest != null && (_lastCacheRequest != null &&
@ -606,10 +608,13 @@ class OpenSSSS {
final SSSS ssss; final SSSS ssss;
final String keyId; final String keyId;
final SecretStorageKeyContent keyData; final SecretStorageKeyContent keyData;
OpenSSSS({this.ssss, this.keyId, this.keyData}); OpenSSSS({this.ssss, this.keyId, this.keyData});
Uint8List privateKey; Uint8List privateKey;
bool get isUnlocked => privateKey != null; bool get isUnlocked => privateKey != null;
bool get hasPassphrase => keyData.passphrase != null; bool get hasPassphrase => keyData.passphrase != null;
String get recoveryKey => String get recoveryKey =>
@ -708,6 +713,7 @@ class OpenSSSS {
class _KeyFromPassphraseArgs { class _KeyFromPassphraseArgs {
final String passphrase; final String passphrase;
final PassphraseInfo info; final PassphraseInfo info;
_KeyFromPassphraseArgs({this.passphrase, this.info}); _KeyFromPassphraseArgs({this.passphrase, this.info});
} }

View File

@ -563,8 +563,8 @@ class Bootstrap {
keyObj.free(); keyObj.free();
} }
Logs().v('Create the new backup version...'); Logs().v('Create the new backup version...');
await client.createRoomKeysBackup( await client.postRoomKeysVersion(
RoomKeysAlgorithmType.v1Curve25519AesSha2, BackupAlgorithm.mMegolmBackupV1Curve25519AesSha2,
<String, dynamic>{ <String, dynamic>{
'public_key': pubKey, 'public_key': pubKey,
}, },

View File

@ -21,8 +21,8 @@ import 'dart:convert';
import 'dart:core'; import 'dart:core';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:matrix/src/utils/run_in_root.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:matrix/src/utils/run_in_root.dart';
import 'package:olm/olm.dart' as olm; import 'package:olm/olm.dart' as olm;
import '../encryption.dart'; import '../encryption.dart';
@ -44,6 +44,12 @@ typedef RoomSorter = int Function(Room a, Room b);
enum LoginState { logged, loggedOut } enum LoginState { logged, loggedOut }
extension TrailingSlash on Uri {
Uri stripTrailingSlash() => path.endsWith('/')
? replace(path: path.substring(0, path.length - 1))
: this;
}
/// Represents a Matrix client to communicate with a /// Represents a Matrix client to communicate with a
/// [Matrix](https://matrix.org) homeserver and is the entry point for this /// [Matrix](https://matrix.org) homeserver and is the entry point for this
/// SDK. /// SDK.
@ -158,12 +164,13 @@ class Client extends MatrixApi {
this.compute, this.compute,
Filter syncFilter, Filter syncFilter,
@deprecated bool debug, @deprecated bool debug,
}) : syncFilter = syncFilter ?? }) : syncFilter = syncFilter ??
Filter( Filter(
room: RoomFilter( room: RoomFilter(
state: StateFilter(lazyLoadMembers: true), state: StateFilter(lazyLoadMembers: true),
), ),
) { ),
super(httpClient: httpClient) {
supportedLoginTypes ??= {AuthenticationTypes.password}; supportedLoginTypes ??= {AuthenticationTypes.password};
verificationMethods ??= <KeyVerificationMethod>{}; verificationMethods ??= <KeyVerificationMethod>{};
importantStateEvents ??= {}; importantStateEvents ??= {};
@ -182,7 +189,6 @@ class Client extends MatrixApi {
EventTypes.Encrypted, EventTypes.Encrypted,
EventTypes.Sticker, EventTypes.Sticker,
]); ]);
this.httpClient = httpClient ?? http.Client();
// register all the default commands // register all the default commands
registerDefaultCommands(); registerDefaultCommands();
@ -308,7 +314,7 @@ class Client extends MatrixApi {
} }
/// Gets discovery information about the domain. The file may include additional keys. /// Gets discovery information about the domain. The file may include additional keys.
Future<WellKnownInformation> getWellKnownInformationsByUserId( Future<DiscoveryInformation> getDiscoveryInformationsByUserId(
String MatrixIdOrDomain, String MatrixIdOrDomain,
) async { ) async {
try { try {
@ -321,15 +327,14 @@ class Client extends MatrixApi {
// No-OP // No-OP
} }
final rawJson = json.decode(respBody); final rawJson = json.decode(respBody);
return WellKnownInformation.fromJson(rawJson); return DiscoveryInformation.fromJson(rawJson);
} catch (_) { } catch (_) {
// we got an error processing or fetching the well-known information, let's // we got an error processing or fetching the well-known information, let's
// provide a reasonable fallback. // provide a reasonable fallback.
return WellKnownInformation.fromJson(<String, dynamic>{ return DiscoveryInformation(
'm.homeserver': <String, dynamic>{ mHomeserver: HomeserverInformation(
'base_url': Uri.https(MatrixIdOrDomain.domain, '').toString(), baseUrl: Uri.https(MatrixIdOrDomain.domain, '')),
}, );
});
} }
} }
@ -347,35 +352,19 @@ class Client extends MatrixApi {
/// login types. Throws an exception if the server is not compatible with the /// login types. Throws an exception if the server is not compatible with the
/// client and sets [homeserver] to [homeserverUrl] if it is. Supports the /// client and sets [homeserver] to [homeserverUrl] if it is. Supports the
/// types `Uri` and `String`. /// types `Uri` and `String`.
Future<WellKnownInformation> checkHomeserver(dynamic homeserverUrl, Future<DiscoveryInformation> checkHomeserver(dynamic homeserverUrl,
{bool checkWellKnown = true}) async { {bool checkWellKnown = true}) async {
try { try {
if (homeserverUrl is Uri) { homeserver =
homeserver = homeserverUrl; (homeserverUrl is Uri) ? homeserverUrl : Uri.parse(homeserverUrl);
} else { homeserver = homeserver.stripTrailingSlash();
// URLs allow to have whitespace surrounding them, see https://www.w3.org/TR/2011/WD-html5-20110525/urls.html
// As we want to strip a trailing slash, though, we have to trim the url ourself
// and thus can't let Uri.parse() deal with it.
homeserverUrl = homeserverUrl.trim();
// strip a trailing slash
if (homeserverUrl.endsWith('/')) {
homeserverUrl = homeserverUrl.substring(0, homeserverUrl.length - 1);
}
homeserver = Uri.parse(homeserverUrl);
}
// Look up well known // Look up well known
WellKnownInformation wellKnown; DiscoveryInformation wellKnown;
if (checkWellKnown) { if (checkWellKnown) {
try { try {
wellKnown = await getWellknown(); wellKnown = await getWellknown();
homeserverUrl = wellKnown.mHomeserver.baseUrl.trim(); homeserver = wellKnown.mHomeserver.baseUrl.stripTrailingSlash();
// strip a trailing slash
if (homeserverUrl.endsWith('/')) {
homeserverUrl =
homeserverUrl.substring(0, homeserverUrl.length - 1);
}
homeserver = Uri.parse(homeserverUrl);
} catch (e) { } catch (e) {
Logs().v('Found no well known information', e); Logs().v('Found no well known information', e);
} }
@ -390,9 +379,9 @@ class Client extends MatrixApi {
} }
final loginTypes = await getLoginFlows(); final loginTypes = await getLoginFlows();
if (!loginTypes.flows.any((f) => supportedLoginTypes.contains(f.type))) { if (!loginTypes.any((f) => supportedLoginTypes.contains(f.type))) {
throw BadServerLoginTypesException( throw BadServerLoginTypesException(
loginTypes.flows.map((f) => f.type).toSet(), supportedLoginTypes); loginTypes.map((f) => f.type).toSet(), supportedLoginTypes);
} }
return wellKnown; return wellKnown;
@ -406,14 +395,14 @@ class Client extends MatrixApi {
/// Returns the fully-qualified Matrix user ID (MXID) that has been registered. /// Returns the fully-qualified Matrix user ID (MXID) that has been registered.
/// You have to call [checkHomeserver] first to set a homeserver. /// You have to call [checkHomeserver] first to set a homeserver.
@override @override
Future<LoginResponse> register({ Future<RegisterResponse> register({
String username, String username,
String password, String password,
String deviceId, String deviceId,
String initialDeviceDisplayName, String initialDeviceDisplayName,
bool inhibitLogin, bool inhibitLogin,
AuthenticationData auth, AuthenticationData auth,
String kind, AccountKind kind,
}) async { }) async {
final response = await super.register( final response = await super.register(
username: username, username: username,
@ -447,8 +436,8 @@ class Client extends MatrixApi {
/// Maybe you want to set [user] to the same String to stay compatible with /// Maybe you want to set [user] to the same String to stay compatible with
/// older server versions. /// older server versions.
@override @override
Future<LoginResponse> login({ Future<LoginResponse> login(
String type = AuthenticationTypes.password, LoginType type, {
AuthenticationIdentifier identifier, AuthenticationIdentifier identifier,
String password, String password,
String token, String token,
@ -463,13 +452,12 @@ class Client extends MatrixApi {
await checkHomeserver(user.domain); await checkHomeserver(user.domain);
} }
final loginResp = await super.login( final loginResp = await super.login(
type: type, type,
identifier: identifier, identifier: identifier,
password: password, password: password,
token: token, token: token,
deviceId: deviceId, deviceId: deviceId,
initialDeviceDisplayName: initialDeviceDisplayName, initialDeviceDisplayName: initialDeviceDisplayName,
auth: auth,
// ignore: deprecated_member_use // ignore: deprecated_member_use
user: user, user: user,
// ignore: deprecated_member_use // ignore: deprecated_member_use
@ -553,7 +541,7 @@ class Client extends MatrixApi {
roomId = await createRoom( roomId = await createRoom(
invite: [mxid], invite: [mxid],
isDirect: true, isDirect: true,
preset: CreateRoomPreset.trusted_private_chat, preset: CreateRoomPreset.trustedPrivateChat,
); );
if (roomId == null) return roomId; if (roomId == null) return roomId;
@ -576,7 +564,7 @@ class Client extends MatrixApi {
Visibility visibility = Visibility.public, Visibility visibility = Visibility.public,
String spaceAliasName, String spaceAliasName,
List<String> invite, List<String> invite,
List<Map<String, dynamic>> invite3pid, List<Invite3pid> invite3pid,
String roomVersion, String roomVersion,
}) => }) =>
createRoom( createRoom(
@ -607,7 +595,7 @@ class Client extends MatrixApi {
return getProfileFromUserId(userID); return getProfileFromUserId(userID);
} }
final Map<String, Profile> _profileCache = {}; final Map<String, ProfileInformation> _profileCache = {};
/// Get the combined profile information for this user. /// Get the combined profile information for this user.
/// If [getFromRooms] is true then the profile will first be searched from the /// If [getFromRooms] is true then the profile will first be searched from the
@ -629,15 +617,25 @@ class Client extends MatrixApi {
if (room != null) { if (room != null) {
final user = final user =
room.getParticipants().firstWhere((User user) => user.id == userId); room.getParticipants().firstWhere((User user) => user.id == userId);
return Profile(user.displayName, user.avatarUrl); return Profile(
userId: userId,
displayName: user.displayName,
avatarUrl: user.avatarUrl);
} }
} }
if (cache && _profileCache.containsKey(userId)) { if (cache && _profileCache.containsKey(userId)) {
return _profileCache[userId]; final profile = _profileCache[userId];
return Profile(
userId: userId,
displayName: profile.displayname,
avatarUrl: profile.avatarUrl);
} }
final profile = await getUserProfile(userId); final profile = await getUserProfile(userId);
_profileCache[userId] = profile; _profileCache[userId] = profile;
return profile; return Profile(
userId: userId,
displayName: profile.displayname,
avatarUrl: profile.avatarUrl);
} }
Future<List<Room>> get archive async { Future<List<Room>> get archive async {
@ -685,10 +683,10 @@ class Client extends MatrixApi {
/// Uploads a file and automatically caches it in the database, if it is small enough /// Uploads a file and automatically caches it in the database, if it is small enough
/// and returns the mxc url as a string. /// and returns the mxc url as a string.
@override @override
Future<String> uploadContent(Uint8List file, String fileName, Future<String> uploadContent(Uint8List file,
{String contentType}) async { {String filename, String contentType}) async {
final mxc = final mxc = await super
await super.uploadContent(file, fileName, contentType: contentType); .uploadContent(file, filename: filename, contentType: contentType);
final storeable = database != null && file.length <= database.maxFileSize; final storeable = database != null && file.length <= database.maxFileSize;
if (storeable) { if (storeable) {
await database.storeFile( await database.storeFile(
@ -715,7 +713,7 @@ class Client extends MatrixApi {
/// Uploads a new user avatar for this user. /// Uploads a new user avatar for this user.
Future<void> setAvatar(MatrixFile file) async { Future<void> setAvatar(MatrixFile file) async {
final uploadResp = await uploadContent(file.bytes, file.name); final uploadResp = await uploadContent(file.bytes, filename: file.name);
await setAvatarUrl(userID, Uri.parse(uploadResp)); await setAvatarUrl(userID, Uri.parse(uploadResp));
return; return;
} }
@ -2006,16 +2004,16 @@ sort order of ${prevState.sortOrder}. This should never happen...''');
/// Changes the password. You should either set oldPasswort or another authentication flow. /// Changes the password. You should either set oldPasswort or another authentication flow.
@override @override
Future<void> changePassword(String newPassword, Future<void> changePassword(String newPassword,
{String oldPassword, AuthenticationData auth}) async { {String oldPassword, AuthenticationData auth, bool logoutDevices}) async {
try { try {
if (oldPassword != null) { if (oldPassword != null) {
auth = AuthenticationPassword( auth = AuthenticationPassword(
user: userID,
identifier: AuthenticationUserIdentifier(user: userID), identifier: AuthenticationUserIdentifier(user: userID),
password: oldPassword, password: oldPassword,
); );
} }
await super.changePassword(newPassword, auth: auth); await super.changePassword(newPassword,
auth: auth, logoutDevices: logoutDevices);
} on MatrixException catch (matrixException) { } on MatrixException catch (matrixException) {
if (!matrixException.requireAdditionalAuthentication) { if (!matrixException.requireAdditionalAuthentication) {
rethrow; rethrow;
@ -2031,11 +2029,11 @@ sort order of ${prevState.sortOrder}. This should never happen...''');
return changePassword( return changePassword(
newPassword, newPassword,
auth: AuthenticationPassword( auth: AuthenticationPassword(
user: userID,
identifier: AuthenticationUserIdentifier(user: userID), identifier: AuthenticationUserIdentifier(user: userID),
password: oldPassword, password: oldPassword,
session: matrixException.session, session: matrixException.session,
), ),
logoutDevices: logoutDevices,
); );
} catch (_) { } catch (_) {
rethrow; rethrow;

View File

@ -19,8 +19,8 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:matrix/src/utils/space_child.dart';
import 'package:html_unescape/html_unescape.dart'; import 'package:html_unescape/html_unescape.dart';
import 'package:matrix/src/utils/space_child.dart';
import '../matrix.dart'; import '../matrix.dart';
import 'client.dart'; import 'client.dart';
@ -288,7 +288,7 @@ class Room {
if (!aliases.contains(canonicalAlias)) { if (!aliases.contains(canonicalAlias)) {
await client.setRoomAlias(canonicalAlias, id); await client.setRoomAlias(canonicalAlias, id);
} }
await client.setRoomStateWithKey(id, EventTypes.RoomCanonicalAlias, { await client.setRoomStateWithKey(id, EventTypes.RoomCanonicalAlias, '', {
'alias': canonicalAlias, 'alias': canonicalAlias,
}); });
} }
@ -455,6 +455,7 @@ class Room {
Future<String> setName(String newName) => client.setRoomStateWithKey( Future<String> setName(String newName) => client.setRoomStateWithKey(
id, id,
EventTypes.RoomName, EventTypes.RoomName,
'',
{'name': newName}, {'name': newName},
); );
@ -462,6 +463,7 @@ class Room {
Future<String> setDescription(String newName) => client.setRoomStateWithKey( Future<String> setDescription(String newName) => client.setRoomStateWithKey(
id, id,
EventTypes.RoomTopic, EventTypes.RoomTopic,
'',
{'topic': newName}, {'topic': newName},
); );
@ -506,14 +508,15 @@ class Room {
/// this works if there is no connection to the homeserver. /// this works if there is no connection to the homeserver.
Future<void> setUnread(bool unread) async { Future<void> setUnread(bool unread) async {
final content = MarkedUnread(unread).toJson(); final content = MarkedUnread(unread).toJson();
await _handleFakeSync(SyncUpdate() await _handleFakeSync(SyncUpdate(nextBatch: '')
..rooms = (RoomsUpdate() ..rooms = (RoomsUpdate()
..join = (({}..[id] = (JoinedRoomUpdate() ..join = (({}..[id] = (JoinedRoomUpdate()
..accountData = [ ..accountData = [
BasicRoomEvent() BasicRoomEvent(
..content = content content: content,
..roomId = id roomId: id,
..type = EventType.markedUnread type: EventType.markedUnread,
)
]))))); ])))));
await client.setAccountDataPerRoom( await client.setAccountDataPerRoom(
client.userID, client.userID,
@ -524,23 +527,24 @@ class Room {
if (unread == false && lastEvent != null) { if (unread == false && lastEvent != null) {
await setReadMarker( await setReadMarker(
lastEvent.eventId, lastEvent.eventId,
readReceiptLocationEventId: lastEvent.eventId, mRead: lastEvent.eventId,
); );
} }
} }
/// Returns true if this room has a m.favourite tag. /// Returns true if this room has a m.favourite tag.
bool get isFavourite => tags[TagType.Favourite] != null; bool get isFavourite => tags[TagType.favourite] != null;
/// Sets the m.favourite tag for this room. /// Sets the m.favourite tag for this room.
Future<void> setFavourite(bool favourite) => Future<void> setFavourite(bool favourite) =>
favourite ? addTag(TagType.Favourite) : removeTag(TagType.Favourite); favourite ? addTag(TagType.favourite) : removeTag(TagType.favourite);
/// Call the Matrix API to change the pinned events of this room. /// Call the Matrix API to change the pinned events of this room.
Future<String> setPinnedEvents(List<String> pinnedEventIds) => Future<String> setPinnedEvents(List<String> pinnedEventIds) =>
client.setRoomStateWithKey( client.setRoomStateWithKey(
id, id,
EventTypes.RoomPinnedEvents, EventTypes.RoomPinnedEvents,
'',
{'pinned': pinnedEventIds}, {'pinned': pinnedEventIds},
); );
@ -635,13 +639,13 @@ class Room {
} }
final uploadResp = await client.uploadContent( final uploadResp = await client.uploadContent(
uploadFile.bytes, uploadFile.bytes,
uploadFile.name, filename: uploadFile.name,
contentType: uploadFile.mimeType, contentType: uploadFile.mimeType,
); );
final thumbnailUploadResp = uploadThumbnail != null final thumbnailUploadResp = uploadThumbnail != null
? await client.uploadContent( ? await client.uploadContent(
uploadThumbnail.bytes, uploadThumbnail.bytes,
uploadThumbnail.name, filename: uploadThumbnail.name,
contentType: uploadThumbnail.mimeType, contentType: uploadThumbnail.mimeType,
) )
: null; : null;
@ -751,17 +755,16 @@ class Room {
RegExp(r'<mx-reply>.*<\/mx-reply>', RegExp(r'<mx-reply>.*<\/mx-reply>',
caseSensitive: false, multiLine: false, dotAll: true), caseSensitive: false, multiLine: false, dotAll: true),
''); '');
final repliedHtml = content.tryGet<String>( final repliedHtml = content.tryGet<String>('formatted_body') ??
'formatted_body',
htmlEscape htmlEscape
.convert(content.tryGet<String>('body', '')) .convert(content.tryGet<String>('body') ?? '')
.replaceAll('\n', '<br>')); .replaceAll('\n', '<br>');
content['formatted_body'] = content['formatted_body'] =
'<mx-reply><blockquote><a href="https://matrix.to/#/${inReplyTo.room.id}/${inReplyTo.eventId}">In reply to</a> <a href="https://matrix.to/#/${inReplyTo.senderId}">${inReplyTo.senderId}</a><br>$replyHtml</blockquote></mx-reply>$repliedHtml'; '<mx-reply><blockquote><a href="https://matrix.to/#/${inReplyTo.room.id}/${inReplyTo.eventId}">In reply to</a> <a href="https://matrix.to/#/${inReplyTo.senderId}">${inReplyTo.senderId}</a><br>$replyHtml</blockquote></mx-reply>$repliedHtml';
// We escape all @room-mentions here to prevent accidental room pings when an admin // We escape all @room-mentions here to prevent accidental room pings when an admin
// replies to a message containing that! // replies to a message containing that!
content['body'] = content['body'] =
'${replyText.replaceAll('@room', '@\u200broom')}\n\n${content.tryGet<String>('body', '')}'; '${replyText.replaceAll('@room', '@\u200broom')}\n\n${content.tryGet<String>('body') ?? ''}';
content['m.relates_to'] = { content['m.relates_to'] = {
'm.in_reply_to': { 'm.in_reply_to': {
'event_id': inReplyTo.eventId, 'event_id': inReplyTo.eventId,
@ -783,21 +786,22 @@ class Room {
} }
} }
final sentDate = DateTime.now(); final sentDate = DateTime.now();
final syncUpdate = SyncUpdate() final syncUpdate = SyncUpdate(nextBatch: '')
..rooms = (RoomsUpdate() ..rooms = (RoomsUpdate()
..join = (<String, JoinedRoomUpdate>{}..[id] = (JoinedRoomUpdate() ..join = (<String, JoinedRoomUpdate>{}..[id] = (JoinedRoomUpdate()
..timeline = (TimelineUpdate() ..timeline = (TimelineUpdate()
..events = [ ..events = [
MatrixEvent() MatrixEvent(
..content = content content: content,
..type = type type: type,
..eventId = messageID eventId: messageID,
..senderId = client.userID senderId: client.userID,
..originServerTs = sentDate originServerTs: sentDate,
..unsigned = { unsigned: {
messageSendingStatusKey: 0, messageSendingStatusKey: 0,
'transaction_id': messageID, 'transaction_id': messageID,
}, },
)
])))); ]))));
await _handleFakeSync(syncUpdate); await _handleFakeSync(syncUpdate);
@ -868,7 +872,7 @@ class Room {
if ([MatrixError.M_NOT_FOUND, MatrixError.M_UNKNOWN] if ([MatrixError.M_NOT_FOUND, MatrixError.M_UNKNOWN]
.contains(exception.error)) { .contains(exception.error)) {
await _handleFakeSync( await _handleFakeSync(
SyncUpdate() SyncUpdate(nextBatch: '')
..rooms = (RoomsUpdate() ..rooms = (RoomsUpdate()
..leave = { ..leave = {
'$id': (LeftRoomUpdate()), '$id': (LeftRoomUpdate()),
@ -909,12 +913,13 @@ class Room {
return await client.setRoomStateWithKey( return await client.setRoomStateWithKey(
id, id,
EventTypes.RoomPowerLevels, EventTypes.RoomPowerLevels,
'',
powerMap, powerMap,
); );
} }
/// Call the Matrix API to invite a user to this room. /// Call the Matrix API to invite a user to this room.
Future<void> invite(String userID) => client.inviteToRoom(id, userID); Future<void> invite(String userID) => client.inviteUser(id, userID);
/// Request more previous events from the server. [historyCount] defines how much events should /// Request more previous events from the server. [historyCount] defines how much events should
/// be received maximum. When the request is answered, [onHistoryReceived] will be triggered **before** /// be received maximum. When the request is answered, [onHistoryReceived] will be triggered **before**
@ -936,7 +941,7 @@ class Room {
if (!((resp.chunk?.isNotEmpty ?? false) && resp.end != null)) return; if (!((resp.chunk?.isNotEmpty ?? false) && resp.end != null)) return;
await client.handleSync( await client.handleSync(
SyncUpdate() SyncUpdate(nextBatch: '')
..rooms = (RoomsUpdate() ..rooms = (RoomsUpdate()
..join = ({}..[id] = (JoinedRoomUpdate() ..join = ({}..[id] = (JoinedRoomUpdate()
..state = resp.state ..state = resp.state
@ -1000,16 +1005,15 @@ class Room {
/// Sets the position of the read marker for a given room, and optionally the /// Sets the position of the read marker for a given room, and optionally the
/// read receipt's location. /// read receipt's location.
Future<void> setReadMarker(String eventId, Future<void> setReadMarker(String eventId, {String mRead}) async {
{String readReceiptLocationEventId}) async { if (mRead != null) {
if (readReceiptLocationEventId != null) {
notificationCount = 0; notificationCount = 0;
await client.database?.resetNotificationCount(client.id, id); await client.database?.resetNotificationCount(client.id, id);
} }
await client.setReadMarker( await client.setReadMarker(
id, id,
eventId, eventId,
readReceiptLocationEventId: readReceiptLocationEventId, mRead: mRead,
); );
return; return;
} }
@ -1021,7 +1025,9 @@ class Room {
await client.database?.resetNotificationCount(client.id, id); await client.database?.resetNotificationCount(client.id, id);
await client.postReceipt( await client.postReceipt(
id, id,
ReceiptType.mRead,
eventId, eventId,
{},
); );
return; return;
} }
@ -1034,7 +1040,7 @@ class Room {
await client.setReadMarker( await client.setReadMarker(
id, id,
eventID, eventID,
readReceiptLocationEventId: eventID, mRead: eventID,
); );
return; return;
} }
@ -1294,10 +1300,12 @@ class Room {
/// Uploads a new user avatar for this room. Returns the event ID of the new /// Uploads a new user avatar for this room. Returns the event ID of the new
/// m.room.avatar event. /// m.room.avatar event.
Future<String> setAvatar(MatrixFile file) async { Future<String> setAvatar(MatrixFile file) async {
final uploadResp = await client.uploadContent(file.bytes, file.name); final uploadResp =
await client.uploadContent(file.bytes, filename: file.name);
return await client.setRoomStateWithKey( return await client.setRoomStateWithKey(
id, id,
EventTypes.RoomAvatar, EventTypes.RoomAvatar,
'',
{'url': uploadResp}, {'url': uploadResp},
); );
} }
@ -1402,14 +1410,14 @@ class Room {
'global', 'global',
PushRuleKind.room, PushRuleKind.room,
id, id,
[PushRuleAction.dont_notify], [PushRuleAction.dontNotify],
); );
} else if (pushRuleState == PushRuleState.notify) { } else if (pushRuleState == PushRuleState.notify) {
await client.setPushRule( await client.setPushRule(
'global', 'global',
PushRuleKind.room, PushRuleKind.room,
id, id,
[PushRuleAction.dont_notify], [PushRuleAction.dontNotify],
); );
} }
break; break;
@ -1422,9 +1430,9 @@ class Room {
'global', 'global',
PushRuleKind.override, PushRuleKind.override,
id, id,
[PushRuleAction.dont_notify], [PushRuleAction.dontNotify],
conditions: [ conditions: [
PushConditions('event_match', key: 'room_id', pattern: id) PushCondition(kind: 'event_match', key: 'room_id', pattern: id)
], ],
); );
} }
@ -1578,6 +1586,7 @@ class Room {
await client.setRoomStateWithKey( await client.setRoomStateWithKey(
id, id,
EventTypes.RoomJoinRules, EventTypes.RoomJoinRules,
'',
{ {
'join_rule': joinRules.toString().replaceAll('JoinRules.', ''), 'join_rule': joinRules.toString().replaceAll('JoinRules.', ''),
}, },
@ -1600,6 +1609,7 @@ class Room {
await client.setRoomStateWithKey( await client.setRoomStateWithKey(
id, id,
EventTypes.GuestAccess, EventTypes.GuestAccess,
'',
{ {
'guest_access': _guestAccessMap[guestAccess], 'guest_access': _guestAccessMap[guestAccess],
}, },
@ -1623,6 +1633,7 @@ class Room {
await client.setRoomStateWithKey( await client.setRoomStateWithKey(
id, id,
EventTypes.HistoryVisibility, EventTypes.HistoryVisibility,
'',
{ {
'history_visibility': _historyVisibilityMap[historyVisibility], 'history_visibility': _historyVisibilityMap[historyVisibility],
}, },
@ -1648,6 +1659,7 @@ class Room {
await client.setRoomStateWithKey( await client.setRoomStateWithKey(
id, id,
EventTypes.Encryption, EventTypes.Encryption,
'',
{ {
'algorithm': algorithm, 'algorithm': algorithm,
}, },
@ -1742,22 +1754,14 @@ class Room {
}) async { }) async {
if (!isSpace) throw Exception('Room is not a space!'); if (!isSpace) throw Exception('Room is not a space!');
via ??= [roomId.domain]; via ??= [roomId.domain];
await client.setRoomStateWithKey( await client.setRoomStateWithKey(id, EventTypes.spaceChild, roomId, {
id, 'via': via,
EventTypes.spaceChild, if (order != null) 'order': order,
{ if (suggested != null) 'suggested': suggested,
'via': via, });
if (order != null) 'order': order, await client.setRoomStateWithKey(roomId, EventTypes.spaceParent, id, {
if (suggested != null) 'suggested': suggested, 'via': via,
}, });
roomId);
await client.setRoomStateWithKey(
roomId,
EventTypes.spaceParent,
{
'via': via,
},
id);
return; return;
} }

View File

@ -172,8 +172,8 @@ extension CommandsClientExtension on Client {
return await args.room.client.setRoomStateWithKey( return await args.room.client.setRoomStateWithKey(
args.room.id, args.room.id,
EventTypes.RoomMember, EventTypes.RoomMember,
currentEventJson,
args.room.client.userID, args.room.client.userID,
currentEventJson,
); );
}); });
addCommand('myroomavatar', (CommandArgs args) async { addCommand('myroomavatar', (CommandArgs args) async {
@ -185,8 +185,8 @@ extension CommandsClientExtension on Client {
return await args.room.client.setRoomStateWithKey( return await args.room.client.setRoomStateWithKey(
args.room.id, args.room.id,
EventTypes.RoomMember, EventTypes.RoomMember,
currentEventJson,
args.room.client.userID, args.room.client.userID,
currentEventJson,
); );
}); });
} }

View File

@ -23,7 +23,6 @@ import 'package:matrix/matrix.dart';
import 'package:olm/olm.dart' as olm; import 'package:olm/olm.dart' as olm;
import '../../encryption.dart'; import '../../encryption.dart';
import '../client.dart'; import '../client.dart';
import '../event.dart'; import '../event.dart';
import '../room.dart'; import '../room.dart';
@ -132,12 +131,22 @@ class DeviceKeysList {
DeviceKeysList(this.userId, this.client); DeviceKeysList(this.userId, this.client);
} }
class SimpleSignableKey extends MatrixSignableKey {
@override
String identifier;
SimpleSignableKey.fromJson(Map<String, dynamic> json) : super.fromJson(json);
}
abstract class SignableKey extends MatrixSignableKey { abstract class SignableKey extends MatrixSignableKey {
Client client; Client client;
Map<String, dynamic> validSignatures; Map<String, dynamic> validSignatures;
bool _verified; bool _verified;
bool blocked; bool blocked;
@override
String identifier;
String get ed25519Key => keys['ed25519:$identifier']; String get ed25519Key => keys['ed25519:$identifier'];
bool get verified => (directVerified || crossVerified) && !blocked; bool get verified => (directVerified || crossVerified) && !blocked;
bool get encryptToDevice => bool get encryptToDevice =>
@ -161,8 +170,8 @@ abstract class SignableKey extends MatrixSignableKey {
blocked = false; blocked = false;
} }
MatrixSignableKey cloneForSigning() { SimpleSignableKey cloneForSigning() {
final newKey = MatrixSignableKey.fromJson(toJson().copy()); final newKey = SimpleSignableKey.fromJson(toJson().copy());
newKey.identifier = identifier; newKey.identifier = identifier;
newKey.signatures ??= <String, Map<String, String>>{}; newKey.signatures ??= <String, Map<String, String>>{};
newKey.signatures.clear(); newKey.signatures.clear();

View File

@ -30,7 +30,7 @@ class SpaceChild {
: assert(state.type == EventTypes.spaceChild), : assert(state.type == EventTypes.spaceChild),
roomId = state.stateKey, roomId = state.stateKey,
via = state.content.tryGetList<String>('via'), via = state.content.tryGetList<String>('via'),
order = state.content.tryGet<String>('order', ''), order = state.content.tryGet<String>('order') ?? '',
suggested = state.content.tryGet<bool>('suggested'); suggested = state.content.tryGet<bool>('suggested');
} }

View File

@ -44,6 +44,7 @@ class UiaRequest<T> {
Map<String, dynamic> params = <String, dynamic>{}; Map<String, dynamic> params = <String, dynamic>{};
UiaRequestState get state => _state; UiaRequestState get state => _state;
set state(UiaRequestState newState) { set state(UiaRequestState newState) {
if (_state == newState) return; if (_state == newState) return;
_state = newState; _state = newState;
@ -57,7 +58,8 @@ class UiaRequest<T> {
Future<T> _run([AuthenticationData auth]) async { Future<T> _run([AuthenticationData auth]) async {
state = UiaRequestState.loading; state = UiaRequestState.loading;
try { try {
auth ??= AuthenticationData(session: session); auth ??=
AuthenticationData(session: session, type: AuthenticationTypes.token);
final res = await request(auth); final res = await request(auth);
state = UiaRequestState.done; state = UiaRequestState.done;
result = res; result = res;

View File

@ -17,7 +17,7 @@ dependencies:
crypto: ^3.0.0 crypto: ^3.0.0
base58check: ^2.0.0 base58check: ^2.0.0
olm: ^2.0.0 olm: ^2.0.0
matrix_api_lite: ^0.3.5 matrix_api_lite: ^0.4.0
hive: ^2.0.4 hive: ^2.0.4
ffi: ^1.0.0 ffi: ^1.0.0
js: ^0.6.3 js: ^0.6.3

View File

@ -84,7 +84,7 @@ void main() {
try { try {
await matrix.checkHomeserver('https://fakeserver.wrongaddress'); await matrix.checkHomeserver('https://fakeserver.wrongaddress');
} on MatrixConnectionException catch (exception) { } catch (exception) {
expect(exception != null, true); expect(exception != null, true);
} }
await matrix.checkHomeserver('https://fakeserver.notexisting', await matrix.checkHomeserver('https://fakeserver.notexisting',
@ -323,7 +323,7 @@ void main() {
await matrix.checkHomeserver('https://fakeserver.notexisting', await matrix.checkHomeserver('https://fakeserver.notexisting',
checkWellKnown: false); checkWellKnown: false);
final loginResp = await matrix.login( final loginResp = await matrix.login(LoginType.mLoginPassword,
identifier: AuthenticationUserIdentifier(user: 'test'), identifier: AuthenticationUserIdentifier(user: 'test'),
password: '1234'); password: '1234');
@ -368,12 +368,12 @@ void main() {
final profile = await matrix.getProfileFromUserId('@getme:example.com', final profile = await matrix.getProfileFromUserId('@getme:example.com',
getFromRooms: false); getFromRooms: false);
expect(profile.avatarUrl.toString(), 'mxc://test'); expect(profile.avatarUrl.toString(), 'mxc://test');
expect(profile.displayname, 'You got me'); expect(profile.displayName, 'You got me');
final aliceProfile = final aliceProfile =
await matrix.getProfileFromUserId('@alice:example.com'); await matrix.getProfileFromUserId('@alice:example.com');
expect(aliceProfile.avatarUrl.toString(), expect(aliceProfile.avatarUrl.toString(),
'mxc://example.org/SEsfnsuifSDFSSEF'); 'mxc://example.org/SEsfnsuifSDFSSEF');
expect(aliceProfile.displayname, 'Alice Margatroid'); expect(aliceProfile.displayName, 'Alice Margatroid');
}); });
test('sendToDeviceEncrypted', () async { test('sendToDeviceEncrypted', () async {
if (!olmEnabled) { if (!olmEnabled) {
@ -642,7 +642,8 @@ void main() {
}); });
test('upload', () async { test('upload', () async {
final client = await getClient(); final client = await getClient();
final response = await client.uploadContent(Uint8List(0), 'file.jpeg'); final response =
await client.uploadContent(Uint8List(0), filename: 'file.jpeg');
expect(response, 'mxc://example.com/AQwafuaFswefuhsfAFAgsw'); expect(response, 'mxc://example.com/AQwafuaFswefuhsfAFAgsw');
expect(await client.database.getFile(response) != null, expect(await client.database.getFile(response) != null,
client.database.supportsFileStoring); client.database.supportsFileStoring);

View File

@ -269,7 +269,7 @@ void main() {
final matrix = Client('testclient', httpClient: FakeMatrixApi()); final matrix = Client('testclient', httpClient: FakeMatrixApi());
await matrix.checkHomeserver('https://fakeserver.notexisting', await matrix.checkHomeserver('https://fakeserver.notexisting',
checkWellKnown: false); checkWellKnown: false);
await matrix.login( await matrix.login(LoginType.mLoginPassword,
identifier: AuthenticationUserIdentifier(user: 'test'), identifier: AuthenticationUserIdentifier(user: 'test'),
password: '1234'); password: '1234');
@ -288,7 +288,7 @@ void main() {
final matrix = Client('testclient', httpClient: FakeMatrixApi()); final matrix = Client('testclient', httpClient: FakeMatrixApi());
await matrix.checkHomeserver('https://fakeserver.notexisting', await matrix.checkHomeserver('https://fakeserver.notexisting',
checkWellKnown: false); checkWellKnown: false);
await matrix.login( await matrix.login(LoginType.mLoginPassword,
identifier: AuthenticationUserIdentifier(user: 'test'), identifier: AuthenticationUserIdentifier(user: 'test'),
password: '1234'); password: '1234');

View File

@ -16,15 +16,14 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import 'package:matrix/matrix.dart' as sdk;
import 'package:matrix/matrix.dart';
import 'dart:convert'; import 'dart:convert';
import 'dart:core'; import 'dart:core';
import 'dart:math'; import 'dart:math';
import 'package:http/http.dart'; import 'package:http/http.dart';
import 'package:http/testing.dart'; import 'package:http/testing.dart';
import 'package:matrix/matrix.dart' as sdk;
import 'package:matrix/matrix.dart';
Map<String, dynamic> decodeJson(dynamic data) { Map<String, dynamic> decodeJson(dynamic data) {
if (data is String) { if (data is String) {
@ -122,11 +121,9 @@ class FakeMatrixApi extends MockClient {
action.contains('/account_data/') && action.contains('/account_data/') &&
!action.contains('/room/')) { !action.contains('/room/')) {
final type = Uri.decodeComponent(action.split('/').last); final type = Uri.decodeComponent(action.split('/').last);
final syncUpdate = sdk.SyncUpdate() final syncUpdate = sdk.SyncUpdate(nextBatch: '')
..accountData = [ ..accountData = [
sdk.BasicEvent() sdk.BasicEvent(content: decodeJson(data), type: type)
..content = decodeJson(data)
..type = type
]; ];
if (client.database != null) { if (client.database != null) {
await client.database.transaction(() async { await client.database.transaction(() async {
@ -2111,7 +2108,7 @@ class FakeMatrixApi extends MockClient {
} }
} }
// and generate a fake sync // and generate a fake sync
client.handleSync(sdk.SyncUpdate()); client.handleSync(sdk.SyncUpdate(nextBatch: ''));
} }
return {}; return {};
}, },

View File

@ -648,8 +648,8 @@ void main() {
}); });
test('Test tag methods', () async { test('Test tag methods', () async {
await room.addTag(TagType.Favourite, order: 0.1); await room.addTag(TagType.favourite, order: 0.1);
await room.removeTag(TagType.Favourite); await room.removeTag(TagType.favourite);
expect(room.isFavourite, false); expect(room.isFavourite, false);
room.roomAccountData['m.tag'] = BasicRoomEvent.fromJson({ room.roomAccountData['m.tag'] = BasicRoomEvent.fromJson({
'content': { 'content': {
@ -661,7 +661,7 @@ void main() {
'type': 'm.tag' 'type': 'm.tag'
}); });
expect(room.tags.length, 1); expect(room.tags.length, 1);
expect(room.tags[TagType.Favourite].order, 0.1); expect(room.tags[TagType.favourite].order, 0.1);
expect(room.isFavourite, true); expect(room.isFavourite, true);
await room.setFavourite(false); await room.setFavourite(false);
}); });

View File

@ -49,7 +49,7 @@ void main() {
setUp(() async { setUp(() async {
await client.checkHomeserver('https://fakeserver.notexisting', await client.checkHomeserver('https://fakeserver.notexisting',
checkWellKnown: false); checkWellKnown: false);
await client.login( await client.login(LoginType.mLoginPassword,
identifier: AuthenticationUserIdentifier(user: 'test'), identifier: AuthenticationUserIdentifier(user: 'test'),
password: '1234'); password: '1234');
}); });

View File

@ -22,7 +22,7 @@ void test() async {
Logs().i('++++ Login Alice at ++++'); Logs().i('++++ Login Alice at ++++');
testClientA = Client('TestClientA', databaseBuilder: getDatabase); testClientA = Client('TestClientA', databaseBuilder: getDatabase);
await testClientA.checkHomeserver(TestUser.homeserver); await testClientA.checkHomeserver(TestUser.homeserver);
await testClientA.login( await testClientA.login(LoginType.mLoginPassword,
identifier: AuthenticationUserIdentifier(user: TestUser.username), identifier: AuthenticationUserIdentifier(user: TestUser.username),
password: TestUser.password); password: TestUser.password);
assert(testClientA.encryptionEnabled); assert(testClientA.encryptionEnabled);
@ -30,7 +30,7 @@ void test() async {
Logs().i('++++ Login Bob ++++'); Logs().i('++++ Login Bob ++++');
testClientB = Client('TestClientB', databaseBuilder: getDatabase); testClientB = Client('TestClientB', databaseBuilder: getDatabase);
await testClientB.checkHomeserver(TestUser.homeserver); await testClientB.checkHomeserver(TestUser.homeserver);
await testClientB.login( await testClientB.login(LoginType.mLoginPassword,
identifier: AuthenticationUserIdentifier(user: TestUser.username2), identifier: AuthenticationUserIdentifier(user: TestUser.username2),
password: TestUser.password); password: TestUser.password);
assert(testClientB.encryptionEnabled); assert(testClientB.encryptionEnabled);
@ -220,7 +220,7 @@ void test() async {
Logs().i('++++ Login Bob in another client ++++'); Logs().i('++++ Login Bob in another client ++++');
var testClientC = Client('TestClientC', databaseBuilder: getDatabase); var testClientC = Client('TestClientC', databaseBuilder: getDatabase);
await testClientC.checkHomeserver(TestUser.homeserver); await testClientC.checkHomeserver(TestUser.homeserver);
await testClientC.login( await testClientC.login(LoginType.mLoginPassword,
identifier: AuthenticationUserIdentifier(user: TestUser.username2), identifier: AuthenticationUserIdentifier(user: TestUser.username2),
password: TestUser.password); password: TestUser.password);
await Future.delayed(Duration(seconds: 3)); await Future.delayed(Duration(seconds: 3));