refactor: Create a clean database API
This commit is contained in:
parent
6fae2e1426
commit
c66e35c16c
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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] = <String, SessionKey>{};
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -410,16 +410,9 @@ class OlmManager {
|
|||
if (client.database == null) {
|
||||
return [];
|
||||
}
|
||||
final rows =
|
||||
await client.database.dbGetOlmSessions(client.id, senderKey).get();
|
||||
final res = <OlmSession>[];
|
||||
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<void> 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 = <String, List<OlmSession>>{};
|
||||
for (final row in rows) {
|
||||
res[row.identityKey] ??= <OlmSession>[];
|
||||
final sess = OlmSession.fromDb(row, client.userID);
|
||||
for (final sess in rows) {
|
||||
res[sess.identityKey] ??= <OlmSession>[];
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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 = <String>{
|
||||
EventTypes.CrossSigningSelfSigning,
|
||||
|
|
@ -56,7 +56,7 @@ class SSSS {
|
|||
final pendingShareRequests = <String, _ShareRequest>{};
|
||||
final _validators = <String, FutureOr<bool> Function(String)>{};
|
||||
final _cacheCallbacks = <String, FutureOr<void> Function(String)>{};
|
||||
final Map<String, DbSSSSCache> _cache = <String, DbSSSSCache>{};
|
||||
final Map<String, SSSSCache> _cache = <String, SSSSCache>{};
|
||||
SSSS(this.encryption);
|
||||
|
||||
// for testing
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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 ??= <String, Map<String, int>>{};
|
||||
}
|
||||
|
||||
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 =
|
||||
|
|
|
|||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<String, dynamic> json) => SSSSCache(
|
||||
clientId: json['client_id'],
|
||||
type: json['type'],
|
||||
keyId: json['key_id'],
|
||||
ciphertext: json['ciphertext'],
|
||||
content: json['content'],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'client_id': clientId,
|
||||
'type': type,
|
||||
'key_id': keyId,
|
||||
'ciphertext': ciphertext,
|
||||
'content': content,
|
||||
};
|
||||
}
|
||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<String, dynamic> 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'],
|
||||
);
|
||||
}
|
||||
|
|
@ -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<Database> Function(Client) databaseBuilder;
|
||||
final FutureOr<DatabaseApi> Function(Client) databaseBuilder;
|
||||
final FutureOr<void> 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<String, Map<String, Map<String, dynamic>>>(
|
||||
k,
|
||||
(v as Map).map((k, v) => MapEntry<String, Map<String, dynamic>>(
|
||||
k, Map<String, dynamic>.from(v)))));
|
||||
|
||||
await super.sendToDevice(entry.type, entry.txnId, data);
|
||||
await database.deleteFromToDeviceQueue(id, entry.id);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<DbClient> 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<Map<String, sdk.DeviceKeysList>> getUserDeviceKeys(
|
||||
sdk.Client client) async {
|
||||
final deviceKeys = await getAllUserDeviceKeys(client.id).get();
|
||||
|
|
@ -228,45 +238,51 @@ class Database extends _$Database {
|
|||
return res;
|
||||
}
|
||||
|
||||
Future<DbOutboundGroupSession> getDbOutboundGroupSession(
|
||||
int clientId, String roomId) async {
|
||||
@override
|
||||
Future<OutboundGroupSession> 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<List<DbInboundGroupSession>> getDbInboundGroupSessions(
|
||||
int clientId, String roomId) async {
|
||||
return await dbGetInboundGroupSessionKeys(clientId, roomId).get();
|
||||
}
|
||||
|
||||
Future<DbInboundGroupSession> getDbInboundGroupSession(
|
||||
int clientId, String roomId, String sessionId) async {
|
||||
@override
|
||||
Future<SessionKey> 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<DbSSSSCache> getSSSSCache(int clientId, String type) async {
|
||||
@override
|
||||
Future<SSSSCache> 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<List<sdk.Room>> 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<List<sdk.Room>> 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<Map<String, api.BasicEvent>> getAccountData(int clientId) async {
|
||||
final newAccountData = <String, api.BasicEvent>{};
|
||||
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<String> _ensuredRooms = {};
|
||||
@override
|
||||
Future<void> 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<void> storeEventUpdate(
|
||||
int clientId, sdk.EventUpdate eventUpdate) async {
|
||||
if (eventUpdate.type == sdk.EventUpdateType.ephemeral) return;
|
||||
|
|
@ -579,6 +598,7 @@ class Database extends _$Database {
|
|||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<sdk.Event> 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<void> forgetRoom(int clientId, String roomId) async {
|
||||
final setKey = '$clientId;$roomId';
|
||||
_ensuredRooms.remove(setKey);
|
||||
|
|
@ -634,6 +655,7 @@ class Database extends _$Database {
|
|||
.go();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> 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<void> clear(int clientId) async {
|
||||
await clearCache(clientId);
|
||||
await (delete(inboundGroupSessions)
|
||||
|
|
@ -669,6 +692,7 @@ class Database extends _$Database {
|
|||
.go();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<sdk.User> 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<List<sdk.User>> 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<List<sdk.Event>> 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<Uint8List> getFile(String mxcUri) async {
|
||||
final res = await dbGetFile(mxcUri).get();
|
||||
if (res.isEmpty) return null;
|
||||
return res.single.bytes;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<sdk.Event>> getUnimportantRoomEventStatesForRoom(
|
||||
int client_id,
|
||||
List<String> 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<List<OlmSession>> 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<List<OlmSession>> getOlmSessionsForDevices(
|
||||
int client_id,
|
||||
List<String> 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<List<QueuedToDeviceEvent>> 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<String, dynamic>).copy(),
|
||||
))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<String>> getLastSentMessageUserDeviceKey(
|
||||
int client_id,
|
||||
String user_id,
|
||||
String device_id,
|
||||
) =>
|
||||
dbGetLastSentMessageUserDeviceKey(client_id, user_id, device_id).get();
|
||||
|
||||
@override
|
||||
Future<List<StoredInboundGroupSession>>
|
||||
getInboundGroupSessionsToUpload() async {
|
||||
final rows = await dbGetInboundGroupSessionsToUpload().get();
|
||||
return rows
|
||||
.map((row) => StoredInboundGroupSession.fromJson(row.toJson()))
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6881,7 +6881,7 @@ abstract class _$Database extends GeneratedDatabase {
|
|||
);
|
||||
}
|
||||
|
||||
Selectable<DbInboundGroupSession> getInboundGroupSessionsToUpload() {
|
||||
Selectable<DbInboundGroupSession> dbGetInboundGroupSessionsToUpload() {
|
||||
return customSelect(
|
||||
'SELECT * FROM inbound_group_sessions WHERE uploaded = false LIMIT 500',
|
||||
variables: [],
|
||||
|
|
@ -7021,7 +7021,7 @@ abstract class _$Database extends GeneratedDatabase {
|
|||
);
|
||||
}
|
||||
|
||||
Selectable<String> getLastSentMessageUserDeviceKey(
|
||||
Selectable<String> 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',
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<dynamic> getClient(String name);
|
||||
|
||||
Future<Map<String, DeviceKeysList>> getUserDeviceKeys(Client client);
|
||||
|
||||
Future<OutboundGroupSession> getOutboundGroupSession(
|
||||
int clientId,
|
||||
String roomId,
|
||||
String userId,
|
||||
);
|
||||
|
||||
Future<SessionKey> getInboundGroupSession(
|
||||
int clientId,
|
||||
String roomId,
|
||||
String sessionId,
|
||||
String userId,
|
||||
);
|
||||
|
||||
Future<SSSSCache> getSSSSCache(int clientId, String type);
|
||||
|
||||
Future<List<Room>> getRoomList(Client client);
|
||||
|
||||
Future<Map<String, BasicEvent>> getAccountData(int clientId);
|
||||
|
||||
/// Stores a RoomUpdate object in the database. Must be called inside of
|
||||
/// [transaction].
|
||||
Future<void> storeRoomUpdate(int clientId, RoomUpdate roomUpdate,
|
||||
[Room oldRoom]);
|
||||
|
||||
/// Stores an EventUpdate object in the database. Must be called inside of
|
||||
/// [transaction].
|
||||
Future<void> storeEventUpdate(int clientId, EventUpdate eventUpdate);
|
||||
|
||||
Future<Event> getEventById(int clientId, String eventId, Room room);
|
||||
|
||||
Future<void> forgetRoom(int clientId, String roomId);
|
||||
|
||||
Future<void> clearCache(int clientId);
|
||||
|
||||
Future<void> clear(int clientId);
|
||||
|
||||
Future<User> getUser(int clientId, String userId, Room room);
|
||||
|
||||
Future<List<User>> getUsers(int clientId, Room room);
|
||||
|
||||
Future<List<Event>> getEventList(int clientId, Room room);
|
||||
|
||||
Future<Uint8List> getFile(String mxcUri);
|
||||
|
||||
Future<int> updateInboundGroupSessionIndexes(
|
||||
String indexes,
|
||||
int clientId,
|
||||
String roomId,
|
||||
String sessionId,
|
||||
);
|
||||
|
||||
Future<int> storeInboundGroupSession(
|
||||
int clientId,
|
||||
String roomId,
|
||||
String sessionId,
|
||||
String pickle,
|
||||
String content,
|
||||
String indexes,
|
||||
String allowedAtIndex,
|
||||
String senderKey,
|
||||
String senderClaimedKey,
|
||||
);
|
||||
|
||||
Future<int> markInboundGroupSessionAsUploaded(
|
||||
int clientId,
|
||||
String roomId,
|
||||
String sessionId,
|
||||
);
|
||||
|
||||
Future<int> updateInboundGroupSessionAllowedAtIndex(
|
||||
String allowedAtIndex,
|
||||
int clientId,
|
||||
String roomId,
|
||||
String sessionId,
|
||||
);
|
||||
|
||||
Future<int> removeOutboundGroupSession(int clientId, String roomId);
|
||||
|
||||
Future<int> storeFile(String mxcUri, Uint8List bytes, int time);
|
||||
|
||||
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,
|
||||
String roomId,
|
||||
String pickle,
|
||||
String deviceIds,
|
||||
int creationTime,
|
||||
int sentMessages,
|
||||
);
|
||||
|
||||
Future<int> updateClientKeys(String olmAccount, int clientId);
|
||||
|
||||
Future<int> storeOlmSession(
|
||||
int clientId,
|
||||
String identitiyKey,
|
||||
String sessionId,
|
||||
String pickle,
|
||||
int lastReceived,
|
||||
);
|
||||
|
||||
Future<int> setLastActiveUserDeviceKey(
|
||||
int lastActive,
|
||||
int clientId,
|
||||
String userId,
|
||||
String deviceId,
|
||||
);
|
||||
|
||||
Future<int> setLastSentMessageUserDeviceKey(
|
||||
String lastSentMessage,
|
||||
int clientId,
|
||||
String userId,
|
||||
String deviceId,
|
||||
);
|
||||
|
||||
Future<int> clearSSSSCache(int clientId);
|
||||
|
||||
Future<int> storeSSSSCache(
|
||||
int clientId,
|
||||
String type,
|
||||
String keyId,
|
||||
String ciphertext,
|
||||
String content,
|
||||
);
|
||||
|
||||
Future<int> markInboundGroupSessionsAsNeedingUpload(int clientId);
|
||||
|
||||
Future<int> storePrevBatch(String prevBatch, int clientId);
|
||||
|
||||
Future<int> deleteOldFiles(int savedAt);
|
||||
|
||||
Future<int> storeUserDeviceKeysInfo(
|
||||
int clientId,
|
||||
String userId,
|
||||
bool outdated,
|
||||
);
|
||||
|
||||
Future<int> storeUserDeviceKey(
|
||||
int clientId,
|
||||
String userId,
|
||||
String deviceId,
|
||||
String content,
|
||||
bool verified,
|
||||
bool blocked,
|
||||
int lastActive,
|
||||
);
|
||||
|
||||
Future<int> removeUserDeviceKey(
|
||||
int clientId,
|
||||
String userId,
|
||||
String deviceId,
|
||||
);
|
||||
|
||||
Future<int> removeUserCrossSigningKey(
|
||||
int clientId,
|
||||
String userId,
|
||||
String publicKey,
|
||||
);
|
||||
|
||||
Future<int> storeUserCrossSigningKey(
|
||||
int clientId,
|
||||
String userId,
|
||||
String publicKey,
|
||||
String content,
|
||||
bool verified,
|
||||
bool blocked,
|
||||
);
|
||||
|
||||
Future<int> deleteFromToDeviceQueue(int clientId, int id);
|
||||
|
||||
Future<int> removeEvent(int clientId, String eventId, String roomId);
|
||||
|
||||
Future<int> updateRoomSortOrder(
|
||||
double oldestSortOrder,
|
||||
double newestSortOrder,
|
||||
int clientId,
|
||||
String roomId,
|
||||
);
|
||||
|
||||
Future<int> setRoomPrevBatch(
|
||||
String prevBatch,
|
||||
int clientId,
|
||||
String roomId,
|
||||
);
|
||||
|
||||
Future<int> resetNotificationCount(int clientId, String roomId);
|
||||
|
||||
Future<int> setVerifiedUserCrossSigningKey(
|
||||
bool verified,
|
||||
int clientId,
|
||||
String userId,
|
||||
String publicKey,
|
||||
);
|
||||
|
||||
Future<int> setBlockedUserCrossSigningKey(
|
||||
bool blocked,
|
||||
int clientId,
|
||||
String userId,
|
||||
String publicKey,
|
||||
);
|
||||
|
||||
Future<int> setVerifiedUserDeviceKey(
|
||||
bool verified,
|
||||
int clientId,
|
||||
String userId,
|
||||
String deviceId,
|
||||
);
|
||||
|
||||
Future<int> setBlockedUserDeviceKey(
|
||||
bool blocked,
|
||||
int clientId,
|
||||
String userId,
|
||||
String deviceId,
|
||||
);
|
||||
|
||||
Future<List<Event>> getUnimportantRoomEventStatesForRoom(
|
||||
int clientId,
|
||||
List<String> events,
|
||||
Room room,
|
||||
);
|
||||
|
||||
Future<List<OlmSession>> getOlmSessions(
|
||||
int clientId,
|
||||
String identityKey,
|
||||
String userId,
|
||||
);
|
||||
|
||||
Future<List<OlmSession>> getOlmSessionsForDevices(
|
||||
int clientId,
|
||||
List<String> identityKeys,
|
||||
String userId,
|
||||
);
|
||||
|
||||
Future<List<QueuedToDeviceEvent>> getToDeviceEventQueue(int clientId);
|
||||
|
||||
/// Please do `jsonEncode(content)` in your code to stay compatible with
|
||||
/// auto generated methods here.
|
||||
Future<int> insertIntoToDeviceQueue(
|
||||
int clientId,
|
||||
String type,
|
||||
String txnId,
|
||||
String content,
|
||||
);
|
||||
|
||||
Future<List<String>> getLastSentMessageUserDeviceKey(
|
||||
int clientId,
|
||||
String userId,
|
||||
String deviceId,
|
||||
);
|
||||
|
||||
Future<List<StoredInboundGroupSession>> getInboundGroupSessionsToUpload();
|
||||
|
||||
Future<dynamic> close();
|
||||
|
||||
Future<T> transaction<T>(Future<T> Function() action);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
class QueuedToDeviceEvent {
|
||||
final int id;
|
||||
final String type;
|
||||
final String txnId;
|
||||
final Map<String, dynamic> content;
|
||||
|
||||
QueuedToDeviceEvent({this.id, this.type, this.txnId, this.content});
|
||||
|
||||
factory QueuedToDeviceEvent.fromJson(Map<String, dynamic> json) =>
|
||||
QueuedToDeviceEvent(
|
||||
id: json['id'],
|
||||
type: json['type'],
|
||||
txnId: json['txn_id'],
|
||||
content: json['content'],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'id': id,
|
||||
'type': type,
|
||||
'txn_id': txnId,
|
||||
'content': content,
|
||||
};
|
||||
}
|
||||
|
|
@ -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));
|
||||
|
|
|
|||
Loading…
Reference in New Issue