mirror of
https://gitlab.rtems.org/rtems/rtos/rtems.git
synced 2025-12-27 15:00:16 +00:00
924 lines
22 KiB
C
924 lines
22 KiB
C
/*
|
|
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
|
|
* All rights reserved
|
|
*
|
|
* "THE BEER-WARE LICENSE" (Revision 42):
|
|
* Sergey Lyubka wrote this file. As long as you retain this notice you
|
|
* can do whatever you want with this stuff. If we meet some day, and you think
|
|
* this stuff is worth it, you can buy me a beer in return.
|
|
*/
|
|
|
|
#include "defs.h"
|
|
|
|
static const char *config_file = CONFIG;
|
|
|
|
#if !defined(NO_GUI)
|
|
|
|
static HICON hIcon; /* SHTTPD icon handle */
|
|
HWND hLog; /* Log window */
|
|
|
|
/*
|
|
* Dialog box control IDs
|
|
*/
|
|
#define ID_GROUP 100
|
|
#define ID_SAVE 101
|
|
#define ID_STATUS 102
|
|
#define ID_STATIC 103
|
|
#define ID_SETTINGS 104
|
|
#define ID_QUIT 105
|
|
#define ID_TRAYICON 106
|
|
#define ID_TIMER 107
|
|
#define ID_ICON 108
|
|
#define ID_ADVANCED 109
|
|
#define ID_SHOWLOG 110
|
|
#define ID_LOG 111
|
|
|
|
#define ID_USER 200
|
|
#define ID_DELTA 1000
|
|
|
|
static void
|
|
run_server(void *param)
|
|
{
|
|
struct shttpd_ctx *ctx = param;
|
|
|
|
if (shttpd_listen(ctx, ctx->port) == -1)
|
|
elog(E_FATAL, NULL, "Cannot open socket on port %d", ctx->port);
|
|
|
|
while (WaitForSingleObject(ctx->ev[0], 0) != WAIT_OBJECT_0)
|
|
shttpd_poll(ctx, 1000);
|
|
|
|
SetEvent(ctx->ev[1]);
|
|
shttpd_fini(ctx);
|
|
}
|
|
|
|
/*
|
|
* Save the configuration back into config file
|
|
*/
|
|
static void
|
|
save_config(HWND hDlg, FILE *fp)
|
|
{
|
|
const struct opt *opt;
|
|
char text[FILENAME_MAX];
|
|
int id;
|
|
|
|
if (fp == NULL)
|
|
elog(E_FATAL, NULL, "save_config: cannot open %s", config_file);
|
|
|
|
for (opt = options; opt->name != NULL; opt++) {
|
|
id = ID_USER + (opt - options); /* Control ID */
|
|
|
|
/* Do not save if the text is the same as default */
|
|
|
|
if (opt->flags & OPT_BOOL)
|
|
(void) fprintf(fp, "%s\t%d\n",
|
|
opt->name, IsDlgButtonChecked(hDlg, id));
|
|
else if (GetDlgItemText(hDlg, id, text, sizeof(text)) != 0 &&
|
|
(opt->def == NULL || strcmp(text, opt->def) != 0))
|
|
(void) fprintf(fp, "%s\t%s\n", opt->name, text);
|
|
}
|
|
|
|
(void) fclose(fp);
|
|
}
|
|
|
|
static void
|
|
set_control_values(HWND hDlg, const struct shttpd_ctx *ctx)
|
|
{
|
|
const struct opt *opt;
|
|
const union variant *v;
|
|
char buf[FILENAME_MAX];
|
|
int id;
|
|
|
|
for (opt = options; opt->name != NULL; opt++) {
|
|
id = ID_USER + (opt - options);
|
|
v = (union variant *) ((char *) ctx + opt->ofs);
|
|
if (opt->flags & OPT_BOOL) {
|
|
CheckDlgButton(hDlg, id,
|
|
v->v_int ? BST_CHECKED : BST_UNCHECKED);
|
|
} else if (opt->flags & OPT_INT) {
|
|
my_snprintf(buf, sizeof(buf), "%d", v->v_int);
|
|
SetDlgItemText(hDlg, id, buf);
|
|
} else {
|
|
SetDlgItemText(hDlg, id, v->v_str);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static BOOL CALLBACK
|
|
DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
static struct shttpd_ctx *ctx, **pctx;
|
|
HANDLE ev;
|
|
const struct opt *opt;
|
|
DWORD tid;
|
|
int id, up;
|
|
char text[256];
|
|
|
|
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);
|
|
save_config(hDlg, fopen(config_file, "w+"));
|
|
ev = ctx->ev[1];
|
|
SetEvent(ctx->ev[0]);
|
|
WaitForSingleObject(ev, INFINITE);
|
|
*pctx = ctx = init_from_argc_argv(config_file, 0, NULL);
|
|
shttpd_listen(ctx, ctx->port);
|
|
_beginthread(run_server, 0, ctx);
|
|
EnableWindow(GetDlgItem(hDlg, ID_SAVE), TRUE);
|
|
|
|
break;
|
|
}
|
|
|
|
id = ID_USER + ID_DELTA;
|
|
for (opt = options; opt->name != NULL; opt++, id++)
|
|
if (LOWORD(wParam) == id) {
|
|
OPENFILENAME of;
|
|
BROWSEINFO bi;
|
|
char path[FILENAME_MAX] = "";
|
|
|
|
memset(&of, 0, sizeof(of));
|
|
of.lStructSize = sizeof(of);
|
|
of.hwndOwner = (HWND) hDlg;
|
|
of.lpstrFile = path;
|
|
of.nMaxFile = sizeof(path);
|
|
of.lpstrInitialDir = 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 (opt->flags & OPT_DIR)
|
|
SHGetPathFromIDList(
|
|
SHBrowseForFolder(&bi), path);
|
|
else
|
|
GetOpenFileName(&of);
|
|
|
|
if (path[0] != '\0')
|
|
SetWindowText(GetDlgItem(hDlg,
|
|
id - ID_DELTA), path);
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_INITDIALOG:
|
|
pctx = (struct shttpd_ctx **) lParam;
|
|
ctx = *pctx;
|
|
SendMessage(hDlg,WM_SETICON,(WPARAM)ICON_SMALL,(LPARAM)hIcon);
|
|
SendMessage(hDlg,WM_SETICON,(WPARAM)ICON_BIG,(LPARAM)hIcon);
|
|
SetWindowText(hDlg, "SHTTPD settings");
|
|
SetFocus(GetDlgItem(hDlg, ID_SAVE));
|
|
set_control_values(hDlg, ctx);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void *
|
|
align(void *ptr, DWORD alig)
|
|
{
|
|
ULONG ul = (ULONG) ptr;
|
|
|
|
ul += alig;
|
|
ul &= ~alig;
|
|
|
|
return ((void *) ul);
|
|
}
|
|
|
|
|
|
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(struct shttpd_ctx **ctxp)
|
|
{
|
|
#define HEIGHT 15
|
|
#define WIDTH 400
|
|
#define LABEL_WIDTH 70
|
|
|
|
unsigned char mem[4096], *p;
|
|
DWORD style;
|
|
DLGTEMPLATE *dia = (DLGTEMPLATE *) mem;
|
|
WORD cl, x, y, width, nelems = 0;
|
|
const struct opt *opt;
|
|
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);
|
|
|
|
for (opt = options; opt->name != NULL; opt++) {
|
|
|
|
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 (opt->flags & OPT_INT) {
|
|
style |= ES_NUMBER;
|
|
cl = 0x81;
|
|
style |= WS_BORDER | ES_AUTOHSCROLL;
|
|
} else if (opt->flags & OPT_BOOL) {
|
|
cl = 0x80;
|
|
style |= BS_AUTOCHECKBOX;
|
|
} else if (opt->flags & (OPT_DIR | OPT_FILE)) {
|
|
style |= WS_BORDER | ES_AUTOHSCROLL;
|
|
width -= 20;
|
|
cl = 0x81;
|
|
add_control(&p, dia, 0x80,
|
|
ID_USER + ID_DELTA + (opt - options),
|
|
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, opt->desc);
|
|
add_control(&p, dia, cl, ID_USER + (opt - options), 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");
|
|
#if 0
|
|
add_control(&p, dia, 0x80, ID_ADVANCED,
|
|
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
|
|
WIDTH - 190, y, 110, 12, "Show Advanced Settings >>");
|
|
#endif
|
|
add_control(&p, dia, 0x82, ID_STATIC,
|
|
WS_CHILD | WS_VISIBLE | WS_DISABLED,
|
|
5, y, 180, 12,"SHTTPD v." VERSION
|
|
" (http://shttpd.sourceforge.net)");
|
|
|
|
dia->cy = ((nelems + 1)/2 + 1) * HEIGHT + 30;
|
|
DialogBoxIndirectParam(NULL, dia, NULL, DlgProc, (LPARAM) ctxp);
|
|
guard--;
|
|
}
|
|
|
|
static BOOL CALLBACK
|
|
LogProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
static struct shttpd_ctx *ctx;
|
|
static HWND hStatus;
|
|
HWND hEdit;
|
|
RECT rect, rect2, rect3, rect4;
|
|
int len, up, widths[] = {120, 220, 330, 460, -1};
|
|
char text[256], buf[1024 * 64];
|
|
|
|
switch (msg) {
|
|
|
|
case WM_CLOSE:
|
|
KillTimer(hDlg, ID_TIMER);
|
|
DestroyWindow(hDlg);
|
|
break;
|
|
|
|
case WM_APP:
|
|
hEdit = GetDlgItem(hDlg, ID_LOG);
|
|
len = GetWindowText(hEdit, buf, sizeof(buf));
|
|
if (len > sizeof(buf) * 4 / 5)
|
|
len = sizeof(buf) * 4 / 5;
|
|
my_snprintf(buf + len, sizeof(buf) - len,
|
|
"%s\r\n", (char *) lParam);
|
|
SetWindowText(hEdit, buf);
|
|
SendMessage(hEdit, WM_VSCROLL, SB_BOTTOM, 0);
|
|
break;
|
|
|
|
case WM_TIMER:
|
|
/* Print statistics on a status bar */
|
|
up = current_time - ctx->start_time;
|
|
(void) my_snprintf(text, sizeof(text),
|
|
" Up: %3d h %2d min %2d sec",
|
|
up / 3600, up / 60 % 60, up % 60);
|
|
SendMessage(hStatus, SB_SETTEXT, 0, (LPARAM) text);
|
|
(void) my_snprintf(text, sizeof(text),
|
|
" Requests: %u", ctx->nrequests);
|
|
SendMessage(hStatus, SB_SETTEXT, 1, (LPARAM) text);
|
|
(void) my_snprintf(text, sizeof(text),
|
|
" Sent: %4.2f Mb", (double) ctx->out / 1048576);
|
|
SendMessage(hStatus, SB_SETTEXT, 2, (LPARAM) text);
|
|
(void) my_snprintf(text, sizeof(text),
|
|
" Received: %4.2f Mb", (double) ctx->in / 1048576);
|
|
SendMessage(hStatus, SB_SETTEXT, 3, (LPARAM) text);
|
|
break;
|
|
|
|
case WM_INITDIALOG:
|
|
ctx = (struct shttpd_ctx *) lParam;
|
|
SendMessage(hDlg,WM_SETICON,(WPARAM)ICON_SMALL,(LPARAM)hIcon);
|
|
SendMessage(hDlg,WM_SETICON,(WPARAM)ICON_BIG,(LPARAM)hIcon);
|
|
hStatus = CreateStatusWindow(WS_CHILD | WS_VISIBLE,
|
|
"", hDlg, ID_STATUS);
|
|
SendMessage(hStatus, SB_SETPARTS, 5, (LPARAM) widths);
|
|
SendMessage(hStatus, SB_SETTEXT, 4, (LPARAM) " Running");
|
|
SetWindowText(hDlg, "SHTTPD web server log");
|
|
SetTimer(hDlg, ID_TIMER, 1000, NULL);
|
|
GetWindowRect(GetDesktopWindow(), &rect3);
|
|
GetWindowRect(hDlg, &rect4);
|
|
GetClientRect(hDlg, &rect);
|
|
GetClientRect(hStatus, &rect2);
|
|
SetWindowPos(GetDlgItem(hDlg, ID_LOG), 0,
|
|
0, 0, rect.right, rect.bottom - rect2.bottom, 0);
|
|
SetWindowPos(hDlg, HWND_TOPMOST,
|
|
rect3.right - (rect4.right - rect4.left),
|
|
rect3.bottom - (rect4.bottom - rect4.top) - 30,
|
|
0, 0, SWP_NOSIZE);
|
|
SetFocus(hStatus);
|
|
SendMessage(hDlg, WM_TIMER, 0, 0);
|
|
hLog = hDlg;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
return (FALSE);
|
|
}
|
|
|
|
static void
|
|
show_log_window(struct shttpd_ctx *ctx)
|
|
{
|
|
unsigned char mem[4096], *p;
|
|
DWORD style;
|
|
DLGTEMPLATE *dia = (DLGTEMPLATE *) mem;
|
|
WORD cl, x, y, width, nelems = 0;
|
|
|
|
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_VISIBLE | WS_SYSMENU |
|
|
DS_SETFONT | WS_DLGFRAME, WS_EX_TOOLWINDOW,
|
|
0, 200, 200, 400, 100}, 0, 0, L"", 8, L"Tahoma"};
|
|
|
|
if (hLog != NULL)
|
|
return;
|
|
|
|
(void) memset(mem, 0, sizeof(mem));
|
|
(void) memcpy(mem, &dialog_header, sizeof(dialog_header));
|
|
p = mem + sizeof(dialog_header);
|
|
|
|
add_control(&p, dia, 0x81, ID_LOG, WS_CHILD | WS_VISIBLE |
|
|
WS_BORDER | WS_VSCROLL | ES_MULTILINE | ES_AUTOVSCROLL |
|
|
ES_READONLY, 5, 5, WIDTH - 10, 60, "");
|
|
|
|
DialogBoxIndirectParam(NULL, dia, NULL, LogProc, (LPARAM) ctx);
|
|
|
|
hLog = NULL;
|
|
}
|
|
|
|
static LRESULT CALLBACK
|
|
WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
static NOTIFYICONDATA ni;
|
|
static struct shttpd_ctx *ctx;
|
|
DWORD tid; /* Thread ID */
|
|
HMENU hMenu;
|
|
POINT pt;
|
|
|
|
switch (msg) {
|
|
case WM_CREATE:
|
|
ctx = ((CREATESTRUCT *) lParam)->lpCreateParams;
|
|
memset(&ni, 0, sizeof(ni));
|
|
ni.cbSize = sizeof(ni);
|
|
ni.uID = ID_TRAYICON;
|
|
ni.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
|
|
ni.hIcon = hIcon;
|
|
ni.hWnd = hWnd;
|
|
my_snprintf(ni.szTip, sizeof(ni.szTip), "SHTTPD web server");
|
|
ni.uCallbackMessage = WM_USER;
|
|
Shell_NotifyIcon(NIM_ADD, &ni);
|
|
ctx->ev[0] = CreateEvent(0, TRUE, FALSE, 0);
|
|
ctx->ev[1] = CreateEvent(0, TRUE, FALSE, 0);
|
|
_beginthread(run_server, 0, ctx);
|
|
break;
|
|
case WM_CLOSE:
|
|
Shell_NotifyIcon(NIM_DELETE, &ni);
|
|
PostQuitMessage(0);
|
|
break;
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam)) {
|
|
case ID_SETTINGS:
|
|
show_settings_dialog(&ctx);
|
|
break;
|
|
case ID_QUIT:
|
|
SendMessage(hWnd, WM_CLOSE, wParam, lParam);
|
|
PostQuitMessage(0);
|
|
break;
|
|
case ID_SHOWLOG:
|
|
show_log_window(ctx);
|
|
break;
|
|
}
|
|
break;
|
|
case WM_USER:
|
|
switch (lParam) {
|
|
case WM_RBUTTONUP:
|
|
case WM_LBUTTONUP:
|
|
case WM_LBUTTONDBLCLK:
|
|
hMenu = CreatePopupMenu();
|
|
AppendMenu(hMenu, 0, ID_SETTINGS, "Settings");
|
|
AppendMenu(hMenu, 0, ID_SHOWLOG, "Show Log");
|
|
AppendMenu(hMenu, 0, ID_QUIT, "Exit SHTTPD");
|
|
GetCursorPos(&pt);
|
|
TrackPopupMenu(hMenu, 0, pt.x, pt.y, 0, hWnd, NULL);
|
|
DestroyMenu(hMenu);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return (DefWindowProc(hWnd, msg, wParam, lParam));
|
|
}
|
|
|
|
int WINAPI
|
|
WinMain(HINSTANCE h, HINSTANCE prev, char *cmdline, int show)
|
|
{
|
|
struct shttpd_ctx *ctx;
|
|
WNDCLASS cls;
|
|
HWND hWnd;
|
|
MSG msg;
|
|
|
|
ctx = init_from_argc_argv(config_file, 0, NULL);
|
|
(void) memset(&cls, 0, sizeof(cls));
|
|
|
|
hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(ID_ICON));
|
|
if (hIcon == NULL)
|
|
hIcon = LoadIcon(NULL, IDI_APPLICATION);
|
|
cls.lpfnWndProc = (WNDPROC) WindowProc;
|
|
cls.hIcon = hIcon;
|
|
cls.lpszClassName = "shttpd v." VERSION;
|
|
|
|
if (!RegisterClass(&cls))
|
|
elog(E_FATAL, NULL, "RegisterClass: %d", ERRNO);
|
|
else if ((hWnd = CreateWindow(cls.lpszClassName, "",WS_OVERLAPPEDWINDOW,
|
|
0, 0, 0, 0, NULL, NULL, NULL, ctx)) == NULL)
|
|
elog(E_FATAL, NULL, "CreateWindow: %d", ERRNO);
|
|
|
|
while (GetMessage(&msg, (HWND) NULL, 0, 0)) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
#endif /* NO_GUI */
|
|
|
|
static void
|
|
fix_directory_separators(char *path)
|
|
{
|
|
for (; *path != '\0'; path++) {
|
|
if (*path == '/')
|
|
*path = '\\';
|
|
if (*path == '\\')
|
|
while (path[1] == '\\' || path[1] == '/')
|
|
(void) memmove(path + 1,
|
|
path + 2, strlen(path + 2) + 1);
|
|
}
|
|
}
|
|
|
|
int
|
|
my_open(const char *path, int flags, int mode)
|
|
{
|
|
char buf[FILENAME_MAX];
|
|
wchar_t wbuf[FILENAME_MAX];
|
|
|
|
my_strlcpy(buf, path, sizeof(buf));
|
|
fix_directory_separators(buf);
|
|
MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
|
|
|
|
return (_wopen(wbuf, flags));
|
|
}
|
|
|
|
int
|
|
my_stat(const char *path, struct stat *stp)
|
|
{
|
|
char buf[FILENAME_MAX], *p;
|
|
wchar_t wbuf[FILENAME_MAX];
|
|
|
|
my_strlcpy(buf, path, sizeof(buf));
|
|
fix_directory_separators(buf);
|
|
|
|
p = buf + strlen(buf) - 1;
|
|
while (p > buf && *p == '\\' && p[-1] != ':')
|
|
*p-- = '\0';
|
|
|
|
MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
|
|
|
|
return (_wstat(wbuf, (struct _stat *) stp));
|
|
}
|
|
|
|
int
|
|
my_remove(const char *path)
|
|
{
|
|
char buf[FILENAME_MAX];
|
|
wchar_t wbuf[FILENAME_MAX];
|
|
|
|
my_strlcpy(buf, path, sizeof(buf));
|
|
fix_directory_separators(buf);
|
|
|
|
MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
|
|
|
|
return (_wremove(wbuf));
|
|
}
|
|
|
|
int
|
|
my_rename(const char *path1, const char *path2)
|
|
{
|
|
char buf1[FILENAME_MAX];
|
|
char buf2[FILENAME_MAX];
|
|
wchar_t wbuf1[FILENAME_MAX];
|
|
wchar_t wbuf2[FILENAME_MAX];
|
|
|
|
my_strlcpy(buf1, path1, sizeof(buf1));
|
|
my_strlcpy(buf2, path2, sizeof(buf2));
|
|
fix_directory_separators(buf1);
|
|
fix_directory_separators(buf2);
|
|
|
|
MultiByteToWideChar(CP_UTF8, 0, buf1, -1, wbuf1, sizeof(wbuf1));
|
|
MultiByteToWideChar(CP_UTF8, 0, buf2, -1, wbuf2, sizeof(wbuf2));
|
|
|
|
return (_wrename(wbuf1, wbuf2));
|
|
}
|
|
|
|
int
|
|
my_mkdir(const char *path, int mode)
|
|
{
|
|
char buf[FILENAME_MAX];
|
|
wchar_t wbuf[FILENAME_MAX];
|
|
|
|
my_strlcpy(buf, path, sizeof(buf));
|
|
fix_directory_separators(buf);
|
|
|
|
MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
|
|
|
|
return (_wmkdir(wbuf));
|
|
}
|
|
|
|
static char *
|
|
wide_to_utf8(const wchar_t *str)
|
|
{
|
|
char *buf = NULL;
|
|
if (str) {
|
|
int nchar = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL);
|
|
if (nchar > 0) {
|
|
buf = malloc(nchar);
|
|
if (!buf)
|
|
errno = ENOMEM;
|
|
else if (!WideCharToMultiByte(CP_UTF8, 0, str, -1, buf, nchar, NULL, NULL)) {
|
|
free(buf);
|
|
buf = NULL;
|
|
errno = EINVAL;
|
|
}
|
|
} else
|
|
errno = EINVAL;
|
|
} else
|
|
errno = EINVAL;
|
|
return buf;
|
|
}
|
|
|
|
char *
|
|
my_getcwd(char *buffer, int maxlen)
|
|
{
|
|
char *result = NULL;
|
|
wchar_t *wbuffer, *wresult;
|
|
|
|
if (buffer) {
|
|
/* User-supplied buffer */
|
|
wbuffer = malloc(maxlen * sizeof(wchar_t));
|
|
if (wbuffer == NULL)
|
|
return NULL;
|
|
} else
|
|
/* Dynamically allocated buffer */
|
|
wbuffer = NULL;
|
|
wresult = _wgetcwd(wbuffer, maxlen);
|
|
if (wresult) {
|
|
int err = errno;
|
|
if (buffer) {
|
|
/* User-supplied buffer */
|
|
int n = WideCharToMultiByte(CP_UTF8, 0, wresult, -1, buffer, maxlen, NULL, NULL);
|
|
if (n == 0)
|
|
err = ERANGE;
|
|
free(wbuffer);
|
|
result = buffer;
|
|
} else {
|
|
/* Buffer allocated by _wgetcwd() */
|
|
result = wide_to_utf8(wresult);
|
|
err = errno;
|
|
free(wresult);
|
|
}
|
|
errno = err;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DIR *
|
|
opendir(const char *name)
|
|
{
|
|
DIR *dir = NULL;
|
|
char path[FILENAME_MAX];
|
|
wchar_t wpath[FILENAME_MAX];
|
|
|
|
if (name == NULL || name[0] == '\0') {
|
|
errno = EINVAL;
|
|
} else if ((dir = malloc(sizeof(*dir))) == NULL) {
|
|
errno = ENOMEM;
|
|
} else {
|
|
my_snprintf(path, sizeof(path), "%s/*", name);
|
|
fix_directory_separators(path);
|
|
MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, sizeof(wpath));
|
|
dir->handle = FindFirstFileW(wpath, &dir->info);
|
|
|
|
if (dir->handle != INVALID_HANDLE_VALUE) {
|
|
dir->result.d_name[0] = '\0';
|
|
} else {
|
|
free(dir);
|
|
dir = NULL;
|
|
}
|
|
}
|
|
|
|
return (dir);
|
|
}
|
|
|
|
int
|
|
closedir(DIR *dir)
|
|
{
|
|
int result = -1;
|
|
|
|
if (dir != NULL) {
|
|
if (dir->handle != INVALID_HANDLE_VALUE)
|
|
result = FindClose(dir->handle) ? 0 : -1;
|
|
|
|
free(dir);
|
|
}
|
|
|
|
if (result == -1)
|
|
errno = EBADF;
|
|
|
|
return (result);
|
|
}
|
|
|
|
struct dirent *
|
|
readdir(DIR *dir)
|
|
{
|
|
struct dirent *result = 0;
|
|
|
|
if (dir && dir->handle != INVALID_HANDLE_VALUE) {
|
|
if(!dir->result.d_name ||
|
|
FindNextFileW(dir->handle, &dir->info)) {
|
|
result = &dir->result;
|
|
|
|
WideCharToMultiByte(CP_UTF8, 0, dir->info.cFileName,
|
|
-1, result->d_name,
|
|
sizeof(result->d_name), NULL, NULL);
|
|
}
|
|
} else {
|
|
errno = EBADF;
|
|
}
|
|
|
|
return (result);
|
|
}
|
|
|
|
int
|
|
set_non_blocking_mode(int fd)
|
|
{
|
|
unsigned long on = 1;
|
|
|
|
return (ioctlsocket(fd, FIONBIO, &on));
|
|
}
|
|
|
|
void
|
|
set_close_on_exec(int fd)
|
|
{
|
|
fd = 0; /* Do nothing. There is no FD_CLOEXEC on Windows */
|
|
}
|
|
|
|
#if !defined(NO_CGI)
|
|
|
|
struct threadparam {
|
|
SOCKET s;
|
|
HANDLE hPipe;
|
|
big_int_t content_len;
|
|
};
|
|
|
|
/*
|
|
* Thread function that reads POST data from the socket pair
|
|
* and writes it to the CGI process.
|
|
*/
|
|
static void//DWORD WINAPI
|
|
stdoutput(void *arg)
|
|
{
|
|
struct threadparam *tp = arg;
|
|
int n, sent, stop = 0;
|
|
big_int_t total = 0;
|
|
DWORD k;
|
|
char buf[BUFSIZ];
|
|
size_t max_recv;
|
|
|
|
max_recv = min(sizeof(buf), tp->content_len - total);
|
|
while (!stop && max_recv > 0 && (n = recv(tp->s, buf, max_recv, 0)) > 0) {
|
|
for (sent = 0; !stop && sent < n; sent += k)
|
|
if (!WriteFile(tp->hPipe, buf + sent, n - sent, &k, 0))
|
|
stop++;
|
|
total += n;
|
|
max_recv = min(sizeof(buf), tp->content_len - total);
|
|
}
|
|
|
|
CloseHandle(tp->hPipe); /* Suppose we have POSTed everything */
|
|
free(tp);
|
|
}
|
|
|
|
/*
|
|
* Thread function that reads CGI output and pushes it to the socket pair.
|
|
*/
|
|
static void
|
|
stdinput(void *arg)
|
|
{
|
|
struct threadparam *tp = arg;
|
|
static int ntotal;
|
|
int k, stop = 0;
|
|
DWORD n, sent;
|
|
char buf[BUFSIZ];
|
|
|
|
while (!stop && ReadFile(tp->hPipe, buf, sizeof(buf), &n, NULL)) {
|
|
ntotal += n;
|
|
for (sent = 0; !stop && sent < n; sent += k)
|
|
if ((k = send(tp->s, buf + sent, n - sent, 0)) <= 0)
|
|
stop++;
|
|
}
|
|
CloseHandle(tp->hPipe);
|
|
|
|
/*
|
|
* Windows is a piece of crap. When this thread closes its end
|
|
* of the socket pair, the other end (get_cgi() function) may loose
|
|
* some data. I presume, this happens if get_cgi() is not fast enough,
|
|
* and the data written by this end does not "push-ed" to the other
|
|
* end socket buffer. So after closesocket() the remaining data is
|
|
* gone. If I put shutdown() before closesocket(), that seems to
|
|
* fix the problem, but I am not sure this is the right fix.
|
|
* XXX (submitted by James Marshall) we do not do shutdown() on UNIX.
|
|
* If fork() is called from user callback, shutdown() messes up things.
|
|
*/
|
|
shutdown(tp->s, 2);
|
|
|
|
closesocket(tp->s);
|
|
free(tp);
|
|
|
|
_endthread();
|
|
}
|
|
|
|
static void
|
|
spawn_stdio_thread(int sock, HANDLE hPipe, void (*func)(void *),
|
|
big_int_t content_len)
|
|
{
|
|
struct threadparam *tp;
|
|
DWORD tid;
|
|
|
|
tp = malloc(sizeof(*tp));
|
|
assert(tp != NULL);
|
|
|
|
tp->s = sock;
|
|
tp->hPipe = hPipe;
|
|
tp->content_len = content_len;
|
|
_beginthread(func, 0, tp);
|
|
}
|
|
|
|
int
|
|
spawn_process(struct conn *c, const char *prog, char *envblk,
|
|
char *envp[], int sock, const char *dir)
|
|
{
|
|
HANDLE a[2], b[2], h[2], me;
|
|
DWORD flags;
|
|
char *p, cmdline[FILENAME_MAX], line[FILENAME_MAX];
|
|
FILE *fp;
|
|
STARTUPINFOA si;
|
|
PROCESS_INFORMATION pi;
|
|
|
|
me = GetCurrentProcess();
|
|
flags = DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS;
|
|
|
|
/* FIXME add error checking code here */
|
|
CreatePipe(&a[0], &a[1], NULL, 0);
|
|
CreatePipe(&b[0], &b[1], NULL, 0);
|
|
DuplicateHandle(me, a[0], me, &h[0], 0, TRUE, flags);
|
|
DuplicateHandle(me, b[1], me, &h[1], 0, TRUE, flags);
|
|
|
|
(void) memset(&si, 0, sizeof(si));
|
|
(void) memset(&pi, 0, sizeof(pi));
|
|
|
|
/* XXX redirect CGI errors to the error log file */
|
|
si.cb = sizeof(si);
|
|
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
|
|
si.wShowWindow = SW_HIDE;
|
|
si.hStdOutput = si.hStdError = h[1];
|
|
si.hStdInput = h[0];
|
|
|
|
/* If CGI file is a script, try to read the interpreter line */
|
|
if (c->ctx->cgi_interpreter == NULL) {
|
|
if ((fp = fopen(prog, "r")) != NULL) {
|
|
(void) fgets(line, sizeof(line), fp);
|
|
if (memcmp(line, "#!", 2) != 0)
|
|
line[2] = '\0';
|
|
/* Trim whitespaces from interpreter name */
|
|
for (p = &line[strlen(line) - 1]; p > line &&
|
|
isspace(*p); p--)
|
|
*p = '\0';
|
|
(void) fclose(fp);
|
|
}
|
|
(void) my_snprintf(cmdline, sizeof(cmdline), "%s%s%s",
|
|
line + 2, line[2] == '\0' ? "" : " ", prog);
|
|
} else {
|
|
(void) my_snprintf(cmdline, sizeof(cmdline), "%s %s",
|
|
c->ctx->cgi_interpreter, prog);
|
|
}
|
|
|
|
(void) my_snprintf(line, sizeof(line), "%s", dir);
|
|
fix_directory_separators(line);
|
|
fix_directory_separators(cmdline);
|
|
|
|
/*
|
|
* Spawn reader & writer threads before we create CGI process.
|
|
* Otherwise CGI process may die too quickly, loosing the data
|
|
*/
|
|
spawn_stdio_thread(sock, b[0], stdinput, 0);
|
|
spawn_stdio_thread(sock, a[1], stdoutput, c->rem.content_len);
|
|
|
|
if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE,
|
|
CREATE_NEW_PROCESS_GROUP, envblk, line, &si, &pi) == 0) {
|
|
elog(E_LOG, c,"redirect: CreateProcess(%s): %d",cmdline,ERRNO);
|
|
return (-1);
|
|
} else {
|
|
CloseHandle(h[0]);
|
|
CloseHandle(h[1]);
|
|
CloseHandle(pi.hThread);
|
|
CloseHandle(pi.hProcess);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
#endif /* !NO_CGI */
|