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 static int tabs[4] = { 78, 108 };
78 SendDlgItemMessage(hwnd, IDN_LIST, LB_SETTABSTOPS, 2,
81 for (i = 0; i < nevents; i++)
82 SendDlgItemMessage(hwnd, IDN_LIST, LB_ADDSTRING,
83 0, (LPARAM) events[i]);
86 switch (LOWORD(wParam)) {
90 SetActiveWindow(GetParent(hwnd));
94 if (HIWORD(wParam) == BN_CLICKED ||
95 HIWORD(wParam) == BN_DOUBLECLICKED) {
98 selcount = SendDlgItemMessage(hwnd, IDN_LIST,
99 LB_GETSELCOUNT, 0, 0);
100 if (selcount == 0) { /* don't even try to copy zero items */
105 selitems = smalloc(selcount * sizeof(int));
107 int count = SendDlgItemMessage(hwnd, IDN_LIST,
114 static unsigned char sel_nl[] = SEL_NL;
116 if (count == 0) { /* can't copy zero stuff */
122 for (i = 0; i < count; i++)
124 strlen(events[selitems[i]]) + sizeof(sel_nl);
126 clipdata = smalloc(size);
129 for (i = 0; i < count; i++) {
130 char *q = events[selitems[i]];
131 int qlen = strlen(q);
134 memcpy(p, sel_nl, sizeof(sel_nl));
137 write_aclip(NULL, clipdata, size, TRUE);
142 for (i = 0; i < nevents; i++)
143 SendDlgItemMessage(hwnd, IDN_LIST, LB_SETSEL,
152 SetActiveWindow(GetParent(hwnd));
159 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
160 WPARAM wParam, LPARAM lParam)
166 switch (LOWORD(wParam)) {
179 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
180 WPARAM wParam, LPARAM lParam)
184 SetDlgItemText(hwnd, IDA_VERSION, ver);
187 switch (LOWORD(wParam)) {
190 EndDialog(hwnd, TRUE);
193 EnableWindow(hwnd, 0);
194 DialogBox(hinst, MAKEINTRESOURCE(IDD_LICENCEBOX),
196 EnableWindow(hwnd, 1);
197 SetActiveWindow(hwnd);
201 /* Load web browser */
202 ShellExecute(hwnd, "open",
203 "http://www.chiark.greenend.org.uk/~sgtatham/putty/",
204 0, 0, SW_SHOWDEFAULT);
209 EndDialog(hwnd, TRUE);
216 * Null dialog procedure.
218 static int CALLBACK NullDlgProc(HWND hwnd, UINT msg,
219 WPARAM wParam, LPARAM lParam)
225 IDCX_ABOUT = IDC_ABOUT,
229 IDCX_PANELBASE = IDCX_STDBASE + 32
232 struct treeview_faff {
237 static HTREEITEM treeview_insert(struct treeview_faff *faff,
238 int level, char *text, char *path)
243 ins.hParent = (level > 0 ? faff->lastat[level - 1] : TVI_ROOT);
244 ins.hInsertAfter = faff->lastat[level];
245 #if _WIN32_IE >= 0x0400 && defined NONAMELESSUNION
246 #define INSITEM DUMMYUNIONNAME.item
250 ins.INSITEM.mask = TVIF_TEXT | TVIF_PARAM;
251 ins.INSITEM.pszText = text;
252 ins.INSITEM.cchTextMax = strlen(text)+1;
253 ins.INSITEM.lParam = (LPARAM)path;
254 newitem = TreeView_InsertItem(faff->treeview, &ins);
256 TreeView_Expand(faff->treeview, faff->lastat[level - 1],
258 faff->lastat[level] = newitem;
259 for (i = level + 1; i < 4; i++)
260 faff->lastat[i] = NULL;
265 * Create the panelfuls of controls in the configuration box.
267 static void create_controls(HWND hwnd, char *path)
276 * Here we must create the basic standard controls.
278 ctlposinit(&cp, hwnd, 3, 3, 235);
280 base_id = IDCX_STDBASE;
283 * Otherwise, we're creating the controls for a particular
286 ctlposinit(&cp, hwnd, 80, 3, 13);
288 base_id = IDCX_PANELBASE;
291 for (index=-1; (index = ctrl_find_path(ctrlbox, path, index)) >= 0 ;) {
292 struct controlset *s = ctrlbox->ctrlsets[index];
293 winctrl_layout(&dp, wc, &cp, s, &base_id);
298 * This function is the configuration box.
300 static int CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
301 WPARAM wParam, LPARAM lParam)
304 struct treeview_faff tvfaff;
310 create_controls(hwnd, ""); /* Open and Cancel buttons etc */
311 SetWindowLong(hwnd, GWL_USERDATA, 0);
313 SetWindowLong(hwnd, GWL_EXSTYLE,
314 GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
316 HWND item = GetDlgItem(hwnd, IDC_HELPBTN);
320 requested_help = FALSE;
321 SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
322 (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(IDI_CFGICON)));
326 { /* centre the window */
329 hw = GetDesktopWindow();
330 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
332 (rs.right + rs.left + rd.left - rd.right) / 2,
333 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
334 rd.right - rd.left, rd.bottom - rd.top, TRUE);
338 * Create the tree view.
346 r.right = r.left + 75;
348 r.bottom = r.top + 10;
349 MapDialogRect(hwnd, &r);
350 tvstatic = CreateWindowEx(0, "STATIC", "Cate&gory:",
351 WS_CHILD | WS_VISIBLE,
353 r.right - r.left, r.bottom - r.top,
354 hwnd, (HMENU) IDCX_TVSTATIC, hinst,
356 font = SendMessage(hwnd, WM_GETFONT, 0, 0);
357 SendMessage(tvstatic, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
360 r.right = r.left + 75;
362 r.bottom = r.top + 219;
363 MapDialogRect(hwnd, &r);
364 treeview = CreateWindowEx(WS_EX_CLIENTEDGE, WC_TREEVIEW, "",
365 WS_CHILD | WS_VISIBLE |
366 WS_TABSTOP | TVS_HASLINES |
367 TVS_DISABLEDRAGDROP | TVS_HASBUTTONS
369 TVS_SHOWSELALWAYS, r.left, r.top,
370 r.right - r.left, r.bottom - r.top,
371 hwnd, (HMENU) IDCX_TREEVIEW, hinst,
373 font = SendMessage(hwnd, WM_GETFONT, 0, 0);
374 SendMessage(treeview, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
375 tvfaff.treeview = treeview;
376 memset(tvfaff.lastat, 0, sizeof(tvfaff.lastat));
380 * Set up the tree view contents.
383 HTREEITEM hfirst = NULL;
387 for (i = 0; i < ctrlbox->nctrlsets; i++) {
388 struct controlset *s = ctrlbox->ctrlsets[i];
395 j = path ? ctrl_path_compare(s->pathname, path) : 0;
397 continue; /* same path, nothing to add to tree */
400 * We expect never to find an implicit path
401 * component. For example, we expect never to see
402 * A/B/C followed by A/D/E, because that would
403 * _implicitly_ create A/D. All our path prefixes
404 * are expected to contain actual controls and be
405 * selectable in the treeview; so we would expect
406 * to see A/D _explicitly_ before encountering
409 assert(j == ctrl_path_elements(s->pathname) - 1);
411 c = strrchr(s->pathname, '/');
417 item = treeview_insert(&tvfaff, j, c, s->pathname);
425 * Put the treeview selection on to the Session panel.
426 * This should also cause creation of the relevant
429 TreeView_SelectItem(treeview, hfirst);
433 * Set focus into the first available control.
439 for (i = 0; (c = winctrl_findbyindex(&ctrls_panel, i)) != NULL;
442 dlg_set_focus(c->ctrl, &dp);
448 SetWindowLong(hwnd, GWL_USERDATA, 1);
452 * Button release should trigger WM_OK if there was a
453 * previous double click on the session list.
457 EndDialog(hwnd, dp.endresult ? 1 : 0);
460 if (LOWORD(wParam) == IDCX_TREEVIEW &&
461 ((LPNMHDR) lParam)->code == TVN_SELCHANGED) {
463 TreeView_GetSelection(((LPNMHDR) lParam)->hwndFrom);
467 SendMessage (hwnd, WM_SETREDRAW, FALSE, 0);
470 item.pszText = buffer;
471 item.cchTextMax = sizeof(buffer);
472 item.mask = TVIF_TEXT | TVIF_PARAM;
473 TreeView_GetItem(((LPNMHDR) lParam)->hwndFrom, &item);
475 /* Destroy all controls in the currently visible panel. */
480 while ((c = winctrl_findbyindex(&ctrls_panel, 0)) != NULL) {
481 for (k = 0; k < c->num_ids; k++) {
482 item = GetDlgItem(hwnd, c->base_id + k);
486 winctrl_rem_shortcuts(&dp, c);
487 winctrl_remove(&ctrls_panel, c);
492 create_controls(hwnd, (char *)item.lParam);
494 dlg_refresh(NULL, &dp); /* set up control values */
496 SendMessage (hwnd, WM_SETREDRAW, TRUE, 0);
497 InvalidateRect (hwnd, NULL, TRUE);
499 SetFocus(((LPNMHDR) lParam)->hwndFrom); /* ensure focus stays */
505 default: /* also handle drag list msg here */
507 * Only process WM_COMMAND once the dialog is fully formed.
509 if (GetWindowLong(hwnd, GWL_USERDATA) == 1) {
510 ret = winctrl_handle_command(&dp, msg, wParam, lParam);
511 if (dp.ended && GetCapture() != hwnd)
512 EndDialog(hwnd, dp.endresult ? 1 : 0);
518 if (winctrl_context_help(&dp, hwnd,
519 ((LPHELPINFO)lParam)->iCtrlId))
520 requested_help = TRUE;
526 if (requested_help) {
527 WinHelp(hwnd, help_path, HELP_QUIT, 0);
528 requested_help = FALSE;
533 /* Grrr Explorer will maximize Dialogs! */
535 if (wParam == SIZE_MAXIMIZED)
543 void modal_about_box(HWND hwnd)
545 EnableWindow(hwnd, 0);
546 DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
547 EnableWindow(hwnd, 1);
548 SetActiveWindow(hwnd);
551 void show_help(HWND hwnd)
554 WinHelp(hwnd, help_path,
555 help_has_contents ? HELP_FINDER : HELP_CONTENTS,
557 requested_help = TRUE;
561 void defuse_showwindow(void)
564 * Work around the fact that the app's first call to ShowWindow
565 * will ignore the default in favour of the shell-provided
570 hwnd = CreateDialog(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX),
572 ShowWindow(hwnd, SW_HIDE);
573 SetActiveWindow(hwnd);
582 ctrlbox = ctrl_new_box();
583 setup_config_box(ctrlbox, &sesslist, FALSE, 0);
584 win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), FALSE);
586 winctrl_init(&ctrls_base);
587 winctrl_init(&ctrls_panel);
588 dp_add_tree(&dp, &ctrls_base);
589 dp_add_tree(&dp, &ctrls_panel);
590 dp.errtitle = "PuTTY Error";
592 dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
594 get_sesslist(&sesslist, TRUE);
596 DialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
598 get_sesslist(&sesslist, FALSE);
600 ctrl_free_box(ctrlbox);
601 winctrl_cleanup(&ctrls_panel);
602 winctrl_cleanup(&ctrls_base);
608 int do_reconfig(HWND hwnd)
613 backup_cfg = cfg; /* structure copy */
615 ctrlbox = ctrl_new_box();
616 setup_config_box(ctrlbox, NULL, TRUE, cfg.protocol);
617 win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), TRUE);
619 winctrl_init(&ctrls_base);
620 winctrl_init(&ctrls_panel);
621 dp_add_tree(&dp, &ctrls_base);
622 dp_add_tree(&dp, &ctrls_panel);
623 dp.errtitle = "PuTTY Error";
625 dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
628 DialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
631 ctrl_free_box(ctrlbox);
632 winctrl_cleanup(&ctrls_base);
633 winctrl_cleanup(&ctrls_panel);
637 cfg = backup_cfg; /* structure copy */
642 void logevent(void *frontend, char *string)
647 log_eventlog(logctx, string);
649 if (nevents >= negsize) {
651 events = srealloc(events, negsize * sizeof(*events));
655 strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t",
658 events[nevents] = smalloc(strlen(timebuf) + strlen(string) + 1);
659 strcpy(events[nevents], timebuf);
660 strcat(events[nevents], string);
663 SendDlgItemMessage(logbox, IDN_LIST, LB_ADDSTRING,
664 0, (LPARAM) events[nevents]);
665 count = SendDlgItemMessage(logbox, IDN_LIST, LB_GETCOUNT, 0, 0);
666 SendDlgItemMessage(logbox, IDN_LIST, LB_SETTOPINDEX, count - 1, 0);
671 void showeventlog(HWND hwnd)
674 logbox = CreateDialog(hinst, MAKEINTRESOURCE(IDD_LOGBOX),
676 ShowWindow(logbox, SW_SHOWNORMAL);
678 SetActiveWindow(logbox);
681 void showabout(HWND hwnd)
683 DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
686 void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
687 char *keystr, char *fingerprint)
691 static const char absentmsg[] =
692 "The server's host key is not cached in the registry. You\n"
693 "have no guarantee that the server is the computer you\n"
695 "The server's key fingerprint is:\n"
697 "If you trust this host, hit Yes to add the key to\n"
698 "PuTTY's cache and carry on connecting.\n"
699 "If you want to carry on connecting just once, without\n"
700 "adding the key to the cache, hit No.\n"
701 "If you do not trust this host, hit Cancel to abandon the\n"
704 static const char wrongmsg[] =
705 "WARNING - POTENTIAL SECURITY BREACH!\n"
707 "The server's host key does not match the one PuTTY has\n"
708 "cached in the registry. This means that either the\n"
709 "server administrator has changed the host key, or you\n"
710 "have actually connected to another computer pretending\n"
711 "to be the server.\n"
712 "The new key fingerprint is:\n"
714 "If you were expecting this change and trust the new key,\n"
715 "hit Yes to update PuTTY's cache and continue connecting.\n"
716 "If you want to carry on connecting but without updating\n"
717 "the cache, hit No.\n"
718 "If you want to abandon the connection completely, hit\n"
719 "Cancel. Hitting Cancel is the ONLY guaranteed safe\n" "choice.\n";
721 static const char mbtitle[] = "PuTTY Security Alert";
724 /* sensible fingerprint max size */
725 (sizeof(absentmsg) > sizeof(wrongmsg) ?
726 sizeof(absentmsg) : sizeof(wrongmsg))];
729 * Verify the key against the registry.
731 ret = verify_host_key(host, port, keytype, keystr);
733 if (ret == 0) /* success - key matched OK */
735 if (ret == 2) { /* key was different */
737 sprintf(message, wrongmsg, fingerprint);
738 mbret = MessageBox(NULL, message, mbtitle,
739 MB_ICONWARNING | MB_YESNOCANCEL);
741 store_host_key(host, port, keytype, keystr);
742 if (mbret == IDCANCEL)
745 if (ret == 1) { /* key was absent */
747 sprintf(message, absentmsg, fingerprint);
748 mbret = MessageBox(NULL, message, mbtitle,
749 MB_ICONWARNING | MB_YESNOCANCEL);
751 store_host_key(host, port, keytype, keystr);
752 if (mbret == IDCANCEL)
758 * Ask whether the selected cipher is acceptable (since it was
759 * below the configured 'warn' threshold).
760 * cs: 0 = both ways, 1 = client->server, 2 = server->client
762 void askcipher(void *frontend, char *ciphername, int cs)
764 static const char mbtitle[] = "PuTTY Security Alert";
765 static const char msg[] =
766 "The first %.35scipher supported by the server\n"
767 "is %.64s, which is below the configured\n"
768 "warning threshold.\n"
769 "Do you want to continue with this connection?\n";
770 /* guessed cipher name + type max length */
771 char message[100 + sizeof(msg)];
774 sprintf(message, msg,
776 (cs == 1) ? "client-to-server " :
779 mbret = MessageBox(NULL, message, mbtitle,
780 MB_ICONWARNING | MB_YESNO);
788 * Ask whether to wipe a session log file before writing to it.
789 * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
791 int askappend(void *frontend, Filename filename)
793 static const char mbtitle[] = "PuTTY Log to File";
794 static const char msgtemplate[] =
795 "The session log file \"%.*s\" already exists.\n"
796 "You can overwrite it with a new session log,\n"
797 "append your session log to the end of it,\n"
798 "or disable session logging for this session.\n"
799 "Hit Yes to wipe the file, No to append to it,\n"
800 "or Cancel to disable logging.";
801 char message[sizeof(msgtemplate) + FILENAME_MAX];
804 sprintf(message, msgtemplate, FILENAME_MAX, filename.path);
806 mbret = MessageBox(NULL, message, mbtitle,
807 MB_ICONQUESTION | MB_YESNOCANCEL);
810 else if (mbret == IDNO)
817 * Warn about the obsolescent key file format.
819 * Uniquely among these functions, this one does _not_ expect a
820 * frontend handle. This means that if PuTTY is ported to a
821 * platform which requires frontend handles, this function will be
822 * an anomaly. Fortunately, the problem it addresses will not have
823 * been present on that platform, so it can plausibly be
824 * implemented as an empty function.
826 void old_keyfile_warning(void)
828 static const char mbtitle[] = "PuTTY Key File Warning";
829 static const char message[] =
830 "You are loading an SSH 2 private key which has an\n"
831 "old version of the file format. This means your key\n"
832 "file is not fully tamperproof. Future versions of\n"
833 "PuTTY may stop supporting this private key format,\n"
834 "so we recommend you convert your key to the new\n"
837 "You can perform this conversion by loading the key\n"
838 "into PuTTYgen and then saving it again.";
840 MessageBox(NULL, message, mbtitle, MB_OK);