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