X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/14963b8ffdc189c7f1e0a5006a23ab5b01ae13d1..4e95095a1785e436b46f4e79052189cadac4f668:/windlg.c diff --git a/windlg.c b/windlg.c index afe96249..9c24449b 100644 --- a/windlg.c +++ b/windlg.c @@ -1,1455 +1,874 @@ -#include -#include -#include -#include #include #include +#include +#include +#include +#include -#include "ssh.h" #include "putty.h" +#include "ssh.h" #include "win_res.h" +#include "storage.h" +#include "dialog.h" -#define NPANELS 8 -#define MAIN_NPANELS 8 -#define RECONF_NPANELS 5 - -static const char *const puttystr = PUTTY_REG_POS "\\Sessions"; - -static char **negots = NULL; -static int nnegots = 0, negsize = 0; -static HWND logbox = NULL, abtbox = NULL; - -static char hex[16] = "0123456789ABCDEF"; - -static void mungestr(char *in, char *out) { - int candot = 0; - - while (*in) { - if (*in == ' ' || *in == '\\' || *in == '*' || *in == '?' || - *in == '%' || *in < ' ' || *in > '~' || (*in == '.' && !candot)) { - *out++ = '%'; - *out++ = hex[((unsigned char)*in) >> 4]; - *out++ = hex[((unsigned char)*in) & 15]; - } else - *out++ = *in; - in++; - candot = 1; - } - *out = '\0'; - return; -} - -static void unmungestr(char *in, char *out) { - while (*in) { - if (*in == '%' && in[1] && in[2]) { - int i, j; - - i = in[1] - '0'; i -= (i > 9 ? 7 : 0); - j = in[2] - '0'; j -= (j > 9 ? 7 : 0); - - *out++ = (i<<4) + j; - in += 3; - } else - *out++ = *in++; - } - *out = '\0'; - return; -} - -static void wpps(HKEY key, LPCTSTR name, LPCTSTR value) { - RegSetValueEx(key, name, 0, REG_SZ, value, 1+strlen(value)); -} - -static void wppi(HKEY key, LPCTSTR name, int value) { - RegSetValueEx(key, name, 0, REG_DWORD, - (CONST BYTE *)&value, sizeof(value)); -} - -static void gpps(HKEY key, LPCTSTR name, LPCTSTR def, - LPTSTR val, int len) { - DWORD type, size; - size = len; - - if (key == NULL || - RegQueryValueEx(key, name, 0, &type, val, &size) != ERROR_SUCCESS || - type != REG_SZ) { - strncpy(val, def, len); - val[len-1] = '\0'; - } -} +#include +#include +#include -static void gppi(HKEY key, LPCTSTR name, int def, int *i) { - DWORD type, val, size; - size = sizeof(val); +#ifdef MSVC4 +#define TVINSERTSTRUCT TV_INSERTSTRUCT +#define TVITEM TV_ITEM +#define ICON_BIG 1 +#endif - if (key == NULL || - RegQueryValueEx(key, name, 0, &type, - (BYTE *)&val, &size) != ERROR_SUCCESS || - size != sizeof(val) || type != REG_DWORD) - *i = def; - else - *i = val; -} +/* + * These are the various bits of data required to handle the + * portable-dialog stuff in the config box. Having them at file + * scope in here isn't too bad a place to put them; if we were ever + * to need more than one config box per process we could always + * shift them to a per-config-box structure stored in GWL_USERDATA. + */ +static struct controlbox *ctrlbox; +/* + * ctrls_base holds the OK and Cancel buttons: the controls which + * are present in all dialog panels. ctrls_panel holds the ones + * which change from panel to panel. + */ +static struct winctrls ctrls_base, ctrls_panel; +static struct dlgparam dp; -static HINSTANCE hinst; +static char **events = NULL; +static int nevents = 0, negsize = 0; -static int readytogo; +static int requested_help; -static void save_settings (char *section, int do_host) { - int i; - HKEY subkey1, sesskey; - char *p; - - p = malloc(3*strlen(section)+1); - mungestr(section, p); - - if (RegCreateKey(HKEY_CURRENT_USER, puttystr, &subkey1)!=ERROR_SUCCESS || - RegCreateKey(subkey1, p, &sesskey) != ERROR_SUCCESS) { - sesskey = NULL; - } +extern Config cfg; /* defined in window.c */ - free(p); - RegCloseKey(subkey1); +struct sesslist sesslist; /* exported to window.c */ - wppi (sesskey, "Present", 1); - if (do_host) { - wpps (sesskey, "HostName", cfg.host); - wppi (sesskey, "PortNumber", cfg.port); - wpps (sesskey, "Protocol", - cfg.protocol == PROT_SSH ? "ssh" : - cfg.protocol == PROT_TELNET ? "telnet" : "raw" ); - } - wppi (sesskey, "CloseOnExit", !!cfg.close_on_exit); - wppi (sesskey, "WarnOnClose", !!cfg.warn_on_close); - wpps (sesskey, "TerminalType", cfg.termtype); - wpps (sesskey, "TerminalSpeed", cfg.termspeed); - { - char buf[2*sizeof(cfg.environmt)], *p, *q; - p = buf; - q = cfg.environmt; - while (*q) { - while (*q) { - int c = *q++; - if (c == '=' || c == ',' || c == '\\') - *p++ = '\\'; - if (c == '\t') - c = '='; - *p++ = c; - } - *p++ = ','; - q++; - } - *p = '\0'; - wpps (sesskey, "Environment", buf); - } - wpps (sesskey, "UserName", cfg.username); - wppi (sesskey, "NoPTY", cfg.nopty); - wpps (sesskey, "Cipher", cfg.cipher == CIPHER_BLOWFISH ? "blowfish" : - cfg.cipher == CIPHER_DES ? "des" : "3des"); - wppi (sesskey, "RFCEnviron", cfg.rfc_environ); - wppi (sesskey, "BackspaceIsDelete", cfg.bksp_is_delete); - wppi (sesskey, "RXVTHomeEnd", cfg.rxvt_homeend); - wppi (sesskey, "LinuxFunctionKeys", cfg.linux_funkeys); - wppi (sesskey, "ApplicationCursorKeys", cfg.app_cursor); - wppi (sesskey, "ApplicationKeypad", cfg.app_keypad); - wppi (sesskey, "ScrollbackLines", cfg.savelines); - wppi (sesskey, "DECOriginMode", cfg.dec_om); - wppi (sesskey, "AutoWrapMode", cfg.wrap_mode); - wppi (sesskey, "LFImpliesCR", cfg.lfhascr); - wppi (sesskey, "WinNameAlways", cfg.win_name_always); - wppi (sesskey, "TermWidth", cfg.width); - wppi (sesskey, "TermHeight", cfg.height); - wpps (sesskey, "Font", cfg.font); - wppi (sesskey, "FontIsBold", cfg.fontisbold); - wppi (sesskey, "FontCharSet", cfg.fontcharset); - wppi (sesskey, "FontHeight", cfg.fontheight); - wppi (sesskey, "FontVTMode", cfg.vtmode); - wppi (sesskey, "TryPalette", cfg.try_palette); - wppi (sesskey, "BoldAsColour", cfg.bold_colour); - for (i=0; i<22; i++) { - char buf[20], buf2[30]; - sprintf(buf, "Colour%d", i); - sprintf(buf2, "%d,%d,%d", cfg.colours[i][0], - cfg.colours[i][1], cfg.colours[i][2]); - wpps (sesskey, buf, buf2); - } - wppi (sesskey, "MouseIsXterm", cfg.mouse_is_xterm); - for (i=0; i<256; i+=32) { - char buf[20], buf2[256]; - int j; - sprintf(buf, "Wordness%d", i); - *buf2 = '\0'; - for (j=i; j 0 ? faff->lastat[level - 1] : TVI_ROOT); + ins.hInsertAfter = faff->lastat[level]; +#if _WIN32_IE >= 0x0400 && defined NONAMELESSUNION +#define INSITEM DUMMYUNIONNAME.item +#else +#define INSITEM item +#endif + ins.INSITEM.mask = TVIF_TEXT | TVIF_PARAM; + ins.INSITEM.pszText = text; + ins.INSITEM.cchTextMax = strlen(text)+1; + ins.INSITEM.lParam = (LPARAM)path; + newitem = TreeView_InsertItem(faff->treeview, &ins); + if (level > 0) + TreeView_Expand(faff->treeview, faff->lastat[level - 1], + TVE_EXPAND); + faff->lastat[level] = newitem; + for (i = level + 1; i < 4; i++) + faff->lastat[i] = NULL; + return newitem; +} + +/* + * Create the panelfuls of controls in the configuration box. + */ +static void create_controls(HWND hwnd, char *path) +{ + struct ctlpos cp; + int index; + int base_id; + struct winctrls *wc; + + if (!path[0]) { + /* + * Here we must create the basic standard controls. + */ + ctlposinit(&cp, hwnd, 3, 3, 235); + wc = &ctrls_base; + base_id = IDCX_STDBASE; + } else { + /* + * Otherwise, we're creating the controls for a particular + * panel. + */ + ctlposinit(&cp, hwnd, 80, 3, 13); + wc = &ctrls_panel; + base_id = IDCX_PANELBASE; } - return GeneralPanelProc (hwnd, msg, wParam, lParam); -} -static void fmtfont (char *buf) { - sprintf (buf, "Font: %s, ", cfg.font); - if (cfg.fontisbold) - strcat(buf, "bold, "); - if (cfg.fontheight == 0) - strcat (buf, "default height"); - else - sprintf (buf+strlen(buf), "%d-%s", - (cfg.fontheight < 0 ? -cfg.fontheight : cfg.fontheight), - (cfg.fontheight < 0 ? "pixel" : "point")); -} - -static int CALLBACK TerminalProc (HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) { - CHOOSEFONT cf; - LOGFONT lf; - char fontstatic[256]; - - switch (msg) { - case WM_INITDIALOG: - CheckDlgButton (hwnd, IDC2_WRAPMODE, cfg.wrap_mode); - CheckDlgButton (hwnd, IDC2_WINNAME, cfg.win_name_always); - CheckDlgButton (hwnd, IDC2_DECOM, cfg.dec_om); - CheckDlgButton (hwnd, IDC2_LFHASCR, cfg.lfhascr); - SetDlgItemInt (hwnd, IDC2_ROWSEDIT, cfg.height, FALSE); - SetDlgItemInt (hwnd, IDC2_COLSEDIT, cfg.width, FALSE); - SetDlgItemInt (hwnd, IDC2_SAVEEDIT, cfg.savelines, FALSE); - fmtfont (fontstatic); - SetDlgItemText (hwnd, IDC2_FONTSTATIC, fontstatic); - CheckRadioButton (hwnd, IDC2_VTXWINDOWS, IDC2_VTPOORMAN, - cfg.vtmode == VT_XWINDOWS ? IDC2_VTXWINDOWS : - cfg.vtmode == VT_OEMANSI ? IDC2_VTOEMANSI : - cfg.vtmode == VT_OEMONLY ? IDC2_VTOEMONLY : - IDC2_VTPOORMAN); - break; - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDC2_WRAPMODE: - if (HIWORD(wParam) == BN_CLICKED || - HIWORD(wParam) == BN_DOUBLECLICKED) - cfg.wrap_mode = IsDlgButtonChecked (hwnd, IDC2_WRAPMODE); - break; - case IDC2_WINNAME: - if (HIWORD(wParam) == BN_CLICKED || - HIWORD(wParam) == BN_DOUBLECLICKED) - cfg.win_name_always = IsDlgButtonChecked (hwnd, IDC2_WINNAME); - break; - case IDC2_DECOM: - if (HIWORD(wParam) == BN_CLICKED || - HIWORD(wParam) == BN_DOUBLECLICKED) - cfg.dec_om = IsDlgButtonChecked (hwnd, IDC2_DECOM); - break; - case IDC2_LFHASCR: - if (HIWORD(wParam) == BN_CLICKED || - HIWORD(wParam) == BN_DOUBLECLICKED) - cfg.lfhascr = IsDlgButtonChecked (hwnd, IDC2_LFHASCR); - break; - case IDC2_ROWSEDIT: - if (HIWORD(wParam) == EN_CHANGE) - MyGetDlgItemInt (hwnd, IDC2_ROWSEDIT, &cfg.height); - break; - case IDC2_COLSEDIT: - if (HIWORD(wParam) == EN_CHANGE) - MyGetDlgItemInt (hwnd, IDC2_COLSEDIT, &cfg.width); - break; - case IDC2_SAVEEDIT: - if (HIWORD(wParam) == EN_CHANGE) - MyGetDlgItemInt (hwnd, IDC2_SAVEEDIT, &cfg.savelines); - break; - case IDC2_CHOOSEFONT: - lf.lfHeight = cfg.fontheight; - lf.lfWidth = lf.lfEscapement = lf.lfOrientation = 0; - lf.lfItalic = lf.lfUnderline = lf.lfStrikeOut = 0; - lf.lfWeight = (cfg.fontisbold ? FW_BOLD : 0); - lf.lfCharSet = cfg.fontcharset; - lf.lfOutPrecision = OUT_DEFAULT_PRECIS; - lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; - lf.lfQuality = DEFAULT_QUALITY; - lf.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE; - strncpy (lf.lfFaceName, cfg.font, sizeof(lf.lfFaceName)-1); - lf.lfFaceName[sizeof(lf.lfFaceName)-1] = '\0'; - - cf.lStructSize = sizeof(cf); - cf.hwndOwner = hwnd; - cf.lpLogFont = &lf; - cf.Flags = CF_FIXEDPITCHONLY | CF_FORCEFONTEXIST | - CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS; - - if (ChooseFont (&cf)) { - strncpy (cfg.font, lf.lfFaceName, sizeof(cfg.font)-1); - cfg.font[sizeof(cfg.font)-1] = '\0'; - cfg.fontisbold = (lf.lfWeight == FW_BOLD); - cfg.fontcharset = lf.lfCharSet; - cfg.fontheight = lf.lfHeight; - fmtfont (fontstatic); - SetDlgItemText (hwnd, IDC2_FONTSTATIC, fontstatic); - } - break; - case IDC2_VTXWINDOWS: - case IDC2_VTOEMANSI: - case IDC2_VTOEMONLY: - case IDC2_VTPOORMAN: - cfg.vtmode = - (IsDlgButtonChecked (hwnd, IDC2_VTXWINDOWS) ? VT_XWINDOWS : - IsDlgButtonChecked (hwnd, IDC2_VTOEMANSI) ? VT_OEMANSI : - IsDlgButtonChecked (hwnd, IDC2_VTOEMONLY) ? VT_OEMONLY : - VT_POORMAN); - break; - } - break; + for (index=-1; (index = ctrl_find_path(ctrlbox, path, index)) >= 0 ;) { + struct controlset *s = ctrlbox->ctrlsets[index]; + winctrl_layout(&dp, wc, &cp, s, &base_id); } - return GeneralPanelProc (hwnd, msg, wParam, lParam); } -static int CALLBACK TelnetProc (HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) { - int i; +/* + * This function is the configuration box. + */ +static int CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) +{ + HWND hw, treeview; + struct treeview_faff tvfaff; + int ret; switch (msg) { case WM_INITDIALOG: - SetDlgItemText (hwnd, IDC3_TTEDIT, cfg.termtype); - SetDlgItemText (hwnd, IDC3_TSEDIT, cfg.termspeed); - SetDlgItemText (hwnd, IDC3_LOGEDIT, cfg.username); - { - char *p = cfg.environmt; - while (*p) { - SendDlgItemMessage (hwnd, IDC3_ENVLIST, LB_ADDSTRING, 0, - (LPARAM) p); - p += strlen(p)+1; - } - } - CheckRadioButton (hwnd, IDC3_EMBSD, IDC3_EMRFC, - cfg.rfc_environ ? IDC3_EMRFC : IDC3_EMBSD); - break; - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDC3_TTEDIT: - if (HIWORD(wParam) == EN_CHANGE) - GetDlgItemText (hwnd, IDC3_TTEDIT, cfg.termtype, - sizeof(cfg.termtype)-1); - break; - case IDC3_TSEDIT: - if (HIWORD(wParam) == EN_CHANGE) - GetDlgItemText (hwnd, IDC3_TSEDIT, cfg.termspeed, - sizeof(cfg.termspeed)-1); - break; - case IDC3_LOGEDIT: - if (HIWORD(wParam) == EN_CHANGE) - GetDlgItemText (hwnd, IDC3_LOGEDIT, cfg.username, - sizeof(cfg.username)-1); - break; - case IDC3_EMBSD: - case IDC3_EMRFC: - cfg.rfc_environ = IsDlgButtonChecked (hwnd, IDC3_EMRFC); - break; - case IDC3_ENVADD: - if (HIWORD(wParam) == BN_CLICKED || - HIWORD(wParam) == BN_DOUBLECLICKED) { - char str[sizeof(cfg.environmt)]; - char *p; - GetDlgItemText (hwnd, IDC3_VAREDIT, str, sizeof(str)-1); - if (!*str) { - MessageBeep(0); - break; - } - p = str + strlen(str); - *p++ = '\t'; - GetDlgItemText (hwnd, IDC3_VALEDIT, p, sizeof(str)-1-(p-str)); - if (!*p) { - MessageBeep(0); - break; - } - p = cfg.environmt; - while (*p) { - while (*p) p++; - p++; - } - if ((p-cfg.environmt) + strlen(str) + 2 < sizeof(cfg.environmt)) { - strcpy (p, str); - p[strlen(str)+1] = '\0'; - SendDlgItemMessage (hwnd, IDC3_ENVLIST, LB_ADDSTRING, - 0, (LPARAM)str); - SetDlgItemText (hwnd, IDC3_VAREDIT, ""); - SetDlgItemText (hwnd, IDC3_VALEDIT, ""); - } else { - MessageBox(hwnd, "Environment too big", "PuTTY Error", - MB_OK | MB_ICONERROR); - } - } - break; - case IDC3_ENVREMOVE: - if (HIWORD(wParam) != BN_CLICKED && - HIWORD(wParam) != BN_DOUBLECLICKED) - break; - i = SendDlgItemMessage (hwnd, IDC3_ENVLIST, LB_GETCURSEL, 0, 0); - if (i == LB_ERR) - MessageBeep (0); - else { - char *p, *q; - - SendDlgItemMessage (hwnd, IDC3_ENVLIST, LB_DELETESTRING, - i, 0); - p = cfg.environmt; - while (i > 0) { - if (!*p) - goto disaster; - while (*p) p++; - p++; - i--; - } - q = p; - if (!*p) - goto disaster; - while (*p) p++; - p++; - while (*p) { - while (*p) - *q++ = *p++; - *q++ = *p++; - } - *q = '\0'; - disaster:; - } - break; - } - break; - } - return GeneralPanelProc (hwnd, msg, wParam, lParam); -} + dp.hwnd = hwnd; + create_controls(hwnd, ""); /* Open and Cancel buttons etc */ + SetWindowText(hwnd, dp.wintitle); + SetWindowLong(hwnd, GWL_USERDATA, 0); + if (help_path) + SetWindowLong(hwnd, GWL_EXSTYLE, + GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP); + else { + HWND item = GetDlgItem(hwnd, IDC_HELPBTN); + if (item) + DestroyWindow(item); + } + requested_help = FALSE; + SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG, + (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(IDI_CFGICON))); + /* + * Centre the window. + */ + { /* centre the window */ + RECT rs, rd; -static int CALLBACK SshProc (HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) { - switch (msg) { - case WM_INITDIALOG: - SetDlgItemText (hwnd, IDC3_TTEDIT, cfg.termtype); - SetDlgItemText (hwnd, IDC3_LOGEDIT, cfg.username); - CheckDlgButton (hwnd, IDC3_NOPTY, cfg.nopty); - CheckRadioButton (hwnd, IDC3_CIPHER3DES, IDC3_CIPHERDES, - cfg.cipher == CIPHER_BLOWFISH ? IDC3_CIPHERBLOWF : - cfg.cipher == CIPHER_DES ? IDC3_CIPHERDES : - - IDC3_CIPHER3DES); - break; - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDC3_TTEDIT: - if (HIWORD(wParam) == EN_CHANGE) - GetDlgItemText (hwnd, IDC3_TTEDIT, cfg.termtype, - sizeof(cfg.termtype)-1); - break; - case IDC3_LOGEDIT: - if (HIWORD(wParam) == EN_CHANGE) - GetDlgItemText (hwnd, IDC3_LOGEDIT, cfg.username, - sizeof(cfg.username)-1); - break; - case IDC3_NOPTY: - if (HIWORD(wParam) == BN_CLICKED || - HIWORD(wParam) == BN_DOUBLECLICKED) - cfg.nopty = IsDlgButtonChecked (hwnd, IDC3_NOPTY); - break; - case IDC3_CIPHER3DES: - case IDC3_CIPHERBLOWF: - case IDC3_CIPHERDES: - if (HIWORD(wParam) == BN_CLICKED || - HIWORD(wParam) == BN_DOUBLECLICKED) { - if (IsDlgButtonChecked (hwnd, IDC3_CIPHER3DES)) - cfg.cipher = CIPHER_3DES; - else if (IsDlgButtonChecked (hwnd, IDC3_CIPHERBLOWF)) - cfg.cipher = CIPHER_BLOWFISH; - else if (IsDlgButtonChecked (hwnd, IDC3_CIPHERDES)) - cfg.cipher = CIPHER_DES; - } - break; + hw = GetDesktopWindow(); + if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd)) + MoveWindow(hwnd, + (rs.right + rs.left + rd.left - rd.right) / 2, + (rs.bottom + rs.top + rd.top - rd.bottom) / 2, + rd.right - rd.left, rd.bottom - rd.top, TRUE); } - break; - } - return GeneralPanelProc (hwnd, msg, wParam, lParam); -} - -static int CALLBACK SelectionProc (HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) { - int i; - switch (msg) { - case WM_INITDIALOG: - CheckRadioButton (hwnd, IDC4_MBWINDOWS, IDC4_MBXTERM, - cfg.mouse_is_xterm ? IDC4_MBXTERM : IDC4_MBWINDOWS); + /* + * Create the tree view. + */ { - static int tabs[4] = {25, 61, 96, 128}; - SendDlgItemMessage (hwnd, IDC4_CCLIST, LB_SETTABSTOPS, 4, - (LPARAM) tabs); - } - for (i=0; i<256; i++) { - char str[100]; - sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i, - (i>=0x21 && i != 0x7F) ? i : ' ', - cfg.wordness[i]); - SendDlgItemMessage (hwnd, IDC4_CCLIST, LB_ADDSTRING, 0, - (LPARAM) str); + RECT r; + WPARAM font; + HWND tvstatic; + + r.left = 3; + r.right = r.left + 75; + r.top = 3; + r.bottom = r.top + 10; + MapDialogRect(hwnd, &r); + tvstatic = CreateWindowEx(0, "STATIC", "Cate&gory:", + WS_CHILD | WS_VISIBLE, + r.left, r.top, + r.right - r.left, r.bottom - r.top, + hwnd, (HMENU) IDCX_TVSTATIC, hinst, + NULL); + font = SendMessage(hwnd, WM_GETFONT, 0, 0); + SendMessage(tvstatic, WM_SETFONT, font, MAKELPARAM(TRUE, 0)); + + r.left = 3; + r.right = r.left + 75; + r.top = 13; + r.bottom = r.top + 219; + MapDialogRect(hwnd, &r); + treeview = CreateWindowEx(WS_EX_CLIENTEDGE, WC_TREEVIEW, "", + WS_CHILD | WS_VISIBLE | + WS_TABSTOP | TVS_HASLINES | + TVS_DISABLEDRAGDROP | TVS_HASBUTTONS + | TVS_LINESATROOT | + TVS_SHOWSELALWAYS, r.left, r.top, + r.right - r.left, r.bottom - r.top, + hwnd, (HMENU) IDCX_TREEVIEW, hinst, + NULL); + font = SendMessage(hwnd, WM_GETFONT, 0, 0); + SendMessage(treeview, WM_SETFONT, font, MAKELPARAM(TRUE, 0)); + tvfaff.treeview = treeview; + memset(tvfaff.lastat, 0, sizeof(tvfaff.lastat)); } - break; - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDC4_MBWINDOWS: - case IDC4_MBXTERM: - cfg.mouse_is_xterm = IsDlgButtonChecked (hwnd, IDC4_MBXTERM); - break; - case IDC4_CCSET: - { - BOOL ok; - int i; - int n = GetDlgItemInt (hwnd, IDC4_CCEDIT, &ok, FALSE); - - if (!ok) - MessageBeep (0); - else { - for (i=0; i<256; i++) - if (SendDlgItemMessage (hwnd, IDC4_CCLIST, LB_GETSEL, - i, 0)) { - char str[100]; - cfg.wordness[i] = n; - SendDlgItemMessage (hwnd, IDC4_CCLIST, - LB_DELETESTRING, i, 0); - sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i, - (i>=0x21 && i != 0x7F) ? i : ' ', - cfg.wordness[i]); - SendDlgItemMessage (hwnd, IDC4_CCLIST, - LB_INSERTSTRING, i, - (LPARAM)str); - } - } - } - break; - } - break; - } - return GeneralPanelProc (hwnd, msg, wParam, lParam); -} -static int CALLBACK ColourProc (HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) { - static const char *const colours[] = { - "Default Foreground", "Default Bold Foreground", - "Default Background", "Default Bold Background", - "Cursor Text", "Cursor Colour", - "ANSI Black", "ANSI Black Bold", - "ANSI Red", "ANSI Red Bold", - "ANSI Green", "ANSI Green Bold", - "ANSI Yellow", "ANSI Yellow Bold", - "ANSI Blue", "ANSI Blue Bold", - "ANSI Magenta", "ANSI Magenta Bold", - "ANSI Cyan", "ANSI Cyan Bold", - "ANSI White", "ANSI White Bold" - }; - static const int permanent[] = { - TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, - TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, - TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE - }; - switch (msg) { - case WM_INITDIALOG: - CheckDlgButton (hwnd, IDC5_BOLDCOLOUR, cfg.bold_colour); - CheckDlgButton (hwnd, IDC5_PALETTE, cfg.try_palette); + /* + * Set up the tree view contents. + */ { + HTREEITEM hfirst = NULL; int i; - for (i=0; i<22; i++) - if (cfg.bold_colour || permanent[i]) - SendDlgItemMessage (hwnd, IDC5_LIST, LB_ADDSTRING, 0, - (LPARAM) colours[i]); - } - SendDlgItemMessage (hwnd, IDC5_LIST, LB_SETCURSEL, 0, 0); - SetDlgItemInt (hwnd, IDC5_RVALUE, cfg.colours[0][0], FALSE); - SetDlgItemInt (hwnd, IDC5_GVALUE, cfg.colours[0][1], FALSE); - SetDlgItemInt (hwnd, IDC5_BVALUE, cfg.colours[0][2], FALSE); - break; - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDC5_BOLDCOLOUR: - if (HIWORD(wParam) == BN_CLICKED || - HIWORD(wParam) == BN_DOUBLECLICKED) { - int n, i; - cfg.bold_colour = IsDlgButtonChecked (hwnd, IDC5_BOLDCOLOUR); - n = SendDlgItemMessage (hwnd, IDC5_LIST, LB_GETCOUNT, 0, 0); - if (cfg.bold_colour && n!=22) { - for (i=0; i<22; i++) - if (!permanent[i]) - SendDlgItemMessage (hwnd, IDC5_LIST, - LB_INSERTSTRING, i, - (LPARAM) colours[i]); - } else if (!cfg.bold_colour && n!=12) { - for (i=22; i-- ;) - if (!permanent[i]) - SendDlgItemMessage (hwnd, IDC5_LIST, - LB_DELETESTRING, i, 0); - } - } - break; - case IDC5_PALETTE: - if (HIWORD(wParam) == BN_CLICKED || - HIWORD(wParam) == BN_DOUBLECLICKED) - cfg.try_palette = IsDlgButtonChecked (hwnd, IDC5_PALETTE); - break; - case IDC5_LIST: - if (HIWORD(wParam) == LBN_DBLCLK || - HIWORD(wParam) == LBN_SELCHANGE) { - int i = SendDlgItemMessage (hwnd, IDC5_LIST, LB_GETCURSEL, - 0, 0); - if (!cfg.bold_colour) - i = (i < 3 ? i*2 : i == 3 ? 5 : i*2-2); - SetDlgItemInt (hwnd, IDC5_RVALUE, cfg.colours[i][0], FALSE); - SetDlgItemInt (hwnd, IDC5_GVALUE, cfg.colours[i][1], FALSE); - SetDlgItemInt (hwnd, IDC5_BVALUE, cfg.colours[i][2], FALSE); - } - break; - case IDC5_CHANGE: - if (HIWORD(wParam) == BN_CLICKED || - HIWORD(wParam) == BN_DOUBLECLICKED) { - static CHOOSECOLOR cc; - static DWORD custom[16] = {0}; /* zero initialisers */ - int i = SendDlgItemMessage (hwnd, IDC5_LIST, LB_GETCURSEL, - 0, 0); - if (!cfg.bold_colour) - i = (i < 3 ? i*2 : i == 3 ? 5 : i*2-2); - cc.lStructSize = sizeof(cc); - cc.hwndOwner = hwnd; - cc.hInstance = hinst; - cc.lpCustColors = custom; - cc.rgbResult = RGB (cfg.colours[i][0], cfg.colours[i][1], - cfg.colours[i][2]); - cc.Flags = CC_FULLOPEN | CC_RGBINIT; - if (ChooseColor(&cc)) { - cfg.colours[i][0] = - (unsigned char) (cc.rgbResult & 0xFF); - cfg.colours[i][1] = - (unsigned char) (cc.rgbResult >> 8) & 0xFF; - cfg.colours[i][2] = - (unsigned char) (cc.rgbResult >> 16) & 0xFF; - SetDlgItemInt (hwnd, IDC5_RVALUE, cfg.colours[i][0], - FALSE); - SetDlgItemInt (hwnd, IDC5_GVALUE, cfg.colours[i][1], - FALSE); - SetDlgItemInt (hwnd, IDC5_BVALUE, cfg.colours[i][2], - FALSE); - } - } - break; - } - break; - } - return GeneralPanelProc (hwnd, msg, wParam, lParam); -} + char *path = NULL; -static int CALLBACK LanguageProc (HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) { - switch (msg) { - case WM_INITDIALOG: - CheckDlgButton (hwnd, IDC6_ENABLEKOIWINXLAT, cfg.xlat_enablekoiwin); - CheckDlgButton (hwnd, IDC6_CAPSLOCKCYR, cfg.xlat_capslockcyr); - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDC6_ENABLEKOIWINXLAT: - if (HIWORD(wParam) == BN_CLICKED || - HIWORD(wParam) == BN_DOUBLECLICKED) { - cfg.xlat_enablekoiwin = - IsDlgButtonChecked (hwnd, IDC6_ENABLEKOIWINXLAT); - } - break; - case IDC6_CAPSLOCKCYR: - if (HIWORD(wParam) == BN_CLICKED || - HIWORD(wParam) == BN_DOUBLECLICKED) { - cfg.xlat_capslockcyr = - IsDlgButtonChecked (hwnd, IDC6_CAPSLOCKCYR); - } - break; - } - } - return GeneralPanelProc (hwnd, msg, wParam, lParam); -} + for (i = 0; i < ctrlbox->nctrlsets; i++) { + struct controlset *s = ctrlbox->ctrlsets[i]; + HTREEITEM item; + int j; + char *c; -static DLGPROC panelproc[NPANELS] = { - ConnectionProc, KeyboardProc, TerminalProc, - TelnetProc, SshProc, SelectionProc, ColourProc, LanguageProc -}; -static char *panelids[NPANELS] = { - MAKEINTRESOURCE(IDD_PANEL0), - MAKEINTRESOURCE(IDD_PANEL1), - MAKEINTRESOURCE(IDD_PANEL2), - MAKEINTRESOURCE(IDD_PANEL3), - MAKEINTRESOURCE(IDD_PANEL35), - MAKEINTRESOURCE(IDD_PANEL4), - MAKEINTRESOURCE(IDD_PANEL5), - MAKEINTRESOURCE(IDD_PANEL6) -}; + if (!s->pathname[0]) + continue; + j = path ? ctrl_path_compare(s->pathname, path) : 0; + if (j == INT_MAX) + continue; /* same path, nothing to add to tree */ -static char *names[NPANELS] = { - "Connection", "Keyboard", "Terminal", "Telnet", - "SSH", "Selection", "Colours", "Language" -}; + /* + * We expect never to find an implicit path + * component. For example, we expect never to see + * A/B/C followed by A/D/E, because that would + * _implicitly_ create A/D. All our path prefixes + * are expected to contain actual controls and be + * selectable in the treeview; so we would expect + * to see A/D _explicitly_ before encountering + * A/D/E. + */ + assert(j == ctrl_path_elements(s->pathname) - 1); -static int mainp[MAIN_NPANELS] = { 0, 1, 2, 3, 4, 5, 6, 7}; -static int reconfp[RECONF_NPANELS] = { 1, 2, 5, 6, 7}; + c = strrchr(s->pathname, '/'); + if (!c) + c = s->pathname; + else + c++; -static int GenericMainDlgProc (HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam, - int npanels, int *panelnums, HWND *page) { - HWND hw; + item = treeview_insert(&tvfaff, j, c, s->pathname); + if (!hfirst) + hfirst = item; - switch (msg) { - case WM_INITDIALOG: - { /* centre the window */ - RECT rs, rd; + path = s->pathname; + } - hw = GetDesktopWindow(); - if (GetWindowRect (hw, &rs) && GetWindowRect (hwnd, &rd)) - MoveWindow (hwnd, (rs.right + rs.left + rd.left - rd.right)/2, - (rs.bottom + rs.top + rd.top - rd.bottom)/2, - rd.right-rd.left, rd.bottom-rd.top, TRUE); + /* + * Put the treeview selection on to the Session panel. + * This should also cause creation of the relevant + * controls. + */ + TreeView_SelectItem(treeview, hfirst); } - *page = NULL; - { /* initialise the tab control */ - TC_ITEMHEADER tab; + + /* + * Set focus into the first available control. + */ + { int i; + struct winctrl *c; - hw = GetDlgItem (hwnd, IDC_TAB); - for (i=0; ictrl) { + dlg_set_focus(c->ctrl, &dp); + break; + } } -/* *page = CreateDialogIndirect (hinst, panels[panelnums[0]].temp, - hwnd, panelproc[panelnums[0]]);*/ - *page = CreateDialog (hinst, panelids[panelnums[0]], - hwnd, panelproc[panelnums[0]]); - SetWindowLong (*page, GWL_EXSTYLE, - GetWindowLong (*page, GWL_EXSTYLE) | - WS_EX_CONTROLPARENT); } - SetFocus (*page); + + SetWindowLong(hwnd, GWL_USERDATA, 1); return 0; + case WM_LBUTTONUP: + /* + * Button release should trigger WM_OK if there was a + * previous double click on the session list. + */ + ReleaseCapture(); + if (dp.ended) + SaneEndDialog(hwnd, dp.endresult ? 1 : 0); + break; case WM_NOTIFY: - if (LOWORD(wParam) == IDC_TAB && - ((LPNMHDR)lParam)->code == TCN_SELCHANGE) { - int i = TabCtrl_GetCurSel(((LPNMHDR)lParam)->hwndFrom); - if (*page) - DestroyWindow (*page); -/* *page = CreateDialogIndirect (hinst, panels[panelnums[i]].temp, - hwnd, panelproc[panelnums[i]]);*/ - *page = CreateDialog (hinst, panelids[panelnums[i]], - hwnd, panelproc[panelnums[i]]); - SetWindowLong (*page, GWL_EXSTYLE, - GetWindowLong (*page, GWL_EXSTYLE) | - WS_EX_CONTROLPARENT); - SetFocus (((LPNMHDR)lParam)->hwndFrom); /* ensure focus stays */ + if (LOWORD(wParam) == IDCX_TREEVIEW && + ((LPNMHDR) lParam)->code == TVN_SELCHANGED) { + HTREEITEM i = + TreeView_GetSelection(((LPNMHDR) lParam)->hwndFrom); + TVITEM item; + char buffer[64]; + + SendMessage (hwnd, WM_SETREDRAW, FALSE, 0); + + item.hItem = i; + item.pszText = buffer; + item.cchTextMax = sizeof(buffer); + item.mask = TVIF_TEXT | TVIF_PARAM; + TreeView_GetItem(((LPNMHDR) lParam)->hwndFrom, &item); + { + /* Destroy all controls in the currently visible panel. */ + int k; + HWND item; + struct winctrl *c; + + while ((c = winctrl_findbyindex(&ctrls_panel, 0)) != NULL) { + for (k = 0; k < c->num_ids; k++) { + item = GetDlgItem(hwnd, c->base_id + k); + if (item) + DestroyWindow(item); + } + winctrl_rem_shortcuts(&dp, c); + winctrl_remove(&ctrls_panel, c); + sfree(c->data); + sfree(c); + } + } + create_controls(hwnd, (char *)item.lParam); + + dlg_refresh(NULL, &dp); /* set up control values */ + + SendMessage (hwnd, WM_SETREDRAW, TRUE, 0); + InvalidateRect (hwnd, NULL, TRUE); + + SetFocus(((LPNMHDR) lParam)->hwndFrom); /* ensure focus stays */ return 0; } break; -/* case WM_CTLCOLORDLG: */ -/* return (int) GetStockObject (LTGRAY_BRUSH); */ case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDOK: - if (*cfg.host) - EndDialog (hwnd, 1); + case WM_DRAWITEM: + default: /* also handle drag list msg here */ + /* + * Only process WM_COMMAND once the dialog is fully formed. + */ + if (GetWindowLong(hwnd, GWL_USERDATA) == 1) { + ret = winctrl_handle_command(&dp, msg, wParam, lParam); + if (dp.ended && GetCapture() != hwnd) + SaneEndDialog(hwnd, dp.endresult ? 1 : 0); + } else + ret = 0; + return ret; + case WM_HELP: + if (help_path) { + if (winctrl_context_help(&dp, hwnd, + ((LPHELPINFO)lParam)->iCtrlId)) + requested_help = TRUE; else - MessageBeep (0); - return 0; - case IDCANCEL: - EndDialog (hwnd, 0); - return 0; - } - return 0; + MessageBeep(0); + } + break; case WM_CLOSE: - EndDialog (hwnd, 0); + if (requested_help) { + WinHelp(hwnd, help_path, HELP_QUIT, 0); + requested_help = FALSE; + } + SaneEndDialog(hwnd, 0); return 0; - } - return 0; -} -static int CALLBACK MainDlgProc (HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) { -#if 0 - HWND hw; - int i; -#endif - static HWND page = NULL; + /* Grrr Explorer will maximize Dialogs! */ + case WM_SIZE: + if (wParam == SIZE_MAXIMIZED) + force_normal(hwnd); + return 0; - if (msg == WM_COMMAND && LOWORD(wParam) == IDOK) { -#if 0 - /* - * If the Connection panel is active and the Session List - * box is selected, we treat a press of Open to have an - * implicit press of Load preceding it. - */ - hw = GetDlgItem (hwnd, IDC_TAB); - i = TabCtrl_GetCurSel(hw); - if (panelproc[mainp[i]] == ConnectionProc && - page && implicit_load_ok) { - SendMessage (page, WM_COMMAND, - MAKELONG(IDC0_SESSLOAD, BN_CLICKED), 0); - } -#endif - } - if (msg == WM_COMMAND && LOWORD(wParam) == IDC_ABOUT) { - EnableWindow(hwnd, 0); - DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), - GetParent(hwnd), AboutProc); - EnableWindow(hwnd, 1); } - return GenericMainDlgProc (hwnd, msg, wParam, lParam, - MAIN_NPANELS, mainp, &page); + return 0; } -static int CALLBACK ReconfDlgProc (HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) { - static HWND page; - return GenericMainDlgProc (hwnd, msg, wParam, lParam, - RECONF_NPANELS, reconfp, &page); +void modal_about_box(HWND hwnd) +{ + EnableWindow(hwnd, 0); + DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc); + EnableWindow(hwnd, 1); + SetActiveWindow(hwnd); } -void get_sesslist(int allocate) { - static char *buffer; - int buflen, bufsize, i, ret; - char otherbuf[2048]; - char *p; - HKEY subkey1; - - if (allocate) { - if (RegCreateKey(HKEY_CURRENT_USER, - puttystr, &subkey1) != ERROR_SUCCESS) - return; - - buflen = bufsize = 0; - buffer = NULL; - i = 0; - do { - ret = RegEnumKey(subkey1, i++, otherbuf, sizeof(otherbuf)); - if (ret == ERROR_SUCCESS) { - bufsize = buflen + 2048; - buffer = srealloc(buffer, bufsize); - unmungestr(otherbuf, buffer+buflen); - buflen += strlen(buffer+buflen)+1; - } - } while (ret == ERROR_SUCCESS); - buffer = srealloc(buffer, buflen+1); - buffer[buflen] = '\0'; - - p = buffer; - nsessions = 1; /* "Default Settings" counts as one */ - while (*p) { - if (strcmp(p, "Default Settings")) - nsessions++; - while (*p) p++; - p++; - } +void show_help(HWND hwnd) +{ + if (help_path) { + WinHelp(hwnd, help_path, + help_has_contents ? HELP_FINDER : HELP_CONTENTS, + 0); + requested_help = TRUE; + } +} - sessions = smalloc(nsessions * sizeof(char *)); - sessions[0] = "Default Settings"; - p = buffer; - i = 1; - while (*p) { - if (strcmp(p, "Default Settings")) - sessions[i++] = p; - while (*p) p++; - p++; - } - } else { - sfree (buffer); - sfree (sessions); +void defuse_showwindow(void) +{ + /* + * Work around the fact that the app's first call to ShowWindow + * will ignore the default in favour of the shell-provided + * setting. + */ + { + HWND hwnd; + hwnd = CreateDialog(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), + NULL, NullDlgProc); + ShowWindow(hwnd, SW_HIDE); + SetActiveWindow(hwnd); + DestroyWindow(hwnd); } } -int do_config (void) { +int do_config(void) +{ int ret; - get_sesslist(TRUE); - ret = DialogBox (hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL, MainDlgProc); - get_sesslist(FALSE); + ctrlbox = ctrl_new_box(); + setup_config_box(ctrlbox, &sesslist, FALSE, 0); + win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), FALSE); + dp_init(&dp); + winctrl_init(&ctrls_base); + winctrl_init(&ctrls_panel); + dp_add_tree(&dp, &ctrls_base); + dp_add_tree(&dp, &ctrls_panel); + dp.wintitle = dupprintf("%s Configuration", appname); + dp.errtitle = dupprintf("%s Error", appname); + dp.data = &cfg; + dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */ + + get_sesslist(&sesslist, TRUE); + ret = + SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL, + GenericMainDlgProc); + get_sesslist(&sesslist, FALSE); + + ctrl_free_box(ctrlbox); + winctrl_cleanup(&ctrls_panel); + winctrl_cleanup(&ctrls_base); + dp_cleanup(&dp); return ret; } -int do_reconfig (HWND hwnd) { +int do_reconfig(HWND hwnd) +{ Config backup_cfg; int ret; backup_cfg = cfg; /* structure copy */ - ret = DialogBox (hinst, MAKEINTRESOURCE(IDD_RECONF), hwnd, ReconfDlgProc); + + ctrlbox = ctrl_new_box(); + setup_config_box(ctrlbox, NULL, TRUE, cfg.protocol); + win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), TRUE); + dp_init(&dp); + winctrl_init(&ctrls_base); + winctrl_init(&ctrls_panel); + dp_add_tree(&dp, &ctrls_base); + dp_add_tree(&dp, &ctrls_panel); + dp.wintitle = dupprintf("%s Reconfiguration", appname); + dp.errtitle = dupprintf("%s Error", appname); + dp.data = &cfg; + dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */ + + ret = SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL, + GenericMainDlgProc); + + ctrl_free_box(ctrlbox); + winctrl_cleanup(&ctrls_base); + winctrl_cleanup(&ctrls_panel); + dp_cleanup(&dp); + if (!ret) cfg = backup_cfg; /* structure copy */ + return ret; } -void do_defaults (char *session) { - if (session) - load_settings (session, TRUE); - else - load_settings ("Default Settings", FALSE); -} +void logevent(void *frontend, const char *string) +{ + char timebuf[40]; + time_t t; + + log_eventlog(logctx, string); -void lognegot (char *string) { - if (nnegots >= negsize) { + if (nevents >= negsize) { negsize += 64; - negots = srealloc (negots, negsize * sizeof(*negots)); + events = sresize(events, negsize, char *); + } + + time(&t); + strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t", + localtime(&t)); + + events[nevents] = snewn(strlen(timebuf) + strlen(string) + 1, char); + strcpy(events[nevents], timebuf); + strcat(events[nevents], string); + if (logbox) { + int count; + SendDlgItemMessage(logbox, IDN_LIST, LB_ADDSTRING, + 0, (LPARAM) events[nevents]); + count = SendDlgItemMessage(logbox, IDN_LIST, LB_GETCOUNT, 0, 0); + SendDlgItemMessage(logbox, IDN_LIST, LB_SETTOPINDEX, count - 1, 0); } - negots[nnegots] = smalloc(1+strlen(string)); - strcpy (negots[nnegots], string); - nnegots++; - if (logbox) - SendDlgItemMessage (logbox, IDN_LIST, LB_ADDSTRING, - 0, (LPARAM)string); + nevents++; } -void shownegot (HWND hwnd) { +void showeventlog(HWND hwnd) +{ if (!logbox) { - logbox = CreateDialog (hinst, MAKEINTRESOURCE(IDD_LOGBOX), - hwnd, LogProc); - ShowWindow (logbox, SW_SHOWNORMAL); + logbox = CreateDialog(hinst, MAKEINTRESOURCE(IDD_LOGBOX), + hwnd, LogProc); + ShowWindow(logbox, SW_SHOWNORMAL); } + SetActiveWindow(logbox); } -void showabout (HWND hwnd) { - if (!abtbox) { - abtbox = CreateDialog (hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), - hwnd, AboutProc); - ShowWindow (abtbox, SW_SHOWNORMAL); - } +void showabout(HWND hwnd) +{ + DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc); } -void verify_ssh_host_key(char *host, struct RSAKey *key) { - char *keystr, *otherstr, *mungedhost; - int len; - HKEY rkey; +void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype, + char *keystr, char *fingerprint) +{ + int ret; - /* - * Format the key into a string. - */ - len = rsastr_len(key); - keystr = malloc(len); - if (!keystr) - fatalbox("Out of memory"); - rsastr_fmt(keystr, key); + static const char absentmsg[] = + "The server's host key is not cached in the registry. You\n" + "have no guarantee that the server is the computer you\n" + "think it is.\n" + "The server's %s key fingerprint is:\n" + "%s\n" + "If you trust this host, hit Yes to add the key to\n" + "%s's cache and carry on connecting.\n" + "If you want to carry on connecting just once, without\n" + "adding the key to the cache, hit No.\n" + "If you do not trust this host, hit Cancel to abandon the\n" + "connection.\n"; + + static const char wrongmsg[] = + "WARNING - POTENTIAL SECURITY BREACH!\n" + "\n" + "The server's host key does not match the one %s has\n" + "cached in the registry. This means that either the\n" + "server administrator has changed the host key, or you\n" + "have actually connected to another computer pretending\n" + "to be the server.\n" + "The new %s key fingerprint is:\n" + "%s\n" + "If you were expecting this change and trust the new key,\n" + "hit Yes to update %s's cache and continue connecting.\n" + "If you want to carry on connecting but without updating\n" + "the cache, hit No.\n" + "If you want to abandon the connection completely, hit\n" + "Cancel. Hitting Cancel is the ONLY guaranteed safe\n" "choice.\n"; + + static const char mbtitle[] = "%s Security Alert"; /* - * Now read a saved key in from the registry and see what it - * says. + * Verify the key against the registry. */ - otherstr = malloc(len); - mungedhost = malloc(3*strlen(host)+1); - if (!otherstr || !mungedhost) - fatalbox("Out of memory"); - - mungestr(host, mungedhost); - - if (RegCreateKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys", - &rkey) != ERROR_SUCCESS) { - if (MessageBox(NULL, "PuTTY was unable to open the host key cache\n" - "in the registry. There is thus no way to tell\n" - "if the remote host is what you think it is.\n" - "Connect anyway?", "PuTTY Problem", - MB_ICONWARNING | MB_YESNO) == IDNO) - exit(0); - } else { - DWORD readlen = len; - DWORD type; - int ret; - - ret = RegQueryValueEx(rkey, mungedhost, NULL, - &type, otherstr, &readlen); - - if (ret == ERROR_MORE_DATA || - (ret == ERROR_SUCCESS && type == REG_SZ && - strcmp(otherstr, keystr))) { - if (MessageBox(NULL, - "This host's host key is different from the\n" - "one cached in the registry! Someone may be\n" - "impersonating this host for malicious reasons;\n" - "alternatively, the host key may have changed\n" - "due to sloppy system administration.\n" - "Replace key in registry and connect?", - "PuTTY: Security Warning", - MB_ICONWARNING | MB_YESNO) == IDNO) - exit(0); - RegSetValueEx(rkey, mungedhost, 0, REG_SZ, keystr, - strlen(keystr)+1); - } else if (ret != ERROR_SUCCESS || type != REG_SZ) { - if (MessageBox(NULL, - "This host's host key is not cached in the\n" - "registry. Do you want to add it to the cache\n" - "and carry on connecting?", - "PuTTY: New Host", - MB_ICONWARNING | MB_YESNO) == IDNO) - exit(0); - RegSetValueEx(rkey, mungedhost, 0, REG_SZ, keystr, - strlen(keystr)+1); - } + ret = verify_host_key(host, port, keytype, keystr); - RegCloseKey(rkey); - } + if (ret == 0) /* success - key matched OK */ + return; + if (ret == 2) { /* key was different */ + int mbret; + char *message, *title; + message = dupprintf(wrongmsg, appname, keytype, fingerprint, appname); + title = dupprintf(mbtitle, appname); + mbret = MessageBox(NULL, message, title, + MB_ICONWARNING | MB_YESNOCANCEL); + sfree(message); + sfree(title); + if (mbret == IDYES) + store_host_key(host, port, keytype, keystr); + if (mbret == IDCANCEL) + cleanup_exit(0); + } + if (ret == 1) { /* key was absent */ + int mbret; + char *message, *title; + message = dupprintf(absentmsg, keytype, fingerprint, appname); + title = dupprintf(mbtitle, appname); + mbret = MessageBox(NULL, message, title, + MB_ICONWARNING | MB_YESNOCANCEL); + sfree(message); + sfree(title); + if (mbret == IDYES) + store_host_key(host, port, keytype, keystr); + if (mbret == IDCANCEL) + cleanup_exit(0); + } +} + +/* + * Ask whether the selected cipher is acceptable (since it was + * below the configured 'warn' threshold). + * cs: 0 = both ways, 1 = client->server, 2 = server->client + */ +void askcipher(void *frontend, char *ciphername, int cs) +{ + static const char mbtitle[] = "%s Security Alert"; + static const char msg[] = + "The first %.35scipher supported by the server\n" + "is %.64s, which is below the configured\n" + "warning threshold.\n" + "Do you want to continue with this connection?\n"; + char *message, *title; + int mbret; + + message = dupprintf(msg, ((cs == 0) ? "" : + (cs == 1) ? "client-to-server " : + "server-to-client "), ciphername); + title = dupprintf(mbtitle, appname); + mbret = MessageBox(NULL, message, title, + MB_ICONWARNING | MB_YESNO); + sfree(message); + sfree(title); + if (mbret == IDYES) + return; + else + cleanup_exit(0); +} + +/* + * Ask whether to wipe a session log file before writing to it. + * Returns 2 for wipe, 1 for append, 0 for cancel (don't log). + */ +int askappend(void *frontend, Filename filename) +{ + static const char msgtemplate[] = + "The session log file \"%.*s\" already exists.\n" + "You can overwrite it with a new session log,\n" + "append your session log to the end of it,\n" + "or disable session logging for this session.\n" + "Hit Yes to wipe the file, No to append to it,\n" + "or Cancel to disable logging."; + char *message; + char *mbtitle; + int mbret; + + message = dupprintf(msgtemplate, FILENAME_MAX, filename.path); + mbtitle = dupprintf("%s Log to File", appname); + + mbret = MessageBox(NULL, message, mbtitle, + MB_ICONQUESTION | MB_YESNOCANCEL); + + sfree(message); + sfree(mbtitle); + + if (mbret == IDYES) + return 2; + else if (mbret == IDNO) + return 1; + else + return 0; +} + +/* + * Warn about the obsolescent key file format. + * + * Uniquely among these functions, this one does _not_ expect a + * frontend handle. This means that if PuTTY is ported to a + * platform which requires frontend handles, this function will be + * an anomaly. Fortunately, the problem it addresses will not have + * been present on that platform, so it can plausibly be + * implemented as an empty function. + */ +void old_keyfile_warning(void) +{ + static const char mbtitle[] = "%s Key File Warning"; + static const char message[] = + "You are loading an SSH 2 private key which has an\n" + "old version of the file format. This means your key\n" + "file is not fully tamperproof. Future versions of\n" + "%s may stop supporting this private key format,\n" + "so we recommend you convert your key to the new\n" + "format.\n" + "\n" + "You can perform this conversion by loading the key\n" + "into PuTTYgen and then saving it again."; + + char *msg, *title; + msg = dupprintf(message, appname); + title = dupprintf(mbtitle, appname); + + MessageBox(NULL, msg, title, MB_OK); + + sfree(msg); + sfree(title); }