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