]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windows/windlg.c
91bf75657c0396c30c8736d4c138efc533c425db
[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 #define PRINTER_DISABLED_STRING "None (printing disabled)"
46
47 void force_normal(HWND hwnd)
48 {
49     static int recurse = 0;
50
51     WINDOWPLACEMENT wp;
52
53     if (recurse)
54         return;
55     recurse = 1;
56
57     wp.length = sizeof(wp);
58     if (GetWindowPlacement(hwnd, &wp) && wp.showCmd == SW_SHOWMAXIMIZED) {
59         wp.showCmd = SW_SHOWNORMAL;
60         SetWindowPlacement(hwnd, &wp);
61     }
62     recurse = 0;
63 }
64
65 static int CALLBACK LogProc(HWND hwnd, UINT msg,
66                             WPARAM wParam, LPARAM lParam)
67 {
68     int i;
69
70     switch (msg) {
71       case WM_INITDIALOG:
72         {
73             char *str = dupprintf("%s Event Log", appname);
74             SetWindowText(hwnd, str);
75             sfree(str);
76         }
77         {
78             static int tabs[4] = { 78, 108 };
79             SendDlgItemMessage(hwnd, IDN_LIST, LB_SETTABSTOPS, 2,
80                                (LPARAM) tabs);
81         }
82         for (i = 0; i < nevents; i++)
83             SendDlgItemMessage(hwnd, IDN_LIST, LB_ADDSTRING,
84                                0, (LPARAM) events[i]);
85         return 1;
86       case WM_COMMAND:
87         switch (LOWORD(wParam)) {
88           case IDOK:
89           case IDCANCEL:
90             logbox = NULL;
91             SetActiveWindow(GetParent(hwnd));
92             DestroyWindow(hwnd);
93             return 0;
94           case IDN_COPY:
95             if (HIWORD(wParam) == BN_CLICKED ||
96                 HIWORD(wParam) == BN_DOUBLECLICKED) {
97                 int selcount;
98                 int *selitems;
99                 selcount = SendDlgItemMessage(hwnd, IDN_LIST,
100                                               LB_GETSELCOUNT, 0, 0);
101                 if (selcount == 0) {   /* don't even try to copy zero items */
102                     MessageBeep(0);
103                     break;
104                 }
105
106                 selitems = snewn(selcount, int);
107                 if (selitems) {
108                     int count = SendDlgItemMessage(hwnd, IDN_LIST,
109                                                    LB_GETSELITEMS,
110                                                    selcount,
111                                                    (LPARAM) selitems);
112                     int i;
113                     int size;
114                     char *clipdata;
115                     static unsigned char sel_nl[] = SEL_NL;
116
117                     if (count == 0) {  /* can't copy zero stuff */
118                         MessageBeep(0);
119                         break;
120                     }
121
122                     size = 0;
123                     for (i = 0; i < count; i++)
124                         size +=
125                             strlen(events[selitems[i]]) + sizeof(sel_nl);
126
127                     clipdata = snewn(size, char);
128                     if (clipdata) {
129                         char *p = clipdata;
130                         for (i = 0; i < count; i++) {
131                             char *q = events[selitems[i]];
132                             int qlen = strlen(q);
133                             memcpy(p, q, qlen);
134                             p += qlen;
135                             memcpy(p, sel_nl, sizeof(sel_nl));
136                             p += sizeof(sel_nl);
137                         }
138                         write_aclip(NULL, clipdata, size, TRUE);
139                         sfree(clipdata);
140                     }
141                     sfree(selitems);
142
143                     for (i = 0; i < nevents; i++)
144                         SendDlgItemMessage(hwnd, IDN_LIST, LB_SETSEL,
145                                            FALSE, i);
146                 }
147             }
148             return 0;
149         }
150         return 0;
151       case WM_CLOSE:
152         logbox = NULL;
153         SetActiveWindow(GetParent(hwnd));
154         DestroyWindow(hwnd);
155         return 0;
156     }
157     return 0;
158 }
159
160 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
161                                 WPARAM wParam, LPARAM lParam)
162 {
163     switch (msg) {
164       case WM_INITDIALOG:
165         {
166             char *str = dupprintf("%s Licence", appname);
167             SetWindowText(hwnd, str);
168             sfree(str);
169         }
170         return 1;
171       case WM_COMMAND:
172         switch (LOWORD(wParam)) {
173           case IDOK:
174           case IDCANCEL:
175             EndDialog(hwnd, 1);
176             return 0;
177         }
178         return 0;
179       case WM_CLOSE:
180         EndDialog(hwnd, 1);
181         return 0;
182     }
183     return 0;
184 }
185
186 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
187                               WPARAM wParam, LPARAM lParam)
188 {
189     char *str;
190
191     switch (msg) {
192       case WM_INITDIALOG:
193         str = dupprintf("About %s", appname);
194         SetWindowText(hwnd, str);
195         sfree(str);
196         SetDlgItemText(hwnd, IDA_TEXT1, appname);
197         SetDlgItemText(hwnd, IDA_VERSION, ver);
198         return 1;
199       case WM_COMMAND:
200         switch (LOWORD(wParam)) {
201           case IDOK:
202           case IDCANCEL:
203             EndDialog(hwnd, TRUE);
204             return 0;
205           case IDA_LICENCE:
206             EnableWindow(hwnd, 0);
207             DialogBox(hinst, MAKEINTRESOURCE(IDD_LICENCEBOX),
208                       hwnd, LicenceProc);
209             EnableWindow(hwnd, 1);
210             SetActiveWindow(hwnd);
211             return 0;
212
213           case IDA_WEB:
214             /* Load web browser */
215             ShellExecute(hwnd, "open",
216                          "http://www.chiark.greenend.org.uk/~sgtatham/putty/",
217                          0, 0, SW_SHOWDEFAULT);
218             return 0;
219         }
220         return 0;
221       case WM_CLOSE:
222         EndDialog(hwnd, TRUE);
223         return 0;
224     }
225     return 0;
226 }
227
228 static int SaneDialogBox(HINSTANCE hinst,
229                          LPCTSTR tmpl,
230                          HWND hwndparent,
231                          DLGPROC lpDialogFunc)
232 {
233     WNDCLASS wc;
234     HWND hwnd;
235     MSG msg;
236     int flags;
237     int ret;
238     int gm;
239
240     wc.style = CS_DBLCLKS | CS_SAVEBITS | CS_BYTEALIGNWINDOW;
241     wc.lpfnWndProc = DefDlgProc;
242     wc.cbClsExtra = 0;
243     wc.cbWndExtra = DLGWINDOWEXTRA + 8;
244     wc.hInstance = hinst;
245     wc.hIcon = NULL;
246     wc.hCursor = LoadCursor(NULL, IDC_ARROW);
247     wc.hbrBackground = (HBRUSH) (COLOR_BACKGROUND +1);
248     wc.lpszMenuName = NULL;
249     wc.lpszClassName = "PuTTYConfigBox";
250     RegisterClass(&wc);
251
252     hwnd = CreateDialog(hinst, tmpl, hwndparent, lpDialogFunc);
253
254     SetWindowLong(hwnd, BOXFLAGS, 0); /* flags */
255     SetWindowLong(hwnd, BOXRESULT, 0); /* result from SaneEndDialog */
256
257     while ((gm=GetMessage(&msg, NULL, 0, 0)) > 0) {
258         flags=GetWindowLong(hwnd, BOXFLAGS);
259         if (!(flags & DF_END) && !IsDialogMessage(hwnd, &msg))
260             DispatchMessage(&msg);
261         if (flags & DF_END)
262             break;
263     }
264
265     if (gm == 0)
266         PostQuitMessage(msg.wParam); /* We got a WM_QUIT, pass it on */
267
268     ret=GetWindowLong(hwnd, BOXRESULT);
269     DestroyWindow(hwnd);
270     return ret;
271 }
272
273 static void SaneEndDialog(HWND hwnd, int ret)
274 {
275     SetWindowLong(hwnd, BOXRESULT, ret);
276     SetWindowLong(hwnd, BOXFLAGS, DF_END);
277 }
278
279 /*
280  * Null dialog procedure.
281  */
282 static int CALLBACK NullDlgProc(HWND hwnd, UINT msg,
283                                 WPARAM wParam, LPARAM lParam)
284 {
285     return 0;
286 }
287
288 enum {
289     IDCX_ABOUT = IDC_ABOUT,
290     IDCX_TVSTATIC,
291     IDCX_TREEVIEW,
292     IDCX_STDBASE,
293     IDCX_PANELBASE = IDCX_STDBASE + 32
294 };
295
296 struct treeview_faff {
297     HWND treeview;
298     HTREEITEM lastat[4];
299 };
300
301 static HTREEITEM treeview_insert(struct treeview_faff *faff,
302                                  int level, char *text, char *path)
303 {
304     TVINSERTSTRUCT ins;
305     int i;
306     HTREEITEM newitem;
307     ins.hParent = (level > 0 ? faff->lastat[level - 1] : TVI_ROOT);
308     ins.hInsertAfter = faff->lastat[level];
309 #if _WIN32_IE >= 0x0400 && defined NONAMELESSUNION
310 #define INSITEM DUMMYUNIONNAME.item
311 #else
312 #define INSITEM item
313 #endif
314     ins.INSITEM.mask = TVIF_TEXT | TVIF_PARAM;
315     ins.INSITEM.pszText = text;
316     ins.INSITEM.cchTextMax = strlen(text)+1;
317     ins.INSITEM.lParam = (LPARAM)path;
318     newitem = TreeView_InsertItem(faff->treeview, &ins);
319     if (level > 0)
320         TreeView_Expand(faff->treeview, faff->lastat[level - 1],
321                         TVE_EXPAND);
322     faff->lastat[level] = newitem;
323     for (i = level + 1; i < 4; i++)
324         faff->lastat[i] = NULL;
325     return newitem;
326 }
327
328 /*
329  * Create the panelfuls of controls in the configuration box.
330  */
331 static void create_controls(HWND hwnd, char *path)
332 {
333     struct ctlpos cp;
334     int index;
335     int base_id;
336     struct winctrls *wc;
337
338     if (!path[0]) {
339         /*
340          * Here we must create the basic standard controls.
341          */
342         ctlposinit(&cp, hwnd, 3, 3, 235);
343         wc = &ctrls_base;
344         base_id = IDCX_STDBASE;
345     } else {
346         /*
347          * Otherwise, we're creating the controls for a particular
348          * panel.
349          */
350         ctlposinit(&cp, hwnd, 100, 3, 13);
351         wc = &ctrls_panel;
352         base_id = IDCX_PANELBASE;
353     }
354
355     for (index=-1; (index = ctrl_find_path(ctrlbox, path, index)) >= 0 ;) {
356         struct controlset *s = ctrlbox->ctrlsets[index];
357         winctrl_layout(&dp, wc, &cp, s, &base_id);
358     }
359 }
360
361 /*
362  * This function is the configuration box.
363  * (Being a dialog procedure, in general it returns 0 if the default
364  * dialog processing should be performed, and 1 if it should not.)
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, 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     ret =
663         SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
664                   GenericMainDlgProc);
665
666     ctrl_free_box(ctrlbox);
667     winctrl_cleanup(&ctrls_panel);
668     winctrl_cleanup(&ctrls_base);
669     dp_cleanup(&dp);
670
671     return ret;
672 }
673
674 int do_reconfig(HWND hwnd, int protcfginfo)
675 {
676     Config backup_cfg;
677     int ret;
678
679     backup_cfg = cfg;                  /* structure copy */
680
681     ctrlbox = ctrl_new_box();
682     setup_config_box(ctrlbox, TRUE, cfg.protocol, protcfginfo);
683     win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), TRUE);
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 Reconfiguration", appname);
690     dp.errtitle = dupprintf("%s Error", appname);
691     dp.data = &cfg;
692     dp.shortcuts['g'] = TRUE;          /* the treeview: `Cate&gory' */
693
694     ret = SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
695                   GenericMainDlgProc);
696
697     ctrl_free_box(ctrlbox);
698     winctrl_cleanup(&ctrls_base);
699     winctrl_cleanup(&ctrls_panel);
700     dp_cleanup(&dp);
701
702     if (!ret)
703         cfg = backup_cfg;              /* structure copy */
704
705     return ret;
706 }
707
708 void logevent(void *frontend, const char *string)
709 {
710     char timebuf[40];
711     struct tm tm;
712
713     log_eventlog(logctx, string);
714
715     if (nevents >= negsize) {
716         negsize += 64;
717         events = sresize(events, negsize, char *);
718     }
719
720     tm=ltime();
721     strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t", &tm);
722
723     events[nevents] = snewn(strlen(timebuf) + strlen(string) + 1, char);
724     strcpy(events[nevents], timebuf);
725     strcat(events[nevents], string);
726     if (logbox) {
727         int count;
728         SendDlgItemMessage(logbox, IDN_LIST, LB_ADDSTRING,
729                            0, (LPARAM) events[nevents]);
730         count = SendDlgItemMessage(logbox, IDN_LIST, LB_GETCOUNT, 0, 0);
731         SendDlgItemMessage(logbox, IDN_LIST, LB_SETTOPINDEX, count - 1, 0);
732     }
733     nevents++;
734 }
735
736 void showeventlog(HWND hwnd)
737 {
738     if (!logbox) {
739         logbox = CreateDialog(hinst, MAKEINTRESOURCE(IDD_LOGBOX),
740                               hwnd, LogProc);
741         ShowWindow(logbox, SW_SHOWNORMAL);
742     }
743     SetActiveWindow(logbox);
744 }
745
746 void showabout(HWND hwnd)
747 {
748     DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
749 }
750
751 int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
752                         char *keystr, char *fingerprint,
753                         void (*callback)(void *ctx, int result), void *ctx)
754 {
755     int ret;
756
757     static const char absentmsg[] =
758         "The server's host key is not cached in the registry. You\n"
759         "have no guarantee that the server is the computer you\n"
760         "think it is.\n"
761         "The server's %s key fingerprint is:\n"
762         "%s\n"
763         "If you trust this host, hit Yes to add the key to\n"
764         "%s's cache and carry on connecting.\n"
765         "If you want to carry on connecting just once, without\n"
766         "adding the key to the cache, hit No.\n"
767         "If you do not trust this host, hit Cancel to abandon the\n"
768         "connection.\n";
769
770     static const char wrongmsg[] =
771         "WARNING - POTENTIAL SECURITY BREACH!\n"
772         "\n"
773         "The server's host key does not match the one %s has\n"
774         "cached in the registry. This means that either the\n"
775         "server administrator has changed the host key, or you\n"
776         "have actually connected to another computer pretending\n"
777         "to be the server.\n"
778         "The new %s key fingerprint is:\n"
779         "%s\n"
780         "If you were expecting this change and trust the new key,\n"
781         "hit Yes to update %s's cache and continue connecting.\n"
782         "If you want to carry on connecting but without updating\n"
783         "the cache, hit No.\n"
784         "If you want to abandon the connection completely, hit\n"
785         "Cancel. Hitting Cancel is the ONLY guaranteed safe\n" "choice.\n";
786
787     static const char mbtitle[] = "%s Security Alert";
788
789     /*
790      * Verify the key against the registry.
791      */
792     ret = verify_host_key(host, port, keytype, keystr);
793
794     if (ret == 0)                      /* success - key matched OK */
795         return 1;
796     if (ret == 2) {                    /* key was different */
797         int mbret;
798         char *text = dupprintf(wrongmsg, appname, keytype, fingerprint,
799                                appname);
800         char *caption = dupprintf(mbtitle, appname);
801         mbret = message_box(text, caption,
802                             MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3,
803                             HELPCTXID(errors_hostkey_changed));
804         assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);
805         sfree(text);
806         sfree(caption);
807         if (mbret == IDYES) {
808             store_host_key(host, port, keytype, keystr);
809             return 1;
810         } else if (mbret == IDNO)
811             return 1;
812         return 0;
813     }
814     if (ret == 1) {                    /* key was absent */
815         int mbret;
816         char *text = dupprintf(absentmsg, keytype, fingerprint, appname);
817         char *caption = dupprintf(mbtitle, appname);
818         mbret = message_box(text, caption,
819                             MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3,
820                             HELPCTXID(errors_hostkey_absent));
821         assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);
822         sfree(text);
823         sfree(caption);
824         if (mbret == IDYES) {
825             store_host_key(host, port, keytype, keystr);
826             return 1;
827         } else if (mbret == IDNO)
828             return 1;
829         return 0;
830     }
831 }
832
833 /*
834  * Ask whether the selected algorithm is acceptable (since it was
835  * below the configured 'warn' threshold).
836  */
837 int askalg(void *frontend, const char *algtype, const char *algname,
838            void (*callback)(void *ctx, int result), void *ctx)
839 {
840     static const char mbtitle[] = "%s Security Alert";
841     static const char msg[] =
842         "The first %s supported by the server\n"
843         "is %.64s, which is below the configured\n"
844         "warning threshold.\n"
845         "Do you want to continue with this connection?\n";
846     char *message, *title;
847     int mbret;
848
849     message = dupprintf(msg, algtype, algname);
850     title = dupprintf(mbtitle, appname);
851     mbret = MessageBox(NULL, message, title,
852                        MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2);
853     sfree(message);
854     sfree(title);
855     if (mbret == IDYES)
856         return 1;
857     else
858         return 0;
859 }
860
861 /*
862  * Ask whether to wipe a session log file before writing to it.
863  * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
864  */
865 int askappend(void *frontend, Filename filename,
866               void (*callback)(void *ctx, int result), void *ctx)
867 {
868     static const char msgtemplate[] =
869         "The session log file \"%.*s\" already exists.\n"
870         "You can overwrite it with a new session log,\n"
871         "append your session log to the end of it,\n"
872         "or disable session logging for this session.\n"
873         "Hit Yes to wipe the file, No to append to it,\n"
874         "or Cancel to disable logging.";
875     char *message;
876     char *mbtitle;
877     int mbret;
878
879     message = dupprintf(msgtemplate, FILENAME_MAX, filename.path);
880     mbtitle = dupprintf("%s Log to File", appname);
881
882     mbret = MessageBox(NULL, message, mbtitle,
883                        MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON3);
884
885     sfree(message);
886     sfree(mbtitle);
887
888     if (mbret == IDYES)
889         return 2;
890     else if (mbret == IDNO)
891         return 1;
892     else
893         return 0;
894 }
895
896 /*
897  * Warn about the obsolescent key file format.
898  * 
899  * Uniquely among these functions, this one does _not_ expect a
900  * frontend handle. This means that if PuTTY is ported to a
901  * platform which requires frontend handles, this function will be
902  * an anomaly. Fortunately, the problem it addresses will not have
903  * been present on that platform, so it can plausibly be
904  * implemented as an empty function.
905  */
906 void old_keyfile_warning(void)
907 {
908     static const char mbtitle[] = "%s Key File Warning";
909     static const char message[] =
910         "You are loading an SSH-2 private key which has an\n"
911         "old version of the file format. This means your key\n"
912         "file is not fully tamperproof. Future versions of\n"
913         "%s may stop supporting this private key format,\n"
914         "so we recommend you convert your key to the new\n"
915         "format.\n"
916         "\n"
917         "You can perform this conversion by loading the key\n"
918         "into PuTTYgen and then saving it again.";
919
920     char *msg, *title;
921     msg = dupprintf(message, appname);
922     title = dupprintf(mbtitle, appname);
923
924     MessageBox(NULL, msg, title, MB_OK);
925
926     sfree(msg);
927     sfree(title);
928 }