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.
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, &sesslist, 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' */
662 get_sesslist(&sesslist, TRUE);
664 SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
666 get_sesslist(&sesslist, FALSE);
668 ctrl_free_box(ctrlbox);
669 winctrl_cleanup(&ctrls_panel);
670 winctrl_cleanup(&ctrls_base);
676 int do_reconfig(HWND hwnd, int protcfginfo)
681 backup_cfg = cfg; /* structure copy */
683 ctrlbox = ctrl_new_box();
684 setup_config_box(ctrlbox, &sesslist, TRUE, cfg.protocol, protcfginfo);
685 win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), TRUE);
687 winctrl_init(&ctrls_base);
688 winctrl_init(&ctrls_panel);
689 dp_add_tree(&dp, &ctrls_base);
690 dp_add_tree(&dp, &ctrls_panel);
691 dp.wintitle = dupprintf("%s Reconfiguration", appname);
692 dp.errtitle = dupprintf("%s Error", appname);
694 dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
696 ret = SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
699 ctrl_free_box(ctrlbox);
700 winctrl_cleanup(&ctrls_base);
701 winctrl_cleanup(&ctrls_panel);
705 cfg = backup_cfg; /* structure copy */
710 void logevent(void *frontend, const char *string)
715 log_eventlog(logctx, string);
717 if (nevents >= negsize) {
719 events = sresize(events, negsize, char *);
723 strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t", &tm);
725 events[nevents] = snewn(strlen(timebuf) + strlen(string) + 1, char);
726 strcpy(events[nevents], timebuf);
727 strcat(events[nevents], string);
730 SendDlgItemMessage(logbox, IDN_LIST, LB_ADDSTRING,
731 0, (LPARAM) events[nevents]);
732 count = SendDlgItemMessage(logbox, IDN_LIST, LB_GETCOUNT, 0, 0);
733 SendDlgItemMessage(logbox, IDN_LIST, LB_SETTOPINDEX, count - 1, 0);
738 void showeventlog(HWND hwnd)
741 logbox = CreateDialog(hinst, MAKEINTRESOURCE(IDD_LOGBOX),
743 ShowWindow(logbox, SW_SHOWNORMAL);
745 SetActiveWindow(logbox);
748 void showabout(HWND hwnd)
750 DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
753 int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
754 char *keystr, char *fingerprint,
755 void (*callback)(void *ctx, int result), void *ctx)
759 static const char absentmsg[] =
760 "The server's host key is not cached in the registry. You\n"
761 "have no guarantee that the server is the computer you\n"
763 "The server's %s key fingerprint is:\n"
765 "If you trust this host, hit Yes to add the key to\n"
766 "%s's cache and carry on connecting.\n"
767 "If you want to carry on connecting just once, without\n"
768 "adding the key to the cache, hit No.\n"
769 "If you do not trust this host, hit Cancel to abandon the\n"
772 static const char wrongmsg[] =
773 "WARNING - POTENTIAL SECURITY BREACH!\n"
775 "The server's host key does not match the one %s has\n"
776 "cached in the registry. This means that either the\n"
777 "server administrator has changed the host key, or you\n"
778 "have actually connected to another computer pretending\n"
779 "to be the server.\n"
780 "The new %s key fingerprint is:\n"
782 "If you were expecting this change and trust the new key,\n"
783 "hit Yes to update %s's cache and continue connecting.\n"
784 "If you want to carry on connecting but without updating\n"
785 "the cache, hit No.\n"
786 "If you want to abandon the connection completely, hit\n"
787 "Cancel. Hitting Cancel is the ONLY guaranteed safe\n" "choice.\n";
789 static const char mbtitle[] = "%s Security Alert";
792 * Verify the key against the registry.
794 ret = verify_host_key(host, port, keytype, keystr);
796 if (ret == 0) /* success - key matched OK */
798 if (ret == 2) { /* key was different */
800 char *text = dupprintf(wrongmsg, appname, keytype, fingerprint,
802 char *caption = dupprintf(mbtitle, appname);
803 mbret = message_box(text, caption,
804 MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3,
805 HELPCTXID(errors_hostkey_changed));
806 assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);
809 if (mbret == IDYES) {
810 store_host_key(host, port, keytype, keystr);
812 } else if (mbret == IDNO)
816 if (ret == 1) { /* key was absent */
818 char *text = dupprintf(absentmsg, keytype, fingerprint, appname);
819 char *caption = dupprintf(mbtitle, appname);
820 mbret = message_box(text, caption,
821 MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3,
822 HELPCTXID(errors_hostkey_absent));
823 assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL);
826 if (mbret == IDYES) {
827 store_host_key(host, port, keytype, keystr);
829 } else if (mbret == IDNO)
836 * Ask whether the selected algorithm is acceptable (since it was
837 * below the configured 'warn' threshold).
839 int askalg(void *frontend, const char *algtype, const char *algname,
840 void (*callback)(void *ctx, int result), void *ctx)
842 static const char mbtitle[] = "%s Security Alert";
843 static const char msg[] =
844 "The first %s supported by the server\n"
845 "is %.64s, which is below the configured\n"
846 "warning threshold.\n"
847 "Do you want to continue with this connection?\n";
848 char *message, *title;
851 message = dupprintf(msg, algtype, algname);
852 title = dupprintf(mbtitle, appname);
853 mbret = MessageBox(NULL, message, title,
854 MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2);
864 * Ask whether to wipe a session log file before writing to it.
865 * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
867 int askappend(void *frontend, Filename filename,
868 void (*callback)(void *ctx, int result), void *ctx)
870 static const char msgtemplate[] =
871 "The session log file \"%.*s\" already exists.\n"
872 "You can overwrite it with a new session log,\n"
873 "append your session log to the end of it,\n"
874 "or disable session logging for this session.\n"
875 "Hit Yes to wipe the file, No to append to it,\n"
876 "or Cancel to disable logging.";
881 message = dupprintf(msgtemplate, FILENAME_MAX, filename.path);
882 mbtitle = dupprintf("%s Log to File", appname);
884 mbret = MessageBox(NULL, message, mbtitle,
885 MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON3);
892 else if (mbret == IDNO)
899 * Warn about the obsolescent key file format.
901 * Uniquely among these functions, this one does _not_ expect a
902 * frontend handle. This means that if PuTTY is ported to a
903 * platform which requires frontend handles, this function will be
904 * an anomaly. Fortunately, the problem it addresses will not have
905 * been present on that platform, so it can plausibly be
906 * implemented as an empty function.
908 void old_keyfile_warning(void)
910 static const char mbtitle[] = "%s Key File Warning";
911 static const char message[] =
912 "You are loading an SSH 2 private key which has an\n"
913 "old version of the file format. This means your key\n"
914 "file is not fully tamperproof. Future versions of\n"
915 "%s may stop supporting this private key format,\n"
916 "so we recommend you convert your key to the new\n"
919 "You can perform this conversion by loading the key\n"
920 "into PuTTYgen and then saving it again.";
923 msg = dupprintf(message, appname);
924 title = dupprintf(mbtitle, appname);
926 MessageBox(NULL, msg, title, MB_OK);