import 'dart:convert'; import 'dart:ffi'; import 'dart:io'; import 'dart:isolate'; import 'dart:io' show Platform; import 'package:cwtch/cwtch/cwtchNotifier.dart'; import 'package:flutter/src/services/text_input.dart'; import 'package:path/path.dart' as path; import 'package:ffi/ffi.dart'; import 'package:cwtch/cwtch/cwtch.dart'; import '../config.dart'; ///////////////////// /// 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 void_from_string_string_function = Void Function(Pointer, Int32, Pointer, Int32); typedef VoidFromStringStringFn = void Function(Pointer, int, 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); 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 access_cwtch_eventbus_function = Void Function(); typedef NextEventFn = void Function(); 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 get_json_blob_void_function = Pointer Function(); typedef GetJsonBlobVoidFn = Pointer Function(); typedef get_json_blob_string_function = Pointer Function(Pointer str, Int32 length); typedef GetJsonBlobStringFn = Pointer Function(Pointer str, int len); //func NumMessages(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int) (n C.int) { typedef get_int_from_str_str_function = Int32 Function(Pointer, Int32, Pointer, Int32); typedef GetIntFromStrStrFn = int Function(Pointer, 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); //func GetMessages(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, start C.int, end C.int) *C.char { typedef get_json_blob_from_str_str_int_int_function = Pointer Function(Pointer, Int32, Pointer, Int32, Int32, Int32); typedef GetJsonBlobFromStrStrIntIntFn = Pointer Function(Pointer, int, Pointer, int, int, int); typedef appbus_events_function = Pointer Function(); typedef AppbusEventsFn = Pointer Function(); class CwtchFfi implements Cwtch { late DynamicLibrary library; late CwtchNotifier cwtchNotifier; late Isolate cwtchIsolate; ReceivePort _receivePort = ReceivePort(); CwtchFfi(CwtchNotifier _cwtchNotifier) { if (Platform.isWindows) { library = DynamicLibrary.open("libCwtch.dll"); } else if (Platform.isLinux) { library = DynamicLibrary.open("libCwtch.so"); } else { 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); } cwtchNotifier = _cwtchNotifier; } // ignore: non_constant_identifier_names Future Start() async { String home = ""; String bundledTor = ""; Map envVars = Platform.environment; if (Platform.isLinux) { home = (envVars['HOME'])!; bundledTor = "./tor"; } else if (Platform.isWindows) { home = (envVars['UserProfile'])!; bundledTor = "Tor\\Tor\\tor.exe"; } var cwtchDir = path.join(home, ".cwtch"); if (EnvironmentConfig.BUILD_VER == dev_version) { cwtchDir = path.join(cwtchDir, "dev"); } print("cwtchDir $cwtchDir"); var startCwtchC = library.lookup>("c_StartCwtch"); // ignore: non_constant_identifier_names final StartCwtch = startCwtchC.asFunction(); final ut8CwtchDir = cwtchDir.toNativeUtf8(); StartCwtch(ut8CwtchDir, ut8CwtchDir.length, bundledTor.toNativeUtf8(), bundledTor.length); // 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"]); }); } // ignore: non_constant_identifier_names Future ReconnectCwtchForeground() async { var reconnectCwtch = library.lookup>("c_ReconnectCwtchForeground"); // ignore: non_constant_identifier_names final ReconnectCwtchForeground = reconnectCwtch.asFunction(); ReconnectCwtchForeground(); } // Called on object being disposed to (presumably on app close) to close the isolate that's listening to libcwtch-go events @override void dispose() { if (cwtchIsolate != null) { 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; if (Platform.isWindows) { library = DynamicLibrary.open("libCwtch.dll"); } else if (Platform.isLinux) { library = DynamicLibrary.open("libCwtch.so"); } var getAppbusEventC = library.lookup>("c_GetAppBusEvent"); // ignore: non_constant_identifier_names final GetAppbusEvent = getAppbusEventC.asFunction(); while (true) { Pointer result = GetAppbusEvent(); String event = result.toDartString(); if (event.startsWith("{\"EventType\":\"Shutdown\"")) { print("Shutting down isolate thread: $event"); return; } yield event; } } // ignore: non_constant_identifier_names void SelectProfile(String onion) async { var selectProfileC = library.lookup>("c_SelectProfile"); // ignore: non_constant_identifier_names final SelectProfile = selectProfileC.asFunction(); final ut8Onion = onion.toNativeUtf8(); SelectProfile(ut8Onion, ut8Onion.length); } // ignore: non_constant_identifier_names void CreateProfile(String nick, String pass) { 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); } // 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); } // ignore: non_constant_identifier_names Future GetMessage(String profile, String handle, int index) async { var getMessageC = library.lookup>("c_GetMessage"); // ignore: non_constant_identifier_names final GetMessage = getMessageC.asFunction(); final utf8profile = profile.toNativeUtf8(); final utf8handle = handle.toNativeUtf8(); Pointer jsonMessageBytes = GetMessage(utf8profile, utf8profile.length, utf8handle, utf8handle.length, index); String jsonMessage = jsonMessageBytes.toDartString(); return jsonMessage; } @override // ignore: non_constant_identifier_names void SendProfileEvent(String onion, String json) { var sendAppBusEvent = library.lookup>("c_SendProfileEvent"); // ignore: non_constant_identifier_names final SendAppBusEvent = sendAppBusEvent.asFunction(); final utf8onion = onion.toNativeUtf8(); final utf8json = json.toNativeUtf8(); SendAppBusEvent(utf8onion, utf8onion.length, utf8json, utf8json.length); } @override // ignore: non_constant_identifier_names void SendAppEvent(String json) { var sendAppBusEvent = library.lookup>("c_SendAppEvent"); // ignore: non_constant_identifier_names final SendAppBusEvent = sendAppBusEvent.asFunction(); final utf8json = json.toNativeUtf8(); SendAppBusEvent(utf8json, utf8json.length); } @override // ignore: non_constant_identifier_names void AcceptContact(String profileOnion, String contactHandle) { var acceptContact = library.lookup>("c_AcceptContact"); // ignore: non_constant_identifier_names final AcceptContact = acceptContact.asFunction(); final u1 = profileOnion.toNativeUtf8(); final u2 = contactHandle.toNativeUtf8(); AcceptContact(u1, u1.length, u2, u2.length); } @override // ignore: non_constant_identifier_names void BlockContact(String profileOnion, String contactHandle) { var blockContact = library.lookup>("c_BlockContact"); // ignore: non_constant_identifier_names final BlockContact = blockContact.asFunction(); final u1 = profileOnion.toNativeUtf8(); final u2 = contactHandle.toNativeUtf8(); BlockContact(u1, u1.length, u2, u2.length); } @override // ignore: non_constant_identifier_names void SendMessage(String profileOnion, String contactHandle, String message) { var sendMessage = library.lookup>("c_SendMessage"); // ignore: non_constant_identifier_names final SendMessage = sendMessage.asFunction(); final u1 = profileOnion.toNativeUtf8(); final u2 = contactHandle.toNativeUtf8(); final u3 = message.toNativeUtf8(); SendMessage(u1, u1.length, u2, u2.length, u3, u3.length); } @override // ignore: non_constant_identifier_names void SendInvitation(String profileOnion, String contactHandle, String target) { var sendInvitation = library.lookup>("c_SendInvitation"); // ignore: non_constant_identifier_names final SendInvitation = sendInvitation.asFunction(); final u1 = profileOnion.toNativeUtf8(); final u2 = contactHandle.toNativeUtf8(); final u3 = target.toNativeUtf8(); SendInvitation(u1, u1.length, u2, u2.length, u3, u3.length); } @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 void ImportBundle(String profileOnion, String bundle) { var importBundle = library.lookup>("c_ImportBundle"); // ignore: non_constant_identifier_names final ImportBundle = importBundle.asFunction(); final u1 = profileOnion.toNativeUtf8(); final u2 = bundle.toNativeUtf8(); ImportBundle(u1, u1.length, u2, u2.length); } @override // ignore: non_constant_identifier_names void SetGroupAttribute(String profileOnion, String groupHandle, String key, String value) { var setGroupAttribute = library.lookup>("c_SetGroupAttribute"); // ignore: non_constant_identifier_names final SetGroupAttribute = setGroupAttribute.asFunction(); final u1 = profileOnion.toNativeUtf8(); final u2 = groupHandle.toNativeUtf8(); final u3 = key.toNativeUtf8(); final u4 = value.toNativeUtf8(); SetGroupAttribute(u1, u1.length, u2, u2.length, u3, u3.length, u4, u4.length); } @override // ignore: non_constant_identifier_names void RejectInvite(String profileOnion, String groupHandle) { var rejectInvite = library.lookup>("c_RejectInvite"); // ignore: non_constant_identifier_names final RejectInvite = rejectInvite.asFunction(); final u1 = profileOnion.toNativeUtf8(); final u2 = groupHandle.toNativeUtf8(); RejectInvite(u1, u1.length, u2, u2.length); } @override void CreateGroup(String profileOnion, String server, String groupName) { var createGroup = library.lookup>("c_CreateGroup"); // ignore: non_constant_identifier_names final CreateGroup = createGroup.asFunction(); final u1 = profileOnion.toNativeUtf8(); final u2 = server.toNativeUtf8(); final u3 = groupName.toNativeUtf8(); CreateGroup(u1, u1.length, u2, u2.length, u3, u3.length); } @override // ignore: non_constant_identifier_names void LeaveConversation(String profileOnion, String handle) { var leaveConversation = library.lookup>("c_LeaveConversation"); // ignore: non_constant_identifier_names final LeaveConversation = leaveConversation.asFunction(); final u1 = profileOnion.toNativeUtf8(); final u2 = handle.toNativeUtf8(); LeaveConversation(u1, u1.length, u2, u2.length); } @override // ignore: non_constant_identifier_names void LeaveGroup(String profileOnion, String groupHandle) { var leaveGroup = library.lookup>("c_LeaveGroup"); // ignore: non_constant_identifier_names final LeaveGroup = leaveGroup.asFunction(); final u1 = profileOnion.toNativeUtf8(); final u2 = groupHandle.toNativeUtf8(); LeaveGroup(u1, u1.length, u2, u2.length); } @override void UpdateMessageFlags(String profile, String handle, int index, int flags) { var updateMessageFlagsC = library.lookup>("c_UpdateMessageFlags"); // ignore: non_constant_identifier_names final updateMessageFlags = updateMessageFlagsC.asFunction(); final utf8profile = profile.toNativeUtf8(); final utf8handle = handle.toNativeUtf8(); updateMessageFlags(utf8profile, utf8profile.length, utf8handle, utf8handle.length, index, flags); } @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); } @override Future Shutdown() async { var shutdown = library.lookup>("c_ShutdownCwtch"); // ignore: non_constant_identifier_names // Shutdown Cwtch + Tor... final Shutdown = shutdown.asFunction(); Shutdown(); // Kill our Isolate cwtchIsolate.kill(priority: Isolate.immediate); print("Isolate killed"); _receivePort.close(); print("Receive Port Closed"); } }