]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windlg.c
Fiddly things involving pruning .svn directories, not mentioning
[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 #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 static int requested_help;
44
45 extern Config cfg;                     /* defined in window.c */
46
47 struct sesslist sesslist;              /* exported to 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             EndDialog(hwnd, 1);
179             return 0;
180         }
181         return 0;
182       case WM_CLOSE:
183         EndDialog(hwnd, 1);
184         return 0;
185     }
186     return 0;
187 }
188
189 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
190                               WPARAM wParam, LPARAM lParam)
191 {
192     char *str;
193
194     switch (msg) {
195       case WM_INITDIALOG:
196         str = dupprintf("About %s", appname);
197         SetWindowText(hwnd, str);
198         sfree(str);
199         SetDlgItemText(hwnd, IDA_TEXT1, appname);
200         SetDlgItemText(hwnd, IDA_VERSION, ver);
201         return 1;
202       case WM_COMMAND:
203         switch (LOWORD(wParam)) {
204           case IDOK:
205           case IDCANCEL:
206             EndDialog(hwnd, TRUE);
207             return 0;
208           case IDA_LICENCE:
209             EnableWindow(hwnd, 0);
210             DialogBox(hinst, MAKEINTRESOURCE(IDD_LICENCEBOX),
211                       NULL, LicenceProc);
212             EnableWindow(hwnd, 1);
213             SetActiveWindow(hwnd);
214             return 0;
215
216           case IDA_WEB:
217             /* Load web browser */
218             ShellExecute(hwnd, "open",
219                          "http://www.chiark.greenend.org.uk/~sgtatham/putty/",
220                          0, 0, SW_SHOWDEFAULT);
221             return 0;
222         }
223         return 0;
224       case WM_CLOSE:
225         EndDialog(hwnd, TRUE);
226         return 0;
227     }
228     return 0;
229 }
230
231 /*
232  * Null dialog procedure.
233  */
234 static int CALLBACK NullDlgProc(HWND hwnd, UINT msg,
235                                 WPARAM wParam, LPARAM lParam)
236 {
237     return 0;
238 }
239
240 enum {
241     IDCX_ABOUT = IDC_ABOUT,
242     IDCX_TVSTATIC,
243     IDCX_TREEVIEW,
244     IDCX_STDBASE,
245     IDCX_PANELBASE = IDCX_STDBASE + 32
246 };
247
248 struct treeview_faff {
249     HWND treeview;
250     HTREEITEM lastat[4];
251 };
252
253 static HTREEITEM treeview_insert(struct treeview_faff *faff,
254                                  int level, char *text, char *path)
255 {
256     TVINSERTSTRUCT ins;
257     int i;
258     HTREEITEM newitem;
259     ins.hParent = (level > 0 ? faff->lastat[level - 1] : TVI_ROOT);
260     ins.hInsertAfter = faff->lastat[level];
261 #if _WIN32_IE >= 0x0400 && defined NONAMELESSUNION
262 #define INSITEM DUMMYUNIONNAME.item
263 #else
264 #define INSITEM item
265 #endif
266     ins.INSITEM.mask = TVIF_TEXT | TVIF_PARAM;
267     ins.INSITEM.pszText = text;
268     ins.INSITEM.cchTextMax = strlen(text)+1;
269     ins.INSITEM.lParam = (LPARAM)path;
270     newitem = TreeView_InsertItem(faff->treeview, &ins);
271     if (level > 0)
272         TreeView_Expand(faff->treeview, faff->lastat[level - 1],
273                         TVE_EXPAND);
274     faff->lastat[level] = newitem;
275     for (i = level + 1; i < 4; i++)
276         faff->lastat[i] = NULL;
277     return newitem;
278 }
279
280 /*
281  * Create the panelfuls of controls in the configuration box.
282  */
283 static void create_controls(HWND hwnd, char *path)
284 {
285     struct ctlpos cp;
286     int index;
287     int base_id;
288     struct winctrls *wc;
289
290     if (!path[0]) {
291         /*
292          * Here we must create the basic standard controls.
293          */
294         ctlposinit(&cp, hwnd, 3, 3, 235);
295         wc = &ctrls_base;
296         base_id = IDCX_STDBASE;
297     } else {
298         /*
299          * Otherwise, we're creating the controls for a particular
300          * panel.
301          */
302         ctlposinit(&cp, hwnd, 80, 3, 13);
303         wc = &ctrls_panel;
304         base_id = IDCX_PANELBASE;
305     }
306
307     for (index=-1; (index = ctrl_find_path(ctrlbox, path, index)) >= 0 ;) {
308         struct controlset *s = ctrlbox->ctrlsets[index];
309         winctrl_layout(&dp, wc, &cp, s, &base_id);
310     }
311 }
312
313 /*
314  * This function is the configuration box.
315  */
316 static int CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
317                                        WPARAM wParam, LPARAM lParam)
318 {
319     HWND hw, treeview;
320     struct treeview_faff tvfaff;
321     int ret;
322
323     switch (msg) {
324       case WM_INITDIALOG:
325         dp.hwnd = hwnd;
326         create_controls(hwnd, "");     /* Open and Cancel buttons etc */
327         SetWindowText(hwnd, dp.wintitle);
328         SetWindowLong(hwnd, GWL_USERDATA, 0);
329         if (help_path)
330             SetWindowLong(hwnd, GWL_EXSTYLE,
331                           GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
332         else {
333             HWND item = GetDlgItem(hwnd, IDC_HELPBTN);
334             if (item)
335                 DestroyWindow(item);
336         }
337         requested_help = FALSE;
338         SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
339                     (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(IDI_CFGICON)));
340         /*
341          * Centre the window.
342          */
343         {                              /* centre the window */
344             RECT rs, rd;
345
346             hw = GetDesktopWindow();
347             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
348                 MoveWindow(hwnd,
349                            (rs.right + rs.left + rd.left - rd.right) / 2,
350                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
351                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
352         }
353
354         /*
355          * Create the tree view.
356          */
357         {
358             RECT r;
359             WPARAM font;
360             HWND tvstatic;
361
362             r.left = 3;
363             r.right = r.left + 75;
364             r.top = 3;
365             r.bottom = r.top + 10;
366             MapDialogRect(hwnd, &r);
367             tvstatic = CreateWindowEx(0, "STATIC", "Cate&gory:",
368                                       WS_CHILD | WS_VISIBLE,
369                                       r.left, r.top,
370                                       r.right - r.left, r.bottom - r.top,
371                                       hwnd, (HMENU) IDCX_TVSTATIC, hinst,
372                                       NULL);
373             font = SendMessage(hwnd, WM_GETFONT, 0, 0);
374             SendMessage(tvstatic, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
375
376             r.left = 3;
377             r.right = r.left + 75;
378             r.top = 13;
379             r.bottom = r.top + 219;
380             MapDialogRect(hwnd, &r);
381             treeview = CreateWindowEx(WS_EX_CLIENTEDGE, WC_TREEVIEW, "",
382                                       WS_CHILD | WS_VISIBLE |
383                                       WS_TABSTOP | TVS_HASLINES |
384                                       TVS_DISABLEDRAGDROP | TVS_HASBUTTONS
385                                       | TVS_LINESATROOT |
386                                       TVS_SHOWSELALWAYS, r.left, r.top,
387                                       r.right - r.left, r.bottom - r.top,
388                                       hwnd, (HMENU) IDCX_TREEVIEW, hinst,
389                                       NULL);
390             font = SendMessage(hwnd, WM_GETFONT, 0, 0);
391             SendMessage(treeview, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
392             tvfaff.treeview = treeview;
393             memset(tvfaff.lastat, 0, sizeof(tvfaff.lastat));
394         }
395
396         /*
397          * Set up the tree view contents.
398          */
399         {
400             HTREEITEM hfirst = NULL;
401             int i;
402             char *path = NULL;
403
404             for (i = 0; i < ctrlbox->nctrlsets; i++) {
405                 struct controlset *s = ctrlbox->ctrlsets[i];
406                 HTREEITEM item;
407                 int j;
408                 char *c;
409
410                 if (!s->pathname[0])
411                     continue;
412                 j = path ? ctrl_path_compare(s->pathname, path) : 0;
413                 if (j == INT_MAX)
414                     continue;          /* same path, nothing to add to tree */
415
416                 /*
417                  * We expect never to find an implicit path
418                  * component. For example, we expect never to see
419                  * A/B/C followed by A/D/E, because that would
420                  * _implicitly_ create A/D. All our path prefixes
421                  * are expected to contain actual controls and be
422                  * selectable in the treeview; so we would expect
423                  * to see A/D _explicitly_ before encountering
424                  * A/D/E.
425                  */
426                 assert(j == ctrl_path_elements(s->pathname) - 1);
427
428                 c = strrchr(s->pathname, '/');
429                 if (!c)
430                         c = s->pathname;
431                 else
432                         c++;
433
434                 item = treeview_insert(&tvfaff, j, c, s->pathname);
435                 if (!hfirst)
436                     hfirst = item;
437
438                 path = s->pathname;
439             }
440
441             /*
442              * Put the treeview selection on to the Session panel.
443              * This should also cause creation of the relevant
444              * controls.
445              */
446             TreeView_SelectItem(treeview, hfirst);
447         }
448
449         /*
450          * Set focus into the first available control.
451          */
452         {
453             int i;
454             struct winctrl *c;
455
456             for (i = 0; (c = winctrl_findbyindex(&ctrls_panel, i)) != NULL;
457                  i++) {
458                 if (c->ctrl) {
459                     dlg_set_focus(c->ctrl, &dp);
460                     break;
461                 }
462             }
463         }
464
465         SetWindowLong(hwnd, GWL_USERDATA, 1);
466         return 0;
467       case WM_LBUTTONUP:
468         /*
469          * Button release should trigger WM_OK if there was a
470          * previous double click on the session list.
471          */
472         ReleaseCapture();
473         if (dp.ended)
474             SaneEndDialog(hwnd, dp.endresult ? 1 : 0);
475         break;
476       case WM_NOTIFY:
477         if (LOWORD(wParam) == IDCX_TREEVIEW &&
478             ((LPNMHDR) lParam)->code == TVN_SELCHANGED) {
479             HTREEITEM i =
480                 TreeView_GetSelection(((LPNMHDR) lParam)->hwndFrom);
481             TVITEM item;
482             char buffer[64];
483  
484             SendMessage (hwnd, WM_SETREDRAW, FALSE, 0);
485  
486             item.hItem = i;
487             item.pszText = buffer;
488             item.cchTextMax = sizeof(buffer);
489             item.mask = TVIF_TEXT | TVIF_PARAM;
490             TreeView_GetItem(((LPNMHDR) lParam)->hwndFrom, &item);
491             {
492                 /* Destroy all controls in the currently visible panel. */
493                 int k;
494                 HWND item;
495                 struct winctrl *c;
496
497                 while ((c = winctrl_findbyindex(&ctrls_panel, 0)) != NULL) {
498                     for (k = 0; k < c->num_ids; k++) {
499                         item = GetDlgItem(hwnd, c->base_id + k);
500                         if (item)
501                             DestroyWindow(item);
502                     }
503                     winctrl_rem_shortcuts(&dp, c);
504                     winctrl_remove(&ctrls_panel, c);
505                     sfree(c->data);
506                     sfree(c);
507                 }
508             }
509             create_controls(hwnd, (char *)item.lParam);
510
511             dlg_refresh(NULL, &dp);    /* set up control values */
512  
513             SendMessage (hwnd, WM_SETREDRAW, TRUE, 0);
514             InvalidateRect (hwnd, NULL, TRUE);
515
516             SetFocus(((LPNMHDR) lParam)->hwndFrom);     /* ensure focus stays */
517             return 0;
518         }
519         break;
520       case WM_COMMAND:
521       case WM_DRAWITEM:
522       default:                         /* also handle drag list msg here */
523         /*
524          * Only process WM_COMMAND once the dialog is fully formed.
525          */
526         if (GetWindowLong(hwnd, GWL_USERDATA) == 1) {
527             ret = winctrl_handle_command(&dp, msg, wParam, lParam);
528             if (dp.ended && GetCapture() != hwnd)
529                 SaneEndDialog(hwnd, dp.endresult ? 1 : 0);
530         } else
531             ret = 0;
532         return ret;
533       case WM_HELP:
534         if (help_path) {
535             if (winctrl_context_help(&dp, hwnd,
536                                      ((LPHELPINFO)lParam)->iCtrlId))
537                 requested_help = TRUE;
538             else
539                 MessageBeep(0);
540         }
541         break;
542       case WM_CLOSE:
543         if (requested_help) {
544             WinHelp(hwnd, help_path, HELP_QUIT, 0);
545             requested_help = FALSE;
546         }
547         SaneEndDialog(hwnd, 0);
548         return 0;
549
550         /* Grrr Explorer will maximize Dialogs! */
551       case WM_SIZE:
552         if (wParam == SIZE_MAXIMIZED)
553             force_normal(hwnd);
554         return 0;
555
556     }
557     return 0;
558 }
559
560 void modal_about_box(HWND hwnd)
561 {
562     EnableWindow(hwnd, 0);
563     DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
564     EnableWindow(hwnd, 1);
565     SetActiveWindow(hwnd);
566 }
567
568 void show_help(HWND hwnd)
569 {
570     if (help_path) {
571         WinHelp(hwnd, help_path,
572                 help_has_contents ? HELP_FINDER : HELP_CONTENTS,
573                 0);
574         requested_help = TRUE;
575     }
576 }
577
578 void defuse_showwindow(void)
579 {
580     /*
581      * Work around the fact that the app's first call to ShowWindow
582      * will ignore the default in favour of the shell-provided
583      * setting.
584      */
585     {
586         HWND hwnd;
587         hwnd = CreateDialog(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX),
588                             NULL, NullDlgProc);
589         ShowWindow(hwnd, SW_HIDE);
590         SetActiveWindow(hwnd);
591         DestroyWindow(hwnd);
592     }
593 }
594
595 int do_config(void)
596 {
597     int ret;
598
599     ctrlbox = ctrl_new_box();
600     setup_config_box(ctrlbox, &sesslist, FALSE, 0);
601     win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), FALSE);
602     dp_init(&dp);
603     winctrl_init(&ctrls_base);
604     winctrl_init(&ctrls_panel);
605     dp_add_tree(&dp, &ctrls_base);
606     dp_add_tree(&dp, &ctrls_panel);
607     dp.wintitle = dupprintf("%s Configuration", appname);
608     dp.errtitle = dupprintf("%s Error", appname);
609     dp.data = &cfg;
610     dp.shortcuts['g'] = TRUE;          /* the treeview: `Cate&gory' */
611
612     get_sesslist(&sesslist, TRUE);
613     ret =
614         SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
615                   GenericMainDlgProc);
616     get_sesslist(&sesslist, FALSE);
617
618     ctrl_free_box(ctrlbox);
619     winctrl_cleanup(&ctrls_panel);
620     winctrl_cleanup(&ctrls_base);
621     dp_cleanup(&dp);
622
623     return ret;
624 }
625
626 int do_reconfig(HWND hwnd)
627 {
628     Config backup_cfg;
629     int ret;
630
631     backup_cfg = cfg;                  /* structure copy */
632
633     ctrlbox = ctrl_new_box();
634     setup_config_box(ctrlbox, NULL, TRUE, cfg.protocol);
635     win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), TRUE);
636     dp_init(&dp);
637     winctrl_init(&ctrls_base);
638     winctrl_init(&ctrls_panel);
639     dp_add_tree(&dp, &ctrls_base);
640     dp_add_tree(&dp, &ctrls_panel);
641     dp.wintitle = dupprintf("%s Reconfiguration", appname);
642     dp.errtitle = dupprintf("%s Error", appname);
643     dp.data = &cfg;
644     dp.shortcuts['g'] = TRUE;          /* the treeview: `Cate&gory' */
645
646     ret = SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
647                   GenericMainDlgProc);
648
649     ctrl_free_box(ctrlbox);
650     winctrl_cleanup(&ctrls_base);
651     winctrl_cleanup(&ctrls_panel);
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 }