OSDN Git Service

Modify features and documents for 1.98c.
[ffftp/ffftp.git] / putty / WINDOWS / WINDLG.C
1 /*\r
2  * windlg.c - dialogs for PuTTY(tel), including the configuration dialog.\r
3  */\r
4 \r
5 #include <stdio.h>\r
6 #include <stdlib.h>\r
7 #include <limits.h>\r
8 #include <assert.h>\r
9 #include <ctype.h>\r
10 #include <time.h>\r
11 \r
12 #include "putty.h"\r
13 #include "ssh.h"\r
14 #include "win_res.h"\r
15 #include "storage.h"\r
16 #include "dialog.h"\r
17 \r
18 #include <commctrl.h>\r
19 #include <commdlg.h>\r
20 #include <shellapi.h>\r
21 \r
22 #ifdef MSVC4\r
23 #define TVINSERTSTRUCT  TV_INSERTSTRUCT\r
24 #define TVITEM          TV_ITEM\r
25 #define ICON_BIG        1\r
26 #endif\r
27 \r
28 /*\r
29  * These are the various bits of data required to handle the\r
30  * portable-dialog stuff in the config box. Having them at file\r
31  * scope in here isn't too bad a place to put them; if we were ever\r
32  * to need more than one config box per process we could always\r
33  * shift them to a per-config-box structure stored in GWL_USERDATA.\r
34  */\r
35 static struct controlbox *ctrlbox;\r
36 /*\r
37  * ctrls_base holds the OK and Cancel buttons: the controls which\r
38  * are present in all dialog panels. ctrls_panel holds the ones\r
39  * which change from panel to panel.\r
40  */\r
41 static struct winctrls ctrls_base, ctrls_panel;\r
42 static struct dlgparam dp;\r
43 \r
44 static char **events = NULL;\r
45 static int nevents = 0, negsize = 0;\r
46 \r
47 extern Config cfg;                     /* defined in window.c */\r
48 \r
49 #define PRINTER_DISABLED_STRING "None (printing disabled)"\r
50 \r
51 void force_normal(HWND hwnd)\r
52 {\r
53     static int recurse = 0;\r
54 \r
55     WINDOWPLACEMENT wp;\r
56 \r
57     if (recurse)\r
58         return;\r
59     recurse = 1;\r
60 \r
61     wp.length = sizeof(wp);\r
62     if (GetWindowPlacement(hwnd, &wp) && wp.showCmd == SW_SHOWMAXIMIZED) {\r
63         wp.showCmd = SW_SHOWNORMAL;\r
64         SetWindowPlacement(hwnd, &wp);\r
65     }\r
66     recurse = 0;\r
67 }\r
68 \r
69 static int CALLBACK LogProc(HWND hwnd, UINT msg,\r
70                             WPARAM wParam, LPARAM lParam)\r
71 {\r
72     int i;\r
73 \r
74     switch (msg) {\r
75       case WM_INITDIALOG:\r
76         {\r
77             char *str = dupprintf("%s Event Log", appname);\r
78             SetWindowText(hwnd, str);\r
79             sfree(str);\r
80         }\r
81         {\r
82             static int tabs[4] = { 78, 108 };\r
83             SendDlgItemMessage(hwnd, IDN_LIST, LB_SETTABSTOPS, 2,\r
84                                (LPARAM) tabs);\r
85         }\r
86         for (i = 0; i < nevents; i++)\r
87             SendDlgItemMessage(hwnd, IDN_LIST, LB_ADDSTRING,\r
88                                0, (LPARAM) events[i]);\r
89         return 1;\r
90       case WM_COMMAND:\r
91         switch (LOWORD(wParam)) {\r
92           case IDOK:\r
93           case IDCANCEL:\r
94             logbox = NULL;\r
95             SetActiveWindow(GetParent(hwnd));\r
96             DestroyWindow(hwnd);\r
97             return 0;\r
98           case IDN_COPY:\r
99             if (HIWORD(wParam) == BN_CLICKED ||\r
100                 HIWORD(wParam) == BN_DOUBLECLICKED) {\r
101                 int selcount;\r
102                 int *selitems;\r
103                 selcount = SendDlgItemMessage(hwnd, IDN_LIST,\r
104                                               LB_GETSELCOUNT, 0, 0);\r
105                 if (selcount == 0) {   /* don't even try to copy zero items */\r
106                     MessageBeep(0);\r
107                     break;\r
108                 }\r
109 \r
110                 selitems = snewn(selcount, int);\r
111                 if (selitems) {\r
112                     int count = SendDlgItemMessage(hwnd, IDN_LIST,\r
113                                                    LB_GETSELITEMS,\r
114                                                    selcount,\r
115                                                    (LPARAM) selitems);\r
116                     int i;\r
117                     int size;\r
118                     char *clipdata;\r
119                     static unsigned char sel_nl[] = SEL_NL;\r
120 \r
121                     if (count == 0) {  /* can't copy zero stuff */\r
122                         MessageBeep(0);\r
123                         break;\r
124                     }\r
125 \r
126                     size = 0;\r
127                     for (i = 0; i < count; i++)\r
128                         size +=\r
129                             strlen(events[selitems[i]]) + sizeof(sel_nl);\r
130 \r
131                     clipdata = snewn(size, char);\r
132                     if (clipdata) {\r
133                         char *p = clipdata;\r
134                         for (i = 0; i < count; i++) {\r
135                             char *q = events[selitems[i]];\r
136                             int qlen = strlen(q);\r
137                             memcpy(p, q, qlen);\r
138                             p += qlen;\r
139                             memcpy(p, sel_nl, sizeof(sel_nl));\r
140                             p += sizeof(sel_nl);\r
141                         }\r
142                         write_aclip(NULL, clipdata, size, TRUE);\r
143                         sfree(clipdata);\r
144                     }\r
145                     sfree(selitems);\r
146 \r
147                     for (i = 0; i < nevents; i++)\r
148                         SendDlgItemMessage(hwnd, IDN_LIST, LB_SETSEL,\r
149                                            FALSE, i);\r
150                 }\r
151             }\r
152             return 0;\r
153         }\r
154         return 0;\r
155       case WM_CLOSE:\r
156         logbox = NULL;\r
157         SetActiveWindow(GetParent(hwnd));\r
158         DestroyWindow(hwnd);\r
159         return 0;\r
160     }\r
161     return 0;\r
162 }\r
163 \r
164 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,\r
165                                 WPARAM wParam, LPARAM lParam)\r
166 {\r
167     switch (msg) {\r
168       case WM_INITDIALOG:\r
169         {\r
170             char *str = dupprintf("%s Licence", appname);\r
171             SetWindowText(hwnd, str);\r
172             sfree(str);\r
173         }\r
174         return 1;\r
175       case WM_COMMAND:\r
176         switch (LOWORD(wParam)) {\r
177           case IDOK:\r
178           case IDCANCEL:\r
179             EndDialog(hwnd, 1);\r
180             return 0;\r
181         }\r
182         return 0;\r
183       case WM_CLOSE:\r
184         EndDialog(hwnd, 1);\r
185         return 0;\r
186     }\r
187     return 0;\r
188 }\r
189 \r
190 static int CALLBACK AboutProc(HWND hwnd, UINT msg,\r
191                               WPARAM wParam, LPARAM lParam)\r
192 {\r
193     char *str;\r
194 \r
195     switch (msg) {\r
196       case WM_INITDIALOG:\r
197         str = dupprintf("About %s", appname);\r
198         SetWindowText(hwnd, str);\r
199         sfree(str);\r
200         SetDlgItemText(hwnd, IDA_TEXT1, appname);\r
201         SetDlgItemText(hwnd, IDA_VERSION, ver);\r
202         return 1;\r
203       case WM_COMMAND:\r
204         switch (LOWORD(wParam)) {\r
205           case IDOK:\r
206           case IDCANCEL:\r
207             EndDialog(hwnd, TRUE);\r
208             return 0;\r
209           case IDA_LICENCE:\r
210             EnableWindow(hwnd, 0);\r
211             DialogBox(hinst, MAKEINTRESOURCE(IDD_LICENCEBOX),\r
212                       hwnd, LicenceProc);\r
213             EnableWindow(hwnd, 1);\r
214             SetActiveWindow(hwnd);\r
215             return 0;\r
216 \r
217           case IDA_WEB:\r
218             /* Load web browser */\r
219             ShellExecute(hwnd, "open",\r
220                          "http://www.chiark.greenend.org.uk/~sgtatham/putty/",\r
221                          0, 0, SW_SHOWDEFAULT);\r
222             return 0;\r
223         }\r
224         return 0;\r
225       case WM_CLOSE:\r
226         EndDialog(hwnd, TRUE);\r
227         return 0;\r
228     }\r
229     return 0;\r
230 }\r
231 \r
232 static int SaneDialogBox(HINSTANCE hinst,\r
233                          LPCTSTR tmpl,\r
234                          HWND hwndparent,\r
235                          DLGPROC lpDialogFunc)\r
236 {\r
237     WNDCLASS wc;\r
238     HWND hwnd;\r
239     MSG msg;\r
240     int flags;\r
241     int ret;\r
242     int gm;\r
243 \r
244     wc.style = CS_DBLCLKS | CS_SAVEBITS | CS_BYTEALIGNWINDOW;\r
245     wc.lpfnWndProc = DefDlgProc;\r
246     wc.cbClsExtra = 0;\r
247     wc.cbWndExtra = DLGWINDOWEXTRA + 2*sizeof(LONG_PTR);\r
248     wc.hInstance = hinst;\r
249     wc.hIcon = NULL;\r
250     wc.hCursor = LoadCursor(NULL, IDC_ARROW);\r
251     wc.hbrBackground = (HBRUSH) (COLOR_BACKGROUND +1);\r
252     wc.lpszMenuName = NULL;\r
253     wc.lpszClassName = "PuTTYConfigBox";\r
254     RegisterClass(&wc);\r
255 \r
256     hwnd = CreateDialog(hinst, tmpl, hwndparent, lpDialogFunc);\r
257 \r
258     SetWindowLongPtr(hwnd, BOXFLAGS, 0); /* flags */\r
259     SetWindowLongPtr(hwnd, BOXRESULT, 0); /* result from SaneEndDialog */\r
260 \r
261     while ((gm=GetMessage(&msg, NULL, 0, 0)) > 0) {\r
262         flags=GetWindowLongPtr(hwnd, BOXFLAGS);\r
263         if (!(flags & DF_END) && !IsDialogMessage(hwnd, &msg))\r
264             DispatchMessage(&msg);\r
265         if (flags & DF_END)\r
266             break;\r
267     }\r
268 \r
269     if (gm == 0)\r
270         PostQuitMessage(msg.wParam); /* We got a WM_QUIT, pass it on */\r
271 \r
272     ret=GetWindowLongPtr(hwnd, BOXRESULT);\r
273     DestroyWindow(hwnd);\r
274     return ret;\r
275 }\r
276 \r
277 static void SaneEndDialog(HWND hwnd, int ret)\r
278 {\r
279     SetWindowLongPtr(hwnd, BOXRESULT, ret);\r
280     SetWindowLongPtr(hwnd, BOXFLAGS, DF_END);\r
281 }\r
282 \r
283 /*\r
284  * Null dialog procedure.\r
285  */\r
286 static int CALLBACK NullDlgProc(HWND hwnd, UINT msg,\r
287                                 WPARAM wParam, LPARAM lParam)\r
288 {\r
289     return 0;\r
290 }\r
291 \r
292 enum {\r
293     IDCX_ABOUT = IDC_ABOUT,\r
294     IDCX_TVSTATIC,\r
295     IDCX_TREEVIEW,\r
296     IDCX_STDBASE,\r
297     IDCX_PANELBASE = IDCX_STDBASE + 32\r
298 };\r
299 \r
300 struct treeview_faff {\r
301     HWND treeview;\r
302     HTREEITEM lastat[4];\r
303 };\r
304 \r
305 static HTREEITEM treeview_insert(struct treeview_faff *faff,\r
306                                  int level, char *text, char *path)\r
307 {\r
308     TVINSERTSTRUCT ins;\r
309     int i;\r
310     HTREEITEM newitem;\r
311     ins.hParent = (level > 0 ? faff->lastat[level - 1] : TVI_ROOT);\r
312     ins.hInsertAfter = faff->lastat[level];\r
313 #if _WIN32_IE >= 0x0400 && defined NONAMELESSUNION\r
314 #define INSITEM DUMMYUNIONNAME.item\r
315 #else\r
316 #define INSITEM item\r
317 #endif\r
318     ins.INSITEM.mask = TVIF_TEXT | TVIF_PARAM;\r
319     ins.INSITEM.pszText = text;\r
320     ins.INSITEM.cchTextMax = strlen(text)+1;\r
321     ins.INSITEM.lParam = (LPARAM)path;\r
322     newitem = TreeView_InsertItem(faff->treeview, &ins);\r
323     if (level > 0)\r
324         TreeView_Expand(faff->treeview, faff->lastat[level - 1],\r
325                         (level > 1 ? TVE_COLLAPSE : TVE_EXPAND));\r
326     faff->lastat[level] = newitem;\r
327     for (i = level + 1; i < 4; i++)\r
328         faff->lastat[i] = NULL;\r
329     return newitem;\r
330 }\r
331 \r
332 /*\r
333  * Create the panelfuls of controls in the configuration box.\r
334  */\r
335 static void create_controls(HWND hwnd, char *path)\r
336 {\r
337     struct ctlpos cp;\r
338     int index;\r
339     int base_id;\r
340     struct winctrls *wc;\r
341 \r
342     if (!path[0]) {\r
343         /*\r
344          * Here we must create the basic standard controls.\r
345          */\r
346         ctlposinit(&cp, hwnd, 3, 3, 235);\r
347         wc = &ctrls_base;\r
348         base_id = IDCX_STDBASE;\r
349     } else {\r
350         /*\r
351          * Otherwise, we're creating the controls for a particular\r
352          * panel.\r
353          */\r
354         ctlposinit(&cp, hwnd, 100, 3, 13);\r
355         wc = &ctrls_panel;\r
356         base_id = IDCX_PANELBASE;\r
357     }\r
358 \r
359     for (index=-1; (index = ctrl_find_path(ctrlbox, path, index)) >= 0 ;) {\r
360         struct controlset *s = ctrlbox->ctrlsets[index];\r
361         winctrl_layout(&dp, wc, &cp, s, &base_id);\r
362     }\r
363 }\r
364 \r
365 /*\r
366  * This function is the configuration box.\r
367  * (Being a dialog procedure, in general it returns 0 if the default\r
368  * dialog processing should be performed, and 1 if it should not.)\r
369  */\r
370 static int CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,\r
371                                        WPARAM wParam, LPARAM lParam)\r
372 {\r
373     HWND hw, treeview;\r
374     struct treeview_faff tvfaff;\r
375     int ret;\r
376 \r
377     switch (msg) {\r
378       case WM_INITDIALOG:\r
379         dp.hwnd = hwnd;\r
380         create_controls(hwnd, "");     /* Open and Cancel buttons etc */\r
381         SetWindowText(hwnd, dp.wintitle);\r
382         SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);\r
383         if (has_help())\r
384             SetWindowLongPtr(hwnd, GWL_EXSTYLE,\r
385                              GetWindowLongPtr(hwnd, GWL_EXSTYLE) |\r
386                              WS_EX_CONTEXTHELP);\r
387         else {\r
388             HWND item = GetDlgItem(hwnd, IDC_HELPBTN);\r
389             if (item)\r
390                 DestroyWindow(item);\r
391         }\r
392         SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,\r
393                     (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(IDI_CFGICON)));\r
394         /*\r
395          * Centre the window.\r
396          */\r
397         {                              /* centre the window */\r
398             RECT rs, rd;\r
399 \r
400             hw = GetDesktopWindow();\r
401             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))\r
402                 MoveWindow(hwnd,\r
403                            (rs.right + rs.left + rd.left - rd.right) / 2,\r
404                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,\r
405                            rd.right - rd.left, rd.bottom - rd.top, TRUE);\r
406         }\r
407 \r
408         /*\r
409          * Create the tree view.\r
410          */\r
411         {\r
412             RECT r;\r
413             WPARAM font;\r
414             HWND tvstatic;\r
415 \r
416             r.left = 3;\r
417             r.right = r.left + 95;\r
418             r.top = 3;\r
419             r.bottom = r.top + 10;\r
420             MapDialogRect(hwnd, &r);\r
421             tvstatic = CreateWindowEx(0, "STATIC", "Cate&gory:",\r
422                                       WS_CHILD | WS_VISIBLE,\r
423                                       r.left, r.top,\r
424                                       r.right - r.left, r.bottom - r.top,\r
425                                       hwnd, (HMENU) IDCX_TVSTATIC, hinst,\r
426                                       NULL);\r
427             font = SendMessage(hwnd, WM_GETFONT, 0, 0);\r
428             SendMessage(tvstatic, WM_SETFONT, font, MAKELPARAM(TRUE, 0));\r
429 \r
430             r.left = 3;\r
431             r.right = r.left + 95;\r
432             r.top = 13;\r
433             r.bottom = r.top + 219;\r
434             MapDialogRect(hwnd, &r);\r
435             treeview = CreateWindowEx(WS_EX_CLIENTEDGE, WC_TREEVIEW, "",\r
436                                       WS_CHILD | WS_VISIBLE |\r
437                                       WS_TABSTOP | TVS_HASLINES |\r
438                                       TVS_DISABLEDRAGDROP | TVS_HASBUTTONS\r
439                                       | TVS_LINESATROOT |\r
440                                       TVS_SHOWSELALWAYS, r.left, r.top,\r
441                                       r.right - r.left, r.bottom - r.top,\r
442                                       hwnd, (HMENU) IDCX_TREEVIEW, hinst,\r
443                                       NULL);\r
444             font = SendMessage(hwnd, WM_GETFONT, 0, 0);\r
445             SendMessage(treeview, WM_SETFONT, font, MAKELPARAM(TRUE, 0));\r
446             tvfaff.treeview = treeview;\r
447             memset(tvfaff.lastat, 0, sizeof(tvfaff.lastat));\r
448         }\r
449 \r
450         /*\r
451          * Set up the tree view contents.\r
452          */\r
453         {\r
454             HTREEITEM hfirst = NULL;\r
455             int i;\r
456             char *path = NULL;\r
457 \r
458             for (i = 0; i < ctrlbox->nctrlsets; i++) {\r
459                 struct controlset *s = ctrlbox->ctrlsets[i];\r
460                 HTREEITEM item;\r
461                 int j;\r
462                 char *c;\r
463 \r
464                 if (!s->pathname[0])\r
465                     continue;\r
466                 j = path ? ctrl_path_compare(s->pathname, path) : 0;\r
467                 if (j == INT_MAX)\r
468                     continue;          /* same path, nothing to add to tree */\r
469 \r
470                 /*\r
471                  * We expect never to find an implicit path\r
472                  * component. For example, we expect never to see\r
473                  * A/B/C followed by A/D/E, because that would\r
474                  * _implicitly_ create A/D. All our path prefixes\r
475                  * are expected to contain actual controls and be\r
476                  * selectable in the treeview; so we would expect\r
477                  * to see A/D _explicitly_ before encountering\r
478                  * A/D/E.\r
479                  */\r
480                 assert(j == ctrl_path_elements(s->pathname) - 1);\r
481 \r
482                 c = strrchr(s->pathname, '/');\r
483                 if (!c)\r
484                         c = s->pathname;\r
485                 else\r
486                         c++;\r
487 \r
488                 item = treeview_insert(&tvfaff, j, c, s->pathname);\r
489                 if (!hfirst)\r
490                     hfirst = item;\r
491 \r
492                 path = s->pathname;\r
493             }\r
494 \r
495             /*\r
496              * Put the treeview selection on to the Session panel.\r
497              * This should also cause creation of the relevant\r
498              * controls.\r
499              */\r
500             TreeView_SelectItem(treeview, hfirst);\r
501         }\r
502 \r
503         /*\r
504          * Set focus into the first available control.\r
505          */\r
506         {\r
507             int i;\r
508             struct winctrl *c;\r
509 \r
510             for (i = 0; (c = winctrl_findbyindex(&ctrls_panel, i)) != NULL;\r
511                  i++) {\r
512                 if (c->ctrl) {\r
513                     dlg_set_focus(c->ctrl, &dp);\r
514                     break;\r
515                 }\r
516             }\r
517         }\r
518 \r
519         SetWindowLongPtr(hwnd, GWLP_USERDATA, 1);\r
520         return 0;\r
521       case WM_LBUTTONUP:\r
522         /*\r
523          * Button release should trigger WM_OK if there was a\r
524          * previous double click on the session list.\r
525          */\r
526         ReleaseCapture();\r
527         if (dp.ended)\r
528             SaneEndDialog(hwnd, dp.endresult ? 1 : 0);\r
529         break;\r
530       case WM_NOTIFY:\r
531         if (LOWORD(wParam) == IDCX_TREEVIEW &&\r
532             ((LPNMHDR) lParam)->code == TVN_SELCHANGED) {\r
533             HTREEITEM i =\r
534                 TreeView_GetSelection(((LPNMHDR) lParam)->hwndFrom);\r
535             TVITEM item;\r
536             char buffer[64];\r
537  \r
538             SendMessage (hwnd, WM_SETREDRAW, FALSE, 0);\r
539  \r
540             item.hItem = i;\r
541             item.pszText = buffer;\r
542             item.cchTextMax = sizeof(buffer);\r
543             item.mask = TVIF_TEXT | TVIF_PARAM;\r
544             TreeView_GetItem(((LPNMHDR) lParam)->hwndFrom, &item);\r
545             {\r
546                 /* Destroy all controls in the currently visible panel. */\r
547                 int k;\r
548                 HWND item;\r
549                 struct winctrl *c;\r
550 \r
551                 while ((c = winctrl_findbyindex(&ctrls_panel, 0)) != NULL) {\r
552                     for (k = 0; k < c->num_ids; k++) {\r
553                         item = GetDlgItem(hwnd, c->base_id + k);\r
554                         if (item)\r
555                             DestroyWindow(item);\r
556                     }\r
557                     winctrl_rem_shortcuts(&dp, c);\r
558                     winctrl_remove(&ctrls_panel, c);\r
559                     sfree(c->data);\r
560                     sfree(c);\r
561                 }\r
562             }\r
563             create_controls(hwnd, (char *)item.lParam);\r
564 \r
565             dlg_refresh(NULL, &dp);    /* set up control values */\r
566  \r
567             SendMessage (hwnd, WM_SETREDRAW, TRUE, 0);\r
568             InvalidateRect (hwnd, NULL, TRUE);\r
569 \r
570             SetFocus(((LPNMHDR) lParam)->hwndFrom);     /* ensure focus stays */\r
571             return 0;\r
572         }\r
573         break;\r
574       case WM_COMMAND:\r
575       case WM_DRAWITEM:\r
576       default:                         /* also handle drag list msg here */\r
577         /*\r
578          * Only process WM_COMMAND once the dialog is fully formed.\r
579          */\r
580         if (GetWindowLongPtr(hwnd, GWLP_USERDATA) == 1) {\r
581             ret = winctrl_handle_command(&dp, msg, wParam, lParam);\r
582             if (dp.ended && GetCapture() != hwnd)\r
583                 SaneEndDialog(hwnd, dp.endresult ? 1 : 0);\r
584         } else\r
585             ret = 0;\r
586         return ret;\r
587       case WM_HELP:\r
588         if (!winctrl_context_help(&dp, hwnd,\r
589                                  ((LPHELPINFO)lParam)->iCtrlId))\r
590             MessageBeep(0);\r
591         break;\r
592       case WM_CLOSE:\r
593         quit_help(hwnd);\r
594         SaneEndDialog(hwnd, 0);\r
595         return 0;\r
596 \r
597         /* Grrr Explorer will maximize Dialogs! */\r
598       case WM_SIZE:\r
599         if (wParam == SIZE_MAXIMIZED)\r
600             force_normal(hwnd);\r
601         return 0;\r
602 \r
603     }\r
604     return 0;\r
605 }\r
606 \r
607 void modal_about_box(HWND hwnd)\r
608 {\r
609     EnableWindow(hwnd, 0);\r
610     DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);\r
611     EnableWindow(hwnd, 1);\r
612     SetActiveWindow(hwnd);\r
613 }\r
614 \r
615 void show_help(HWND hwnd)\r
616 {\r
617     launch_help(hwnd, NULL);\r
618 }\r
619 \r
620 void defuse_showwindow(void)\r
621 {\r
622     /*\r
623      * Work around the fact that the app's first call to ShowWindow\r
624      * will ignore the default in favour of the shell-provided\r
625      * setting.\r
626      */\r
627     {\r
628         HWND hwnd;\r
629         hwnd = CreateDialog(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX),\r
630                             NULL, NullDlgProc);\r
631         ShowWindow(hwnd, SW_HIDE);\r
632         SetActiveWindow(hwnd);\r
633         DestroyWindow(hwnd);\r
634     }\r
635 }\r
636 \r
637 int do_config(void)\r
638 {\r
639     int ret;\r
640 \r
641     ctrlbox = ctrl_new_box();\r
642     setup_config_box(ctrlbox, FALSE, 0, 0);\r
643     win_setup_config_box(ctrlbox, &dp.hwnd, has_help(), FALSE, 0);\r
644     dp_init(&dp);\r
645     winctrl_init(&ctrls_base);\r
646     winctrl_init(&ctrls_panel);\r
647     dp_add_tree(&dp, &ctrls_base);\r
648     dp_add_tree(&dp, &ctrls_panel);\r
649     dp.wintitle = dupprintf("%s Configuration", appname);\r
650     dp.errtitle = dupprintf("%s Error", appname);\r
651     dp.data = &cfg;\r
652     dlg_auto_set_fixed_pitch_flag(&dp);\r
653     dp.shortcuts['g'] = TRUE;          /* the treeview: `Cate&gory' */\r
654 \r
655     ret =\r
656         SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,\r
657                   GenericMainDlgProc);\r
658 \r
659     ctrl_free_box(ctrlbox);\r
660     winctrl_cleanup(&ctrls_panel);\r
661     winctrl_cleanup(&ctrls_base);\r
662     dp_cleanup(&dp);\r
663 \r
664     return ret;\r
665 }\r
666 \r
667 int do_reconfig(HWND hwnd, int protcfginfo)\r
668 {\r
669     Config backup_cfg;\r
670     int ret;\r
671 \r
672     backup_cfg = cfg;                  /* structure copy */\r
673 \r
674     ctrlbox = ctrl_new_box();\r
675     setup_config_box(ctrlbox, TRUE, cfg.protocol, protcfginfo);\r
676     win_setup_config_box(ctrlbox, &dp.hwnd, has_help(), TRUE,\r
677                          cfg.protocol);\r
678     dp_init(&dp);\r
679     winctrl_init(&ctrls_base);\r
680     winctrl_init(&ctrls_panel);\r
681     dp_add_tree(&dp, &ctrls_base);\r
682     dp_add_tree(&dp, &ctrls_panel);\r
683     dp.wintitle = dupprintf("%s Reconfiguration", appname);\r
684     dp.errtitle = dupprintf("%s Error", appname);\r
685     dp.data = &cfg;\r
686     dlg_auto_set_fixed_pitch_flag(&dp);\r
687     dp.shortcuts['g'] = TRUE;          /* the treeview: `Cate&gory' */\r
688 \r
689     ret = SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,\r
690                   GenericMainDlgProc);\r
691 \r
692     ctrl_free_box(ctrlbox);\r
693     winctrl_cleanup(&ctrls_base);\r
694     winctrl_cleanup(&ctrls_panel);\r
695     dp_cleanup(&dp);\r
696 \r
697     if (!ret)\r
698         cfg = backup_cfg;              /* structure copy */\r
699 \r
700     return ret;\r
701 }\r
702 \r
703 void logevent(void *frontend, const char *string)\r
704 {\r
705     char timebuf[40];\r
706     struct tm tm;\r
707 \r
708     log_eventlog(logctx, string);\r
709 \r
710     if (nevents >= negsize) {\r
711         negsize += 64;\r
712         events = sresize(events, negsize, char *);\r
713     }\r
714 \r
715     tm=ltime();\r
716     strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t", &tm);\r
717 \r
718     events[nevents] = snewn(strlen(timebuf) + strlen(string) + 1, char);\r
719     strcpy(events[nevents], timebuf);\r
720     strcat(events[nevents], string);\r
721     if (logbox) {\r
722         int count;\r
723         SendDlgItemMessage(logbox, IDN_LIST, LB_ADDSTRING,\r
724                            0, (LPARAM) events[nevents]);\r
725         count = SendDlgItemMessage(logbox, IDN_LIST, LB_GETCOUNT, 0, 0);\r
726         SendDlgItemMessage(logbox, IDN_LIST, LB_SETTOPINDEX, count - 1, 0);\r
727     }\r
728     nevents++;\r
729 }\r
730 \r
731 void showeventlog(HWND hwnd)\r
732 {\r
733     if (!logbox) {\r
734         logbox = CreateDialog(hinst, MAKEINTRESOURCE(IDD_LOGBOX),\r
735                               hwnd, LogProc);\r
736         ShowWindow(logbox, SW_SHOWNORMAL);\r
737     }\r
738     SetActiveWindow(logbox);\r
739 }\r
740 \r
741 void showabout(HWND hwnd)\r
742 {\r
743     DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);\r
744 }\r
745 \r
746 int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,\r
747                         char *keystr, char *fingerprint,\r
748                         void (*callback)(void *ctx, int result), void *ctx)\r
749 {\r
750     int ret;\r
751 \r
752     static const char absentmsg[] =\r
753         "The server's host key is not cached in the registry. You\n"\r
754         "have no guarantee that the server is the computer you\n"\r
755         "think it is.\n"\r
756         "The server's %s key fingerprint is:\n"\r
757         "%s\n"\r
758         "If you trust this host, hit Yes to add the key to\n"\r
759         "%s's cache and carry on connecting.\n"\r
760         "If you want to carry on connecting just once, without\n"\r
761         "adding the key to the cache, hit No.\n"\r
762         "If you do not trust this host, hit Cancel to abandon the\n"\r
763         "connection.\n";\r
764 \r
765     static const char wrongmsg[] =\r
766         "WARNING - POTENTIAL SECURITY BREACH!\n"\r
767         "\n"\r
768         "The server's host key does not match the one %s has\n"\r
769         "cached in the registry. This means that either the\n"\r
770         "server administrator has changed the host key, or you\n"\r
771         "have actually connected to another computer pretending\n"\r
772         "to be the server.\n"\r
773         "The new %s key fingerprint is:\n"\r
774         "%s\n"\r
775         "If you were expecting this change and trust the new key,\n"\r
776         "hit Yes to update %s's cache and continue connecting.\n"\r
777         "If you want to carry on connecting but without updating\n"\r
778         "the cache, hit No.\n"\r
779         "If you want to abandon the connection completely, hit\n"\r
780         "Cancel. Hitting Cancel is the ONLY guaranteed safe\n" "choice.\n";\r
781 \r
782     static const char mbtitle[] = "%s Security Alert";\r
783 \r
784     /*\r
785      * Verify the key against the registry.\r
786      */\r
787     ret = verify_host_key(host, port, keytype, keystr);\r
788 \r
789     if (ret == 0)                      /* success - key matched OK */\r
790         return 1;\r
791     else if (ret == 2) {               /* key was different */\r
792         int mbret;\r
793         char *text = dupprintf(wrongmsg, appname, keytype, fingerprint,\r
794                                appname);\r
795         char *caption = dupprintf(mbtitle, appname);\r
796         mbret = message_box(text, caption,\r
797                             MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3,\r
798                             HELPCTXID(errors_hostkey_changed));\r
799         assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);\r
800         sfree(text);\r
801         sfree(caption);\r
802         if (mbret == IDYES) {\r
803             store_host_key(host, port, keytype, keystr);\r
804             return 1;\r
805         } else if (mbret == IDNO)\r
806             return 1;\r
807     } else if (ret == 1) {             /* key was absent */\r
808         int mbret;\r
809         char *text = dupprintf(absentmsg, keytype, fingerprint, appname);\r
810         char *caption = dupprintf(mbtitle, appname);\r
811         mbret = message_box(text, caption,\r
812                             MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3,\r
813                             HELPCTXID(errors_hostkey_absent));\r
814         assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);\r
815         sfree(text);\r
816         sfree(caption);\r
817         if (mbret == IDYES) {\r
818             store_host_key(host, port, keytype, keystr);\r
819             return 1;\r
820         } else if (mbret == IDNO)\r
821             return 1;\r
822     }\r
823     return 0;   /* abandon the connection */\r
824 }\r
825 \r
826 /*\r
827  * Ask whether the selected algorithm is acceptable (since it was\r
828  * below the configured 'warn' threshold).\r
829  */\r
830 int askalg(void *frontend, const char *algtype, const char *algname,\r
831            void (*callback)(void *ctx, int result), void *ctx)\r
832 {\r
833     static const char mbtitle[] = "%s Security Alert";\r
834     static const char msg[] =\r
835         "The first %s supported by the server\n"\r
836         "is %.64s, which is below the configured\n"\r
837         "warning threshold.\n"\r
838         "Do you want to continue with this connection?\n";\r
839     char *message, *title;\r
840     int mbret;\r
841 \r
842     message = dupprintf(msg, algtype, algname);\r
843     title = dupprintf(mbtitle, appname);\r
844     mbret = MessageBox(NULL, message, title,\r
845                        MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2);\r
846     socket_reselect_all();\r
847     sfree(message);\r
848     sfree(title);\r
849     if (mbret == IDYES)\r
850         return 1;\r
851     else\r
852         return 0;\r
853 }\r
854 \r
855 /*\r
856  * Ask whether to wipe a session log file before writing to it.\r
857  * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).\r
858  */\r
859 int askappend(void *frontend, Filename filename,\r
860               void (*callback)(void *ctx, int result), void *ctx)\r
861 {\r
862     static const char msgtemplate[] =\r
863         "The session log file \"%.*s\" already exists.\n"\r
864         "You can overwrite it with a new session log,\n"\r
865         "append your session log to the end of it,\n"\r
866         "or disable session logging for this session.\n"\r
867         "Hit Yes to wipe the file, No to append to it,\n"\r
868         "or Cancel to disable logging.";\r
869     char *message;\r
870     char *mbtitle;\r
871     int mbret;\r
872 \r
873     message = dupprintf(msgtemplate, FILENAME_MAX, filename.path);\r
874     mbtitle = dupprintf("%s Log to File", appname);\r
875 \r
876     mbret = MessageBox(NULL, message, mbtitle,\r
877                        MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON3);\r
878 \r
879     socket_reselect_all();\r
880 \r
881     sfree(message);\r
882     sfree(mbtitle);\r
883 \r
884     if (mbret == IDYES)\r
885         return 2;\r
886     else if (mbret == IDNO)\r
887         return 1;\r
888     else\r
889         return 0;\r
890 }\r
891 \r
892 /*\r
893  * Warn about the obsolescent key file format.\r
894  * \r
895  * Uniquely among these functions, this one does _not_ expect a\r
896  * frontend handle. This means that if PuTTY is ported to a\r
897  * platform which requires frontend handles, this function will be\r
898  * an anomaly. Fortunately, the problem it addresses will not have\r
899  * been present on that platform, so it can plausibly be\r
900  * implemented as an empty function.\r
901  */\r
902 void old_keyfile_warning(void)\r
903 {\r
904     static const char mbtitle[] = "%s Key File Warning";\r
905     static const char message[] =\r
906         "You are loading an SSH-2 private key which has an\n"\r
907         "old version of the file format. This means your key\n"\r
908         "file is not fully tamperproof. Future versions of\n"\r
909         "%s may stop supporting this private key format,\n"\r
910         "so we recommend you convert your key to the new\n"\r
911         "format.\n"\r
912         "\n"\r
913         "You can perform this conversion by loading the key\n"\r
914         "into PuTTYgen and then saving it again.";\r
915 \r
916     char *msg, *title;\r
917     msg = dupprintf(message, appname);\r
918     title = dupprintf(mbtitle, appname);\r
919 \r
920     MessageBox(NULL, msg, title, MB_OK);\r
921 \r
922     socket_reselect_all();\r
923 \r
924     sfree(msg);\r
925     sfree(title);\r
926 }\r