]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windows/windlg.c
Stop copying the licence text into C source code.
[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 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 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 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 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 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             create_controls(hwnd, firstpath);
517             dlg_refresh(NULL, &dp);    /* and set up control values */
518         }
519
520         /*
521          * Set focus into the first available control.
522          */
523         {
524             int i;
525             struct winctrl *c;
526
527             for (i = 0; (c = winctrl_findbyindex(&ctrls_panel, i)) != NULL;
528                  i++) {
529                 if (c->ctrl) {
530                     dlg_set_focus(c->ctrl, &dp);
531                     break;
532                 }
533             }
534         }
535
536         /*
537          * Now we've finished creating our initial set of controls,
538          * it's safe to actually show the window without risking setup
539          * flicker.
540          */
541         ShowWindow(hwnd, SW_SHOWNORMAL);
542
543         /*
544          * Set the flag that activates a couple of the other message
545          * handlers below, which were disabled until now to avoid
546          * spurious firing during the above setup procedure.
547          */
548         SetWindowLongPtr(hwnd, GWLP_USERDATA, 1);
549         return 0;
550       case WM_LBUTTONUP:
551         /*
552          * Button release should trigger WM_OK if there was a
553          * previous double click on the session list.
554          */
555         ReleaseCapture();
556         if (dp.ended)
557             SaneEndDialog(hwnd, dp.endresult ? 1 : 0);
558         break;
559       case WM_NOTIFY:
560         if (LOWORD(wParam) == IDCX_TREEVIEW &&
561             ((LPNMHDR) lParam)->code == TVN_SELCHANGED) {
562             /*
563              * Selection-change events on the treeview cause us to do
564              * a flurry of control deletion and creation - but only
565              * after WM_INITDIALOG has finished. The initial
566              * selection-change event(s) during treeview setup are
567              * ignored.
568              */
569             HTREEITEM i;
570             TVITEM item;
571             char buffer[64];
572
573             if (GetWindowLongPtr(hwnd, GWLP_USERDATA) != 1)
574                 return 0;
575
576             i = TreeView_GetSelection(((LPNMHDR) lParam)->hwndFrom);
577  
578             SendMessage (hwnd, WM_SETREDRAW, FALSE, 0);
579  
580             item.hItem = i;
581             item.pszText = buffer;
582             item.cchTextMax = sizeof(buffer);
583             item.mask = TVIF_TEXT | TVIF_PARAM;
584             TreeView_GetItem(((LPNMHDR) lParam)->hwndFrom, &item);
585             {
586                 /* Destroy all controls in the currently visible panel. */
587                 int k;
588                 HWND item;
589                 struct winctrl *c;
590
591                 while ((c = winctrl_findbyindex(&ctrls_panel, 0)) != NULL) {
592                     for (k = 0; k < c->num_ids; k++) {
593                         item = GetDlgItem(hwnd, c->base_id + k);
594                         if (item)
595                             DestroyWindow(item);
596                     }
597                     winctrl_rem_shortcuts(&dp, c);
598                     winctrl_remove(&ctrls_panel, c);
599                     sfree(c->data);
600                     sfree(c);
601                 }
602             }
603             create_controls(hwnd, (char *)item.lParam);
604
605             dlg_refresh(NULL, &dp);    /* set up control values */
606  
607             SendMessage (hwnd, WM_SETREDRAW, TRUE, 0);
608             InvalidateRect (hwnd, NULL, TRUE);
609
610             SetFocus(((LPNMHDR) lParam)->hwndFrom);     /* ensure focus stays */
611             return 0;
612         }
613         break;
614       case WM_COMMAND:
615       case WM_DRAWITEM:
616       default:                         /* also handle drag list msg here */
617         /*
618          * Only process WM_COMMAND once the dialog is fully formed.
619          */
620         if (GetWindowLongPtr(hwnd, GWLP_USERDATA) == 1) {
621             ret = winctrl_handle_command(&dp, msg, wParam, lParam);
622             if (dp.ended && GetCapture() != hwnd)
623                 SaneEndDialog(hwnd, dp.endresult ? 1 : 0);
624         } else
625             ret = 0;
626         return ret;
627       case WM_HELP:
628         if (!winctrl_context_help(&dp, hwnd,
629                                  ((LPHELPINFO)lParam)->iCtrlId))
630             MessageBeep(0);
631         break;
632       case WM_CLOSE:
633         quit_help(hwnd);
634         SaneEndDialog(hwnd, 0);
635         return 0;
636
637         /* Grrr Explorer will maximize Dialogs! */
638       case WM_SIZE:
639         if (wParam == SIZE_MAXIMIZED)
640             force_normal(hwnd);
641         return 0;
642
643     }
644     return 0;
645 }
646
647 void modal_about_box(HWND hwnd)
648 {
649     EnableWindow(hwnd, 0);
650     DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
651     EnableWindow(hwnd, 1);
652     SetActiveWindow(hwnd);
653 }
654
655 void show_help(HWND hwnd)
656 {
657     launch_help(hwnd, NULL);
658 }
659
660 void defuse_showwindow(void)
661 {
662     /*
663      * Work around the fact that the app's first call to ShowWindow
664      * will ignore the default in favour of the shell-provided
665      * setting.
666      */
667     {
668         HWND hwnd;
669         hwnd = CreateDialog(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX),
670                             NULL, NullDlgProc);
671         ShowWindow(hwnd, SW_HIDE);
672         SetActiveWindow(hwnd);
673         DestroyWindow(hwnd);
674     }
675 }
676
677 int do_config(void)
678 {
679     int ret;
680
681     ctrlbox = ctrl_new_box();
682     setup_config_box(ctrlbox, FALSE, 0, 0);
683     win_setup_config_box(ctrlbox, &dp.hwnd, has_help(), FALSE, 0);
684     dp_init(&dp);
685     winctrl_init(&ctrls_base);
686     winctrl_init(&ctrls_panel);
687     dp_add_tree(&dp, &ctrls_base);
688     dp_add_tree(&dp, &ctrls_panel);
689     dp.wintitle = dupprintf("%s Configuration", appname);
690     dp.errtitle = dupprintf("%s Error", appname);
691     dp.data = conf;
692     dlg_auto_set_fixed_pitch_flag(&dp);
693     dp.shortcuts['g'] = TRUE;          /* the treeview: `Cate&gory' */
694
695     ret =
696         SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
697                   GenericMainDlgProc);
698
699     ctrl_free_box(ctrlbox);
700     winctrl_cleanup(&ctrls_panel);
701     winctrl_cleanup(&ctrls_base);
702     dp_cleanup(&dp);
703
704     return ret;
705 }
706
707 int do_reconfig(HWND hwnd, int protcfginfo)
708 {
709     Conf *backup_conf;
710     int ret, protocol;
711
712     backup_conf = conf_copy(conf);
713
714     ctrlbox = ctrl_new_box();
715     protocol = conf_get_int(conf, CONF_protocol);
716     setup_config_box(ctrlbox, TRUE, protocol, protcfginfo);
717     win_setup_config_box(ctrlbox, &dp.hwnd, has_help(), TRUE, protocol);
718     dp_init(&dp);
719     winctrl_init(&ctrls_base);
720     winctrl_init(&ctrls_panel);
721     dp_add_tree(&dp, &ctrls_base);
722     dp_add_tree(&dp, &ctrls_panel);
723     dp.wintitle = dupprintf("%s Reconfiguration", appname);
724     dp.errtitle = dupprintf("%s Error", appname);
725     dp.data = conf;
726     dlg_auto_set_fixed_pitch_flag(&dp);
727     dp.shortcuts['g'] = TRUE;          /* the treeview: `Cate&gory' */
728
729     ret = SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
730                   GenericMainDlgProc);
731
732     ctrl_free_box(ctrlbox);
733     winctrl_cleanup(&ctrls_base);
734     winctrl_cleanup(&ctrls_panel);
735     dp_cleanup(&dp);
736
737     if (!ret)
738         conf_copy_into(conf, backup_conf);
739
740     conf_free(backup_conf);
741
742     return ret;
743 }
744
745 void logevent(void *frontend, const char *string)
746 {
747     char timebuf[40];
748     struct tm tm;
749
750     log_eventlog(logctx, string);
751
752     if (nevents >= negsize) {
753         negsize += 64;
754         events = sresize(events, negsize, char *);
755     }
756
757     tm=ltime();
758     strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t", &tm);
759
760     events[nevents] = snewn(strlen(timebuf) + strlen(string) + 1, char);
761     strcpy(events[nevents], timebuf);
762     strcat(events[nevents], string);
763     if (logbox) {
764         int count;
765         SendDlgItemMessage(logbox, IDN_LIST, LB_ADDSTRING,
766                            0, (LPARAM) events[nevents]);
767         count = SendDlgItemMessage(logbox, IDN_LIST, LB_GETCOUNT, 0, 0);
768         SendDlgItemMessage(logbox, IDN_LIST, LB_SETTOPINDEX, count - 1, 0);
769     }
770     nevents++;
771 }
772
773 void showeventlog(HWND hwnd)
774 {
775     if (!logbox) {
776         logbox = CreateDialog(hinst, MAKEINTRESOURCE(IDD_LOGBOX),
777                               hwnd, LogProc);
778         ShowWindow(logbox, SW_SHOWNORMAL);
779     }
780     SetActiveWindow(logbox);
781 }
782
783 void showabout(HWND hwnd)
784 {
785     DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
786 }
787
788 int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
789                         char *keystr, char *fingerprint,
790                         void (*callback)(void *ctx, int result), void *ctx)
791 {
792     int ret;
793
794     static const char absentmsg[] =
795         "The server's host key is not cached in the registry. You\n"
796         "have no guarantee that the server is the computer you\n"
797         "think it is.\n"
798         "The server's %s key fingerprint is:\n"
799         "%s\n"
800         "If you trust this host, hit Yes to add the key to\n"
801         "%s's cache and carry on connecting.\n"
802         "If you want to carry on connecting just once, without\n"
803         "adding the key to the cache, hit No.\n"
804         "If you do not trust this host, hit Cancel to abandon the\n"
805         "connection.\n";
806
807     static const char wrongmsg[] =
808         "WARNING - POTENTIAL SECURITY BREACH!\n"
809         "\n"
810         "The server's host key does not match the one %s has\n"
811         "cached in the registry. This means that either the\n"
812         "server administrator has changed the host key, or you\n"
813         "have actually connected to another computer pretending\n"
814         "to be the server.\n"
815         "The new %s key fingerprint is:\n"
816         "%s\n"
817         "If you were expecting this change and trust the new key,\n"
818         "hit Yes to update %s's cache and continue connecting.\n"
819         "If you want to carry on connecting but without updating\n"
820         "the cache, hit No.\n"
821         "If you want to abandon the connection completely, hit\n"
822         "Cancel. Hitting Cancel is the ONLY guaranteed safe\n" "choice.\n";
823
824     static const char mbtitle[] = "%s Security Alert";
825
826     /*
827      * Verify the key against the registry.
828      */
829     ret = verify_host_key(host, port, keytype, keystr);
830
831     if (ret == 0)                      /* success - key matched OK */
832         return 1;
833     else if (ret == 2) {               /* key was different */
834         int mbret;
835         char *text = dupprintf(wrongmsg, appname, keytype, fingerprint,
836                                appname);
837         char *caption = dupprintf(mbtitle, appname);
838         mbret = message_box(text, caption,
839                             MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3,
840                             HELPCTXID(errors_hostkey_changed));
841         assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);
842         sfree(text);
843         sfree(caption);
844         if (mbret == IDYES) {
845             store_host_key(host, port, keytype, keystr);
846             return 1;
847         } else if (mbret == IDNO)
848             return 1;
849     } else if (ret == 1) {             /* key was absent */
850         int mbret;
851         char *text = dupprintf(absentmsg, keytype, fingerprint, appname);
852         char *caption = dupprintf(mbtitle, appname);
853         mbret = message_box(text, caption,
854                             MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3,
855                             HELPCTXID(errors_hostkey_absent));
856         assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);
857         sfree(text);
858         sfree(caption);
859         if (mbret == IDYES) {
860             store_host_key(host, port, keytype, keystr);
861             return 1;
862         } else if (mbret == IDNO)
863             return 1;
864     }
865     return 0;   /* abandon the connection */
866 }
867
868 /*
869  * Ask whether the selected algorithm is acceptable (since it was
870  * below the configured 'warn' threshold).
871  */
872 int askalg(void *frontend, const char *algtype, const char *algname,
873            void (*callback)(void *ctx, int result), void *ctx)
874 {
875     static const char mbtitle[] = "%s Security Alert";
876     static const char msg[] =
877         "The first %s supported by the server\n"
878         "is %.64s, which is below the configured\n"
879         "warning threshold.\n"
880         "Do you want to continue with this connection?\n";
881     char *message, *title;
882     int mbret;
883
884     message = dupprintf(msg, algtype, algname);
885     title = dupprintf(mbtitle, appname);
886     mbret = MessageBox(NULL, message, title,
887                        MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2);
888     socket_reselect_all();
889     sfree(message);
890     sfree(title);
891     if (mbret == IDYES)
892         return 1;
893     else
894         return 0;
895 }
896
897 /*
898  * Ask whether to wipe a session log file before writing to it.
899  * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
900  */
901 int askappend(void *frontend, Filename *filename,
902               void (*callback)(void *ctx, int result), void *ctx)
903 {
904     static const char msgtemplate[] =
905         "The session log file \"%.*s\" already exists.\n"
906         "You can overwrite it with a new session log,\n"
907         "append your session log to the end of it,\n"
908         "or disable session logging for this session.\n"
909         "Hit Yes to wipe the file, No to append to it,\n"
910         "or Cancel to disable logging.";
911     char *message;
912     char *mbtitle;
913     int mbret;
914
915     message = dupprintf(msgtemplate, FILENAME_MAX, filename->path);
916     mbtitle = dupprintf("%s Log to File", appname);
917
918     mbret = MessageBox(NULL, message, mbtitle,
919                        MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON3);
920
921     socket_reselect_all();
922
923     sfree(message);
924     sfree(mbtitle);
925
926     if (mbret == IDYES)
927         return 2;
928     else if (mbret == IDNO)
929         return 1;
930     else
931         return 0;
932 }
933
934 /*
935  * Warn about the obsolescent key file format.
936  * 
937  * Uniquely among these functions, this one does _not_ expect a
938  * frontend handle. This means that if PuTTY is ported to a
939  * platform which requires frontend handles, this function will be
940  * an anomaly. Fortunately, the problem it addresses will not have
941  * been present on that platform, so it can plausibly be
942  * implemented as an empty function.
943  */
944 void old_keyfile_warning(void)
945 {
946     static const char mbtitle[] = "%s Key File Warning";
947     static const char message[] =
948         "You are loading an SSH-2 private key which has an\n"
949         "old version of the file format. This means your key\n"
950         "file is not fully tamperproof. Future versions of\n"
951         "%s may stop supporting this private key format,\n"
952         "so we recommend you convert your key to the new\n"
953         "format.\n"
954         "\n"
955         "You can perform this conversion by loading the key\n"
956         "into PuTTYgen and then saving it again.";
957
958     char *msg, *title;
959     msg = dupprintf(message, appname);
960     title = dupprintf(mbtitle, appname);
961
962     MessageBox(NULL, msg, title, MB_OK);
963
964     socket_reselect_all();
965
966     sfree(msg);
967     sfree(title);
968 }