chore: matrix_api_lite 0.4.0
This commit is contained in:
parent
bc2dac2ecc
commit
8665f092f4
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 {};
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -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');
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue