Github User Fetcher 1.0.0
C Application with Server and GUI
Loading...
Searching...
No Matches
vendor/civetweb/main.c
Go to the documentation of this file.
1/* Copyright (c) 2013-2021 the Civetweb developers
2 * Copyright (c) 2004-2013 Sergey Lyubka
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23#if defined(_WIN32)
24
25#if !defined(_CRT_SECURE_NO_WARNINGS)
26#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005 */
27#endif
28#if !defined(_CRT_SECURE_NO_DEPRECATE)
29#define _CRT_SECURE_NO_DEPRECATE
30#endif
31#if defined(WIN32_LEAN_AND_MEAN)
32#undef WIN32_LEAN_AND_MEAN /* Required for some functions (tray icons, ...) */
33#endif
34
35#else
36
37#if defined(__clang__) /* GCC does not (yet) support this pragma */
38/* We must set some flags for the headers we include. These flags
39 * are reserved ids according to C99, so we need to disable a
40 * warning for that. */
41#pragma GCC diagnostic push
42#pragma GCC diagnostic ignored "-Wreserved-id-macro"
43#endif
44#if !defined(_XOPEN_SOURCE)
45#define _XOPEN_SOURCE 600 /* For PATH_MAX on linux */
46/* This should also be sufficient for "realpath", according to
47 * http://man7.org/linux/man-pages/man3/realpath.3.html, but in
48 * reality it does not seem to work. */
49/* In case this causes a problem, disable the warning:
50 * #pragma GCC diagnostic ignored "-Wimplicit-function-declaration"
51 * #pragma clang diagnostic ignored "-Wimplicit-function-declaration"
52 */
53#endif
54#endif
55
56#if !defined(IGNORE_UNUSED_RESULT)
57#define IGNORE_UNUSED_RESULT(a) ((void)((a) && 1))
58#endif
59
60#if defined(__cplusplus) && (__cplusplus >= 201103L)
61#define NO_RETURN [[noreturn]]
62#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
63#define NO_RETURN _Noreturn
64#elif defined(__GNUC__)
65#define NO_RETURN __attribute((noreturn))
66#else
67#define NO_RETURN
68#endif
69
70/* Use same defines as in civetweb.c before including system headers. */
71#if !defined(_LARGEFILE_SOURCE)
72#define _LARGEFILE_SOURCE /* For fseeko(), ftello() */
73#endif
74#if !defined(_FILE_OFFSET_BITS)
75#define _FILE_OFFSET_BITS 64 /* Use 64-bit file offsets by default */
76#endif
77#if !defined(__STDC_FORMAT_MACROS)
78#define __STDC_FORMAT_MACROS /* <inttypes.h> wants this for C++ */
79#endif
80#if !defined(__STDC_LIMIT_MACROS)
81#define __STDC_LIMIT_MACROS /* C++ wants that for INT64_MAX */
82#endif
83
84#if defined(__clang__)
85/* Enable reserved-id-macro warning again. */
86#pragma GCC diagnostic pop
87#endif
88
89#include <ctype.h>
90#include <errno.h>
91#include <limits.h>
92#include <signal.h>
93#include <stdarg.h>
94#include <stddef.h>
95#include <stdint.h>
96#include <stdio.h>
97#include <stdlib.h>
98#include <string.h>
99#include <sys/stat.h>
100
101#include "civetweb.h"
102
103#undef printf
104#define printf \
105 DO_NOT_USE_THIS_FUNCTION__USE_fprintf /* Required for unit testing */
106
107#if defined(_WIN32) /* WINDOWS include block */
108#if !defined(_WIN32_WINNT)
109#define _WIN32_WINNT 0x0501 /* for tdm-gcc so we can use getconsolewindow */
110#endif
111#undef UNICODE
112#include <io.h>
113#include <shlobj.h>
114#include <windows.h>
115#include <winsvc.h>
116
117#define getcwd(a, b) (_getcwd(a, b))
118#if !defined(__MINGW32__)
119extern char *_getcwd(char *buf, size_t size);
120#endif
121
122#if !defined(PATH_MAX)
123#define PATH_MAX MAX_PATH
124#endif
125
126#if !defined(S_ISDIR)
127#define S_ISDIR(x) ((x)&_S_IFDIR)
128#endif
129
130#define DIRSEP '\\'
131#define snprintf _snprintf
132#define vsnprintf _vsnprintf
133#define sleep(x) (Sleep((x)*1000))
134#define WINCDECL __cdecl
135#define abs_path(rel, abs, abs_size) (_fullpath((abs), (rel), (abs_size)))
136
137#else /* defined(_WIN32) - WINDOWS / UNIX include \
138 block */
139
140#include <sys/utsname.h>
141#include <sys/wait.h>
142#include <unistd.h>
143
144#define DIRSEP '/'
145#define WINCDECL
146#define abs_path(rel, abs, abs_size) (realpath((rel), (abs)))
147
148#endif /* defined(_WIN32) - WINDOWS / UNIX include \
149 block */
150
151#if !defined(DEBUG_ASSERT)
152#if defined(DEBUG)
153
154#if defined(_MSC_VER)
155/* DEBUG_ASSERT has some const conditions */
156#pragma warning(disable : 4127)
157#endif
158
159#define DEBUG_ASSERT(cond) \
160 do { \
161 if (!(cond)) { \
162 fprintf(stderr, "ASSERTION FAILED: %s", #cond); \
163 exit(2); /* Exit with error */ \
164 } \
165 } while (0)
166
167#else
168#define DEBUG_ASSERT(cond)
169#endif /* DEBUG */
170#endif
171
172#if !defined(PATH_MAX)
173#define PATH_MAX (1024)
174#endif
175
176#define MAX_OPTIONS (100) /* TODO: Read from civetweb.c ? */
177#define MAX_CONF_FILE_LINE_SIZE (8 * 1024)
178
181};
182
183/* Exit flag for the main loop (read and written by different threads, thus
184 * volatile). */
185volatile int g_exit_flag = 0; /* 0 = continue running main loop */
186
187static char g_server_base_name[40]; /* Set by init_server_name() */
188
189static const char *g_server_name; /* Default from init_server_name,
190 * updated later from the server config */
191static const char *g_icon_name; /* Default from init_server_name,
192 * updated later from the server config */
193static const char *g_website; /* Default from init_server_name,
194 * updated later from the server config */
195static int g_num_add_domains; /* Default from init_server_name,
196 * updated later from the server config */
197static const char **g_add_domain; /* Default from init_server_name,
198 * updated later from the server config */
199static int g_hide_tray = 0; /* Default = do not hide (0),
200 * updated later from the server config */
201
202static char *g_system_info; /* Set by init_system_info() */
204 ""; /* Set by
205 * process_command_line_arguments() */
206
207static struct mg_context *g_ctx; /* Set by start_civetweb() */
208static struct tuser_data
209 g_user_data; /* Passed to mg_start() by start_civetweb() */
210
211#if !defined(CONFIG_FILE)
212#define CONFIG_FILE "civetweb.conf"
213#endif /* !CONFIG_FILE */
214
215#if !defined(PASSWORDS_FILE_NAME)
216#define PASSWORDS_FILE_NAME ".htpasswd"
217#endif
218
219/* backup config file */
220#if !defined(CONFIG_FILE2) && defined(__linux__)
221#define CONFIG_FILE2 "/usr/local/etc/civetweb.conf"
222#endif
223
224enum {
230#if defined(DAEMONIZE)
231 ENABLE_DAEMONIZE,
232#endif
233
236
238 {"title", MG_CONFIG_TYPE_STRING, NULL},
239 {"icon", MG_CONFIG_TYPE_STRING, NULL},
240 {"website", MG_CONFIG_TYPE_STRING, NULL},
241 {"add_domain", MG_CONFIG_TYPE_STRING_LIST, NULL},
242 {"hide_tray", MG_CONFIG_TYPE_BOOLEAN, NULL},
243#if defined(DAEMONIZE)
244 {"daemonize", MG_CONFIG_TYPE_BOOLEAN, "no"},
245#endif
246
248
249
250static void WINCDECL
251signal_handler(int sig_num)
252{
253 g_exit_flag = sig_num;
254}
255
256
257static NO_RETURN void
258die(const char *fmt, ...)
259{
260 va_list ap;
261 char msg[1024] = "";
262
263 va_start(ap, fmt);
264 (void)vsnprintf(msg, sizeof(msg) - 1, fmt, ap);
265 msg[sizeof(msg) - 1] = 0;
266 va_end(ap);
267
268#if defined(_WIN32)
269 MessageBox(NULL, msg, "Error", MB_ICONERROR | MB_OK);
270#else
271 fprintf(stderr, "%s\n", msg);
272#endif
273
274 exit(EXIT_FAILURE);
275}
276
277
278static void
279warn(const char *fmt, ...)
280{
281 va_list ap;
282 char msg[512] = "";
283
284 va_start(ap, fmt);
285 (void)vsnprintf(msg, sizeof(msg) - 1, fmt, ap);
286 msg[sizeof(msg) - 1] = 0;
287 va_end(ap);
288
289#if defined(_WIN32)
290 MessageBox(NULL, msg, "Warning", MB_OK);
291#else
292 fprintf(stderr, "%s\n", msg);
293#endif
294}
295
296
297#if defined(WIN32)
298static int MakeConsole(void);
299#endif
300
301
302static void
304{
305#if defined(BUILD_DATE)
306 const char *bd = BUILD_DATE;
307#else
308 const char *bd = __DATE__;
309#endif
310#if defined(WIN32)
311 (void)MakeConsole();
312#endif
313
314 fprintf(stderr, "CivetWeb v%s, built on %s\n", mg_version(), bd);
315}
316
317
318static NO_RETURN void
319show_usage_and_exit(const char *exeName)
320{
321 const struct mg_option *options;
322 int i;
323
324 if (exeName == 0 || *exeName == 0) {
325 exeName = "civetweb";
326 }
327
329
330 fprintf(stderr, "\nUsage:\n");
331 fprintf(stderr, " Start server with a set of options:\n");
332 fprintf(stderr, " %s [config_file]\n", exeName);
333 fprintf(stderr, " %s [-option value ...]\n", exeName);
334 fprintf(stderr, " Run as client:\n");
335 fprintf(stderr, " %s -C url\n", exeName);
336 fprintf(stderr, " Show system information:\n");
337 fprintf(stderr, " %s -I\n", exeName);
338 fprintf(stderr, " Add user/change password:\n");
339 fprintf(stderr,
340 " %s -A <htpasswd_file> <realm> <user> <passwd>\n",
341 exeName);
342 fprintf(stderr, " Remove user:\n");
343 fprintf(stderr, " %s -R <htpasswd_file> <realm> <user>\n", exeName);
344 fprintf(stderr, "\nOPTIONS:\n");
345
346 options = mg_get_valid_options();
347 for (i = 0; options[i].name != NULL; i++) {
348 fprintf(stderr,
349 " -%s %s\n",
350 options[i].name,
351 ((options[i].default_value == NULL)
352 ? "<empty>"
353 : options[i].default_value));
354 }
355
356 options = main_config_options;
357 for (i = 0; options[i].name != NULL; i++) {
358 fprintf(stderr,
359 " -%s %s\n",
360 options[i].name,
361 ((options[i].default_value == NULL)
362 ? "<empty>"
363 : options[i].default_value));
364 }
365
366 exit(EXIT_FAILURE);
367}
368
369
370#if defined(_WIN32) || defined(USE_COCOA) || defined(MAIN_C_UNIT_TEST)
371static const char *config_file_top_comment =
372 "# CivetWeb web server configuration file.\n"
373 "# For detailed description of every option, visit\n"
374 "# https://github.com/civetweb/civetweb/blob/master/docs/UserManual.md\n"
375 "# Lines starting with '#' and empty lines are ignored.\n"
376 "# To make changes, remove leading '#', modify option values,\n"
377 "# save this file and then restart CivetWeb.\n\n";
378
379static const char *
380get_url_to_first_open_port(const struct mg_context *ctx)
381{
382 static char url[128];
383
384#define MAX_PORT_COUNT (32)
385
386 struct mg_server_port ports[MAX_PORT_COUNT];
387 int portNum = mg_get_server_ports(ctx, MAX_PORT_COUNT, ports);
388 int i;
389
390 memset(url, 0, sizeof(url));
391
392 /* Prefer IPv4 http, ignore redirects */
393 for (i = 0; i < portNum; i++) {
394 if ((ports[i].protocol == 1) && (ports[i].is_redirect == 0)
395 && (ports[i].is_ssl == 0)) {
396 snprintf(url, sizeof(url), "http://localhost:%d/", ports[i].port);
397 return url;
398 }
399 }
400 /* Use IPv4 https */
401 for (i = 0; i < portNum; i++) {
402 if ((ports[i].protocol == 1) && (ports[i].is_redirect == 0)
403 && (ports[i].is_ssl == 1)) {
404 snprintf(url, sizeof(url), "https://localhost:%d/", ports[i].port);
405 return url;
406 }
407 }
408 /* Try IPv6 http, ignore redirects */
409 if (portNum > 0) {
410 snprintf(url,
411 sizeof(url),
412 "%s://localhost:%d/",
413 (ports[0].is_ssl ? "https" : "http"),
414 ports[0].port);
415 }
416
417#undef MAX_PORT_COUNT
418
419 return url;
420}
421
422
423#if defined(ENABLE_CREATE_CONFIG_FILE) || defined(MAIN_C_UNIT_TEST)
424static void
425create_config_file(const struct mg_context *ctx, const char *path)
426{
427 const struct mg_option *options;
428 const char *value;
429 FILE *fp;
430 int i;
431
432 /* Create config file if it is not present yet */
433 if ((fp = fopen(path, "r")) != NULL) {
434 fclose(fp);
435 } else if ((fp = fopen(path, "a+")) != NULL) {
436 fprintf(fp, "%s", config_file_top_comment);
437 options = mg_get_valid_options();
438 for (i = 0; options[i].name != NULL; i++) {
439 value = mg_get_option(ctx, options[i].name);
440 fprintf(fp,
441 "# %s %s\n",
442 options[i].name,
443 value ? value : "<value>");
444 }
445 fclose(fp);
446 }
447}
448#endif
449#endif
450
451
452static char *
453sdup(const char *str)
454{
455 size_t len;
456 char *p;
457
458 len = strlen(str) + 1;
459 p = (char *)malloc(len);
460
461 if (p == NULL) {
462 die("Cannot allocate %u bytes", (unsigned)len);
463 }
464
465 memcpy(p, str, len);
466 return p;
467}
468
469
470static const char *
471get_option(const char **options, const char *option_name)
472{
473 int i = 0;
474 const char *opt_value = NULL;
475
476 /* TODO (low, api makeover): options should be an array of key-value-pairs,
477 * like
478 * struct {const char * key, const char * value} options[]
479 * but it currently is an array with
480 * options[2*i] = key, options[2*i + 1] = value
481 * (probably with a MG_LEGACY_INTERFACE definition)
482 */
483 while (options[2 * i] != NULL) {
484 if (strcmp(options[2 * i], option_name) == 0) {
485 opt_value = options[2 * i + 1];
486 break;
487 }
488 i++;
489 }
490 return opt_value;
491}
492
493
494static int
495set_option(const char **options, const char *name, const char *value)
496{
497 int i, type;
498 const struct mg_option *default_options = mg_get_valid_options();
499 const char *multi_sep = NULL;
500
501 for (i = 0; main_config_options[i].name != NULL; i++) {
502 /* These options are evaluated by main.c, not civetweb.c.
503 * Do not add it to options, and return OK */
504 if (!strcmp(name, main_config_options[OPTION_TITLE].name)) {
506 return 1;
507 }
508 if (!strcmp(name, main_config_options[OPTION_ICON].name)) {
509
511 return 1;
512 }
515 return 1;
516 }
518 if (!strcmp(value, "yes")) {
519 g_hide_tray = 1;
520 } else if (!strcmp(value, "no")) {
521 g_hide_tray = 0;
522 }
523 return 1;
524 }
526 if (g_num_add_domains > 0) {
527 g_add_domain = (const char **)realloc(
528 (void *)g_add_domain,
529 sizeof(char *) * ((unsigned)g_num_add_domains + 1u));
530 if (!g_add_domain) {
531 die("Out of memory");
532 }
535 } else {
536 g_add_domain = (const char **)malloc(sizeof(char *));
537 if (!g_add_domain) {
538 die("Out of memory");
539 }
540 g_add_domain[0] = sdup(value);
542 }
543 return 1;
544 }
545 }
546
547 /* Not an option of main.c, so check if it is a CivetWeb server option */
548 type = MG_CONFIG_TYPE_UNKNOWN; /* type "unknown" means "option not found" */
549 for (i = 0; default_options[i].name != NULL; i++) {
550 if (!strcmp(default_options[i].name, name)) {
551 type = default_options[i].type;
552 break; /* no need to search for another option */
553 }
554 }
555
556 switch (type) {
557
559 /* unknown option */
560 return 0; /* return error */
561
563 /* integer number >= 0, e.g. number of threads */
564 {
565 char *chk = 0;
566 unsigned long num = strtoul(value, &chk, 10);
567 (void)num; /* do not check value, only syntax */
568 if ((chk == NULL) || (*chk != 0) || (chk == value)) {
569 /* invalid number */
570 return 0;
571 }
572 }
573 break;
574
576 /* any text */
577 break;
578
580 /* list of text items, separated by , */
581 multi_sep = ",";
582 break;
583
585 /* lines of text, separated by carriage return line feed */
586 multi_sep = "\r\n";
587 break;
588
590 /* boolean value, yes or no */
591 if ((0 != strcmp(value, "yes")) && (0 != strcmp(value, "no"))) {
592 /* invalid boolean */
593 return 0;
594 }
595 break;
596
598 /* boolean value, yes or no */
599 if ((0 != strcmp(value, "yes")) && (0 != strcmp(value, "no"))
600 && (0 != strcmp(value, "optional"))) {
601 /* invalid boolean */
602 return 0;
603 }
604 break;
605
608 /* TODO (low): check this option when it is set, instead of calling
609 * verify_existence later */
610 break;
611
613 /* list of patterns, separated by | */
614 multi_sep = "|";
615 break;
616
617 default:
618 die("Unknown option type - option %s", name);
619 }
620
621 for (i = 0; i < MAX_OPTIONS; i++) {
622 if (options[2 * i] == NULL) {
623 /* Option not set yet. Add new option */
624 options[2 * i] = sdup(name);
625 options[2 * i + 1] = sdup(value);
626 options[2 * i + 2] = NULL;
627 break;
628 } else if (!strcmp(options[2 * i], name)) {
629 if (multi_sep) {
630 /* Option already set. Append new value. */
631 char *s =
632 (char *)malloc(strlen(options[2 * i + 1])
633 + strlen(multi_sep) + strlen(value) + 1);
634 if (!s) {
635 die("Out of memory");
636 }
637 sprintf(s, "%s%s%s", options[2 * i + 1], multi_sep, value);
638 free((char *)options[2 * i + 1]);
639 options[2 * i + 1] = s;
640 } else {
641 /* Option already set. Overwrite */
642 free((char *)options[2 * i + 1]);
643 options[2 * i + 1] = sdup(value);
644 }
645 break;
646 }
647 }
648
649 if (i == MAX_OPTIONS) {
650 die("Too many options specified");
651 }
652
653 if (options[2 * i] == NULL) {
654 die("Out of memory");
655 }
656 if (options[2 * i + 1] == NULL) {
657 die("Illegal escape sequence, or out of memory");
658 }
659
660 /* option set correctly */
661 return 1;
662}
663
664
665static int
666read_config_file(const char *config_file, const char **options)
667{
668 char line[MAX_CONF_FILE_LINE_SIZE], *p;
669 FILE *fp = NULL;
670 size_t i, j, line_no = 0;
671
672 /* Open the config file */
673 fp = fopen(config_file, "r");
674 if (fp == NULL) {
675 /* Failed to open the file. Keep errno for the caller. */
676 return 0;
677 }
678
679 /* Load config file settings first */
680 fprintf(stdout, "Loading config file %s\n", config_file);
681
682 /* Loop over the lines in config file */
683 while (fgets(line, sizeof(line), fp) != NULL) {
684
685 if (!line_no && !memcmp(line, "\xEF\xBB\xBF", 3)) {
686 /* strip UTF-8 BOM */
687 p = line + 3;
688 } else {
689 p = line;
690 }
691 line_no++;
692
693 /* Ignore empty lines and comments */
694 for (i = 0; isspace((unsigned char)p[i]);)
695 i++;
696 if (p[i] == '#' || p[i] == '\0') {
697 continue;
698 }
699
700 /* Skip spaces, \r and \n at the end of the line */
701 for (j = strlen(p); (j > 0)
702 && (isspace((unsigned char)p[j - 1])
703 || iscntrl((unsigned char)p[j - 1]));)
704 p[--j] = 0;
705
706 /* Find the space character between option name and value */
707 for (j = i; !isspace((unsigned char)p[j]) && (p[j] != 0);)
708 j++;
709
710 /* Terminate the string - then the string at (p+i) contains the
711 * option name */
712 p[j] = 0;
713 j++;
714
715 /* Trim additional spaces between option name and value - then
716 * (p+j) contains the option value */
717 while (isspace((unsigned char)p[j])) {
718 j++;
719 }
720
721 /* Set option */
722 if (!set_option(options, p + i, p + j)) {
723 fprintf(stderr,
724 "%s: line %d is invalid, ignoring it:\n %s",
725 config_file,
726 (int)line_no,
727 p);
728 }
729 }
730
731 (void)fclose(fp);
732
733 return 1;
734}
735
736
737static void
738process_command_line_arguments(int argc, char *argv[], const char **options)
739{
740 char *p;
741 size_t i, cmd_line_opts_start = 1;
742#if defined(CONFIG_FILE2)
743 FILE *fp = NULL;
744#endif
745
746 /* Should we use a config file ? */
747 if ((argc > 1) && (argv[1] != NULL) && (argv[1][0] != '-')
748 && (argv[1][0] != 0)) {
749 /* The first command line parameter is a config file name. */
751 sizeof(g_config_file_name) - 1,
752 "%s",
753 argv[1]);
754 cmd_line_opts_start = 2;
755 } else if ((p = strrchr(argv[0], DIRSEP)) == NULL) {
756 /* No config file set. No path in arg[0] found.
757 * Use default file name in the current path. */
759 sizeof(g_config_file_name) - 1,
760 "%s",
762 } else {
763 /* No config file set. Path to exe found in arg[0].
764 * Use default file name next to the executable. */
766 sizeof(g_config_file_name) - 1,
767 "%.*s%c%s",
768 (int)(p - argv[0]),
769 argv[0],
770 DIRSEP,
772 }
773 g_config_file_name[sizeof(g_config_file_name) - 1] = 0;
774
775#if defined(CONFIG_FILE2)
776 fp = fopen(g_config_file_name, "r");
777
778 /* try alternate config file */
779 if (fp == NULL) {
780 fp = fopen(CONFIG_FILE2, "r");
781 if (fp != NULL) {
782 strcpy(g_config_file_name, CONFIG_FILE2);
783 }
784 }
785 if (fp != NULL) {
786 fclose(fp);
787 }
788#endif
789
790 /* read all configurations from a config file */
791 if (0 == read_config_file(g_config_file_name, options)) {
792 if (cmd_line_opts_start == 2) {
793 /* If config file was set in command line and open failed, die. */
794 /* Errno will still hold the error from fopen. */
795 die("Cannot open config file %s: %s",
797 strerror(errno));
798 }
799 /* Otherwise: CivetWeb can work without a config file */
800 }
801
802 /* If we're under MacOS and started by launchd, then the second
803 argument is process serial number, -psn_.....
804 In this case, don't process arguments at all. */
805 if (argv[1] == NULL || memcmp(argv[1], "-psn_", 5) != 0) {
806 /* Handle command line flags.
807 They override config file and default settings. */
808 for (i = cmd_line_opts_start; argv[i] != NULL; i += 2) {
809 if (argv[i][0] != '-' || argv[i + 1] == NULL) {
810 show_usage_and_exit(argv[0]);
811 }
812 if (!set_option(options, &argv[i][1], argv[i + 1])) {
813 fprintf(
814 stderr,
815 "command line option is invalid, ignoring it:\n %s %s\n",
816 argv[i],
817 argv[i + 1]);
818 }
819 }
820 }
821}
822
823
824static void
826{
827 int len = mg_get_system_info(NULL, 0);
828 if (len > 0) {
829 g_system_info = (char *)malloc((unsigned)len + 1);
830 (void)mg_get_system_info(g_system_info, len + 1);
831 } else {
832 g_system_info = sdup("Not available");
833 }
834}
835
836
837static void
839{
841 == NUM_MAIN_OPTIONS + 1);
842 DEBUG_ASSERT((strlen(mg_version()) + 12) < sizeof(g_server_base_name));
844 sizeof(g_server_base_name),
845 "CivetWeb V%s",
846 mg_version());
849 g_website = "http://civetweb.github.io/civetweb/";
852}
853
854
855static void
857{
859}
860
861
862static int
863is_path_absolute(const char *path)
864{
865#if defined(_WIN32)
866 return path != NULL
867 && ((path[0] == '\\' && path[1] == '\\') || /* UNC path, e.g.
868 \\server\dir */
869 (isalpha((unsigned char)path[0]) && path[1] == ':'
870 && path[2] == '\\')); /* E.g. X:\dir */
871#else
872 return path != NULL && path[0] == '/';
873#endif
874}
875
876
877static int
878verify_existence(const char **options, const char *option_name, int must_be_dir)
879{
880 struct stat st;
881 const char *path = get_option(options, option_name);
882
883#if defined(_WIN32)
884 wchar_t wbuf[1024];
885 char mbbuf[1024];
886 int len;
887
888 if (path) {
889 memset(wbuf, 0, sizeof(wbuf));
890 memset(mbbuf, 0, sizeof(mbbuf));
891 len = MultiByteToWideChar(
892 CP_UTF8, 0, path, -1, wbuf, sizeof(wbuf) / sizeof(wbuf[0]) - 1);
893 wcstombs(mbbuf, wbuf, sizeof(mbbuf) - 1);
894 path = mbbuf;
895 (void)len;
896 }
897#endif
898
899 if (path != NULL
900 && (stat(path, &st) != 0
901 || ((S_ISDIR(st.st_mode) ? 1 : 0) != must_be_dir))) {
902 warn("Invalid path for %s: [%s]: (%s). Make sure that path is either "
903 "absolute, or it is relative to civetweb executable.",
904 option_name,
905 path,
906 strerror(errno));
907 return 0;
908 }
909 return 1;
910}
911
912
913static void
914set_absolute_path(const char *options[],
915 const char *option_name,
916 const char *path_to_civetweb_exe)
917{
918 char path[PATH_MAX] = "", absolute[PATH_MAX] = "";
919 const char *option_value;
920 const char *p;
921
922 /* Check whether option is already set */
923 option_value = get_option(options, option_name);
924
925 /* If option is already set and it is an absolute path,
926 leave it as it is -- it's already absolute. */
927 if (option_value != NULL && !is_path_absolute(option_value)) {
928 /* Not absolute. Use the directory where civetweb executable lives
929 be the relative directory for everything.
930 Extract civetweb executable directory into path. */
931 if ((p = strrchr(path_to_civetweb_exe, DIRSEP)) == NULL) {
932 IGNORE_UNUSED_RESULT(getcwd(path, sizeof(path)));
933 } else {
934 snprintf(path,
935 sizeof(path) - 1,
936 "%.*s",
937 (int)(p - path_to_civetweb_exe),
938 path_to_civetweb_exe);
939 path[sizeof(path) - 1] = 0;
940 }
941
942 strncat(path, "/", sizeof(path) - strlen(path) - 1);
943 strncat(path, option_value, sizeof(path) - strlen(path) - 1);
944
945 /* Absolutize the path, and set the option */
946 IGNORE_UNUSED_RESULT(abs_path(path, absolute, sizeof(absolute)));
947 set_option(options, option_name, absolute);
948 }
949}
950
951
952#if defined(USE_LUA)
953
954#include "civetweb_private_lua.h"
955
956#endif
957
958
959#if defined(USE_DUKTAPE)
960
961#include "duktape.h"
962
963static int
964run_duktape(const char *file_name)
965{
966 duk_context *ctx = NULL;
967
969 if (!ctx) {
970 fprintf(stderr, "Failed to create a Duktape heap.\n");
971 goto finished;
972 }
973
974 if (duk_peval_file(ctx, file_name) != 0) {
975 fprintf(stderr, "%s\n", duk_safe_to_string(ctx, -1));
976 goto finished;
977 }
978 duk_pop(ctx); /* ignore result */
979
980finished:
981 duk_destroy_heap(ctx);
982
983 return 0;
984}
985#endif
986
987
988#if defined(__MINGW32__) || defined(__MINGW64__)
989/* For __MINGW32/64_MAJOR/MINOR_VERSION define */
990#include <_mingw.h>
991#endif
992
993
994static int
995run_client(const char *url_arg)
996{
997 /* connection data */
998 char *url = sdup(url_arg); /* OOM will cause program to exit */
999 char *host;
1000 char *resource;
1001 int is_ssl = 0;
1002 unsigned long port = 0;
1003 size_t sep;
1004 char *endp = 0;
1005 char empty[] = "";
1006
1007 /* connection object */
1008 struct mg_connection *conn;
1009 char ebuf[1024] = {0};
1010
1011#if 0 /* Unreachable code, since sdup will never return NULL */
1012 /* Check out of memory */
1013 if (!url) {
1014 fprintf(stderr, "Out of memory\n");
1015 return 0;
1016 }
1017#endif
1018
1019 /* Check parameter */
1020 if (!strncmp(url, "http://", 7)) {
1021 host = url + 7;
1022 port = 80;
1023 } else if (!strncmp(url, "https://", 8)) {
1024 host = url + 8;
1025 is_ssl = 1;
1026 port = 443;
1027 } else {
1028 fprintf(stderr, "URL must start with http:// or https://\n");
1029 free(url);
1030 return 0;
1031 }
1032 if ((host[0] <= 32) || (host[0] > 126) || (host[0] == '/')
1033 || (host[0] == ':')) {
1034 fprintf(stderr, "Invalid host\n");
1035 free(url);
1036 return 0;
1037 }
1038
1039 sep = strcspn(host, "/:");
1040 switch (host[sep]) {
1041 case 0:
1042 resource = empty;
1043 break;
1044 case '/':
1045 host[sep] = 0;
1046 resource = host + sep + 1;
1047 break;
1048 case ':':
1049 host[sep] = 0;
1050 port = strtoul(host + sep + 1, &endp, 10);
1051 if (!endp || (*endp != '/' && *endp != 0) || (port < 1)
1052 || (port > 0xFFFF)) {
1053 fprintf(stderr, "Invalid port\n");
1054 free(url);
1055 return 0;
1056 }
1057 if (*endp) {
1058 *endp = 0;
1059 resource = endp + 1;
1060 } else {
1061 resource = empty;
1062 }
1063 break;
1064 default:
1065 fprintf(stderr, "Syntax error\n");
1066 free(url);
1067 return 0;
1068 }
1069
1070 fprintf(stdout, "Protocol: %s\n", is_ssl ? "https" : "http");
1071 fprintf(stdout, "Host: %s\n", host);
1072 fprintf(stdout, "Port: %lu\n", port);
1073 fprintf(stdout, "Resource: %s\n", resource);
1074
1075 /* Initialize library */
1076 if (is_ssl) {
1078 } else {
1080 }
1081
1082 /* Connect to host */
1083 conn = mg_connect_client(host, (int)port, is_ssl, ebuf, sizeof(ebuf));
1084 if (conn) {
1085 /* Connecting to server worked */
1086 char buf[1024] = {0};
1087 int ret;
1088
1089 fprintf(stdout, "Connected to %s\n", host);
1090
1091 /* Send GET request */
1092 mg_printf(conn,
1093 "GET /%s HTTP/1.1\r\n"
1094 "Host: %s\r\n"
1095 "Connection: close\r\n"
1096 "\r\n",
1097 resource,
1098 host);
1099
1100 /* Wait for server to respond with a HTTP header */
1101 ret = mg_get_response(conn, ebuf, sizeof(ebuf), 10000);
1102
1103 if (ret >= 0) {
1104 const struct mg_response_info *ri = mg_get_response_info(conn);
1105
1106 fprintf(stdout,
1107 "Response info: %i %s\n",
1108 ri->status_code,
1109 ri->status_text);
1110
1111 /* Respond reader read. Read body (if any) */
1112 ret = mg_read(conn, buf, sizeof(buf));
1113 while (ret > 0) {
1114 fwrite(buf, 1, (unsigned)ret, stdout);
1115 ret = mg_read(conn, buf, sizeof(buf));
1116 }
1117
1118 fprintf(stdout, "Closing connection to %s\n", host);
1119
1120 } else {
1121 /* Server did not reply to HTTP request */
1122 fprintf(stderr, "Got no response from %s:\n%s\n", host, ebuf);
1123 }
1124 mg_close_connection(conn);
1125
1126 } else {
1127 /* Connecting to server failed */
1128 fprintf(stderr, "Error connecting to %s:\n%s\n", host, ebuf);
1129 }
1130
1131 /* Free memory and exit library */
1132 free(url);
1134 return 1;
1135}
1136
1137
1138static int
1139sanitize_options(const char *options[] /* server options */,
1140 const char *arg0 /* argv[0] */)
1141{
1142 int ok = 1;
1143 /* Make sure we have absolute paths for files and directories */
1144 set_absolute_path(options, "document_root", arg0);
1145 set_absolute_path(options, "put_delete_auth_file", arg0);
1146 set_absolute_path(options, "cgi_interpreter", arg0);
1147 set_absolute_path(options, "access_log_file", arg0);
1148 set_absolute_path(options, "error_log_file", arg0);
1149 set_absolute_path(options, "global_auth_file", arg0);
1150#if defined(USE_LUA)
1151 set_absolute_path(options, "lua_preload_file", arg0);
1152#endif
1153 set_absolute_path(options, "ssl_certificate", arg0);
1154
1155 /* Make extra verification for certain options */
1156 if (!verify_existence(options, "document_root", 1))
1157 ok = 0;
1158 if (!verify_existence(options, "cgi_interpreter", 0))
1159 ok = 0;
1160 if (!verify_existence(options, "ssl_certificate", 0))
1161 ok = 0;
1162 if (!verify_existence(options, "ssl_ca_path", 1))
1163 ok = 0;
1164 if (!verify_existence(options, "ssl_ca_file", 0))
1165 ok = 0;
1166#if defined(USE_LUA)
1167 if (!verify_existence(options, "lua_preload_file", 0))
1168 ok = 0;
1169#endif
1170 return ok;
1171}
1172
1173
1174#ifdef _WIN32
1175/* Forward declaration for Windows only */
1176static void show_settings_dialog(void);
1177#endif
1178
1179
1180static void
1181start_civetweb(int argc, char *argv[])
1182{
1183 struct mg_callbacks callbacks;
1184 const char *options[2 * MAX_OPTIONS + 1];
1185 struct mg_init_data init;
1186 struct mg_error_data error;
1187 char error_text[256];
1188 int i;
1189
1190 /* Start option -I:
1191 * Show system information and exit
1192 * This is very useful for diagnosis. */
1193 if (argc > 1 && !strcmp(argv[1], "-I")) {
1194
1195#if defined(WIN32)
1196 (void)MakeConsole();
1197#endif
1198 fprintf(stdout,
1199 "\n%s (%s)\n%s\n",
1203
1204 exit(EXIT_SUCCESS);
1205 }
1206
1207 /* Edit passwords file: Add user or change password, if -A option is
1208 * specified */
1209 if (argc > 1 && !strcmp(argv[1], "-A")) {
1210 if (argc != 6) {
1211 show_usage_and_exit(argv[0]);
1212 }
1213 exit(mg_modify_passwords_file(argv[2], argv[3], argv[4], argv[5])
1214 ? EXIT_SUCCESS
1215 : EXIT_FAILURE);
1216 }
1217
1218 /* Edit passwords file: Remove user, if -R option is specified */
1219 if (argc > 1 && !strcmp(argv[1], "-R")) {
1220 if (argc != 5) {
1221 show_usage_and_exit(argv[0]);
1222 }
1223 exit(mg_modify_passwords_file(argv[2], argv[3], argv[4], NULL)
1224 ? EXIT_SUCCESS
1225 : EXIT_FAILURE);
1226 }
1227
1228 /* Client mode */
1229 if (argc > 1 && !strcmp(argv[1], "-C")) {
1230 if (argc != 3) {
1231 show_usage_and_exit(argv[0]);
1232 }
1233
1234 exit(run_client(argv[2]) ? EXIT_SUCCESS : EXIT_FAILURE);
1235 }
1236
1237 /* Call Lua with additional CivetWeb specific Lua functions, if -L option
1238 * is specified */
1239 if (argc > 1 && !strcmp(argv[1], "-L")) {
1240
1241#if defined(USE_LUA)
1242 if (argc != 3) {
1243 show_usage_and_exit(argv[0]);
1244 }
1245#if defined(WIN32)
1246 (void)MakeConsole();
1247#endif
1248 exit(run_lua(argv[2]));
1249#else
1251 fprintf(stderr, "\nError: Lua support not enabled\n");
1252 exit(EXIT_FAILURE);
1253#endif
1254 }
1255
1256 /* Call Duktape, if -E option is specified */
1257 if (argc > 1 && !strcmp(argv[1], "-E")) {
1258
1259#if defined(USE_DUKTAPE)
1260 if (argc != 3) {
1261 show_usage_and_exit(argv[0]);
1262 }
1263#if defined(WIN32)
1264 (void)MakeConsole();
1265#endif
1266 exit(run_duktape(argv[2]));
1267#else
1269 fprintf(stderr, "\nError: Ecmascript support not enabled\n");
1270 exit(EXIT_FAILURE);
1271#endif
1272 }
1273
1274 /* Show usage if -h or --help options are specified */
1275 if (argc == 2
1276 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "-H")
1277 || !strcmp(argv[1], "--help"))) {
1278 show_usage_and_exit(argv[0]);
1279 }
1280
1281 /* Initialize options structure */
1282 memset((void *)options, 0, sizeof(options));
1283 set_option(options, "document_root", ".");
1284
1285 /* Update config based on command line arguments */
1286 process_command_line_arguments(argc, argv, options);
1287
1288 i = sanitize_options(options, argv[0]);
1289 if (!i) {
1290 die("Invalid options");
1291 }
1292
1293 /* Setup signal handler: quit on Ctrl-C */
1294 signal(SIGTERM, signal_handler);
1295 signal(SIGINT, signal_handler);
1296
1297#if defined(DAEMONIZE)
1298 /* Daemonize */
1299 for (i = 0; options[i] != NULL; i++) {
1300 if (strcmp(options[i], "daemonize") == 0) {
1301 if (options[i + 1] != NULL) {
1302 if (mg_strcasecmp(options[i + 1], "yes") == 0) {
1303 fprintf(stdout, "daemonize.\n");
1304 if (daemon(0, 0) != 0) {
1305 fprintf(stdout, "Failed to daemonize main process.\n");
1306 exit(EXIT_FAILURE);
1307 }
1308 FILE *fp;
1309 if ((fp = fopen(PID_FILE, "w")) == 0) {
1310 fprintf(stdout, "Can not open %s.\n", PID_FILE);
1311 exit(EXIT_FAILURE);
1312 }
1313 fprintf(fp, "%d", getpid());
1314 fclose(fp);
1315 }
1316 }
1317 break;
1318 }
1319 }
1320#endif
1321
1322 /* Initialize user data */
1323 memset(&g_user_data, 0, sizeof(g_user_data));
1324
1325 memset(&callbacks, 0, sizeof(callbacks));
1326
1327 memset(&init, 0, sizeof(init));
1328 init.callbacks = &callbacks;
1329 init.configuration_options = options;
1330 init.user_data = &g_user_data;
1331
1332 memset(&error, 0, sizeof(error));
1333 error.text = error_text;
1334 error.text_buffer_size = sizeof(error_text);
1335
1336 /* Start Civetweb */
1337 g_ctx = mg_start2(&init, &error);
1338
1339 /* mg_start copies all options to an internal buffer.
1340 * The options data field here is not required anymore. */
1341 for (i = 0; options[i] != NULL; i++) {
1342 free((char *)options[i]);
1343 }
1344
1345 /* If mg_start fails, it returns NULL */
1346 if (g_ctx == NULL) {
1347#ifdef _WIN32
1348 /* On Windows: provide option to edit configuration file. */
1349 char msgboxtxt[1024];
1350 int ret;
1351 sprintf(msgboxtxt,
1352 "Failed to start %s with code %u:\n%s\n\nEdit settings?",
1354 error.code,
1355 error_text);
1356 ret =
1357 MessageBox(NULL, msgboxtxt, "Error", MB_ICONERROR | MB_YESNOCANCEL);
1358 if (ret == IDYES) {
1359 show_settings_dialog();
1360
1361 /* Hitting "save" will also restart the server. */
1362 if (g_ctx != NULL) {
1363 return;
1364 }
1365 }
1366 exit(EXIT_FAILURE);
1367#else
1368 die("Failed to start %s with code %u:\n%s",
1370 error.code,
1371 error_text);
1372#endif
1373 }
1374
1375#if defined(MG_EXPERIMENTAL_INTERFACES)
1376 for (i = 0; i < g_num_add_domains; i++) {
1377
1378 int j;
1379 memset(options, 0, sizeof(options));
1380 set_option(options, "document_root", ".");
1381
1382 if (0 == read_config_file(g_add_domain[i], options)) {
1383 die("Cannot open config file %s: %s",
1384 g_add_domain[i],
1385 strerror(errno));
1386 }
1387
1388 j = sanitize_options(options, argv[0]);
1389 if (!j) {
1390 die("Invalid options");
1391 }
1392
1393 j = mg_start_domain(g_ctx, (const char **)options);
1394 if (j < 0) {
1395 die("Error loading domain file %s: %i", g_add_domain[i], j);
1396 } else {
1397 fprintf(stdout, "Domain file %s loaded\n", g_add_domain[i]);
1398 }
1399
1400 for (j = 0; options[j] != NULL; j++) {
1401 free((void *)options[j]);
1402 }
1403 }
1404#endif
1405}
1406
1407
1408static void
1410{
1411 mg_stop(g_ctx);
1412}
1413
1414
1415#if defined(_WIN32)
1416/* Win32 has a small GUI.
1417 * Define some GUI elements and Windows message handlers. */
1418
1419enum {
1420 ID_ICON = 100,
1421 ID_QUIT,
1422 ID_SETTINGS,
1423 ID_SEPARATOR,
1424 ID_INSTALL_SERVICE,
1425 ID_REMOVE_SERVICE,
1426 ID_STATIC,
1427 ID_GROUP,
1428 ID_PASSWORD,
1429 ID_SAVE,
1430 ID_RESET_DEFAULTS,
1431 ID_RESET_FILE,
1432 ID_RESET_ACTIVE,
1433 ID_STATUS,
1434 ID_CONNECT,
1435 ID_ADD_USER,
1436 ID_ADD_USER_NAME,
1437 ID_ADD_USER_REALM,
1438 ID_INPUT_LINE,
1439 ID_SYSINFO,
1440 ID_WEBSITE,
1441
1442 /* All dynamically created text boxes for options have IDs starting from
1443 ID_CONTROLS, incremented by one. */
1444 ID_CONTROLS = 200,
1445
1446 /* Text boxes for files have "..." buttons to open file browser. These
1447 buttons have IDs that are ID_FILE_BUTTONS_DELTA higher than associated
1448 text box ID. */
1449 ID_FILE_BUTTONS_DELTA = 1000
1450};
1451
1452
1453static HICON hIcon;
1454static SERVICE_STATUS ss;
1455static SERVICE_STATUS_HANDLE hStatus;
1456static const char *service_magic_argument = "--";
1457static NOTIFYICONDATA TrayIcon;
1458static UINT msg_taskbar_created;
1459
1460static void WINAPI
1461ControlHandler(DWORD code)
1462{
1463 if (code == SERVICE_CONTROL_STOP || code == SERVICE_CONTROL_SHUTDOWN) {
1464 ss.dwWin32ExitCode = 0;
1465 ss.dwCurrentState = SERVICE_STOPPED;
1466 }
1467 SetServiceStatus(hStatus, &ss);
1468}
1469
1470
1471static void WINAPI
1472ServiceMain(void)
1473{
1474 ss.dwServiceType = SERVICE_WIN32;
1475 ss.dwCurrentState = SERVICE_RUNNING;
1476 ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
1477
1478 hStatus = RegisterServiceCtrlHandler(g_server_name, ControlHandler);
1479 SetServiceStatus(hStatus, &ss);
1480
1481 while (ss.dwCurrentState == SERVICE_RUNNING) {
1482 Sleep(1000);
1483 }
1484 stop_civetweb();
1485
1486 ss.dwCurrentState = SERVICE_STOPPED;
1487 ss.dwWin32ExitCode = (DWORD)-1;
1488 SetServiceStatus(hStatus, &ss);
1489}
1490
1491
1492static void
1493show_error(void)
1494{
1495 char buf[256];
1496 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
1497 NULL,
1498 GetLastError(),
1499 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
1500 buf,
1501 sizeof(buf),
1502 NULL);
1503 MessageBox(NULL, buf, "Error", MB_OK);
1504}
1505
1506
1507static void
1508save_config(HWND hDlg, FILE *fp)
1509{
1510 char value[2000] = "";
1511 const char *default_value;
1512 const struct mg_option *options;
1513 int i, id;
1514
1515 fprintf(fp, "%s", config_file_top_comment);
1516 options = mg_get_valid_options();
1517 for (i = 0; options[i].name != NULL; i++) {
1518 id = ID_CONTROLS + i;
1519 if (options[i].type == MG_CONFIG_TYPE_BOOLEAN) {
1521 sizeof(value) - 1,
1522 "%s",
1523 IsDlgButtonChecked(hDlg, id) ? "yes" : "no");
1524 value[sizeof(value) - 1] = 0;
1525 } else {
1526 GetDlgItemText(hDlg, id, value, sizeof(value));
1527 }
1529 options[i].default_value == NULL ? "" : options[i].default_value;
1530 /* If value is the same as default, skip it */
1531 if (strcmp(value, default_value) != 0) {
1532 fprintf(fp, "%s %s\n", options[i].name, value);
1533 }
1534 }
1535}
1536
1537
1538/* LPARAM pointer passed to WM_INITDIALOG */
1539struct dlg_proc_param {
1540 int guard;
1541 HWND hWnd;
1542 const char *name;
1543 char *buffer;
1544 unsigned buflen;
1545 int idRetry;
1546 BOOL (*fRetry)(struct dlg_proc_param *data);
1547};
1548
1549struct dlg_header_param {
1550 /* https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-dlgtemplate
1551 */
1552 DLGTEMPLATE dlg_template; /* 18 bytes */
1553 WORD menu, dlg_class;
1554 wchar_t caption[1];
1555 WORD fontsize;
1556 wchar_t fontface[7]; /* L"Tahoma" = 6 characters + terminating zero */
1557};
1558
1559/* complete data required to create a dialog including child elements */
1560struct dlg_complete {
1561 struct dlg_header_param header;
1562 /* TODO: if sizeof(header)%4 is not 0, add some filling bytes here */
1563 BYTE elements[4096 * 2];
1564 int used;
1565};
1566
1567
1568static void
1569FillDialogHeader(struct dlg_complete *dlg, const short width)
1570{
1571 memset(dlg, 0, sizeof(*dlg));
1572
1573 dlg->header.dlg_template.style = WS_CAPTION | WS_POPUP | WS_SYSMENU
1574 | WS_VISIBLE | DS_SETFONT | DS_CENTER
1575 | WS_DLGFRAME;
1576 dlg->header.dlg_template.dwExtendedStyle = WS_EX_TOOLWINDOW;
1577 dlg->header.dlg_template.cdit = 0;
1578 dlg->header.dlg_template.x = 0; /* ignored by DS_CENTER */
1579 dlg->header.dlg_template.y = 0; /* ignored by DS_CENTER */
1580 dlg->header.dlg_template.cx = width;
1581 dlg->header.dlg_template.cy =
1582 0; /* to be calculated after adding elements */
1583 dlg->header.menu = 0;
1584 dlg->header.dlg_class = 0;
1585 dlg->header.caption[0] = (wchar_t)0;
1586 dlg->header.fontsize = 8;
1587 wcscpy(dlg->header.fontface, L"Tahoma");
1588
1589 /* counting used bytes */
1590 dlg->used = 0;
1591}
1592
1593
1594/* Dialog proc for settings dialog */
1595static INT_PTR CALLBACK
1596SettingsDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
1597{
1598 FILE *fp;
1599 int i, j;
1600 const char *name, *value;
1601 const struct mg_option *default_options = mg_get_valid_options();
1602 const char *file_options[MAX_OPTIONS * 2 + 1] = {0};
1603 char *title;
1604 struct dlg_proc_param *pdlg_proc_param;
1605
1606 switch (msg) {
1607
1608 case WM_CLOSE:
1609 DestroyWindow(hDlg);
1610 break;
1611
1612 case WM_COMMAND:
1613 switch (LOWORD(wParam)) {
1614
1615 case ID_SAVE:
1616 EnableWindow(GetDlgItem(hDlg, ID_SAVE), FALSE);
1617 if ((fp = fopen(g_config_file_name, "w+")) != NULL) {
1618 save_config(hDlg, fp);
1619 fclose(fp);
1620 stop_civetweb();
1621 start_civetweb(__argc, __argv);
1622 }
1623 EnableWindow(GetDlgItem(hDlg, ID_SAVE), TRUE);
1624 break;
1625
1626 case ID_RESET_DEFAULTS:
1627 for (i = 0; default_options[i].name != NULL; i++) {
1628 name = default_options[i].name;
1629 value = default_options[i].default_value == NULL
1630 ? ""
1631 : default_options[i].default_value;
1632 if (default_options[i].type == MG_CONFIG_TYPE_BOOLEAN) {
1633 CheckDlgButton(hDlg,
1634 ID_CONTROLS + i,
1635 !strcmp(value, "yes") ? BST_CHECKED
1636 : BST_UNCHECKED);
1637 } else {
1638 SetWindowText(GetDlgItem(hDlg, ID_CONTROLS + i), value);
1639 }
1640 }
1641 break;
1642
1643 case ID_RESET_FILE:
1644 read_config_file(g_config_file_name, file_options);
1645 for (i = 0; default_options[i].name != NULL; i++) {
1646 name = default_options[i].name;
1647 value = default_options[i].default_value;
1648 for (j = 0; file_options[j * 2] != NULL; j++) {
1649 if (!strcmp(name, file_options[j * 2])) {
1650 value = file_options[j * 2 + 1];
1651 }
1652 }
1653 if (value == NULL) {
1654 value = "";
1655 }
1656 if (default_options[i].type == MG_CONFIG_TYPE_BOOLEAN) {
1657 CheckDlgButton(hDlg,
1658 ID_CONTROLS + i,
1659 !strcmp(value, "yes") ? BST_CHECKED
1660 : BST_UNCHECKED);
1661 } else {
1662 SetWindowText(GetDlgItem(hDlg, ID_CONTROLS + i), value);
1663 }
1664 }
1665 for (i = 0; i < MAX_OPTIONS; i++) {
1666 free((void *)file_options[2 * i]);
1667 free((void *)file_options[2 * i + 1]);
1668 }
1669 break;
1670
1671 case ID_RESET_ACTIVE:
1672 for (i = 0; default_options[i].name != NULL; i++) {
1673 name = default_options[i].name;
1675 if (default_options[i].type == MG_CONFIG_TYPE_BOOLEAN) {
1676 CheckDlgButton(hDlg,
1677 ID_CONTROLS + i,
1678 !strcmp(value, "yes") ? BST_CHECKED
1679 : BST_UNCHECKED);
1680 } else {
1681 SetDlgItemText(hDlg,
1682 ID_CONTROLS + i,
1683 value == NULL ? "" : value);
1684 }
1685 }
1686 break;
1687 }
1688
1689 for (i = 0; default_options[i].name != NULL; i++) {
1690 name = default_options[i].name;
1691 if (((default_options[i].type == MG_CONFIG_TYPE_FILE)
1692 || (default_options[i].type == MG_CONFIG_TYPE_DIRECTORY))
1693 && LOWORD(wParam) == ID_CONTROLS + i + ID_FILE_BUTTONS_DELTA) {
1694
1695 /* Result of the Dialog: File/Folder selected by user */
1696 char path[PATH_MAX] = "";
1697
1698 if (default_options[i].type == MG_CONFIG_TYPE_DIRECTORY) {
1699
1700 BROWSEINFO bi;
1701 memset(&bi, 0, sizeof(bi));
1702 bi.hwndOwner = (HWND)hDlg;
1703 bi.ulFlags = BIF_RETURNONLYFSDIRS;
1704
1705 /* Use option name as Window title */
1706 bi.lpszTitle = name;
1707
1708 SHGetPathFromIDList(SHBrowseForFolder(&bi), path);
1709
1710 } else {
1711
1712 OPENFILENAME of;
1713 memset(&of, 0, sizeof(of));
1714 of.lStructSize = sizeof(of);
1715 of.hwndOwner = (HWND)hDlg;
1716 of.lpstrFile = path;
1717 of.nMaxFile = sizeof(path);
1718 of.lpstrInitialDir = mg_get_option(g_ctx, "document_root");
1719 of.Flags =
1720 OFN_CREATEPROMPT | OFN_NOCHANGEDIR | OFN_HIDEREADONLY;
1721
1722 /* Use option name as Window title */
1723 of.lpstrTitle = name;
1724
1725 GetOpenFileName(&of);
1726 }
1727
1728 if (path[0] != '\0') {
1729 /* Something has been chosen */
1730 SetWindowText(GetDlgItem(hDlg, ID_CONTROLS + i), path);
1731 }
1732 }
1733 }
1734 break;
1735
1736 case WM_INITDIALOG:
1737 /* Store hWnd in a parameter accessible by the parent, so we can
1738 * bring this window to front if required. */
1739 pdlg_proc_param = (struct dlg_proc_param *)lParam;
1740 pdlg_proc_param->hWnd = hDlg;
1741
1742 /* Initialize the dialog elements */
1743 SendMessage(hDlg, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)hIcon);
1744 SendMessage(hDlg, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)hIcon);
1745 title = (char *)malloc(strlen(g_server_name) + 16);
1746 if (title) {
1747 strcpy(title, g_server_name);
1748 strcat(title, " settings");
1749 SetWindowText(hDlg, title);
1750 free(title);
1751 }
1752 SetFocus(GetDlgItem(hDlg, ID_SAVE));
1753
1754 /* Init dialog with active settings */
1755 SendMessage(hDlg, WM_COMMAND, ID_RESET_ACTIVE, 0);
1756 /* alternative: SendMessage(hDlg, WM_COMMAND, ID_RESET_FILE, 0); */
1757 break;
1758
1759 default:
1760 break;
1761 }
1762
1763 return FALSE;
1764}
1765
1766
1767/* Dialog proc for input dialog */
1768static INT_PTR CALLBACK
1769InputDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
1770{
1771 static struct dlg_proc_param *inBuf = 0;
1772 WORD ctrlId;
1773 HWND hIn;
1774
1775 switch (msg) {
1776 case WM_CLOSE:
1777 inBuf = 0;
1778 DestroyWindow(hDlg);
1779 break;
1780
1781 case WM_COMMAND:
1782 ctrlId = LOWORD(wParam);
1783 if (ctrlId == IDOK) {
1784 /* Get handle of input line */
1785 hIn = GetDlgItem(hDlg, ID_INPUT_LINE);
1786
1787 if (hIn) {
1788 /* Get content of input line */
1789 GetWindowText(hIn, inBuf->buffer, (int)inBuf->buflen);
1790 if (inBuf->buffer[0] != 0) {
1791 /* Input dialog is not empty. */
1792 EndDialog(hDlg, IDOK);
1793 }
1794 } else {
1795 /* There is no input line in this dialog. */
1796 EndDialog(hDlg, IDOK);
1797 }
1798
1799 } else if (ctrlId == IDRETRY) {
1800
1801 /* Get handle of input line */
1802 hIn = GetDlgItem(hDlg, inBuf->idRetry);
1803
1804 if (hIn) {
1805 /* Load current string */
1806 GetWindowText(hIn, inBuf->buffer, (int)inBuf->buflen);
1807 if (inBuf->fRetry) {
1808 if (inBuf->fRetry(inBuf)) {
1809 SetWindowText(hIn, inBuf->buffer);
1810 }
1811 }
1812 }
1813
1814 } else if (ctrlId == IDCANCEL) {
1815 EndDialog(hDlg, IDCANCEL);
1816 }
1817 break;
1818
1819 case WM_INITDIALOG:
1820 /* Get handle of input line */
1821 hIn = GetDlgItem(hDlg, ID_INPUT_LINE);
1822
1823 /* Get dialog parameters */
1824 inBuf = (struct dlg_proc_param *)lParam;
1825
1826 /* Set dialog handle for the caller */
1827 inBuf->hWnd = hDlg;
1828
1829 /* Set dialog name */
1830 SetWindowText(hDlg, inBuf->name);
1831
1832 if (hIn) {
1833 /* This is an input dialog */
1834 DEBUG_ASSERT(inBuf != NULL);
1835 DEBUG_ASSERT((inBuf->buffer != NULL) && (inBuf->buflen != 0));
1836 DEBUG_ASSERT(strlen(inBuf->buffer) < inBuf->buflen);
1837 SendMessage(hDlg, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)hIcon);
1838 SendMessage(hDlg, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)hIcon);
1839 SendMessage(hIn, EM_LIMITTEXT, inBuf->buflen - 1, 0);
1840 SetWindowText(hIn, inBuf->buffer);
1841 SetFocus(hIn);
1842 }
1843
1844 break;
1845
1846 default:
1847 break;
1848 }
1849
1850 return FALSE;
1851}
1852
1853
1854static void
1855suggest_passwd(char *passwd)
1856{
1857 unsigned u;
1858 char *p;
1859 union {
1860 FILETIME ft;
1861 LARGE_INTEGER li;
1862 } num;
1863
1864 /* valid characters are 32 to 126 */
1865 GetSystemTimeAsFileTime(&num.ft);
1866 num.li.HighPart |= (LONG)GetCurrentProcessId();
1867 p = passwd;
1868 while (num.li.QuadPart) {
1869 u = (unsigned)(num.li.QuadPart % 95);
1870 num.li.QuadPart -= u;
1871 num.li.QuadPart /= 95;
1872 *p = (char)(u + 32);
1873 p++;
1874 }
1875}
1876
1877
1878static void add_control(struct dlg_complete *dlg,
1879 WORD type,
1880 WORD id,
1881 DWORD style,
1882 short x,
1883 short y,
1884 short cx,
1885 short cy,
1886 const char *caption);
1887
1888
1889static int
1890get_password(const char *user,
1891 const char *realm,
1892 char *passwd,
1893 unsigned passwd_len)
1894{
1895 /* Parameter for size/format tuning of the dialog */
1896 short HEIGHT = 15;
1897 short WIDTH = 280;
1898 short LABEL_WIDTH = 90;
1899
1900 /* Other variables */
1901 struct dlg_complete dlg;
1902 static struct dlg_proc_param s_dlg_proc_param;
1903 int ok;
1904 short y;
1905
1906 DEBUG_ASSERT((user != NULL) && (realm != NULL) && (passwd != NULL));
1907
1908 /* Only allow one instance of this dialog to be open. */
1909 if (s_dlg_proc_param.guard == 0) {
1910 memset(&s_dlg_proc_param, 0, sizeof(s_dlg_proc_param));
1911 s_dlg_proc_param.guard = 1;
1912 } else {
1913 SetForegroundWindow(s_dlg_proc_param.hWnd);
1914 return 0;
1915 }
1916
1917 FillDialogHeader(&dlg, WIDTH);
1918
1919 /* Do not open a password dialog, if the username is empty */
1920 if (user[0] == 0) {
1921 s_dlg_proc_param.guard = 0;
1922 return 0;
1923 }
1924
1925 /* Create a password suggestion */
1926 memset(passwd, 0, passwd_len);
1927 suggest_passwd(passwd);
1928
1929 /* Make buffer available for input dialog */
1930 s_dlg_proc_param.buffer = passwd;
1931 s_dlg_proc_param.buflen = passwd_len;
1932
1933 /* Create the dialog */
1934 y = HEIGHT;
1935 add_control(&dlg,
1936 0x82,
1937 ID_STATIC,
1938 WS_VISIBLE | WS_CHILD,
1939 10,
1940 y,
1941 LABEL_WIDTH,
1942 HEIGHT,
1943 "User:");
1944 add_control(&dlg,
1945 0x81,
1946 ID_CONTROLS + 1,
1947 WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL
1948 | ES_READONLY,
1949 15 + LABEL_WIDTH,
1950 y,
1951 WIDTH - LABEL_WIDTH - 25,
1952 HEIGHT,
1953 user);
1954
1955 y += HEIGHT;
1956 add_control(&dlg,
1957 0x82,
1958 ID_STATIC,
1959 WS_VISIBLE | WS_CHILD,
1960 10,
1961 y,
1962 LABEL_WIDTH,
1963 HEIGHT,
1964 "Realm:");
1965 add_control(&dlg,
1966 0x81,
1967 ID_CONTROLS + 2,
1968 WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL
1969 | ES_READONLY,
1970 15 + LABEL_WIDTH,
1971 y,
1972 WIDTH - LABEL_WIDTH - 25,
1973 HEIGHT,
1974 realm);
1975
1976 y += HEIGHT;
1977 add_control(&dlg,
1978 0x82,
1979 ID_STATIC,
1980 WS_VISIBLE | WS_CHILD,
1981 10,
1982 y,
1983 LABEL_WIDTH,
1984 HEIGHT,
1985 "Password:");
1986 add_control(&dlg,
1987 0x81,
1988 ID_INPUT_LINE,
1989 WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL | WS_TABSTOP,
1990 15 + LABEL_WIDTH,
1991 y,
1992 WIDTH - LABEL_WIDTH - 25,
1993 HEIGHT,
1994 "");
1995
1996 y += (WORD)(HEIGHT * 2);
1997 add_control(&dlg,
1998 0x80,
1999 IDOK,
2000 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
2001 80,
2002 y,
2003 55,
2004 12,
2005 "Ok");
2006 add_control(&dlg,
2007 0x80,
2008 IDCANCEL,
2009 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
2010 140,
2011 y,
2012 55,
2013 12,
2014 "Cancel");
2015
2016 dlg.header.dlg_template.cy = y + (WORD)(HEIGHT * 1.5);
2017
2018 s_dlg_proc_param.name = "Modify password";
2019 s_dlg_proc_param.fRetry = NULL;
2020
2021 ok = (IDOK
2022 == DialogBoxIndirectParam(NULL,
2023 &dlg.header.dlg_template,
2024 NULL,
2025 InputDlgProc,
2026 (LPARAM)&s_dlg_proc_param));
2027
2028 s_dlg_proc_param.hWnd = NULL;
2029 s_dlg_proc_param.guard = 0;
2030
2031 return ok;
2032}
2033
2034
2035/* Dialog proc for password dialog */
2036static INT_PTR CALLBACK
2037PasswordDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
2038{
2039 static const char *passfile = 0;
2040 char domain[256], user[256], password[256];
2041 WORD ctrlId;
2042 struct dlg_proc_param *pdlg_proc_param;
2043
2044 switch (msg) {
2045 case WM_CLOSE:
2046 passfile = 0;
2047 DestroyWindow(hDlg);
2048 break;
2049
2050 case WM_COMMAND:
2051 ctrlId = LOWORD(wParam);
2052 if (ctrlId == ID_ADD_USER) {
2053 /* Add user */
2054 GetWindowText(GetDlgItem(hDlg, ID_ADD_USER_NAME),
2055 user,
2056 sizeof(user));
2057 GetWindowText(GetDlgItem(hDlg, ID_ADD_USER_REALM),
2058 domain,
2059 sizeof(domain));
2060 if (get_password(user, domain, password, sizeof(password))) {
2061 mg_modify_passwords_file(passfile, domain, user, password);
2062 EndDialog(hDlg, IDOK);
2063 }
2064 } else if ((ctrlId >= (ID_CONTROLS + ID_FILE_BUTTONS_DELTA * 3))
2065 && (ctrlId < (ID_CONTROLS + ID_FILE_BUTTONS_DELTA * 4))) {
2066 /* Modify password */
2067 GetWindowText(GetDlgItem(hDlg, ctrlId - ID_FILE_BUTTONS_DELTA * 3),
2068 user,
2069 sizeof(user));
2070 GetWindowText(GetDlgItem(hDlg, ctrlId - ID_FILE_BUTTONS_DELTA * 2),
2071 domain,
2072 sizeof(domain));
2073 if (get_password(user, domain, password, sizeof(password))) {
2074 mg_modify_passwords_file(passfile, domain, user, password);
2075 EndDialog(hDlg, IDOK);
2076 }
2077 } else if ((ctrlId >= (ID_CONTROLS + ID_FILE_BUTTONS_DELTA * 2))
2078 && (ctrlId < (ID_CONTROLS + ID_FILE_BUTTONS_DELTA * 3))) {
2079 /* Remove user */
2080 GetWindowText(GetDlgItem(hDlg, ctrlId - ID_FILE_BUTTONS_DELTA * 2),
2081 user,
2082 sizeof(user));
2083 GetWindowText(GetDlgItem(hDlg, ctrlId - ID_FILE_BUTTONS_DELTA),
2084 domain,
2085 sizeof(domain));
2086 mg_modify_passwords_file(passfile, domain, user, NULL);
2087 EndDialog(hDlg, IDOK);
2088 }
2089 break;
2090
2091 case WM_INITDIALOG:
2092 pdlg_proc_param = (struct dlg_proc_param *)lParam;
2093 pdlg_proc_param->hWnd = hDlg;
2094 passfile = pdlg_proc_param->name;
2095 SendMessage(hDlg, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)hIcon);
2096 SendMessage(hDlg, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)hIcon);
2097 SetWindowText(hDlg, passfile);
2098 SetFocus(GetDlgItem(hDlg, ID_ADD_USER_NAME));
2099 break;
2100
2101 default:
2102 break;
2103 }
2104
2105 return FALSE;
2106}
2107
2108
2109static void
2110add_control(struct dlg_complete *dlg,
2111 WORD type,
2112 WORD id,
2113 DWORD style,
2114 short x,
2115 short y,
2116 short cx,
2117 short cy,
2118 const char *caption)
2119{
2120 DLGITEMTEMPLATE *tp;
2121 LPWORD p;
2122 WORD cap_len = caption ? (WORD)strlen(caption) : 0;
2123 int i;
2124 DWORD expected_size = sizeof(DLGITEMTEMPLATE) + 4 + (cap_len + 1) * 2 + 2;
2125
2126 if ((dlg->used + expected_size + /* alignment */ 16)
2127 >= sizeof(dlg->elements)) {
2128 /* out if memory protection */
2129 return;
2130 }
2131
2132 /* Add one child element */
2133 dlg->header.dlg_template.cdit++;
2134
2135 /* align to 4 bytes */
2136 while (dlg->used % 4) {
2137 dlg->used++;
2138 }
2139
2140 /* start with DLGITEMTEMPLATE structure */
2141 tp = (DLGITEMTEMPLATE *)(dlg->elements + dlg->used);
2142 tp->id = id;
2143 tp->style = style;
2144 tp->dwExtendedStyle = 0;
2145 tp->x = x;
2146 tp->y = y;
2147 tp->cx = cx;
2148 tp->cy = cy;
2149 dlg->used += sizeof(*tp);
2150
2151 /* add class */
2152 p = (LPWORD)(dlg->elements + dlg->used);
2153 p[0] = 0xffff;
2154 p[1] = type;
2155 dlg->used += 2 * sizeof(*p);
2156
2157 /* add title */
2158 p = (LPWORD)(dlg->elements + dlg->used);
2159 for (i = 0; i < cap_len; i++) {
2160 p[i] = (WCHAR)caption[i];
2161 }
2162 p[cap_len] = 0;
2163 dlg->used += (cap_len + 1) * sizeof(*p);
2164
2165 /* align to 2 bytes */
2166 while (dlg->used % 2) {
2167 dlg->used++;
2168 }
2169
2170 /* add creation data */
2171 p = (LPWORD)(dlg->elements + dlg->used);
2172 *p = 0;
2173 dlg->used += sizeof(*p);
2174}
2175
2176
2177static int
2178optioncmp(const char *o1, const char *o2)
2179{
2180 /* string compare for option names */
2181 while (*o1 || *o2) {
2182 int c1 = 256 * (int)*o1;
2183 int c2 = 256 * (int)*o2;
2184 if (isalpha(*o1))
2185 c1 = toupper(*o1);
2186 else if (*o1 == '_')
2187 c1 = 1;
2188 if (isalpha(*o2))
2189 c2 = toupper(*o2);
2190 else if (*o2 == '_')
2191 c2 = 1;
2192 if (c1 < c2)
2193 return -1;
2194 if (c1 > c2)
2195 return +1;
2196 o1++;
2197 o2++;
2198 }
2199 return 0;
2200}
2201
2202
2203static void
2204show_settings_dialog(void)
2205{
2206 /* Parameter for size/format tuning of the dialog */
2207 short HEIGHT = 15;
2208 short BORDER_WIDTH = 10;
2209 short BORDER_HEIGTH = BORDER_WIDTH / 2;
2210 short CELL_WIDTH = 125;
2211 short LABEL_WIDTH = 115;
2212 short FILE_DIALOG_BUTTON_WIDTH = 15;
2213 short NO_OF_COLUMNS = 3;
2214 short NO_OF_OPTIONS = 0; /* to be calculated */
2215 short NO_OF_OPTION_SPACES = 0; /* to be calculated */
2216 short NO_OF_ROWS = 0; /* to be calculated */
2217
2218 /* Calculates size */
2219 short COLUMN_WIDTH = LABEL_WIDTH + CELL_WIDTH + BORDER_WIDTH;
2220 short DIALOG_WIDTH = BORDER_WIDTH + NO_OF_COLUMNS * COLUMN_WIDTH;
2221
2222 /* All other variables */
2223 const struct mg_option *cv_options;
2224 DWORD style;
2225 WORD i, cl, nelems = 0;
2226 short x, y, next_cell_width, next_cell_height;
2227 static struct dlg_proc_param s_dlg_proc_param;
2228 short *option_index, *option_top, *option_bottom;
2229 char text[64];
2230 struct dlg_complete dlg;
2231
2232 if (s_dlg_proc_param.guard == 0) {
2233 memset(&s_dlg_proc_param, 0, sizeof(s_dlg_proc_param));
2234 s_dlg_proc_param.guard = 1;
2235 } else {
2236 SetForegroundWindow(s_dlg_proc_param.hWnd);
2237 return;
2238 }
2239
2240 FillDialogHeader(&dlg, DIALOG_WIDTH);
2241
2242 /* Determine space required for input fields */
2243 cv_options = mg_get_valid_options();
2244 for (i = 0; cv_options[i].name != NULL; i++) {
2245 NO_OF_OPTIONS++;
2246 if (cv_options[i].type == MG_CONFIG_TYPE_STRING_MULTILINE) {
2247 /* Multiline input fields require double space */
2248 NO_OF_OPTION_SPACES += 2;
2249 } else {
2250 /* All other option types require single space */
2251 NO_OF_OPTION_SPACES++;
2252 }
2253 }
2254 NO_OF_ROWS = (NO_OF_OPTION_SPACES + NO_OF_COLUMNS - 1) / NO_OF_COLUMNS;
2255
2256 /* All options should be displayed sorted. */
2257 /* First allocate some memory to store option order: The array should store
2258 * 1) the option order of all options (NO_OF_OPTIONS), followed by
2259 * 2) the option index for the option name displayed on top of a column
2260 * 3) the option index for the option name displayed on bottom of a column
2261 */
2262 option_index = (short *)calloc(NO_OF_OPTIONS + 2 * NO_OF_COLUMNS,
2263 sizeof(short)); /* 1 */
2264 if (!option_index) {
2265 /* unlikely case of "out of memory" */
2266 return;
2267 }
2268 option_top = option_index + NO_OF_OPTIONS; /* 2 */
2269 option_bottom = option_top + NO_OF_COLUMNS; /* 3 */
2270
2271 /* Initialize option order */
2272 for (i = 0; i < NO_OF_OPTIONS; i++) {
2273 option_index[i] = i;
2274 }
2275 /* Sort all options */
2276 for (;;) {
2277 int swapped = 0;
2278 for (i = 1; i < NO_OF_OPTIONS; i++) {
2279 if (optioncmp(cv_options[option_index[i - 1]].name,
2280 cv_options[option_index[i]].name)
2281 > 0) {
2282 short swap = option_index[i];
2283 option_index[i] = option_index[i - 1];
2284 option_index[i - 1] = swap;
2285 swapped = 1;
2286 }
2287 }
2288 if (!swapped) {
2289 break;
2290 }
2291 }
2292
2293 /* Create input fields for all options */
2294 for (i = 0; i < NO_OF_OPTIONS; i++) {
2295
2296 /* Get option according to option order */
2297 const struct mg_option *opt = &cv_options[option_index[i]];
2298
2299 /* Template style for all input fields (will be modified for specific
2300 * field types) */
2301 style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
2302
2303 /* TODO: Bottom of a column must not be a MULTILINE input field.
2304 * If the last input field in a column would of multi-line type
2305 * (opt->type == MG_CONFIG_TYPE_STRING_MULTILINE), then
2306 * skip one field (nelems++).
2307 * But in this case, maybe one more ROW might be required. */
2308
2309 /* Position and size of the input field (will be modified) */
2310 x = BORDER_WIDTH + COLUMN_WIDTH * (nelems / NO_OF_ROWS);
2311 y = BORDER_HEIGTH + HEIGHT + HEIGHT * (nelems % NO_OF_ROWS);
2312 next_cell_width = CELL_WIDTH;
2313 next_cell_height = HEIGHT - 3;
2314
2315 /* Determine top/bottom option for every column */
2316 if ((nelems % NO_OF_ROWS) == 0) {
2317 /* Set option on top of a new column once */
2318 option_top[nelems / NO_OF_ROWS] = option_index[i];
2319 }
2320 /* Set/overwrite option on bottom of a column */
2321 option_bottom[nelems / NO_OF_ROWS] = option_index[i];
2322
2323 /* Depending on option type: create suitable input field */
2324 if (opt->type == MG_CONFIG_TYPE_NUMBER) {
2325 style |= ES_NUMBER;
2326 cl = 0x81;
2327 style |= WS_BORDER | ES_AUTOHSCROLL;
2328
2329 } else if (opt->type == MG_CONFIG_TYPE_BOOLEAN) {
2330 cl = 0x80;
2331 style |= BS_AUTOCHECKBOX;
2332
2333 } else if ((opt->type == MG_CONFIG_TYPE_FILE)
2334 || (opt->type == MG_CONFIG_TYPE_DIRECTORY)) {
2335 style |= WS_BORDER | ES_AUTOHSCROLL;
2336 cl = 0x81;
2337
2338 /* Additional button for file dialog */
2339 add_control(&dlg,
2340 0x80,
2341 ID_CONTROLS + option_index[i] + ID_FILE_BUTTONS_DELTA,
2342 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
2343 x + LABEL_WIDTH + CELL_WIDTH - FILE_DIALOG_BUTTON_WIDTH,
2344 y,
2345 FILE_DIALOG_BUTTON_WIDTH,
2346 HEIGHT - 3,
2347 "...");
2348
2349 next_cell_width -= FILE_DIALOG_BUTTON_WIDTH + BORDER_WIDTH / 2;
2350
2351 } else if (opt->type == MG_CONFIG_TYPE_STRING_MULTILINE) {
2352
2353 /* Multiline input */
2354 cl = 0x81;
2355 style |= WS_BORDER | ES_AUTOHSCROLL | ES_MULTILINE | ES_WANTRETURN
2356 | WS_VSCROLL | ES_AUTOVSCROLL;
2357 /* Add more space below */
2358 nelems += 1;
2359 next_cell_height += HEIGHT;
2360
2361 } else {
2362 /* Standard text input field */
2363 cl = 0x81;
2364 style |= WS_BORDER | ES_AUTOHSCROLL;
2365 }
2366
2367 /* Add label (static text) */
2368 add_control(&dlg,
2369 0x82,
2370 ID_STATIC,
2371 WS_VISIBLE | WS_CHILD,
2372 x,
2373 y,
2374 LABEL_WIDTH,
2375 HEIGHT,
2376 opt->name);
2377
2378 /* Add input field */
2379 add_control(&dlg,
2380 cl,
2381 ID_CONTROLS + option_index[i],
2382 style,
2383 x + LABEL_WIDTH,
2384 y,
2385 next_cell_width,
2386 next_cell_height,
2387 "");
2388 nelems++;
2389 }
2390
2391 /* "Settings" frame around all options */
2392 y = ((nelems + NO_OF_COLUMNS - 1) / NO_OF_COLUMNS + 1) * HEIGHT;
2393 for (i = 0; i < NO_OF_COLUMNS; i++) {
2394 sprintf(text,
2395 " Settings %c - %c ",
2396 cv_options[option_top[i]].name[0],
2397 cv_options[option_bottom[i]].name[0]);
2398 add_control(&dlg,
2399 0x80,
2400 ID_GROUP,
2401 WS_CHILD | WS_VISIBLE | BS_GROUPBOX,
2402 BORDER_WIDTH / 2
2403 + i * (DIALOG_WIDTH - BORDER_WIDTH) / NO_OF_COLUMNS,
2404 BORDER_WIDTH / 2,
2405 (DIALOG_WIDTH - BORDER_WIDTH) / NO_OF_COLUMNS,
2406 y + 2,
2407 text);
2408 }
2409
2410 /* Buttons below "Settings" frame */
2411 y += HEIGHT;
2412 add_control(&dlg,
2413 0x80,
2414 ID_SAVE,
2415 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
2416 DIALOG_WIDTH - 70,
2417 y,
2418 65,
2419 12,
2420 "Save Settings");
2421 add_control(&dlg,
2422 0x80,
2423 ID_RESET_DEFAULTS,
2424 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
2425 DIALOG_WIDTH - 140,
2426 y,
2427 65,
2428 12,
2429 "Reset to defaults");
2430 add_control(&dlg,
2431 0x80,
2432 ID_RESET_FILE,
2433 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
2434 DIALOG_WIDTH - 210,
2435 y,
2436 65,
2437 12,
2438 "Reload from file");
2439 add_control(&dlg,
2440 0x80,
2441 ID_RESET_ACTIVE,
2442 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
2443 DIALOG_WIDTH - 280,
2444 y,
2445 65,
2446 12,
2447 "Reload active");
2448 add_control(&dlg,
2449 0x82,
2450 ID_STATIC,
2451 WS_CHILD | WS_VISIBLE | WS_DISABLED,
2452 5,
2453 y,
2454 100,
2455 12,
2457
2458 /* Calculate total height of the dialog */
2459 dlg.header.dlg_template.cy = y + HEIGHT;
2460
2461 s_dlg_proc_param.fRetry = NULL;
2462
2463 DialogBoxIndirectParam(NULL,
2464 &dlg.header.dlg_template,
2465 NULL,
2466 SettingsDlgProc,
2467 (LPARAM)&s_dlg_proc_param);
2468
2469 free(option_index);
2470
2471 s_dlg_proc_param.hWnd = NULL;
2472 s_dlg_proc_param.guard = 0;
2473}
2474
2475
2476static void
2477change_password_file()
2478{
2479 /* Parameter for size/format tuning of the dialog */
2480 short HEIGHT = 15;
2481 short WIDTH = 320;
2482 short LABEL_WIDTH = 90;
2483
2484 /* Other variables */
2485 OPENFILENAME of;
2486 char path[PATH_MAX] = PASSWORDS_FILE_NAME;
2487 char strbuf[256], u[256], d[256];
2488 HWND hDlg = NULL;
2489 FILE *f;
2490 short y, nelems;
2491 const char *domain = mg_get_option(g_ctx, "authentication_domain");
2492 static struct dlg_proc_param s_dlg_proc_param;
2493 struct dlg_complete dlg;
2494
2495 if (s_dlg_proc_param.guard == 0) {
2496 memset(&s_dlg_proc_param, 0, sizeof(s_dlg_proc_param));
2497 s_dlg_proc_param.guard = 1;
2498 } else {
2499 SetForegroundWindow(s_dlg_proc_param.hWnd);
2500 return;
2501 }
2502
2503 memset(&of, 0, sizeof(of));
2504 of.lStructSize = sizeof(of);
2505 of.hwndOwner = (HWND)hDlg;
2506 of.lpstrFile = path;
2507 of.nMaxFile = sizeof(path);
2508 of.lpstrInitialDir = mg_get_option(g_ctx, "document_root");
2509 of.Flags = OFN_CREATEPROMPT | OFN_NOCHANGEDIR | OFN_HIDEREADONLY;
2510
2511 if (!GetSaveFileName(&of)) {
2512 /* Cancel/Close by user */
2513 s_dlg_proc_param.guard = 0;
2514 return;
2515 }
2516
2517 f = fopen(path, "a+");
2518 if (f) {
2519 fclose(f);
2520 } else {
2521 MessageBox(NULL, path, "Can not open file", MB_ICONERROR);
2522 s_dlg_proc_param.guard = 0;
2523 return;
2524 }
2525
2526 do {
2527 s_dlg_proc_param.hWnd = NULL;
2528
2529 FillDialogHeader(&dlg, WIDTH);
2530
2531 f = fopen(path, "r+");
2532 if (!f) {
2533 MessageBox(NULL, path, "Can not open file", MB_ICONERROR);
2534 s_dlg_proc_param.guard = 0;
2535 return;
2536 }
2537
2538 nelems = 0;
2539 while (fgets(strbuf, sizeof(strbuf), f)) {
2540 if (sscanf(strbuf, "%255[^:]:%255[^:]:%*s", u, d) != 2) {
2541 continue;
2542 }
2543 u[255] = 0;
2544 d[255] = 0;
2545 y = (nelems + 1) * HEIGHT + 5;
2546 add_control(&dlg,
2547 0x80,
2548 ID_CONTROLS + nelems + ID_FILE_BUTTONS_DELTA * 3,
2549 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
2550 10,
2551 y,
2552 65,
2553 12,
2554 "Modify password");
2555 add_control(&dlg,
2556 0x80,
2557 ID_CONTROLS + nelems + ID_FILE_BUTTONS_DELTA * 2,
2558 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
2559 80,
2560 y,
2561 55,
2562 12,
2563 "Remove user");
2564 add_control(&dlg,
2565 0x81,
2566 ID_CONTROLS + nelems + ID_FILE_BUTTONS_DELTA,
2567 WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL
2568 | ES_READONLY,
2569 245,
2570 y,
2571 60,
2572 12,
2573 d);
2574 add_control(&dlg,
2575 0x81,
2576 ID_CONTROLS + nelems,
2577 WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL
2578 | ES_READONLY,
2579 140,
2580 y,
2581 100,
2582 12,
2583 u);
2584
2585 nelems++;
2586 }
2587 fclose(f);
2588
2589 y = (nelems + 1) * HEIGHT + 10;
2590 add_control(&dlg,
2591 0x80,
2592 ID_ADD_USER,
2593 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
2594 80,
2595 y,
2596 55,
2597 12,
2598 "Add user");
2599 add_control(&dlg,
2600 0x81,
2601 ID_ADD_USER_NAME,
2602 WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL
2603 | WS_TABSTOP,
2604 140,
2605 y,
2606 100,
2607 12,
2608 "");
2609 add_control(&dlg,
2610 0x81,
2611 ID_ADD_USER_REALM,
2612 WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL
2613 | WS_TABSTOP,
2614 245,
2615 y,
2616 60,
2617 12,
2618 domain);
2619
2620 y = (nelems + 2) * HEIGHT + 10;
2621 add_control(&dlg,
2622 0x80,
2623 ID_GROUP,
2624 WS_CHILD | WS_VISIBLE | BS_GROUPBOX,
2625 5,
2626 5,
2627 WIDTH - 10,
2628 y,
2629 " Users ");
2630
2631 y += HEIGHT;
2632 add_control(&dlg,
2633 0x82,
2634 ID_STATIC,
2635 WS_CHILD | WS_VISIBLE | WS_DISABLED,
2636 5,
2637 y,
2638 100,
2639 12,
2641
2642 dlg.header.dlg_template.cy = y + 20;
2643
2644 s_dlg_proc_param.name = path;
2645 s_dlg_proc_param.fRetry = NULL;
2646
2647 } while ((IDOK
2648 == DialogBoxIndirectParam(NULL,
2649 &dlg.header.dlg_template,
2650 NULL,
2651 PasswordDlgProc,
2652 (LPARAM)&s_dlg_proc_param))
2653 && (!g_exit_flag));
2654
2655 s_dlg_proc_param.hWnd = NULL;
2656 s_dlg_proc_param.guard = 0;
2657}
2658
2659
2660static BOOL
2661sysinfo_reload(struct dlg_proc_param *prm)
2662{
2663 static char *buf = 0;
2664 int cl, rl;
2665
2666 cl = mg_get_context_info(g_ctx, NULL, 0);
2667 free(buf);
2668 cl += 510;
2669 buf = (char *)malloc(cl + 1);
2670 rl = mg_get_context_info(g_ctx, buf, cl);
2671 if ((rl > cl) || (rl <= 0)) {
2672 if (g_ctx == NULL) {
2673 prm->buffer = "Server not running";
2674 } else if (rl <= 0) {
2675 prm->buffer = "No server statistics available";
2676 } else {
2677 prm->buffer = "Please retry";
2678 }
2679 } else {
2680 prm->buffer = buf;
2681 }
2682
2683 return TRUE;
2684}
2685
2686
2687int
2688show_system_info()
2689{
2690 /* Parameter for size/format tuning of the dialog */
2691 short HEIGHT = 15;
2692 short WIDTH = 320;
2693 short LABEL_WIDTH = 50;
2694
2695 /* Other parameters */
2696 int ok;
2697 short y;
2698 static struct dlg_proc_param s_dlg_proc_param;
2699 struct dlg_complete dlg;
2700
2701 /* Only allow one instance of this dialog to be open. */
2702 if (s_dlg_proc_param.guard == 0) {
2703 memset(&s_dlg_proc_param, 0, sizeof(s_dlg_proc_param));
2704 s_dlg_proc_param.guard = 1;
2705 } else {
2706 SetForegroundWindow(s_dlg_proc_param.hWnd);
2707 return 0;
2708 }
2709
2710 /* Create the dialog */
2711 FillDialogHeader(&dlg, WIDTH);
2712
2713 y = HEIGHT;
2714 add_control(&dlg,
2715 0x82,
2716 ID_STATIC,
2717 WS_VISIBLE | WS_CHILD,
2718 10,
2719 y,
2720 LABEL_WIDTH,
2721 HEIGHT,
2722 "System Information:");
2723 add_control(&dlg,
2724 0x81,
2725 ID_CONTROLS + 1,
2726 WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL
2727 | ES_AUTOVSCROLL | ES_MULTILINE | ES_READONLY,
2728 15 + LABEL_WIDTH,
2729 y,
2730 WIDTH - LABEL_WIDTH - 25,
2731 HEIGHT * 7,
2733
2734 y += (WORD)(HEIGHT * 8);
2735
2736 add_control(&dlg,
2737 0x80,
2738 IDRETRY,
2739 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
2740 WIDTH - 10 - 55 - 10 - 55,
2741 y,
2742 55,
2743 12,
2744 "Reload");
2745
2746 add_control(&dlg,
2747 0x80,
2748 IDOK,
2749 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
2750 WIDTH - 10 - 55,
2751 y,
2752 55,
2753 12,
2754 "Close");
2755
2756 dlg.header.dlg_template.cy = y + (WORD)(HEIGHT * 1.5);
2757
2758 s_dlg_proc_param.name = "System information";
2759 s_dlg_proc_param.fRetry = sysinfo_reload;
2760 s_dlg_proc_param.idRetry = ID_CONTROLS + 1; /* Reload field with this ID */
2761
2762 ok = (IDOK
2763 == DialogBoxIndirectParam(NULL,
2764 &dlg.header.dlg_template,
2765 NULL,
2766 InputDlgProc,
2767 (LPARAM)&s_dlg_proc_param));
2768
2769 s_dlg_proc_param.hWnd = NULL;
2770 s_dlg_proc_param.guard = 0;
2771
2772 return ok;
2773}
2774
2775
2776static int
2777manage_service(int action)
2778{
2779 const char *service_name = g_server_name;
2780 SC_HANDLE hSCM = NULL, hService = NULL;
2781 SERVICE_DESCRIPTION descr;
2782 char path[PATH_MAX + 20] = ""; /* Path to executable plus magic argument */
2783 int success = 1;
2784
2785 descr.lpDescription = (LPSTR)g_server_name;
2786
2787 if ((hSCM = OpenSCManager(NULL,
2788 NULL,
2789 action == ID_INSTALL_SERVICE ? GENERIC_WRITE
2790 : GENERIC_READ))
2791 == NULL) {
2792 success = 0;
2793 show_error();
2794 } else if (action == ID_INSTALL_SERVICE) {
2795 path[sizeof(path) - 1] = 0;
2796 GetModuleFileName(NULL, path, sizeof(path) - 1);
2797 strncat(path, " ", sizeof(path) - 1 - strlen(path));
2798 strncat(path, service_magic_argument, sizeof(path) - 1 - strlen(path));
2799 hService = CreateService(hSCM,
2800 service_name,
2801 service_name,
2802 SERVICE_ALL_ACCESS,
2803 SERVICE_WIN32_OWN_PROCESS,
2804 SERVICE_AUTO_START,
2805 SERVICE_ERROR_NORMAL,
2806 path,
2807 NULL,
2808 NULL,
2809 NULL,
2810 NULL,
2811 NULL);
2812 if (hService) {
2813 ChangeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, &descr);
2814 } else {
2815 show_error();
2816 }
2817 } else if (action == ID_REMOVE_SERVICE) {
2818 if ((hService = OpenService(hSCM, service_name, DELETE)) == NULL
2819 || !DeleteService(hService)) {
2820 show_error();
2821 }
2822 } else if ((hService =
2823 OpenService(hSCM, service_name, SERVICE_QUERY_STATUS))
2824 == NULL) {
2825 success = 0;
2826 }
2827
2828 if (hService)
2829 CloseServiceHandle(hService);
2830 if (hSCM)
2831 CloseServiceHandle(hSCM);
2832
2833 return success;
2834}
2835
2836
2837static void
2838add_icon_to_systray(HWND hWnd)
2839{
2840 /* tray icon is entry point to the menu */
2841 if (!g_hide_tray) {
2842 TrayIcon.cbSize = sizeof(TrayIcon);
2843 TrayIcon.uID = ID_ICON;
2844 TrayIcon.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
2845 TrayIcon.hIcon = hIcon;
2846 TrayIcon.hWnd = hWnd;
2847 snprintf(TrayIcon.szTip, sizeof(TrayIcon.szTip), "%s", g_server_name);
2848 TrayIcon.uCallbackMessage = WM_USER;
2849 Shell_NotifyIcon(NIM_ADD, &TrayIcon);
2850 } else {
2851 TrayIcon.cbSize = 0;
2852 }
2853}
2854
2855
2856/* Window proc for taskbar icon */
2857static LRESULT CALLBACK
2858WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
2859{
2860
2861 int service_installed;
2862 char buf[200];
2863 POINT pt;
2864 HMENU hMenu;
2865
2866 switch (msg) {
2867
2868 case WM_CREATE:
2869 if ((__argv[1] != NULL) && !strcmp(__argv[1], service_magic_argument)) {
2870 static SERVICE_TABLE_ENTRY service_table[2];
2871 char *service_argv[2];
2872
2873 service_argv[0] = __argv[0];
2874 service_argv[1] = NULL;
2875
2876 start_civetweb(1, service_argv);
2877
2878 memset(service_table, 0, sizeof(service_table));
2879 service_table[0].lpServiceName = (LPSTR)g_server_name;
2880 service_table[0].lpServiceProc =
2881 (LPSERVICE_MAIN_FUNCTION)ServiceMain;
2882
2883 StartServiceCtrlDispatcher(service_table);
2884 exit(EXIT_SUCCESS);
2885 } else {
2886 start_civetweb(__argc, __argv);
2887 }
2888 break;
2889
2890 case WM_COMMAND:
2891 switch (LOWORD(wParam)) {
2892 case ID_QUIT:
2893 stop_civetweb();
2894 if (TrayIcon.cbSize) {
2895 Shell_NotifyIcon(NIM_DELETE, &TrayIcon);
2896 }
2897 g_exit_flag = 1;
2898 PostQuitMessage(0);
2899 return 0;
2900 case ID_SETTINGS:
2901 show_settings_dialog();
2902 break;
2903 case ID_PASSWORD:
2904 change_password_file();
2905 break;
2906 case ID_SYSINFO:
2907 show_system_info();
2908 break;
2909 case ID_INSTALL_SERVICE:
2910 case ID_REMOVE_SERVICE:
2911 manage_service(LOWORD(wParam));
2912 break;
2913 case ID_CONNECT: {
2914 /* Get port from server configuration (listening_ports) and build
2915 * URL from port. */
2916 const char *url = get_url_to_first_open_port(g_ctx);
2917
2918 /* Open URL with Windows default browser */
2919 ShellExecute(NULL, "open", url, NULL, NULL, SW_SHOW);
2920 } break;
2921 case ID_WEBSITE:
2922 fprintf(stdout, "[%s]\n", g_website);
2923 ShellExecute(NULL, "open", g_website, NULL, NULL, SW_SHOW);
2924 break;
2925 }
2926 break;
2927
2928 case WM_USER:
2929 switch (lParam) {
2930 case WM_RBUTTONUP:
2931 case WM_LBUTTONUP:
2932 case WM_LBUTTONDBLCLK:
2933 hMenu = CreatePopupMenu();
2934 AppendMenu(hMenu,
2935 MF_STRING | MF_GRAYED,
2936 ID_SEPARATOR,
2938 AppendMenu(hMenu, MF_SEPARATOR, ID_SEPARATOR, "");
2939 service_installed = manage_service(0);
2940 snprintf(buf,
2941 sizeof(buf) - 1,
2942 "NT service: %s installed",
2943 service_installed ? "" : "not");
2944 buf[sizeof(buf) - 1] = 0;
2945 AppendMenu(hMenu, MF_STRING | MF_GRAYED, ID_SEPARATOR, buf);
2946 AppendMenu(hMenu,
2947 MF_STRING | (service_installed ? MF_GRAYED : 0),
2948 ID_INSTALL_SERVICE,
2949 "Install service");
2950 AppendMenu(hMenu,
2951 MF_STRING | (!service_installed ? MF_GRAYED : 0),
2952 ID_REMOVE_SERVICE,
2953 "Deinstall service");
2954 AppendMenu(hMenu, MF_SEPARATOR, ID_SEPARATOR, "");
2955 AppendMenu(hMenu, MF_STRING, ID_CONNECT, "Start browser");
2956 AppendMenu(hMenu, MF_STRING, ID_SETTINGS, "Edit settings");
2957 AppendMenu(hMenu, MF_STRING, ID_PASSWORD, "Modify password file");
2958 AppendMenu(hMenu, MF_STRING, ID_SYSINFO, "Show system info");
2959 AppendMenu(hMenu, MF_STRING, ID_WEBSITE, "Visit website");
2960 AppendMenu(hMenu, MF_SEPARATOR, ID_SEPARATOR, "");
2961 AppendMenu(hMenu, MF_STRING, ID_QUIT, "Exit");
2962 GetCursorPos(&pt);
2963 SetForegroundWindow(hWnd);
2964 TrackPopupMenu(hMenu, 0, pt.x, pt.y, 0, hWnd, NULL);
2965 PostMessage(hWnd, WM_NULL, 0, 0);
2966 DestroyMenu(hMenu);
2967 break;
2968 }
2969 break;
2970
2971 case WM_CLOSE:
2972 stop_civetweb();
2973 if (TrayIcon.cbSize) {
2974 Shell_NotifyIcon(NIM_DELETE, &TrayIcon);
2975 }
2976 g_exit_flag = 1;
2977 PostQuitMessage(0);
2978 return 0; /* We've just sent our own quit message, with proper hwnd. */
2979
2980 default:
2981 if (msg == msg_taskbar_created) {
2982 add_icon_to_systray(hWnd);
2983 }
2984 break;
2985 }
2986
2987 return DefWindowProc(hWnd, msg, wParam, lParam);
2988}
2989
2990
2991/* An executable with "Subsystem: Windows" does not have a Console.
2992 * Create one manually, if required. */
2993static int
2994MakeConsole(void)
2995{
2996 DWORD err;
2997 HANDLE hConWnd = GetConsoleWindow();
2998 int ok = 1;
2999
3000 if (hConWnd == NULL) {
3001 if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
3002 FreeConsole();
3003 if (!AllocConsole()) {
3004 err = GetLastError();
3005 if (err == ERROR_ACCESS_DENIED) {
3006 ok = 0;
3007 MessageBox(NULL,
3008 "Insufficient rights to create a console window",
3009 "Error",
3010 MB_ICONERROR);
3011 }
3012 }
3013 AttachConsole(GetCurrentProcessId());
3014 }
3015
3016 /* Retry to get a console handle */
3017 hConWnd = GetConsoleWindow();
3018
3019 if (hConWnd == NULL) {
3020 ok = 0;
3021 } else {
3022 /* Reopen console handles according to
3023 * https://stackoverflow.com/questions/9020790/using-stdin-with-an-allocconsole
3024 */
3025 if (NULL == freopen("CONIN$", "r", stdin)) {
3026 ok = 0;
3027 }
3028 if (NULL == freopen("CONOUT$", "w", stdout)) {
3029 ok = 0;
3030 }
3031 if (NULL == freopen("CONOUT$", "w", stderr)) {
3032 ok = 0;
3033 }
3034 }
3035 }
3036
3037 if (hConWnd == NULL) {
3038 ok = 0;
3039 } else {
3040 SetConsoleTitle(g_server_name);
3041 }
3042
3043 return ok;
3044}
3045
3046
3047/* main() for Windows (Subsystem: Windows). */
3048int WINAPI
3049WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR cmdline, int show)
3050{
3051 WNDCLASS cls;
3052 HWND hWnd;
3053 MSG msg;
3054
3055#if defined(DEBUG)
3056 (void)MakeConsole();
3057#endif
3058
3059 (void)hInst;
3060 (void)hPrev;
3061 (void)cmdline;
3062 (void)show;
3063
3066 msg_taskbar_created = RegisterWindowMessage("TaskbarCreated");
3067
3068 memset(&cls, 0, sizeof(cls));
3069 cls.lpfnWndProc = WindowProc;
3070 cls.hInstance = GetModuleHandle(NULL);
3071 cls.hIcon = LoadIcon(NULL, IDI_APPLICATION);
3072 cls.lpszClassName = g_server_base_name;
3073
3074 RegisterClass(&cls);
3075 hWnd = CreateWindow(cls.lpszClassName,
3077 WS_OVERLAPPEDWINDOW,
3078 0,
3079 0,
3080 0,
3081 0,
3082 NULL,
3083 NULL,
3084 cls.hInstance,
3085 NULL);
3086 ShowWindow(hWnd, SW_HIDE);
3087
3088 /* Load icon for systray and other dialogs */
3089 if (g_icon_name) {
3090 hIcon = (HICON)
3091 LoadImage(NULL, g_icon_name, IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
3092 } else {
3093 hIcon = (HICON)LoadImage(GetModuleHandle(NULL),
3094 MAKEINTRESOURCE(ID_ICON),
3095 IMAGE_ICON,
3096 16,
3097 16,
3098 0);
3099 }
3100
3101 add_icon_to_systray(hWnd);
3102
3103 /* Message loop */
3104 while (GetMessage(&msg, hWnd, 0, 0) > 0) {
3105 TranslateMessage(&msg);
3106 DispatchMessage(&msg);
3107 }
3108
3110
3111 /* Return the WM_QUIT value. */
3112 return (int)msg.wParam;
3113}
3114
3115
3116/* main() for Windows (Subsystem: Console). */
3117int
3118main(int argc, char *argv[])
3119{
3120 (void)argc;
3121 (void)argv;
3122
3123 return WinMain(0, 0, 0, 0);
3124}
3125
3126
3127#elif defined(USE_COCOA)
3128#import <Cocoa/Cocoa.h>
3129
3130@interface Civetweb : NSObject <NSApplicationDelegate>
3131- (void)openBrowser;
3132- (void)shutDown;
3133@end
3134
3135@implementation Civetweb
3136- (void)openBrowser
3137{
3138 [[NSWorkspace sharedWorkspace]
3139 openURL:[NSURL URLWithString:[NSString stringWithUTF8String:
3140 get_url_to_first_open_port(
3141 g_ctx)]]];
3142}
3143- (void)editConfig
3144{
3145 create_config_file(g_ctx, g_config_file_name);
3146 NSString *path = [NSString stringWithUTF8String:g_config_file_name];
3147 if (![[NSWorkspace sharedWorkspace] openFile:path
3148 withApplication:@"TextEdit"]) {
3149 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
3150 [alert setAlertStyle:NSWarningAlertStyle];
3151 [alert setMessageText:NSLocalizedString(@"Unable to open config file.",
3152 "")];
3153 [alert setInformativeText:path];
3154 (void)[alert runModal];
3155 }
3156}
3157- (void)shutDown
3158{
3159 [NSApp terminate:nil];
3160}
3161@end
3162
3163int
3164main(int argc, char *argv[])
3165{
3168 start_civetweb(argc, argv);
3169
3170 [NSAutoreleasePool new];
3171 [NSApplication sharedApplication];
3172
3173 /* Add delegate to process menu item actions */
3174 Civetweb *myDelegate = [[Civetweb alloc] autorelease];
3175 [NSApp setDelegate:myDelegate];
3176
3177 /* Run this app as agent */
3178 ProcessSerialNumber psn = {0, kCurrentProcess};
3179 TransformProcessType(&psn, kProcessTransformToBackgroundApplication);
3180 SetFrontProcess(&psn);
3181
3182 /* Add status bar menu */
3183 id menu = [[NSMenu new] autorelease];
3184
3185 /* Add version menu item */
3186 [menu
3187 addItem:[[[NSMenuItem alloc]
3188 /*initWithTitle:[NSString stringWithFormat:@"%s",
3189 server_name]*/
3190 initWithTitle:[NSString stringWithUTF8String:g_server_name]
3191 action:@selector(noexist)
3192 keyEquivalent:@""] autorelease]];
3193
3194 /* Add configuration menu item */
3195 [menu addItem:[[[NSMenuItem alloc] initWithTitle:@"Edit configuration"
3196 action:@selector(editConfig)
3197 keyEquivalent:@""] autorelease]];
3198
3199 /* Add connect menu item */
3200 [menu
3201 addItem:[[[NSMenuItem alloc] initWithTitle:@"Open web root in a browser"
3202 action:@selector(openBrowser)
3203 keyEquivalent:@""] autorelease]];
3204
3205 /* Separator */
3206 [menu addItem:[NSMenuItem separatorItem]];
3207
3208 /* Add quit menu item */
3209 [menu addItem:[[[NSMenuItem alloc] initWithTitle:@"Quit"
3210 action:@selector(shutDown)
3211 keyEquivalent:@"q"] autorelease]];
3212
3213 /* Attach menu to the status bar */
3214 id item = [[[NSStatusBar systemStatusBar]
3215 statusItemWithLength:NSVariableStatusItemLength] retain];
3216 [item setHighlightMode:YES];
3217 [item setImage:[NSImage imageNamed:@"civetweb_22x22.png"]];
3218 [item setMenu:menu];
3219
3220 /* Run the app */
3221 [NSApp activateIgnoringOtherApps:YES];
3222 [NSApp run];
3223
3224 stop_civetweb();
3226
3227 return EXIT_SUCCESS;
3228}
3229
3230#else
3231
3232
3233/* main for Linux (and others) */
3234int
3235main(int argc, char *argv[])
3236{
3239 start_civetweb(argc, argv);
3240 fprintf(stdout,
3241 "%s started on port(s) %s with web root [%s]\n",
3243 mg_get_option(g_ctx, "listening_ports"),
3244 mg_get_option(g_ctx, "document_root"));
3245
3246 while (g_exit_flag == 0) {
3247 sleep(1);
3248 }
3249
3250 fprintf(stdout,
3251 "Exiting on signal %d, waiting for all threads to finish...",
3252 g_exit_flag);
3253 fflush(stdout);
3254 stop_civetweb();
3255 fprintf(stdout, "%s", " done.\n");
3256
3258
3259 return EXIT_SUCCESS;
3260}
3261#endif /* _WIN32 */
3262#undef printf
CIVETWEB_API int mg_start_domain(struct mg_context *ctx, const char **options)
Definition civetweb.c:21422
CIVETWEB_API const char * mg_version(void)
Definition civetweb.c:3514
#define realloc
Definition civetweb.c:1541
CIVETWEB_API struct mg_context * mg_start2(struct mg_init_data *init, struct mg_error_data *error)
Definition civetweb.c:20464
#define free
Definition civetweb.c:1542
CIVETWEB_API void mg_stop(struct mg_context *ctx)
Definition civetweb.c:20346
CIVETWEB_API int mg_get_server_ports(const struct mg_context *ctx, int size, struct mg_server_port *ports)
Definition civetweb.c:3248
#define mg_get_option
Definition civetweb.c:3185
CIVETWEB_API int mg_strcasecmp(const char *s1, const char *s2)
Definition civetweb.c:3034
CIVETWEB_API const struct mg_response_info * mg_get_response_info(const struct mg_connection *conn)
Definition civetweb.c:3561
CIVETWEB_API int mg_get_response(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int timeout)
Definition civetweb.c:18822
CIVETWEB_API unsigned mg_exit_library(void)
Definition civetweb.c:22477
CIVETWEB_API int mg_get_context_info(const struct mg_context *ctx, char *buffer, int buflen)
Definition civetweb.c:21843
CIVETWEB_API int mg_modify_passwords_file(const char *fname, const char *domain, const char *user, const char *pass)
Definition civetweb.c:9220
#define calloc
Definition civetweb.c:1540
CIVETWEB_API const struct mg_option * mg_get_valid_options(void)
Definition civetweb.c:2833
#define snprintf
Definition civetweb.c:1543
CIVETWEB_API unsigned mg_init_library(unsigned features)
Definition civetweb.c:22347
CIVETWEB_API int mg_printf(struct mg_connection *conn, const char *fmt,...)
Definition civetweb.c:7034
CIVETWEB_API struct mg_connection * mg_connect_client(const char *host, int port, int use_ssl, char *error_buffer, size_t error_buffer_size)
Definition civetweb.c:18253
#define malloc
Definition civetweb.c:1539
CIVETWEB_API void mg_close_connection(struct mg_connection *conn)
Definition civetweb.c:17938
CIVETWEB_API int mg_get_system_info(char *buffer, int buflen)
Definition civetweb.c:21512
CIVETWEB_API int mg_read(struct mg_connection *conn, void *buf, size_t len)
Definition civetweb.c:6614
@ MG_FEATURES_DEFAULT
Definition civetweb.h:57
@ MG_FEATURES_TLS
Definition civetweb.h:66
@ MG_CONFIG_TYPE_UNKNOWN
Definition civetweb.h:694
@ MG_CONFIG_TYPE_FILE
Definition civetweb.h:697
@ MG_CONFIG_TYPE_STRING
Definition civetweb.h:696
@ MG_CONFIG_TYPE_DIRECTORY
Definition civetweb.h:698
@ MG_CONFIG_TYPE_EXT_PATTERN
Definition civetweb.h:700
@ MG_CONFIG_TYPE_STRING_MULTILINE
Definition civetweb.h:702
@ MG_CONFIG_TYPE_STRING_LIST
Definition civetweb.h:701
@ MG_CONFIG_TYPE_YES_NO_OPTIONAL
Definition civetweb.h:703
@ MG_CONFIG_TYPE_NUMBER
Definition civetweb.h:695
@ MG_CONFIG_TYPE_BOOLEAN
Definition civetweb.h:699
int run_lua(const char *file_name)
DUK_EXTERNAL void duk_destroy_heap(duk_context *ctx)
DUK_EXTERNAL void duk_pop(duk_context *ctx)
#define duk_safe_to_string(ctx, index)
#define duk_create_heap_default()
#define duk_peval_file(ctx, path)
#define NULL
Definition gmacros.h:924
#define TRUE
Definition gmacros.h:933
#define FALSE
Definition gmacros.h:929
const char * name
Definition lsqlite3.c:2154
int value
Definition lsqlite3.c:2155
static void error(LoadState *S, const char *why)
CURL_EXTERN CURLMcode curl_socket_t s
Definition multi.h:318
int main(void)
Definition sanitycheckc.c:1
size_t fwrite(const void *, size_t, size_t, FILE *)
unsigned code
Definition civetweb.h:1724
void * user_data
Definition civetweb.h:1800
const struct mg_callbacks * callbacks
Definition civetweb.h:1799
const char ** configuration_options
Definition civetweb.h:1801
const char * default_value
Definition civetweb.h:688
const char * name
Definition civetweb.h:686
const char * status_text
Definition civetweb.h:193
static NO_RETURN void die(const char *fmt,...)
@ OPTION_WEBPAGE
@ OPTION_HIDE_TRAY
@ NUM_MAIN_OPTIONS
@ OPTION_ADD_DOMAIN
static int read_config_file(const char *config_file, const char **options)
static NO_RETURN void show_usage_and_exit(const char *exeName)
static const char * g_server_name
#define DIRSEP
static void set_absolute_path(const char *options[], const char *option_name, const char *path_to_civetweb_exe)
static const char * g_icon_name
static int run_client(const char *url_arg)
#define CONFIG_FILE
static void free_system_info(void)
static void warn(const char *fmt,...)
static int sanitize_options(const char *options[], const char *arg0)
#define DEBUG_ASSERT(cond)
static void stop_civetweb(void)
static char * g_system_info
static int g_num_add_domains
static char g_server_base_name[40]
static void start_civetweb(int argc, char *argv[])
#define MAX_OPTIONS
static char * sdup(const char *str)
static int is_path_absolute(const char *path)
static int set_option(const char **options, const char *name, const char *value)
#define abs_path(rel, abs, abs_size)
static const char ** g_add_domain
static void show_server_name(void)
#define NO_RETURN
static const char * get_option(const char **options, const char *option_name)
static void process_command_line_arguments(int argc, char *argv[], const char **options)
static int g_hide_tray
static char g_config_file_name[PATH_MAX]
static const char * g_website
static struct tuser_data g_user_data
static struct mg_context * g_ctx
static struct mg_option main_config_options[]
#define WINCDECL
volatile int g_exit_flag
#define IGNORE_UNUSED_RESULT(a)
#define MAX_CONF_FILE_LINE_SIZE
static int verify_existence(const char **options, const char *option_name, int must_be_dir)
#define PATH_MAX
static void init_server_name(void)
static void init_system_info(void)
#define PASSWORDS_FILE_NAME
static void WINCDECL signal_handler(int sig_num)