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