mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-24 11:54:49 +00:00
Added config editor for Win32
This commit is contained in:
1 parent
f109030cd0
commit
f0a2924a7c
2 files changed
+298
-33
No files matched your search
+1
-1
@@ -1 +1 @@
|
||||
200 ICON DISCARDABLE "systray.ico"
|
||||
100 ICON DISCARDABLE "systray.ico"
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user