X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=libs%2Fshttpd%2Fcompat_win32.c;fp=libs%2Fshttpd%2Fcompat_win32.c;h=fb5b1a1f218bbbdb31d9dc304ba1abb1dd77e0a4;hb=d8ccfe2b3944adf07d35534459cdda19d15217c8;hp=0000000000000000000000000000000000000000;hpb=21f835f30b4e092c847bf4569a00995774f7330e;p=fms.git diff --git a/libs/shttpd/compat_win32.c b/libs/shttpd/compat_win32.c new file mode 100644 index 0000000..fb5b1a1 --- /dev/null +++ b/libs/shttpd/compat_win32.c @@ -0,0 +1,922 @@ +/* + * Copyright (c) 2004-2005 Sergey Lyubka + * 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; + + open_listening_ports(ctx); + + 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); + open_listening_ports(ctx); + _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 */