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