From c3d47b16c63462bfc57be8edcad17ef6c46940eb Mon Sep 17 00:00:00 2001 From: Christian Pauly Date: Thu, 20 May 2021 09:21:37 +0200 Subject: [PATCH] CI: Use correct image --- .gitlab-ci.yml | 2 +- lib/encryption/key_manager.dart | 7 +- lib/encryption/utils/olm_session.dart | 11 +- .../utils/outbound_group_session.dart | 12 +- lib/famedlysdk.dart | 1 + lib/src/client.dart | 18 +- lib/src/database/database.dart | 130 +++++- lib/src/database/database_api.dart | 140 +++--- lib/src/event.dart | 27 -- lib/src/room.dart | 53 --- lib/src/utils/QueuedToDeviceEvent.dart | 17 + lib/src/utils/device_keys_list.dart | 43 +- test/database_api_test.dart | 431 ++++++++++++++++++ test/room_test.dart | 4 +- 14 files changed, 674 insertions(+), 222 deletions(-) create mode 100644 test/database_api_test.dart diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b9b9a096..00569a44 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -85,7 +85,7 @@ code_analyze: tags: - docker stage: coverage - image: cirrusci/flutter + image: registry.gitlab.com/famedly/containers/flutter-dockerimages:stable dependencies: [] script: - flutter format lib/ test/ test_driver/ --set-exit-if-changed diff --git a/lib/encryption/key_manager.dart b/lib/encryption/key_manager.dart index a7a91e30..b40649ce 100644 --- a/lib/encryption/key_manager.dart +++ b/lib/encryption/key_manager.dart @@ -231,11 +231,12 @@ class KeyManager { } return sess; // nothing to do } - final sess = await client.database - ?.getInboundGroupSession(client.id, roomId, sessionId, client.userID); - if (sess == null) { + final session = await client.database + ?.getInboundGroupSession(client.id, roomId, sessionId); + if (session == null) { return null; } + final sess = SessionKey.fromDb(session, client.userID); if (!_inboundGroupSessions.containsKey(roomId)) { _inboundGroupSessions[roomId] = {}; } diff --git a/lib/encryption/utils/olm_session.dart b/lib/encryption/utils/olm_session.dart index 7343152e..1af9113c 100644 --- a/lib/encryption/utils/olm_session.dart +++ b/lib/encryption/utils/olm_session.dart @@ -19,7 +19,6 @@ import 'package:olm/olm.dart' as olm; import '../../famedlysdk.dart'; -import '../../src/database/database.dart' show DbOlmSessions; class OlmSession { String identityKey; @@ -39,14 +38,14 @@ class OlmSession { this.lastReceived, }); - OlmSession.fromDb(DbOlmSessions dbEntry, String key) : key = key { + OlmSession.fromJson(Map dbEntry, String key) : key = key { session = olm.Session(); try { - session.unpickle(key, dbEntry.pickle); - identityKey = dbEntry.identityKey; - sessionId = dbEntry.sessionId; + session.unpickle(key, dbEntry['pickle']); + identityKey = dbEntry['identity_key']; + sessionId = dbEntry['session_id']; lastReceived = - DateTime.fromMillisecondsSinceEpoch(dbEntry.lastReceived ?? 0); + DateTime.fromMillisecondsSinceEpoch(dbEntry['last_received'] ?? 0); assert(sessionId == session.session_id()); } catch (e, s) { Logs().e('[LibOlm] Could not unpickle olm session', e, s); diff --git a/lib/encryption/utils/outbound_group_session.dart b/lib/encryption/utils/outbound_group_session.dart index 91660706..3861bfba 100644 --- a/lib/encryption/utils/outbound_group_session.dart +++ b/lib/encryption/utils/outbound_group_session.dart @@ -21,7 +21,6 @@ import 'dart:convert'; import 'package:olm/olm.dart' as olm; import '../../famedlysdk.dart'; -import '../../src/database/database.dart' show DbOutboundGroupSession; class OutboundGroupSession { /// The devices is a map from user id to device id to if the device is blocked. @@ -42,11 +41,11 @@ class OutboundGroupSession { this.sentMessages, this.key}); - OutboundGroupSession.fromDb(DbOutboundGroupSession dbEntry, String key) + OutboundGroupSession.fromJson(Map dbEntry, String key) : key = key { try { devices = {}; - for (final entry in json.decode(dbEntry.deviceIds).entries) { + for (final entry in json.decode(dbEntry['device_ids']).entries) { devices[entry.key] = Map.from(entry.value); } } catch (e) { @@ -58,9 +57,10 @@ class OutboundGroupSession { } outboundGroupSession = olm.OutboundGroupSession(); try { - outboundGroupSession.unpickle(key, dbEntry.pickle); - creationTime = DateTime.fromMillisecondsSinceEpoch(dbEntry.creationTime); - sentMessages = dbEntry.sentMessages; + outboundGroupSession.unpickle(key, dbEntry['pickle']); + creationTime = + DateTime.fromMillisecondsSinceEpoch(dbEntry['creation_time']); + sentMessages = dbEntry['sent_messages']; } catch (e, s) { dispose(); Logs().e('[LibOlm] Unable to unpickle outboundGroupSession', e, s); diff --git a/lib/famedlysdk.dart b/lib/famedlysdk.dart index 3b9a3fdb..e3f23141 100644 --- a/lib/famedlysdk.dart +++ b/lib/famedlysdk.dart @@ -38,3 +38,4 @@ export 'src/room.dart'; export 'src/timeline.dart'; export 'src/user.dart'; export 'src/database/database.dart' show Database; +export 'src/database/database_api.dart'; diff --git a/lib/src/client.dart b/lib/src/client.dart index 29125916..bc70d885 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -843,15 +843,15 @@ class Client extends MatrixApi { if (database != null) { final account = await database.getClient(clientName); if (account != null) { - _id = account.clientId; - homeserver = Uri.parse(account.homeserverUrl); - accessToken = account.token; - _userID = account.userId; - _deviceID = account.deviceId; - _deviceName = account.deviceName; - syncFilterId = account.syncFilterId; - prevBatch = account.prevBatch; - olmAccount = account.olmAccount; + _id = account['client_id']; + homeserver = Uri.parse(account['homeserver_url']); + accessToken = account['token']; + _userID = account['user_id']; + _deviceID = account['device_id']; + _deviceName = account['device_name']; + syncFilterId = account['sync_filter_id']; + prevBatch = account['prev_batch']; + olmAccount = account['olm_account']; } } if (newToken != null) { diff --git a/lib/src/database/database.dart b/lib/src/database/database.dart index 350adc08..471d2850 100644 --- a/lib/src/database/database.dart +++ b/lib/src/database/database.dart @@ -21,7 +21,6 @@ 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'; @@ -211,11 +210,11 @@ class Database extends _$Database implements DatabaseApi { ); @override - Future getClient(String name) async { + Future> getClient(String name) async { final res = await dbGetClient(name).get(); if (res.isEmpty) return null; await markPendingEventsAsError(res.single.clientId); - return res.single; + return res.single.toJson(); } @override @@ -229,10 +228,16 @@ class Database extends _$Database implements DatabaseApi { final crossSigningKeys = await getAllUserCrossSigningKeys(client.id).get(); final res = {}; for (final entry in deviceKeys) { - res[entry.userId] = sdk.DeviceKeysList.fromDb( - entry, - deviceKeysKeys.where((k) => k.userId == entry.userId).toList(), - crossSigningKeys.where((k) => k.userId == entry.userId).toList(), + res[entry.userId] = sdk.DeviceKeysList.fromDbJson( + entry.toJson(), + deviceKeysKeys + .where((k) => k.userId == entry.userId) + .map((d) => d.toJson()) + .toList(), + crossSigningKeys + .where((k) => k.userId == entry.userId) + .map((d) => d.toJson()) + .toList(), client); } return res; @@ -245,23 +250,21 @@ class Database extends _$Database implements DatabaseApi { if (res.isEmpty) { return null; } - return OutboundGroupSession.fromDb(res.single, userId); + return OutboundGroupSession.fromJson(res.single.toJson(), userId); } @override - Future getInboundGroupSession( + 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 SessionKey.fromDb( - StoredInboundGroupSession.fromJson(res.single.toJson()), userId); + return StoredInboundGroupSession.fromJson(res.single.toJson()); } @override @@ -290,7 +293,7 @@ class Database extends _$Database implements DatabaseApi { final roomList = []; final allMembersToPostload = >{}; for (final r in res) { - final room = await sdk.Room.getRoomFromTableRow( + final room = await getRoomFromTableRow( r, client, states: resStates.where((rs) => rs.roomId == r.roomId), @@ -366,7 +369,7 @@ class Database extends _$Database implements DatabaseApi { // now that we got all the entries from the database, set them as room states for (final dbMember in membersRes) { final room = roomList.firstWhere((r) => r.id == dbMember.roomId); - final event = sdk.Event.fromDb(dbMember, room); + final event = getEventFromDb(dbMember, room); room.setState(event); } } @@ -605,7 +608,7 @@ class Database extends _$Database implements DatabaseApi { if (event.isEmpty) { return null; } - return sdk.Event.fromDb(event.single, room); + return getEventFromDb(event.single, room); } Future redactMessage(int clientId, sdk.EventUpdate eventUpdate) async { @@ -614,7 +617,7 @@ class Database extends _$Database implements DatabaseApi { .get(); var success = false; for (final dbEvent in events) { - final event = sdk.Event.fromDb(dbEvent, null); + final event = getEventFromDb(dbEvent, null); event.setRedactionEvent(sdk.Event.fromJson(eventUpdate.content, null)); final changes1 = await updateEvent( json.encode(event.unsigned ?? ''), @@ -698,19 +701,19 @@ class Database extends _$Database implements DatabaseApi { if (res.isEmpty) { return null; } - return sdk.Event.fromDb(res.single, room).asUser; + return getEventFromDb(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(); + return res.map((r) => getEventFromDb(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(); + return res.map((r) => getEventFromDb(r, room)).toList(); } @override @@ -731,7 +734,7 @@ class Database extends _$Database implements DatabaseApi { room.id, events, ).get(); - return entries.map((dbEvent) => sdk.Event.fromDb(dbEvent, room)).toList(); + return entries.map((dbEvent) => getEventFromDb(dbEvent, room)).toList(); } @override @@ -741,7 +744,9 @@ class Database extends _$Database implements DatabaseApi { String userId, ) async { final rows = await dbGetOlmSessions(client_id, identity_key).get(); - return rows.map((row) => OlmSession.fromDb(row, userId)).toList(); + return rows + .map((row) => OlmSession.fromJson(row.toJson(), userId)) + .toList(); } @override @@ -752,7 +757,9 @@ class Database extends _$Database implements DatabaseApi { ) async { final rows = await dbGetOlmSessionsForDevices(client_id, identity_keys).get(); - return rows.map((row) => OlmSession.fromDb(row, userId)).toList(); + return rows + .map((row) => OlmSession.fromJson(row.toJson(), userId)) + .toList(); } @override @@ -786,3 +793,82 @@ class Database extends _$Database implements DatabaseApi { .toList(); } } + +/// Get an event from either DbRoomState or DbEvent +sdk.Event getEventFromDb(dynamic dbEntry, sdk.Room room) { + if (!(dbEntry is DbRoomState || dbEntry is DbEvent)) { + throw ('Unknown db type'); + } + final content = sdk.Event.getMapFromPayload(dbEntry.content); + final unsigned = sdk.Event.getMapFromPayload(dbEntry.unsigned); + final prevContent = sdk.Event.getMapFromPayload(dbEntry.prevContent); + return sdk.Event( + status: + (dbEntry is DbEvent ? dbEntry.status : null) ?? sdk.Event.defaultStatus, + stateKey: dbEntry.stateKey, + prevContent: prevContent, + content: content, + type: dbEntry.type, + eventId: dbEntry.eventId, + roomId: dbEntry.roomId, + senderId: dbEntry.sender, + originServerTs: dbEntry.originServerTs != null + ? DateTime.fromMillisecondsSinceEpoch(dbEntry.originServerTs) + : DateTime.now(), + unsigned: unsigned, + room: room, + sortOrder: dbEntry.sortOrder ?? 0.0, + ); +} + +/// Returns a Room from a json String which comes normally from the store. If the +/// state are also given, the method will await them. +Future getRoomFromTableRow( + // either Map or DbRoom + DbRoom row, + Client matrix, { + dynamic states, // DbRoomState, as iterator and optionally as future + dynamic + roomAccountData, // DbRoomAccountData, as iterator and optionally as future +}) async { + final newRoom = Room( + id: row.roomId, + membership: sdk.Membership.values + .firstWhere((e) => e.toString() == 'Membership.' + row.membership), + notificationCount: row.notificationCount, + highlightCount: row.highlightCount, + // TODO: do proper things + notificationSettings: 'mention', + prev_batch: row.prevBatch, + mInvitedMemberCount: row.invitedMemberCount, + mJoinedMemberCount: row.joinedMemberCount, + mHeroes: row.heroes?.split(',') ?? [], + client: matrix, + roomAccountData: {}, + newestSortOrder: row.newestSortOrder, + oldestSortOrder: row.oldestSortOrder, + ); + + if (states != null) { + for (final rawState in await states) { + final newState = getEventFromDb(rawState, newRoom); + newRoom.setState(newState); + } + } + + final newRoomAccountData = {}; + if (roomAccountData != null) { + for (final singleAccountData in await roomAccountData) { + final content = sdk.Event.getMapFromPayload(singleAccountData.content); + final newData = sdk.BasicRoomEvent( + content: content, + type: singleAccountData.type, + roomId: singleAccountData.roomId, + ); + newRoomAccountData[newData.type] = newData; + } + } + newRoom.roomAccountData = newRoomAccountData; + + return newRoom; +} diff --git a/lib/src/database/database_api.dart b/lib/src/database/database_api.dart index 5785df80..cec08ffb 100644 --- a/lib/src/database/database_api.dart +++ b/lib/src/database/database_api.dart @@ -20,7 +20,6 @@ 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'; @@ -29,25 +28,30 @@ import '../../famedlysdk.dart'; abstract class DatabaseApi { int get maxFileSize => 1 * 1024 * 1024; - Future getClient(String name); + Future> getClient(String name); - Future> getUserDeviceKeys(Client client); - - Future getOutboundGroupSession( - int clientId, - String roomId, + Future updateClient( + String homeserverUrl, + String token, String userId, + String deviceId, + String deviceName, + String prevBatch, + String olmAccount, + int clientId, ); - Future getInboundGroupSession( - int clientId, - String roomId, - String sessionId, + Future insertClient( + String name, + String homeserverUrl, + String token, String userId, + String deviceId, + String deviceName, + String prevBatch, + String olmAccount, ); - Future getSSSSCache(int clientId, String type); - Future> getRoomList(Client client); Future> getAccountData(int clientId); @@ -77,14 +81,36 @@ abstract class DatabaseApi { Future getFile(String mxcUri); - Future updateInboundGroupSessionIndexes( + Future storeFile(String mxcUri, Uint8List bytes, int time); + + Future storeSyncFilterId(String syncFilterId, int clientId); + + Future storeAccountData(int clientId, String type, String content); + + Future> getUserDeviceKeys(Client client); + + Future getSSSSCache(int clientId, String type); + + Future getOutboundGroupSession( + int clientId, + String roomId, + String userId, + ); + + Future getInboundGroupSession( + int clientId, + String roomId, + String sessionId, + ); + + Future updateInboundGroupSessionIndexes( String indexes, int clientId, String roomId, String sessionId, ); - Future storeInboundGroupSession( + Future storeInboundGroupSession( int clientId, String roomId, String sessionId, @@ -96,50 +122,22 @@ abstract class DatabaseApi { String senderClaimedKey, ); - Future markInboundGroupSessionAsUploaded( + Future markInboundGroupSessionAsUploaded( int clientId, String roomId, String sessionId, ); - Future updateInboundGroupSessionAllowedAtIndex( + Future updateInboundGroupSessionAllowedAtIndex( String allowedAtIndex, int clientId, String roomId, String sessionId, ); - Future removeOutboundGroupSession(int clientId, String roomId); + 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( + Future storeOutboundGroupSession( int clientId, String roomId, String pickle, @@ -148,9 +146,9 @@ abstract class DatabaseApi { int sentMessages, ); - Future updateClientKeys(String olmAccount, int clientId); + Future updateClientKeys(String olmAccount, int clientId); - Future storeOlmSession( + Future storeOlmSession( int clientId, String identitiyKey, String sessionId, @@ -158,23 +156,23 @@ abstract class DatabaseApi { int lastReceived, ); - Future setLastActiveUserDeviceKey( + Future setLastActiveUserDeviceKey( int lastActive, int clientId, String userId, String deviceId, ); - Future setLastSentMessageUserDeviceKey( + Future setLastSentMessageUserDeviceKey( String lastSentMessage, int clientId, String userId, String deviceId, ); - Future clearSSSSCache(int clientId); + Future clearSSSSCache(int clientId); - Future storeSSSSCache( + Future storeSSSSCache( int clientId, String type, String keyId, @@ -182,19 +180,19 @@ abstract class DatabaseApi { String content, ); - Future markInboundGroupSessionsAsNeedingUpload(int clientId); + Future markInboundGroupSessionsAsNeedingUpload(int clientId); - Future storePrevBatch(String prevBatch, int clientId); + Future storePrevBatch(String prevBatch, int clientId); - Future deleteOldFiles(int savedAt); + Future deleteOldFiles(int savedAt); - Future storeUserDeviceKeysInfo( + Future storeUserDeviceKeysInfo( int clientId, String userId, bool outdated, ); - Future storeUserDeviceKey( + Future storeUserDeviceKey( int clientId, String userId, String deviceId, @@ -204,19 +202,19 @@ abstract class DatabaseApi { int lastActive, ); - Future removeUserDeviceKey( + Future removeUserDeviceKey( int clientId, String userId, String deviceId, ); - Future removeUserCrossSigningKey( + Future removeUserCrossSigningKey( int clientId, String userId, String publicKey, ); - Future storeUserCrossSigningKey( + Future storeUserCrossSigningKey( int clientId, String userId, String publicKey, @@ -225,47 +223,47 @@ abstract class DatabaseApi { bool blocked, ); - Future deleteFromToDeviceQueue(int clientId, int id); + Future deleteFromToDeviceQueue(int clientId, int id); - Future removeEvent(int clientId, String eventId, String roomId); + Future removeEvent(int clientId, String eventId, String roomId); - Future updateRoomSortOrder( + Future updateRoomSortOrder( double oldestSortOrder, double newestSortOrder, int clientId, String roomId, ); - Future setRoomPrevBatch( + Future setRoomPrevBatch( String prevBatch, int clientId, String roomId, ); - Future resetNotificationCount(int clientId, String roomId); + Future resetNotificationCount(int clientId, String roomId); - Future setVerifiedUserCrossSigningKey( + Future setVerifiedUserCrossSigningKey( bool verified, int clientId, String userId, String publicKey, ); - Future setBlockedUserCrossSigningKey( + Future setBlockedUserCrossSigningKey( bool blocked, int clientId, String userId, String publicKey, ); - Future setVerifiedUserDeviceKey( + Future setVerifiedUserDeviceKey( bool verified, int clientId, String userId, String deviceId, ); - Future setBlockedUserDeviceKey( + Future setBlockedUserDeviceKey( bool blocked, int clientId, String userId, @@ -294,7 +292,7 @@ abstract class DatabaseApi { /// Please do `jsonEncode(content)` in your code to stay compatible with /// auto generated methods here. - Future insertIntoToDeviceQueue( + Future insertIntoToDeviceQueue( int clientId, String type, String txnId, diff --git a/lib/src/event.dart b/lib/src/event.dart index 1c72be9b..4f72d350 100644 --- a/lib/src/event.dart +++ b/lib/src/event.dart @@ -22,7 +22,6 @@ import 'dart:typed_data'; import 'package:http/http.dart' as http; import '../famedlysdk.dart'; -import 'database/database.dart' show DbRoomState, DbEvent; import 'room.dart'; import 'utils/matrix_localizations.dart'; import 'utils/receipt.dart'; @@ -178,32 +177,6 @@ class Event extends MatrixEvent { ); } - /// Get an event from either DbRoomState or DbEvent - factory Event.fromDb(dynamic dbEntry, Room room) { - if (!(dbEntry is DbRoomState || dbEntry is DbEvent)) { - throw ('Unknown db type'); - } - final content = Event.getMapFromPayload(dbEntry.content); - final unsigned = Event.getMapFromPayload(dbEntry.unsigned); - final prevContent = Event.getMapFromPayload(dbEntry.prevContent); - return Event( - status: (dbEntry is DbEvent ? dbEntry.status : null) ?? defaultStatus, - stateKey: dbEntry.stateKey, - prevContent: prevContent, - content: content, - type: dbEntry.type, - eventId: dbEntry.eventId, - roomId: dbEntry.roomId, - senderId: dbEntry.sender, - originServerTs: dbEntry.originServerTs != null - ? DateTime.fromMillisecondsSinceEpoch(dbEntry.originServerTs) - : DateTime.now(), - unsigned: unsigned, - room: room, - sortOrder: dbEntry.sortOrder ?? 0.0, - ); - } - @override Map toJson() { final data = {}; diff --git a/lib/src/room.dart b/lib/src/room.dart index 2f576739..c173c4ea 100644 --- a/lib/src/room.dart +++ b/lib/src/room.dart @@ -24,7 +24,6 @@ import 'package:html_unescape/html_unescape.dart'; import '../famedlysdk.dart'; import 'client.dart'; -import 'database/database.dart' show DbRoom; import 'event.dart'; import 'timeline.dart'; import 'user.dart'; @@ -1079,58 +1078,6 @@ class Room { return; } - /// Returns a Room from a json String which comes normally from the store. If the - /// state are also given, the method will await them. - static Future getRoomFromTableRow( - // either Map or DbRoom - DbRoom row, - Client matrix, { - dynamic states, // DbRoomState, as iterator and optionally as future - dynamic - roomAccountData, // DbRoomAccountData, as iterator and optionally as future - }) async { - final newRoom = Room( - id: row.roomId, - membership: Membership.values - .firstWhere((e) => e.toString() == 'Membership.' + row.membership), - notificationCount: row.notificationCount, - highlightCount: row.highlightCount, - // TODO: do proper things - notificationSettings: 'mention', - prev_batch: row.prevBatch, - mInvitedMemberCount: row.invitedMemberCount, - mJoinedMemberCount: row.joinedMemberCount, - mHeroes: row.heroes?.split(',') ?? [], - client: matrix, - roomAccountData: {}, - newestSortOrder: row.newestSortOrder, - oldestSortOrder: row.oldestSortOrder, - ); - - if (states != null) { - for (final rawState in await states) { - final newState = Event.fromDb(rawState, newRoom); - newRoom.setState(newState); - } - } - - final newRoomAccountData = {}; - if (roomAccountData != null) { - for (final singleAccountData in await roomAccountData) { - final content = Event.getMapFromPayload(singleAccountData.content); - final newData = BasicRoomEvent( - content: content, - type: singleAccountData.type, - roomId: singleAccountData.roomId, - ); - newRoomAccountData[newData.type] = newData; - } - } - newRoom.roomAccountData = newRoomAccountData; - - return newRoom; - } - /// Creates a timeline from the store. Returns a [Timeline] object. Future getTimeline( {onTimelineUpdateCallback onUpdate, diff --git a/lib/src/utils/QueuedToDeviceEvent.dart b/lib/src/utils/QueuedToDeviceEvent.dart index 7c1915d7..b73f1c7f 100644 --- a/lib/src/utils/QueuedToDeviceEvent.dart +++ b/lib/src/utils/QueuedToDeviceEvent.dart @@ -1,3 +1,20 @@ +/* + * Famedly Matrix SDK + * Copyright (C) 2019, 2020 Famedly GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ class QueuedToDeviceEvent { final int id; final String type; diff --git a/lib/src/utils/device_keys_list.dart b/lib/src/utils/device_keys_list.dart index 52e2078f..1b5dd19a 100644 --- a/lib/src/utils/device_keys_list.dart +++ b/lib/src/utils/device_keys_list.dart @@ -25,8 +25,6 @@ import 'package:olm/olm.dart' as olm; import '../../encryption.dart'; import '../client.dart'; -import '../database/database.dart' - show DbUserDeviceKey, DbUserDeviceKeysKey, DbUserCrossSigningKey; import '../event.dart'; import '../room.dart'; import '../user.dart'; @@ -104,27 +102,27 @@ class DeviceKeysList { } } - DeviceKeysList.fromDb( - DbUserDeviceKey dbEntry, - List childEntries, - List crossSigningEntries, + DeviceKeysList.fromDbJson( + Map dbEntry, + List> childEntries, + List> crossSigningEntries, Client cl) { client = cl; - userId = dbEntry.userId; - outdated = dbEntry.outdated; + userId = dbEntry['user_id']; + outdated = dbEntry['outdated']; deviceKeys = {}; for (final childEntry in childEntries) { final entry = DeviceKeys.fromDb(childEntry, client); if (entry.isValid) { - deviceKeys[childEntry.deviceId] = entry; + deviceKeys[childEntry['device_id']] = entry; } else { outdated = true; } } for (final crossSigningEntry in crossSigningEntries) { - final entry = CrossSigningKey.fromDb(crossSigningEntry, client); + final entry = CrossSigningKey.fromDbJson(crossSigningEntry, client); if (entry.isValid) { - crossSigningKeys[crossSigningEntry.publicKey] = entry; + crossSigningKeys[crossSigningEntry['public_key']] = entry; } else { outdated = true; } @@ -365,13 +363,13 @@ class CrossSigningKey extends SignableKey { usage = json['usage'].cast(); } - CrossSigningKey.fromDb(DbUserCrossSigningKey dbEntry, Client cl) - : super.fromJson(Event.getMapFromPayload(dbEntry.content), cl) { + CrossSigningKey.fromDbJson(Map dbEntry, Client cl) + : super.fromJson(Event.getMapFromPayload(dbEntry['content']), cl) { final json = toJson(); - identifier = dbEntry.publicKey; + identifier = dbEntry['public_key']; usage = json['usage'].cast(); - _verified = dbEntry.verified; - blocked = dbEntry.blocked; + _verified = dbEntry['verified']; + blocked = dbEntry['blocked']; } CrossSigningKey.fromJson(Map json, Client cl) @@ -441,14 +439,15 @@ class DeviceKeys extends SignableKey { lastActive = lastActiveTs ?? DateTime.now(); } - DeviceKeys.fromDb(DbUserDeviceKeysKey dbEntry, Client cl) - : super.fromJson(Event.getMapFromPayload(dbEntry.content), cl) { + DeviceKeys.fromDb(Map dbEntry, Client cl) + : super.fromJson(Event.getMapFromPayload(dbEntry['content']), cl) { final json = toJson(); - identifier = dbEntry.deviceId; + identifier = dbEntry['device_id']; algorithms = json['algorithms'].cast(); - _verified = dbEntry.verified; - blocked = dbEntry.blocked; - lastActive = DateTime.fromMillisecondsSinceEpoch(dbEntry.lastActive ?? 0); + _verified = dbEntry['verified']; + blocked = dbEntry['blocked']; + lastActive = + DateTime.fromMillisecondsSinceEpoch(dbEntry['last_active'] ?? 0); } DeviceKeys.fromJson(Map json, Client cl) diff --git a/test/database_api_test.dart b/test/database_api_test.dart new file mode 100644 index 00000000..ca47d1ba --- /dev/null +++ b/test/database_api_test.dart @@ -0,0 +1,431 @@ +/* + * Famedly Matrix SDK + * Copyright (C) 2019, 2020 Famedly GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import 'dart:convert'; + +import 'package:famedlysdk/famedlysdk.dart'; +import 'package:moor/moor.dart'; +import 'package:test/test.dart'; +import 'package:olm/olm.dart' as olm; + +import 'fake_database_native.dart'; + +void main() { + /// All Tests related to the ChatTime + group('Moor Database Test', () { + testDatabase(getDatabase(null), 0); + }); +} + +Future olmEnabled() async { + var olmEnabled = true; + try { + await olm.init(); + olm.get_library_version(); + } catch (e) { + olmEnabled = false; + } + return olmEnabled; +} + +void testDatabase(Future futureDatabase, int clientId) { + DatabaseApi database; + int toDeviceQueueIndex; + test('Open', () async { + database = await futureDatabase; + }); + test('insertIntoToDeviceQueue', () async { + toDeviceQueueIndex = await database.insertIntoToDeviceQueue( + clientId, + 'm.test', + 'txnId', + '{"foo":"bar"}', + ); + }); + test('getToDeviceEventQueue', () async { + final toDeviceQueue = await database.getToDeviceEventQueue(clientId); + expect(toDeviceQueue.single.type, 'm.test'); + }); + test('deleteFromToDeviceQueue', () async { + await database.deleteFromToDeviceQueue(clientId, toDeviceQueueIndex); + final toDeviceQueue = await database.getToDeviceEventQueue(clientId); + expect(toDeviceQueue.isEmpty, true); + }); + test('storeFile', () async { + await database.storeFile('mxc://test', Uint8List.fromList([0]), 0); + final file = await database.getFile('mxc://test'); + expect(file != null, true); + }); + test('getFile', () async { + await database.getFile('mxc://test'); + }); + test('deleteOldFiles', () async { + await database.deleteOldFiles(1); + final file = await database.getFile('mxc://test'); + expect(file == null, true); + }); + test('storeRoomUpdate', () async { + await database.storeRoomUpdate( + clientId, + RoomUpdate( + id: '!testroom', + highlight_count: 0, + notification_count: 0, + limitedTimeline: false, + membership: Membership.join, + )); + final rooms = await database.getRoomList(Client('testclient')); + expect(rooms.single.id, '!testroom'); + }); + test('getRoomList', () async { + final list = await database.getRoomList(Client('testclient')); + expect(list.single.id, '!testroom'); + }); + test('setRoomPrevBatch', () async { + await database.setRoomPrevBatch('1234', clientId, '!testroom'); + final rooms = await database.getRoomList(Client('testclient')); + expect(rooms.single.prev_batch, '1234'); + }); + test('forgetRoom', () async { + await database.forgetRoom(clientId, '!testroom'); + final rooms = await database.getRoomList(Client('testclient')); + expect(rooms.isEmpty, true); + }); + test('getClient', () async { + await database.getClient('name'); + }); + test('insertClient', () async { + clientId = await database.insertClient( + 'name', + 'homeserverUrl', + 'token', + 'userId', + 'deviceId', + 'deviceName', + 'prevBatch', + 'olmAccount', + ); + final client = await database.getClient('name'); + expect(client['token'], 'token'); + }); + test('updateClient', () async { + await database.updateClient( + 'homeserverUrl', + 'token_different', + 'userId', + 'deviceId', + 'deviceName', + 'prevBatch', + 'olmAccount', + clientId, + ); + final client = await database.getClient('name'); + expect(client['token'], 'token_different'); + }); + test('updateClientKeys', () async { + await database.updateClientKeys('olmAccount2', clientId); + final client = await database.getClient('name'); + expect(client['olm_account'], 'olmAccount2'); + }); + test('storeSyncFilterId', () async { + await database.storeSyncFilterId('1234', clientId); + final client = await database.getClient('name'); + expect(client['sync_filter_id'], '1234'); + }); + test('getAccountData', () async { + await database.getAccountData(clientId); + }); + test('storeAccountData', () async { + await database.storeAccountData(clientId, 'm.test', '{}'); + final events = await database.getAccountData(clientId); + expect(events.values.single.type, 'm.test'); + }); + test('storeEventUpdate', () async { + await database.storeEventUpdate( + clientId, + EventUpdate( + roomID: '!testroom:example.com', + type: EventUpdateType.timeline, + sortOrder: DateTime.now().millisecondsSinceEpoch.toDouble(), + content: { + 'type': EventTypes.Message, + 'content': { + 'body': '* edit 3', + 'msgtype': 'm.text', + 'm.new_content': { + 'body': 'edit 3', + 'msgtype': 'm.text', + }, + 'm.relates_to': { + 'event_id': '\$source', + 'rel_type': RelationshipTypes.edit, + }, + }, + 'event_id': '\$event:example.com', + 'sender': '@bob:example.org', + }, + ), + ); + }); + test('getEventById', () async { + final event = await database.getEventById( + clientId, '\$event:example.com', Room(id: '!testroom:example.com')); + expect(event.type, EventTypes.Message); + }); + test('getEventList', () async { + final events = await database.getEventList( + clientId, Room(id: '!testroom:example.com')); + expect(events.single.type, EventTypes.Message); + }); + test('getUser', () async { + final user = await database.getUser( + clientId, '@bob:example.org', Room(id: '!testroom:example.com')); + expect(user, null); + }); + test('getUsers', () async { + final users = + await database.getUsers(clientId, Room(id: '!testroom:example.com')); + expect(users.isEmpty, true); + }); + test('removeEvent', () async { + await database.removeEvent( + clientId, '\$event:example.com', '!testroom:example.com'); + final event = await database.getEventById( + clientId, '\$event:example.com', Room(id: '!testroom:example.com')); + expect(event, null); + }); + test('getInboundGroupSession', () async { + await database.getInboundGroupSession( + clientId, '!testroom:example.com', 'sessionId'); + }); + test('getInboundGroupSessionsToUpload', () async { + await database.getInboundGroupSessionsToUpload(); + }); + test('getInboundGroupSessionsToUpload', () async { + await database.storeInboundGroupSession( + clientId, + '!testroom:example.com', + 'sessionId', + 'pickle', + '{"foo":"bar"}', + '{}', + '{}', + 'senderKey', + '{}', + ); + final session = await database.getInboundGroupSession( + clientId, + '!testroom:example.com', + 'sessionId', + ); + expect(jsonDecode(session.content)['foo'], 'bar'); + }); + test('markInboundGroupSessionAsUploaded', () async { + await database.markInboundGroupSessionAsUploaded( + clientId, '!testroom:example.com', 'sessionId'); + }); + test('markInboundGroupSessionsAsNeedingUpload', () async { + await database.markInboundGroupSessionsAsNeedingUpload(clientId); + }); + test('updateInboundGroupSessionAllowedAtIndex', () async { + await database.updateInboundGroupSessionAllowedAtIndex( + '{}', + clientId, + '!testroom:example.com', + 'sessionId', + ); + }); + test('updateInboundGroupSessionIndexes', () async { + await database.updateInboundGroupSessionIndexes( + '{}', + clientId, + '!testroom:example.com', + 'sessionId', + ); + }); + test('getSSSSCache', () async { + final cache = await database.getSSSSCache(clientId, 'type'); + expect(cache, null); + }); + test('storeSSSSCache', () async { + await database.storeSSSSCache( + clientId, 'type', 'keyId', 'ciphertext', '{}'); + final cache = await database.getSSSSCache(clientId, 'type'); + expect(cache.type, 'type'); + expect(cache.keyId, 'keyId'); + expect(cache.ciphertext, 'ciphertext'); + expect(cache.content, '{}'); + }); + test('getOlmSessions', () async { + final olm = await database.getOlmSessions( + clientId, + 'identityKey', + 'userId', + ); + expect(olm.isEmpty, true); + }); + test('getOlmSessionsForDevices', () async { + final olm = await database.getOlmSessionsForDevices( + clientId, + ['identityKeys'], + 'userId', + ); + expect(olm.isEmpty, true); + }); + test('storeOlmSession', () async { + if (!(await olmEnabled())) return; + await database.storeOlmSession( + clientId, + 'identityKey', + 'sessionId', + 'pickle', + 0, + ); + final olm = await database.getOlmSessions( + clientId, + 'identityKey', + 'userId', + ); + expect(olm.isNotEmpty, true); + }); + test('getOutboundGroupSession', () async { + final session = await database.getOutboundGroupSession( + clientId, + '!testroom:example.com', + '@alice:example.com', + ); + expect(session, null); + }); + test('storeOutboundGroupSession', () async { + if (!(await olmEnabled())) return; + await database.storeOutboundGroupSession( + clientId, + '!testroom:example.com', + 'pickle', + '{}', + 0, + 0, + ); + final session = await database.getOutboundGroupSession( + clientId, + '!testroom:example.com', + '@alice:example.com', + ); + expect(session.devices.isEmpty, true); + }); + test('getLastSentMessageUserDeviceKey', () async { + final list = await database.getLastSentMessageUserDeviceKey( + clientId, + 'userId', + 'deviceId', + ); + expect(list.isEmpty, true); + }); + test('getUnimportantRoomEventStatesForRoom', () async { + final events = await database.getUnimportantRoomEventStatesForRoom( + clientId, + ['events'], + Room(id: '!mep'), + ); + expect(events.isEmpty, true); + }); + test('getUserDeviceKeys', () async { + await database.getUserDeviceKeys(Client('testclient')); + }); + test('storeUserCrossSigningKey', () async { + await database.storeUserCrossSigningKey( + clientId, + '@alice:example.com', + 'publicKey', + '{}', + false, + false, + ); + }); + test('setVerifiedUserCrossSigningKey', () async { + await database.setVerifiedUserCrossSigningKey( + true, + clientId, + '@alice:example.com', + 'publicKey', + ); + }); + test('setBlockedUserCrossSigningKey', () async { + await database.setBlockedUserCrossSigningKey( + true, + clientId, + '@alice:example.com', + 'publicKey', + ); + }); + test('removeUserCrossSigningKey', () async { + await database.removeUserCrossSigningKey( + clientId, + '@alice:example.com', + 'publicKey', + ); + }); + test('storeUserDeviceKeysInfo', () async { + await database.storeUserDeviceKeysInfo( + clientId, + '@alice:example.com', + true, + ); + }); + test('storeUserDeviceKey', () async { + await database.storeUserDeviceKey( + clientId, + '@alice:example.com', + 'deviceId', + '{}', + false, + false, + 0, + ); + }); + test('setVerifiedUserDeviceKey', () async { + await database.setVerifiedUserDeviceKey( + true, + clientId, + '@alice:example.com', + 'deviceId', + ); + }); + test('setBlockedUserDeviceKey', () async { + await database.setBlockedUserDeviceKey( + true, + clientId, + '@alice:example.com', + 'deviceId', + ); + }); + + // Clearing up from here + test('clearSSSSCache', () async { + await database.clearSSSSCache(clientId); + }); + test('clearCache', () async { + await database.clearCache(clientId); + }); + test('clear', () async { + await database.clear(clientId); + }); + test('Close', () async { + await database.close(); + }); + return; +} diff --git a/test/room_test.dart b/test/room_test.dart index 2beda6da..92b19796 100644 --- a/test/room_test.dart +++ b/test/room_test.dart @@ -22,7 +22,7 @@ import 'dart:typed_data'; import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/src/client.dart'; import 'package:famedlysdk/src/database/database.dart' - show DbRoom, DbRoomState, DbRoomAccountData; + show DbRoom, DbRoomAccountData, DbRoomState, getRoomFromTableRow; import 'package:famedlysdk/src/event.dart'; import 'package:famedlysdk/src/room.dart'; import 'package:famedlysdk/src/user.dart'; @@ -94,7 +94,7 @@ void main() { ), ]; - room = await Room.getRoomFromTableRow( + room = await getRoomFromTableRow( dbRoom, matrix, states: states,