finally fixing #44

This commit is contained in:
otsmr 2025-05-30 23:38:05 +02:00
parent e69870ea14
commit 0d2533e9a6
15 changed files with 5086 additions and 72 deletions

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,67 @@
import 'package:drift/drift.dart';
import 'package:twonly/src/database/tables/signal_contact_prekey_table.dart';
import 'package:twonly/src/database/tables/signal_contact_signed_prekey_table.dart';
import 'package:twonly/src/database/twonly_database.dart';
part 'signal_dao.g.dart';
@DriftAccessor(tables: [SignalContactPreKeys, SignalContactSignedPreKeys])
class SignalDao extends DatabaseAccessor<TwonlyDatabase> with _$SignalDaoMixin {
// this constructor is required so that the main database can create an instance
// of this object.
SignalDao(super.db);
// 1: Count the number of pre-keys by contact ID
Future<int> countPreKeysByContactId(int contactId) {
return (select(signalContactPreKeys)
..where((tbl) => tbl.contactId.equals(contactId)))
.get()
.then((rows) => rows.length);
}
// 2: Pop a pre-key by contact ID
Future<SignalContactPreKey?> popPreKeyByContactId(int contactId) async {
final preKey =
await ((select(signalContactPreKeys)..where((tbl) => tbl.contactId.equals(contactId)))
..limit(1))
.getSingleOrNull();
if (preKey != null) {
// remove the pre key...
await (delete(signalContactPreKeys)
..where((tbl) =>
tbl.contactId.equals(contactId) &
tbl.preKeyId.equals(preKey.preKeyId)))
.go();
return preKey;
}
return null;
}
// 3: Insert multiple pre-keys
Future<void> insertPreKeys(
List<SignalContactPreKeysCompanion> preKeys) async {
await batch((batch) {
batch.insertAll(signalContactPreKeys, preKeys);
});
}
// 4: Get signed pre-key by contact ID
Future<SignalContactSignedPreKey?> getSignedPreKeyByContactId(int contactId) {
return (select(signalContactSignedPreKeys)
..where((tbl) => tbl.contactId.equals(contactId)))
.getSingleOrNull();
}
// 5: Insert or update signed pre-key by contact ID
Future<void> insertOrUpdateSignedPreKeyByContactId(
SignalContactSignedPreKeysCompanion signedPreKey) async {
final existingKey =
await getSignedPreKeyByContactId(signedPreKey.contactId.value);
if (existingKey != null) {
await update(signalContactSignedPreKeys).replace(signedPreKey);
} else {
await into(signalContactSignedPreKeys).insert(signedPreKey);
}
}
}

View file

@ -0,0 +1,11 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'signal_dao.dart';
// ignore_for_file: type=lint
mixin _$SignalDaoMixin on DatabaseAccessor<TwonlyDatabase> {
$SignalContactPreKeysTable get signalContactPreKeys =>
attachedDatabase.signalContactPreKeys;
$SignalContactSignedPreKeysTable get signalContactSignedPreKeys =>
attachedDatabase.signalContactSignedPreKeys;
}

View file

@ -3,6 +3,7 @@ import 'package:logging/logging.dart';
import 'package:twonly/globals.dart'; import 'package:twonly/globals.dart';
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart'; import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
import 'package:twonly/src/database/twonly_database.dart'; import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/utils/log.dart';
class ConnectPreKeyStore extends PreKeyStore { class ConnectPreKeyStore extends PreKeyStore {
@override @override
@ -32,6 +33,25 @@ class ConnectPreKeyStore extends PreKeyStore {
.go(); .go();
} }
static Future<int?> getNextPreKeyId() async {
try {
String tableName = twonlyDB.signalPreKeyStores.actualTableName;
String columnName = twonlyDB.signalPreKeyStores.preKeyId.name;
final result = await twonlyDB
.customSelect('SELECT MAX($columnName) AS max_id FROM $tableName')
.get();
int? count = result.first.read<int?>('max_id');
if (count == null) {
return 0;
}
return count + 1;
} catch (e) {
Log.error("$e");
}
return null;
}
@override @override
Future<void> storePreKey(int preKeyId, PreKeyRecord record) async { Future<void> storePreKey(int preKeyId, PreKeyRecord record) async {
final preKeyCompanion = SignalPreKeyStoresCompanion( final preKeyCompanion = SignalPreKeyStoresCompanion(

View file

@ -0,0 +1,11 @@
import 'package:drift/drift.dart';
@DataClassName('SignalContactPreKey')
class SignalContactPreKeys extends Table {
IntColumn get contactId => integer()();
IntColumn get preKeyId => integer()();
BlobColumn get preKey => blob()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
@override
Set<Column> get primaryKey => {contactId, preKeyId};
}

View file

@ -0,0 +1,12 @@
import 'package:drift/drift.dart';
@DataClassName('SignalContactSignedPreKey')
class SignalContactSignedPreKeys extends Table {
IntColumn get contactId => integer()();
IntColumn get signedPreKeyId => integer()();
BlobColumn get signedPreKey => blob()();
BlobColumn get signedPreKeySignature => blob()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
@override
Set<Column> get primaryKey => {contactId};
}

View file

@ -6,10 +6,13 @@ import 'package:twonly/src/database/daos/contacts_dao.dart';
import 'package:twonly/src/database/daos/media_downloads_dao.dart'; import 'package:twonly/src/database/daos/media_downloads_dao.dart';
import 'package:twonly/src/database/daos/media_uploads_dao.dart'; import 'package:twonly/src/database/daos/media_uploads_dao.dart';
import 'package:twonly/src/database/daos/messages_dao.dart'; import 'package:twonly/src/database/daos/messages_dao.dart';
import 'package:twonly/src/database/daos/signal_dao.dart';
import 'package:twonly/src/database/tables/contacts_table.dart'; import 'package:twonly/src/database/tables/contacts_table.dart';
import 'package:twonly/src/database/tables/media_download_table.dart'; import 'package:twonly/src/database/tables/media_download_table.dart';
import 'package:twonly/src/database/tables/media_uploads_table.dart'; import 'package:twonly/src/database/tables/media_uploads_table.dart';
import 'package:twonly/src/database/tables/messages_table.dart'; import 'package:twonly/src/database/tables/messages_table.dart';
import 'package:twonly/src/database/tables/signal_contact_prekey_table.dart';
import 'package:twonly/src/database/tables/signal_contact_signed_prekey_table.dart';
import 'package:twonly/src/database/tables/signal_identity_key_store_table.dart'; import 'package:twonly/src/database/tables/signal_identity_key_store_table.dart';
import 'package:twonly/src/database/tables/signal_pre_key_store_table.dart'; import 'package:twonly/src/database/tables/signal_pre_key_store_table.dart';
import 'package:twonly/src/database/tables/signal_sender_key_store_table.dart'; import 'package:twonly/src/database/tables/signal_sender_key_store_table.dart';
@ -27,12 +30,15 @@ part 'twonly_database.g.dart';
SignalIdentityKeyStores, SignalIdentityKeyStores,
SignalPreKeyStores, SignalPreKeyStores,
SignalSenderKeyStores, SignalSenderKeyStores,
SignalSessionStores SignalSessionStores,
SignalContactPreKeys,
SignalContactSignedPreKeys
], daos: [ ], daos: [
MessagesDao, MessagesDao,
ContactsDao, ContactsDao,
MediaUploadsDao, MediaUploadsDao,
MediaDownloadsDao, MediaDownloadsDao,
SignalDao
]) ])
class TwonlyDatabase extends _$TwonlyDatabase { class TwonlyDatabase extends _$TwonlyDatabase {
TwonlyDatabase([QueryExecutor? e]) TwonlyDatabase([QueryExecutor? e])
@ -43,7 +49,7 @@ class TwonlyDatabase extends _$TwonlyDatabase {
TwonlyDatabase.forTesting(DatabaseConnection super.connection); TwonlyDatabase.forTesting(DatabaseConnection super.connection);
@override @override
int get schemaVersion => 9; int get schemaVersion => 10;
static QueryExecutor _openConnection() { static QueryExecutor _openConnection() {
return driftDatabase( return driftDatabase(
@ -57,26 +63,17 @@ class TwonlyDatabase extends _$TwonlyDatabase {
@override @override
MigrationStrategy get migration { MigrationStrategy get migration {
return MigrationStrategy( return MigrationStrategy(
onUpgrade: stepByStep(from1To2: (m, schema) async { onUpgrade: stepByStep(
from1To2: (m, schema) async {
m.addColumn(schema.messages, schema.messages.errorWhileSending); m.addColumn(schema.messages, schema.messages.errorWhileSending);
}, from2To3: (m, schema) async { },
from2To3: (m, schema) async {
m.addColumn(schema.contacts, schema.contacts.archived); m.addColumn(schema.contacts, schema.contacts.archived);
m.addColumn( m.addColumn(
schema.contacts, schema.contacts.deleteMessagesAfterXMinutes); schema.contacts, schema.contacts.deleteMessagesAfterXMinutes);
}, from3To4: (m, schema) async { },
from3To4: (m, schema) async {
m.createTable(mediaUploads); m.createTable(mediaUploads);
}, from4To5: (m, schema) async {
m.createTable(mediaDownloads);
m.addColumn(schema.messages, schema.messages.mediaDownloadId);
m.addColumn(schema.messages, schema.messages.mediaUploadId);
}, from5To6: (m, schema) async {
m.addColumn(schema.messages, schema.messages.mediaStored);
}, from6To7: (m, schema) async {
m.addColumn(schema.contacts, schema.contacts.pinned);
}, from7To8: (m, schema) async {
m.addColumn(schema.contacts, schema.contacts.alsoBestFriend);
m.addColumn(schema.contacts, schema.contacts.lastFlameSync);
}, from8To9: (m, schema) async {
await m.alterTable(TableMigration( await m.alterTable(TableMigration(
schema.mediaUploads, schema.mediaUploads,
columnTransformer: { columnTransformer: {
@ -84,7 +81,36 @@ class TwonlyDatabase extends _$TwonlyDatabase {
schema.mediaUploads.metadata.cast<String>(), schema.mediaUploads.metadata.cast<String>(),
}, },
)); ));
}), },
from4To5: (m, schema) async {
m.createTable(mediaDownloads);
m.addColumn(schema.messages, schema.messages.mediaDownloadId);
m.addColumn(schema.messages, schema.messages.mediaUploadId);
},
from5To6: (m, schema) async {
m.addColumn(schema.messages, schema.messages.mediaStored);
},
from6To7: (m, schema) async {
m.addColumn(schema.contacts, schema.contacts.pinned);
},
from7To8: (m, schema) async {
m.addColumn(schema.contacts, schema.contacts.alsoBestFriend);
m.addColumn(schema.contacts, schema.contacts.lastFlameSync);
},
from8To9: (m, schema) async {
await m.alterTable(TableMigration(
schema.mediaUploads,
columnTransformer: {
schema.mediaUploads.metadata:
schema.mediaUploads.metadata.cast<String>(),
},
));
},
from9To10: (m, schema) async {
m.createTable(signalContactPreKeys);
m.createTable(signalContactSignedPreKeys);
},
),
); );
} }

File diff suppressed because it is too large Load diff

View file

@ -1879,6 +1879,259 @@ final class Schema9 extends i0.VersionedSchema {
i1.GeneratedColumn<String> _column_56(String aliasedName) => i1.GeneratedColumn<String> _column_56(String aliasedName) =>
i1.GeneratedColumn<String>('metadata', aliasedName, true, i1.GeneratedColumn<String>('metadata', aliasedName, true,
type: i1.DriftSqlType.string); type: i1.DriftSqlType.string);
final class Schema10 extends i0.VersionedSchema {
Schema10({required super.database}) : super(version: 10);
@override
late final List<i1.DatabaseSchemaEntity> entities = [
contacts,
messages,
mediaUploads,
mediaDownloads,
signalIdentityKeyStores,
signalPreKeyStores,
signalSenderKeyStores,
signalSessionStores,
signalContactPreKeys,
signalContactSignedPreKeys,
];
late final Shape12 contacts = Shape12(
source: i0.VersionedTable(
entityName: 'contacts',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(user_id)',
],
columns: [
_column_0,
_column_1,
_column_2,
_column_3,
_column_4,
_column_5,
_column_6,
_column_7,
_column_8,
_column_9,
_column_39,
_column_53,
_column_54,
_column_40,
_column_10,
_column_11,
_column_12,
_column_13,
_column_14,
_column_55,
_column_15,
_column_16,
],
attachedDatabase: database,
),
alias: null);
late final Shape10 messages = Shape10(
source: i0.VersionedTable(
entityName: 'messages',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_17,
_column_18,
_column_19,
_column_48,
_column_49,
_column_20,
_column_21,
_column_22,
_column_52,
_column_23,
_column_24,
_column_25,
_column_26,
_column_27,
_column_28,
_column_29,
_column_30,
],
attachedDatabase: database,
),
alias: null);
late final Shape7 mediaUploads = Shape7(
source: i0.VersionedTable(
entityName: 'media_uploads',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_41,
_column_42,
_column_56,
_column_44,
_column_45,
_column_46,
_column_47,
],
attachedDatabase: database,
),
alias: null);
late final Shape9 mediaDownloads = Shape9(
source: i0.VersionedTable(
entityName: 'media_downloads',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_50,
_column_51,
],
attachedDatabase: database,
),
alias: null);
late final Shape2 signalIdentityKeyStores = Shape2(
source: i0.VersionedTable(
entityName: 'signal_identity_key_stores',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(device_id, name)',
],
columns: [
_column_31,
_column_32,
_column_33,
_column_10,
],
attachedDatabase: database,
),
alias: null);
late final Shape3 signalPreKeyStores = Shape3(
source: i0.VersionedTable(
entityName: 'signal_pre_key_stores',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(pre_key_id)',
],
columns: [
_column_34,
_column_35,
_column_10,
],
attachedDatabase: database,
),
alias: null);
late final Shape4 signalSenderKeyStores = Shape4(
source: i0.VersionedTable(
entityName: 'signal_sender_key_stores',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(sender_key_name)',
],
columns: [
_column_36,
_column_37,
],
attachedDatabase: database,
),
alias: null);
late final Shape5 signalSessionStores = Shape5(
source: i0.VersionedTable(
entityName: 'signal_session_stores',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(device_id, name)',
],
columns: [
_column_31,
_column_32,
_column_38,
_column_10,
],
attachedDatabase: database,
),
alias: null);
late final Shape13 signalContactPreKeys = Shape13(
source: i0.VersionedTable(
entityName: 'signal_contact_pre_keys',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(contact_id, pre_key_id)',
],
columns: [
_column_57,
_column_34,
_column_35,
_column_10,
],
attachedDatabase: database,
),
alias: null);
late final Shape14 signalContactSignedPreKeys = Shape14(
source: i0.VersionedTable(
entityName: 'signal_contact_signed_pre_keys',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(contact_id, signed_pre_key_id)',
],
columns: [
_column_57,
_column_58,
_column_59,
_column_60,
_column_10,
],
attachedDatabase: database,
),
alias: null);
}
class Shape13 extends i0.VersionedTable {
Shape13({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<int> get contactId =>
columnsByName['contact_id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<int> get preKeyId =>
columnsByName['pre_key_id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<i2.Uint8List> get preKey =>
columnsByName['pre_key']! as i1.GeneratedColumn<i2.Uint8List>;
i1.GeneratedColumn<DateTime> get createdAt =>
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
}
i1.GeneratedColumn<int> _column_57(String aliasedName) =>
i1.GeneratedColumn<int>('contact_id', aliasedName, false,
type: i1.DriftSqlType.int);
class Shape14 extends i0.VersionedTable {
Shape14({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<int> get contactId =>
columnsByName['contact_id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<int> get signedPreKeyId =>
columnsByName['signed_pre_key_id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<i2.Uint8List> get signedPreKey =>
columnsByName['signed_pre_key']! as i1.GeneratedColumn<i2.Uint8List>;
i1.GeneratedColumn<i2.Uint8List> get signedPreKeySignature =>
columnsByName['signed_pre_key_signature']!
as i1.GeneratedColumn<i2.Uint8List>;
i1.GeneratedColumn<DateTime> get createdAt =>
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
}
i1.GeneratedColumn<int> _column_58(String aliasedName) =>
i1.GeneratedColumn<int>('signed_pre_key_id', aliasedName, false,
type: i1.DriftSqlType.int);
i1.GeneratedColumn<i2.Uint8List> _column_59(String aliasedName) =>
i1.GeneratedColumn<i2.Uint8List>('signed_pre_key', aliasedName, false,
type: i1.DriftSqlType.blob);
i1.GeneratedColumn<i2.Uint8List> _column_60(String aliasedName) =>
i1.GeneratedColumn<i2.Uint8List>(
'signed_pre_key_signature', aliasedName, false,
type: i1.DriftSqlType.blob);
i0.MigrationStepWithVersion migrationSteps({ i0.MigrationStepWithVersion migrationSteps({
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2, required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3, required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
@ -1888,6 +2141,7 @@ i0.MigrationStepWithVersion migrationSteps({
required Future<void> Function(i1.Migrator m, Schema7 schema) from6To7, required Future<void> Function(i1.Migrator m, Schema7 schema) from6To7,
required Future<void> Function(i1.Migrator m, Schema8 schema) from7To8, required Future<void> Function(i1.Migrator m, Schema8 schema) from7To8,
required Future<void> Function(i1.Migrator m, Schema9 schema) from8To9, required Future<void> Function(i1.Migrator m, Schema9 schema) from8To9,
required Future<void> Function(i1.Migrator m, Schema10 schema) from9To10,
}) { }) {
return (currentVersion, database) async { return (currentVersion, database) async {
switch (currentVersion) { switch (currentVersion) {
@ -1931,6 +2185,11 @@ i0.MigrationStepWithVersion migrationSteps({
final migrator = i1.Migrator(database, schema); final migrator = i1.Migrator(database, schema);
await from8To9(migrator, schema); await from8To9(migrator, schema);
return 9; return 9;
case 9:
final schema = Schema10(database: database);
final migrator = i1.Migrator(database, schema);
await from9To10(migrator, schema);
return 10;
default: default:
throw ArgumentError.value('Unknown migration from $currentVersion'); throw ArgumentError.value('Unknown migration from $currentVersion');
} }
@ -1946,6 +2205,7 @@ i1.OnUpgrade stepByStep({
required Future<void> Function(i1.Migrator m, Schema7 schema) from6To7, required Future<void> Function(i1.Migrator m, Schema7 schema) from6To7,
required Future<void> Function(i1.Migrator m, Schema8 schema) from7To8, required Future<void> Function(i1.Migrator m, Schema8 schema) from7To8,
required Future<void> Function(i1.Migrator m, Schema9 schema) from8To9, required Future<void> Function(i1.Migrator m, Schema9 schema) from8To9,
required Future<void> Function(i1.Migrator m, Schema10 schema) from9To10,
}) => }) =>
i0.VersionedSchema.stepByStepHelper( i0.VersionedSchema.stepByStepHelper(
step: migrationSteps( step: migrationSteps(
@ -1957,4 +2217,5 @@ i1.OnUpgrade stepByStep({
from6To7: from6To7, from6To7: from6To7,
from7To8: from7To8, from7To8: from7To8,
from8To9: from8To9, from8To9: from8To9,
from9To10: from9To10,
)); ));

View file

@ -21,6 +21,7 @@ import 'package:twonly/src/services/api/utils.dart';
import 'package:twonly/src/services/api/media_received.dart'; import 'package:twonly/src/services/api/media_received.dart';
import 'package:twonly/src/services/api/media_send.dart'; import 'package:twonly/src/services/api/media_send.dart';
import 'package:twonly/src/services/api/server_messages.dart'; import 'package:twonly/src/services/api/server_messages.dart';
import 'package:twonly/src/services/signal/encryption.signal.dart';
import 'package:twonly/src/services/signal/identity.signal.dart'; import 'package:twonly/src/services/signal/identity.signal.dart';
import 'package:twonly/src/services/signal/utils.signal.dart'; import 'package:twonly/src/services/signal/utils.signal.dart';
import 'package:twonly/src/utils/hive.dart'; import 'package:twonly/src/utils/hive.dart';
@ -559,6 +560,44 @@ class ApiService {
return await sendRequestSync(req); return await sendRequestSync(req);
} }
Future<Response_SignedPreKey?> getSignedKeyByUserId(int userId) async {
var get = ApplicationData_GetSignedPreKeyByUserId()..userId = Int64(userId);
var appData = ApplicationData()..getsignedprekeybyuserid = get;
var req = createClientToServerFromApplicationData(appData);
Result res = await sendRequestSync(req);
if (res.isSuccess) {
server.Response_Ok ok = res.value;
if (ok.hasSignedprekey()) {
return ok.signedprekey;
}
}
return null;
}
Future<OtherPreKeys?> getPreKeysByUserId(int userId) async {
var get = ApplicationData_GetPrekeysByUserId()..userId = Int64(userId);
var appData = ApplicationData()..getprekeysbyuserid = get;
var req = createClientToServerFromApplicationData(appData);
Result res = await sendRequestSync(req);
if (res.isSuccess) {
server.Response_Ok ok = res.value;
if (ok.hasUserdata()) {
server.Response_UserData data = ok.userdata;
if (data.hasSignedPrekey() &&
data.hasSignedPrekeyId() &&
data.hasSignedPrekeySignature()) {
return OtherPreKeys(
preKeys: ok.userdata.prekeys,
signedPreKey: data.signedPrekey,
signedPreKeyId: data.signedPrekeyId.toInt(),
signedPreKeySignature: data.signedPrekeySignature,
);
}
}
}
return null;
}
Future<Result> sendTextMessage( Future<Result> sendTextMessage(
int target, Uint8List msg, List<int>? pushData) async { int target, Uint8List msg, List<int>? pushData) async {
var testMessage = ApplicationData_TextMessage() var testMessage = ApplicationData_TextMessage()

View file

@ -1,13 +1,31 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:drift/drift.dart';
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart'; import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/model/json/message.dart'; import 'package:twonly/src/model/json/message.dart';
import 'package:twonly/src/database/signal/connect_signal_protocol_store.dart'; import 'package:twonly/src/database/signal/connect_signal_protocol_store.dart';
import 'package:twonly/src/services/signal/consts.signal.dart'; import 'package:twonly/src/services/signal/consts.signal.dart';
import 'package:twonly/src/services/signal/utils.signal.dart'; import 'package:twonly/src/services/signal/utils.signal.dart';
import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/model/protobuf/api/server_to_client.pb.dart'
as server;
class OtherPreKeys {
OtherPreKeys({
required this.preKeys,
required this.signedPreKey,
required this.signedPreKeyId,
required this.signedPreKeySignature,
});
final List<server.Response_PreKey> preKeys;
final int signedPreKeyId;
final List<int> signedPreKey;
final List<int> signedPreKeySignature;
}
// Future<void> deleteSession(String userId) async { // Future<void> deleteSession(String userId) async {
// await mixinSignalProtocolStore.sessionStore.sessionDao // await mixinSignalProtocolStore.sessionStore.sessionDao
@ -31,36 +49,72 @@ import 'package:twonly/src/utils/misc.dart';
// } // }
// } // }
// Future<bool> checkSignalSession(String recipientId, String sessionId) async { Future requestNewPrekeysForContact(int contactId) async {
// final contains = await signalProtocol.containsSession( final otherKeys = await apiService.getPreKeysByUserId(contactId);
// recipientId, if (otherKeys != null) {
// deviceId: sessionId.getDeviceId(), Log.info("got fresh pre keys from other $contactId!");
// ); final preKeys = otherKeys.preKeys
// if (!contains) { .map(
// final requestKeys = <BlazeMessageParamSession>[ (preKey) => SignalContactPreKeysCompanion(
// BlazeMessageParamSession(userId: recipientId, sessionId: sessionId), contactId: Value(contactId),
// ]; preKey: Value(Uint8List.fromList(preKey.prekey)),
// final blazeMessage = createConsumeSessionSignalKeys( preKeyId: Value(preKey.id.toInt()),
// createConsumeSignalKeysParam(requestKeys), ),
// ); )
// final data = (await signalKeysChannel(blazeMessage))?.data; .toList();
// if (data == null) { await twonlyDB.signalDao.insertPreKeys(preKeys);
// return false; } else {
// } Log.error("could not load new pre keys for user $contactId");
// final keys = List<SignalKey>.from( }
// (data as List<dynamic>).map( }
// (e) => SignalKey.fromJson(e as Map<String, dynamic>),
// ), Future<SignalContactPreKey?> getPreKeyByContactId(int contactId) async {
// ); int count = await twonlyDB.signalDao.countPreKeysByContactId(contactId);
// if (keys.isNotEmpty) { if (count < 10) {
// final preKeyBundle = keys.first.createPreKeyBundle(); Log.info(
// await signalProtocol.processSession(recipientId, preKeyBundle); "There are $count < 10 prekeys for $contactId. Loading fresh once from the server.",
// } else { );
// return false; requestNewPrekeysForContact(contactId);
// } }
// } return twonlyDB.signalDao.popPreKeyByContactId(contactId);
// return true; }
// }
Future requestNewSignedPreKeyForContact(int contactId) async {
final signedPreKey = await apiService.getSignedKeyByUserId(contactId);
if (signedPreKey != null) {
Log.info("got fresh signed pre keys from other $contactId!");
await twonlyDB.signalDao.insertOrUpdateSignedPreKeyByContactId(
SignalContactSignedPreKeysCompanion(
contactId: Value(contactId),
signedPreKey: Value(Uint8List.fromList(signedPreKey.signedPrekey)),
signedPreKeySignature:
Value(Uint8List.fromList(signedPreKey.signedPrekeySignature)),
signedPreKeyId: Value(signedPreKey.signedPrekeyId.toInt()),
));
} else {
Log.error("could not load new signed pre key for user $contactId");
}
}
Future<SignalContactSignedPreKey?> getSignedPreKeyByContactId(
int contactId,
) async {
SignalContactSignedPreKey? signedPreKey =
await twonlyDB.signalDao.getSignedPreKeyByContactId(contactId);
if (signedPreKey != null) {
DateTime fortyEightHoursAgo = DateTime.now().subtract(Duration(hours: 48));
bool isOlderThan48Hours =
(signedPreKey.createdAt).isBefore(fortyEightHoursAgo);
if (isOlderThan48Hours) {
requestNewSignedPreKeyForContact(contactId);
}
} else {
requestNewSignedPreKeyForContact(contactId);
Log.error("Contact $contactId does not have a signed pre key!");
}
return signedPreKey;
}
Future<Uint8List?> signalEncryptMessage(int target, MessageJson msg) async { Future<Uint8List?> signalEncryptMessage(int target, MessageJson msg) async {
try { try {
@ -69,16 +123,61 @@ Future<Uint8List?> signalEncryptMessage(int target, MessageJson msg) async {
SessionCipher session = SessionCipher.fromStore(signalStore, address); SessionCipher session = SessionCipher.fromStore(signalStore, address);
final SessionRecord sessionRecord = SignalContactPreKey? preKey = await getPreKeyByContactId(target);
await signalStore.sessionStore.loadSession(address); SignalContactSignedPreKey? signedPreKey = await getSignedPreKeyByContactId(
target,
);
if (!sessionRecord.sessionState.hasUnacknowledgedPreKeyMessage()) { if (signedPreKey != null) {
Log.info("There are now pre keys any more... load new..."); SessionBuilder sessionBuilder = SessionBuilder.fromSignalStore(
signalStore,
address,
);
ECPublicKey? tempPrePublicKey;
if (preKey != null) {
tempPrePublicKey = Curve.decodePoint(
DjbECPublicKey(
Uint8List.fromList(preKey.preKey),
).serialize(),
1,
);
} }
// sessionRecord.sessionState.sign ECPublicKey? tempSignedPreKeyPublic = Curve.decodePoint(
DjbECPublicKey(Uint8List.fromList(signedPreKey.signedPreKey))
.serialize(),
1,
);
// session. Uint8List? tempSignedPreKeySignature = Uint8List.fromList(
signedPreKey.signedPreKeySignature,
);
final IdentityKey? tempIdentityKey =
await signalStore.getIdentity(address);
if (tempIdentityKey != null) {
PreKeyBundle preKeyBundle = PreKeyBundle(
target,
defaultDeviceId,
preKey?.preKeyId,
tempPrePublicKey,
signedPreKey.signedPreKeyId,
tempSignedPreKeyPublic,
tempSignedPreKeySignature,
tempIdentityKey,
);
try {
await sessionBuilder.processPreKeyBundle(preKeyBundle);
} catch (e) {
Log.error("could not process pre key bundle: $e");
}
} else {
Log.error("did not get the identity of the remote address");
}
}
final ciphertext = await session.encrypt( final ciphertext = await session.encrypt(
Uint8List.fromList(gzip.encode(utf8.encode(jsonEncode(msg.toJson())))), Uint8List.fromList(gzip.encode(utf8.encode(jsonEncode(msg.toJson())))),

View file

@ -3,6 +3,7 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart'; import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
import 'package:twonly/globals.dart'; import 'package:twonly/globals.dart';
import 'package:twonly/src/constants/secure_storage_keys.dart'; import 'package:twonly/src/constants/secure_storage_keys.dart';
import 'package:twonly/src/database/signal/connect_pre_key_store.dart';
import 'package:twonly/src/model/json/signal_identity.dart'; import 'package:twonly/src/model/json/signal_identity.dart';
import 'package:twonly/src/database/signal/connect_signal_protocol_store.dart'; import 'package:twonly/src/database/signal/connect_signal_protocol_store.dart';
import 'package:twonly/src/model/json/userdata.dart'; import 'package:twonly/src/model/json/userdata.dart';
@ -56,7 +57,10 @@ Future signalHandleNewServerConnection() async {
} }
Future<List<PreKeyRecord>> signalGetPreKeys() async { Future<List<PreKeyRecord>> signalGetPreKeys() async {
final preKeys = generatePreKeys(0, 200); int? start = await ConnectPreKeyStore.getNextPreKeyId();
if (start == null) return [];
print(start);
final preKeys = generatePreKeys(start, 200);
final signalStore = await getSignalStore(); final signalStore = await getSignalStore();
if (signalStore == null) return []; if (signalStore == null) return [];
for (final p in preKeys) { for (final p in preKeys) {

View file

@ -34,7 +34,7 @@ Future<void> _writeLogToFile(LogRecord record) async {
// Prepare the log message // Prepare the log message
final logMessage = final logMessage =
'${DateTime.now()} ${record.level.name} [twonly] ${record.loggerName} > ${record.message}\n'; '${DateTime.now().toString().split(".")[0]} ${record.level.name} [twonly] ${record.loggerName} > ${record.message}\n';
// Append the log message to the file // Append the log message to the file
await logFile.writeAsString(logMessage, mode: FileMode.append); await logFile.writeAsString(logMessage, mode: FileMode.append);

View file

@ -12,6 +12,7 @@ import 'schema_v6.dart' as v6;
import 'schema_v7.dart' as v7; import 'schema_v7.dart' as v7;
import 'schema_v8.dart' as v8; import 'schema_v8.dart' as v8;
import 'schema_v9.dart' as v9; import 'schema_v9.dart' as v9;
import 'schema_v10.dart' as v10;
class GeneratedHelper implements SchemaInstantiationHelper { class GeneratedHelper implements SchemaInstantiationHelper {
@override @override
@ -35,10 +36,12 @@ class GeneratedHelper implements SchemaInstantiationHelper {
return v8.DatabaseAtV8(db); return v8.DatabaseAtV8(db);
case 9: case 9:
return v9.DatabaseAtV9(db); return v9.DatabaseAtV9(db);
case 10:
return v10.DatabaseAtV10(db);
default: default:
throw MissingSchemaException(version, versions); throw MissingSchemaException(version, versions);
} }
} }
static const versions = const [1, 2, 3, 4, 5, 6, 7, 8, 9]; static const versions = const [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
} }

File diff suppressed because it is too large Load diff