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 static int requested_help;
45 extern Config cfg; /* defined in window.c */
47 struct sesslist sesslist; /* exported to window.c */
49 #define PRINTER_DISABLED_STRING "None (printing disabled)"
51 void force_normal(HWND hwnd)
53 static int recurse = 0;
61 wp.length = sizeof(wp);
62 if (GetWindowPlacement(hwnd, &wp) && wp.showCmd == SW_SHOWMAXIMIZED) {
63 wp.showCmd = SW_SHOWNORMAL;
64 SetWindowPlacement(hwnd, &wp);
69 static int CALLBACK LogProc(HWND hwnd, UINT msg,
70 WPARAM wParam, LPARAM lParam)
77 char *str = dupprintf("%s Event Log", appname);
78 SetWindowText(hwnd, str);
82 static int tabs[4] = { 78, 108 };
83 SendDlgItemMessage(hwnd, IDN_LIST, LB_SETTABSTOPS, 2,
86 for (i = 0; i < nevents; i++)
87 SendDlgItemMessage(hwnd, IDN_LIST, LB_ADDSTRING,
88 0, (LPARAM) events[i]);
91 switch (LOWORD(wParam)) {
95 SetActiveWindow(GetParent(hwnd));
99 if (HIWORD(wParam) == BN_CLICKED ||
100 HIWORD(wParam) == BN_DOUBLECLICKED) {
103 selcount = SendDlgItemMessage(hwnd, IDN_LIST,
104 LB_GETSELCOUNT, 0, 0);
105 if (selcount == 0) { /* don't even try to copy zero items */
110 selitems = snewn(selcount, int);
112 int count = SendDlgItemMessage(hwnd, IDN_LIST,
119 static unsigned char sel_nl[] = SEL_NL;
121 if (count == 0) { /* can't copy zero stuff */
127 for (i = 0; i < count; i++)
129 strlen(events[selitems[i]]) + sizeof(sel_nl);
131 clipdata = snewn(size, char);
134 for (i = 0; i < count; i++) {
135 char *q = events[selitems[i]];
136 int qlen = strlen(q);
139 memcpy(p, sel_nl, sizeof(sel_nl));
142 write_aclip(NULL, clipdata, size, TRUE);
147 for (i = 0; i < nevents; i++)
148 SendDlgItemMessage(hwnd, IDN_LIST, LB_SETSEL,
157 SetActiveWindow(GetParent(hwnd));
164 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
165 WPARAM wParam, LPARAM lParam)
170 char *str = dupprintf("%s Licence", appname);
171 SetWindowText(hwnd, str);
176 switch (LOWORD(wParam)) {
189 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
190 WPARAM wParam, LPARAM lParam)
196 str = dupprintf("About %s", appname);
197 SetWindowText(hwnd, str);
199 SetDlgItemText(hwnd, IDA_TEXT1, appname);
200 SetDlgItemText(hwnd, IDA_VERSION, ver);
203 switch (LOWORD(wParam)) {
206 EndDialog(hwnd, TRUE);
209 EnableWindow(hwnd, 0);
210 DialogBox(hinst, MAKEINTRESOURCE(IDD_LICENCEBOX),
212 EnableWindow(hwnd, 1);
213 SetActiveWindow(hwnd);
217 /* Load web browser */
218 ShellExecute(hwnd, "open",
219 "http://www.chiark.greenend.org.uk/~sgtatham/putty/",
220 0, 0, SW_SHOWDEFAULT);
225 EndDialog(hwnd, TRUE);
232 * Null dialog procedure.
234 static int CALLBACK NullDlgProc(HWND hwnd, UINT msg,
235 WPARAM wParam, LPARAM lParam)
241 IDCX_ABOUT = IDC_ABOUT,
245 IDCX_PANELBASE = IDCX_STDBASE + 32
248 struct treeview_faff {
253 static HTREEITEM treeview_insert(struct treeview_faff *faff,
254 int level, char *text, char *path)
259 ins.hParent = (level > 0 ? faff->lastat[level - 1] : TVI_ROOT);
260 ins.hInsertAfter = faff->lastat[level];
261 #if _WIN32_IE >= 0x0400 && defined NONAMELESSUNION
262 #define INSITEM DUMMYUNIONNAME.item
266 ins.INSITEM.mask = TVIF_TEXT | TVIF_PARAM;
267 ins.INSITEM.pszText = text;
268 ins.INSITEM.cchTextMax = strlen(text)+1;
269 ins.INSITEM.lParam = (LPARAM)path;
270 newitem = TreeView_InsertItem(faff->treeview, &ins);
272 TreeView_Expand(faff->treeview, faff->lastat[level - 1],
274 faff->lastat[level] = newitem;
275 for (i = level + 1; i < 4; i++)
276 faff->lastat[i] = NULL;
281 * Create the panelfuls of controls in the configuration box.
283 static void create_controls(HWND hwnd, char *path)
292 * Here we must create the basic standard controls.
294 ctlposinit(&cp, hwnd, 3, 3, 235);
296 base_id = IDCX_STDBASE;
299 * Otherwise, we're creating the controls for a particular
302 ctlposinit(&cp, hwnd, 80, 3, 13);
304 base_id = IDCX_PANELBASE;
307 for (index=-1; (index = ctrl_find_path(ctrlbox, path, index)) >= 0 ;) {
308 struct controlset *s = ctrlbox->ctrlsets[index];
309 winctrl_layout(&dp, wc, &cp, s, &base_id);
314 * This function is the configuration box.
316 static int CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
317 WPARAM wParam, LPARAM lParam)
320 struct treeview_faff tvfaff;
326 create_controls(hwnd, ""); /* Open and Cancel buttons etc */
327 SetWindowText(hwnd, dp.wintitle);
328 SetWindowLong(hwnd, GWL_USERDATA, 0);
330 SetWindowLong(hwnd, GWL_EXSTYLE,
331 GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
333 HWND item = GetDlgItem(hwnd, IDC_HELPBTN);
337 requested_help = FALSE;
338 SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
339 (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(IDI_CFGICON)));
343 { /* centre the window */
346 hw = GetDesktopWindow();
347 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
349 (rs.right + rs.left + rd.left - rd.right) / 2,
350 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
351 rd.right - rd.left, rd.bottom - rd.top, TRUE);
355 * Create the tree view.
363 r.right = r.left + 75;
365 r.bottom = r.top + 10;
366 MapDialogRect(hwnd, &r);
367 tvstatic = CreateWindowEx(0, "STATIC", "Cate&gory:",
368 WS_CHILD | WS_VISIBLE,
370 r.right - r.left, r.bottom - r.top,
371 hwnd, (HMENU) IDCX_TVSTATIC, hinst,
373 font = SendMessage(hwnd, WM_GETFONT, 0, 0);
374 SendMessage(tvstatic, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
377 r.right = r.left + 75;
379 r.bottom = r.top + 219;
380 MapDialogRect(hwnd, &r);
381 treeview = CreateWindowEx(WS_EX_CLIENTEDGE, WC_TREEVIEW, "",
382 WS_CHILD | WS_VISIBLE |
383 WS_TABSTOP | TVS_HASLINES |
384 TVS_DISABLEDRAGDROP | TVS_HASBUTTONS
386 TVS_SHOWSELALWAYS, r.left, r.top,
387 r.right - r.left, r.bottom - r.top,
388 hwnd, (HMENU) IDCX_TREEVIEW, hinst,
390 font = SendMessage(hwnd, WM_GETFONT, 0, 0);
391 SendMessage(treeview, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
392 tvfaff.treeview = treeview;
393 memset(tvfaff.lastat, 0, sizeof(tvfaff.lastat));
397 * Set up the tree view contents.
400 HTREEITEM hfirst = NULL;
404 for (i = 0; i < ctrlbox->nctrlsets; i++) {
405 struct controlset *s = ctrlbox->ctrlsets[i];
412 j = path ? ctrl_path_compare(s->pathname, path) : 0;
414 continue; /* same path, nothing to add to tree */
417 * We expect never to find an implicit path
418 * component. For example, we expect never to see
419 * A/B/C followed by A/D/E, because that would
420 * _implicitly_ create A/D. All our path prefixes
421 * are expected to contain actual controls and be
422 * selectable in the treeview; so we would expect
423 * to see A/D _explicitly_ before encountering
426 assert(j == ctrl_path_elements(s->pathname) - 1);
428 c = strrchr(s->pathname, '/');
434 item = treeview_insert(&tvfaff, j, c, s->pathname);
442 * Put the treeview selection on to the Session panel.
443 * This should also cause creation of the relevant
446 TreeView_SelectItem(treeview, hfirst);
450 * Set focus into the first available control.
456 for (i = 0; (c = winctrl_findbyindex(&ctrls_panel, i)) != NULL;
459 dlg_set_focus(c->ctrl, &dp);
465 SetWindowLong(hwnd, GWL_USERDATA, 1);
469 * Button release should trigger WM_OK if there was a
470 * previous double click on the session list.
474 SaneEndDialog(hwnd, dp.endresult ? 1 : 0);
477 if (LOWORD(wParam) == IDCX_TREEVIEW &&
478 ((LPNMHDR) lParam)->code == TVN_SELCHANGED) {
480 TreeView_GetSelection(((LPNMHDR) lParam)->hwndFrom);
484 SendMessage (hwnd, WM_SETREDRAW, FALSE, 0);
487 item.pszText = buffer;
488 item.cchTextMax = sizeof(buffer);
489 item.mask = TVIF_TEXT | TVIF_PARAM;
490 TreeView_GetItem(((LPNMHDR) lParam)->hwndFrom, &item);
492 /* Destroy all controls in the currently visible panel. */
497 while ((c = winctrl_findbyindex(&ctrls_panel, 0)) != NULL) {
498 for (k = 0; k < c->num_ids; k++) {
499 item = GetDlgItem(hwnd, c->base_id + k);
503 winctrl_rem_shortcuts(&dp, c);
504 winctrl_remove(&ctrls_panel, c);
509 create_controls(hwnd, (char *)item.lParam);
511 dlg_refresh(NULL, &dp); /* set up control values */
513 SendMessage (hwnd, WM_SETREDRAW, TRUE, 0);
514 InvalidateRect (hwnd, NULL, TRUE);
516 SetFocus(((LPNMHDR) lParam)->hwndFrom); /* ensure focus stays */
522 default: /* also handle drag list msg here */
524 * Only process WM_COMMAND once the dialog is fully formed.
526 if (GetWindowLong(hwnd, GWL_USERDATA) == 1) {
527 ret = winctrl_handle_command(&dp, msg, wParam, lParam);
528 if (dp.ended && GetCapture() != hwnd)
529 SaneEndDialog(hwnd, dp.endresult ? 1 : 0);
535 if (winctrl_context_help(&dp, hwnd,
536 ((LPHELPINFO)lParam)->iCtrlId))
537 requested_help = TRUE;
543 if (requested_help) {
544 WinHelp(hwnd, help_path, HELP_QUIT, 0);
545 requested_help = FALSE;
547 SaneEndDialog(hwnd, 0);
550 /* Grrr Explorer will maximize Dialogs! */
552 if (wParam == SIZE_MAXIMIZED)
560 void modal_about_box(HWND hwnd)
562 EnableWindow(hwnd, 0);
563 DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
564 EnableWindow(hwnd, 1);
565 SetActiveWindow(hwnd);
568 void show_help(HWND hwnd)
571 WinHelp(hwnd, help_path,
572 help_has_contents ? HELP_FINDER : HELP_CONTENTS,
574 requested_help = TRUE;
578 void defuse_showwindow(void)
581 * Work around the fact that the app's first call to ShowWindow
582 * will ignore the default in favour of the shell-provided
587 hwnd = CreateDialog(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX),
589 ShowWindow(hwnd, SW_HIDE);
590 SetActiveWindow(hwnd);
599 ctrlbox = ctrl_new_box();
600 setup_config_box(ctrlbox, &sesslist, FALSE, 0);
601 win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), FALSE);
603 winctrl_init(&ctrls_base);
604 winctrl_init(&ctrls_panel);
605 dp_add_tree(&dp, &ctrls_base);
606 dp_add_tree(&dp, &ctrls_panel);
607 dp.wintitle = dupprintf("%s Configuration", appname);
608 dp.errtitle = dupprintf("%s Error", appname);
610 dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
612 get_sesslist(&sesslist, TRUE);
614 SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
616 get_sesslist(&sesslist, FALSE);
618 ctrl_free_box(ctrlbox);
619 winctrl_cleanup(&ctrls_panel);
620 winctrl_cleanup(&ctrls_base);
626 int do_reconfig(HWND hwnd)
631 backup_cfg = cfg; /* structure copy */
633 ctrlbox = ctrl_new_box();
634 setup_config_box(ctrlbox, NULL, TRUE, cfg.protocol);
635 win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), TRUE);
637 winctrl_init(&ctrls_base);
638 winctrl_init(&ctrls_panel);
639 dp_add_tree(&dp, &ctrls_base);
640 dp_add_tree(&dp, &ctrls_panel);
641 dp.wintitle = dupprintf("%s Reconfiguration", appname);
642 dp.errtitle = dupprintf("%s Error", appname);
644 dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
646 ret = SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
649 ctrl_free_box(ctrlbox);
650 winctrl_cleanup(&ctrls_base);
651 winctrl_cleanup(&ctrls_panel);
656 cfg = backup_cfg; /* structure copy */
661 void logevent(void *frontend, const char *string)
666 log_eventlog(logctx, string);
668 if (nevents >= negsize) {
670 events = sresize(events, negsize, char *);
674 strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t",
677 events[nevents] = snewn(strlen(timebuf) + strlen(string) + 1, char);
678 strcpy(events[nevents], timebuf);
679 strcat(events[nevents], string);
682 SendDlgItemMessage(logbox, IDN_LIST, LB_ADDSTRING,
683 0, (LPARAM) events[nevents]);
684 count = SendDlgItemMessage(logbox, IDN_LIST, LB_GETCOUNT, 0, 0);
685 SendDlgItemMessage(logbox, IDN_LIST, LB_SETTOPINDEX, count - 1, 0);
690 void showeventlog(HWND hwnd)
693 logbox = CreateDialog(hinst, MAKEINTRESOURCE(IDD_LOGBOX),
695 ShowWindow(logbox, SW_SHOWNORMAL);
697 SetActiveWindow(logbox);
700 void showabout(HWND hwnd)
702 DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
705 void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
706 char *keystr, char *fingerprint)
710 static const char absentmsg[] =
711 "The server's host key is not cached in the registry. You\n"
712 "have no guarantee that the server is the computer you\n"
714 "The server's %s key fingerprint is:\n"
716 "If you trust this host, hit Yes to add the key to\n"
717 "%s's cache and carry on connecting.\n"
718 "If you want to carry on connecting just once, without\n"
719 "adding the key to the cache, hit No.\n"
720 "If you do not trust this host, hit Cancel to abandon the\n"
723 static const char wrongmsg[] =
724 "WARNING - POTENTIAL SECURITY BREACH!\n"
726 "The server's host key does not match the one %s has\n"
727 "cached in the registry. This means that either the\n"
728 "server administrator has changed the host key, or you\n"
729 "have actually connected to another computer pretending\n"
730 "to be the server.\n"
731 "The new %s key fingerprint is:\n"
733 "If you were expecting this change and trust the new key,\n"
734 "hit Yes to update %s's cache and continue connecting.\n"
735 "If you want to carry on connecting but without updating\n"
736 "the cache, hit No.\n"
737 "If you want to abandon the connection completely, hit\n"
738 "Cancel. Hitting Cancel is the ONLY guaranteed safe\n" "choice.\n";
740 static const char mbtitle[] = "%s Security Alert";
743 * Verify the key against the registry.
745 ret = verify_host_key(host, port, keytype, keystr);
747 if (ret == 0) /* success - key matched OK */
749 if (ret == 2) { /* key was different */
751 char *message, *title;
752 message = dupprintf(wrongmsg, appname, keytype, fingerprint, appname);
753 title = dupprintf(mbtitle, appname);
754 mbret = MessageBox(NULL, message, title,
755 MB_ICONWARNING | MB_YESNOCANCEL);
759 store_host_key(host, port, keytype, keystr);
760 if (mbret == IDCANCEL)
763 if (ret == 1) { /* key was absent */
765 char *message, *title;
766 message = dupprintf(absentmsg, keytype, fingerprint, appname);
767 title = dupprintf(mbtitle, appname);
768 mbret = MessageBox(NULL, message, title,
769 MB_ICONWARNING | MB_YESNOCANCEL);
773 store_host_key(host, port, keytype, keystr);
774 if (mbret == IDCANCEL)
780 * Ask whether the selected cipher is acceptable (since it was
781 * below the configured 'warn' threshold).
782 * cs: 0 = both ways, 1 = client->server, 2 = server->client
784 void askcipher(void *frontend, char *ciphername, int cs)
786 static const char mbtitle[] = "%s Security Alert";
787 static const char msg[] =
788 "The first %.35scipher supported by the server\n"
789 "is %.64s, which is below the configured\n"
790 "warning threshold.\n"
791 "Do you want to continue with this connection?\n";
792 char *message, *title;
795 message = dupprintf(msg, ((cs == 0) ? "" :
796 (cs == 1) ? "client-to-server " :
797 "server-to-client "), ciphername);
798 title = dupprintf(mbtitle, appname);
799 mbret = MessageBox(NULL, message, title,
800 MB_ICONWARNING | MB_YESNO);
810 * Ask whether to wipe a session log file before writing to it.
811 * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
813 int askappend(void *frontend, Filename filename)
815 static const char msgtemplate[] =
816 "The session log file \"%.*s\" already exists.\n"
817 "You can overwrite it with a new session log,\n"
818 "append your session log to the end of it,\n"
819 "or disable session logging for this session.\n"
820 "Hit Yes to wipe the file, No to append to it,\n"
821 "or Cancel to disable logging.";
826 message = dupprintf(msgtemplate, FILENAME_MAX, filename.path);
827 mbtitle = dupprintf("%s Log to File", appname);
829 mbret = MessageBox(NULL, message, mbtitle,
830 MB_ICONQUESTION | MB_YESNOCANCEL);
837 else if (mbret == IDNO)
844 * Warn about the obsolescent key file format.
846 * Uniquely among these functions, this one does _not_ expect a
847 * frontend handle. This means that if PuTTY is ported to a
848 * platform which requires frontend handles, this function will be
849 * an anomaly. Fortunately, the problem it addresses will not have
850 * been present on that platform, so it can plausibly be
851 * implemented as an empty function.
853 void old_keyfile_warning(void)
855 static const char mbtitle[] = "%s Key File Warning";
856 static const char message[] =
857 "You are loading an SSH 2 private key which has an\n"
858 "old version of the file format. This means your key\n"
859 "file is not fully tamperproof. Future versions of\n"
860 "%s may stop supporting this private key format,\n"
861 "so we recommend you convert your key to the new\n"
864 "You can perform this conversion by loading the key\n"
865 "into PuTTYgen and then saving it again.";
868 msg = dupprintf(message, appname);
869 title = dupprintf(mbtitle, appname);
871 MessageBox(NULL, msg, title, MB_OK);