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