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