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 struct sesslist sesslist; /* exported to window.c */
47 #define PRINTER_DISABLED_STRING "None (printing disabled)"
49 void force_normal(HWND hwnd)
51 static int recurse = 0;
59 wp.length = sizeof(wp);
60 if (GetWindowPlacement(hwnd, &wp) && wp.showCmd == SW_SHOWMAXIMIZED) {
61 wp.showCmd = SW_SHOWNORMAL;
62 SetWindowPlacement(hwnd, &wp);
67 static int CALLBACK LogProc(HWND hwnd, UINT msg,
68 WPARAM wParam, LPARAM lParam)
75 char *str = dupprintf("%s Event Log", appname);
76 SetWindowText(hwnd, str);
80 static int tabs[4] = { 78, 108 };
81 SendDlgItemMessage(hwnd, IDN_LIST, LB_SETTABSTOPS, 2,
84 for (i = 0; i < nevents; i++)
85 SendDlgItemMessage(hwnd, IDN_LIST, LB_ADDSTRING,
86 0, (LPARAM) events[i]);
89 switch (LOWORD(wParam)) {
93 SetActiveWindow(GetParent(hwnd));
97 if (HIWORD(wParam) == BN_CLICKED ||
98 HIWORD(wParam) == BN_DOUBLECLICKED) {
101 selcount = SendDlgItemMessage(hwnd, IDN_LIST,
102 LB_GETSELCOUNT, 0, 0);
103 if (selcount == 0) { /* don't even try to copy zero items */
108 selitems = snewn(selcount, int);
110 int count = SendDlgItemMessage(hwnd, IDN_LIST,
117 static unsigned char sel_nl[] = SEL_NL;
119 if (count == 0) { /* can't copy zero stuff */
125 for (i = 0; i < count; i++)
127 strlen(events[selitems[i]]) + sizeof(sel_nl);
129 clipdata = snewn(size, char);
132 for (i = 0; i < count; i++) {
133 char *q = events[selitems[i]];
134 int qlen = strlen(q);
137 memcpy(p, sel_nl, sizeof(sel_nl));
140 write_aclip(NULL, clipdata, size, TRUE);
145 for (i = 0; i < nevents; i++)
146 SendDlgItemMessage(hwnd, IDN_LIST, LB_SETSEL,
155 SetActiveWindow(GetParent(hwnd));
162 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
163 WPARAM wParam, LPARAM lParam)
168 char *str = dupprintf("%s Licence", appname);
169 SetWindowText(hwnd, str);
174 switch (LOWORD(wParam)) {
188 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
189 WPARAM wParam, LPARAM lParam)
195 str = dupprintf("About %s", appname);
196 SetWindowText(hwnd, str);
198 SetDlgItemText(hwnd, IDA_TEXT1, appname);
199 SetDlgItemText(hwnd, IDA_VERSION, ver);
202 switch (LOWORD(wParam)) {
205 EndDialog(hwnd, TRUE);
208 EnableWindow(hwnd, 0);
209 DialogBox(hinst, MAKEINTRESOURCE(IDD_LICENCEBOX),
211 EnableWindow(hwnd, 1);
212 SetActiveWindow(hwnd);
216 /* Load web browser */
217 ShellExecute(hwnd, "open",
218 "http://www.chiark.greenend.org.uk/~sgtatham/putty/",
219 0, 0, SW_SHOWDEFAULT);
224 EndDialog(hwnd, TRUE);
230 static int SaneDialogBox(HINSTANCE hinst,
233 DLGPROC lpDialogFunc)
242 wc.style = CS_DBLCLKS | CS_SAVEBITS | CS_BYTEALIGNWINDOW;
243 wc.lpfnWndProc = DefDlgProc;
245 wc.cbWndExtra = DLGWINDOWEXTRA + 8;
246 wc.hInstance = hinst;
248 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
249 wc.hbrBackground = (HBRUSH) (COLOR_BACKGROUND +1);
250 wc.lpszMenuName = NULL;
251 wc.lpszClassName = "PuTTYConfigBox";
254 hwnd = CreateDialog(hinst, tmpl, hwndparent, lpDialogFunc);
256 SetWindowLong(hwnd, BOXFLAGS, 0); /* flags */
257 SetWindowLong(hwnd, BOXRESULT, 0); /* result from SaneEndDialog */
259 while ((gm=GetMessage(&msg, NULL, 0, 0)) > 0) {
260 flags=GetWindowLong(hwnd, BOXFLAGS);
261 if (!(flags & DF_END) && !IsDialogMessage(hwnd, &msg))
262 DispatchMessage(&msg);
268 PostQuitMessage(msg.wParam); /* We got a WM_QUIT, pass it on */
270 ret=GetWindowLong(hwnd, BOXRESULT);
275 static void SaneEndDialog(HWND hwnd, int ret)
277 SetWindowLong(hwnd, BOXRESULT, ret);
278 SetWindowLong(hwnd, BOXFLAGS, DF_END);
282 * Null dialog procedure.
284 static int CALLBACK NullDlgProc(HWND hwnd, UINT msg,
285 WPARAM wParam, LPARAM lParam)
291 IDCX_ABOUT = IDC_ABOUT,
295 IDCX_PANELBASE = IDCX_STDBASE + 32
298 struct treeview_faff {
303 static HTREEITEM treeview_insert(struct treeview_faff *faff,
304 int level, char *text, char *path)
309 ins.hParent = (level > 0 ? faff->lastat[level - 1] : TVI_ROOT);
310 ins.hInsertAfter = faff->lastat[level];
311 #if _WIN32_IE >= 0x0400 && defined NONAMELESSUNION
312 #define INSITEM DUMMYUNIONNAME.item
316 ins.INSITEM.mask = TVIF_TEXT | TVIF_PARAM;
317 ins.INSITEM.pszText = text;
318 ins.INSITEM.cchTextMax = strlen(text)+1;
319 ins.INSITEM.lParam = (LPARAM)path;
320 newitem = TreeView_InsertItem(faff->treeview, &ins);
322 TreeView_Expand(faff->treeview, faff->lastat[level - 1],
324 faff->lastat[level] = newitem;
325 for (i = level + 1; i < 4; i++)
326 faff->lastat[i] = NULL;
331 * Create the panelfuls of controls in the configuration box.
333 static void create_controls(HWND hwnd, char *path)
342 * Here we must create the basic standard controls.
344 ctlposinit(&cp, hwnd, 3, 3, 235);
346 base_id = IDCX_STDBASE;
349 * Otherwise, we're creating the controls for a particular
352 ctlposinit(&cp, hwnd, 100, 3, 13);
354 base_id = IDCX_PANELBASE;
357 for (index=-1; (index = ctrl_find_path(ctrlbox, path, index)) >= 0 ;) {
358 struct controlset *s = ctrlbox->ctrlsets[index];
359 winctrl_layout(&dp, wc, &cp, s, &base_id);
364 * This function is the configuration box.
365 * (Being a dialog procedure, in general it returns 0 if the default
366 * dialog processing should be performed, and 1 if it should not.)
368 static int CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
369 WPARAM wParam, LPARAM lParam)
372 struct treeview_faff tvfaff;
378 create_controls(hwnd, ""); /* Open and Cancel buttons etc */
379 SetWindowText(hwnd, dp.wintitle);
380 SetWindowLong(hwnd, GWL_USERDATA, 0);
382 SetWindowLong(hwnd, GWL_EXSTYLE,
383 GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
385 HWND item = GetDlgItem(hwnd, IDC_HELPBTN);
389 requested_help = FALSE;
390 SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
391 (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(IDI_CFGICON)));
395 { /* centre the window */
398 hw = GetDesktopWindow();
399 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
401 (rs.right + rs.left + rd.left - rd.right) / 2,
402 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
403 rd.right - rd.left, rd.bottom - rd.top, TRUE);
407 * Create the tree view.
415 r.right = r.left + 95;
417 r.bottom = r.top + 10;
418 MapDialogRect(hwnd, &r);
419 tvstatic = CreateWindowEx(0, "STATIC", "Cate&gory:",
420 WS_CHILD | WS_VISIBLE,
422 r.right - r.left, r.bottom - r.top,
423 hwnd, (HMENU) IDCX_TVSTATIC, hinst,
425 font = SendMessage(hwnd, WM_GETFONT, 0, 0);
426 SendMessage(tvstatic, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
429 r.right = r.left + 95;
431 r.bottom = r.top + 219;
432 MapDialogRect(hwnd, &r);
433 treeview = CreateWindowEx(WS_EX_CLIENTEDGE, WC_TREEVIEW, "",
434 WS_CHILD | WS_VISIBLE |
435 WS_TABSTOP | TVS_HASLINES |
436 TVS_DISABLEDRAGDROP | TVS_HASBUTTONS
438 TVS_SHOWSELALWAYS, r.left, r.top,
439 r.right - r.left, r.bottom - r.top,
440 hwnd, (HMENU) IDCX_TREEVIEW, hinst,
442 font = SendMessage(hwnd, WM_GETFONT, 0, 0);
443 SendMessage(treeview, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
444 tvfaff.treeview = treeview;
445 memset(tvfaff.lastat, 0, sizeof(tvfaff.lastat));
449 * Set up the tree view contents.
452 HTREEITEM hfirst = NULL;
456 for (i = 0; i < ctrlbox->nctrlsets; i++) {
457 struct controlset *s = ctrlbox->ctrlsets[i];
464 j = path ? ctrl_path_compare(s->pathname, path) : 0;
466 continue; /* same path, nothing to add to tree */
469 * We expect never to find an implicit path
470 * component. For example, we expect never to see
471 * A/B/C followed by A/D/E, because that would
472 * _implicitly_ create A/D. All our path prefixes
473 * are expected to contain actual controls and be
474 * selectable in the treeview; so we would expect
475 * to see A/D _explicitly_ before encountering
478 assert(j == ctrl_path_elements(s->pathname) - 1);
480 c = strrchr(s->pathname, '/');
486 item = treeview_insert(&tvfaff, j, c, s->pathname);
494 * Put the treeview selection on to the Session panel.
495 * This should also cause creation of the relevant
498 TreeView_SelectItem(treeview, hfirst);
502 * Set focus into the first available control.
508 for (i = 0; (c = winctrl_findbyindex(&ctrls_panel, i)) != NULL;
511 dlg_set_focus(c->ctrl, &dp);
517 SetWindowLong(hwnd, GWL_USERDATA, 1);
521 * Button release should trigger WM_OK if there was a
522 * previous double click on the session list.
526 SaneEndDialog(hwnd, dp.endresult ? 1 : 0);
529 if (LOWORD(wParam) == IDCX_TREEVIEW &&
530 ((LPNMHDR) lParam)->code == TVN_SELCHANGED) {
532 TreeView_GetSelection(((LPNMHDR) lParam)->hwndFrom);
536 SendMessage (hwnd, WM_SETREDRAW, FALSE, 0);
539 item.pszText = buffer;
540 item.cchTextMax = sizeof(buffer);
541 item.mask = TVIF_TEXT | TVIF_PARAM;
542 TreeView_GetItem(((LPNMHDR) lParam)->hwndFrom, &item);
544 /* Destroy all controls in the currently visible panel. */
549 while ((c = winctrl_findbyindex(&ctrls_panel, 0)) != NULL) {
550 for (k = 0; k < c->num_ids; k++) {
551 item = GetDlgItem(hwnd, c->base_id + k);
555 winctrl_rem_shortcuts(&dp, c);
556 winctrl_remove(&ctrls_panel, c);
561 create_controls(hwnd, (char *)item.lParam);
563 dlg_refresh(NULL, &dp); /* set up control values */
565 SendMessage (hwnd, WM_SETREDRAW, TRUE, 0);
566 InvalidateRect (hwnd, NULL, TRUE);
568 SetFocus(((LPNMHDR) lParam)->hwndFrom); /* ensure focus stays */
574 default: /* also handle drag list msg here */
576 * Only process WM_COMMAND once the dialog is fully formed.
578 if (GetWindowLong(hwnd, GWL_USERDATA) == 1) {
579 ret = winctrl_handle_command(&dp, msg, wParam, lParam);
580 if (dp.ended && GetCapture() != hwnd)
581 SaneEndDialog(hwnd, dp.endresult ? 1 : 0);
587 if (winctrl_context_help(&dp, hwnd,
588 ((LPHELPINFO)lParam)->iCtrlId))
589 requested_help = TRUE;
595 if (requested_help) {
596 WinHelp(hwnd, help_path, HELP_QUIT, 0);
597 requested_help = FALSE;
599 SaneEndDialog(hwnd, 0);
602 /* Grrr Explorer will maximize Dialogs! */
604 if (wParam == SIZE_MAXIMIZED)
612 void modal_about_box(HWND hwnd)
614 EnableWindow(hwnd, 0);
615 DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
616 EnableWindow(hwnd, 1);
617 SetActiveWindow(hwnd);
620 void show_help(HWND hwnd)
623 WinHelp(hwnd, help_path,
624 help_has_contents ? HELP_FINDER : HELP_CONTENTS,
626 requested_help = TRUE;
630 void defuse_showwindow(void)
633 * Work around the fact that the app's first call to ShowWindow
634 * will ignore the default in favour of the shell-provided
639 hwnd = CreateDialog(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX),
641 ShowWindow(hwnd, SW_HIDE);
642 SetActiveWindow(hwnd);
651 ctrlbox = ctrl_new_box();
652 setup_config_box(ctrlbox, &sesslist, FALSE, 0, 0);
653 win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), FALSE);
655 winctrl_init(&ctrls_base);
656 winctrl_init(&ctrls_panel);
657 dp_add_tree(&dp, &ctrls_base);
658 dp_add_tree(&dp, &ctrls_panel);
659 dp.wintitle = dupprintf("%s Configuration", appname);
660 dp.errtitle = dupprintf("%s Error", appname);
662 dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
664 get_sesslist(&sesslist, TRUE);
666 SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
668 get_sesslist(&sesslist, FALSE);
670 ctrl_free_box(ctrlbox);
671 winctrl_cleanup(&ctrls_panel);
672 winctrl_cleanup(&ctrls_base);
678 int do_reconfig(HWND hwnd, int protcfginfo)
683 backup_cfg = cfg; /* structure copy */
685 ctrlbox = ctrl_new_box();
686 setup_config_box(ctrlbox, &sesslist, TRUE, cfg.protocol, protcfginfo);
687 win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), TRUE);
689 winctrl_init(&ctrls_base);
690 winctrl_init(&ctrls_panel);
691 dp_add_tree(&dp, &ctrls_base);
692 dp_add_tree(&dp, &ctrls_panel);
693 dp.wintitle = dupprintf("%s Reconfiguration", appname);
694 dp.errtitle = dupprintf("%s Error", appname);
696 dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
698 ret = SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
701 ctrl_free_box(ctrlbox);
702 winctrl_cleanup(&ctrls_base);
703 winctrl_cleanup(&ctrls_panel);
707 cfg = backup_cfg; /* structure copy */
712 void logevent(void *frontend, const char *string)
717 log_eventlog(logctx, string);
719 if (nevents >= negsize) {
721 events = sresize(events, negsize, char *);
725 strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t", &tm);
727 events[nevents] = snewn(strlen(timebuf) + strlen(string) + 1, char);
728 strcpy(events[nevents], timebuf);
729 strcat(events[nevents], string);
732 SendDlgItemMessage(logbox, IDN_LIST, LB_ADDSTRING,
733 0, (LPARAM) events[nevents]);
734 count = SendDlgItemMessage(logbox, IDN_LIST, LB_GETCOUNT, 0, 0);
735 SendDlgItemMessage(logbox, IDN_LIST, LB_SETTOPINDEX, count - 1, 0);
740 void showeventlog(HWND hwnd)
743 logbox = CreateDialog(hinst, MAKEINTRESOURCE(IDD_LOGBOX),
745 ShowWindow(logbox, SW_SHOWNORMAL);
747 SetActiveWindow(logbox);
750 void showabout(HWND hwnd)
752 DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
755 int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
756 char *keystr, char *fingerprint,
757 void (*callback)(void *ctx, int result), void *ctx)
761 static const char absentmsg[] =
762 "The server's host key is not cached in the registry. You\n"
763 "have no guarantee that the server is the computer you\n"
765 "The server's %s key fingerprint is:\n"
767 "If you trust this host, hit Yes to add the key to\n"
768 "%s's cache and carry on connecting.\n"
769 "If you want to carry on connecting just once, without\n"
770 "adding the key to the cache, hit No.\n"
771 "If you do not trust this host, hit Cancel to abandon the\n"
774 static const char wrongmsg[] =
775 "WARNING - POTENTIAL SECURITY BREACH!\n"
777 "The server's host key does not match the one %s has\n"
778 "cached in the registry. This means that either the\n"
779 "server administrator has changed the host key, or you\n"
780 "have actually connected to another computer pretending\n"
781 "to be the server.\n"
782 "The new %s key fingerprint is:\n"
784 "If you were expecting this change and trust the new key,\n"
785 "hit Yes to update %s's cache and continue connecting.\n"
786 "If you want to carry on connecting but without updating\n"
787 "the cache, hit No.\n"
788 "If you want to abandon the connection completely, hit\n"
789 "Cancel. Hitting Cancel is the ONLY guaranteed safe\n" "choice.\n";
791 static const char mbtitle[] = "%s Security Alert";
794 * Verify the key against the registry.
796 ret = verify_host_key(host, port, keytype, keystr);
798 if (ret == 0) /* success - key matched OK */
800 if (ret == 2) { /* key was different */
802 char *text = dupprintf(wrongmsg, appname, keytype, fingerprint,
804 char *caption = dupprintf(mbtitle, appname);
805 mbret = message_box(text, caption,
806 MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3,
807 HELPCTXID(errors_hostkey_changed));
808 assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);
811 if (mbret == IDYES) {
812 store_host_key(host, port, keytype, keystr);
814 } else if (mbret == IDNO)
818 if (ret == 1) { /* key was absent */
820 char *text = dupprintf(absentmsg, keytype, fingerprint, appname);
821 char *caption = dupprintf(mbtitle, appname);
822 mbret = message_box(text, caption,
823 MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3,
824 HELPCTXID(errors_hostkey_absent));
825 assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);
828 if (mbret == IDYES) {
829 store_host_key(host, port, keytype, keystr);
831 } else if (mbret == IDNO)
838 * Ask whether the selected algorithm is acceptable (since it was
839 * below the configured 'warn' threshold).
841 int askalg(void *frontend, const char *algtype, const char *algname,
842 void (*callback)(void *ctx, int result), void *ctx)
844 static const char mbtitle[] = "%s Security Alert";
845 static const char msg[] =
846 "The first %s supported by the server\n"
847 "is %.64s, which is below the configured\n"
848 "warning threshold.\n"
849 "Do you want to continue with this connection?\n";
850 char *message, *title;
853 message = dupprintf(msg, algtype, algname);
854 title = dupprintf(mbtitle, appname);
855 mbret = MessageBox(NULL, message, title,
856 MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2);
866 * Ask whether to wipe a session log file before writing to it.
867 * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
869 int askappend(void *frontend, Filename filename,
870 void (*callback)(void *ctx, int result), void *ctx)
872 static const char msgtemplate[] =
873 "The session log file \"%.*s\" already exists.\n"
874 "You can overwrite it with a new session log,\n"
875 "append your session log to the end of it,\n"
876 "or disable session logging for this session.\n"
877 "Hit Yes to wipe the file, No to append to it,\n"
878 "or Cancel to disable logging.";
883 message = dupprintf(msgtemplate, FILENAME_MAX, filename.path);
884 mbtitle = dupprintf("%s Log to File", appname);
886 mbret = MessageBox(NULL, message, mbtitle,
887 MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON3);
894 else if (mbret == IDNO)
901 * Warn about the obsolescent key file format.
903 * Uniquely among these functions, this one does _not_ expect a
904 * frontend handle. This means that if PuTTY is ported to a
905 * platform which requires frontend handles, this function will be
906 * an anomaly. Fortunately, the problem it addresses will not have
907 * been present on that platform, so it can plausibly be
908 * implemented as an empty function.
910 void old_keyfile_warning(void)
912 static const char mbtitle[] = "%s Key File Warning";
913 static const char message[] =
914 "You are loading an SSH-2 private key which has an\n"
915 "old version of the file format. This means your key\n"
916 "file is not fully tamperproof. Future versions of\n"
917 "%s may stop supporting this private key format,\n"
918 "so we recommend you convert your key to the new\n"
921 "You can perform this conversion by loading the key\n"
922 "into PuTTYgen and then saving it again.";
925 msg = dupprintf(message, appname);
926 title = dupprintf(mbtitle, appname);
928 MessageBox(NULL, msg, title, MB_OK);