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 *buildinfo_text = buildinfo("\r\n");
204 char *text = dupprintf
205 ("%s\r\n\r\n%s\r\n\r\n%s\r\n\r\n%s",
206 appname, ver, buildinfo_text,
207 "\251 " SHORT_COPYRIGHT_DETAILS ". All rights reserved.");
208 sfree(buildinfo_text);
209 SetDlgItemText(hwnd, IDA_TEXT, text);
214 switch (LOWORD(wParam)) {
217 EndDialog(hwnd, TRUE);
220 EnableWindow(hwnd, 0);
221 DialogBox(hinst, MAKEINTRESOURCE(IDD_LICENCEBOX),
223 EnableWindow(hwnd, 1);
224 SetActiveWindow(hwnd);
228 /* Load web browser */
229 ShellExecute(hwnd, "open",
230 "http://www.chiark.greenend.org.uk/~sgtatham/putty/",
231 0, 0, SW_SHOWDEFAULT);
236 EndDialog(hwnd, TRUE);
242 static int SaneDialogBox(HINSTANCE hinst,
245 DLGPROC lpDialogFunc)
254 wc.style = CS_DBLCLKS | CS_SAVEBITS | CS_BYTEALIGNWINDOW;
255 wc.lpfnWndProc = DefDlgProc;
257 wc.cbWndExtra = DLGWINDOWEXTRA + 2*sizeof(LONG_PTR);
258 wc.hInstance = hinst;
260 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
261 wc.hbrBackground = (HBRUSH) (COLOR_BACKGROUND +1);
262 wc.lpszMenuName = NULL;
263 wc.lpszClassName = "PuTTYConfigBox";
266 hwnd = CreateDialog(hinst, tmpl, hwndparent, lpDialogFunc);
268 SetWindowLongPtr(hwnd, BOXFLAGS, 0); /* flags */
269 SetWindowLongPtr(hwnd, BOXRESULT, 0); /* result from SaneEndDialog */
271 while ((gm=GetMessage(&msg, NULL, 0, 0)) > 0) {
272 flags=GetWindowLongPtr(hwnd, BOXFLAGS);
273 if (!(flags & DF_END) && !IsDialogMessage(hwnd, &msg))
274 DispatchMessage(&msg);
280 PostQuitMessage(msg.wParam); /* We got a WM_QUIT, pass it on */
282 ret=GetWindowLongPtr(hwnd, BOXRESULT);
287 static void SaneEndDialog(HWND hwnd, int ret)
289 SetWindowLongPtr(hwnd, BOXRESULT, ret);
290 SetWindowLongPtr(hwnd, BOXFLAGS, DF_END);
294 * Null dialog procedure.
296 static INT_PTR CALLBACK NullDlgProc(HWND hwnd, UINT msg,
297 WPARAM wParam, LPARAM lParam)
303 IDCX_ABOUT = IDC_ABOUT,
307 IDCX_PANELBASE = IDCX_STDBASE + 32
310 struct treeview_faff {
315 static HTREEITEM treeview_insert(struct treeview_faff *faff,
316 int level, char *text, char *path)
321 ins.hParent = (level > 0 ? faff->lastat[level - 1] : TVI_ROOT);
322 ins.hInsertAfter = faff->lastat[level];
323 #if _WIN32_IE >= 0x0400 && defined NONAMELESSUNION
324 #define INSITEM DUMMYUNIONNAME.item
328 ins.INSITEM.mask = TVIF_TEXT | TVIF_PARAM;
329 ins.INSITEM.pszText = text;
330 ins.INSITEM.cchTextMax = strlen(text)+1;
331 ins.INSITEM.lParam = (LPARAM)path;
332 newitem = TreeView_InsertItem(faff->treeview, &ins);
334 TreeView_Expand(faff->treeview, faff->lastat[level - 1],
335 (level > 1 ? TVE_COLLAPSE : TVE_EXPAND));
336 faff->lastat[level] = newitem;
337 for (i = level + 1; i < 4; i++)
338 faff->lastat[i] = NULL;
343 * Create the panelfuls of controls in the configuration box.
345 static void create_controls(HWND hwnd, char *path)
354 * Here we must create the basic standard controls.
356 ctlposinit(&cp, hwnd, 3, 3, 235);
358 base_id = IDCX_STDBASE;
361 * Otherwise, we're creating the controls for a particular
364 ctlposinit(&cp, hwnd, 100, 3, 13);
366 base_id = IDCX_PANELBASE;
369 for (index=-1; (index = ctrl_find_path(ctrlbox, path, index)) >= 0 ;) {
370 struct controlset *s = ctrlbox->ctrlsets[index];
371 winctrl_layout(&dp, wc, &cp, s, &base_id);
376 * This function is the configuration box.
377 * (Being a dialog procedure, in general it returns 0 if the default
378 * dialog processing should be performed, and 1 if it should not.)
380 static INT_PTR CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
381 WPARAM wParam, LPARAM lParam)
384 struct treeview_faff tvfaff;
390 create_controls(hwnd, ""); /* Open and Cancel buttons etc */
391 SetWindowText(hwnd, dp.wintitle);
392 SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
394 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
395 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
398 HWND item = GetDlgItem(hwnd, IDC_HELPBTN);
402 SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
403 (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(IDI_CFGICON)));
407 { /* centre the window */
410 hw = GetDesktopWindow();
411 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
413 (rs.right + rs.left + rd.left - rd.right) / 2,
414 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
415 rd.right - rd.left, rd.bottom - rd.top, TRUE);
419 * Create the tree view.
427 r.right = r.left + 95;
429 r.bottom = r.top + 10;
430 MapDialogRect(hwnd, &r);
431 tvstatic = CreateWindowEx(0, "STATIC", "Cate&gory:",
432 WS_CHILD | WS_VISIBLE,
434 r.right - r.left, r.bottom - r.top,
435 hwnd, (HMENU) IDCX_TVSTATIC, hinst,
437 font = SendMessage(hwnd, WM_GETFONT, 0, 0);
438 SendMessage(tvstatic, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
441 r.right = r.left + 95;
443 r.bottom = r.top + 219;
444 MapDialogRect(hwnd, &r);
445 treeview = CreateWindowEx(WS_EX_CLIENTEDGE, WC_TREEVIEW, "",
446 WS_CHILD | WS_VISIBLE |
447 WS_TABSTOP | TVS_HASLINES |
448 TVS_DISABLEDRAGDROP | TVS_HASBUTTONS
450 TVS_SHOWSELALWAYS, r.left, r.top,
451 r.right - r.left, r.bottom - r.top,
452 hwnd, (HMENU) IDCX_TREEVIEW, hinst,
454 font = SendMessage(hwnd, WM_GETFONT, 0, 0);
455 SendMessage(treeview, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
456 tvfaff.treeview = treeview;
457 memset(tvfaff.lastat, 0, sizeof(tvfaff.lastat));
461 * Set up the tree view contents.
464 HTREEITEM hfirst = NULL;
467 char *firstpath = NULL;
469 for (i = 0; i < ctrlbox->nctrlsets; i++) {
470 struct controlset *s = ctrlbox->ctrlsets[i];
477 j = path ? ctrl_path_compare(s->pathname, path) : 0;
479 continue; /* same path, nothing to add to tree */
482 * We expect never to find an implicit path
483 * component. For example, we expect never to see
484 * A/B/C followed by A/D/E, because that would
485 * _implicitly_ create A/D. All our path prefixes
486 * are expected to contain actual controls and be
487 * selectable in the treeview; so we would expect
488 * to see A/D _explicitly_ before encountering
491 assert(j == ctrl_path_elements(s->pathname) - 1);
493 c = strrchr(s->pathname, '/');
499 item = treeview_insert(&tvfaff, j, c, s->pathname);
502 firstpath = s->pathname;
509 * Put the treeview selection on to the first panel in the
512 TreeView_SelectItem(treeview, hfirst);
515 * And create the actual control set for that panel, to
516 * match the initial treeview selection.
518 assert(firstpath); /* config.c must have given us _something_ */
519 create_controls(hwnd, firstpath);
520 dlg_refresh(NULL, &dp); /* and set up control values */
524 * Set focus into the first available control.
530 for (i = 0; (c = winctrl_findbyindex(&ctrls_panel, i)) != NULL;
533 dlg_set_focus(c->ctrl, &dp);
540 * Now we've finished creating our initial set of controls,
541 * it's safe to actually show the window without risking setup
544 ShowWindow(hwnd, SW_SHOWNORMAL);
547 * Set the flag that activates a couple of the other message
548 * handlers below, which were disabled until now to avoid
549 * spurious firing during the above setup procedure.
551 SetWindowLongPtr(hwnd, GWLP_USERDATA, 1);
555 * Button release should trigger WM_OK if there was a
556 * previous double click on the session list.
560 SaneEndDialog(hwnd, dp.endresult ? 1 : 0);
563 if (LOWORD(wParam) == IDCX_TREEVIEW &&
564 ((LPNMHDR) lParam)->code == TVN_SELCHANGED) {
566 * Selection-change events on the treeview cause us to do
567 * a flurry of control deletion and creation - but only
568 * after WM_INITDIALOG has finished. The initial
569 * selection-change event(s) during treeview setup are
576 if (GetWindowLongPtr(hwnd, GWLP_USERDATA) != 1)
579 i = TreeView_GetSelection(((LPNMHDR) lParam)->hwndFrom);
581 SendMessage (hwnd, WM_SETREDRAW, FALSE, 0);
584 item.pszText = buffer;
585 item.cchTextMax = sizeof(buffer);
586 item.mask = TVIF_TEXT | TVIF_PARAM;
587 TreeView_GetItem(((LPNMHDR) lParam)->hwndFrom, &item);
589 /* Destroy all controls in the currently visible panel. */
594 while ((c = winctrl_findbyindex(&ctrls_panel, 0)) != NULL) {
595 for (k = 0; k < c->num_ids; k++) {
596 item = GetDlgItem(hwnd, c->base_id + k);
600 winctrl_rem_shortcuts(&dp, c);
601 winctrl_remove(&ctrls_panel, c);
606 create_controls(hwnd, (char *)item.lParam);
608 dlg_refresh(NULL, &dp); /* set up control values */
610 SendMessage (hwnd, WM_SETREDRAW, TRUE, 0);
611 InvalidateRect (hwnd, NULL, TRUE);
613 SetFocus(((LPNMHDR) lParam)->hwndFrom); /* ensure focus stays */
619 default: /* also handle drag list msg here */
621 * Only process WM_COMMAND once the dialog is fully formed.
623 if (GetWindowLongPtr(hwnd, GWLP_USERDATA) == 1) {
624 ret = winctrl_handle_command(&dp, msg, wParam, lParam);
625 if (dp.ended && GetCapture() != hwnd)
626 SaneEndDialog(hwnd, dp.endresult ? 1 : 0);
631 if (!winctrl_context_help(&dp, hwnd,
632 ((LPHELPINFO)lParam)->iCtrlId))
637 SaneEndDialog(hwnd, 0);
640 /* Grrr Explorer will maximize Dialogs! */
642 if (wParam == SIZE_MAXIMIZED)
650 void modal_about_box(HWND hwnd)
652 EnableWindow(hwnd, 0);
653 DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
654 EnableWindow(hwnd, 1);
655 SetActiveWindow(hwnd);
658 void show_help(HWND hwnd)
660 launch_help(hwnd, NULL);
663 void defuse_showwindow(void)
666 * Work around the fact that the app's first call to ShowWindow
667 * will ignore the default in favour of the shell-provided
672 hwnd = CreateDialog(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX),
674 ShowWindow(hwnd, SW_HIDE);
675 SetActiveWindow(hwnd);
684 ctrlbox = ctrl_new_box();
685 setup_config_box(ctrlbox, FALSE, 0, 0);
686 win_setup_config_box(ctrlbox, &dp.hwnd, has_help(), FALSE, 0);
688 winctrl_init(&ctrls_base);
689 winctrl_init(&ctrls_panel);
690 dp_add_tree(&dp, &ctrls_base);
691 dp_add_tree(&dp, &ctrls_panel);
692 dp.wintitle = dupprintf("%s Configuration", appname);
693 dp.errtitle = dupprintf("%s Error", appname);
695 dlg_auto_set_fixed_pitch_flag(&dp);
696 dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
699 SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
702 ctrl_free_box(ctrlbox);
703 winctrl_cleanup(&ctrls_panel);
704 winctrl_cleanup(&ctrls_base);
710 int do_reconfig(HWND hwnd, int protcfginfo)
715 backup_conf = conf_copy(conf);
717 ctrlbox = ctrl_new_box();
718 protocol = conf_get_int(conf, CONF_protocol);
719 setup_config_box(ctrlbox, TRUE, protocol, protcfginfo);
720 win_setup_config_box(ctrlbox, &dp.hwnd, has_help(), TRUE, protocol);
722 winctrl_init(&ctrls_base);
723 winctrl_init(&ctrls_panel);
724 dp_add_tree(&dp, &ctrls_base);
725 dp_add_tree(&dp, &ctrls_panel);
726 dp.wintitle = dupprintf("%s Reconfiguration", appname);
727 dp.errtitle = dupprintf("%s Error", appname);
729 dlg_auto_set_fixed_pitch_flag(&dp);
730 dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
732 ret = SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
735 ctrl_free_box(ctrlbox);
736 winctrl_cleanup(&ctrls_base);
737 winctrl_cleanup(&ctrls_panel);
741 conf_copy_into(conf, backup_conf);
743 conf_free(backup_conf);
748 void logevent(void *frontend, const char *string)
753 log_eventlog(logctx, string);
755 if (nevents >= negsize) {
757 events = sresize(events, negsize, char *);
761 strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t", &tm);
763 events[nevents] = snewn(strlen(timebuf) + strlen(string) + 1, char);
764 strcpy(events[nevents], timebuf);
765 strcat(events[nevents], string);
768 SendDlgItemMessage(logbox, IDN_LIST, LB_ADDSTRING,
769 0, (LPARAM) events[nevents]);
770 count = SendDlgItemMessage(logbox, IDN_LIST, LB_GETCOUNT, 0, 0);
771 SendDlgItemMessage(logbox, IDN_LIST, LB_SETTOPINDEX, count - 1, 0);
776 void showeventlog(HWND hwnd)
779 logbox = CreateDialog(hinst, MAKEINTRESOURCE(IDD_LOGBOX),
781 ShowWindow(logbox, SW_SHOWNORMAL);
783 SetActiveWindow(logbox);
786 void showabout(HWND hwnd)
788 DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
791 int verify_ssh_host_key(void *frontend, char *host, int port,
792 const char *keytype, char *keystr, char *fingerprint,
793 void (*callback)(void *ctx, int result), void *ctx)
797 static const char absentmsg[] =
798 "The server's host key is not cached in the registry. You\n"
799 "have no guarantee that the server is the computer you\n"
801 "The server's %s key fingerprint is:\n"
803 "If you trust this host, hit Yes to add the key to\n"
804 "%s's cache and carry on connecting.\n"
805 "If you want to carry on connecting just once, without\n"
806 "adding the key to the cache, hit No.\n"
807 "If you do not trust this host, hit Cancel to abandon the\n"
810 static const char wrongmsg[] =
811 "WARNING - POTENTIAL SECURITY BREACH!\n"
813 "The server's host key does not match the one %s has\n"
814 "cached in the registry. This means that either the\n"
815 "server administrator has changed the host key, or you\n"
816 "have actually connected to another computer pretending\n"
817 "to be the server.\n"
818 "The new %s key fingerprint is:\n"
820 "If you were expecting this change and trust the new key,\n"
821 "hit Yes to update %s's cache and continue connecting.\n"
822 "If you want to carry on connecting but without updating\n"
823 "the cache, hit No.\n"
824 "If you want to abandon the connection completely, hit\n"
825 "Cancel. Hitting Cancel is the ONLY guaranteed safe\n" "choice.\n";
827 static const char mbtitle[] = "%s Security Alert";
830 * Verify the key against the registry.
832 ret = verify_host_key(host, port, keytype, keystr);
834 if (ret == 0) /* success - key matched OK */
836 else if (ret == 2) { /* key was different */
838 char *text = dupprintf(wrongmsg, appname, keytype, fingerprint,
840 char *caption = dupprintf(mbtitle, appname);
841 mbret = message_box(text, caption,
842 MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3,
843 HELPCTXID(errors_hostkey_changed));
844 assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);
847 if (mbret == IDYES) {
848 store_host_key(host, port, keytype, keystr);
850 } else if (mbret == IDNO)
852 } else if (ret == 1) { /* key was absent */
854 char *text = dupprintf(absentmsg, keytype, fingerprint, appname);
855 char *caption = dupprintf(mbtitle, appname);
856 mbret = message_box(text, caption,
857 MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3,
858 HELPCTXID(errors_hostkey_absent));
859 assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);
862 if (mbret == IDYES) {
863 store_host_key(host, port, keytype, keystr);
865 } else if (mbret == IDNO)
868 return 0; /* abandon the connection */
872 * Ask whether the selected algorithm is acceptable (since it was
873 * below the configured 'warn' threshold).
875 int askalg(void *frontend, const char *algtype, const char *algname,
876 void (*callback)(void *ctx, int result), void *ctx)
878 static const char mbtitle[] = "%s Security Alert";
879 static const char msg[] =
880 "The first %s supported by the server\n"
881 "is %.64s, which is below the configured\n"
882 "warning threshold.\n"
883 "Do you want to continue with this connection?\n";
884 char *message, *title;
887 message = dupprintf(msg, algtype, algname);
888 title = dupprintf(mbtitle, appname);
889 mbret = MessageBox(NULL, message, title,
890 MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2);
891 socket_reselect_all();
900 int askhk(void *frontend, const char *algname, const char *betteralgs,
901 void (*callback)(void *ctx, int result), void *ctx)
903 static const char mbtitle[] = "%s Security Alert";
904 static const char msg[] =
905 "The first host key type we have stored for this server\n"
906 "is %s, which is below the configured warning threshold.\n"
907 "The server also provides the following types of host key\n"
908 "above the threshold, which we do not have stored:\n"
910 "Do you want to continue with this connection?\n";
911 char *message, *title;
914 message = dupprintf(msg, algname, betteralgs);
915 title = dupprintf(mbtitle, appname);
916 mbret = MessageBox(NULL, message, title,
917 MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2);
918 socket_reselect_all();
928 * Ask whether to wipe a session log file before writing to it.
929 * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
931 int askappend(void *frontend, Filename *filename,
932 void (*callback)(void *ctx, int result), void *ctx)
934 static const char msgtemplate[] =
935 "The session log file \"%.*s\" already exists.\n"
936 "You can overwrite it with a new session log,\n"
937 "append your session log to the end of it,\n"
938 "or disable session logging for this session.\n"
939 "Hit Yes to wipe the file, No to append to it,\n"
940 "or Cancel to disable logging.";
945 message = dupprintf(msgtemplate, FILENAME_MAX, filename->path);
946 mbtitle = dupprintf("%s Log to File", appname);
948 mbret = MessageBox(NULL, message, mbtitle,
949 MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON3);
951 socket_reselect_all();
958 else if (mbret == IDNO)
965 * Warn about the obsolescent key file format.
967 * Uniquely among these functions, this one does _not_ expect a
968 * frontend handle. This means that if PuTTY is ported to a
969 * platform which requires frontend handles, this function will be
970 * an anomaly. Fortunately, the problem it addresses will not have
971 * been present on that platform, so it can plausibly be
972 * implemented as an empty function.
974 void old_keyfile_warning(void)
976 static const char mbtitle[] = "%s Key File Warning";
977 static const char message[] =
978 "You are loading an SSH-2 private key which has an\n"
979 "old version of the file format. This means your key\n"
980 "file is not fully tamperproof. Future versions of\n"
981 "%s may stop supporting this private key format,\n"
982 "so we recommend you convert your key to the new\n"
985 "You can perform this conversion by loading the key\n"
986 "into PuTTYgen and then saving it again.";
989 msg = dupprintf(message, appname);
990 title = dupprintf(mbtitle, appname);
992 MessageBox(NULL, msg, title, MB_OK);
994 socket_reselect_all();