-
Notifications
You must be signed in to change notification settings - Fork 158
/
webui.c
11465 lines (9433 loc) · 364 KB
/
webui.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
WebUI Library
https://webui.me
https://github.com/webui-dev/webui
Copyright (c) 2020-2024 Hassan Draga.
Licensed under MIT License.
All rights reserved.
Canada.
*/
// 64Mb max dynamic memory allocation
#define WEBUI_MAX_BUF (64000000)
// -- Includes ------------------------
#include "../bridge/webui_bridge.h" // WebUI Bridge (JavaScript)
#include "webui.h" // WebUI Header
// -- WebView -------------------------
#ifdef _WIN32
#include "webview/WebView2.h"
#elif __linux__
#include <dlfcn.h>
#else
// ...
#endif
// -- Third-party ---------------------
#ifdef WEBUI_TLS
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#endif
// OpenSSL
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/ssl.h>
#include <openssl/x509.h>
#ifdef WEBUI_LOG
#include <openssl/err.h>
#endif
#endif
#define MG_BUF_LEN (WEBUI_MAX_BUF)
#include "civetweb/civetweb.h"
// -- Disable Non-critical warnings ---
#ifdef _MSC_VER
#pragma warning(push, 0)
// Disable spectre warnings
#pragma warning(disable: 5045)
#elif defined(__clang__)
// ...
#elif defined(__GNUC__)
// ...
#endif
// -- Defines -------------------------
#define WEBUI_SIGNATURE 0xDD // All packets should start with this 8bit
#define WEBUI_CMD_JS 0xFE // Command: JavaScript result in frontend
#define WEBUI_CMD_JS_QUICK 0xFD // Command: JavaScript result in frontend
#define WEBUI_CMD_CLICK 0xFC // Command: Click event
#define WEBUI_CMD_NAVIGATION 0xFB // Command: Frontend navigation
#define WEBUI_CMD_CLOSE 0xFA // Command: Close window
#define WEBUI_CMD_CALL_FUNC 0xF9 // Command: Backend function call
#define WEBUI_CMD_SEND_RAW 0xF8 // Command: Send raw binary data to the UI
#define WEBUI_CMD_ADD_ID 0xF7 // Command: Add new bind ID
#define WEBUI_CMD_MULTI 0xF6 // Command: Multi packet data
#define WEBUI_CMD_CHECK_TK 0xF5 // Command: Check validity of a client's token
#define WEBUI_PROTOCOL_SIZE (8) // Protocol header size in bytes
#define WEBUI_PROTOCOL_SIGN (0) // Protocol byte position: Signature (1 Byte)
#define WEBUI_PROTOCOL_TOKEN (1) // Protocol byte position: Token (4 Bytes)
#define WEBUI_PROTOCOL_ID (5) // Protocol byte position: ID (2 Bytes)
#define WEBUI_PROTOCOL_CMD (7) // Protocol byte position: Command (1 Byte)
#define WEBUI_PROTOCOL_DATA (8) // Protocol byte position: Data (n Byte)
#define WEBUI_MUTEX_NONE (0) // Check boolen mutex without update
#define WEBUI_MUTEX_TRUE (1) // Check boolen mutex and update to true
#define WEBUI_MUTEX_FALSE (2) // Check boolen mutex and update to false
#define WEBUI_WS_DATA (1) // Internal WS Event (Data received)
#define WEBUI_WS_OPEN (2) // Internal WS Event (New connection)
#define WEBUI_WS_CLOSE (3) // Internal WS Event (Connection close)
#define WEBUI_SHOW_HTML (1) // Show window using HTML
#define WEBUI_SHOW_FILE (2) // Show window using a local file
#define WEBUI_SHOW_URL (3) // Show window using a URL
#define WEBUI_SHOW_FOLDER (4) // Show window using a Folder
#define WEBUI_MIN_PORT (10000) // Minimum socket port
#define WEBUI_MAX_PORT (65500) // Should be less than 65535
#define WEBUI_STDOUT_BUF (10240) // Command STDOUT output buffer size
#define WEBUI_DEFAULT_PATH "." // Default root path
#define WEBUI_DEF_TIMEOUT (15) // Default startup timeout in seconds
#define WEBUI_RELOAD_TIMEOUT (1500) // Default reload page timeout in milliseconds
#define WEBUI_MAX_TIMEOUT (60) // Maximum startup timeout in seconds the user can set
#define WEBUI_MIN_WIDTH (100) // Minimal window width
#define WEBUI_MIN_HEIGHT (100) // Minimal window height
#define WEBUI_MAX_WIDTH (3840) // Maximal window width (4K Monitor)
#define WEBUI_MAX_HEIGHT (2160) // Maximal window height (4K Monitor)
#define WEBUI_MIN_X (0) // Minimal window X
#define WEBUI_MIN_Y (0) // Minimal window Y
#define WEBUI_MAX_X (3000) // Maximal window X (4K Monitor)
#define WEBUI_MAX_Y (1800) // Maximal window Y (4K Monitor)
#define WEBUI_PROFILE_NAME "WebUI" // Default browser profile name (Used only for Firefox)
#define WEBUI_COOKIES_LEN (32) // Authentification cookies len
#define WEBUI_COOKIES_BUF (64) // Authentification cookies buffer size
#ifdef WEBUI_TLS
#define WEBUI_SECURE "TLS-Encryption"
#define WEBUI_SSL_SIZE (4096) // SSL Max PEM Size
#define WEBUI_SSL_EXPIRE (72 * 60 * 60) // SSL Expires (Integer)
#define WEBUI_SSL_EXPIRE_STR "259201" // SSL Expires (String)
#define WEBUI_HTTP_PROTOCOL "https://"
#define WEBUI_WS_PROTOCOL "wss://"
#else
#define WEBUI_SECURE "Non-Encrypted"
#define WEBUI_HTTP_PROTOCOL "http://"
#define WEBUI_WS_PROTOCOL "ws://"
#endif
#ifdef WEBUI_DYNAMIC
#define WEBUI_LIB_TYPE "Dynamic"
#else
#define WEBUI_LIB_TYPE "Static"
#endif
#ifdef _WIN32
#define WEBUI_OS "Microsoft Windows"
#elif __APPLE__
#define WEBUI_OS "Apple macOS"
#else
#define WEBUI_OS "GNU/Linux"
#endif
// Mutex
#ifdef _WIN32
typedef CRITICAL_SECTION webui_mutex_t;
typedef CONDITION_VARIABLE webui_condition_t;
#else
typedef pthread_mutex_t webui_mutex_t;
typedef pthread_cond_t webui_condition_t;
#endif
// Compiler
#if defined(_MSC_VER)
#define WEBUI_COMPILER "MSVC"
#elif defined(__GNUC__) && !defined(__clang__)
#define WEBUI_COMPILER "GCC"
#elif defined(__clang__)
#define WEBUI_COMPILER "Clang"
#else
#define WEBUI_COMPILER "Unknown"
#endif
// Verbose Log
#ifdef WEBUI_LOG
#define WEBUI_LOG_VERBOSE
#endif
// Timer
typedef struct _webui_timer_t {
struct timespec start;
struct timespec now;
} _webui_timer_t;
// Event data
typedef struct webui_event_inf_t {
char* event_data[WEBUI_MAX_ARG + 1]; // Event data (string | num | bool | raw)
size_t event_size[WEBUI_MAX_ARG + 1]; // Event data size (in bytes)
char* response; // Event response (string)
size_t count; // Event arguments count
} webui_event_inf_t;
// WebView
#ifdef _WIN32
typedef struct _webui_wv_win32_t {
// Win32 WebView
ICoreWebView2Environment* webviewEnvironment;
ICoreWebView2Controller* webviewController;
ICoreWebView2* webviewWindow;
HWND hwnd;
void* titleChangedHandler;
void* titleChangedHandler_lpVtbl;
void* createWebViewEnvironmentHandler;
void* createWebViewEnvironmentHandler_lpVtbl;
void* createWebViewControllerHandler;
void* createWebViewControllerHandler_lpVtbl;
// WebUI Window
wchar_t* url;
bool navigate;
bool size;
bool position;
int width;
int height;
int x;
int y;
bool stop;
} _webui_wv_win32_t;
#elif __linux__
void* libgtk;
void* libwebkit;
// GTK Symbol Addresses
typedef void *(*gtk_init_func)(int *argc, char ***argv);
typedef void (*gtk_widget_show_all_func)(void *);
typedef void (*gtk_main_iteration_do_func)(int);
typedef int (*gtk_events_pending_func)(void);
typedef void (*gtk_container_add_func)(void *, void *);
typedef void *(*gtk_window_new_func)(int);
typedef void (*gtk_window_set_default_size_func)(void *, int, int);
typedef void (*gtk_window_set_title_func)(void *, const char *);
typedef void (*gtk_window_move_func)(void *, int, int);
typedef void (*gtk_window_close_func)(void *);
typedef void (*gtk_window_resize_func)(void *, int, int);
typedef void (*gtk_window_set_position_func)(void *, int);
typedef int (*g_idle_add_func)(int (*function)(void*), void*);
typedef void (*g_signal_connect_data_func)(void *, const char *,
void (*callback)(void), void *, void *, int);
gtk_init_func gtk_init = NULL;
gtk_widget_show_all_func gtk_widget_show_all = NULL;
gtk_main_iteration_do_func gtk_main_iteration_do = NULL;
gtk_events_pending_func gtk_events_pending = NULL;
gtk_container_add_func gtk_container_add = NULL;
gtk_window_new_func gtk_window_new = NULL;
gtk_window_set_default_size_func gtk_window_set_default_size = NULL;
gtk_window_set_title_func gtk_window_set_title = NULL;
gtk_window_move_func gtk_window_move = NULL;
gtk_window_close_func gtk_window_close = NULL;
gtk_window_resize_func gtk_window_resize = NULL;
gtk_window_set_position_func gtk_window_set_position = NULL;
g_signal_connect_data_func g_signal_connect_data = NULL;
g_idle_add_func g_idle_add = NULL;
// WebKit Symbol Addresses
typedef void *(*webkit_web_view_new_func)(void);
typedef void (*webkit_web_view_load_uri_func)(void *, const char *);
typedef const char *(*webkit_web_view_get_title_func)(void *);
webkit_web_view_new_func webkit_web_view_new = NULL;
webkit_web_view_load_uri_func webkit_web_view_load_uri = NULL;
webkit_web_view_get_title_func webkit_web_view_get_title = NULL;
typedef struct _webui_wv_linux_t {
// Linux WebView
void* gtk_win;
void* gtk_wv;
bool open;
// WebUI Window
char* url;
bool navigate;
bool size;
bool position;
unsigned int width;
unsigned int height;
unsigned int x;
unsigned int y;
bool stop;
} _webui_wv_linux_t;
#else
extern bool _webui_macos_wv_new(int index);
extern bool _webui_macos_wv_show(int index, const char* urlString, int x, int y, int width, int height);
extern bool _webui_macos_wv_close(int index);
extern bool _webui_macos_wv_set_position(int index, int x, int y);
extern bool _webui_macos_wv_set_size(int index, int width, int height);
extern bool _webui_macos_wv_navigate(int index, const char* urlString);
extern void _webui_macos_wv_process();
extern void _webui_macos_wv_stop();
extern void _webui_macos_wv_set_close_cb(void (*cb)(int index));
extern void _webui_macos_wv_new_thread_safe(int index);
typedef struct _webui_wv_macos_t {
// macOS WebView
int index;
// WebUI Window
char* url;
bool navigate;
bool size;
bool position;
unsigned int width;
unsigned int height;
unsigned int x;
unsigned int y;
bool stop;
} _webui_wv_macos_t;
#endif
// Window
typedef struct _webui_window_t {
// Client
size_t clients_count;
struct mg_connection* single_client; // Single client
bool single_client_token_check;
// Server
bool wait; // Let server thread wait more time for websocket
bool server_running; // Slow check
bool connected; // Fast check
size_t server_port;
char* url;
const char* html;
char* server_root_path;
#ifdef _WIN32
HANDLE server_thread;
#else
pthread_t server_thread;
#endif
// Window
uint32_t token;
size_t num; // Window number
const char* html_elements[WEBUI_MAX_IDS];
bool has_all_events;
void(*cb[WEBUI_MAX_IDS])(webui_event_t* e);
void(*cb_interface[WEBUI_MAX_IDS])(size_t, size_t, char* , size_t, size_t);
bool ws_block;
bool is_embedded_html;
bool is_closed;
size_t custom_server_port;
const char* icon;
const char* icon_type;
size_t current_browser;
char* browser_path;
bool custom_profile;
bool default_profile;
char* profile_path;
char* profile_name;
size_t runtime;
bool kiosk_mode;
bool disable_browser_high_contrast;
bool hide;
int width;
int height;
bool size_set;
int x;
int y;
bool position_set;
size_t process_id;
const void*(*files_handler)(const char* filename, int* length);
webui_event_inf_t* events[WEBUI_MAX_IDS];
size_t events_count;
bool is_public;
bool proxy_set;
char *proxy_server;
// WebView
bool allow_webview;
bool update_webview;
#ifdef _WIN32
_webui_wv_win32_t* webView;
#elif __linux__
_webui_wv_linux_t* webView;
#else
_webui_wv_macos_t* webView;
#endif
}
_webui_window_t;
// Core
typedef struct _webui_core_t {
struct {
bool show_wait_connection;
bool show_auto_js_inject;
bool ws_block;
bool folder_monitor;
bool multi_client;
bool use_cookies;
} config;
struct mg_connection* clients[WEBUI_MAX_IDS];
size_t clients_win_num[WEBUI_MAX_IDS];
bool clients_token_check[WEBUI_MAX_IDS];
char* cookies[WEBUI_MAX_IDS];
bool cookies_single_set[WEBUI_MAX_IDS];
size_t servers;
size_t used_ports[WEBUI_MAX_IDS];
size_t startup_timeout;
size_t cb_count;
bool exit_now;
bool run_done[WEBUI_MAX_IDS]; // 2 Bytes ID
char* run_userBuffer[WEBUI_MAX_IDS];
size_t run_userBufferLen[WEBUI_MAX_IDS];
bool run_error[WEBUI_MAX_IDS];
uint16_t run_last_id;
bool initialized;
char* executable_path;
void * ptr_list[WEBUI_MAX_IDS * 2];
size_t ptr_position;
size_t ptr_size[WEBUI_MAX_IDS * 2];
size_t current_browser;
_webui_window_t* wins[WEBUI_MAX_IDS];
bool wins_reserved[WEBUI_MAX_IDS];
webui_mutex_t mutex_server_start;
webui_mutex_t mutex_send;
webui_mutex_t mutex_receive;
webui_mutex_t mutex_wait;
webui_mutex_t mutex_bridge;
webui_mutex_t mutex_js_run;
webui_mutex_t mutex_win_connect;
webui_mutex_t mutex_exit_now;
webui_mutex_t mutex_webview_stop;
webui_mutex_t mutex_http_handler;
webui_mutex_t mutex_client;
webui_condition_t condition_wait;
char* default_server_root_path;
bool ui;
#ifdef WEBUI_TLS
char* root_cert;
char* root_key;
char* ssl_cert;
char* ssl_key;
#endif
// WebView
bool is_browser_main_run;
bool is_webview;
#ifdef _WIN32
char* webview_cacheFolder;
HMODULE webviewLib;
#elif __linux__
bool is_gtk_main_run;
#else
bool is_wkwebview_main_run;
#endif
}
_webui_core_t;
typedef struct _webui_cb_arg_t {
// Event
_webui_window_t* window;
size_t event_type;
char* element;
char* data;
size_t event_number;
}
_webui_cb_arg_t;
typedef struct _webui_recv_arg_t {
_webui_window_t* win;
void * ptr;
size_t len;
size_t recvNum;
int event_type;
struct mg_connection* client;
size_t connection_id;
}
_webui_recv_arg_t;
typedef struct _webui_cmd_async_t {
_webui_window_t* win;
char* cmd;
}
_webui_cmd_async_t;
// -- Definitions ---------------------
#ifdef _WIN32
static const char* os_sep = "\\";
static DWORD WINAPI _webui_run_browser_task(LPVOID _arg);
static int _webui_system_win32(_webui_window_t* win, char* cmd, bool show);
static int _webui_system_win32_out(const char* cmd, char ** output, bool show);
static bool _webui_socket_test_listen_win32(size_t port_num);
static bool _webui_get_windows_reg_value(HKEY key, LPCWSTR reg, LPCWSTR value_name, char value[WEBUI_MAX_PATH]);
static bool _webui_str_to_wide(const char *s, wchar_t **w);
#define WEBUI_THREAD_SERVER_START DWORD WINAPI _webui_server_thread(LPVOID arg)
#define WEBUI_THREAD_RECEIVE DWORD WINAPI _webui_ws_process_thread(LPVOID _arg)
#define WEBUI_THREAD_WEBVIEW DWORD WINAPI _webui_webview_thread(LPVOID arg)
#define WEBUI_THREAD_MONITOR DWORD WINAPI _webui_folder_monitor_thread(LPVOID arg)
#define WEBUI_THREAD_RETURN return 0;
#else
static const char* os_sep = "/";
static void * _webui_run_browser_task(void * _arg);
#define WEBUI_THREAD_SERVER_START void * _webui_server_thread(void * arg)
#define WEBUI_THREAD_RECEIVE void * _webui_ws_process_thread(void * _arg)
#define WEBUI_THREAD_WEBVIEW void * _webui_webview_thread(void * arg)
#define WEBUI_THREAD_MONITOR void * _webui_folder_monitor_thread(void * arg)
#define WEBUI_THREAD_RETURN pthread_exit(NULL);
#endif
static void _webui_init(void);
static bool _webui_show(_webui_window_t* win, struct mg_connection* client, const char* content, size_t browser);
static bool _webui_get_cb_index(_webui_window_t* win, const char* element, size_t* id);
static size_t _webui_get_free_port(void);
static void _webui_free_port(size_t port);
static char* _webui_get_current_path(void);
static void _webui_send_client_ws(_webui_window_t* win, struct mg_connection* client,
size_t connection_id, char* packet, size_t packets_size);
static void _webui_window_event(
_webui_window_t* win, size_t connection_id, int event_type, char* element, size_t event_number,
size_t client_id, const char* cookies
);
static int _webui_cmd_sync(_webui_window_t* win, char* cmd, bool show);
static int _webui_cmd_async(_webui_window_t* win, char* cmd, bool show);
static int _webui_run_browser(_webui_window_t* win, char* cmd);
static void _webui_clean(void);
static bool _webui_browser_exist(_webui_window_t* win, size_t browser);
static const char* _webui_get_temp_path();
static bool _webui_folder_exist(const char* folder);
static void _webui_delete_folder(char* folder);
static bool _webui_browser_create_new_profile(_webui_window_t* win, size_t browser);
static bool _webui_browser_start_chrome(_webui_window_t* win, const char* address);
static bool _webui_browser_start_edge(_webui_window_t* win, const char* address);
static bool _webui_browser_start_epic(_webui_window_t* win, const char* address);
static bool _webui_browser_start_vivaldi(_webui_window_t* win, const char* address);
static bool _webui_browser_start_brave(_webui_window_t* win, const char* address);
static bool _webui_browser_start_firefox(_webui_window_t* win, const char* address);
static bool _webui_browser_start_yandex(_webui_window_t* win, const char* address);
static bool _webui_browser_start_chromium(_webui_window_t* win, const char* address);
static bool _webui_browser_start(_webui_window_t* win, const char* address, size_t _browser);
static long _webui_timer_diff(struct timespec * start, struct timespec* end);
static void _webui_timer_start(_webui_timer_t* t);
static bool _webui_timer_is_end(_webui_timer_t* t, size_t ms);
static void _webui_timer_clock_gettime(struct timespec * spec);
static bool _webui_set_root_folder(_webui_window_t* win, const char* path);
static const char* _webui_generate_js_bridge(_webui_window_t* win, struct mg_connection* client);
static void _webui_free_mem(void * ptr);
static size_t _webui_mb(size_t size);
static bool _webui_file_exist_mg(_webui_window_t* win, struct mg_connection* client);
static bool _webui_file_exist(const char* path);
static void _webui_free_all_mem(void);
static bool _webui_show_window(_webui_window_t* win, struct mg_connection* client,
const char* content, int type, size_t browser);
static bool _webui_is_empty(const char* s);
static size_t _webui_strlen(const char* s);
static uint16_t _webui_get_run_id(void);
static void * _webui_malloc(size_t size);
static void _webui_sleep(long unsigned int ms);
static size_t _webui_find_the_best_browser(_webui_window_t* win);
static bool _webui_is_process_running(const char* process_name);
static void _webui_panic(char* msg);
static void _webui_kill_pid(size_t pid);
static _webui_window_t* _webui_dereference_win_ptr(void * ptr);
static int _webui_get_browser_args(_webui_window_t* win, size_t browser, char* buffer, size_t len);
static void _webui_mutex_init(webui_mutex_t* mutex);
static void _webui_mutex_lock(webui_mutex_t* mutex);
static void _webui_mutex_unlock(webui_mutex_t* mutex);
static void _webui_mutex_destroy(webui_mutex_t* mutex);
static bool _webui_mutex_is_connected(_webui_window_t* win, int update);
static bool _webui_mutex_is_exit_now(int update);
static bool _webui_mutex_is_webview_update(_webui_window_t* win, int update);
static void _webui_condition_init(webui_condition_t* cond);
static void _webui_condition_wait(webui_condition_t* cond, webui_mutex_t* mutex);
static void _webui_condition_signal(webui_condition_t* cond);
static void _webui_condition_destroy(webui_condition_t* cond);
static void _webui_http_send(_webui_window_t* win, struct mg_connection* client,
const char* mime_type, const char* body, size_t body_len, bool cache);
static void _webui_http_send_file(_webui_window_t* win, struct mg_connection* client,
const char* mime_type, const char* path, bool cache);
static void _webui_http_send_header(_webui_window_t* win, struct mg_connection* client,
const char* mime_type, size_t body_len, bool cache);
static void _webui_http_send_error(struct mg_connection* client, const char* body, int status);
static int _webui_http_handler(struct mg_connection* client, void * _win);
static int _webui_ws_connect_handler(const struct mg_connection* client, void * _win);
static void _webui_ws_ready_handler(struct mg_connection* client, void * _win);
static int _webui_ws_data_handler(struct mg_connection* client, int opcode, char* data, size_t datasize, void * _win);
static void _webui_ws_close_handler(const struct mg_connection* client, void * _win);
static void _webui_receive(_webui_window_t* win, struct mg_connection* client, int event_type, void * data, size_t len);
static void _webui_ws_process(_webui_window_t* win, struct mg_connection* client, size_t connection_id,
void* ptr, size_t len, size_t recvNum, int event_type);
static bool _webui_connection_save(_webui_window_t* win, struct mg_connection* client, size_t* connection_id);
static bool _webui_connection_get_id(_webui_window_t* win, struct mg_connection* client, size_t* connection_id);
static void _webui_connection_remove(_webui_window_t* win, struct mg_connection* client);
static void _webui_remove_firefox_profile_ini(const char* path, const char* profile_name);
static bool _webui_is_firefox_ini_profile_exist(const char* path, const char* profile_name);
static void _webui_send_client(_webui_window_t* win, struct mg_connection *client,
uint16_t id, unsigned char cmd, const char* data, size_t len, bool token_bypass);
static void _webui_send_all(_webui_window_t* win, uint16_t id, unsigned char cmd, const char* data, size_t len);
static uint16_t _webui_get_id(const char* data);
static uint32_t _webui_get_token(const char* data);
static uint32_t _webui_generate_random_uint32();
static const char* _webui_url_encode(const char* str);
static bool _webui_open_url_native(const char* url);
static bool _webui_is_valid_url(const char* url);
static bool _webui_port_is_used(size_t port_num);
static char* _webui_str_dup(const char* src);
static void _webui_bridge_api_handler(webui_event_t* e);
// static size_t _webui_hash_djb2(const char* s);
static size_t _webui_new_event_inf(_webui_window_t* win, webui_event_inf_t** event_inf);
static void _webui_free_event_inf(_webui_window_t* win, size_t event_num);
static const char* _webui_get_cookies_full(const struct mg_connection* client);
static void _webui_get_cookies(const struct mg_connection* client, char* buffer);
static bool _webui_client_cookies_save(_webui_window_t* win, const char* cookies, size_t* client_id);
static bool _webui_client_cookies_get_id(_webui_window_t* win, const char* cookies, size_t* client_id);
// static void _webui_client_cookies_free_all(_webui_window_t* win);
// static void _webui_client_cookies_free(_webui_window_t* win, struct mg_connection* client);
static size_t _webui_client_get_id(_webui_window_t* win, struct mg_connection* client);
static void _webui_generate_cookies(char* cookies, size_t length);
static int _webui_serve_file(_webui_window_t* win, struct mg_connection* client, size_t client_id);
static int _webui_external_file_handler(_webui_window_t* win, struct mg_connection* client, size_t client_id);
static int _webui_interpret_file(_webui_window_t* win, struct mg_connection* client, char* index, size_t client_id);
// WebView
#ifdef _WIN32
// Microsoft Windows
static void _webui_wv_free(_webui_wv_win32_t* webView);
static void _webui_wv_close(_webui_wv_win32_t *webView);
static bool _webui_wv_navigate(_webui_wv_win32_t* webView, wchar_t* url);
static bool _webui_wv_set_position(_webui_wv_win32_t* webView, int x, int y);
static bool _webui_wv_set_size(_webui_wv_win32_t* webView, int windowWidth, int windowHeight);
static bool _webui_wv_show(_webui_window_t* win, char* url);
static void _webui_wv_event_closed(_webui_window_t* win);
#elif __linux__
// Linux
static void _webui_wv_free();
static void _webui_wv_close(_webui_wv_linux_t *webView);
static bool _webui_wv_navigate(_webui_wv_linux_t* webView, char* url);
static bool _webui_wv_set_position(_webui_wv_linux_t* webView, int x, int y);
static bool _webui_wv_set_size(_webui_wv_linux_t* webView, int windowWidth, int windowHeight);
static bool _webui_wv_show(_webui_window_t* win, char* url);
static void _webui_wv_event_closed(void *widget, void *arg);
#else
// macOS
static void _webui_wv_free(_webui_wv_macos_t* webView);
static void _webui_wv_close(_webui_wv_macos_t *webView);
static bool _webui_wv_navigate(_webui_wv_macos_t* webView, char* url);
static bool _webui_wv_set_position(_webui_wv_macos_t* webView, int x, int y);
static bool _webui_wv_set_size(_webui_wv_macos_t* webView, int windowWidth, int windowHeight);
static bool _webui_wv_show(_webui_window_t* win, char* url);
static void _webui_wv_event_closed(int index);
#endif
#ifdef WEBUI_TLS
static int _webui_tls_initialization(void * ssl_ctx, void * ptr);
static bool _webui_tls_generate_self_signed_cert(char* root_cert, char* root_key, char* ssl_cert, char* ssl_key);
static bool _webui_check_certificate(const char* certificate_pem, const char* private_key_pem);
#endif
#ifdef WEBUI_LOG
static void _webui_print_hex(const char* data, size_t len);
static void _webui_print_ascii(const char* data, size_t len);
static int _webui_http_log(const struct mg_connection* client, const char* message);
#endif
static WEBUI_THREAD_SERVER_START;
static WEBUI_THREAD_RECEIVE;
static WEBUI_THREAD_WEBVIEW;
static WEBUI_THREAD_MONITOR;
// Safe C STD
#ifdef _WIN32
#define WEBUI_STR_TOK(str, delim, context) strtok_s(str, delim, context)
#define WEBUI_FILE_OPEN(file, filename, mode) fopen_s(&file, filename, mode)
#define WEBUI_SN_PRINTF_DYN(buffer, buffer_size, format, ...) snprintf(buffer, _webui_mb(buffer_size), format, ##__VA_ARGS__)
#define WEBUI_SN_PRINTF_STATIC(buffer, buffer_size, format, ...) snprintf(buffer, buffer_size, format, ##__VA_ARGS__)
#define WEBUI_STR_COPY_DYN(dest, dest_size, src) strcpy_s(dest, _webui_mb(dest_size), src)
#define WEBUI_STR_COPY_STATIC(dest, dest_size, src) strcpy_s(dest, dest_size, src)
#define WEBUI_STR_CAT_DYN(dest, dest_size, src) strcat_s(dest, _webui_mb(dest_size), src)
#define WEBUI_STR_CAT_STATIC(dest, dest_size, src) strcat_s(dest, dest_size, src)
#else
#define WEBUI_STR_TOK(str, delim, context) strtok_r(str, delim, context)
#define WEBUI_FILE_OPEN(file, filename, mode) ((file) = fopen(filename, mode))
#define WEBUI_SN_PRINTF_DYN(buffer, buffer_size, format, ...) snprintf(buffer, _webui_mb(buffer_size), format, ##__VA_ARGS__)
#define WEBUI_SN_PRINTF_STATIC(buffer, buffer_size, format, ...) snprintf(buffer, buffer_size, format, ##__VA_ARGS__)
#define WEBUI_STR_COPY_DYN(dest, dest_size, src) strncpy(dest, src, _webui_mb(dest_size))
#define WEBUI_STR_COPY_STATIC(dest, dest_size, src) strncpy(dest, src, dest_size)
#define WEBUI_STR_CAT_DYN(dest, dest_size, src) strncat(dest, src, _webui_mb(dest_size))
#define WEBUI_STR_CAT_STATIC(dest, dest_size, src) strncat(dest, src, dest_size)
#endif
// Assert
#define WEBUI_ASSERT(s) _webui_panic(s);assert(0 && s);
// -- Heap ----------------------------
static _webui_core_t _webui;
static const char* webui_html_served = "<html><head><title>Access Denied</title><script src=\"/webui.js\"></script><style>"
"body{margin:0;background-repeat:no-repeat;background-attachment:fixed;background-color:#FF3CAC;background-image:linear-"
"gradient(225deg,#FF3CAC 0%,#784BA0 45%,#2B86C5 100%);font-family:sans-serif;margin:20px;color:#fff}a{color:#fff}</style>"
"</head><body><h2>⚠ Access Denied</h2><p>You can't access this content<br>because it's already in use in<br>another "
"window.<br><br>Note: Multi-client mode is disabled.</p><br><a href=\"https://www.webui.me\">"
"<small>WebUI v" WEBUI_VERSION "</small></a></body></html>";
static const char* webui_html_res_not_available = "<html><head><title>Resource Not Available</title><script src=\"/webui.js\">"
"</script><style>body{margin:0;background-repeat:no-repeat;background-attachment:fixed;background-color:#FF3CAC;background-"
"image:linear-gradient(225deg,#FF3CAC 0%,#784BA0 45%,#2B86C5 100%);font-family:sans-serif;margin:20px;color:#fff}a{color:#fff}"
"</style></head><body><h2>⚠ Resource Not Available</h2><p>The requested resource is not available.</p><br><a href=\""
"https://www.webui.me\"><small>WebUI v" WEBUI_VERSION "</small></a></body></html>";
static const char* webui_def_icon = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"500\" zoomAndPan=\"magnify\" viewBox=\"0 0 375 374.999991\" height=\"500\" preserveAspectRatio=\"xMidYMid meet\" version=\"1.0\"><defs><clipPath id=\"3530adfd62\"><path d=\"M 22.375 22.558594 L 352.625 22.558594 L 352.625 352.441406 L 22.375 352.441406 Z M 22.375 22.558594 \" clip-rule=\"nonzero\"/></clipPath></defs><g clip-path=\"url(#3530adfd62)\"><path fill=\"#2a6699\" d=\"M 22.375 22.558594 L 352.257812 22.558594 L 352.257812 352.441406 L 22.375 352.441406 Z M 22.375 22.558594 \" fill-opacity=\"1\" fill-rule=\"nonzero\"/></g></svg>"; static const char* webui_def_icon_type = "image/svg+xml";
// -- Functions -----------------------
void webui_run_client(webui_event_t* e, const char* script) {
#ifdef WEBUI_LOG
printf("[User] webui_run_client([%zu])\n", e->window);
printf("[User] webui_run_client([%zu]) -> Script: [%s]\n", e->window, script);
#endif
// Initialization
_webui_init();
size_t js_len = _webui_strlen(script);
if (js_len < 1)
return;
// Dereference
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
return;
_webui_window_t* win = _webui.wins[e->window];
if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE))
return;
// Packet Protocol Format:
// [...]
// [CMD]
// [Script]
// Send the packet to a single client
_webui_send_client(win, _webui.clients[e->connection_id], 0, WEBUI_CMD_JS_QUICK, script, js_len, false);
}
void webui_run(size_t window, const char* script) {
#ifdef WEBUI_LOG
printf("[User] webui_run([%zu])\n", window);
printf("[User] webui_run([%zu]) -> Script: [%s]\n", window, script);
#endif
// Initialization
_webui_init();
size_t js_len = _webui_strlen(script);
if (js_len < 1)
return;
// Dereference
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
return;
_webui_window_t* win = _webui.wins[window];
if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE))
return;
// Packet Protocol Format:
// [...]
// [CMD]
// [Script]
// Send the packet to all clients
_webui_send_all(win, 0, WEBUI_CMD_JS_QUICK, script, js_len);
}
void webui_set_file_handler(size_t window, const void*(*handler)(const char* filename, int* length)) {
if (handler == NULL)
return;
// Initialization
_webui_init();
// Dereference
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
return;
_webui_window_t* win = _webui.wins[window];
win->files_handler = handler;
}
bool webui_script_client(webui_event_t* e, const char* script, size_t timeout,
char* buffer, size_t buffer_length) {
#ifdef WEBUI_LOG
printf("[User] webui_script_client([%zu])\n", e->window);
printf("[User] webui_script_client([%zu]) -> Script [%s] \n", e->window, script);
printf("[User] webui_script_client([%zu]) -> Response Buffer @ 0x%p \n", e->window, buffer);
printf("[User] webui_script_client([%zu]) -> Response Buffer Size %zu bytes \n", e->window, buffer_length);
#endif
// Initializing response buffer
if (buffer_length > 0) {
memset(buffer, 0, buffer_length);
}
// Initialization
_webui_init();
// Dereference
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[e->window] == NULL)
return false;
_webui_window_t* win = _webui.wins[e->window];
if (!_webui_mutex_is_connected(win, WEBUI_MUTEX_NONE))
return false;
size_t js_len = _webui_strlen(script);
if (js_len < 1)
return false;
// Initializing pipe
_webui_mutex_lock(&_webui.mutex_js_run);
uint16_t run_id = _webui_get_run_id();
_webui.run_done[run_id] = false;
_webui.run_error[run_id] = false;
_webui.run_userBuffer[run_id] = buffer;
_webui.run_userBufferLen[run_id] = buffer_length;
_webui_mutex_unlock(&_webui.mutex_js_run);
// Packet Protocol Format:
// [...]
// [CMD]
// [Script]
// Send the packet to a single client
_webui_send_client(win, _webui.clients[e->connection_id], run_id, WEBUI_CMD_JS, script, js_len, false);
bool js_status = false;
// Wait for UI response
if (timeout < 1 || timeout > 86400) {
// Wait forever
for (;;) {
_webui_sleep(1);
_webui_mutex_lock(&_webui.mutex_js_run);
js_status = _webui.run_done[run_id];
_webui_mutex_unlock(&_webui.mutex_js_run);
if (js_status)
break;
}
} else {
// Using timeout
_webui_timer_t timer;
_webui_timer_start(&timer);
for (;;) {
_webui_sleep(1);
_webui_mutex_lock(&_webui.mutex_js_run);
js_status = _webui.run_done[run_id];
_webui_mutex_unlock(&_webui.mutex_js_run);
if (js_status)
break;
if (_webui_timer_is_end(&timer, (timeout * 1000)))
break;
}
}
if (js_status) {
#ifdef WEBUI_LOG
printf(
"[User] webui_script -> Response found. User buffer len: %zu bytes \n",
_webui.run_userBufferLen[run_id]
);
printf(
"[User] webui_script -> Response found. User buffer data: [%s] \n",
_webui.run_userBuffer[run_id]
);
#endif
return !_webui.run_error[run_id];
} else {
#ifdef WEBUI_LOG
printf("[User] webui_script -> No Response is found. \n");
#endif
}
return false;
}
bool webui_script(size_t window, const char* script, size_t timeout,
char* buffer, size_t buffer_length) {
#ifdef WEBUI_LOG
printf("[User] webui_script([%zu])\n", window);
#endif
// Stop if multi-client mode is enabled.
// we can't send and receive from all clients
if (_webui.config.multi_client) {
#ifdef WEBUI_LOG
printf("[User] webui_script() -> Multi-client mode is enabled, stop.\n");
#endif
// Initializing response buffer
if (buffer_length > 0) {
memset(buffer, 0, buffer_length);
}
return false;
}
// Event
webui_event_t e;
e.window = window;
e.connection_id = 0; // Single client
return webui_script_client(&e, script, timeout, buffer, buffer_length);
}
static uint32_t _webui_generate_random_uint32() {
uint32_t timestamp = (uint32_t) time(NULL);
// Get the higher 16 bits
uint32_t high = ((uint32_t) rand()&0xFFFF) + ((timestamp >> 16)&0xFFFF);
// Get the lower 16 bits
uint32_t low = ((uint32_t) rand()&0xFFFF) + (timestamp&0xFFFF);
// Combine
return (high << 16) | low;
}
size_t webui_new_window(void) {
#ifdef WEBUI_LOG
printf("[User] webui_new_window()\n");
#endif
// Create a new window
return webui_new_window_id(webui_get_new_window_id());
}
size_t webui_new_window_id(size_t num) {
#ifdef WEBUI_LOG
printf("[User] webui_new_window_id([%zu])\n", num);
#endif
// Initialization
_webui_init();
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
return 0;
// Check window ID
if (num < 1 || num > WEBUI_MAX_IDS)
return 0;
// Destroy the window if already exist
if (_webui.wins[num] != NULL)
webui_destroy(num);
// Create a new window
_webui_window_t* win = (_webui_window_t* ) _webui_malloc(sizeof(_webui_window_t));
_webui.wins[num] = win;
// Initialisation
win->ws_block = _webui.config.ws_block;
win->num = num;
win->browser_path = (char*)_webui_malloc(WEBUI_MAX_PATH);
win->server_root_path = (char*)_webui_malloc(WEBUI_MAX_PATH);
if (_webui_is_empty(_webui.default_server_root_path))
WEBUI_SN_PRINTF_DYN(win->server_root_path, WEBUI_MAX_PATH, "%s", WEBUI_DEFAULT_PATH);
else
WEBUI_SN_PRINTF_DYN(win->server_root_path, WEBUI_MAX_PATH, "%s", _webui.default_server_root_path);
// Auto bind JavaScript-Bridge Core API Handler
webui_bind(num, "__webui_core_api__", _webui_bridge_api_handler);
#ifdef WEBUI_LOG
printf("[User] webui_new_window_id() -> New window #%zu @ 0x%p\n", num, win);
#endif
return num;
}
size_t webui_get_new_window_id(void) {
#ifdef WEBUI_LOG
printf("[User] webui_get_new_window_id()\n");
#endif
// Initialization
_webui_init();
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE))
return 0;
for (size_t i = 1; i < WEBUI_MAX_IDS; i++) {
if (_webui.wins[i] == NULL && !_webui.wins_reserved[i]) {
_webui.wins_reserved[i] = true;
return i;
}
}
// We should never reach here
WEBUI_ASSERT("webui_get_new_window_id() failed");
return 0;
}
void webui_set_kiosk(size_t window, bool status) {
#ifdef WEBUI_LOG
printf("[User] webui_set_kiosk([%zu])\n", window);
#endif
// Initialization
_webui_init();
// Dereference
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
return;
_webui_window_t* win = _webui.wins[window];
win->kiosk_mode = status;
}
void webui_set_high_contrast(size_t window, bool status) {
#ifdef WEBUI_LOG
printf("[User] webui_set_high_contrast([%zu], [%d])\n", window, status);
#endif
// Initialization
_webui_init();
// Dereference
if (_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE) || _webui.wins[window] == NULL)
return;
_webui_window_t* win = _webui.wins[window];
win->disable_browser_high_contrast = !status;
}
bool webui_browser_exist(size_t browser) {
#ifdef WEBUI_LOG
printf("[User] webui_browser_exist([%zu])\n", browser);
#endif
return _webui_browser_exist(NULL, browser);
}