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