version 0.2.1
[fms.git] / libs / shttpd / compat_win32.c
index fb5b1a1..05f6036 100644 (file)
 
 #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)
+fix_directory_separators(char *path)
 {
-       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;
+       for (; *path != '\0'; path++) {
+               if (*path == '/')
+                       *path = '\\';
+               if (*path == '\\')
+                       while (path[1] == '\\' || path[1] == '/') 
+                               (void) memmove(path + 1,
+                                   path + 2, strlen(path + 2) + 1);
        }
-
-       return FALSE;
 }
 
-static void *
-align(void *ptr, DWORD alig)
+static int
+protect_against_code_disclosure(const char *path)
 {
-       ULONG ul = (ULONG) ptr;
+       WIN32_FIND_DATA data;
+       HANDLE          handle;
+       const char      *p;
 
-       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;
+       /*
+        * Protect against CGI code disclosure under Windows.
+        * This is very nasty hole. Windows happily opens files with
+        * some garbage in the end of file name. So fopen("a.cgi    ", "r")
+        * actually opens "a.cgi", and does not return an error! And since
+        * "a.cgi    " does not have valid CGI extension, this leads to
+        * the CGI code disclosure.
+        * To protect, here we delete all fishy characters from the
+        * end of file name.
+        */
 
-       while (*caption != '\0')
-               *p++ = (WCHAR) *caption++;
-       *p++ = 0;
-       p = align(p, 1);
+       if ((handle = FindFirstFile(path, &data)) == INVALID_HANDLE_VALUE)
+               return (FALSE);
 
-       *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++;
-       }
+       FindClose(handle);
 
-       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)");
+       for (p = path + strlen(path); p > path && p[-1] != '\\';)
+               p--;
        
-       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;
-       }
+       if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
+           strcmp(data.cFileName, p) != 0)
+               return (FALSE);
 
-       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);
-       }
+       return (TRUE);
 }
 
 int
@@ -549,6 +66,9 @@ my_open(const char *path, int flags, int mode)
        fix_directory_separators(buf);
        MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
 
+       if (protect_against_code_disclosure(buf) == FALSE)
+               return (-1);
+
        return (_wopen(wbuf, flags));
 }
 
@@ -876,7 +396,7 @@ spawn_process(struct conn *c, const char *prog, char *envblk,
        si.hStdInput    = h[0];
 
        /* If CGI file is a script, try to read the interpreter line */
-       if (c->ctx->cgi_interpreter == NULL) {
+       if (c->ctx->options[OPT_CGI_INTERPRETER] == NULL) {
                if ((fp = fopen(prog, "r")) != NULL) {
                        (void) fgets(line, sizeof(line), fp);
                        if (memcmp(line, "#!", 2) != 0)
@@ -891,7 +411,7 @@ spawn_process(struct conn *c, const char *prog, char *envblk,
                    line + 2, line[2] == '\0' ? "" : " ", prog);
        } else {
                (void) my_snprintf(cmdline, sizeof(cmdline), "%s %s",
-                   c->ctx->cgi_interpreter, prog);
+                   c->ctx->options[OPT_CGI_INTERPRETER], prog);
        }
 
        (void) my_snprintf(line, sizeof(line), "%s", dir);