mirror of
https://github.com/webui-dev/webui
synced 2025-03-28 21:13:17 +00:00
11914 lines
381 KiB
C
11914 lines
381 KiB
C
/*
|
|
WebUI Library
|
|
https://webui.me
|
|
https://github.com/webui-dev/webui
|
|
Copyright (c) 2020-2024 Hassan Draga.
|
|
Licensed under MIT License.
|
|
All rights reserved.
|
|
Canada.
|
|
*/
|
|
|
|
// 64Mb max dynamic memory allocation
|
|
#define WEBUI_MAX_BUF (64000000)
|
|
|
|
// -- Includes ------------------------
|
|
#include "../bridge/webui_bridge.h" // WebUI Bridge (JavaScript)
|
|
#include "webui.h" // WebUI Header
|
|
|
|
// -- WebView -------------------------
|
|
#ifdef _WIN32
|
|
#include "webview/WebView2.h"
|
|
#elif __linux__
|
|
#include <dlfcn.h>
|
|
#else
|
|
// ...
|
|
#endif
|
|
|
|
// -- Third-party ---------------------
|
|
#ifdef WEBUI_TLS
|
|
#ifdef _WIN32
|
|
#ifndef WIN32_LEAN_AND_MEAN
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#endif
|
|
#endif
|
|
// OpenSSL
|
|
#include <openssl/bio.h>
|
|
#include <openssl/evp.h>
|
|
#include <openssl/pem.h>
|
|
#include <openssl/rsa.h>
|
|
#include <openssl/ssl.h>
|
|
#include <openssl/x509.h>
|
|
#ifdef WEBUI_LOG
|
|
#include <openssl/err.h>
|
|
#endif
|
|
#endif
|
|
#define MG_BUF_LEN (WEBUI_MAX_BUF)
|
|
#include "civetweb/civetweb.h"
|
|
|
|
// -- Disable Non-critical warnings ---
|
|
#ifdef _MSC_VER
|
|
#pragma warning(push, 0)
|
|
// Disable spectre warnings
|
|
#pragma warning(disable: 5045)
|
|
#elif defined(__clang__)
|
|
// ...
|
|
#elif defined(__GNUC__)
|
|
// ...
|
|
#endif
|
|
|
|
// -- Defines -------------------------
|
|
#define WEBUI_SIGNATURE 0xDD // All packets should start with this 8bit
|
|
#define WEBUI_CMD_JS 0xFE // Command: JavaScript result in frontend
|
|
#define WEBUI_CMD_JS_QUICK 0xFD // Command: JavaScript result in frontend
|
|
#define WEBUI_CMD_CLICK 0xFC // Command: Click event
|
|
#define WEBUI_CMD_NAVIGATION 0xFB // Command: Frontend navigation
|
|
#define WEBUI_CMD_CLOSE 0xFA // Command: Close window
|
|
#define WEBUI_CMD_CALL_FUNC 0xF9 // Command: Backend function call
|
|
#define WEBUI_CMD_SEND_RAW 0xF8 // Command: Send raw binary data to the UI
|
|
#define WEBUI_CMD_ADD_ID 0xF7 // Command: Add new bind ID
|
|
#define WEBUI_CMD_MULTI 0xF6 // Command: Multi packet data
|
|
#define WEBUI_CMD_CHECK_TK 0xF5 // Command: Check validity of a client's token
|
|
#define WEBUI_PROTOCOL_SIZE (8) // Protocol header size in bytes
|
|
#define WEBUI_PROTOCOL_SIGN (0) // Protocol byte position: Signature (1 Byte)
|
|
#define WEBUI_PROTOCOL_TOKEN (1) // Protocol byte position: Token (4 Bytes)
|
|
#define WEBUI_PROTOCOL_ID (5) // Protocol byte position: ID (2 Bytes)
|
|
#define WEBUI_PROTOCOL_CMD (7) // Protocol byte position: Command (1 Byte)
|
|
#define WEBUI_PROTOCOL_DATA (8) // Protocol byte position: Data (n Byte)
|
|
#define WEBUI_MUTEX_NONE (0) // Check boolen mutex without update
|
|
#define WEBUI_MUTEX_TRUE (1) // Check boolen mutex and update to true
|
|
#define WEBUI_MUTEX_FALSE (2) // Check boolen mutex and update to false
|
|
#define WEBUI_WS_DATA (1) // Internal WS Event (Data received)
|
|
#define WEBUI_WS_OPEN (2) // Internal WS Event (New connection)
|
|
#define WEBUI_WS_CLOSE (3) // Internal WS Event (Connection close)
|
|
#define WEBUI_SHOW_HTML (1) // Show window using HTML
|
|
#define WEBUI_SHOW_FILE (2) // Show window using a local file
|
|
#define WEBUI_SHOW_URL (3) // Show window using a URL
|
|
#define WEBUI_SHOW_FOLDER (4) // Show window using a Folder
|
|
#define WEBUI_MIN_PORT (10000) // Minimum socket port
|
|
#define WEBUI_MAX_PORT (65500) // Should be less than 65535
|
|
#define WEBUI_STDOUT_BUF (10240) // Command STDOUT output buffer size
|
|
#define WEBUI_DEFAULT_PATH "." // Default root path
|
|
#define WEBUI_DEF_TIMEOUT (15) // Default startup timeout in seconds
|
|
#define WEBUI_RELOAD_TIMEOUT (1500) // Default reload page timeout in milliseconds
|
|
#define WEBUI_MAX_TIMEOUT (60) // Maximum startup timeout in seconds the user can set
|
|
#define WEBUI_MIN_WIDTH (100) // Minimal window width
|
|
#define WEBUI_MIN_HEIGHT (100) // Minimal window height
|
|
#define WEBUI_MAX_WIDTH (3840) // Maximal window width (4K Monitor)
|
|
#define WEBUI_MAX_HEIGHT (2160) // Maximal window height (4K Monitor)
|
|
#define WEBUI_MIN_X (0) // Minimal window X
|
|
#define WEBUI_MIN_Y (0) // Minimal window Y
|
|
#define WEBUI_MAX_X (3000) // Maximal window X (4K Monitor)
|
|
#define WEBUI_MAX_Y (1800) // Maximal window Y (4K Monitor)
|
|
#define WEBUI_PROFILE_NAME "WebUI" // Default browser profile name (Used only for Firefox)
|
|
#define WEBUI_COOKIES_LEN (32) // Authentification cookies len
|
|
#define WEBUI_COOKIES_BUF (64) // Authentification cookies buffer size
|
|
|
|
#ifdef WEBUI_TLS
|
|
#define WEBUI_SECURE "TLS-Encryption"
|
|
#define WEBUI_SSL_SIZE (4096) // SSL Max PEM Size
|
|
#define WEBUI_SSL_EXPIRE (72 * 60 * 60) // SSL Expires (Integer)
|
|
#define WEBUI_SSL_EXPIRE_STR "259201" // SSL Expires (String)
|
|
#define WEBUI_HTTP_PROTOCOL "https://"
|
|
#define WEBUI_WS_PROTOCOL "wss://"
|
|
#else
|
|
#define WEBUI_SECURE "Non-Encrypted"
|
|
#define WEBUI_HTTP_PROTOCOL "http://"
|
|
#define WEBUI_WS_PROTOCOL "ws://"
|
|
#endif
|
|
|
|
#ifdef WEBUI_DYNAMIC
|
|
#define WEBUI_LIB_TYPE "Dynamic"
|
|
#else
|
|
#define WEBUI_LIB_TYPE "Static"
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
#define WEBUI_OS "Microsoft Windows"
|
|
#elif __APPLE__
|
|
#define WEBUI_OS "Apple macOS"
|
|
#else
|
|
#define WEBUI_OS "GNU/Linux"
|
|
#endif
|
|
|
|
// Mutex
|
|
#ifdef _WIN32
|
|
typedef CRITICAL_SECTION webui_mutex_t;
|
|
typedef CONDITION_VARIABLE webui_condition_t;
|
|
#else
|
|
typedef pthread_mutex_t webui_mutex_t;
|
|
typedef pthread_cond_t webui_condition_t;
|
|
#endif
|
|
|
|
// Compiler
|
|
#if defined(_MSC_VER)
|
|
#define WEBUI_COMPILER "MSVC"
|
|
#elif defined(__GNUC__) && !defined(__clang__)
|
|
#define WEBUI_COMPILER "GCC"
|
|
#elif defined(__clang__)
|
|
#define WEBUI_COMPILER "Clang"
|
|
#else
|
|
#define WEBUI_COMPILER "Unknown"
|
|
#endif
|
|
|
|
// Verbose Log
|
|
#ifdef WEBUI_LOG
|
|
#define WEBUI_LOG_VERBOSE
|
|
#endif
|
|
|
|
// Timer
|
|
typedef struct _webui_timer_t {
|
|
struct timespec start;
|
|
struct timespec now;
|
|
} _webui_timer_t;
|
|
|
|
// Event data
|
|
typedef struct webui_event_inf_t {
|
|
// Client
|
|
struct mg_connection* client;
|
|
size_t connection_id;
|
|
// Args and Response
|
|
char* event_data[WEBUI_MAX_ARG + 1]; // Event data (string | num | bool | raw)
|
|
size_t event_size[WEBUI_MAX_ARG + 1]; // Event data size (in bytes)
|
|
char* response; // Event response (string)
|
|
size_t count; // Event arguments count
|
|
bool done;
|
|
} webui_event_inf_t;
|
|
|
|
// WebView
|
|
#ifdef _WIN32
|
|
typedef struct _webui_wv_win32_t {
|
|
// Win32 WebView
|
|
ICoreWebView2Environment* webviewEnvironment;
|
|
ICoreWebView2Controller* webviewController;
|
|
ICoreWebView2* webviewWindow;
|
|
HWND hwnd;
|
|
void* titleChangedHandler;
|
|
void* titleChangedHandler_lpVtbl;
|
|
void* createWebViewEnvironmentHandler;
|
|
void* createWebViewEnvironmentHandler_lpVtbl;
|
|
void* createWebViewControllerHandler;
|
|
void* createWebViewControllerHandler_lpVtbl;
|
|
// WebUI Window
|
|
wchar_t* url;
|
|
bool navigate;
|
|
bool size;
|
|
bool position;
|
|
int width;
|
|
int height;
|
|
int x;
|
|
int y;
|
|
bool stop;
|
|
} _webui_wv_win32_t;
|
|
#elif __linux__
|
|
void* libgtk;
|
|
void* libwebkit;
|
|
// GTK Symbol Addresses
|
|
typedef void *(*gtk_init_func)(int *argc, char ***argv);
|
|
typedef void (*gtk_widget_show_all_func)(void *);
|
|
typedef void (*gtk_main_iteration_do_func)(int);
|
|
typedef int (*gtk_events_pending_func)(void);
|
|
typedef void (*gtk_container_add_func)(void *, void *);
|
|
typedef void *(*gtk_window_new_func)(int);
|
|
typedef void (*gtk_window_set_default_size_func)(void *, int, int);
|
|
typedef void (*gtk_window_set_title_func)(void *, const char *);
|
|
typedef void (*gtk_window_move_func)(void *, int, int);
|
|
typedef void (*gtk_window_close_func)(void *);
|
|
typedef void (*gtk_window_resize_func)(void *, int, int);
|
|
typedef void (*gtk_window_set_position_func)(void *, int);
|
|
typedef int (*g_idle_add_func)(int (*function)(void*), void*);
|
|
typedef void (*g_signal_connect_data_func)(void *, const char *,
|
|
void (*callback)(void), void *, void *, int);
|
|
gtk_init_func gtk_init = NULL;
|
|
gtk_widget_show_all_func gtk_widget_show_all = NULL;
|
|
gtk_main_iteration_do_func gtk_main_iteration_do = NULL;
|
|
gtk_events_pending_func gtk_events_pending = NULL;
|
|
gtk_container_add_func gtk_container_add = NULL;
|
|
gtk_window_new_func gtk_window_new = NULL;
|
|
gtk_window_set_default_size_func gtk_window_set_default_size = NULL;
|
|
gtk_window_set_title_func gtk_window_set_title = NULL;
|
|
gtk_window_move_func gtk_window_move = NULL;
|
|
gtk_window_close_func gtk_window_close = NULL;
|
|
gtk_window_resize_func gtk_window_resize = NULL;
|
|
gtk_window_set_position_func gtk_window_set_position = NULL;
|
|
g_signal_connect_data_func g_signal_connect_data = NULL;
|
|
g_idle_add_func g_idle_add = NULL;
|
|
// WebKit Symbol Addresses
|
|
typedef void *(*webkit_web_view_new_func)(void);
|
|
typedef void (*webkit_web_view_load_uri_func)(void *, const char *);
|
|
typedef const char *(*webkit_web_view_get_title_func)(void *);
|
|
webkit_web_view_new_func webkit_web_view_new = NULL;
|
|
webkit_web_view_load_uri_func webkit_web_view_load_uri = NULL;
|
|
webkit_web_view_get_title_func webkit_web_view_get_title = NULL;
|
|
|
|
typedef struct _webui_wv_linux_t {
|
|
// Linux WebView
|
|
void* gtk_win;
|
|
void* gtk_wv;
|
|
bool open;
|
|
// WebUI Window
|
|
char* url;
|
|
bool navigate;
|
|
bool size;
|
|
bool position;
|
|
unsigned int width;
|
|
unsigned int height;
|
|
unsigned int x;
|
|
unsigned int y;
|
|
bool stop;
|
|
} _webui_wv_linux_t;
|
|
#else
|
|
extern bool _webui_macos_wv_new(int index);
|
|
extern bool _webui_macos_wv_show(int index, const char* urlString, int x, int y, int width, int height);
|
|
extern bool _webui_macos_wv_close(int index);
|
|
extern bool _webui_macos_wv_set_position(int index, int x, int y);
|
|
extern bool _webui_macos_wv_set_size(int index, int width, int height);
|
|
extern bool _webui_macos_wv_navigate(int index, const char* urlString);
|
|
extern void _webui_macos_wv_process();
|
|
extern void _webui_macos_wv_stop();
|
|
extern void _webui_macos_wv_set_close_cb(void (*cb)(int index));
|
|
extern void _webui_macos_wv_new_thread_safe(int index);
|
|
|
|
typedef struct _webui_wv_macos_t {
|
|
// macOS WebView
|
|
int index;
|
|
// WebUI Window
|
|
char* url;
|
|
bool navigate;
|
|
bool size;
|
|
bool position;
|
|
unsigned int width;
|
|
unsigned int height;
|
|
unsigned int x;
|
|
unsigned int y;
|
|
bool stop;
|
|
} _webui_wv_macos_t;
|
|
#endif
|
|
|
|
// Window
|
|
typedef struct _webui_window_t {
|
|
// Client
|
|
size_t clients_count;
|
|
struct mg_connection* single_client; // Single client
|
|
bool single_client_token_check;
|
|
// Server
|
|
bool wait; // Let server thread wait more time for websocket
|
|
bool server_running; // Slow check
|
|
bool connected; // Fast check
|
|
size_t server_port;
|
|
char* url;
|
|
const char* html;
|
|
char* server_root_path;
|
|
#ifdef _WIN32
|
|
HANDLE server_thread;
|
|
#else
|
|
pthread_t server_thread;
|
|
#endif
|
|
// Window
|
|
uint32_t token;
|
|
size_t num; // Window number
|
|
const char* html_elements[WEBUI_MAX_IDS];
|
|
bool has_all_events;
|
|
void(*cb[WEBUI_MAX_IDS])(webui_event_t* e);
|
|
void(*cb_interface[WEBUI_MAX_IDS])(size_t, size_t, char* , size_t, size_t);
|
|
bool ws_block;
|
|
bool is_embedded_html;
|
|
bool is_closed;
|
|
size_t custom_server_port;
|
|
const char* icon;
|
|
const char* icon_type;
|
|
size_t current_browser;
|
|
char* browser_path;
|
|
bool custom_profile;
|
|
bool default_profile;
|
|
char* profile_path;
|
|
char* profile_name;
|
|
char* custom_parameters;
|
|
size_t runtime;
|
|
bool kiosk_mode;
|
|
bool disable_browser_high_contrast;
|
|
bool hide;
|
|
int width;
|
|
int height;
|
|
bool size_set;
|
|
int minimum_width;
|
|
int minimum_height;
|
|
bool minimum_size_set;
|
|
int x;
|
|
int y;
|
|
bool position_set;
|
|
size_t process_id;
|
|
const void*(*files_handler)(const char* filename, int* length);
|
|
const void*(*files_handler_window)(size_t window, const char* filename, int* length);
|
|
const void* file_handler_async_response;
|
|
int file_handler_async_len;
|
|
bool file_handler_async_done;
|
|
webui_event_inf_t* events[WEBUI_MAX_IDS];
|
|
size_t events_count;
|
|
bool is_public;
|
|
bool proxy_set;
|
|
char *proxy_server;
|
|
// WebView
|
|
bool allow_webview;
|
|
bool update_webview;
|
|
#ifdef _WIN32
|
|
_webui_wv_win32_t* webView;
|
|
#elif __linux__
|
|
_webui_wv_linux_t* webView;
|
|
#else
|
|
_webui_wv_macos_t* webView;
|
|
#endif
|
|
}
|
|
_webui_window_t;
|
|
|
|
// Core
|
|
typedef struct _webui_core_t {
|
|
struct {
|
|
bool show_wait_connection;
|
|
bool show_auto_js_inject;
|
|
bool ws_block;
|
|
bool folder_monitor;
|
|
bool multi_client;
|
|
bool use_cookies;
|
|
bool asynchronous_response;
|
|
} config;
|
|
struct mg_connection* clients[WEBUI_MAX_IDS];
|
|
size_t clients_win_num[WEBUI_MAX_IDS];
|
|
bool clients_token_check[WEBUI_MAX_IDS];
|
|
char* cookies[WEBUI_MAX_IDS];
|
|
bool cookies_single_set[WEBUI_MAX_IDS];
|
|
size_t servers;
|
|
size_t used_ports[WEBUI_MAX_IDS];
|
|
size_t startup_timeout;
|
|
size_t cb_count;
|
|
bool exit_now;
|
|
bool run_done[WEBUI_MAX_IDS]; // 2 Bytes ID
|
|
char* run_userBuffer[WEBUI_MAX_IDS];
|
|
size_t run_userBufferLen[WEBUI_MAX_IDS];
|
|
bool run_error[WEBUI_MAX_IDS];
|
|
uint16_t run_last_id;
|
|
bool initialized;
|
|
char* executable_path;
|
|
void * ptr_list[WEBUI_MAX_IDS * 2];
|
|
size_t ptr_last_pos;
|
|
size_t ptr_size[WEBUI_MAX_IDS * 2];
|
|
size_t current_browser;
|
|
_webui_window_t* wins[WEBUI_MAX_IDS];
|
|
bool wins_reserved[WEBUI_MAX_IDS];
|
|
webui_mutex_t mutex_server_start;
|
|
webui_mutex_t mutex_send;
|
|
webui_mutex_t mutex_receive;
|
|
webui_mutex_t mutex_wait;
|
|
webui_mutex_t mutex_bridge;
|
|
webui_mutex_t mutex_js_run;
|
|
webui_mutex_t mutex_win_connect;
|
|
webui_mutex_t mutex_exit_now;
|
|
webui_mutex_t mutex_webview_stop;
|
|
webui_mutex_t mutex_http_handler;
|
|
webui_mutex_t mutex_client;
|
|
webui_mutex_t mutex_async_response;
|
|
webui_mutex_t mutex_mem;
|
|
webui_mutex_t mutex_token;
|
|
webui_condition_t condition_wait;
|
|
char* default_server_root_path;
|
|
bool ui;
|
|
#ifdef WEBUI_TLS
|
|
char* root_cert;
|
|
char* root_key;
|
|
char* ssl_cert;
|
|
char* ssl_key;
|
|
#endif
|
|
// WebView
|
|
bool is_browser_main_run;
|
|
bool is_webview;
|
|
#ifdef _WIN32
|
|
char* webview_cacheFolder;
|
|
HMODULE webviewLib;
|
|
#elif __linux__
|
|
bool is_gtk_main_run;
|
|
#else
|
|
bool is_wkwebview_main_run;
|
|
#endif
|
|
}
|
|
_webui_core_t;
|
|
|
|
typedef struct _webui_cb_arg_t {
|
|
// Event
|
|
_webui_window_t* window;
|
|
size_t event_type;
|
|
char* element;
|
|
char* data;
|
|
size_t event_number;
|
|
}
|
|
_webui_cb_arg_t;
|
|
|
|
typedef struct _webui_recv_arg_t {
|
|
_webui_window_t* win;
|
|
void * ptr;
|
|
size_t len;
|
|
size_t recvNum;
|
|
int event_type;
|
|
struct mg_connection* client;
|
|
size_t connection_id;
|
|
}
|
|
_webui_recv_arg_t;
|
|
|
|
typedef struct _webui_cmd_async_t {
|
|
_webui_window_t* win;
|
|
char* cmd;
|
|
}
|
|
_webui_cmd_async_t;
|
|
|
|
// -- Definitions ---------------------
|
|
#ifdef _WIN32
|
|
static const char* os_sep = "\\";
|
|
static DWORD WINAPI _webui_run_browser_task(LPVOID _arg);
|
|
static int _webui_system_win32(_webui_window_t* win, char* cmd, bool show);
|
|
static int _webui_system_win32_out(const char* cmd, char ** output, bool show);
|
|
static bool _webui_socket_test_listen_win32(size_t port_num);
|
|
static bool _webui_get_windows_reg_value(HKEY key, LPCWSTR reg, LPCWSTR value_name, char value[WEBUI_MAX_PATH]);
|
|
static bool _webui_str_to_wide(const char *s, wchar_t **w);
|
|
#define WEBUI_THREAD_SERVER_START DWORD WINAPI _webui_server_thread(LPVOID arg)
|
|
#define WEBUI_THREAD_RECEIVE DWORD WINAPI _webui_ws_process_thread(LPVOID _arg)
|
|
#define WEBUI_THREAD_WEBVIEW DWORD WINAPI _webui_webview_thread(LPVOID arg)
|
|
#define WEBUI_THREAD_MONITOR DWORD WINAPI _webui_folder_monitor_thread(LPVOID arg)
|
|
#define WEBUI_THREAD_RETURN return 0;
|
|
#else
|
|
static const char* os_sep = "/";
|
|
static void * _webui_run_browser_task(void * _arg);
|
|
#define WEBUI_THREAD_SERVER_START void * _webui_server_thread(void * arg)
|
|
#define WEBUI_THREAD_RECEIVE void * _webui_ws_process_thread(void * _arg)
|
|
#define WEBUI_THREAD_WEBVIEW void * _webui_webview_thread(void * arg)
|
|
#define WEBUI_THREAD_MONITOR void * _webui_folder_monitor_thread(void * arg)
|
|
#define WEBUI_THREAD_RETURN pthread_exit(NULL);
|
|
#endif
|
|
static void _webui_init(void);
|
|
static bool _webui_show(_webui_window_t* win, struct mg_connection* client, const char* content, size_t browser);
|
|
static bool _webui_get_cb_index(_webui_window_t* win, const char* element, size_t* id);
|
|
static size_t _webui_get_free_port(void);
|
|
static void _webui_free_port(size_t port);
|
|
static char* _webui_get_current_path(void);
|
|
static void _webui_send_client_ws(_webui_window_t* win, struct mg_connection* client,
|
|
size_t connection_id, char* packet, size_t packets_size);
|
|
static void _webui_window_event(
|
|
_webui_window_t* win, size_t connection_id, int event_type, char* element, size_t event_number,
|
|
size_t client_id, const char* cookies
|
|
);
|
|
static int _webui_cmd_sync(_webui_window_t* win, char* cmd, bool show);
|
|
static int _webui_cmd_async(_webui_window_t* win, char* cmd, bool show);
|
|
static int _webui_run_browser(_webui_window_t* win, char* cmd);
|
|
static void _webui_clean(void);
|
|
static bool _webui_browser_exist(_webui_window_t* win, size_t browser);
|
|
static const char* _webui_get_temp_path();
|
|
static bool _webui_folder_exist(const char* folder);
|
|
static void _webui_delete_folder(char* folder);
|
|
static bool _webui_browser_create_new_profile(_webui_window_t* win, size_t browser);
|
|
static bool _webui_browser_start_chrome(_webui_window_t* win, const char* address);
|
|
static bool _webui_browser_start_edge(_webui_window_t* win, const char* address);
|
|
static bool _webui_browser_start_epic(_webui_window_t* win, const char* address);
|
|
static bool _webui_browser_start_vivaldi(_webui_window_t* win, const char* address);
|
|
static bool _webui_browser_start_brave(_webui_window_t* win, const char* address);
|
|
static bool _webui_browser_start_firefox(_webui_window_t* win, const char* address);
|
|
static bool _webui_browser_start_yandex(_webui_window_t* win, const char* address);
|
|
static bool _webui_browser_start_chromium(_webui_window_t* win, const char* address);
|
|
static bool _webui_browser_start(_webui_window_t* win, const char* address, size_t _browser);
|
|
static long _webui_timer_diff(struct timespec * start, struct timespec* end);
|
|
static void _webui_timer_start(_webui_timer_t* t);
|
|
static bool _webui_timer_is_end(_webui_timer_t* t, size_t ms);
|
|
static void _webui_timer_clock_gettime(struct timespec * spec);
|
|
static bool _webui_set_root_folder(_webui_window_t* win, const char* path);
|
|
static const char* _webui_generate_js_bridge(_webui_window_t* win, struct mg_connection* client);
|
|
static void _webui_free_mem(void * ptr);
|
|
static bool _webui_file_exist_mg(_webui_window_t* win, struct mg_connection* client);
|
|
static bool _webui_file_exist(const char* path);
|
|
static void _webui_free_all_mem(void);
|
|
static bool _webui_show_window(_webui_window_t* win, struct mg_connection* client,
|
|
const char* content, int type, size_t browser);
|
|
static bool _webui_is_empty(const char* s);
|
|
static size_t _webui_strlen(const char* s);
|
|
static uint16_t _webui_get_run_id(void);
|
|
static void * _webui_malloc(size_t size);
|
|
static void _webui_sleep(long unsigned int ms);
|
|
static size_t _webui_find_the_best_browser(_webui_window_t* win);
|
|
static bool _webui_is_process_running(const char* process_name);
|
|
static void _webui_panic(char* msg);
|
|
static void _webui_kill_pid(size_t pid);
|
|
static _webui_window_t* _webui_dereference_win_ptr(void * ptr);
|
|
static int _webui_get_browser_args(_webui_window_t* win, size_t browser, char* buffer, size_t len);
|
|
static void _webui_mutex_init(webui_mutex_t* mutex);
|
|
static void _webui_mutex_lock(webui_mutex_t* mutex);
|
|
static void _webui_mutex_unlock(webui_mutex_t* mutex);
|
|
static void _webui_mutex_destroy(webui_mutex_t* mutex);
|
|
static bool _webui_mutex_is_connected(_webui_window_t* win, int update);
|
|
static bool _webui_mutex_is_exit_now(int update);
|
|
static bool _webui_mutex_is_webview_update(_webui_window_t* win, int update);
|
|
static void _webui_condition_init(webui_condition_t* cond);
|
|
static void _webui_condition_wait(webui_condition_t* cond, webui_mutex_t* mutex);
|
|
static void _webui_condition_signal(webui_condition_t* cond);
|
|
static void _webui_condition_destroy(webui_condition_t* cond);
|
|
static void _webui_http_send(_webui_window_t* win, struct mg_connection* client,
|
|
const char* mime_type, const char* body, size_t body_len, bool cache);
|
|
static void _webui_http_send_file(_webui_window_t* win, struct mg_connection* client,
|
|
const char* mime_type, const char* path, bool cache);
|
|
static void _webui_http_send_header(_webui_window_t* win, struct mg_connection* client,
|
|
const char* mime_type, size_t body_len, bool cache);
|
|
static void _webui_http_send_error(struct mg_connection* client, const char* body, int status);
|
|
static int _webui_http_handler(struct mg_connection* client, void * _win);
|
|
static int _webui_ws_connect_handler(const struct mg_connection* client, void * _win);
|
|
static void _webui_ws_ready_handler(struct mg_connection* client, void * _win);
|
|
static int _webui_ws_data_handler(struct mg_connection* client, int opcode, char* data, size_t datasize, void * _win);
|
|
static void _webui_ws_close_handler(const struct mg_connection* client, void * _win);
|
|
static void _webui_receive(_webui_window_t* win, struct mg_connection* client, int event_type, void * data, size_t len);
|
|
static void _webui_ws_process(_webui_window_t* win, struct mg_connection* client, size_t connection_id,
|
|
void* ptr, size_t len, size_t recvNum, int event_type);
|
|
static bool _webui_connection_save(_webui_window_t* win, struct mg_connection* client, size_t* connection_id);
|
|
static bool _webui_connection_get_id(_webui_window_t* win, struct mg_connection* client, size_t* connection_id);
|
|
static void _webui_connection_remove(_webui_window_t* win, struct mg_connection* client);
|
|
static void _webui_remove_firefox_profile_ini(const char* path, const char* profile_name);
|
|
static bool _webui_is_firefox_ini_profile_exist(const char* path, const char* profile_name);
|
|
static void _webui_send_client(_webui_window_t* win, struct mg_connection *client,
|
|
uint16_t id, unsigned char cmd, const char* data, size_t len, bool token_bypass);
|
|
static void _webui_send_all(_webui_window_t* win, uint16_t id, unsigned char cmd, const char* data, size_t len);
|
|
static uint16_t _webui_get_id(const char* data);
|
|
static uint32_t _webui_get_token(const char* data);
|
|
static uint32_t _webui_generate_random_uint32();
|
|
static const char* _webui_url_encode(const char* str);
|
|
static bool _webui_open_url_native(const char* url);
|
|
static bool _webui_is_valid_url(const char* url);
|
|
static bool _webui_port_is_used(size_t port_num);
|
|
static char* _webui_str_dup(const char* src);
|
|
static void _webui_bridge_api_handler(webui_event_t* e);
|
|
// static size_t _webui_hash_djb2(const char* s);
|
|
static size_t _webui_new_event_inf(_webui_window_t* win, webui_event_inf_t** event_inf);
|
|
static void _webui_free_event_inf(_webui_window_t* win, size_t event_num);
|
|
static const char* _webui_get_cookies_full(const struct mg_connection* client);
|
|
static void _webui_get_cookies(const struct mg_connection* client, char* buffer);
|
|
static bool _webui_client_cookies_save(_webui_window_t* win, const char* cookies, size_t* client_id);
|
|
static bool _webui_client_cookies_get_id(_webui_window_t* win, const char* cookies, size_t* client_id);
|
|
// static void _webui_client_cookies_free_all(_webui_window_t* win);
|
|
// static void _webui_client_cookies_free(_webui_window_t* win, struct mg_connection* client);
|
|
static size_t _webui_client_get_id(_webui_window_t* win, struct mg_connection* client);
|
|
static void _webui_generate_cookies(char* cookies, size_t length);
|
|
static int _webui_serve_file(_webui_window_t* win, struct mg_connection* client, size_t client_id);
|
|
static int _webui_external_file_handler(_webui_window_t* win, struct mg_connection* client, size_t client_id);
|
|
static int _webui_interpret_file(_webui_window_t* win, struct mg_connection* client, char* index, size_t client_id);
|
|
// WebView
|
|
#ifdef _WIN32
|
|
// Microsoft Windows
|
|
static void _webui_wv_free(_webui_wv_win32_t* webView);
|
|
static void _webui_wv_close(_webui_wv_win32_t *webView);
|
|
static bool _webui_wv_navigate(_webui_wv_win32_t* webView, wchar_t* url);
|
|
static bool _webui_wv_set_position(_webui_wv_win32_t* webView, int x, int y);
|
|
static bool _webui_wv_set_size(_webui_wv_win32_t* webView, int windowWidth, int windowHeight);
|
|
static bool _webui_wv_show(_webui_window_t* win, char* url);
|
|
static void _webui_wv_event_closed(_webui_window_t* win);
|
|
#elif __linux__
|
|
// Linux
|
|
static void _webui_wv_free();
|
|
static void _webui_wv_close(_webui_wv_linux_t *webView);
|
|
static bool _webui_wv_navigate(_webui_wv_linux_t* webView, char* url);
|
|
static bool _webui_wv_set_position(_webui_wv_linux_t* webView, int x, int y);
|
|
static bool _webui_wv_set_size(_webui_wv_linux_t* webView, int windowWidth, int windowHeight);
|
|
static bool _webui_wv_show(_webui_window_t* win, char* url);
|
|
static void _webui_wv_event_closed(void *widget, void *arg);
|
|
#else
|
|
// macOS
|
|
static void _webui_wv_free(_webui_wv_macos_t* webView);
|
|
static void _webui_wv_close(_webui_wv_macos_t *webView);
|
|
static bool _webui_wv_navigate(_webui_wv_macos_t* webView, char* url);
|
|
static bool _webui_wv_set_position(_webui_wv_macos_t* webView, int x, int y);
|
|
static bool _webui_wv_set_size(_webui_wv_macos_t* webView, int windowWidth, int windowHeight);
|
|
static bool _webui_wv_show(_webui_window_t* win, char* url);
|
|
static void _webui_wv_event_closed(int index);
|
|
#endif
|
|
|
|
#ifdef WEBUI_TLS
|
|
static int _webui_tls_initialization(void * ssl_ctx, void * ptr);
|
|
static bool _webui_tls_generate_self_signed_cert(char* root_cert, char* root_key, char* ssl_cert, char* ssl_key);
|
|
static bool _webui_check_certificate(const char* certificate_pem, const char* private_key_pem);
|
|
#endif
|
|
#ifdef WEBUI_LOG
|
|
static void _webui_print_hex(const char* data, size_t len);
|
|
static void _webui_print_ascii(const char* data, size_t len);
|
|
static int _webui_http_log(const struct mg_connection* client, const char* message);
|
|
#endif
|
|
static WEBUI_THREAD_SERVER_START;
|
|
static WEBUI_THREAD_RECEIVE;
|
|
static WEBUI_THREAD_WEBVIEW;
|
|
static WEBUI_THREAD_MONITOR;
|
|
|
|
// Safe C STD
|
|
#ifdef _WIN32
|
|
#define WEBUI_STR_TOK(str, delim, context) strtok_s(str, delim, context)
|
|
#define WEBUI_FILE_OPEN(file, filename, mode) fopen_s(&file, filename, mode)
|
|
#define WEBUI_SN_PRINTF_DYN(buffer, buffer_size, format, ...) snprintf(buffer, (buffer_size + 1), format, ##__VA_ARGS__)
|
|
#define WEBUI_SN_PRINTF_STATIC(buffer, buffer_size, format, ...) snprintf(buffer, buffer_size, format, ##__VA_ARGS__)
|
|
#define WEBUI_STR_COPY_DYN(dest, dest_size, src) strcpy_s(dest, (dest_size + 1), src)
|
|
#define WEBUI_STR_COPY_STATIC(dest, dest_size, src) strcpy_s(dest, dest_size, src)
|
|
#define WEBUI_STR_CAT_DYN(dest, dest_size, src) strcat_s(dest, (dest_size + 1), src)
|
|
#define WEBUI_STR_CAT_STATIC(dest, dest_size, src) strcat_s(dest, dest_size, src)
|
|
#else
|
|
#define WEBUI_STR_TOK(str, delim, context) strtok_r(str, delim, context)
|
|
#define WEBUI_FILE_OPEN(file, filename, mode) ((file) = fopen(filename, mode))
|
|
#define WEBUI_SN_PRINTF_DYN(buffer, buffer_size, format, ...) snprintf(buffer, (buffer_size + 1), format, ##__VA_ARGS__)
|
|
#define WEBUI_SN_PRINTF_STATIC(buffer, buffer_size, format, ...) snprintf(buffer, buffer_size, format, ##__VA_ARGS__)
|
|
#define WEBUI_STR_COPY_DYN(dest, dest_size, src) strncpy(dest, src, (dest_size + 1))
|
|
#define WEBUI_STR_COPY_STATIC(dest, dest_size, src) strncpy(dest, src, dest_size)
|
|
#define WEBUI_STR_CAT_DYN(dest, dest_size, src) strncat(dest, src, (dest_size + 1))
|
|
#define WEBUI_STR_CAT_STATIC(dest, dest_size, src) strncat(dest, src, dest_size)
|
|
#endif
|
|
|
|
// Assert
|
|
#define WEBUI_ASSERT(s) _webui_panic(s);assert(0 && s);
|
|
|
|
// -- Heap ----------------------------
|
|
static _webui_core_t _webui;
|
|
static const char* webui_html_served = "<html><head><title>Access Denied</title><script src=\"/webui.js\"></script><style>"
|
|
"body{margin:0;background-repeat:no-repeat;background-attachment:fixed;background-color:#FF3CAC;background-image:linear-"
|
|
"gradient(225deg,#FF3CAC 0%,#784BA0 45%,#2B86C5 100%);font-family:sans-serif;margin:20px;color:#fff}a{color:#fff}</style>"
|
|
"</head><body><h2>⚠ Access Denied</h2><p>You can't access this content<br>because it's already in use in<br>another "
|
|
"window.<br><br>Note: Multi-client mode is disabled.</p><br><a href=\"https://www.webui.me\">"
|
|
"<small>WebUI v" WEBUI_VERSION "</small></a></body></html>";
|
|
static const char* webui_html_res_not_available = "<html><head><title>Resource Not Available</title><script src=\"/webui.js\">"
|
|
"</script><style>body{margin:0;background-repeat:no-repeat;background-attachment:fixed;background-color:#FF3CAC;background-"
|
|
"image:linear-gradient(225deg,#FF3CAC 0%,#784BA0 45%,#2B86C5 100%);font-family:sans-serif;margin:20px;color:#fff}a{color:#fff}"
|
|
"</style></head><body><h2>⚠ Resource Not Available</h2><p>The requested resource is not available.</p><br><a href=\""
|
|
"https://www.webui.me\"><small>WebUI v" WEBUI_VERSION "</small></a></body></html>";
|
|
static const char* webui_def_icon = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"500\" zoomAndPan=\"magnify\" viewBox=\"0 0 375 374.999991\" height=\"500\" preserveAspectRatio=\"xMidYMid meet\" version=\"1.0\"><defs><clipPath id=\"3530adfd62\"><path d=\"M 22.375 22.558594 L 352.625 22.558594 L 352.625 352.441406 L 22.375 352.441406 Z M 22.375 22.558594 \" clip-rule=\"nonzero\"/></clipPath></defs><g clip-path=\"url(#3530adfd62)\"><path fill=\"#2a6699\" d=\"M 22.375 22.558594 L 352.257812 22.558594 L 352.257812 352.441406 L 22.375 352.441406 Z M 22.375 22.558594 \" fill-opacity=\"1\" fill-rule=\"nonzero\"/></g></svg>"; static const char* webui_def_icon_type = "image/svg+xml";
|
|
|
|
// -- Functions -----------------------
|
|
void webui_run_client(webui_event_t* e, const char* script) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_run_client([%zu])\n", e->window);
|
|
printf("[User] webui_run_client([%zu]) -> Script: [%s]\n", e->window, script);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
size_t js_len = _webui_strlen(script);
|
|
if (js_len < 1)
|
|
return;
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[e->window];
|
|
|
|
if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE))
|
|
return;
|
|
|
|
// Packet Protocol Format:
|
|
// [...]
|
|
// [CMD]
|
|
// [Script]
|
|
|
|
// Send the packet to a single client
|
|
_webui_send_client(win, _webui.clients[e->connection_id], 0, WEBUI_CMD_JS_QUICK, script, js_len, false);
|
|
}
|
|
|
|
void webui_run(size_t window, const char* script) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_run([%zu])\n", window);
|
|
printf("[User] webui_run([%zu]) -> Script: [%s]\n", window, script);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
size_t js_len = _webui_strlen(script);
|
|
if (js_len < 1)
|
|
return;
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE))
|
|
return;
|
|
|
|
// Packet Protocol Format:
|
|
// [...]
|
|
// [CMD]
|
|
// [Script]
|
|
|
|
// Send the packet to all clients because no need for client's response
|
|
_webui_send_all(win, 0, WEBUI_CMD_JS_QUICK, script, js_len);
|
|
}
|
|
|
|
void webui_set_file_handler(size_t window, const void*(*handler)(const char* filename, int* length)) {
|
|
|
|
if (handler == NULL)
|
|
return;
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// Set the new `files_handler`
|
|
win->files_handler = handler;
|
|
// And reset any previous `files_handler_window`
|
|
win->files_handler_window = NULL;
|
|
}
|
|
|
|
void webui_set_file_handler_window(size_t window, const void*(*handler)(size_t window, const char* filename, int* length)) {
|
|
|
|
if (handler == NULL)
|
|
return;
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// Reset any previous `files_handler`
|
|
win->files_handler = NULL;
|
|
// And set `files_handler_window`
|
|
win->files_handler_window = handler;
|
|
}
|
|
|
|
bool webui_script_client(webui_event_t* e, const char* script, size_t timeout,
|
|
char* buffer, size_t buffer_length) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_script_client([%zu])\n", e->window);
|
|
printf("[User] webui_script_client([%zu]) -> Script [%s] \n", e->window, script);
|
|
printf("[User] webui_script_client([%zu]) -> Response Buffer @ 0x%p \n", e->window, buffer);
|
|
printf("[User] webui_script_client([%zu]) -> Response Buffer Size %zu bytes \n", e->window, buffer_length);
|
|
#endif
|
|
|
|
// Initializing response buffer
|
|
if (buffer_length > 0) {
|
|
memset(buffer, 0, buffer_length);
|
|
}
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
|
|
return false;
|
|
_webui_window_t* win = _webui.wins[e->window];
|
|
|
|
if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE))
|
|
return false;
|
|
|
|
size_t js_len = _webui_strlen(script);
|
|
|
|
if (js_len < 1)
|
|
return false;
|
|
|
|
// Initializing pipe
|
|
_webui_mutex_lock(&_webui.mutex_js_run);
|
|
uint16_t run_id = _webui_get_run_id();
|
|
_webui.run_done[run_id] = false;
|
|
_webui.run_error[run_id] = false;
|
|
_webui.run_userBuffer[run_id] = buffer;
|
|
_webui.run_userBufferLen[run_id] = buffer_length;
|
|
_webui_mutex_unlock(&_webui.mutex_js_run);
|
|
|
|
// Packet Protocol Format:
|
|
// [...]
|
|
// [CMD]
|
|
// [Script]
|
|
|
|
// Send the packet to a single specific client and wait for response
|
|
_webui_send_client(win, _webui.clients[e->connection_id], run_id, WEBUI_CMD_JS, script, js_len, false);
|
|
|
|
bool js_status = false;
|
|
|
|
// Wait for UI response
|
|
if (timeout < 1 || timeout > 86400) {
|
|
|
|
// Wait forever
|
|
for (;;) {
|
|
_webui_sleep(1);
|
|
_webui_mutex_lock(&_webui.mutex_js_run);
|
|
js_status = _webui.run_done[run_id];
|
|
_webui_mutex_unlock(&_webui.mutex_js_run);
|
|
if (js_status)
|
|
break;
|
|
}
|
|
} else {
|
|
|
|
// Using timeout
|
|
_webui_timer_t timer;
|
|
_webui_timer_start(&timer);
|
|
for (;;) {
|
|
_webui_sleep(1);
|
|
_webui_mutex_lock(&_webui.mutex_js_run);
|
|
js_status = _webui.run_done[run_id];
|
|
_webui_mutex_unlock(&_webui.mutex_js_run);
|
|
if (js_status)
|
|
break;
|
|
if (_webui_timer_is_end(&timer, (timeout * 1000)))
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (js_status) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[User] webui_script -> Response found. User buffer len: %zu bytes \n",
|
|
_webui.run_userBufferLen[run_id]
|
|
);
|
|
printf(
|
|
"[User] webui_script -> Response found. User buffer data: [%s] \n",
|
|
_webui.run_userBuffer[run_id]
|
|
);
|
|
#endif
|
|
|
|
return !_webui.run_error[run_id];
|
|
} else {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_script -> No Response is found. \n");
|
|
#endif
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool webui_script(size_t window, const char* script, size_t timeout,
|
|
char* buffer, size_t buffer_length) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_script([%zu])\n", window);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return false;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// Stop if multi-client mode is enabled.
|
|
// we can't send and receive from all clients
|
|
if (_webui.config.multi_client) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_script() -> Multi-client mode is enabled, stop.\n");
|
|
#endif
|
|
// Initializing response buffer
|
|
if (buffer_length > 0) {
|
|
memset(buffer, 0, buffer_length);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// New Event
|
|
webui_event_t e;
|
|
e.window = window;
|
|
e.connection_id = 0;
|
|
|
|
// Get the single client ID of this current window
|
|
// because we are in single client mode, and we need
|
|
// to wait for this client's response.
|
|
if (!_webui_connection_get_id(win, win->single_client, &e.connection_id))
|
|
return false;
|
|
if (_webui.clients[e.connection_id] == NULL)
|
|
return false;
|
|
|
|
return webui_script_client(&e, script, timeout, buffer, buffer_length);
|
|
}
|
|
|
|
static uint32_t _webui_generate_random_uint32() {
|
|
uint32_t timestamp = (uint32_t) time(NULL);
|
|
// Get the higher 16 bits
|
|
uint32_t high = ((uint32_t) rand()&0xFFFF) + ((timestamp >> 16)&0xFFFF);
|
|
// Get the lower 16 bits
|
|
uint32_t low = ((uint32_t) rand()&0xFFFF) + (timestamp&0xFFFF);
|
|
// Combine
|
|
return (high << 16) | low;
|
|
}
|
|
|
|
size_t webui_new_window(void) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_new_window()\n");
|
|
#endif
|
|
|
|
// Create a new window
|
|
return webui_new_window_id(webui_get_new_window_id());
|
|
}
|
|
|
|
size_t webui_new_window_id(size_t num) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_new_window_id([%zu])\n", num);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
|
|
return 0;
|
|
|
|
// Check window ID
|
|
if (num < 1 || num > WEBUI_MAX_IDS)
|
|
return 0;
|
|
|
|
// Destroy the window if already exist
|
|
if (_webui.wins[num] != NULL)
|
|
webui_destroy(num);
|
|
|
|
// Create a new window
|
|
_webui_window_t* win = (_webui_window_t* ) _webui_malloc(sizeof(_webui_window_t));
|
|
_webui.wins[num] = win;
|
|
|
|
// Initialisation
|
|
win->ws_block = _webui.config.ws_block;
|
|
win->num = num;
|
|
win->browser_path = (char*)_webui_malloc(WEBUI_MAX_PATH);
|
|
win->server_root_path = (char*)_webui_malloc(WEBUI_MAX_PATH);
|
|
if (_webui_is_empty(_webui.default_server_root_path))
|
|
WEBUI_SN_PRINTF_DYN(win->server_root_path, WEBUI_MAX_PATH, "%s", WEBUI_DEFAULT_PATH);
|
|
else
|
|
WEBUI_SN_PRINTF_DYN(win->server_root_path, WEBUI_MAX_PATH, "%s", _webui.default_server_root_path);
|
|
|
|
// Auto bind JavaScript-Bridge Core API Handler
|
|
webui_bind(num, "__webui_core_api__", _webui_bridge_api_handler);
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_new_window_id() -> New window #%zu @ 0x%p\n", num, win);
|
|
#endif
|
|
|
|
return num;
|
|
}
|
|
|
|
size_t webui_get_new_window_id(void) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_get_new_window_id()\n");
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
|
|
return 0;
|
|
|
|
for (size_t i = 1; i < WEBUI_MAX_IDS; i++) {
|
|
if (_webui.wins[i] == NULL && !_webui.wins_reserved[i]) {
|
|
_webui.wins_reserved[i] = true;
|
|
return i;
|
|
}
|
|
}
|
|
|
|
// We should never reach here
|
|
WEBUI_ASSERT("webui_get_new_window_id() failed");
|
|
return 0;
|
|
}
|
|
|
|
void webui_set_kiosk(size_t window, bool status) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_set_kiosk([%zu])\n", window);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
win->kiosk_mode = status;
|
|
}
|
|
|
|
void webui_set_custom_parameters(size_t window, char* params) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_set_custom_parameters([%zu], [%s])\n", window, params);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// Always free old data to allow user to clear custom params
|
|
// by passing an empty `params`.
|
|
_webui_free_mem((void*)win->custom_parameters);
|
|
win->custom_parameters = NULL;
|
|
|
|
// Check size
|
|
size_t len = _webui_strlen(params);
|
|
if (len < 1)
|
|
return;
|
|
|
|
// Set new
|
|
win->custom_parameters = (char*)_webui_malloc(len);
|
|
WEBUI_STR_COPY_DYN(win->custom_parameters, len, params);
|
|
}
|
|
|
|
void webui_set_high_contrast(size_t window, bool status) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_set_high_contrast([%zu], [%d])\n", window, status);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
win->disable_browser_high_contrast = !status;
|
|
}
|
|
|
|
bool webui_browser_exist(size_t browser) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_browser_exist([%zu])\n", browser);
|
|
#endif
|
|
|
|
return _webui_browser_exist(NULL, browser);
|
|
}
|
|
|
|
bool webui_is_high_contrast(void) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_is_high_contrast()\n");
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
bool is_enabled = false;
|
|
|
|
#ifdef _WIN32
|
|
char hc[WEBUI_MAX_PATH];
|
|
if (_webui_get_windows_reg_value(
|
|
HKEY_CURRENT_USER,
|
|
L"Control Panel\\Accessibility\\HighContrast",
|
|
L"Flags",
|
|
hc))
|
|
{
|
|
long flags = strtol(hc, NULL, 10);
|
|
if (flags != LONG_MIN && flags != LONG_MAX) {
|
|
is_enabled = ((flags&0x01) == 1);
|
|
}
|
|
}
|
|
#elif __linux__
|
|
FILE* process_output;
|
|
char buf[128];
|
|
process_output = popen("gsettings get org.gnome.desktop.a11y.interface high-contrast", "r");
|
|
if (process_output != NULL) {
|
|
if (fgets(buf, sizeof(buf), process_output) != NULL) {
|
|
is_enabled = (strstr(buf, "true") != NULL);
|
|
}
|
|
pclose(process_output);
|
|
}
|
|
#else
|
|
FILE* process_output;
|
|
char buf[128];
|
|
process_output = popen("defaults read -g AppleInterfaceStyle", "r");
|
|
if (process_output != NULL) {
|
|
if (fgets(buf, sizeof(buf), process_output) != NULL) {
|
|
is_enabled = (strstr(buf, "Dark") != NULL);
|
|
}
|
|
pclose(process_output);
|
|
}
|
|
#endif
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_is_high_contrast() -> %d\n", is_enabled);
|
|
#endif
|
|
|
|
return is_enabled;
|
|
}
|
|
|
|
void webui_close_client(webui_event_t* e) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_close_client([%zu])\n", e->window);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[e->window];
|
|
|
|
// Remove cookies
|
|
// _webui_client_cookies_free(win, _webui.clients[e->connection_id]);
|
|
|
|
// Packet Protocol Format:
|
|
// [...]
|
|
// [CMD]
|
|
|
|
// Send the packet to a single client
|
|
_webui_send_client(win, _webui.clients[e->connection_id], 0, WEBUI_CMD_CLOSE, NULL, 0, false);
|
|
|
|
// Forced close
|
|
mg_close_connection(_webui.clients[e->connection_id]);
|
|
}
|
|
|
|
void webui_close(size_t window) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_close([%zu])\n", window);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// Close
|
|
if (!win->webView) {
|
|
if (_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE)) {
|
|
// Packet Protocol Format:
|
|
// [...]
|
|
// [CMD]
|
|
// Send the packet
|
|
_webui_send_all(win, 0, WEBUI_CMD_CLOSE, NULL, 0);
|
|
}
|
|
}
|
|
else {
|
|
// Stop WebView thread if any
|
|
if (win->webView) {
|
|
win->webView->stop = true;
|
|
_webui_mutex_is_webview_update(win, WEBUI_MUTEX_TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
void webui_destroy(size_t window) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_destroy([%zu])\n", window);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
if (win->server_running) {
|
|
|
|
// Freindly close
|
|
webui_close(window);
|
|
|
|
// Wait for server threads to stop
|
|
_webui_timer_t timer_1;
|
|
_webui_timer_start(&timer_1);
|
|
for (;;) {
|
|
_webui_sleep(10);
|
|
if (!win->server_running)
|
|
break;
|
|
if (_webui_timer_is_end(&timer_1, 2500))
|
|
break;
|
|
}
|
|
|
|
if (win->server_running) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_destroy([%zu]) -> Forced close\n", window);
|
|
#endif
|
|
|
|
// Forced close
|
|
_webui_mutex_is_connected(win, WEBUI_MUTEX_FALSE);
|
|
|
|
// Wait for server threads to stop
|
|
_webui_timer_t timer_2;
|
|
_webui_timer_start(&timer_2);
|
|
for (;;) {
|
|
_webui_sleep(10);
|
|
if (!win->server_running)
|
|
break;
|
|
if (_webui_timer_is_end(&timer_2, 1500))
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Free memory resources
|
|
_webui_free_mem((void*)win->url);
|
|
_webui_free_mem((void*)win->html);
|
|
_webui_free_mem((void*)win->icon);
|
|
_webui_free_mem((void*)win->icon_type);
|
|
_webui_free_mem((void*)win->browser_path);
|
|
_webui_free_mem((void*)win->profile_path);
|
|
_webui_free_mem((void*)win->profile_name);
|
|
_webui_free_mem((void*)win->server_root_path);
|
|
|
|
// Free events
|
|
for (size_t i = 1; i < WEBUI_MAX_IDS; i++) {
|
|
if (win->events[i] != NULL)
|
|
_webui_free_mem((void*)win->events[i]);
|
|
}
|
|
|
|
// Free window struct
|
|
_webui_free_mem((void*)_webui.wins[window]);
|
|
_webui.wins[window] = NULL;
|
|
_webui.wins_reserved[window] = false;
|
|
}
|
|
|
|
bool webui_is_shown(size_t window) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_is_shown([%zu])\n", window);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return false;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
return _webui_mutex_is_connected(win, WEBUI_MUTEX_NONE);
|
|
}
|
|
|
|
void webui_set_icon(size_t window, const char* icon, const char* icon_type) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_set_icon([%zu], [%s], [%s])\n", window, icon, icon_type);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// Some wrappers do not guarantee pointers stay valid,
|
|
// so, let's make our copy.
|
|
|
|
// Icon
|
|
size_t len = _webui_strlen(icon);
|
|
const char* icon_cpy = (const char*)_webui_malloc(len);
|
|
memcpy((char*)icon_cpy, icon, len);
|
|
|
|
// Icon Type
|
|
len = _webui_strlen(icon_type);
|
|
const char* icon_type_cpy = (const char*)_webui_malloc(len);
|
|
memcpy((char*)icon_type_cpy, icon_type, len);
|
|
|
|
// Clean old sets if any
|
|
if (win->icon != NULL)
|
|
_webui_free_mem((void*)win->icon);
|
|
if (win->icon_type != NULL)
|
|
_webui_free_mem((void*)win->icon_type);
|
|
|
|
win->icon = icon_cpy;
|
|
win->icon_type = icon_type_cpy;
|
|
}
|
|
|
|
void webui_navigate_client(webui_event_t* e, const char* url) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_navigate_client([%zu], [%s])\n", e->window, url);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[e->window];
|
|
|
|
// Web-Browser Window
|
|
if (!win->webView) {
|
|
|
|
if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE))
|
|
return;
|
|
|
|
// Packet Protocol Format:
|
|
// [...]
|
|
// [CMD]
|
|
// [URL]
|
|
|
|
// Send the packet to a single client
|
|
_webui_send_client(win, _webui.clients[e->connection_id],
|
|
0, WEBUI_CMD_NAVIGATION, url, _webui_strlen(url), false
|
|
);
|
|
}
|
|
}
|
|
|
|
void webui_navigate(size_t window, const char* url) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_navigate([%zu], [%s])\n", window, url);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// Web-Browser Window
|
|
if (!win->webView) {
|
|
|
|
if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE))
|
|
return;
|
|
|
|
// Packet Protocol Format:
|
|
// [...]
|
|
// [CMD]
|
|
// [URL]
|
|
|
|
// Send the packet
|
|
_webui_send_all(win, 0, WEBUI_CMD_NAVIGATION, url, _webui_strlen(url));
|
|
}
|
|
else {
|
|
// WebView
|
|
_webui_free_mem((void*) win->webView->url);
|
|
|
|
#ifdef _WIN32
|
|
wchar_t* wURL = NULL;
|
|
_webui_str_to_wide(url, &wURL);
|
|
win->webView->url = wURL;
|
|
#else
|
|
char* url_cp = _webui_str_dup(url);
|
|
win->webView->url = url_cp;
|
|
#endif
|
|
|
|
win->webView->navigate = true;
|
|
_webui_mutex_is_webview_update(win, WEBUI_MUTEX_TRUE);
|
|
}
|
|
}
|
|
|
|
void webui_clean(void) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_clean()\n");
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Final memory cleaning
|
|
_webui_clean();
|
|
}
|
|
|
|
void webui_delete_all_profiles(void) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_delete_all_profiles()\n");
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Loop trough all windows
|
|
for (size_t i = 1; i < WEBUI_MAX_IDS; i++) {
|
|
if (_webui.wins[i] != NULL) {
|
|
webui_delete_profile(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool _webui_is_firefox_ini_profile_exist(const char* path, const char* profile_name) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_is_firefox_ini_profile_exist([%s], [%s])\n", path, profile_name);
|
|
#endif
|
|
|
|
// Parse home environments in the path
|
|
#ifdef _WIN32
|
|
// Windows
|
|
char full_path[WEBUI_MAX_PATH];
|
|
ExpandEnvironmentStringsA(path, full_path, sizeof(full_path));
|
|
#else
|
|
// Linux&macOS
|
|
char full_path[WEBUI_MAX_PATH];
|
|
if (path[0] == '~') {
|
|
const char* home = getenv("HOME");
|
|
if (home) {
|
|
WEBUI_SN_PRINTF_STATIC(full_path, sizeof(full_path), "%s/%s", home, &path[1]);
|
|
} else {
|
|
// If for some reason HOME isn't set
|
|
// fall back to the original path.
|
|
strncpy(full_path, path, sizeof(full_path));
|
|
}
|
|
} else {
|
|
strncpy(full_path, path, sizeof(full_path));
|
|
}
|
|
#endif
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_is_firefox_ini_profile_exist() -> Opening [%s]\n", full_path);
|
|
#endif
|
|
|
|
// Open
|
|
FILE* file;
|
|
WEBUI_FILE_OPEN(file, full_path, "r");
|
|
if (!file)
|
|
return false;
|
|
|
|
char target[128] = {
|
|
0x00
|
|
};
|
|
WEBUI_SN_PRINTF_STATIC(target, sizeof(target), "Name=%s", profile_name);
|
|
|
|
char line[1024];
|
|
while(fgets(line, sizeof(line), file)) {
|
|
if (strstr(line, target) != NULL) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_is_firefox_ini_profile_exist() -> Target found\n");
|
|
#endif
|
|
fclose(file);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
fclose(file);
|
|
return false;
|
|
}
|
|
|
|
static void _webui_remove_firefox_profile_ini(const char* path, const char* profile_name) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_remove_firefox_profile_ini([%s], [%s])\n", path, profile_name);
|
|
#endif
|
|
|
|
// Parse home environments in the path
|
|
#ifdef _WIN32
|
|
// Windows
|
|
char full_path[WEBUI_MAX_PATH];
|
|
ExpandEnvironmentStringsA(path, full_path, sizeof(full_path));
|
|
#else
|
|
// Linux&macOS
|
|
char full_path[WEBUI_MAX_PATH];
|
|
if (path[0] == '~') {
|
|
const char* home = getenv("HOME");
|
|
if (home) {
|
|
WEBUI_SN_PRINTF_STATIC(full_path, sizeof(full_path), "%s/%s", home,&path[1]);
|
|
} else {
|
|
// If for some reason HOME isn't set
|
|
// fall back to the original path.
|
|
strncpy(full_path, path, sizeof(full_path));
|
|
}
|
|
} else {
|
|
strncpy(full_path, path, sizeof(full_path));
|
|
}
|
|
#endif
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_remove_firefox_profile_ini() -> Opening [%s]\n", full_path);
|
|
#endif
|
|
|
|
// Open
|
|
FILE * file;
|
|
WEBUI_FILE_OPEN(file, full_path, "r");
|
|
if (!file)
|
|
return;
|
|
|
|
char buffer[1024 * 3] = {0};
|
|
char output[1024 * 4] = {0};
|
|
char target[128] = {0};
|
|
|
|
WEBUI_SN_PRINTF_STATIC(target, sizeof(target), "Name=%s", profile_name);
|
|
|
|
bool skip = false;
|
|
while(fgets(buffer, sizeof(buffer), file)) {
|
|
if (strncmp(buffer, "[Profile", 8) == 0) {
|
|
if (skip)
|
|
skip = false;
|
|
}
|
|
if (!skip) {
|
|
if (strstr(buffer, target) != NULL) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_remove_firefox_profile_ini() -> Target found\n");
|
|
#endif
|
|
skip = true;
|
|
continue;
|
|
} else {
|
|
WEBUI_STR_CAT_STATIC(output, sizeof(output), buffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
fclose(file);
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_remove_firefox_profile_ini() -> Saving\n");
|
|
#endif
|
|
|
|
// Save
|
|
WEBUI_FILE_OPEN(file, full_path, "w");
|
|
if (!file)
|
|
return;
|
|
fputs(output, file);
|
|
fclose(file);
|
|
}
|
|
|
|
void webui_delete_profile(size_t window) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_delete_profile([%zu])\n", window);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui.wins[window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
if (_webui_folder_exist(win->profile_path)) {
|
|
|
|
if (win->current_browser == Firefox) {
|
|
|
|
// Delete Firefox profile
|
|
|
|
#ifdef _WIN32
|
|
// Windows
|
|
_webui_remove_firefox_profile_ini(
|
|
"%APPDATA%\\Mozilla\\Firefox\\profiles.ini", win->profile_name
|
|
);
|
|
_webui_delete_folder(win->profile_path);
|
|
#elif __linux__
|
|
// Linux
|
|
_webui_remove_firefox_profile_ini("~/.mozilla/firefox/profiles.ini", win->profile_name);
|
|
_webui_remove_firefox_profile_ini(
|
|
"~/snap/firefox/common/.mozilla/firefox/profiles.ini", win->profile_name
|
|
);
|
|
_webui_delete_folder(win->profile_path);
|
|
#else
|
|
// macOS
|
|
_webui_remove_firefox_profile_ini(
|
|
"~/Library/Application Support/Firefox/profiles.ini", win->profile_name
|
|
);
|
|
_webui_delete_folder(win->profile_path);
|
|
#endif
|
|
} else {
|
|
|
|
// Delete Chromium-based profile
|
|
_webui_delete_folder(win->profile_path);
|
|
}
|
|
}
|
|
}
|
|
|
|
const char* webui_start_server(size_t window, const char* content) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_start_server([%zu])\n", window);
|
|
#endif
|
|
|
|
if (_webui_is_empty(content))
|
|
return "";
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return "";
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// Check
|
|
if (win->server_running)
|
|
return "";
|
|
|
|
// Make `wait()` waits forever
|
|
webui_set_timeout(0);
|
|
|
|
// Start the window without any GUI
|
|
if (webui_show_browser(window, content, NoBrowser)) {
|
|
return webui_get_url(window);
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
bool webui_show_client(webui_event_t* e, const char* content) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_show_client([%zu])\n", e->window);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
|
|
return false;
|
|
_webui_window_t* win = _webui.wins[e->window];
|
|
|
|
// Show the window using WebView or using any browser
|
|
win->allow_webview = true;
|
|
|
|
// Show for single a client
|
|
return _webui_show(win, _webui.clients[e->connection_id], content, AnyBrowser);
|
|
}
|
|
|
|
bool webui_show(size_t window, const char* content) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_show([%zu])\n", window);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return false;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// Show the window using WebView or using any browser
|
|
win->allow_webview = true;
|
|
|
|
// Show for all connected clients
|
|
return _webui_show(win, NULL, content, AnyBrowser);
|
|
}
|
|
|
|
bool webui_show_wv(size_t window, const char* content) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_show_wv([%zu])\n", window);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return false;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// Show the window using WebView only
|
|
win->allow_webview = true;
|
|
|
|
// Show for all connected clients
|
|
return _webui_show(win, NULL, content, NoBrowser);
|
|
}
|
|
|
|
bool webui_show_browser(size_t window, const char* content, size_t browser) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_show_browser([%zu], [%zu])\n", window, browser);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return false;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// Show the window using a specific browser only
|
|
win->allow_webview = (browser == Webview ? true : false);
|
|
|
|
// Show for all connected clients
|
|
return _webui_show(win, NULL, content, browser);
|
|
}
|
|
|
|
size_t webui_bind(size_t window, const char* element, void(*func)(webui_event_t* e)) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_bind([%zu])\n", window);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return 0;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// Search
|
|
size_t cb_index = 0;
|
|
bool exist = _webui_get_cb_index(win, element, &cb_index);
|
|
|
|
// All events
|
|
if (_webui_is_empty(element)) {
|
|
win->has_all_events = true;
|
|
size_t index = (exist ? cb_index : _webui.cb_count++);
|
|
win->html_elements[index] = "";
|
|
win->cb[index] = func;
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_bind() -> Save bind (all events) at %zu\n", index);
|
|
#endif
|
|
return index;
|
|
}
|
|
|
|
// New bind
|
|
const char* element_cpy = (const char*)_webui_str_dup(element);
|
|
size_t index = (exist ? cb_index : _webui.cb_count++);
|
|
win->html_elements[index] = element_cpy;
|
|
win->cb[index] = func;
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_bind() -> Save bind at %zu\n", index);
|
|
#endif
|
|
|
|
// Send to all connected clients the new binding ID
|
|
// Packet Protocol Format:
|
|
|
|
// [...]
|
|
// [CMD]
|
|
// [NewElement]
|
|
// Send the packet
|
|
|
|
_webui_send_all(
|
|
win, 0, WEBUI_CMD_ADD_ID,
|
|
element, _webui_strlen(element_cpy)
|
|
);
|
|
|
|
return index;
|
|
}
|
|
|
|
size_t webui_get_best_browser(size_t window) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_get_best_browser([%zu])\n", window);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return 1; // 1. Default recommended web browser
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
return _webui_find_the_best_browser(win);
|
|
}
|
|
|
|
const char* webui_get_string_at(webui_event_t* e, size_t index) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_get_string_at([%zu])\n", index);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
if (index > WEBUI_MAX_ARG)
|
|
return NULL;
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
|
|
return NULL;
|
|
_webui_window_t* win = _webui.wins[e->window];
|
|
|
|
// Get event inf
|
|
webui_event_inf_t* event_inf = win->events[e->event_number];
|
|
if (event_inf == NULL)
|
|
return NULL;
|
|
|
|
if (event_inf->event_data[index] != NULL) {
|
|
size_t len = _webui_strlen(event_inf->event_data[index]);
|
|
if (len > 0 && len <= WEBUI_MAX_BUF)
|
|
return (const char*)event_inf->event_data[index];
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
size_t webui_get_count(webui_event_t* e) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_get_count()\n");
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
|
|
return 0;
|
|
_webui_window_t* win = _webui.wins[e->window];
|
|
|
|
// Get event inf
|
|
webui_event_inf_t* event_inf = win->events[e->event_number];
|
|
if (event_inf == NULL)
|
|
return 0;
|
|
|
|
return event_inf->count;
|
|
}
|
|
|
|
long long int webui_get_int_at(webui_event_t* e, size_t index) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_get_int_at([%zu])\n", index);
|
|
#endif
|
|
|
|
// Initialization&Dereference
|
|
// are done by webui_get_string()
|
|
|
|
if (index > WEBUI_MAX_ARG)
|
|
return 0;
|
|
|
|
const char* str = webui_get_string_at(e, index);
|
|
if (str == NULL)
|
|
return 0;
|
|
|
|
size_t len = _webui_strlen(str);
|
|
if (len > 0 && len <= 20) {
|
|
// 64-bit max is -9,223,372,036,854,775,808 (20 character)
|
|
char* endptr;
|
|
return strtoll((const char*)str, &endptr, 10);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
double webui_get_float_at(webui_event_t* e, size_t index) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_get_float_at([%zu])\n", index);
|
|
#endif
|
|
|
|
// Initialization&Dereference
|
|
// are done by webui_get_string()
|
|
|
|
if (index > WEBUI_MAX_ARG)
|
|
return 0.0;
|
|
|
|
const char* str = webui_get_string_at(e, index);
|
|
if (str == NULL)
|
|
return 0.0;
|
|
|
|
size_t len = _webui_strlen(str);
|
|
if (len > 0 && len <= 20) {
|
|
// 64-bit max is -9,223,372,036,854,775,808 (20 character)
|
|
char* endptr;
|
|
return strtod(str, &endptr);
|
|
}
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
bool webui_get_bool_at(webui_event_t* e, size_t index) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_get_bool_at([%zu])\n", index);
|
|
#endif
|
|
|
|
// Initialization&Dereference
|
|
// are done by webui_get_string()
|
|
|
|
if (index > WEBUI_MAX_ARG)
|
|
return false;
|
|
|
|
const char* str = webui_get_string_at(e, index);
|
|
if (str == NULL)
|
|
return false;
|
|
|
|
if (str[0] == '0' || str[0] == 'f' || str[0] == 'F') // `0` | `false` | `FALSE`
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
size_t webui_get_size_at(webui_event_t* e, size_t index) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_get_size_at([%zu])\n", index);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
if (index > WEBUI_MAX_ARG)
|
|
return 0;
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
|
|
return 0;
|
|
_webui_window_t* win = _webui.wins[e->window];
|
|
|
|
// Get event inf
|
|
webui_event_inf_t* event_inf = win->events[e->event_number];
|
|
if (event_inf == NULL)
|
|
return 0;
|
|
|
|
return event_inf->event_size[index];
|
|
}
|
|
|
|
const char* webui_get_string(webui_event_t* e) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_get_string()\n");
|
|
#endif
|
|
|
|
return webui_get_string_at(e, 0);
|
|
}
|
|
|
|
long long int webui_get_int(webui_event_t* e) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_get_int()\n");
|
|
#endif
|
|
|
|
return webui_get_int_at(e, 0);
|
|
}
|
|
|
|
double webui_get_float(webui_event_t* e) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_get_float()\n");
|
|
#endif
|
|
|
|
return webui_get_float_at(e, 0);
|
|
}
|
|
|
|
bool webui_get_bool(webui_event_t* e) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_get_bool()\n");
|
|
#endif
|
|
|
|
return webui_get_bool_at(e, 0);
|
|
}
|
|
|
|
size_t webui_get_size(webui_event_t* e) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_get_size()\n");
|
|
#endif
|
|
|
|
return webui_get_size_at(e, 0);
|
|
}
|
|
|
|
void webui_return_int(webui_event_t* e, long long int n) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_return_int([%lld])\n", n);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[e->window];
|
|
|
|
// Get event inf
|
|
webui_event_inf_t* event_inf = win->events[e->event_number];
|
|
if (event_inf == NULL)
|
|
return;
|
|
|
|
// Free
|
|
if (event_inf->response != NULL)
|
|
_webui_free_mem((void*)event_inf->response);
|
|
|
|
// Int to Str
|
|
// 64-bit max is -9,223,372,036,854,775,808 (20 character)
|
|
char* buf = (char*)_webui_malloc(20);
|
|
WEBUI_SN_PRINTF_DYN(buf, 20, "%lld", n);
|
|
|
|
// Set response
|
|
event_inf->response = buf;
|
|
|
|
// Async response
|
|
if (_webui.config.asynchronous_response) {
|
|
_webui_mutex_lock(&_webui.mutex_async_response);
|
|
event_inf->done = true;
|
|
_webui_mutex_unlock(&_webui.mutex_async_response);
|
|
}
|
|
}
|
|
|
|
void webui_return_float(webui_event_t* e, double f) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_return_float([%f])\n", f);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[e->window];
|
|
|
|
// Get event inf
|
|
webui_event_inf_t* event_inf = win->events[e->event_number];
|
|
if (event_inf == NULL)
|
|
return;
|
|
|
|
// Free
|
|
if (event_inf->response != NULL)
|
|
_webui_free_mem((void*)event_inf->response);
|
|
|
|
// Float to Str
|
|
// 64-bit max is -9,223,372,036,854,775,808 (20 character)
|
|
char* buf = (char*)_webui_malloc(20);
|
|
WEBUI_SN_PRINTF_DYN(buf, 20, "%lf", f);
|
|
|
|
// Set response
|
|
event_inf->response = buf;
|
|
|
|
// Async response
|
|
if (_webui.config.asynchronous_response) {
|
|
_webui_mutex_lock(&_webui.mutex_async_response);
|
|
event_inf->done = true;
|
|
_webui_mutex_unlock(&_webui.mutex_async_response);
|
|
}
|
|
}
|
|
|
|
void webui_return_string(webui_event_t* e, const char* s) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_return_string([%s])\n", s);
|
|
#endif
|
|
|
|
if (_webui_is_empty(s))
|
|
return;
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[e->window];
|
|
|
|
// Get event inf
|
|
webui_event_inf_t* event_inf = win->events[e->event_number];
|
|
if (event_inf == NULL)
|
|
return;
|
|
|
|
// Free
|
|
if (event_inf->response != NULL)
|
|
_webui_free_mem((void*)event_inf->response);
|
|
|
|
// Copy Str
|
|
size_t len = _webui_strlen(s);
|
|
char* buf = (char*)_webui_malloc(len);
|
|
memcpy(buf, s, len);
|
|
|
|
// Set response
|
|
event_inf->response = buf;
|
|
|
|
// Async response
|
|
if (_webui.config.asynchronous_response) {
|
|
_webui_mutex_lock(&_webui.mutex_async_response);
|
|
event_inf->done = true;
|
|
_webui_mutex_unlock(&_webui.mutex_async_response);
|
|
}
|
|
}
|
|
|
|
void webui_return_bool(webui_event_t* e, bool b) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_return_bool([%d])\n", b);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[e->window];
|
|
|
|
// Get event inf
|
|
webui_event_inf_t* event_inf = win->events[e->event_number];
|
|
if (event_inf == NULL)
|
|
return;
|
|
|
|
// Free
|
|
if (event_inf->response != NULL)
|
|
_webui_free_mem((void*)event_inf->response);
|
|
|
|
// Bool to Str
|
|
int len = 1;
|
|
char* buf = (char*)_webui_malloc(len);
|
|
WEBUI_SN_PRINTF_DYN(buf, len, "%d", b);
|
|
|
|
// Set response
|
|
event_inf->response = buf;
|
|
|
|
// Async response
|
|
if (_webui.config.asynchronous_response) {
|
|
_webui_mutex_lock(&_webui.mutex_async_response);
|
|
event_inf->done = true;
|
|
_webui_mutex_unlock(&_webui.mutex_async_response);
|
|
}
|
|
}
|
|
|
|
size_t webui_get_parent_process_id(size_t window) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_get_parent_process_id([%zu])\n", window);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return 0;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
return win->process_id;
|
|
}
|
|
|
|
const char* webui_get_mime_type(const char* file) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_get_mime_type([%s])\n", file);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
return mg_get_builtin_mime_type(file);
|
|
}
|
|
|
|
size_t webui_get_free_port(void) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_get_free_port()\n");
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
return _webui_get_free_port();
|
|
}
|
|
|
|
size_t webui_get_port(size_t window) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_get_port([%zu])\n", window);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return 0;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
return win->server_port;
|
|
}
|
|
|
|
#ifdef WEBUI_TLS
|
|
static bool _webui_check_certificate(const char* certificate_pem, const char* private_key_pem) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_check_certificate()\n");
|
|
#endif
|
|
|
|
OpenSSL_add_all_algorithms();
|
|
|
|
// SSL Context
|
|
SSL_CTX * ctx;
|
|
SSL_library_init();
|
|
ctx = SSL_CTX_new(TLS_client_method());
|
|
if (!ctx)
|
|
return false;
|
|
|
|
// Disable security levels. It is The end-user
|
|
// responsability to provide a high encryption
|
|
// level certificate. While WebUI should just
|
|
// use the end-user's certificates.
|
|
SSL_CTX_set_security_level(ctx, 0);
|
|
|
|
// Load Certificate
|
|
BIO * bio_cert = BIO_new_mem_buf((void*)certificate_pem, -1);
|
|
X509 * cert = PEM_read_bio_X509(bio_cert, NULL, 0, NULL);
|
|
if (cert == NULL) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_check_certificate() -> PEM_read_bio_X509 failed\n");
|
|
#endif
|
|
BIO_free_all(bio_cert);
|
|
X509_free(cert);
|
|
SSL_CTX_free(ctx);
|
|
return false;
|
|
}
|
|
|
|
// Use certificate
|
|
if (SSL_CTX_use_certificate(ctx, cert) <= 0) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_check_certificate() -> SSL_CTX_use_certificate "
|
|
"failed\n");
|
|
#endif
|
|
BIO_free_all(bio_cert);
|
|
X509_free(cert);
|
|
SSL_CTX_free(ctx);
|
|
return false;
|
|
}
|
|
|
|
// Load Key
|
|
BIO * bio_key = BIO_new_mem_buf((void*)private_key_pem, -1);
|
|
EVP_PKEY * private_key = PEM_read_bio_PrivateKey(bio_key, NULL, 0, NULL);
|
|
if (private_key == NULL) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_check_certificate() -> PEM_read_bio_PrivateKey "
|
|
"failed\n");
|
|
#endif
|
|
EVP_PKEY_free(private_key);
|
|
BIO_free_all(bio_key);
|
|
BIO_free_all(bio_cert);
|
|
X509_free(cert);
|
|
SSL_CTX_free(ctx);
|
|
EVP_cleanup();
|
|
return false;
|
|
}
|
|
|
|
// Use key
|
|
if (SSL_CTX_use_PrivateKey(ctx, private_key) <= 0) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_check_certificate() -> SSL_CTX_use_PrivateKey "
|
|
"failed\n");
|
|
#endif
|
|
EVP_PKEY_free(private_key);
|
|
BIO_free_all(bio_key);
|
|
BIO_free_all(bio_cert);
|
|
X509_free(cert);
|
|
SSL_CTX_free(ctx);
|
|
EVP_cleanup();
|
|
return false;
|
|
}
|
|
|
|
// Free
|
|
EVP_PKEY_free(private_key);
|
|
BIO_free_all(bio_key);
|
|
BIO_free_all(bio_cert);
|
|
X509_free(cert);
|
|
SSL_CTX_free(ctx);
|
|
EVP_cleanup();
|
|
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
bool webui_set_tls_certificate(const char* certificate_pem, const char* private_key_pem) {
|
|
(void)certificate_pem;
|
|
(void)private_key_pem;
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_set_tls_certificate()\n");
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
#ifdef WEBUI_TLS
|
|
if (!_webui_is_empty(certificate_pem) && !_webui_is_empty(private_key_pem)) {
|
|
|
|
// Check size
|
|
size_t certificate_len = _webui_strlen(certificate_pem);
|
|
size_t private_key_len = _webui_strlen(private_key_pem);
|
|
if (certificate_len >= WEBUI_SSL_SIZE || private_key_len >= WEBUI_SSL_SIZE)
|
|
return false;
|
|
|
|
// Check certificate validity
|
|
if (!_webui_check_certificate(certificate_pem, private_key_pem)) {
|
|
#ifdef WEBUI_LOG
|
|
unsigned long err = ERR_get_error();
|
|
char err_buf[1024];
|
|
ERR_error_string_n(err, err_buf, sizeof(err_buf));
|
|
printf("[User] webui_set_tls_certificate() -> Invalid certificate:\n%s\n", err_buf);
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
// Free generated self-signed
|
|
_webui_free_mem((void*)_webui.root_cert);
|
|
_webui_free_mem((void*)_webui.root_key);
|
|
_webui_free_mem((void*)_webui.ssl_cert);
|
|
_webui_free_mem((void*)_webui.ssl_key);
|
|
|
|
// Set user TLS
|
|
char* ssl_cert = (char*)_webui_malloc(certificate_len);
|
|
char* ssl_key = (char*)_webui_malloc(private_key_len);
|
|
WEBUI_SN_PRINTF_DYN(ssl_cert, certificate_len, "%s", certificate_pem);
|
|
WEBUI_SN_PRINTF_DYN(ssl_key, private_key_len, "%s", private_key_pem);
|
|
_webui.ssl_cert = ssl_cert;
|
|
_webui.ssl_key = ssl_key;
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_set_tls_certificate() -> SSL/TLS Certificate:\n");
|
|
printf("%s\n", (const char*)_webui.ssl_cert);
|
|
printf("%s\n", (const char*)_webui.ssl_key);
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
#else
|
|
WEBUI_ASSERT("SSL/TLS is not available in this library. "
|
|
"Please use the secure version of WebUI library");
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
void webui_set_config(webui_config option, bool status) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_config([%d], [%d])\n", option, status);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
|
|
return;
|
|
|
|
switch (option) {
|
|
case show_wait_connection:
|
|
_webui.config.show_wait_connection = status;
|
|
break;
|
|
case folder_monitor:
|
|
_webui.config.folder_monitor = status;
|
|
break;
|
|
case multi_client:
|
|
_webui.config.multi_client = status;
|
|
break;
|
|
case use_cookies:
|
|
_webui.config.use_cookies = status;
|
|
break;
|
|
case asynchronous_response:
|
|
_webui.config.asynchronous_response = status;
|
|
break;
|
|
case ui_event_blocking:
|
|
_webui.config.ws_block = status;
|
|
// Update all created windows
|
|
for (size_t i = 1; i < WEBUI_MAX_IDS; i++) {
|
|
if (_webui.wins[i] != NULL) {
|
|
_webui.wins[i]->ws_block = status;
|
|
}
|
|
}
|
|
break;
|
|
#ifdef WEBUI_LOG
|
|
default:
|
|
printf("[User] webui_config -> Unknown option [%d]\n", option);
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void webui_set_event_blocking(size_t window, bool status) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_set_event_blocking([%zu], [%d])\n", window, status);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
win->ws_block = status;
|
|
}
|
|
|
|
bool webui_set_port(size_t window, size_t port) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_set_port([%zu], [%zu])\n", window, port);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return false;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
if (_webui_port_is_used(port))
|
|
return false;
|
|
|
|
win->custom_server_port = port;
|
|
return true;
|
|
}
|
|
|
|
size_t webui_get_child_process_id(size_t window) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_get_child_process_id([%zu])\n", window);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return 0;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
#ifdef _WIN32
|
|
DWORD childProcessId = 0;
|
|
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
|
if (hSnapshot != INVALID_HANDLE_VALUE) {
|
|
PROCESSENTRY32 pe32;
|
|
pe32.dwSize = sizeof(PROCESSENTRY32);
|
|
if (Process32First(hSnapshot,&pe32)) {
|
|
do {
|
|
if (pe32.th32ParentProcessID == win->process_id) {
|
|
childProcessId = pe32.th32ProcessID;
|
|
}
|
|
} while(Process32Next(hSnapshot,&pe32));
|
|
}
|
|
CloseHandle(hSnapshot);
|
|
}
|
|
return childProcessId;
|
|
#elif __linux__
|
|
DIR * dir;
|
|
struct dirent* entry;
|
|
pid_t lastChildPid = (pid_t) win->process_id;
|
|
dir = opendir("/proc");
|
|
if (!dir)
|
|
return win->process_id;
|
|
while((entry = readdir(dir)) != NULL) {
|
|
// Ensure we're looking at a process directory (directories that are just
|
|
// numbers)
|
|
if (entry->d_type == DT_DIR && strspn(entry->d_name, "0123456789") == _webui_strlen(entry->d_name)) {
|
|
char statFilepath[1024];
|
|
WEBUI_SN_PRINTF_STATIC(statFilepath, sizeof(statFilepath), "/proc/%s/stat", entry->d_name);
|
|
FILE * f;
|
|
WEBUI_FILE_OPEN(f, statFilepath, "r");
|
|
if (f) {
|
|
pid_t pid, ppid;
|
|
char comm[1024];
|
|
char state;
|
|
// Extract data from the stat file;fields are space-delimited
|
|
if (fscanf(f, "%d %s %c %d",&pid, comm,&state,&ppid) == 4) {
|
|
if ((intmax_t) ppid == (intmax_t) win->process_id) {
|
|
// Convert directory name (string) to integer PID
|
|
lastChildPid = atoi(entry->d_name);
|
|
}
|
|
}
|
|
fclose(f);
|
|
}
|
|
}
|
|
}
|
|
closedir(dir);
|
|
return lastChildPid;
|
|
#else
|
|
pid_t lastChildPid = -1;
|
|
// Get the size required to hold all process information
|
|
int mib[4] = {
|
|
CTL_KERN,
|
|
KERN_PROC,
|
|
KERN_PROC_ALL,
|
|
0
|
|
};
|
|
size_t size;
|
|
if (sysctl(mib, 3, NULL,&size, NULL, 0) < 0) {
|
|
return win->process_id;
|
|
}
|
|
// Allocate memory and get all process information
|
|
struct kinfo_proc * procList = (struct kinfo_proc * ) malloc(size);
|
|
if (!procList) {
|
|
return win->process_id;
|
|
}
|
|
if (sysctl(mib, 3, procList,&size, NULL, 0) < 0) {
|
|
free(procList);
|
|
return win->process_id;
|
|
}
|
|
// Calculate the number of processes from the returned data
|
|
int procCount = size / sizeof(struct kinfo_proc);
|
|
// Search for the last child process
|
|
for (int i = 0; i < procCount; i++) {
|
|
if ((intmax_t) procList[i].kp_eproc.e_ppid == (intmax_t) win->process_id) {
|
|
lastChildPid = procList[i].kp_proc.p_pid;
|
|
}
|
|
}
|
|
free(procList);
|
|
return (size_t) lastChildPid;
|
|
#endif
|
|
}
|
|
|
|
void webui_set_hide(size_t window, bool status) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_set_hide(%zu, %d)\n", window, status);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
win->hide = status;
|
|
}
|
|
|
|
void webui_set_size(size_t window, unsigned int width, unsigned int height) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_set_size(%zu, %u, %u)\n", window, width, height);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
if (width < WEBUI_MIN_WIDTH || width > WEBUI_MAX_WIDTH || height < WEBUI_MIN_HEIGHT ||
|
|
height > WEBUI_MAX_HEIGHT) {
|
|
|
|
win->size_set = false;
|
|
return;
|
|
}
|
|
|
|
win->width = width;
|
|
win->height = height;
|
|
win->size_set = true;
|
|
|
|
// Resize the current window
|
|
if (!win->webView) {
|
|
|
|
// web-browser window
|
|
if (_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE)) {
|
|
char script[128];
|
|
WEBUI_SN_PRINTF_STATIC(script, sizeof(script), "window.resizeTo(%u, %u);", width, height);
|
|
webui_run(window, script);
|
|
}
|
|
}
|
|
else {
|
|
|
|
// webView window
|
|
if (win->webView) {
|
|
win->webView->width = width;
|
|
win->webView->height = height;
|
|
win->webView->size = true;
|
|
_webui_mutex_is_webview_update(win, WEBUI_MUTEX_TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
void webui_set_minimum_size(size_t window, unsigned int width, unsigned int height) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_set_minimum_size(%zu, %u, %u)\n", window, width, height);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
if (width < WEBUI_MIN_WIDTH || width > WEBUI_MAX_WIDTH || height < WEBUI_MIN_HEIGHT ||
|
|
height > WEBUI_MAX_HEIGHT) {
|
|
|
|
win->minimum_size_set = false;
|
|
return;
|
|
}
|
|
|
|
win->minimum_width = width;
|
|
win->minimum_height = height;
|
|
win->minimum_size_set = true;
|
|
}
|
|
|
|
void webui_set_position(size_t window, unsigned int x, unsigned int y) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_set_position(%zu, %u, %u)\n", window, x, y);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
int X = x;
|
|
int Y = y;
|
|
|
|
if (X < WEBUI_MIN_X || X > WEBUI_MAX_X || Y < WEBUI_MIN_Y || Y > WEBUI_MAX_Y) {
|
|
|
|
win->position_set = false;
|
|
return;
|
|
}
|
|
|
|
win->x = X;
|
|
win->y = Y;
|
|
win->position_set = true;
|
|
|
|
// Positioning the current window
|
|
if (!win->webView) {
|
|
|
|
// web-browser window
|
|
if (_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE)) {
|
|
char script[128];
|
|
WEBUI_SN_PRINTF_STATIC(script, sizeof(script), "window.moveTo(%u, %u);", X, Y);
|
|
webui_run(window, script);
|
|
}
|
|
}
|
|
else {
|
|
|
|
// WebView window
|
|
if (win->webView) {
|
|
win->webView->x = X;
|
|
win->webView->y = Y;
|
|
win->webView->position = true;
|
|
_webui_mutex_is_webview_update(win, WEBUI_MUTEX_TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
void webui_set_profile(size_t window, const char* name, const char* path) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_set_profile([%s], [%s])\n", name, path);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// Some wrappers do not guarantee pointers stay valid,
|
|
// so, let's make our copy.
|
|
|
|
char* name_cpy = NULL;
|
|
size_t len = _webui_strlen(name);
|
|
if (len > 0) {
|
|
name_cpy = (char*)_webui_malloc(len);
|
|
memcpy((char*)name_cpy, name, len);
|
|
}
|
|
|
|
char* path_cpy = NULL;
|
|
len = _webui_strlen(path);
|
|
if (len > 0) {
|
|
path_cpy = (char*)_webui_malloc(len);
|
|
memcpy((char*)path_cpy, path, len);
|
|
}
|
|
|
|
// Free
|
|
if (win->profile_name != NULL)
|
|
_webui_free_mem((void*)win->profile_name);
|
|
if (win->profile_path != NULL)
|
|
_webui_free_mem((void*)win->profile_path);
|
|
|
|
// Save
|
|
win->profile_name = name_cpy;
|
|
win->profile_path = path_cpy;
|
|
win->custom_profile = true;
|
|
|
|
// Default local machine profile
|
|
if (name_cpy == NULL && path_cpy == NULL)
|
|
win->default_profile = true;
|
|
else
|
|
win->default_profile = false;
|
|
}
|
|
|
|
|
|
void webui_set_proxy(size_t window, const char* proxy_server) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_set_proxy([%s])\n", proxy_server);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// Some wrappers do not guarantee pointers stay valid,
|
|
// so, let's make our copy.
|
|
|
|
char* proxy_server_cpy = NULL;
|
|
size_t len = _webui_strlen(proxy_server);
|
|
if (len > 0) {
|
|
proxy_server_cpy = (char*)_webui_malloc(len);
|
|
memcpy((char*)proxy_server_cpy, proxy_server, len);
|
|
}
|
|
|
|
// Free
|
|
if (win->proxy_server != NULL)
|
|
_webui_free_mem((void*)win->proxy_server);
|
|
|
|
// Save
|
|
win->proxy_server = proxy_server_cpy;
|
|
|
|
// Set
|
|
if (_webui_is_empty(win->proxy_server))
|
|
// Disable the proxy because the server
|
|
// address is an empty string
|
|
win->proxy_set = false;
|
|
else
|
|
// Enable proxy
|
|
win->proxy_set = true;
|
|
}
|
|
|
|
void webui_open_url(const char* url) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_open_url([%s])\n", url);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
_webui_open_url_native(url);
|
|
}
|
|
|
|
const char* webui_get_url(size_t window) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_get_url([%zu])\n", window);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return NULL;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// Check if server is started
|
|
if (_webui_is_empty(win->url)) {
|
|
// Start server
|
|
webui_show_browser(window, "<html><head><script src=\"webui.js\"></script></head></html>", NoBrowser);
|
|
}
|
|
|
|
return (const char*) win->url;
|
|
}
|
|
|
|
void webui_set_public(size_t window, bool status) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_set_public([%zu])\n", window);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
win->is_public = status;
|
|
}
|
|
|
|
void webui_send_raw_client(webui_event_t* e, const char* function, const void* raw, size_t size) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_send_raw_client(%zu bytes)\n", size);
|
|
#endif
|
|
|
|
if ((size < 1) || (_webui_strlen(function) < 1) || (raw == NULL))
|
|
return;
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[e->window];
|
|
|
|
// Generate data
|
|
size_t data_len = _webui_strlen(function) + 1 + size;
|
|
char* buf = (char*)_webui_malloc(data_len);
|
|
|
|
// Add Function
|
|
size_t p = 0;
|
|
for (size_t i = 0; i < _webui_strlen(function); i++)
|
|
buf[p++] = function [i];
|
|
|
|
// Add Null
|
|
buf[p] = 0x00;
|
|
p++;
|
|
|
|
// Add Data
|
|
char* ptr = (char*)raw;
|
|
for (size_t i = 0; i < size; i++) {
|
|
buf[p++] = * ptr;
|
|
ptr++;
|
|
}
|
|
|
|
// Packet Protocol Format:
|
|
// [...]
|
|
// [CMD]
|
|
// [Function, Null, RawData]
|
|
|
|
// Send the packet to single a client
|
|
_webui_send_client(win, _webui.clients[e->connection_id],
|
|
0, WEBUI_CMD_SEND_RAW, (const char*)buf, data_len, false
|
|
);
|
|
|
|
_webui_free_mem((void*)buf);
|
|
}
|
|
|
|
void webui_send_raw(size_t window, const char* function, const void * raw, size_t size) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_send_raw(%zu bytes)\n", size);
|
|
#endif
|
|
|
|
if ((size < 1) || (_webui_strlen(function) < 1) || (raw == NULL))
|
|
return;
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// Generate data
|
|
size_t data_len = _webui_strlen(function) + 1 + size;
|
|
char* buf = (char*)_webui_malloc(data_len);
|
|
|
|
// Add Function
|
|
size_t p = 0;
|
|
for (size_t i = 0; i < _webui_strlen(function); i++)
|
|
buf[p++] = function [i];
|
|
|
|
// Add Null
|
|
buf[p] = 0x00;
|
|
p++;
|
|
|
|
// Add Data
|
|
char* ptr = (char*)raw;
|
|
for (size_t i = 0; i < size; i++) {
|
|
buf[p++] = *ptr;
|
|
ptr++;
|
|
}
|
|
|
|
// Packet Protocol Format:
|
|
// [...]
|
|
// [CMD]
|
|
// [Function, Null, RawData]
|
|
|
|
// Send the packet
|
|
_webui_send_all(win, 0, WEBUI_CMD_SEND_RAW, (const char*)buf, data_len);
|
|
_webui_free_mem((void*)buf);
|
|
}
|
|
|
|
char* webui_encode(const char* str) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_encode()\n");
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
|
|
return NULL;
|
|
|
|
size_t len = _webui_strlen(str);
|
|
if (len < 1)
|
|
return NULL;
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_encode() -> Text: [%s]\n", str);
|
|
#endif
|
|
|
|
size_t buf_len = (((len + 2) / 3) * 4) + 8;
|
|
char* buf = (char*)_webui_malloc(buf_len);
|
|
|
|
int ret = mg_base64_encode((const unsigned char*)str, len, buf,&buf_len);
|
|
|
|
if (ret > (-1)) {
|
|
|
|
// Failed
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_encode() -> Failed (%d).\n", ret);
|
|
#endif
|
|
_webui_free_mem((void*)buf);
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_encode() -> Encoded: [%s]\n", buf);
|
|
#endif
|
|
|
|
// Success
|
|
return buf;
|
|
}
|
|
|
|
char* webui_decode(const char* str) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_decode()\n");
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
|
|
return NULL;
|
|
|
|
size_t len = _webui_strlen(str);
|
|
if (len < 1)
|
|
return NULL;
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_decode() -> Encoded: [%s]\n", str);
|
|
#endif
|
|
|
|
size_t buf_len = (((len + 2) / 3) * 4) + 8;
|
|
unsigned char* buf = (unsigned char*)_webui_malloc(buf_len);
|
|
|
|
int ret = mg_base64_decode(str, len, buf,&buf_len);
|
|
|
|
if (ret > (-1)) {
|
|
|
|
// Failed
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_decode() -> Failed (%d).\n", ret);
|
|
#endif
|
|
_webui_free_mem((void*)buf);
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_decode() -> Decoded: [%s]\n", buf);
|
|
#endif
|
|
|
|
// Success
|
|
return (char*)buf;
|
|
}
|
|
|
|
void webui_free(void * ptr) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_free([0x%p])\n", ptr);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
|
|
return;
|
|
|
|
_webui_free_mem(ptr);
|
|
}
|
|
|
|
void * webui_malloc(size_t size) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_malloc(%zu bytes)\n", size);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
|
|
return NULL;
|
|
|
|
return _webui_malloc(size);
|
|
}
|
|
|
|
void webui_exit(void) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_exit()\n");
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
|
|
return;
|
|
|
|
// Close all windows
|
|
for (size_t i = 1; i < WEBUI_MAX_IDS; i++) {
|
|
if (_webui.wins[i] != NULL) {
|
|
if (_webui_mutex_is_connected(_webui.wins[i], WEBUI_MUTEX_NONE)) {
|
|
|
|
if (!_webui.wins[i]->webView) {
|
|
|
|
// Web browser
|
|
|
|
// Packet Protocol Format:
|
|
// [...]
|
|
// [CMD]
|
|
|
|
// Send the packet
|
|
_webui_send_all(
|
|
_webui.wins[i], 0, WEBUI_CMD_CLOSE, NULL, 0
|
|
);
|
|
}
|
|
else {
|
|
|
|
// WebView
|
|
|
|
// Stop WebView thread if any
|
|
if (_webui.wins[i]->webView) {
|
|
_webui.wins[i]->webView->stop = true;
|
|
_webui_mutex_is_webview_update(_webui.wins[i], WEBUI_MUTEX_TRUE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Stop all threads
|
|
_webui_mutex_is_exit_now(WEBUI_MUTEX_TRUE);
|
|
|
|
// Let's give other threads more time to
|
|
// safely exit and finish cleaning up.
|
|
_webui_sleep(500);
|
|
|
|
// Fire the mutex condition for wait()
|
|
_webui_condition_signal(&_webui.condition_wait);
|
|
#ifdef __APPLE__
|
|
_webui_macos_wv_stop();
|
|
#endif
|
|
}
|
|
|
|
void webui_wait(void) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Loop] webui_wait()\n");
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
|
|
return;
|
|
|
|
if (_webui.startup_timeout > 0) {
|
|
|
|
// Check if there is atleast one window (UI)
|
|
// is running. Otherwise the mutex condition
|
|
// signal will never come
|
|
if (!_webui.ui) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Loop] webui_wait() -> No window is found. Stop\n");
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Loop] webui_wait() -> Waiting (Timeout in %zu seconds)\n",
|
|
_webui.startup_timeout);
|
|
#endif
|
|
|
|
// The mutex conditional signal will
|
|
// be fired when no more UI (servers)
|
|
// is running.
|
|
} else {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Loop] webui_wait() -> Infinite waiting\n");
|
|
#endif
|
|
|
|
// The mutex conditional signal will
|
|
// be fired when `webui_exit()` is
|
|
// called by the user.
|
|
}
|
|
|
|
// Main loop
|
|
#ifdef _WIN32
|
|
if (!_webui.is_webview) {
|
|
// Windows Web browser main loop
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Loop] webui_wait() -> Windows web browser loop\n");
|
|
#endif
|
|
|
|
_webui.is_browser_main_run = true;
|
|
_webui_mutex_lock(&_webui.mutex_wait);
|
|
_webui_condition_wait(&_webui.condition_wait,&_webui.mutex_wait);
|
|
_webui.is_browser_main_run = false;
|
|
}
|
|
else {
|
|
// Windows WebView main loop
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Loop] webui_wait() -> Windows WebView loop\n");
|
|
#endif
|
|
|
|
_webui_mutex_lock(&_webui.mutex_wait);
|
|
_webui_condition_wait(&_webui.condition_wait,&_webui.mutex_wait);
|
|
}
|
|
#elif __linux__
|
|
if (!_webui.is_webview) {
|
|
// Linux Web browser main loop
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Loop] webui_wait() -> Linux web browser loop\n");
|
|
#endif
|
|
|
|
_webui.is_browser_main_run = true;
|
|
_webui_mutex_lock(&_webui.mutex_wait);
|
|
_webui_condition_wait(&_webui.condition_wait,&_webui.mutex_wait);
|
|
_webui.is_browser_main_run = false;
|
|
}
|
|
else {
|
|
// Linux WebView main loop
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Loop] webui_wait() -> Linux WebView loop\n");
|
|
#endif
|
|
|
|
_webui.is_gtk_main_run = true;
|
|
|
|
while (true) {
|
|
|
|
while (gtk_events_pending()) {
|
|
gtk_main_iteration_do(0);
|
|
}
|
|
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
|
|
break;
|
|
}
|
|
|
|
_webui.is_gtk_main_run = false;
|
|
}
|
|
#else
|
|
if (!_webui.is_webview) {
|
|
// macOS Web browser main loop
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Loop] webui_wait() -> macOS web browser loop\n");
|
|
#endif
|
|
|
|
_webui.is_browser_main_run = true;
|
|
_webui_mutex_lock(&_webui.mutex_wait);
|
|
_webui_condition_wait(&_webui.condition_wait,&_webui.mutex_wait);
|
|
_webui.is_browser_main_run = false;
|
|
}
|
|
else {
|
|
// macOS WebView main loop
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Loop] webui_wait() -> macOS WebView loop\n");
|
|
#endif
|
|
|
|
_webui.is_wkwebview_main_run = true;
|
|
|
|
while (true) {
|
|
|
|
_webui_macos_wv_process();
|
|
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
|
|
break;
|
|
}
|
|
|
|
_webui.is_wkwebview_main_run = false;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Loop] webui_wait() -> Cleaning\n");
|
|
#endif
|
|
|
|
// Clean
|
|
#ifdef _WIN32
|
|
if (!_webui.is_webview) {
|
|
// Windows Web browser Clean
|
|
|
|
// ...
|
|
}
|
|
else {
|
|
// Windows WebView Clean
|
|
|
|
PostQuitMessage(0);
|
|
if (_webui.webview_cacheFolder) {
|
|
_webui_delete_folder(_webui.webview_cacheFolder);
|
|
_webui_free_mem((void*) _webui.webview_cacheFolder);
|
|
_webui.webview_cacheFolder = NULL;
|
|
}
|
|
_webui_sleep(750);
|
|
}
|
|
#elif __linux__
|
|
if (!_webui.is_webview) {
|
|
// Linux Web browser Clean
|
|
|
|
// ...
|
|
}
|
|
else {
|
|
// Linux WebView Clean
|
|
|
|
// Close all GTK windows if any
|
|
for (size_t i = 1; i < WEBUI_MAX_IDS; i++) {
|
|
if (_webui.wins[i] != NULL) {
|
|
if (_webui.wins[i]->webView) {
|
|
_webui.wins[i]->webView->stop = true;
|
|
_webui_mutex_is_webview_update(_webui.wins[i], WEBUI_MUTEX_TRUE);
|
|
}
|
|
}
|
|
// Process drawing events if any
|
|
while (gtk_events_pending()) {
|
|
gtk_main_iteration_do(0);
|
|
}
|
|
}
|
|
_webui_sleep(750);
|
|
// Process drawing events if any
|
|
while (gtk_events_pending()) {
|
|
gtk_main_iteration_do(0);
|
|
}
|
|
_webui_wv_free();
|
|
}
|
|
#else
|
|
if (!_webui.is_webview) {
|
|
// macOS Web browser Clean
|
|
|
|
// ...
|
|
}
|
|
else {
|
|
// macOS WebView Clean
|
|
|
|
_webui_sleep(750);
|
|
}
|
|
#endif
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Loop] webui_wait() -> Main loop exit successfully\n");
|
|
#endif
|
|
|
|
_webui_mutex_unlock(&_webui.mutex_wait);
|
|
}
|
|
|
|
void webui_set_timeout(size_t second) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_set_timeout([%zu])\n", second);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
|
|
return;
|
|
|
|
if (second > WEBUI_MAX_TIMEOUT)
|
|
second = WEBUI_MAX_TIMEOUT;
|
|
|
|
_webui.startup_timeout = second;
|
|
}
|
|
|
|
void webui_set_runtime(size_t window, size_t runtime) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_set_runtime([%zu], [%zu])\n", window, runtime);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
if (runtime != Deno && runtime != NodeJS && runtime != Bun)
|
|
win->runtime = None;
|
|
else
|
|
win->runtime = runtime;
|
|
}
|
|
|
|
bool webui_set_root_folder(size_t window, const char* path) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_set_root_folder([%zu], [%s])\n", window, path);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return false;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// Fix path
|
|
char full_path[WEBUI_MAX_PATH] = {0};
|
|
#if defined(_WIN32)
|
|
// ...
|
|
#else
|
|
if ((path[0] != '.') && (path[0] != '/')) {
|
|
WEBUI_SN_PRINTF_STATIC(full_path, sizeof(full_path), "./%s", path);
|
|
path = full_path;
|
|
}
|
|
#endif
|
|
|
|
if (win->server_running ||
|
|
_webui_is_empty(path) ||
|
|
(_webui_strlen(path) > WEBUI_MAX_PATH) ||
|
|
!_webui_folder_exist(path)) {
|
|
|
|
WEBUI_SN_PRINTF_DYN(win->server_root_path, WEBUI_MAX_PATH, "%s", WEBUI_DEFAULT_PATH);
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_set_root_folder() -> failed\n");
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_set_root_folder() -> Success\n");
|
|
#endif
|
|
WEBUI_SN_PRINTF_DYN(win->server_root_path, WEBUI_MAX_PATH, "%s", path);
|
|
return true;
|
|
}
|
|
|
|
bool webui_set_default_root_folder(const char* path) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_set_default_root_folder([%s])\n", path);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
|
|
return false;
|
|
|
|
if (_webui_is_empty(path) || (_webui_strlen(path) > WEBUI_MAX_PATH) || !_webui_folder_exist((char*)path)) {
|
|
|
|
_webui.default_server_root_path[0] = '\0';
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_set_default_root_folder() -> failed\n");
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_set_default_root_folder() -> Success\n");
|
|
#endif
|
|
WEBUI_SN_PRINTF_DYN(_webui.default_server_root_path, WEBUI_MAX_PATH, "%s", path);
|
|
|
|
// Update all windows. This will works only
|
|
// for non-running windows.
|
|
for (size_t i = 1; i < WEBUI_MAX_IDS; i++) {
|
|
if (_webui.wins[i] != NULL) {
|
|
WEBUI_SN_PRINTF_DYN(_webui.wins[i]->server_root_path, WEBUI_MAX_PATH,
|
|
"%s", _webui.default_server_root_path);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// -- Interface's Functions ----------------
|
|
static void _webui_interface_bind_handler_all(webui_event_t* e) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_interface_bind_handler_all()\n");
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[e->window];
|
|
|
|
// Check for all events-bind functions
|
|
if (win->has_all_events) {
|
|
size_t events_cb_index = 0;
|
|
bool exist = _webui_get_cb_index(win, "", &events_cb_index);
|
|
if (exist && win->cb_interface[events_cb_index] != NULL) {
|
|
// Call user all-events cb
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[Core]\t\t_webui_interface_bind_handler_all() -> User all-events callback @ 0x%p\n",
|
|
win->cb_interface[events_cb_index]
|
|
);
|
|
printf("[Core]\t\t_webui_interface_bind_handler_all() -> User all-events e->event_type [%zu]\n", e->event_type);
|
|
printf("[Core]\t\t_webui_interface_bind_handler_all() -> User all-events e->element [%s]\n", e->element);
|
|
printf("[Core]\t\t_webui_interface_bind_handler_all() -> User all-events e->event_number %zu\n", e->event_number);
|
|
printf("[Core]\t\t_webui_interface_bind_handler_all() -> User all-events e->bind_id %zu\n", e->bind_id);
|
|
#endif
|
|
// Call all-events cb
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_interface_bind_handler_all() -> Calling user all-events callback\n[Call]\n");
|
|
#endif
|
|
win->cb_interface[events_cb_index](e->window, e->event_type, e->element, e->event_number, e->bind_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void _webui_interface_bind_handler(webui_event_t* e) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_interface_bind_handler()\n");
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[e->window];
|
|
|
|
// Check for the regular bind functions
|
|
if (!_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) && !_webui_is_empty(e->element)) {
|
|
size_t cb_index = 0;
|
|
bool exist = _webui_get_cb_index(win, e->element, &cb_index);
|
|
if (exist && win->cb_interface[cb_index] != NULL) {
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[Core]\t\t_webui_interface_bind_handler() -> User callback @ 0x%p\n",
|
|
win->cb_interface[cb_index]
|
|
);
|
|
printf("[Core]\t\t_webui_interface_bind_handler() -> e->event_type [%zu]\n", e->event_type);
|
|
printf("[Core]\t\t_webui_interface_bind_handler() -> e->element [%s]\n", e->element);
|
|
printf("[Core]\t\t_webui_interface_bind_handler() -> e->event_number %zu\n", e->event_number);
|
|
printf("[Core]\t\t_webui_interface_bind_handler() -> e->bind_id %zu\n", e->bind_id);
|
|
#endif
|
|
// Call cb
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_interface_bind_handler() -> Calling user callback\n[Call]\n");
|
|
#endif
|
|
win->cb_interface[cb_index](e->window, e->event_type, e->element, e->event_number, e->bind_id);
|
|
}
|
|
}
|
|
|
|
// Get event inf
|
|
webui_event_inf_t* event_inf = win->events[e->event_number];
|
|
if (event_inf != NULL) {
|
|
|
|
// Async response wait
|
|
if (_webui.config.asynchronous_response) {
|
|
bool done = false;
|
|
while (!done) {
|
|
_webui_sleep(10);
|
|
_webui_mutex_lock(&_webui.mutex_async_response);
|
|
if (event_inf->done) done = true;
|
|
_webui_mutex_unlock(&_webui.mutex_async_response);
|
|
}
|
|
}
|
|
|
|
#ifdef WEBUI_LOG
|
|
// Print cb response
|
|
printf(
|
|
"[Core]\t\t_webui_interface_bind_handler() -> user-callback response [%s]\n",
|
|
event_inf->response
|
|
);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
const char* webui_interface_get_string_at(size_t window, size_t event_number, size_t index) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_interface_get_string_at([%zu], [%zu], [%zu])\n", window, event_number, index);
|
|
#endif
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return NULL;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// New Event (Wrapper)
|
|
webui_event_t e;
|
|
e.window = window;
|
|
e.event_number = event_number;
|
|
|
|
return webui_get_string_at(&e, index);
|
|
}
|
|
|
|
long long int webui_interface_get_int_at(size_t window, size_t event_number, size_t index) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_interface_get_int_at([%zu], [%zu], [%zu])\n", window, event_number, index);
|
|
#endif
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return 0;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// New Event (Wrapper)
|
|
webui_event_t e;
|
|
e.window = window;
|
|
e.event_number = event_number;
|
|
|
|
return webui_get_int_at(&e, index);
|
|
}
|
|
|
|
double webui_interface_get_float_at(size_t window, size_t event_number, size_t index) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_interface_get_float_at([%zu], [%zu], [%zu])\n", window, event_number, index);
|
|
#endif
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return ((double)(0.0));
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// New Event (Wrapper)
|
|
webui_event_t e;
|
|
e.window = window;
|
|
e.event_number = event_number;
|
|
|
|
return webui_get_float_at(&e, index);
|
|
}
|
|
|
|
bool webui_interface_get_bool_at(size_t window, size_t event_number, size_t index) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_interface_get_bool_at([%zu], [%zu], [%zu])\n", window, event_number, index);
|
|
#endif
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return false;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// New Event (Wrapper)
|
|
webui_event_t e;
|
|
e.window = window;
|
|
e.event_number = event_number;
|
|
|
|
return webui_get_bool_at(&e, index);
|
|
}
|
|
|
|
size_t webui_interface_get_size_at(size_t window, size_t event_number, size_t index) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_interface_get_size_at([%zu], [%zu], [%zu])\n", window, event_number, index);
|
|
#endif
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return 0;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// New Event (Wrapper)
|
|
webui_event_t e;
|
|
e.window = window;
|
|
e.event_number = event_number;
|
|
|
|
return webui_get_size_at(&e, index);
|
|
}
|
|
|
|
size_t webui_interface_bind(size_t window, const char* element, void(*func)(size_t, size_t, char* , size_t, size_t)) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_interface_bind([%zu], [%s], [0x%p])\n", window, element, func);
|
|
#endif
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return 0;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// Bind
|
|
size_t cb_index = 0;
|
|
if (_webui_is_empty(element)) {
|
|
cb_index = webui_bind(window, "", _webui_interface_bind_handler_all);
|
|
} else {
|
|
cb_index = webui_bind(window, element, _webui_interface_bind_handler);
|
|
}
|
|
win->cb_interface[cb_index] = func;
|
|
return cb_index;
|
|
}
|
|
|
|
void webui_interface_set_response(size_t window, size_t event_number, const char* response) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_interface_set_response()\n");
|
|
printf("[User] webui_interface_set_response() -> event_number %zu \n", event_number);
|
|
printf("[User] webui_interface_set_response() -> Response [%s] \n", response);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// Get event inf
|
|
webui_event_inf_t* event_inf = win->events[event_number];
|
|
if (event_inf == NULL)
|
|
return;
|
|
|
|
// Free
|
|
if (event_inf->response != NULL)
|
|
_webui_free_mem((void*)event_inf->response);
|
|
|
|
// Set the response
|
|
size_t len = _webui_strlen(response);
|
|
event_inf->response = (char*)_webui_malloc(len);
|
|
WEBUI_STR_COPY_DYN(event_inf->response, len, response);
|
|
|
|
// Async response
|
|
if (_webui.config.asynchronous_response) {
|
|
_webui_mutex_lock(&_webui.mutex_async_response);
|
|
event_inf->done = true;
|
|
_webui_mutex_unlock(&_webui.mutex_async_response);
|
|
}
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_interface_set_response() -> Internal buffer [%s] \n", event_inf->response);
|
|
#endif
|
|
}
|
|
|
|
void webui_interface_set_response_file_handler(size_t window, const void* response, int length) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_interface_set_response_file_handler()\n");
|
|
printf("[User] webui_interface_set_response_file_handler() -> window #%zu\n", window);
|
|
printf("[User] webui_interface_set_response_file_handler() -> Response %zu bytes\n", length);
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// Set the response
|
|
win->file_handler_async_response = response;
|
|
win->file_handler_async_len = length;
|
|
|
|
// Async response
|
|
if (_webui.config.asynchronous_response) {
|
|
_webui_mutex_lock(&_webui.mutex_async_response);
|
|
win->file_handler_async_done = true;
|
|
_webui_mutex_unlock(&_webui.mutex_async_response);
|
|
}
|
|
}
|
|
|
|
bool webui_interface_is_app_running(void) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
// printf("[User] webui_is_app_running()\n");
|
|
#endif
|
|
|
|
// Stop if already flagged
|
|
static bool app_is_running = true;
|
|
if (!app_is_running)
|
|
return false;
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
|
|
return false;
|
|
|
|
// Get app status
|
|
if (_webui.startup_timeout > 0) {
|
|
if (_webui.servers < 1)
|
|
app_is_running = false;
|
|
}
|
|
|
|
#ifdef WEBUI_LOG
|
|
if (!app_is_running)
|
|
printf("[User] webui_is_app_running() -> App Stopped\n");
|
|
#endif
|
|
|
|
return app_is_running;
|
|
}
|
|
|
|
size_t webui_interface_get_window_id(size_t window) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_interface_get_window_id()\n");
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return 0;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
return win->num;
|
|
}
|
|
|
|
bool webui_interface_show_client(size_t window, size_t event_number, const char* content) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_interface_show_client()\n");
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return false;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// Get event inf
|
|
webui_event_inf_t* event_inf = win->events[event_number];
|
|
if (event_inf == NULL)
|
|
return false;
|
|
|
|
webui_event_t e;
|
|
e.window = window;
|
|
e.event_number = event_number;
|
|
e.connection_id = event_inf->connection_id;
|
|
|
|
return webui_show_client(&e, content);
|
|
}
|
|
|
|
void webui_interface_close_client(size_t window, size_t event_number) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_interface_close_client()\n");
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// Get event inf
|
|
webui_event_inf_t* event_inf = win->events[event_number];
|
|
if (event_inf == NULL)
|
|
return;
|
|
|
|
webui_event_t e;
|
|
e.window = window;
|
|
e.event_number = event_number;
|
|
e.connection_id = event_inf->connection_id;
|
|
|
|
webui_close_client(&e);
|
|
}
|
|
|
|
void webui_interface_send_raw_client(size_t window, size_t event_number, const char* function, const void* raw, size_t size) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_interface_send_raw_client()\n");
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// Get event inf
|
|
webui_event_inf_t* event_inf = win->events[event_number];
|
|
if (event_inf == NULL)
|
|
return;
|
|
|
|
webui_event_t e;
|
|
e.window = window;
|
|
e.event_number = event_number;
|
|
e.connection_id = event_inf->connection_id;
|
|
|
|
webui_send_raw_client(&e, function, raw, size);
|
|
}
|
|
|
|
void webui_interface_navigate_client(size_t window, size_t event_number, const char* url) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_interface_navigate_client()\n");
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// Get event inf
|
|
webui_event_inf_t* event_inf = win->events[event_number];
|
|
if (event_inf == NULL)
|
|
return;
|
|
|
|
webui_event_t e;
|
|
e.window = window;
|
|
e.event_number = event_number;
|
|
e.connection_id = event_inf->connection_id;
|
|
|
|
webui_navigate_client(&e, url);
|
|
}
|
|
|
|
void webui_interface_run_client(size_t window, size_t event_number, const char* script) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_interface_run_client()\n");
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// Get event inf
|
|
webui_event_inf_t* event_inf = win->events[event_number];
|
|
if (event_inf == NULL)
|
|
return;
|
|
|
|
webui_event_t e;
|
|
e.window = window;
|
|
e.event_number = event_number;
|
|
e.connection_id = event_inf->connection_id;
|
|
|
|
webui_run_client(&e, script);
|
|
}
|
|
|
|
bool webui_interface_script_client(size_t window, size_t event_number, const char* script, size_t timeout, char* buffer, size_t buffer_length) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[User] webui_interface_script_client()\n");
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui_init();
|
|
|
|
// Dereference
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
|
|
return false;
|
|
_webui_window_t* win = _webui.wins[window];
|
|
|
|
// Get event inf
|
|
webui_event_inf_t* event_inf = win->events[event_number];
|
|
if (event_inf == NULL)
|
|
return false;
|
|
|
|
webui_event_t e;
|
|
e.window = window;
|
|
e.event_number = event_number;
|
|
e.connection_id = event_inf->connection_id;
|
|
|
|
return webui_script_client(&e, script, timeout, buffer, buffer_length);
|
|
}
|
|
|
|
// -- Core's Functions ----------------
|
|
static bool _webui_ptr_exist(void * ptr) {
|
|
|
|
#ifdef WEBUI_LOG_VERBOSE
|
|
printf("[Core]\t\t_webui_ptr_exist()\n");
|
|
#endif
|
|
|
|
if (ptr == NULL)
|
|
return false;
|
|
|
|
for (size_t i = 0; i < _webui.ptr_last_pos; i++) {
|
|
if (_webui.ptr_list[i] == ptr)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void _webui_ptr_add(void * ptr, size_t size) {
|
|
|
|
#ifdef WEBUI_LOG_VERBOSE
|
|
printf("[Core]\t\t_webui_ptr_add(0x%p)\n", ptr);
|
|
#endif
|
|
|
|
if (ptr == NULL)
|
|
return;
|
|
|
|
// Search for first empty slot & save pointer
|
|
if (!_webui_ptr_exist(ptr)) {
|
|
size_t i = 0;
|
|
for (; i < _webui.ptr_last_pos; i++) {
|
|
if (_webui.ptr_list[i] == NULL) {
|
|
// Pointer found
|
|
break;
|
|
}
|
|
}
|
|
if (i == _webui.ptr_last_pos) {
|
|
// Pointer not found
|
|
i = _webui.ptr_last_pos++;
|
|
if (_webui.ptr_last_pos >= (WEBUI_MAX_IDS * 2)) {
|
|
_webui.ptr_last_pos = ((WEBUI_MAX_IDS * 2) - 1);
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_ptr_add(0x%p) -> ERROR: Maximum pointer capacity reached.\n",
|
|
ptr);
|
|
#endif
|
|
}
|
|
}
|
|
// Add pointer
|
|
_webui.ptr_list[i] = ptr;
|
|
_webui.ptr_size[i] = size;
|
|
#ifdef WEBUI_LOG_VERBOSE
|
|
printf("[Core]\t\t_webui_ptr_add(0x%p) -> Pointer #%zu saved (%zu + 1 bytes)\n", ptr, i, size);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void _webui_free_mem(void * ptr) {
|
|
|
|
#ifdef WEBUI_LOG_VERBOSE
|
|
printf("[Core]\t\t_webui_free_mem(0x%p)\n", ptr);
|
|
#endif
|
|
|
|
if (ptr == NULL)
|
|
return;
|
|
|
|
_webui_mutex_lock(&_webui.mutex_mem);
|
|
|
|
// Search for pointer & free
|
|
for (size_t i = 0; i < _webui.ptr_last_pos; i++) {
|
|
if (_webui.ptr_list[i] == ptr) {
|
|
#ifdef WEBUI_LOG_VERBOSE
|
|
printf("[Core]\t\t_webui_free_mem(0x%p) -> Pointer #%zu freed (%zu + 1 bytes)\n",
|
|
ptr, i, _webui.ptr_size[i]);
|
|
#endif
|
|
free(ptr);
|
|
_webui.ptr_size[i] = 0;
|
|
_webui.ptr_list[i] = NULL;
|
|
}
|
|
}
|
|
|
|
// Search (backward) for first empty slot
|
|
for (int i = _webui.ptr_last_pos; i >= 0;i--) {
|
|
if (_webui.ptr_list[i] == NULL) {
|
|
_webui.ptr_last_pos = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
_webui_mutex_unlock(&_webui.mutex_mem);
|
|
}
|
|
|
|
static void _webui_free_all_mem(void) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_free_all_mem()\n");
|
|
#endif
|
|
|
|
_webui_mutex_lock(&_webui.mutex_mem);
|
|
|
|
// Makes sure we run this once
|
|
static bool freed = false;
|
|
if (freed)
|
|
return;
|
|
freed = true;
|
|
|
|
// Free all pointers in the list
|
|
void * ptr = NULL;
|
|
for (size_t i = 0; i < _webui.ptr_last_pos; i++) {
|
|
ptr = _webui.ptr_list[i];
|
|
if (ptr != NULL) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_free_all_mem() -> Pointer #%zu freed (%zu + 1 bytes) 0x%p\n",
|
|
i, _webui.ptr_size[i], ptr);
|
|
#endif
|
|
free(ptr);
|
|
_webui.ptr_size[i] = 0;
|
|
_webui.ptr_list[i] = NULL;
|
|
}
|
|
}
|
|
|
|
_webui_mutex_unlock(&_webui.mutex_mem);
|
|
}
|
|
|
|
static void _webui_panic(char* msg) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_panic() -> %s.\n", msg);
|
|
#endif
|
|
|
|
fprintf(stderr, "WebUI Error: %s.\n", msg);
|
|
webui_exit();
|
|
}
|
|
|
|
static void * _webui_malloc(size_t size) {
|
|
|
|
#ifdef WEBUI_LOG_VERBOSE
|
|
printf("[Core]\t\t_webui_malloc([%zu])\n", size);
|
|
#endif
|
|
|
|
_webui_mutex_lock(&_webui.mutex_mem);
|
|
|
|
// Dynamic allocation + null terminator
|
|
void* mem = NULL;
|
|
mem = malloc(size + 1);
|
|
|
|
// Check
|
|
if (mem == NULL) {
|
|
WEBUI_ASSERT("malloc() failed");
|
|
return NULL;
|
|
}
|
|
|
|
// Ini memory block
|
|
memset(mem, 0, (size + 1));
|
|
|
|
// Add pointer to the list
|
|
_webui_ptr_add((void*)mem, size);
|
|
|
|
_webui_mutex_unlock(&_webui.mutex_mem);
|
|
|
|
return mem;
|
|
}
|
|
|
|
static _webui_window_t* _webui_dereference_win_ptr(void * ptr) {
|
|
|
|
#ifdef WEBUI_LOG_VERBOSE
|
|
//printf("[Core]\t\t_webui_dereference_win_ptr()\n");
|
|
#endif
|
|
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
|
|
return NULL;
|
|
|
|
_webui_window_t* win = (_webui_window_t* ) ptr;
|
|
|
|
for (size_t i = 1; i < WEBUI_MAX_IDS; i++) {
|
|
if (_webui.wins[i] == win)
|
|
return win;
|
|
}
|
|
|
|
// This pointer is not a valid
|
|
// webui window struct
|
|
return NULL;
|
|
}
|
|
|
|
static void _webui_sleep(long unsigned int ms) {
|
|
|
|
#ifdef WEBUI_LOG_VERBOSE
|
|
// printf("[Core]\t\t_webui_sleep([%zu])\n", ms);
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
Sleep(ms);
|
|
#else
|
|
struct timespec req;
|
|
req.tv_sec = ms / 1000; // Convert ms to seconds
|
|
req.tv_nsec = (ms % 1000) * 1000000L; // Convert remainder to nanoseconds
|
|
nanosleep(&req, NULL);
|
|
#endif
|
|
}
|
|
|
|
static long _webui_timer_diff(struct timespec * start, struct timespec* end) {
|
|
|
|
#ifdef WEBUI_LOG_VERBOSE
|
|
// printf("[Core]\t\t_webui_timer_diff()\n");
|
|
#endif
|
|
|
|
return ((long)(end->tv_sec * 1000) + (long)(end->tv_nsec / 1000000)) -
|
|
((long)(start->tv_sec * 1000) + (long)(start->tv_nsec / 1000000));
|
|
}
|
|
|
|
static void _webui_timer_clock_gettime(struct timespec * spec) {
|
|
|
|
#ifdef WEBUI_LOG_VERBOSE
|
|
// printf("[Core]\t\t_webui_timer_clock_gettime()\n");
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
__int64 wintime;
|
|
GetSystemTimeAsFileTime((FILETIME * )&wintime);
|
|
wintime -= ((__int64) 116444736000000000);
|
|
spec->tv_sec = wintime / ((__int64) 10000000);
|
|
spec->tv_nsec = wintime % ((__int64) 10000000) * 100;
|
|
#else
|
|
clock_gettime(CLOCK_MONOTONIC, spec);
|
|
#endif
|
|
}
|
|
|
|
static void _webui_timer_start(_webui_timer_t* t) {
|
|
|
|
#ifdef WEBUI_LOG_VERBOSE
|
|
printf("[Core]\t\t_webui_timer_start()\n");
|
|
#endif
|
|
|
|
_webui_timer_clock_gettime(&t->start);
|
|
}
|
|
|
|
static bool _webui_timer_is_end(_webui_timer_t* t, size_t ms) {
|
|
|
|
#ifdef WEBUI_LOG_VERBOSE
|
|
// printf("[Core]\t\t_webui_timer_is_end()\n");
|
|
#endif
|
|
|
|
_webui_timer_clock_gettime(&t->now);
|
|
|
|
size_t def = (size_t) _webui_timer_diff(&t->start,&t->now);
|
|
if (def > ms)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
static bool _webui_is_empty(const char* s) {
|
|
|
|
#ifdef WEBUI_LOG_VERBOSE
|
|
//printf("[Core]\t\t_webui_is_empty()\n");
|
|
#endif
|
|
|
|
if ((s != NULL) && (s[0] != '\0'))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
static size_t _webui_strlen(const char* s) {
|
|
|
|
#ifdef WEBUI_LOG_VERBOSE
|
|
//printf("[Core]\t\t_webui_strlen()\n");
|
|
#endif
|
|
|
|
if (_webui_is_empty(s))
|
|
return 0;
|
|
|
|
size_t length = 0;
|
|
|
|
while((s[length] != '\0') && (length < WEBUI_MAX_BUF)) {
|
|
length++;
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
static bool _webui_file_exist_mg(_webui_window_t* win, struct mg_connection* client) {
|
|
|
|
#ifdef WEBUI_LOG_VERBOSE
|
|
printf("[Core]\t\t_webui_file_exist_mg()\n");
|
|
#endif
|
|
|
|
char* file;
|
|
char* full_path;
|
|
|
|
const struct mg_request_info * ri = mg_get_request_info(client);
|
|
const char* url = ri->local_uri;
|
|
size_t url_len = _webui_strlen(url);
|
|
|
|
// Get file name
|
|
file = (char*)_webui_malloc(url_len);
|
|
const char* p = url;
|
|
p++; // Skip "/"
|
|
WEBUI_SN_PRINTF_DYN(file, url_len, "%.*s", (int)(url_len - 1), p);
|
|
|
|
// Get full path
|
|
// [current folder][/][file]
|
|
size_t bf_len = (_webui_strlen(win->server_root_path) + 1 + _webui_strlen(file));
|
|
full_path = (char*)_webui_malloc(bf_len);
|
|
WEBUI_SN_PRINTF_DYN(full_path, bf_len, "%s%s%s", win->server_root_path, os_sep, file);
|
|
|
|
bool exist = _webui_file_exist(full_path);
|
|
|
|
_webui_free_mem((void*)file);
|
|
_webui_free_mem((void*)full_path);
|
|
|
|
return exist;
|
|
}
|
|
|
|
static bool _webui_is_valid_url(const char* url) {
|
|
|
|
#ifdef WEBUI_LOG_VERBOSE
|
|
printf("[Core]\t\t_webui_is_valid_url([%.8s...])\n", url);
|
|
#endif
|
|
|
|
if ((_webui_is_empty(url)) || (url[0] != 'h'))
|
|
return false;
|
|
if (strncmp(url, "http://", 7) == 0 || strncmp(url, "https://", 8) == 0)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
static bool _webui_open_url_native(const char* url) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_open_url_native([%s])\n", url);
|
|
#endif
|
|
|
|
#if defined(_WIN32)
|
|
HINSTANCE result = ShellExecuteA(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL);
|
|
return ((INT_PTR) result > 32);
|
|
#elif defined(__APPLE__)
|
|
char command[1024];
|
|
WEBUI_SN_PRINTF_STATIC(command, sizeof(command), "open \"%s\"", url);
|
|
return (system(command) == 0);
|
|
#else
|
|
// Assuming Linux
|
|
char command[1024];
|
|
WEBUI_SN_PRINTF_STATIC(command, sizeof(command), "xdg-open \"%s\"", url);
|
|
return (system(command) == 0);
|
|
#endif
|
|
}
|
|
|
|
static bool _webui_file_exist(const char* path) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_file_exist([%s])\n", path);
|
|
#endif
|
|
|
|
if (_webui_is_empty(path))
|
|
return false;
|
|
|
|
// Parse home environments in the path
|
|
#ifdef _WIN32
|
|
// Windows
|
|
char full_path[WEBUI_MAX_PATH];
|
|
ExpandEnvironmentStringsA(path, full_path, sizeof(full_path));
|
|
#else
|
|
// Linux / macOS
|
|
char full_path[WEBUI_MAX_PATH];
|
|
if (path[0] == '~') {
|
|
const char* home = getenv("HOME");
|
|
if (home) {
|
|
WEBUI_SN_PRINTF_STATIC(full_path, sizeof(full_path), "%s/%s", home, &path[1]);
|
|
} else {
|
|
// If for some reason HOME isn't set
|
|
// fall back to the original path.
|
|
strncpy(full_path, path, sizeof(full_path));
|
|
}
|
|
} else {
|
|
strncpy(full_path, path, sizeof(full_path));
|
|
}
|
|
#endif
|
|
|
|
#ifdef WEBUI_LOG_VERBOSE
|
|
printf("[Core]\t\t_webui_file_exist() -> Parsed to [%s]\n", full_path);
|
|
#endif
|
|
|
|
#if defined(_WIN32)
|
|
// Convert UTF-8 to wide string on Windows
|
|
wchar_t* wfilePath;
|
|
if (!_webui_str_to_wide(full_path, &wfilePath))
|
|
return false;
|
|
DWORD dwAttrib = GetFileAttributesW(wfilePath);
|
|
_webui_free_mem((void*)wfilePath);
|
|
return (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib&FILE_ATTRIBUTE_DIRECTORY));
|
|
#else
|
|
// Linux / macOS
|
|
return (WEBUI_FILE_EXIST(full_path, 0) == 0);
|
|
#endif
|
|
}
|
|
|
|
static const char* _webui_get_extension(const char* f) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_get_extension()\n");
|
|
#endif
|
|
|
|
if (f == NULL)
|
|
return "";
|
|
|
|
const char* ext = strrchr(f, '.');
|
|
|
|
if (ext == NULL || !ext || ext == f)
|
|
return "";
|
|
return ext + 1;
|
|
}
|
|
|
|
static uint16_t _webui_get_run_id(void) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_get_run_id()\n");
|
|
#endif
|
|
|
|
if (_webui.run_last_id >= WEBUI_MAX_IDS)
|
|
_webui.run_last_id = 0;
|
|
return _webui.run_last_id++;
|
|
}
|
|
|
|
static bool _webui_socket_test_listen_mg(size_t port_num) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_socket_test_listen_mg([%zu])\n", port_num);
|
|
#endif
|
|
|
|
// HTTP Port Test
|
|
char* test_port = (char*)_webui_malloc(64);
|
|
WEBUI_SN_PRINTF_DYN(test_port, 64, "127.0.0.1:%zu", port_num);
|
|
|
|
// Start HTTP Server
|
|
const char* http_options[] = {
|
|
"listening_ports",
|
|
test_port,
|
|
NULL,
|
|
NULL
|
|
};
|
|
struct mg_callbacks http_callbacks;
|
|
struct mg_context * http_ctx;
|
|
memset(&http_callbacks, 0, sizeof(http_callbacks));
|
|
http_ctx = mg_start(&http_callbacks, 0, http_options);
|
|
|
|
if (http_ctx == NULL) {
|
|
|
|
// Cannot listen
|
|
mg_stop(http_ctx);
|
|
return false;
|
|
}
|
|
|
|
// Listening success
|
|
mg_stop(http_ctx);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool _webui_port_is_used(size_t port_num) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_port_is_used([%zu])\n", port_num);
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
// Listener test
|
|
if (!_webui_socket_test_listen_win32(port_num))
|
|
return true; // Port is already used
|
|
return false; // Port is not in use
|
|
#else
|
|
// Listener test MG
|
|
if (!_webui_socket_test_listen_mg(port_num))
|
|
return true; // Port is already used
|
|
return false; // Port is not in use
|
|
#endif
|
|
}
|
|
|
|
static char* _webui_get_file_name_from_url(const char* url) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_get_file_name_from_url([%s])\n", url);
|
|
#endif
|
|
|
|
if (_webui_is_empty(url))
|
|
return NULL;
|
|
|
|
// Find the position of "://"
|
|
const char* pos = strstr(url, "://");
|
|
if (pos == NULL) {
|
|
pos = url;
|
|
} else {
|
|
// Move the position after "://"
|
|
pos += 3;
|
|
}
|
|
|
|
// Find the position of the first '/'
|
|
pos = strchr(pos, '/');
|
|
if (pos == NULL) {
|
|
// Invalid URL
|
|
return NULL;
|
|
} else {
|
|
// Move the position after "/"
|
|
pos++;
|
|
}
|
|
|
|
// Copy the path to a new string
|
|
char* file = _webui_str_dup(pos);
|
|
|
|
// Find the position of the first '?'
|
|
char* question_mark = strchr(file, '?');
|
|
if (question_mark != NULL) {
|
|
// Replace '?' with NULL
|
|
* question_mark = '\0';
|
|
}
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_get_file_name_from_url() -> File name: [%s]\n", file);
|
|
#endif
|
|
|
|
return file;
|
|
}
|
|
|
|
static char* _webui_get_full_path(_webui_window_t* win, const char* file) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_get_full_path([%s])\n", file);
|
|
#endif
|
|
|
|
if (!file)
|
|
return NULL;
|
|
|
|
if (file[0] == '/')
|
|
file++;
|
|
|
|
// Get full path
|
|
// [current folder][/][file]
|
|
size_t bf_len = (_webui_strlen(win->server_root_path) + 1 + _webui_strlen(file));
|
|
char* full_path = (char*)_webui_malloc(bf_len);
|
|
WEBUI_SN_PRINTF_DYN(full_path, bf_len, "%s%s%s", win->server_root_path, os_sep, file);
|
|
|
|
#ifdef _WIN32
|
|
// Replace `/` by `\`
|
|
for (int i = 0; full_path[i] != '\0'; i++) {
|
|
if (full_path[i] == '/') {
|
|
full_path[i] = *os_sep;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef WEBUI_LOG_VERBOSE
|
|
printf("[Core]\t\t_webui_get_full_path() -> Full path: [%s]\n", full_path);
|
|
#endif
|
|
|
|
return full_path;
|
|
}
|
|
|
|
static size_t _webui_new_event_inf(_webui_window_t* win, webui_event_inf_t** event_inf) {
|
|
(*event_inf) = (webui_event_inf_t*)_webui_malloc(sizeof(webui_event_inf_t));
|
|
if (win->events_count > WEBUI_MAX_ARG)
|
|
win->events_count = 0;
|
|
size_t event_num = win->events_count++;
|
|
win->events[event_num] = (*event_inf);
|
|
return event_num;
|
|
}
|
|
|
|
static void _webui_free_event_inf(_webui_window_t* win, size_t event_num) {
|
|
webui_event_inf_t* event_inf = win->events[event_num];
|
|
for (size_t i = 0; i < (WEBUI_MAX_ARG + 1); i++) {
|
|
if (event_inf->event_data[i] != NULL)
|
|
webui_free((void*)event_inf->event_data[i]);
|
|
}
|
|
if (event_inf->response != NULL)
|
|
webui_free((void*)event_inf->response);
|
|
webui_free((void*)event_inf);
|
|
win->events[event_num] = NULL;
|
|
}
|
|
|
|
static int _webui_external_file_handler(_webui_window_t* win, struct mg_connection* client, size_t client_id) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_external_file_handler()\n");
|
|
#endif
|
|
|
|
int http_status_code = 0;
|
|
const struct mg_request_info * ri = mg_get_request_info(client);
|
|
const char* url = ri->local_uri;
|
|
|
|
if (win->files_handler != NULL || win->files_handler_window != NULL) {
|
|
// Get file content from the external files handler
|
|
size_t length = 0;
|
|
|
|
// This is a breaking changes, should be
|
|
// added in the next major WebUI version.
|
|
//
|
|
// New Event (HTTP)
|
|
// webui_event_t e;
|
|
// e.window = win->num;
|
|
// e.event_type = WEBUI_EVENT_HTTP_GET;
|
|
// e.element = "";
|
|
// e.event_number = 0;
|
|
// e.bind_id = 0;
|
|
// e.connection_id = 0;
|
|
// e.client_id = client_id;
|
|
// e.cookies = "";
|
|
|
|
// Files handler callback
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_external_file_handler() -> Path [%s]\n", url);
|
|
printf("[Core]\t\t_webui_external_file_handler() -> Calling custom files handler callback\n");
|
|
printf("[Call]\n");
|
|
#endif
|
|
|
|
// Async response ini
|
|
if (_webui.config.asynchronous_response) {
|
|
win->file_handler_async_response = NULL;
|
|
win->file_handler_async_len = 0;
|
|
win->file_handler_async_done = false;
|
|
}
|
|
|
|
// True if we pass the window num to the handler, false otherwise.
|
|
int is_file_handler_window = win->files_handler_window != NULL;
|
|
const void* callback_resp = is_file_handler_window ? win->files_handler_window(win->num, url, (int*)&length) : win->files_handler(url, (int*)&length);
|
|
|
|
// Async response wait
|
|
if (_webui.config.asynchronous_response) {
|
|
bool done = false;
|
|
while (!done) {
|
|
_webui_sleep(10);
|
|
_webui_mutex_lock(&_webui.mutex_async_response);
|
|
if(win->file_handler_async_done) done = true;
|
|
_webui_mutex_unlock(&_webui.mutex_async_response);
|
|
}
|
|
}
|
|
|
|
if (callback_resp != NULL) {
|
|
|
|
// File content found
|
|
|
|
if (length == 0)
|
|
length = _webui_strlen(callback_resp);
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_external_file_handler() -> Custom files handler found (%zu bytes)\n",
|
|
length
|
|
);
|
|
#endif
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("---[ External File Handler ]--------\n");
|
|
printf("%s\n", (char*)callback_resp);
|
|
printf("------------------------------------\n");
|
|
#endif
|
|
|
|
// Send user data (Header + Body)
|
|
mg_write(client, callback_resp, length);
|
|
|
|
// Safely free resources if end-user allocated
|
|
// using `webui_malloc()`. Otherwise just do nothing.
|
|
_webui_free_mem((void*)callback_resp);
|
|
|
|
http_status_code = 200;
|
|
}
|
|
else {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_external_file_handler() -> Custom files handler failed\n");
|
|
#endif
|
|
}
|
|
|
|
// If `data == NULL` thats mean the external handler
|
|
// did not find the requested file. So WebUI will try
|
|
// looking for the file locally.
|
|
}
|
|
|
|
return http_status_code;
|
|
}
|
|
|
|
static int _webui_serve_file(_webui_window_t* win, struct mg_connection* client, size_t client_id) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_serve_file()\n");
|
|
#endif
|
|
|
|
// Serve a normal text based file
|
|
|
|
int http_status_code = 0;
|
|
const struct mg_request_info * ri = mg_get_request_info(client);
|
|
const char* url = ri->local_uri;
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_serve_file() -> Looking for file locally\n");
|
|
#endif
|
|
|
|
// Get full path
|
|
char* file = _webui_get_file_name_from_url(url);
|
|
char* full_path = _webui_get_full_path(win, file);
|
|
|
|
if (_webui_file_exist(full_path)) {
|
|
|
|
// 200 - File exist
|
|
_webui_http_send_file(win, client, mg_get_builtin_mime_type(url), full_path, false);
|
|
http_status_code = 200;
|
|
}
|
|
else {
|
|
|
|
// 404 - File not exist
|
|
_webui_http_send_error(client, webui_html_res_not_available, 404);
|
|
http_status_code = 404;
|
|
}
|
|
|
|
_webui_free_mem((void*)full_path);
|
|
_webui_free_mem((void*)file);
|
|
|
|
return http_status_code;
|
|
}
|
|
|
|
static bool _webui_bun_exist(_webui_window_t* win) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_bun_exist()\n");
|
|
#endif
|
|
|
|
static bool found = false;
|
|
|
|
if (found)
|
|
return true;
|
|
|
|
if (_webui_cmd_sync(win, "bun -v", false) == 0) {
|
|
|
|
found = true;
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
static bool _webui_deno_exist(_webui_window_t* win) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_deno_exist()\n");
|
|
#endif
|
|
|
|
static bool found = false;
|
|
|
|
if (found)
|
|
return true;
|
|
|
|
if (_webui_cmd_sync(win, "deno --version", false) == 0) {
|
|
|
|
found = true;
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
static bool _webui_nodejs_exist(_webui_window_t* win) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_nodejs_exist()\n");
|
|
#endif
|
|
|
|
static bool found = false;
|
|
|
|
if (found)
|
|
return true;
|
|
|
|
if (_webui_cmd_sync(win, "node -v", false) == 0) {
|
|
|
|
found = true;
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
static const char* _webui_interpret_command(const char* cmd) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_interpret_command([%s])\n", cmd);
|
|
#endif
|
|
|
|
// Run the command with redirection of errors to stdout
|
|
// and return the output.
|
|
|
|
// Output buffer
|
|
char* out = NULL;
|
|
|
|
#ifdef _WIN32
|
|
// Redirect stderr to stdout
|
|
char cmd_with_redirection[512] = {0};
|
|
WEBUI_SN_PRINTF_STATIC(cmd_with_redirection, sizeof(cmd_with_redirection), "cmd.exe /c %s 2>&1", cmd);
|
|
_webui_system_win32_out(cmd_with_redirection,&out, false);
|
|
#else
|
|
// Redirect stderr to stdout
|
|
char cmd_with_redirection[512] = {0};
|
|
WEBUI_SN_PRINTF_STATIC(cmd_with_redirection, sizeof(cmd_with_redirection), "%s 2>&1", cmd);
|
|
|
|
FILE * pipe = WEBUI_POPEN(cmd_with_redirection, "r");
|
|
|
|
if (pipe == NULL)
|
|
return NULL;
|
|
|
|
// Read STDOUT
|
|
out = (char*)_webui_malloc(WEBUI_STDOUT_BUF);
|
|
char* line = (char*)_webui_malloc(1024);
|
|
while(fgets(line, 1024, pipe) != NULL)
|
|
WEBUI_STR_CAT_DYN(out, WEBUI_STDOUT_BUF, line);
|
|
WEBUI_PCLOSE(pipe);
|
|
|
|
// Clean
|
|
_webui_free_mem((void*)line);
|
|
#endif
|
|
|
|
return (const char*)out;
|
|
}
|
|
|
|
static void _webui_condition_init(webui_condition_t* cond) {
|
|
|
|
#ifdef _WIN32
|
|
InitializeConditionVariable(cond);
|
|
#else
|
|
pthread_cond_init(cond, NULL);
|
|
#endif
|
|
}
|
|
|
|
static void _webui_condition_wait(webui_condition_t* cond, webui_mutex_t* mutex) {
|
|
|
|
#ifdef _WIN32
|
|
SleepConditionVariableCS(cond, mutex, INFINITE);
|
|
#else
|
|
pthread_cond_wait(cond, mutex);
|
|
#endif
|
|
}
|
|
|
|
static void _webui_condition_signal(webui_condition_t* cond) {
|
|
|
|
#ifdef _WIN32
|
|
WakeConditionVariable(cond);
|
|
#else
|
|
pthread_cond_signal(cond);
|
|
#endif
|
|
}
|
|
|
|
static void _webui_condition_destroy(webui_condition_t* cond) {
|
|
|
|
#ifdef _WIN32
|
|
// No need
|
|
(void)cond;
|
|
#else
|
|
pthread_cond_destroy(cond);
|
|
#endif
|
|
}
|
|
|
|
static void _webui_mutex_init(webui_mutex_t* mutex) {
|
|
|
|
#ifdef _WIN32
|
|
InitializeCriticalSection(mutex);
|
|
#else
|
|
pthread_mutex_init(mutex, NULL);
|
|
#endif
|
|
}
|
|
|
|
static void _webui_mutex_lock(webui_mutex_t* mutex) {
|
|
|
|
#ifdef _WIN32
|
|
EnterCriticalSection(mutex);
|
|
#else
|
|
pthread_mutex_lock(mutex);
|
|
#endif
|
|
}
|
|
|
|
static void _webui_mutex_unlock(webui_mutex_t* mutex) {
|
|
|
|
#ifdef _WIN32
|
|
LeaveCriticalSection(mutex);
|
|
#else
|
|
pthread_mutex_unlock(mutex);
|
|
#endif
|
|
}
|
|
|
|
static void _webui_mutex_destroy(webui_mutex_t* mutex) {
|
|
|
|
#ifdef _WIN32
|
|
DeleteCriticalSection(mutex);
|
|
#else
|
|
pthread_mutex_destroy(mutex);
|
|
#endif
|
|
}
|
|
|
|
static int _webui_interpret_file(_webui_window_t* win, struct mg_connection* client, char* index, size_t client_id) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_interpret_file()\n");
|
|
#endif
|
|
|
|
// Interpret the file using JavaScript/TypeScript runtimes
|
|
// and send back the output. otherwise, send the file as a normal text based
|
|
|
|
int interpret_http_stat = 200;
|
|
char* file = NULL;
|
|
char* full_path = NULL;
|
|
const char* query = NULL;
|
|
|
|
const struct mg_request_info * ri = mg_get_request_info(client);
|
|
const char* url = ri->local_uri;
|
|
|
|
// Get file full path
|
|
if (index != NULL && !_webui_is_empty(index)) {
|
|
|
|
// Parse as index file
|
|
|
|
file = index;
|
|
full_path = index;
|
|
} else {
|
|
|
|
// Parse as other non-index files
|
|
|
|
// Get file name
|
|
file = _webui_get_file_name_from_url(url);
|
|
|
|
// Get full path
|
|
full_path = _webui_get_full_path(win, file);
|
|
}
|
|
|
|
// Get file extension
|
|
const char* extension = _webui_get_extension(file);
|
|
|
|
if (strcmp(extension, "ts") == 0 || strcmp(extension, "js") == 0) {
|
|
|
|
// TypeScript / JavaScript
|
|
|
|
if (!_webui_file_exist(full_path)) {
|
|
|
|
// File not exist - 404
|
|
_webui_http_send_error(client, webui_html_res_not_available, 404);
|
|
|
|
_webui_free_mem((void*)file);
|
|
_webui_free_mem((void*)full_path);
|
|
return 404;
|
|
}
|
|
|
|
// Get query
|
|
query = ri->query_string;
|
|
|
|
if (win->runtime == Deno) {
|
|
|
|
// Use Deno
|
|
if (_webui_deno_exist(win)) {
|
|
|
|
// Set command
|
|
// [disable coloring][deno][file][query]
|
|
size_t bf_len = (128 + _webui_strlen(full_path) + _webui_strlen(query));
|
|
char* cmd = (char*)_webui_malloc(bf_len);
|
|
#ifdef _WIN32
|
|
WEBUI_SN_PRINTF_DYN(
|
|
cmd, bf_len,
|
|
"Set NO_COLOR=1&Set DENO_NO_UPDATE_CHECK=1&deno run --quiet --allow-all --unstable-ffi --allow-ffi \"%s\" \"%s\"",
|
|
full_path, query
|
|
);
|
|
#else
|
|
WEBUI_SN_PRINTF_DYN(
|
|
cmd, bf_len,
|
|
"NO_COLOR=1;DENO_NO_UPDATE_CHECK=1;deno run --quiet --allow-all --unstable-ffi --allow-ffi \"%s\" \"%s\"",
|
|
full_path, query
|
|
);
|
|
#endif
|
|
|
|
// Run command
|
|
const char* out = _webui_interpret_command(cmd);
|
|
|
|
if (out != NULL) {
|
|
|
|
// Send Deno output 200
|
|
_webui_http_send(win, client, "text/plain", out, _webui_strlen(out), false);
|
|
}
|
|
else {
|
|
|
|
// Deno interpretation failed.
|
|
// Send back an empty `200 OK`
|
|
_webui_http_send(win, client, "text/plain", "", 0, false);
|
|
}
|
|
|
|
_webui_free_mem((void*)cmd);
|
|
_webui_free_mem((void*)out);
|
|
}
|
|
else {
|
|
|
|
// Deno not installed
|
|
// Send back an empty `200 OK`
|
|
_webui_http_send(win, client, "text/plain", "", 0, false);
|
|
}
|
|
} else if (win->runtime == Bun) {
|
|
|
|
// Use Bun
|
|
if (_webui_bun_exist(win)) {
|
|
|
|
// Set command
|
|
// [bun][file][query]
|
|
size_t bf_len = (32 + _webui_strlen(full_path) + _webui_strlen(query));
|
|
char* cmd = (char*)_webui_malloc(bf_len);
|
|
WEBUI_SN_PRINTF_DYN(cmd, bf_len, "bun \"%s\" \"%s\"", full_path, query);
|
|
|
|
// Run command
|
|
const char* out = _webui_interpret_command(cmd);
|
|
|
|
if (out != NULL) {
|
|
|
|
// Send Bun output 200
|
|
_webui_http_send(win, client, "text/plain", out, _webui_strlen(out), false);
|
|
}
|
|
else {
|
|
|
|
// Bun interpretation failed.
|
|
// Send back an empty `200 OK`
|
|
_webui_http_send(win, client, "text/plain", "", 0, false);
|
|
}
|
|
|
|
_webui_free_mem((void*)cmd);
|
|
_webui_free_mem((void*)out);
|
|
}
|
|
else {
|
|
|
|
// Bun not installed
|
|
// Send back an empty `200 OK`
|
|
_webui_http_send(win, client, "text/plain", "", 0, false);
|
|
}
|
|
} else if (win->runtime == NodeJS) {
|
|
|
|
// Use Nodejs
|
|
|
|
if (_webui_nodejs_exist(win)) {
|
|
|
|
// Set command
|
|
// [node][file]
|
|
size_t bf_len = (32 + _webui_strlen(full_path) + _webui_strlen(query));
|
|
char* cmd = (char*)_webui_malloc(bf_len);
|
|
WEBUI_SN_PRINTF_DYN(cmd, bf_len, "node \"%s\" \"%s\"", full_path, query);
|
|
|
|
// Run command
|
|
const char* out = _webui_interpret_command(cmd);
|
|
|
|
if (out != NULL) {
|
|
|
|
// Send Node.js output 200
|
|
_webui_http_send(win, client, "text/plain", out, _webui_strlen(out), false);
|
|
}
|
|
else {
|
|
|
|
// Node.js interpretation failed.
|
|
// Send back an empty `200 OK`
|
|
_webui_http_send(win, client, "text/plain", "", 0, false);
|
|
}
|
|
|
|
_webui_free_mem((void*)cmd);
|
|
_webui_free_mem((void*)out);
|
|
} else {
|
|
|
|
// Node.js not installed
|
|
// Send back an empty `200 OK`
|
|
_webui_http_send(win, client, "text/plain", "", 0, false);
|
|
}
|
|
} else {
|
|
|
|
// Unknown runtime
|
|
// Serve as a normal text-based file 200
|
|
_webui_http_send_file(win, client, mg_get_builtin_mime_type(url), full_path, false);
|
|
}
|
|
} else {
|
|
|
|
// Unknown file extension
|
|
|
|
// Serve as a normal text-based file
|
|
interpret_http_stat = _webui_serve_file(win, client, client_id);
|
|
}
|
|
|
|
_webui_free_mem((void*)file);
|
|
_webui_free_mem((void*)full_path);
|
|
|
|
return interpret_http_stat;
|
|
}
|
|
|
|
static const char* _webui_generate_js_bridge(_webui_window_t* win, struct mg_connection* client) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_generate_js_bridge()\n");
|
|
#endif
|
|
|
|
_webui_mutex_lock(&_webui.mutex_bridge);
|
|
|
|
// Token
|
|
uint32_t token = 0x00000000;
|
|
if (!_webui.config.multi_client) {
|
|
// Single client mode
|
|
if (_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE)) {
|
|
// Non-authorized request to `webui.js` because
|
|
// the single client already connected.
|
|
_webui_mutex_unlock(&_webui.mutex_bridge);
|
|
return NULL;
|
|
}
|
|
if (win->token == 0) {
|
|
win->token = _webui_generate_random_uint32();
|
|
token = win->token;
|
|
}
|
|
}
|
|
else {
|
|
// Multi client mode
|
|
if (win->token == 0) {
|
|
win->token = _webui_generate_random_uint32();
|
|
}
|
|
token = win->token;
|
|
}
|
|
|
|
// Generate the full WebUI Bridge
|
|
#ifdef WEBUI_LOG
|
|
const char* log = "true";
|
|
#else
|
|
const char* log = "false";
|
|
#endif
|
|
size_t len = 256 + _webui_strlen((const char*)webui_javascript_bridge);
|
|
char* js = (char*)_webui_malloc(len);
|
|
#ifdef WEBUI_TLS
|
|
const char* TLS = "true";
|
|
#else
|
|
const char* TLS = "false";
|
|
#endif
|
|
int c = WEBUI_SN_PRINTF_DYN(
|
|
js, len,
|
|
"%s\n document.addEventListener(\"DOMContentLoaded\",function(){ globalThis.webui = "
|
|
"new WebuiBridge({ secure: %s, token: %" PRIu32 ", port: %zu, log: %s, ",
|
|
webui_javascript_bridge, TLS, token, win->server_port, log
|
|
);
|
|
// Window Size
|
|
if (win->size_set)
|
|
c += WEBUI_SN_PRINTF_DYN(js + c, len, "winW: %u, winH: %u, ", win->width, win->height);
|
|
// Window Position
|
|
if (win->position_set)
|
|
c += WEBUI_SN_PRINTF_DYN(js + c, len, "winX: %u, winY: %u, ", win->x, win->y);
|
|
// Close
|
|
WEBUI_STR_CAT_DYN(js, len, "});});");
|
|
|
|
// Free
|
|
_webui_mutex_unlock(&_webui.mutex_bridge);
|
|
return js;
|
|
}
|
|
|
|
static bool _webui_mutex_is_connected(_webui_window_t* win, int update) {
|
|
|
|
bool status = false;
|
|
_webui_mutex_lock(&_webui.mutex_win_connect);
|
|
if (update == WEBUI_MUTEX_TRUE) win->connected = true;
|
|
else if (update == WEBUI_MUTEX_FALSE) win->connected = false;
|
|
status = ((win->clients_count > 0) && (win->connected));
|
|
_webui_mutex_unlock(&_webui.mutex_win_connect);
|
|
return status;
|
|
}
|
|
|
|
static bool _webui_mutex_is_single_client_token_valid(_webui_window_t* win, int update) {
|
|
|
|
bool status = false;
|
|
_webui_mutex_lock(&_webui.mutex_token);
|
|
if (update == WEBUI_MUTEX_TRUE) win->single_client_token_check = true;
|
|
else if (update == WEBUI_MUTEX_FALSE) win->single_client_token_check = false;
|
|
status = win->single_client_token_check;
|
|
_webui_mutex_unlock(&_webui.mutex_token);
|
|
return status;
|
|
}
|
|
|
|
static bool _webui_mutex_is_multi_client_token_valid(_webui_window_t* win, int update, int index) {
|
|
|
|
bool status = false;
|
|
_webui_mutex_lock(&_webui.mutex_token);
|
|
if (update == WEBUI_MUTEX_TRUE) _webui.clients_token_check[index] = true;
|
|
else if (update == WEBUI_MUTEX_FALSE) _webui.clients_token_check[index] = false;
|
|
status = _webui.clients_token_check[index];
|
|
_webui_mutex_unlock(&_webui.mutex_token);
|
|
return status;
|
|
}
|
|
|
|
static bool _webui_mutex_is_exit_now(int update) {
|
|
|
|
bool status = false;
|
|
_webui_mutex_lock(&_webui.mutex_exit_now);
|
|
if (update == WEBUI_MUTEX_TRUE) _webui.exit_now = true;
|
|
else if (update == WEBUI_MUTEX_FALSE) _webui.exit_now = false;
|
|
status = _webui.exit_now;
|
|
_webui_mutex_unlock(&_webui.mutex_exit_now);
|
|
return status;
|
|
}
|
|
|
|
static bool _webui_mutex_is_webview_update(_webui_window_t* win, int update) {
|
|
|
|
bool status = false;
|
|
_webui_mutex_lock(&_webui.mutex_webview_stop);
|
|
if (update == WEBUI_MUTEX_TRUE) win->update_webview = true;
|
|
else if (update == WEBUI_MUTEX_FALSE) win->update_webview = false;
|
|
status = win->update_webview;
|
|
_webui_mutex_unlock(&_webui.mutex_webview_stop);
|
|
return status;
|
|
}
|
|
|
|
static bool _webui_browser_create_new_profile(_webui_window_t* win, size_t browser) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_browser_create_new_profile(%zu)\n", browser);
|
|
#endif
|
|
|
|
// Default local machine profile
|
|
if (win->default_profile)
|
|
return true;
|
|
|
|
if (win->custom_profile) {
|
|
|
|
// Custom profile
|
|
if (_webui_is_empty(win->profile_path) || _webui_is_empty(win->profile_name))
|
|
return false;
|
|
} else {
|
|
|
|
// WebUI profile
|
|
if (win->profile_name != NULL)
|
|
_webui_free_mem((void*)win->profile_name);
|
|
if (win->profile_path != NULL)
|
|
_webui_free_mem((void*)win->profile_path);
|
|
win->profile_path = (char*)_webui_malloc(WEBUI_MAX_PATH);
|
|
win->profile_name = (char*)_webui_malloc(WEBUI_MAX_PATH);
|
|
if(!win->disable_browser_high_contrast)
|
|
WEBUI_STR_COPY_DYN(win->profile_name, WEBUI_MAX_PATH, WEBUI_PROFILE_NAME);
|
|
else
|
|
WEBUI_STR_COPY_DYN(win->profile_name, WEBUI_MAX_PATH, WEBUI_PROFILE_NAME "-NoHC");
|
|
}
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[Core]\t\t_webui_browser_create_new_profile(%zu) -> Generating WebUI profile\n",
|
|
browser
|
|
);
|
|
#endif
|
|
|
|
// Temp folder
|
|
const char* temp = _webui_get_temp_path();
|
|
|
|
if (browser == Chrome) {
|
|
|
|
// Google Chrome
|
|
if (!win->custom_profile)
|
|
WEBUI_SN_PRINTF_DYN(win->profile_path, WEBUI_MAX_PATH, "%s%s.WebUI%sWebUIChromeProfile", temp, os_sep, os_sep);
|
|
return true;
|
|
} else if (browser == Edge) {
|
|
|
|
// Edge
|
|
if (!win->custom_profile)
|
|
WEBUI_SN_PRINTF_DYN(win->profile_path, WEBUI_MAX_PATH, "%s%s.WebUI%sWebUIEdgeProfile", temp, os_sep, os_sep);
|
|
return true;
|
|
} else if (browser == Epic) {
|
|
|
|
// Epic
|
|
if (!win->custom_profile)
|
|
WEBUI_SN_PRINTF_DYN(win->profile_path, WEBUI_MAX_PATH, "%s%s.WebUI%sWebUIEpicProfile", temp, os_sep, os_sep);
|
|
return true;
|
|
} else if (browser == Vivaldi) {
|
|
|
|
// Vivaldi
|
|
if (!win->custom_profile)
|
|
WEBUI_SN_PRINTF_DYN(win->profile_path, WEBUI_MAX_PATH, "%s%s.WebUI%sWebUIVivaldiProfile", temp, os_sep, os_sep);
|
|
return true;
|
|
} else if (browser == Brave) {
|
|
|
|
// Brave
|
|
if (!win->custom_profile)
|
|
WEBUI_SN_PRINTF_DYN(win->profile_path, WEBUI_MAX_PATH, "%s%s.WebUI%sWebUIBraveProfile", temp, os_sep, os_sep);
|
|
return true;
|
|
} else if (browser == Yandex) {
|
|
|
|
// Yandex
|
|
if (!win->custom_profile)
|
|
WEBUI_SN_PRINTF_DYN(win->profile_path, WEBUI_MAX_PATH, "%s%s.WebUI%sWebUIYandexProfile", temp, os_sep, os_sep);
|
|
return true;
|
|
} else if (browser == Chromium) {
|
|
|
|
// Chromium
|
|
if (!win->custom_profile)
|
|
WEBUI_SN_PRINTF_DYN(win->profile_path, WEBUI_MAX_PATH, "%s%s.WebUI%sWebUIChromiumProfile", temp, os_sep, os_sep);
|
|
return true;
|
|
} else if (browser == Firefox) {
|
|
|
|
// Firefox (We need to create the profile folder)
|
|
|
|
#ifdef _WIN32
|
|
// Windows
|
|
const char* path_ini = "%APPDATA%\\Mozilla\\Firefox\\profiles.ini";
|
|
#elif __linux__
|
|
// Linux
|
|
bool snap = false;
|
|
const char* path_ini = "";
|
|
if (_webui_file_exist("~/.mozilla/firefox/profiles.ini"))
|
|
path_ini = "~/.mozilla/firefox/profiles.ini";
|
|
else if (_webui_file_exist("~/snap/firefox/common/.mozilla/firefox/profiles.ini")) {
|
|
path_ini = "~/snap/firefox/common/.mozilla/firefox/profiles.ini";
|
|
snap = true;
|
|
}
|
|
else return false;
|
|
// Firefox linux snap version
|
|
// ~/snap/firefox/common/.mozilla/firefox/WebUIFirefoxProfile
|
|
char snap_path[WEBUI_MAX_PATH];
|
|
const char* home = getenv("HOME");
|
|
if (home) {
|
|
WEBUI_SN_PRINTF_STATIC(snap_path, sizeof(snap_path), "%s/snap/firefox/common/.mozilla/firefox", home);
|
|
temp = snap_path;
|
|
} else {
|
|
// Failed to get HOME
|
|
return false;
|
|
}
|
|
#else
|
|
// macOS
|
|
const char* path_ini = "~/Library/Application Support/Firefox/profiles.ini";
|
|
#endif
|
|
|
|
if (!win->custom_profile){
|
|
if(!win->disable_browser_high_contrast)
|
|
WEBUI_SN_PRINTF_DYN(win->profile_path, WEBUI_MAX_PATH, "%s%s.WebUI%sWebUIFirefoxProfile", temp, os_sep, os_sep);
|
|
else
|
|
WEBUI_SN_PRINTF_DYN(win->profile_path, WEBUI_MAX_PATH, "%s%s.WebUI%sWebUIFirefoxProfile-NoHC", temp, os_sep, os_sep);
|
|
}
|
|
|
|
if (!_webui_folder_exist(win->profile_path) ||
|
|
!_webui_is_firefox_ini_profile_exist(path_ini, win->profile_name)) {
|
|
|
|
char buf[2048] = {0};
|
|
|
|
// There is a possibility that the profile name
|
|
// does not exist in the INI file. or folder does not exist.
|
|
// let's delete the profile name from the ini file, and folder.
|
|
_webui_remove_firefox_profile_ini(path_ini, win->profile_name);
|
|
_webui_delete_folder(win->profile_path);
|
|
|
|
// Creating the Firefox profile
|
|
WEBUI_SN_PRINTF_STATIC(
|
|
buf, sizeof(buf), "%s -CreateProfile \"%s %s\"", win->browser_path, win->profile_name, win->profile_path
|
|
);
|
|
_webui_cmd_sync(win, buf, false);
|
|
|
|
// Wait for Firefox profile to be created
|
|
_webui_timer_t timer;
|
|
_webui_timer_start(&timer);
|
|
for (;;) {
|
|
_webui_sleep(1000);
|
|
if (_webui_folder_exist(win->profile_path))
|
|
break;
|
|
if (_webui_timer_is_end(&timer, 10000))
|
|
break;
|
|
}
|
|
|
|
// Check if Firefox profile is created
|
|
if (!_webui_folder_exist(win->profile_path) ||
|
|
!_webui_is_firefox_ini_profile_exist(path_ini, win->profile_name))
|
|
return false;
|
|
|
|
// prefs.js
|
|
FILE * file;
|
|
WEBUI_SN_PRINTF_STATIC(buf, sizeof(buf), "%s%sprefs.js", win->profile_path, os_sep);
|
|
WEBUI_FILE_OPEN(file, buf, "a");
|
|
if (file == NULL)
|
|
return false;
|
|
fputs(
|
|
"user_pref(\"toolkit.legacyUserProfileCustomizations.stylesheets\","
|
|
" true);",
|
|
file
|
|
);
|
|
fputs("user_pref(\"browser.shell.checkDefaultBrowser\", false);", file);
|
|
fputs("user_pref(\"browser.tabs.warnOnClose\", false);", file);
|
|
fputs("user_pref(\"browser.tabs.inTitlebar\", 0);", file);
|
|
if(win->disable_browser_high_contrast){
|
|
fputs("user_pref(\"browser.display.document_color_use\", 1);", file);
|
|
}
|
|
fclose(file);
|
|
|
|
// userChrome.css
|
|
WEBUI_SN_PRINTF_STATIC(buf, sizeof(buf), "\"%s%schrome%s\"", win->profile_path, os_sep, os_sep);
|
|
if (!_webui_folder_exist(buf)) {
|
|
|
|
WEBUI_SN_PRINTF_STATIC(buf, sizeof(buf), "mkdir \"%s%schrome%s\"", win->profile_path, os_sep, os_sep);
|
|
_webui_cmd_sync(win, buf, false); // Create directory
|
|
}
|
|
WEBUI_SN_PRINTF_STATIC(buf, sizeof(buf), "%s%schrome%suserChrome.css", win->profile_path, os_sep, os_sep);
|
|
WEBUI_FILE_OPEN(file, buf, "a");
|
|
if (file == NULL)
|
|
return false;
|
|
fputs(
|
|
"#navigator-toolbox{opacity:0 !important;height:0px !important;max-height:0px !important;"
|
|
"width:0px !important;max-width:0px !important;}"
|
|
, file);
|
|
fclose(file);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool _webui_folder_exist(const char* folder) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_folder_exist([%s])\n", folder);
|
|
#endif
|
|
|
|
#if defined(_WIN32)
|
|
DWORD attributes = GetFileAttributesA(folder);
|
|
if ((attributes != INVALID_FILE_ATTRIBUTES) && (attributes & FILE_ATTRIBUTE_DIRECTORY))
|
|
return true;
|
|
#else
|
|
DIR * dir = opendir(folder);
|
|
if (dir) {
|
|
closedir(dir);
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
static void _webui_delete_folder(char* folder) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_delete_folder([%s])\n", folder);
|
|
#endif
|
|
|
|
char command[1024];
|
|
#if defined(_WIN32)
|
|
WEBUI_SN_PRINTF_STATIC(command, sizeof(command), "cmd /c \"rmdir /s /q \"%s\"\" > nul 2>&1", folder);
|
|
#else
|
|
WEBUI_SN_PRINTF_STATIC(command, sizeof(command), "rm -rf \"%s\" >>/dev/null 2>>/dev/null", folder);
|
|
#endif
|
|
|
|
// Try 6 times in 3 seconds
|
|
for (size_t i = 0; i < 6; i++) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_delete_folder() -> Running [%s] \n", command);
|
|
#endif
|
|
|
|
#if defined(_WIN32)
|
|
_webui_system_win32(NULL, command, false);
|
|
#else
|
|
system(command);
|
|
#endif
|
|
|
|
if (!_webui_folder_exist(folder))
|
|
break;
|
|
_webui_sleep(500);
|
|
}
|
|
}
|
|
|
|
static uint32_t _webui_get_token(const char* data) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_get_token()\n");
|
|
#endif
|
|
|
|
uint32_t token = 0;
|
|
|
|
// Little-endian
|
|
token |= ((uint32_t) data[WEBUI_PROTOCOL_TOKEN]&0xFF);
|
|
token |= ((uint32_t) data[WEBUI_PROTOCOL_TOKEN + 1]&0xFF) << 8;
|
|
token |= ((uint32_t) data[WEBUI_PROTOCOL_TOKEN + 2]&0xFF) << 16;
|
|
token |= ((uint32_t) data[WEBUI_PROTOCOL_TOKEN + 3]&0xFF) << 24;
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_get_token() -> 0x%08X\n", token);
|
|
#endif
|
|
|
|
return token;
|
|
}
|
|
|
|
static uint16_t _webui_get_id(const char* data) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_get_id()\n");
|
|
#endif
|
|
|
|
uint16_t id = 0;
|
|
|
|
// Little-endian
|
|
id |= ((uint16_t) data[WEBUI_PROTOCOL_ID]&0xFF);
|
|
id |= ((uint16_t) data[WEBUI_PROTOCOL_ID + 1]&0xFF) << 8;
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_get_id() -> 0x%04X\n", id);
|
|
#endif
|
|
|
|
return id;
|
|
}
|
|
|
|
static void _webui_send_all(_webui_window_t* win, uint16_t id, unsigned char cmd, const char* data, size_t len) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_send_all()\n");
|
|
#endif
|
|
|
|
// Send the WebSocket packet to a all connected clients if
|
|
// `multi_client` mode is enabled, if not then send packet
|
|
// to the only single connected client.
|
|
|
|
// Send the packet
|
|
if (_webui.config.multi_client) {
|
|
// Loop trough all connected clients in this window
|
|
for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
|
|
if ((_webui.clients[i] != NULL) && (_webui.clients_win_num[i] == win->num) &&
|
|
(_webui_mutex_is_multi_client_token_valid(win, WEBUI_MUTEX_NONE, i))) {
|
|
_webui_send_client(win, _webui.clients[i], 0, cmd, data, len, false);
|
|
}
|
|
}
|
|
} else {
|
|
// Single client
|
|
if ((win->single_client != NULL) && (_webui_mutex_is_single_client_token_valid(win, WEBUI_MUTEX_NONE))) {
|
|
_webui_send_client(win, win->single_client, 0, cmd, data, len, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void _webui_send_client(
|
|
_webui_window_t* win, struct mg_connection *client,
|
|
uint16_t id, unsigned char cmd, const char* data, size_t len, bool token_bypass) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_send_client()\n");
|
|
#endif
|
|
|
|
// Get connection id
|
|
size_t connection_id = 0;
|
|
if (!_webui_connection_get_id(win, client, &connection_id))
|
|
return;
|
|
if (_webui.clients[connection_id] == NULL)
|
|
return;
|
|
|
|
// Check Token
|
|
if (!token_bypass) {
|
|
if (!_webui_mutex_is_multi_client_token_valid(win, WEBUI_MUTEX_NONE, connection_id))
|
|
return;
|
|
}
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_send_client() -> Connection ID = %zu \n", connection_id);
|
|
printf("[Core]\t\t_webui_send_client() -> Packet ID = 0x%04X \n", id);
|
|
printf("[Core]\t\t_webui_send_client() -> Packet CMD = 0x%02x \n", cmd);
|
|
printf("[Core]\t\t_webui_send_client() -> Packet Data = %zu bytes \n", len);
|
|
#endif
|
|
|
|
// Protocol
|
|
// 0: [SIGNATURE]
|
|
// 1: [TOKEN]
|
|
// 2: [ID]
|
|
// 3: [CMD]
|
|
// 4: [Data]
|
|
|
|
// Prepare the packet
|
|
size_t packet_len = WEBUI_PROTOCOL_SIZE + len + 1;
|
|
char* packet = (char*)_webui_malloc(packet_len);
|
|
|
|
// Signature (1 Byte)
|
|
packet[WEBUI_PROTOCOL_SIGN] = WEBUI_SIGNATURE;
|
|
|
|
// Little-endian
|
|
// Token (4 Bytes)
|
|
packet[WEBUI_PROTOCOL_TOKEN] = 0xFF;
|
|
packet[WEBUI_PROTOCOL_TOKEN + 1] = 0xFF;
|
|
packet[WEBUI_PROTOCOL_TOKEN + 2] = 0xFF;
|
|
packet[WEBUI_PROTOCOL_TOKEN + 3] = 0xFF;
|
|
|
|
// ID (2 Bytes)
|
|
packet[WEBUI_PROTOCOL_ID] = id&0xFF;
|
|
packet[WEBUI_PROTOCOL_ID + 1] = (id >> 8)&0xFF;
|
|
|
|
// Command (1 Byte)
|
|
packet[WEBUI_PROTOCOL_CMD] = cmd;
|
|
|
|
// Data (n Bytes)
|
|
if (len > 0) {
|
|
size_t j = WEBUI_PROTOCOL_DATA;
|
|
for (size_t i = 0; i < len; i++) {
|
|
packet[j++] = data[i];
|
|
}
|
|
}
|
|
|
|
// Send packet
|
|
_webui_send_client_ws(win, client, connection_id, packet, packet_len);
|
|
|
|
// Free
|
|
_webui_free_mem((void*)packet);
|
|
}
|
|
|
|
static char* _webui_str_dup(const char* src) {
|
|
size_t len = _webui_strlen(src);
|
|
char* dst = (char*)_webui_malloc(len);
|
|
WEBUI_STR_COPY_DYN(dst, len, src);
|
|
return dst;
|
|
}
|
|
|
|
static const char* _webui_get_temp_path() {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_get_temp_path()\n");
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
// Resolve %USERPROFILE%
|
|
#ifdef _MSC_VER
|
|
char* WinUserProfile = NULL;
|
|
size_t sz = 0;
|
|
if (_dupenv_s(&WinUserProfile,&sz, "USERPROFILE") != 0 || WinUserProfile == NULL)
|
|
return "";
|
|
#else
|
|
char* WinUserProfile = getenv("USERPROFILE");
|
|
if (WinUserProfile == NULL)
|
|
return "";
|
|
#endif
|
|
return WinUserProfile;
|
|
#elif __APPLE__
|
|
// Resolve $HOME
|
|
char* MacUserProfile = getenv("HOME");
|
|
if (MacUserProfile == NULL)
|
|
return "";
|
|
return MacUserProfile;
|
|
#else
|
|
// Resolve $HOME
|
|
char* LinuxUserProfile = getenv("HOME");
|
|
if (LinuxUserProfile == NULL)
|
|
return "";
|
|
return LinuxUserProfile;
|
|
#endif
|
|
}
|
|
|
|
static bool _webui_is_google_chrome_folder(const char* folder) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_is_google_chrome_folder([%s])\n", folder);
|
|
#endif
|
|
|
|
char browser_full_path[WEBUI_MAX_PATH];
|
|
|
|
// Make sure this folder is Google Chrome setup and not Chromium
|
|
// by checking if `master_preferences` file exist or `initial_preferences`
|
|
// Ref: https://support.google.com/chrome/a/answer/187948?hl=en
|
|
|
|
WEBUI_SN_PRINTF_STATIC(browser_full_path, WEBUI_MAX_PATH, "%s\\master_preferences", folder);
|
|
if (!_webui_file_exist(browser_full_path)) {
|
|
|
|
WEBUI_SN_PRINTF_STATIC(browser_full_path, WEBUI_MAX_PATH, "%s\\initial_preferences", folder);
|
|
if (!_webui_file_exist(browser_full_path))
|
|
return false; // This is Chromium or something else
|
|
}
|
|
|
|
// Make sure the browser executable file exist
|
|
WEBUI_SN_PRINTF_STATIC(browser_full_path, WEBUI_MAX_PATH, "%s\\chrome.exe", folder);
|
|
if (!_webui_file_exist(browser_full_path))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool _webui_browser_exist(_webui_window_t* win, size_t browser) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_browser_exist([%zu])\n", browser);
|
|
#endif
|
|
|
|
// Check if a web browser is installed on this machine
|
|
|
|
if (browser == Chrome) {
|
|
|
|
// Google Chrome
|
|
|
|
static bool ChromeExist = false;
|
|
if(win) {
|
|
if (ChromeExist && !_webui_is_empty(win->browser_path))
|
|
return true;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
|
|
// Google Chrome on Windows
|
|
|
|
char browser_folder[WEBUI_MAX_PATH];
|
|
|
|
// Search in `HKEY_LOCAL_MACHINE` (If Google Chrome installed for
|
|
// multi-user)
|
|
if (_webui_get_windows_reg_value(
|
|
HKEY_LOCAL_MACHINE,
|
|
L"Software\\Microsoft\\Windows\\CurrentVer"
|
|
L"sion\\App Paths\\chrome.exe",
|
|
L"Path", browser_folder
|
|
)) {
|
|
|
|
// Make sure its Google Chrome and not Chromium
|
|
if (_webui_is_google_chrome_folder(browser_folder)) {
|
|
|
|
// Google Chrome Found (multi-user)
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\\chrome.exe\"", browser_folder);
|
|
ChromeExist = true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Search in `HKEY_CURRENT_USER` (If Google Chrome installed for one user)
|
|
if (_webui_get_windows_reg_value(
|
|
HKEY_CURRENT_USER,
|
|
L"Software\\Microsoft\\Windows\\CurrentVer"
|
|
L"sion\\App Paths\\chrome.exe",
|
|
L"Path", browser_folder
|
|
)) {
|
|
|
|
// Make sure its Google Chrome and not Chromium
|
|
if (_webui_is_google_chrome_folder(browser_folder)) {
|
|
|
|
// Google Chrome Found (one user)
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\\chrome.exe\"", browser_folder);
|
|
ChromeExist = true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
|
|
#elif __APPLE__
|
|
|
|
// Google Chrome on macOS
|
|
if (_webui_cmd_sync(win, "open -R -a \"Google Chrome\"", false) == 0) {
|
|
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "open --new -a \"Google Chrome.app\" --args");
|
|
ChromeExist = true;
|
|
return true;
|
|
} else
|
|
return false;
|
|
#else
|
|
|
|
// Google Chrome on Linux
|
|
if (_webui_cmd_sync(win, "google-chrome --version", false) == 0) {
|
|
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "google-chrome");
|
|
ChromeExist = true;
|
|
return true;
|
|
} else if (_webui_cmd_sync(win, "google-chrome-stable --version", false) == 0) {
|
|
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "google-chrome-stable");
|
|
ChromeExist = true;
|
|
return true;
|
|
} else
|
|
return false;
|
|
|
|
#endif
|
|
} else if (browser == Edge) {
|
|
|
|
// Edge
|
|
|
|
static bool EdgeExist = false;
|
|
if(win) {
|
|
if (EdgeExist && !_webui_is_empty(win->browser_path))
|
|
return true;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
|
|
// Edge on Windows
|
|
|
|
char browser_fullpath[WEBUI_MAX_PATH];
|
|
|
|
// Search in `HKEY_LOCAL_MACHINE` (If Edge installed for multi-user)
|
|
if (_webui_get_windows_reg_value(
|
|
HKEY_LOCAL_MACHINE,
|
|
L"Software\\Microsoft\\Windows\\CurrentVer"
|
|
L"sion\\App Paths\\msedge.exe",
|
|
L"", browser_fullpath
|
|
)) {
|
|
|
|
// Make sure the browser executable file exist
|
|
if (_webui_file_exist(browser_fullpath)) {
|
|
|
|
// Edge Found (multi-user)
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\"", browser_fullpath);
|
|
EdgeExist = true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Search in `HKEY_CURRENT_USER` (If Edge installed for one user)
|
|
if (_webui_get_windows_reg_value(
|
|
HKEY_CURRENT_USER,
|
|
L"Software\\Microsoft\\Windows\\CurrentVer"
|
|
L"sion\\App Paths\\msedge.exe",
|
|
L"", browser_fullpath
|
|
)) {
|
|
|
|
// Make sure the browser executable file exist
|
|
if (_webui_file_exist(browser_fullpath)) {
|
|
|
|
// Edge Found (one user)
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\"", browser_fullpath);
|
|
EdgeExist = true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
|
|
#elif __APPLE__
|
|
|
|
// Edge on macOS
|
|
if (_webui_cmd_sync(win, "open -R -a \"Microsoft Edge\"", false) == 0) {
|
|
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "open --new -a \"Microsoft Edge.app\" --args");
|
|
EdgeExist = true;
|
|
return true;
|
|
} else
|
|
return false;
|
|
|
|
#else
|
|
|
|
// Edge on Linux
|
|
if (_webui_cmd_sync(win, "microsoft-edge-stable --version", false) == 0) {
|
|
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "microsoft-edge-stable");
|
|
EdgeExist = true;
|
|
return true;
|
|
} else if (_webui_cmd_sync(win, "microsoft-edge-beta --version", false) == 0) {
|
|
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "microsoft-edge-beta");
|
|
EdgeExist = true;
|
|
return true;
|
|
} else if (_webui_cmd_sync(win, "microsoft-edge-dev --version", false) == 0) {
|
|
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "microsoft-edge-dev");
|
|
EdgeExist = true;
|
|
return true;
|
|
} else
|
|
return false;
|
|
|
|
#endif
|
|
} else if (browser == Epic) {
|
|
|
|
// Epic Privacy Browser
|
|
|
|
static bool EpicExist = false;
|
|
if(win) {
|
|
if (EpicExist && !_webui_is_empty(win->browser_path))
|
|
return true;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
|
|
// Epic on Windows
|
|
|
|
char browser_fullpath[WEBUI_MAX_PATH];
|
|
|
|
// Search in `HKEY_CURRENT_USER` (If Epic installed for one user)
|
|
if (_webui_get_windows_reg_value(
|
|
HKEY_CURRENT_USER,
|
|
L"Software\\Microsoft\\Windows\\CurrentVer"
|
|
L"sion\\App Paths\\epic.exe",
|
|
L"", browser_fullpath
|
|
)) {
|
|
|
|
// Make sure the browser executable file exist
|
|
if (_webui_file_exist(browser_fullpath)) {
|
|
|
|
// Epic Found (one user)
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\"", browser_fullpath);
|
|
EpicExist = true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Search in `HKEY_LOCAL_MACHINE` (If Epic installed for multi-user)
|
|
if (_webui_get_windows_reg_value(
|
|
HKEY_LOCAL_MACHINE,
|
|
L"Software\\Microsoft\\Windows\\CurrentVer"
|
|
L"sion\\App Paths\\epic.exe",
|
|
L"", browser_fullpath
|
|
)) {
|
|
|
|
// Make sure the browser executable file exist
|
|
if (_webui_file_exist(browser_fullpath)) {
|
|
|
|
// Epic Found (multi-user)
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\"", browser_fullpath);
|
|
EpicExist = true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
|
|
#elif __APPLE__
|
|
|
|
// Epic on macOS
|
|
if (_webui_cmd_sync(win, "open -R -a \"Epic\"", false) == 0) {
|
|
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "open --new -a \"Epic.app\" --args");
|
|
EpicExist = true;
|
|
return true;
|
|
} else
|
|
return false;
|
|
#else
|
|
|
|
// Epic on Linux
|
|
if (_webui_cmd_sync(win, "epic --version", false) == 0) {
|
|
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "epic");
|
|
EpicExist = true;
|
|
return true;
|
|
} else
|
|
return false;
|
|
#endif
|
|
} else if (browser == Vivaldi) {
|
|
|
|
// Vivaldi Browser
|
|
|
|
static bool VivaldiExist = false;
|
|
if(win) {
|
|
if (VivaldiExist && !_webui_is_empty(win->browser_path))
|
|
return true;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
|
|
// Vivaldi on Windows
|
|
|
|
char browser_fullpath[WEBUI_MAX_PATH];
|
|
|
|
// Search in `HKEY_LOCAL_MACHINE` (If Vivaldi installed for multi-user)
|
|
if (_webui_get_windows_reg_value(
|
|
HKEY_LOCAL_MACHINE,
|
|
L"Software\\Microsoft\\Windows\\CurrentVer"
|
|
L"sion\\App Paths\\vivaldi.exe",
|
|
L"", browser_fullpath
|
|
)) {
|
|
|
|
// Make sure the browser executable file exist
|
|
if (_webui_file_exist(browser_fullpath)) {
|
|
|
|
// Vivaldi Found (multi-user)
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\"", browser_fullpath);
|
|
VivaldiExist = true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Search in `HKEY_CURRENT_USER` (If Vivaldi installed for one user)
|
|
if (_webui_get_windows_reg_value(
|
|
HKEY_CURRENT_USER,
|
|
L"Software\\Microsoft\\Windows\\CurrentVer"
|
|
L"sion\\App Paths\\vivaldi.exe",
|
|
L"", browser_fullpath
|
|
)) {
|
|
|
|
// Make sure the browser executable file exist
|
|
if (_webui_file_exist(browser_fullpath)) {
|
|
|
|
// Vivaldi Found (one user)
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\"", browser_fullpath);
|
|
VivaldiExist = true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
|
|
#elif __APPLE__
|
|
|
|
// Vivaldi on macOS
|
|
if (_webui_cmd_sync(win, "open -R -a \"Vivaldi\"", false) == 0) {
|
|
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "open --new -a \"Vivaldi.app\" --args");
|
|
VivaldiExist = true;
|
|
return true;
|
|
} else
|
|
return false;
|
|
#else
|
|
|
|
// Vivaldi on Linux
|
|
if (_webui_cmd_sync(win, "vivaldi --version", false) == 0) {
|
|
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "vivaldi");
|
|
VivaldiExist = true;
|
|
return true;
|
|
} else if (_webui_cmd_sync(win, "vivaldi-stable --version", false) == 0) {
|
|
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "vivaldi-stable");
|
|
VivaldiExist = true;
|
|
return true;
|
|
} else if (_webui_cmd_sync(win, "vivaldi-snapshot --version", false) == 0) {
|
|
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "vivaldi-snapshot");
|
|
VivaldiExist = true;
|
|
return true;
|
|
} else
|
|
return false;
|
|
#endif
|
|
} else if (browser == Brave) {
|
|
|
|
// Brave Browser
|
|
|
|
static bool BraveExist = false;
|
|
if(win) {
|
|
if (BraveExist && !_webui_is_empty(win->browser_path))
|
|
return true;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
|
|
// Brave on Windows
|
|
|
|
char browser_fullpath[WEBUI_MAX_PATH];
|
|
|
|
// Search in `HKEY_LOCAL_MACHINE` (If Brave installed for multi-user)
|
|
if (_webui_get_windows_reg_value(
|
|
HKEY_LOCAL_MACHINE,
|
|
L"Software\\Microsoft\\Windows\\CurrentVer"
|
|
L"sion\\App Paths\\brave.exe",
|
|
L"", browser_fullpath
|
|
)) {
|
|
|
|
// Make sure the browser executable file exist
|
|
if (_webui_file_exist(browser_fullpath)) {
|
|
|
|
// Brave Found (multi-user)
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\"", browser_fullpath);
|
|
BraveExist = true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Search in `HKEY_CURRENT_USER` (If Brave installed for one user)
|
|
if (_webui_get_windows_reg_value(
|
|
HKEY_CURRENT_USER,
|
|
L"Software\\Microsoft\\Windows\\CurrentVer"
|
|
L"sion\\App Paths\\brave.exe",
|
|
L"", browser_fullpath
|
|
)) {
|
|
|
|
// Make sure the browser executable file exist
|
|
if (_webui_file_exist(browser_fullpath)) {
|
|
|
|
// Brave Found (one user)
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\"", browser_fullpath);
|
|
BraveExist = true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
|
|
#elif __APPLE__
|
|
|
|
// Brave on macOS
|
|
if (_webui_cmd_sync(win, "open -R -a \"Brave Browser\"", false) == 0) {
|
|
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "open --new -a \"Brave Browser.app\" --args");
|
|
BraveExist = true;
|
|
return true;
|
|
} else
|
|
return false;
|
|
#else
|
|
|
|
// Brave on Linux
|
|
if (_webui_cmd_sync(win, "brave --version", false) == 0) {
|
|
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "brave");
|
|
BraveExist = true;
|
|
return true;
|
|
} else
|
|
return false;
|
|
#endif
|
|
} else if (browser == Firefox) {
|
|
|
|
// Firefox
|
|
|
|
static bool FirefoxExist = false;
|
|
if(win) {
|
|
if (FirefoxExist && !_webui_is_empty(win->browser_path))
|
|
return true;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
|
|
// Firefox on Windows
|
|
|
|
char browser_fullpath[WEBUI_MAX_PATH];
|
|
|
|
// Search in `HKEY_LOCAL_MACHINE` (If Firefox installed for multi-user)
|
|
if (_webui_get_windows_reg_value(
|
|
HKEY_LOCAL_MACHINE,
|
|
L"Software\\Microsoft\\Windows\\CurrentVer"
|
|
L"sion\\App Paths\\firefox.exe",
|
|
L"", browser_fullpath
|
|
)) {
|
|
|
|
// Make sure the browser executable file exist
|
|
if (_webui_file_exist(browser_fullpath)) {
|
|
|
|
// Firefox Found (multi-user)
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\"", browser_fullpath);
|
|
FirefoxExist = true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Search in `HKEY_CURRENT_USER` (If Firefox installed for one user)
|
|
if (_webui_get_windows_reg_value(
|
|
HKEY_CURRENT_USER,
|
|
L"Software\\Microsoft\\Windows\\CurrentVer"
|
|
L"sion\\App Paths\\firefox.exe",
|
|
L"", browser_fullpath
|
|
)) {
|
|
|
|
// Make sure the browser executable file exist
|
|
if (_webui_file_exist(browser_fullpath)) {
|
|
|
|
// Firefox Found (one user)
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\"", browser_fullpath);
|
|
FirefoxExist = true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
|
|
#elif __APPLE__
|
|
|
|
// Firefox on macOS
|
|
if (_webui_cmd_sync(win, "open -R -a \"Firefox\"", false) == 0) {
|
|
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "open --new -a \"Firefox.app\" --args");
|
|
FirefoxExist = true;
|
|
return true;
|
|
} else
|
|
return false;
|
|
#else
|
|
|
|
// Firefox on Linux
|
|
|
|
if (_webui_cmd_sync(win, "firefox -v", false) == 0) {
|
|
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "firefox");
|
|
FirefoxExist = true;
|
|
return true;
|
|
} else
|
|
return false;
|
|
|
|
#endif
|
|
|
|
} else if (browser == Yandex) {
|
|
|
|
// Yandex Browser
|
|
|
|
static bool YandexExist = false;
|
|
if(win) {
|
|
if (YandexExist && !_webui_is_empty(win->browser_path))
|
|
return true;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
|
|
// Yandex on Windows
|
|
|
|
char browser_fullpath[WEBUI_MAX_PATH];
|
|
|
|
// Search in `HKEY_CURRENT_USER` (If Yandex installed for one user)
|
|
if (_webui_get_windows_reg_value(
|
|
HKEY_CURRENT_USER,
|
|
L"Software\\Microsoft\\Windows\\CurrentVer"
|
|
L"sion\\App Paths\\browser.exe",
|
|
L"", browser_fullpath
|
|
)) {
|
|
|
|
// Make sure the browser executable file exist
|
|
if (_webui_file_exist(browser_fullpath)) {
|
|
|
|
// Yandex Found (one user)
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\"", browser_fullpath);
|
|
YandexExist = true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Search in `HKEY_LOCAL_MACHINE` (If Yandex installed for multi-user)
|
|
if (_webui_get_windows_reg_value(
|
|
HKEY_LOCAL_MACHINE,
|
|
L"Software\\Microsoft\\Windows\\CurrentVer"
|
|
L"sion\\App Paths\\browser.exe",
|
|
L"", browser_fullpath
|
|
)) {
|
|
|
|
// Make sure the browser executable file exist
|
|
if (_webui_file_exist(browser_fullpath)) {
|
|
|
|
// Yandex Found (multi-user)
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\"", browser_fullpath);
|
|
YandexExist = true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
|
|
#elif __APPLE__
|
|
|
|
// Yandex on macOS
|
|
if (_webui_cmd_sync(win, "open -R -a \"Yandex\"", false) == 0) {
|
|
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "open --new -a \"Yandex.app\" --args");
|
|
YandexExist = true;
|
|
return true;
|
|
} else
|
|
return false;
|
|
#else
|
|
|
|
// Yandex on Linux
|
|
if (_webui_cmd_sync(win, "yandex-browser --version", false) == 0) {
|
|
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "yandex-browser");
|
|
YandexExist = true;
|
|
return true;
|
|
} else
|
|
return false;
|
|
#endif
|
|
} else if (browser == Chromium) {
|
|
|
|
// The Chromium Projects
|
|
|
|
static bool ChromiumExist = false;
|
|
if(win) {
|
|
if (ChromiumExist && !_webui_is_empty(win->browser_path))
|
|
return true;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
|
|
// Chromium on Windows
|
|
|
|
char browser_folder[WEBUI_MAX_PATH];
|
|
|
|
// Search in `HKEY_CURRENT_USER` (If Chromium installed for one user)
|
|
if (_webui_get_windows_reg_value(
|
|
HKEY_CURRENT_USER,
|
|
L"Software\\Microsoft\\Windows\\CurrentVer"
|
|
L"sion\\App Paths\\chrome.exe",
|
|
L"Path", browser_folder
|
|
)) {
|
|
|
|
// Make sure its Chromium and not Google Chrome
|
|
if (!_webui_is_google_chrome_folder(browser_folder)) {
|
|
|
|
// Chromium Found (one user)
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\\chrome.exe\"", browser_folder);
|
|
ChromiumExist = true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Search in `HKEY_LOCAL_MACHINE` (If Chromium installed for multi-user)
|
|
if (_webui_get_windows_reg_value(
|
|
HKEY_LOCAL_MACHINE,
|
|
L"Software\\Microsoft\\Windows\\CurrentVer"
|
|
L"sion\\App Paths\\chrome.exe",
|
|
L"Path", browser_folder
|
|
)) {
|
|
|
|
// Make sure its Chromium and not Google Chrome
|
|
if (!_webui_is_google_chrome_folder(browser_folder)) {
|
|
|
|
// Chromium Found (multi-user)
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "\"%s\\chrome.exe\"", browser_folder);
|
|
ChromiumExist = true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
|
|
#elif __APPLE__
|
|
|
|
// Chromium on macOS
|
|
if (_webui_cmd_sync(win, "open -R -a \"Chromium\"", false) == 0) {
|
|
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "open --new -a \"Chromium.app\" --args");
|
|
ChromiumExist = true;
|
|
return true;
|
|
} else
|
|
return false;
|
|
#else
|
|
|
|
// Chromium on Linux
|
|
if (_webui_cmd_sync(win, "chromium-browser --version", false) == 0) {
|
|
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "chromium-browser");
|
|
ChromiumExist = true;
|
|
return true;
|
|
} else if (_webui_cmd_sync(win, "chromium --version", false) == 0) {
|
|
|
|
if(win) WEBUI_SN_PRINTF_DYN(win->browser_path, WEBUI_MAX_PATH, "chromium");
|
|
ChromiumExist = true;
|
|
return true;
|
|
} else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void _webui_clean(void) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_clean()\n");
|
|
#endif
|
|
|
|
static bool cleaned = false;
|
|
if (cleaned)
|
|
return;
|
|
cleaned = true;
|
|
|
|
// Stop all threads
|
|
_webui_mutex_is_exit_now(WEBUI_MUTEX_TRUE);
|
|
|
|
// Let's give other threads more time to safely exit
|
|
// and finish cleaning up.
|
|
// _webui_sleep(500);
|
|
|
|
// Clean all servers services
|
|
mg_exit_library();
|
|
|
|
// Free all non-freed memory allocations
|
|
_webui_free_all_mem();
|
|
|
|
// Destroy all mutex
|
|
_webui_mutex_destroy(&_webui.mutex_server_start);
|
|
_webui_mutex_destroy(&_webui.mutex_send);
|
|
_webui_mutex_destroy(&_webui.mutex_receive);
|
|
_webui_mutex_destroy(&_webui.mutex_wait);
|
|
_webui_mutex_destroy(&_webui.mutex_js_run);
|
|
_webui_mutex_destroy(&_webui.mutex_win_connect);
|
|
_webui_mutex_destroy(&_webui.mutex_exit_now);
|
|
_webui_mutex_destroy(&_webui.mutex_webview_stop);
|
|
_webui_mutex_destroy(&_webui.mutex_http_handler);
|
|
_webui_mutex_destroy(&_webui.mutex_client);
|
|
_webui_mutex_destroy(&_webui.mutex_async_response);
|
|
_webui_mutex_destroy(&_webui.mutex_mem);
|
|
_webui_mutex_destroy(&_webui.mutex_token);
|
|
_webui_condition_destroy(&_webui.condition_wait);
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\tWebUI exit successfully\n");
|
|
#endif
|
|
}
|
|
|
|
static int _webui_cmd_sync(_webui_window_t* win, char* cmd, bool show) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_cmd_sync()\n");
|
|
#endif
|
|
|
|
// Run sync command and
|
|
// return the exit code
|
|
|
|
char buf[2048] = {0};
|
|
|
|
// Sync command
|
|
#ifdef _WIN32
|
|
// Using: _CMD_
|
|
WEBUI_SN_PRINTF_STATIC(buf, sizeof(buf), "cmd /c \"%s\" > nul 2>&1", cmd);
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_cmd_sync() -> Running [%s] \n", buf);
|
|
#endif
|
|
return _webui_system_win32(win, buf, show);
|
|
#else
|
|
// Using: _CMD_
|
|
WEBUI_SN_PRINTF_STATIC(buf, sizeof(buf), "%s >>/dev/null 2>>/dev/null", cmd);
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_cmd_sync() -> Running [%s] \n", buf);
|
|
#endif
|
|
int r = system(buf);
|
|
r = (r != -1 && r != 127 && WIFEXITED(r)) ? WEXITSTATUS(r) : -1;
|
|
return r;
|
|
#endif
|
|
}
|
|
|
|
static int _webui_cmd_async(_webui_window_t* win, char* cmd, bool show) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_cmd_async()\n");
|
|
#endif
|
|
|
|
// Run a async command
|
|
// and return immediately
|
|
|
|
char buf[1024] = {0};
|
|
|
|
// Asynchronous command
|
|
#ifdef _WIN32
|
|
// Using: START "" _CMD_
|
|
WEBUI_SN_PRINTF_STATIC(buf, sizeof(buf), "cmd /c \"START \"\" %s\" > nul 2>&1", cmd);
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_cmd_async() -> Running [%s] \n", buf);
|
|
#endif
|
|
return _webui_system_win32(win, buf, show);
|
|
#else
|
|
// Using: _CMD_ &
|
|
WEBUI_SN_PRINTF_STATIC(buf, sizeof(buf), "%s >>/dev/null 2>>/dev/null &", cmd);
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_cmd_async() -> Running [%s] \n", buf);
|
|
#endif
|
|
int r = system(buf);
|
|
r = (r != -1 && r != 127 && WIFEXITED(r)) ? WEXITSTATUS(r) : -1;
|
|
return r;
|
|
#endif
|
|
}
|
|
|
|
static int _webui_run_browser(_webui_window_t* win, char* cmd) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_run_browser()\n");
|
|
#endif
|
|
|
|
// Run a async command
|
|
return _webui_cmd_async(win, cmd, false);
|
|
}
|
|
|
|
static int _webui_get_browser_args(_webui_window_t* win, size_t browser, char* buffer, size_t len) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_get_browser_args([%zu])\n", browser);
|
|
#endif
|
|
|
|
const char* chromium_options[] = {
|
|
"--no-first-run",
|
|
// "--no-proxy-server",
|
|
"--safe-mode",
|
|
"--disable-extensions",
|
|
"--disable-background-mode",
|
|
"--disable-plugins",
|
|
"--disable-plugins-discovery",
|
|
"--disable-translate",
|
|
"--disable-features=Translate",
|
|
"--bwsi",
|
|
"--disable-sync",
|
|
"--disable-sync-preferences",
|
|
"--disable-component-update",
|
|
"--allow-insecure-localhost",
|
|
"--auto-accept-camera-and-microphone-capture",
|
|
};
|
|
|
|
int c = 0;
|
|
switch(browser) {
|
|
case Chrome:
|
|
case Edge:
|
|
case Epic:
|
|
case Vivaldi:
|
|
case Brave:
|
|
case Yandex:
|
|
case Chromium:
|
|
// Profile
|
|
if (!_webui_is_empty(win->profile_path))
|
|
c = WEBUI_SN_PRINTF_DYN(buffer, len, " --user-data-dir=\"%s\"", win->profile_path);
|
|
// Basic
|
|
for (int i = 0; i < (int)(sizeof(chromium_options) / sizeof(chromium_options[0])); i++) {
|
|
c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " %s", chromium_options[i]);
|
|
}
|
|
// Kiosk Mode
|
|
if (win->kiosk_mode)
|
|
c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " %s", "--chrome-frame --kiosk");
|
|
// High Contrast Support
|
|
if (win->disable_browser_high_contrast)
|
|
c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " %s", "--disable-features=ForcedColors");
|
|
// Hide Mode
|
|
if (win->hide)
|
|
c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " %s", "--headless --headless=new");
|
|
// Window Size
|
|
if (win->size_set)
|
|
c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " --window-size=%u,%u", win->width, win->height);
|
|
// Window Position
|
|
if (win->position_set)
|
|
c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " --window-position=%u,%u", win->x, win->y);
|
|
// Proxy
|
|
if (win->proxy_set)
|
|
c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " --proxy-server=%s", win->proxy_server);
|
|
else
|
|
c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " %s", "--no-proxy-server");
|
|
// User-defined command line parameters.
|
|
if (!_webui_is_empty(win->custom_parameters)) {
|
|
c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " %s", win->custom_parameters);
|
|
}
|
|
|
|
// URL (END)
|
|
c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " %s", "--app=");
|
|
return c;
|
|
case Firefox:
|
|
// Profile
|
|
if (!_webui_is_empty(win->profile_name))
|
|
c = WEBUI_SN_PRINTF_DYN(buffer, len, " -P %s", win->profile_name);
|
|
// Basic
|
|
c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " -purgecaches");
|
|
// Kiosk Mode
|
|
if (win->kiosk_mode)
|
|
c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " %s", "-kiosk");
|
|
// Hide Mode
|
|
if (win->hide)
|
|
c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " %s", "-headless");
|
|
// Window Size
|
|
if (win->size_set)
|
|
c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " -width %u -height %u", win->width, win->height);
|
|
// Window Position
|
|
// Firefox does not support window positioning.
|
|
// Proxy
|
|
if (win->proxy_set) {
|
|
// Server: `win->proxy_server`
|
|
// TODO: Add proxy feature to Firefox
|
|
// Method 1: modifying `prefs.js` / user.js
|
|
// Method 2: use Proxy Auto-Configuration (PAC) file
|
|
}
|
|
// User-defined command line parameters.
|
|
if (!_webui_is_empty(win->custom_parameters))
|
|
c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " %s", win->custom_parameters);
|
|
|
|
// URL (END)
|
|
c += WEBUI_SN_PRINTF_DYN(buffer + c, len, " -new-window ");
|
|
return c;
|
|
}
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_get_browser_args() -> Unknown Browser (%zu)\n", browser);
|
|
#endif
|
|
WEBUI_STR_COPY_DYN(buffer, len, "");
|
|
return 0;
|
|
}
|
|
|
|
static bool _webui_browser_start_chrome(_webui_window_t* win, const char* address) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_browser_start_chrome([%s])\n", address);
|
|
#endif
|
|
|
|
// -- Google Chrome ----------------------
|
|
|
|
if (win->current_browser != 0 && win->current_browser != Chrome)
|
|
return false;
|
|
|
|
if (!_webui_browser_exist(win, Chrome))
|
|
return false;
|
|
|
|
if (!_webui_browser_create_new_profile(win, Chrome))
|
|
return false;
|
|
|
|
char arg[1024] = {0};
|
|
_webui_get_browser_args(win, Chrome, arg, sizeof(arg));
|
|
|
|
char full[1024] = {0};
|
|
WEBUI_SN_PRINTF_STATIC(full, sizeof(full), "%s%s%s", win->browser_path, arg, address);
|
|
|
|
if (_webui_run_browser(win, full) == 0) {
|
|
|
|
win->current_browser = Chrome;
|
|
_webui.current_browser = Chrome;
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
static bool _webui_browser_start_edge(_webui_window_t* win, const char* address) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_browser_start_edge([%s])\n", address);
|
|
#endif
|
|
|
|
// -- Microsoft Edge ----------------------
|
|
|
|
if (win->current_browser != 0 && win->current_browser != Edge)
|
|
return false;
|
|
|
|
if (!_webui_browser_exist(win, Edge))
|
|
return false;
|
|
|
|
if (!_webui_browser_create_new_profile(win, Edge))
|
|
return false;
|
|
|
|
char arg[1024] = {0};
|
|
_webui_get_browser_args(win, Edge, arg, sizeof(arg));
|
|
|
|
char full[1024] = {0};
|
|
WEBUI_SN_PRINTF_STATIC(full, sizeof(full), "%s%s%s", win->browser_path, arg, address);
|
|
|
|
if (_webui_run_browser(win, full) == 0) {
|
|
|
|
win->current_browser = Edge;
|
|
_webui.current_browser = Edge;
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
static bool _webui_browser_start_epic(_webui_window_t* win, const char* address) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_browser_start_epic([%s])\n", address);
|
|
#endif
|
|
|
|
// -- Epic Privacy Browser ----------------------
|
|
|
|
if (win->current_browser != 0 && win->current_browser != Epic)
|
|
return false;
|
|
|
|
if (!_webui_browser_exist(win, Epic))
|
|
return false;
|
|
|
|
if (!_webui_browser_create_new_profile(win, Epic))
|
|
return false;
|
|
|
|
char arg[1024] = {0};
|
|
_webui_get_browser_args(win, Epic, arg, sizeof(arg));
|
|
|
|
char full[1024] = {0};
|
|
WEBUI_SN_PRINTF_STATIC(full, sizeof(full), "%s%s%s", win->browser_path, arg, address);
|
|
|
|
if (_webui_run_browser(win, full) == 0) {
|
|
|
|
win->current_browser = Epic;
|
|
_webui.current_browser = Epic;
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
static bool _webui_browser_start_vivaldi(_webui_window_t* win, const char* address) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_browser_start_vivaldi([%s])\n", address);
|
|
#endif
|
|
|
|
// -- Vivaldi Browser ----------------------
|
|
|
|
if (win->current_browser != 0 && win->current_browser != Vivaldi)
|
|
return false;
|
|
|
|
if (!_webui_browser_exist(win, Vivaldi))
|
|
return false;
|
|
|
|
if (!_webui_browser_create_new_profile(win, Vivaldi))
|
|
return false;
|
|
|
|
char arg[1024] = {0};
|
|
_webui_get_browser_args(win, Vivaldi, arg, sizeof(arg));
|
|
|
|
char full[1024] = {0};
|
|
WEBUI_SN_PRINTF_STATIC(full, sizeof(full), "%s%s%s", win->browser_path, arg, address);
|
|
|
|
if (_webui_run_browser(win, full) == 0) {
|
|
|
|
win->current_browser = Vivaldi;
|
|
_webui.current_browser = Vivaldi;
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
static bool _webui_browser_start_brave(_webui_window_t* win, const char* address) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_browser_start_brave([%s])\n", address);
|
|
#endif
|
|
|
|
// -- Brave Browser ----------------------
|
|
|
|
if (win->current_browser != 0 && win->current_browser != Brave)
|
|
return false;
|
|
|
|
if (!_webui_browser_exist(win, Brave))
|
|
return false;
|
|
|
|
if (!_webui_browser_create_new_profile(win, Brave))
|
|
return false;
|
|
|
|
char arg[1024] = {0};
|
|
_webui_get_browser_args(win, Brave, arg, sizeof(arg));
|
|
|
|
char full[1024] = {0};
|
|
WEBUI_SN_PRINTF_STATIC(full, sizeof(full), "%s%s%s", win->browser_path, arg, address);
|
|
|
|
if (_webui_run_browser(win, full) == 0) {
|
|
|
|
win->current_browser = Brave;
|
|
_webui.current_browser = Brave;
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
static bool _webui_browser_start_firefox(_webui_window_t* win, const char* address) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_browser_start_firefox([%s])\n", address);
|
|
#endif
|
|
|
|
// -- Mozilla Firefox ----------------------
|
|
|
|
if (win->current_browser != 0 && win->current_browser != Firefox)
|
|
return false;
|
|
|
|
if (!_webui_browser_exist(win, Firefox))
|
|
return false;
|
|
|
|
if (!_webui_browser_create_new_profile(win, Firefox))
|
|
return false;
|
|
|
|
char arg[1024] = {0};
|
|
_webui_get_browser_args(win, Firefox, arg, sizeof(arg));
|
|
|
|
char full[1024] = {0};
|
|
WEBUI_SN_PRINTF_STATIC(full, sizeof(full), "%s%s%s", win->browser_path, arg, address);
|
|
|
|
if (_webui_run_browser(win, full) == 0) {
|
|
|
|
win->current_browser = Firefox;
|
|
_webui.current_browser = Firefox;
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
static bool _webui_browser_start_yandex(_webui_window_t* win, const char* address) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_browser_start_yandex([%s])\n", address);
|
|
#endif
|
|
|
|
// -- Yandex Browser ----------------------
|
|
|
|
if (win->current_browser != 0 && win->current_browser != Yandex)
|
|
return false;
|
|
|
|
if (!_webui_browser_exist(win, Yandex))
|
|
return false;
|
|
|
|
if (!_webui_browser_create_new_profile(win, Yandex))
|
|
return false;
|
|
|
|
char arg[1024] = {0};
|
|
_webui_get_browser_args(win, Yandex, arg, sizeof(arg));
|
|
|
|
char full[1024] = {0};
|
|
WEBUI_SN_PRINTF_STATIC(full, sizeof(full), "%s%s%s", win->browser_path, arg, address);
|
|
|
|
if (_webui_run_browser(win, full) == 0) {
|
|
|
|
win->current_browser = Yandex;
|
|
_webui.current_browser = Yandex;
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
static bool _webui_browser_start_chromium(_webui_window_t* win, const char* address) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_browser_start_chromium([%s])\n", address);
|
|
#endif
|
|
|
|
// -- The Chromium Projects -------------------
|
|
|
|
if (win->current_browser != 0 && win->current_browser != Chromium)
|
|
return false;
|
|
|
|
if (!_webui_browser_exist(win, Chromium))
|
|
return false;
|
|
|
|
if (!_webui_browser_create_new_profile(win, Chromium))
|
|
return false;
|
|
|
|
char arg[1024] = {0};
|
|
_webui_get_browser_args(win, Chromium, arg, sizeof(arg));
|
|
|
|
char full[1024] = {0};
|
|
WEBUI_SN_PRINTF_STATIC(full, sizeof(full), "%s%s%s", win->browser_path, arg, address);
|
|
|
|
if (_webui_run_browser(win, full) == 0) {
|
|
|
|
win->current_browser = Chromium;
|
|
_webui.current_browser = Chromium;
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
static bool _webui_browser_start(_webui_window_t* win, const char* address, size_t _browser) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_browser_start([%s], [%zu])\n", address, _browser);
|
|
#endif
|
|
|
|
// Non existing browser
|
|
if (_browser > 12)
|
|
return false;
|
|
|
|
// No browser mode
|
|
if (_browser == NoBrowser)
|
|
return true;
|
|
|
|
// Find the best web browser to use
|
|
size_t browser = _browser;
|
|
if (browser == AnyBrowser) {
|
|
browser = _webui.current_browser != 0 ?
|
|
_webui.current_browser : _webui_find_the_best_browser(win);
|
|
}
|
|
|
|
// Current browser used in the last opened window
|
|
if (browser == AnyBrowser && _webui.current_browser != 0)
|
|
browser = _webui.current_browser;
|
|
|
|
// #1 - Chrome - Works perfectly
|
|
// #2 - Edge - Works perfectly like Chrome
|
|
// #3 - Epic - Works perfectly like Chrome
|
|
// #4 - Vivaldi - Works perfectly like Chrome
|
|
// #5 - Brave - Shows a policy notification in the first run
|
|
// #6 - Firefox - Does not support App-Mode like Chrome (Looks not great)
|
|
// #7 - Yandex - Shows a big welcome window in the first run
|
|
// #8 - Chromium - Some Anti-Malware shows a false alert
|
|
|
|
if (browser != AnyBrowser) {
|
|
|
|
// Open the window using the user specified browser
|
|
|
|
if (browser == Chrome)
|
|
return _webui_browser_start_chrome(win, address);
|
|
else if (browser == Edge)
|
|
return _webui_browser_start_edge(win, address);
|
|
else if (browser == Epic)
|
|
return _webui_browser_start_epic(win, address);
|
|
else if (browser == Vivaldi)
|
|
return _webui_browser_start_vivaldi(win, address);
|
|
else if (browser == Brave)
|
|
return _webui_browser_start_brave(win, address);
|
|
else if (browser == Firefox)
|
|
return _webui_browser_start_firefox(win, address);
|
|
else if (browser == Yandex)
|
|
return _webui_browser_start_yandex(win, address);
|
|
else if (browser == Chromium)
|
|
return _webui_browser_start_chromium(win, address);
|
|
else if (browser == ChromiumBased) {
|
|
|
|
// Open the window using a Chromium-based browser
|
|
|
|
if (!_webui_browser_start_chrome(win, address))
|
|
if (!_webui_browser_start_edge(win, address))
|
|
if (!_webui_browser_start_epic(win, address))
|
|
if (!_webui_browser_start_vivaldi(win, address))
|
|
if (!_webui_browser_start_brave(win, address))
|
|
if (!_webui_browser_start_yandex(win, address))
|
|
if (!_webui_browser_start_chromium(win, address))
|
|
return false;
|
|
} else
|
|
return false;
|
|
} else if (win->current_browser != 0) {
|
|
|
|
// Open the window using the same web browser used for this current window
|
|
|
|
if (win->current_browser == Chrome)
|
|
return _webui_browser_start_chrome(win, address);
|
|
else if (win->current_browser == Edge)
|
|
return _webui_browser_start_edge(win, address);
|
|
else if (win->current_browser == Epic)
|
|
return _webui_browser_start_epic(win, address);
|
|
else if (win->current_browser == Vivaldi)
|
|
return _webui_browser_start_vivaldi(win, address);
|
|
else if (win->current_browser == Brave)
|
|
return _webui_browser_start_brave(win, address);
|
|
else if (win->current_browser == Firefox)
|
|
return _webui_browser_start_firefox(win, address);
|
|
else if (win->current_browser == Yandex)
|
|
return _webui_browser_start_yandex(win, address);
|
|
else if (win->current_browser == Chromium)
|
|
return _webui_browser_start_chromium(win, address);
|
|
else
|
|
return false;
|
|
} else {
|
|
|
|
// Open the window using the default OS browser
|
|
|
|
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
|
|
// Windows
|
|
if (!_webui_browser_start_chrome(win, address))
|
|
if (!_webui_browser_start_edge(win, address))
|
|
if (!_webui_browser_start_epic(win, address))
|
|
if (!_webui_browser_start_vivaldi(win, address))
|
|
if (!_webui_browser_start_brave(win, address))
|
|
if (!_webui_browser_start_firefox(win, address))
|
|
if (!_webui_browser_start_yandex(win, address))
|
|
if (!_webui_browser_start_chromium(win, address))
|
|
return false;
|
|
#elif __APPLE__
|
|
// macOS
|
|
if (!_webui_browser_start_chrome(win, address))
|
|
if (!_webui_browser_start_edge(win, address))
|
|
if (!_webui_browser_start_epic(win, address))
|
|
if (!_webui_browser_start_vivaldi(win, address))
|
|
if (!_webui_browser_start_brave(win, address))
|
|
if (!_webui_browser_start_firefox(win, address))
|
|
if (!_webui_browser_start_yandex(win, address))
|
|
if (!_webui_browser_start_chromium(win, address))
|
|
return false;
|
|
#else
|
|
// Linux
|
|
if (!_webui_browser_start_chrome(win, address))
|
|
if (!_webui_browser_start_edge(win, address))
|
|
if (!_webui_browser_start_epic(win, address))
|
|
if (!_webui_browser_start_vivaldi(win, address))
|
|
if (!_webui_browser_start_brave(win, address))
|
|
if (!_webui_browser_start_firefox(win, address))
|
|
if (!_webui_browser_start_yandex(win, address))
|
|
if (!_webui_browser_start_chromium(win, address))
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool _webui_is_process_running(const char* process_name) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_is_process_running([%s])\n", process_name);
|
|
#endif
|
|
|
|
bool isRunning = false;
|
|
|
|
#ifdef _WIN32
|
|
// Microsoft Windows
|
|
HANDLE hSnapshot;
|
|
PROCESSENTRY32 pe32;
|
|
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
|
if (hSnapshot == INVALID_HANDLE_VALUE)
|
|
return false;
|
|
pe32.dwSize = sizeof(PROCESSENTRY32);
|
|
if (!Process32First(hSnapshot,&pe32)) {
|
|
CloseHandle(hSnapshot);
|
|
return false;
|
|
}
|
|
do {
|
|
if (strcmp(pe32.szExeFile, process_name) == 0) {
|
|
isRunning = true;
|
|
break;
|
|
}
|
|
} while(Process32Next(hSnapshot,&pe32));
|
|
CloseHandle(hSnapshot);
|
|
#elif __linux__
|
|
// Linux
|
|
DIR * dir;
|
|
struct dirent* entry;
|
|
char status_path[WEBUI_MAX_PATH];
|
|
char line[WEBUI_MAX_PATH];
|
|
dir = opendir("/proc");
|
|
if (!dir)
|
|
return false; // Unable to open /proc
|
|
while((entry = readdir(dir))) {
|
|
if (entry->d_type == DT_DIR && atoi(entry->d_name) > 0) {
|
|
WEBUI_SN_PRINTF_STATIC(status_path, sizeof(status_path), "/proc/%s/status", entry->d_name);
|
|
FILE * status_file;
|
|
WEBUI_FILE_OPEN(status_file, status_path, "r");
|
|
if (status_file) {
|
|
while(fgets(line, sizeof(line), status_file)) {
|
|
if (strncmp(line, "Name:", 5) == 0) {
|
|
char proc_name[WEBUI_MAX_PATH];
|
|
sscanf(line, "Name: %s", proc_name);
|
|
if (strcmp(proc_name, process_name) == 0) {
|
|
isRunning = true;
|
|
fclose(status_file);
|
|
goto _close_dir;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
fclose(status_file);
|
|
}
|
|
}
|
|
}
|
|
_close_dir:
|
|
closedir(dir);
|
|
#else
|
|
// macOS
|
|
int mib[4] = {
|
|
CTL_KERN,
|
|
KERN_PROC,
|
|
KERN_PROC_ALL,
|
|
0
|
|
};
|
|
struct kinfo_proc * procs = NULL;
|
|
size_t size;
|
|
if (sysctl(mib, 4, NULL,&size, NULL, 0) < 0)
|
|
return false; // Failed to get process list size
|
|
procs = (struct kinfo_proc * ) malloc(size);
|
|
if (!procs)
|
|
return false; // Failed to allocate memory for process list
|
|
if (sysctl(mib, 4, procs,&size, NULL, 0) < 0) {
|
|
free(procs);
|
|
return false; // Failed to get process list
|
|
}
|
|
size_t count = size / sizeof(struct kinfo_proc);
|
|
for (size_t i = 0; i < count; i++) {
|
|
if (strcmp(procs[i].kp_proc.p_comm, process_name) == 0) {
|
|
isRunning = true;
|
|
break;
|
|
}
|
|
}
|
|
free(procs);
|
|
#endif
|
|
|
|
return isRunning;
|
|
}
|
|
|
|
static size_t _webui_find_the_best_browser(_webui_window_t* win) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_find_the_best_browser()\n");
|
|
#endif
|
|
|
|
// #1 - Chrome - Works perfectly
|
|
// #2 - Edge - Works perfectly like Chrome
|
|
// #3 - Epic - Works perfectly like Chrome
|
|
// #4 - Vivaldi - Works perfectly like Chrome
|
|
// #5 - Brave - Shows a policy notification in the first run
|
|
// #6 - Firefox - Does not support App-Mode like Chrome (Looks not great)
|
|
// #7 - Yandex - Shows a big welcome window in the first run
|
|
// #8 - Chromium - Some Anti-Malware shows a false alert
|
|
|
|
// To save memory, let's search if a web browser is already running
|
|
|
|
#ifdef _WIN32
|
|
// Microsoft Windows
|
|
if (_webui_is_process_running("chrome.exe") && _webui_browser_exist(win, Chrome))
|
|
return Chrome;
|
|
else if (_webui_is_process_running("msedge.exe") && _webui_browser_exist(win, Edge))
|
|
return Edge;
|
|
else if (_webui_is_process_running("epic.exe") && _webui_browser_exist(win, Epic))
|
|
return Epic;
|
|
else if (_webui_is_process_running("vivaldi.exe") && _webui_browser_exist(win, Vivaldi))
|
|
return Vivaldi;
|
|
else if (_webui_is_process_running("brave.exe") && _webui_browser_exist(win, Brave))
|
|
return Brave;
|
|
else if (_webui_is_process_running("firefox.exe") && _webui_browser_exist(win, Firefox))
|
|
return Firefox;
|
|
else if (_webui_is_process_running("browser.exe") && _webui_browser_exist(win, Yandex))
|
|
return Yandex;
|
|
// Chromium check is never reached if Google Chrome is installed
|
|
// due to duplicate process name `chrome.exe`
|
|
else if (_webui_is_process_running("chrome.exe") && _webui_browser_exist(win, Chromium))
|
|
return Chromium;
|
|
#elif __linux__
|
|
// Linux
|
|
if (_webui_is_process_running("chrome") && _webui_browser_exist(win, Chrome))
|
|
return Chrome;
|
|
else if (_webui_is_process_running("msedge") && _webui_browser_exist(win, Edge))
|
|
return Edge;
|
|
// Epic...
|
|
else if (_webui_is_process_running("vivaldi-bin") && _webui_browser_exist(win, Vivaldi))
|
|
return Vivaldi;
|
|
else if (_webui_is_process_running("brave") && _webui_browser_exist(win, Brave))
|
|
return Brave;
|
|
else if (_webui_is_process_running("firefox") && _webui_browser_exist(win, Firefox))
|
|
return Firefox;
|
|
else if (_webui_is_process_running("yandex_browser") && _webui_browser_exist(win, Yandex))
|
|
return Yandex;
|
|
// Chromium check is never reached if Google Chrome is installed
|
|
// due to duplicate process name `chrome`
|
|
else if (_webui_is_process_running("chrome") && _webui_browser_exist(win, Chromium))
|
|
return Chromium;
|
|
#else
|
|
// macOS
|
|
if (_webui_is_process_running("Google Chrome") && _webui_browser_exist(win, Chrome))
|
|
return Chrome;
|
|
else if (_webui_is_process_running("Epic") && _webui_browser_exist(win, Epic))
|
|
return Epic;
|
|
else if (_webui_is_process_running("Vivaldi") && _webui_browser_exist(win, Vivaldi))
|
|
return Vivaldi;
|
|
else if (_webui_is_process_running("Brave") && _webui_browser_exist(win, Brave))
|
|
return Brave;
|
|
else if (_webui_is_process_running("Firefox") && _webui_browser_exist(win, Firefox))
|
|
return Firefox;
|
|
else if (_webui_is_process_running("Yandex") && _webui_browser_exist(win, Yandex))
|
|
return Yandex;
|
|
else if (_webui_is_process_running("Chromium") && _webui_browser_exist(win, Chromium))
|
|
return Chromium;
|
|
#endif
|
|
|
|
return AnyBrowser;
|
|
}
|
|
|
|
static bool _webui_show(_webui_window_t* win, struct mg_connection* client, const char* content, size_t browser) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_show([%zu])\n", browser);
|
|
#endif
|
|
|
|
if (_webui_is_empty(content))
|
|
return false;
|
|
|
|
// Some wrappers do not guarantee pointers stay valid,
|
|
// so, let's make our copy.
|
|
size_t content_len = _webui_strlen(content);
|
|
const char* content_cpy = (const char*)_webui_malloc(content_len);
|
|
memcpy((char*)content_cpy, content, content_len);
|
|
|
|
// URL
|
|
if (_webui_is_valid_url(content_cpy)) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_show() -> URL: [%s]\n", content_cpy);
|
|
#endif
|
|
return _webui_show_window(win, client, content_cpy, WEBUI_SHOW_URL, browser);
|
|
}
|
|
// Embedded HTML
|
|
else if (strstr(content_cpy, "<html") ||
|
|
strstr(content_cpy, "<!DOCTYPE") ||
|
|
strstr(content_cpy, "<!doctype") ||
|
|
strstr(content_cpy, "<!Doctype")) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_show() -> Embedded HTML:\n");
|
|
printf("- - -[HTML]- - - - - - - - - -\n%s\n- - - - - - - - - - - - - - - -\n", content_cpy);
|
|
#endif
|
|
return _webui_show_window(win, client, content_cpy, WEBUI_SHOW_HTML, browser);
|
|
}
|
|
// Folder
|
|
else if (_webui_folder_exist(content_cpy)) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_show() -> Folder: [%s]\n", content_cpy);
|
|
#endif
|
|
// Set root folder
|
|
if (!webui_set_root_folder(win->num, content_cpy)) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_show() -> Failed to set folder root path\n");
|
|
#endif
|
|
_webui_free_mem((void*)content_cpy);
|
|
return false;
|
|
}
|
|
return _webui_show_window(win, client, content_cpy, WEBUI_SHOW_FOLDER, browser);
|
|
}
|
|
// File
|
|
else {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_show() -> File: [%s]\n", content_cpy);
|
|
#endif
|
|
if (content_len > WEBUI_MAX_PATH || strstr(content_cpy, "<"))
|
|
return false;
|
|
return _webui_show_window(win, client, content_cpy, WEBUI_SHOW_FILE, browser);
|
|
}
|
|
}
|
|
|
|
// TLS
|
|
#ifdef WEBUI_TLS
|
|
static int _webui_tls_initialization(void * ssl_ctx, void * ptr) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_tls_initialization()\n");
|
|
#endif
|
|
|
|
SSL_CTX * ctx = (SSL_CTX * ) ssl_ctx;
|
|
|
|
// Disable security levels. It is The end-user
|
|
// responsability to provide a high encryption
|
|
// level certificate. While WebUI should just
|
|
// use the end-user's certificates.
|
|
SSL_CTX_set_security_level(ctx, 0);
|
|
|
|
// Load Certificate
|
|
BIO * bio_cert = BIO_new_mem_buf((void*)_webui.ssl_cert, -1);
|
|
X509 * cert = PEM_read_bio_X509(bio_cert, NULL, 0, NULL);
|
|
if (cert == NULL) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_tls_initialization() -> PEM_read_bio_X509 failed\n");
|
|
#endif
|
|
WEBUI_ASSERT("PEM_read_bio_X509 failed");
|
|
return -1;
|
|
}
|
|
if (SSL_CTX_use_certificate(ctx, cert) <= 0) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_tls_initialization() -> SSL_CTX_use_certificate failed\n");
|
|
#endif
|
|
WEBUI_ASSERT("SSL_CTX_use_certificate failed");
|
|
return -1;
|
|
}
|
|
X509_free(cert);
|
|
BIO_free(bio_cert);
|
|
|
|
// Load Key
|
|
BIO * bio_key = BIO_new_mem_buf((void*)_webui.ssl_key, -1);
|
|
EVP_PKEY * private_key = PEM_read_bio_PrivateKey(bio_key, NULL, 0, NULL);
|
|
if (private_key == NULL) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_tls_initialization() -> PEM_read_bio_PrivateKey failed\n");
|
|
#endif
|
|
WEBUI_ASSERT("PEM_read_bio_PrivateKey failed");
|
|
return -1;
|
|
}
|
|
if (SSL_CTX_use_PrivateKey(ctx, private_key) <= 0) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_tls_initialization() -> SSL_CTX_use_PrivateKey failed\n");
|
|
#endif
|
|
WEBUI_ASSERT("SSL_CTX_use_PrivateKey failed");
|
|
return -1;
|
|
}
|
|
EVP_PKEY_free(private_key);
|
|
BIO_free(bio_key);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool _webui_tls_generate_self_signed_cert(char* root_cert, char* root_key, char* ssl_cert, char* ssl_key) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_tls_generate_self_signed_cert()\n");
|
|
#endif
|
|
|
|
int ret = 0;
|
|
int bits = 2048;
|
|
|
|
// ----- Create Root Certificate -----
|
|
EVP_PKEY * root_pkey = NULL;
|
|
EVP_PKEY_CTX * root_ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
|
|
if (!root_ctx)
|
|
return false;
|
|
|
|
if (EVP_PKEY_keygen_init(root_ctx) <= 0 || EVP_PKEY_CTX_set_rsa_keygen_bits(root_ctx, bits) <= 0 ||
|
|
EVP_PKEY_keygen(root_ctx,&root_pkey) <= 0) {
|
|
EVP_PKEY_CTX_free(root_ctx);
|
|
return false;
|
|
}
|
|
|
|
EVP_PKEY_CTX_free(root_ctx);
|
|
|
|
X509 * root_x509 = X509_new();
|
|
X509_set_version(root_x509, 2);
|
|
ASN1_INTEGER_set(X509_get_serialNumber(root_x509), (long) _webui_generate_random_uint32());
|
|
X509_gmtime_adj(X509_get_notBefore(root_x509), 0);
|
|
X509_gmtime_adj(X509_get_notAfter(root_x509), (long)(WEBUI_SSL_EXPIRE));
|
|
|
|
X509_NAME * root_name = X509_get_subject_name(root_x509);
|
|
X509_NAME_add_entry_by_txt(root_name, "C", MBSTRING_ASC, (const unsigned char *)"CA", -1, -1,
|
|
0); // Country
|
|
X509_NAME_add_entry_by_txt(root_name, "O", MBSTRING_ASC, (const unsigned char *)"WebUI Root Authority", -1, -1, 0); // Organization
|
|
X509_NAME_add_entry_by_txt(root_name, "OU", MBSTRING_ASC, (const unsigned char *)"WebUI", -1, -1,
|
|
0); // Organizational Unit
|
|
X509_NAME_add_entry_by_txt(root_name, "CN", MBSTRING_ASC, (const unsigned char *)"localhost", -1, -1,
|
|
0); // Common Name
|
|
X509_NAME_add_entry_by_txt(
|
|
root_name, "subjectAltName", MBSTRING_ASC, (const unsigned char *)"127.0.0.1", -1, -1,
|
|
0
|
|
); // Subject Alternative Name
|
|
X509_NAME_add_entry_by_txt(root_name, "ST", MBSTRING_ASC, (const unsigned char *)"WebUI", -1, -1,
|
|
0); // State
|
|
X509_NAME_add_entry_by_txt(root_name, "L", MBSTRING_ASC, (const unsigned char *)"WebUI", -1, -1,
|
|
0); // Locality
|
|
|
|
X509_set_issuer_name(root_x509, root_name);
|
|
X509_set_pubkey(root_x509, root_pkey);
|
|
ret = X509_sign(root_x509, root_pkey, EVP_sha256());
|
|
if (ret <= 0) {
|
|
X509_free(root_x509);
|
|
EVP_PKEY_free(root_pkey);
|
|
return false;
|
|
}
|
|
|
|
// Write Root Certificate and Key
|
|
BIO * bio_root_cert = BIO_new(BIO_s_mem());
|
|
PEM_write_bio_X509(bio_root_cert, root_x509);
|
|
memset(root_cert, 0, WEBUI_SSL_SIZE);
|
|
BIO_read(bio_root_cert, root_cert, (WEBUI_SSL_SIZE - 1));
|
|
|
|
BIO * bio_root_key = BIO_new(BIO_s_mem());
|
|
PEM_write_bio_PrivateKey(bio_root_key, root_pkey, NULL, NULL, 0, NULL, NULL);
|
|
memset(root_key, 0, WEBUI_SSL_SIZE);
|
|
BIO_read(bio_root_key, root_key, (WEBUI_SSL_SIZE - 1));
|
|
|
|
// ----- Create Server Certificate and sign with Root -----
|
|
EVP_PKEY * pkey = NULL;
|
|
EVP_PKEY_CTX * ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
|
|
if (!ctx) {
|
|
X509_free(root_x509);
|
|
EVP_PKEY_free(root_pkey);
|
|
return false;
|
|
}
|
|
|
|
if (EVP_PKEY_keygen_init(ctx) <= 0 || EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) <= 0 ||
|
|
EVP_PKEY_keygen(ctx,&pkey) <= 0) {
|
|
X509_free(root_x509);
|
|
EVP_PKEY_free(root_pkey);
|
|
EVP_PKEY_CTX_free(ctx);
|
|
return false;
|
|
}
|
|
|
|
EVP_PKEY_CTX_free(ctx);
|
|
|
|
X509 * x509 = X509_new();
|
|
X509_set_version(x509, 2);
|
|
ASN1_INTEGER_set(X509_get_serialNumber(x509), (long) _webui_generate_random_uint32());
|
|
X509_gmtime_adj(X509_get_notBefore(x509), 0);
|
|
X509_gmtime_adj(X509_get_notAfter(x509), (long)(WEBUI_SSL_EXPIRE));
|
|
|
|
X509_NAME * name = X509_get_subject_name(x509);
|
|
X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (const unsigned char *)"CA", -1, -1,
|
|
0); // Country
|
|
X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (const unsigned char *)"WebUI", -1, -1,
|
|
0); // Organization
|
|
X509_NAME_add_entry_by_txt(name, "OU", MBSTRING_ASC, (const unsigned char *)"WebUI", -1, -1,
|
|
0); // Organizational Unit
|
|
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (const unsigned char *)"localhost", -1, -1,
|
|
0); // Common Name
|
|
X509_NAME_add_entry_by_txt(
|
|
name, "subjectAltName", MBSTRING_ASC, (const unsigned char *)"127.0.0.1", -1, -1, 0
|
|
); // Subject Alternative Name
|
|
X509_NAME_add_entry_by_txt(name, "ST", MBSTRING_ASC, (const unsigned char *)"WebUI", -1, -1,
|
|
0); // State
|
|
X509_NAME_add_entry_by_txt(name, "L", MBSTRING_ASC, (const unsigned char *)"WebUI", -1, -1,
|
|
0); // Locality
|
|
|
|
X509_set_issuer_name(x509, root_name);
|
|
X509_set_pubkey(x509, pkey);
|
|
ret = X509_sign(x509, root_pkey, EVP_sha256());
|
|
if (ret <= 0) {
|
|
X509_free(root_x509);
|
|
EVP_PKEY_free(root_pkey);
|
|
X509_free(x509);
|
|
EVP_PKEY_free(pkey);
|
|
return false;
|
|
}
|
|
|
|
// Write the Server Certificate and Key
|
|
BIO * bio_cert = BIO_new(BIO_s_mem());
|
|
PEM_write_bio_X509(bio_cert, x509);
|
|
memset(ssl_cert, 0, WEBUI_SSL_SIZE);
|
|
BIO_read(bio_cert, ssl_cert, (WEBUI_SSL_SIZE - 1));
|
|
|
|
BIO * bio_key = BIO_new(BIO_s_mem());
|
|
PEM_write_bio_PrivateKey(bio_key, pkey, NULL, NULL, 0, NULL, NULL);
|
|
memset(ssl_key, 0, WEBUI_SSL_SIZE);
|
|
BIO_read(bio_key, ssl_key, (WEBUI_SSL_SIZE - 1));
|
|
|
|
// Cleanup
|
|
X509_free(root_x509);
|
|
EVP_PKEY_free(root_pkey);
|
|
BIO_free_all(bio_root_cert);
|
|
BIO_free_all(bio_root_key);
|
|
X509_free(x509);
|
|
EVP_PKEY_free(pkey);
|
|
BIO_free_all(bio_cert);
|
|
BIO_free_all(bio_key);
|
|
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
static bool _webui_show_window(_webui_window_t* win, struct mg_connection* client, const char* content, int type, size_t browser) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
if (type == WEBUI_SHOW_HTML)
|
|
printf("[Core]\t\t_webui_show_window(HTML, [%zu])\n", browser);
|
|
else if (type == WEBUI_SHOW_URL)
|
|
printf("[Core]\t\t_webui_show_window(URL, [%zu])\n", browser);
|
|
else if (type == WEBUI_SHOW_FOLDER)
|
|
printf("[Core]\t\t_webui_show_window(FOLDER, [%zu])\n", browser);
|
|
else
|
|
printf("[Core]\t\t_webui_show_window(FILE, [%zu])\n", browser);
|
|
#endif
|
|
|
|
#ifdef WEBUI_TLS
|
|
// TLS
|
|
if (_webui_is_empty(_webui.ssl_cert) || _webui_is_empty(_webui.ssl_key)) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_show_window() -> Generating self-signed TLS "
|
|
"certificate\n");
|
|
#endif
|
|
|
|
// Generate SSL self-signed certificate once
|
|
char* root_cert = (char*)_webui_malloc(WEBUI_SSL_SIZE);
|
|
char* root_key = (char*)_webui_malloc(WEBUI_SSL_SIZE);
|
|
char* ssl_cert = (char*)_webui_malloc(WEBUI_SSL_SIZE);
|
|
char* ssl_key = (char*)_webui_malloc(WEBUI_SSL_SIZE);
|
|
if (!_webui_tls_generate_self_signed_cert(root_cert, root_key, ssl_cert, ssl_key)) {
|
|
#ifdef WEBUI_LOG
|
|
unsigned long err = ERR_get_error();
|
|
char err_buf[1024];
|
|
ERR_error_string_n(err, err_buf, sizeof(err_buf));
|
|
printf(
|
|
"[Core]\t\t_webui_show_window() -> Generating self-signed TLS "
|
|
"certificate failed:\n%s\n",
|
|
err_buf
|
|
);
|
|
#endif
|
|
_webui_free_mem((void*)root_cert);
|
|
_webui_free_mem((void*)root_key);
|
|
_webui_free_mem((void*)ssl_cert);
|
|
_webui_free_mem((void*)ssl_key);
|
|
WEBUI_ASSERT("Generating self-signed TLS certificate failed");
|
|
return false;
|
|
}
|
|
|
|
_webui.root_cert = root_cert;
|
|
_webui.root_key = root_key;
|
|
_webui.ssl_cert = ssl_cert;
|
|
_webui.ssl_key = ssl_key;
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_show_window() -> Self-signed SSL/TLS "
|
|
"Certificate:\nRoot:\n");
|
|
printf("%s\n", (const char*)_webui.root_cert);
|
|
printf("%s\nServer:\n", (const char*)_webui.root_key);
|
|
printf("%s\n", (const char*)_webui.ssl_cert);
|
|
printf("%s\n", (const char*)_webui.ssl_key);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
// Initialization
|
|
if (win->html != NULL)
|
|
_webui_free_mem((void*)win->html);
|
|
if (win->url != NULL)
|
|
_webui_free_mem((void*)win->url);
|
|
|
|
// Get network ports
|
|
if (win->custom_server_port > 0) win->server_port = win->custom_server_port;
|
|
else if (win->server_port == 0) win->server_port = _webui_get_free_port();
|
|
|
|
// Generate the server URL
|
|
win->url = (char*)_webui_malloc(32); // [http][domain][port]
|
|
WEBUI_SN_PRINTF_DYN(win->url, 32, WEBUI_HTTP_PROTOCOL "localhost:%zu", win->server_port);
|
|
|
|
// Generate the window URL
|
|
char* window_url = NULL;
|
|
if (type == WEBUI_SHOW_HTML) {
|
|
|
|
const char* user_html = content;
|
|
|
|
// Show a window using the embedded HTML
|
|
win->is_embedded_html = true;
|
|
win->html = (user_html == NULL ? "" : user_html);
|
|
|
|
// Set window URL
|
|
size_t len = _webui_strlen(win->url);
|
|
window_url = (char*)_webui_malloc(len);
|
|
WEBUI_STR_COPY_DYN(window_url, len, win->url);
|
|
}
|
|
else if (type == WEBUI_SHOW_URL) {
|
|
|
|
const char* user_url = content;
|
|
|
|
// Show a window using a specific URL
|
|
win->is_embedded_html = true;
|
|
size_t bf_len = (64 + _webui_strlen(user_url));
|
|
char* refresh = (char*)_webui_malloc(bf_len);
|
|
WEBUI_SN_PRINTF_DYN(refresh, bf_len, "<meta http-equiv=\"refresh\" content=\"0;url=%s\">", user_url);
|
|
win->html = refresh;
|
|
|
|
// Set window URL
|
|
window_url = (char*)user_url;
|
|
}
|
|
else if (type == WEBUI_SHOW_FOLDER) {
|
|
|
|
const char* folder_path = content;
|
|
|
|
// Show a window using a local folder
|
|
win->is_embedded_html = false;
|
|
win->html = NULL;
|
|
|
|
// Set window URL
|
|
window_url = win->url;
|
|
}
|
|
else {
|
|
const char* user_file = content;
|
|
|
|
// Show a window using a local file
|
|
win->is_embedded_html = false;
|
|
win->html = NULL;
|
|
|
|
// Generate the URL
|
|
const char* file_url_encoded = _webui_url_encode(user_file);
|
|
size_t bf_len = (64 + _webui_strlen(file_url_encoded));
|
|
char* url_encoded = (char*)_webui_malloc(bf_len); // [http][domain][port] [file_encoded]
|
|
WEBUI_SN_PRINTF_DYN(url_encoded, bf_len, WEBUI_HTTP_PROTOCOL "localhost:%zu/%s",
|
|
win->server_port, file_url_encoded);
|
|
_webui_free_mem((void*)file_url_encoded);
|
|
_webui_free_mem((void*)user_file);
|
|
|
|
// Set window URL
|
|
window_url = url_encoded;
|
|
}
|
|
|
|
// Run the window
|
|
if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE)) {
|
|
|
|
// Start a new window
|
|
|
|
// Prioritize the server thread if we
|
|
// knows that there is UIs running
|
|
if (_webui.ui) {
|
|
|
|
// New server thread
|
|
#ifdef _WIN32
|
|
HANDLE thread = CreateThread(NULL, 0, _webui_server_thread, (void*)win, 0, NULL);
|
|
win->server_thread = thread;
|
|
if (thread != NULL)
|
|
CloseHandle(thread);
|
|
#else
|
|
pthread_t thread;
|
|
pthread_create(&thread, NULL,&_webui_server_thread, (void*)win);
|
|
pthread_detach(thread);
|
|
win->server_thread = thread;
|
|
#endif
|
|
}
|
|
|
|
// New WebView
|
|
bool runWebView = false;
|
|
if (win->allow_webview) {
|
|
// Trying to use WebView
|
|
if (_webui_wv_show(win, window_url)) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_show_window() -> WebView Found\n");
|
|
#endif
|
|
win->current_browser = Webview;
|
|
runWebView = true;
|
|
}
|
|
else {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_show_window() -> WebView Not Found\n");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Run browser
|
|
bool runBrowser = false;
|
|
if (!runWebView) {
|
|
if (browser != NoBrowser) {
|
|
if (!_webui_browser_start(win, window_url, browser)) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_show_window() -> App-mode browser failed\n");
|
|
#endif
|
|
// Opening App-mode browser failed
|
|
// let's try opening UI in native default browser
|
|
if (browser == AnyBrowser && _webui_open_url_native(window_url)) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_show_window() -> Native browser succeeded\n");
|
|
#endif
|
|
runBrowser = true;
|
|
}
|
|
else {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_show_window() -> Native browser failed\n");
|
|
#endif
|
|
}
|
|
}
|
|
else {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_show_window() -> App-mode browser succeeded\n");
|
|
#endif
|
|
runBrowser = true;
|
|
}
|
|
} else {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_show_window() -> Starting server only mode (NoBrowser)\n");
|
|
#endif
|
|
runBrowser = true;
|
|
}
|
|
}
|
|
|
|
_webui_free_mem((void*)window_url);
|
|
if (browser != NoBrowser) {
|
|
if (!runWebView && !runBrowser) {
|
|
// Browser and WebView are not available
|
|
_webui_free_mem((void*)win->html);
|
|
_webui_free_mem((void*)win->url);
|
|
_webui_free_port(win->server_port);
|
|
win->server_port = 0;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!_webui.ui) {
|
|
|
|
// Let the wait() knows that this app
|
|
// has atleast one window to wait for
|
|
_webui.ui = true;
|
|
|
|
// New server thread
|
|
#ifdef _WIN32
|
|
HANDLE thread = CreateThread(NULL, 0, _webui_server_thread, (void*)win, 0, NULL);
|
|
win->server_thread = thread;
|
|
if (thread != NULL)
|
|
CloseHandle(thread);
|
|
#else
|
|
pthread_t thread;
|
|
pthread_create(&thread, NULL,&_webui_server_thread, (void*)win);
|
|
pthread_detach(thread);
|
|
win->server_thread = thread;
|
|
#endif
|
|
}
|
|
|
|
} else {
|
|
|
|
// Refresh an existing running window (All connected clients)
|
|
|
|
// Packet Protocol Format:
|
|
// [...]
|
|
// [CMD]
|
|
// [URL]
|
|
|
|
// Send the packet
|
|
if (client) {
|
|
// Update single client
|
|
_webui_send_client(
|
|
win, client, 0, WEBUI_CMD_NAVIGATION,
|
|
(const char*)window_url, _webui_strlen(window_url), false
|
|
);
|
|
}
|
|
else {
|
|
// Update all clients
|
|
_webui_send_all(
|
|
win, 0, WEBUI_CMD_NAVIGATION,
|
|
(const char*)window_url, _webui_strlen(window_url)
|
|
);
|
|
}
|
|
|
|
// Free
|
|
_webui_free_mem((void*)window_url);
|
|
}
|
|
|
|
// Wait for window connection & token validation
|
|
if ((browser != NoBrowser) && _webui.config.show_wait_connection) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_show_window() -> Waiting for window connection & token validation\n");
|
|
#endif
|
|
|
|
size_t timeout = (_webui.startup_timeout > 0 ?
|
|
_webui.startup_timeout : WEBUI_DEF_TIMEOUT
|
|
);
|
|
|
|
if (_webui.is_webview) {
|
|
|
|
// WebView
|
|
|
|
_webui_timer_t timer;
|
|
_webui_timer_start(&timer);
|
|
for (;;) {
|
|
|
|
_webui_sleep(10);
|
|
|
|
// Process WebView rendering if any
|
|
if (_webui.is_webview) {
|
|
#ifdef _WIN32
|
|
// ...
|
|
#elif __linux__
|
|
if (_webui.is_webview) {
|
|
while (gtk_events_pending()) {
|
|
gtk_main_iteration_do(0);
|
|
}
|
|
}
|
|
#else
|
|
if (!_webui.is_wkwebview_main_run) {
|
|
if (_webui.is_webview) {
|
|
_webui_macos_wv_process();
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Stop if window is connected & token is valid
|
|
if (_webui_mutex_is_single_client_token_valid(win, WEBUI_MUTEX_NONE))
|
|
break;
|
|
|
|
// Stop if timer is finished
|
|
if (_webui_timer_is_end(&timer, (timeout * 1000)))
|
|
break;
|
|
}
|
|
} else {
|
|
|
|
// Web Browser
|
|
|
|
_webui_timer_t timer;
|
|
_webui_timer_start(&timer);
|
|
for (;;) {
|
|
|
|
_webui_sleep(10);
|
|
|
|
// Stop if window is connected & token is valid
|
|
if (_webui_mutex_is_single_client_token_valid(win, WEBUI_MUTEX_NONE))
|
|
break;
|
|
|
|
// Stop if timer is finished
|
|
if (_webui_timer_is_end(&timer, (timeout * 1000)))
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Return status of the window connection (not token validation)
|
|
return _webui_mutex_is_connected(win, WEBUI_MUTEX_NONE);
|
|
}
|
|
|
|
// The window is successfully launched.
|
|
return true;
|
|
}
|
|
|
|
static void _webui_window_event(
|
|
_webui_window_t* win, size_t connection_id, int event_type, char* element,
|
|
size_t event_number, size_t client_id, const char* cookies) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_window_event([%zu], [%zu], [%s])\n",
|
|
win->num, connection_id, element);
|
|
#endif
|
|
|
|
// New Event (General)
|
|
webui_event_t e;
|
|
e.window = win->num;
|
|
e.event_type = event_type;
|
|
e.element = element;
|
|
e.event_number = event_number;
|
|
e.connection_id = connection_id;
|
|
e.client_id = client_id;
|
|
e.cookies = (char*)cookies;
|
|
|
|
// Check for all events-bind functions
|
|
if (!_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) && win->has_all_events) {
|
|
size_t events_cb_index = 0;
|
|
bool exist = _webui_get_cb_index(win, "", &events_cb_index);
|
|
if (exist && win->cb[events_cb_index] != NULL) {
|
|
// Call user all-events cb
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_window_event() -> Calling all-events user callback\n");
|
|
printf("[Call]\n");
|
|
#endif
|
|
e.bind_id = events_cb_index;
|
|
win->cb[events_cb_index](&e);
|
|
}
|
|
}
|
|
|
|
if (!_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE)) {
|
|
// Check for the regular bind functions
|
|
if (!_webui_is_empty(element)) {
|
|
size_t cb_index = 0;
|
|
bool exist = _webui_get_cb_index(win, element, &cb_index);
|
|
if (exist && win->cb[cb_index] != NULL) {
|
|
// Call user cb
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_window_event() -> Calling user callback\n");
|
|
printf("[Call]\n");
|
|
#endif
|
|
e.bind_id = cb_index;
|
|
win->cb[cb_index](&e);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get event inf
|
|
webui_event_inf_t* event_inf = win->events[e.event_number];
|
|
if (event_inf != NULL) {
|
|
|
|
// Async response wait
|
|
if (_webui.config.asynchronous_response) {
|
|
bool done = false;
|
|
while (!done) {
|
|
_webui_sleep(10);
|
|
_webui_mutex_lock(&_webui.mutex_async_response);
|
|
if (event_inf->done) done = true;
|
|
_webui_mutex_unlock(&_webui.mutex_async_response);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_window_event() -> Finished\n");
|
|
#endif
|
|
}
|
|
|
|
static void _webui_send_client_ws(_webui_window_t* win, struct mg_connection* client,
|
|
size_t connection_id, char* packet, size_t packets_size) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_send_client_ws()\n");
|
|
printf("[Core]\t\t_webui_send_client_ws() -> Client #%zu\n", connection_id);
|
|
printf("[Core]\t\t_webui_send_client_ws() -> Packet size: %zu bytes \n", packets_size);
|
|
printf("[Core]\t\t_webui_send_client_ws() -> Packet hex : [ ");
|
|
_webui_print_hex(packet, packets_size);
|
|
printf("]\n");
|
|
#endif
|
|
|
|
if (win == NULL || client == NULL) {
|
|
WEBUI_ASSERT("_webui_send_client_ws() null ptr");
|
|
}
|
|
|
|
if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE) || packet == NULL ||
|
|
packets_size < WEBUI_PROTOCOL_SIZE)
|
|
return;
|
|
|
|
int ret = 0;
|
|
if (win->num > 0 && win->num < WEBUI_MAX_IDS) {
|
|
if (client != NULL) {
|
|
// Mutex
|
|
_webui_mutex_lock(&_webui.mutex_send);
|
|
ret = mg_websocket_write(client, MG_WEBSOCKET_OPCODE_BINARY, packet, packets_size);
|
|
_webui_mutex_unlock(&_webui.mutex_send);
|
|
}
|
|
}
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_send_client_ws() -> %d bytes sent.\n", ret);
|
|
#endif
|
|
}
|
|
|
|
static char* _webui_get_current_path(void) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_get_current_path()\n");
|
|
#endif
|
|
|
|
char* path = (char*)_webui_malloc(WEBUI_MAX_PATH);
|
|
if (WEBUI_GET_CURRENT_DIR(path, WEBUI_MAX_PATH) == NULL)
|
|
path[0] = 0x00;
|
|
|
|
return path;
|
|
}
|
|
|
|
static void _webui_free_port(size_t port) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_free_port([%zu])\n", port);
|
|
#endif
|
|
|
|
for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
|
|
if (_webui.used_ports[i] == port) {
|
|
_webui.used_ports[i] = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static size_t _webui_get_free_port(void) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_get_free_port()\n");
|
|
#endif
|
|
|
|
size_t port = (rand() % (WEBUI_MAX_PORT + 1 - WEBUI_MIN_PORT)) + WEBUI_MIN_PORT;
|
|
|
|
for (size_t i = WEBUI_MIN_PORT; i <= WEBUI_MAX_PORT; i++) {
|
|
|
|
// Search [port] in [_webui.used_ports]
|
|
bool found = false;
|
|
for (size_t j = 0; j < WEBUI_MAX_IDS; j++) {
|
|
if (_webui.used_ports[j] == port) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
// Port used by local window
|
|
port = (rand() % (WEBUI_MAX_PORT + 1 - WEBUI_MIN_PORT)) + WEBUI_MIN_PORT;
|
|
else {
|
|
if (_webui_port_is_used(port))
|
|
// Port used by an external app
|
|
port = (rand() % (WEBUI_MAX_PORT + 1 - WEBUI_MIN_PORT)) + WEBUI_MIN_PORT;
|
|
else
|
|
// Port is free
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Add
|
|
for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
|
|
if (_webui.used_ports[i] == 0) {
|
|
_webui.used_ports[i] = port;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return port;
|
|
}
|
|
|
|
static void _webui_init(void) {
|
|
|
|
if (_webui.initialized)
|
|
return;
|
|
memset(&_webui, 0, sizeof(_webui_core_t));
|
|
_webui.initialized = true;
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\tWebUI v"
|
|
WEBUI_VERSION " ("
|
|
WEBUI_OS ", "
|
|
WEBUI_COMPILER ", "
|
|
WEBUI_LIB_TYPE ", "
|
|
WEBUI_SECURE ")\n");
|
|
printf("[Core]\t\t_webui_init()\n");
|
|
#endif
|
|
|
|
// Initializing mutex
|
|
_webui_mutex_init(&_webui.mutex_server_start);
|
|
_webui_mutex_init(&_webui.mutex_send);
|
|
_webui_mutex_init(&_webui.mutex_receive);
|
|
_webui_mutex_init(&_webui.mutex_wait);
|
|
_webui_mutex_init(&_webui.mutex_bridge);
|
|
_webui_mutex_init(&_webui.mutex_js_run);
|
|
_webui_mutex_init(&_webui.mutex_win_connect);
|
|
_webui_mutex_init(&_webui.mutex_exit_now);
|
|
_webui_mutex_init(&_webui.mutex_webview_stop);
|
|
_webui_mutex_init(&_webui.mutex_http_handler);
|
|
_webui_mutex_init(&_webui.mutex_client);
|
|
_webui_mutex_init(&_webui.mutex_async_response);
|
|
_webui_mutex_init(&_webui.mutex_mem);
|
|
_webui_mutex_init(&_webui.mutex_token);
|
|
_webui_condition_init(&_webui.condition_wait);
|
|
|
|
// Random
|
|
#ifdef _WIN32
|
|
srand((unsigned int) time(NULL));
|
|
#else
|
|
srand(time(NULL));
|
|
#endif
|
|
|
|
// Initializing core
|
|
_webui.startup_timeout = WEBUI_DEF_TIMEOUT;
|
|
_webui.executable_path = _webui_get_current_path();
|
|
_webui.default_server_root_path = (char*)_webui_malloc(WEBUI_MAX_PATH);
|
|
|
|
// Initializing configs
|
|
_webui.config.show_wait_connection = true;
|
|
_webui.config.use_cookies = true;
|
|
|
|
// Initializing server services
|
|
#ifdef WEBUI_TLS
|
|
if ((unsigned)mg_init_library(MG_FEATURES_TLS) != (unsigned)MG_FEATURES_TLS) {
|
|
WEBUI_ASSERT("mg_init_library() failed");
|
|
}
|
|
#else
|
|
mg_init_library(0);
|
|
#endif
|
|
}
|
|
|
|
static const char* _webui_url_encode(const char* str) {
|
|
|
|
#ifdef WEBUI_LOG_VERBOSE
|
|
printf("[Core]\t\t_webui_url_encode()\n");
|
|
#endif
|
|
|
|
const char* hex = "0123456789ABCDEF";
|
|
size_t len = _webui_strlen(str);
|
|
char* encoded = (char*)_webui_malloc(4 * len + 1);
|
|
if (!encoded)
|
|
return NULL;
|
|
|
|
char* pOutput = encoded;
|
|
while(*str) {
|
|
unsigned char byte = (unsigned char)(*str);
|
|
if (isalnum(byte) || byte == '-' || byte == '_' || byte == '.' || byte == '~') {
|
|
* pOutput++ = byte;
|
|
} else {
|
|
* pOutput++ = '%';
|
|
* pOutput++ = hex[byte >> 4];
|
|
* pOutput++ = hex[byte&15];
|
|
}
|
|
str++;
|
|
}
|
|
|
|
return (const char*)encoded;
|
|
}
|
|
|
|
static bool _webui_get_cb_index(_webui_window_t* win, const char* element, size_t* id) {
|
|
|
|
_webui_mutex_lock(&_webui.mutex_bridge);
|
|
|
|
#ifdef WEBUI_LOG_VERBOSE
|
|
printf("[Core]\t\t_webui_get_cb_index([%zu])\n", win->num);
|
|
printf("[Core]\t\t_webui_get_cb_index() -> Element: [%s]\n", element);
|
|
#endif
|
|
|
|
// Search
|
|
if (element != NULL) {
|
|
for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
|
|
if (win->html_elements[i] != NULL) {
|
|
if (strcmp(win->html_elements[i], element) == 0) {
|
|
#ifdef WEBUI_LOG_VERBOSE
|
|
printf("[Core]\t\t_webui_get_cb_index() -> Found at %zu\n", i);
|
|
#endif
|
|
_webui_mutex_unlock(&_webui.mutex_bridge);
|
|
*id = i;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef WEBUI_LOG_VERBOSE
|
|
printf("[Core]\t\t_webui_get_cb_index() -> Not found\n");
|
|
#endif
|
|
|
|
_webui_mutex_unlock(&_webui.mutex_bridge);
|
|
return false;
|
|
}
|
|
|
|
#ifdef WEBUI_LOG
|
|
static void _webui_print_hex(const char* data, size_t len) {
|
|
for (size_t i = 0; i < len; i++) {
|
|
printf("0x%02X ", (unsigned char)* data);
|
|
data++;
|
|
}
|
|
}
|
|
static void _webui_print_ascii(const char* data, size_t len) {
|
|
for (size_t i = 0; i < len; i++) {
|
|
if ((unsigned char)* data == 0x00)
|
|
putchar(0xCF); // ¤
|
|
else
|
|
printf("%c", (unsigned char)* data);
|
|
data++;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// HTTP Server
|
|
|
|
static void _webui_http_send_header(
|
|
_webui_window_t* win, struct mg_connection* client,
|
|
const char* mime_type, size_t body_len, bool cache) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_http_send_header([%zu])\n", win->num);
|
|
printf("[Core]\t\t_webui_http_send_header() -> mime_type: [%s]\n", mime_type);
|
|
printf("[Core]\t\t_webui_http_send_header() -> body_len: [%zu]\n", body_len);
|
|
printf("[Core]\t\t_webui_http_send_header() -> cache: [%d]\n", cache);
|
|
#endif
|
|
|
|
const char* no_cache = "no-cache, no-store, must-revalidate, private, max-age=0";
|
|
const char* with_cache = "public, max-age=31536000";
|
|
const char* cache_header = (cache ? with_cache : no_cache);
|
|
|
|
// Cookies
|
|
bool set_cookies = false;
|
|
size_t new_client_id = 0;
|
|
if (_webui.config.use_cookies) {
|
|
// Cookies config is enabled
|
|
char cookies[WEBUI_COOKIES_BUF] = {0};
|
|
_webui_get_cookies(client, cookies);
|
|
bool client_found = false;
|
|
if (!_webui_is_empty(cookies)) {
|
|
size_t client_id = 0;
|
|
if (_webui_client_cookies_get_id(win, cookies, &client_id)) {
|
|
client_found = true;
|
|
}
|
|
}
|
|
if (!client_found) {
|
|
// Browser does not have cookies yet, let's set a new cookies
|
|
char new_auth_cookies[WEBUI_COOKIES_BUF];
|
|
_webui_generate_cookies(new_auth_cookies, WEBUI_COOKIES_LEN);
|
|
if (_webui_client_cookies_save(win, new_auth_cookies, &new_client_id)) {
|
|
set_cookies = true;
|
|
_webui.cookies_single_set[win->num] = true;
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_http_send() -> New auth cookies [%s]\n",
|
|
_webui.cookies[new_client_id]
|
|
);
|
|
#endif
|
|
}
|
|
else {
|
|
// Cookies list is full
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_http_send() -> Cookies list is full\n");
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
// [header only]
|
|
char buffer[1024] = {0};
|
|
int to_send = 0;
|
|
if (set_cookies) {
|
|
// Header with auth cookies
|
|
to_send = WEBUI_SN_PRINTF_STATIC(buffer, sizeof(buffer),
|
|
"HTTP/1.1 200 OK\r\n"
|
|
"Set-Cookie: webui_auth=%s; Path=/; HttpOnly; SameSite=Strict\r\n"
|
|
"Access-Control-Allow-Origin: *\r\n"
|
|
"Cache-Control: %s\r\n"
|
|
"Content-Type: %s\r\n"
|
|
"Content-Length: %zu\r\n"
|
|
"Connection: close\r\n\r\n",
|
|
_webui.cookies[new_client_id],
|
|
cache_header, mime_type, body_len
|
|
);
|
|
}
|
|
else {
|
|
// Header without auth cookies
|
|
to_send = WEBUI_SN_PRINTF_STATIC(buffer, sizeof(buffer),
|
|
"HTTP/1.1 200 OK\r\n"
|
|
"Access-Control-Allow-Origin: *\r\n"
|
|
"Cache-Control: %s\r\n"
|
|
"Content-Type: %s\r\n"
|
|
"Content-Length: %zu\r\n"
|
|
"Connection: close\r\n\r\n",
|
|
cache_header, mime_type, body_len
|
|
);
|
|
}
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("---[ HTTP Header ]-----------------\n");
|
|
printf("%s\n", buffer);
|
|
printf("-----------------------------------\n");
|
|
#endif
|
|
|
|
// Send
|
|
mg_write(client, buffer, to_send);
|
|
}
|
|
|
|
static void _webui_http_send_file(
|
|
_webui_window_t* win, struct mg_connection* client,
|
|
const char* mime_type, const char* path, bool cache) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_http_send_file([%zu])\n", win->num);
|
|
#endif
|
|
|
|
// Open the file
|
|
FILE* file = NULL;
|
|
WEBUI_FILE_OPEN(file, path, "rb");
|
|
if (!file) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_http_send_file() -> Can't open file [%s]\n", path);
|
|
#endif
|
|
_webui_http_send_error(client, webui_html_res_not_available, 404);
|
|
return;
|
|
}
|
|
|
|
// Get file size
|
|
fseek(file, 0, SEEK_END);
|
|
size_t file_size = ftell(file);
|
|
fseek(file, 0, SEEK_SET);
|
|
|
|
// Send header
|
|
_webui_http_send_header(win, client, mime_type, file_size, cache);
|
|
|
|
// Send body
|
|
mg_send_file_body(client, path);
|
|
|
|
fclose(file);
|
|
}
|
|
|
|
static void _webui_http_send(
|
|
_webui_window_t* win, struct mg_connection* client,
|
|
const char* mime_type, const char* body, size_t body_len, bool cache) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_http_send([%zu])\n", win->num);
|
|
#endif
|
|
|
|
// Send header
|
|
_webui_http_send_header(win, client, mime_type, body_len, cache);
|
|
|
|
// Send body
|
|
mg_write(client, body, body_len);
|
|
}
|
|
|
|
static void _webui_http_send_error(struct mg_connection* client, const char* body, int status) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_http_send_error()\n");
|
|
#endif
|
|
|
|
// [header][body]
|
|
int to_send = 0;
|
|
size_t body_len = _webui_strlen(body);
|
|
size_t buffer_len = (512 + body_len);
|
|
char* buffer = (char*)_webui_malloc(buffer_len);
|
|
to_send = WEBUI_SN_PRINTF_DYN(buffer, buffer_len,
|
|
"HTTP/1.1 %d OK\r\n"
|
|
"Content-Type: text/html; charset=utf-8\r\n"
|
|
"Cache-Control: no-cache, no-store, must-revalidate, private, max-age=0\r\n"
|
|
"Pragma: no-cache\r\n"
|
|
"Expires: 0\r\n"
|
|
"Content-Length: %zu\r\n"
|
|
"Connection: close\r\n\r\n%s",
|
|
status, body_len, body
|
|
);
|
|
|
|
// Send
|
|
mg_write(client, buffer, to_send);
|
|
_webui_free_mem((void*)buffer);
|
|
}
|
|
|
|
#ifdef WEBUI_LOG
|
|
static int _webui_http_log(const struct mg_connection* client, const char* message) {
|
|
(void)client;
|
|
printf("[Core]\t\t_webui_http_log()\n");
|
|
printf("[Core]\t\t_webui_http_log() -> Log: %s.\n", message);
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
// static size_t _webui_hash_djb2(const char* s) {
|
|
// // DJB2 Algorithm
|
|
// size_t hash = 5381;
|
|
// int c;
|
|
// while ((c = *s++)) {
|
|
// // hash * 33 + c
|
|
// hash = ((hash << 5) + hash) + c;
|
|
// }
|
|
// return hash;
|
|
// }
|
|
|
|
static void _webui_generate_cookies(char* cookies, size_t length) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_generate_cookies()\n");
|
|
#endif
|
|
const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
size_t charset_size = sizeof(charset) - 1;
|
|
for (size_t i = 0; i < length - 1; i++) {
|
|
uint32_t random_value = _webui_generate_random_uint32();
|
|
int key = random_value % charset_size;
|
|
cookies[i] = charset[key];
|
|
}
|
|
cookies[length - 1] = '\0';
|
|
}
|
|
|
|
static bool _webui_client_cookies_save(_webui_window_t* win, const char* cookies, size_t* client_id) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_client_cookies_save()\n");
|
|
#endif
|
|
// [win number][_][cookies]
|
|
char win_cookies[WEBUI_COOKIES_BUF];
|
|
WEBUI_SN_PRINTF_STATIC(win_cookies, sizeof(win_cookies), "%zu_%s", win->num, cookies);
|
|
for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
|
|
if (_webui.cookies[i] == NULL) {
|
|
_webui.cookies[i] = _webui_str_dup(win_cookies);
|
|
*client_id = i;
|
|
return true;
|
|
}
|
|
}
|
|
// List is full
|
|
return false;
|
|
}
|
|
|
|
static bool _webui_client_cookies_get_id(_webui_window_t* win, const char* cookies, size_t* client_id) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_client_cookies_get_id()\n");
|
|
#endif
|
|
for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
|
|
if (_webui.cookies[i] != NULL) {
|
|
if (strcmp(_webui.cookies[i], cookies) == 0) {
|
|
*client_id = i;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
// Not found
|
|
return false;
|
|
}
|
|
|
|
static size_t _webui_client_get_id(_webui_window_t* win, struct mg_connection* client) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_client_get_id()\n");
|
|
#endif
|
|
size_t client_id = 0;
|
|
if (_webui.config.use_cookies) {
|
|
char cookies[WEBUI_COOKIES_BUF] = {0};
|
|
_webui_get_cookies(client, cookies);
|
|
_webui_client_cookies_get_id(win, cookies, &client_id);
|
|
}
|
|
return client_id;
|
|
}
|
|
|
|
// static void _webui_client_cookies_free_all(_webui_window_t* win) {
|
|
// #ifdef WEBUI_LOG
|
|
// printf("[Core]\t\t_webui_client_cookies_free_all()\n");
|
|
// #endif
|
|
// // [win number]
|
|
// char win_num[24];
|
|
// WEBUI_SN_PRINTF_STATIC(win_num, sizeof(win_num), "%zu_", win->num);
|
|
// size_t len = strlen(win_num);
|
|
// for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
|
|
// if (_webui.cookies[i] != NULL) {
|
|
// // [{win_number}_{cookies}]
|
|
// if (strncmp(_webui.cookies[i], win_num, len) == 0) {
|
|
// _webui_free_mem((void*)_webui.cookies[i]);
|
|
// _webui.cookies[i] = NULL;
|
|
// }
|
|
// }
|
|
// }
|
|
// // Single
|
|
// _webui.cookies_single_set[win->num] = false;
|
|
// }
|
|
|
|
// static void _webui_client_cookies_free(_webui_window_t* win, struct mg_connection* client) {
|
|
// #ifdef WEBUI_LOG
|
|
// printf("[Core]\t\t_webui_client_cookies_free()\n");
|
|
// #endif
|
|
// char cookies[WEBUI_COOKIES_BUF] = {0};
|
|
// _webui_get_cookies(client, cookies);
|
|
// if (cookies != NULL) {
|
|
// size_t client_id = 0;
|
|
// if (_webui_client_cookies_get_id(win, cookies, &client_id)) {
|
|
// _webui_free_mem((void*)_webui.cookies[client_id]);
|
|
// _webui.cookies[client_id] = NULL;
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
static const char* _webui_get_cookies_full(const struct mg_connection* client) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_get_cookies_full()\n");
|
|
#endif
|
|
const char* header = mg_get_header(client, "Cookie");
|
|
if (header != NULL)
|
|
return header;
|
|
return "";
|
|
}
|
|
|
|
static void _webui_get_cookies(const struct mg_connection* client, char* buffer) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_get_cookies()\n");
|
|
#endif
|
|
const char* header = mg_get_header(client, "Cookie");
|
|
if (!_webui_is_empty(header)) {
|
|
mg_get_cookie(header, "webui_auth", buffer, WEBUI_COOKIES_BUF);
|
|
}
|
|
}
|
|
|
|
static int _webui_http_handler(struct mg_connection* client, void * _win) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_http_handler()\n");
|
|
#endif
|
|
|
|
// Mutex
|
|
_webui_mutex_lock(&_webui.mutex_http_handler);
|
|
|
|
// Get the window object
|
|
_webui_window_t* win = _webui_dereference_win_ptr(_win);
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || win == NULL) {
|
|
_webui_mutex_unlock(&_webui.mutex_http_handler);
|
|
return 500; // Internal Server Error
|
|
}
|
|
|
|
// Initializing
|
|
int http_status_code = 200;
|
|
const struct mg_request_info * ri = mg_get_request_info(client);
|
|
const char* url = ri->local_uri;
|
|
|
|
if (strcmp(ri->request_method, "GET") == 0) {
|
|
|
|
// GET
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_http_handler() -> GET [%s]\n", url);
|
|
#endif
|
|
|
|
// Cookies
|
|
size_t client_id = 0;
|
|
bool client_found = false;
|
|
if (_webui.config.use_cookies) {
|
|
// Cookies config is enabled
|
|
char cookies[WEBUI_COOKIES_BUF] = {0};
|
|
_webui_get_cookies(client, cookies);
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_http_handler() -> Client cookies [%s]\n", cookies);
|
|
#endif
|
|
// Get client ID based on `webui_auth` cookies
|
|
if (_webui_client_cookies_get_id(win, cookies, &client_id)) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_http_handler() -> Client ID found [%zu]\n", client_id);
|
|
#endif
|
|
client_found = true;
|
|
}
|
|
#ifdef WEBUI_LOG
|
|
else {
|
|
printf("[Core]\t\t_webui_http_handler() -> Client ID not found\n");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Single client authorisation
|
|
if (!_webui.config.multi_client) {
|
|
if (!client_found) {
|
|
if ((_webui.cookies_single_set[win->num])) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_http_handler() -> 403 Forbidden\n");
|
|
#endif
|
|
_webui_http_send_error(client, webui_html_served, 403);
|
|
_webui_mutex_unlock(&_webui.mutex_http_handler);
|
|
return 403;
|
|
}
|
|
} else _webui.cookies_single_set[win->num] = true;
|
|
}
|
|
|
|
// Let the server thread waits more time for `webui.js`
|
|
win->wait = true;
|
|
|
|
if (strcmp(url, "/webui.js") == 0) {
|
|
|
|
// WebUI Bridge
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_http_handler() -> WebUI-Bridge\n");
|
|
#endif
|
|
|
|
// Generate JavaScript bridge
|
|
const char* js = _webui_generate_js_bridge(win, client);
|
|
|
|
if (js != NULL) {
|
|
// Send 200
|
|
_webui_http_send(win, client, "application/javascript", js, _webui_strlen(js), false);
|
|
_webui_free_mem((void*)js);
|
|
} else {
|
|
// Non-authorized request to `webui.js`, like requesting twice
|
|
// Send 200 (Empty)
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_http_handler() -> Non-authorized request to webui.js\n");
|
|
#endif
|
|
_webui_http_send(win, client, "application/javascript", "", 0, false);
|
|
}
|
|
}
|
|
else if ((win->files_handler != NULL || (win->files_handler_window != NULL)) && (_webui_external_file_handler(win, client, client_id) != 0)) {
|
|
|
|
// File already handled by the custom external file handler
|
|
// nothing to do now.
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_http_handler() -> Handled by custom external file handler\n");
|
|
#endif
|
|
}
|
|
else if (strcmp(url, "/") == 0) {
|
|
|
|
// [/]
|
|
|
|
if (win->is_embedded_html) {
|
|
|
|
// Main HTML
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_http_handler() -> Embedded Index HTML\n");
|
|
#endif
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("---[ HTML (%zu bytes)]--------------\n", _webui_strlen(win->html));
|
|
printf("%s\n", win->html);
|
|
printf("------------------------------------\n");
|
|
#endif
|
|
|
|
// Send 200
|
|
_webui_http_send(win, client, "text/html", win->html, _webui_strlen(win->html), false);
|
|
}
|
|
else {
|
|
|
|
// Looking for index file and redirect
|
|
|
|
const char* index_files[] = {"index.ts", "index.js", "index.html", "index.htm"};
|
|
|
|
// [Path][Sep][File Name]
|
|
size_t bf_len = (_webui_strlen(win->server_root_path) + 1 + 24);
|
|
char* index_path = (char*)_webui_malloc(bf_len);
|
|
for (size_t i = 0; i < (sizeof(index_files) / sizeof(index_files[0])); i++) {
|
|
WEBUI_SN_PRINTF_DYN(index_path, bf_len, "%s%s%s",
|
|
win->server_root_path, os_sep, index_files[i]
|
|
);
|
|
if (_webui_file_exist(index_path)) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_http_handler() -> 302 Redirecting to [%s]\n", index_files[i]);
|
|
#endif
|
|
mg_send_http_redirect(client, index_files[i], 302);
|
|
_webui_free_mem((void*)index_path);
|
|
_webui_mutex_unlock(&_webui.mutex_http_handler);
|
|
return 302;
|
|
}
|
|
}
|
|
|
|
// No index file is found in this folder
|
|
_webui_free_mem((void*)index_path);
|
|
_webui_http_send_error(client, webui_html_res_not_available, 404);
|
|
http_status_code = 404;
|
|
}
|
|
}
|
|
else if (strcmp(url, "/favicon.ico") == 0 || strcmp(url, "/favicon.svg") == 0) {
|
|
|
|
// Favicon
|
|
|
|
if (win->icon != NULL && win->icon_type != NULL) {
|
|
|
|
// Custom user icon
|
|
|
|
// User icon 200
|
|
_webui_http_send(win, client, win->icon_type, win->icon, _webui_strlen(win->icon), false);
|
|
}
|
|
else if (_webui_file_exist_mg(win, client)) {
|
|
|
|
// Local icon file
|
|
http_status_code = _webui_serve_file(win, client, client_id);
|
|
}
|
|
else {
|
|
|
|
// Default embedded icon
|
|
|
|
if (strcmp(url, "/favicon.ico") == 0) {
|
|
|
|
mg_send_http_redirect(client, "favicon.svg", 302);
|
|
http_status_code = 302;
|
|
}
|
|
else {
|
|
|
|
// Default icon 200
|
|
_webui_http_send(win, client, webui_def_icon_type, webui_def_icon, _webui_strlen(webui_def_icon), false);
|
|
}
|
|
}
|
|
} else {
|
|
|
|
// [/file] or [/folder]
|
|
|
|
// [Path][Sep][folder]
|
|
char* folder_path = _webui_get_full_path(win, url);
|
|
|
|
if (_webui_file_exist(folder_path)) {
|
|
|
|
// [/file]
|
|
|
|
bool script = false;
|
|
if (win->runtime != None) {
|
|
const char* extension = _webui_get_extension(url);
|
|
const char* index_extensions[] = {
|
|
"js", "ts"
|
|
};
|
|
for (size_t i = 0; i < (sizeof(index_extensions) / sizeof(index_extensions[0])); i++) {
|
|
if (strcmp(extension, index_extensions[i]) == 0) {
|
|
script = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (script) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_http_handler() -> Interpret local script file\n");
|
|
#endif
|
|
|
|
// Serve as a script file to be interpreted by
|
|
// an external interpreter (Deno, Bun, Nodejs)
|
|
http_status_code = _webui_interpret_file(win, client, NULL, client_id);
|
|
}
|
|
else {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_http_handler() -> Local file\n");
|
|
#endif
|
|
|
|
// Serve as a normal text-based file
|
|
http_status_code = _webui_serve_file(win, client, client_id);
|
|
}
|
|
}
|
|
else if (_webui_folder_exist(folder_path)) {
|
|
|
|
// [/folder]
|
|
|
|
// Looking for index file and redirect
|
|
|
|
const char* index_files[] = {"index.ts", "index.js", "index.html", "index.htm"};
|
|
|
|
// [Path][Sep][File Name]
|
|
size_t bf_len = (_webui_strlen(folder_path) + 1 + 24);
|
|
char* index_path = (char*)_webui_malloc(bf_len);
|
|
for (size_t i = 0; i < (sizeof(index_files) / sizeof(index_files[0])); i++) {
|
|
WEBUI_SN_PRINTF_DYN(index_path, bf_len, "%s%s%s", folder_path, os_sep, index_files[i]);
|
|
if (_webui_file_exist(index_path)) {
|
|
// [URL][/][Index Name]
|
|
size_t redirect_len = (_webui_strlen(url) + 1 + 24);
|
|
char* redirect_url = (char*)_webui_malloc(bf_len);
|
|
WEBUI_SN_PRINTF_DYN(redirect_url, redirect_len, "%s%s%s", url, "/", index_files[i]);
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_http_handler() -> 302 Redirecting to [%s]\n", redirect_url);
|
|
#endif
|
|
mg_send_http_redirect(client, redirect_url, 302);
|
|
_webui_free_mem((void*)redirect_url);
|
|
_webui_free_mem((void*)folder_path);
|
|
_webui_free_mem((void*)index_path);
|
|
_webui_mutex_unlock(&_webui.mutex_http_handler);
|
|
return 302;
|
|
}
|
|
}
|
|
|
|
// No index file is found in this folder
|
|
_webui_free_mem((void*)folder_path);
|
|
_webui_free_mem((void*)index_path);
|
|
_webui_http_send_error(client, webui_html_res_not_available, 404);
|
|
http_status_code = 404;
|
|
}
|
|
else {
|
|
|
|
// [invalid]
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_http_handler() -> Not found\n");
|
|
#endif
|
|
|
|
// No file or folder is found at this path
|
|
_webui_http_send_error(client, webui_html_res_not_available, 404);
|
|
_webui_free_mem((void*)folder_path);
|
|
http_status_code = 404;
|
|
}
|
|
|
|
// Clear
|
|
_webui_free_mem((void*)folder_path);
|
|
}
|
|
} else {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_http_handler() -> Unknown request method [%s]\n", ri->request_method);
|
|
#endif
|
|
}
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_http_handler() -> HTTP Status Code: %d\n", http_status_code);
|
|
#endif
|
|
|
|
_webui_mutex_unlock(&_webui.mutex_http_handler);
|
|
return http_status_code;
|
|
}
|
|
|
|
static int _webui_ws_connect_handler(const struct mg_connection* client, void * _win) {
|
|
(void)client;
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_ws_connect_handler()\n");
|
|
#endif
|
|
|
|
// Dereference
|
|
_webui_window_t* win = _webui_dereference_win_ptr(_win);
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || win == NULL)
|
|
return 1;
|
|
|
|
// Check connection status
|
|
if (!_webui.config.multi_client) {
|
|
if (_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE)) {
|
|
// Multi-client is disabled, and the single client already connected.
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_ws_connect_handler() -> Single client already connected\n");
|
|
printf("[Core]\t\t_webui_ws_connect_handler() -> Non-authorized connection\n");
|
|
#endif
|
|
// Block handshake
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// Cookies
|
|
size_t client_id = 0;
|
|
bool client_found = false;
|
|
if (_webui.config.use_cookies) {
|
|
// Cookies config is enabled
|
|
char cookies[WEBUI_COOKIES_BUF] = {0};
|
|
_webui_get_cookies(client, cookies);
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_ws_connect_handler() -> Client cookies [%s]\n", cookies);
|
|
#endif
|
|
// Get client ID based on `webui_auth` cookies
|
|
if (_webui_client_cookies_get_id(win, cookies, &client_id)) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_ws_connect_handler() -> Client ID found [%zu]\n", client_id);
|
|
#endif
|
|
client_found = true;
|
|
}
|
|
#ifdef WEBUI_LOG
|
|
else {
|
|
printf("[Core]\t\t_webui_ws_connect_handler() -> Client ID not found\n");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Single client authorisation
|
|
if (!_webui.config.multi_client) {
|
|
if (!client_found) {
|
|
if ((_webui.cookies_single_set[win->num])) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_ws_connect_handler() -> 403 Forbidden\n");
|
|
#endif
|
|
// Block handshake
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_ws_connect_handler() -> Connection authentication OK\n");
|
|
#endif
|
|
|
|
// OK. Process handshake
|
|
return 0;
|
|
}
|
|
|
|
static void _webui_ws_ready_handler(struct mg_connection* client, void * _win) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_ws_ready_handler()\n");
|
|
#endif
|
|
|
|
// Dereference
|
|
_webui_window_t* win = _webui_dereference_win_ptr(_win);
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || win == NULL)
|
|
return;
|
|
|
|
_webui_receive(win, client, WEBUI_WS_OPEN, NULL, 0);
|
|
}
|
|
|
|
static int _webui_ws_data_handler(struct mg_connection* client, int opcode, char* data, size_t datasize, void * _win) {
|
|
(void)client;
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_ws_data_handler()\n");
|
|
#endif
|
|
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || datasize < WEBUI_PROTOCOL_SIZE)
|
|
return 1; // OK
|
|
|
|
switch(opcode&0xf) {
|
|
|
|
case MG_WEBSOCKET_OPCODE_BINARY: {
|
|
_webui_window_t* win = _webui_dereference_win_ptr(_win);
|
|
if (win != NULL)
|
|
_webui_receive(win, client, WEBUI_WS_DATA, data, datasize);
|
|
break;
|
|
}
|
|
case MG_WEBSOCKET_OPCODE_TEXT: {
|
|
break;
|
|
}
|
|
case MG_WEBSOCKET_OPCODE_PING: {
|
|
break;
|
|
}
|
|
case MG_WEBSOCKET_OPCODE_PONG: {
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_ws_data_handler() -> Finished\n");
|
|
#endif
|
|
|
|
// OK
|
|
return 1;
|
|
}
|
|
|
|
static void _webui_ws_close_handler(const struct mg_connection* client, void * _win) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_ws_close_handler()\n");
|
|
#endif
|
|
|
|
// Dereference
|
|
_webui_window_t* win = _webui_dereference_win_ptr(_win);
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || win == NULL || !_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE))
|
|
return;
|
|
|
|
_webui_receive(win, (struct mg_connection*)client, WEBUI_WS_CLOSE, NULL, 0);
|
|
}
|
|
|
|
static WEBUI_THREAD_SERVER_START {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_server_thread()\n");
|
|
#endif
|
|
|
|
// Mutex
|
|
_webui_mutex_lock(&_webui.mutex_server_start);
|
|
|
|
_webui_window_t* win = _webui_dereference_win_ptr(arg);
|
|
if (win == NULL || win->server_running) {
|
|
_webui_mutex_unlock(&_webui.mutex_server_start);
|
|
WEBUI_THREAD_RETURN
|
|
}
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_server_thread([%zu]) -> URL: [%s]\n", win->num, win->url);
|
|
#endif
|
|
|
|
// Folder monitor thread
|
|
bool monitor_created = false;
|
|
#ifdef _WIN32
|
|
HANDLE monitor_thread = NULL;
|
|
#else
|
|
pthread_t monitor_thread;
|
|
#endif
|
|
|
|
// Initialization
|
|
_webui.servers++;
|
|
win->server_running = true;
|
|
if (_webui.startup_timeout < 1)
|
|
_webui.startup_timeout = 0;
|
|
if (_webui.startup_timeout > WEBUI_MAX_TIMEOUT)
|
|
_webui.startup_timeout = WEBUI_MAX_TIMEOUT;
|
|
|
|
// Public host access
|
|
char host[16] = {0};
|
|
if (!win->is_public)
|
|
// Private localhost access
|
|
WEBUI_STR_COPY_STATIC(host, sizeof(host), "127.0.0.1:");
|
|
|
|
#ifdef WEBUI_TLS
|
|
// HTTP Secure Port
|
|
char* server_port = (char*)_webui_malloc(64);
|
|
WEBUI_SN_PRINTF_DYN(server_port, 64, "%s%zus", host, win->server_port);
|
|
#else
|
|
// HTTP Port
|
|
char* server_port = (char*)_webui_malloc(64);
|
|
WEBUI_SN_PRINTF_DYN(server_port, 64, "%s%zu", host, win->server_port);
|
|
#endif
|
|
|
|
// Server Options
|
|
const char* http_options[] = {
|
|
// HTTP
|
|
"listening_ports", server_port,
|
|
"document_root", win->server_root_path,
|
|
"access_control_allow_headers", "*",
|
|
"access_control_allow_methods", "*",
|
|
"access_control_allow_origin", "*",
|
|
#ifdef WEBUI_TLS
|
|
"authentication_domain", "localhost",
|
|
"enable_auth_domain_check", "no",
|
|
"ssl_protocol_version", "4",
|
|
"ssl_cipher_list", "ECDH+AESGCM+AES256:!aNULL:!MD5:!DSS",
|
|
"strict_transport_security_max_age", WEBUI_SSL_EXPIRE_STR,
|
|
#endif
|
|
// WS
|
|
"websocket_timeout_ms", "3600000",
|
|
"enable_websocket_ping_pong", "yes",
|
|
NULL, NULL
|
|
};
|
|
|
|
// Server Settings
|
|
struct mg_callbacks http_callbacks;
|
|
struct mg_context * http_ctx = NULL;
|
|
memset(&http_callbacks, 0, sizeof(http_callbacks));
|
|
#ifdef WEBUI_TLS
|
|
http_callbacks.init_ssl = _webui_tls_initialization;
|
|
#endif
|
|
#ifdef WEBUI_LOG
|
|
http_callbacks.log_message = _webui_http_log;
|
|
#endif
|
|
|
|
// Start Server
|
|
http_ctx = mg_start(&http_callbacks, 0, http_options);
|
|
mg_set_request_handler(http_ctx, "/", _webui_http_handler, (void*)win);
|
|
|
|
if (http_ctx) {
|
|
|
|
mg_set_websocket_handler(
|
|
http_ctx, "/_webui_ws_connect", _webui_ws_connect_handler, _webui_ws_ready_handler,
|
|
_webui_ws_data_handler, _webui_ws_close_handler, (void*)win
|
|
);
|
|
|
|
// Mutex
|
|
_webui_mutex_unlock(&_webui.mutex_server_start);
|
|
|
|
if (_webui.startup_timeout > 0) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_server_thread([%zu]) -> Listening Success\n",
|
|
win->num);
|
|
printf("[Core]\t\t_webui_server_thread([%zu]) -> HTTP/WS Port: %s\n",
|
|
win->num, server_port);
|
|
printf("[Core]\t\t_webui_server_thread([%zu]) -> Root path: %s\n",
|
|
win->num, win->server_root_path);
|
|
printf("[Core]\t\t_webui_server_thread([%zu]) -> Timeout is %zu seconds\n",
|
|
win->num, _webui.startup_timeout);
|
|
#endif
|
|
|
|
bool stop = false;
|
|
win->wait = false;
|
|
|
|
while(!stop) {
|
|
|
|
if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE)) {
|
|
|
|
// UI is not connected
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[Core]\t\t_webui_server_thread([%zu]) -> Waiting for connection\n",
|
|
win->num
|
|
);
|
|
#endif
|
|
|
|
// Wait for first connection
|
|
_webui_timer_t timer_1;
|
|
_webui_timer_start(&timer_1);
|
|
for (;;) {
|
|
|
|
_webui_sleep(1);
|
|
|
|
// Stop if window is connected
|
|
if (_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE))
|
|
break;
|
|
|
|
// Stop if timer is finished (Default WEBUI_DEF_TIMEOUT)
|
|
if (_webui_timer_is_end(&timer_1, (_webui.startup_timeout * 1000)))
|
|
break;
|
|
}
|
|
|
|
if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE) && win->wait) {
|
|
|
|
// At this moment the browser is already started and HTML
|
|
// files are already handled, let's wait more time to give
|
|
// the WebSocket an extra seconds to connect. This is helpful
|
|
// when a web app have too many files to handel before `webui.js`
|
|
// get requested.
|
|
|
|
do {
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[Core]\t\t_webui_server_thread([%zu]) -> Waiting more for connection\n",
|
|
win->num
|
|
);
|
|
#endif
|
|
|
|
win->wait = false;
|
|
|
|
_webui_timer_t timer_2;
|
|
_webui_timer_start(&timer_2);
|
|
for (;;) {
|
|
|
|
// Stop if window is connected
|
|
_webui_sleep(1);
|
|
if (_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE))
|
|
break;
|
|
|
|
// Stop if timer is finished
|
|
if (_webui_timer_is_end(&timer_2, 5000))
|
|
break;
|
|
}
|
|
} while(win->wait && !_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE));
|
|
}
|
|
|
|
if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE))
|
|
stop = true; // First run failed
|
|
}
|
|
else {
|
|
|
|
// UI is connected
|
|
win->is_closed = false;
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[Core]\t\t_webui_server_thread([%zu]) -> Window Connected.\n",
|
|
win->num
|
|
);
|
|
#endif
|
|
|
|
// Folder monitor thread
|
|
if (_webui.config.folder_monitor && !monitor_created) {
|
|
monitor_created = true;
|
|
#ifdef _WIN32
|
|
monitor_thread = CreateThread(NULL, 0, _webui_folder_monitor_thread, (void*)win, 0, NULL);
|
|
if (monitor_thread != NULL)
|
|
CloseHandle(monitor_thread);
|
|
#else
|
|
pthread_create(&monitor_thread, NULL, &_webui_folder_monitor_thread, (void*)win);
|
|
pthread_detach(monitor_thread);
|
|
#endif
|
|
}
|
|
|
|
while(!stop) {
|
|
|
|
// Wait forever for disconnection
|
|
|
|
_webui_sleep(1);
|
|
|
|
// Exit signal
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE)) {
|
|
stop = true;
|
|
break;
|
|
}
|
|
|
|
if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE)) {
|
|
|
|
// The UI is just get disconnected
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[Core]\t\t_webui_server_thread([%zu]) -> Window disconnected\n",
|
|
win->num
|
|
);
|
|
#endif
|
|
|
|
if (!win->is_closed) {
|
|
|
|
// probably the user did a refresh
|
|
// let's wait for re-connection...
|
|
|
|
do {
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[Core]\t\t_webui_server_thread([%zu]) -> Waiting for reconnection\n",
|
|
win->num
|
|
);
|
|
#endif
|
|
|
|
win->wait = false;
|
|
|
|
_webui_timer_t timer_3;
|
|
_webui_timer_start(&timer_3);
|
|
for (;;) {
|
|
|
|
// Stop if window is re-connected
|
|
_webui_sleep(1);
|
|
if (_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE))
|
|
break;
|
|
|
|
// Stop if timer is finished
|
|
if (_webui_timer_is_end(&timer_3, WEBUI_RELOAD_TIMEOUT))
|
|
break;
|
|
}
|
|
} while(win->wait && !_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE));
|
|
|
|
if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE)) {
|
|
stop = true;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
|
|
// Window get closed
|
|
stop = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Let's check the flag again, there is a change that
|
|
// the flag has ben changed during the first loop for
|
|
// example when set_timeout() get called after show()
|
|
if (_webui.startup_timeout == 0) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_server_thread([%zu]) -> Listening Success\n",
|
|
win->num);
|
|
printf("[Core]\t\t_webui_server_thread([%zu]) -> HTTP/WS Port: %s\n",
|
|
win->num, server_port);
|
|
printf("[Core]\t\t_webui_server_thread([%zu]) -> Root path: %s\n",
|
|
win->num, win->server_root_path);
|
|
printf("[Core]\t\t_webui_server_thread([%zu]) -> Infinite loop\n",
|
|
win->num);
|
|
#endif
|
|
|
|
// Folder monitor thread
|
|
if (_webui.config.folder_monitor && !monitor_created) {
|
|
monitor_created = true;
|
|
#ifdef _WIN32
|
|
monitor_thread = CreateThread(NULL, 0, _webui_folder_monitor_thread, (void*)win, 0, NULL);
|
|
if (monitor_thread != NULL)
|
|
CloseHandle(monitor_thread);
|
|
#else
|
|
pthread_create(&monitor_thread, NULL, &_webui_folder_monitor_thread, (void*)win);
|
|
pthread_detach(monitor_thread);
|
|
#endif
|
|
}
|
|
|
|
// Wait forever
|
|
for (;;) {
|
|
_webui_sleep(1);
|
|
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
|
|
#ifdef WEBUI_LOG
|
|
if (!http_ctx) printf("[Core]\t\t_webui_server_thread([%zu]) -> Listening failed.\n", win->num);
|
|
#endif
|
|
|
|
// Mutex
|
|
_webui_mutex_unlock(&_webui.mutex_server_start);
|
|
}
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_server_thread([%zu]) -> Cleaning\n", win->num);
|
|
#endif
|
|
|
|
_webui_mutex_is_connected(win, WEBUI_MUTEX_FALSE);
|
|
|
|
// Clean WebView
|
|
if (win->webView) {
|
|
win->webView->stop = true;
|
|
_webui_mutex_is_webview_update(win, WEBUI_MUTEX_TRUE);
|
|
}
|
|
|
|
// Clean
|
|
win->server_running = false;
|
|
_webui_free_port(win->server_port);
|
|
win->server_port = 0;
|
|
_webui_free_mem((void*)server_port);
|
|
// _webui_client_cookies_free_all(win);
|
|
|
|
// Kill Process
|
|
// _webui_kill_pid(win->process_id);
|
|
// win->process_id = 0;
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_server_thread([%zu]) -> Server stopped.\n", win->num);
|
|
#endif
|
|
|
|
// Let the main wait() know that
|
|
// this server thread is finished
|
|
if (_webui.servers > 0)
|
|
_webui.servers--;
|
|
|
|
// Stop server services
|
|
// This should be at the
|
|
// end as it may take time
|
|
mg_stop(http_ctx);
|
|
|
|
// Fire the mutex condition for wait()
|
|
if (_webui.startup_timeout > 0 && _webui.servers < 1) {
|
|
|
|
// Stop all threads
|
|
_webui.ui = false;
|
|
_webui_mutex_is_exit_now(WEBUI_MUTEX_TRUE);
|
|
// Break main loop
|
|
_webui_condition_signal(&_webui.condition_wait);
|
|
#ifdef __APPLE__
|
|
_webui_macos_wv_stop();
|
|
#endif
|
|
}
|
|
|
|
// Clean monitor thread
|
|
if (_webui.config.folder_monitor && monitor_created) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_server_thread([%zu]) -> Killing folder monitor thread\n", win->num);
|
|
#endif
|
|
#ifdef _WIN32
|
|
TerminateThread(monitor_thread, 0);
|
|
CloseHandle(monitor_thread);
|
|
#else
|
|
if (monitor_thread) {
|
|
pthread_cancel(monitor_thread);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
WEBUI_THREAD_RETURN
|
|
}
|
|
|
|
static void _webui_receive(_webui_window_t* win, struct mg_connection* client,
|
|
int event_type, void * data, size_t len) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_receive([%zu], [%d], [%zu])\n", win->num, event_type, len);
|
|
#endif
|
|
|
|
static size_t recvNum = 0;
|
|
static bool multi_packet = false;
|
|
static size_t multi_expect = 0;
|
|
static size_t multi_receive = 0;
|
|
static void * multi_buf = NULL;
|
|
|
|
// Get connection id
|
|
size_t connection_id = 0;
|
|
if (event_type != WEBUI_WS_OPEN) {
|
|
if (!_webui_connection_get_id(win, client, &connection_id)) {
|
|
// Failed to find connection ID
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_receive() -> Failed to find connection ID\n");
|
|
#endif
|
|
_webui_connection_remove(win, client);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Websocket Open/Close Events
|
|
if (event_type == WEBUI_WS_OPEN) {
|
|
// New connection
|
|
// Autorisation to register
|
|
bool authorization = false;
|
|
if (!_webui.config.multi_client) {
|
|
if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE)) {
|
|
authorization = true;
|
|
}
|
|
} else authorization = true;
|
|
if (!authorization) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_receive(%zu) -> Connection not authorized to register\n",
|
|
recvNum);
|
|
#endif
|
|
_webui_connection_remove(win, client);
|
|
return;
|
|
}
|
|
else {
|
|
// Register
|
|
if (_webui_connection_save(win, client, &connection_id)) {
|
|
// Update window connection status
|
|
_webui_mutex_is_connected(win, WEBUI_MUTEX_TRUE);
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[Core]\t\t_webui_receive(%zu) -> Connection #%zu registered\n",
|
|
recvNum, connection_id
|
|
);
|
|
#endif
|
|
}
|
|
else {
|
|
// Connection failed to register
|
|
_webui_connection_remove(win, client);
|
|
return;
|
|
}
|
|
}
|
|
} else if (event_type == WEBUI_WS_CLOSE) {
|
|
// Connection close
|
|
_webui_connection_remove(win, client);
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[Core]\t\t_webui_receive(%zu) -> Connection #%zu Closed\n",
|
|
recvNum, connection_id
|
|
);
|
|
#endif
|
|
}
|
|
|
|
// Multi Packet (big data)
|
|
if (multi_packet) {
|
|
if ((multi_receive + len) > multi_expect) {
|
|
// Received more data than expected
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[Core]\t\t_webui_receive() -> Multi packet received more data than expected (%zu + %zu > %zu).\n",
|
|
multi_receive, len, multi_expect
|
|
);
|
|
#endif
|
|
multi_packet = false;
|
|
multi_expect = 0;
|
|
multi_receive = 0;
|
|
_webui_free_mem(multi_buf);
|
|
multi_buf = NULL;
|
|
return;
|
|
}
|
|
// Accumulate packet
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_receive() -> Multi packet accumulate %zu bytes\n", len);
|
|
#endif
|
|
memcpy(((unsigned char*)multi_buf + multi_receive), data, len);
|
|
multi_receive += len;
|
|
// Check if theire is more packets comming
|
|
if (multi_receive < multi_expect)
|
|
return;
|
|
}
|
|
else if (event_type == WEBUI_WS_DATA) {
|
|
if (((unsigned char*)data)[WEBUI_PROTOCOL_CMD] == WEBUI_CMD_MULTI) {
|
|
if (len >= WEBUI_PROTOCOL_SIZE && ((unsigned char*)data)[WEBUI_PROTOCOL_SIGN] == WEBUI_SIGNATURE) {
|
|
size_t expect_len = (size_t) strtoul(&((const char*)data)[WEBUI_PROTOCOL_DATA], NULL, 10);
|
|
if (expect_len > 0 && expect_len <= WEBUI_MAX_BUF) {
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[Core]\t\t_webui_receive() -> Multi packet started, Expecting %zu bytes\n",
|
|
expect_len
|
|
);
|
|
#endif
|
|
multi_buf = _webui_malloc(expect_len);
|
|
memcpy(multi_buf, data, len);
|
|
multi_receive = 0;
|
|
multi_expect = expect_len;
|
|
multi_packet = true;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Generate args
|
|
void * arg_ptr = NULL;
|
|
size_t arg_len = 0;
|
|
if (multi_packet) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_receive() -> Processing multi packet\n");
|
|
#endif
|
|
// Get data from accumulated multipackets
|
|
arg_len = multi_receive;
|
|
arg_ptr = multi_buf;
|
|
// Reset
|
|
multi_packet = false;
|
|
multi_expect = 0;
|
|
multi_receive = 0;
|
|
multi_buf = NULL;
|
|
} else {
|
|
arg_len = len;
|
|
if (len > 0) {
|
|
if (win->ws_block) {
|
|
// This event has data. And it will be processed
|
|
// in this current thread, no need to copy data
|
|
arg_ptr = data;
|
|
}
|
|
else {
|
|
// This event has data. And it will be processed
|
|
// in a new thread, we should copy data once
|
|
void * data_cpy = (void*)_webui_malloc(len);
|
|
memcpy((char*)data_cpy, data, len);
|
|
arg_ptr = data_cpy;
|
|
}
|
|
} else {
|
|
// This is a WS connect/disconnect event
|
|
arg_ptr = NULL;
|
|
}
|
|
}
|
|
|
|
// Process
|
|
if (win->ws_block) {
|
|
// Process the packet in this current thread
|
|
_webui_ws_process(win, client, connection_id, arg_ptr, arg_len, ++recvNum, event_type);
|
|
if (arg_ptr != data)
|
|
_webui_free_mem((void*)arg_ptr);
|
|
}
|
|
else {
|
|
// Process the packet in a new thread
|
|
_webui_recv_arg_t* arg = (_webui_recv_arg_t* ) _webui_malloc(sizeof(_webui_recv_arg_t));
|
|
arg->win = win;
|
|
arg->ptr = arg_ptr;
|
|
arg->len = arg_len;
|
|
arg->recvNum = ++recvNum;
|
|
arg->event_type = event_type;
|
|
arg->client = client;
|
|
arg->connection_id = connection_id;
|
|
#ifdef _WIN32
|
|
HANDLE thread = CreateThread(NULL, 0, _webui_ws_process_thread, (void*)arg, 0, NULL);
|
|
if (thread != NULL)
|
|
CloseHandle(thread);
|
|
#else
|
|
pthread_t thread;
|
|
pthread_create(&thread, NULL,&_webui_ws_process_thread, (void*)arg);
|
|
pthread_detach(thread);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static bool _webui_connection_save(_webui_window_t* win, struct mg_connection* client, size_t* connection_id) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_connection_save([%zu])\n", win->num);
|
|
#endif
|
|
|
|
// Save new ws client
|
|
_webui_mutex_lock(&_webui.mutex_client);
|
|
for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
|
|
if (_webui.clients[i] == NULL) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_connection_save() -> Registering client #%zu \n", i);
|
|
#endif
|
|
// Save
|
|
if (win->single_client == NULL) {
|
|
win->single_client = client;
|
|
_webui_mutex_is_single_client_token_valid(win, WEBUI_MUTEX_FALSE);
|
|
}
|
|
_webui.clients[i] = client;
|
|
_webui.clients_win_num[i] = win->num;
|
|
_webui_mutex_is_multi_client_token_valid(win, WEBUI_MUTEX_FALSE, i);
|
|
win->clients_count++;
|
|
_webui_mutex_unlock(&_webui.mutex_client);
|
|
*connection_id = i;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// List is full
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_connection_save() -> Clients list is full\n");
|
|
#endif
|
|
_webui_mutex_unlock(&_webui.mutex_client);
|
|
return false;
|
|
}
|
|
|
|
static void _webui_connection_remove(_webui_window_t* win, struct mg_connection* client) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_connection_remove([%zu])\n", win->num);
|
|
#endif
|
|
|
|
_webui_mutex_lock(&_webui.mutex_client);
|
|
|
|
// Remove a ws client
|
|
for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
|
|
if (_webui.clients[i] == client) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_connection_remove() -> Removing client #%zu \n", i);
|
|
#endif
|
|
// Reset Token
|
|
if (!_webui.config.multi_client) {
|
|
if (_webui_mutex_is_multi_client_token_valid(win, WEBUI_MUTEX_NONE, i)) {
|
|
win->token = 0;
|
|
}
|
|
}
|
|
// Clear
|
|
if (win->single_client == client) {
|
|
win->single_client = NULL;
|
|
_webui_mutex_is_single_client_token_valid(win, WEBUI_MUTEX_FALSE);
|
|
}
|
|
_webui.clients[i] = NULL;
|
|
_webui.clients_win_num[i] = 0;
|
|
_webui_mutex_is_multi_client_token_valid(win, WEBUI_MUTEX_FALSE, i);
|
|
if (win->clients_count > 0)
|
|
win->clients_count--;
|
|
// Close
|
|
_webui_mutex_unlock(&_webui.mutex_client);
|
|
mg_close_connection(client);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Client not found
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_connection_remove() -> Client not found\n");
|
|
#endif
|
|
_webui_mutex_unlock(&_webui.mutex_client);
|
|
mg_close_connection(client);
|
|
}
|
|
|
|
static bool _webui_connection_get_id(_webui_window_t* win, struct mg_connection* client, size_t* connection_id) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_connection_get_id([%zu], [%p])\n", win->num, client);
|
|
#endif
|
|
|
|
// Find a ws client
|
|
_webui_mutex_lock(&_webui.mutex_client);
|
|
for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
|
|
if (_webui.clients[i] == client) {
|
|
*connection_id = i;
|
|
_webui_mutex_unlock(&_webui.mutex_client);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Client not found
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_connection_get_id() -> Client not found\n");
|
|
#endif
|
|
_webui_mutex_unlock(&_webui.mutex_client);
|
|
return false;
|
|
}
|
|
|
|
static void _webui_ws_process(
|
|
_webui_window_t* win, struct mg_connection* client, size_t connection_id,
|
|
void* ptr, size_t len, size_t recvNum, int event_type) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_ws_process(%zu)\n", recvNum);
|
|
#endif
|
|
|
|
if (!_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE)) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_ws_process(%zu) -> Start\n", recvNum);
|
|
#endif
|
|
|
|
if (event_type == WEBUI_WS_DATA) {
|
|
|
|
const char* packet = (const char*)ptr;
|
|
uint32_t packet_token = _webui_get_token(packet);
|
|
uint16_t packet_id = _webui_get_id(packet);
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> Data received\n",
|
|
recvNum
|
|
);
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> Packet Size : %zu bytes\n",
|
|
recvNum, len
|
|
);
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> Packet Header : [ ",
|
|
recvNum
|
|
);
|
|
_webui_print_hex(packet, WEBUI_PROTOCOL_SIZE);
|
|
printf("]\n");
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> Packet Token: 0x%08X (%" PRIu32 ")\n",
|
|
recvNum, packet_token, packet_token
|
|
);
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> Packet ID: 0x%04X (%u)\n",
|
|
recvNum, packet_id, packet_id
|
|
);
|
|
printf("[Core]\t\t_webui_ws_process(%zu) -> Packet Data: [", recvNum);
|
|
// _webui_print_ascii(&packet[WEBUI_PROTOCOL_DATA], (len -
|
|
// WEBUI_PROTOCOL_SIZE));
|
|
printf("]\n");
|
|
#endif
|
|
|
|
if ((len >= WEBUI_PROTOCOL_SIZE) &&
|
|
((unsigned char)packet[WEBUI_PROTOCOL_SIGN] == WEBUI_SIGNATURE) &&
|
|
(packet_token == win->token)) {
|
|
|
|
// Mutex
|
|
if (_webui.config.ws_block) {
|
|
// wait for previous event to finish
|
|
if ((unsigned char)packet[WEBUI_PROTOCOL_CMD] != WEBUI_CMD_JS) {
|
|
_webui_mutex_lock(&_webui.mutex_receive);
|
|
}
|
|
}
|
|
|
|
if (!_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE)) { // Check if previous event called exit()
|
|
|
|
if ((unsigned char)packet[WEBUI_PROTOCOL_CMD] == WEBUI_CMD_CLICK) {
|
|
|
|
// Click Event
|
|
|
|
// Protocol
|
|
// [Header]
|
|
// [Element]
|
|
|
|
// Get html element id
|
|
char* element = (char*)&packet[WEBUI_PROTOCOL_DATA];
|
|
size_t element_len = _webui_strlen(element);
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> WEBUI_CMD_CLICK \n",
|
|
recvNum
|
|
);
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> Element size: %zu bytes \n",
|
|
recvNum, element_len
|
|
);
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> Element : [%s] \n",
|
|
recvNum, element
|
|
);
|
|
#endif
|
|
|
|
// Event Info
|
|
webui_event_inf_t* event_inf = NULL;
|
|
size_t event_num = _webui_new_event_inf(win, &event_inf);
|
|
event_inf->client = client;
|
|
event_inf->connection_id = connection_id;
|
|
|
|
_webui_window_event(
|
|
win, // Event -> Window
|
|
connection_id, // Event -> Client Unique ID
|
|
WEBUI_EVENT_MOUSE_CLICK, // Event -> Type of this event
|
|
element, // Event -> HTML Element
|
|
event_num, // Event -> Event Number
|
|
_webui_client_get_id(win, client), // Event -> Client ID
|
|
_webui_get_cookies_full(client) // Event -> Full cookies
|
|
);
|
|
|
|
// Free event
|
|
_webui_free_event_inf(win, event_num);
|
|
} else if ((unsigned char)packet[WEBUI_PROTOCOL_CMD] == WEBUI_CMD_JS) {
|
|
|
|
// JS Result
|
|
|
|
// Protocol
|
|
// [Header]
|
|
// [Error, ScriptResponse]
|
|
|
|
// Get pipe id
|
|
if (packet_id < WEBUI_MAX_IDS) {
|
|
|
|
if (_webui.run_userBuffer[packet_id] != NULL) {
|
|
|
|
_webui_mutex_lock(&_webui.mutex_js_run);
|
|
_webui.run_done[packet_id] = false;
|
|
_webui_mutex_unlock(&_webui.mutex_js_run);
|
|
|
|
// Get js-error
|
|
bool error = true;
|
|
if ((unsigned char)packet[WEBUI_PROTOCOL_DATA] == 0x00)
|
|
error = false;
|
|
|
|
// Get data part
|
|
char* data = (char*)&packet[WEBUI_PROTOCOL_DATA + 1];
|
|
size_t data_len = _webui_strlen(data);
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> WEBUI_CMD_JS \n",
|
|
recvNum
|
|
);
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> run_id = 0x%02x (%u) \n",
|
|
recvNum, packet_id, packet_id
|
|
);
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> error = %s \n",
|
|
recvNum, error ? "true" : "false"
|
|
);
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> %zu bytes of data\n",
|
|
recvNum, data_len
|
|
);
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> data = [%s] @ 0x%p\n",
|
|
recvNum, data, data
|
|
);
|
|
#endif
|
|
|
|
// Set pipe
|
|
_webui.run_error[packet_id] = error;
|
|
if (data_len > 0) {
|
|
|
|
// Copy response to the user's response buffer
|
|
// directly
|
|
size_t response_len = data_len + 1;
|
|
size_t bytes_to_cpy =
|
|
(response_len <=
|
|
_webui
|
|
.run_userBufferLen[packet_id] ?
|
|
response_len :
|
|
_webui
|
|
.run_userBufferLen[packet_id]);
|
|
memcpy(
|
|
_webui.run_userBuffer[packet_id], data,
|
|
bytes_to_cpy
|
|
);
|
|
} else {
|
|
|
|
// Empty Result
|
|
_webui.run_userBuffer[packet_id] = 0x00;
|
|
}
|
|
|
|
// Send ready signal to webui_script()
|
|
_webui_mutex_lock(&_webui.mutex_js_run);
|
|
_webui.run_done[packet_id] = true;
|
|
_webui_mutex_unlock(&_webui.mutex_js_run);
|
|
}
|
|
}
|
|
} else if ((unsigned char)packet[WEBUI_PROTOCOL_CMD] == WEBUI_CMD_NAVIGATION) {
|
|
|
|
// Navigation Event
|
|
|
|
// Protocol
|
|
// [Header]
|
|
// [URL]
|
|
|
|
// Events
|
|
if (win->has_all_events) {
|
|
|
|
// Get URL
|
|
char* url = (char*)&packet[WEBUI_PROTOCOL_DATA];
|
|
size_t url_len = _webui_strlen(url);
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> WEBUI_CMD_NAVIGATION \n",
|
|
recvNum
|
|
);
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> URL size: %zu bytes \n",
|
|
recvNum, url_len
|
|
);
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> URL: [%s] \n",
|
|
recvNum, url
|
|
);
|
|
#endif
|
|
|
|
// Event Info
|
|
webui_event_inf_t* event_inf = NULL;
|
|
size_t event_num = _webui_new_event_inf(win, &event_inf);
|
|
event_inf->client = client;
|
|
event_inf->connection_id = connection_id;
|
|
|
|
// Event Info Extras
|
|
event_inf->event_data[0] = url;
|
|
event_inf->event_size[0] = url_len;
|
|
|
|
_webui_window_event(
|
|
win, // Event -> Window
|
|
connection_id, // Event -> Client Unique ID
|
|
WEBUI_EVENT_NAVIGATION, // Event -> Type of this event
|
|
"", // Event -> HTML Element
|
|
event_num, // Event -> Event Number
|
|
_webui_client_get_id(win, client), // Event -> Client ID
|
|
_webui_get_cookies_full(client) // Event -> Full cookies
|
|
);
|
|
|
|
// Free event
|
|
_webui_free_event_inf(win, event_num);
|
|
}
|
|
} else if ((unsigned char)packet[WEBUI_PROTOCOL_CMD] == WEBUI_CMD_CALL_FUNC) {
|
|
|
|
// Function Call
|
|
|
|
// Protocol
|
|
// [Header]
|
|
// [Fn, Null, {Len;Len;...}, Null, {Data,Null,Data,Null...}]
|
|
|
|
// Get html element id
|
|
char* element = (char*)&packet[WEBUI_PROTOCOL_DATA];
|
|
size_t element_len = _webui_strlen(element);
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> WEBUI_CMD_CALL_FUNC \n",
|
|
recvNum
|
|
);
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> Call ID: [%u] \n",
|
|
recvNum, packet_id
|
|
);
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> Element: [%s] \n",
|
|
recvNum, element
|
|
);
|
|
#endif
|
|
|
|
// New event inf (Function Call)
|
|
webui_event_inf_t* event_inf = NULL;
|
|
size_t event_num = _webui_new_event_inf(win, &event_inf);
|
|
|
|
// Loop trough args
|
|
size_t data_size_expected = 0;
|
|
char* args_lens = (char*)&packet[WEBUI_PROTOCOL_DATA + element_len + 1];
|
|
size_t args_len = _webui_strlen(args_lens);
|
|
char* args_ptr = (char*)&packet[WEBUI_PROTOCOL_DATA + element_len + 1 + args_len + 1];
|
|
char* context;
|
|
char* tk = WEBUI_STR_TOK(args_lens, ";", &context);
|
|
size_t tk_num = 0;
|
|
while(tk != NULL && tk_num < WEBUI_MAX_ARG + 1) {
|
|
|
|
size_t arg_len = (size_t) strtoul(tk, NULL, 10);
|
|
data_size_expected = data_size_expected + arg_len + 1;
|
|
|
|
if (arg_len > 0) {
|
|
|
|
// Set argument
|
|
event_inf->event_size[tk_num] = arg_len;
|
|
event_inf->event_data[tk_num] = args_ptr;
|
|
}
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> Argument %zu: %zu bytes\n",
|
|
recvNum, tk_num, arg_len
|
|
);
|
|
#endif
|
|
|
|
args_ptr = args_ptr + (arg_len + 1);
|
|
tk = WEBUI_STR_TOK(NULL, ";", &context);
|
|
|
|
// Save total found arguments
|
|
event_inf->count = ++tk_num;
|
|
}
|
|
|
|
// Check data validity
|
|
size_t data_size_recv = len - (WEBUI_PROTOCOL_SIZE + // [Header]
|
|
element_len + // [Fn]
|
|
1 + // [Null]
|
|
args_len + // [{Len;Len;...}]
|
|
1); // [Null]
|
|
|
|
if (data_size_expected == data_size_recv) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> Expected and received %zu bytes of data.\n",
|
|
recvNum, data_size_expected
|
|
);
|
|
#endif
|
|
|
|
// Create new event (Function Call)
|
|
webui_event_t e;
|
|
e.window = win->num;
|
|
e.event_type = WEBUI_EVENT_CALLBACK;
|
|
e.element = element;
|
|
e.event_number = event_num;
|
|
e.connection_id = connection_id;
|
|
e.client_id = _webui_client_get_id(win, client);
|
|
e.cookies = (char*)_webui_get_cookies_full(client); // Full cookies
|
|
|
|
// Call user function
|
|
size_t cb_index = 0;
|
|
bool exist = _webui_get_cb_index(win, element, &cb_index);
|
|
if (exist && win->cb[cb_index] != NULL) {
|
|
|
|
// Call user cb
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> Calling user callback\n[Call]\n",
|
|
recvNum
|
|
);
|
|
#endif
|
|
e.bind_id = cb_index;
|
|
win->cb[cb_index](&e);
|
|
|
|
// Async response wait
|
|
if (_webui.config.asynchronous_response) {
|
|
bool done = false;
|
|
while (!done) {
|
|
_webui_sleep(10);
|
|
_webui_mutex_lock(&_webui.mutex_async_response);
|
|
if (event_inf->done) done = true;
|
|
_webui_mutex_unlock(&_webui.mutex_async_response);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check the response
|
|
if (_webui_is_empty(event_inf->response))
|
|
event_inf->response = NULL;
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> user-callback response [%s]\n",
|
|
recvNum, event_inf->response
|
|
);
|
|
#endif
|
|
|
|
// Packet Protocol Format:
|
|
// [...]
|
|
// [CMD]
|
|
// [CallResponse]
|
|
|
|
// Send the packet
|
|
_webui_send_client(
|
|
win, client, packet_id, WEBUI_CMD_CALL_FUNC,
|
|
event_inf->response, _webui_strlen(event_inf->response), false
|
|
);
|
|
|
|
// Free event
|
|
_webui_free_event_inf(win, event_num);
|
|
} else {
|
|
|
|
// WebSocket/Civetweb did not send all the data as expected.
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> No enough data received. "
|
|
"Expected %zu bytes, received %zu bytes.\n",
|
|
recvNum, data_size_expected, data_size_recv
|
|
);
|
|
#endif
|
|
|
|
// Send a void response to solve `.call()` promise
|
|
|
|
// Packet Protocol Format:
|
|
// [...]
|
|
// [CMD]
|
|
// [CallResponse]
|
|
|
|
// Send the packet
|
|
_webui_send_client(
|
|
win, client, packet_id, WEBUI_CMD_CALL_FUNC, NULL, 0, false
|
|
);
|
|
}
|
|
|
|
// Free event
|
|
_webui_free_mem((void*)event_inf);
|
|
} else if ((unsigned char)packet[WEBUI_PROTOCOL_CMD] == WEBUI_CMD_CHECK_TK) {
|
|
|
|
// Check Token Event
|
|
|
|
// Protocol
|
|
// [Header]
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> WEBUI_CMD_CHECK_TK \n",
|
|
recvNum
|
|
);
|
|
#endif
|
|
|
|
size_t connection_id = 0;
|
|
if (_webui_connection_get_id(win, client, &connection_id)) {
|
|
|
|
if (win->single_client == client) {
|
|
_webui_mutex_is_single_client_token_valid(win, WEBUI_MUTEX_TRUE);
|
|
}
|
|
_webui_mutex_is_multi_client_token_valid(win, WEBUI_MUTEX_TRUE, connection_id);
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> Token accepted. Sending bind list\n",
|
|
recvNum
|
|
);
|
|
#endif
|
|
|
|
// Calculate the bind list size
|
|
// [0x01][element1,element2,element3,]
|
|
size_t csv_size = 1;
|
|
for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
|
|
if (!_webui_is_empty(win->html_elements[i])) {
|
|
csv_size += _webui_strlen(win->html_elements[i]) + 1;
|
|
}
|
|
}
|
|
if (win->has_all_events) {
|
|
csv_size++;
|
|
}
|
|
// Allocate
|
|
char* csv = (char*)_webui_malloc(csv_size);
|
|
csv[0] = 0x01;
|
|
csv_size--;
|
|
|
|
// Generate the bind list array (CSV)
|
|
for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
|
|
if (!_webui_is_empty(win->html_elements[i])) {
|
|
// [element1,element2,element3,]
|
|
WEBUI_STR_CAT_DYN(&csv[1], csv_size, win->html_elements[i]);
|
|
WEBUI_STR_CAT_DYN(&csv[1], csv_size, ",");
|
|
}
|
|
}
|
|
|
|
// Add all events bind element (empty)
|
|
if (win->has_all_events) {
|
|
// [element1,...,,]
|
|
WEBUI_STR_CAT_DYN(&csv[1], csv_size, ",");
|
|
}
|
|
|
|
// Packet Protocol Format:
|
|
// [...]
|
|
// [CMD]
|
|
// [Token Status]
|
|
// [0x01 element1,element2,element3,]
|
|
|
|
// Send the packet
|
|
_webui_send_client(
|
|
win, client, packet_id, WEBUI_CMD_CHECK_TK,
|
|
(const char*)csv, _webui_strlen(csv), true
|
|
);
|
|
|
|
// Free
|
|
_webui_free_mem((void*)csv);
|
|
|
|
// New Event
|
|
if (win->has_all_events) {
|
|
|
|
// Event Info
|
|
webui_event_inf_t* event_inf = NULL;
|
|
size_t event_num = _webui_new_event_inf(win, &event_inf);
|
|
event_inf->client = client;
|
|
event_inf->connection_id = connection_id;
|
|
|
|
_webui_window_event(
|
|
win, // Event -> Window
|
|
connection_id, // Event -> Client Unique ID
|
|
WEBUI_EVENT_CONNECTED, // Event -> Type of this event
|
|
"", // Event -> HTML Element
|
|
event_num, // Event -> Event Number
|
|
_webui_client_get_id(win, client), // Event -> Client ID
|
|
_webui_get_cookies_full(client) // Event -> Full cookies
|
|
);
|
|
|
|
// Free event
|
|
_webui_free_event_inf(win, event_num);
|
|
}
|
|
}
|
|
else {
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> Token not accepted.\n",
|
|
recvNum
|
|
);
|
|
#endif
|
|
|
|
unsigned char status = 0x00;
|
|
|
|
// Packet Protocol Format:
|
|
// [...]
|
|
// [CMD]
|
|
// [Token Status]
|
|
// [0x00]
|
|
|
|
// Send the packet
|
|
_webui_send_client(
|
|
win, client, packet_id, WEBUI_CMD_CHECK_TK,
|
|
(const char*)&status, 1, true
|
|
);
|
|
}
|
|
}
|
|
#ifdef WEBUI_LOG
|
|
else {
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> Unknown command "
|
|
"[0x%02x]\n",
|
|
recvNum, (unsigned char)packet[WEBUI_PROTOCOL_CMD]
|
|
);
|
|
}
|
|
#endif
|
|
}
|
|
#ifdef WEBUI_LOG
|
|
else {
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> Window is not connected.\n",
|
|
recvNum
|
|
);
|
|
}
|
|
#endif
|
|
|
|
// Unlock Mutex
|
|
if (_webui.config.ws_block) {
|
|
if ((unsigned char)packet[WEBUI_PROTOCOL_CMD] != WEBUI_CMD_JS) {
|
|
_webui_mutex_unlock(&_webui.mutex_receive);
|
|
}
|
|
}
|
|
} else {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> Invalid Packet.\n",
|
|
recvNum
|
|
);
|
|
#endif
|
|
|
|
// Send a void response to solve `.call()` promise
|
|
|
|
// Packet Protocol Format:
|
|
// [...]
|
|
// [CMD]
|
|
|
|
// Send the packet
|
|
_webui_send_client(
|
|
win, client, packet_id, (unsigned char)packet[WEBUI_PROTOCOL_CMD], NULL, 0, true
|
|
);
|
|
|
|
// Forced close
|
|
_webui_connection_remove(win, client);
|
|
}
|
|
} else if (event_type == WEBUI_WS_OPEN) {
|
|
|
|
// New connection
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> WEBUI_WS_OPEN \n",
|
|
recvNum
|
|
);
|
|
#endif
|
|
|
|
/*
|
|
// New Event
|
|
if (win->has_all_events) {
|
|
|
|
// Event Info
|
|
webui_event_inf_t* event_inf = NULL;
|
|
size_t event_num = _webui_new_event_inf(win, &event_inf);
|
|
event_inf->client = client;
|
|
event_inf->connection_id = connection_id;
|
|
|
|
_webui_window_event(
|
|
win, // Event -> Window
|
|
connection_id, // Event -> Client Unique ID
|
|
WEBUI_EVENT_CONNECTED, // Event -> Type of this event
|
|
"", // Event -> HTML Element
|
|
event_num, // Event -> Event Number
|
|
_webui_client_get_id(win, client), // Event -> Client ID
|
|
_webui_get_cookies_full(client) // Event -> Full cookies
|
|
);
|
|
|
|
// Free event
|
|
_webui_free_event_inf(win, event_num);
|
|
}
|
|
*/
|
|
} else if (event_type == WEBUI_WS_CLOSE) {
|
|
|
|
// Connection close
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> WEBUI_WS_CLOSE \n",
|
|
recvNum
|
|
);
|
|
#endif
|
|
|
|
// Events
|
|
if (win->has_all_events) {
|
|
|
|
// Event Info
|
|
webui_event_inf_t* event_inf = NULL;
|
|
size_t event_num = _webui_new_event_inf(win, &event_inf);
|
|
event_inf->client = client;
|
|
event_inf->connection_id = connection_id;
|
|
|
|
_webui_window_event(
|
|
win, // Event -> Window
|
|
connection_id, // Event -> Client Unique ID
|
|
WEBUI_EVENT_DISCONNECTED, // Event -> Type of this event
|
|
"", // Event -> HTML Element
|
|
event_num, // Event -> Event Number
|
|
_webui_client_get_id(win, client), // Event -> Client ID
|
|
_webui_get_cookies_full(client) // Event -> Full cookies
|
|
);
|
|
|
|
// Free event
|
|
_webui_free_event_inf(win, event_num);
|
|
}
|
|
}
|
|
#ifdef WEBUI_LOG
|
|
else {
|
|
printf(
|
|
"[Core]\t\t_webui_ws_process(%zu) -> UNKNOWN EVENT "
|
|
"TYPE (%d)\n",
|
|
recvNum, event_type
|
|
);
|
|
}
|
|
#endif
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_ws_process(%zu) -> Finished.\n", recvNum);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static WEBUI_THREAD_RECEIVE {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t[Thread .] _webui_ws_process_thread()\n");
|
|
#endif
|
|
|
|
// Get arguments
|
|
_webui_recv_arg_t* arg = (_webui_recv_arg_t* ) _arg;
|
|
|
|
// Process
|
|
_webui_ws_process(arg->win, arg->client, arg->connection_id, arg->ptr, arg->len, arg->recvNum, arg->event_type);
|
|
|
|
// Free
|
|
_webui_free_mem((void*)arg->ptr);
|
|
_webui_free_mem((void*)arg);
|
|
|
|
WEBUI_THREAD_RETURN
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
static void _webui_kill_pid(size_t pid) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_kill_pid(%zu)\n", pid);
|
|
#endif
|
|
|
|
if (pid < 1)
|
|
return;
|
|
HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, (DWORD) pid);
|
|
if (hProcess != NULL) {
|
|
TerminateProcess(hProcess, 1);
|
|
CloseHandle(hProcess);
|
|
}
|
|
}
|
|
#else
|
|
static void _webui_kill_pid(size_t pid) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_kill_pid(%zu)\n", pid);
|
|
#endif
|
|
|
|
if (pid < 1)
|
|
return;
|
|
kill((pid_t) pid, SIGTERM);
|
|
}
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
|
|
static bool _webui_str_to_wide(const char *s, wchar_t **w) {
|
|
int wlen = MultiByteToWideChar(CP_UTF8, 0, s, -1, NULL, 0);
|
|
if (wlen < 1)
|
|
return false;
|
|
wchar_t *wide = (wchar_t *)_webui_malloc(wlen * sizeof(wchar_t));
|
|
if (!wide)
|
|
return false;
|
|
MultiByteToWideChar(CP_UTF8, 0, s, -1, wide, wlen);
|
|
*w = wide;
|
|
return true;
|
|
}
|
|
|
|
static bool _webui_socket_test_listen_win32(size_t port_num) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_socket_test_listen_win32([%zu])\n", port_num);
|
|
#endif
|
|
|
|
WSADATA wsaData;
|
|
size_t iResult;
|
|
SOCKET ListenSocket = INVALID_SOCKET;
|
|
struct addrinfo * result = NULL;
|
|
struct addrinfo hints;
|
|
|
|
// Initialize Winsock
|
|
iResult = WSAStartup(MAKEWORD(2, 2),&wsaData);
|
|
if (iResult != 0) {
|
|
// WSAStartup failed
|
|
return false;
|
|
}
|
|
ZeroMemory(&hints, sizeof(hints));
|
|
hints.ai_family = AF_INET;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
hints.ai_protocol = IPPROTO_TCP;
|
|
hints.ai_flags = AI_PASSIVE;
|
|
|
|
// Resolve the server address and port
|
|
char the_port[16] = {0};
|
|
WEBUI_SN_PRINTF_STATIC(&the_port[0], sizeof(the_port), "%zu", port_num);
|
|
iResult = getaddrinfo("127.0.0.1",&the_port[0],&hints,&result);
|
|
if (iResult != 0) {
|
|
// WSACleanup();
|
|
return false;
|
|
}
|
|
|
|
// Create a SOCKET for the server to listen for client connections.
|
|
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
|
|
if (ListenSocket == INVALID_SOCKET) {
|
|
freeaddrinfo(result);
|
|
// WSACleanup();
|
|
return false;
|
|
}
|
|
|
|
// Setup the TCP listening socket
|
|
iResult = bind(ListenSocket, result->ai_addr, (int) result->ai_addrlen);
|
|
if (iResult == (size_t)SOCKET_ERROR) {
|
|
freeaddrinfo(result);
|
|
closesocket(ListenSocket);
|
|
shutdown(ListenSocket, SD_BOTH);
|
|
// WSACleanup();
|
|
return false;
|
|
}
|
|
|
|
// Clean
|
|
freeaddrinfo(result);
|
|
closesocket(ListenSocket);
|
|
shutdown(ListenSocket, SD_BOTH);
|
|
// WSACleanup();
|
|
|
|
// Listening Success
|
|
return true;
|
|
}
|
|
|
|
static int _webui_system_win32_out(const char* cmd, char ** output, bool show) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_system_win32_out()\n");
|
|
#endif
|
|
|
|
// Ini
|
|
*
|
|
output = NULL;
|
|
if (cmd == NULL)
|
|
return -1;
|
|
|
|
// Return
|
|
DWORD Return = 0;
|
|
|
|
// Flags
|
|
DWORD CreationFlags = CREATE_NO_WINDOW;
|
|
if (show)
|
|
CreationFlags = SW_SHOW;
|
|
|
|
SECURITY_ATTRIBUTES sa;
|
|
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
sa.bInheritHandle = TRUE;
|
|
sa.lpSecurityDescriptor = NULL;
|
|
HANDLE stdout_read, stdout_write;
|
|
if (!CreatePipe(&stdout_read,&stdout_write,&sa, 0)) {
|
|
return -1;
|
|
}
|
|
if (!SetHandleInformation(stdout_read, HANDLE_FLAG_INHERIT, 0)) {
|
|
CloseHandle(stdout_read);
|
|
CloseHandle(stdout_write);
|
|
return -1;
|
|
}
|
|
|
|
STARTUPINFOA si;
|
|
ZeroMemory(&si, sizeof(STARTUPINFOA));
|
|
si.cb = sizeof(STARTUPINFOA);
|
|
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
|
|
si.wShowWindow = SW_HIDE;
|
|
si.hStdOutput = stdout_write;
|
|
si.hStdError = stdout_write;
|
|
|
|
PROCESS_INFORMATION pi;
|
|
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
|
|
|
|
if (!CreateProcessA(
|
|
NULL, // No module name (use cmd line)
|
|
(LPSTR) cmd, // Command line
|
|
NULL, // Process handle not inheritable
|
|
NULL, // Thread handle not inheritable
|
|
TRUE, // Set handle inheritance to FALSE
|
|
CreationFlags, // Creation flags
|
|
NULL, // Use parent's environment block
|
|
NULL, // Use parent's starting directory
|
|
&
|
|
si, // Pointer to STARTUP INFO structure
|
|
&
|
|
pi
|
|
)) // Pointer to PROCESS_INFORMATION structure
|
|
{
|
|
CloseHandle(stdout_read);
|
|
CloseHandle(stdout_write);
|
|
return -1;
|
|
}
|
|
CloseHandle(stdout_write);
|
|
|
|
SetFocus(pi.hProcess);
|
|
WaitForSingleObject(pi.hProcess, INFINITE);
|
|
GetExitCodeProcess(pi.hProcess,&Return);
|
|
|
|
DWORD bytes_read;
|
|
char buffer[WEBUI_STDOUT_BUF];
|
|
size_t output_size = 0;
|
|
|
|
while(ReadFile(stdout_read, buffer, WEBUI_STDOUT_BUF,&bytes_read, NULL) && bytes_read > 0) {
|
|
|
|
char* new_output = realloc(*output, output_size + bytes_read + 1);
|
|
if (new_output == NULL) {
|
|
free(*output);
|
|
CloseHandle(stdout_read);
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(pi.hThread);
|
|
return -1;
|
|
}
|
|
|
|
* output = new_output;
|
|
memcpy(*output + output_size, buffer, bytes_read);
|
|
output_size += bytes_read;
|
|
}
|
|
|
|
if (*output != NULL)
|
|
(*output)[output_size] = '\0';
|
|
|
|
CloseHandle(stdout_read);
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(pi.hThread);
|
|
|
|
if (Return == 0)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
static BOOL CALLBACK _webui_enum_windows_proc_win32(HWND hwnd, LPARAM
|
|
targetProcessId) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_enum_windows_proc_win32()\n");
|
|
#endif
|
|
|
|
DWORD windowProcessId;
|
|
GetWindowThreadProcessId(hwnd, &windowProcessId);
|
|
|
|
if (windowProcessId == targetProcessId) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_enum_windows_proc_win32() -> Bring the"
|
|
"process (%lu) to the front\n", windowProcessId);
|
|
#endif
|
|
|
|
SetFocus(hwnd);
|
|
SetForegroundWindow(hwnd);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
*/
|
|
|
|
static int _webui_system_win32(_webui_window_t* win, char* cmd, bool show) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_system_win32()\n");
|
|
#endif
|
|
|
|
// Convert UTF-8 to wide string
|
|
wchar_t* wcmd;
|
|
if (!_webui_str_to_wide(cmd, &wcmd))
|
|
return -1;
|
|
|
|
/*
|
|
We should not kill this process, because may had many child
|
|
process of other WebUI app instances. Unfortunately, this is
|
|
how modern browsers save memory by combine all windows into one
|
|
single parent process, and we can't control this behavior.
|
|
|
|
// Automatically close the browser process when the
|
|
// parent process (this app) get closed. If this fail
|
|
// webui.js will try to close the window.
|
|
HANDLE JobObject = CreateJobObject(NULL, NULL);
|
|
JOB_OBJECT_EXTENDED_LIMIT_INFORMATION ExtendedInfo = { 0 };
|
|
ExtendedInfo.BasicLimitInformation.LimitFlags =
|
|
JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION |
|
|
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;SetInformationJobObject(JobObject,
|
|
JobObjectExtendedLimitInformation, &ExtendedInfo, sizeof(ExtendedInfo));
|
|
*/
|
|
|
|
DWORD Return = 0;
|
|
DWORD CreationFlags = CREATE_NO_WINDOW;
|
|
|
|
if (show)
|
|
CreationFlags = SW_SHOW;
|
|
|
|
STARTUPINFOW si;
|
|
PROCESS_INFORMATION pi;
|
|
ZeroMemory(&si, sizeof(si));
|
|
si.cb = sizeof(si);
|
|
ZeroMemory(&pi, sizeof(pi));
|
|
if (!CreateProcessW(
|
|
NULL, // No module name (use command line)
|
|
wcmd, // Command line
|
|
NULL, // Process handle not inheritable
|
|
NULL, // Thread handle not inheritable
|
|
FALSE, // Set handle inheritance to FALSE
|
|
CreationFlags, // Creation flags
|
|
NULL, // Use parent's environment block
|
|
NULL, // Use parent's starting directory
|
|
&
|
|
si, // Pointer to STARTUP INFO structure
|
|
&
|
|
pi
|
|
)) // Pointer to PROCESS_INFORMATION structure
|
|
{
|
|
// CreateProcess failed
|
|
_webui_free_mem((void*)wcmd);
|
|
return -1;
|
|
}
|
|
|
|
if (win) {
|
|
win->process_id = (size_t)pi.dwProcessId;
|
|
}
|
|
|
|
SetFocus(pi.hProcess);
|
|
// EnumWindows(_webui_enum_windows_proc_win32, (LPARAM)(pi.dwProcessId));
|
|
// AssignProcessToJobObject(JobObject, pi.hProcess);
|
|
WaitForSingleObject(pi.hProcess, INFINITE);
|
|
GetExitCodeProcess(pi.hProcess,&Return);
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(pi.hThread);
|
|
_webui_free_mem((void*)wcmd);
|
|
|
|
if (Return == 0)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
static bool _webui_get_windows_reg_value(HKEY key, LPCWSTR reg, LPCWSTR value_name, char value[WEBUI_MAX_PATH]) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_get_windows_reg_value([%ls])\n", reg);
|
|
#endif
|
|
|
|
HKEY hKey;
|
|
|
|
if (RegOpenKeyExW(key, reg, 0, KEY_READ,&hKey) == ERROR_SUCCESS) {
|
|
|
|
DWORD VALUE_TYPE;
|
|
BYTE VALUE_DATA[WEBUI_MAX_PATH];
|
|
DWORD VALUE_SIZE = sizeof(VALUE_DATA);
|
|
|
|
// If `value_name` is empty then it will read the "(default)" reg-key
|
|
if (RegQueryValueExW(hKey, value_name, NULL,&VALUE_TYPE, VALUE_DATA,&VALUE_SIZE) == ERROR_SUCCESS) {
|
|
|
|
if (VALUE_TYPE == REG_SZ)
|
|
WEBUI_SN_PRINTF_STATIC(value, WEBUI_MAX_PATH, "%S", (LPCWSTR) VALUE_DATA);
|
|
else if (VALUE_TYPE == REG_DWORD)
|
|
WEBUI_SN_PRINTF_STATIC(value, WEBUI_MAX_PATH, "%lu", *((DWORD * ) VALUE_DATA));
|
|
|
|
RegCloseKey(hKey);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) {
|
|
(void)hinstDLL;
|
|
(void)fdwReason;
|
|
(void)lpReserved;
|
|
return true;
|
|
}
|
|
|
|
#endif
|
|
|
|
// -- WebView -------------------------
|
|
|
|
#ifdef _WIN32
|
|
// Microsoft Windows
|
|
|
|
typedef HRESULT (__stdcall *CreateCoreWebView2EnvironmentWithOptionsFunc)(
|
|
PCWSTR browserExecutableFolder, PCWSTR userDataFolder, ICoreWebView2EnvironmentOptions* environmentOptions,
|
|
ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* environment_created_handler
|
|
);
|
|
|
|
typedef struct {
|
|
ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVtbl* lpVtbl;
|
|
ULONG refCount;
|
|
_webui_wv_win32_t* webView;
|
|
} CreateWebViewEnvironmentHandler;
|
|
|
|
typedef struct {
|
|
ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerVtbl* lpVtbl;
|
|
ULONG refCount;
|
|
_webui_wv_win32_t* webView;
|
|
} CreateWebViewControllerHandler;
|
|
|
|
typedef struct {
|
|
ICoreWebView2DocumentTitleChangedEventHandlerVtbl* lpVtbl;
|
|
ULONG refCount;
|
|
_webui_wv_win32_t* webView;
|
|
} TitleChangedHandler;
|
|
|
|
HRESULT STDMETHODCALLTYPE CreateWebViewEnvironmentHandler_Invoke(
|
|
ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* This,
|
|
HRESULT result,
|
|
ICoreWebView2Environment* env
|
|
);
|
|
|
|
HRESULT STDMETHODCALLTYPE CreateWebViewControllerHandler_Invoke(
|
|
ICoreWebView2CreateCoreWebView2ControllerCompletedHandler* This,
|
|
HRESULT result,
|
|
ICoreWebView2Controller* controller
|
|
);
|
|
|
|
HRESULT STDMETHODCALLTYPE QueryInterfaceEnvironment(
|
|
ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* This,
|
|
REFIID riid,
|
|
void** ppvObject
|
|
);
|
|
|
|
HRESULT STDMETHODCALLTYPE QueryInterfaceController(
|
|
ICoreWebView2CreateCoreWebView2ControllerCompletedHandler* This,
|
|
REFIID riid,
|
|
void** ppvObject
|
|
);
|
|
|
|
ULONG STDMETHODCALLTYPE AddRefEnvironment(
|
|
ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* This
|
|
);
|
|
|
|
ULONG STDMETHODCALLTYPE AddRefController(ICoreWebView2CreateCoreWebView2ControllerCompletedHandler* This);
|
|
ULONG STDMETHODCALLTYPE ReleaseEnvironment(ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* This);
|
|
ULONG STDMETHODCALLTYPE ReleaseController(ICoreWebView2CreateCoreWebView2ControllerCompletedHandler* This);
|
|
|
|
HRESULT STDMETHODCALLTYPE TitleChanged_Invoke(
|
|
ICoreWebView2DocumentTitleChangedEventHandler* This, ICoreWebView2* sender, IUnknown* args) {
|
|
(void)args;
|
|
TitleChangedHandler* handler = (TitleChangedHandler*)This;
|
|
_webui_wv_win32_t* webView = handler->webView;
|
|
LPWSTR newTitle = NULL;
|
|
sender->lpVtbl->get_DocumentTitle(sender, &newTitle);
|
|
SetWindowTextW(webView->hwnd, newTitle);
|
|
CoTaskMemFree(newTitle);
|
|
return S_OK;
|
|
};
|
|
|
|
ULONG STDMETHODCALLTYPE TitleChanged_AddRef(ICoreWebView2DocumentTitleChangedEventHandler* This) {
|
|
TitleChangedHandler* handler = (TitleChangedHandler*)This;
|
|
return ++handler->refCount;
|
|
};
|
|
|
|
ULONG STDMETHODCALLTYPE TitleChanged_Release(ICoreWebView2DocumentTitleChangedEventHandler* This) {
|
|
TitleChangedHandler* handler = (TitleChangedHandler*)This;
|
|
if (--handler->refCount == 0) {
|
|
_webui_free_mem((void*) handler->lpVtbl);
|
|
_webui_free_mem((void*) handler);
|
|
return 0;
|
|
}
|
|
return handler->refCount;
|
|
};
|
|
|
|
TitleChangedHandler* CreateTitleChangedHandler(_webui_wv_win32_t* webView) {
|
|
TitleChangedHandler* handler = _webui_malloc(sizeof(TitleChangedHandler));
|
|
handler->lpVtbl = _webui_malloc(sizeof(ICoreWebView2DocumentTitleChangedEventHandlerVtbl));
|
|
handler->lpVtbl->Invoke = TitleChanged_Invoke;
|
|
handler->lpVtbl->AddRef = TitleChanged_AddRef;
|
|
handler->lpVtbl->Release = TitleChanged_Release;
|
|
handler->refCount = 1;
|
|
handler->webView = webView;
|
|
|
|
// Save pointers to be freed by `_webui_wv_free()`
|
|
webView->titleChangedHandler = handler;
|
|
webView->titleChangedHandler_lpVtbl = handler->lpVtbl;
|
|
return handler;
|
|
};
|
|
|
|
CreateWebViewEnvironmentHandler* CreateEnvironmentHandler(_webui_wv_win32_t* webView) {
|
|
CreateWebViewEnvironmentHandler* handler = _webui_malloc(sizeof(CreateWebViewEnvironmentHandler));
|
|
if (!handler) return NULL;
|
|
handler->lpVtbl = _webui_malloc(sizeof(ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVtbl));
|
|
if (!handler->lpVtbl) {
|
|
_webui_free_mem((void*) handler);
|
|
return NULL;
|
|
}
|
|
handler->lpVtbl->QueryInterface = QueryInterfaceEnvironment;
|
|
handler->lpVtbl->AddRef = AddRefEnvironment;
|
|
handler->lpVtbl->Release = ReleaseEnvironment;
|
|
handler->lpVtbl->Invoke = CreateWebViewEnvironmentHandler_Invoke;
|
|
handler->refCount = 1;
|
|
handler->webView = webView;
|
|
|
|
// Save pointers to be freed by `_webui_wv_free()`
|
|
webView->createWebViewEnvironmentHandler = handler;
|
|
webView->createWebViewEnvironmentHandler_lpVtbl = handler->lpVtbl;
|
|
return handler;
|
|
};
|
|
|
|
CreateWebViewControllerHandler* CreateControllerHandler(_webui_wv_win32_t* webView) {
|
|
CreateWebViewControllerHandler* handler = _webui_malloc(sizeof(CreateWebViewControllerHandler));
|
|
if (!handler) return NULL;
|
|
handler->lpVtbl = _webui_malloc(sizeof(ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerVtbl));
|
|
if (!handler->lpVtbl) {
|
|
_webui_free_mem((void*) handler);
|
|
return NULL;
|
|
}
|
|
handler->lpVtbl->QueryInterface = QueryInterfaceController;
|
|
handler->lpVtbl->AddRef = AddRefController;
|
|
handler->lpVtbl->Release = ReleaseController;
|
|
handler->lpVtbl->Invoke = CreateWebViewControllerHandler_Invoke;
|
|
handler->refCount = 1;
|
|
handler->webView = webView;
|
|
|
|
// Save pointers to be freed by `_webui_wv_free()`
|
|
webView->createWebViewControllerHandler = handler;
|
|
webView->createWebViewControllerHandler_lpVtbl = handler->lpVtbl;
|
|
return handler;
|
|
};
|
|
|
|
HRESULT STDMETHODCALLTYPE CreateWebViewEnvironmentHandler_Invoke(
|
|
ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* This, HRESULT result,
|
|
ICoreWebView2Environment* env
|
|
) {
|
|
CreateWebViewEnvironmentHandler* handler = (CreateWebViewEnvironmentHandler*)This;
|
|
_webui_wv_win32_t* webView = handler->webView;
|
|
if (SUCCEEDED(result)) {
|
|
CreateWebViewControllerHandler* controllerHandler = CreateControllerHandler(webView);
|
|
if (controllerHandler) {
|
|
env->lpVtbl->CreateCoreWebView2Controller(env, webView->hwnd,
|
|
(ICoreWebView2CreateCoreWebView2ControllerCompletedHandler*)controllerHandler);
|
|
}
|
|
}
|
|
return S_OK;
|
|
};
|
|
|
|
HRESULT STDMETHODCALLTYPE CreateWebViewControllerHandler_Invoke(
|
|
ICoreWebView2CreateCoreWebView2ControllerCompletedHandler* This, HRESULT result,
|
|
ICoreWebView2Controller* controller) {
|
|
CreateWebViewControllerHandler* handler = (CreateWebViewControllerHandler*)This;
|
|
_webui_wv_win32_t* webView = handler->webView;
|
|
if (SUCCEEDED(result) && controller != NULL) {
|
|
webView->webviewController = controller;
|
|
webView->webviewController->lpVtbl->get_CoreWebView2(webView->webviewController,
|
|
&webView->webviewWindow);
|
|
webView->webviewController->lpVtbl->AddRef(webView->webviewController);
|
|
ICoreWebView2Settings* settings;
|
|
webView->webviewWindow->lpVtbl->get_Settings(webView->webviewWindow, &settings);
|
|
settings->lpVtbl->put_IsScriptEnabled(settings, TRUE);
|
|
settings->lpVtbl->put_AreDefaultScriptDialogsEnabled(settings, TRUE);
|
|
settings->lpVtbl->put_IsWebMessageEnabled(settings, TRUE);
|
|
RECT bounds = {0, 0, webView->width, webView->height};
|
|
webView->webviewController->lpVtbl->put_Bounds(webView->webviewController, bounds);
|
|
TitleChangedHandler* titleChangedHandler = CreateTitleChangedHandler(webView);
|
|
EventRegistrationToken tk;
|
|
webView->webviewWindow->lpVtbl->add_DocumentTitleChanged(webView->webviewWindow,
|
|
(ICoreWebView2DocumentTitleChangedEventHandler*)titleChangedHandler, &tk);
|
|
webView->webviewWindow->lpVtbl->Navigate(webView->webviewWindow, webView->url);
|
|
// Microsoft WebView2 Auto JS Inject
|
|
if (_webui.config.show_auto_js_inject) {
|
|
// HRESULT AutoInject = webView->webviewWindow->lpVtbl->AddScriptToExecuteOnDocumentCreated(
|
|
// webView->webviewWindow, L"var script = document.createElement('script');"
|
|
// "script.src = 'webui.js';document.head.appendChild(script);",
|
|
// NULL
|
|
// );
|
|
// if (FAILED(AutoInject)) {
|
|
// #ifdef WEBUI_LOG
|
|
// printf("[Core]\t\t[Thread .] _webui_webview_thread() -> Auto Inject creation failed\n");
|
|
// #endif
|
|
// }
|
|
// else {
|
|
// #ifdef WEBUI_LOG
|
|
// printf("[Core]\t\t[Thread .] _webui_webview_thread() -> Auto Inject creation succeeds\n");
|
|
// #endif
|
|
// }
|
|
}
|
|
} else return S_FALSE;
|
|
return S_OK;
|
|
};
|
|
|
|
HRESULT STDMETHODCALLTYPE QueryInterfaceEnvironment(
|
|
ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* This, REFIID riid, void** ppvObject) {
|
|
(void)This;
|
|
(void)riid;
|
|
*ppvObject = NULL;
|
|
return E_NOINTERFACE;
|
|
};
|
|
|
|
HRESULT STDMETHODCALLTYPE QueryInterfaceController(
|
|
ICoreWebView2CreateCoreWebView2ControllerCompletedHandler* This, REFIID riid, void** ppvObject) {
|
|
(void)This;
|
|
(void)riid;
|
|
*ppvObject = NULL;
|
|
return E_NOINTERFACE;
|
|
};
|
|
|
|
ULONG STDMETHODCALLTYPE AddRefEnvironment(ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* This) {
|
|
CreateWebViewEnvironmentHandler* handler = (CreateWebViewEnvironmentHandler*)This;
|
|
return ++handler->refCount;
|
|
};
|
|
|
|
ULONG STDMETHODCALLTYPE AddRefController(ICoreWebView2CreateCoreWebView2ControllerCompletedHandler* This) {
|
|
CreateWebViewControllerHandler* handler = (CreateWebViewControllerHandler*)This;
|
|
return ++handler->refCount;
|
|
};
|
|
|
|
ULONG STDMETHODCALLTYPE ReleaseEnvironment(ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* This) {
|
|
CreateWebViewEnvironmentHandler* handler = (CreateWebViewEnvironmentHandler*)This;
|
|
if (--handler->refCount == 0) {
|
|
_webui_free_mem((void*) handler->lpVtbl);
|
|
_webui_free_mem((void*) handler);
|
|
return 0;
|
|
}
|
|
return handler->refCount;
|
|
};
|
|
|
|
ULONG STDMETHODCALLTYPE ReleaseController(ICoreWebView2CreateCoreWebView2ControllerCompletedHandler* This) {
|
|
CreateWebViewControllerHandler* handler = (CreateWebViewControllerHandler*)This;
|
|
if (--handler->refCount == 0) {
|
|
_webui_free_mem((void*) handler->lpVtbl);
|
|
_webui_free_mem((void*) handler);
|
|
return 0;
|
|
}
|
|
return handler->refCount;
|
|
};
|
|
|
|
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
|
void* ptr = (void*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
_webui_window_t* win = _webui_dereference_win_ptr(ptr);
|
|
|
|
switch (uMsg) {
|
|
case WM_SIZE:
|
|
if (win) {
|
|
if (win->webView && win->webView->webviewController) {
|
|
RECT bounds;
|
|
GetClientRect(hwnd, &bounds);
|
|
win->webView->webviewController->lpVtbl->put_Bounds(win->webView->webviewController, bounds);
|
|
}
|
|
}
|
|
break;
|
|
case WM_GETMINMAXINFO:
|
|
if (win) {
|
|
if (win->minimum_size_set) {
|
|
LPMINMAXINFO lpMMI = (LPMINMAXINFO)lParam;
|
|
lpMMI->ptMinTrackSize.x = win->minimum_width;
|
|
lpMMI->ptMinTrackSize.y = win->minimum_height;
|
|
}
|
|
}
|
|
break;
|
|
case WM_CLOSE:
|
|
if (win) {
|
|
// Stop the WebView thread, close the window
|
|
// and free resources.
|
|
if (win->webView) {
|
|
win->webView->stop = true;
|
|
_webui_mutex_is_webview_update(win, WEBUI_MUTEX_TRUE);
|
|
}
|
|
_webui_wv_event_closed(win);
|
|
}
|
|
break;
|
|
case WM_DESTROY:
|
|
if (win) {
|
|
// Destroy message will be
|
|
// sent by `webui_wait()`
|
|
// Nothing to do here.
|
|
}
|
|
break;
|
|
default:
|
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
// Close Event
|
|
static void _webui_wv_event_closed(_webui_window_t* win) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_event_closed([%zu])\n", win->num);
|
|
#endif
|
|
win->is_closed = true;
|
|
}
|
|
|
|
static bool _webui_wv_show(_webui_window_t* win, char* url) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_show([%s])\n", url);
|
|
#endif
|
|
|
|
// Microsoft Windows WebView2
|
|
|
|
// Free old WebView
|
|
if (win->webView) {
|
|
_webui_wv_free(win->webView);
|
|
win->webView = NULL;
|
|
}
|
|
|
|
// Get wide URL
|
|
wchar_t* wURL = NULL;
|
|
_webui_str_to_wide(url, &wURL);
|
|
|
|
// Initializing the Win32 WebView struct
|
|
_webui_wv_win32_t* webView = (_webui_wv_win32_t*) _webui_malloc(sizeof(_webui_wv_win32_t));
|
|
webView->url = wURL;
|
|
webView->width = (win->width > 0 ? win->width : 800);
|
|
webView->height = (win->height > 0 ? win->height : 600);
|
|
webView->x = (win->x > 0 ? win->x : (int)((GetSystemMetrics(SM_CXSCREEN) - 800) / 2));
|
|
webView->y = (win->y > 0 ? win->y : (int)((GetSystemMetrics(SM_CYSCREEN) - 600) / 2));
|
|
win->webView = webView;
|
|
|
|
// Note: To garantee all Microsoft WebView's operations ownership we should
|
|
// process all the WebView's operations in one single thread for each window.
|
|
|
|
// Initializing
|
|
// Expecting `_webui_webview_thread` to change `mutex_is_webview_update`
|
|
// to `false` when initialization is done, and `_webui.is_webview`
|
|
// to `true` if loading the WebView is succeeded.
|
|
_webui_mutex_is_webview_update(win, WEBUI_MUTEX_TRUE);
|
|
|
|
// Win32 WebView thread
|
|
#ifdef _WIN32
|
|
HANDLE thread = CreateThread(NULL, 0, _webui_webview_thread, (void*)win, 0, NULL);
|
|
if (thread != NULL)
|
|
CloseHandle(thread);
|
|
#else
|
|
pthread_t thread;
|
|
pthread_create(&thread, NULL, &_webui_webview_thread, (void*)win);
|
|
pthread_detach(thread);
|
|
#endif
|
|
|
|
// Wait for WebView thread to start
|
|
_webui_timer_t timer;
|
|
_webui_timer_start(&timer);
|
|
for (;;) {
|
|
_webui_sleep(10);
|
|
if (!_webui_mutex_is_webview_update(win, WEBUI_MUTEX_NONE)) {
|
|
// WebView thread just started
|
|
// and loaded WebView successfully
|
|
break;
|
|
}
|
|
if (_webui_timer_is_end(&timer, 2500)) {
|
|
// Timeout. WebView thread failed.
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_show() -> Return [%d]\n", (_webui.is_webview == true));
|
|
#endif
|
|
|
|
return (_webui.is_webview);
|
|
};
|
|
|
|
static bool _webui_wv_set_size(_webui_wv_win32_t* webView, int windowWidth, int windowHeight) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_set_size(%d. %d)\n", windowWidth, windowHeight);
|
|
#endif
|
|
// if (webView && webView->webviewController) {
|
|
// RECT bounds = {0, 0, windowWidth, windowHeight};
|
|
// HRESULT hr = webView->webviewController->lpVtbl->put_Bounds(webView->webviewController, bounds);
|
|
// return SUCCEEDED(hr);
|
|
// }
|
|
if (webView) {
|
|
return (SetWindowPos(webView->hwnd, NULL, 0, 0, windowWidth, windowHeight, SWP_NOMOVE| SWP_NOREPOSITION));
|
|
}
|
|
return false;
|
|
};
|
|
|
|
static bool _webui_wv_set_position(_webui_wv_win32_t* webView, int x, int y) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_set_position(%d. %d)\n", x, y);
|
|
#endif
|
|
if (webView && webView->webviewController) {
|
|
RECT bounds;
|
|
webView->webviewController->lpVtbl->get_Bounds(webView->webviewController, &bounds);
|
|
HRESULT hr = MoveWindow(webView->hwnd, x, y, bounds.right - bounds.left, bounds.bottom - bounds.top, TRUE);
|
|
return hr != 0;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
static bool _webui_wv_navigate(_webui_wv_win32_t* webView, wchar_t* url) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_navigate([%ls])\n", url);
|
|
#endif
|
|
if (webView && webView->webviewWindow) {
|
|
HRESULT hr = webView->webviewWindow->lpVtbl->Navigate(webView->webviewWindow, url);
|
|
return SUCCEEDED(hr);
|
|
}
|
|
return false;
|
|
};
|
|
|
|
static void _webui_wv_free(_webui_wv_win32_t* webView) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_free()\n");
|
|
#endif
|
|
if (webView != NULL) {
|
|
if (webView->webviewWindow) {
|
|
webView->webviewWindow->lpVtbl->Release(webView->webviewWindow);
|
|
}
|
|
if (webView->webviewController) {
|
|
webView->webviewController->lpVtbl->Release(webView->webviewController);
|
|
}
|
|
if (webView->webviewEnvironment) {
|
|
webView->webviewEnvironment->lpVtbl->Release(webView->webviewEnvironment);
|
|
}
|
|
}
|
|
|
|
_webui_free_mem((void*) webView->titleChangedHandler_lpVtbl);
|
|
_webui_free_mem((void*) webView->titleChangedHandler);
|
|
_webui_free_mem((void*) webView->createWebViewEnvironmentHandler_lpVtbl);
|
|
_webui_free_mem((void*) webView->createWebViewEnvironmentHandler);
|
|
_webui_free_mem((void*) webView->createWebViewControllerHandler_lpVtbl);
|
|
_webui_free_mem((void*) webView->createWebViewControllerHandler);
|
|
|
|
_webui_free_mem((void*) webView->url);
|
|
_webui_free_mem((void*) webView);
|
|
};
|
|
|
|
static void _webui_wv_close(_webui_wv_win32_t *webView) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_close()\n");
|
|
#endif
|
|
(void)webView;
|
|
// ...
|
|
}
|
|
|
|
static WEBUI_THREAD_WEBVIEW {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t[Thread .] _webui_webview_thread()\n");
|
|
#endif
|
|
|
|
_webui_window_t* win = _webui_dereference_win_ptr(arg);
|
|
if (win == NULL) {
|
|
_webui_wv_free(win->webView);
|
|
win->webView = NULL;
|
|
_webui_mutex_is_webview_update(win, WEBUI_MUTEX_FALSE);
|
|
WEBUI_THREAD_RETURN
|
|
}
|
|
|
|
// WebView Dynamic Library
|
|
if (!_webui.webviewLib) {
|
|
_webui.webviewLib = LoadLibraryA("WebView2Loader.dll");
|
|
if (!_webui.webviewLib) {
|
|
_webui_wv_free(win->webView);
|
|
win->webView = NULL;
|
|
_webui_mutex_is_webview_update(win, WEBUI_MUTEX_FALSE);
|
|
WEBUI_THREAD_RETURN
|
|
}
|
|
}
|
|
|
|
// Window class
|
|
const char wvClass[] = "WebViewWindow";
|
|
WNDCLASSA wc;
|
|
wc.lpfnWndProc = WndProc;
|
|
wc.hInstance = GetModuleHandle(NULL);
|
|
wc.lpszClassName = wvClass;
|
|
wc.style = 0;
|
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
wc.hIcon = LoadIcon(GetModuleHandle(NULL) ,MAKEINTRESOURCE(101)); // default user icon resouce : 101
|
|
if(!wc.hIcon) wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); // if not existed, use system default icon
|
|
|
|
if (!RegisterClassA(&wc) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) {
|
|
_webui_wv_free(win->webView);
|
|
win->webView = NULL;
|
|
_webui_mutex_is_webview_update(win, WEBUI_MUTEX_FALSE);
|
|
WEBUI_THREAD_RETURN
|
|
}
|
|
|
|
win->webView->hwnd = CreateWindowExA(
|
|
0, wvClass, "", WS_OVERLAPPEDWINDOW,
|
|
win->webView->x, win->webView->y,
|
|
win->webView->width, win->webView->height,
|
|
NULL, NULL, GetModuleHandle(NULL), NULL
|
|
);
|
|
|
|
{ // window size correction
|
|
RECT rc;
|
|
GetClientRect(win->webView->hwnd, &rc);
|
|
win->webView->width = rc.right - rc.left;
|
|
win->webView->height = rc.bottom - rc.top;
|
|
}
|
|
|
|
if (!win->webView->hwnd) {
|
|
_webui_wv_free(win->webView);
|
|
win->webView = NULL;
|
|
_webui_mutex_is_webview_update(win, WEBUI_MUTEX_FALSE);
|
|
WEBUI_THREAD_RETURN
|
|
}
|
|
|
|
SetWindowLongPtr(win->webView->hwnd, GWLP_USERDATA, (LONG_PTR)win);
|
|
ShowWindow(win->webView->hwnd, SW_SHOW);
|
|
static CreateCoreWebView2EnvironmentWithOptionsFunc createEnv = NULL;
|
|
createEnv = (CreateCoreWebView2EnvironmentWithOptionsFunc)(void*)GetProcAddress(
|
|
_webui.webviewLib,
|
|
"CreateCoreWebView2EnvironmentWithOptions"
|
|
);
|
|
|
|
if (!createEnv) {
|
|
_webui_wv_free(win->webView);
|
|
win->webView = NULL;
|
|
_webui_mutex_is_webview_update(win, WEBUI_MUTEX_FALSE);
|
|
WEBUI_THREAD_RETURN
|
|
}
|
|
|
|
CreateWebViewEnvironmentHandler* environmentHandler = CreateEnvironmentHandler(win->webView);
|
|
if (!environmentHandler) {
|
|
_webui_wv_free(win->webView);
|
|
win->webView = NULL;
|
|
_webui_mutex_is_webview_update(win, WEBUI_MUTEX_FALSE);
|
|
WEBUI_THREAD_RETURN
|
|
}
|
|
|
|
// Get temp chache folder path
|
|
if (!_webui.webview_cacheFolder) {
|
|
const char* temp = _webui_get_temp_path();
|
|
_webui.webview_cacheFolder = (char*)_webui_malloc(WEBUI_MAX_PATH);
|
|
WEBUI_SN_PRINTF_DYN(_webui.webview_cacheFolder, WEBUI_MAX_PATH,
|
|
"%s%s.WebUI%sWebUIWebViewCache_%"PRIu32, temp, os_sep, os_sep,
|
|
_webui_generate_random_uint32());
|
|
}
|
|
|
|
// Convert chache folder path to wide
|
|
wchar_t* cacheFolderW = NULL;
|
|
_webui_str_to_wide(_webui.webview_cacheFolder, &cacheFolderW);
|
|
|
|
HRESULT hr = createEnv(NULL, cacheFolderW, NULL,
|
|
(ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler*)environmentHandler
|
|
);
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
// Success
|
|
// Let `wait()` use safe main-thread WebView2 loop
|
|
_webui.is_webview = true;
|
|
|
|
_webui_mutex_is_webview_update(win, WEBUI_MUTEX_FALSE);
|
|
MSG msg;
|
|
while (true) {
|
|
|
|
// Check if there is any Win32 Messages
|
|
|
|
if (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE) > 0) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessageW(&msg);
|
|
// Window manually closed
|
|
if (msg.message == WM_QUIT) {
|
|
if (win->webView) {
|
|
DestroyWindow(win->webView->hwnd);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
|
|
// Check if there is any WebUI Messages
|
|
|
|
if (_webui_mutex_is_webview_update(win, WEBUI_MUTEX_NONE)) {
|
|
_webui_mutex_is_webview_update(win, WEBUI_MUTEX_FALSE);
|
|
if (win->webView) {
|
|
// Stop this thread
|
|
if (win->webView->stop) {
|
|
DestroyWindow(win->webView->hwnd);
|
|
break;
|
|
}
|
|
// Window Size
|
|
if (win->webView->size) {
|
|
win->webView->size = false;
|
|
_webui_wv_set_size(win->webView, win->webView->width, win->webView->height);
|
|
}
|
|
// Window Position
|
|
if (win->webView->position) {
|
|
win->webView->position = false;
|
|
_webui_wv_set_position(win->webView, win->webView->x, win->webView->y);
|
|
}
|
|
// Navigation
|
|
if (win->webView->navigate) {
|
|
win->webView->navigate = false;
|
|
_webui_wv_navigate(win->webView, win->webView->url);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
// At this moment, there is no Win32 or WebUI messages
|
|
// let's IDLE for 10ms in this current thread.
|
|
_webui_sleep(10);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clean
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t[Thread .] _webui_webview_thread() -> Cleaning\n");
|
|
#endif
|
|
_webui_wv_free(win->webView);
|
|
_webui_free_mem((void*) cacheFolderW);
|
|
win->webView = NULL;
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t[Thread .] _webui_webview_thread() -> End\n");
|
|
#endif
|
|
|
|
WEBUI_THREAD_RETURN
|
|
};
|
|
|
|
#elif __linux__
|
|
// Linux
|
|
|
|
#define G_CALLBACK(f) ((void (*)(void)) (f))
|
|
#define GTK_RUNTIME_ARR { "libgtk-3.so.0" } // TODO: Add GTK v4 APIs "libgtk-4.so.1"
|
|
#define WEBKIT_RUNTIME_ARR { "libwebkit2gtk-4.1.so.0", "libwebkit2gtk-4.0.so.37" }
|
|
|
|
// Title Event
|
|
static void _webui_wv_event_title(void *web_view, void *pspec, void *arg) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_event_title()\n");
|
|
#endif
|
|
_webui_window_t* win = (_webui_window_t *)arg;
|
|
webkit_web_view_get_title_func webkit_web_view_get_title = (
|
|
webkit_web_view_get_title_func)dlsym(libwebkit, "webkit_web_view_get_title");
|
|
const char *title = webkit_web_view_get_title(web_view);
|
|
if (title) {
|
|
gtk_window_set_title_func gtk_window_set_title = (
|
|
gtk_window_set_title_func)dlsym(libgtk, "gtk_window_set_title");
|
|
gtk_window_set_title(win->webView->gtk_win, title);
|
|
}
|
|
}
|
|
|
|
// Close Event
|
|
static void _webui_wv_event_closed(void *widget, void *arg) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_event_closed()\n");
|
|
#endif
|
|
_webui_window_t* win = _webui_dereference_win_ptr(arg);
|
|
if (win) {
|
|
win->is_closed = true;
|
|
if (win->webView) {
|
|
win->webView->open = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool _webui_wv_set_size(_webui_wv_linux_t* webView, int windowWidth, int windowHeight) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_set_size(%d. %d)\n", windowWidth, windowHeight);
|
|
#endif
|
|
if (webView) {
|
|
if (webView->gtk_win) {
|
|
gtk_window_resize(webView->gtk_win, webView->width, webView->height);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
static bool _webui_wv_set_position(_webui_wv_linux_t* webView, int x, int y) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_set_position(%d. %d)\n", x, y);
|
|
#endif
|
|
if (webView) {
|
|
if (webView->gtk_win) {
|
|
// Note:
|
|
// This API does not work under Wayland, and it has been removed
|
|
// in GTK v4, alongside all the APIs that relies on a global
|
|
// coordinates system. So, `gtk_window_move` may have no effect.
|
|
gtk_window_move(webView->gtk_win, webView->x, webView->y);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
static bool _webui_wv_navigate(_webui_wv_linux_t* webView, char* url) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_navigate([%s])\n", url);
|
|
#endif
|
|
if (webView) {
|
|
if (webView->gtk_win) {
|
|
webkit_web_view_load_uri(webView->gtk_wv, webView->url);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
static void _webui_wv_close(_webui_wv_linux_t* webView) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_close()\n");
|
|
#endif
|
|
|
|
if (webView) {
|
|
|
|
if (webView->open) {
|
|
|
|
webView->open = false;
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_close() -> Closing WebView window\n");
|
|
#endif
|
|
gtk_window_close(webView->gtk_win);
|
|
}
|
|
}
|
|
|
|
_webui_free_mem((void*) webView->url);
|
|
_webui_free_mem((void*) webView);
|
|
};
|
|
|
|
static void _webui_wv_free() {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_free()\n");
|
|
#endif
|
|
|
|
if (libwebkit) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_free() -> Unload WebKit\n");
|
|
#endif
|
|
dlclose(libwebkit);
|
|
}
|
|
|
|
if (libgtk) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_free() -> Unload GTK\n");
|
|
#endif
|
|
dlclose(libgtk);
|
|
}
|
|
|
|
_webui.is_webview = false;
|
|
libgtk = NULL;
|
|
libwebkit = NULL;
|
|
};
|
|
|
|
static void _webui_wv_create(_webui_window_t* win) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_create()\n");
|
|
#endif
|
|
|
|
// Initialize GTK Window
|
|
win->webView->gtk_win = gtk_window_new(0);
|
|
|
|
// Initialize WebView
|
|
win->webView->gtk_wv = webkit_web_view_new();
|
|
|
|
// Window Settings
|
|
gtk_window_set_default_size(win->webView->gtk_win, win->webView->width, win->webView->height);
|
|
gtk_container_add(win->webView->gtk_win, win->webView->gtk_wv);
|
|
|
|
// Window position
|
|
// Note: The new positioning system it's not GTK's toolkit job anymore.
|
|
// if ((win->x > 0) && (win->y > 0)) {
|
|
// gtk_window_move(win->webView->gtk_win, win->webView->x, win->webView->y);
|
|
// }
|
|
// else {
|
|
// gtk_window_set_position(win->webView->gtk_wv, 1);
|
|
// }
|
|
|
|
// Events
|
|
g_signal_connect_data(win->webView->gtk_wv, "notify::title", G_CALLBACK(
|
|
_webui_wv_event_title), (void *)win, NULL, 0);
|
|
g_signal_connect_data(win->webView->gtk_win, "destroy", G_CALLBACK(
|
|
_webui_wv_event_closed), (void *)win, NULL, 0);
|
|
|
|
// Linux GTK WebView Auto JS Inject
|
|
if (_webui.config.show_auto_js_inject) {
|
|
// ...
|
|
}
|
|
|
|
// Show
|
|
webkit_web_view_load_uri(win->webView->gtk_wv, win->webView->url);
|
|
gtk_widget_show_all(win->webView->gtk_win);
|
|
win->webView->open = true;
|
|
|
|
// Note: All the GTK WebView's operations should be
|
|
// processed in one single thread for each window.
|
|
|
|
// Linux WebView thread
|
|
#ifdef _WIN32
|
|
HANDLE thread = CreateThread(NULL, 0, _webui_webview_thread, (void*)win, 0, NULL);
|
|
if (thread != NULL)
|
|
CloseHandle(thread);
|
|
#else
|
|
pthread_t thread;
|
|
pthread_create(&thread, NULL, &_webui_webview_thread, (void*)win);
|
|
pthread_detach(thread);
|
|
#endif
|
|
}
|
|
|
|
static int _webui_wv_create_schedule(void* arg) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_create_schedule()\n");
|
|
#endif
|
|
|
|
// This callback is fired by GTK. so it's safe
|
|
// to create the new WebView window right now.
|
|
|
|
_webui_window_t* win = _webui_dereference_win_ptr(arg);
|
|
if (win) {
|
|
_webui_wv_create(win);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool _webui_wv_show(_webui_window_t* win, char* url) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_show([%s])\n", url);
|
|
#endif
|
|
|
|
// Linux GTK WebView
|
|
|
|
#ifdef WEBUI_DYNAMIC
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_show() -> WebUI dynamic version does not support Linux WebView\n");
|
|
#endif
|
|
return false;
|
|
#endif
|
|
|
|
if (_webui.is_browser_main_run)
|
|
return false;
|
|
|
|
// Dynamic Load
|
|
if (!libgtk || !libwebkit) {
|
|
|
|
_webui.is_webview = false;
|
|
|
|
// GTK Dynamic Load
|
|
const char *gtk_libs[] = GTK_RUNTIME_ARR;
|
|
for (size_t i = 0; i < (sizeof(gtk_libs) / sizeof(gtk_libs[0]));++i) {
|
|
libgtk = dlopen(gtk_libs[i], RTLD_LAZY);
|
|
if (libgtk) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_show() -> GTK loaded [%s]\n",
|
|
gtk_libs[i]);
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!libgtk) {
|
|
_webui_wv_free();
|
|
return false;
|
|
}
|
|
|
|
// WebView Dynamic Load
|
|
const char *webkit_libs[] = WEBKIT_RUNTIME_ARR;
|
|
for (size_t i = 0; i < (sizeof(webkit_libs) / sizeof(webkit_libs[0]));++i) {
|
|
libwebkit = dlopen(webkit_libs[i], RTLD_LAZY);
|
|
if (libwebkit) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_show() -> WebKit loaded [%s]\n",
|
|
webkit_libs[i]);
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!libwebkit) {
|
|
_webui_wv_free();
|
|
return false;
|
|
}
|
|
|
|
// GTK Symbol Addresses
|
|
gtk_init = (gtk_init_func)dlsym(
|
|
libgtk, "gtk_init");
|
|
gtk_widget_show_all = (gtk_widget_show_all_func)dlsym(
|
|
libgtk, "gtk_widget_show_all");
|
|
gtk_main_iteration_do = (gtk_main_iteration_do_func)dlsym(
|
|
libgtk, "gtk_main_iteration_do");
|
|
gtk_events_pending = (gtk_events_pending_func)dlsym(
|
|
libgtk, "gtk_events_pending");
|
|
gtk_container_add = (gtk_container_add_func)dlsym(
|
|
libgtk, "gtk_container_add");
|
|
gtk_window_new = (gtk_window_new_func)dlsym(
|
|
libgtk, "gtk_window_new");
|
|
gtk_window_set_default_size = (gtk_window_set_default_size_func)dlsym(
|
|
libgtk, "gtk_window_set_default_size");
|
|
gtk_window_set_title = (gtk_window_set_title_func)dlsym(
|
|
libgtk, "gtk_window_set_title");
|
|
gtk_window_move = (gtk_window_move_func)dlsym(
|
|
libgtk, "gtk_window_move");
|
|
gtk_window_close = (gtk_window_close_func)dlsym(
|
|
libgtk, "gtk_window_close");
|
|
gtk_window_resize = (gtk_window_resize_func)dlsym(
|
|
libgtk, "gtk_window_resize");
|
|
gtk_window_set_position = (gtk_window_set_position_func)dlsym(
|
|
libgtk, "gtk_window_set_position");
|
|
g_signal_connect_data = (g_signal_connect_data_func)dlsym(
|
|
libgtk, "g_signal_connect_data");
|
|
g_idle_add = (g_idle_add_func)dlsym(
|
|
libgtk, "g_idle_add");
|
|
|
|
// WebView Symbol Addresses
|
|
webkit_web_view_new = (webkit_web_view_new_func)dlsym(
|
|
libwebkit, "webkit_web_view_new");
|
|
webkit_web_view_load_uri = (webkit_web_view_load_uri_func)dlsym(
|
|
libwebkit, "webkit_web_view_load_uri");
|
|
webkit_web_view_get_title = (webkit_web_view_get_title_func)dlsym(
|
|
libwebkit, "webkit_web_view_get_title");
|
|
|
|
// Check GTK
|
|
if (
|
|
// GTK Commun
|
|
!gtk_init || !gtk_window_new || !gtk_window_set_default_size
|
|
|| !gtk_window_set_title || !g_signal_connect_data
|
|
// GTK v3
|
|
|| !gtk_widget_show_all || !gtk_main_iteration_do
|
|
|| !gtk_events_pending || !gtk_container_add
|
|
|| !gtk_window_move
|
|
// GTK v4
|
|
// ...
|
|
)
|
|
{
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t[Thread .] _webui_webview_thread() -> GTK symbol addresses failed\n");
|
|
#endif
|
|
_webui_wv_free();
|
|
return false;
|
|
}
|
|
|
|
// Check WebView
|
|
if (!webkit_web_view_new || !webkit_web_view_load_uri || !webkit_web_view_get_title) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t[Thread .] _webui_webview_thread() -> WebKit symbol addresses failed\n");
|
|
#endif
|
|
_webui_wv_free();
|
|
return false;
|
|
}
|
|
|
|
// Initialize GTK
|
|
gtk_init(NULL, NULL);
|
|
}
|
|
|
|
// Free old WebView
|
|
if (win->webView) {
|
|
win->webView->stop = true;
|
|
_webui_mutex_is_webview_update(win, WEBUI_MUTEX_TRUE);
|
|
}
|
|
|
|
// Copy URL
|
|
char* url_copy = _webui_str_dup(url);
|
|
|
|
// Initializing the Linux WebView struct
|
|
_webui_wv_linux_t* webView = (_webui_wv_linux_t*) _webui_malloc(sizeof(_webui_wv_linux_t));
|
|
webView->url = url_copy;
|
|
webView->width = (win->width > 0 ? win->width : 800);
|
|
webView->height = (win->height > 0 ? win->height : 600);
|
|
webView->x = (win->x > 0 ? win->x : 0);
|
|
webView->y = (win->y > 0 ? win->y : 0);
|
|
win->webView = webView;
|
|
|
|
// New WebView window
|
|
if (_webui.is_gtk_main_run) {
|
|
|
|
// Schedule the creation of the new WebView
|
|
// window in the main thread `webui_wait()`
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_show() -> Schedule the creation of the new WebView window\n");
|
|
#endif
|
|
|
|
g_idle_add(_webui_wv_create_schedule, (void*)win);
|
|
}
|
|
else {
|
|
|
|
// The main thread `webui_wait()` is not running
|
|
// so it's safe to create the new WebView window
|
|
// from this thread, which is should be fired from
|
|
// the back-end main thread.
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_show() -> New WebView window\n");
|
|
#endif
|
|
|
|
_webui_wv_create(win);
|
|
}
|
|
|
|
// Initializing
|
|
// Expecting `_webui_webview_thread` to change `mutex_is_webview_update`
|
|
// to `false` when initialization is done, and `_webui.is_webview`
|
|
// to `true` if loading the WebView is succeeded.
|
|
_webui_mutex_is_webview_update(win, WEBUI_MUTEX_TRUE);
|
|
|
|
// Wait for WebView thread to get
|
|
// started by `_webui_wv_create()`
|
|
|
|
_webui_timer_t timer;
|
|
_webui_timer_start(&timer);
|
|
for (;;) {
|
|
_webui_sleep(100);
|
|
if (!_webui_mutex_is_webview_update(win, WEBUI_MUTEX_NONE)) {
|
|
// WebView thread just started
|
|
// and loaded window successfully
|
|
break;
|
|
}
|
|
if (_webui_timer_is_end(&timer, 2500)) {
|
|
// Timeout. WebView thread failed.
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_show() -> Return [%d]\n", (_webui.is_webview == true));
|
|
#endif
|
|
|
|
return (_webui.is_webview);
|
|
};
|
|
|
|
static WEBUI_THREAD_WEBVIEW {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t[Thread .] _webui_webview_thread()\n");
|
|
#endif
|
|
|
|
_webui_window_t* win = _webui_dereference_win_ptr(arg);
|
|
if (win == NULL) {
|
|
_webui_wv_close(win->webView);
|
|
win->webView = NULL;
|
|
_webui_mutex_is_webview_update(win, WEBUI_MUTEX_FALSE);
|
|
WEBUI_THREAD_RETURN
|
|
}
|
|
|
|
// Check GTK is loaded
|
|
if (!libgtk || !libwebkit) {
|
|
_webui_wv_close(win->webView);
|
|
win->webView = NULL;
|
|
_webui_mutex_is_webview_update(win, WEBUI_MUTEX_FALSE);
|
|
WEBUI_THREAD_RETURN
|
|
}
|
|
|
|
if (win->webView->gtk_win && win->webView->gtk_wv) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t[Thread .] _webui_webview_thread() -> Started\n");
|
|
#endif
|
|
|
|
// Success
|
|
// Let `wait()` use safe main-thread GTK WebView loop
|
|
_webui.is_webview = true;
|
|
_webui_mutex_is_webview_update(win, WEBUI_MUTEX_FALSE);
|
|
|
|
while (true) {
|
|
|
|
// Check if there is any WebUI Messages
|
|
|
|
if (_webui_mutex_is_webview_update(win, WEBUI_MUTEX_NONE)) {
|
|
_webui_mutex_is_webview_update(win, WEBUI_MUTEX_FALSE);
|
|
if (win->webView) {
|
|
// Stop this thread
|
|
if (win->webView->stop) {
|
|
break;
|
|
}
|
|
// Window Size
|
|
if (win->webView->size) {
|
|
win->webView->size = false;
|
|
_webui_wv_set_size(win->webView, win->webView->width, win->webView->height);
|
|
}
|
|
// Window Position
|
|
if (win->webView->position) {
|
|
win->webView->position = false;
|
|
_webui_wv_set_position(win->webView, win->webView->x, win->webView->y);
|
|
}
|
|
// Navigation
|
|
if (win->webView->navigate) {
|
|
win->webView->navigate = false;
|
|
_webui_wv_navigate(win->webView, win->webView->url);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
// At this moment, there is no WebUI messages
|
|
// let's IDLE for 250ms in this current thread.
|
|
_webui_sleep(250);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t[Thread .] _webui_webview_thread() -> Cleaning\n");
|
|
#endif
|
|
|
|
// Clean
|
|
_webui_wv_close(win->webView);
|
|
win->webView = NULL;
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t[Thread .] _webui_webview_thread() -> End\n");
|
|
#endif
|
|
|
|
WEBUI_THREAD_RETURN
|
|
}
|
|
#else
|
|
// macOS
|
|
|
|
static bool _webui_wv_set_size(_webui_wv_macos_t* webView, int windowWidth, int windowHeight) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_set_size(%d. %d)\n", windowWidth, windowHeight);
|
|
#endif
|
|
_webui_macos_wv_set_size(webView->index, windowWidth, windowHeight);
|
|
return false;
|
|
};
|
|
|
|
static bool _webui_wv_set_position(_webui_wv_macos_t* webView, int x, int y) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_set_position(%d. %d)\n", x, y);
|
|
#endif
|
|
_webui_macos_wv_set_position(webView->index, x, y);
|
|
return false;
|
|
};
|
|
|
|
static bool _webui_wv_navigate(_webui_wv_macos_t* webView, char* url) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_navigate([%s])\n", url);
|
|
#endif
|
|
_webui_macos_wv_navigate(webView->index, (const char*)url);
|
|
return false;
|
|
};
|
|
|
|
static void _webui_wv_free(_webui_wv_macos_t* webView) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_free()\n");
|
|
#endif
|
|
// ...
|
|
_webui_free_mem((void*) webView->url);
|
|
_webui_free_mem((void*) webView);
|
|
};
|
|
|
|
static void _webui_wv_close(_webui_wv_macos_t *webView) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_close()\n");
|
|
#endif
|
|
// ...
|
|
}
|
|
|
|
// Close Event
|
|
static void _webui_wv_event_closed(int index) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_event_closed([%d])\n", index);
|
|
#endif
|
|
if (_webui.wins[index] != NULL) {
|
|
_webui.wins[index]->is_closed = true;
|
|
if (_webui.wins[index]->webView) {
|
|
// Close window
|
|
if (_webui_mutex_is_connected(_webui.wins[index], WEBUI_MUTEX_NONE)) {
|
|
_webui_send_all(
|
|
_webui.wins[index], 0, WEBUI_CMD_CLOSE, NULL, 0
|
|
);
|
|
}
|
|
// Stop WebView thread if any
|
|
if (_webui.wins[index]->webView) {
|
|
_webui.wins[index]->webView->stop = true;
|
|
_webui_mutex_is_webview_update(_webui.wins[index], WEBUI_MUTEX_TRUE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool _webui_wv_show(_webui_window_t* win, char* url) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_wv_show([%s])\n", url);
|
|
#endif
|
|
|
|
// Apple macOS WKWebView
|
|
|
|
if (_webui.is_browser_main_run)
|
|
return false;
|
|
|
|
if (!_webui.is_wkwebview_main_run) {
|
|
if (_webui_macos_wv_new(win->num)) {
|
|
if (!_webui.is_webview) {
|
|
// Let `wait()` use safe main-thread WKWebView loop
|
|
_webui.is_webview = true;
|
|
// Set close callback once
|
|
_webui_macos_wv_set_close_cb(_webui_wv_event_closed);
|
|
}
|
|
} else return false;
|
|
}
|
|
else {
|
|
|
|
_webui_macos_wv_new_thread_safe(win->num);
|
|
_webui_sleep(250);
|
|
}
|
|
|
|
// Free old WebView
|
|
if (win->webView) {
|
|
_webui_wv_free(win->webView);
|
|
win->webView = NULL;
|
|
}
|
|
|
|
// Copy URL
|
|
char* url_copy = _webui_str_dup(url);
|
|
|
|
// Initializing the macOS WebView struct
|
|
_webui_wv_macos_t* webView = (_webui_wv_macos_t*) _webui_malloc(sizeof(_webui_wv_macos_t));
|
|
webView->url = url_copy;
|
|
webView->width = (win->width > 0 ? win->width : 800);
|
|
webView->height = (win->height > 0 ? win->height : 600);
|
|
webView->x = (win->x > 0 ? win->x : 100);
|
|
webView->y = (win->y > 0 ? win->y : 100);
|
|
webView->index = win->num;
|
|
win->webView = webView;
|
|
|
|
// Show window
|
|
_webui_macos_wv_show(
|
|
webView->index, webView->url,
|
|
webView->x, webView->y,
|
|
webView->width, webView->height);
|
|
|
|
// Initializing
|
|
// Expecting `_webui_webview_thread` to change
|
|
// `mutex_is_webview_update` to false when success
|
|
_webui_mutex_is_webview_update(win, WEBUI_MUTEX_TRUE);
|
|
|
|
// macOS WebView thread
|
|
#ifdef _WIN32
|
|
HANDLE thread = CreateThread(NULL, 0, _webui_webview_thread, (void*)win, 0, NULL);
|
|
if (thread != NULL)
|
|
CloseHandle(thread);
|
|
#else
|
|
pthread_t thread;
|
|
pthread_create(&thread, NULL, &_webui_webview_thread, (void*)win);
|
|
pthread_detach(thread);
|
|
#endif
|
|
|
|
// Wait for WebView thread to start
|
|
_webui_timer_t timer;
|
|
_webui_timer_start(&timer);
|
|
for (;;) {
|
|
_webui_sleep(10);
|
|
if (!_webui_mutex_is_webview_update(win, WEBUI_MUTEX_NONE)) {
|
|
// WebView thread just started
|
|
// and loaded WebView successfully
|
|
break;
|
|
}
|
|
if (_webui_timer_is_end(&timer, 2500)) {
|
|
// Timeout. WebView thread failed.
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (_webui_mutex_is_webview_update(win, WEBUI_MUTEX_NONE) == false);
|
|
};
|
|
|
|
static WEBUI_THREAD_WEBVIEW {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t[Thread .] _webui_webview_thread()\n");
|
|
#endif
|
|
|
|
_webui_window_t* win = _webui_dereference_win_ptr(arg);
|
|
if (win == NULL) {
|
|
_webui_wv_free(win->webView);
|
|
win->webView = NULL;
|
|
WEBUI_THREAD_RETURN
|
|
}
|
|
|
|
// ...
|
|
|
|
if (true) {
|
|
|
|
// Success
|
|
_webui_mutex_is_webview_update(win, WEBUI_MUTEX_FALSE);
|
|
|
|
while (true) {
|
|
|
|
// Check if there is any WebUI Messages
|
|
|
|
if (_webui_mutex_is_webview_update(win, WEBUI_MUTEX_NONE)) {
|
|
_webui_mutex_is_webview_update(win, WEBUI_MUTEX_FALSE);
|
|
if (win->webView) {
|
|
// Stop this thread
|
|
if (win->webView->stop) {
|
|
_webui_macos_wv_close(win->webView->index);
|
|
break;
|
|
}
|
|
// Window Size
|
|
if (win->webView->size) {
|
|
win->webView->size = false;
|
|
_webui_wv_set_size(win->webView, win->webView->width, win->webView->height);
|
|
}
|
|
// Window Position
|
|
if (win->webView->position) {
|
|
win->webView->position = false;
|
|
_webui_wv_set_position(win->webView, win->webView->x, win->webView->y);
|
|
}
|
|
// Navigation
|
|
if (win->webView->navigate) {
|
|
win->webView->navigate = false;
|
|
_webui_wv_navigate(win->webView, win->webView->url);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
// At this moment, there is no WebUI messages
|
|
// let's IDLE for 250ms in this current thread.
|
|
_webui_sleep(250);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Close window in case WKWebView did
|
|
// not fire the close event.
|
|
if (_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE)) {
|
|
_webui_send_all(
|
|
win, 0, WEBUI_CMD_CLOSE, NULL, 0
|
|
);
|
|
}
|
|
|
|
// Clean
|
|
_webui_wv_free(win->webView);
|
|
win->webView = NULL;
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t[Thread .] _webui_webview_thread() -> End\n");
|
|
#endif
|
|
|
|
WEBUI_THREAD_RETURN
|
|
}
|
|
#endif
|
|
|
|
static WEBUI_THREAD_MONITOR {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread()\n");
|
|
#endif
|
|
|
|
_webui_window_t* win = _webui_dereference_win_ptr(arg);
|
|
if (win == NULL) {
|
|
WEBUI_THREAD_RETURN
|
|
}
|
|
|
|
// Stop if a lower window already monitoring the same folder
|
|
// Loop trough all windows
|
|
for (size_t i = 1; i < WEBUI_MAX_IDS; i++) {
|
|
if ((_webui.wins[i] != NULL) && (_webui.wins[i] != win) && (i < win->num)) {
|
|
WEBUI_THREAD_RETURN
|
|
}
|
|
}
|
|
|
|
const char* js = "location.reload();";
|
|
size_t js_len = _webui_strlen(js);
|
|
|
|
#ifdef _WIN32
|
|
// Windows
|
|
HANDLE hDir = CreateFile(
|
|
win->server_root_path, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL
|
|
);
|
|
if (hDir == INVALID_HANDLE_VALUE) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> Failed to open folder: %s\n",
|
|
win->server_root_path
|
|
);
|
|
#endif
|
|
WEBUI_THREAD_RETURN
|
|
}
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> Monitoring [%s]\n", win->server_root_path);
|
|
#endif
|
|
char buffer[1024];
|
|
DWORD bytesReturned;
|
|
while ((!_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE)) && (win->server_running)) {
|
|
if (ReadDirectoryChangesW(
|
|
hDir, buffer, sizeof(buffer), TRUE,
|
|
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES |
|
|
FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE, &bytesReturned, NULL, NULL
|
|
))
|
|
{
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> Folder updated\n");
|
|
#endif
|
|
// Loop trough all connected clients in this window
|
|
for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
|
|
if ((_webui.clients[i] != NULL) && (_webui.clients_win_num[i] == win->num) &&
|
|
(_webui_mutex_is_multi_client_token_valid(win, WEBUI_MUTEX_NONE, i))) {
|
|
_webui_send_client(win, _webui.clients[i], 0, WEBUI_CMD_JS_QUICK, js, js_len, false);
|
|
}
|
|
}
|
|
} else {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> Failed to read folder changes\n");
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
CloseHandle(hDir);
|
|
#elif __linux__
|
|
// Linux
|
|
int fd = inotify_init();
|
|
if (fd < 0) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> inotify_init error\n");
|
|
#endif
|
|
WEBUI_THREAD_RETURN
|
|
}
|
|
int wd = inotify_add_watch(fd, win->server_root_path, IN_MODIFY | IN_CREATE | IN_DELETE);
|
|
if (wd < 0) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> inotify_add_watch error\n");
|
|
#endif
|
|
close(fd);
|
|
WEBUI_THREAD_RETURN
|
|
}
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> Monitoring [%s]\n", win->server_root_path);
|
|
#endif
|
|
char buffer[1024];
|
|
while (!_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE)) {
|
|
int length = read(fd, buffer, sizeof(buffer));
|
|
if (length < 0) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> read error\n");
|
|
#endif
|
|
break;
|
|
}
|
|
int i = 0;
|
|
while (i < length) {
|
|
struct inotify_event *event = (struct inotify_event *)&buffer[i];
|
|
if (event->len) {
|
|
if (event->mask&(IN_CREATE | IN_DELETE | IN_MODIFY)) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> Folder updated\n");
|
|
#endif
|
|
// Loop trough all connected clients in this window
|
|
for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
|
|
if ((_webui.clients[i] != NULL) && (_webui.clients_win_num[i] == win->num) &&
|
|
(_webui_mutex_is_multi_client_token_valid(win, WEBUI_MUTEX_NONE, i))) {
|
|
_webui_send_client(win, _webui.clients[i], 0, WEBUI_CMD_JS_QUICK, js, js_len, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
i += sizeof(struct inotify_event) + event->len;
|
|
}
|
|
}
|
|
inotify_rm_watch(fd, wd);
|
|
close(fd);
|
|
#else
|
|
// macOS
|
|
int kq = kqueue();
|
|
if (kq == -1) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> kqueue error\n");
|
|
#endif
|
|
WEBUI_THREAD_RETURN
|
|
}
|
|
int fd = open(win->server_root_path, O_RDONLY);
|
|
if (fd == -1) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> open error\n");
|
|
#endif
|
|
close(kq);
|
|
WEBUI_THREAD_RETURN
|
|
}
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> Monitoring [%s]\n", win->server_root_path);
|
|
#endif
|
|
struct kevent change;
|
|
EV_SET(&change, fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT, NOTE_WRITE, 0, NULL);
|
|
while (!_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE)) {
|
|
struct kevent event;
|
|
int nev = kevent(kq, &change, 1, &event, 1, NULL);
|
|
if (nev == -1) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> kevent error\n");
|
|
#endif
|
|
break;
|
|
} else if (nev > 0) {
|
|
if (event.fflags&NOTE_WRITE) {
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> Folder updated\n");
|
|
#endif
|
|
// Loop trough all connected clients in this window
|
|
for (size_t i = 0; i < WEBUI_MAX_IDS; i++) {
|
|
if ((_webui.clients[i] != NULL) && (_webui.clients_win_num[i] == win->num) &&
|
|
(_webui_mutex_is_multi_client_token_valid(win, WEBUI_MUTEX_NONE, i))) {
|
|
_webui_send_client(win, _webui.clients[i], 0, WEBUI_CMD_JS_QUICK, js, js_len, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
close(fd);
|
|
close(kq);
|
|
#endif
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> Exit\n");
|
|
#endif
|
|
WEBUI_THREAD_RETURN
|
|
}
|
|
|
|
static void _webui_bridge_api_handler(webui_event_t* e) {
|
|
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_bridge_api_handler()\n");
|
|
#endif
|
|
|
|
// This function gets called by `webui.js` to reach
|
|
// an internal core API. This is not a public user API.
|
|
|
|
const char* api_name = webui_get_string(e);
|
|
#ifdef WEBUI_LOG
|
|
printf("[Core]\t\t_webui_bridge_api_handler() -> api_name: [%s]\n", api_name);
|
|
#endif
|
|
|
|
if (strcmp(api_name, "high_contrast") == 0) {
|
|
webui_return_bool(e, webui_is_high_contrast());
|
|
}
|
|
}
|