diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d025bbd4..b9b9a096 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -136,7 +136,6 @@ pages: - public rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - # Adds license-scanning job. Because Gitlab does not support pub.dev # we added https://github.com/oss-review-toolkit/ort diff --git a/lib/encryption/key_manager.dart b/lib/encryption/key_manager.dart index 1ae95baf..a7a91e30 100644 --- a/lib/encryption/key_manager.dart +++ b/lib/encryption/key_manager.dart @@ -18,13 +18,13 @@ import 'dart:convert'; +import 'package:famedlysdk/encryption/utils/stored_inbound_group_session.dart'; import 'package:olm/olm.dart' as olm; import './encryption.dart'; import './utils/outbound_group_session.dart'; import './utils/session_key.dart'; import '../famedlysdk.dart'; -import '../src/database/database.dart'; import '../src/utils/run_in_background.dart'; import '../src/utils/run_in_root.dart'; @@ -231,15 +231,14 @@ class KeyManager { } return sess; // nothing to do } - final session = await client.database - ?.getDbInboundGroupSession(client.id, roomId, sessionId); - if (session == null) { + final sess = await client.database + ?.getInboundGroupSession(client.id, roomId, sessionId, client.userID); + if (sess == null) { return null; } if (!_inboundGroupSessions.containsKey(roomId)) { _inboundGroupSessions[roomId] = {}; } - final sess = SessionKey.fromDb(session, client.userID); if (!sess.isValid || (sess.senderKey.isNotEmpty && sess.senderKey != senderKey)) { return null; @@ -506,13 +505,12 @@ class KeyManager { return; // nothing to do } _loadedOutboundGroupSessions.add(roomId); - final session = - await client.database.getDbOutboundGroupSession(client.id, roomId); - if (session == null) { - return; - } - final sess = OutboundGroupSession.fromDb(session, client.userID); - if (!sess.isValid) { + final sess = await client.database.getOutboundGroupSession( + client.id, + roomId, + client.userID, + ); + if (sess == null || !sess.isValid) { return; } _outboundGroupSessions[roomId] = sess; @@ -693,7 +691,7 @@ class KeyManager { return; // we can't backup anyways } final dbSessions = - await client.database.getInboundGroupSessionsToUpload().get(); + await client.database.getInboundGroupSessionsToUpload(); if (dbSessions.isEmpty) { _haveKeysToUpload = false; return; // nothing to do @@ -1060,7 +1058,7 @@ RoomKeys _generateUploadKeys(_GenerateUploadKeysArgs args) { class _DbInboundGroupSessionBundle { _DbInboundGroupSessionBundle({this.dbSession, this.verified}); - DbInboundGroupSession dbSession; + StoredInboundGroupSession dbSession; bool verified; } diff --git a/lib/encryption/olm_manager.dart b/lib/encryption/olm_manager.dart index a19348bb..cd44266a 100644 --- a/lib/encryption/olm_manager.dart +++ b/lib/encryption/olm_manager.dart @@ -410,16 +410,9 @@ class OlmManager { if (client.database == null) { return []; } - final rows = - await client.database.dbGetOlmSessions(client.id, senderKey).get(); - final res = []; - for (final row in rows) { - final sess = OlmSession.fromDb(row, client.userID); - if (sess.isValid) { - res.add(sess); - } - } - return res; + final olmSessions = await client.database + .getOlmSessions(client.id, senderKey, client.userID); + return olmSessions.where((sess) => sess.isValid).toList(); } Future getOlmSessionsForDevicesFromDatabase( @@ -427,15 +420,16 @@ class OlmManager { if (client.database == null) { return; } - final rows = await client.database - .dbGetOlmSessionsForDevices(client.id, senderKeys) - .get(); + final rows = await client.database.getOlmSessionsForDevices( + client.id, + senderKeys, + client.userID, + ); final res = >{}; - for (final row in rows) { - res[row.identityKey] ??= []; - final sess = OlmSession.fromDb(row, client.userID); + for (final sess in rows) { + res[sess.identityKey] ??= []; if (sess.isValid) { - res[row.identityKey].add(sess); + res[sess.identityKey].add(sess); } } for (final entry in res.entries) { @@ -653,8 +647,7 @@ class OlmManager { '[OlmManager] Device ${device.userId}:${device.deviceId} generated a new olm session, replaying last sent message...'); final lastSentMessageRes = await client.database .getLastSentMessageUserDeviceKey( - client.id, device.userId, device.deviceId) - .get(); + client.id, device.userId, device.deviceId); if (lastSentMessageRes.isEmpty || (lastSentMessageRes.first?.isEmpty ?? true)) { return; diff --git a/lib/encryption/ssss.dart b/lib/encryption/ssss.dart index ed14dda1..8d05c191 100644 --- a/lib/encryption/ssss.dart +++ b/lib/encryption/ssss.dart @@ -25,11 +25,11 @@ import 'package:base58check/base58.dart'; import 'package:crypto/crypto.dart'; import '../famedlysdk.dart'; -import '../src/database/database.dart'; import '../src/utils/crypto/crypto.dart' as uc; import '../src/utils/run_in_background.dart'; import '../src/utils/run_in_root.dart'; import 'encryption.dart'; +import 'utils/ssss_cache.dart'; const cacheTypes = { EventTypes.CrossSigningSelfSigning, @@ -56,7 +56,7 @@ class SSSS { final pendingShareRequests = {}; final _validators = Function(String)>{}; final _cacheCallbacks = Function(String)>{}; - final Map _cache = {}; + final Map _cache = {}; SSSS(this.encryption); // for testing diff --git a/lib/encryption/utils/session_key.dart b/lib/encryption/utils/session_key.dart index 3605b719..29d0af16 100644 --- a/lib/encryption/utils/session_key.dart +++ b/lib/encryption/utils/session_key.dart @@ -16,10 +16,10 @@ * along with this program. If not, see . */ +import 'package:famedlysdk/encryption/utils/stored_inbound_group_session.dart'; import 'package:olm/olm.dart' as olm; import '../../famedlysdk.dart'; -import '../../src/database/database.dart' show DbInboundGroupSession; class SessionKey { /// The raw json content of the key @@ -76,7 +76,7 @@ class SessionKey { allowedAtIndex ??= >{}; } - SessionKey.fromDb(DbInboundGroupSession dbEntry, String key) : key = key { + SessionKey.fromDb(StoredInboundGroupSession dbEntry, String key) : key = key { final parsedContent = Event.getMapFromPayload(dbEntry.content); final parsedIndexes = Event.getMapFromPayload(dbEntry.indexes); final parsedAllowedAtIndex = diff --git a/lib/encryption/utils/ssss_cache.dart b/lib/encryption/utils/ssss_cache.dart new file mode 100644 index 00000000..9ec71a9f --- /dev/null +++ b/lib/encryption/utils/ssss_cache.dart @@ -0,0 +1,44 @@ +/* + * Famedly Matrix SDK + * Copyright (C) 2021 Famedly GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +class SSSSCache { + final int clientId; + final String type; + final String keyId; + final String ciphertext; + final String content; + + const SSSSCache( + {this.clientId, this.type, this.keyId, this.ciphertext, this.content}); + + factory SSSSCache.fromJson(Map json) => SSSSCache( + clientId: json['client_id'], + type: json['type'], + keyId: json['key_id'], + ciphertext: json['ciphertext'], + content: json['content'], + ); + + Map toJson() => { + 'client_id': clientId, + 'type': type, + 'key_id': keyId, + 'ciphertext': ciphertext, + 'content': content, + }; +} diff --git a/lib/encryption/utils/stored_inbound_group_session.dart b/lib/encryption/utils/stored_inbound_group_session.dart new file mode 100644 index 00000000..a74af3b9 --- /dev/null +++ b/lib/encryption/utils/stored_inbound_group_session.dart @@ -0,0 +1,57 @@ +/* + * Famedly Matrix SDK + * Copyright (C) 2021 Famedly GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +class StoredInboundGroupSession { + final int clientId; + final String roomId; + final String sessionId; + final String pickle; + final String content; + final String indexes; + final String allowedAtIndex; + final bool uploaded; + final String senderKey; + final String senderClaimedKeys; + + StoredInboundGroupSession({ + this.clientId, + this.roomId, + this.sessionId, + this.pickle, + this.content, + this.indexes, + this.allowedAtIndex, + this.uploaded, + this.senderKey, + this.senderClaimedKeys, + }); + + factory StoredInboundGroupSession.fromJson(Map json) => + StoredInboundGroupSession( + clientId: json['client_id'], + roomId: json['room_id'], + sessionId: json['session_id'], + pickle: json['pickle'], + content: json['content'], + indexes: json['indexes'], + allowedAtIndex: json['allowed_at_index'], + uploaded: json['uploaded'], + senderKey: json['sender_key'], + senderClaimedKeys: json['sender_claimed_keys'], + ); +} diff --git a/lib/src/client.dart b/lib/src/client.dart index 6dfd04ed..29125916 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -28,7 +28,7 @@ import 'package:pedantic/pedantic.dart'; import '../encryption.dart'; import '../famedlysdk.dart'; -import 'database/database.dart' show Database; +import 'database/database_api.dart'; import 'event.dart'; import 'room.dart'; import 'user.dart'; @@ -56,11 +56,11 @@ class Client extends MatrixApi { int get id => _id; - final FutureOr Function(Client) databaseBuilder; + final FutureOr Function(Client) databaseBuilder; final FutureOr Function(Client) databaseDestroyer; - Database _database; + DatabaseApi _database; - Database get database => _database; + DatabaseApi get database => _database; bool enableE2eeRecovery; @@ -919,7 +919,7 @@ class Client extends MatrixApi { ); } _userDeviceKeys = await database.getUserDeviceKeys(this); - _rooms = await database.getRoomList(this, onlyLeft: false); + _rooms = await database.getRoomList(this); _sortRooms(); accountData = await database.getAccountData(id); presences.clear(); @@ -1753,18 +1753,20 @@ sort order of ${prevState.sortOrder}. This should never happen...'''); if (database == null || !_toDeviceQueueNeedsProcessing) { return; } - final entries = await database.getToDeviceQueue(id).get(); + final entries = await database.getToDeviceEventQueue(id); if (entries.isEmpty) { _toDeviceQueueNeedsProcessing = false; return; } for (final entry in entries) { - // ohgod what is this... - final data = (json.decode(entry.content) as Map).map((k, v) => + // Convert the Json Map to the correct format regarding + // https: //matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-sendtodevice-eventtype-txnid + final data = entry.content.map((k, v) => MapEntry>>( k, (v as Map).map((k, v) => MapEntry>( k, Map.from(v))))); + await super.sendToDevice(entry.type, entry.txnId, data); await database.deleteFromToDeviceQueue(id, entry.id); } diff --git a/lib/src/database/database.dart b/lib/src/database/database.dart index 581f2287..350adc08 100644 --- a/lib/src/database/database.dart +++ b/lib/src/database/database.dart @@ -19,12 +19,19 @@ import 'dart:async'; import 'dart:convert'; +import 'package:famedlysdk/encryption/utils/olm_session.dart'; +import 'package:famedlysdk/encryption/utils/outbound_group_session.dart'; +import 'package:famedlysdk/encryption/utils/session_key.dart'; +import 'package:famedlysdk/encryption/utils/ssss_cache.dart'; +import 'package:famedlysdk/encryption/utils/stored_inbound_group_session.dart'; +import 'package:famedlysdk/src/utils/QueuedToDeviceEvent.dart'; import 'package:moor/moor.dart'; import '../../famedlysdk.dart' as sdk; import 'package:matrix_api_lite/matrix_api_lite.dart' as api; import '../client.dart'; import '../room.dart'; +import 'database_api.dart'; part 'database.g.dart'; @@ -64,7 +71,7 @@ extension MigratorExtension on Migrator { @UseMoor( include: {'database.moor'}, ) -class Database extends _$Database { +class Database extends _$Database implements DatabaseApi { Database(QueryExecutor e) : super(e); Database.connect(DatabaseConnection connection) : super.connect(connection); @@ -72,6 +79,7 @@ class Database extends _$Database { @override int get schemaVersion => 12; + @override int get maxFileSize => 1 * 1024 * 1024; /// Update errors are coming here. @@ -202,6 +210,7 @@ class Database extends _$Database { }, ); + @override Future getClient(String name) async { final res = await dbGetClient(name).get(); if (res.isEmpty) return null; @@ -209,6 +218,7 @@ class Database extends _$Database { return res.single; } + @override Future> getUserDeviceKeys( sdk.Client client) async { final deviceKeys = await getAllUserDeviceKeys(client.id).get(); @@ -228,45 +238,51 @@ class Database extends _$Database { return res; } - Future getDbOutboundGroupSession( - int clientId, String roomId) async { + @override + Future getOutboundGroupSession( + int clientId, String roomId, String userId) async { final res = await dbGetOutboundGroupSession(clientId, roomId).get(); if (res.isEmpty) { return null; } - return res.single; + return OutboundGroupSession.fromDb(res.single, userId); } - Future> getDbInboundGroupSessions( - int clientId, String roomId) async { - return await dbGetInboundGroupSessionKeys(clientId, roomId).get(); - } - - Future getDbInboundGroupSession( - int clientId, String roomId, String sessionId) async { + @override + Future getInboundGroupSession( + int clientId, + String roomId, + String sessionId, + String userId, + ) async { final res = await dbGetInboundGroupSessionKey(clientId, roomId, sessionId).get(); if (res.isEmpty) { return null; } - return res.single; + return SessionKey.fromDb( + StoredInboundGroupSession.fromJson(res.single.toJson()), userId); } - Future getSSSSCache(int clientId, String type) async { + @override + Future getSSSSCache(int clientId, String type) async { final res = await dbGetSSSSCache(clientId, type).get(); if (res.isEmpty) { return null; } - return res.single; + final dbCache = res.single; + return SSSSCache( + ciphertext: dbCache.ciphertext, + clientId: dbCache.clientId, + type: dbCache.type, + keyId: dbCache.keyId, + content: dbCache.content, + ); } - Future> getRoomList(sdk.Client client, - {bool onlyLeft = false}) async { - final res = await (select(rooms) - ..where((t) => onlyLeft - ? t.membership.equals('leave') - : t.membership.equals('leave').not())) - .get(); + @override + Future> getRoomList(sdk.Client client) async { + final res = await select(rooms).get(); final resStates = await getImportantRoomStates( client.id, client.importantStateEvents.toList()) .get(); @@ -358,6 +374,7 @@ class Database extends _$Database { return roomList; } + @override Future> getAccountData(int clientId) async { final newAccountData = {}; final rawAccountData = await getAllAccountData(clientId).get(); @@ -381,6 +398,7 @@ class Database extends _$Database { /// Stores a RoomUpdate object in the database. Must be called inside of /// [transaction]. final Set _ensuredRooms = {}; + @override Future storeRoomUpdate(int clientId, sdk.RoomUpdate roomUpdate, [sdk.Room oldRoom]) async { final setKey = '$clientId;${roomUpdate.id}'; @@ -446,6 +464,7 @@ class Database extends _$Database { /// Stores an EventUpdate object in the database. Must be called inside of /// [transaction]. + @override Future storeEventUpdate( int clientId, sdk.EventUpdate eventUpdate) async { if (eventUpdate.type == sdk.EventUpdateType.ephemeral) return; @@ -579,6 +598,7 @@ class Database extends _$Database { } } + @override Future getEventById( int clientId, String eventId, sdk.Room room) async { final event = await getEvent(clientId, eventId, room.id).get(); @@ -617,6 +637,7 @@ class Database extends _$Database { return success; } + @override Future forgetRoom(int clientId, String roomId) async { final setKey = '$clientId;$roomId'; _ensuredRooms.remove(setKey); @@ -634,6 +655,7 @@ class Database extends _$Database { .go(); } + @override Future clearCache(int clientId) async { await (delete(presences)..where((r) => r.clientId.equals(clientId))).go(); await (delete(roomAccountData)..where((r) => r.clientId.equals(clientId))) @@ -649,6 +671,7 @@ class Database extends _$Database { await storePrevBatch(null, clientId); } + @override Future clear(int clientId) async { await clearCache(clientId); await (delete(inboundGroupSessions) @@ -669,6 +692,7 @@ class Database extends _$Database { .go(); } + @override Future getUser(int clientId, String userId, sdk.Room room) async { final res = await dbGetUser(clientId, userId, room.id).get(); if (res.isEmpty) { @@ -677,19 +701,88 @@ class Database extends _$Database { return sdk.Event.fromDb(res.single, room).asUser; } + @override Future> getUsers(int clientId, sdk.Room room) async { final res = await dbGetUsers(clientId, room.id).get(); return res.map((r) => sdk.Event.fromDb(r, room).asUser).toList(); } + @override Future> getEventList(int clientId, sdk.Room room) async { final res = await dbGetEventList(clientId, room.id).get(); return res.map((r) => sdk.Event.fromDb(r, room)).toList(); } + @override Future getFile(String mxcUri) async { final res = await dbGetFile(mxcUri).get(); if (res.isEmpty) return null; return res.single.bytes; } + + @override + Future> getUnimportantRoomEventStatesForRoom( + int client_id, + List events, + Room room, + ) async { + final entries = await getUnimportantRoomStatesForRoom( + client_id, + room.id, + events, + ).get(); + return entries.map((dbEvent) => sdk.Event.fromDb(dbEvent, room)).toList(); + } + + @override + Future> getOlmSessions( + int client_id, + String identity_key, + String userId, + ) async { + final rows = await dbGetOlmSessions(client_id, identity_key).get(); + return rows.map((row) => OlmSession.fromDb(row, userId)).toList(); + } + + @override + Future> getOlmSessionsForDevices( + int client_id, + List identity_keys, + String userId, + ) async { + final rows = + await dbGetOlmSessionsForDevices(client_id, identity_keys).get(); + return rows.map((row) => OlmSession.fromDb(row, userId)).toList(); + } + + @override + Future> getToDeviceEventQueue(int client_id) async { + final rows = await getToDeviceQueue(client_id).get(); + return rows + .map((row) => QueuedToDeviceEvent( + id: row.id, + type: row.type, + txnId: row.txnId, + content: + (json.decode(row.content) as Map).copy(), + )) + .toList(); + } + + @override + Future> getLastSentMessageUserDeviceKey( + int client_id, + String user_id, + String device_id, + ) => + dbGetLastSentMessageUserDeviceKey(client_id, user_id, device_id).get(); + + @override + Future> + getInboundGroupSessionsToUpload() async { + final rows = await dbGetInboundGroupSessionsToUpload().get(); + return rows + .map((row) => StoredInboundGroupSession.fromJson(row.toJson())) + .toList(); + } } diff --git a/lib/src/database/database.g.dart b/lib/src/database/database.g.dart index 563132d7..0bd20e2a 100644 --- a/lib/src/database/database.g.dart +++ b/lib/src/database/database.g.dart @@ -6881,7 +6881,7 @@ abstract class _$Database extends GeneratedDatabase { ); } - Selectable getInboundGroupSessionsToUpload() { + Selectable dbGetInboundGroupSessionsToUpload() { return customSelect( 'SELECT * FROM inbound_group_sessions WHERE uploaded = false LIMIT 500', variables: [], @@ -7021,7 +7021,7 @@ abstract class _$Database extends GeneratedDatabase { ); } - Selectable getLastSentMessageUserDeviceKey( + Selectable dbGetLastSentMessageUserDeviceKey( int client_id, String user_id, String device_id) { return customSelect( 'SELECT last_sent_message FROM user_device_keys_key WHERE client_id = :client_id AND user_id = :user_id AND device_id = :device_id', diff --git a/lib/src/database/database.moor b/lib/src/database/database.moor index 8aebc61e..6637bd99 100644 --- a/lib/src/database/database.moor +++ b/lib/src/database/database.moor @@ -208,7 +208,7 @@ getAllInboundGroupSessions: SELECT * FROM inbound_group_sessions WHERE client_id storeInboundGroupSession: INSERT OR REPLACE INTO inbound_group_sessions (client_id, room_id, session_id, pickle, content, indexes, allowed_at_index, sender_key, sender_claimed_keys) VALUES (:client_id, :room_id, :session_id, :pickle, :content, :indexes, :allowed_at_index, :sender_key, :sender_claimed_keys); updateInboundGroupSessionIndexes: UPDATE inbound_group_sessions SET indexes = :indexes WHERE client_id = :client_id AND room_id = :room_id AND session_id = :session_id; updateInboundGroupSessionAllowedAtIndex: UPDATE inbound_group_sessions SET allowed_at_index = :allowed_at_index WHERE client_id = :client_id AND room_id = :room_id AND session_id = :session_id; -getInboundGroupSessionsToUpload: SELECT * FROM inbound_group_sessions WHERE uploaded = false LIMIT 500; +dbGetInboundGroupSessionsToUpload: SELECT * FROM inbound_group_sessions WHERE uploaded = false LIMIT 500; markInboundGroupSessionAsUploaded: UPDATE inbound_group_sessions SET uploaded = true WHERE client_id = :client_id AND room_id = :room_id AND session_id = :session_id; markInboundGroupSessionsAsNeedingUpload: UPDATE inbound_group_sessions SET uploaded = false WHERE client_id = :client_id; storeUserDeviceKeysInfo: INSERT OR REPLACE INTO user_device_keys (client_id, user_id, outdated) VALUES (:client_id, :user_id, :outdated); @@ -218,7 +218,7 @@ storeUserDeviceKey: INSERT OR REPLACE INTO user_device_keys_key (client_id, user removeUserDeviceKey: DELETE FROM user_device_keys_key WHERE client_id = :client_id AND user_id = :user_id AND device_id = :device_id; setLastActiveUserDeviceKey: UPDATE user_device_keys_key SET last_active = :last_active WHERE client_id = :client_id AND user_id = :user_id AND device_id = :device_id; setLastSentMessageUserDeviceKey: UPDATE user_device_keys_key SET last_sent_message = :last_sent_message WHERE client_id = :client_id AND user_id = :user_id AND device_id = :device_id; -getLastSentMessageUserDeviceKey: SELECT last_sent_message FROM user_device_keys_key WHERE client_id = :client_id AND user_id = :user_id AND device_id = :device_id; +dbGetLastSentMessageUserDeviceKey: SELECT last_sent_message FROM user_device_keys_key WHERE client_id = :client_id AND user_id = :user_id AND device_id = :device_id; setVerifiedUserCrossSigningKey: UPDATE user_cross_signing_keys SET verified = :verified WHERE client_id = :client_id AND user_id = :user_id AND public_key = :public_key; setBlockedUserCrossSigningKey: UPDATE user_cross_signing_keys SET blocked = :blocked WHERE client_id = :client_id AND user_id = :user_id AND public_key = :public_key; storeUserCrossSigningKey: INSERT OR REPLACE INTO user_cross_signing_keys (client_id, user_id, public_key, content, verified, blocked) VALUES (:client_id, :user_id, :public_key, :content, :verified, :blocked); diff --git a/lib/src/database/database_api.dart b/lib/src/database/database_api.dart new file mode 100644 index 00000000..5785df80 --- /dev/null +++ b/lib/src/database/database_api.dart @@ -0,0 +1,315 @@ +/* + * Famedly Matrix SDK + * Copyright (C) 2021 Famedly GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import 'dart:typed_data'; + +import 'package:famedlysdk/encryption/utils/olm_session.dart'; +import 'package:famedlysdk/encryption/utils/outbound_group_session.dart'; +import 'package:famedlysdk/encryption/utils/session_key.dart'; +import 'package:famedlysdk/encryption/utils/ssss_cache.dart'; +import 'package:famedlysdk/encryption/utils/stored_inbound_group_session.dart'; +import 'package:famedlysdk/src/utils/QueuedToDeviceEvent.dart'; + +import '../../famedlysdk.dart'; + +abstract class DatabaseApi { + int get maxFileSize => 1 * 1024 * 1024; + Future getClient(String name); + + Future> getUserDeviceKeys(Client client); + + Future getOutboundGroupSession( + int clientId, + String roomId, + String userId, + ); + + Future getInboundGroupSession( + int clientId, + String roomId, + String sessionId, + String userId, + ); + + Future getSSSSCache(int clientId, String type); + + Future> getRoomList(Client client); + + Future> getAccountData(int clientId); + + /// Stores a RoomUpdate object in the database. Must be called inside of + /// [transaction]. + Future storeRoomUpdate(int clientId, RoomUpdate roomUpdate, + [Room oldRoom]); + + /// Stores an EventUpdate object in the database. Must be called inside of + /// [transaction]. + Future storeEventUpdate(int clientId, EventUpdate eventUpdate); + + Future getEventById(int clientId, String eventId, Room room); + + Future forgetRoom(int clientId, String roomId); + + Future clearCache(int clientId); + + Future clear(int clientId); + + Future getUser(int clientId, String userId, Room room); + + Future> getUsers(int clientId, Room room); + + Future> getEventList(int clientId, Room room); + + Future getFile(String mxcUri); + + Future updateInboundGroupSessionIndexes( + String indexes, + int clientId, + String roomId, + String sessionId, + ); + + Future storeInboundGroupSession( + int clientId, + String roomId, + String sessionId, + String pickle, + String content, + String indexes, + String allowedAtIndex, + String senderKey, + String senderClaimedKey, + ); + + Future markInboundGroupSessionAsUploaded( + int clientId, + String roomId, + String sessionId, + ); + + Future updateInboundGroupSessionAllowedAtIndex( + String allowedAtIndex, + int clientId, + String roomId, + String sessionId, + ); + + Future removeOutboundGroupSession(int clientId, String roomId); + + Future storeFile(String mxcUri, Uint8List bytes, int time); + + Future updateClient( + String homeserverUrl, + String token, + String userId, + String deviceId, + String deviceName, + String prevBatch, + String olmAccount, + int clientId, + ); + + Future insertClient( + String name, + String homeserverUrl, + String token, + String userId, + String deviceId, + String deviceName, + String prevBatch, + String olmAccount, + ); + + Future storeSyncFilterId(String syncFilterId, int clientId); + + Future storeAccountData(int clientId, String type, String content); + + Future storeOutboundGroupSession( + int clientId, + String roomId, + String pickle, + String deviceIds, + int creationTime, + int sentMessages, + ); + + Future updateClientKeys(String olmAccount, int clientId); + + Future storeOlmSession( + int clientId, + String identitiyKey, + String sessionId, + String pickle, + int lastReceived, + ); + + Future setLastActiveUserDeviceKey( + int lastActive, + int clientId, + String userId, + String deviceId, + ); + + Future setLastSentMessageUserDeviceKey( + String lastSentMessage, + int clientId, + String userId, + String deviceId, + ); + + Future clearSSSSCache(int clientId); + + Future storeSSSSCache( + int clientId, + String type, + String keyId, + String ciphertext, + String content, + ); + + Future markInboundGroupSessionsAsNeedingUpload(int clientId); + + Future storePrevBatch(String prevBatch, int clientId); + + Future deleteOldFiles(int savedAt); + + Future storeUserDeviceKeysInfo( + int clientId, + String userId, + bool outdated, + ); + + Future storeUserDeviceKey( + int clientId, + String userId, + String deviceId, + String content, + bool verified, + bool blocked, + int lastActive, + ); + + Future removeUserDeviceKey( + int clientId, + String userId, + String deviceId, + ); + + Future removeUserCrossSigningKey( + int clientId, + String userId, + String publicKey, + ); + + Future storeUserCrossSigningKey( + int clientId, + String userId, + String publicKey, + String content, + bool verified, + bool blocked, + ); + + Future deleteFromToDeviceQueue(int clientId, int id); + + Future removeEvent(int clientId, String eventId, String roomId); + + Future updateRoomSortOrder( + double oldestSortOrder, + double newestSortOrder, + int clientId, + String roomId, + ); + + Future setRoomPrevBatch( + String prevBatch, + int clientId, + String roomId, + ); + + Future resetNotificationCount(int clientId, String roomId); + + Future setVerifiedUserCrossSigningKey( + bool verified, + int clientId, + String userId, + String publicKey, + ); + + Future setBlockedUserCrossSigningKey( + bool blocked, + int clientId, + String userId, + String publicKey, + ); + + Future setVerifiedUserDeviceKey( + bool verified, + int clientId, + String userId, + String deviceId, + ); + + Future setBlockedUserDeviceKey( + bool blocked, + int clientId, + String userId, + String deviceId, + ); + + Future> getUnimportantRoomEventStatesForRoom( + int clientId, + List events, + Room room, + ); + + Future> getOlmSessions( + int clientId, + String identityKey, + String userId, + ); + + Future> getOlmSessionsForDevices( + int clientId, + List identityKeys, + String userId, + ); + + Future> getToDeviceEventQueue(int clientId); + + /// Please do `jsonEncode(content)` in your code to stay compatible with + /// auto generated methods here. + Future insertIntoToDeviceQueue( + int clientId, + String type, + String txnId, + String content, + ); + + Future> getLastSentMessageUserDeviceKey( + int clientId, + String userId, + String deviceId, + ); + + Future> getInboundGroupSessionsToUpload(); + + Future close(); + + Future transaction(Future Function() action); +} diff --git a/lib/src/room.dart b/lib/src/room.dart index e1d8514f..2f576739 100644 --- a/lib/src/room.dart +++ b/lib/src/room.dart @@ -130,12 +130,11 @@ class Room { return; } final allStates = await client.database - .getUnimportantRoomStatesForRoom( - client.id, id, client.importantStateEvents.toList()) - .get(); + .getUnimportantRoomEventStatesForRoom( + client.id, client.importantStateEvents.toList(), this); + for (final state in allStates) { - final newState = Event.fromDb(state, this); - setState(newState); + setState(state); } partial = false; } diff --git a/lib/src/utils/QueuedToDeviceEvent.dart b/lib/src/utils/QueuedToDeviceEvent.dart new file mode 100644 index 00000000..7c1915d7 --- /dev/null +++ b/lib/src/utils/QueuedToDeviceEvent.dart @@ -0,0 +1,23 @@ +class QueuedToDeviceEvent { + final int id; + final String type; + final String txnId; + final Map content; + + QueuedToDeviceEvent({this.id, this.type, this.txnId, this.content}); + + factory QueuedToDeviceEvent.fromJson(Map json) => + QueuedToDeviceEvent( + id: json['id'], + type: json['type'], + txnId: json['txn_id'], + content: json['content'], + ); + + Map toJson() => { + 'id': id, + 'type': type, + 'txn_id': txnId, + 'content': content, + }; +} diff --git a/test/encryption/online_key_backup_test.dart b/test/encryption/online_key_backup_test.dart index 9588ef22..63420aaf 100644 --- a/test/encryption/online_key_backup_test.dart +++ b/test/encryption/online_key_backup_test.dart @@ -96,14 +96,12 @@ void main() { client.encryption.keyManager.setInboundGroupSession( roomId, sessionId, senderKey, sessionPayload, forwarded: true); - var dbSessions = - await client.database.getInboundGroupSessionsToUpload().get(); + var dbSessions = await client.database.getInboundGroupSessionsToUpload(); expect(dbSessions.isNotEmpty, true); await client.encryption.keyManager.backgroundTasks(); final payload = FakeMatrixApi .calledEndpoints['/client/unstable/room_keys/keys?version=5'].first; - dbSessions = - await client.database.getInboundGroupSessionsToUpload().get(); + dbSessions = await client.database.getInboundGroupSessionsToUpload(); expect(dbSessions.isEmpty, true); final onlineKeys = RoomKeys.fromJson(json.decode(payload));