Krille/make client extend matrixapi

This commit is contained in:
Christian Pauly 2020-08-11 16:11:51 +00:00
parent 574fe27101
commit fb9b505988
19 changed files with 230 additions and 269 deletions

View File

@ -167,7 +167,7 @@ class CrossSigning {
}
if (signedKeys.isNotEmpty) {
// post our new keys!
await client.api.uploadKeySignatures(signedKeys);
await client.uploadKeySignatures(signedKeys);
}
}

View File

@ -44,7 +44,7 @@ class KeyManager {
encryption.ssss.setValidator(MEGOLM_KEY, (String secret) async {
final keyObj = olm.PkDecryption();
try {
final info = await client.api.getRoomKeysBackup();
final info = await client.getRoomKeysBackup();
if (info.algorithm != RoomKeysAlgorithmType.v1Curve25519AesSha2) {
return false;
}
@ -288,7 +288,7 @@ class KeyManager {
key: client.userID,
);
try {
await client.sendToDevice(deviceKeys, 'm.room_key', rawSession);
await client.sendToDeviceEncrypted(deviceKeys, 'm.room_key', rawSession);
await storeOutboundGroupSession(roomId, sess);
_outboundGroupSessions[roomId] = sess;
} catch (e, s) {
@ -339,7 +339,7 @@ class KeyManager {
final privateKey =
base64.decode(await encryption.ssss.getCached(MEGOLM_KEY));
final decryption = olm.PkDecryption();
final info = await client.api.getRoomKeysBackup();
final info = await client.getRoomKeysBackup();
String backupPubKey;
try {
backupPubKey = decryption.init_with_private_key(privateKey);
@ -387,9 +387,9 @@ class KeyManager {
}
Future<void> loadSingleKey(String roomId, String sessionId) async {
final info = await client.api.getRoomKeysBackup();
final info = await client.getRoomKeysBackup();
final ret =
await client.api.getRoomKeysSingleKey(roomId, sessionId, info.version);
await client.getRoomKeysSingleKey(roomId, sessionId, info.version);
final keys = RoomKeys.fromJson({
'rooms': {
roomId: {
@ -434,22 +434,22 @@ class KeyManager {
sessionId: sessionId,
senderKey: senderKey,
);
await client.sendToDevice(
[],
'm.room_key_request',
{
'action': 'request',
'body': {
'algorithm': 'm.megolm.v1.aes-sha2',
'room_id': room.id,
'sender_key': senderKey,
'session_id': sessionId,
},
'request_id': requestId,
'requesting_device_id': client.deviceID,
final userList = await room.requestParticipants();
await client.sendToDevicesOfUserIds(
userList.map<String>((u) => u.id).toSet(),
'm.room_key_request',
{
'action': 'request',
'body': {
'algorithm': 'm.megolm.v1.aes-sha2',
'room_id': room.id,
'sender_key': senderKey,
'session_id': sessionId,
},
encrypted: false,
toUsers: await room.requestParticipants());
'request_id': requestId,
'requesting_device_id': client.deviceID,
},
);
outgoingShareRequests[request.requestId] = request;
} catch (e, s) {
Logs.error(
@ -568,15 +568,24 @@ class KeyManager {
if (request.devices.isEmpty) {
return; // no need to send any cancellation
}
// Send with send-to-device messaging
final sendToDeviceMessage = {
'action': 'request_cancellation',
'request_id': request.requestId,
'requesting_device_id': client.deviceID,
};
var data = <String, Map<String, Map<String, dynamic>>>{};
for (final device in request.devices) {
if (!data.containsKey(device.userId)) {
data[device.userId] = {};
}
data[device.userId][device.deviceId] = sendToDeviceMessage;
}
await client.sendToDevice(
request.devices,
'm.room_key_request',
{
'action': 'request_cancellation',
'request_id': request.requestId,
'requesting_device_id': client.deviceID,
},
encrypted: false);
'm.room_key_request',
client.generateUniqueTransactionId(),
data,
);
} else if (event.type == 'm.room_key') {
if (event.encryptedContent == null) {
return; // the event wasn't encrypted, this is a security risk;
@ -675,7 +684,7 @@ class RoomKeyRequest extends ToDeviceEvent {
message['session_key'] = session.inboundGroupSession
.export_session(session.inboundGroupSession.first_known_index());
// send the actual reply of the key back to the requester
await keyManager.client.sendToDevice(
await keyManager.client.sendToDeviceEncrypted(
[requestingDevice],
'm.forwarded_room_key',
message,

View File

@ -183,7 +183,7 @@ class OlmManager {
signJson(keysContent['device_keys'] as Map<String, dynamic>);
}
final response = await client.api.uploadDeviceKeys(
final response = await client.uploadDeviceKeys(
deviceKeys: uploadDeviceKeys
? MatrixDeviceKeys.fromJson(keysContent['device_keys'])
: null,
@ -335,7 +335,7 @@ class OlmManager {
return;
}
await startOutgoingOlmSessions([device]);
await client.sendToDevice([device], 'm.dummy', {});
await client.sendToDeviceEncrypted([device], 'm.dummy', {});
}
Future<ToDeviceEvent> decryptToDeviceEvent(ToDeviceEvent event) async {
@ -383,7 +383,7 @@ class OlmManager {
}
final response =
await client.api.requestOneTimeKeys(requestingKeysFrom, timeout: 10000);
await client.requestOneTimeKeys(requestingKeysFrom, timeout: 10000);
for (var userKeysEntry in response.oneTimeKeys.entries) {
final userId = userKeysEntry.key;

View File

@ -222,7 +222,7 @@ class SSSS {
'mac': encrypted.mac,
};
// store the thing in your account data
await client.api.setAccountData(client.userID, type, content);
await client.setAccountData(client.userID, type, content);
if (CACHE_TYPES.contains(type) && client.database != null) {
// cache the thing
await client.database
@ -271,7 +271,7 @@ class SSSS {
devices: devices,
);
pendingShareRequests[requestId] = request;
await client.sendToDevice(devices, 'm.secret.request', {
await client.sendToDeviceEncrypted(devices, 'm.secret.request', {
'action': 'request',
'requesting_device_id': client.deviceID,
'request_id': requestId,
@ -308,7 +308,7 @@ class SSSS {
}
// okay, all checks out...time to share this secret!
Logs.info('[SSSS] Replying with secret for ${type}');
await client.sendToDevice(
await client.sendToDeviceEncrypted(
[device],
'm.secret.send',
{

View File

@ -571,7 +571,7 @@ class KeyVerification {
} else {
Logs.info(
'[Key Verification] Sending to ${userId} device ${deviceId}...');
await client.sendToDevice(
await client.sendToDeviceEncrypted(
[client.userDeviceKeys[userId].deviceKeys[deviceId]], type, payload);
}
}

View File

@ -1329,8 +1329,11 @@ class MatrixApi {
/// This endpoint is used to send send-to-device events to a set of client devices.
/// https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-sendtodevice-eventtype-txnid
Future<void> sendToDevice(String eventType, String txnId,
Map<String, Map<String, Map<String, dynamic>>> messages) async {
Future<void> sendToDevice(
String eventType,
String txnId,
Map<String, Map<String, Map<String, dynamic>>> messages,
) async {
await request(
RequestType.PUT,
'/client/r0/sendToDevice/${Uri.encodeComponent(eventType)}/${Uri.encodeComponent(txnId)}',

View File

@ -22,7 +22,6 @@ import 'dart:core';
import 'package:famedlysdk/encryption.dart';
import 'package:famedlysdk/famedlysdk.dart';
import 'package:famedlysdk/matrix_api.dart';
import 'package:famedlysdk/src/room.dart';
import 'package:famedlysdk/src/utils/device_keys_list.dart';
import 'package:famedlysdk/src/utils/logs.dart';
@ -45,7 +44,7 @@ enum LoginState { logged, loggedOut }
/// Represents a Matrix client to communicate with a
/// [Matrix](https://matrix.org) homeserver and is the entry point for this
/// SDK.
class Client {
class Client extends MatrixApi {
int _id;
int get id => _id;
@ -53,7 +52,8 @@ class Client {
bool enableE2eeRecovery;
MatrixApi api;
@deprecated
MatrixApi get api => this;
Encryption encryption;
@ -103,7 +103,7 @@ class Client {
EventTypes.RoomCanonicalAlias,
EventTypes.RoomTombstone,
]);
api = MatrixApi(httpClient: httpClient);
this.httpClient = httpClient;
}
/// The required name for this client.
@ -125,7 +125,7 @@ class Client {
String _deviceName;
/// Returns the current login state.
bool isLogged() => api.accessToken != null;
bool isLogged() => accessToken != null;
/// A list of all rooms the user is participating or invited.
List<Room> get rooms => _rooms;
@ -160,21 +160,6 @@ class Client {
int _transactionCounter = 0;
@Deprecated('Use [api.request()] instead')
Future<Map<String, dynamic>> jsonRequest(
{RequestType type,
String action,
dynamic data = '',
int timeout,
String contentType = 'application/json'}) =>
api.request(
type,
action,
data: data,
timeout: timeout,
contentType: contentType,
);
String generateUniqueTransactionId() {
_transactionCounter++;
return '${clientName}-${_transactionCounter}-${DateTime.now().millisecondsSinceEpoch}';
@ -243,7 +228,7 @@ class Client {
Future<bool> checkServer(dynamic serverUrl) async {
try {
if (serverUrl is Uri) {
api.homeserver = serverUrl;
homeserver = serverUrl;
} else {
// 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
@ -253,9 +238,9 @@ class Client {
if (serverUrl.endsWith('/')) {
serverUrl = serverUrl.substring(0, serverUrl.length - 1);
}
api.homeserver = Uri.parse(serverUrl);
homeserver = Uri.parse(serverUrl);
}
final versions = await api.requestSupportedVersions();
final versions = await requestSupportedVersions();
for (var i = 0; i < versions.versions.length; i++) {
if (versions.versions[i] == 'r0.5.0' ||
@ -266,7 +251,7 @@ class Client {
}
}
final loginTypes = await api.requestLoginTypes();
final loginTypes = await requestLoginTypes();
if (loginTypes.flows.indexWhere((f) => f.type == 'm.login.password') ==
-1) {
return false;
@ -274,7 +259,7 @@ class Client {
return true;
} catch (_) {
api.homeserver = null;
homeserver = null;
rethrow;
}
}
@ -282,16 +267,17 @@ class Client {
/// Checks to see if a username is available, and valid, for the server.
/// Returns the fully-qualified Matrix user ID (MXID) that has been registered.
/// You have to call [checkServer] first to set a homeserver.
Future<void> register({
String kind,
@override
Future<LoginResponse> register({
String username,
String password,
Map<String, dynamic> auth,
String deviceId,
String initialDeviceDisplayName,
bool inhibitLogin,
Map<String, dynamic> auth,
String kind,
}) async {
final response = await api.register(
final response = await super.register(
username: username,
password: password,
auth: auth,
@ -309,66 +295,62 @@ class Client {
await connect(
newToken: response.accessToken,
newUserID: response.userId,
newHomeserver: api.homeserver,
newHomeserver: homeserver,
newDeviceName: initialDeviceDisplayName ?? '',
newDeviceID: response.deviceId);
return;
return response;
}
/// Handles the login and allows the client to call all APIs which require
/// authentication. Returns false if the login was not successful. Throws
/// MatrixException if login was not successful.
/// You have to call [checkServer] first to set a homeserver.
Future<bool> login(
String username,
String password, {
String initialDeviceDisplayName,
@override
Future<LoginResponse> login({
String type = 'm.login.password',
String userIdentifierType = 'm.id.user',
String user,
String medium,
String address,
String password,
String token,
String deviceId,
String initialDeviceDisplayName,
}) async {
var data = <String, dynamic>{
'type': 'm.login.password',
'user': username,
'identifier': {
'type': 'm.id.user',
'user': username,
},
'password': password,
};
if (deviceId != null) data['device_id'] = deviceId;
if (initialDeviceDisplayName != null) {
data['initial_device_display_name'] = initialDeviceDisplayName;
}
final loginResp = await api.login(
type: 'm.login.password',
userIdentifierType: 'm.id.user',
user: username,
final loginResp = await super.login(
type: type,
userIdentifierType: userIdentifierType,
user: user,
password: password,
deviceId: deviceId,
initialDeviceDisplayName: initialDeviceDisplayName,
medium: medium,
address: address,
token: token,
);
// Connect if there is an access token in the response.
if (loginResp.accessToken == null ||
loginResp.deviceId == null ||
loginResp.userId == null) {
throw 'Registered but token, device ID or user ID is null.';
throw Exception('Registered but token, device ID or user ID is null.');
}
await connect(
newToken: loginResp.accessToken,
newUserID: loginResp.userId,
newHomeserver: api.homeserver,
newHomeserver: homeserver,
newDeviceName: initialDeviceDisplayName ?? '',
newDeviceID: loginResp.deviceId,
);
return true;
return loginResp;
}
/// Sends a logout command to the homeserver and clears all local data,
/// including all persistent data from the store.
@override
Future<void> logout() async {
try {
await api.logout();
await super.logout();
} catch (e, s) {
Logs.error(e, s);
rethrow;
@ -421,19 +403,19 @@ class Client {
if (cache && _profileCache.containsKey(userId)) {
return _profileCache[userId];
}
final profile = await api.requestProfile(userId);
final profile = await requestProfile(userId);
_profileCache[userId] = profile;
return profile;
}
Future<List<Room>> get archive async {
var archiveList = <Room>[];
final sync = await api.sync(
final syncResp = await sync(
filter: '{"room":{"include_leave":true,"timeline":{"limit":10}}}',
timeout: 0,
);
if (sync.rooms.leave is Map<String, dynamic>) {
for (var entry in sync.rooms.leave.entries) {
if (syncResp.rooms.leave is Map<String, dynamic>) {
for (var entry in syncResp.rooms.leave.entries) {
final id = entry.key;
final room = entry.value;
var leftRoom = Room(
@ -460,14 +442,10 @@ class Client {
return archiveList;
}
/// Changes the user's displayname.
Future<void> setDisplayname(String displayname) =>
api.setDisplayname(userID, displayname);
/// Uploads a new user avatar for this user.
Future<void> setAvatar(MatrixFile file) async {
final uploadResp = await api.upload(file.bytes, file.name);
await api.setAvatarUrl(userID, Uri.parse(uploadResp));
final uploadResp = await upload(file.bytes, file.name);
await setAvatarUrl(userID, Uri.parse(uploadResp));
return;
}
@ -550,10 +528,6 @@ class Client {
final StreamController<KeyVerification> onKeyVerificationRequest =
StreamController.broadcast();
/// Matrix synchronisation is done with https long polling. This needs a
/// timeout which is usually 30 seconds.
int syncTimeoutSec = 30;
/// How long should the app wait until it retrys the synchronisation after
/// an error?
int syncErrorTimeoutSec = 3;
@ -575,7 +549,7 @@ class Client {
/// "type": "m.login.password",
/// "user": "test",
/// "password": "1234",
/// "initial_device_display_name": "Fluffy Matrix Client"
/// "initial_device_display_name": "Matrix Client"
/// });
/// ```
///
@ -604,8 +578,8 @@ class Client {
final account = await database.getClient(clientName);
if (account != null) {
_id = account.clientId;
api.homeserver = Uri.parse(account.homeserverUrl);
api.accessToken = account.token;
homeserver = Uri.parse(account.homeserverUrl);
accessToken = account.token;
_userID = account.userId;
_deviceID = account.deviceId;
_deviceName = account.deviceName;
@ -613,15 +587,15 @@ class Client {
olmAccount = account.olmAccount;
}
}
api.accessToken = newToken ?? api.accessToken;
api.homeserver = newHomeserver ?? api.homeserver;
accessToken = newToken ?? accessToken;
homeserver = newHomeserver ?? homeserver;
_userID = newUserID ?? _userID;
_deviceID = newDeviceID ?? _deviceID;
_deviceName = newDeviceName ?? _deviceName;
prevBatch = newPrevBatch ?? prevBatch;
olmAccount = newOlmAccount ?? olmAccount;
if (api.accessToken == null || api.homeserver == null || _userID == null) {
if (accessToken == null || homeserver == null || _userID == null) {
// we aren't logged in
encryption?.dispose();
encryption = null;
@ -636,8 +610,8 @@ class Client {
if (database != null) {
if (id != null) {
await database.updateClient(
api.homeserver.toString(),
api.accessToken,
homeserver.toString(),
accessToken,
_userID,
_deviceID,
_deviceName,
@ -648,8 +622,8 @@ class Client {
} else {
_id = await database.insertClient(
clientName,
api.homeserver.toString(),
api.accessToken,
homeserver.toString(),
accessToken,
_userID,
_deviceID,
_deviceName,
@ -666,7 +640,7 @@ class Client {
onLoginStateChanged.add(LoginState.logged);
Logs.success(
'Successfully connected as ${userID.localpart} with ${api.homeserver.toString()}',
'Successfully connected as ${userID.localpart} with ${homeserver.toString()}',
);
return _sync();
@ -680,8 +654,8 @@ class Client {
/// Resets all settings and stops the synchronisation.
void clear() {
database?.clear(id);
_id = api.accessToken =
api.homeserver = _userID = _deviceID = _deviceName = prevBatch = null;
_id = accessToken =
homeserver = _userID = _deviceID = _deviceName = prevBatch = null;
_rooms = [];
encryption?.dispose();
encryption = null;
@ -694,13 +668,11 @@ class Client {
Future<void> _sync() async {
if (isLogged() == false || _disposed) return;
try {
_syncRequest = api
.sync(
_syncRequest = sync(
filter: syncFilters,
since: prevBatch,
timeout: prevBatch != null ? 30000 : null,
)
.catchError((e) {
).catchError((e) {
_lastSyncError = e;
return null;
});
@ -1197,8 +1169,7 @@ class Client {
if (outdatedLists.isNotEmpty) {
// Request the missing device key lists from the server.
final response =
await api.requestDeviceKeys(outdatedLists, timeout: 10000);
final response = await requestDeviceKeys(outdatedLists, timeout: 10000);
for (final rawDeviceKeyListEntry in response.deviceKeys.entries) {
final userId = rawDeviceKeyListEntry.key;
@ -1347,17 +1318,35 @@ class Client {
}
}
/// Send an (unencrypted) to device [message] of a specific [eventType] to all
/// devices of a set of [users].
Future<void> sendToDevicesOfUserIds(
Set<String> users,
String eventType,
Map<String, dynamic> message, {
String messageId,
}) async {
// Send with send-to-device messaging
var data = <String, Map<String, Map<String, dynamic>>>{};
for (var user in users) {
data[user] = {};
data[user]['*'] = message;
}
await sendToDevice(
eventType, messageId ?? generateUniqueTransactionId(), data);
return;
}
/// Sends an encrypted [message] of this [type] to these [deviceKeys]. To send
/// the request to all devices of the current user, pass an empty list to [deviceKeys].
Future<void> sendToDevice(
Future<void> sendToDeviceEncrypted(
List<DeviceKeys> deviceKeys,
String type,
String eventType,
Map<String, dynamic> message, {
bool encrypted = true,
List<User> toUsers,
String messageId,
bool onlyVerified = false,
}) async {
if (encrypted && !encryptionEnabled) return;
if (!encryptionEnabled) return;
// Don't send this message to blocked devices, and if specified onlyVerified
// then only send it to verified devices
if (deviceKeys.isNotEmpty) {
@ -1368,36 +1357,13 @@ class Client {
if (deviceKeys.isEmpty) return;
}
var sendToDeviceMessage = message;
// Send with send-to-device messaging
var data = <String, Map<String, Map<String, dynamic>>>{};
if (deviceKeys.isEmpty) {
if (toUsers == null) {
data[userID] = {};
data[userID]['*'] = sendToDeviceMessage;
} else {
for (var user in toUsers) {
data[user.id] = {};
data[user.id]['*'] = sendToDeviceMessage;
}
}
} else {
if (encrypted) {
data =
await encryption.encryptToDeviceMessage(deviceKeys, type, message);
} else {
for (final device in deviceKeys) {
if (!data.containsKey(device.userId)) {
data[device.userId] = {};
}
data[device.userId][device.deviceId] = sendToDeviceMessage;
}
}
}
if (encrypted) type = EventTypes.Encrypted;
final messageID = generateUniqueTransactionId();
await api.sendToDevice(type, messageID, data);
data =
await encryption.encryptToDeviceMessage(deviceKeys, eventType, message);
eventType = EventTypes.Encrypted;
await sendToDevice(
eventType, messageId ?? generateUniqueTransactionId(), data);
}
/// Whether all push notifications are muted using the [.m.rule.master]
@ -1422,7 +1388,7 @@ class Client {
}
Future<void> setMuteAllPushNotifications(bool muted) async {
await api.enablePushRule(
await enablePushRule(
'global',
PushRuleKind.override,
'.m.rule.master',
@ -1432,6 +1398,7 @@ class Client {
}
/// Changes the password. You should either set oldPasswort or another authentication flow.
@override
Future<void> changePassword(String newPassword,
{String oldPassword, Map<String, dynamic> auth}) async {
try {
@ -1442,7 +1409,7 @@ class Client {
'password': oldPassword,
};
}
await api.changePassword(newPassword, auth: auth);
await super.changePassword(newPassword, auth: auth);
} on MatrixException catch (matrixException) {
if (!matrixException.requireAdditionalAuthentication) {
rethrow;

View File

@ -374,21 +374,21 @@ class Room {
/// Call the Matrix API to change the name of this room. Returns the event ID of the
/// new m.room.name event.
Future<String> setName(String newName) => client.api.sendState(
Future<String> setName(String newName) => client.sendState(
id,
EventTypes.RoomName,
{'name': newName},
);
/// Call the Matrix API to change the topic of this room.
Future<String> setDescription(String newName) => client.api.sendState(
Future<String> setDescription(String newName) => client.sendState(
id,
EventTypes.RoomTopic,
{'topic': newName},
);
/// Add a tag to the room.
Future<void> addTag(String tag, {double order}) => client.api.addRoomTag(
Future<void> addTag(String tag, {double order}) => client.addRoomTag(
client.userID,
id,
tag,
@ -396,7 +396,7 @@ class Room {
);
/// Removes a tag from the room.
Future<void> removeTag(String tag) => client.api.removeRoomTag(
Future<void> removeTag(String tag) => client.removeRoomTag(
client.userID,
id,
tag,
@ -423,7 +423,7 @@ class Room {
/// Call the Matrix API to change the pinned events of this room.
Future<String> setPinnedEvents(List<String> pinnedEventIds) =>
client.api.sendState(
client.sendState(
id,
EventTypes.RoomPinnedEvents,
{'pinned': pinnedEventIds},
@ -565,13 +565,13 @@ class Room {
uploadThumbnail = encryptedThumbnail.toMatrixFile();
}
}
final uploadResp = await client.api.upload(
final uploadResp = await client.upload(
uploadFile.bytes,
uploadFile.name,
contentType: uploadFile.mimeType,
);
final thumbnailUploadResp = uploadThumbnail != null
? await client.api.upload(
? await client.upload(
uploadThumbnail.bytes,
uploadThumbnail.name,
contentType: uploadThumbnail.mimeType,
@ -705,7 +705,7 @@ class Room {
? await client.encryption
.encryptGroupMessagePayload(id, content, type: type)
: content;
final res = await client.api.sendMessage(
final res = await client.sendMessage(
id,
sendType,
messageID,
@ -731,7 +731,7 @@ class Room {
/// automatically be set.
Future<void> join() async {
try {
await client.api.joinRoom(id);
await client.joinRoom(id);
final invitation = getState(EventTypes.RoomMember, client.userID);
if (invitation != null &&
invitation.content['is_direct'] is bool &&
@ -757,25 +757,25 @@ class Room {
/// chat, this will be removed too.
Future<void> leave() async {
if (directChatMatrixID != '') await removeFromDirectChat();
await client.api.leaveRoom(id);
await client.leaveRoom(id);
return;
}
/// Call the Matrix API to forget this room if you already left it.
Future<void> forget() async {
await client.database?.forgetRoom(client.id, id);
await client.api.forgetRoom(id);
await client.forgetRoom(id);
return;
}
/// Call the Matrix API to kick a user from this room.
Future<void> kick(String userID) => client.api.kickFromRoom(id, userID);
Future<void> kick(String userID) => client.kickFromRoom(id, userID);
/// Call the Matrix API to ban a user from this room.
Future<void> ban(String userID) => client.api.banFromRoom(id, userID);
Future<void> ban(String userID) => client.banFromRoom(id, userID);
/// Call the Matrix API to unban a banned user from this room.
Future<void> unban(String userID) => client.api.unbanInRoom(id, userID);
Future<void> unban(String userID) => client.unbanInRoom(id, userID);
/// Set the power level of the user with the [userID] to the value [power].
/// Returns the event ID of the new state event. If there is no known
@ -787,7 +787,7 @@ class Room {
if (powerMap['users'] == null) powerMap['users'] = {};
powerMap['users'][userID] = power;
return await client.api.sendState(
return await client.sendState(
id,
EventTypes.RoomPowerLevels,
powerMap,
@ -795,14 +795,14 @@ class Room {
}
/// Call the Matrix API to invite a user to this room.
Future<void> invite(String userID) => client.api.inviteToRoom(id, userID);
Future<void> invite(String userID) => client.inviteToRoom(id, userID);
/// 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**
/// the historical events will be published in the onEvent stream.
Future<void> requestHistory(
{int historyCount = DefaultHistoryCount, onHistoryReceived}) async {
final resp = await client.api.requestMessages(
final resp = await client.requestMessages(
id,
prev_batch,
Direction.b,
@ -853,7 +853,7 @@ class Room {
directChats[userID] = [id];
}
await client.api.setAccountData(
await client.setAccountData(
client.userID,
'm.direct',
directChats,
@ -871,7 +871,7 @@ class Room {
return;
} // Nothing to do here
await client.api.setRoomAccountData(
await client.setRoomAccountData(
client.userID,
id,
'm.direct',
@ -884,7 +884,7 @@ class Room {
Future<void> sendReadReceipt(String eventID) async {
notificationCount = 0;
await client.database?.resetNotificationCount(client.id, id);
await client.api.sendReadMarker(
await client.sendReadMarker(
id,
eventID,
readReceiptLocationEventId: eventID,
@ -1017,7 +1017,7 @@ class Room {
}
}
if (participantListComplete) return getParticipants();
final matrixEvents = await client.api.requestMembers(id);
final matrixEvents = await client.requestMembers(id);
final users =
matrixEvents.map((e) => Event.fromMatrixEvent(e, this).asUser).toList();
for (final user in users) {
@ -1080,7 +1080,7 @@ class Room {
if (mxID == null || !_requestingMatrixIds.add(mxID)) return null;
Map<String, dynamic> resp;
try {
resp = await client.api.requestStateContent(
resp = await client.requestStateContent(
id,
EventTypes.RoomMember,
mxID,
@ -1093,7 +1093,7 @@ class Room {
}
if (resp == null && requestProfile) {
try {
final profile = await client.api.requestProfile(mxID);
final profile = await client.requestProfile(mxID);
resp = {
'displayname': profile.displayname,
'avatar_url': profile.avatarUrl,
@ -1135,7 +1135,7 @@ class Room {
/// Searches for the event on the server. Returns null if not found.
Future<Event> getEventById(String eventID) async {
final matrixEvent = await client.api.requestEvent(id, eventID);
final matrixEvent = await client.requestEvent(id, eventID);
return Event.fromMatrixEvent(matrixEvent, this);
}
@ -1169,8 +1169,8 @@ class Room {
/// Uploads a new user avatar for this room. Returns the event ID of the new
/// m.room.avatar event.
Future<String> setAvatar(MatrixFile file) async {
final uploadResp = await client.api.upload(file.bytes, file.name);
return await client.api.sendState(
final uploadResp = await client.upload(file.bytes, file.name);
return await client.sendState(
id,
EventTypes.RoomAvatar,
{'url': uploadResp},
@ -1267,23 +1267,23 @@ class Room {
// All push notifications should be sent to the user
case PushRuleState.notify:
if (pushRuleState == PushRuleState.dont_notify) {
await client.api.deletePushRule('global', PushRuleKind.override, id);
await client.deletePushRule('global', PushRuleKind.override, id);
} else if (pushRuleState == PushRuleState.mentions_only) {
await client.api.deletePushRule('global', PushRuleKind.room, id);
await client.deletePushRule('global', PushRuleKind.room, id);
}
break;
// Only when someone mentions the user, a push notification should be sent
case PushRuleState.mentions_only:
if (pushRuleState == PushRuleState.dont_notify) {
await client.api.deletePushRule('global', PushRuleKind.override, id);
await client.api.setPushRule(
await client.deletePushRule('global', PushRuleKind.override, id);
await client.setPushRule(
'global',
PushRuleKind.room,
id,
[PushRuleAction.dont_notify],
);
} else if (pushRuleState == PushRuleState.notify) {
await client.api.setPushRule(
await client.setPushRule(
'global',
PushRuleKind.room,
id,
@ -1294,9 +1294,9 @@ class Room {
// No push notification should be ever sent for this room.
case PushRuleState.dont_notify:
if (pushRuleState == PushRuleState.mentions_only) {
await client.api.deletePushRule('global', PushRuleKind.room, id);
await client.deletePushRule('global', PushRuleKind.room, id);
}
await client.api.setPushRule(
await client.setPushRule(
'global',
PushRuleKind.override,
id,
@ -1322,7 +1322,7 @@ class Room {
}
var data = <String, dynamic>{};
if (reason != null) data['reason'] = reason;
return await client.api.redact(
return await client.redact(
id,
eventId,
messageID,
@ -1335,7 +1335,7 @@ class Room {
'typing': isTyping,
};
if (timeout != null) data['timeout'] = timeout;
return client.api.sendTypingNotification(client.userID, id, isTyping);
return client.sendTypingNotification(client.userID, id, isTyping);
}
/// This is sent by the caller when they wish to establish a call.
@ -1349,7 +1349,7 @@ class Room {
{String type = 'offer', int version = 0, String txid}) async {
txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}';
return await client.api.sendMessage(
return await client.sendMessage(
id,
EventTypes.CallInvite,
txid,
@ -1387,7 +1387,7 @@ class Room {
String txid,
}) async {
txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}';
return await client.api.sendMessage(
return await client.sendMessage(
id,
EventTypes.CallCandidates,
txid,
@ -1407,7 +1407,7 @@ class Room {
Future<String> answerCall(String callId, String sdp,
{String type = 'answer', int version = 0, String txid}) async {
txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}';
return await client.api.sendMessage(
return await client.sendMessage(
id,
EventTypes.CallAnswer,
txid,
@ -1425,7 +1425,7 @@ class Room {
Future<String> hangupCall(String callId,
{int version = 0, String txid}) async {
txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}';
return await client.api.sendMessage(
return await client.sendMessage(
id,
EventTypes.CallHangup,
txid,
@ -1461,7 +1461,7 @@ class Room {
/// Changes the join rules. You should check first if the user is able to change it.
Future<void> setJoinRules(JoinRules joinRules) async {
await client.api.sendState(
await client.sendState(
id,
EventTypes.RoomJoinRules,
{
@ -1486,7 +1486,7 @@ class Room {
/// Changes the guest access. You should check first if the user is able to change it.
Future<void> setGuestAccess(GuestAccess guestAccess) async {
await client.api.sendState(
await client.sendState(
id,
EventTypes.GuestAccess,
{
@ -1512,7 +1512,7 @@ class Room {
/// Changes the history visibility. You should check first if the user is able to change it.
Future<void> setHistoryVisibility(HistoryVisibility historyVisibility) async {
await client.api.sendState(
await client.sendState(
id,
EventTypes.HistoryVisibility,
{
@ -1539,7 +1539,7 @@ class Room {
Future<void> enableEncryption({int algorithmIndex = 0}) async {
if (encrypted) throw ('Encryption is already enabled!');
final algorithm = Client.supportedGroupEncryptionAlgorithms[algorithmIndex];
await client.api.sendState(
await client.sendState(
id,
EventTypes.Encryption,
{

View File

@ -146,7 +146,7 @@ class User extends Event {
if (roomID != null) return roomID;
// Start a new direct chat
final newRoomID = await room.client.api.createRoom(
final newRoomID = await room.client.createRoom(
invite: [id],
isDirect: true,
preset: CreateRoomPreset.trusted_private_chat,

View File

@ -22,8 +22,8 @@ import 'dart:core';
extension MxcUriExtension on Uri {
/// Returns a download Link to this content.
String getDownloadLink(Client matrix) => isScheme('mxc')
? matrix.api.homeserver != null
? '${matrix.api.homeserver.toString()}/_matrix/media/r0/download/$host$path'
? matrix.homeserver != null
? '${matrix.homeserver.toString()}/_matrix/media/r0/download/$host$path'
: ''
: toString();
@ -36,8 +36,8 @@ extension MxcUriExtension on Uri {
final methodStr = method.toString().split('.').last;
width = width.round();
height = height.round();
return matrix.api.homeserver != null
? '${matrix.api.homeserver.toString()}/_matrix/media/r0/thumbnail/$host$path?width=$width&height=$height&method=$methodStr'
return matrix.homeserver != null
? '${matrix.homeserver.toString()}/_matrix/media/r0/thumbnail/$host$path?width=$width&height=$height&method=$methodStr'
: '';
}
}

View File

@ -46,7 +46,7 @@ void main() {
const fingerprintKey = 'gjL//fyaFHADt9KBADGag8g7F8Up78B/K1zXeiEPLJo';
/// All Tests related to the Login
group('FluffyMatrix', () {
group('Client', () {
/// Check if all Elements get created
matrix = Client('testclient', httpClient: FakeMatrixApi());
@ -74,7 +74,7 @@ void main() {
accountDataCounter++;
});
expect(matrix.api.homeserver, null);
expect(matrix.homeserver, null);
try {
await matrix.checkServer('https://fakeserver.wrongaddress');
@ -82,17 +82,9 @@ void main() {
expect(exception != null, true);
}
await matrix.checkServer('https://fakeserver.notexisting');
expect(
matrix.api.homeserver.toString(), 'https://fakeserver.notexisting');
expect(matrix.homeserver.toString(), 'https://fakeserver.notexisting');
final resp = await matrix.api.login(
type: 'm.login.password',
user: 'test',
password: '1234',
initialDeviceDisplayName: 'Fluffy Matrix Client',
);
final available = await matrix.api.usernameAvailable('testuser');
final available = await matrix.usernameAvailable('testuser');
expect(available, true);
var loginStateFuture = matrix.onLoginStateChanged.stream.first;
@ -100,21 +92,16 @@ void main() {
var syncFuture = matrix.onSync.stream.first;
matrix.connect(
newToken: resp.accessToken,
newUserID: resp.userId,
newHomeserver: matrix.api.homeserver,
newToken: 'abcd',
newUserID: '@test:fakeServer.notExisting',
newHomeserver: matrix.homeserver,
newDeviceName: 'Text Matrix Client',
newDeviceID: resp.deviceId,
newDeviceID: 'GHTYAJCE',
newOlmAccount: pickledOlmAccount,
);
await Future.delayed(Duration(milliseconds: 50));
expect(matrix.api.accessToken == resp.accessToken, true);
expect(matrix.deviceName == 'Text Matrix Client', true);
expect(matrix.deviceID == resp.deviceId, true);
expect(matrix.userID == resp.userId, true);
var loginState = await loginStateFuture;
var firstSync = await firstSyncFuture;
var sync = await syncFuture;
@ -208,14 +195,11 @@ void main() {
});
test('Logout', () async {
await matrix.api.logout();
var loginStateFuture = matrix.onLoginStateChanged.stream.first;
await matrix.logout();
matrix.clear();
expect(matrix.api.accessToken == null, true);
expect(matrix.api.homeserver == null, true);
expect(matrix.accessToken == null, true);
expect(matrix.homeserver == null, true);
expect(matrix.userID == null, true);
expect(matrix.deviceID == null, true);
expect(matrix.deviceName == null, true);
@ -330,10 +314,10 @@ void main() {
final checkResp =
await matrix.checkServer('https://fakeServer.notExisting');
final loginResp = await matrix.login('test', '1234');
final loginResp = await matrix.login(user: 'test', password: '1234');
expect(checkResp, true);
expect(loginResp, true);
expect(loginResp != null, true);
});
test('setAvatar', () async {
@ -386,8 +370,8 @@ void main() {
}
}
}, matrix);
test('sendToDevice', () async {
await matrix.sendToDevice(
test('sendToDeviceEncrypted', () async {
await matrix.sendToDeviceEncrypted(
[deviceKeys],
'm.message',
{
@ -420,9 +404,9 @@ void main() {
await Future.delayed(Duration(milliseconds: 100));
expect(client2.isLogged(), true);
expect(client2.api.accessToken, client1.api.accessToken);
expect(client2.accessToken, client1.accessToken);
expect(client2.userID, client1.userID);
expect(client2.api.homeserver, client1.api.homeserver);
expect(client2.homeserver, client1.homeserver);
expect(client2.deviceID, client1.deviceID);
expect(client2.deviceName, client1.deviceName);
if (client2.encryptionEnabled) {

View File

@ -54,7 +54,7 @@ void main() {
otherClient.connect(
newToken: 'abc',
newUserID: '@othertest:fakeServer.notExisting',
newHomeserver: otherClient.api.homeserver,
newHomeserver: otherClient.homeserver,
newDeviceName: 'Text Matrix Client',
newDeviceID: 'FOXDEVICE',
newOlmAccount: otherPickledOlmAccount,

View File

@ -89,7 +89,7 @@ void main() {
client2.connect(
newToken: 'abc',
newUserID: '@othertest:fakeServer.notExisting',
newHomeserver: client2.api.homeserver,
newHomeserver: client2.homeserver,
newDeviceName: 'Text Matrix Client',
newDeviceID: 'FOXDEVICE',
newOlmAccount: otherPickledOlmAccount,

View File

@ -246,7 +246,7 @@ void main() {
test('sendAgain', () async {
var matrix = Client('testclient', httpClient: FakeMatrixApi());
await matrix.checkServer('https://fakeServer.notExisting');
await matrix.login('test', '1234');
await matrix.login(user: 'test', password: '1234');
var event = Event.fromJson(
jsonObj, Room(id: '!1234:example.com', client: matrix));
@ -262,7 +262,7 @@ void main() {
test('requestKey', () async {
var matrix = Client('testclient', httpClient: FakeMatrixApi());
await matrix.checkServer('https://fakeServer.notExisting');
await matrix.login('test', '1234');
await matrix.login(user: 'test', password: '1234');
var event = Event.fromJson(
jsonObj, Room(id: '!1234:example.com', client: matrix));

View File

@ -32,18 +32,12 @@ Future<Client> getClient() async {
final client = Client('testclient', httpClient: FakeMatrixApi());
client.database = getDatabase();
await client.checkServer('https://fakeServer.notExisting');
final resp = await client.api.login(
type: 'm.login.password',
user: 'test',
password: '1234',
initialDeviceDisplayName: 'Fluffy Matrix Client',
);
client.connect(
newToken: resp.accessToken,
newUserID: resp.userId,
newHomeserver: client.api.homeserver,
newToken: 'abcd',
newUserID: '@test:fakeServer.notExisting',
newHomeserver: client.homeserver,
newDeviceName: 'Text Matrix Client',
newDeviceID: resp.deviceId,
newDeviceID: 'GHTYAJCE',
newOlmAccount: pickledOlmAccount,
);
await Future.delayed(Duration(milliseconds: 10));

View File

@ -82,6 +82,10 @@ class FakeMatrixApi extends MockClient {
action.contains(
'/client/r0/rooms/!1234%3AfakeServer.notExisting/send/')) {
res = {'event_id': '\$event${FakeMatrixApi.eventCounter++}'};
} else if (action.contains('/client/r0/sync')) {
res = {
'next_batch': DateTime.now().millisecondsSinceEpoch.toString
};
} else {
res = {
'errcode': 'M_UNRECOGNIZED',

View File

@ -33,13 +33,13 @@ void main() {
expect(content.isScheme('mxc'), true);
expect(content.getDownloadLink(client),
'${client.api.homeserver.toString()}/_matrix/media/r0/download/exampleserver.abc/abcdefghijklmn');
'${client.homeserver.toString()}/_matrix/media/r0/download/exampleserver.abc/abcdefghijklmn');
expect(content.getThumbnail(client, width: 50, height: 50),
'${client.api.homeserver.toString()}/_matrix/media/r0/thumbnail/exampleserver.abc/abcdefghijklmn?width=50&height=50&method=crop');
'${client.homeserver.toString()}/_matrix/media/r0/thumbnail/exampleserver.abc/abcdefghijklmn?width=50&height=50&method=crop');
expect(
content.getThumbnail(client,
width: 50, height: 50, method: ThumbnailMethod.scale),
'${client.api.homeserver.toString()}/_matrix/media/r0/thumbnail/exampleserver.abc/abcdefghijklmn?width=50&height=50&method=scale');
'${client.homeserver.toString()}/_matrix/media/r0/thumbnail/exampleserver.abc/abcdefghijklmn?width=50&height=50&method=scale');
});
});
}

View File

@ -102,7 +102,7 @@ void main() {
});
test('startDirectChat', () async {
await client.checkServer('https://fakeserver.notexisting');
await client.login('test', '1234');
await client.login(user: 'test', password: '1234');
await user1.startDirectChat();
});
test('getPresence', () async {

View File

@ -22,14 +22,14 @@ void test() async {
var testClientA = Client('TestClientA');
testClientA.database = getDatabase();
await testClientA.checkServer(homeserver);
await testClientA.login(testUserA, testPasswordA);
await testClientA.login(user: testUserA, password: testPasswordA);
assert(testClientA.encryptionEnabled);
Logs.success('++++ Login $testUserB ++++');
var testClientB = Client('TestClientB');
testClientB.database = getDatabase();
await testClientB.checkServer(homeserver);
await testClientB.login(testUserB, testPasswordA);
await testClientB.login(user: testUserB, password: testPasswordA);
assert(testClientB.encryptionEnabled);
Logs.success('++++ ($testUserA) Leave all rooms ++++');
@ -72,7 +72,7 @@ void test() async {
.userDeviceKeys[testUserB].deviceKeys[testClientB.deviceID].blocked);
Logs.success('++++ ($testUserA) Create room and invite $testUserB ++++');
await testClientA.api.createRoom(invite: [testUserB]);
await testClientA.createRoom(invite: [testUserB]);
await Future.delayed(Duration(seconds: 1));
var room = testClientA.rooms.first;
assert(room != null);
@ -217,7 +217,7 @@ void test() async {
Logs.success('++++ Login $testUserB in another client ++++');
var testClientC = Client('TestClientC', database: getDatabase());
await testClientC.checkServer(homeserver);
await testClientC.login(testUserB, testPasswordA);
await testClientC.login(user: testUserB, password: testPasswordA);
await Future.delayed(Duration(seconds: 3));
Logs.success(
@ -346,8 +346,8 @@ void test() async {
await Future.delayed(Duration(seconds: 1));
await testClientA.dispose();
await testClientB.dispose();
await testClientA.api.logoutAll();
await testClientB.api.logoutAll();
await testClientA.logoutAll();
await testClientB.logoutAll();
testClientA = null;
testClientB = null;
return;