import 'dart:collection'; import 'dart:convert'; import 'dart:ffi'; import 'dart:io'; import 'dart:isolate'; import 'package:cwtch/cwtch/cwtchNotifier.dart'; import 'package:path/path.dart' as path; import 'package:ffi/ffi.dart'; import 'package:cwtch/cwtch/cwtch.dart'; import '../config.dart'; import "package:path/path.dart" show dirname, join; ///////////////////// /// Cwtch API /// ///////////////////// typedef start_cwtch_function = Int8 Function(Pointer str, Int32 length, Pointer str2, Int32 length2); typedef StartCwtchFn = int Function(Pointer dir, int len, Pointer tor, int torLen); typedef void_from_void_funtion = Void Function(); typedef VoidFromVoidFunction = void Function(); typedef free_function = Void Function(Pointer); typedef FreeFn = void Function(Pointer); typedef void_from_string_string_function = Void Function(Pointer, Int32, Pointer, Int32); typedef VoidFromStringStringFn = void Function(Pointer, int, Pointer, int); typedef VoidFromStringFn = void Function(Pointer, int); typedef void_from_string_string_string_function = Void Function(Pointer, Int32, Pointer, Int32, Pointer, Int32); typedef VoidFromStringStringStringFn = void Function(Pointer, int, Pointer, int, Pointer, int); typedef void_from_string_string_string_string_function = Void Function(Pointer, Int32, Pointer, Int32, Pointer, Int32, Pointer, Int32); typedef VoidFromStringStringStringStringFn = void Function(Pointer, int, Pointer, int, Pointer, int, Pointer, int); // DownloadFile typedef void_from_string_int_string_string_string_function = Void Function(Pointer, Int32, Int32, Pointer, Int32, Pointer, Int32, Pointer, Int32); typedef VoidFromStringIntStringStringStringFn = void Function(Pointer, int, int, Pointer, int, Pointer, int, Pointer, int); typedef void_from_string_string_int_int_function = Void Function(Pointer, Int32, Pointer, Int32, Int64, Int64); typedef VoidFromStringStringIntIntFn = void Function(Pointer, int, Pointer, int, int, int); typedef void_from_string_bool_bool_bool = Void Function(Pointer, Int32, Bool, Bool, Bool); typedef VoidFromStringBoolBoolBool = void Function(Pointer, int, bool, bool, bool); typedef void_from_string_string_byte_function = Void Function(Pointer, Int32, Pointer, Int32, Int8); typedef VoidFromStringStringByteFn = void Function(Pointer, int, Pointer, int, int); typedef string_to_void_function = Void Function(Pointer str, Int32 length); typedef StringFn = void Function(Pointer dir, int); typedef string_string_to_void_function = Void Function(Pointer str, Int32 length, Pointer str2, Int32 length2); typedef StringStringFn = void Function(Pointer, int, Pointer, int); typedef string_string_to_string_function = Pointer Function(Pointer str, Int32 length, Pointer str2, Int32 length2); typedef StringFromStringStringFn = Pointer Function(Pointer, int, Pointer, int); typedef string_int_to_void_function = Void Function(Pointer str, Int32 length, Int32 handle); typedef VoidFromStringIntFn = void Function(Pointer, int, int); typedef get_json_blob_string_function = Pointer Function(Pointer str, Int32 length); typedef GetJsonBlobStringFn = Pointer Function(Pointer str, int len); typedef get_json_blob_from_string_int_string_function = Pointer Function(Pointer, Int32, Int32, Pointer, Int32); typedef GetJsonBlobFromStrIntStrFn = Pointer Function(Pointer, int, int, Pointer, int); //func GetMessage(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, message_index C.int) *C.char { typedef get_json_blob_from_str_str_int_function = Pointer Function(Pointer, Int32, Pointer, Int32, Int32); typedef GetJsonBlobFromStrStrIntFn = Pointer Function(Pointer, int, Pointer, int, int); typedef get_json_blob_from_str_int_function = Pointer Function(Pointer, Int32, Int32); typedef GetJsonBlobFromStrIntFn = Pointer Function(Pointer, int, int); typedef get_json_blob_from_str_str_function = Pointer Function(Pointer, Int32, Pointer, Int32); typedef GetJsonBlobFromStrStrFn = Pointer Function(Pointer, int, Pointer, int); typedef get_json_blob_from_str_int_int_str_function = Pointer Function(Pointer, Int32, Int32, Int32, Pointer, Int32); typedef GetJsonBlobFromStrIntIntStrFn = Pointer Function( Pointer, int, int, int, Pointer, int, ); typedef get_json_blob_from_str_int_int_function = Pointer Function(Pointer, Int32, Int32, Int32); typedef GetJsonBlobFromStrIntIntFn = Pointer Function(Pointer, int, int, int); typedef get_json_blob_from_str_int_int_int_function = Pointer Function(Pointer, Int32, Int32, Int32, Int32); typedef GetJsonBlobFromStrIntIntIntFn = Pointer Function(Pointer, int, int, int, int); typedef get_json_blob_from_str_int_string_function = Pointer Function(Pointer, Int32, Int32, Pointer, Int32); typedef GetJsonBlobFromStrIntStringFn = Pointer Function( Pointer, int, int, Pointer, int, ); // func c_GetMessagesByContentHash(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, contenthash_ptr *C.char, contenthash_len C.int) *C.char typedef get_json_blob_from_str_str_str_function = Pointer Function(Pointer, Int32, Pointer, Int32, Pointer, Int32); typedef GetJsonBlobFromStrStrStrFn = Pointer Function(Pointer, int, Pointer, int, Pointer, int); typedef void_from_string_int_string_function = Void Function(Pointer, Int32, Int32, Pointer, Int32); typedef VoidFromStringIntStringFn = void Function(Pointer, int, int, Pointer, int); typedef void_from_string_int_string_string_function = Void Function(Pointer, Int32, Int32, Pointer, Int32, Pointer, Int32); typedef VoidFromStringIntStringStringFn = void Function(Pointer, int, int, Pointer, int, Pointer, int); typedef void_from_string_int_int_int_string_string_function = Void Function(Pointer, Int32, Int32, Int32, Int32, Pointer, Int32, Pointer, Int32); typedef VoidFromStringIntIntIntStringStringFn = void Function(Pointer, int, int, int, int, Pointer, int, Pointer, int); typedef void_from_string_int_int_function = Void Function(Pointer, Int32, Int32, Int32); typedef VoidFromStringIntIntFn = void Function(Pointer, int, int, int); typedef appbus_events_function = Pointer Function(); typedef AppbusEventsFn = Pointer Function(); typedef void_to_string = Pointer Function(); typedef StringFromVoid = Pointer Function(); const String UNSUPPORTED_OS = "unsupported-os"; class CwtchFfi implements Cwtch { late DynamicLibrary library; late CwtchNotifier cwtchNotifier; late Isolate cwtchIsolate; ReceivePort _receivePort = ReceivePort(); bool _isL10nInit = false; String _assetsDir = path.join(Directory.current.path, "data", "flutter_assets"); String _cwtchDir = ""; static String getLibraryPath() { if (Platform.isWindows) { return "libCwtch.dll"; } else if (Platform.isLinux) { return "libCwtch.so"; } else if (Platform.isMacOS) { if (Abi.current() == Abi.macosX64) { return "libCwtch.x64.dylib"; } else { return "libCwtch.arm64.dylib"; } } else { return UNSUPPORTED_OS; } } CwtchFfi(CwtchNotifier _cwtchNotifier) { String libraryPath = getLibraryPath(); if (libraryPath == UNSUPPORTED_OS) { print("OS ${Platform.operatingSystem} not supported by cwtch/ffi"); // emergency, ideally the app stays on splash and just posts the error till user closes exit(0); } library = DynamicLibrary.open(libraryPath); cwtchNotifier = _cwtchNotifier; cwtchNotifier.setMessageSeenCallback((String profile, int conversation, DateTime time) => {this.SetConversationAttribute(profile, conversation, LastMessageSeenTimeKey, time.toIso8601String())}); } // ignore: non_constant_identifier_names Future Start() async { String home = ""; String bundledTor = ""; Map envVars = Platform.environment; if (Platform.isLinux) { home = envVars['HOME'] ?? ""; if (EnvironmentConfig.TEST_MODE) { _cwtchDir = envVars['CWTCH_HOME']!; } else { _cwtchDir = envVars['CWTCH_HOME'] ?? path.join(envVars['HOME']!, ".cwtch"); } if (await File("linux/Tor/tor").exists()) { bundledTor = "linux/Tor/tor"; } else if (await File("lib/Tor/tor").exists()) { bundledTor = "lib/Tor/tor"; } else if (await File(path.join(home, ".local/lib/cwtch/Tor/tor")).exists()) { bundledTor = path.join(home, ".local/lib/cwtch/Tor/tor"); _assetsDir = path.join(home, ".local", "share", "cwtch", "data", "flutter_assets"); } else if (await File("/usr/lib/cwtch/Tor/tor").exists()) { bundledTor = "/usr/lib/cwtch/Tor/tor"; _assetsDir = path.join("usr", "share", "cwtch", "data", "flutter_assets"); } else { bundledTor = "tor"; } } else if (Platform.isWindows) { _cwtchDir = envVars['CWTCH_DIR'] ?? path.join(envVars['UserProfile']!, ".cwtch"); String currentTor = path.join(Directory.current.absolute.path, "Tor\\Tor\\tor.exe"); if (await File(currentTor).exists()) { bundledTor = currentTor; _assetsDir = path.join(Directory.current.absolute.path, "data", "flutter_assets"); } else { String exeDir = path.dirname(Platform.resolvedExecutable); bundledTor = path.join(exeDir, "Tor\\Tor\\tor.exe"); _assetsDir = path.join(exeDir, "data", "flutter_assets"); } } else if (Platform.isMacOS) { _cwtchDir = envVars['CWTCH_HOME'] ?? path.join(envVars['HOME']!, "Library/Application Support/Cwtch"); _assetsDir = "/Applications/Cwtch.app/Contents/Frameworks/App.framework/Versions/Current/Resources/flutter_assets/"; if (await File("Cwtch.app/Contents/MacOS/Tor/tor").exists()) { bundledTor = "Cwtch.app/Contents/MacOS/Tor/tor"; _assetsDir = "Cwtch.app/Contents/Frameworks/App.framework/Versions/Current/Resources/flutter_assets/"; } else if (await File("/Applications/Cwtch.app/Contents/MacOS/Tor/tor").exists()) { bundledTor = "/Applications/Cwtch.app/Contents/MacOS/Tor/tor"; } else if (await File("/Volumes/Cwtch/Cwtch.app/Contents/MacOS/Tor/tor").exists()) { bundledTor = "/Volumes/Cwtch/Cwtch.app/Contents/MacOS/Tor/tor"; } else if (await File("/Applications/Tor Browser.app/Contents/MacOS/Tor/tor").exists()) { bundledTor = "/Applications/Tor Browser.app/Contents/MacOS/Tor/tor"; print("We couldn't find Tor in the Cwtch app directory, however we can fall back to the Tor Browser binary"); } else { var splitPath = path.split(dirname(Platform.script.path)); if (splitPath[0] == "/" && splitPath[1] == "Applications") { var appName = splitPath[2]; print("We're running in /Applications in a non standard app name: $appName"); if (await File("/Applications/$appName/Contents/MacOS/Tor/tor").exists()) { bundledTor = "/Applications/$appName/Contents/MacOS/Tor/tor"; } } } } // the first Cwtch MacOS release (1.2) accidently was a dev build // we need to temporarily remedy this for a release or two then delete // if macOs and release build and no profile and is dev profile // copy dev profile to release profile if (Platform.isMacOS && EnvironmentConfig.BUILD_VER != dev_version) { var devProfileExists = await Directory(path.join(_cwtchDir, "dev", "profiles")).exists(); var releaseProfileExists = await Directory(path.join(_cwtchDir, "profiles")).exists(); if (devProfileExists && !releaseProfileExists) { print("MacOS one time dev -> release profile migration..."); await Process.run("cp", ["-r", "-p", path.join(_cwtchDir, "dev", "profiles"), _cwtchDir]); await Process.run("cp", ["-r", "-p", path.join(_cwtchDir, "dev", "SALT"), _cwtchDir]); await Process.run("cp", ["-r", "-p", path.join(_cwtchDir, "dev", "ui.globals"), _cwtchDir]); } } if (EnvironmentConfig.BUILD_VER == dev_version) { _cwtchDir = path.join(_cwtchDir, "dev"); } print("StartCwtch( cwtchdir: $_cwtchDir, torPath: $bundledTor )"); var startCwtchC = library.lookup>("c_StartCwtch"); // ignore: non_constant_identifier_names final StartCwtch = startCwtchC.asFunction(); final utf8CwtchDir = _cwtchDir.toNativeUtf8(); StartCwtch(utf8CwtchDir, utf8CwtchDir.length, bundledTor.toNativeUtf8(), bundledTor.length); malloc.free(utf8CwtchDir); // Spawn an isolate to listen to events from libcwtch-go and then dispatch them when received on main thread to cwtchNotifier cwtchIsolate = await Isolate.spawn(_checkAppbusEvents, _receivePort.sendPort); _receivePort.listen((message) { var env = jsonDecode(message); cwtchNotifier.handleMessage(env["EventType"], env["Data"]); }); } String getAssetsDir() { return _assetsDir; } Future getCwtchDir() async { return _cwtchDir; } // Called on object being disposed to (presumably on app close) to close the isolate that's listening to libcwtch-go events @override void dispose() { EnvironmentConfig.debugLog("tearing down cwtch FFI isolate"); library.close(); cwtchIsolate.kill(priority: Isolate.immediate); } // Entry point for an isolate to listen to a stream of events pulled from libcwtch-go and return them on the sendPort static void _checkAppbusEvents(SendPort sendPort) async { var stream = pollAppbusEvents(); await for (var value in stream) { sendPort.send(value); } print("checkAppBusEvents finished..."); } // Steam of appbus events. Call blocks in libcwtch-go GetAppbusEvent. Static so the isolate can use it static Stream pollAppbusEvents() async* { late DynamicLibrary library = DynamicLibrary.open(getLibraryPath()); var getAppbusEventC = library.lookup>("c_GetAppBusEvent"); // ignore: non_constant_identifier_names final GetAppbusEvent = getAppbusEventC.asFunction(); // Embedded Version of _UnsafeFreePointerAnyUseOfThisFunctionMustBeDoubleApproved var free = library.lookup>("c_FreePointer"); final Free = free.asFunction(); // ignore: non_constant_identifier_names final GetAppBusEvent = () { // ignore: non_constant_identifier_names Pointer result = GetAppbusEvent(); String event = result.toDartString(); Free(result); return event; }; while (true) { final event = GetAppBusEvent(); if (event.startsWith("{\"EventType\":\"Shutdown\"")) { print("Shutting down isolate thread: $event"); return; } yield event; } } // ignore: non_constant_identifier_names void CreateProfile(String nick, String pass, bool autostart) { var createProfileC = library.lookup>("c_CreateProfile"); // ignore: non_constant_identifier_names final CreateProfile = createProfileC.asFunction(); final utf8nick = nick.toNativeUtf8(); final ut8pass = pass.toNativeUtf8(); CreateProfile(utf8nick, utf8nick.length, ut8pass, ut8pass.length, autostart ? 1 : 0); malloc.free(utf8nick); malloc.free(ut8pass); } // ignore: non_constant_identifier_names void ActivatePeerEngine(String profile) { var activatePeerEngineC = library.lookup>("c_ActivatePeerEngine"); final ActivatePeerEngine = activatePeerEngineC.asFunction(); final ut8profile = profile.toNativeUtf8(); ActivatePeerEngine(ut8profile, ut8profile.length); malloc.free(ut8profile); } // ignore: non_constant_identifier_names void DeactivatePeerEngine(String profile) { var deactivatePeerEngineC = library.lookup>("c_DeactivatePeerEngine"); final DeactivatePeerEngine = deactivatePeerEngineC.asFunction(); final ut8profile = profile.toNativeUtf8(); DeactivatePeerEngine(ut8profile, ut8profile.length); malloc.free(ut8profile); } // ignore: non_constant_identifier_names void LoadProfiles(String pass) { var loadProfileC = library.lookup>("c_LoadProfiles"); // ignore: non_constant_identifier_names final LoadProfiles = loadProfileC.asFunction(); final ut8pass = pass.toNativeUtf8(); LoadProfiles(ut8pass, ut8pass.length); malloc.free(ut8pass); } // ignore: non_constant_identifier_names Future GetMessage(String profile, int handle, int index) async { var getMessageC = library.lookup>("c_GetMessage"); // ignore: non_constant_identifier_names final GetMessage = getMessageC.asFunction(); final utf8profile = profile.toNativeUtf8(); Pointer jsonMessageBytes = GetMessage(utf8profile, utf8profile.length, handle, index); String jsonMessage = jsonMessageBytes.toDartString(); _UnsafeFreePointerAnyUseOfThisFunctionMustBeDoubleApproved(jsonMessageBytes); malloc.free(utf8profile); return jsonMessage; } // ignore: non_constant_identifier_names Future GetMessages(String profile, int handle, int index, int count) async { var getMessagesC = library.lookup>("c_GetMessages"); // ignore: non_constant_identifier_names final GetMessages = getMessagesC.asFunction(); final utf8profile = profile.toNativeUtf8(); Pointer jsonMessageBytes = GetMessages(utf8profile, utf8profile.length, handle, index, count); String jsonMessage = jsonMessageBytes.toDartString(); _UnsafeFreePointerAnyUseOfThisFunctionMustBeDoubleApproved(jsonMessageBytes); malloc.free(utf8profile); return jsonMessage; } @override // ignore: non_constant_identifier_names void AcceptContact(String profileOnion, int contactHandle) { var acceptContact = library.lookup>("c_AcceptConversation"); // ignore: non_constant_identifier_names final AcceptContact = acceptContact.asFunction(); final u1 = profileOnion.toNativeUtf8(); AcceptContact(u1, u1.length, contactHandle); malloc.free(u1); } @override // ignore: non_constant_identifier_names void BlockContact(String profileOnion, int contactHandle) { var blockContact = library.lookup>("c_BlockConversation"); // ignore: non_constant_identifier_names final BlockContact = blockContact.asFunction(); final u1 = profileOnion.toNativeUtf8(); BlockContact(u1, u1.length, contactHandle); malloc.free(u1); } @override // ignore: non_constant_identifier_names void UnblockContact(String profileOnion, int contactHandle) { var unblockContact = library.lookup>("c_UnblockConversation"); // ignore: non_constant_identifier_names final UnblockContact = unblockContact.asFunction(); final u1 = profileOnion.toNativeUtf8(); UnblockContact(u1, u1.length, contactHandle); malloc.free(u1); } @override // ignore: non_constant_identifier_names Future SendMessage(String profileOnion, int contactHandle, String message) async { var sendMessage = library.lookup>("c_SendMessage"); // ignore: non_constant_identifier_names final SendMessage = sendMessage.asFunction(); final u1 = profileOnion.toNativeUtf8(); final u3 = message.toNativeUtf8(); Pointer jsonMessageBytes = SendMessage(u1, u1.length, contactHandle, u3, u3.length); String jsonMessage = jsonMessageBytes.toDartString(); _UnsafeFreePointerAnyUseOfThisFunctionMustBeDoubleApproved(jsonMessageBytes); malloc.free(u1); malloc.free(u3); return jsonMessage; } @override // ignore: non_constant_identifier_names Future SendInvitation(String profileOnion, int contactHandle, int target) async { var sendInvitation = library.lookup>("c_SendInviteMessage"); // ignore: non_constant_identifier_names final SendInvitation = sendInvitation.asFunction(); final u1 = profileOnion.toNativeUtf8(); Pointer jsonMessageBytes = SendInvitation(u1, u1.length, contactHandle, target); String jsonMessage = jsonMessageBytes.toDartString(); _UnsafeFreePointerAnyUseOfThisFunctionMustBeDoubleApproved(jsonMessageBytes); malloc.free(u1); return jsonMessage; } @override // ignore: non_constant_identifier_names Future ShareFile(String profileOnion, int contactHandle, String filepath) async { var shareFile = library.lookup>("c_ShareFile"); // ignore: non_constant_identifier_names final ShareFile = shareFile.asFunction(); final u1 = profileOnion.toNativeUtf8(); final u3 = filepath.toNativeUtf8(); Pointer jsonMessageBytes = ShareFile(u1, u1.length, contactHandle, u3, u3.length); String jsonMessage = jsonMessageBytes.toDartString(); _UnsafeFreePointerAnyUseOfThisFunctionMustBeDoubleApproved(jsonMessageBytes); malloc.free(u1); malloc.free(u3); return jsonMessage; } @override // ignore: non_constant_identifier_names void DownloadFile(String profileOnion, int contactHandle, String filepath, String manifestpath, String filekey) { var dlFile = library.lookup>("c_DownloadFileDefaultLimit"); // ignore: non_constant_identifier_names final DownloadFile = dlFile.asFunction(); final u1 = profileOnion.toNativeUtf8(); final u3 = filepath.toNativeUtf8(); final u4 = manifestpath.toNativeUtf8(); final u5 = filekey.toNativeUtf8(); DownloadFile(u1, u1.length, contactHandle, u3, u3.length, u4, u4.length, u5, u5.length); malloc.free(u1); malloc.free(u3); malloc.free(u4); malloc.free(u5); } @override // ignore: non_constant_identifier_names void CreateDownloadableFile(String profileOnion, int contactHandle, String filenameSuggestion, String filekey, String manifestPath) { // android only - do nothing } // ignore: non_constant_identifier_names void ExportPreviewedFile(String sourceFile, String suggestion) { // android only - do nothing } @override // ignore: non_constant_identifier_names void CheckDownloadStatus(String profileOnion, String fileKey) { var checkDownloadStatus = library.lookup>("c_CheckDownloadStatus"); // ignore: non_constant_identifier_names final CheckDownloadStatus = checkDownloadStatus.asFunction(); final u1 = profileOnion.toNativeUtf8(); final u2 = fileKey.toNativeUtf8(); CheckDownloadStatus(u1, u1.length, u2, u2.length); malloc.free(u1); malloc.free(u2); } @override // ignore: non_constant_identifier_names void VerifyOrResumeDownload(String profileOnion, int contactHandle, String filekey) { var fn = library.lookup>("c_VerifyOrResumeDownloadDefaultLimit"); // ignore: non_constant_identifier_names final VerifyOrResumeDownload = fn.asFunction(); final u1 = profileOnion.toNativeUtf8(); final u3 = filekey.toNativeUtf8(); VerifyOrResumeDownload(u1, u1.length, contactHandle, u3, u3.length); malloc.free(u1); malloc.free(u3); } @override // ignore: non_constant_identifier_names void ResetTor() { var resetTor = library.lookup>("c_ResetTor"); // ignore: non_constant_identifier_names final ResetTor = resetTor.asFunction(); ResetTor(); } @override // ignore: non_constant_identifier_names Future ImportBundle(String profileOnion, String bundle) async { var importBundle = library.lookup>("c_ImportBundle"); // ignore: non_constant_identifier_names final ImportBundle = importBundle.asFunction(); final u1 = profileOnion.toNativeUtf8(); final u2 = bundle.toNativeUtf8(); Pointer responsePtr = ImportBundle(u1, u1.length, u2, u2.length); String response = responsePtr.toDartString(); _UnsafeFreePointerAnyUseOfThisFunctionMustBeDoubleApproved(responsePtr); malloc.free(u1); malloc.free(u2); return response; } @override // ignore: non_constant_identifier_names void CreateGroup(String profileOnion, String server, String groupName) { var createGroup = library.lookup>("c_StartGroup"); // ignore: non_constant_identifier_names final CreateGroup = createGroup.asFunction(); final u1 = profileOnion.toNativeUtf8(); final u3 = server.toNativeUtf8(); final u2 = groupName.toNativeUtf8(); CreateGroup(u1, u1.length, u2, u2.length, u3, u3.length); malloc.free(u1); malloc.free(u2); malloc.free(u3); } @override // ignore: non_constant_identifier_names void ArchiveConversation(String profileOnion, int handle) { var archiveConversation = library.lookup>("c_ArchiveConversation"); // ignore: non_constant_identifier_names final ArchiveConversation = archiveConversation.asFunction(); final u1 = profileOnion.toNativeUtf8(); ArchiveConversation(u1, u1.length, handle); malloc.free(u1); } @override // ignore: non_constant_identifier_names void DeleteContact(String profileOnion, int handle) { var deleteContact = library.lookup>("c_DeleteConversation"); // ignore: non_constant_identifier_names final DeleteContact = deleteContact.asFunction(); final u1 = profileOnion.toNativeUtf8(); DeleteContact(u1, u1.length, handle); malloc.free(u1); } @override // ignore: non_constant_identifier_names void DeleteProfile(String onion, String currentPassword) { var deleteprofile = library.lookup>("c_DeleteProfile"); // ignore: non_constant_identifier_names final DeleteProfile = deleteprofile.asFunction(); final u1 = onion.toNativeUtf8(); final u2 = currentPassword.toNativeUtf8(); DeleteProfile(u1, u1.length, u2, u2.length); malloc.free(u1); malloc.free(u2); } @override // ignore: non_constant_identifier_names void SetProfileAttribute(String profile, String key, String val) { var setProfileAttribute = library.lookup>("c_SetProfileAttribute"); // ignore: non_constant_identifier_names final SetProfileAttribute = setProfileAttribute.asFunction(); final u1 = profile.toNativeUtf8(); final u2 = key.toNativeUtf8(); final u3 = val.toNativeUtf8(); SetProfileAttribute(u1, u1.length, u2, u2.length, u3, u3.length); malloc.free(u1); malloc.free(u2); malloc.free(u3); } @override // ignore: non_constant_identifier_names void SetConversationAttribute(String profile, int contact, String key, String val) { var setContactAttribute = library.lookup>("c_SetConversationAttribute"); // ignore: non_constant_identifier_names final SetContactAttribute = setContactAttribute.asFunction(); final u1 = profile.toNativeUtf8(); final u3 = key.toNativeUtf8(); final u4 = val.toNativeUtf8(); SetContactAttribute(u1, u1.length, contact, u3, u3.length, u4, u4.length); malloc.free(u1); malloc.free(u3); malloc.free(u4); } @override // ignore: non_constant_identifier_names void SetMessageAttribute(String profile, int conversation, int channel, int message, String key, String val) { var setMessageAttribute = library.lookup>("c_UpdateMessageAttribute"); // ignore: non_constant_identifier_names final SetMessageAttribute = setMessageAttribute.asFunction(); final u1 = profile.toNativeUtf8(); final u3 = key.toNativeUtf8(); final u4 = val.toNativeUtf8(); SetMessageAttribute(u1, u1.length, conversation, channel, message, u3, u3.length, u4, u4.length); malloc.free(u1); malloc.free(u3); malloc.free(u4); } @override // ignore: non_constant_identifier_names void LoadServers(String password) { var loadServers = library.lookup>("c_LoadServers"); // ignore: non_constant_identifier_names final LoadServers = loadServers.asFunction(); final u1 = password.toNativeUtf8(); LoadServers(u1, u1.length); malloc.free(u1); } @override // ignore: non_constant_identifier_names void CreateServer(String password, String description, bool autostart) { var createServer = library.lookup>("c_CreateServer"); // ignore: non_constant_identifier_names final CreateServer = createServer.asFunction(); final u1 = password.toNativeUtf8(); final u2 = description.toNativeUtf8(); CreateServer(u1, u1.length, u2, u2.length, autostart ? 1 : 0); malloc.free(u1); malloc.free(u2); } @override // ignore: non_constant_identifier_names void DeleteServer(String serverOnion, String password) { var deleteServer = library.lookup>("c_DeleteServer"); // ignore: non_constant_identifier_names final DeleteServer = deleteServer.asFunction(); final u1 = serverOnion.toNativeUtf8(); final u2 = password.toNativeUtf8(); DeleteServer(u1, u1.length, u2, u2.length); malloc.free(u1); malloc.free(u2); } @override // ignore: non_constant_identifier_names void LaunchServers() { var launchServers = library.lookup>("c_LaunchServers"); // ignore: non_constant_identifier_names final LaunchServers = launchServers.asFunction(); LaunchServers(); } @override // ignore: non_constant_identifier_names void LaunchServer(String serverOnion) { var launchServer = library.lookup>("c_LaunchServer"); // ignore: non_constant_identifier_names final LaunchServer = launchServer.asFunction(); final u1 = serverOnion.toNativeUtf8(); LaunchServer(u1, u1.length); malloc.free(u1); } @override // ignore: non_constant_identifier_names void StopServer(String serverOnion) { var shutdownServer = library.lookup>("c_StopServer"); // ignore: non_constant_identifier_names final ShutdownServer = shutdownServer.asFunction(); final u1 = serverOnion.toNativeUtf8(); ShutdownServer(u1, u1.length); malloc.free(u1); } @override // ignore: non_constant_identifier_names void StopServers() { var shutdownServers = library.lookup>("c_StopServers"); // ignore: non_constant_identifier_names final ShutdownServers = shutdownServers.asFunction(); ShutdownServers(); } @override // ignore: non_constant_identifier_names void DestroyServers() { var destroyServers = library.lookup>("c_DestroyServers"); // ignore: non_constant_identifier_names final DestroyServers = destroyServers.asFunction(); DestroyServers(); } @override // ignore: non_constant_identifier_names void SetServerAttribute(String serverOnion, String key, String val) { var setServerAttribute = library.lookup>("c_SetServerAttribute"); // ignore: non_constant_identifier_names final SetServerAttribute = setServerAttribute.asFunction(); final u1 = serverOnion.toNativeUtf8(); final u2 = key.toNativeUtf8(); final u3 = val.toNativeUtf8(); SetServerAttribute(u1, u1.length, u2, u2.length, u3, u3.length); malloc.free(u1); malloc.free(u2); malloc.free(u3); } @override // ignore: non_constant_identifier_names Future Shutdown() async { var shutdown = library.lookup>("c_ShutdownCwtch"); // ignore: non_constant_identifier_names // Shutdown Cwtch + Tor... // ignore: non_constant_identifier_names final Shutdown = shutdown.asFunction(); Shutdown(); // Kill our Isolate cwtchIsolate.kill(priority: Isolate.immediate); print("Isolate killed"); _receivePort.close(); print("Receive Port Closed"); } @override // ignore: non_constant_identifier_names Future GetMessageByContentHash(String profile, int handle, String contentHash) async { var getMessagesByContentHashC = library.lookup>("c_GetMessageByContentHash"); // ignore: non_constant_identifier_names final GetMessagesByContentHash = getMessagesByContentHashC.asFunction(); final utf8profile = profile.toNativeUtf8(); final utf8contentHash = contentHash.toNativeUtf8(); Pointer jsonMessageBytes = GetMessagesByContentHash(utf8profile, utf8profile.length, handle, utf8contentHash, utf8contentHash.length); String jsonMessage = jsonMessageBytes.toDartString(); _UnsafeFreePointerAnyUseOfThisFunctionMustBeDoubleApproved(jsonMessageBytes); malloc.free(utf8profile); malloc.free(utf8contentHash); return jsonMessage; } // ignore: non_constant_identifier_names // Incredibly dangerous function which invokes a free in libCwtch, should only be used // as documented in `MEMORY.md` in libCwtch repo. void _UnsafeFreePointerAnyUseOfThisFunctionMustBeDoubleApproved(Pointer ptr) { var free = library.lookup>("c_FreePointer"); final Free = free.asFunction(); Free(ptr); } @override String? defaultDownloadPath() { Map envVars = Platform.environment; String nominalPath = path.join(envVars[Platform.isWindows ? 'UserProfile' : 'HOME']!, "Downloads"); if (Directory(nominalPath).existsSync() == false) { return Directory.current.path; } return nominalPath; } @override // ignore: non_constant_identifier_names Future GetMessageByID(String profile, int handle, int index) async { var getMessageC = library.lookup>("c_GetMessageById"); // ignore: non_constant_identifier_names final GetMessage = getMessageC.asFunction(); final utf8profile = profile.toNativeUtf8(); Pointer jsonMessageBytes = GetMessage(utf8profile, utf8profile.length, handle, index); String jsonMessage = jsonMessageBytes.toDartString(); _UnsafeFreePointerAnyUseOfThisFunctionMustBeDoubleApproved(jsonMessageBytes); malloc.free(utf8profile); return jsonMessage; } @override // ignore: non_constant_identifier_names void ChangePassword(String profile, String pass, String newpass, String newpassAgain) { var changePasswordC = library.lookup>("c_ChangePassword"); // ignore: non_constant_identifier_names final ChangePasswordFn = changePasswordC.asFunction(); final utf8profile = profile.toNativeUtf8(); final utf8pass = pass.toNativeUtf8(); final utf8newpass = newpass.toNativeUtf8(); final utf8newpasssagain = newpassAgain.toNativeUtf8(); ChangePasswordFn(utf8profile, utf8profile.length, utf8pass, utf8pass.length, utf8newpass, utf8newpass.length, utf8newpasssagain, utf8newpasssagain.length); malloc.free(utf8profile); malloc.free(utf8pass); malloc.free(utf8newpass); malloc.free(utf8newpasssagain); } @override bool isL10nInit() { return _isL10nInit; } @override void l10nInit(String notificationSimple, String notificationConversationInfo) { cwtchNotifier.l10nInit(notificationSimple, notificationConversationInfo); _isL10nInit = true; } @override // ignore: non_constant_identifier_names void ExportProfile(String profile, String file) { final utf8profile = profile.toNativeUtf8(); final utf8file = file.toNativeUtf8(); var exportProfileC = library.lookup>("c_ExportProfile"); // ignore: non_constant_identifier_names final ExportProfileFn = exportProfileC.asFunction(); ExportProfileFn(utf8profile, utf8profile.length, utf8file, utf8file.length); malloc.free(utf8profile); malloc.free(utf8file); } @override // ignore: non_constant_identifier_names Future ImportProfile(String file, String pass) async { final utf8pass = pass.toNativeUtf8(); final utf8file = file.toNativeUtf8(); var exportProfileC = library.lookup>("c_ImportProfile"); // ignore: non_constant_identifier_names final ExportProfileFn = exportProfileC.asFunction(); Pointer result = ExportProfileFn(utf8file, utf8file.length, utf8pass, utf8pass.length); String importResult = result.toDartString(); _UnsafeFreePointerAnyUseOfThisFunctionMustBeDoubleApproved(result); malloc.free(utf8pass); malloc.free(utf8file); return importResult; } @override Future GetDebugInfo() async { var getDebugInfo = library.lookup>("c_GetDebugInfo"); final GetDebugInfo = getDebugInfo.asFunction(); Pointer result = GetDebugInfo(); String debugResult = result.toDartString(); _UnsafeFreePointerAnyUseOfThisFunctionMustBeDoubleApproved(result); return debugResult; } @override Future GetSharedFiles(String profile, int handle) async { var getSharedFiles = library.lookup>("c_GetSharedFiles"); final GetSharedFiles = getSharedFiles.asFunction(); final utf8profile = profile.toNativeUtf8(); Pointer jsonMessageBytes = GetSharedFiles(utf8profile, utf8profile.length, handle); String jsonMessage = jsonMessageBytes.toDartString(); _UnsafeFreePointerAnyUseOfThisFunctionMustBeDoubleApproved(jsonMessageBytes); malloc.free(utf8profile); return jsonMessage; } @override void RestartSharing(String profile, String filekey) { var restartSharingC = library.lookup>("c_RestartFileShare"); // ignore: non_constant_identifier_names final RestartSharing = restartSharingC.asFunction(); final utf8profile = profile.toNativeUtf8(); final ut8filekey = filekey.toNativeUtf8(); RestartSharing(utf8profile, utf8profile.length, ut8filekey, ut8filekey.length); malloc.free(utf8profile); malloc.free(ut8filekey); } @override void StopSharing(String profile, String filekey) { var stopSharingC = library.lookup>("c_StopFileShare"); // ignore: non_constant_identifier_names final StopSharing = stopSharingC.asFunction(); final utf8profile = profile.toNativeUtf8(); final ut8filekey = filekey.toNativeUtf8(); StopSharing(utf8profile, utf8profile.length, ut8filekey, ut8filekey.length); malloc.free(utf8profile); malloc.free(ut8filekey); } @override void DeleteServerInfo(String profile, String handle) { var deleteServerInfoC = library.lookup>("c_DeleteServerInfo"); // ignore: non_constant_identifier_names final StopSharing = deleteServerInfoC.asFunction(); final utf8profile = profile.toNativeUtf8(); final ut8handle = handle.toNativeUtf8(); StopSharing(utf8profile, utf8profile.length, ut8handle, ut8handle.length); malloc.free(utf8profile); malloc.free(ut8handle); } @override void UpdateSettings(String json) { var updateSettings = library.lookup>("c_UpdateSettings"); // ignore: non_constant_identifier_names final UpdateSettingsFn = updateSettings.asFunction(); final u1 = json.toNativeUtf8(); UpdateSettingsFn(u1, u1.length); malloc.free(u1); } @override bool IsServersCompiled() { return library.providesSymbol("c_LoadServers"); } @override Future SummarizeConversation(String profile, int conversation) async { if (!library.providesSymbol("c_Summarize")) { return Future.value(""); } var summarize = library.lookup>("c_Summarize"); // ignore: non_constant_identifier_names final SummarizeFn = summarize.asFunction(); final utf8profile = profile.toNativeUtf8(); Pointer jsonMessageBytes = SummarizeFn(utf8profile, utf8profile.length, conversation); String jsonMessage = jsonMessageBytes.toDartString(); _UnsafeFreePointerAnyUseOfThisFunctionMustBeDoubleApproved(jsonMessageBytes); malloc.free(utf8profile); return jsonMessage; } @override Future TranslateMessage(String profile, int conversation, int message, String language) async { if (!library.providesSymbol("c_Translate")) { return Future.value(""); } var translate = library.lookup>("c_Translate"); // ignore: non_constant_identifier_names final TranslateFn = translate.asFunction(); final utf8profile = profile.toNativeUtf8(); final utf8lang = language.toNativeUtf8(); Pointer jsonMessageBytes = TranslateFn( utf8profile, utf8profile.length, conversation, message, utf8lang, utf8lang.length, ); String jsonMessage = jsonMessageBytes.toDartString(); _UnsafeFreePointerAnyUseOfThisFunctionMustBeDoubleApproved(jsonMessageBytes); malloc.free(utf8profile); malloc.free(utf8lang); return jsonMessage; } @override bool IsBlodeuweddSupported() { if (library.providesSymbol("c_Translate")) { return true; } return false; } @override Future GetProfileAttribute(String profile, String key) { var getProfileAttributeC = library.lookup>("c_GetProfileAttribute"); // ignore: non_constant_identifier_names final GetProfileAttribute = getProfileAttributeC.asFunction(); final utf8profile = profile.toNativeUtf8(); final utf8key = key.toNativeUtf8(); Pointer jsonMessageBytes = GetProfileAttribute(utf8profile, utf8profile.length, utf8key, utf8key.length); String jsonMessage = jsonMessageBytes.toDartString(); _UnsafeFreePointerAnyUseOfThisFunctionMustBeDoubleApproved(jsonMessageBytes); malloc.free(utf8profile); malloc.free(utf8key); try { dynamic attributeResult = json.decode(jsonMessage); if (attributeResult["Exists"]) { return Future.value(attributeResult["Value"]); } } catch (e) { EnvironmentConfig.debugLog("error getting profile attribute: $e"); } return Future.value(null); } @override Future GetConversationAttribute(String profile, int conversation, String key) { var getConversationAttributeC = library.lookup>("c_GetConversationAttribute"); // ignore: non_constant_identifier_names final GetConversationAttribute = getConversationAttributeC.asFunction(); final utf8profile = profile.toNativeUtf8(); final utf8key = key.toNativeUtf8(); Pointer jsonMessageBytes = GetConversationAttribute(utf8profile, utf8profile.length, conversation, utf8key, utf8key.length); String jsonMessage = jsonMessageBytes.toDartString(); _UnsafeFreePointerAnyUseOfThisFunctionMustBeDoubleApproved(jsonMessageBytes); malloc.free(utf8profile); malloc.free(utf8key); try { dynamic attributeResult = json.decode(jsonMessage); if (attributeResult["Exists"]) { return Future.value(attributeResult["Value"]); } } catch (e) { EnvironmentConfig.debugLog("error getting profile attribute: $e"); } return Future.value(null); } @override void AttemptReconnection(String profile, String onion) { // ignore: non_constant_identifier_names var peerWithOnionC = library.lookup>("c_PeerWithOnion"); final PeerWithOnionF = peerWithOnionC.asFunction(); final utf8profile = profile.toNativeUtf8(); final utf8onion = onion.toNativeUtf8(); PeerWithOnionF(utf8profile, utf8profile.length, utf8onion, utf8onion.length); malloc.free(utf8profile); malloc.free(utf8onion); } @override void AttemptReconnectionServer(String profile, String onion) { // ignore: non_constant_identifier_names var queueJoinServerC = library.lookup>("c_QueueJoinServer"); final QueueJoinServerC = queueJoinServerC.asFunction(); final utf8profile = profile.toNativeUtf8(); final utf8onion = onion.toNativeUtf8(); QueueJoinServerC(utf8profile, utf8profile.length, utf8onion, utf8onion.length); malloc.free(utf8profile); malloc.free(utf8onion); } @override void DisconnectFromPeer(String profile, String onion) { // ignore: non_constant_identifier_names var disconnectFromPeerC = library.lookup>("c_DisconnectFromPeer"); final DisconnectFromPeerC = disconnectFromPeerC.asFunction(); final utf8profile = profile.toNativeUtf8(); final utf8onion = onion.toNativeUtf8(); DisconnectFromPeerC(utf8profile, utf8profile.length, utf8onion, utf8onion.length); malloc.free(utf8profile); malloc.free(utf8onion); } @override void DisconnectFromServer(String profile, String onion) { // ignore: non_constant_identifier_names var disconnectFromServerC = library.lookup>("c_DisconnectFromServer"); final DisconnectFromServerC = disconnectFromServerC.asFunction(); final utf8profile = profile.toNativeUtf8(); final utf8onion = onion.toNativeUtf8(); DisconnectFromServerC(utf8profile, utf8profile.length, utf8onion, utf8onion.length); malloc.free(utf8profile); malloc.free(utf8onion); } @override Future SearchConversations(String profile, String pattern) async { var searchConversationsC = library.lookup>("c_SearchConversations"); // ignore: non_constant_identifier_names final SearchConversations = searchConversationsC.asFunction(); final utf8profile = profile.toNativeUtf8(); final utf8pattern = pattern.toNativeUtf8(); EnvironmentConfig.debugLog("Searching for $profile $pattern"); Pointer searchIDRaw = SearchConversations(utf8profile, utf8profile.length, utf8pattern, utf8pattern.length); String searchID = searchIDRaw.toDartString(); _UnsafeFreePointerAnyUseOfThisFunctionMustBeDoubleApproved(searchIDRaw); malloc.free(utf8profile); malloc.free(utf8pattern); return searchID; } @override Future> PlatformChannelInfo() { return Future.value(HashMap()); } @override Future ConfigureConnections(String profile, bool listen, bool peers, bool servers) async { var configureConnections = library.lookup>("c_ConfigureConnections"); // ignore: non_constant_identifier_names final ConfigureConnections = configureConnections.asFunction(); final utf8profile = profile.toNativeUtf8(); ConfigureConnections(utf8profile, utf8profile.length, listen, peers, servers); malloc.free(utf8profile); return; } @override void PublishServerUpdate(String profile) { var publishServerUpdate = library.lookup>("c_PublishServerUpdate"); // ignore: non_constant_identifier_names final PublishServerUpdate = publishServerUpdate.asFunction(); final utf8profile = profile.toNativeUtf8(); PublishServerUpdate(utf8profile, utf8profile.length); malloc.free(utf8profile); } @override bool IsLoaded() { bool check = library.providesSymbol("c_UpdateSettings"); EnvironmentConfig.debugLog("Checking that the FFI Interface is Correctly Loaded... $check"); return check; } }