Commit f0a2924a authored by Sergey Lyubka's avatar Sergey Lyubka

Added config editor for Win32

parent f109030c
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,19 +218,21 @@ 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')
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);
}
}
(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,7 +746,8 @@ 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),
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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment