-
Notifications
You must be signed in to change notification settings - Fork 275
Expand file tree
/
Copy pathwebui.c
More file actions
8136 lines (6549 loc) · 250 KB
/
webui.c
File metadata and controls
8136 lines (6549 loc) · 250 KB
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 2.4.2
http://webui.me
https://github.com/webui-dev/webui
Copyright (c) 2020-2023 Hassan Draga.
Licensed under MIT License.
All rights reserved.
Canada.
*/
// 64Mb max dynamic memory allocation
#define WEBUI_MAX_BUF (64000000)
// -- Third-party ---------------------
#ifdef WEBUI_TLS
// 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"
// -- Includes ------------------------
#include "../bridge/webui_bridge.h" // WebUI Bridge (JavaScript)
#include "webui.h" // WebUI Header
// -- 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_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_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 (30) // Default startup timeout in seconds
#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)
#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 _WIN32
#define WEBUI_OS "Microsoft Windows"
#elif __APPLE__
#define WEBUI_OS "Apple macOS"
#else
#define WEBUI_OS "GNU/Linux"
#endif
#define MAGIC_COOKIE 0xEBEBBE
#define INVALID_COOKIE 0xFEEEFE
// 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
#if defined(__clang__) || defined(__GNUC__)
#define WEBUI_DISABLE_OPTIMIZATION_START _Pragma("GCC optimize (\"O0\")")
#define WEBUI_DISABLE_OPTIMIZATION_END
#elif defined(_MSC_VER)
#define WEBUI_DISABLE_OPTIMIZATION_START __pragma(optimize("", off))
#define WEBUI_DISABLE_OPTIMIZATION_END __pragma(optimize("", on))
#else
#define WEBUI_DISABLE_OPTIMIZATION_START
#define WEBUI_DISABLE_OPTIMIZATION_END
#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)
} webui_event_inf_t;
// Window
typedef struct _webui_window_t {
size_t window_number;
volatile bool server_running;
volatile bool connected;
volatile bool file_handled;
bool html_handled;
bool bridge_handled;
bool server_handled;
bool is_embedded_html;
size_t custom_server_port;
size_t server_port;
size_t ws_port;
char* url;
const char* html;
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 has_events;
char* server_root_path;
bool kiosk_mode;
bool hide;
unsigned int width;
unsigned int height;
bool size_set;
unsigned int x;
unsigned int y;
bool position_set;
size_t process_id;
#ifdef _WIN32
HANDLE server_thread;
#else
pthread_t server_thread;
#endif
const void * ( * files_handler)(const char* filename, int * length);
uint32_t token;
struct mg_connection * mg_connection;
webui_event_inf_t* events[WEBUI_MAX_IDS];
size_t events_count;
bool is_public;
bool proxy_set;
char *proxy_server;
}
_webui_window_t;
// Core
typedef struct _webui_core_t {
volatile size_t servers;
char* html_elements[WEBUI_MAX_IDS];
size_t used_ports[WEBUI_MAX_IDS];
size_t startup_timeout;
volatile bool exit_now;
volatile 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;
void( * cb[WEBUI_MAX_IDS])(webui_event_t* e);
void( * cb_interface[WEBUI_MAX_IDS])(size_t, size_t, const char* , size_t, size_t);
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];
size_t last_win_number;
bool server_handled;
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_condition_t condition_wait;
char* default_server_root_path;
bool ui;
// bool little_endian;
#ifdef WEBUI_TLS
uint8_t * root_cert;
uint8_t * root_key;
uint8_t * ssl_cert;
uint8_t * ssl_key;
#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;
// Extras
char* webui_internal_id;
}
_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;
}
_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* webui_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]);
#define WEBUI_THREAD_SERVER_START DWORD WINAPI _webui_server_thread(LPVOID arg)
#define WEBUI_THREAD_RECEIVE DWORD WINAPI _webui_receive_thread(LPVOID _arg)
#define WEBUI_THREAD_RETURN return 0;
#else
static const char* webui_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_receive_thread(void * _arg)
#define WEBUI_THREAD_RETURN pthread_exit(NULL);
#endif
static void _webui_init(void);
static bool _webui_show(_webui_window_t * win, const char* content, size_t browser);
static size_t _webui_get_cb_index(char* webui_internal_id);
static size_t _webui_set_cb_index(char* webui_internal_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_ws_send(_webui_window_t * win, char* packet, size_t packets_size);
static void _webui_window_event(
_webui_window_t * win, int event_type, const char* element, size_t event_number, char* webui_internal_id
);
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(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);
static void _webui_free_mem(void * ptr);
static bool _webui_file_exist_mg(_webui_window_t * win, struct mg_connection * conn);
static bool _webui_file_exist(char* path);
static void _webui_free_all_mem(void);
static bool _webui_show_window(_webui_window_t * win, const char* content, int type, size_t browser);
static char* _webui_generate_internal_id(_webui_window_t * win, const char* element);
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(void);
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 max);
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 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(struct mg_connection * conn, const char* mime_type, const char* body);
static void _webui_http_send_error_page(struct mg_connection * conn, const char* body, int status);
static int _webui_http_handler(struct mg_connection * conn, void * _win);
static int _webui_ws_connect_handler(const struct mg_connection * conn, void * _win);
static void _webui_ws_ready_handler(struct mg_connection * conn, void * _win);
static int _webui_ws_data_handler(struct mg_connection * conn, int opcode, char* data, size_t datasize, void * _win);
static void _webui_ws_close_handler(const struct mg_connection * conn, void * _win);
static void _webui_receive(_webui_window_t * win, int event_type, void * data, size_t len);
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(_webui_window_t * win, uint32_t token, uint16_t id, uint8_t 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 bool _webui_mtx_is_connected(_webui_window_t * win, int update);
static bool _webui_mtx_is_exit_now(int update);
#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 * conn, const char* message);
#endif
static WEBUI_THREAD_SERVER_START;
static WEBUI_THREAD_RECEIVE;
// -- Heap ----------------------------
static _webui_core_t _webui_core;
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.</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_deno_not_found = "<html><head><title>Deno Not Found</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>⚠ 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><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>⚠ 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 xmlns=\"http://www.w3.org/2000/svg\" width=\"64\" height=\"64\" viewBox=\"0 0 64 64\""
" version=\"1.1\"><path d=\"M 35.315 15.983 C 30.885 17.816, 29.305 25.835, 33.500 25.195 C 34.600 25.027, 37.177 24.802, 39.227"
" 24.695 C 44.084 24.441, 49.054 19.899, 47.386 17.239 C 46.146 15.262, 38.884 14.507, 35.315 15.983 M 54.602 17.835 C 54.058"
" 18.716, 60.204 22.022, 62.284 21.968 C 63.958 21.925, 58.228 17, 56.503 17 C 55.741 17, 54.886 17.376, 54.602 17.835\""
" stroke=\"none\" fill=\"#789dcc\" fill-rule=\"evenodd\"/><path d=\"M 3.635 19.073 C 2.098 20.282, 1 22.144, 1 23.542 C 1 26.692,"
" 12.655 53.139, 14.754 54.750 C 15.650 55.437, 17.882 56, 19.716 56 C 23.227 56, 22.667 56.645, 30.331 43.762 L 32.163 40.684"
" 36.109 47.830 C 40.333 55.479, 42.889 57.131, 47.815 55.394 C 49.855 54.675, 51.575 51.765, 56.620 40.500 C 60.068 32.800,"
" 62.904 25.600, 62.921 24.500 C 62.944 23.042, 61.572 21.893, 57.862 20.262 C 55.062 19.031, 52.336 18.292, 51.806 18.620 C "
"51.275 18.948, 49.385 22.428, 47.604 26.353 L 44.367 33.490 42.504 30.647 C 41.121 28.536, 40.907 27.379, 41.673 26.152 C 42.567"
" 24.721, 42.224 24.526, 39.103 24.695 C 37.121 24.802, 34.600 25.027, 33.500 25.195 C 31.780 25.457, 31.517 24.966, 31.620 "
"21.688 L31.739 17.876 28.799 20.688 C 27.182 22.235, 24.694 25.637, 23.270 28.250 C 21.847 30.863, 20.354 33, 19.954 33 C 19.553"
" 33, 17.969 30.044, 16.433 26.431 C 12.452 17.064, 8.833 14.984, 3.635 19.073\" stroke=\"none\" fill=\"#294fb7\" fill-rule=\""
"evenodd\"/></svg>"; static const char* webui_def_icon_type = "image/svg+xml";
// -- Functions -----------------------
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_mtx_is_exit_now(WEBUI_MUTEX_NONE) || _webui_core.wins[window] == NULL)
return;
_webui_window_t * win = _webui_core.wins[window];
if (!_webui_mtx_is_connected(win, WEBUI_MUTEX_NONE))
return;
// Packet Protocol Format:
// [...]
// [CMD]
// [Script]
// Send the packet
_webui_send(win, win->token, 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_mtx_is_exit_now(WEBUI_MUTEX_NONE) || _webui_core.wins[window] == NULL)
return;
_webui_window_t * win = _webui_core.wins[window];
win->files_handler = handler;
}
bool webui_script(size_t window, const char* script, size_t timeout_second, char* buffer, size_t buffer_length) {
#ifdef WEBUI_LOG
printf("[User] webui_script([%zu])...\n", window);
printf("[User] webui_script([%zu]) -> Script [%s] \n", window, script);
printf("[User] webui_script([%zu]) -> Response Buffer @ 0x%p \n", window, buffer);
printf("[User] webui_script([%zu]) -> Response Buffer Size %zu bytes \n", window, buffer_length);
#endif
// Initialization
_webui_init();
// Dereference
if (_webui_mtx_is_exit_now(WEBUI_MUTEX_NONE) || _webui_core.wins[window] == NULL)
return false;
_webui_window_t * win = _webui_core.wins[window];
// Initializing response buffer
if (buffer_length > 0)
memset(buffer, 0, buffer_length);
if (!_webui_mtx_is_connected(win, WEBUI_MUTEX_NONE))
return false;
size_t js_len = _webui_strlen(script);
if (js_len < 1)
return false;
// Initializing pipe
uint16_t run_id = _webui_get_run_id();
_webui_mutex_lock( & _webui_core.mutex_js_run);
_webui_core.run_done[run_id] = false;
_webui_mutex_unlock( & _webui_core.mutex_js_run);
_webui_core.run_error[run_id] = false;
_webui_core.run_userBuffer[run_id] = buffer;
_webui_core.run_userBufferLen[run_id] = buffer_length;
// Packet Protocol Format:
// [...]
// [CMD]
// [Script]
// Send the packet
_webui_send(win, win->token, run_id, WEBUI_CMD_JS, script, js_len);
bool js_status = false;
// Wait for UI response
if (timeout_second < 1 || timeout_second > 86400) {
// Wait forever
for (;;) {
_webui_sleep(1);
_webui_mutex_lock( & _webui_core.mutex_js_run);
js_status = _webui_core.run_done[run_id];
_webui_mutex_unlock( & _webui_core.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_core.mutex_js_run);
js_status = _webui_core.run_done[run_id];
_webui_mutex_unlock( & _webui_core.mutex_js_run);
if (js_status)
break;
if (_webui_timer_is_end( & timer, (timeout_second * 1000)))
break;
}
}
if (js_status) {
#ifdef WEBUI_LOG
printf(
"[User] webui_script -> Response found. User buffer len: %zu bytes \n",
_webui_core.run_userBufferLen[run_id]
);
printf(
"[User] webui_script -> Response found. User buffer data: [%s] \n",
_webui_core.run_userBuffer[run_id]
);
#endif
return !_webui_core.run_error[run_id];
} else {
#ifdef WEBUI_LOG
printf("[User] webui_script -> No Response is found. \n");
#endif
}
return false;
}
WEBUI_DISABLE_OPTIMIZATION_START
static uint32_t _webui_generate_random_uint32() {
// Generate two random numbers using rand() and combine them
// to get a 32-bit random number.
uint32_t high = (uint32_t) rand() & 0xFFFF; // Get the higher 16 bits
uint32_t low = (uint32_t) rand() & 0xFFFF; // Get the lower 16 bits
return (high << 16) | low;
}
WEBUI_DISABLE_OPTIMIZATION_END
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 window_number) {
#ifdef WEBUI_LOG
printf("[User] webui_new_window_id([%zu])...\n", window_number);
#endif
// Initialization
_webui_init();
if (_webui_mtx_is_exit_now(WEBUI_MUTEX_NONE))
return 0;
// Check window ID
if (window_number < 1 || window_number > WEBUI_MAX_IDS)
return 0;
// Destroy the window if already exist
if (_webui_core.wins[window_number] != NULL)
webui_destroy(window_number);
// Create a new window
_webui_window_t * win = (_webui_window_t * ) _webui_malloc(sizeof(_webui_window_t));
_webui_core.wins[window_number] = win;
// Initialisation
win->window_number = window_number;
win->browser_path = (char*)_webui_malloc(WEBUI_MAX_PATH);
win->server_root_path = (char*)_webui_malloc(WEBUI_MAX_PATH);
if (_webui_is_empty(_webui_core.default_server_root_path))
sprintf(win->server_root_path, "%s", WEBUI_DEFAULT_PATH);
else
sprintf(win->server_root_path, "%s", _webui_core.default_server_root_path);
// Save window ID
if (window_number > _webui_core.last_win_number)
_webui_core.last_win_number = window_number;
// Generate a random token
win->token = _webui_generate_random_uint32();
#ifdef WEBUI_LOG
printf("[User] webui_new_window_id() -> New window #%zu @ 0x%p\n", window_number, win);
printf("[User] webui_new_window_id() -> New window Token 0x%08X (%"
PRIu32 ")\n", win->token, win->token);
#endif
return window_number;
}
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_mtx_is_exit_now(WEBUI_MUTEX_NONE))
return 0;
for (size_t i = 1; i < WEBUI_MAX_IDS; i++) {
if (_webui_core.wins[i] == NULL) {
if (i > _webui_core.last_win_number)
_webui_core.last_win_number = i;
return i;
}
}
// We should never reach here
_webui_panic();
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_mtx_is_exit_now(WEBUI_MUTEX_NONE) || _webui_core.wins[window] == NULL)
return;
_webui_window_t * win = _webui_core.wins[window];
win->kiosk_mode = status;
}
void webui_close(size_t window) {
#ifdef WEBUI_LOG
printf("[User] webui_close([%zu])...\n", window);
#endif
// Initialization
_webui_init();
// Dereference
if (_webui_mtx_is_exit_now(WEBUI_MUTEX_NONE) || _webui_core.wins[window] == NULL)
return;
_webui_window_t * win = _webui_core.wins[window];
if (_webui_mtx_is_connected(win, WEBUI_MUTEX_NONE)) {
// Packet Protocol Format:
// [...]
// [CMD]
// Send the packet
_webui_send(win, win->token, 0, WEBUI_CMD_CLOSE, NULL, 0);
}
}
void webui_destroy(size_t window) {
#ifdef WEBUI_LOG
printf("[User] webui_destroy([%zu])...\n", window);
#endif
// Initialization
_webui_init();
// Dereference
if (_webui_mtx_is_exit_now(WEBUI_MUTEX_NONE) || _webui_core.wins[window] == NULL)
return;
_webui_window_t * win = _webui_core.wins[window];
if (win->server_running) {
// Freindly close
webui_close(window);
// Wait for server threads to stop
_webui_timer_t timer_1;
_webui_timer_start( & timer_1);
for (;;) {
_webui_sleep(10);
if (!win->server_running)
break;
if (_webui_timer_is_end( & timer_1, 2500))
break;
}
if (win->server_running) {
#ifdef WEBUI_LOG
printf("[User] webui_destroy([%zu]) -> Forced close...\n", window);
#endif
// Forced close
_webui_mtx_is_connected(win, WEBUI_MUTEX_FALSE);
// Wait for server threads to stop
_webui_timer_t timer_2;
_webui_timer_start( & timer_2);
for (;;) {
_webui_sleep(10);
if (!win->server_running)
break;
if (_webui_timer_is_end( & timer_2, 1500))
break;
}
}
}
// Free memory resources
_webui_free_mem((void * ) win->url);
_webui_free_mem((void * ) win->html);
_webui_free_mem((void * ) win->icon);
_webui_free_mem((void * ) win->icon_type);
_webui_free_mem((void * ) win->browser_path);
_webui_free_mem((void * ) win->profile_path);
_webui_free_mem((void * ) win->profile_name);
_webui_free_mem((void * ) win->server_root_path);
// Free events
for (size_t i = 1; i < WEBUI_MAX_IDS; i++) {
if (win->events[i] != NULL)
_webui_free_mem((void * ) win->events[i]);
}
// Free window struct
_webui_free_mem((void * ) _webui_core.wins[window]);
_webui_core.wins[window] = NULL;
}
bool webui_is_shown(size_t window) {
#ifdef WEBUI_LOG
printf("[User] webui_is_shown([%zu])...\n", window);
#endif
// Initialization
_webui_init();
// Dereference
if (_webui_mtx_is_exit_now(WEBUI_MUTEX_NONE) || _webui_core.wins[window] == NULL)
return false;
_webui_window_t * win = _webui_core.wins[window];
return _webui_mtx_is_connected(win, WEBUI_MUTEX_NONE);
}
void webui_set_icon(size_t window, const char* icon, const char* icon_type) {
#ifdef WEBUI_LOG
printf("[User] webui_set_icon([%zu], [%s], [%s])...\n", window, icon, icon_type);
#endif
// Initialization
_webui_init();
// Dereference
if (_webui_mtx_is_exit_now(WEBUI_MUTEX_NONE) || _webui_core.wins[window] == NULL)
return;
_webui_window_t * win = _webui_core.wins[window];
// Some wrappers do not guarantee pointers stay valid,
// so, let's make our copy.
// Icon
size_t len = _webui_strlen(icon);
const char* icon_cpy = (const char* ) _webui_malloc(len);
memcpy((char*)icon_cpy, icon, len);
// Icon Type
len = _webui_strlen(icon_type);
const char* icon_type_cpy = (const char* ) _webui_malloc(len);
memcpy((char*)icon_type_cpy, icon_type, len);
// Clean old sets if any
if (win->icon != NULL)
_webui_free_mem((void * ) win->icon);
if (win->icon_type != NULL)
_webui_free_mem((void * ) win->icon_type);
win->icon = icon_cpy;
win->icon_type = icon_type_cpy;
}
void webui_navigate(size_t window, const char* url) {
#ifdef WEBUI_LOG
printf("[User] webui_navigate([%zu], [%s])...\n", window, url);
#endif
// Initialization
_webui_init();
// Dereference
if (_webui_mtx_is_exit_now(WEBUI_MUTEX_NONE) || _webui_core.wins[window] == NULL)
return;
_webui_window_t * win = _webui_core.wins[window];
if (!_webui_mtx_is_connected(win, WEBUI_MUTEX_NONE))
return;
// Packet Protocol Format:
// [...]
// [CMD]
// [URL]
// Send the packet
_webui_send(win, win->token, 0, WEBUI_CMD_NAVIGATION, url, _webui_strlen(url));
}
void webui_clean() {
#ifdef WEBUI_LOG
printf("[User] webui_clean()...\n");
#endif
// Initialization
_webui_init();
// Final memory cleaning
_webui_clean();
}
void webui_delete_all_profiles() {
#ifdef WEBUI_LOG
printf("[User] webui_delete_all_profiles()...\n");
#endif
// Initialization
_webui_init();
// Loop trough windows
for (size_t i = 1; i <= _webui_core.last_win_number; i++) {
if (_webui_core.wins[i] != NULL) {
webui_delete_profile(i);
}
}
}
static bool _webui_is_firefox_ini_profile_exist(const char* path, const char* profile_name) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_is_firefox_ini_profile_exist([%s], [%s])...\n", path, profile_name);
#endif
// Parse home environments in the path
#ifdef _WIN32
// Windows
char full_path[WEBUI_MAX_PATH];
ExpandEnvironmentStringsA(path, full_path, sizeof(full_path));
#else
// Linux & macOS
char full_path[WEBUI_MAX_PATH];
if (path[0] == '~') {
const char* home = getenv("HOME");
if (home) {
snprintf(full_path, sizeof(full_path), "%s/%s", home, & path[1]);
} else {
// If for some reason HOME isn't set
// fall back to the original path.
strncpy(full_path, path, sizeof(full_path));
}
} else {
strncpy(full_path, path, sizeof(full_path));
}
#endif
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_is_firefox_ini_profile_exist() -> Opening [%s]\n", full_path);
#endif
// Open
FILE * file = fopen(full_path, "r");
if (!file)
return false;
char target[128] = {
0x00
};
sprintf(target, "Name=%s", profile_name);
char line[1024];
while(fgets(line, sizeof(line), file)) {
if (strstr(line, target) != NULL) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_is_firefox_ini_profile_exist() -> Target found.\n");
#endif
fclose(file);
return true;
}
}
fclose(file);
return false;
}
static void _webui_remove_firefox_profile_ini(const char* path, const char* profile_name) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_remove_firefox_profile_ini([%s], [%s])...\n", path, profile_name);
#endif
// Parse home environments in the path
#ifdef _WIN32
// Windows
char full_path[WEBUI_MAX_PATH];
ExpandEnvironmentStringsA(path, full_path, sizeof(full_path));
#else
// Linux & macOS
char full_path[WEBUI_MAX_PATH];
if (path[0] == '~') {
const char* home = getenv("HOME");
if (home) {
snprintf(full_path, sizeof(full_path), "%s/%s", home, & path[1]);
} else {
// If for some reason HOME isn't set
// fall back to the original path.
strncpy(full_path, path, sizeof(full_path));
}
} else {
strncpy(full_path, path, sizeof(full_path));
}
#endif
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_remove_firefox_profile_ini() -> Opening [%s]\n", full_path);
#endif
// Open
FILE * file = fopen(full_path, "r");
if (!file)
return;
char buffer[4096] = {
0x00
};
char output[4096] = {
0x00
};
char target[128] = {
0x00
};
sprintf(target, "Name=%s", profile_name);
bool skip = false;
while(fgets(buffer, sizeof(buffer), file)) {
if (strncmp(buffer, "[Profile", 8) == 0) {
if (skip)
skip = false;
}
if (!skip) {
if (strstr(buffer, target) != NULL) {
#ifdef WEBUI_LOG
printf("[Core]\t\t_webui_remove_firefox_profile_ini() -> Target found.\n");
#endif
skip = true;
continue;
} else
strcat(output, buffer);
}
}
fclose(file);