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