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