The WinSock library is now loaded at run-time, which means we can
[u/mdw/putty] / 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 }