2 * windlg.c - dialogs for PuTTY(tel), including the configuration dialog.
24 #define TVINSERTSTRUCT TV_INSERTSTRUCT
25 #define TVITEM TV_ITEM
30 * These are the various bits of data required to handle the
31 * portable-dialog stuff in the config box. Having them at file
32 * scope in here isn't too bad a place to put them; if we were ever
33 * to need more than one config box per process we could always
34 * shift them to a per-config-box structure stored in GWL_USERDATA.
36 static struct controlbox *ctrlbox;
38 * ctrls_base holds the OK and Cancel buttons: the controls which
39 * are present in all dialog panels. ctrls_panel holds the ones
40 * which change from panel to panel.
42 static struct winctrls ctrls_base, ctrls_panel;
43 static struct dlgparam dp;
45 static char **events = NULL;
46 static int nevents = 0, negsize = 0;
48 extern Conf *conf; /* defined in window.c */
50 #define PRINTER_DISABLED_STRING "None (printing disabled)"
52 void force_normal(HWND hwnd)
54 static int recurse = 0;
62 wp.length = sizeof(wp);
63 if (GetWindowPlacement(hwnd, &wp) && wp.showCmd == SW_SHOWMAXIMIZED) {
64 wp.showCmd = SW_SHOWNORMAL;
65 SetWindowPlacement(hwnd, &wp);
70 static INT_PTR CALLBACK LogProc(HWND hwnd, UINT msg,
71 WPARAM wParam, LPARAM lParam)
78 char *str = dupprintf("%s Event Log", appname);
79 SetWindowText(hwnd, str);
83 static int tabs[4] = { 78, 108 };
84 SendDlgItemMessage(hwnd, IDN_LIST, LB_SETTABSTOPS, 2,
87 for (i = 0; i < nevents; i++)
88 SendDlgItemMessage(hwnd, IDN_LIST, LB_ADDSTRING,
89 0, (LPARAM) events[i]);
92 switch (LOWORD(wParam)) {
96 SetActiveWindow(GetParent(hwnd));
100 if (HIWORD(wParam) == BN_CLICKED ||
101 HIWORD(wParam) == BN_DOUBLECLICKED) {
104 selcount = SendDlgItemMessage(hwnd, IDN_LIST,
105 LB_GETSELCOUNT, 0, 0);
106 if (selcount == 0) { /* don't even try to copy zero items */
111 selitems = snewn(selcount, int);
113 int count = SendDlgItemMessage(hwnd, IDN_LIST,
120 static unsigned char sel_nl[] = SEL_NL;
122 if (count == 0) { /* can't copy zero stuff */
128 for (i = 0; i < count; i++)
130 strlen(events[selitems[i]]) + sizeof(sel_nl);
132 clipdata = snewn(size, char);
135 for (i = 0; i < count; i++) {
136 char *q = events[selitems[i]];
137 int qlen = strlen(q);
140 memcpy(p, sel_nl, sizeof(sel_nl));
143 write_aclip(NULL, clipdata, size, TRUE);
148 for (i = 0; i < nevents; i++)
149 SendDlgItemMessage(hwnd, IDN_LIST, LB_SETSEL,
158 SetActiveWindow(GetParent(hwnd));
165 static INT_PTR CALLBACK LicenceProc(HWND hwnd, UINT msg,
166 WPARAM wParam, LPARAM lParam)
171 char *str = dupprintf("%s Licence", appname);
172 SetWindowText(hwnd, str);
174 SetDlgItemText(hwnd, IDA_TEXT, LICENCE_TEXT("\r\n\r\n"));
178 switch (LOWORD(wParam)) {
192 static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg,
193 WPARAM wParam, LPARAM lParam)
199 str = dupprintf("About %s", appname);
200 SetWindowText(hwnd, str);
203 char *text = dupprintf
204 ("%s\r\n\r\n%s\r\n\r\n%s",
206 "\251 " SHORT_COPYRIGHT_DETAILS ". All rights reserved.");
207 SetDlgItemText(hwnd, IDA_TEXT, text);
212 switch (LOWORD(wParam)) {
215 EndDialog(hwnd, TRUE);
218 EnableWindow(hwnd, 0);
219 DialogBox(hinst, MAKEINTRESOURCE(IDD_LICENCEBOX),
221 EnableWindow(hwnd, 1);
222 SetActiveWindow(hwnd);
226 /* Load web browser */
227 ShellExecute(hwnd, "open",
228 "http://www.chiark.greenend.org.uk/~sgtatham/putty/",
229 0, 0, SW_SHOWDEFAULT);
234 EndDialog(hwnd, TRUE);
240 static int SaneDialogBox(HINSTANCE hinst,
243 DLGPROC lpDialogFunc)
252 wc.style = CS_DBLCLKS | CS_SAVEBITS | CS_BYTEALIGNWINDOW;
253 wc.lpfnWndProc = DefDlgProc;
255 wc.cbWndExtra = DLGWINDOWEXTRA + 2*sizeof(LONG_PTR);
256 wc.hInstance = hinst;
258 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
259 wc.hbrBackground = (HBRUSH) (COLOR_BACKGROUND +1);
260 wc.lpszMenuName = NULL;
261 wc.lpszClassName = "PuTTYConfigBox";
264 hwnd = CreateDialog(hinst, tmpl, hwndparent, lpDialogFunc);
266 SetWindowLongPtr(hwnd, BOXFLAGS, 0); /* flags */
267 SetWindowLongPtr(hwnd, BOXRESULT, 0); /* result from SaneEndDialog */
269 while ((gm=GetMessage(&msg, NULL, 0, 0)) > 0) {
270 flags=GetWindowLongPtr(hwnd, BOXFLAGS);
271 if (!(flags & DF_END) && !IsDialogMessage(hwnd, &msg))
272 DispatchMessage(&msg);
278 PostQuitMessage(msg.wParam); /* We got a WM_QUIT, pass it on */
280 ret=GetWindowLongPtr(hwnd, BOXRESULT);
285 static void SaneEndDialog(HWND hwnd, int ret)
287 SetWindowLongPtr(hwnd, BOXRESULT, ret);
288 SetWindowLongPtr(hwnd, BOXFLAGS, DF_END);
292 * Null dialog procedure.
294 static INT_PTR CALLBACK NullDlgProc(HWND hwnd, UINT msg,
295 WPARAM wParam, LPARAM lParam)
301 IDCX_ABOUT = IDC_ABOUT,
305 IDCX_PANELBASE = IDCX_STDBASE + 32
308 struct treeview_faff {
313 static HTREEITEM treeview_insert(struct treeview_faff *faff,
314 int level, char *text, char *path)
319 ins.hParent = (level > 0 ? faff->lastat[level - 1] : TVI_ROOT);
320 ins.hInsertAfter = faff->lastat[level];
321 #if _WIN32_IE >= 0x0400 && defined NONAMELESSUNION
322 #define INSITEM DUMMYUNIONNAME.item
326 ins.INSITEM.mask = TVIF_TEXT | TVIF_PARAM;
327 ins.INSITEM.pszText = text;
328 ins.INSITEM.cchTextMax = strlen(text)+1;
329 ins.INSITEM.lParam = (LPARAM)path;
330 newitem = TreeView_InsertItem(faff->treeview, &ins);
332 TreeView_Expand(faff->treeview, faff->lastat[level - 1],
333 (level > 1 ? TVE_COLLAPSE : TVE_EXPAND));
334 faff->lastat[level] = newitem;
335 for (i = level + 1; i < 4; i++)
336 faff->lastat[i] = NULL;
341 * Create the panelfuls of controls in the configuration box.
343 static void create_controls(HWND hwnd, char *path)
352 * Here we must create the basic standard controls.
354 ctlposinit(&cp, hwnd, 3, 3, 235);
356 base_id = IDCX_STDBASE;
359 * Otherwise, we're creating the controls for a particular
362 ctlposinit(&cp, hwnd, 100, 3, 13);
364 base_id = IDCX_PANELBASE;
367 for (index=-1; (index = ctrl_find_path(ctrlbox, path, index)) >= 0 ;) {
368 struct controlset *s = ctrlbox->ctrlsets[index];
369 winctrl_layout(&dp, wc, &cp, s, &base_id);
374 * This function is the configuration box.
375 * (Being a dialog procedure, in general it returns 0 if the default
376 * dialog processing should be performed, and 1 if it should not.)
378 static INT_PTR CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
379 WPARAM wParam, LPARAM lParam)
382 struct treeview_faff tvfaff;
388 create_controls(hwnd, ""); /* Open and Cancel buttons etc */
389 SetWindowText(hwnd, dp.wintitle);
390 SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
392 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
393 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
396 HWND item = GetDlgItem(hwnd, IDC_HELPBTN);
400 SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
401 (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(IDI_CFGICON)));
405 { /* centre the window */
408 hw = GetDesktopWindow();
409 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
411 (rs.right + rs.left + rd.left - rd.right) / 2,
412 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
413 rd.right - rd.left, rd.bottom - rd.top, TRUE);
417 * Create the tree view.
425 r.right = r.left + 95;
427 r.bottom = r.top + 10;
428 MapDialogRect(hwnd, &r);
429 tvstatic = CreateWindowEx(0, "STATIC", "Cate&gory:",
430 WS_CHILD | WS_VISIBLE,
432 r.right - r.left, r.bottom - r.top,
433 hwnd, (HMENU) IDCX_TVSTATIC, hinst,
435 font = SendMessage(hwnd, WM_GETFONT, 0, 0);
436 SendMessage(tvstatic, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
439 r.right = r.left + 95;
441 r.bottom = r.top + 219;
442 MapDialogRect(hwnd, &r);
443 treeview = CreateWindowEx(WS_EX_CLIENTEDGE, WC_TREEVIEW, "",
444 WS_CHILD | WS_VISIBLE |
445 WS_TABSTOP | TVS_HASLINES |
446 TVS_DISABLEDRAGDROP | TVS_HASBUTTONS
448 TVS_SHOWSELALWAYS, r.left, r.top,
449 r.right - r.left, r.bottom - r.top,
450 hwnd, (HMENU) IDCX_TREEVIEW, hinst,
452 font = SendMessage(hwnd, WM_GETFONT, 0, 0);
453 SendMessage(treeview, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
454 tvfaff.treeview = treeview;
455 memset(tvfaff.lastat, 0, sizeof(tvfaff.lastat));
459 * Set up the tree view contents.
462 HTREEITEM hfirst = NULL;
465 char *firstpath = NULL;
467 for (i = 0; i < ctrlbox->nctrlsets; i++) {
468 struct controlset *s = ctrlbox->ctrlsets[i];
475 j = path ? ctrl_path_compare(s->pathname, path) : 0;
477 continue; /* same path, nothing to add to tree */
480 * We expect never to find an implicit path
481 * component. For example, we expect never to see
482 * A/B/C followed by A/D/E, because that would
483 * _implicitly_ create A/D. All our path prefixes
484 * are expected to contain actual controls and be
485 * selectable in the treeview; so we would expect
486 * to see A/D _explicitly_ before encountering
489 assert(j == ctrl_path_elements(s->pathname) - 1);
491 c = strrchr(s->pathname, '/');
497 item = treeview_insert(&tvfaff, j, c, s->pathname);
500 firstpath = s->pathname;
507 * Put the treeview selection on to the first panel in the
510 TreeView_SelectItem(treeview, hfirst);
513 * And create the actual control set for that panel, to
514 * match the initial treeview selection.
516 assert(firstpath); /* config.c must have given us _something_ */
517 create_controls(hwnd, firstpath);
518 dlg_refresh(NULL, &dp); /* and set up control values */
522 * Set focus into the first available control.
528 for (i = 0; (c = winctrl_findbyindex(&ctrls_panel, i)) != NULL;
531 dlg_set_focus(c->ctrl, &dp);
538 * Now we've finished creating our initial set of controls,
539 * it's safe to actually show the window without risking setup
542 ShowWindow(hwnd, SW_SHOWNORMAL);
545 * Set the flag that activates a couple of the other message
546 * handlers below, which were disabled until now to avoid
547 * spurious firing during the above setup procedure.
549 SetWindowLongPtr(hwnd, GWLP_USERDATA, 1);
553 * Button release should trigger WM_OK if there was a
554 * previous double click on the session list.
558 SaneEndDialog(hwnd, dp.endresult ? 1 : 0);
561 if (LOWORD(wParam) == IDCX_TREEVIEW &&
562 ((LPNMHDR) lParam)->code == TVN_SELCHANGED) {
564 * Selection-change events on the treeview cause us to do
565 * a flurry of control deletion and creation - but only
566 * after WM_INITDIALOG has finished. The initial
567 * selection-change event(s) during treeview setup are
574 if (GetWindowLongPtr(hwnd, GWLP_USERDATA) != 1)
577 i = TreeView_GetSelection(((LPNMHDR) lParam)->hwndFrom);
579 SendMessage (hwnd, WM_SETREDRAW, FALSE, 0);
582 item.pszText = buffer;
583 item.cchTextMax = sizeof(buffer);
584 item.mask = TVIF_TEXT | TVIF_PARAM;
585 TreeView_GetItem(((LPNMHDR) lParam)->hwndFrom, &item);
587 /* Destroy all controls in the currently visible panel. */
592 while ((c = winctrl_findbyindex(&ctrls_panel, 0)) != NULL) {
593 for (k = 0; k < c->num_ids; k++) {
594 item = GetDlgItem(hwnd, c->base_id + k);
598 winctrl_rem_shortcuts(&dp, c);
599 winctrl_remove(&ctrls_panel, c);
604 create_controls(hwnd, (char *)item.lParam);
606 dlg_refresh(NULL, &dp); /* set up control values */
608 SendMessage (hwnd, WM_SETREDRAW, TRUE, 0);
609 InvalidateRect (hwnd, NULL, TRUE);
611 SetFocus(((LPNMHDR) lParam)->hwndFrom); /* ensure focus stays */
617 default: /* also handle drag list msg here */
619 * Only process WM_COMMAND once the dialog is fully formed.
621 if (GetWindowLongPtr(hwnd, GWLP_USERDATA) == 1) {
622 ret = winctrl_handle_command(&dp, msg, wParam, lParam);
623 if (dp.ended && GetCapture() != hwnd)
624 SaneEndDialog(hwnd, dp.endresult ? 1 : 0);
629 if (!winctrl_context_help(&dp, hwnd,
630 ((LPHELPINFO)lParam)->iCtrlId))
635 SaneEndDialog(hwnd, 0);
638 /* Grrr Explorer will maximize Dialogs! */
640 if (wParam == SIZE_MAXIMIZED)
648 void modal_about_box(HWND hwnd)
650 EnableWindow(hwnd, 0);
651 DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
652 EnableWindow(hwnd, 1);
653 SetActiveWindow(hwnd);
656 void show_help(HWND hwnd)
658 launch_help(hwnd, NULL);
661 void defuse_showwindow(void)
664 * Work around the fact that the app's first call to ShowWindow
665 * will ignore the default in favour of the shell-provided
670 hwnd = CreateDialog(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX),
672 ShowWindow(hwnd, SW_HIDE);
673 SetActiveWindow(hwnd);
682 ctrlbox = ctrl_new_box();
683 setup_config_box(ctrlbox, FALSE, 0, 0);
684 win_setup_config_box(ctrlbox, &dp.hwnd, has_help(), FALSE, 0);
686 winctrl_init(&ctrls_base);
687 winctrl_init(&ctrls_panel);
688 dp_add_tree(&dp, &ctrls_base);
689 dp_add_tree(&dp, &ctrls_panel);
690 dp.wintitle = dupprintf("%s Configuration", appname);
691 dp.errtitle = dupprintf("%s Error", appname);
693 dlg_auto_set_fixed_pitch_flag(&dp);
694 dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
697 SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
700 ctrl_free_box(ctrlbox);
701 winctrl_cleanup(&ctrls_panel);
702 winctrl_cleanup(&ctrls_base);
708 int do_reconfig(HWND hwnd, int protcfginfo)
713 backup_conf = conf_copy(conf);
715 ctrlbox = ctrl_new_box();
716 protocol = conf_get_int(conf, CONF_protocol);
717 setup_config_box(ctrlbox, TRUE, protocol, protcfginfo);
718 win_setup_config_box(ctrlbox, &dp.hwnd, has_help(), TRUE, protocol);
720 winctrl_init(&ctrls_base);
721 winctrl_init(&ctrls_panel);
722 dp_add_tree(&dp, &ctrls_base);
723 dp_add_tree(&dp, &ctrls_panel);
724 dp.wintitle = dupprintf("%s Reconfiguration", appname);
725 dp.errtitle = dupprintf("%s Error", appname);
727 dlg_auto_set_fixed_pitch_flag(&dp);
728 dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
730 ret = SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
733 ctrl_free_box(ctrlbox);
734 winctrl_cleanup(&ctrls_base);
735 winctrl_cleanup(&ctrls_panel);
739 conf_copy_into(conf, backup_conf);
741 conf_free(backup_conf);
746 void logevent(void *frontend, const char *string)
751 log_eventlog(logctx, string);
753 if (nevents >= negsize) {
755 events = sresize(events, negsize, char *);
759 strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t", &tm);
761 events[nevents] = snewn(strlen(timebuf) + strlen(string) + 1, char);
762 strcpy(events[nevents], timebuf);
763 strcat(events[nevents], string);
766 SendDlgItemMessage(logbox, IDN_LIST, LB_ADDSTRING,
767 0, (LPARAM) events[nevents]);
768 count = SendDlgItemMessage(logbox, IDN_LIST, LB_GETCOUNT, 0, 0);
769 SendDlgItemMessage(logbox, IDN_LIST, LB_SETTOPINDEX, count - 1, 0);
774 void showeventlog(HWND hwnd)
777 logbox = CreateDialog(hinst, MAKEINTRESOURCE(IDD_LOGBOX),
779 ShowWindow(logbox, SW_SHOWNORMAL);
781 SetActiveWindow(logbox);
784 void showabout(HWND hwnd)
786 DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
789 int verify_ssh_host_key(void *frontend, char *host, int port,
790 const char *keytype, char *keystr, char *fingerprint,
791 void (*callback)(void *ctx, int result), void *ctx)
795 static const char absentmsg[] =
796 "The server's host key is not cached in the registry. You\n"
797 "have no guarantee that the server is the computer you\n"
799 "The server's %s key fingerprint is:\n"
801 "If you trust this host, hit Yes to add the key to\n"
802 "%s's cache and carry on connecting.\n"
803 "If you want to carry on connecting just once, without\n"
804 "adding the key to the cache, hit No.\n"
805 "If you do not trust this host, hit Cancel to abandon the\n"
808 static const char wrongmsg[] =
809 "WARNING - POTENTIAL SECURITY BREACH!\n"
811 "The server's host key does not match the one %s has\n"
812 "cached in the registry. This means that either the\n"
813 "server administrator has changed the host key, or you\n"
814 "have actually connected to another computer pretending\n"
815 "to be the server.\n"
816 "The new %s key fingerprint is:\n"
818 "If you were expecting this change and trust the new key,\n"
819 "hit Yes to update %s's cache and continue connecting.\n"
820 "If you want to carry on connecting but without updating\n"
821 "the cache, hit No.\n"
822 "If you want to abandon the connection completely, hit\n"
823 "Cancel. Hitting Cancel is the ONLY guaranteed safe\n" "choice.\n";
825 static const char mbtitle[] = "%s Security Alert";
828 * Verify the key against the registry.
830 ret = verify_host_key(host, port, keytype, keystr);
832 if (ret == 0) /* success - key matched OK */
834 else if (ret == 2) { /* key was different */
836 char *text = dupprintf(wrongmsg, appname, keytype, fingerprint,
838 char *caption = dupprintf(mbtitle, appname);
839 mbret = message_box(text, caption,
840 MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3,
841 HELPCTXID(errors_hostkey_changed));
842 assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);
845 if (mbret == IDYES) {
846 store_host_key(host, port, keytype, keystr);
848 } else if (mbret == IDNO)
850 } else if (ret == 1) { /* key was absent */
852 char *text = dupprintf(absentmsg, keytype, fingerprint, appname);
853 char *caption = dupprintf(mbtitle, appname);
854 mbret = message_box(text, caption,
855 MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3,
856 HELPCTXID(errors_hostkey_absent));
857 assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);
860 if (mbret == IDYES) {
861 store_host_key(host, port, keytype, keystr);
863 } else if (mbret == IDNO)
866 return 0; /* abandon the connection */
870 * Ask whether the selected algorithm is acceptable (since it was
871 * below the configured 'warn' threshold).
873 int askalg(void *frontend, const char *algtype, const char *algname,
874 void (*callback)(void *ctx, int result), void *ctx)
876 static const char mbtitle[] = "%s Security Alert";
877 static const char msg[] =
878 "The first %s supported by the server\n"
879 "is %.64s, which is below the configured\n"
880 "warning threshold.\n"
881 "Do you want to continue with this connection?\n";
882 char *message, *title;
885 message = dupprintf(msg, algtype, algname);
886 title = dupprintf(mbtitle, appname);
887 mbret = MessageBox(NULL, message, title,
888 MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2);
889 socket_reselect_all();
898 int askhk(void *frontend, const char *algname, const char *betteralgs,
899 void (*callback)(void *ctx, int result), void *ctx)
901 static const char mbtitle[] = "%s Security Alert";
902 static const char msg[] =
903 "The first host key type we have stored for this server\n"
904 "is %s, which is below the configured warning threshold.\n"
905 "The server also provides the following types of host key\n"
906 "above the threshold, which we do not have stored:\n"
908 "Do you want to continue with this connection?\n";
909 char *message, *title;
912 message = dupprintf(msg, algname, betteralgs);
913 title = dupprintf(mbtitle, appname);
914 mbret = MessageBox(NULL, message, title,
915 MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2);
916 socket_reselect_all();
926 * Ask whether to wipe a session log file before writing to it.
927 * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
929 int askappend(void *frontend, Filename *filename,
930 void (*callback)(void *ctx, int result), void *ctx)
932 static const char msgtemplate[] =
933 "The session log file \"%.*s\" already exists.\n"
934 "You can overwrite it with a new session log,\n"
935 "append your session log to the end of it,\n"
936 "or disable session logging for this session.\n"
937 "Hit Yes to wipe the file, No to append to it,\n"
938 "or Cancel to disable logging.";
943 message = dupprintf(msgtemplate, FILENAME_MAX, filename->path);
944 mbtitle = dupprintf("%s Log to File", appname);
946 mbret = MessageBox(NULL, message, mbtitle,
947 MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON3);
949 socket_reselect_all();
956 else if (mbret == IDNO)
963 * Warn about the obsolescent key file format.
965 * Uniquely among these functions, this one does _not_ expect a
966 * frontend handle. This means that if PuTTY is ported to a
967 * platform which requires frontend handles, this function will be
968 * an anomaly. Fortunately, the problem it addresses will not have
969 * been present on that platform, so it can plausibly be
970 * implemented as an empty function.
972 void old_keyfile_warning(void)
974 static const char mbtitle[] = "%s Key File Warning";
975 static const char message[] =
976 "You are loading an SSH-2 private key which has an\n"
977 "old version of the file format. This means your key\n"
978 "file is not fully tamperproof. Future versions of\n"
979 "%s may stop supporting this private key format,\n"
980 "so we recommend you convert your key to the new\n"
983 "You can perform this conversion by loading the key\n"
984 "into PuTTYgen and then saving it again.";
987 msg = dupprintf(message, appname);
988 title = dupprintf(mbtitle, appname);
990 MessageBox(NULL, msg, title, MB_OK);
992 socket_reselect_all();