CI: Use correct image

This commit is contained in:
Christian Pauly 2021-05-20 09:21:37 +02:00
parent c66e35c16c
commit c3d47b16c6
14 changed files with 674 additions and 222 deletions

View File

@ -85,7 +85,7 @@ code_analyze:
tags: tags:
- docker - docker
stage: coverage stage: coverage
image: cirrusci/flutter image: registry.gitlab.com/famedly/containers/flutter-dockerimages:stable
dependencies: [] dependencies: []
script: script:
- flutter format lib/ test/ test_driver/ --set-exit-if-changed - flutter format lib/ test/ test_driver/ --set-exit-if-changed

View File

@ -231,11 +231,12 @@ class KeyManager {
} }
return sess; // nothing to do return sess; // nothing to do
} }
final sess = await client.database final session = await client.database
?.getInboundGroupSession(client.id, roomId, sessionId, client.userID); ?.getInboundGroupSession(client.id, roomId, sessionId);
if (sess == null) { if (session == null) {
return null; return null;
} }
final sess = SessionKey.fromDb(session, client.userID);
if (!_inboundGroupSessions.containsKey(roomId)) { if (!_inboundGroupSessions.containsKey(roomId)) {
_inboundGroupSessions[roomId] = <String, SessionKey>{}; _inboundGroupSessions[roomId] = <String, SessionKey>{};
} }

View File

@ -19,7 +19,6 @@
import 'package:olm/olm.dart' as olm; import 'package:olm/olm.dart' as olm;
import '../../famedlysdk.dart'; import '../../famedlysdk.dart';
import '../../src/database/database.dart' show DbOlmSessions;
class OlmSession { class OlmSession {
String identityKey; String identityKey;
@ -39,14 +38,14 @@ class OlmSession {
this.lastReceived, this.lastReceived,
}); });
OlmSession.fromDb(DbOlmSessions dbEntry, String key) : key = key { OlmSession.fromJson(Map<String, dynamic> dbEntry, String key) : key = key {
session = olm.Session(); session = olm.Session();
try { try {
session.unpickle(key, dbEntry.pickle); session.unpickle(key, dbEntry['pickle']);
identityKey = dbEntry.identityKey; identityKey = dbEntry['identity_key'];
sessionId = dbEntry.sessionId; sessionId = dbEntry['session_id'];
lastReceived = lastReceived =
DateTime.fromMillisecondsSinceEpoch(dbEntry.lastReceived ?? 0); DateTime.fromMillisecondsSinceEpoch(dbEntry['last_received'] ?? 0);
assert(sessionId == session.session_id()); assert(sessionId == session.session_id());
} catch (e, s) { } catch (e, s) {
Logs().e('[LibOlm] Could not unpickle olm session', e, s); Logs().e('[LibOlm] Could not unpickle olm session', e, s);

View File

@ -21,7 +21,6 @@ import 'dart:convert';
import 'package:olm/olm.dart' as olm; import 'package:olm/olm.dart' as olm;
import '../../famedlysdk.dart'; import '../../famedlysdk.dart';
import '../../src/database/database.dart' show DbOutboundGroupSession;
class OutboundGroupSession { class OutboundGroupSession {
/// The devices is a map from user id to device id to if the device is blocked. /// 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.sentMessages,
this.key}); this.key});
OutboundGroupSession.fromDb(DbOutboundGroupSession dbEntry, String key) OutboundGroupSession.fromJson(Map<String, dynamic> dbEntry, String key)
: key = key { : key = key {
try { try {
devices = {}; devices = {};
for (final entry in json.decode(dbEntry.deviceIds).entries) { for (final entry in json.decode(dbEntry['device_ids']).entries) {
devices[entry.key] = Map<String, bool>.from(entry.value); devices[entry.key] = Map<String, bool>.from(entry.value);
} }
} catch (e) { } catch (e) {
@ -58,9 +57,10 @@ class OutboundGroupSession {
} }
outboundGroupSession = olm.OutboundGroupSession(); outboundGroupSession = olm.OutboundGroupSession();
try { try {
outboundGroupSession.unpickle(key, dbEntry.pickle); outboundGroupSession.unpickle(key, dbEntry['pickle']);
creationTime = DateTime.fromMillisecondsSinceEpoch(dbEntry.creationTime); creationTime =
sentMessages = dbEntry.sentMessages; DateTime.fromMillisecondsSinceEpoch(dbEntry['creation_time']);
sentMessages = dbEntry['sent_messages'];
} catch (e, s) { } catch (e, s) {
dispose(); dispose();
Logs().e('[LibOlm] Unable to unpickle outboundGroupSession', e, s); Logs().e('[LibOlm] Unable to unpickle outboundGroupSession', e, s);

View File

@ -38,3 +38,4 @@ export 'src/room.dart';
export 'src/timeline.dart'; export 'src/timeline.dart';
export 'src/user.dart'; export 'src/user.dart';
export 'src/database/database.dart' show Database; export 'src/database/database.dart' show Database;
export 'src/database/database_api.dart';

View File

@ -843,15 +843,15 @@ class Client extends MatrixApi {
if (database != null) { if (database != null) {
final account = await database.getClient(clientName); final account = await database.getClient(clientName);
if (account != null) { if (account != null) {
_id = account.clientId; _id = account['client_id'];
homeserver = Uri.parse(account.homeserverUrl); homeserver = Uri.parse(account['homeserver_url']);
accessToken = account.token; accessToken = account['token'];
_userID = account.userId; _userID = account['user_id'];
_deviceID = account.deviceId; _deviceID = account['device_id'];
_deviceName = account.deviceName; _deviceName = account['device_name'];
syncFilterId = account.syncFilterId; syncFilterId = account['sync_filter_id'];
prevBatch = account.prevBatch; prevBatch = account['prev_batch'];
olmAccount = account.olmAccount; olmAccount = account['olm_account'];
} }
} }
if (newToken != null) { if (newToken != null) {

View File

@ -21,7 +21,6 @@ import 'dart:convert';
import 'package:famedlysdk/encryption/utils/olm_session.dart'; import 'package:famedlysdk/encryption/utils/olm_session.dart';
import 'package:famedlysdk/encryption/utils/outbound_group_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/ssss_cache.dart';
import 'package:famedlysdk/encryption/utils/stored_inbound_group_session.dart'; import 'package:famedlysdk/encryption/utils/stored_inbound_group_session.dart';
import 'package:famedlysdk/src/utils/QueuedToDeviceEvent.dart'; import 'package:famedlysdk/src/utils/QueuedToDeviceEvent.dart';
@ -211,11 +210,11 @@ class Database extends _$Database implements DatabaseApi {
); );
@override @override
Future<DbClient> getClient(String name) async { Future<Map<String, dynamic>> getClient(String name) async {
final res = await dbGetClient(name).get(); final res = await dbGetClient(name).get();
if (res.isEmpty) return null; if (res.isEmpty) return null;
await markPendingEventsAsError(res.single.clientId); await markPendingEventsAsError(res.single.clientId);
return res.single; return res.single.toJson();
} }
@override @override
@ -229,10 +228,16 @@ class Database extends _$Database implements DatabaseApi {
final crossSigningKeys = await getAllUserCrossSigningKeys(client.id).get(); final crossSigningKeys = await getAllUserCrossSigningKeys(client.id).get();
final res = <String, sdk.DeviceKeysList>{}; final res = <String, sdk.DeviceKeysList>{};
for (final entry in deviceKeys) { for (final entry in deviceKeys) {
res[entry.userId] = sdk.DeviceKeysList.fromDb( res[entry.userId] = sdk.DeviceKeysList.fromDbJson(
entry, entry.toJson(),
deviceKeysKeys.where((k) => k.userId == entry.userId).toList(), deviceKeysKeys
crossSigningKeys.where((k) => k.userId == entry.userId).toList(), .where((k) => k.userId == entry.userId)
.map((d) => d.toJson())
.toList(),
crossSigningKeys
.where((k) => k.userId == entry.userId)
.map((d) => d.toJson())
.toList(),
client); client);
} }
return res; return res;
@ -245,23 +250,21 @@ class Database extends _$Database implements DatabaseApi {
if (res.isEmpty) { if (res.isEmpty) {
return null; return null;
} }
return OutboundGroupSession.fromDb(res.single, userId); return OutboundGroupSession.fromJson(res.single.toJson(), userId);
} }
@override @override
Future<SessionKey> getInboundGroupSession( Future<StoredInboundGroupSession> getInboundGroupSession(
int clientId, int clientId,
String roomId, String roomId,
String sessionId, String sessionId,
String userId,
) async { ) async {
final res = final res =
await dbGetInboundGroupSessionKey(clientId, roomId, sessionId).get(); await dbGetInboundGroupSessionKey(clientId, roomId, sessionId).get();
if (res.isEmpty) { if (res.isEmpty) {
return null; return null;
} }
return SessionKey.fromDb( return StoredInboundGroupSession.fromJson(res.single.toJson());
StoredInboundGroupSession.fromJson(res.single.toJson()), userId);
} }
@override @override
@ -290,7 +293,7 @@ class Database extends _$Database implements DatabaseApi {
final roomList = <sdk.Room>[]; final roomList = <sdk.Room>[];
final allMembersToPostload = <String, Set<String>>{}; final allMembersToPostload = <String, Set<String>>{};
for (final r in res) { for (final r in res) {
final room = await sdk.Room.getRoomFromTableRow( final room = await getRoomFromTableRow(
r, r,
client, client,
states: resStates.where((rs) => rs.roomId == r.roomId), 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 // now that we got all the entries from the database, set them as room states
for (final dbMember in membersRes) { for (final dbMember in membersRes) {
final room = roomList.firstWhere((r) => r.id == dbMember.roomId); final room = roomList.firstWhere((r) => r.id == dbMember.roomId);
final event = sdk.Event.fromDb(dbMember, room); final event = getEventFromDb(dbMember, room);
room.setState(event); room.setState(event);
} }
} }
@ -605,7 +608,7 @@ class Database extends _$Database implements DatabaseApi {
if (event.isEmpty) { if (event.isEmpty) {
return null; return null;
} }
return sdk.Event.fromDb(event.single, room); return getEventFromDb(event.single, room);
} }
Future<bool> redactMessage(int clientId, sdk.EventUpdate eventUpdate) async { Future<bool> redactMessage(int clientId, sdk.EventUpdate eventUpdate) async {
@ -614,7 +617,7 @@ class Database extends _$Database implements DatabaseApi {
.get(); .get();
var success = false; var success = false;
for (final dbEvent in events) { 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)); event.setRedactionEvent(sdk.Event.fromJson(eventUpdate.content, null));
final changes1 = await updateEvent( final changes1 = await updateEvent(
json.encode(event.unsigned ?? ''), json.encode(event.unsigned ?? ''),
@ -698,19 +701,19 @@ class Database extends _$Database implements DatabaseApi {
if (res.isEmpty) { if (res.isEmpty) {
return null; return null;
} }
return sdk.Event.fromDb(res.single, room).asUser; return getEventFromDb(res.single, room).asUser;
} }
@override @override
Future<List<sdk.User>> getUsers(int clientId, sdk.Room room) async { Future<List<sdk.User>> getUsers(int clientId, sdk.Room room) async {
final res = await dbGetUsers(clientId, room.id).get(); 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 @override
Future<List<sdk.Event>> getEventList(int clientId, sdk.Room room) async { Future<List<sdk.Event>> getEventList(int clientId, sdk.Room room) async {
final res = await dbGetEventList(clientId, room.id).get(); 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 @override
@ -731,7 +734,7 @@ class Database extends _$Database implements DatabaseApi {
room.id, room.id,
events, events,
).get(); ).get();
return entries.map((dbEvent) => sdk.Event.fromDb(dbEvent, room)).toList(); return entries.map((dbEvent) => getEventFromDb(dbEvent, room)).toList();
} }
@override @override
@ -741,7 +744,9 @@ class Database extends _$Database implements DatabaseApi {
String userId, String userId,
) async { ) async {
final rows = await dbGetOlmSessions(client_id, identity_key).get(); 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 @override
@ -752,7 +757,9 @@ class Database extends _$Database implements DatabaseApi {
) async { ) async {
final rows = final rows =
await dbGetOlmSessionsForDevices(client_id, identity_keys).get(); 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 @override
@ -786,3 +793,82 @@ class Database extends _$Database implements DatabaseApi {
.toList(); .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<Room> getRoomFromTableRow(
// either Map<String, dynamic> 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 = <String, sdk.BasicRoomEvent>{};
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;
}

View File

@ -20,7 +20,6 @@ import 'dart:typed_data';
import 'package:famedlysdk/encryption/utils/olm_session.dart'; import 'package:famedlysdk/encryption/utils/olm_session.dart';
import 'package:famedlysdk/encryption/utils/outbound_group_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/ssss_cache.dart';
import 'package:famedlysdk/encryption/utils/stored_inbound_group_session.dart'; import 'package:famedlysdk/encryption/utils/stored_inbound_group_session.dart';
import 'package:famedlysdk/src/utils/QueuedToDeviceEvent.dart'; import 'package:famedlysdk/src/utils/QueuedToDeviceEvent.dart';
@ -29,25 +28,30 @@ import '../../famedlysdk.dart';
abstract class DatabaseApi { abstract class DatabaseApi {
int get maxFileSize => 1 * 1024 * 1024; int get maxFileSize => 1 * 1024 * 1024;
Future<dynamic> getClient(String name); Future<Map<String, dynamic>> getClient(String name);
Future<Map<String, DeviceKeysList>> getUserDeviceKeys(Client client); Future updateClient(
String homeserverUrl,
Future<OutboundGroupSession> getOutboundGroupSession( String token,
int clientId,
String roomId,
String userId, String userId,
String deviceId,
String deviceName,
String prevBatch,
String olmAccount,
int clientId,
); );
Future<SessionKey> getInboundGroupSession( Future insertClient(
int clientId, String name,
String roomId, String homeserverUrl,
String sessionId, String token,
String userId, String userId,
String deviceId,
String deviceName,
String prevBatch,
String olmAccount,
); );
Future<SSSSCache> getSSSSCache(int clientId, String type);
Future<List<Room>> getRoomList(Client client); Future<List<Room>> getRoomList(Client client);
Future<Map<String, BasicEvent>> getAccountData(int clientId); Future<Map<String, BasicEvent>> getAccountData(int clientId);
@ -77,14 +81,36 @@ abstract class DatabaseApi {
Future<Uint8List> getFile(String mxcUri); Future<Uint8List> getFile(String mxcUri);
Future<int> updateInboundGroupSessionIndexes( Future storeFile(String mxcUri, Uint8List bytes, int time);
Future storeSyncFilterId(String syncFilterId, int clientId);
Future storeAccountData(int clientId, String type, String content);
Future<Map<String, DeviceKeysList>> getUserDeviceKeys(Client client);
Future<SSSSCache> getSSSSCache(int clientId, String type);
Future<OutboundGroupSession> getOutboundGroupSession(
int clientId,
String roomId,
String userId,
);
Future<StoredInboundGroupSession> getInboundGroupSession(
int clientId,
String roomId,
String sessionId,
);
Future updateInboundGroupSessionIndexes(
String indexes, String indexes,
int clientId, int clientId,
String roomId, String roomId,
String sessionId, String sessionId,
); );
Future<int> storeInboundGroupSession( Future storeInboundGroupSession(
int clientId, int clientId,
String roomId, String roomId,
String sessionId, String sessionId,
@ -96,50 +122,22 @@ abstract class DatabaseApi {
String senderClaimedKey, String senderClaimedKey,
); );
Future<int> markInboundGroupSessionAsUploaded( Future markInboundGroupSessionAsUploaded(
int clientId, int clientId,
String roomId, String roomId,
String sessionId, String sessionId,
); );
Future<int> updateInboundGroupSessionAllowedAtIndex( Future updateInboundGroupSessionAllowedAtIndex(
String allowedAtIndex, String allowedAtIndex,
int clientId, int clientId,
String roomId, String roomId,
String sessionId, String sessionId,
); );
Future<int> removeOutboundGroupSession(int clientId, String roomId); Future removeOutboundGroupSession(int clientId, String roomId);
Future<int> storeFile(String mxcUri, Uint8List bytes, int time); Future storeOutboundGroupSession(
Future<int> updateClient(
String homeserverUrl,
String token,
String userId,
String deviceId,
String deviceName,
String prevBatch,
String olmAccount,
int clientId,
);
Future<int> insertClient(
String name,
String homeserverUrl,
String token,
String userId,
String deviceId,
String deviceName,
String prevBatch,
String olmAccount,
);
Future<int> storeSyncFilterId(String syncFilterId, int clientId);
Future<int> storeAccountData(int clientId, String type, String content);
Future<int> storeOutboundGroupSession(
int clientId, int clientId,
String roomId, String roomId,
String pickle, String pickle,
@ -148,9 +146,9 @@ abstract class DatabaseApi {
int sentMessages, int sentMessages,
); );
Future<int> updateClientKeys(String olmAccount, int clientId); Future updateClientKeys(String olmAccount, int clientId);
Future<int> storeOlmSession( Future storeOlmSession(
int clientId, int clientId,
String identitiyKey, String identitiyKey,
String sessionId, String sessionId,
@ -158,23 +156,23 @@ abstract class DatabaseApi {
int lastReceived, int lastReceived,
); );
Future<int> setLastActiveUserDeviceKey( Future setLastActiveUserDeviceKey(
int lastActive, int lastActive,
int clientId, int clientId,
String userId, String userId,
String deviceId, String deviceId,
); );
Future<int> setLastSentMessageUserDeviceKey( Future setLastSentMessageUserDeviceKey(
String lastSentMessage, String lastSentMessage,
int clientId, int clientId,
String userId, String userId,
String deviceId, String deviceId,
); );
Future<int> clearSSSSCache(int clientId); Future clearSSSSCache(int clientId);
Future<int> storeSSSSCache( Future storeSSSSCache(
int clientId, int clientId,
String type, String type,
String keyId, String keyId,
@ -182,19 +180,19 @@ abstract class DatabaseApi {
String content, String content,
); );
Future<int> markInboundGroupSessionsAsNeedingUpload(int clientId); Future markInboundGroupSessionsAsNeedingUpload(int clientId);
Future<int> storePrevBatch(String prevBatch, int clientId); Future storePrevBatch(String prevBatch, int clientId);
Future<int> deleteOldFiles(int savedAt); Future deleteOldFiles(int savedAt);
Future<int> storeUserDeviceKeysInfo( Future storeUserDeviceKeysInfo(
int clientId, int clientId,
String userId, String userId,
bool outdated, bool outdated,
); );
Future<int> storeUserDeviceKey( Future storeUserDeviceKey(
int clientId, int clientId,
String userId, String userId,
String deviceId, String deviceId,
@ -204,19 +202,19 @@ abstract class DatabaseApi {
int lastActive, int lastActive,
); );
Future<int> removeUserDeviceKey( Future removeUserDeviceKey(
int clientId, int clientId,
String userId, String userId,
String deviceId, String deviceId,
); );
Future<int> removeUserCrossSigningKey( Future removeUserCrossSigningKey(
int clientId, int clientId,
String userId, String userId,
String publicKey, String publicKey,
); );
Future<int> storeUserCrossSigningKey( Future storeUserCrossSigningKey(
int clientId, int clientId,
String userId, String userId,
String publicKey, String publicKey,
@ -225,47 +223,47 @@ abstract class DatabaseApi {
bool blocked, bool blocked,
); );
Future<int> deleteFromToDeviceQueue(int clientId, int id); Future deleteFromToDeviceQueue(int clientId, int id);
Future<int> removeEvent(int clientId, String eventId, String roomId); Future removeEvent(int clientId, String eventId, String roomId);
Future<int> updateRoomSortOrder( Future updateRoomSortOrder(
double oldestSortOrder, double oldestSortOrder,
double newestSortOrder, double newestSortOrder,
int clientId, int clientId,
String roomId, String roomId,
); );
Future<int> setRoomPrevBatch( Future setRoomPrevBatch(
String prevBatch, String prevBatch,
int clientId, int clientId,
String roomId, String roomId,
); );
Future<int> resetNotificationCount(int clientId, String roomId); Future resetNotificationCount(int clientId, String roomId);
Future<int> setVerifiedUserCrossSigningKey( Future setVerifiedUserCrossSigningKey(
bool verified, bool verified,
int clientId, int clientId,
String userId, String userId,
String publicKey, String publicKey,
); );
Future<int> setBlockedUserCrossSigningKey( Future setBlockedUserCrossSigningKey(
bool blocked, bool blocked,
int clientId, int clientId,
String userId, String userId,
String publicKey, String publicKey,
); );
Future<int> setVerifiedUserDeviceKey( Future setVerifiedUserDeviceKey(
bool verified, bool verified,
int clientId, int clientId,
String userId, String userId,
String deviceId, String deviceId,
); );
Future<int> setBlockedUserDeviceKey( Future setBlockedUserDeviceKey(
bool blocked, bool blocked,
int clientId, int clientId,
String userId, String userId,
@ -294,7 +292,7 @@ abstract class DatabaseApi {
/// Please do `jsonEncode(content)` in your code to stay compatible with /// Please do `jsonEncode(content)` in your code to stay compatible with
/// auto generated methods here. /// auto generated methods here.
Future<int> insertIntoToDeviceQueue( Future insertIntoToDeviceQueue(
int clientId, int clientId,
String type, String type,
String txnId, String txnId,

View File

@ -22,7 +22,6 @@ import 'dart:typed_data';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import '../famedlysdk.dart'; import '../famedlysdk.dart';
import 'database/database.dart' show DbRoomState, DbEvent;
import 'room.dart'; import 'room.dart';
import 'utils/matrix_localizations.dart'; import 'utils/matrix_localizations.dart';
import 'utils/receipt.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 @override
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final data = <String, dynamic>{}; final data = <String, dynamic>{};

View File

@ -24,7 +24,6 @@ import 'package:html_unescape/html_unescape.dart';
import '../famedlysdk.dart'; import '../famedlysdk.dart';
import 'client.dart'; import 'client.dart';
import 'database/database.dart' show DbRoom;
import 'event.dart'; import 'event.dart';
import 'timeline.dart'; import 'timeline.dart';
import 'user.dart'; import 'user.dart';
@ -1079,58 +1078,6 @@ class Room {
return; 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<Room> getRoomFromTableRow(
// either Map<String, dynamic> 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 = <String, BasicRoomEvent>{};
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. /// Creates a timeline from the store. Returns a [Timeline] object.
Future<Timeline> getTimeline( Future<Timeline> getTimeline(
{onTimelineUpdateCallback onUpdate, {onTimelineUpdateCallback onUpdate,

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
class QueuedToDeviceEvent { class QueuedToDeviceEvent {
final int id; final int id;
final String type; final String type;

View File

@ -25,8 +25,6 @@ import 'package:olm/olm.dart' as olm;
import '../../encryption.dart'; import '../../encryption.dart';
import '../client.dart'; import '../client.dart';
import '../database/database.dart'
show DbUserDeviceKey, DbUserDeviceKeysKey, DbUserCrossSigningKey;
import '../event.dart'; import '../event.dart';
import '../room.dart'; import '../room.dart';
import '../user.dart'; import '../user.dart';
@ -104,27 +102,27 @@ class DeviceKeysList {
} }
} }
DeviceKeysList.fromDb( DeviceKeysList.fromDbJson(
DbUserDeviceKey dbEntry, Map<String, dynamic> dbEntry,
List<DbUserDeviceKeysKey> childEntries, List<Map<String, dynamic>> childEntries,
List<DbUserCrossSigningKey> crossSigningEntries, List<Map<String, dynamic>> crossSigningEntries,
Client cl) { Client cl) {
client = cl; client = cl;
userId = dbEntry.userId; userId = dbEntry['user_id'];
outdated = dbEntry.outdated; outdated = dbEntry['outdated'];
deviceKeys = {}; deviceKeys = {};
for (final childEntry in childEntries) { for (final childEntry in childEntries) {
final entry = DeviceKeys.fromDb(childEntry, client); final entry = DeviceKeys.fromDb(childEntry, client);
if (entry.isValid) { if (entry.isValid) {
deviceKeys[childEntry.deviceId] = entry; deviceKeys[childEntry['device_id']] = entry;
} else { } else {
outdated = true; outdated = true;
} }
} }
for (final crossSigningEntry in crossSigningEntries) { for (final crossSigningEntry in crossSigningEntries) {
final entry = CrossSigningKey.fromDb(crossSigningEntry, client); final entry = CrossSigningKey.fromDbJson(crossSigningEntry, client);
if (entry.isValid) { if (entry.isValid) {
crossSigningKeys[crossSigningEntry.publicKey] = entry; crossSigningKeys[crossSigningEntry['public_key']] = entry;
} else { } else {
outdated = true; outdated = true;
} }
@ -365,13 +363,13 @@ class CrossSigningKey extends SignableKey {
usage = json['usage'].cast<String>(); usage = json['usage'].cast<String>();
} }
CrossSigningKey.fromDb(DbUserCrossSigningKey dbEntry, Client cl) CrossSigningKey.fromDbJson(Map<String, dynamic> dbEntry, Client cl)
: super.fromJson(Event.getMapFromPayload(dbEntry.content), cl) { : super.fromJson(Event.getMapFromPayload(dbEntry['content']), cl) {
final json = toJson(); final json = toJson();
identifier = dbEntry.publicKey; identifier = dbEntry['public_key'];
usage = json['usage'].cast<String>(); usage = json['usage'].cast<String>();
_verified = dbEntry.verified; _verified = dbEntry['verified'];
blocked = dbEntry.blocked; blocked = dbEntry['blocked'];
} }
CrossSigningKey.fromJson(Map<String, dynamic> json, Client cl) CrossSigningKey.fromJson(Map<String, dynamic> json, Client cl)
@ -441,14 +439,15 @@ class DeviceKeys extends SignableKey {
lastActive = lastActiveTs ?? DateTime.now(); lastActive = lastActiveTs ?? DateTime.now();
} }
DeviceKeys.fromDb(DbUserDeviceKeysKey dbEntry, Client cl) DeviceKeys.fromDb(Map<String, dynamic> dbEntry, Client cl)
: super.fromJson(Event.getMapFromPayload(dbEntry.content), cl) { : super.fromJson(Event.getMapFromPayload(dbEntry['content']), cl) {
final json = toJson(); final json = toJson();
identifier = dbEntry.deviceId; identifier = dbEntry['device_id'];
algorithms = json['algorithms'].cast<String>(); algorithms = json['algorithms'].cast<String>();
_verified = dbEntry.verified; _verified = dbEntry['verified'];
blocked = dbEntry.blocked; blocked = dbEntry['blocked'];
lastActive = DateTime.fromMillisecondsSinceEpoch(dbEntry.lastActive ?? 0); lastActive =
DateTime.fromMillisecondsSinceEpoch(dbEntry['last_active'] ?? 0);
} }
DeviceKeys.fromJson(Map<String, dynamic> json, Client cl) DeviceKeys.fromJson(Map<String, dynamic> json, Client cl)

431
test/database_api_test.dart Normal file
View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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<bool> olmEnabled() async {
var olmEnabled = true;
try {
await olm.init();
olm.get_library_version();
} catch (e) {
olmEnabled = false;
}
return olmEnabled;
}
void testDatabase(Future<DatabaseApi> 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;
}

View File

@ -22,7 +22,7 @@ import 'dart:typed_data';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:famedlysdk/src/client.dart'; import 'package:famedlysdk/src/client.dart';
import 'package:famedlysdk/src/database/database.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/event.dart';
import 'package:famedlysdk/src/room.dart'; import 'package:famedlysdk/src/room.dart';
import 'package:famedlysdk/src/user.dart'; import 'package:famedlysdk/src/user.dart';
@ -94,7 +94,7 @@ void main() {
), ),
]; ];
room = await Room.getRoomFromTableRow( room = await getRoomFromTableRow(
dbRoom, dbRoom,
matrix, matrix,
states: states, states: states,