Revamp interface to verify_ssh_host_key() and askalg(). Each of them
[u/mdw/putty] / 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, 100, 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 + 95;
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 + 95;
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, 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, int protcfginfo)
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, protcfginfo);
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 struct tm tm;
665
666 log_eventlog(logctx, string);
667
668 if (nevents >= negsize) {
669 negsize += 64;
670 events = sresize(events, negsize, char *);
671 }
672
673 tm=ltime();
674 strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t", &tm);
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 /* Helper function for verify_ssh_host_key(). */
705 static VOID CALLBACK verify_ssh_host_key_help(LPHELPINFO lpHelpInfo)
706 {
707 if (help_path) {
708 char *context = NULL;
709 #define CHECK_CTX(name) \
710 do { \
711 if (lpHelpInfo->dwContextId == WINHELP_CTXID_ ## name) \
712 context = WINHELP_CTX_ ## name; \
713 } while (0)
714 CHECK_CTX(errors_hostkey_absent);
715 CHECK_CTX(errors_hostkey_changed);
716 #undef CHECK_CTX
717 if (context) {
718 char *cmd = dupprintf("JI(`',`%s')", context);
719 WinHelp(hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
720 sfree(cmd);
721 requested_help = TRUE;
722 }
723 }
724 }
725
726 int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
727 char *keystr, char *fingerprint,
728 void (*callback)(void *ctx, int result), void *ctx)
729 {
730 int ret;
731
732 static const char absentmsg[] =
733 "The server's host key is not cached in the registry. You\n"
734 "have no guarantee that the server is the computer you\n"
735 "think it is.\n"
736 "The server's %s key fingerprint is:\n"
737 "%s\n"
738 "If you trust this host, hit Yes to add the key to\n"
739 "%s's cache and carry on connecting.\n"
740 "If you want to carry on connecting just once, without\n"
741 "adding the key to the cache, hit No.\n"
742 "If you do not trust this host, hit Cancel to abandon the\n"
743 "connection.\n";
744
745 static const char wrongmsg[] =
746 "WARNING - POTENTIAL SECURITY BREACH!\n"
747 "\n"
748 "The server's host key does not match the one %s has\n"
749 "cached in the registry. This means that either the\n"
750 "server administrator has changed the host key, or you\n"
751 "have actually connected to another computer pretending\n"
752 "to be the server.\n"
753 "The new %s key fingerprint is:\n"
754 "%s\n"
755 "If you were expecting this change and trust the new key,\n"
756 "hit Yes to update %s's cache and continue connecting.\n"
757 "If you want to carry on connecting but without updating\n"
758 "the cache, hit No.\n"
759 "If you want to abandon the connection completely, hit\n"
760 "Cancel. Hitting Cancel is the ONLY guaranteed safe\n" "choice.\n";
761
762 static const char mbtitle[] = "%s Security Alert";
763
764 UINT help_button = 0;
765 MSGBOXPARAMS mbox;
766
767 /*
768 * We use MessageBoxIndirect() because it allows us to specify a
769 * callback function for the Help button.
770 */
771 mbox.cbSize = sizeof(mbox);
772 mbox.hwndOwner = hwnd;
773 mbox.lpfnMsgBoxCallback = &verify_ssh_host_key_help;
774 mbox.dwLanguageId = LANG_NEUTRAL;
775
776 /* Do we have a help file? */
777 if (help_path)
778 help_button = MB_HELP;
779
780 /*
781 * Verify the key against the registry.
782 */
783 ret = verify_host_key(host, port, keytype, keystr);
784
785 if (ret == 0) /* success - key matched OK */
786 return 1;
787 if (ret == 2) { /* key was different */
788 int mbret;
789 mbox.lpszText = dupprintf(wrongmsg, appname, keytype, fingerprint,
790 appname);
791 mbox.lpszCaption = dupprintf(mbtitle, appname);
792 mbox.dwContextHelpId = HELPCTXID(errors_hostkey_changed);
793 mbox.dwStyle = MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3 |
794 help_button;
795 mbret = MessageBoxIndirect(&mbox);
796 sfree((void *)mbox.lpszText);
797 sfree((void *)mbox.lpszCaption);
798 if (mbret == IDYES)
799 store_host_key(host, port, keytype, keystr);
800 if (mbret == IDCANCEL)
801 return 0;
802 return 1;
803 }
804 if (ret == 1) { /* key was absent */
805 int mbret;
806 mbox.lpszText = dupprintf(absentmsg, keytype, fingerprint, appname);
807 mbox.lpszCaption = dupprintf(mbtitle, appname);
808 mbox.dwContextHelpId = HELPCTXID(errors_hostkey_absent);
809 mbox.dwStyle = MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3 |
810 help_button;
811 mbret = MessageBoxIndirect(&mbox);
812 sfree((void *)mbox.lpszText);
813 sfree((void *)mbox.lpszCaption);
814 if (mbret == IDYES)
815 store_host_key(host, port, keytype, keystr);
816 if (mbret == IDCANCEL)
817 return 0;
818 return 1;
819 }
820 }
821
822 /*
823 * Ask whether the selected algorithm is acceptable (since it was
824 * below the configured 'warn' threshold).
825 */
826 int askalg(void *frontend, const char *algtype, const char *algname,
827 void (*callback)(void *ctx, int result), void *ctx)
828 {
829 static const char mbtitle[] = "%s Security Alert";
830 static const char msg[] =
831 "The first %s supported by the server\n"
832 "is %.64s, which is below the configured\n"
833 "warning threshold.\n"
834 "Do you want to continue with this connection?\n";
835 char *message, *title;
836 int mbret;
837
838 message = dupprintf(msg, algtype, algname);
839 title = dupprintf(mbtitle, appname);
840 mbret = MessageBox(NULL, message, title,
841 MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2);
842 sfree(message);
843 sfree(title);
844 if (mbret == IDYES)
845 return 1;
846 else
847 return 0;
848 }
849
850 /*
851 * Ask whether to wipe a session log file before writing to it.
852 * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
853 */
854 int askappend(void *frontend, Filename filename)
855 {
856 static const char msgtemplate[] =
857 "The session log file \"%.*s\" already exists.\n"
858 "You can overwrite it with a new session log,\n"
859 "append your session log to the end of it,\n"
860 "or disable session logging for this session.\n"
861 "Hit Yes to wipe the file, No to append to it,\n"
862 "or Cancel to disable logging.";
863 char *message;
864 char *mbtitle;
865 int mbret;
866
867 message = dupprintf(msgtemplate, FILENAME_MAX, filename.path);
868 mbtitle = dupprintf("%s Log to File", appname);
869
870 mbret = MessageBox(NULL, message, mbtitle,
871 MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON3);
872
873 sfree(message);
874 sfree(mbtitle);
875
876 if (mbret == IDYES)
877 return 2;
878 else if (mbret == IDNO)
879 return 1;
880 else
881 return 0;
882 }
883
884 /*
885 * Warn about the obsolescent key file format.
886 *
887 * Uniquely among these functions, this one does _not_ expect a
888 * frontend handle. This means that if PuTTY is ported to a
889 * platform which requires frontend handles, this function will be
890 * an anomaly. Fortunately, the problem it addresses will not have
891 * been present on that platform, so it can plausibly be
892 * implemented as an empty function.
893 */
894 void old_keyfile_warning(void)
895 {
896 static const char mbtitle[] = "%s Key File Warning";
897 static const char message[] =
898 "You are loading an SSH 2 private key which has an\n"
899 "old version of the file format. This means your key\n"
900 "file is not fully tamperproof. Future versions of\n"
901 "%s may stop supporting this private key format,\n"
902 "so we recommend you convert your key to the new\n"
903 "format.\n"
904 "\n"
905 "You can perform this conversion by loading the key\n"
906 "into PuTTYgen and then saving it again.";
907
908 char *msg, *title;
909 msg = dupprintf(message, appname);
910 title = dupprintf(mbtitle, appname);
911
912 MessageBox(NULL, msg, title, MB_OK);
913
914 sfree(msg);
915 sfree(title);
916 }