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