webui/src/webui.c
Hassan DRAGA 3cb8962b9a Linux Memory Optimization
* Implementing #81 for Linux
2023-04-24 21:56:07 -04:00

4334 lines
136 KiB
C

/*
WebUI Library 2.2.0
http://_webui_core.me
https://github.com/alifcommunity/webui
Copyright (c) 2020-2023 Hassan Draga.
Licensed under GNU General Public License v2.0.
All rights reserved.
Canada.
*/
// -- Third-party ---------------------
#include "mongoose.h"
// -- WebUI ---------------------------
#include "webui_core.h"
// -- Heap ----------------------------
static _webui_core_t _webui_core;
// -- WebUI JS-Bridge ---------
// This is a uncompressed version to make the debugging
// more easy in the browser using the builtin dev-tools
#ifdef WEBUI_LOG
#define WEBUI_JS_LOG "true"
#else
#define WEBUI_JS_LOG "false"
#endif
static const char* webui_javascript_bridge =
"var _webui_log = " WEBUI_JS_LOG "; \n"
"var _webui_ws; \n"
"var _webui_ws_status = false; \n"
"var _webui_ws_status_once = false; \n"
"var _webui_close_reason = 0; \n"
"var _webui_close_value; \n"
"var _webui_has_events = false; \n"
"const _WEBUI_SIGNATURE = 221; \n"
"const _WEBUI_JS = 254; \n"
"const _WEBUI_JS_QUICK = 253; \n"
"const _WEBUI_CLICK = 252; \n"
"const _WEBUI_SWITCH = 251; \n"
"const _WEBUI_CLOSE = 250; \n"
"const _WEBUI_FUNCTION = 249; \n"
"function _webui_close(reason = 0, value = 0) { \n"
" _webui_send_event_navigation(value); \n"
" _webui_ws_status = false; \n"
" _webui_close_reason = reason; \n"
" _webui_close_value = value; \n"
" _webui_ws.close(); \n"
"} \n"
"function _webui_freeze_ui() { \n"
" document.body.style.filter = 'contrast(1%)'; \n"
"} \n"
"function _webui_start() { \n"
" if('WebSocket' in window) { \n"
" if(_webui_bind_list.includes(_webui_win_num + '/')) _webui_has_events = true; \n"
" _webui_ws = new WebSocket('ws://localhost:' + _webui_port + '/_webui_ws_connect'); \n"
" _webui_ws.binaryType = 'arraybuffer'; \n"
" _webui_ws.onopen = function () { \n"
" _webui_ws.binaryType = 'arraybuffer'; \n"
" _webui_ws_status = true; \n"
" _webui_ws_status_once = true; \n"
" if(_webui_log) \n"
" console.log('WebUI -> Connected'); \n"
" _webui_clicks_listener(); \n"
" }; \n"
" _webui_ws.onerror = function () { \n"
" if(_webui_log) \n"
" console.log('WebUI -> Connection Failed'); \n"
" _webui_freeze_ui(); \n"
" }; \n"
" _webui_ws.onclose = function (evt) { \n"
" _webui_ws_status = false; \n"
" if(_webui_close_reason === _WEBUI_SWITCH) { \n"
" if(_webui_log) \n"
" console.log('WebUI -> Connection lost -> Navigation to [' + _webui_close_value + ']'); \n"
" window.location.replace(_webui_close_value); \n"
" } else { \n"
" if(_webui_log) \n"
" console.log('WebUI -> Connection lost (' + evt.code + ')'); \n"
" if(!_webui_log && evt.code != 1005) window.close(); \n"
" else _webui_freeze_ui(); \n"
" } \n"
" }; \n"
" _webui_ws.onmessage = function (evt) { \n"
" const buffer8 = new Uint8Array(evt.data); \n"
" if(buffer8.length < 4) return; \n"
" if(buffer8[0] !== _WEBUI_SIGNATURE) \n"
" return; \n"
" var len = buffer8.length - 3; \n"
" if(buffer8[buffer8.length - 1] === 0) \n"
" len--; // Null byte (0x00) can break eval() \n"
" data8 = new Uint8Array(len); \n"
" for (i = 0; i < len; i++) data8[i] = buffer8[i + 3]; \n"
" var data8utf8 = new TextDecoder('utf-8').decode(data8); \n"
" // Process Command \n"
" if(buffer8[1] === _WEBUI_SWITCH) { \n"
" _webui_close(_WEBUI_SWITCH, data8utf8); \n"
" } else if(buffer8[1] === _WEBUI_CLOSE) { \n"
" _webui_close(_WEBUI_CLOSE); \n"
" } else if(buffer8[1] === _WEBUI_JS_QUICK || buffer8[1] === _WEBUI_JS) { \n"
" data8utf8 = data8utf8.replace(/(?:\\r\\n|\\r|\\n)/g, \"\\\\n\"); \n"
" if(_webui_log) \n"
" console.log('WebUI -> JS [' + data8utf8 + ']'); \n"
" var FunReturn = 'undefined'; \n"
" var FunError = false; \n"
" try { FunReturn = eval('(() => {' + data8utf8 + '})()'); } catch (e) { FunError = true; FunReturn = e.message } \n"
" if(buffer8[1] === _WEBUI_JS_QUICK) return; \n"
" if(typeof FunReturn === 'undefined' || FunReturn === undefined) FunReturn = 'undefined'; \n"
" if(_webui_log && !FunError) console.log('WebUI -> JS -> Return [' + FunReturn + ']'); \n"
" if(_webui_log && FunError) console.log('WebUI -> JS -> Error [' + FunReturn + ']'); \n"
" var FunReturn8 = new TextEncoder('utf-8').encode(FunReturn); \n"
" var Return8 = new Uint8Array(4 + FunReturn8.length); \n"
" Return8[0] = _WEBUI_SIGNATURE; \n"
" Return8[1] = _WEBUI_JS; \n"
" Return8[2] = buffer8[2]; \n"
" if(FunError) Return8[3] = 0; \n"
" else Return8[3] = 1; \n"
" var p = -1; \n"
" for (i = 4; i < FunReturn8.length + 4; i++) Return8[i] = FunReturn8[++p]; \n"
" if(_webui_ws_status) _webui_ws.send(Return8.buffer); \n"
" } \n"
" }; \n"
" } else { \n"
" alert('Sorry. WebSocket not supported by your Browser.'); \n"
" if(!_webui_log) window.close(); \n"
" } \n"
"} \n"
"function _webui_clicks_listener() { \n"
" Object.keys(window).forEach(key=>{ \n"
" if(/^on(click)/.test(key)) { \n"
" window.addEventListener(key.slice(2),event=>{ \n"
" if(event.target.id !== '') { \n"
" if(_webui_has_events || _webui_bind_list.includes(_webui_win_num + '/' + event.target.id)) \n"
" _webui_send_click(event.target.id); \n"
" } \n"
" }); \n"
" } \n"
" }); \n"
"} \n"
"function _webui_send_click(elem) { \n"
" if(_webui_ws_status && elem !== '') { \n"
" var elem8 = new TextEncoder('utf-8').encode(elem); \n"
" var packet = new Uint8Array(3 + elem8.length); \n"
" packet[0] = _WEBUI_SIGNATURE; \n"
" packet[1] = _WEBUI_CLICK; \n"
" packet[2] = 0; \n"
" var p = -1; \n"
" for (i = 3; i < elem8.length + 3; i++) \n"
" packet[i] = elem8[++p]; \n"
" _webui_ws.send(packet.buffer); \n"
" if(_webui_log) \n"
" console.log('WebUI -> Click [' + elem + ']'); \n"
" } \n"
"} \n"
"function _webui_send_event_navigation(url) { \n"
" if(_webui_ws_status && url !== '') { \n"
" var url8 = new TextEncoder('utf-8').encode(url); \n"
" var packet = new Uint8Array(3 + url8.length); \n"
" packet[0] = _WEBUI_SIGNATURE; \n"
" packet[1] = _WEBUI_SWITCH; \n"
" packet[2] = 0; \n"
" var p = -1; \n"
" for (i = 3; i < url8.length + 3; i++) \n"
" packet[i] = url8[++p]; \n"
" _webui_ws.send(packet.buffer); \n"
" if(_webui_log) \n"
" console.log('WebUI -> Navigation [' + url + ']'); \n"
" } \n"
"} \n"
"function webui_is_external_link(url) { \n"
" const currentUrl = new URL(window.location.href); \n"
" const targetUrl = new URL(url, window.location.href); \n"
" currentUrl.hash = ''; \n"
" targetUrl.hash = ''; \n"
" if (url.startsWith('#') || url === currentUrl.href + '#' || currentUrl.href === targetUrl.href) { \n"
" return false; \n"
" } \n"
" return true; \n"
"} \n"
" // -- APIs -------------------------- \n"
"function webui_fn(fn, value) { \n"
" if(!_webui_has_events && !_webui_bind_list.includes(_webui_win_num + '/' + fn)) \n"
" return; \n"
" if(typeof value == 'undefined') \n"
" var value = ''; \n"
" var data = ''; \n"
" if(_webui_ws_status && fn !== '') { \n"
" if(_webui_log) \n"
" console.log('WebUI -> Func [' + fn + ']'); \n"
" var xmlHttp = new XMLHttpRequest(); \n"
" xmlHttp.open('GET', ('http://localhost:' + _webui_port + '/WEBUI/FUNC/' + fn + '/' + value), false); \n"
" xmlHttp.send(null); \n"
" if(xmlHttp.status == 200) \n"
" data = String(xmlHttp.responseText); \n"
" } \n"
" return data; \n"
"} \n"
"function webui_log(status) { \n"
" if(status) { \n"
" console.log('WebUI -> Log Enabled.'); \n"
" _webui_log = true; \n"
" } else { \n"
" console.log('WebUI -> Log Disabled.'); \n"
" _webui_log = false; \n"
" } \n"
"} \n"
" // -- DOM --------------------------- \n"
"document.addEventListener('keydown', function (e) { \n"
" // Disable F5 \n"
" if(e.keyCode === 116) { \n"
" e.preventDefault(); \n"
" e.returnValue = false; \n"
" e.keyCode = 0; \n"
" return false; \n"
" } \n"
"}); \n"
"window.onbeforeunload = function () { \n"
" _webui_ws.close(); \n"
"}; \n"
"setTimeout(function () { \n"
" if(!_webui_ws_status_once) { \n"
" _webui_freeze_ui(); \n"
" alert('WebUI failed to connect to the background application. Please try again.'); \n"
" if(!_webui_log) window.close(); \n"
" } \n"
"}, 1500); \n"
"window.addEventListener('unload', unload_handler, false); \n"
"function unload_handler() { \n"
" // Unload for 'back' & 'forward' navigation \n"
" window.removeEventListener('unload', unload_handler, false); \n"
"} \n"
"// Links \n"
"document.addEventListener('click', e => { \n"
" const attribute = e.target.closest('a'); \n"
" if(attribute) { \n"
" const link = attribute.href; \n"
" if(webui_is_external_link(link)) { \n"
" e.preventDefault(); \n"
" _webui_close(_WEBUI_SWITCH, link); \n"
" } \n"
" } \n"
"}); \n"
"if(typeof navigation !== 'undefined') { \n"
" navigation.addEventListener('navigate', (event) => { \n"
" const url = new URL(event.destination.url); \n"
" _webui_send_event_navigation(url); \n"
" }); \n"
"} \n"
"document.body.addEventListener('contextmenu', function(event){ event.preventDefault(); }); \n"
"var inputs = document.getElementsByTagName('input'); \n"
"for(var i = 0; i < inputs.length; i++){ inputs[i].addEventListener('contextmenu', function(event){ event.stopPropagation(); });} \n"
"// Load \n"
"window.addEventListener('load', _webui_start()); \n";
// -- Heap ----------------------------
static const char* webui_html_served = "<html><head><title>Access Denied</title><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>&#9888; Access Denied</h2><p>You can't access this content<br>because it's already processed.<br><br>The current security policy denies<br>multiple requests.</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><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>&#9888; 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_deno_not_found = "<html><head><title>Deno Not Found</title><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>&#9888; Deno Not Found</h2><p>Deno is not found on this system.<br>Please download it from <a href=\"https://github.com/denoland/deno/releases\">https://github.com/denoland/deno/releases</a></p><br><a href=\"https://www.webui.me\"><small>WebUI v" WEBUI_VERSION "<small></a></body></html>";
static const char* webui_nodejs_not_found = "<html><head><title>Node.js Not Found</title><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>&#9888; Node.js Not Found</h2><p>Node.js is not found on this system.<br>Please download it from <a href=\"https://nodejs.org/en/download/\">https://nodejs.org/en/download/</a></p><br><a href=\"https://www.webui.me\"><small>WebUI v" WEBUI_VERSION "<small></a></body></html>";
static const char* webui_def_icon = "<svg height=\"24\" width=\"24\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M3 2c-1.105 0-2 .9-2 2v16c0 1.1.895 2 2 2h18c1.105 0 2-.9 2-2V4c0-1.1-.895-2-2-2H3z\" fill=\"#2c3e50\"/><path d=\"M3 21c-1.105 0-2-.9-2-2V7h22v12c0 1.1-.895 2-2 2H3z\" fill=\"#34495e\"/><path d=\"M4 4.5v1.1L6 8l-2 2.3v1.1L7 8 4 4.5z\" fill=\"#ecf0f1\"/><path d=\"M3 2a2 2 0 0 0-2 2v12h22V4a2 2 0 0 0-2-2H3z\" fill=\"#34495e\"/><path d=\"M4 5.125V6.25L7 8 4 9.75v1.125L9 8 4 5.125zM9 10v1h5v-1H9z\" fill=\"#ecf0f1\"/></svg>";
static const char* webui_def_icon_type = "Content-Type: image/svg+xml\r\n";
static const char* webui_js_empty = "ERR_WEBUI_NO_SCRIPT_FOUND";
static const char* webui_js_timeout = "ERR_WEBUI_TIMEOUT";
static const char* const webui_empty_string = ""; // In case the compiler optimization is disabled
// -- Functions -----------------------
bool webui_run(void* window, const char* script) {
#ifdef WEBUI_LOG
printf("[User] webui_run([%s])... \n", script);
#endif
size_t js_len = strlen(script);
if(js_len < 1)
return false;
// Dereference
_webui_window_t* win = (_webui_window_t*)window;
// Initializing pipe
unsigned char run_id = _webui_get_run_id();
_webui_core.run_done[run_id] = false;
_webui_core.run_error[run_id] = false;
if((void *)_webui_core.run_responses[run_id] != NULL)
_webui_free_mem((void *)_webui_core.run_responses[run_id]);
// Prepare the packet
size_t packet_len = 3 + js_len; // [header][js]
char* packet = (char*) _webui_malloc(packet_len);
packet[0] = WEBUI_HEADER_SIGNATURE; // Signature
packet[1] = WEBUI_HEADER_JS_QUICK; // Type
packet[2] = run_id; // ID
for(unsigned int i = 0; i < js_len; i++) // Data
packet[i + 3] = script[i];
// Send packets
_webui_window_send(win, packet, packet_len);
_webui_free_mem((void *)packet);
return true;
}
bool webui_script(void* window, const char* script, unsigned int timeout_second, char* buffer, size_t buffer_length) {
// Dereference
_webui_window_t* win = (_webui_window_t*)window;
#ifdef WEBUI_LOG
printf("[User] webui_script()... \n");
printf("[User] webui_script() -> Script [%s] \n", script);
printf("[User] webui_script() -> Response Buffer @ 0x%p \n", buffer);
printf("[User] webui_script() -> Response Buffer Size %zu bytes \n", buffer_length);
#endif
_webui_init();
// Initializing response buffer
if(buffer_length > 0)
memset(buffer, 0, buffer_length);
size_t js_len = strlen(script);
if(js_len < 1) {
if(buffer != NULL && buffer_length > 1)
snprintf(buffer, buffer_length, "%s", webui_js_empty);
return false;
}
// Initializing pipe
unsigned char run_id = _webui_get_run_id();
_webui_core.run_done[run_id] = false;
_webui_core.run_error[run_id] = false;
if((void *)_webui_core.run_responses[run_id] != NULL)
_webui_free_mem((void *)_webui_core.run_responses[run_id]);
// Prepare the packet
size_t packet_len = 3 + js_len; // [header][js]
char* packet = (char*) _webui_malloc(packet_len);
packet[0] = WEBUI_HEADER_SIGNATURE; // Signature
packet[1] = WEBUI_HEADER_JS; // Type
packet[2] = run_id; // ID
for(unsigned int i = 0; i < js_len; i++) // Data
packet[i + 3] = script[i];
// Send packets
_webui_window_send(win, packet, packet_len);
_webui_free_mem((void *)packet);
// Wait for UI response
if(timeout_second < 1 || timeout_second > 86400) {
// Wait forever
for(;;) {
if(_webui_core.run_done[run_id])
break;
_webui_sleep(1);
}
}
else {
// Using timeout
for(unsigned int n = 0; n <= (timeout_second * 1000); n++) {
if(_webui_core.run_done[run_id])
break;
_webui_sleep(1);
}
}
if(_webui_core.run_responses[run_id] != NULL) {
#ifdef WEBUI_LOG
printf("[User] webui_script -> Response found [%s] \n", _webui_core.run_responses[run_id]);
#endif
// Response found
if(buffer != NULL && buffer_length > 1) {
// Copy response to the user's response buffer
size_t response_len = strlen(_webui_core.run_responses[run_id]) + 1;
size_t bytes_to_cpy = (response_len <= buffer_length ? response_len : buffer_length);
memcpy(buffer, _webui_core.run_responses[run_id], bytes_to_cpy);
}
_webui_free_mem((void *)_webui_core.run_responses[run_id]);
return _webui_core.run_error[run_id];
}
return false;
}
void* webui_new_window(void) {
#ifdef WEBUI_LOG
printf("[User] webui_new_window()... \n");
#endif
_webui_init();
_webui_window_t* win = (_webui_window_t*) _webui_malloc(sizeof(_webui_window_t));
// Initialisation
win->window_number = _webui_get_new_window_number();
win->browser_path = (char*) _webui_malloc(WEBUI_MAX_PATH);
win->profile_path = (char*) _webui_malloc(WEBUI_MAX_PATH);
win->server_root_path = (char*) _webui_malloc(WEBUI_MAX_PATH);
sprintf(win->server_root_path, "%s", WEBUI_DEFAULT_PATH);
#ifdef WEBUI_LOG
printf("[User] webui_new_window() -> New window @ 0x%p\n", win);
#endif
return (void*)win;
}
void webui_close(void* window) {
// Dereference
_webui_window_t* win = (_webui_window_t*)window;
#ifdef WEBUI_LOG
printf("[User] webui_close()... \n");
#endif
_webui_init();
if(win->connected) {
// Prepare packets
char* packet = (char*) _webui_malloc(4);
packet[0] = WEBUI_HEADER_SIGNATURE; // Signature
packet[1] = WEBUI_HEADER_CLOSE; // Type
packet[2] = 0; // ID
packet[3] = 0; // Data
// Send packets
_webui_window_send(win, packet, 4);
_webui_free_mem((void *)packet);
}
}
bool webui_is_shown(void* window) {
// Dereference
_webui_window_t* win = (_webui_window_t*)window;
#ifdef WEBUI_LOG
printf("[User] webui_is_shown()... \n");
#endif
return win->connected;
}
void webui_set_multi_access(void* window, bool status) {
// Dereference
_webui_window_t* win = (_webui_window_t*)window;
#ifdef WEBUI_LOG
printf("[User] webui_set_multi_access([%d])... \n", status);
#endif
win->multi_access = status;
}
void webui_set_icon(void* window, const char* icon, const char* type) {
// Dereference
_webui_window_t* win = (_webui_window_t*)window;
#ifdef WEBUI_LOG
printf("[User] webui_set_icon([%s], [%s])... \n", icon, type);
#endif
win->icon = icon;
win->icon_type = type;
}
bool webui_show(void* window, const char* content) {
// Dereference
_webui_window_t* win = (_webui_window_t*)window;
#ifdef WEBUI_LOG
printf("[User] webui_show()... \n");
#endif
// Find the best web browser to use
unsigned int browser = _webui_core.current_browser != 0 ? _webui_core.current_browser : _webui_find_the_best_browser(win);
// Show the window
return _webui_show(win, content, browser);
}
bool webui_show_browser(void* window, const char* content, unsigned int browser) {
// Dereference
_webui_window_t* win = (_webui_window_t*)window;
#ifdef WEBUI_LOG
printf("[User] webui_show_browser([%u])... \n", browser);
#endif
return _webui_show(win, content, browser);
}
unsigned int webui_bind(void* window, const char* element, void (*func)(webui_event_t* e)) {
// Dereference
_webui_window_t* win = (_webui_window_t*)window;
#ifdef WEBUI_LOG
printf("[User] webui_bind([%s], [0x%p])... \n", element, func);
#endif
_webui_init();
int len = 0;
if(_webui_is_empty(element))
win->has_events = true;
else
len = strlen(element);
// [win num][/][element]
char* webui_internal_id = _webui_malloc(3 + 1 + len);
sprintf(webui_internal_id, "%u/%s", win->window_number, element);
unsigned int cb_index = _webui_get_cb_index(webui_internal_id);
if(cb_index > 0) {
// Replace a reference
_webui_core.cb[cb_index] = func;
_webui_free_mem((void *)webui_internal_id);
}
else {
// New reference
cb_index = _webui_set_cb_index(webui_internal_id);
if(cb_index > 0)
_webui_core.cb[cb_index] = func;
else
_webui_free_mem((void *)webui_internal_id);
}
return cb_index;
}
const char* webui_get_string(webui_event_t* e) {
#ifdef WEBUI_LOG
printf("[User] webui_get_string()... \n");
#endif
if(e->data != NULL) {
size_t len = strlen(e->data);
if(len > 0 && len <= WEBUI_MAX_BUF)
return (const char *) e->data;
}
return webui_empty_string;
}
long long int webui_get_int(webui_event_t* e) {
#ifdef WEBUI_LOG
printf("[User] webui_get_int()... \n");
#endif
char *endptr;
if(e->data != NULL) {
size_t len = strlen(e->data);
if(len > 0 && len <= 20) // 64-bit max is -9,223,372,036,854,775,808 (20 character)
return strtoll((const char *) e->data, &endptr, 10);
}
return 0;
}
bool webui_get_bool(webui_event_t* e) {
#ifdef WEBUI_LOG
printf("[User] webui_get_bool()... \n");
#endif
const char* str = webui_get_string(e);
if(str[0] == 't' || str[0] == 'T') // true || True
return true;
return false;
}
void webui_return_int(webui_event_t* e, long long int n) {
#ifdef WEBUI_LOG
printf("[User] webui_return_int([%lld])... \n", n);
#endif
// Free
if(e->response != NULL)
_webui_free_mem(e->response);
// Int to Str
// 64-bit max is -9,223,372,036,854,775,808 (20 character)
char* buf = (char*) _webui_malloc(20);
sprintf(buf, "%lld", n);
// Set response
e->response = buf;
}
void webui_return_string(webui_event_t* e, char* s) {
#ifdef WEBUI_LOG
printf("[User] webui_return_string([%s])... \n", s);
#endif
if(_webui_is_empty(s))
return;
// Free
if(e->response != NULL)
_webui_free_mem(e->response);
// Copy Str
int len = strlen(s);
char* buf = (char*) _webui_malloc(len);
memcpy(buf, s, len);
// Set response
e->response = buf;
}
void webui_return_bool(webui_event_t* e, bool b) {
#ifdef WEBUI_LOG
printf("[User] webui_return_bool([%d])... \n", b);
#endif
// Free
if(e->response != NULL)
_webui_free_mem(e->response);
// Bool to Str
int len = 1;
char* buf = (char*) _webui_malloc(len);
sprintf(buf, "%d", b);
// Set response
e->response = buf;
}
void webui_exit(void) {
#ifdef WEBUI_LOG
printf("[User] webui_exit()... \n");
#endif
_webui_core.exit_now = true;
// Let's give other threads more time to
// safely exit and finish their cleaning up.
_webui_sleep(100);
}
void webui_wait(void) {
#ifdef WEBUI_LOG
printf("[Loop] webui_wait()... \n");
#endif
_webui_init();
if(_webui_core.startup_timeout > 0) {
#ifdef WEBUI_LOG
printf("[Loop] webui_wait() -> Using timeout %u second\n", _webui_core.startup_timeout);
#endif
// Wait for browser to start
_webui_wait_for_startup();
#ifdef WEBUI_LOG
printf("[Loop] webui_wait() -> Wait for connected UI...\n");
#endif
while(_webui_core.servers > 0) {
#ifdef WEBUI_LOG
// printf("[%u/%u]", _webui_core.servers, _webui_core.connections);
#endif
_webui_sleep(50);
}
}
else {
#ifdef WEBUI_LOG
printf("[Loop] webui_wait() -> Infinite wait...\n");
#endif
// Infinite wait
while(!_webui_core.exit_now)
_webui_sleep(50);
}
#ifdef WEBUI_LOG
printf("[Loop] webui_wait() -> Wait finished.\n");
#endif
// Final cleaning
_webui_clean();
}
void webui_set_timeout(unsigned int second) {
#ifdef WEBUI_LOG
printf("[User] webui_set_timeout([%u])... \n", second);
#endif
_webui_init();
if(second > WEBUI_MAX_TIMEOUT)
second = WEBUI_MAX_TIMEOUT;
_webui_core.startup_timeout = second;
}
void webui_set_runtime(void* window, unsigned int runtime) {
// Dereference
_webui_window_t* win = (_webui_window_t*)window;
#ifdef WEBUI_LOG
printf("[User] webui_script_runtime(%u)... \n", runtime);
#endif
_webui_init();
if(runtime != Deno && runtime != NodeJS)
win->runtime = None;
else
win->runtime = runtime;
}
// -- Interface's Functions ----------------
void _webui_interface_bind_handler(webui_event_t* e) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_interface_bind_handler()... \n");
#endif
// Generate WebUI internal id
char* webui_internal_id = _webui_generate_internal_id(e->window, e->element);
unsigned int cb_index = _webui_get_cb_index(webui_internal_id);
if(cb_index > 0 && _webui_core.cb_interface[cb_index] != NULL) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_interface_bind_handler() -> User callback @ 0x%p\n", _webui_core.cb_interface[cb_index]);
printf("[Core]\t\t_webui_interface_bind_handler() -> Response set @ 0x%p\n", (char*)&e->response);
printf("[Core]\t\t_webui_interface_bind_handler() -> type [%u]\n", e->type);
printf("[Core]\t\t_webui_interface_bind_handler() -> data [%s]\n", e->data);
printf("[Core]\t\t_webui_interface_bind_handler() -> element [%s]\n", e->element);
#endif
_webui_core.cb_interface[cb_index](e->window, e->type, e->element, e->data, (char*)&e->response);
}
if(_webui_is_empty((const char *)e->response))
e->response = (char*)webui_empty_string;
// Free
_webui_free_mem((void *)webui_internal_id);
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_interface_bind_handler() -> user-callback response [%s] @ 0x%p\n", (const char *)e->response, e->response);
#endif
}
unsigned int webui_interface_bind(void* window, const char* element, void (*func)(void*, unsigned int, char*, char*, char*)) {
// Dereference
_webui_window_t* win = (_webui_window_t*)window;
#ifdef WEBUI_LOG
printf("[User] webui_interface_bind()... \n");
#endif
// Bind
unsigned int cb_index = webui_bind(win, element, _webui_interface_bind_handler);
_webui_core.cb_interface[cb_index] = func;
return cb_index;
}
void webui_interface_set_response(char* ptr, const char* response) {
#ifdef WEBUI_LOG
printf("[User] webui_interface_set_response()... \n");
printf("[User] webui_interface_set_response() -> Pointer @ 0x%p \n", ptr);
printf("[User] webui_interface_set_response() -> Response [%s] \n", response);
#endif
size_t len = strlen(response);
if(len < 1)
return;
char* buf = (char*) _webui_malloc(len);
char** _ptr = (char**)ptr;
*_ptr = buf;
strcpy(*_ptr, response);
#ifdef WEBUI_LOG
printf("[User] webui_interface_set_response() -> Internal buffer @ 0x%p \n", (*_ptr));
printf("[User] webui_interface_set_response() -> Internal buffer [%s] \n", (*_ptr));
#endif
}
bool webui_interface_is_app_running(void) {
#ifdef WEBUI_LOG
// printf("[User] webui_is_app_running()... \n");
#endif
static bool app_is_running = true;
// Stop if already flagged
if(!app_is_running) return false;
// Initialization
if(!_webui_core.initialized)
_webui_init();
// Get app status
if(_webui_core.exit_now) {
app_is_running = false;
}
else if(_webui_core.startup_timeout > 0) {
if(_webui_core.servers < 1)
app_is_running = false;
}
// Final cleaning
if(!app_is_running) {
#ifdef WEBUI_LOG
printf("[User] webui_is_app_running() -> App Stopped.\n");
#endif
_webui_clean();
}
return app_is_running;
}
unsigned int webui_interface_get_window_id(void* window) {
#ifdef WEBUI_LOG
printf("[User] webui_interface_get_window_id()... \n");
#endif
// Dereference
_webui_window_t* win = (_webui_window_t*)window;
return win->window_number;
}
// -- Core's Functions ----------------
bool _webui_ptr_exist(void* ptr) {
#ifdef WEBUI_LOG
// printf("[Core]\t\t_webui_ptr_exist()... \n");
#endif
if(ptr == NULL)
return false;
for(unsigned int i = 0; i < _webui_core.ptr_position; i++) {
if(_webui_core.ptr_list[i] == ptr)
return true;
}
return false;
}
static void _webui_ptr_add(void* ptr, size_t size) {
#ifdef WEBUI_LOG
// printf("[Core]\t\t_webui_ptr_add(0x%p)... \n", ptr);
#endif
if(ptr == NULL)
return;
if(!_webui_ptr_exist(ptr)) {
for(unsigned int i = 0; i < _webui_core.ptr_position; i++) {
if(_webui_core.ptr_list[i] == NULL) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_ptr_add(0x%p)... Allocate %d bytes\n", ptr, (int)size);
#endif
_webui_core.ptr_list[i] = ptr;
_webui_core.ptr_size[i] = size;
return;
}
}
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_ptr_add(0x%p)... Allocate %d bytes\n", ptr, (int)size);
#endif
_webui_core.ptr_list[_webui_core.ptr_position] = ptr;
_webui_core.ptr_size[_webui_core.ptr_position] = size;
_webui_core.ptr_position++;
if(_webui_core.ptr_position >= WEBUI_MAX_ARRAY)
_webui_core.ptr_position = (WEBUI_MAX_ARRAY - 1);
}
}
static void _webui_free_mem(void* ptr) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_free_mem(0x%p)... \n", ptr);
#endif
if(ptr == NULL)
return;
for(unsigned int i = 0; i < _webui_core.ptr_position; i++) {
if(_webui_core.ptr_list[i] == ptr) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_free_mem(0x%p)... Free %d bytes\n", ptr, (int)_webui_core.ptr_size[i]);
#endif
memset(ptr, 0, _webui_core.ptr_size[i]);
free(ptr);
_webui_core.ptr_size[i] = 0;
_webui_core.ptr_list[i] = NULL;
}
}
for(int i = _webui_core.ptr_position; i >= 0; i--) {
if(_webui_core.ptr_list[i] == NULL) {
_webui_core.ptr_position = i;
break;
}
}
}
static void _webui_free_all_mem(void) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_free_all_mem()... \n");
#endif
// Makes sure we run this once
static bool freed = false;
if(freed) return;
freed = true;
void* ptr = NULL;
for(unsigned int i = 0; i < _webui_core.ptr_position; i++) {
ptr = _webui_core.ptr_list[i];
if(ptr != NULL) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_free_all_mem()... Free %d bytes @ 0x%p\n", (int)_webui_core.ptr_size[i], ptr);
#endif
memset(ptr, 0, _webui_core.ptr_size[i]);
free(ptr);
}
}
}
static void _webui_panic(void) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_panic()... \n");
#endif
webui_exit();
exit(EXIT_FAILURE);
}
size_t _webui_round_to_memory_block(int size) {
// If size is negative
if(size < 4)
size = 4;
// If size is already a full block
// we should return the same block
size--;
int block_size = 4;
while (block_size <= size)
block_size *= 2;
return (size_t)block_size;
}
void* _webui_malloc(int size) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_malloc([%d])... \n", size);
#endif
// Make sure we have the null
// terminator if it's a string
size++;
size = _webui_round_to_memory_block(size);
void* block = NULL;
for(unsigned int i = 0; i < 8; i++) {
if(size > WEBUI_MAX_BUF)
size = WEBUI_MAX_BUF;
block = malloc(size);
if(block == NULL)
size++;
else
break;
}
if(block == NULL) {
_webui_panic();
return NULL;
}
memset(block, 0, size);
_webui_ptr_add((void *) block, size);
return block;
}
static void _webui_sleep(long unsigned int ms) {
#ifdef WEBUI_LOG
// printf("[Core]\t\t_webui_sleep([%u])... \n", ms);
#endif
#ifdef _WIN32
Sleep(ms);
#else
usleep(ms);
#endif
}
long _webui_timer_diff(struct timespec *start, struct timespec *end) {
#ifdef WEBUI_LOG
// 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
// 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
// printf("[Core]\t\t_webui_timer_start()... \n");
#endif
_webui_timer_clock_gettime(&t->start);
}
bool _webui_timer_is_end(_webui_timer_t* t, unsigned int ms) {
#ifdef WEBUI_LOG
// printf("[Core]\t\t_webui_timer_is_end()... \n");
#endif
_webui_timer_clock_gettime(&t->now);
unsigned int def = (unsigned int) _webui_timer_diff(&t->start, &t->now);
if(def > ms)
return true;
return false;
}
bool _webui_is_empty(const char* s) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_is_empty()... \n");
#endif
if((s != NULL) && (s[0] != '\0'))
return false;
return true;
}
bool _webui_file_exist_mg(void *ev_data) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_file_exist_mg()... \n");
#endif
char* file;
char* full_path;
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
// Get file name
file = (char*) _webui_malloc(hm->uri.len);
const char* p = hm->uri.ptr;
p++; // Skip "/"
sprintf(file, "%.*s", (int)(hm->uri.len - 1), p);
// Get full path
// [current folder][/][file]
full_path = (char*) _webui_malloc(strlen(_webui_core.executable_path) + 1 + strlen(file));
sprintf(full_path, "%s%s%s", _webui_core.executable_path, webui_sep, file);
bool exist = _webui_file_exist(full_path);
_webui_free_mem((void *)file);
_webui_free_mem((void *)full_path);
return exist;
}
bool _webui_file_exist(char* file) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_file_exist([%s])... \n", file);
#endif
if(_webui_is_empty(file))
return false;
if(WEBUI_FILE_EXIST(file, 0) == 0)
return true;
return false;
}
const char* _webui_get_extension(const char*f) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_get_extension()... \n");
#endif
if(f == NULL)
return webui_empty_string;
const char*ext = strrchr(f, '.');
if(ext == NULL || !ext || ext == f)
return webui_empty_string;
return ext + 1;
}
unsigned char _webui_get_run_id(void) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_get_run_id()... \n");
#endif
return ++_webui_core.run_last_id;
}
bool _webui_socket_test_listen_mg(unsigned int port_num) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_socket_test_listen_mg([%u])... \n", port_num);
#endif
struct mg_connection *c;
struct mg_mgr mgr;
mg_mgr_init(&mgr);
char url[32];
sprintf(url, "http://localhost:%u", port_num);
if((c = mg_http_listen(&mgr, url, NULL, &mgr)) == NULL) {
// Cannot listen
mg_mgr_free(&mgr);
return false;
}
// Listening success
mg_mgr_free(&mgr);
return true;
}
bool _webui_port_is_used(unsigned int port_num) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_port_is_used([%u])... \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 void _webui_serve_file(_webui_window_t* win, struct mg_connection *c, void *ev_data) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_serve_file()... \n");
#endif
// Serve a normal text based file
// send with HTTP 200 status code
struct mg_http_serve_opts opts = {
.root_dir = win->server_root_path
};
mg_http_serve_dir(c, ev_data, &opts);
}
bool _webui_deno_exist(void) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_deno_exist()... \n");
#endif
static bool found = false;
if(found)
return true;
if(_webui_cmd_sync("deno --version", false) == 0) {
found = true;
return true;
}
else
return false;
}
bool _webui_nodejs_exist(void) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_nodejs_exist()... \n");
#endif
static bool found = false;
if(found)
return true;
if(_webui_cmd_sync("node -v", false) == 0) {
found = true;
return true;
}
else
return false;
}
const char* _webui_interpret_command(const char* cmd) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_interpret_command()... \n");
#endif
// Redirect stderr to stdout
char cmd_redirected[1024];
sprintf(cmd_redirected, "%s 2>&1", cmd);
FILE *runtime = WEBUI_POPEN(cmd_redirected, "r");
if(runtime == NULL)
return NULL;
// Get STDOUT length
// int c;
// while ((c = fgetc(runtime)) != EOF)
// len++;
int len = 1024 * 8;
// Read STDOUT
char* out = (char*) _webui_malloc(len);
char* line = (char*) _webui_malloc(4000);
while(fgets(line, 4000, runtime) != NULL)
strcat(out, line);
WEBUI_PCLOSE(runtime);
_webui_free_mem((void *)line);
return (const char*) out;
}
static void _webui_interpret_file(_webui_window_t* win, struct mg_connection *c, void *ev_data, char* index) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_interpret_file()... \n");
#endif
// Run the JavaScript / TypeScript runtime
// and send back the output with HTTP 200 status code
// otherwise, send the file as a normal text based one
char* file;
char* full_path;
if(index != NULL && !_webui_is_empty(index)) {
// Parse index file
file = index;
full_path = index;
}
else {
// Parse other files
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
// Get file name
file = (char*) _webui_malloc(hm->uri.len);
const char* p = hm->uri.ptr;
p++; // Skip "/"
sprintf(file, "%.*s", (int)(hm->uri.len - 1), p);
// Get full path
// [current folder][/][file]
full_path = (char*) _webui_malloc(strlen(_webui_core.executable_path) + 1 + strlen(file));
sprintf(full_path, "%s%s%s", _webui_core.executable_path, webui_sep, file);
if(!_webui_file_exist(full_path)) {
// File not exist - 404
_webui_serve_file(win, c, ev_data);
_webui_free_mem((void *)file);
_webui_free_mem((void *)full_path);
return;
}
}
// Get file extension
const char* extension = _webui_get_extension(file);
if(strcmp(extension, "ts") == 0 || strcmp(extension, "js") == 0) {
// TypeScript / JavaScript
if(win->runtime == Deno) {
// Use Deno
if(_webui_deno_exist()) {
// Set command
// [disable coloring][file]
char* cmd = (char*) _webui_malloc(64 + strlen(full_path));
#ifdef _WIN32
sprintf(cmd, "Set NO_COLOR=1 & deno run --allow-all \"%s\"", full_path);
#else
sprintf(cmd, "NO_COLOR=1 & deno run --allow-all \"%s\"", full_path);
#endif
// Run command
const char* out = _webui_interpret_command(cmd);
if(out != NULL) {
// Send deno output
mg_http_reply(
c, 200,
"",
out
);
}
else {
// Deno failed.
// Serve as a normal text-based file
_webui_serve_file(win, c, ev_data);
}
_webui_free_mem((void *)cmd);
_webui_free_mem((void *)out);
}
else {
// Deno not installed
mg_http_reply(
c, 200,
"",
webui_deno_not_found
);
}
}
else if(win->runtime == NodeJS) {
// Use Nodejs
if(_webui_nodejs_exist()) {
// Set command
// [node][file]
char* cmd = (char*) _webui_malloc(16 + strlen(full_path));
sprintf(cmd, "node \"%s\"", full_path);
// Run command
const char* out = _webui_interpret_command(cmd);
if(out != NULL) {
// Send Node.js output
mg_http_reply(
c, 200,
"",
out
);
}
else {
// Node.js failed.
// Serve as a normal text-based file
_webui_serve_file(win, c, ev_data);
}
_webui_free_mem((void *)cmd);
_webui_free_mem((void *)out);
}
else {
// Node.js not installed
mg_http_reply(
c, 200,
"",
webui_nodejs_not_found
);
}
}
else {
// Unknown runtime
// Serve as a normal text-based file
_webui_serve_file(win, c, ev_data);
}
}
else {
// Unknown file extension
// Serve as a normal text-based file
_webui_serve_file(win, c, ev_data);
}
_webui_free_mem((void *)file);
_webui_free_mem((void *)full_path);
}
const char* _webui_generate_js_bridge(_webui_window_t* win) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_generate_js_bridge()... \n");
#endif
// Calculate the cb size
size_t cb_mem_size = 64; // To hold 'const _webui_bind_list = ["elem1", "elem2",];'
for(unsigned int i = 1; i < WEBUI_MAX_ARRAY; i++)
if(_webui_core.html_elements[i] != NULL && !_webui_is_empty(_webui_core.html_elements[i]))
cb_mem_size += strlen(_webui_core.html_elements[i]) + 3;
// Generate the cb array
char* event_cb_js_array = (char*) _webui_malloc(cb_mem_size);
strcat(event_cb_js_array, "const _webui_bind_list = [");
for(unsigned int i = 1; i < WEBUI_MAX_ARRAY; i++) {
if(_webui_core.html_elements[i] != NULL && !_webui_is_empty(_webui_core.html_elements[i])) {
strcat(event_cb_js_array, "\"");
strcat(event_cb_js_array, _webui_core.html_elements[i]);
strcat(event_cb_js_array, "\",");
}
}
strcat(event_cb_js_array, "]; \n");
// Generate the full WebUI JS-Bridge
size_t len = cb_mem_size + strlen(webui_javascript_bridge);
char* js = (char*) _webui_malloc(len);
sprintf(js,
"_webui_port = %u; \n_webui_win_num = %u; \n%s \n%s \n",
win->server_port, win->window_number, event_cb_js_array, webui_javascript_bridge
);
return js;
}
static void _webui_server_event_handler(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
#ifdef WEBUI_LOG
// printf("[Core]\t\t_webui_server_event_handler()... \n");
#endif
_webui_window_t* win = (_webui_window_t *) fn_data;
if(ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if(mg_http_match_uri(hm, "/_webui_ws_connect")) {
// WebSocket
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_server_event_handler()... HTML Upgrade to WebSocket\n");
#endif
mg_ws_upgrade(c, hm, NULL);
}
else if(mg_http_match_uri(hm, "/webui.js")) {
// WebUI JS-Bridge
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_server_event_handler()... HTML WebUI JS\n");
#endif
// Generate JavaScript bridge
const char* js = _webui_generate_js_bridge(win);
// Header
// Content-Type: text/javascript
// Send
mg_http_reply(
c, 200,
"",
js
);
_webui_free_mem((void *)js);
}
else if(strncmp(hm->uri.ptr, "/WEBUI/FUNC/", 12) == 0 && hm->uri.len >= 15) {
// Function Call (With response)
// [/WEBUI/FUNC/ELEMENT_ID/DATA]
// 0 12
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_server_event_handler()... CB start\n");
#endif
// Copy packet
size_t len = hm->uri.len;
char* packet = (char*) _webui_malloc(len);
memcpy(packet, hm->uri.ptr, len);
// Get html element id
char* element = &packet[12];
size_t element_len = 0;
for (size_t i = 12; i < len; i++) {
if(packet[i] == '/') {
packet[i] = '\0';
break;
}
element_len++;
}
// [/WEBUI/FUNC/ELEMENT_ID DATA]
// 0 12
// Get data
void* data = &packet[11 + element_len + 2];
size_t data_len = strlen(data);
// Generate WebUI internal id
char* webui_internal_id = _webui_generate_internal_id(win, element);
// Call user function
webui_event_t e;
e.element = element;
e.window = win;
e.data = data;
e.response = NULL;
e.type = WEBUI_EVENT_CALLBACK;
unsigned int cb_index = _webui_get_cb_index(webui_internal_id);
// Check for bind
if(cb_index > 0 && _webui_core.cb[cb_index] != NULL) {
// Call user cb
_webui_core.cb[cb_index](&e);
}
if(_webui_is_empty(e.response))
e.response = (char*)webui_empty_string;
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_server_event_handler()... user-callback response [%s]\n", (const char*)e.response);
#endif
// Send response
mg_http_reply(
c, 200,
"",
e.response
);
// Free
_webui_free_mem((void *)packet);
_webui_free_mem((void *)webui_internal_id);
_webui_free_mem((void *)e.response);
}
else if(mg_http_match_uri(hm, "/")) {
// [/]
if(win->is_embedded_html) {
// Main HTML
if(!win->multi_access && win->html_handled) {
// Main HTML already handled.
// Forbidden 403
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_server_event_handler()... HTML Main Already Handled (403)\n");
#endif
// Header
// text/html; charset=utf-8
mg_http_reply(
c, 403,
"",
webui_html_served
);
}
else {
// Send main HTML
win->html_handled = true;
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_server_event_handler()... HTML Main\n");
#endif
char* html = (char*) webui_empty_string;
if(win->html != NULL) {
// Generate the full WebUI JS-Bridge
const char* js = _webui_generate_js_bridge(win);
// Inject WebUI JS-Bridge into HTML
size_t len = strlen(win->html) + strlen(js) + 128;
html = (char*) _webui_malloc(len);
sprintf(html,
"%s \n <script type = \"text/javascript\"> \n %s \n </script>",
win->html, js
);
_webui_free_mem((void *)js);
}
// // HTTP Header
// char header[512];
// memset(header, 0x00, 512);
// sprintf(header,
// "HTTP/1.1 200 OK\r\n"
// "Content-Type: text/html; charset=utf-8\r\n"
// "Host: localhost:%d\r\n"
// "Cache-Control: no-cache\r\n"
// "Content-Length: %d\r\n"
// "Connection: close\r\n\r\n",
// win->server_port, strlen(html)
// );
// Send
mg_http_reply(
c, 200,
"",
html
);
_webui_free_mem((void *)html);
}
}
else {
// Serve local files
win->html_handled = true;
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_server_event_handler()... HTML Root Index\n");
#endif
// Set full path
// [Path][Sep][File Name]
char* index = (char*) _webui_malloc(strlen(_webui_core.executable_path) + 1 + 8);
// Index.ts
sprintf(index, "%s%sindex.ts", _webui_core.executable_path, webui_sep);
if(_webui_file_exist(index)) {
// TypeScript Index
_webui_interpret_file(win, c, ev_data, index);
_webui_free_mem((void *)index);
return;
}
// Index.js
sprintf(index, "%s%sindex.js", _webui_core.executable_path, webui_sep);
if(_webui_file_exist(index)) {
// JavaScript Index
_webui_interpret_file(win, c, ev_data, index);
_webui_free_mem((void *)index);
return;
}
_webui_free_mem((void *)index);
// Index.html
// Serve as a normal HTML text-based file
_webui_serve_file(win, c, ev_data);
}
}
else if(mg_http_match_uri(hm, "/favicon.ico") || mg_http_match_uri(hm, "/favicon.svg")) {
// Favicon
if(win->icon != NULL && win->icon_type != NULL) {
// Custom user icon
char* icon_header = (char*) _webui_malloc(strlen(win->icon_type) + 32);
sprintf(icon_header, "Content-Type: %s\r\n", win->icon_type);
// User icon
mg_http_reply(
c, 200,
icon_header,
win->icon
);
}
else if(_webui_file_exist_mg(ev_data)) {
// Local icon file
_webui_serve_file(win, c, ev_data);
}
else {
// Default embedded icon
if(mg_http_match_uri(hm, "/favicon.ico")) {
mg_http_reply(c, 302, "Location: /favicon.svg\r\n", "");
}
else {
// TODO: Use webui_def_icon_type
// Header
// Content-Type: image/svg+xml
// Default icon
mg_http_reply(
c, 200,
webui_def_icon_type,
webui_def_icon
);
}
}
}
else {
// [/file]
if(win->is_embedded_html) {
if(win->runtime != None) {
// Interpret file
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_server_event_handler()... HTML Interpret file\n");
#endif
_webui_interpret_file(win, c, ev_data, NULL);
}
else {
// Serve local files
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_server_event_handler()... HTML Root file\n");
#endif
// Serve as a normal text-based file
_webui_serve_file(win, c, ev_data);
}
}
else {
// This is a non-server-folder mode
// but the HTML body request a local file
// this request can be css, js, image, etc...
if(_webui_file_exist_mg(ev_data)) {
// Serve as a normal text-based file
_webui_serve_file(win, c, ev_data);
}
else {
// 404
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_server_event_handler()... HTML 404\n");
#endif
// Header
// text/html; charset=utf-8
mg_http_reply(
c, 404,
"",
webui_html_res_not_available
);
}
}
}
}
else if(ev == MG_EV_WS_MSG) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_server_event_handler()... WebSocket Data\n");
#endif
struct mg_ws_message *wm = (struct mg_ws_message *) ev_data;
// Parse the packet
_webui_window_receive(win, wm->data.ptr, wm->data.len);
}
else if(ev == MG_EV_WS_OPEN) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_server_event_handler()... WebSocket Connected\n");
#endif
int event_type = WEBUI_EVENT_CONNECTED;
if(!win->connected) {
// First connection
win->connected = true; // server thread
_webui_core.connections++; // main loop
_webui_core.mg_connections[win->window_number] = c; // websocket send func
}
else {
if(win->multi_access) {
// Multi connections
win->connections++;
event_type = WEBUI_EVENT_MULTI_CONNECTION;
}
else {
// UNWANTED Multi connections
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_server_event_handler() -> UNWANTED Multi Connections\n");
#endif
mg_close_conn(c);
event_type = WEBUI_EVENT_UNWANTED_CONNECTION;
}
}
// Events
if(win->has_events) {
// Generate WebUI internal id
char* webui_internal_id = _webui_generate_internal_id(win, "");
_webui_window_event(
win, // Window
webui_internal_id, // WebUI Internal ID
"", // User HTML ID
NULL, // User Custom Data
0, // User Data Len
event_type // Type of this event
);
}
}
else if(ev == MG_EV_WS_CTL) {
win->html_handled = false;
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_server_event_handler()... WebSocket Closed\n");
#endif
if(win->connected) {
if(win->multi_access && win->connections > 0) {
// Multi connections close
win->connections--;
}
else {
// Main connection close
_webui_core.connections--; // main loop
win->connected = false; // server thread
}
}
// Events
if(win->has_events) {
// Generate WebUI internal id
char* webui_internal_id = _webui_generate_internal_id(win, "");
_webui_window_event(
win, // Window
webui_internal_id, // WebUI Internal ID
"", // User HTML ID
NULL, // User Custom Data
0, // User Data Len
WEBUI_EVENT_DISCONNECTED // Type of this event
);
}
}
}
bool _webui_browser_create_profile_folder(_webui_window_t* win, unsigned int browser) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_browser_create_profile_folder(%u)... \n", browser);
#endif
const char* temp = _webui_browser_get_temp_path(browser);
if(browser == Chrome) {
// Google Chrome
sprintf(win->profile_path, "%s%s.WebUI%sWebUIChromeProfile", temp, webui_sep, webui_sep);
return true;
}
else if(browser == Edge) {
// Edge
sprintf(win->profile_path, "%s%s.WebUI%sWebUIEdgeProfile", temp, webui_sep, webui_sep);
return true;
}
else if(browser == Epic) {
// Epic
sprintf(win->profile_path, "%s%s.WebUI%sWebUIEpicProfile", temp, webui_sep, webui_sep);
return true;
}
else if(browser == Vivaldi) {
// Vivaldi
sprintf(win->profile_path, "%s%s.WebUI%sWebUIVivaldiProfile", temp, webui_sep, webui_sep);
return true;
}
else if(browser == Brave) {
// Brave
sprintf(win->profile_path, "%s%s.WebUI%sWebUIBraveProfile", temp, webui_sep, webui_sep);
return true;
}
else if(browser == Yandex) {
// Yandex
sprintf(win->profile_path, "%s%s.WebUI%sWebUIYandexProfile", temp, webui_sep, webui_sep);
return true;
}
else if(browser == Chromium) {
// Chromium
sprintf(win->profile_path, "%s%s.WebUI%sWebUIChromiumProfile", temp, webui_sep, webui_sep);
return true;
}
else if(browser == Firefox) {
// Firefox (We need to create a folder)
char* profile_name = "WebUIFirefoxProfile";
char firefox_profile_path[1024];
sprintf(firefox_profile_path, "%s%s.WebUI%s%s", temp, webui_sep, webui_sep, profile_name);
if(!_webui_folder_exist(firefox_profile_path)) {
char buf[2048];
sprintf(buf, "%s -CreateProfile \"WebUI %s\"", win->browser_path, firefox_profile_path);
_webui_cmd_sync(buf, false);
// Creating the browser profile
for(unsigned int n = 0; n <= (_webui_core.startup_timeout * 4); n++) {
if(_webui_folder_exist(firefox_profile_path))
break;
_webui_sleep(250);
}
if(!_webui_folder_exist(firefox_profile_path))
return false;
// prefs.js
FILE *file;
sprintf(buf, "%s%sprefs.js", firefox_profile_path, webui_sep);
file = fopen(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);
fclose(file);
// userChrome.css
sprintf(buf, "\"%s%schrome%s\"", firefox_profile_path, webui_sep, webui_sep);
if(!_webui_folder_exist(buf)) {
sprintf(buf, "mkdir \"%s%schrome%s\"", firefox_profile_path, webui_sep, webui_sep);
_webui_cmd_sync(buf, false); // Create directory
}
sprintf(buf, "%s%schrome%suserChrome.css", firefox_profile_path, webui_sep, webui_sep);
file = fopen(buf, "a");
if(file == NULL)
return false;
#ifdef _WIN32
fputs(":root{--uc-toolbar-height:32px}:root:not([uidensity=\"compact\"]) {--uc-toolbar-height:38px}#TabsToolbar{visibility:collapse!important}:root:not([inFullscreen]) #nav-bar{margin-top:calc(0px - var(--uc-toolbar-height))}#toolbar-menubar{min-height:unset!important;height:var(--uc-toolbar-height)!important;position:relative}#main-menubar{-moz-box-flex:1;background-color:var(--toolbar-bgcolor,--toolbar-non-lwt-bgcolor);background-clip:padding-box;border-right:30px solid transparent;border-image:linear-gradient(to left,transparent,var(--toolbar-bgcolor,--toolbar-non-lwt-bgcolor) 30px) 20 / 30px}#toolbar-menubar:not([inactive]) {z-index:2}#toolbar-menubar[inactive] > #menubar-items{opacity:0;pointer-events:none;margin-left:var(--uc-window-drag-space-width,0px)}#nav-bar{visibility:collapse}@-moz-document url(chrome://browser/content/browser.xhtml) {:root:not([sizemode=\"fullscreen\"]) > head{display: block;position: fixed;width: calc(200vw - 440px);text-align: left;z-index: 9;pointer-events: none;}head > *{ display: none }head > title{display: -moz-inline-box;padding: 4px;max-width: 50vw;overflow-x: hidden;text-overflow: ellipsis;}}", file);
#elif __APPLE__
fputs(":root{--uc-toolbar-height:32px}:root:not([uidensity=\"compact\"]) {--uc-toolbar-height:38px}#TabsToolbar{visibility:collapse!important}:root:not([inFullscreen]) #nav-bar{margin-top:calc(0px - var(--uc-toolbar-height))}#toolbar-menubar{min-height:unset!important;height:var(--uc-toolbar-height)!important;position:relative}#main-menubar{-moz-box-flex:1;background-color:var(--toolbar-bgcolor,--toolbar-non-lwt-bgcolor);background-clip:padding-box;border-right:30px solid transparent;border-image:linear-gradient(to left,transparent,var(--toolbar-bgcolor,--toolbar-non-lwt-bgcolor) 30px) 20 / 30px}#toolbar-menubar:not([inactive]) {z-index:2}#toolbar-menubar[inactive] > #menubar-items{opacity:0;pointer-events:none;margin-left:var(--uc-window-drag-space-width,0px)}#nav-bar{visibility:collapse}@-moz-document url(chrome://browser/content/browser.xhtml) {:root:not([sizemode=\"fullscreen\"]) > head{display: block;position: fixed;width: calc(200vw - 440px);text-align: left;z-index: 9;pointer-events: none;}head > *{ display: none }head > title{display: -moz-inline-box;padding: 4px;max-width: 50vw;overflow-x: hidden;text-overflow: ellipsis;}}", file);
#else
fputs(":root{--uc-toolbar-height:32px}:root:not([uidensity=\"compact\"]) {--uc-toolbar-height:38px}#TabsToolbar{visibility:collapse!important}:root:not([inFullscreen]) #nav-bar{margin-top:calc(0px - var(--uc-toolbar-height))}#toolbar-menubar{min-height:unset!important;height:var(--uc-toolbar-height)!important;position:relative}#main-menubar{-moz-box-flex:1;background-color:var(--toolbar-bgcolor,--toolbar-non-lwt-bgcolor);background-clip:padding-box;border-right:30px solid transparent;border-image:linear-gradient(to left,transparent,var(--toolbar-bgcolor,--toolbar-non-lwt-bgcolor) 30px) 20 / 30px}#toolbar-menubar:not([inactive]) {z-index:2}#toolbar-menubar[inactive] > #menubar-items{opacity:0;pointer-events:none;margin-left:var(--uc-window-drag-space-width,0px)}#nav-bar{visibility:collapse}@-moz-document url(chrome://browser/content/browser.xhtml) {:root:not([sizemode=\"fullscreen\"]) > head{display: block;position: fixed;width: calc(200vw - 440px);text-align: left;z-index: 9;pointer-events: none;}head > *{ display: none }head > title{display: -moz-inline-box;padding: 4px;max-width: 50vw;overflow-x: hidden;text-overflow: ellipsis;}}", file);
#endif
fclose(file);
sprintf(win->profile_path, "%s%s%s", temp, webui_sep, profile_name);
}
return true;
}
return false;
}
bool _webui_folder_exist(char* folder) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_folder_exist([%s])... \n", folder);
#endif
#if defined(_MSC_VER)
if(GetFileAttributesA(folder) != INVALID_FILE_ATTRIBUTES)
return true;
#else
DIR* dir = opendir(folder);
if(dir) {
closedir(dir);
return true;
}
#endif
return false;
}
char* _webui_generate_internal_id(_webui_window_t* win, const char* element) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_generate_internal_id([%s])... \n", element);
#endif
// Generate WebUI internal id
size_t element_len = strlen(element);
size_t internal_id_size = 3 + 1 + element_len; // [win num][/][name]
char* webui_internal_id = (char*) _webui_malloc(internal_id_size);
sprintf(webui_internal_id, "%u/%s", win->window_number, element);
return webui_internal_id;
}
const char* _webui_browser_get_temp_path(unsigned int browser) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_browser_get_temp_path([%u])... \n", browser);
#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 webui_empty_string;
#else
char* WinUserProfile = getenv("USERPROFILE");
if(WinUserProfile == NULL)
return webui_empty_string;
#endif
#elif __APPLE__
// Resolve $HOME
char* MacUserProfile = getenv("HOME");
if(MacUserProfile == NULL)
return webui_empty_string;
#else
// Resolve $HOME
char* LinuxUserProfile = getenv("HOME");
if(LinuxUserProfile == NULL)
return webui_empty_string;
#endif
#ifdef _WIN32
return WinUserProfile;
#elif __APPLE__
return MacUserProfile;
#else
return LinuxUserProfile;
#endif
}
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
sprintf(browser_full_path, "%s\\master_preferences", folder);
if(!_webui_file_exist(browser_full_path)) {
sprintf(browser_full_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
sprintf(browser_full_path, "%s\\chrome.exe", folder);
if(!_webui_file_exist(browser_full_path))
return false;
return true;
}
bool _webui_browser_exist(_webui_window_t* win, unsigned int browser) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_browser_exist([%u])... \n", browser);
#endif
// Check if a web browser is installed on this machine
if(browser == Chrome) {
// Google Chrome
static bool ChromeExist = false;
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, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe", "Path", browser_folder)) {
// Make sure its Google Chrome and not Chromium
if(_webui_is_google_chrome_folder(browser_folder)) {
// Google Chrome Found (multi-user)
sprintf(win->browser_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, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe", "Path", browser_folder)) {
// Make sure its Google Chrome and not Chromium
if(_webui_is_google_chrome_folder(browser_folder)) {
// Google Chrome Found (one user)
sprintf(win->browser_path, "\"%s\\chrome.exe\"", browser_folder);
ChromeExist = true;
return true;
}
}
return false;
#elif __APPLE__
// Google Chrome on macOS
if(_webui_cmd_sync("open -R -a \"Google Chrome\"", false) == 0) {
sprintf(win->browser_path, "open -W \"/Applications/Google Chrome.app\" --args");
ChromeExist = true;
return true;
}
else
return false;
#else
// Google Chrome on Linux
if(_webui_cmd_sync("google-chrome --version", false) == 0) {
sprintf(win->browser_path, "google-chrome");
ChromeExist = true;
return true;
}
else if(_webui_cmd_sync("google-chrome-stable --version", false) == 0) {
sprintf(win->browser_path, "google-chrome-stable");
ChromeExist = true;
return true;
}
else
return false;
#endif
}
else if(browser == Edge) {
// Edge
static bool EdgeExist = false;
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, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\msedge.exe", "", browser_fullpath)) {
// Make sure the browser executable file exist
if(_webui_file_exist(browser_fullpath)) {
// Edge Found (multi-user)
sprintf(win->browser_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, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\msedge.exe", "", browser_fullpath)) {
// Make sure the browser executable file exist
if(_webui_file_exist(browser_fullpath)) {
// Edge Found (one user)
sprintf(win->browser_path, "\"%s\"", browser_fullpath);
EdgeExist = true;
return true;
}
}
return false;
#elif __APPLE__
// Edge on macOS
return false;
#else
// Edge on Linux
if(_webui_cmd_sync("microsoft-edge-stable --version", false) == 0) {
sprintf(win->browser_path, "microsoft-edge-stable");
EdgeExist = true;
return true;
}
else if(_webui_cmd_sync("microsoft-edge-dev --version", false) == 0) {
sprintf(win->browser_path, "microsoft-edge-dev");
EdgeExist = true;
return true;
}
else
return false;
#endif
}
else if(browser == Epic) {
// Epic Privacy Browser
static bool EpicExist = false;
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, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\epic.exe", "", browser_fullpath)) {
// Make sure the browser executable file exist
if(_webui_file_exist(browser_fullpath)) {
// Epic Found (one user)
sprintf(win->browser_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, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\epic.exe", "", browser_fullpath)) {
// Make sure the browser executable file exist
if(_webui_file_exist(browser_fullpath)) {
// Epic Found (multi-user)
sprintf(win->browser_path, "\"%s\"", browser_fullpath);
EpicExist = true;
return true;
}
}
return false;
#elif __APPLE__
// Epic on macOS
if(_webui_cmd_sync("open -R -a \"Epic\"", false) == 0) {
sprintf(win->browser_path, "open -W \"/Applications/Epic.app\" --args");
EpicExist = true;
return true;
}
else
return false;
#else
// Epic on Linux
if(_webui_cmd_sync("epic --version", false) == 0) {
sprintf(win->browser_path, "epic");
EpicExist = true;
return true;
}
else
return false;
#endif
}
else if(browser == Vivaldi) {
// Vivaldi Browser
static bool VivaldiExist = false;
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, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\vivaldi.exe", "", browser_fullpath)) {
// Make sure the browser executable file exist
if(_webui_file_exist(browser_fullpath)) {
// Vivaldi Found (multi-user)
sprintf(win->browser_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, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\vivaldi.exe", "", browser_fullpath)) {
// Make sure the browser executable file exist
if(_webui_file_exist(browser_fullpath)) {
// Vivaldi Found (one user)
sprintf(win->browser_path, "\"%s\"", browser_fullpath);
VivaldiExist = true;
return true;
}
}
return false;
#elif __APPLE__
// Vivaldi on macOS
if(_webui_cmd_sync("open -R -a \"Vivaldi\"", false) == 0) {
sprintf(win->browser_path, "open -W \"/Applications/Vivaldi.app\" --args");
VivaldiExist = true;
return true;
}
else
return false;
#else
// Vivaldi on Linux
if(_webui_cmd_sync("vivaldi --version", false) == 0) {
sprintf(win->browser_path, "vivaldi");
VivaldiExist = true;
return true;
}
else
return false;
#endif
}
else if(browser == Brave) {
// Brave Browser
static bool BraveExist = false;
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, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\brave.exe", "", browser_fullpath)) {
// Make sure the browser executable file exist
if(_webui_file_exist(browser_fullpath)) {
// Brave Found (multi-user)
sprintf(win->browser_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, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\brave.exe", "", browser_fullpath)) {
// Make sure the browser executable file exist
if(_webui_file_exist(browser_fullpath)) {
// Brave Found (one user)
sprintf(win->browser_path, "\"%s\"", browser_fullpath);
BraveExist = true;
return true;
}
}
return false;
#elif __APPLE__
// Brave on macOS
if(_webui_cmd_sync("open -R -a \"Brave\"", false) == 0) {
sprintf(win->browser_path, "open -W \"/Applications/Brave.app\" --args");
BraveExist = true;
return true;
}
else
return false;
#else
// Brave on Linux
if(_webui_cmd_sync("brave --version", false) == 0) {
sprintf(win->browser_path, "brave");
BraveExist = true;
return true;
}
else
return false;
#endif
}
else if(browser == Firefox) {
// Firefox
static bool FirefoxExist = false;
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, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\firefox.exe", "", browser_fullpath)) {
// Make sure the browser executable file exist
if(_webui_file_exist(browser_fullpath)) {
// Firefox Found (multi-user)
sprintf(win->browser_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, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\firefox.exe", "", browser_fullpath)) {
// Make sure the browser executable file exist
if(_webui_file_exist(browser_fullpath)) {
// Firefox Found (one user)
sprintf(win->browser_path, "\"%s\"", browser_fullpath);
FirefoxExist = true;
return true;
}
}
return false;
#elif __APPLE__
// Firefox on macOS
if(_webui_cmd_sync("open -R -a \"firefox\"", false) == 0) {
sprintf(win->browser_path, "open -W \"/Applications/Firefox.app\" --args");
FirefoxExist = true;
return true;
}
else
return false;
#else
// Firefox on Linux
if(_webui_cmd_sync("firefox -v", false) == 0) {
sprintf(win->browser_path, "firefox");
FirefoxExist = true;
return true;
}
else
return false;
#endif
}
else if(browser == Yandex) {
// Yandex Browser
static bool YandexExist = false;
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, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\browser.exe", "", browser_fullpath)) {
// Make sure the browser executable file exist
if(_webui_file_exist(browser_fullpath)) {
// Yandex Found (one user)
sprintf(win->browser_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, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\browser.exe", "", browser_fullpath)) {
// Make sure the browser executable file exist
if(_webui_file_exist(browser_fullpath)) {
// Yandex Found (multi-user)
sprintf(win->browser_path, "\"%s\"", browser_fullpath);
YandexExist = true;
return true;
}
}
return false;
#elif __APPLE__
// Yandex on macOS
if(_webui_cmd_sync("open -R -a \"Yandex\"", false) == 0) {
sprintf(win->browser_path, "open -W \"/Applications/Yandex.app\" --args");
YandexExist = true;
return true;
}
else
return false;
#else
// Yandex on Linux
if(_webui_cmd_sync("yandex-browser --version", false) == 0) {
sprintf(win->browser_path, "yandex-browser");
YandexExist = true;
return true;
}
else
return false;
#endif
}
else if(browser == Chromium) {
// The Chromium Projects
static bool ChromiumExist = false;
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, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe", "Path", browser_folder)) {
// Make sure its Chromium and not Google Chrome
if(!_webui_is_google_chrome_folder(browser_folder)) {
// Chromium Found (one user)
sprintf(win->browser_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, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe", "Path", browser_folder)) {
// Make sure its Chromium and not Google Chrome
if(!_webui_is_google_chrome_folder(browser_folder)) {
// Chromium Found (multi-user)
sprintf(win->browser_path, "\"%s\\chrome.exe\"", browser_folder);
ChromiumExist = true;
return true;
}
}
return false;
#elif __APPLE__
// Chromium on macOS
if(_webui_cmd_sync("open -R -a \"Chromium\"", false) == 0) {
sprintf(win->browser_path, "open -W \"/Applications/Chromium.app\" --args");
ChromiumExist = true;
return true;
}
else
return false;
#else
// Chromium on Linux
if(_webui_cmd_sync("chromium-browser --version", false) == 0) {
sprintf(win->browser_path, "chromium-browser");
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;
// Let's give other threads more time to safely exit
// and finish their cleaning up.
_webui_sleep(120);
// TODO: Add option to let the user decide if
// WebUI should delete the web browser profile
// folder or not.
// Free all non-freed memory allocations
_webui_free_all_mem();
}
int _webui_cmd_sync(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[1024];
#ifdef _WIN32
sprintf(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(buf, show);
#else
sprintf(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
}
int _webui_cmd_async(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];
int res = 0;
// Asynchronous command
#ifdef _WIN32
sprintf(buf, "START \"\" %s", cmd);
res = _webui_cmd_sync(buf, show);
#else
sprintf(buf, "%s >>/dev/null 2>>/dev/null &", cmd);
res = _webui_cmd_sync(buf, show);
#endif
return res;
}
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(cmd, false);
}
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_profile_folder(win, Chrome))
return false;
char arg[1024];
sprintf(arg, " --user-data-dir=\"%s\" --no-first-run --disable-gpu --disable-software-rasterizer --no-proxy-server --safe-mode --disable-extensions --disable-background-mode --disable-plugins --disable-plugins-discovery --disable-translate --bwsi --disable-sync --disable-sync-preferences --app=", win->profile_path);
char full[1024];
sprintf(full, "%s%s%s", win->browser_path, arg, address);
if(_webui_run_browser(win, full) == 0) {
win->current_browser = Chrome;
_webui_core.current_browser = Chrome;
return true;
}
else
return false;
}
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_profile_folder(win, Edge))
return false;
char arg[1024];
sprintf(arg, " --user-data-dir=\"%s\" --no-first-run --disable-gpu --disable-software-rasterizer --no-proxy-server --safe-mode --disable-extensions --disable-background-mode --disable-plugins --disable-plugins-discovery --disable-translate --bwsi --disable-sync --disable-sync-preferences --app=", win->profile_path);
char full[1024];
sprintf(full, "%s%s%s", win->browser_path, arg, address);
if(_webui_run_browser(win, full) == 0) {
win->current_browser = Edge;
_webui_core.current_browser = Edge;
return true;
}
else
return false;
}
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_profile_folder(win, Epic))
return false;
char arg[1024];
sprintf(arg, " --user-data-dir=\"%s\" --no-first-run --disable-gpu --disable-software-rasterizer --no-proxy-server --safe-mode --disable-extensions --disable-background-mode --disable-plugins --disable-plugins-discovery --disable-translate --bwsi --disable-sync --disable-sync-preferences --app=", win->profile_path);
char full[1024];
sprintf(full, "%s%s%s", win->browser_path, arg, address);
if(_webui_run_browser(win, full) == 0) {
win->current_browser = Epic;
_webui_core.current_browser = Epic;
return true;
}
else
return false;
}
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_profile_folder(win, Vivaldi))
return false;
char arg[1024];
sprintf(arg, " --user-data-dir=\"%s\" --no-first-run --disable-gpu --disable-software-rasterizer --no-proxy-server --safe-mode --disable-extensions --disable-background-mode --disable-plugins --disable-plugins-discovery --disable-translate --bwsi --disable-sync --disable-sync-preferences --app=", win->profile_path);
char full[1024];
sprintf(full, "%s%s%s", win->browser_path, arg, address);
if(_webui_run_browser(win, full) == 0) {
win->current_browser = Vivaldi;
_webui_core.current_browser = Vivaldi;
return true;
}
else
return false;
}
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_profile_folder(win, Brave))
return false;
char arg[1024];
sprintf(arg, " --user-data-dir=\"%s\" --no-first-run --disable-gpu --disable-software-rasterizer --no-proxy-server --safe-mode --disable-extensions --disable-background-mode --disable-plugins --disable-plugins-discovery --disable-translate --bwsi --disable-sync --disable-sync-preferences --app=", win->profile_path);
char full[1024];
sprintf(full, "%s%s%s", win->browser_path, arg, address);
if(_webui_run_browser(win, full) == 0) {
win->current_browser = Brave;
_webui_core.current_browser = Brave;
return true;
}
else
return false;
}
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_profile_folder(win, Firefox))
return false;
char full[1024];
sprintf(full, "%s -P WebUI -purgecaches -new-window %s", win->browser_path, address);
if(_webui_run_browser(win, full) == 0) {
win->current_browser = Firefox;
_webui_core.current_browser = Firefox;
return true;
}
else
return false;
}
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_profile_folder(win, Yandex))
return false;
char arg[1024];
sprintf(arg, " --user-data-dir=\"%s\" --no-first-run --disable-gpu --disable-software-rasterizer --no-proxy-server --safe-mode --disable-extensions --disable-background-mode --disable-plugins --disable-plugins-discovery --disable-translate --bwsi --disable-sync --disable-sync-preferences --app=", win->profile_path);
char full[1024];
sprintf(full, "%s%s%s", win->browser_path, arg, address);
if(_webui_run_browser(win, full) == 0) {
win->current_browser = Yandex;
_webui_core.current_browser = Yandex;
return true;
}
else
return false;
}
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_profile_folder(win, Chromium))
return false;
char arg[1024];
sprintf(arg, " --user-data-dir=\"%s\" --no-first-run --disable-gpu --disable-software-rasterizer --no-proxy-server --safe-mode --disable-extensions --disable-background-mode --disable-plugins --disable-plugins-discovery --disable-translate --bwsi --disable-sync --disable-sync-preferences --app=", win->profile_path);
char full[1024];
sprintf(full, "%s%s%s", win->browser_path, arg, address);
if (_webui_run_browser(win, full) == 0) {
win->current_browser = Chromium;
_webui_core.current_browser = Chromium;
return true;
}
else
return false;
}
bool _webui_browser_start(_webui_window_t* win, const char* address, unsigned int browser) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_browser_start([%s], [%u])... \n", address, browser);
#endif
// Non existing browser
if(browser > 10)
return false;
// Current browser used in the last opened window
if(browser == AnyBrowser && _webui_core.current_browser != 0)
browser = _webui_core.current_browser;
// TODO: Convert address from [/...] to [file://...]
if(browser != 0) {
// 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
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(browser == Chromium)
return _webui_browser_start_chromium(win, address);
else
return false;
}
else {
// Open the window using the default OS 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 when using ungoogled-chromium-binaries
#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;
}
bool _webui_set_root_folder(_webui_window_t* win, const char* path) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_set_root_folder([%s])... \n", path);
#endif
if((path == NULL) || (strlen(path) > WEBUI_MAX_PATH))
return false;
win->is_embedded_html = true;
if(_webui_is_empty(path))
sprintf(win->server_root_path, "%s", WEBUI_DEFAULT_PATH);
else
sprintf(win->server_root_path, "%s", path);
webui_set_multi_access(win, true);
return true;
}
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) {
snprintf(status_path, sizeof(status_path), "/proc/%s/status", entry->d_name);
FILE *status_file = fopen(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;
}
unsigned int _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 when using ungoogled-chromium-binaries
// 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;
}
bool _webui_show(_webui_window_t* win, const char* content, unsigned int browser) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_show([%u])... \n", browser);
#endif
if(_webui_is_empty(content))
return false;
// Some wrappers does not guarantee `content` to
// stay valid, so, let's make our copy right now
size_t content_len = strlen(content);
const char* content_cpy = (const char*)_webui_malloc(content_len);
memcpy((char*)content_cpy, content, content_len);
if(strstr(content_cpy, "<html")) {
// Embedded HTML
#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, content_cpy, true, browser);
}
else {
// File
#ifdef WEBUI_LOG
printf("[User] webui_show() -> File: [%s]\n", content_cpy);
#endif
if(content_len > WEBUI_MAX_PATH || strstr(content_cpy, "<"))
return false;
return _webui_show_window(win, content_cpy, false, browser);
}
}
bool _webui_show_window(_webui_window_t* win, const char* content, bool is_embedded_html, unsigned int browser) {
#ifdef WEBUI_LOG
if(is_embedded_html)
printf("[Core]\t\t_webui_show_window(HTML, [%u])... \n", browser);
else
printf("[Core]\t\t_webui_show_window(FILE, [%u])... \n", browser);
#endif
_webui_init();
char* url = NULL;
unsigned int port = (win->server_port == 0 ? _webui_get_free_port() : win->server_port);
// Initialization
if(win->html != NULL)
_webui_free_mem((void *)win->html);
if(win->url != NULL)
_webui_free_mem((void *)win->url);
if(is_embedded_html) {
// Show a window using the embedded HTML
win->is_embedded_html = true;
win->html = (content == NULL ? webui_empty_string : content);
// Generate the URL
size_t url_len = 32; // [http][domain][port]
url = (char*) _webui_malloc(url_len);
sprintf(url, "http://localhost:%u", port);
}
else {
// Show a window using a local file
win->is_embedded_html = false;
win->html = NULL;
// Generate the URL
size_t url_len = 32 + strlen(content); // [http][domain][port][file]
url = (char*) _webui_malloc(url_len);
sprintf(url, "http://localhost:%u/%s", port, content);
}
// Set URL
win->url = url;
win->server_port = port;
if(!webui_is_shown(win)) {
// Start a new window
// Run browser
if(!_webui_browser_start(win, win->url, browser)) {
// Browser not available
_webui_free_mem((void *)win->html);
_webui_free_mem((void *)win->url);
_webui_free_port(win->server_port);
return false;
}
// New server thread
#ifdef _WIN32
HANDLE thread = CreateThread(NULL, 0, _webui_server_start, (void *)win, 0, NULL);
win->server_thread = thread;
if(thread != NULL)
CloseHandle(thread);
#else
pthread_t thread;
pthread_create(&thread, NULL, &_webui_server_start, (void *)win);
pthread_detach(thread);
win->server_thread = thread;
#endif
}
else {
// Refresh an existing running window
// Prepare packets
size_t packet_len = 3 + strlen(url); // [header][url]
char* packet = (char*) _webui_malloc(packet_len);
packet[0] = WEBUI_HEADER_SIGNATURE; // Signature
packet[1] = WEBUI_HEADER_SWITCH; // Type
packet[2] = 0; // ID
for(unsigned int i = 0; i < strlen(win->url); i++) // URL
packet[i + 3] = win->url[i];
// Send the packet
_webui_window_send(win, packet, packet_len);
_webui_free_mem((void *)packet);
}
return true;
}
static void _webui_window_event(_webui_window_t* win, char* webui_internal_id, char* element, void* data, unsigned int data_len, int event_type) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_window_event([%s], [%s])... \n", webui_internal_id, element);
#endif
// Create a thread, and call the used cb function
_webui_cb_t* arg = (_webui_cb_t*) _webui_malloc(sizeof(_webui_cb_t));
arg->win = win;
arg->webui_internal_id = webui_internal_id;
arg->element_name = element;
arg->event_type = event_type;
if(data != NULL) {
arg->data = data;
arg->data_len = data_len;
}
else {
arg->data = (void*) webui_empty_string;
arg->data_len = 0;
}
#ifdef _WIN32
HANDLE user_fun_thread = CreateThread(NULL, 0, _webui_cb, (void *) arg, 0, NULL);
if(user_fun_thread != NULL)
CloseHandle(user_fun_thread);
#else
pthread_t thread;
pthread_create(&thread, NULL, &_webui_cb, (void *) arg);
pthread_detach(thread);
#endif
}
static void _webui_window_send(_webui_window_t* win, char* packet, size_t packets_size) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_window_send()... \n");
printf("[Core]\t\t_webui_window_send() -> %d bytes \n", (int)packets_size);
printf("[Core]\t\t_webui_window_send() -> [ ");
_webui_print_hex(packet, 3);
printf("]\n");
printf("[Core]\t\t_webui_window_send() -> [%.*s] \n", (int)(packets_size - 3), (const char*)&packet[3]);
#endif
if(!win->connected ||
_webui_core.mg_connections[win->window_number] == NULL ||
packet == NULL ||
packets_size < 4)
return;
struct mg_connection* c = _webui_core.mg_connections[win->window_number];
mg_ws_send(
c,
packet,
packets_size,
WEBSOCKET_OP_BINARY
);
}
bool _webui_get_data(const char* packet, size_t packet_len, unsigned int pos, size_t* data_len, char** data) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_get_data()... \n");
#endif
if((pos + 1) > packet_len) {
*data = NULL;
data_len = 0;
return false;
}
*data = (char*) _webui_malloc((packet_len - pos));
// Check mem
if(*data == NULL) {
data_len = 0;
return false;
}
// Copy data part
char* p = *data;
for(unsigned int i = pos; i < packet_len; i++) {
memcpy(p, &packet[i], 1);
p++;
}
// Check data size
*data_len = strlen(*data);
if(*data_len < 1) {
_webui_free_mem((void *) data);
*data = NULL;
data_len = 0;
return false;
}
return true;
}
static void _webui_window_receive(_webui_window_t* win, const char* packet, size_t len) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_window_receive()... \n");
#endif
if((unsigned char) packet[0] != WEBUI_HEADER_SIGNATURE || len < 4)
return;
if((unsigned char) packet[1] == WEBUI_HEADER_CLICK) {
// Click Event
// 0: [Signature]
// 1: [Type]
// 2:
// 3: [Data]
// Get html element id
char* element;
size_t element_len;
if(!_webui_get_data(packet, len, 3, &element_len, &element))
return;
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_window_receive() -> WEBUI_HEADER_CLICK \n");
printf("[Core]\t\t_webui_window_receive() -> %d bytes \n", (int)element_len);
printf("[Core]\t\t_webui_window_receive() -> [%s] \n", element);
#endif
// Generate WebUI internal id
char* webui_internal_id = _webui_generate_internal_id(win, element);
_webui_window_event(
win, // Window
webui_internal_id, // WebUI Internal ID
element, // User HTML ID
NULL, // User Custom Data
0, // User Data Len
WEBUI_EVENT_MOUSE_CLICK // Type of this event
);
}
else if((unsigned char) packet[1] == WEBUI_HEADER_JS) {
// JS Result
// 0: [Signature]
// 1: [Type]
// 2: [ID]
// 3: [Error]
// 4: [Data]
// Get pipe id
unsigned char run_id = packet[2];
if(run_id < 0x01) {
// Fatal.
// The pipe ID is not valid
// we can't send the ready signal to webui_script()
return;
}
// Get data part
char* data;
size_t data_len;
bool data_status = _webui_get_data(packet, len, 4, &data_len, &data);
// Get js-error
bool error = true;
if((unsigned char) packet[3] == 0x00)
error = false;
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_window_receive() -> WEBUI_HEADER_JS \n");
printf("[Core]\t\t_webui_window_receive() -> run_id = 0x%02x \n", run_id);
printf("[Core]\t\t_webui_window_receive() -> error = 0x%02x \n", error);
printf("[Core]\t\t_webui_window_receive() -> %d bytes of data\n", (int)data_len);
printf("[Core]\t\t_webui_window_receive() -> data = [%s] @ 0x%p\n", data, data);
#endif
// Initialize pipe
if((void *)_webui_core.run_responses[run_id] != NULL)
_webui_free_mem((void *)_webui_core.run_responses[run_id]);
// Set pipe
if(data_status && data_len > 0) {
_webui_core.run_error[run_id] = error;
_webui_core.run_responses[run_id] = data;
}
else {
// Empty Result
_webui_core.run_error[run_id] = error;
_webui_core.run_responses[run_id] = webui_empty_string;
}
// Send ready signal to webui_script()
_webui_core.run_done[run_id] = true;
}
else if((unsigned char) packet[1] == WEBUI_HEADER_CALL_FUNC) {
// Function Call (No response)
// 0: [Signature]
// 1: [Type]
// 2:
// 3: [ID, Null, Data]
// Get html element id
char* element;
size_t element_len;
if(!_webui_get_data(packet, len, 3, &element_len, &element))
return;
// Get data
void* data;
size_t data_len;
if(!_webui_get_data(packet, len, (3 + element_len + 1), &data_len, (char **) &data))
return;
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_window_receive() -> WEBUI_HEADER_CALL_FUNC \n");
printf("[Core]\t\t_webui_window_receive() -> %d bytes \n", (int)element_len);
printf("[Core]\t\t_webui_window_receive() -> [%s] \n", element);
#endif
// Generate WebUI internal id
char* webui_internal_id = _webui_generate_internal_id(win, element);
_webui_window_event(
win, // Window
webui_internal_id, // WebUI Internal ID
element, // User HTML ID
data, // User Custom Data
data_len, // User Data Len
WEBUI_EVENT_CALLBACK // Type of this event
);
}
else if((unsigned char) packet[1] == WEBUI_HEADER_SWITCH) {
// Navigation Event
// 0: [Signature]
// 1: [Type]
// 2:
// 3: [URL]
// Events
if(win->has_events) {
// Get URL
char* url;
size_t url_len;
if(!_webui_get_data(packet, len, 3, &url_len, &url))
return;
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_window_receive() -> WEBUI_HEADER_SWITCH \n");
printf("[Core]\t\t_webui_window_receive() -> %d bytes \n", (int)url_len);
printf("[Core]\t\t_webui_window_receive() -> [%s] \n", url);
#endif
// Generate WebUI internal id
char* webui_internal_id = _webui_generate_internal_id(win, "");
_webui_window_event(
win, // Window
webui_internal_id, // WebUI Internal ID
"", // HTML ID
url, // URL
url_len, // URL Len
WEBUI_EVENT_NAVIGATION // Type of this event
);
}
}
}
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(unsigned int port) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_free_port([%u])... \n", port);
#endif
for(unsigned int i = 0; i < WEBUI_MAX_ARRAY; i++) {
if(_webui_core.used_ports[i] == port) {
_webui_core.used_ports[i] = 0;
break;
}
}
}
static void _webui_wait_for_startup(void) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_wait_for_startup()... \n");
#endif
if(_webui_core.connections > 0)
return;
// Wait for the first connection
for(unsigned int n = 0; n <= (_webui_core.startup_timeout * 10); n++) {
if(_webui_core.connections > 0)
break;
_webui_sleep(50);
}
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_wait_for_startup() -> Finish.\n");
#endif
}
unsigned int _webui_get_new_window_number(void) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_get_new_window_number()... \n");
#endif
return ++_webui_core.last_window;
}
unsigned int _webui_get_free_port(void) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_get_free_port()... \n");
#endif
#ifdef _WIN32
srand((unsigned int)time(NULL));
#else
srand(time(NULL));
#endif
unsigned int port = (rand() % (WEBUI_MAX_PORT + 1 - WEBUI_MIN_PORT)) + WEBUI_MIN_PORT;
for(unsigned int i = WEBUI_MIN_PORT; i <= WEBUI_MAX_PORT; i++) {
// Search [port] in [_webui_core.used_ports]
bool found = false;
for(unsigned int j = 0; j < WEBUI_MAX_ARRAY; j++) {
if(_webui_core.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(unsigned int i = 0; i < WEBUI_MAX_ARRAY; i++) {
if(_webui_core.used_ports[i] == 0) {
_webui_core.used_ports[i] = port;
break;
}
}
return port;
}
static void _webui_init(void) {
if(_webui_core.initialized)
return;
#ifdef WEBUI_LOG
printf("[Core]\t\tWebUI v%s \n", WEBUI_VERSION);
printf("[Core]\t\t_webui_init()... \n");
#endif
// Initializing
memset(&_webui_core, 0, sizeof(_webui_core_t));
_webui_core.initialized = true;
_webui_core.startup_timeout = WEBUI_DEF_TIMEOUT;
_webui_core.executable_path = _webui_get_current_path();
}
unsigned int _webui_get_cb_index(char* webui_internal_id) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_get_cb_index([%s])... \n", webui_internal_id);
#endif
if(webui_internal_id != NULL) {
for(unsigned int i = 1; i < WEBUI_MAX_ARRAY; i++) {
if(_webui_core.html_elements[i] != NULL && !_webui_is_empty(_webui_core.html_elements[i]))
if(strcmp(_webui_core.html_elements[i], webui_internal_id) == 0)
return i;
}
}
return 0;
}
unsigned int _webui_set_cb_index(char* webui_internal_id) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_set_cb_index([%s])... \n", webui_internal_id);
#endif
// Add
for(unsigned int i = 1; i < WEBUI_MAX_ARRAY; i++) {
if(_webui_is_empty(_webui_core.html_elements[i])) {
_webui_core.html_elements[i] = webui_internal_id;
return i;
}
}
return 0;
}
#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++;
}
}
#endif
WEBUI_SERVER_START
{
_webui_window_t* win = (_webui_window_t*) arg;
#ifdef WEBUI_LOG
printf("[Core]\t\t[Thread] _webui_server_start(%s)... \n", win->url);
#endif
// Initialization
_webui_core.servers++;
win->server_running = true;
if(_webui_core.startup_timeout < 1)
_webui_core.startup_timeout = 0;
if(_webui_core.startup_timeout > 30)
_webui_core.startup_timeout = 30;
// Start Server
struct mg_mgr mgr;
mg_mgr_init(&mgr);
_webui_core.mg_mgrs[win->window_number] = &mgr;
if(mg_http_listen(&mgr, win->url, _webui_server_event_handler, (void *)win) != NULL) {
if(_webui_core.startup_timeout > 0) {
#ifdef WEBUI_LOG
printf("[Core]\t\t[Thread] _webui_server_start()... Listening Success\n");
printf("[Core]\t\t[Thread] _webui_server_start()... Timeout is %u seconds\n", _webui_core.startup_timeout);
#endif
bool stop = false;
while(!stop) {
if(!win->connected) {
// Wait for first connection
_webui_timer_t timer_1;
_webui_timer_start(&timer_1);
for(;;) {
// Stop if window is connected
mg_mgr_poll(&mgr, 1);
if(win->connected)
break;
// Stop if timer is finished
if(_webui_timer_is_end(&timer_1, (_webui_core.startup_timeout * 1000)))
break;
}
if(!win->connected && win->html_handled) {
// At this moment the browser is already started and HTML
// is already handled, so, let's wait more time to give
// the WebSocket an extra one second to connect.
_webui_timer_t timer_2;
_webui_timer_start(&timer_2);
for(;;) {
// Stop if window is connected
mg_mgr_poll(&mgr, 1);
if(win->connected)
break;
// Stop if timer is finished
if(_webui_timer_is_end(&timer_2, 1000))
break;
}
}
if(!win->connected)
stop = true; // First run failed
}
else {
// UI is connected
while(!stop) {
// Wait forever for disconnection
mg_mgr_poll(&mgr, 1);
// Exit signal
if(_webui_core.exit_now) {
stop = true;
break;
}
if(!win->connected) {
// The UI is just get disconnected
// probably the user did a refresh
// let's wait for re-connection...
_webui_timer_t timer_3;
_webui_timer_start(&timer_3);
for(;;) {
// Stop if window is re-connected
mg_mgr_poll(&mgr, 1);
if(win->connected)
break;
// Stop if timer is finished
if(_webui_timer_is_end(&timer_3, 600))
break;
}
if(!win->connected) {
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 later
if(_webui_core.startup_timeout == 0) {
#ifdef WEBUI_LOG
printf("[Core]\t\t[Thread] _webui_server_start(%s)... Listening Success -> Infinite Loop... \n", win->url);
#endif
// Wait forever
for(;;) {
mg_mgr_poll(&mgr, 1);
if(_webui_core.exit_now)
break;
}
}
}
else {
#ifdef WEBUI_LOG
printf("[Core]\t\t[Thread] _webui_server_start(%s)... Listening failed\n", win->url);
#endif
}
// Stop server
mg_mgr_free(&mgr);
_webui_core.servers--;
#ifdef WEBUI_LOG
printf("[Core]\t\t[Thread] _webui_server_start()... Server Stop.\n");
#endif
// Clean
win->server_running = false;
win->html_handled = false;
win->connected = false;
_webui_core.mg_mgrs[win->window_number] = NULL;
_webui_core.mg_connections[win->window_number] = NULL;
_webui_free_port(win->server_port);
THREAD_RETURN
}
WEBUI_CB
{
_webui_cb_t* arg = (_webui_cb_t*) _arg;
#ifdef WEBUI_LOG
printf("[Core]\t\t[Thread] _webui_cb()... \n");
#endif
webui_event_t e;
e.element = arg->element_name;
e.window = arg->win;
e.data = arg->data;
e.response = NULL;
e.type = arg->event_type;
// Check for all events-binded function
if(arg->win->has_events) {
char* events_id = _webui_generate_internal_id(arg->win, "");
unsigned int events_cb_index = _webui_get_cb_index(events_id);
_webui_free_mem((void *)events_id);
if(events_cb_index > 0 && _webui_core.cb[events_cb_index] != NULL) {
// Call user all events cb
_webui_core.cb[events_cb_index](&e);
}
}
// Check for the binded function
if(arg->element_name != NULL && !_webui_is_empty(arg->element_name)) {
unsigned int cb_index = _webui_get_cb_index(arg->webui_internal_id);
if(cb_index > 0 && _webui_core.cb[cb_index] != NULL) {
// Call user cb
_webui_core.cb[cb_index](&e);
}
}
#ifdef WEBUI_LOG
printf("[Core]\t\t[Thread] _webui_cb()... Stop.\n");
#endif
// Free
_webui_free_mem((void *)e.response);
_webui_free_mem((void *)arg->webui_internal_id);
_webui_free_mem((void *)arg->element_name);
_webui_free_mem((void *)arg);
THREAD_RETURN
}
#ifdef _WIN32
bool _webui_socket_test_listen_win32(unsigned int port_num) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_socket_test_listen_win32([%u])... \n", port_num);
#endif
WSADATA wsaData;
unsigned int 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];
sprintf(&the_port[0], "%u", 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, (unsigned int)result->ai_addrlen);
if(iResult == 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;
}
int _webui_system_win32(char* cmd, bool show) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_system_win32()... \n");
#endif
DWORD Return = 0;
DWORD CreationFlags = CREATE_NO_WINDOW;
/*
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));
*/
if(show)
CreationFlags = SW_SHOW;
STARTUPINFOA si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
if(!CreateProcessA(
NULL, // No module name (use command line)
cmd, // 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
return -1;
}
SetFocus(pi.hProcess);
// AssignProcessToJobObject(JobObject, pi.hProcess);
WaitForSingleObject(pi.hProcess, INFINITE);
GetExitCodeProcess(pi.hProcess, &Return);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
if(Return == 0)
return 0;
else
return -1;
}
bool _webui_get_windows_reg_value(HKEY key, const char* reg, const char* value_name, char value[WEBUI_MAX_PATH]) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_get_windows_reg_value([%s])... \n", reg);
#endif
HKEY hKey;
if(RegOpenKeyEx(key, reg, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
DWORD valueSize = WEBUI_MAX_PATH;
// If `value_name` is empty then
// will read the "(default)" reg-key
if(RegQueryValueEx(hKey, value_name, NULL, NULL, (LPBYTE)value, &valueSize) == ERROR_SUCCESS) {
RegCloseKey(hKey);
return true;
}
}
return false;
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) {
return true;
}
#endif