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