| /* |
| * 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 "shttpd_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) { |
| 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; |
| 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) 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) snprintf(text, sizeof(text), |
| " Requests: %u", ctx->nrequests); |
| SendMessage(hStatus, SB_SETTEXT, 1, (LPARAM) text); |
| (void) snprintf(text, sizeof(text), |
| " Sent: %4.2f Mb", (double) ctx->out / 1048576); |
| SendMessage(hStatus, SB_SETTEXT, 2, (LPARAM) text); |
| (void) 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; |
| 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 { |
| 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) snprintf(cmdline, sizeof(cmdline), "%s%s%s", |
| line + 2, line[2] == '\0' ? "" : " ", prog); |
| } else { |
| (void) snprintf(cmdline, sizeof(cmdline), "%s %s", |
| c->ctx->cgi_interpreter, prog); |
| } |
| |
| (void) 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 */ |