]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windows/windlg.c
Sprinkle some header comments in various files in an attempt to explain what
[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);
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     dp_init(&dp);
690     winctrl_init(&ctrls_base);
691     winctrl_init(&ctrls_panel);
692     dp_add_tree(&dp, &ctrls_base);
693     dp_add_tree(&dp, &ctrls_panel);
694     dp.wintitle = dupprintf("%s Reconfiguration", appname);
695     dp.errtitle = dupprintf("%s Error", appname);
696     dp.data = &cfg;
697     dp.shortcuts['g'] = TRUE;          /* the treeview: `Cate&gory' */
698
699     ret = SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
700                   GenericMainDlgProc);
701
702     ctrl_free_box(ctrlbox);
703     winctrl_cleanup(&ctrls_base);
704     winctrl_cleanup(&ctrls_panel);
705     dp_cleanup(&dp);
706
707     if (!ret)
708         cfg = backup_cfg;              /* structure copy */
709
710     return ret;
711 }
712
713 void logevent(void *frontend, const char *string)
714 {
715     char timebuf[40];
716     struct tm tm;
717
718     log_eventlog(logctx, string);
719
720     if (nevents >= negsize) {
721         negsize += 64;
722         events = sresize(events, negsize, char *);
723     }
724
725     tm=ltime();
726     strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t", &tm);
727
728     events[nevents] = snewn(strlen(timebuf) + strlen(string) + 1, char);
729     strcpy(events[nevents], timebuf);
730     strcat(events[nevents], string);
731     if (logbox) {
732         int count;
733         SendDlgItemMessage(logbox, IDN_LIST, LB_ADDSTRING,
734                            0, (LPARAM) events[nevents]);
735         count = SendDlgItemMessage(logbox, IDN_LIST, LB_GETCOUNT, 0, 0);
736         SendDlgItemMessage(logbox, IDN_LIST, LB_SETTOPINDEX, count - 1, 0);
737     }
738     nevents++;
739 }
740
741 void showeventlog(HWND hwnd)
742 {
743     if (!logbox) {
744         logbox = CreateDialog(hinst, MAKEINTRESOURCE(IDD_LOGBOX),
745                               hwnd, LogProc);
746         ShowWindow(logbox, SW_SHOWNORMAL);
747     }
748     SetActiveWindow(logbox);
749 }
750
751 void showabout(HWND hwnd)
752 {
753     DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
754 }
755
756 int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
757                         char *keystr, char *fingerprint,
758                         void (*callback)(void *ctx, int result), void *ctx)
759 {
760     int ret;
761
762     static const char absentmsg[] =
763         "The server's host key is not cached in the registry. You\n"
764         "have no guarantee that the server is the computer you\n"
765         "think it is.\n"
766         "The server's %s key fingerprint is:\n"
767         "%s\n"
768         "If you trust this host, hit Yes to add the key to\n"
769         "%s's cache and carry on connecting.\n"
770         "If you want to carry on connecting just once, without\n"
771         "adding the key to the cache, hit No.\n"
772         "If you do not trust this host, hit Cancel to abandon the\n"
773         "connection.\n";
774
775     static const char wrongmsg[] =
776         "WARNING - POTENTIAL SECURITY BREACH!\n"
777         "\n"
778         "The server's host key does not match the one %s has\n"
779         "cached in the registry. This means that either the\n"
780         "server administrator has changed the host key, or you\n"
781         "have actually connected to another computer pretending\n"
782         "to be the server.\n"
783         "The new %s key fingerprint is:\n"
784         "%s\n"
785         "If you were expecting this change and trust the new key,\n"
786         "hit Yes to update %s's cache and continue connecting.\n"
787         "If you want to carry on connecting but without updating\n"
788         "the cache, hit No.\n"
789         "If you want to abandon the connection completely, hit\n"
790         "Cancel. Hitting Cancel is the ONLY guaranteed safe\n" "choice.\n";
791
792     static const char mbtitle[] = "%s Security Alert";
793
794     /*
795      * Verify the key against the registry.
796      */
797     ret = verify_host_key(host, port, keytype, keystr);
798
799     if (ret == 0)                      /* success - key matched OK */
800         return 1;
801     if (ret == 2) {                    /* key was different */
802         int mbret;
803         char *text = dupprintf(wrongmsg, appname, keytype, fingerprint,
804                                appname);
805         char *caption = dupprintf(mbtitle, appname);
806         mbret = message_box(text, caption,
807                             MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3,
808                             HELPCTXID(errors_hostkey_changed));
809         assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);
810         sfree(text);
811         sfree(caption);
812         if (mbret == IDYES) {
813             store_host_key(host, port, keytype, keystr);
814             return 1;
815         } else if (mbret == IDNO)
816             return 1;
817         return 0;
818     }
819     if (ret == 1) {                    /* key was absent */
820         int mbret;
821         char *text = dupprintf(absentmsg, keytype, fingerprint, appname);
822         char *caption = dupprintf(mbtitle, appname);
823         mbret = message_box(text, caption,
824                             MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3,
825                             HELPCTXID(errors_hostkey_absent));
826         assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);
827         sfree(text);
828         sfree(caption);
829         if (mbret == IDYES) {
830             store_host_key(host, port, keytype, keystr);
831             return 1;
832         } else if (mbret == IDNO)
833             return 1;
834         return 0;
835     }
836 }
837
838 /*
839  * Ask whether the selected algorithm is acceptable (since it was
840  * below the configured 'warn' threshold).
841  */
842 int askalg(void *frontend, const char *algtype, const char *algname,
843            void (*callback)(void *ctx, int result), void *ctx)
844 {
845     static const char mbtitle[] = "%s Security Alert";
846     static const char msg[] =
847         "The first %s supported by the server\n"
848         "is %.64s, which is below the configured\n"
849         "warning threshold.\n"
850         "Do you want to continue with this connection?\n";
851     char *message, *title;
852     int mbret;
853
854     message = dupprintf(msg, algtype, algname);
855     title = dupprintf(mbtitle, appname);
856     mbret = MessageBox(NULL, message, title,
857                        MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2);
858     sfree(message);
859     sfree(title);
860     if (mbret == IDYES)
861         return 1;
862     else
863         return 0;
864 }
865
866 /*
867  * Ask whether to wipe a session log file before writing to it.
868  * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
869  */
870 int askappend(void *frontend, Filename filename,
871               void (*callback)(void *ctx, int result), void *ctx)
872 {
873     static const char msgtemplate[] =
874         "The session log file \"%.*s\" already exists.\n"
875         "You can overwrite it with a new session log,\n"
876         "append your session log to the end of it,\n"
877         "or disable session logging for this session.\n"
878         "Hit Yes to wipe the file, No to append to it,\n"
879         "or Cancel to disable logging.";
880     char *message;
881     char *mbtitle;
882     int mbret;
883
884     message = dupprintf(msgtemplate, FILENAME_MAX, filename.path);
885     mbtitle = dupprintf("%s Log to File", appname);
886
887     mbret = MessageBox(NULL, message, mbtitle,
888                        MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON3);
889
890     sfree(message);
891     sfree(mbtitle);
892
893     if (mbret == IDYES)
894         return 2;
895     else if (mbret == IDNO)
896         return 1;
897     else
898         return 0;
899 }
900
901 /*
902  * Warn about the obsolescent key file format.
903  * 
904  * Uniquely among these functions, this one does _not_ expect a
905  * frontend handle. This means that if PuTTY is ported to a
906  * platform which requires frontend handles, this function will be
907  * an anomaly. Fortunately, the problem it addresses will not have
908  * been present on that platform, so it can plausibly be
909  * implemented as an empty function.
910  */
911 void old_keyfile_warning(void)
912 {
913     static const char mbtitle[] = "%s Key File Warning";
914     static const char message[] =
915         "You are loading an SSH-2 private key which has an\n"
916         "old version of the file format. This means your key\n"
917         "file is not fully tamperproof. Future versions of\n"
918         "%s may stop supporting this private key format,\n"
919         "so we recommend you convert your key to the new\n"
920         "format.\n"
921         "\n"
922         "You can perform this conversion by loading the key\n"
923         "into PuTTYgen and then saving it again.";
924
925     char *msg, *title;
926     msg = dupprintf(message, appname);
927     title = dupprintf(mbtitle, appname);
928
929     MessageBox(NULL, msg, title, MB_OK);
930
931     sfree(msg);
932     sfree(title);
933 }