source: rtems/cpukit/shttpd/compat_win32.c @ ba3e987e

4.104.115
Last change on this file since ba3e987e was 086442b7, checked in by Ralf Corsepius <ralf.corsepius@…>, on 12/02/09 at 11:00:38

Whitespace removal.

  • Property mode set to 100644
File size: 21.8 KB
Line 
1/*
2 * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
3 * All rights reserved
4 *
5 * "THE BEER-WARE LICENSE" (Revision 42):
6 * Sergey Lyubka wrote this file.  As long as you retain this notice you
7 * can do whatever you want with this stuff. If we meet some day, and you think
8 * this stuff is worth it, you can buy me a beer in return.
9 */
10
11#include "defs.h"
12
13static const char       *config_file = CONFIG;
14
15#if !defined(NO_GUI)
16
17static HICON            hIcon;                  /* SHTTPD icon handle   */
18HWND                    hLog;                   /* Log window           */
19
20/*
21 * Dialog box control IDs
22 */
23#define ID_GROUP        100
24#define ID_SAVE         101
25#define ID_STATUS       102
26#define ID_STATIC       103
27#define ID_SETTINGS     104
28#define ID_QUIT         105
29#define ID_TRAYICON     106
30#define ID_TIMER        107
31#define ID_ICON         108
32#define ID_ADVANCED     109
33#define ID_SHOWLOG      110
34#define ID_LOG          111
35
36#define ID_USER         200
37#define ID_DELTA        1000
38
39static void
40run_server(void *param)
41{
42        struct shttpd_ctx       *ctx = param;
43
44        if (shttpd_listen(ctx, ctx->port) == -1)
45                elog(E_FATAL, NULL, "Cannot open socket on port %d", ctx->port);
46
47        while (WaitForSingleObject(ctx->ev[0], 0) != WAIT_OBJECT_0)
48                shttpd_poll(ctx, 1000);
49
50        SetEvent(ctx->ev[1]);
51        shttpd_fini(ctx);
52}
53
54/*
55 * Save the configuration back into config file
56 */
57static void
58save_config(HWND hDlg, FILE *fp)
59{
60        const struct opt        *opt;
61        char                    text[FILENAME_MAX];
62        int                     id;
63
64        if (fp == NULL)
65                elog(E_FATAL, NULL, "save_config: cannot open %s", config_file);
66
67        for (opt = options; opt->name != NULL; opt++) {
68                id = ID_USER + (opt - options);         /* Control ID */
69
70                /* Do not save if the text is the same as default */
71
72                if (opt->flags & OPT_BOOL)
73                        (void) fprintf(fp, "%s\t%d\n",
74                            opt->name, IsDlgButtonChecked(hDlg, id));
75                else if (GetDlgItemText(hDlg, id, text, sizeof(text)) != 0 &&
76                    (opt->def == NULL || strcmp(text, opt->def) != 0))
77                        (void) fprintf(fp, "%s\t%s\n", opt->name, text);
78        }
79
80        (void) fclose(fp);
81}
82
83static void
84set_control_values(HWND hDlg, const struct shttpd_ctx *ctx)
85{
86        const struct opt        *opt;
87        const union variant     *v;
88        char                    buf[FILENAME_MAX];
89        int                     id;
90
91        for (opt = options; opt->name != NULL; opt++) {
92                id = ID_USER + (opt - options);
93                v = (union variant *) ((char *) ctx + opt->ofs);
94                if (opt->flags & OPT_BOOL) {
95                        CheckDlgButton(hDlg, id,
96                            v->v_int ? BST_CHECKED : BST_UNCHECKED);
97                } else if (opt->flags & OPT_INT) {
98                        my_snprintf(buf, sizeof(buf), "%d", v->v_int);
99                        SetDlgItemText(hDlg, id, buf);
100                } else {
101                        SetDlgItemText(hDlg, id, v->v_str);
102                }
103        }
104
105}
106
107static BOOL CALLBACK
108DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
109{
110        static struct shttpd_ctx *ctx, **pctx;
111        HANDLE          ev;
112        const struct opt *opt;
113        DWORD tid;
114        int             id, up;
115        char            text[256];
116
117        switch (msg) {
118
119        case WM_CLOSE:
120                KillTimer(hDlg, ID_TIMER);
121                DestroyWindow(hDlg);
122                break;
123
124        case WM_COMMAND:
125                switch (LOWORD(wParam)) {
126                case ID_SAVE:
127                        EnableWindow(GetDlgItem(hDlg, ID_SAVE), FALSE);
128                        save_config(hDlg, fopen(config_file, "w+"));
129                        ev = ctx->ev[1];
130                        SetEvent(ctx->ev[0]);
131                        WaitForSingleObject(ev, INFINITE);
132                        *pctx = ctx = init_from_argc_argv(config_file, 0, NULL);
133                        shttpd_listen(ctx, ctx->port);
134                        _beginthread(run_server, 0, ctx);
135                        EnableWindow(GetDlgItem(hDlg, ID_SAVE), TRUE);
136
137                        break;
138                }
139
140                id = ID_USER + ID_DELTA;
141                for (opt = options; opt->name != NULL; opt++, id++)
142                        if (LOWORD(wParam) == id) {
143                                OPENFILENAME    of;
144                                BROWSEINFO      bi;
145                                char            path[FILENAME_MAX] = "";
146
147                                memset(&of, 0, sizeof(of));
148                                of.lStructSize = sizeof(of);
149                                of.hwndOwner = (HWND) hDlg;
150                                of.lpstrFile = path;
151                                of.nMaxFile = sizeof(path);
152                                of.lpstrInitialDir = ctx->document_root;
153                                of.Flags = OFN_CREATEPROMPT | OFN_NOCHANGEDIR;
154                               
155                                memset(&bi, 0, sizeof(bi));
156                                bi.hwndOwner = (HWND) hDlg;
157                                bi.lpszTitle = "Choose WWW root directory:";
158                                bi.ulFlags = BIF_RETURNONLYFSDIRS;
159
160                                if (opt->flags & OPT_DIR)
161                                        SHGetPathFromIDList(
162                                                SHBrowseForFolder(&bi), path);
163                                else
164                                        GetOpenFileName(&of);
165
166                                if (path[0] != '\0')
167                                        SetWindowText(GetDlgItem(hDlg,
168                                                id - ID_DELTA), path);
169                        }
170
171                break;
172
173        case WM_INITDIALOG:
174                pctx = (struct shttpd_ctx **) lParam;
175                ctx = *pctx;
176                SendMessage(hDlg,WM_SETICON,(WPARAM)ICON_SMALL,(LPARAM)hIcon);
177                SendMessage(hDlg,WM_SETICON,(WPARAM)ICON_BIG,(LPARAM)hIcon);
178                SetWindowText(hDlg, "SHTTPD settings");
179                SetFocus(GetDlgItem(hDlg, ID_SAVE));
180                set_control_values(hDlg, ctx);
181                break;
182        default:
183                break;
184        }
185
186        return FALSE;
187}
188
189static void *
190align(void *ptr, DWORD alig)
191{
192        ULONG ul = (ULONG) ptr;
193
194        ul += alig;
195        ul &= ~alig;
196       
197        return ((void *) ul);
198}
199
200
201static void
202add_control(unsigned char **mem, DLGTEMPLATE *dia, WORD type, DWORD id,
203        DWORD style, WORD x, WORD y, WORD cx, WORD cy, const char *caption)
204{
205        DLGITEMTEMPLATE *tp;
206        LPWORD          p;
207
208        dia->cdit++;
209
210        *mem = align(*mem, 3);
211        tp = (DLGITEMTEMPLATE *) *mem;
212
213        tp->id                  = (WORD)id;
214        tp->style               = style;
215        tp->dwExtendedStyle     = 0;
216        tp->x                   = x;
217        tp->y                   = y;
218        tp->cx                  = cx;
219        tp->cy                  = cy;
220
221        p = align(*mem + sizeof(*tp), 1);
222        *p++ = 0xffff;
223        *p++ = type;
224
225        while (*caption != '\0')
226                *p++ = (WCHAR) *caption++;
227        *p++ = 0;
228        p = align(p, 1);
229
230        *p++ = 0;
231        *mem = (unsigned char *) p;
232}
233
234static void
235show_settings_dialog(struct shttpd_ctx **ctxp)
236{
237#define HEIGHT          15
238#define WIDTH           400
239#define LABEL_WIDTH     70
240
241        unsigned char           mem[4096], *p;
242        DWORD                   style;
243        DLGTEMPLATE             *dia = (DLGTEMPLATE *) mem;
244        WORD                    cl, x, y, width, nelems = 0;
245        const struct opt        *opt;
246        static int              guard;
247
248        static struct {
249                DLGTEMPLATE     template;       /* 18 bytes */
250                WORD            menu, class;
251                wchar_t         caption[1];
252                WORD            fontsiz;
253                wchar_t         fontface[7];
254        } dialog_header = {{WS_CAPTION | WS_POPUP | WS_SYSMENU | WS_VISIBLE |
255                DS_SETFONT | WS_DLGFRAME, WS_EX_TOOLWINDOW,
256                0, 200, 200, WIDTH, 0}, 0, 0, L"", 8, L"Tahoma"};
257
258        if (guard == 0)
259                guard++;
260        else
261                return;
262
263        (void) memset(mem, 0, sizeof(mem));
264        (void) memcpy(mem, &dialog_header, sizeof(dialog_header));
265        p = mem + sizeof(dialog_header);
266
267        for (opt = options; opt->name != NULL; opt++) {
268
269                style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
270                x = 10 + (WIDTH / 2) * (nelems % 2);
271                y = (nelems/2 + 1) * HEIGHT + 5;
272                width = WIDTH / 2 - 20 - LABEL_WIDTH;
273                if (opt->flags & OPT_INT) {
274                        style |= ES_NUMBER;
275                        cl = 0x81;
276                        style |= WS_BORDER | ES_AUTOHSCROLL;
277                } else if (opt->flags & OPT_BOOL) {
278                        cl = 0x80;
279                        style |= BS_AUTOCHECKBOX;
280                } else if (opt->flags & (OPT_DIR | OPT_FILE)) {
281                        style |= WS_BORDER | ES_AUTOHSCROLL;
282                        width -= 20;
283                        cl = 0x81;
284                        add_control(&p, dia, 0x80,
285                                ID_USER + ID_DELTA + (opt - options),
286                                WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
287                                (WORD) (x + width + LABEL_WIDTH + 5),
288                                y, 15, 12, "...");
289                } else {
290                        cl = 0x81;
291                        style |= WS_BORDER | ES_AUTOHSCROLL;
292                }
293                add_control(&p, dia, 0x82, ID_STATIC, WS_VISIBLE | WS_CHILD,
294                        x, y, LABEL_WIDTH, HEIGHT, opt->desc);
295                add_control(&p, dia, cl, ID_USER + (opt - options), style,
296                        (WORD) (x + LABEL_WIDTH), y, width, 12, "");
297                nelems++;
298        }
299
300        y = (WORD) (((nelems + 1)/2 + 1) * HEIGHT + 5);
301        add_control(&p, dia, 0x80, ID_GROUP, WS_CHILD | WS_VISIBLE |
302                BS_GROUPBOX, 5, 5, WIDTH - 10, y, "Settings");
303        y += 10;
304        add_control(&p, dia, 0x80, ID_SAVE,
305                WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
306                WIDTH - 70, y, 65, 12, "Save Settings");
307#if 0
308        add_control(&p, dia, 0x80, ID_ADVANCED,
309                WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
310                WIDTH - 190, y, 110, 12, "Show Advanced Settings >>");
311#endif
312        add_control(&p, dia, 0x82, ID_STATIC,
313                WS_CHILD | WS_VISIBLE | WS_DISABLED,
314                5, y, 180, 12,"SHTTPD v." VERSION
315                "      (http://shttpd.sourceforge.net)");
316       
317        dia->cy = ((nelems + 1)/2 + 1) * HEIGHT + 30;
318        DialogBoxIndirectParam(NULL, dia, NULL, DlgProc, (LPARAM) ctxp);
319        guard--;
320}
321
322static BOOL CALLBACK
323LogProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
324{
325        static struct shttpd_ctx        *ctx;
326        static HWND     hStatus;
327        HWND            hEdit;
328        RECT            rect, rect2, rect3, rect4;
329        int             len, up, widths[] = {120, 220, 330, 460, -1};
330        char            text[256], buf[1024 * 64];
331
332        switch (msg) {
333
334        case WM_CLOSE:
335                KillTimer(hDlg, ID_TIMER);
336                DestroyWindow(hDlg);
337                break;
338
339        case WM_APP:
340                hEdit = GetDlgItem(hDlg, ID_LOG);
341                len = GetWindowText(hEdit, buf, sizeof(buf));
342                if (len > sizeof(buf) * 4 / 5)
343                        len = sizeof(buf) * 4 / 5;
344                my_snprintf(buf + len, sizeof(buf) - len,
345                    "%s\r\n", (char *) lParam);
346                SetWindowText(hEdit, buf);
347                SendMessage(hEdit, WM_VSCROLL, SB_BOTTOM, 0);
348                break;
349
350        case WM_TIMER:
351                /* Print statistics on a status bar */
352                up = current_time - ctx->start_time;
353                (void) my_snprintf(text, sizeof(text),
354                    " Up: %3d h %2d min %2d sec",
355                    up / 3600, up / 60 % 60, up % 60);
356                SendMessage(hStatus, SB_SETTEXT, 0, (LPARAM) text);
357                (void) my_snprintf(text, sizeof(text),
358                    " Requests: %u", ctx->nrequests);
359                SendMessage(hStatus, SB_SETTEXT, 1, (LPARAM) text);
360                (void) my_snprintf(text, sizeof(text),
361                    " Sent: %4.2f Mb", (double) ctx->out / 1048576);
362                SendMessage(hStatus, SB_SETTEXT, 2, (LPARAM) text);
363                (void) my_snprintf(text, sizeof(text),
364                    " Received: %4.2f Mb", (double) ctx->in / 1048576);
365                SendMessage(hStatus, SB_SETTEXT, 3, (LPARAM) text);
366                break;
367
368        case WM_INITDIALOG:
369                ctx = (struct shttpd_ctx *) lParam;
370                SendMessage(hDlg,WM_SETICON,(WPARAM)ICON_SMALL,(LPARAM)hIcon);
371                SendMessage(hDlg,WM_SETICON,(WPARAM)ICON_BIG,(LPARAM)hIcon);
372                hStatus = CreateStatusWindow(WS_CHILD | WS_VISIBLE,
373                        "", hDlg, ID_STATUS);
374                SendMessage(hStatus, SB_SETPARTS, 5, (LPARAM) widths);
375                SendMessage(hStatus, SB_SETTEXT, 4, (LPARAM) " Running");
376                SetWindowText(hDlg, "SHTTPD web server log");
377                SetTimer(hDlg, ID_TIMER, 1000, NULL);
378                GetWindowRect(GetDesktopWindow(), &rect3);
379                GetWindowRect(hDlg, &rect4);
380                GetClientRect(hDlg, &rect);
381                GetClientRect(hStatus, &rect2);
382                SetWindowPos(GetDlgItem(hDlg, ID_LOG), 0,
383                        0, 0, rect.right, rect.bottom - rect2.bottom, 0);
384                SetWindowPos(hDlg, HWND_TOPMOST,
385                                rect3.right - (rect4.right - rect4.left),
386                                rect3.bottom - (rect4.bottom - rect4.top) - 30,
387                                0, 0, SWP_NOSIZE);
388                SetFocus(hStatus);
389                SendMessage(hDlg, WM_TIMER, 0, 0);
390                hLog = hDlg;
391                break;
392        default:
393                break;
394        }
395
396
397        return (FALSE);
398}
399
400static void
401show_log_window(struct shttpd_ctx *ctx)
402{
403        unsigned char           mem[4096], *p;
404        DWORD                   style;
405        DLGTEMPLATE             *dia = (DLGTEMPLATE *) mem;
406        WORD                    cl, x, y, width, nelems = 0;
407
408        static struct {
409                DLGTEMPLATE     template;       /* 18 bytes */
410                WORD            menu, class;
411                wchar_t         caption[1];
412                WORD            fontsiz;
413                wchar_t         fontface[7];
414        } dialog_header = {{WS_CAPTION | WS_POPUP | WS_VISIBLE | WS_SYSMENU |
415                DS_SETFONT | WS_DLGFRAME, WS_EX_TOOLWINDOW,
416                0, 200, 200, 400, 100}, 0, 0, L"", 8, L"Tahoma"};
417
418        if (hLog != NULL)
419                return;
420
421        (void) memset(mem, 0, sizeof(mem));
422        (void) memcpy(mem, &dialog_header, sizeof(dialog_header));
423        p = mem + sizeof(dialog_header);
424
425        add_control(&p, dia, 0x81, ID_LOG, WS_CHILD | WS_VISIBLE |
426            WS_BORDER | WS_VSCROLL | ES_MULTILINE | ES_AUTOVSCROLL |
427            ES_READONLY, 5, 5, WIDTH - 10, 60, "");
428
429        DialogBoxIndirectParam(NULL, dia, NULL, LogProc, (LPARAM) ctx);
430
431        hLog = NULL;
432}
433
434static LRESULT CALLBACK
435WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
436{
437        static NOTIFYICONDATA   ni;
438        static struct shttpd_ctx *ctx;
439        DWORD                   tid;            /* Thread ID */
440        HMENU                   hMenu;
441        POINT                   pt;
442
443        switch (msg) {
444        case WM_CREATE:
445                ctx = ((CREATESTRUCT *) lParam)->lpCreateParams;
446                memset(&ni, 0, sizeof(ni));
447                ni.cbSize = sizeof(ni);
448                ni.uID = ID_TRAYICON;
449                ni.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
450                ni.hIcon = hIcon;
451                ni.hWnd = hWnd;
452                my_snprintf(ni.szTip, sizeof(ni.szTip), "SHTTPD web server");
453                ni.uCallbackMessage = WM_USER;
454                Shell_NotifyIcon(NIM_ADD, &ni);
455                ctx->ev[0] = CreateEvent(0, TRUE, FALSE, 0);
456                ctx->ev[1] = CreateEvent(0, TRUE, FALSE, 0);
457                _beginthread(run_server, 0, ctx);
458                break;
459        case WM_CLOSE:
460                Shell_NotifyIcon(NIM_DELETE, &ni);
461                PostQuitMessage(0);
462                break;
463        case WM_COMMAND:
464                switch (LOWORD(wParam)) {
465                case ID_SETTINGS:
466                        show_settings_dialog(&ctx);
467                        break;
468                case ID_QUIT:
469                        SendMessage(hWnd, WM_CLOSE, wParam, lParam);
470                        PostQuitMessage(0);
471                        break;
472                case ID_SHOWLOG:
473                        show_log_window(ctx);
474                        break;
475                }
476                break;
477        case WM_USER:
478                switch (lParam) {
479                case WM_RBUTTONUP:
480                case WM_LBUTTONUP:
481                case WM_LBUTTONDBLCLK:
482                        hMenu = CreatePopupMenu();
483                        AppendMenu(hMenu, 0, ID_SETTINGS, "Settings");
484                        AppendMenu(hMenu, 0, ID_SHOWLOG, "Show Log");
485                        AppendMenu(hMenu, 0, ID_QUIT, "Exit SHTTPD");
486                        GetCursorPos(&pt);
487                        TrackPopupMenu(hMenu, 0, pt.x, pt.y, 0, hWnd, NULL);
488                        DestroyMenu(hMenu);
489                        break;
490                }
491                break;
492        }
493
494        return (DefWindowProc(hWnd, msg, wParam, lParam));
495}
496
497int WINAPI
498WinMain(HINSTANCE h, HINSTANCE prev, char *cmdline, int show)
499{
500        struct shttpd_ctx       *ctx;
501        WNDCLASS                cls;
502        HWND                    hWnd;
503        MSG                     msg;
504
505        ctx = init_from_argc_argv(config_file, 0, NULL);
506        (void) memset(&cls, 0, sizeof(cls));
507
508        hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(ID_ICON));
509        if (hIcon == NULL)
510                hIcon = LoadIcon(NULL, IDI_APPLICATION);
511        cls.lpfnWndProc = (WNDPROC) WindowProc;
512        cls.hIcon = hIcon;
513        cls.lpszClassName = "shttpd v." VERSION;
514
515        if (!RegisterClass(&cls))
516                elog(E_FATAL, NULL, "RegisterClass: %d", ERRNO);
517        else if ((hWnd = CreateWindow(cls.lpszClassName, "",WS_OVERLAPPEDWINDOW,
518            0, 0, 0, 0, NULL, NULL, NULL, ctx)) == NULL)
519                elog(E_FATAL, NULL, "CreateWindow: %d", ERRNO);
520
521        while (GetMessage(&msg, (HWND) NULL, 0, 0)) {
522                TranslateMessage(&msg);
523                DispatchMessage(&msg);
524        }
525
526        return (0);
527}
528#endif /* NO_GUI */
529
530static void
531fix_directory_separators(char *path)
532{
533        for (; *path != '\0'; path++) {
534                if (*path == '/')
535                        *path = '\\';
536                if (*path == '\\')
537                        while (path[1] == '\\' || path[1] == '/')
538                                (void) memmove(path + 1,
539                                    path + 2, strlen(path + 2) + 1);
540        }
541}
542
543int
544my_open(const char *path, int flags, int mode)
545{
546        char    buf[FILENAME_MAX];
547        wchar_t wbuf[FILENAME_MAX];
548
549        my_strlcpy(buf, path, sizeof(buf));
550        fix_directory_separators(buf);
551        MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
552
553        return (_wopen(wbuf, flags));
554}
555
556int
557my_stat(const char *path, struct stat *stp)
558{
559        char    buf[FILENAME_MAX], *p;
560        wchar_t wbuf[FILENAME_MAX];
561
562        my_strlcpy(buf, path, sizeof(buf));
563        fix_directory_separators(buf);
564
565        p = buf + strlen(buf) - 1;
566        while (p > buf && *p == '\\' && p[-1] != ':')
567                *p-- = '\0';
568
569        MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
570
571        return (_wstat(wbuf, (struct _stat *) stp));
572}
573
574int
575my_remove(const char *path)
576{
577        char    buf[FILENAME_MAX];
578        wchar_t wbuf[FILENAME_MAX];
579
580        my_strlcpy(buf, path, sizeof(buf));
581        fix_directory_separators(buf);
582
583        MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
584
585        return (_wremove(wbuf));
586}
587
588int
589my_rename(const char *path1, const char *path2)
590{
591        char    buf1[FILENAME_MAX];
592        char    buf2[FILENAME_MAX];
593        wchar_t wbuf1[FILENAME_MAX];
594        wchar_t wbuf2[FILENAME_MAX];
595
596        my_strlcpy(buf1, path1, sizeof(buf1));
597        my_strlcpy(buf2, path2, sizeof(buf2));
598        fix_directory_separators(buf1);
599        fix_directory_separators(buf2);
600
601        MultiByteToWideChar(CP_UTF8, 0, buf1, -1, wbuf1, sizeof(wbuf1));
602        MultiByteToWideChar(CP_UTF8, 0, buf2, -1, wbuf2, sizeof(wbuf2));
603
604        return (_wrename(wbuf1, wbuf2));
605}
606
607int
608my_mkdir(const char *path, int mode)
609{
610        char    buf[FILENAME_MAX];
611        wchar_t wbuf[FILENAME_MAX];
612
613        my_strlcpy(buf, path, sizeof(buf));
614        fix_directory_separators(buf);
615
616        MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
617
618        return (_wmkdir(wbuf));
619}
620
621static char *
622wide_to_utf8(const wchar_t *str)
623{
624        char *buf = NULL;
625        if (str) {
626                int nchar = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL);
627                if (nchar > 0) {
628                        buf = malloc(nchar);
629                        if (!buf)
630                                errno = ENOMEM;
631                        else if (!WideCharToMultiByte(CP_UTF8, 0, str, -1, buf, nchar, NULL, NULL)) {
632                                free(buf);
633                                buf = NULL;
634                                errno = EINVAL;
635                        }
636                } else
637                        errno = EINVAL;
638        } else
639                errno = EINVAL;
640        return buf;
641}
642
643char *
644my_getcwd(char *buffer, int maxlen)
645{
646        char *result = NULL;
647        wchar_t *wbuffer, *wresult;
648
649        if (buffer) {
650                /* User-supplied buffer */
651                wbuffer = malloc(maxlen * sizeof(wchar_t));
652                if (wbuffer == NULL)
653                        return NULL;
654        } else
655                /* Dynamically allocated buffer */
656                wbuffer = NULL;
657        wresult = _wgetcwd(wbuffer, maxlen);
658        if (wresult) {
659                int err = errno;
660                if (buffer) {
661                        /* User-supplied buffer */
662                        int n = WideCharToMultiByte(CP_UTF8, 0, wresult, -1, buffer, maxlen, NULL, NULL);
663                        if (n == 0)
664                                err = ERANGE;
665                        free(wbuffer);
666                        result = buffer;
667                } else {
668                        /* Buffer allocated by _wgetcwd() */
669                        result = wide_to_utf8(wresult);
670                        err = errno;
671                        free(wresult);
672                }
673                errno = err;
674        }
675        return result;
676}
677
678DIR *
679opendir(const char *name)
680{
681        DIR             *dir = NULL;
682        char            path[FILENAME_MAX];
683        wchar_t         wpath[FILENAME_MAX];
684
685        if (name == NULL || name[0] == '\0') {
686                errno = EINVAL;
687        } else if ((dir = malloc(sizeof(*dir))) == NULL) {
688                errno = ENOMEM;
689        } else {
690                my_snprintf(path, sizeof(path), "%s/*", name);
691                fix_directory_separators(path);
692                MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, sizeof(wpath));
693                dir->handle = FindFirstFileW(wpath, &dir->info);
694
695                if (dir->handle != INVALID_HANDLE_VALUE) {
696                        dir->result.d_name[0] = '\0';
697                } else {
698                        free(dir);
699                        dir = NULL;
700                }
701        }
702
703        return (dir);
704}
705
706int
707closedir(DIR *dir)
708{
709        int result = -1;
710
711        if (dir != NULL) {
712                if (dir->handle != INVALID_HANDLE_VALUE)
713                        result = FindClose(dir->handle) ? 0 : -1;
714
715                free(dir);
716        }
717
718        if (result == -1)
719                errno = EBADF;
720
721        return (result);
722}
723
724struct dirent *
725readdir(DIR *dir)
726{
727        struct dirent *result = 0;
728
729        if (dir && dir->handle != INVALID_HANDLE_VALUE) {
730                if(!dir->result.d_name ||
731                    FindNextFileW(dir->handle, &dir->info)) {
732                        result = &dir->result;
733
734                        WideCharToMultiByte(CP_UTF8, 0, dir->info.cFileName,
735                            -1, result->d_name,
736                            sizeof(result->d_name), NULL, NULL);
737                }
738        } else {
739                errno = EBADF;
740        }
741
742        return (result);
743}
744
745int
746set_non_blocking_mode(int fd)
747{
748        unsigned long   on = 1;
749
750        return (ioctlsocket(fd, FIONBIO, &on));
751}
752
753void
754set_close_on_exec(int fd)
755{
756        fd = 0; /* Do nothing. There is no FD_CLOEXEC on Windows */
757}
758
759#if !defined(NO_CGI)
760
761struct threadparam {
762        SOCKET  s;
763        HANDLE  hPipe;
764        big_int_t content_len;
765};
766
767/*
768 * Thread function that reads POST data from the socket pair
769 * and writes it to the CGI process.
770 */
771static void//DWORD WINAPI
772stdoutput(void *arg)
773{
774        struct threadparam      *tp = arg;
775        int                     n, sent, stop = 0;
776        big_int_t               total = 0;
777        DWORD k;
778        char                    buf[BUFSIZ];
779        size_t                  max_recv;
780
781        max_recv = min(sizeof(buf), tp->content_len - total);
782        while (!stop && max_recv > 0 && (n = recv(tp->s, buf, max_recv, 0)) > 0) {
783                for (sent = 0; !stop && sent < n; sent += k)
784                        if (!WriteFile(tp->hPipe, buf + sent, n - sent, &k, 0))
785                                stop++;
786                total += n;
787                max_recv = min(sizeof(buf), tp->content_len - total);
788        }
789       
790        CloseHandle(tp->hPipe); /* Suppose we have POSTed everything */
791        free(tp);
792}
793
794/*
795 * Thread function that reads CGI output and pushes it to the socket pair.
796 */
797static void
798stdinput(void *arg)
799{
800        struct threadparam      *tp = arg;
801        static                  int ntotal;
802        int                     k, stop = 0;
803        DWORD n, sent;
804        char                    buf[BUFSIZ];
805
806        while (!stop && ReadFile(tp->hPipe, buf, sizeof(buf), &n, NULL)) {
807                ntotal += n;
808                for (sent = 0; !stop && sent < n; sent += k)
809                        if ((k = send(tp->s, buf + sent, n - sent, 0)) <= 0)
810                                stop++;
811        }
812        CloseHandle(tp->hPipe);
813       
814        /*
815         * Windows is a piece of crap. When this thread closes its end
816         * of the socket pair, the other end (get_cgi() function) may loose
817         * some data. I presume, this happens if get_cgi() is not fast enough,
818         * and the data written by this end does not "push-ed" to the other
819         * end socket buffer. So after closesocket() the remaining data is
820         * gone. If I put shutdown() before closesocket(), that seems to
821         * fix the problem, but I am not sure this is the right fix.
822         * XXX (submitted by James Marshall) we do not do shutdown() on UNIX.
823         * If fork() is called from user callback, shutdown() messes up things.
824         */
825        shutdown(tp->s, 2);
826
827        closesocket(tp->s);
828        free(tp);
829
830        _endthread();
831}
832
833static void
834spawn_stdio_thread(int sock, HANDLE hPipe, void (*func)(void *),
835                big_int_t content_len)
836{
837        struct threadparam      *tp;
838        DWORD                   tid;
839
840        tp = malloc(sizeof(*tp));
841        assert(tp != NULL);
842
843        tp->s           = sock;
844        tp->hPipe       = hPipe;
845        tp->content_len = content_len;
846        _beginthread(func, 0, tp);
847}
848
849int
850spawn_process(struct conn *c, const char *prog, char *envblk,
851                char *envp[], int sock, const char *dir)
852{
853        HANDLE                  a[2], b[2], h[2], me;
854        DWORD                   flags;
855        char                    *p, cmdline[FILENAME_MAX], line[FILENAME_MAX];
856        FILE                    *fp;
857        STARTUPINFOA    si;
858        PROCESS_INFORMATION     pi;
859
860        me = GetCurrentProcess();
861        flags = DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS;
862
863        /* FIXME add error checking code here */
864        CreatePipe(&a[0], &a[1], NULL, 0);
865        CreatePipe(&b[0], &b[1], NULL, 0);
866        DuplicateHandle(me, a[0], me, &h[0], 0, TRUE, flags);
867        DuplicateHandle(me, b[1], me, &h[1], 0, TRUE, flags);
868       
869        (void) memset(&si, 0, sizeof(si));
870        (void) memset(&pi, 0, sizeof(pi));
871
872        /* XXX redirect CGI errors to the error log file */
873        si.cb           = sizeof(si);
874        si.dwFlags      = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
875        si.wShowWindow  = SW_HIDE;
876        si.hStdOutput   = si.hStdError = h[1];
877        si.hStdInput    = h[0];
878
879        /* If CGI file is a script, try to read the interpreter line */
880        if (c->ctx->cgi_interpreter == NULL) {
881                if ((fp = fopen(prog, "r")) != NULL) {
882                        (void) fgets(line, sizeof(line), fp);
883                        if (memcmp(line, "#!", 2) != 0)
884                                line[2] = '\0';
885                        /* Trim whitespaces from interpreter name */
886                        for (p = &line[strlen(line) - 1]; p > line &&
887                            isspace(*p); p--)
888                                *p = '\0';
889                        (void) fclose(fp);
890                }
891                (void) my_snprintf(cmdline, sizeof(cmdline), "%s%s%s",
892                    line + 2, line[2] == '\0' ? "" : " ", prog);
893        } else {
894                (void) my_snprintf(cmdline, sizeof(cmdline), "%s %s",
895                    c->ctx->cgi_interpreter, prog);
896        }
897
898        (void) my_snprintf(line, sizeof(line), "%s", dir);
899        fix_directory_separators(line);
900        fix_directory_separators(cmdline);
901
902        /*
903         * Spawn reader & writer threads before we create CGI process.
904         * Otherwise CGI process may die too quickly, loosing the data
905         */
906        spawn_stdio_thread(sock, b[0], stdinput, 0);
907        spawn_stdio_thread(sock, a[1], stdoutput, c->rem.content_len);
908
909        if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE,
910            CREATE_NEW_PROCESS_GROUP, envblk, line, &si, &pi) == 0) {
911                elog(E_LOG, c,"redirect: CreateProcess(%s): %d",cmdline,ERRNO);
912                return (-1);
913        } else {
914                CloseHandle(h[0]);
915                CloseHandle(h[1]);
916                CloseHandle(pi.hThread);
917                CloseHandle(pi.hProcess);
918        }
919
920        return (0);
921}
922
923#endif /* !NO_CGI */
Note: See TracBrowser for help on using the repository browser.