]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windows/windlg.c
dba4d5fa903613ffcaef7d46e6a4faaf2a1b41d8
[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 #ifdef LONG_PTR
244     wc.cbWndExtra = DLGWINDOWEXTRA + 2*sizeof(LONG_PTR);
245 #else
246     wc.cbWndExtra = DLGWINDOWEXTRA + 8;
247 #endif
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 }