19 #define TVINSERTSTRUCT TV_INSERTSTRUCT
20 #define TVITEM TV_ITEM
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.
31 static struct controlbox *ctrlbox;
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.
37 static struct winctrls ctrls_base, ctrls_panel;
38 static struct dlgparam dp;
40 static char **events = NULL;
41 static int nevents = 0, negsize = 0;
43 extern Config cfg; /* defined in window.c */
45 #define PRINTER_DISABLED_STRING "None (printing disabled)"
47 void force_normal(HWND hwnd)
49 static int recurse = 0;
57 wp.length = sizeof(wp);
58 if (GetWindowPlacement(hwnd, &wp) && wp.showCmd == SW_SHOWMAXIMIZED) {
59 wp.showCmd = SW_SHOWNORMAL;
60 SetWindowPlacement(hwnd, &wp);
65 static int CALLBACK LogProc(HWND hwnd, UINT msg,
66 WPARAM wParam, LPARAM lParam)
73 char *str = dupprintf("%s Event Log", appname);
74 SetWindowText(hwnd, str);
78 static int tabs[4] = { 78, 108 };
79 SendDlgItemMessage(hwnd, IDN_LIST, LB_SETTABSTOPS, 2,
82 for (i = 0; i < nevents; i++)
83 SendDlgItemMessage(hwnd, IDN_LIST, LB_ADDSTRING,
84 0, (LPARAM) events[i]);
87 switch (LOWORD(wParam)) {
91 SetActiveWindow(GetParent(hwnd));
95 if (HIWORD(wParam) == BN_CLICKED ||
96 HIWORD(wParam) == BN_DOUBLECLICKED) {
99 selcount = SendDlgItemMessage(hwnd, IDN_LIST,
100 LB_GETSELCOUNT, 0, 0);
101 if (selcount == 0) { /* don't even try to copy zero items */
106 selitems = snewn(selcount, int);
108 int count = SendDlgItemMessage(hwnd, IDN_LIST,
115 static unsigned char sel_nl[] = SEL_NL;
117 if (count == 0) { /* can't copy zero stuff */
123 for (i = 0; i < count; i++)
125 strlen(events[selitems[i]]) + sizeof(sel_nl);
127 clipdata = snewn(size, char);
130 for (i = 0; i < count; i++) {
131 char *q = events[selitems[i]];
132 int qlen = strlen(q);
135 memcpy(p, sel_nl, sizeof(sel_nl));
138 write_aclip(NULL, clipdata, size, TRUE);
143 for (i = 0; i < nevents; i++)
144 SendDlgItemMessage(hwnd, IDN_LIST, LB_SETSEL,
153 SetActiveWindow(GetParent(hwnd));
160 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
161 WPARAM wParam, LPARAM lParam)
166 char *str = dupprintf("%s Licence", appname);
167 SetWindowText(hwnd, str);
172 switch (LOWORD(wParam)) {
186 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
187 WPARAM wParam, LPARAM lParam)
193 str = dupprintf("About %s", appname);
194 SetWindowText(hwnd, str);
196 SetDlgItemText(hwnd, IDA_TEXT1, appname);
197 SetDlgItemText(hwnd, IDA_VERSION, ver);
200 switch (LOWORD(wParam)) {
203 EndDialog(hwnd, TRUE);
206 EnableWindow(hwnd, 0);
207 DialogBox(hinst, MAKEINTRESOURCE(IDD_LICENCEBOX),
209 EnableWindow(hwnd, 1);
210 SetActiveWindow(hwnd);
214 /* Load web browser */
215 ShellExecute(hwnd, "open",
216 "http://www.chiark.greenend.org.uk/~sgtatham/putty/",
217 0, 0, SW_SHOWDEFAULT);
222 EndDialog(hwnd, TRUE);
228 static int SaneDialogBox(HINSTANCE hinst,
231 DLGPROC lpDialogFunc)
240 wc.style = CS_DBLCLKS | CS_SAVEBITS | CS_BYTEALIGNWINDOW;
241 wc.lpfnWndProc = DefDlgProc;
243 wc.cbWndExtra = DLGWINDOWEXTRA + 2*sizeof(LONG_PTR);
244 wc.hInstance = hinst;
246 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
247 wc.hbrBackground = (HBRUSH) (COLOR_BACKGROUND +1);
248 wc.lpszMenuName = NULL;
249 wc.lpszClassName = "PuTTYConfigBox";
252 hwnd = CreateDialog(hinst, tmpl, hwndparent, lpDialogFunc);
254 SetWindowLongPtr(hwnd, BOXFLAGS, 0); /* flags */
255 SetWindowLongPtr(hwnd, BOXRESULT, 0); /* result from SaneEndDialog */
257 while ((gm=GetMessage(&msg, NULL, 0, 0)) > 0) {
258 flags=GetWindowLongPtr(hwnd, BOXFLAGS);
259 if (!(flags & DF_END) && !IsDialogMessage(hwnd, &msg))
260 DispatchMessage(&msg);
266 PostQuitMessage(msg.wParam); /* We got a WM_QUIT, pass it on */
268 ret=GetWindowLongPtr(hwnd, BOXRESULT);
273 static void SaneEndDialog(HWND hwnd, int ret)
275 SetWindowLongPtr(hwnd, BOXRESULT, ret);
276 SetWindowLongPtr(hwnd, BOXFLAGS, DF_END);
280 * Null dialog procedure.
282 static int CALLBACK NullDlgProc(HWND hwnd, UINT msg,
283 WPARAM wParam, LPARAM lParam)
289 IDCX_ABOUT = IDC_ABOUT,
293 IDCX_PANELBASE = IDCX_STDBASE + 32
296 struct treeview_faff {
301 static HTREEITEM treeview_insert(struct treeview_faff *faff,
302 int level, char *text, char *path)
307 ins.hParent = (level > 0 ? faff->lastat[level - 1] : TVI_ROOT);
308 ins.hInsertAfter = faff->lastat[level];
309 #if _WIN32_IE >= 0x0400 && defined NONAMELESSUNION
310 #define INSITEM DUMMYUNIONNAME.item
314 ins.INSITEM.mask = TVIF_TEXT | TVIF_PARAM;
315 ins.INSITEM.pszText = text;
316 ins.INSITEM.cchTextMax = strlen(text)+1;
317 ins.INSITEM.lParam = (LPARAM)path;
318 newitem = TreeView_InsertItem(faff->treeview, &ins);
320 TreeView_Expand(faff->treeview, faff->lastat[level - 1],
322 faff->lastat[level] = newitem;
323 for (i = level + 1; i < 4; i++)
324 faff->lastat[i] = NULL;
329 * Create the panelfuls of controls in the configuration box.
331 static void create_controls(HWND hwnd, char *path)
340 * Here we must create the basic standard controls.
342 ctlposinit(&cp, hwnd, 3, 3, 235);
344 base_id = IDCX_STDBASE;
347 * Otherwise, we're creating the controls for a particular
350 ctlposinit(&cp, hwnd, 100, 3, 13);
352 base_id = IDCX_PANELBASE;
355 for (index=-1; (index = ctrl_find_path(ctrlbox, path, index)) >= 0 ;) {
356 struct controlset *s = ctrlbox->ctrlsets[index];
357 winctrl_layout(&dp, wc, &cp, s, &base_id);
362 * This function is the configuration box.
363 * (Being a dialog procedure, in general it returns 0 if the default
364 * dialog processing should be performed, and 1 if it should not.)
366 static int CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
367 WPARAM wParam, LPARAM lParam)
370 struct treeview_faff tvfaff;
376 create_controls(hwnd, ""); /* Open and Cancel buttons etc */
377 SetWindowText(hwnd, dp.wintitle);
378 SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
380 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
381 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
384 HWND item = GetDlgItem(hwnd, IDC_HELPBTN);
388 requested_help = FALSE;
389 SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
390 (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(IDI_CFGICON)));
394 { /* centre the window */
397 hw = GetDesktopWindow();
398 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
400 (rs.right + rs.left + rd.left - rd.right) / 2,
401 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
402 rd.right - rd.left, rd.bottom - rd.top, TRUE);
406 * Create the tree view.
414 r.right = r.left + 95;
416 r.bottom = r.top + 10;
417 MapDialogRect(hwnd, &r);
418 tvstatic = CreateWindowEx(0, "STATIC", "Cate&gory:",
419 WS_CHILD | WS_VISIBLE,
421 r.right - r.left, r.bottom - r.top,
422 hwnd, (HMENU) IDCX_TVSTATIC, hinst,
424 font = SendMessage(hwnd, WM_GETFONT, 0, 0);
425 SendMessage(tvstatic, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
428 r.right = r.left + 95;
430 r.bottom = r.top + 219;
431 MapDialogRect(hwnd, &r);
432 treeview = CreateWindowEx(WS_EX_CLIENTEDGE, WC_TREEVIEW, "",
433 WS_CHILD | WS_VISIBLE |
434 WS_TABSTOP | TVS_HASLINES |
435 TVS_DISABLEDRAGDROP | TVS_HASBUTTONS
437 TVS_SHOWSELALWAYS, r.left, r.top,
438 r.right - r.left, r.bottom - r.top,
439 hwnd, (HMENU) IDCX_TREEVIEW, hinst,
441 font = SendMessage(hwnd, WM_GETFONT, 0, 0);
442 SendMessage(treeview, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
443 tvfaff.treeview = treeview;
444 memset(tvfaff.lastat, 0, sizeof(tvfaff.lastat));
448 * Set up the tree view contents.
451 HTREEITEM hfirst = NULL;
455 for (i = 0; i < ctrlbox->nctrlsets; i++) {
456 struct controlset *s = ctrlbox->ctrlsets[i];
463 j = path ? ctrl_path_compare(s->pathname, path) : 0;
465 continue; /* same path, nothing to add to tree */
468 * We expect never to find an implicit path
469 * component. For example, we expect never to see
470 * A/B/C followed by A/D/E, because that would
471 * _implicitly_ create A/D. All our path prefixes
472 * are expected to contain actual controls and be
473 * selectable in the treeview; so we would expect
474 * to see A/D _explicitly_ before encountering
477 assert(j == ctrl_path_elements(s->pathname) - 1);
479 c = strrchr(s->pathname, '/');
485 item = treeview_insert(&tvfaff, j, c, s->pathname);
493 * Put the treeview selection on to the Session panel.
494 * This should also cause creation of the relevant
497 TreeView_SelectItem(treeview, hfirst);
501 * Set focus into the first available control.
507 for (i = 0; (c = winctrl_findbyindex(&ctrls_panel, i)) != NULL;
510 dlg_set_focus(c->ctrl, &dp);
516 SetWindowLongPtr(hwnd, GWLP_USERDATA, 1);
520 * Button release should trigger WM_OK if there was a
521 * previous double click on the session list.
525 SaneEndDialog(hwnd, dp.endresult ? 1 : 0);
528 if (LOWORD(wParam) == IDCX_TREEVIEW &&
529 ((LPNMHDR) lParam)->code == TVN_SELCHANGED) {
531 TreeView_GetSelection(((LPNMHDR) lParam)->hwndFrom);
535 SendMessage (hwnd, WM_SETREDRAW, FALSE, 0);
538 item.pszText = buffer;
539 item.cchTextMax = sizeof(buffer);
540 item.mask = TVIF_TEXT | TVIF_PARAM;
541 TreeView_GetItem(((LPNMHDR) lParam)->hwndFrom, &item);
543 /* Destroy all controls in the currently visible panel. */
548 while ((c = winctrl_findbyindex(&ctrls_panel, 0)) != NULL) {
549 for (k = 0; k < c->num_ids; k++) {
550 item = GetDlgItem(hwnd, c->base_id + k);
554 winctrl_rem_shortcuts(&dp, c);
555 winctrl_remove(&ctrls_panel, c);
560 create_controls(hwnd, (char *)item.lParam);
562 dlg_refresh(NULL, &dp); /* set up control values */
564 SendMessage (hwnd, WM_SETREDRAW, TRUE, 0);
565 InvalidateRect (hwnd, NULL, TRUE);
567 SetFocus(((LPNMHDR) lParam)->hwndFrom); /* ensure focus stays */
573 default: /* also handle drag list msg here */
575 * Only process WM_COMMAND once the dialog is fully formed.
577 if (GetWindowLongPtr(hwnd, GWLP_USERDATA) == 1) {
578 ret = winctrl_handle_command(&dp, msg, wParam, lParam);
579 if (dp.ended && GetCapture() != hwnd)
580 SaneEndDialog(hwnd, dp.endresult ? 1 : 0);
586 if (winctrl_context_help(&dp, hwnd,
587 ((LPHELPINFO)lParam)->iCtrlId))
588 requested_help = TRUE;
594 if (requested_help) {
595 WinHelp(hwnd, help_path, HELP_QUIT, 0);
596 requested_help = FALSE;
598 SaneEndDialog(hwnd, 0);
601 /* Grrr Explorer will maximize Dialogs! */
603 if (wParam == SIZE_MAXIMIZED)
611 void modal_about_box(HWND hwnd)
613 EnableWindow(hwnd, 0);
614 DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
615 EnableWindow(hwnd, 1);
616 SetActiveWindow(hwnd);
619 void show_help(HWND hwnd)
622 WinHelp(hwnd, help_path,
623 help_has_contents ? HELP_FINDER : HELP_CONTENTS,
625 requested_help = TRUE;
629 void defuse_showwindow(void)
632 * Work around the fact that the app's first call to ShowWindow
633 * will ignore the default in favour of the shell-provided
638 hwnd = CreateDialog(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX),
640 ShowWindow(hwnd, SW_HIDE);
641 SetActiveWindow(hwnd);
650 ctrlbox = ctrl_new_box();
651 setup_config_box(ctrlbox, FALSE, 0, 0);
652 win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), FALSE);
654 winctrl_init(&ctrls_base);
655 winctrl_init(&ctrls_panel);
656 dp_add_tree(&dp, &ctrls_base);
657 dp_add_tree(&dp, &ctrls_panel);
658 dp.wintitle = dupprintf("%s Configuration", appname);
659 dp.errtitle = dupprintf("%s Error", appname);
661 dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
664 SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
667 ctrl_free_box(ctrlbox);
668 winctrl_cleanup(&ctrls_panel);
669 winctrl_cleanup(&ctrls_base);
675 int do_reconfig(HWND hwnd, int protcfginfo)
680 backup_cfg = cfg; /* structure copy */
682 ctrlbox = ctrl_new_box();
683 setup_config_box(ctrlbox, TRUE, cfg.protocol, protcfginfo);
684 win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), TRUE);
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 Reconfiguration", appname);
691 dp.errtitle = dupprintf("%s Error", appname);
693 dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
695 ret = SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
698 ctrl_free_box(ctrlbox);
699 winctrl_cleanup(&ctrls_base);
700 winctrl_cleanup(&ctrls_panel);
704 cfg = backup_cfg; /* structure copy */
709 void logevent(void *frontend, const char *string)
714 log_eventlog(logctx, string);
716 if (nevents >= negsize) {
718 events = sresize(events, negsize, char *);
722 strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t", &tm);
724 events[nevents] = snewn(strlen(timebuf) + strlen(string) + 1, char);
725 strcpy(events[nevents], timebuf);
726 strcat(events[nevents], string);
729 SendDlgItemMessage(logbox, IDN_LIST, LB_ADDSTRING,
730 0, (LPARAM) events[nevents]);
731 count = SendDlgItemMessage(logbox, IDN_LIST, LB_GETCOUNT, 0, 0);
732 SendDlgItemMessage(logbox, IDN_LIST, LB_SETTOPINDEX, count - 1, 0);
737 void showeventlog(HWND hwnd)
740 logbox = CreateDialog(hinst, MAKEINTRESOURCE(IDD_LOGBOX),
742 ShowWindow(logbox, SW_SHOWNORMAL);
744 SetActiveWindow(logbox);
747 void showabout(HWND hwnd)
749 DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
752 int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
753 char *keystr, char *fingerprint,
754 void (*callback)(void *ctx, int result), void *ctx)
758 static const char absentmsg[] =
759 "The server's host key is not cached in the registry. You\n"
760 "have no guarantee that the server is the computer you\n"
762 "The server's %s key fingerprint is:\n"
764 "If you trust this host, hit Yes to add the key to\n"
765 "%s's cache and carry on connecting.\n"
766 "If you want to carry on connecting just once, without\n"
767 "adding the key to the cache, hit No.\n"
768 "If you do not trust this host, hit Cancel to abandon the\n"
771 static const char wrongmsg[] =
772 "WARNING - POTENTIAL SECURITY BREACH!\n"
774 "The server's host key does not match the one %s has\n"
775 "cached in the registry. This means that either the\n"
776 "server administrator has changed the host key, or you\n"
777 "have actually connected to another computer pretending\n"
778 "to be the server.\n"
779 "The new %s key fingerprint is:\n"
781 "If you were expecting this change and trust the new key,\n"
782 "hit Yes to update %s's cache and continue connecting.\n"
783 "If you want to carry on connecting but without updating\n"
784 "the cache, hit No.\n"
785 "If you want to abandon the connection completely, hit\n"
786 "Cancel. Hitting Cancel is the ONLY guaranteed safe\n" "choice.\n";
788 static const char mbtitle[] = "%s Security Alert";
791 * Verify the key against the registry.
793 ret = verify_host_key(host, port, keytype, keystr);
795 if (ret == 0) /* success - key matched OK */
797 if (ret == 2) { /* key was different */
799 char *text = dupprintf(wrongmsg, appname, keytype, fingerprint,
801 char *caption = dupprintf(mbtitle, appname);
802 mbret = message_box(text, caption,
803 MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3,
804 HELPCTXID(errors_hostkey_changed));
805 assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);
808 if (mbret == IDYES) {
809 store_host_key(host, port, keytype, keystr);
811 } else if (mbret == IDNO)
815 if (ret == 1) { /* key was absent */
817 char *text = dupprintf(absentmsg, keytype, fingerprint, appname);
818 char *caption = dupprintf(mbtitle, appname);
819 mbret = message_box(text, caption,
820 MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3,
821 HELPCTXID(errors_hostkey_absent));
822 assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);
825 if (mbret == IDYES) {
826 store_host_key(host, port, keytype, keystr);
828 } else if (mbret == IDNO)
835 * Ask whether the selected algorithm is acceptable (since it was
836 * below the configured 'warn' threshold).
838 int askalg(void *frontend, const char *algtype, const char *algname,
839 void (*callback)(void *ctx, int result), void *ctx)
841 static const char mbtitle[] = "%s Security Alert";
842 static const char msg[] =
843 "The first %s supported by the server\n"
844 "is %.64s, which is below the configured\n"
845 "warning threshold.\n"
846 "Do you want to continue with this connection?\n";
847 char *message, *title;
850 message = dupprintf(msg, algtype, algname);
851 title = dupprintf(mbtitle, appname);
852 mbret = MessageBox(NULL, message, title,
853 MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2);
863 * Ask whether to wipe a session log file before writing to it.
864 * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
866 int askappend(void *frontend, Filename filename,
867 void (*callback)(void *ctx, int result), void *ctx)
869 static const char msgtemplate[] =
870 "The session log file \"%.*s\" already exists.\n"
871 "You can overwrite it with a new session log,\n"
872 "append your session log to the end of it,\n"
873 "or disable session logging for this session.\n"
874 "Hit Yes to wipe the file, No to append to it,\n"
875 "or Cancel to disable logging.";
880 message = dupprintf(msgtemplate, FILENAME_MAX, filename.path);
881 mbtitle = dupprintf("%s Log to File", appname);
883 mbret = MessageBox(NULL, message, mbtitle,
884 MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON3);
891 else if (mbret == IDNO)
898 * Warn about the obsolescent key file format.
900 * Uniquely among these functions, this one does _not_ expect a
901 * frontend handle. This means that if PuTTY is ported to a
902 * platform which requires frontend handles, this function will be
903 * an anomaly. Fortunately, the problem it addresses will not have
904 * been present on that platform, so it can plausibly be
905 * implemented as an empty function.
907 void old_keyfile_warning(void)
909 static const char mbtitle[] = "%s Key File Warning";
910 static const char message[] =
911 "You are loading an SSH-2 private key which has an\n"
912 "old version of the file format. This means your key\n"
913 "file is not fully tamperproof. Future versions of\n"
914 "%s may stop supporting this private key format,\n"
915 "so we recommend you convert your key to the new\n"
918 "You can perform this conversion by loading the key\n"
919 "into PuTTYgen and then saving it again.";
922 msg = dupprintf(message, appname);
923 title = dupprintf(mbtitle, appname);
925 MessageBox(NULL, msg, title, MB_OK);