#include #include #include #include #include #include #include #include // For string streams #include #include // For std::system #include // For std::runtime_error #ifdef _WIN32 #include // For ShellExecuteW #include // For ShellExecuteW #include // For wstring_convert #elif defined(__APPLE__) || defined(__linux__) #include // For fork, exec #include // For waitpid #endif #include "bella_sdk/bella_engine.h" #include "dl_core/dl_fs.h" using namespace dl; using namespace dl::bella_sdk; std::string initializeGlobalLicense(); std::string initializeGlobalThirdPsrtyLicences(); //std::atomic heartbeat_state (true); std::atomic connection_state (false); std::atomic abort_state (false); std::atomic server (false); std::string get_pubkey_from_srv(std::string server_address, uint16_t publickey_port); void client_thread( std::string server_pkey, std::string client_pkey, std::string client_skey, std::string server_address, uint16_t command_port); void server_thread( std::string server_skey, uint16_t command_port, bool test_render, Engine engine); void openFileWithDefaultProgram(const std::string& filePath); bool ends_with_suffix(const std::string& str, const std::string& suffix); void pkey_server(const std::string& pub_key, uint16_t publickey_port); struct MyEngineObserver : public EngineObserver { public: void onStarted(String pass) override { logInfo("Started pass %s", pass.buf()); } void onStatus(String pass, String status) override { logInfo("%s [%s]", status.buf(), pass.buf()); } void onProgress(String pass, Progress progress) override { std::cout << progress.toString().buf() << std::endl; setString(new std::string(progress.toString().buf())); logInfo("%s [%s]", progress.toString().buf(), pass.buf()); } void onError(String pass, String msg) override { logError("%s [%s]", msg.buf(), pass.buf()); } void onStopped(String pass) override { logInfo("Stopped %s", pass.buf()); } std::string getProgress() const { // Add this function std::string* currentProgress = progressPtr.load(); if (currentProgress) { return *currentProgress; // Return a copy of the string } else { return ""; // Or some default value if no progress yet } } ~MyEngineObserver() { setString(nullptr); } private: std::atomic progressPtr{nullptr}; void setString(std::string* newStatus) { std::string* oldStatus = progressPtr.exchange(newStatus); delete oldStatus; } }; void heartbeat_thread( std::string server_pkey, //CLIENT std::string server_skey, //SERVER std::string client_pkey, //CLIENT std::string client_skey, //CLIENT bool is_server, //BOTH std::string server_address, //CLIENT uint16_t heartbeat_port ) { //BOTH zmq::context_t ctx; zmq::socket_t heartbeat_sock; //top scope if(is_server) { heartbeat_sock = zmq::socket_t(ctx, zmq::socket_type::rep); heartbeat_sock.set(zmq::sockopt::curve_server, true); heartbeat_sock.set(zmq::sockopt::curve_secretkey, server_skey); std::string url = "tcp://*:" + std::to_string(heartbeat_port); heartbeat_sock.bind(url); while(true) { //Start polling heartbeats once client connects if (connection_state == true) { zmq::pollitem_t response_item = { heartbeat_sock, 0, ZMQ_POLLIN, 0 }; zmq::poll(&response_item, 1, 5000); // Wait for response with timeout if (response_item.revents & ZMQ_POLLIN) { //heartbeat zmq::message_t message; //ZIN<<< heartbeat_sock.recv(message, zmq::recv_flags::none); //ZOUT>>> heartbeat_sock.send(zmq::message_t("ACK"), zmq::send_flags::dontwait); // No block } else { //timeout std::cout << "Bella Client Lost" << std::endl; connection_state = false; } } std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } else { zmq::socket_t heartbeat_sock (ctx, zmq::socket_type::req); heartbeat_sock.set(zmq::sockopt::curve_serverkey, server_pkey); heartbeat_sock.set(zmq::sockopt::curve_publickey, client_pkey); heartbeat_sock.set(zmq::sockopt::curve_secretkey, client_skey); heartbeat_sock.set(zmq::sockopt::linger, 1); // Close immediately on disconnect std::string url = "tcp://" + server_address + ":" +std::to_string(heartbeat_port); heartbeat_sock.connect(url); //int heartbeat_count = 0; //std::vector items = {}; while (true) { if(abort_state.load()==true) { // Check for other threads abort break; } std::this_thread::sleep_for(std::chrono::milliseconds(100)); if(connection_state == true) { heartbeat_sock.send(zmq::message_t("ACK"), zmq::send_flags::none); // Wait for response (poll for ZMQ_POLLIN) zmq::pollitem_t response_item = { heartbeat_sock, 0, ZMQ_POLLIN, 0 }; zmq::poll(&response_item, 1, 5000); // Wait for response with timeout if (response_item.revents & ZMQ_POLLIN) { zmq::message_t msg_response; heartbeat_sock.recv(msg_response, zmq::recv_flags::none); //std::cout << "Heartbeat Response: " << std::endl; } else { std::cout << "Bella Server is unavailable" << std::endl; connection_state = false; break; } } } } heartbeat_sock.close(); ctx.close(); } #include "dl_core/dl_main.inl" int DL_main(Args& args) { const size_t chunk_size = 65536; std::string server_address = "localhost"; uint16_t command_port = 5797; uint16_t heartbeat_port = 5798; uint16_t publickey_port = 5799; bool test_render = false; /*logBanner("Bella Engine SDK (version: %s, build date: %llu)", bellaSdkVersion().toString().buf(), bellaSdkBuildDate() );*/ args.add("sa", "serverAddress", "", "Bella render server ip address"); args.add("cp", "commandPort", "", "tcp port for zmq server socket for commands"); args.add("hp", "heartbeatPort", "", "tcp port for zmq server socket for heartbeats"); args.add("pp", "publickeyPort", "", "tcp port for zmq server socket for server pubkey"); args.add("s", "server", "", "turn on server mode"); args.add("tr", "testRender", "", "force res to 100x100"); args.add("tp", "thirdparty", "", "prints third party licenses"); args.add("li", "licenseinfo", "", "prints license info"); if (args.versionReqested()) { printf("%s", bellaSdkVersion().toString().buf()); return 0; } if (args.helpRequested()) { printf("%s", args.help("SDK Test", fs::exePath(), bellaSdkVersion().toString()).buf()); return 0; } if (args.have("--licenseinfo")) { std::cout << initializeGlobalLicense() << std::endl; return 0; } if (args.have("--thirdparty")) { std::cout << initializeGlobalThirdPsrtyLicences() << std::endl; return 0; } // Turn on server mode if (args.have("--server")) { server=true; } if (args.have("--testRender")) { test_render=true; } if (args.have("--serverAddress")) { //server_address = std::string(args.value("--serverAddress").buf()); server_address = args.value("--serverAddress").buf(); } if (args.have("--heartbeatPort")) { String argString = args.value("--heartbeatPort"); uint16_t u16; if (argString.parse(u16)) { heartbeat_port = u16; } else { std::cerr << "invalid --heartbeatPort" << argString << std::endl; } } if (args.have("--commandPort")) { String argString = args.value("--commandPort"); uint16_t u16; if (argString.parse(u16)) { command_port = u16; } else { std::cerr << "invalid --commandPort" << argString << std::endl; } } if (args.have("--publickeyPort")) { String argString = args.value("--publickeyPort"); uint16_t u16; if (argString.parse(u16)) { publickey_port = u16; } else { std::cerr << "invalid --commandPort" << argString << std::endl; } } Engine engine; engine.scene().loadDefs(); // Generate brand new keypair on launch // [TODO] Add client side public key fingerprinting for added security if(server.load()) { std::cout << "BellaTUI server started ..." << std::endl; char server_skey[41] = { 0 }; char server_pkey[41] = { 0 }; if ( zmq_curve_keypair(&server_pkey[0], &server_skey[0])) { // 1 is fail std::cout << "\ncurve keypair gen failed."; exit(EXIT_FAILURE); } std::thread server_t(server_thread, server_skey, command_port, test_render, engine); ///std::thread heartbeat_t(heartbeat_thread, server_skey, server.load(), 5555); std::thread heartbeat_t(heartbeat_thread, //function "", //NA Public server key server_skey, //Secret servery key "", //NA Public client key "", //NA Secret client key true, //is server "", //FQDN or ip address of server heartbeat_port); //bind port // while(true) { // awaiting new client loop std::cout << "Awaiting new client ..." << std::endl; pkey_server(server_pkey, publickey_port); // blocking wait client to get public key std::cout << "Client connected" << std::endl; connection_state = true; while(true) { // inner loop if (connection_state.load()==false) { std::cout << "Client connectiono dead" << std::endl; break; // Go back to awaiting client } std::this_thread::sleep_for(std::chrono::milliseconds(10)); } } //abort_state==true; server_t.join(); heartbeat_t.join(); return 0; } else { //Client char client_skey[41] = { 0 }; char client_pkey[41] = { 0 }; if ( zmq_curve_keypair(&client_pkey[0], &client_skey[0])) { // 1 is fail std::cout << "\ncurve keypair gen failed."; exit(EXIT_FAILURE); } std::string server_pkey = get_pubkey_from_srv(server_address, publickey_port); std::string client_pkey_str(client_pkey); std::string client_skey_str(client_skey); // Multithreaded std::thread command_t( client_thread, server_pkey, client_pkey_str, client_skey_str, server_address, command_port); //std::thread heartbeat_t(heartbeat_thread, server_pkey, client_pkey_str, client_skey_str); std::thread heartbeat_t(heartbeat_thread, //function server_pkey, //Public server key "", //NA Secret server key client_pkey_str, //Public client key client_skey_str, //Secret client key false, //is server server_address, //Server FQDN or ip address heartbeat_port); //connect port while (true) { /*if (!heartbeat_state.load()) { std::cout << "Dead" << std::endl; abort_state==true; break; }*/ if (connection_state.load() == false) { std::cout << "Dead2" << std::endl; abort_state==true; break; } std::this_thread::sleep_for(std::chrono::milliseconds(500)); } } } std::string get_pubkey_from_srv(std::string server_address, uint16_t publickey_port) { // No authentication is used, server will give out pubkey to anybody // Could use a unique message but since socket is unencrypted this provides // no protection. In main loop we establish an encrypted connection with the server // now that we have the pubkey and in combo with the client_secret_key we can // be secure. 0MQ uses PFS perfect forward security, because this initial // back and forth is extended with behind the scenes new keypairs taken care of by // 0MQ after we establish our intitial encrypted socket zmq::context_t ctx; zmq::socket_t pubkey_sock(ctx, zmq::socket_type::req); std::string url = "tcp://" + server_address + ":" + std::to_string(publickey_port); pubkey_sock.connect(url); zmq::message_t z_out(std::string("Bellarender123")); try { zmq::send_result_t send_result = pubkey_sock.send(z_out, zmq::send_flags::none); } catch (const zmq::error_t& e) { std::cout << "ERROR" << std::endl; } std::cout << "\nbellatui connecting to " << server_address << " ..." << std::endl; zmq::message_t z_in; pubkey_sock.recv(z_in); std::string pub_key = z_in.to_string(); pubkey_sock.close(); ctx.close(); std::cout << "Connection to " << server_address << " successful" << std::endl; connection_state = true; return pub_key; } void client_thread( std::string server_pkey, std::string client_pkey, std::string client_skey, std::string server_address, uint16_t command_port ) { //std::cout << "client thread: " << server_address << " " << command_port << std::endl; const size_t chunk_size = 65536; zmq::context_t ctx; zmq::socket_t command_sock (ctx, zmq::socket_type::req); //command_sock.set(zmq::sockopt::sndtimeo, 10000); //command_sock.set(zmq::sockopt::rcvtimeo, 10000); command_sock.set(zmq::sockopt::curve_serverkey, server_pkey); command_sock.set(zmq::sockopt::curve_publickey, client_pkey); command_sock.set(zmq::sockopt::curve_secretkey, client_skey); command_sock.set(zmq::sockopt::linger, 1); // Close immediately on disconnect //std::string url = "tcp://" + server_address+ ":" + std::to_string(command_port); std::string url = "tcp://" + server_address + ":" + std::to_string(command_port); //std::cout << "client thread " << url << std::endl; command_sock.connect(url); std::string input; while (true) { if(abort_state.load()==true) { break; } std::getline(std::cin, input); std::stringstream ss(input); std::string arg; std::vector args; while (ss >> arg) { args.push_back(arg); } // Sanity checks on input before sending to server int num_args = args.size(); std::string command; if (num_args > 0) { command = args[0]; if ( command == "send") { if(num_args == 1) { std::cout << "Please provide a .bsz file" << std::endl; continue; } if(!ends_with_suffix(args[1],".bsz")) { std::cout << "Only .bsz files can be sent" << std::endl; continue; } if(!dl::fs::exists(args[1].data())) { std::cout << args[1] << " cannot be found" << std::endl; continue; } std::cout << "Sending:" << args[1] << std::endl; } else if (command == "get") { if(num_args == 1) { std::cout << "Please provide image filename" << std::endl; continue; } } else if (command == "exit") { ; } else if (command == "render") { std::string compoundArg; if(num_args > 1) { for (size_t i = 1; i < args.size(); ++i) { compoundArg += args[i]; if (i < args.size() - 1) { compoundArg += " "; // Add spaces between arguments } } std::cout << compoundArg << std::endl; } } else if (command == "hello") { ; } else if (command == "stat") { ; } else if (command == "help") { std::cout << "\033[32msend file.bsz\033[0m upload bella scene to server\n"; std::cout << "\033[32mrender\033[0m start render on server\n"; std::cout << "\033[32mget file.png\033[0m download png from server\n"; std::cout << "\033[32mstop\033[0m stop render on server\n"; std::cout << "\033[32mstat\033[0m display progress\n"; continue; } else if (command == "stop") { ; } else { std::cout << "unknown" << std::endl; continue; } } // Sanity check input complete // Push to server over encrypted socket zmq::message_t server_response; zmq::message_t msg_command(command); //>>>ZOUT command_sock.send(msg_command, zmq::send_flags::none); //SEND //std::cout << "Sent: " << input.data() << std::endl; //ZIN<<< command_sock.recv(server_response, zmq::recv_flags::none); //RECV std::string response_str(static_cast(server_response.data()), server_response.size()-1); std::string response_str2(static_cast(server_response.data()), server_response.size()); if(response_str=="RDY") { // Server acknowledges readiness for multi message commands std::cout << "Server Readiness: " << response_str << std::endl; if(command == "exit") { exit(0); // RENDER } else if(command == "render") { //>>>ZOUT command_sock.send(zmq::message_t("render"), zmq::send_flags::none); //ZIN<<< command_sock.recv(server_response, zmq::recv_flags::none); } else if(command == "stat") { //>>>ZOUT command_sock.send(zmq::message_t("stat"), zmq::send_flags::none); //ZIN<<< command_sock.recv(server_response, zmq::recv_flags::none); // GET } else if(command == "get") { std::ofstream output_file(args[1], std::ios::binary); // Open file in binary mode if (!output_file.is_open()) { std::cerr << "Error opening file for writing" << std::endl; std::cout << "ERR" << std::endl; continue; // Don't bother server } else { while (true) { //>>>ZOUT command_sock.send(zmq::message_t("GO"), zmq::send_flags::none); zmq::message_t recv_data; //ZIN<<< command_sock.recv(recv_data, zmq::recv_flags::none); // data transfer // inline messaging with data, breaks to exit loop if (recv_data.size() < 8) { std::string recv_string(static_cast(recv_data.data()), recv_data.size()-1); //std::string recv_string = recv_data.to_string(); if (recv_string == "EOF") { std::cout << "EOF" << std::endl; break; // End of file } else if(recv_string == "ERR") { //LIKELY ERR\0 from client, can't find file std::cout << "ERR client read ACK" << std::endl; break; // Err } else { std::cout << "HUH" << recv_string << std::endl; break; } } // by reaching this point we assume binary data ( even 8 bytes will reach here ) std::cout << "\033[32m.\033[0m"; output_file.write(static_cast(recv_data.data()), recv_data.size()); } output_file.close(); try { openFileWithDefaultProgram(args[1]); // Replace with your file path std::cout << "File opened successfully." << std::endl; } catch (const std::runtime_error& e) { std::cerr << "Error: " << e.what() << std::endl; } } // SEND } else if(command == "send") { std::string read_file = args[1]; std::cout << "sending\n"; std::ifstream binaryInputFile; binaryInputFile.open(read_file, std::ios::binary);// for reading if (!binaryInputFile.is_open()) { std::cerr << "Error opening file for read" << std::endl; //>>>ZOUT command_sock.send(zmq::message_t("ERR"), zmq::send_flags::none); ///ZIN<<< command_sock.recv(server_response, zmq::recv_flags::none); } else { std::vector send_buffer(chunk_size); std::streamsize bytes_read_in_chunk; while (true) { binaryInputFile.read(send_buffer.data(), chunk_size); // read the file into the buffer bytes_read_in_chunk = binaryInputFile.gcount(); // Actual bytes read if(bytes_read_in_chunk > 0){ std::cout << "\033[32m.\033[0m"; zmq::message_t message(send_buffer.data(), bytes_read_in_chunk); //>>>ZOUT command_sock.send(message, zmq::send_flags::none); //ZIN<<< command_sock.recv(server_response, zmq::recv_flags::none); } else { std::cout << "\n"; break; } } //<<>> command_sock.recv(server_response, zmq::recv_flags::none); } } } else { std::cout << "Server response: \033[32m" << response_str2 << "\033[0m" << std::endl; } } command_sock.close(); ctx.close(); } void server_thread( std::string server_skey, uint16_t command_port, bool test_render, Engine engine) { MyEngineObserver engineObserver; engine.subscribe(&engineObserver); zmq::context_t ctx; zmq::socket_t command_sock(ctx, zmq::socket_type::rep); //command_sock.set(zmq::sockopt::sndtimeo, 10000); //command_sock.set(zmq::sockopt::rcvtimeo, 10000); command_sock.set(zmq::sockopt::curve_server, true); command_sock.set(zmq::sockopt::curve_secretkey, server_skey); //command_sock.set(zmq::sockopt::linger, 100); // Close immediately on disconnect std::string url = "tcp://*:" + std::to_string(command_port); command_sock.bind(url); zmq::message_t client_response; try { std::string write_file = "./oomer.bsz"; std::string read_file = "./oomer.png"; const size_t chunk_size = 65536; std::vector sftp_buffer(chunk_size); // Buffer to hold each chunk std::ofstream binaryOutputFile;// for writing std::ifstream binaryInputFile;// for reading while (true) { zmq::message_t msg_command; //ZIN<<< command_sock.recv(msg_command, zmq::recv_flags::none); std::string client_command = msg_command.to_string(); std::cout << "\033[32mCommand: " << client_command << "\033[0m"<< std::endl; if(client_command == "hello"){ std::cout << "bye" << std::endl; //>>>ZOUT command_sock.send(zmq::message_t("bye"), zmq::send_flags::none); } else if (client_command == "exit") { std::cout << "Client disconnecting..." << std::endl; //>>>ZOUT command_sock.send(zmq::message_t("RDY"), zmq::send_flags::none); connection_state = false; //<< // RENDER } else if (client_command == "render") { std::cout << "start render" << std::endl; if(test_render) { engine.scene().camera()["resolution"]= Vec2 {100, 100}; } engine.start(); //>>>ZOUT command_sock.send(zmq::message_t("render started...type stat to get progress"), zmq::send_flags::none); } else if (client_command == "stop") { std::cout << "stop render" << std::endl; engine.stop(); //>>>ZOUT command_sock.send(zmq::message_t("render stopped"), zmq::send_flags::none); //GET } else if (client_command == "get") { //REP mode std::string read_file = "./oomer.png"; std::cout << "Executing get command\n"; std::ifstream binaryInputFile; binaryInputFile.open(read_file, std::ios::binary);// for reading if (!binaryInputFile.is_open()) { std::cerr << "Error opening file for read" << std::endl; //>>>ZOUT command_sock.send(zmq::message_t("ERR"), zmq::send_flags::none); } else { //>>>ZOUT command_sock.send(zmq::message_t("RDY"), zmq::send_flags::none); std::vector send_buffer(chunk_size); std::streamsize bytes_read_in_chunk; while (true) { zmq::message_t z_in; //ZIN command_sock.recv(z_in); // Block until zGo, or any message binaryInputFile.read(send_buffer.data(), chunk_size); // read the file into the buffer bytes_read_in_chunk = binaryInputFile.gcount(); // Actual bytes read if(bytes_read_in_chunk > 0){ std::cout << bytes_read_in_chunk << std::endl; zmq::message_t message(send_buffer.data(), bytes_read_in_chunk); //ZOUT command_sock.send(message, zmq::send_flags::none); } else { //ZOUT command_sock.send(zmq::message_t("EOF"), zmq::send_flags::none); std::cout << "EOF" << std::endl; break; // Exit when 0 bytes read } } } } else if (client_command == "stat") { std::string currentProgress = engineObserver.getProgress(); if (!currentProgress.empty()) { std::cout << "Current Progress: " << currentProgress << std::endl; command_sock.send(zmq::message_t(currentProgress), zmq::send_flags::none); } else { command_sock.send(zmq::message_t("ACK"), zmq::send_flags::none); } } else if (client_command == "send") { std::ofstream output_file("oomer.bsz", std::ios::binary); // Open file in binary mode if (!output_file.is_open()) { std::cerr << "Error opening file for writing" << std::endl; //>>>ZOUT command_sock.send(zmq::message_t("ERR"), zmq::send_flags::none); } else { // File handle open and ready //>>>ZOUT command_sock.send(zmq::message_t("RDY"), zmq::send_flags::none); while (true) { zmq::message_t recv_data; //ZIN<<< command_sock.recv(recv_data, zmq::recv_flags::none); if(recv_data.size() < 8) { // data and signals sent on same socket // Allow for signals up to 8 bytes, EOF, ERR // messages are null terminated requiring -1 std::string response_str(static_cast(recv_data.data()), recv_data.size()-1); if (response_str=="EOF") { //>>>ZOUT command_sock.send(zmq::message_t("ACK"), zmq::send_flags::none); break; // End of file } else if(response_str=="ERR") { std::cout << "ERR on client" << std::endl; //>>>ZOUT command_sock.send(zmq::message_t("ACK"), zmq::send_flags::none); break; // End of file } } // File write output_file.write(static_cast(recv_data.data()), recv_data.size()); //>>ZOUT command_sock.send(zmq::message_t("ACK"), zmq::send_flags::none); } output_file.close(); std::cout << "\033[32mClient uploaded .bsz successfully saved\033[0m" << std::endl; engine.scene().read("oomer.bsz"); engine.scene().beautyPass()["outputExt"] = ".png"; engine.scene().beautyPass()["outputName"] = ""; engine.scene().beautyPass()["overridePath"] = bella_sdk::Node(); } } else { // A unknown REQ sent, acknowledge because req-rep pattern is blocking //>>ZOUT command_sock.send(zmq::message_t("ACK"), zmq::send_flags::none); } std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } catch (const zmq::error_t& e) { // Handle ZMQ-specific exceptions std::cerr << "ZMQ error: " << e.what() << std::endl; ctx.close(); command_sock.close(); //Potentially close sockets, clean up etc. } catch (const std::exception& e) { // Handle standard exceptions (e.g., std::bad_alloc) std::cerr << "Standard exception: " << e.what() << std::endl; } catch (...) { // Catch any other exceptions std::cerr << "Unknown exception caught." << std::endl; } command_sock.close(); ctx.close(); } void openFileWithDefaultProgram(const std::string& filePath) { #ifdef _WIN32 std::wstring_convert> converter; std::wstring wideFilePath = converter.from_bytes(filePath); HINSTANCE result = ShellExecuteW(nullptr, nullptr, wideFilePath.c_str(), nullptr, nullptr, SW_SHOW); if ((intptr_t)result <= 32) { throw std::runtime_error("Failed to open file: ShellExecuteW returned " + std::to_string((intptr_t)result)); } #elif defined(__APPLE__) pid_t pid = fork(); if (pid == 0) { // Child process execl("/usr/bin/open", "open", filePath.c_str(), nullptr); // If execl fails: std::cerr << "Failed to open file: execl failed" << std::endl; exit(1); // Exit child process on error } else if (pid > 0) { // Parent process int status; waitpid(pid, &status, 0); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { throw std::runtime_error("Failed to open file: open command failed"); } } else { throw std::runtime_error("Failed to open file: fork failed"); } #elif defined(__linux__) pid_t pid = fork(); if (pid == 0) { // Child process execl("/usr/bin/xdg-open", "xdg-open", filePath.c_str(), nullptr); // If execl fails: std::cerr << "Failed to open file: execl failed" << std::endl; exit(1); // Exit child process on error } else if (pid > 0) { // Parent process int status; waitpid(pid, &status, 0); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { throw std::runtime_error("Failed to open file: xdg-open command failed"); } } else { throw std::runtime_error("Failed to open file: fork failed"); } #else // Fallback: Use system, but this is less reliable and secure. std::string command = "open \"" + filePath + "\""; // May need to adapt quoting int result = std::system(command.c_str()); if (result != 0) { throw std::runtime_error("Failed to open file: system command failed"); } #endif } bool ends_with_suffix(const std::string& str, const std::string& suffix) { if (str.length() >= 4) { return str.substr(str.length() - 4) == suffix; } return false; } // Blocking zmq rep socket to pass server_public_key void pkey_server(const std::string& pub_key, uint16_t publickey_port) { zmq::context_t ctx; zmq::socket_t sock(ctx, zmq::socket_type::rep); std::string url = "tcp://*:" + std::to_string(publickey_port); sock.bind(url); zmq::message_t z_in; //ZIN<<< sock.recv(z_in); if (z_in.to_string().compare("Bellarender123") == 0) { zmq::message_t z_out(pub_key); //ZOUT>>> sock.send(z_out, zmq::send_flags::none); connection_state = true; } sock.close(); ctx.close(); } std::string initializeGlobalLicense() { return R"( bellatui Copyright (c) 2025 Harvey Fong Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.)"; } std::string initializeGlobalThirdPsrtyLicences() { return R"( ==== Bella SDK (Software Development Kit) Copyright Diffuse Logic SCP, all rights reserved. Permission is hereby granted to any person obtaining a copy of this software (the "Software"), to use, copy, publish, distribute, sublicense, and/or sell copies of the Software. THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF MERCHANTABILITY ARE HEREBY DISCLAIMED. ==== CppZMQ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ==== libsodium ISC License Copyright (c) 2013-2025 Frank Denis Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ===== libzmq is free software; you can redistribute it and/or modify it under the terms of the Mozilla Public License Version 2.0.)"; }