Introduced wrapper macros snew(), snewn() and sresize() for the
[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 = snewn(selcount, 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 = snewn(size, char);
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 dp_init(&dp);
586 winctrl_init(&ctrls_base);
587 winctrl_init(&ctrls_panel);
588 dp_add_tree(&dp, &ctrls_base);
589 dp_add_tree(&dp, &ctrls_panel);
590 dp.errtitle = "PuTTY Error";
591 dp.data = &cfg;
592 dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
593
594 get_sesslist(&sesslist, TRUE);
595 ret =
596 DialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
597 GenericMainDlgProc);
598 get_sesslist(&sesslist, FALSE);
599
600 ctrl_free_box(ctrlbox);
601 winctrl_cleanup(&ctrls_panel);
602 winctrl_cleanup(&ctrls_base);
603 dp_cleanup(&dp);
604
605 return ret;
606 }
607
608 int do_reconfig(HWND hwnd)
609 {
610 Config backup_cfg;
611 int ret;
612
613 backup_cfg = cfg; /* structure copy */
614
615 ctrlbox = ctrl_new_box();
616 setup_config_box(ctrlbox, NULL, TRUE, cfg.protocol);
617 win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), TRUE);
618 dp_init(&dp);
619 winctrl_init(&ctrls_base);
620 winctrl_init(&ctrls_panel);
621 dp_add_tree(&dp, &ctrls_base);
622 dp_add_tree(&dp, &ctrls_panel);
623 dp.errtitle = "PuTTY Error";
624 dp.data = &cfg;
625 dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
626
627 ret =
628 DialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
629 GenericMainDlgProc);
630
631 ctrl_free_box(ctrlbox);
632 winctrl_cleanup(&ctrls_base);
633 winctrl_cleanup(&ctrls_panel);
634 dp_cleanup(&dp);
635
636 if (!ret)
637 cfg = backup_cfg; /* structure copy */
638
639 return ret;
640 }
641
642 void logevent(void *frontend, char *string)
643 {
644 char timebuf[40];
645 time_t t;
646
647 log_eventlog(logctx, string);
648
649 if (nevents >= negsize) {
650 negsize += 64;
651 events = sresize(events, negsize, char *);
652 }
653
654 time(&t);
655 strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t",
656 localtime(&t));
657
658 events[nevents] = snewn(strlen(timebuf) + strlen(string) + 1, char);
659 strcpy(events[nevents], timebuf);
660 strcat(events[nevents], string);
661 if (logbox) {
662 int count;
663 SendDlgItemMessage(logbox, IDN_LIST, LB_ADDSTRING,
664 0, (LPARAM) events[nevents]);
665 count = SendDlgItemMessage(logbox, IDN_LIST, LB_GETCOUNT, 0, 0);
666 SendDlgItemMessage(logbox, IDN_LIST, LB_SETTOPINDEX, count - 1, 0);
667 }
668 nevents++;
669 }
670
671 void showeventlog(HWND hwnd)
672 {
673 if (!logbox) {
674 logbox = CreateDialog(hinst, MAKEINTRESOURCE(IDD_LOGBOX),
675 hwnd, LogProc);
676 ShowWindow(logbox, SW_SHOWNORMAL);
677 }
678 SetActiveWindow(logbox);
679 }
680
681 void showabout(HWND hwnd)
682 {
683 DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
684 }
685
686 void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
687 char *keystr, char *fingerprint)
688 {
689 int ret;
690
691 static const char absentmsg[] =
692 "The server's host key is not cached in the registry. You\n"
693 "have no guarantee that the server is the computer you\n"
694 "think it is.\n"
695 "The server's key fingerprint is:\n"
696 "%s\n"
697 "If you trust this host, hit Yes to add the key to\n"
698 "PuTTY's cache and carry on connecting.\n"
699 "If you want to carry on connecting just once, without\n"
700 "adding the key to the cache, hit No.\n"
701 "If you do not trust this host, hit Cancel to abandon the\n"
702 "connection.\n";
703
704 static const char wrongmsg[] =
705 "WARNING - POTENTIAL SECURITY BREACH!\n"
706 "\n"
707 "The server's host key does not match the one PuTTY has\n"
708 "cached in the registry. This means that either the\n"
709 "server administrator has changed the host key, or you\n"
710 "have actually connected to another computer pretending\n"
711 "to be the server.\n"
712 "The new key fingerprint is:\n"
713 "%s\n"
714 "If you were expecting this change and trust the new key,\n"
715 "hit Yes to update PuTTY's cache and continue connecting.\n"
716 "If you want to carry on connecting but without updating\n"
717 "the cache, hit No.\n"
718 "If you want to abandon the connection completely, hit\n"
719 "Cancel. Hitting Cancel is the ONLY guaranteed safe\n" "choice.\n";
720
721 static const char mbtitle[] = "PuTTY Security Alert";
722
723 char message[160 +
724 /* sensible fingerprint max size */
725 (sizeof(absentmsg) > sizeof(wrongmsg) ?
726 sizeof(absentmsg) : sizeof(wrongmsg))];
727
728 /*
729 * Verify the key against the registry.
730 */
731 ret = verify_host_key(host, port, keytype, keystr);
732
733 if (ret == 0) /* success - key matched OK */
734 return;
735 if (ret == 2) { /* key was different */
736 int mbret;
737 sprintf(message, wrongmsg, fingerprint);
738 mbret = MessageBox(NULL, message, mbtitle,
739 MB_ICONWARNING | MB_YESNOCANCEL);
740 if (mbret == IDYES)
741 store_host_key(host, port, keytype, keystr);
742 if (mbret == IDCANCEL)
743 cleanup_exit(0);
744 }
745 if (ret == 1) { /* key was absent */
746 int mbret;
747 sprintf(message, absentmsg, fingerprint);
748 mbret = MessageBox(NULL, message, mbtitle,
749 MB_ICONWARNING | MB_YESNOCANCEL);
750 if (mbret == IDYES)
751 store_host_key(host, port, keytype, keystr);
752 if (mbret == IDCANCEL)
753 cleanup_exit(0);
754 }
755 }
756
757 /*
758 * Ask whether the selected cipher is acceptable (since it was
759 * below the configured 'warn' threshold).
760 * cs: 0 = both ways, 1 = client->server, 2 = server->client
761 */
762 void askcipher(void *frontend, char *ciphername, int cs)
763 {
764 static const char mbtitle[] = "PuTTY Security Alert";
765 static const char msg[] =
766 "The first %.35scipher supported by the server\n"
767 "is %.64s, which is below the configured\n"
768 "warning threshold.\n"
769 "Do you want to continue with this connection?\n";
770 /* guessed cipher name + type max length */
771 char message[100 + sizeof(msg)];
772 int mbret;
773
774 sprintf(message, msg,
775 (cs == 0) ? "" :
776 (cs == 1) ? "client-to-server " :
777 "server-to-client ",
778 ciphername);
779 mbret = MessageBox(NULL, message, mbtitle,
780 MB_ICONWARNING | MB_YESNO);
781 if (mbret == IDYES)
782 return;
783 else
784 cleanup_exit(0);
785 }
786
787 /*
788 * Ask whether to wipe a session log file before writing to it.
789 * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
790 */
791 int askappend(void *frontend, Filename filename)
792 {
793 static const char mbtitle[] = "PuTTY Log to File";
794 static const char msgtemplate[] =
795 "The session log file \"%.*s\" already exists.\n"
796 "You can overwrite it with a new session log,\n"
797 "append your session log to the end of it,\n"
798 "or disable session logging for this session.\n"
799 "Hit Yes to wipe the file, No to append to it,\n"
800 "or Cancel to disable logging.";
801 char message[sizeof(msgtemplate) + FILENAME_MAX];
802 int mbret;
803
804 sprintf(message, msgtemplate, FILENAME_MAX, filename.path);
805
806 mbret = MessageBox(NULL, message, mbtitle,
807 MB_ICONQUESTION | MB_YESNOCANCEL);
808 if (mbret == IDYES)
809 return 2;
810 else if (mbret == IDNO)
811 return 1;
812 else
813 return 0;
814 }
815
816 /*
817 * Warn about the obsolescent key file format.
818 *
819 * Uniquely among these functions, this one does _not_ expect a
820 * frontend handle. This means that if PuTTY is ported to a
821 * platform which requires frontend handles, this function will be
822 * an anomaly. Fortunately, the problem it addresses will not have
823 * been present on that platform, so it can plausibly be
824 * implemented as an empty function.
825 */
826 void old_keyfile_warning(void)
827 {
828 static const char mbtitle[] = "PuTTY Key File Warning";
829 static const char message[] =
830 "You are loading an SSH 2 private key which has an\n"
831 "old version of the file format. This means your key\n"
832 "file is not fully tamperproof. Future versions of\n"
833 "PuTTY may stop supporting this private key format,\n"
834 "so we recommend you convert your key to the new\n"
835 "format.\n"
836 "\n"
837 "You can perform this conversion by loading the key\n"
838 "into PuTTYgen and then saving it again.";
839
840 MessageBox(NULL, message, mbtitle, MB_OK);
841 }