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