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