|
@@ -40,6 +40,7 @@
|
|
|
#ifdef _WIN32
|
|
|
#include <windows.h>
|
|
|
#include <winsvc.h>
|
|
|
+#include <shlobj.h>
|
|
|
#define PATH_MAX MAX_PATH
|
|
|
#define S_ISDIR(x) ((x) & _S_IFDIR)
|
|
|
#define DIRSEP '\\'
|
|
@@ -111,6 +112,14 @@ static void show_usage_and_exit(void) {
|
|
|
}
|
|
|
|
|
|
#if defined(_WIN32) || defined(USE_COCOA)
|
|
|
+static const char *config_file_top_comment =
|
|
|
+"# Mongoose web server configuration file.\n"
|
|
|
+"# For detailed description of every option, visit\n"
|
|
|
+"# https://github.com/valenok/mongoose/blob/master/UserManual.md\n"
|
|
|
+"# Lines starting with '#' and empty lines are ignored.\n"
|
|
|
+"# To make a change, remove leading '#', modify option's value,\n"
|
|
|
+"# save this file and then restart Mongoose.\n\n";
|
|
|
+
|
|
|
static void create_config_file(const char *path) {
|
|
|
const char **names, *value;
|
|
|
FILE *fp;
|
|
@@ -120,13 +129,7 @@ static void create_config_file(const char *path) {
|
|
|
if ((fp = fopen(path, "r")) != NULL) {
|
|
|
fclose(fp);
|
|
|
} else if ((fp = fopen(path, "a+")) != NULL) {
|
|
|
- fprintf(fp, "%s",
|
|
|
- "# Mongoose web server configuration file.\n"
|
|
|
- "# For detailed description of every option, visit\n"
|
|
|
- "# https://github.com/valenok/mongoose/blob/master/UserManual.md\n"
|
|
|
- "# Lines starting with '#' and empty lines are ignored.\n"
|
|
|
- "# To make a change, remove leading '#', modify option's value,\n"
|
|
|
- "# save this file and then restart Mongoose.\n\n");
|
|
|
+ fprintf(fp, "%s", config_file_top_comment);
|
|
|
names = mg_get_valid_option_names();
|
|
|
for (i = 0; names[i] != NULL; i += 3) {
|
|
|
value = mg_get_option(ctx, names[i]);
|
|
@@ -215,18 +218,20 @@ static void process_command_line_arguments(char *argv[], char **options) {
|
|
|
|
|
|
// Loop over the lines in config file
|
|
|
while (fgets(line, sizeof(line), fp) != NULL) {
|
|
|
-
|
|
|
line_no++;
|
|
|
|
|
|
// Ignore empty lines and comments
|
|
|
- for (i = 0; isspace(* (unsigned char *) &line[i]); ) i++;
|
|
|
- if (line[i] == '#' || line[i] == '\0')
|
|
|
+ for (i = 0; isspace(* (unsigned char *) &line[i]); ) i++;
|
|
|
+ if (line[i] == '#' || line[i] == '\0') {
|
|
|
continue;
|
|
|
+ }
|
|
|
|
|
|
if (sscanf(line, "%s %[^\r\n#]", opt, val) != 2) {
|
|
|
- die("%s: line %d is invalid", config_file, (int) line_no);
|
|
|
+ printf("%s: line %d is invalid, ignoring it:\n %s",
|
|
|
+ config_file, (int) line_no, line);
|
|
|
+ } else {
|
|
|
+ set_option(options, opt, val);
|
|
|
}
|
|
|
- set_option(options, opt, val);
|
|
|
}
|
|
|
|
|
|
(void) fclose(fp);
|
|
@@ -293,9 +298,16 @@ static void start_mongoose(int argc, char *argv[]) {
|
|
|
}
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
+enum {
|
|
|
+ ID_TRAYICON = 100, ID_QUIT, ID_SETTINGS, ID_SEPARATOR, ID_INSTALL_SERVICE,
|
|
|
+ ID_REMOVE_SERVICE, ID_STATIC, ID_GROUP, ID_SAVE, ID_TIMER, ID_RESET_DEFAULTS,
|
|
|
+ ID_STATUS, ID_CONTROLS = 200, ID_FILE_BUTTONS_DELTA = 1000
|
|
|
+};
|
|
|
+static HICON hIcon;
|
|
|
static SERVICE_STATUS ss;
|
|
|
static SERVICE_STATUS_HANDLE hStatus;
|
|
|
static const char *service_magic_argument = "--";
|
|
|
+static NOTIFYICONDATA TrayIcon;
|
|
|
|
|
|
static void WINAPI ControlHandler(DWORD code) {
|
|
|
if (code == SERVICE_CONTROL_STOP || code == SERVICE_CONTROL_SHUTDOWN) {
|
|
@@ -323,21 +335,6 @@ static void WINAPI ServiceMain(void) {
|
|
|
SetServiceStatus(hStatus, &ss);
|
|
|
}
|
|
|
|
|
|
-#define ID_TRAYICON 100
|
|
|
-#define ID_QUIT 101
|
|
|
-#define ID_EDIT_CONFIG 102
|
|
|
-#define ID_SEPARATOR 103
|
|
|
-#define ID_INSTALL_SERVICE 104
|
|
|
-#define ID_REMOVE_SERVICE 105
|
|
|
-#define ID_ICON 200
|
|
|
-static NOTIFYICONDATA TrayIcon;
|
|
|
-
|
|
|
-static void edit_config_file(void) {
|
|
|
- char cmd[200];
|
|
|
- create_config_file(config_file);
|
|
|
- snprintf(cmd, sizeof(cmd), "notepad.exe %s", config_file);
|
|
|
- WinExec(cmd, SW_SHOW);
|
|
|
-}
|
|
|
|
|
|
static void show_error(void) {
|
|
|
char buf[256];
|
|
@@ -348,6 +345,273 @@ static void show_error(void) {
|
|
|
MessageBox(NULL, buf, "Error", MB_OK);
|
|
|
}
|
|
|
|
|
|
+static void *align(void *ptr, DWORD alig) {
|
|
|
+ ULONG ul = (ULONG) ptr;
|
|
|
+ ul += alig;
|
|
|
+ ul &= ~alig;
|
|
|
+ return ((void *) ul);
|
|
|
+}
|
|
|
+
|
|
|
+static int is_boolean_option(const char *option_name) {
|
|
|
+ return !strcmp(option_name, "enable_directory_listing") ||
|
|
|
+ !strcmp(option_name, "enable_keep_alive");
|
|
|
+}
|
|
|
+
|
|
|
+static int is_filename_option(const char *option_name) {
|
|
|
+ return !strcmp(option_name, "cgi_interpreter") ||
|
|
|
+ !strcmp(option_name, "global_auth_file") ||
|
|
|
+ !strcmp(option_name, "put_delete_auth_file") ||
|
|
|
+ !strcmp(option_name, "access_log_file") ||
|
|
|
+ !strcmp(option_name, "error_log_file") ||
|
|
|
+ !strcmp(option_name, "ssl_certificate");
|
|
|
+}
|
|
|
+
|
|
|
+static int is_directory_option(const char *option_name) {
|
|
|
+ return !strcmp(option_name, "document_root");
|
|
|
+}
|
|
|
+
|
|
|
+static int is_numeric_options(const char *option_name) {
|
|
|
+ return !strcmp(option_name, "num_threads");
|
|
|
+}
|
|
|
+
|
|
|
+static void save_config(HWND hDlg, FILE *fp) {
|
|
|
+ char value[2000];
|
|
|
+ const char **options, *name, *default_value;
|
|
|
+ int i, id;
|
|
|
+
|
|
|
+ fprintf(fp, "%s", config_file_top_comment);
|
|
|
+ options = mg_get_valid_option_names();
|
|
|
+ for (i = 0; options[i] != NULL; i += 3) {
|
|
|
+ name = options[i + 1];
|
|
|
+ id = ID_CONTROLS + i / 3;
|
|
|
+ if (is_boolean_option(name)) {
|
|
|
+ snprintf(value, sizeof(value), "%s",
|
|
|
+ IsDlgButtonChecked(hDlg, id) ? "yes" : "no");
|
|
|
+ } else {
|
|
|
+ GetDlgItemText(hDlg, id, value, sizeof(value));
|
|
|
+ }
|
|
|
+ default_value = options[i + 2] == NULL ? "" : options[i + 2];
|
|
|
+ // If value is the same as default, skip it
|
|
|
+ if (strcmp(value, default_value) != 0) {
|
|
|
+ fprintf(fp, "%s %s\n", name, value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) {
|
|
|
+ FILE *fp;
|
|
|
+ int i;
|
|
|
+ const char *name, *value, **options = mg_get_valid_option_names();
|
|
|
+
|
|
|
+ switch (msg) {
|
|
|
+ case WM_CLOSE:
|
|
|
+ KillTimer(hDlg, ID_TIMER);
|
|
|
+ DestroyWindow(hDlg);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case WM_COMMAND:
|
|
|
+ switch (LOWORD(wParam)) {
|
|
|
+ case ID_SAVE:
|
|
|
+ EnableWindow(GetDlgItem(hDlg, ID_SAVE), FALSE);
|
|
|
+ if ((fp = fopen(config_file, "w+")) != NULL) {
|
|
|
+ save_config(hDlg, fp);
|
|
|
+ fclose(fp);
|
|
|
+ mg_stop(ctx);
|
|
|
+ start_mongoose(__argc, __argv);
|
|
|
+ }
|
|
|
+ EnableWindow(GetDlgItem(hDlg, ID_SAVE), TRUE);
|
|
|
+ break;
|
|
|
+ case ID_RESET_DEFAULTS:
|
|
|
+ for (i = 0; options[i] != NULL; i += 3) {
|
|
|
+ name = options[i + 1];
|
|
|
+ value = options[i + 2] == NULL ? "" : options[i + 2];
|
|
|
+ if (is_boolean_option(name)) {
|
|
|
+ CheckDlgButton(hDlg, ID_CONTROLS + i / 3, !strcmp(value, "yes") ?
|
|
|
+ BST_CHECKED : BST_UNCHECKED);
|
|
|
+ } else {
|
|
|
+ SetWindowText(GetDlgItem(hDlg, ID_CONTROLS + i / 3), value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; options[i] != NULL; i += 3) {
|
|
|
+ name = options[i + 1];
|
|
|
+ if ((is_filename_option(name) || is_directory_option(name)) &&
|
|
|
+ LOWORD(wParam) == ID_CONTROLS + i / 3 + ID_FILE_BUTTONS_DELTA) {
|
|
|
+ OPENFILENAME of;
|
|
|
+ BROWSEINFO bi;
|
|
|
+ char path[PATH_MAX] = "";
|
|
|
+
|
|
|
+ memset(&of, 0, sizeof(of));
|
|
|
+ of.lStructSize = sizeof(of);
|
|
|
+ of.hwndOwner = (HWND) hDlg;
|
|
|
+ of.lpstrFile = path;
|
|
|
+ of.nMaxFile = sizeof(path);
|
|
|
+ of.lpstrInitialDir = mg_get_option(ctx, "document_root");
|
|
|
+ of.Flags = OFN_CREATEPROMPT | OFN_NOCHANGEDIR;
|
|
|
+
|
|
|
+ memset(&bi, 0, sizeof(bi));
|
|
|
+ bi.hwndOwner = (HWND) hDlg;
|
|
|
+ bi.lpszTitle = "Choose WWW root directory:";
|
|
|
+ bi.ulFlags = BIF_RETURNONLYFSDIRS;
|
|
|
+
|
|
|
+ if (is_directory_option(name)) {
|
|
|
+ SHGetPathFromIDList(SHBrowseForFolder(&bi), path);
|
|
|
+ } else {
|
|
|
+ GetOpenFileName(&of);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (path[0] != '\0') {
|
|
|
+ SetWindowText(GetDlgItem(hDlg, ID_CONTROLS + i / 3), path);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ case WM_INITDIALOG:
|
|
|
+ SendMessage(hDlg, WM_SETICON,(WPARAM) ICON_SMALL, (LPARAM) hIcon);
|
|
|
+ SendMessage(hDlg, WM_SETICON,(WPARAM) ICON_BIG, (LPARAM) hIcon);
|
|
|
+ SetWindowText(hDlg, "Mongoose settings");
|
|
|
+ SetFocus(GetDlgItem(hDlg, ID_SAVE));
|
|
|
+ for (i = 0; options[i] != NULL; i += 3) {
|
|
|
+ name = options[i + 1];
|
|
|
+ value = mg_get_option(ctx, name);
|
|
|
+ if (is_boolean_option(name)) {
|
|
|
+ CheckDlgButton(hDlg, ID_CONTROLS + i / 3, !strcmp(value, "yes") ?
|
|
|
+ BST_CHECKED : BST_UNCHECKED);
|
|
|
+ } else {
|
|
|
+ SetDlgItemText(hDlg, ID_CONTROLS + i / 3, value == NULL ? "" : value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return FALSE;
|
|
|
+}
|
|
|
+
|
|
|
+static void add_control(unsigned char **mem, DLGTEMPLATE *dia, WORD type,
|
|
|
+ DWORD id, DWORD style, WORD x, WORD y,
|
|
|
+ WORD cx, WORD cy, const char *caption) {
|
|
|
+ DLGITEMTEMPLATE *tp;
|
|
|
+ LPWORD p;
|
|
|
+
|
|
|
+ dia->cdit++;
|
|
|
+
|
|
|
+ *mem = align(*mem, 3);
|
|
|
+ tp = (DLGITEMTEMPLATE *) *mem;
|
|
|
+
|
|
|
+ tp->id = (WORD)id;
|
|
|
+ tp->style = style;
|
|
|
+ tp->dwExtendedStyle = 0;
|
|
|
+ tp->x = x;
|
|
|
+ tp->y = y;
|
|
|
+ tp->cx = cx;
|
|
|
+ tp->cy = cy;
|
|
|
+
|
|
|
+ p = align(*mem + sizeof(*tp), 1);
|
|
|
+ *p++ = 0xffff;
|
|
|
+ *p++ = type;
|
|
|
+
|
|
|
+ while (*caption != '\0') {
|
|
|
+ *p++ = (WCHAR) *caption++;
|
|
|
+ }
|
|
|
+ *p++ = 0;
|
|
|
+ p = align(p, 1);
|
|
|
+
|
|
|
+ *p++ = 0;
|
|
|
+ *mem = (unsigned char *) p;
|
|
|
+}
|
|
|
+
|
|
|
+static void show_settings_dialog() {
|
|
|
+#define HEIGHT 15
|
|
|
+#define WIDTH 400
|
|
|
+#define LABEL_WIDTH 80
|
|
|
+
|
|
|
+ unsigned char mem[4096], *p;
|
|
|
+ const char **option_names, *long_option_name;
|
|
|
+ DWORD style;
|
|
|
+ DLGTEMPLATE *dia = (DLGTEMPLATE *) mem;
|
|
|
+ WORD i, cl, x, y, width, nelems = 0;
|
|
|
+ static int guard;
|
|
|
+
|
|
|
+ static struct {
|
|
|
+ DLGTEMPLATE template; // 18 bytes
|
|
|
+ WORD menu, class;
|
|
|
+ wchar_t caption[1];
|
|
|
+ WORD fontsiz;
|
|
|
+ wchar_t fontface[7];
|
|
|
+ } dialog_header = {{WS_CAPTION | WS_POPUP | WS_SYSMENU | WS_VISIBLE |
|
|
|
+ DS_SETFONT | WS_DLGFRAME, WS_EX_TOOLWINDOW, 0, 200, 200, WIDTH, 0},
|
|
|
+ 0, 0, L"", 8, L"Tahoma"};
|
|
|
+
|
|
|
+ if (guard == 0) {
|
|
|
+ guard++;
|
|
|
+ } else {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ (void) memset(mem, 0, sizeof(mem));
|
|
|
+ (void) memcpy(mem, &dialog_header, sizeof(dialog_header));
|
|
|
+ p = mem + sizeof(dialog_header);
|
|
|
+
|
|
|
+ option_names = mg_get_valid_option_names();
|
|
|
+ for (i = 0; option_names[i] != NULL; i += 3) {
|
|
|
+ long_option_name = option_names[i + 1];
|
|
|
+ style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
|
|
|
+ x = 10 + (WIDTH / 2) * (nelems % 2);
|
|
|
+ y = (nelems/2 + 1) * HEIGHT + 5;
|
|
|
+ width = WIDTH / 2 - 20 - LABEL_WIDTH;
|
|
|
+ if (is_numeric_options(long_option_name)) {
|
|
|
+ style |= ES_NUMBER;
|
|
|
+ cl = 0x81;
|
|
|
+ style |= WS_BORDER | ES_AUTOHSCROLL;
|
|
|
+ } else if (is_boolean_option(long_option_name)) {
|
|
|
+ cl = 0x80;
|
|
|
+ style |= BS_AUTOCHECKBOX;
|
|
|
+ } else if (is_filename_option(long_option_name) ||
|
|
|
+ is_directory_option(long_option_name)) {
|
|
|
+ style |= WS_BORDER | ES_AUTOHSCROLL;
|
|
|
+ width -= 20;
|
|
|
+ cl = 0x81;
|
|
|
+ add_control(&p, dia, 0x80,
|
|
|
+ ID_CONTROLS + (i / 3) + ID_FILE_BUTTONS_DELTA,
|
|
|
+ WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
|
|
|
+ (WORD) (x + width + LABEL_WIDTH + 5),
|
|
|
+ y, 15, 12, "...");
|
|
|
+ } else {
|
|
|
+ cl = 0x81;
|
|
|
+ style |= WS_BORDER | ES_AUTOHSCROLL;
|
|
|
+ }
|
|
|
+ add_control(&p, dia, 0x82, ID_STATIC, WS_VISIBLE | WS_CHILD,
|
|
|
+ x, y, LABEL_WIDTH, HEIGHT, long_option_name);
|
|
|
+ add_control(&p, dia, cl, ID_CONTROLS + (i / 3), style,
|
|
|
+ (WORD) (x + LABEL_WIDTH), y, width, 12, "");
|
|
|
+ nelems++;
|
|
|
+ }
|
|
|
+
|
|
|
+ y = (WORD) (((nelems + 1) / 2 + 1) * HEIGHT + 5);
|
|
|
+ add_control(&p, dia, 0x80, ID_GROUP, WS_CHILD | WS_VISIBLE |
|
|
|
+ BS_GROUPBOX, 5, 5, WIDTH - 10, y, " Settings ");
|
|
|
+ y += 10;
|
|
|
+ add_control(&p, dia, 0x80, ID_SAVE,
|
|
|
+ WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
|
|
|
+ WIDTH - 70, y, 65, 12, "Save Settings");
|
|
|
+ add_control(&p, dia, 0x80, ID_RESET_DEFAULTS,
|
|
|
+ WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
|
|
|
+ WIDTH - 140, y, 65, 12, "Reset to defaults");
|
|
|
+ add_control(&p, dia, 0x82, ID_STATIC,
|
|
|
+ WS_CHILD | WS_VISIBLE | WS_DISABLED,
|
|
|
+ 5, y, 180, 12, server_name);
|
|
|
+
|
|
|
+ dia->cy = ((nelems + 1) / 2 + 1) * HEIGHT + 30;
|
|
|
+ DialogBoxIndirectParam(NULL, dia, NULL, DlgProc, (LPARAM) NULL);
|
|
|
+ guard--;
|
|
|
+}
|
|
|
+
|
|
|
static int manage_service(int action) {
|
|
|
static const char *service_name = "Mongoose";
|
|
|
SC_HANDLE hSCM = NULL, hService = NULL;
|
|
@@ -417,8 +681,8 @@ static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam,
|
|
|
Shell_NotifyIcon(NIM_DELETE, &TrayIcon);
|
|
|
PostQuitMessage(0);
|
|
|
return 0;
|
|
|
- case ID_EDIT_CONFIG:
|
|
|
- edit_config_file();
|
|
|
+ case ID_SETTINGS:
|
|
|
+ show_settings_dialog();
|
|
|
break;
|
|
|
case ID_INSTALL_SERVICE:
|
|
|
case ID_REMOVE_SERVICE:
|
|
@@ -443,7 +707,7 @@ static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam,
|
|
|
AppendMenu(hMenu, MF_STRING | (!service_installed ? MF_GRAYED : 0),
|
|
|
ID_REMOVE_SERVICE, "Deinstall service");
|
|
|
AppendMenu(hMenu, MF_SEPARATOR, ID_SEPARATOR, "");
|
|
|
- AppendMenu(hMenu, MF_STRING, ID_EDIT_CONFIG, "Edit config file");
|
|
|
+ AppendMenu(hMenu, MF_STRING, ID_SETTINGS, "Settings");
|
|
|
AppendMenu(hMenu, MF_STRING, ID_QUIT, "Exit");
|
|
|
GetCursorPos(&pt);
|
|
|
SetForegroundWindow(hWnd);
|
|
@@ -482,8 +746,9 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR cmdline, int show) {
|
|
|
TrayIcon.cbSize = sizeof(TrayIcon);
|
|
|
TrayIcon.uID = ID_TRAYICON;
|
|
|
TrayIcon.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
|
|
|
- TrayIcon.hIcon = LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(ID_ICON),
|
|
|
- IMAGE_ICON, 16, 16, 0);
|
|
|
+ TrayIcon.hIcon = hIcon = LoadImage(GetModuleHandle(NULL),
|
|
|
+ MAKEINTRESOURCE(ID_TRAYICON),
|
|
|
+ IMAGE_ICON, 16, 16, 0);
|
|
|
TrayIcon.hWnd = hWnd;
|
|
|
snprintf(TrayIcon.szTip, sizeof(TrayIcon.szTip), "%s", server_name);
|
|
|
TrayIcon.uCallbackMessage = WM_USER;
|