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 + 8;
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 SetWindowLong(hwnd, BOXFLAGS, 0); /* flags */
255 SetWindowLong(hwnd, BOXRESULT, 0); /* result from SaneEndDialog */
257 while ((gm=GetMessage(&msg, NULL, 0, 0)) > 0) {
258 flags=GetWindowLong(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=GetWindowLong(hwnd, BOXRESULT);
273 static void SaneEndDialog(HWND hwnd, int ret)
275 SetWindowLong(hwnd, BOXRESULT, ret);
276 SetWindowLong(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 SetWindowLong(hwnd, GWL_USERDATA, 0);
380 SetWindowLong(hwnd, GWL_EXSTYLE,
381 GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
383 HWND item = GetDlgItem(hwnd, IDC_HELPBTN);
387 requested_help = FALSE;
388 SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
389 (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(IDI_CFGICON)));
393 { /* centre the window */
396 hw = GetDesktopWindow();
397 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
399 (rs.right + rs.left + rd.left - rd.right) / 2,
400 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
401 rd.right - rd.left, rd.bottom - rd.top, TRUE);
405 * Create the tree view.
413 r.right = r.left + 95;
415 r.bottom = r.top + 10;
416 MapDialogRect(hwnd, &r);
417 tvstatic = CreateWindowEx(0, "STATIC", "Cate&gory:",
418 WS_CHILD | WS_VISIBLE,
420 r.right - r.left, r.bottom - r.top,
421 hwnd, (HMENU) IDCX_TVSTATIC, hinst,
423 font = SendMessage(hwnd, WM_GETFONT, 0, 0);
424 SendMessage(tvstatic, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
427 r.right = r.left + 95;
429 r.bottom = r.top + 219;
430 MapDialogRect(hwnd, &r);
431 treeview = CreateWindowEx(WS_EX_CLIENTEDGE, WC_TREEVIEW, "",
432 WS_CHILD | WS_VISIBLE |
433 WS_TABSTOP | TVS_HASLINES |
434 TVS_DISABLEDRAGDROP | TVS_HASBUTTONS
436 TVS_SHOWSELALWAYS, r.left, r.top,
437 r.right - r.left, r.bottom - r.top,
438 hwnd, (HMENU) IDCX_TREEVIEW, hinst,
440 font = SendMessage(hwnd, WM_GETFONT, 0, 0);
441 SendMessage(treeview, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
442 tvfaff.treeview = treeview;
443 memset(tvfaff.lastat, 0, sizeof(tvfaff.lastat));
447 * Set up the tree view contents.
450 HTREEITEM hfirst = NULL;
454 for (i = 0; i < ctrlbox->nctrlsets; i++) {
455 struct controlset *s = ctrlbox->ctrlsets[i];
462 j = path ? ctrl_path_compare(s->pathname, path) : 0;
464 continue; /* same path, nothing to add to tree */
467 * We expect never to find an implicit path
468 * component. For example, we expect never to see
469 * A/B/C followed by A/D/E, because that would
470 * _implicitly_ create A/D. All our path prefixes
471 * are expected to contain actual controls and be
472 * selectable in the treeview; so we would expect
473 * to see A/D _explicitly_ before encountering
476 assert(j == ctrl_path_elements(s->pathname) - 1);
478 c = strrchr(s->pathname, '/');
484 item = treeview_insert(&tvfaff, j, c, s->pathname);
492 * Put the treeview selection on to the Session panel.
493 * This should also cause creation of the relevant
496 TreeView_SelectItem(treeview, hfirst);
500 * Set focus into the first available control.
506 for (i = 0; (c = winctrl_findbyindex(&ctrls_panel, i)) != NULL;
509 dlg_set_focus(c->ctrl, &dp);
515 SetWindowLong(hwnd, GWL_USERDATA, 1);
519 * Button release should trigger WM_OK if there was a
520 * previous double click on the session list.
524 SaneEndDialog(hwnd, dp.endresult ? 1 : 0);
527 if (LOWORD(wParam) == IDCX_TREEVIEW &&
528 ((LPNMHDR) lParam)->code == TVN_SELCHANGED) {
530 TreeView_GetSelection(((LPNMHDR) lParam)->hwndFrom);
534 SendMessage (hwnd, WM_SETREDRAW, FALSE, 0);
537 item.pszText = buffer;
538 item.cchTextMax = sizeof(buffer);
539 item.mask = TVIF_TEXT | TVIF_PARAM;
540 TreeView_GetItem(((LPNMHDR) lParam)->hwndFrom, &item);
542 /* Destroy all controls in the currently visible panel. */
547 while ((c = winctrl_findbyindex(&ctrls_panel, 0)) != NULL) {
548 for (k = 0; k < c->num_ids; k++) {
549 item = GetDlgItem(hwnd, c->base_id + k);
553 winctrl_rem_shortcuts(&dp, c);
554 winctrl_remove(&ctrls_panel, c);
559 create_controls(hwnd, (char *)item.lParam);
561 dlg_refresh(NULL, &dp); /* set up control values */
563 SendMessage (hwnd, WM_SETREDRAW, TRUE, 0);
564 InvalidateRect (hwnd, NULL, TRUE);
566 SetFocus(((LPNMHDR) lParam)->hwndFrom); /* ensure focus stays */
572 default: /* also handle drag list msg here */
574 * Only process WM_COMMAND once the dialog is fully formed.
576 if (GetWindowLong(hwnd, GWL_USERDATA) == 1) {
577 ret = winctrl_handle_command(&dp, msg, wParam, lParam);
578 if (dp.ended && GetCapture() != hwnd)
579 SaneEndDialog(hwnd, dp.endresult ? 1 : 0);
585 if (winctrl_context_help(&dp, hwnd,
586 ((LPHELPINFO)lParam)->iCtrlId))
587 requested_help = TRUE;
593 if (requested_help) {
594 WinHelp(hwnd, help_path, HELP_QUIT, 0);
595 requested_help = FALSE;
597 SaneEndDialog(hwnd, 0);
600 /* Grrr Explorer will maximize Dialogs! */
602 if (wParam == SIZE_MAXIMIZED)
610 void modal_about_box(HWND hwnd)
612 EnableWindow(hwnd, 0);
613 DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
614 EnableWindow(hwnd, 1);
615 SetActiveWindow(hwnd);
618 void show_help(HWND hwnd)
621 WinHelp(hwnd, help_path,
622 help_has_contents ? HELP_FINDER : HELP_CONTENTS,
624 requested_help = TRUE;
628 void defuse_showwindow(void)
631 * Work around the fact that the app's first call to ShowWindow
632 * will ignore the default in favour of the shell-provided
637 hwnd = CreateDialog(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX),
639 ShowWindow(hwnd, SW_HIDE);
640 SetActiveWindow(hwnd);
649 ctrlbox = ctrl_new_box();
650 setup_config_box(ctrlbox, FALSE, 0, 0);
651 win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), FALSE);
653 winctrl_init(&ctrls_base);
654 winctrl_init(&ctrls_panel);
655 dp_add_tree(&dp, &ctrls_base);
656 dp_add_tree(&dp, &ctrls_panel);
657 dp.wintitle = dupprintf("%s Configuration", appname);
658 dp.errtitle = dupprintf("%s Error", appname);
660 dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
663 SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
666 ctrl_free_box(ctrlbox);
667 winctrl_cleanup(&ctrls_panel);
668 winctrl_cleanup(&ctrls_base);
674 int do_reconfig(HWND hwnd, int protcfginfo)
679 backup_cfg = cfg; /* structure copy */
681 ctrlbox = ctrl_new_box();
682 setup_config_box(ctrlbox, TRUE, cfg.protocol, protcfginfo);
683 win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), TRUE);
685 winctrl_init(&ctrls_base);
686 winctrl_init(&ctrls_panel);
687 dp_add_tree(&dp, &ctrls_base);
688 dp_add_tree(&dp, &ctrls_panel);
689 dp.wintitle = dupprintf("%s Reconfiguration", appname);
690 dp.errtitle = dupprintf("%s Error", appname);
692 dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
694 ret = SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
697 ctrl_free_box(ctrlbox);
698 winctrl_cleanup(&ctrls_base);
699 winctrl_cleanup(&ctrls_panel);
703 cfg = backup_cfg; /* structure copy */
708 void logevent(void *frontend, const char *string)
713 log_eventlog(logctx, string);
715 if (nevents >= negsize) {
717 events = sresize(events, negsize, char *);
721 strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t", &tm);
723 events[nevents] = snewn(strlen(timebuf) + strlen(string) + 1, char);
724 strcpy(events[nevents], timebuf);
725 strcat(events[nevents], string);
728 SendDlgItemMessage(logbox, IDN_LIST, LB_ADDSTRING,
729 0, (LPARAM) events[nevents]);
730 count = SendDlgItemMessage(logbox, IDN_LIST, LB_GETCOUNT, 0, 0);
731 SendDlgItemMessage(logbox, IDN_LIST, LB_SETTOPINDEX, count - 1, 0);
736 void showeventlog(HWND hwnd)
739 logbox = CreateDialog(hinst, MAKEINTRESOURCE(IDD_LOGBOX),
741 ShowWindow(logbox, SW_SHOWNORMAL);
743 SetActiveWindow(logbox);
746 void showabout(HWND hwnd)
748 DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
751 int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
752 char *keystr, char *fingerprint,
753 void (*callback)(void *ctx, int result), void *ctx)
757 static const char absentmsg[] =
758 "The server's host key is not cached in the registry. You\n"
759 "have no guarantee that the server is the computer you\n"
761 "The server's %s key fingerprint is:\n"
763 "If you trust this host, hit Yes to add the key to\n"
764 "%s's cache and carry on connecting.\n"
765 "If you want to carry on connecting just once, without\n"
766 "adding the key to the cache, hit No.\n"
767 "If you do not trust this host, hit Cancel to abandon the\n"
770 static const char wrongmsg[] =
771 "WARNING - POTENTIAL SECURITY BREACH!\n"
773 "The server's host key does not match the one %s has\n"
774 "cached in the registry. This means that either the\n"
775 "server administrator has changed the host key, or you\n"
776 "have actually connected to another computer pretending\n"
777 "to be the server.\n"
778 "The new %s key fingerprint is:\n"
780 "If you were expecting this change and trust the new key,\n"
781 "hit Yes to update %s's cache and continue connecting.\n"
782 "If you want to carry on connecting but without updating\n"
783 "the cache, hit No.\n"
784 "If you want to abandon the connection completely, hit\n"
785 "Cancel. Hitting Cancel is the ONLY guaranteed safe\n" "choice.\n";
787 static const char mbtitle[] = "%s Security Alert";
790 * Verify the key against the registry.
792 ret = verify_host_key(host, port, keytype, keystr);
794 if (ret == 0) /* success - key matched OK */
796 if (ret == 2) { /* key was different */
798 char *text = dupprintf(wrongmsg, appname, keytype, fingerprint,
800 char *caption = dupprintf(mbtitle, appname);
801 mbret = message_box(text, caption,
802 MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3,
803 HELPCTXID(errors_hostkey_changed));
804 assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);
807 if (mbret == IDYES) {
808 store_host_key(host, port, keytype, keystr);
810 } else if (mbret == IDNO)
814 if (ret == 1) { /* key was absent */
816 char *text = dupprintf(absentmsg, keytype, fingerprint, appname);
817 char *caption = dupprintf(mbtitle, appname);
818 mbret = message_box(text, caption,
819 MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3,
820 HELPCTXID(errors_hostkey_absent));
821 assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);
824 if (mbret == IDYES) {
825 store_host_key(host, port, keytype, keystr);
827 } else if (mbret == IDNO)
834 * Ask whether the selected algorithm is acceptable (since it was
835 * below the configured 'warn' threshold).
837 int askalg(void *frontend, const char *algtype, const char *algname,
838 void (*callback)(void *ctx, int result), void *ctx)
840 static const char mbtitle[] = "%s Security Alert";
841 static const char msg[] =
842 "The first %s supported by the server\n"
843 "is %.64s, which is below the configured\n"
844 "warning threshold.\n"
845 "Do you want to continue with this connection?\n";
846 char *message, *title;
849 message = dupprintf(msg, algtype, algname);
850 title = dupprintf(mbtitle, appname);
851 mbret = MessageBox(NULL, message, title,
852 MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2);
862 * Ask whether to wipe a session log file before writing to it.
863 * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
865 int askappend(void *frontend, Filename filename,
866 void (*callback)(void *ctx, int result), void *ctx)
868 static const char msgtemplate[] =
869 "The session log file \"%.*s\" already exists.\n"
870 "You can overwrite it with a new session log,\n"
871 "append your session log to the end of it,\n"
872 "or disable session logging for this session.\n"
873 "Hit Yes to wipe the file, No to append to it,\n"
874 "or Cancel to disable logging.";
879 message = dupprintf(msgtemplate, FILENAME_MAX, filename.path);
880 mbtitle = dupprintf("%s Log to File", appname);
882 mbret = MessageBox(NULL, message, mbtitle,
883 MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON3);
890 else if (mbret == IDNO)
897 * Warn about the obsolescent key file format.
899 * Uniquely among these functions, this one does _not_ expect a
900 * frontend handle. This means that if PuTTY is ported to a
901 * platform which requires frontend handles, this function will be
902 * an anomaly. Fortunately, the problem it addresses will not have
903 * been present on that platform, so it can plausibly be
904 * implemented as an empty function.
906 void old_keyfile_warning(void)
908 static const char mbtitle[] = "%s Key File Warning";
909 static const char message[] =
910 "You are loading an SSH-2 private key which has an\n"
911 "old version of the file format. This means your key\n"
912 "file is not fully tamperproof. Future versions of\n"
913 "%s may stop supporting this private key format,\n"
914 "so we recommend you convert your key to the new\n"
917 "You can perform this conversion by loading the key\n"
918 "into PuTTYgen and then saving it again.";
921 msg = dupprintf(message, appname);
922 title = dupprintf(mbtitle, appname);
924 MessageBox(NULL, msg, title, MB_OK);