18 #define TVINSERTSTRUCT TV_INSERTSTRUCT
19 #define TVITEM TV_ITEM
24 * These are the various bits of data required to handle the
25 * portable-dialog stuff in the config box. Having them at file
26 * scope in here isn't too bad a place to put them; if we were ever
27 * to need more than one config box per process we could always
28 * shift them to a per-config-box structure stored in GWL_USERDATA.
30 static struct controlbox *ctrlbox;
32 * ctrls_base holds the OK and Cancel buttons: the controls which
33 * are present in all dialog panels. ctrls_panel holds the ones
34 * which change from panel to panel.
36 static struct winctrls ctrls_base, ctrls_panel;
37 static struct dlgparam dp;
39 static char **events = NULL;
40 static int nevents = 0, negsize = 0;
42 static int requested_help;
44 extern Config cfg; /* defined in window.c */
46 struct sesslist sesslist; /* exported to window.c */
48 #define PRINTER_DISABLED_STRING "None (printing disabled)"
50 void force_normal(HWND hwnd)
52 static int recurse = 0;
60 wp.length = sizeof(wp);
61 if (GetWindowPlacement(hwnd, &wp) && wp.showCmd == SW_SHOWMAXIMIZED) {
62 wp.showCmd = SW_SHOWNORMAL;
63 SetWindowPlacement(hwnd, &wp);
68 static int CALLBACK LogProc(HWND hwnd, UINT msg,
69 WPARAM wParam, LPARAM lParam)
76 char *str = dupprintf("%s Event Log", appname);
77 SetWindowText(hwnd, str);
81 static int tabs[4] = { 78, 108 };
82 SendDlgItemMessage(hwnd, IDN_LIST, LB_SETTABSTOPS, 2,
85 for (i = 0; i < nevents; i++)
86 SendDlgItemMessage(hwnd, IDN_LIST, LB_ADDSTRING,
87 0, (LPARAM) events[i]);
90 switch (LOWORD(wParam)) {
94 SetActiveWindow(GetParent(hwnd));
98 if (HIWORD(wParam) == BN_CLICKED ||
99 HIWORD(wParam) == BN_DOUBLECLICKED) {
102 selcount = SendDlgItemMessage(hwnd, IDN_LIST,
103 LB_GETSELCOUNT, 0, 0);
104 if (selcount == 0) { /* don't even try to copy zero items */
109 selitems = snewn(selcount, int);
111 int count = SendDlgItemMessage(hwnd, IDN_LIST,
118 static unsigned char sel_nl[] = SEL_NL;
120 if (count == 0) { /* can't copy zero stuff */
126 for (i = 0; i < count; i++)
128 strlen(events[selitems[i]]) + sizeof(sel_nl);
130 clipdata = snewn(size, char);
133 for (i = 0; i < count; i++) {
134 char *q = events[selitems[i]];
135 int qlen = strlen(q);
138 memcpy(p, sel_nl, sizeof(sel_nl));
141 write_aclip(NULL, clipdata, size, TRUE);
146 for (i = 0; i < nevents; i++)
147 SendDlgItemMessage(hwnd, IDN_LIST, LB_SETSEL,
156 SetActiveWindow(GetParent(hwnd));
163 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
164 WPARAM wParam, LPARAM lParam)
169 char *str = dupprintf("%s Licence", appname);
170 SetWindowText(hwnd, str);
175 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);
231 * Null dialog procedure.
233 static int CALLBACK NullDlgProc(HWND hwnd, UINT msg,
234 WPARAM wParam, LPARAM lParam)
240 IDCX_ABOUT = IDC_ABOUT,
244 IDCX_PANELBASE = IDCX_STDBASE + 32
247 struct treeview_faff {
252 static HTREEITEM treeview_insert(struct treeview_faff *faff,
253 int level, char *text, char *path)
258 ins.hParent = (level > 0 ? faff->lastat[level - 1] : TVI_ROOT);
259 ins.hInsertAfter = faff->lastat[level];
260 #if _WIN32_IE >= 0x0400 && defined NONAMELESSUNION
261 #define INSITEM DUMMYUNIONNAME.item
265 ins.INSITEM.mask = TVIF_TEXT | TVIF_PARAM;
266 ins.INSITEM.pszText = text;
267 ins.INSITEM.cchTextMax = strlen(text)+1;
268 ins.INSITEM.lParam = (LPARAM)path;
269 newitem = TreeView_InsertItem(faff->treeview, &ins);
271 TreeView_Expand(faff->treeview, faff->lastat[level - 1],
273 faff->lastat[level] = newitem;
274 for (i = level + 1; i < 4; i++)
275 faff->lastat[i] = NULL;
280 * Create the panelfuls of controls in the configuration box.
282 static void create_controls(HWND hwnd, char *path)
291 * Here we must create the basic standard controls.
293 ctlposinit(&cp, hwnd, 3, 3, 235);
295 base_id = IDCX_STDBASE;
298 * Otherwise, we're creating the controls for a particular
301 ctlposinit(&cp, hwnd, 80, 3, 13);
303 base_id = IDCX_PANELBASE;
306 for (index=-1; (index = ctrl_find_path(ctrlbox, path, index)) >= 0 ;) {
307 struct controlset *s = ctrlbox->ctrlsets[index];
308 winctrl_layout(&dp, wc, &cp, s, &base_id);
313 * This function is the configuration box.
315 static int CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
316 WPARAM wParam, LPARAM lParam)
319 struct treeview_faff tvfaff;
325 create_controls(hwnd, ""); /* Open and Cancel buttons etc */
326 SetWindowText(hwnd, dp.wintitle);
327 SetWindowLong(hwnd, GWL_USERDATA, 0);
329 SetWindowLong(hwnd, GWL_EXSTYLE,
330 GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
332 HWND item = GetDlgItem(hwnd, IDC_HELPBTN);
336 requested_help = FALSE;
337 SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
338 (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(IDI_CFGICON)));
342 { /* centre the window */
345 hw = GetDesktopWindow();
346 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
348 (rs.right + rs.left + rd.left - rd.right) / 2,
349 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
350 rd.right - rd.left, rd.bottom - rd.top, TRUE);
354 * Create the tree view.
362 r.right = r.left + 75;
364 r.bottom = r.top + 10;
365 MapDialogRect(hwnd, &r);
366 tvstatic = CreateWindowEx(0, "STATIC", "Cate&gory:",
367 WS_CHILD | WS_VISIBLE,
369 r.right - r.left, r.bottom - r.top,
370 hwnd, (HMENU) IDCX_TVSTATIC, hinst,
372 font = SendMessage(hwnd, WM_GETFONT, 0, 0);
373 SendMessage(tvstatic, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
376 r.right = r.left + 75;
378 r.bottom = r.top + 219;
379 MapDialogRect(hwnd, &r);
380 treeview = CreateWindowEx(WS_EX_CLIENTEDGE, WC_TREEVIEW, "",
381 WS_CHILD | WS_VISIBLE |
382 WS_TABSTOP | TVS_HASLINES |
383 TVS_DISABLEDRAGDROP | TVS_HASBUTTONS
385 TVS_SHOWSELALWAYS, r.left, r.top,
386 r.right - r.left, r.bottom - r.top,
387 hwnd, (HMENU) IDCX_TREEVIEW, hinst,
389 font = SendMessage(hwnd, WM_GETFONT, 0, 0);
390 SendMessage(treeview, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
391 tvfaff.treeview = treeview;
392 memset(tvfaff.lastat, 0, sizeof(tvfaff.lastat));
396 * Set up the tree view contents.
399 HTREEITEM hfirst = NULL;
403 for (i = 0; i < ctrlbox->nctrlsets; i++) {
404 struct controlset *s = ctrlbox->ctrlsets[i];
411 j = path ? ctrl_path_compare(s->pathname, path) : 0;
413 continue; /* same path, nothing to add to tree */
416 * We expect never to find an implicit path
417 * component. For example, we expect never to see
418 * A/B/C followed by A/D/E, because that would
419 * _implicitly_ create A/D. All our path prefixes
420 * are expected to contain actual controls and be
421 * selectable in the treeview; so we would expect
422 * to see A/D _explicitly_ before encountering
425 assert(j == ctrl_path_elements(s->pathname) - 1);
427 c = strrchr(s->pathname, '/');
433 item = treeview_insert(&tvfaff, j, c, s->pathname);
441 * Put the treeview selection on to the Session panel.
442 * This should also cause creation of the relevant
445 TreeView_SelectItem(treeview, hfirst);
449 * Set focus into the first available control.
455 for (i = 0; (c = winctrl_findbyindex(&ctrls_panel, i)) != NULL;
458 dlg_set_focus(c->ctrl, &dp);
464 SetWindowLong(hwnd, GWL_USERDATA, 1);
468 * Button release should trigger WM_OK if there was a
469 * previous double click on the session list.
473 SaneEndDialog(hwnd, dp.endresult ? 1 : 0);
476 if (LOWORD(wParam) == IDCX_TREEVIEW &&
477 ((LPNMHDR) lParam)->code == TVN_SELCHANGED) {
479 TreeView_GetSelection(((LPNMHDR) lParam)->hwndFrom);
483 SendMessage (hwnd, WM_SETREDRAW, FALSE, 0);
486 item.pszText = buffer;
487 item.cchTextMax = sizeof(buffer);
488 item.mask = TVIF_TEXT | TVIF_PARAM;
489 TreeView_GetItem(((LPNMHDR) lParam)->hwndFrom, &item);
491 /* Destroy all controls in the currently visible panel. */
496 while ((c = winctrl_findbyindex(&ctrls_panel, 0)) != NULL) {
497 for (k = 0; k < c->num_ids; k++) {
498 item = GetDlgItem(hwnd, c->base_id + k);
502 winctrl_rem_shortcuts(&dp, c);
503 winctrl_remove(&ctrls_panel, c);
508 create_controls(hwnd, (char *)item.lParam);
510 dlg_refresh(NULL, &dp); /* set up control values */
512 SendMessage (hwnd, WM_SETREDRAW, TRUE, 0);
513 InvalidateRect (hwnd, NULL, TRUE);
515 SetFocus(((LPNMHDR) lParam)->hwndFrom); /* ensure focus stays */
521 default: /* also handle drag list msg here */
523 * Only process WM_COMMAND once the dialog is fully formed.
525 if (GetWindowLong(hwnd, GWL_USERDATA) == 1) {
526 ret = winctrl_handle_command(&dp, msg, wParam, lParam);
527 if (dp.ended && GetCapture() != hwnd)
528 SaneEndDialog(hwnd, dp.endresult ? 1 : 0);
534 if (winctrl_context_help(&dp, hwnd,
535 ((LPHELPINFO)lParam)->iCtrlId))
536 requested_help = TRUE;
542 if (requested_help) {
543 WinHelp(hwnd, help_path, HELP_QUIT, 0);
544 requested_help = FALSE;
546 SaneEndDialog(hwnd, 0);
549 /* Grrr Explorer will maximize Dialogs! */
551 if (wParam == SIZE_MAXIMIZED)
559 void modal_about_box(HWND hwnd)
561 EnableWindow(hwnd, 0);
562 DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
563 EnableWindow(hwnd, 1);
564 SetActiveWindow(hwnd);
567 void show_help(HWND hwnd)
570 WinHelp(hwnd, help_path,
571 help_has_contents ? HELP_FINDER : HELP_CONTENTS,
573 requested_help = TRUE;
577 void defuse_showwindow(void)
580 * Work around the fact that the app's first call to ShowWindow
581 * will ignore the default in favour of the shell-provided
586 hwnd = CreateDialog(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX),
588 ShowWindow(hwnd, SW_HIDE);
589 SetActiveWindow(hwnd);
598 ctrlbox = ctrl_new_box();
599 setup_config_box(ctrlbox, &sesslist, FALSE, 0);
600 win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), FALSE);
602 winctrl_init(&ctrls_base);
603 winctrl_init(&ctrls_panel);
604 dp_add_tree(&dp, &ctrls_base);
605 dp_add_tree(&dp, &ctrls_panel);
606 dp.wintitle = dupprintf("%s Configuration", appname);
607 dp.errtitle = dupprintf("%s Error", appname);
609 dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
611 get_sesslist(&sesslist, TRUE);
613 SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
615 get_sesslist(&sesslist, FALSE);
617 ctrl_free_box(ctrlbox);
618 winctrl_cleanup(&ctrls_panel);
619 winctrl_cleanup(&ctrls_base);
625 int do_reconfig(HWND hwnd)
630 backup_cfg = cfg; /* structure copy */
632 ctrlbox = ctrl_new_box();
633 setup_config_box(ctrlbox, NULL, TRUE, cfg.protocol);
634 win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), TRUE);
636 winctrl_init(&ctrls_base);
637 winctrl_init(&ctrls_panel);
638 dp_add_tree(&dp, &ctrls_base);
639 dp_add_tree(&dp, &ctrls_panel);
640 dp.wintitle = dupprintf("%s Reconfiguration", appname);
641 dp.errtitle = dupprintf("%s Error", appname);
643 dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
645 ret = SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
648 ctrl_free_box(ctrlbox);
649 winctrl_cleanup(&ctrls_base);
650 winctrl_cleanup(&ctrls_panel);
655 cfg = backup_cfg; /* structure copy */
660 void logevent(void *frontend, const char *string)
665 log_eventlog(logctx, string);
667 if (nevents >= negsize) {
669 events = sresize(events, negsize, char *);
673 strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t",
676 events[nevents] = snewn(strlen(timebuf) + strlen(string) + 1, char);
677 strcpy(events[nevents], timebuf);
678 strcat(events[nevents], string);
681 SendDlgItemMessage(logbox, IDN_LIST, LB_ADDSTRING,
682 0, (LPARAM) events[nevents]);
683 count = SendDlgItemMessage(logbox, IDN_LIST, LB_GETCOUNT, 0, 0);
684 SendDlgItemMessage(logbox, IDN_LIST, LB_SETTOPINDEX, count - 1, 0);
689 void showeventlog(HWND hwnd)
692 logbox = CreateDialog(hinst, MAKEINTRESOURCE(IDD_LOGBOX),
694 ShowWindow(logbox, SW_SHOWNORMAL);
696 SetActiveWindow(logbox);
699 void showabout(HWND hwnd)
701 DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
704 void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
705 char *keystr, char *fingerprint)
709 static const char absentmsg[] =
710 "The server's host key is not cached in the registry. You\n"
711 "have no guarantee that the server is the computer you\n"
713 "The server's %s key fingerprint is:\n"
715 "If you trust this host, hit Yes to add the key to\n"
716 "%s's cache and carry on connecting.\n"
717 "If you want to carry on connecting just once, without\n"
718 "adding the key to the cache, hit No.\n"
719 "If you do not trust this host, hit Cancel to abandon the\n"
722 static const char wrongmsg[] =
723 "WARNING - POTENTIAL SECURITY BREACH!\n"
725 "The server's host key does not match the one %s has\n"
726 "cached in the registry. This means that either the\n"
727 "server administrator has changed the host key, or you\n"
728 "have actually connected to another computer pretending\n"
729 "to be the server.\n"
730 "The new %s key fingerprint is:\n"
732 "If you were expecting this change and trust the new key,\n"
733 "hit Yes to update %s's cache and continue connecting.\n"
734 "If you want to carry on connecting but without updating\n"
735 "the cache, hit No.\n"
736 "If you want to abandon the connection completely, hit\n"
737 "Cancel. Hitting Cancel is the ONLY guaranteed safe\n" "choice.\n";
739 static const char mbtitle[] = "%s Security Alert";
742 * Verify the key against the registry.
744 ret = verify_host_key(host, port, keytype, keystr);
746 if (ret == 0) /* success - key matched OK */
748 if (ret == 2) { /* key was different */
750 char *message, *title;
751 message = dupprintf(wrongmsg, appname, keytype, fingerprint, appname);
752 title = dupprintf(mbtitle, appname);
753 mbret = MessageBox(NULL, message, title,
754 MB_ICONWARNING | MB_YESNOCANCEL);
758 store_host_key(host, port, keytype, keystr);
759 if (mbret == IDCANCEL)
762 if (ret == 1) { /* key was absent */
764 char *message, *title;
765 message = dupprintf(absentmsg, keytype, fingerprint, appname);
766 title = dupprintf(mbtitle, appname);
767 mbret = MessageBox(NULL, message, title,
768 MB_ICONWARNING | MB_YESNOCANCEL);
772 store_host_key(host, port, keytype, keystr);
773 if (mbret == IDCANCEL)
779 * Ask whether the selected cipher is acceptable (since it was
780 * below the configured 'warn' threshold).
781 * cs: 0 = both ways, 1 = client->server, 2 = server->client
783 void askcipher(void *frontend, char *ciphername, int cs)
785 static const char mbtitle[] = "%s Security Alert";
786 static const char msg[] =
787 "The first %.35scipher supported by the server\n"
788 "is %.64s, which is below the configured\n"
789 "warning threshold.\n"
790 "Do you want to continue with this connection?\n";
791 char *message, *title;
794 message = dupprintf(msg, ((cs == 0) ? "" :
795 (cs == 1) ? "client-to-server " :
796 "server-to-client "), ciphername);
797 title = dupprintf(mbtitle, appname);
798 mbret = MessageBox(NULL, message, title,
799 MB_ICONWARNING | MB_YESNO);
809 * Ask whether to wipe a session log file before writing to it.
810 * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
812 int askappend(void *frontend, Filename filename)
814 static const char msgtemplate[] =
815 "The session log file \"%.*s\" already exists.\n"
816 "You can overwrite it with a new session log,\n"
817 "append your session log to the end of it,\n"
818 "or disable session logging for this session.\n"
819 "Hit Yes to wipe the file, No to append to it,\n"
820 "or Cancel to disable logging.";
825 message = dupprintf(msgtemplate, FILENAME_MAX, filename.path);
826 mbtitle = dupprintf("%s Log to File", appname);
828 mbret = MessageBox(NULL, message, mbtitle,
829 MB_ICONQUESTION | MB_YESNOCANCEL);
836 else if (mbret == IDNO)
843 * Warn about the obsolescent key file format.
845 * Uniquely among these functions, this one does _not_ expect a
846 * frontend handle. This means that if PuTTY is ported to a
847 * platform which requires frontend handles, this function will be
848 * an anomaly. Fortunately, the problem it addresses will not have
849 * been present on that platform, so it can plausibly be
850 * implemented as an empty function.
852 void old_keyfile_warning(void)
854 static const char mbtitle[] = "%s Key File Warning";
855 static const char message[] =
856 "You are loading an SSH 2 private key which has an\n"
857 "old version of the file format. This means your key\n"
858 "file is not fully tamperproof. Future versions of\n"
859 "%s may stop supporting this private key format,\n"
860 "so we recommend you convert your key to the new\n"
863 "You can perform this conversion by loading the key\n"
864 "into PuTTYgen and then saving it again.";
867 msg = dupprintf(message, appname);
868 title = dupprintf(mbtitle, appname);
870 MessageBox(NULL, msg, title, MB_OK);