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 | 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 | */ |
---|
57 | static void |
---|
58 | save_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 | |
---|
83 | static void |
---|
84 | set_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 | |
---|
107 | static BOOL CALLBACK |
---|
108 | DlgProc(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 | |
---|
189 | static void * |
---|
190 | align(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 | |
---|
201 | static void |
---|
202 | add_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 | |
---|
234 | static void |
---|
235 | show_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 | |
---|
322 | static BOOL CALLBACK |
---|
323 | LogProc(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 | |
---|
400 | static void |
---|
401 | show_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 | |
---|
434 | static LRESULT CALLBACK |
---|
435 | WindowProc(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 | |
---|
497 | int WINAPI |
---|
498 | WinMain(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 | |
---|
530 | static void |
---|
531 | fix_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 | |
---|
543 | int |
---|
544 | my_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 | |
---|
556 | int |
---|
557 | my_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 | |
---|
574 | int |
---|
575 | my_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 | |
---|
588 | int |
---|
589 | my_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 | |
---|
607 | int |
---|
608 | my_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 | |
---|
621 | static char * |
---|
622 | wide_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 | |
---|
643 | char * |
---|
644 | my_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 | |
---|
678 | DIR * |
---|
679 | opendir(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 | |
---|
706 | int |
---|
707 | closedir(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 | |
---|
724 | struct dirent * |
---|
725 | readdir(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 | |
---|
745 | int |
---|
746 | set_non_blocking_mode(int fd) |
---|
747 | { |
---|
748 | unsigned long on = 1; |
---|
749 | |
---|
750 | return (ioctlsocket(fd, FIONBIO, &on)); |
---|
751 | } |
---|
752 | |
---|
753 | void |
---|
754 | set_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 | |
---|
761 | struct 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 | */ |
---|
771 | static void//DWORD WINAPI |
---|
772 | stdoutput(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 | */ |
---|
797 | static void |
---|
798 | stdinput(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 | |
---|
833 | static void |
---|
834 | spawn_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 | |
---|
849 | int |
---|
850 | spawn_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 */ |
---|