From 622acfaa59c11687bcf6b522fcda3794fe04dad2 Mon Sep 17 00:00:00 2001 From: Krille Fear Date: Wed, 17 Nov 2021 12:20:15 +0100 Subject: [PATCH] feat: FluffyBox --- lib/utils/client_manager.dart | 6 +- .../fluffybox_database.dart | 134 ++++++++++++++++++ macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 22 ++- pubspec.yaml | 8 +- 5 files changed, 165 insertions(+), 7 deletions(-) create mode 100644 lib/utils/matrix_sdk_extensions.dart/fluffybox_database.dart diff --git a/lib/utils/client_manager.dart b/lib/utils/client_manager.dart index 279fc0cb..72f28587 100644 --- a/lib/utils/client_manager.dart +++ b/lib/utils/client_manager.dart @@ -10,7 +10,7 @@ import 'package:path_provider/path_provider.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'famedlysdk_store.dart'; -import 'matrix_sdk_extensions.dart/flutter_matrix_hive_database.dart'; +import 'matrix_sdk_extensions.dart/fluffybox_database.dart'; import 'matrix_sdk_extensions.dart/flutter_matrix_sembast_database.dart'; abstract class ClientManager { @@ -80,8 +80,8 @@ abstract class ClientManager { KeyVerificationMethod.emoji, }, importantStateEvents: {'im.ponies.room_emotes'}, - databaseBuilder: FlutterMatrixSembastDatabase.databaseBuilder, - legacyDatabaseBuilder: FlutterMatrixHiveStore.hiveDatabaseBuilder, + databaseBuilder: FluffyBoxDatabase.databaseBuilder, + legacyDatabaseBuilder: FlutterMatrixSembastDatabase.databaseBuilder, supportedLoginTypes: { AuthenticationTypes.password, if (PlatformInfos.isMobile || PlatformInfos.isWeb) diff --git a/lib/utils/matrix_sdk_extensions.dart/fluffybox_database.dart b/lib/utils/matrix_sdk_extensions.dart/fluffybox_database.dart new file mode 100644 index 00000000..d2b87cd8 --- /dev/null +++ b/lib/utils/matrix_sdk_extensions.dart/fluffybox_database.dart @@ -0,0 +1,134 @@ +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:flutter/foundation.dart' hide Key; +import 'package:flutter/services.dart'; +import 'package:idb_shim/idb_browser.dart'; +import 'package:idb_shim/idb.dart' hide Event; +import 'package:sqflite/sqflite.dart' as sqflite; + +import 'package:encrypt/encrypt.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:idb_sqflite/idb_sqflite.dart'; +import 'package:matrix/matrix.dart'; +import 'package:path_provider/path_provider.dart'; + +import '../platform_infos.dart'; + +class FluffyBoxDatabase extends MatrixIndexedDatabase { + FluffyBoxDatabase( + String name, { + String path, + IdbFactory dbFactory, + }) : super( + name, + path, + factory: dbFactory, + ); + + static const String _cipherStorageKey = 'database_encryption_key'; + static const int _cipherStorageKeyLength = 512; + + static Future databaseBuilder(Client client) async { + Logs().d('Open Sembast...'); + try { + // Workaround for secure storage is calling Platform.operatingSystem on web + if (kIsWeb) throw MissingPluginException(); + + const secureStorage = FlutterSecureStorage(); + final containsEncryptionKey = + await secureStorage.containsKey(key: _cipherStorageKey); + if (!containsEncryptionKey) { + final key = SecureRandom(_cipherStorageKeyLength).base64; + await secureStorage.write( + key: _cipherStorageKey, + value: key, + ); + } + + // workaround for if we just wrote to the key and it still doesn't exist + final rawEncryptionKey = await secureStorage.read(key: _cipherStorageKey); + if (rawEncryptionKey == null) throw MissingPluginException(); + + //codec = getEncryptSembastCodec(password: rawEncryptionKey); + } on MissingPluginException catch (_) { + Logs().i('Sembast encryption is not supported on this platform'); + } + + final db = FluffyBoxDatabase( + client.clientName, + path: await _findDatabasePath(client), + dbFactory: factory, + ); + await db.open(); + Logs().d('Sembast is ready'); + return db; + } + + static IdbFactory get factory { + if (kIsWeb) return idbFactoryBrowser; + if (Platform.isAndroid || Platform.isIOS) { + return getIdbFactorySqflite(sqflite.databaseFactory); + } + return idbFactoryNative; + } + + static Future _findDatabasePath(Client client) async { + String path = client.clientName; + if (!kIsWeb) { + Directory directory; + try { + directory = await getApplicationSupportDirectory(); + } catch (_) { + try { + directory = await getLibraryDirectory(); + } catch (_) { + directory = Directory.current; + } + } + path = '${directory.path}${client.clientName}.db'; + } + return path; + } + + @override + int get maxFileSize => supportsFileStoring ? 100 * 1024 * 1024 : 0; + @override + bool get supportsFileStoring => (PlatformInfos.isIOS || + PlatformInfos.isAndroid || + PlatformInfos.isDesktop); + + Future _getFileStoreDirectory() async { + try { + try { + return (await getApplicationSupportDirectory()).path; + } catch (_) { + return (await getApplicationDocumentsDirectory()).path; + } + } catch (_) { + return (await getDownloadsDirectory()).path; + } + } + + @override + Future getFile(Uri mxcUri) async { + if (!supportsFileStoring) return null; + final tempDirectory = await _getFileStoreDirectory(); + final file = + File('$tempDirectory/${Uri.encodeComponent(mxcUri.toString())}'); + if (await file.exists() == false) return null; + final bytes = await file.readAsBytes(); + return bytes; + } + + @override + Future storeFile(Uri mxcUri, Uint8List bytes, int time) async { + if (!supportsFileStoring) return null; + final tempDirectory = await _getFileStoreDirectory(); + final file = + File('$tempDirectory/${Uri.encodeComponent(mxcUri.toString())}'); + if (await file.exists()) return; + await file.writeAsBytes(bytes); + return; + } +} diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index be69c99c..bc1ef7cf 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -14,6 +14,7 @@ import package_info_plus_macos import path_provider_macos import shared_preferences_macos import sqflite +import sqflite_sqlcipher import url_launcher_macos import wakelock_macos @@ -27,6 +28,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) + SqfliteSqlCipherPlugin.register(with: registry.registrar(forPlugin: "SqfliteSqlCipherPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) WakelockMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockMacosPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index 0567662e..d9f7bed1 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -651,6 +651,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.1" + idb_sqflite: + dependency: "direct main" + description: + name: idb_sqflite + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" image: dependency: transitive description: @@ -773,9 +780,11 @@ packages: matrix: dependency: "direct main" description: - name: matrix - url: "https://pub.dartlang.org" - source: hosted + path: "." + ref: "krille/idb" + resolved-ref: "5996f6bb01ba1110c1f8fb84c39dd6bcf0d2900a" + url: "https://gitlab.com/famedly/company/frontend/famedlysdk.git" + source: git version: "0.7.0-nullsafety.6" matrix_api_lite: dependency: transitive @@ -1309,6 +1318,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.0+2" + sqflite_sqlcipher: + dependency: "direct main" + description: + name: sqflite_sqlcipher + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" stack_trace: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 59bd7d1e..cb10e97c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -42,6 +42,7 @@ dependencies: future_loading_dialog: ^0.2.1 geolocator: ^7.6.2 hive_flutter: ^1.1.0 + idb_sqflite: ^1.0.1 image_picker: ^0.8.4+2 intl: any localstorage: ^4.0.0+1 @@ -67,6 +68,7 @@ dependencies: sentry: ^6.0.1 share: ^2.0.4 slugify: ^2.0.0 + sqflite_sqlcipher: ^2.1.0 swipe_to_action: ^0.2.0 uni_links: ^0.5.1 unifiedpush: ^1.0.6 @@ -115,4 +117,8 @@ dependency_overrides: hosted: name: geolocator_android url: https://hanntech-gmbh.gitlab.io/free2pass/flutter-geolocator-floss - provider: 5.0.0 + matrix: + git: + url: https://gitlab.com/famedly/company/frontend/famedlysdk.git + ref: krille/idb + provider: 5.0.0 \ No newline at end of file