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);
585 winctrl_init(&ctrls_base);
586 winctrl_init(&ctrls_panel);
587 dp.controltrees[0] = &ctrls_base;
588 dp.controltrees[1] = &ctrls_panel;
590 dp.errtitle = "PuTTY Error";
593 dp.lastfocused = NULL;
594 memset(dp.shortcuts, 0, sizeof(dp.shortcuts));
595 dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
597 get_sesslist(&sesslist, TRUE);
599 DialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
601 get_sesslist(&sesslist, FALSE);
603 ctrl_free_box(ctrlbox);
604 winctrl_cleanup(&ctrls_base);
605 winctrl_cleanup(&ctrls_panel);
610 int do_reconfig(HWND hwnd)
615 backup_cfg = cfg; /* structure copy */
617 ctrlbox = ctrl_new_box();
618 setup_config_box(ctrlbox, NULL, TRUE, cfg.protocol);
619 win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), TRUE);
620 winctrl_init(&ctrls_base);
621 winctrl_init(&ctrls_panel);
622 dp.controltrees[0] = &ctrls_base;
623 dp.controltrees[1] = &ctrls_panel;
625 dp.errtitle = "PuTTY Error";
628 dp.lastfocused = NULL;
629 memset(dp.shortcuts, 0, sizeof(dp.shortcuts));
630 dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
633 DialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
636 ctrl_free_box(ctrlbox);
637 winctrl_cleanup(&ctrls_base);
638 winctrl_cleanup(&ctrls_panel);
641 cfg = backup_cfg; /* structure copy */
646 void logevent(void *frontend, char *string)
651 log_eventlog(logctx, string);
653 if (nevents >= negsize) {
655 events = srealloc(events, negsize * sizeof(*events));
659 strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t",
662 events[nevents] = smalloc(strlen(timebuf) + strlen(string) + 1);
663 strcpy(events[nevents], timebuf);
664 strcat(events[nevents], string);
667 SendDlgItemMessage(logbox, IDN_LIST, LB_ADDSTRING,
668 0, (LPARAM) events[nevents]);
669 count = SendDlgItemMessage(logbox, IDN_LIST, LB_GETCOUNT, 0, 0);
670 SendDlgItemMessage(logbox, IDN_LIST, LB_SETTOPINDEX, count - 1, 0);
675 void showeventlog(HWND hwnd)
678 logbox = CreateDialog(hinst, MAKEINTRESOURCE(IDD_LOGBOX),
680 ShowWindow(logbox, SW_SHOWNORMAL);
682 SetActiveWindow(logbox);
685 void showabout(HWND hwnd)
687 DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
690 void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
691 char *keystr, char *fingerprint)
695 static const char absentmsg[] =
696 "The server's host key is not cached in the registry. You\n"
697 "have no guarantee that the server is the computer you\n"
699 "The server's key fingerprint is:\n"
701 "If you trust this host, hit Yes to add the key to\n"
702 "PuTTY's cache and carry on connecting.\n"
703 "If you want to carry on connecting just once, without\n"
704 "adding the key to the cache, hit No.\n"
705 "If you do not trust this host, hit Cancel to abandon the\n"
708 static const char wrongmsg[] =
709 "WARNING - POTENTIAL SECURITY BREACH!\n"
711 "The server's host key does not match the one PuTTY has\n"
712 "cached in the registry. This means that either the\n"
713 "server administrator has changed the host key, or you\n"
714 "have actually connected to another computer pretending\n"
715 "to be the server.\n"
716 "The new key fingerprint is:\n"
718 "If you were expecting this change and trust the new key,\n"
719 "hit Yes to update PuTTY's cache and continue connecting.\n"
720 "If you want to carry on connecting but without updating\n"
721 "the cache, hit No.\n"
722 "If you want to abandon the connection completely, hit\n"
723 "Cancel. Hitting Cancel is the ONLY guaranteed safe\n" "choice.\n";
725 static const char mbtitle[] = "PuTTY Security Alert";
728 /* sensible fingerprint max size */
729 (sizeof(absentmsg) > sizeof(wrongmsg) ?
730 sizeof(absentmsg) : sizeof(wrongmsg))];
733 * Verify the key against the registry.
735 ret = verify_host_key(host, port, keytype, keystr);
737 if (ret == 0) /* success - key matched OK */
739 if (ret == 2) { /* key was different */
741 sprintf(message, wrongmsg, fingerprint);
742 mbret = MessageBox(NULL, message, mbtitle,
743 MB_ICONWARNING | MB_YESNOCANCEL);
745 store_host_key(host, port, keytype, keystr);
746 if (mbret == IDCANCEL)
749 if (ret == 1) { /* key was absent */
751 sprintf(message, absentmsg, fingerprint);
752 mbret = MessageBox(NULL, message, mbtitle,
753 MB_ICONWARNING | MB_YESNOCANCEL);
755 store_host_key(host, port, keytype, keystr);
756 if (mbret == IDCANCEL)
762 * Ask whether the selected cipher is acceptable (since it was
763 * below the configured 'warn' threshold).
764 * cs: 0 = both ways, 1 = client->server, 2 = server->client
766 void askcipher(void *frontend, char *ciphername, int cs)
768 static const char mbtitle[] = "PuTTY Security Alert";
769 static const char msg[] =
770 "The first %.35scipher supported by the server\n"
771 "is %.64s, which is below the configured\n"
772 "warning threshold.\n"
773 "Do you want to continue with this connection?\n";
774 /* guessed cipher name + type max length */
775 char message[100 + sizeof(msg)];
778 sprintf(message, msg,
780 (cs == 1) ? "client-to-server " :
783 mbret = MessageBox(NULL, message, mbtitle,
784 MB_ICONWARNING | MB_YESNO);
792 * Ask whether to wipe a session log file before writing to it.
793 * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
795 int askappend(void *frontend, Filename filename)
797 static const char mbtitle[] = "PuTTY Log to File";
798 static const char msgtemplate[] =
799 "The session log file \"%.*s\" already exists.\n"
800 "You can overwrite it with a new session log,\n"
801 "append your session log to the end of it,\n"
802 "or disable session logging for this session.\n"
803 "Hit Yes to wipe the file, No to append to it,\n"
804 "or Cancel to disable logging.";
805 char message[sizeof(msgtemplate) + FILENAME_MAX];
808 sprintf(message, msgtemplate, FILENAME_MAX, filename.path);
810 mbret = MessageBox(NULL, message, mbtitle,
811 MB_ICONQUESTION | MB_YESNOCANCEL);
814 else if (mbret == IDNO)
821 * Warn about the obsolescent key file format.
823 * Uniquely among these functions, this one does _not_ expect a
824 * frontend handle. This means that if PuTTY is ported to a
825 * platform which requires frontend handles, this function will be
826 * an anomaly. Fortunately, the problem it addresses will not have
827 * been present on that platform, so it can plausibly be
828 * implemented as an empty function.
830 void old_keyfile_warning(void)
832 static const char mbtitle[] = "PuTTY Key File Warning";
833 static const char message[] =
834 "You are loading an SSH 2 private key which has an\n"
835 "old version of the file format. This means your key\n"
836 "file is not fully tamperproof. Future versions of\n"
837 "PuTTY may stop supporting this private key format,\n"
838 "so we recommend you convert your key to the new\n"
841 "You can perform this conversion by loading the key\n"
842 "into PuTTYgen and then saving it again.";
844 MessageBox(NULL, message, mbtitle, MB_OK);