diff options
author | Dimitrij <kvarkas@gmail.com> | 2022-10-31 00:45:23 +0300 |
---|---|---|
committer | Dimitrij <kvarkas@gmail.com> | 2022-10-31 00:45:23 +0300 |
commit | 302fb2e8ddea1c993552c9a30c02f41d01ca54a9 (patch) | |
tree | d6cf1b32664296ef2cecda33caeafbe39e6695c1 /WINDOWS | |
parent | 59105d9b26363e47f00676bd365b2ac8d4cb536a (diff) | |
parent | 4ff82ab29a22936b78510c68f544a99e677efed3 (diff) |
Diffstat (limited to 'WINDOWS')
53 files changed, 442 insertions, 21591 deletions
diff --git a/WINDOWS/PAGEANT.RC b/WINDOWS/PAGEANT.RC index a4a15195..1bea78b4 100644 --- a/WINDOWS/PAGEANT.RC +++ b/WINDOWS/PAGEANT.RC @@ -9,7 +9,7 @@ #include "pageant-rc.h" -#include "winhelp.rc2" +#include "help.rc2" IDI_MAINICON ICON "pageant.ico" IDI_TRAYICON ICON "pageants.ico" @@ -51,8 +51,8 @@ STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Pageant Key List" FONT 8, "MS Shell Dlg" BEGIN - LISTBOX 100, 10, 10, 420, 155, - LBS_EXTENDEDSEL | LBS_HASSTRINGS | LBS_USETABSTOPS | WS_VSCROLL | WS_TABSTOP + LISTBOX IDC_KEYLIST_LISTBOX, 10, 10, 420, 155, + LBS_EXTENDEDSEL | LBS_OWNERDRAWFIXED | WS_VSCROLL | WS_TABSTOP PUSHBUTTON "&Add Key", IDC_KEYLIST_ADDKEY, 10, 187, 60, 14 PUSHBUTTON "Add Key (&encrypted)", IDC_KEYLIST_ADDKEY_ENC, 75, 187, 80, 14 PUSHBUTTON "Re-e&ncrypt", IDC_KEYLIST_REENCRYPT, 315, 187, 60, 14 @@ -60,7 +60,7 @@ BEGIN PUSHBUTTON "&Help", IDC_KEYLIST_HELP, 10, 212, 50, 14 DEFPUSHBUTTON "&Close", IDOK, 390, 212, 50, 14 LTEXT "&Fingerprint type:", IDC_KEYLIST_FPTYPE_STATIC, 10, 172, 60, 8 - COMBOBOX IDC_KEYLIST_FPTYPE, 70, 170, 60, 12, CBS_DROPDOWNLIST + COMBOBOX IDC_KEYLIST_FPTYPE, 70, 170, 100, 12, CBS_DROPDOWNLIST END /* Accelerators used: cl */ diff --git a/WINDOWS/PUTTY.RC b/WINDOWS/PUTTY.RC index 14f62f48..b8df49f2 100644 --- a/WINDOWS/PUTTY.RC +++ b/WINDOWS/PUTTY.RC @@ -1,10 +1,14 @@ #include "rcstuff.h" +#include "putty-rc.h" #define APPNAME "PuTTYNG" #define APPDESC "SSH, Telnet, Rlogin, and SUPDUP client" -#include "winhelp.rc2" -#include "win_res.rc2" +IDI_MAINICON ICON "putty.ico" +IDI_CFGICON ICON "puttycfg.ico" + +#include "help.rc2" +#include "putty-common.rc2" #ifndef NO_MANIFESTS 1 RT_MANIFEST "putty.mft" diff --git a/WINDOWS/PUTTYGEN.RC b/WINDOWS/PUTTYGEN.RC index b910b6a3..fbe14fc3 100644 --- a/WINDOWS/PUTTYGEN.RC +++ b/WINDOWS/PUTTYGEN.RC @@ -7,7 +7,7 @@ #define APPNAME "PuTTYgen" #define APPDESC "PuTTY SSH key generation utility" -#include "winhelp.rc2" +#include "help.rc2" #include "puttygen-rc.h" 200 ICON "puttygen.ico" @@ -82,6 +82,16 @@ BEGIN PUSHBUTTON "&Cancel", IDCANCEL, 134, 80, 40, 14 END +/* Accelerators used: clw */ +216 DIALOG DISCARDABLE 140, 40, 450, 300 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "PuTTYgen: certificate information" +FONT 8, "MS Shell Dlg" +CLASS "PuTTYgenCertInfo" +BEGIN + DEFPUSHBUTTON "&Close", IDOK, 201, 130, 48, 14 +END + #include "version.rc2" #ifndef NO_MANIFESTS diff --git a/WINDOWS/PUTTYTEL.RC b/WINDOWS/PUTTYTEL.RC index 259bc683..41767a10 100644 --- a/WINDOWS/PUTTYTEL.RC +++ b/WINDOWS/PUTTYTEL.RC @@ -1,10 +1,14 @@ #include "rcstuff.h" +#include "putty-rc.h" #define APPNAME "PuTTYtel" #define APPDESC "Telnet and Rlogin client" -#include "winhelp.rc2" -#include "win_res.rc2" +IDI_MAINICON ICON "putty.ico" +IDI_CFGICON ICON "puttycfg.ico" + +#include "help.rc2" +#include "putty-common.rc2" #ifndef NO_MANIFESTS 1 RT_MANIFEST "puttytel.mft" diff --git a/WINDOWS/RCSTUFF.H b/WINDOWS/RCSTUFF.H index ee2c7696..dbace3f5 100644 --- a/WINDOWS/RCSTUFF.H +++ b/WINDOWS/RCSTUFF.H @@ -5,20 +5,15 @@ #ifndef PUTTY_RCSTUFF_H #define PUTTY_RCSTUFF_H -#ifdef __LCC__ -#include <win.h> -#else +#ifdef HAVE_CMAKE_H +#include "cmake.h" +#endif -/* Some compilers don't have winresrc.h */ -#ifndef NO_WINRESRC_H -#ifndef MSVC4 +#if HAVE_WINRESRC_H #include <winresrc.h> -#else +#elif HAVE_WINRES_H #include <winres.h> #endif -#endif - -#endif /* end #ifdef __LCC__ */ /* Some systems don't define this, so I do it myself if necessary */ #ifndef TCS_MULTILINE diff --git a/WINDOWS/WINCFG.C b/WINDOWS/WINCFG.C deleted file mode 100644 index fab3240f..00000000 --- a/WINDOWS/WINCFG.C +++ /dev/null @@ -1,405 +0,0 @@ -/* - * wincfg.c - the Windows-specific parts of the PuTTY configuration - * box. - */ - -#include <assert.h> -#include <stdlib.h> - -#include "putty.h" -#include "dialog.h" -#include "storage.h" - -static void about_handler(union control *ctrl, dlgparam *dlg, - void *data, int event) -{ - HWND *hwndp = (HWND *)ctrl->generic.context.p; - - if (event == EVENT_ACTION) { - modal_about_box(*hwndp); - } -} - -static void help_handler(union control *ctrl, dlgparam *dlg, - void *data, int event) -{ - HWND *hwndp = (HWND *)ctrl->generic.context.p; - - if (event == EVENT_ACTION) { - show_help(*hwndp); - } -} - -static void variable_pitch_handler(union control *ctrl, dlgparam *dlg, - void *data, int event) -{ - if (event == EVENT_REFRESH) { - dlg_checkbox_set(ctrl, dlg, !dlg_get_fixed_pitch_flag(dlg)); - } else if (event == EVENT_VALCHANGE) { - dlg_set_fixed_pitch_flag(dlg, !dlg_checkbox_get(ctrl, dlg)); - } -} - -void win_setup_config_box(struct controlbox *b, HWND *hwndp, bool has_help, - bool midsession, int protocol) -{ - const struct BackendVtable *backvt; - bool resize_forbidden = false; - struct controlset *s; - union control *c; - char *str; - - if (!midsession) { - /* - * Add the About and Help buttons to the standard panel. - */ - s = ctrl_getset(b, "", "", ""); - c = ctrl_pushbutton(s, "About", 'a', HELPCTX(no_help), - about_handler, P(hwndp)); - c->generic.column = 0; - if (has_help) { - c = ctrl_pushbutton(s, "Help", 'h', HELPCTX(no_help), - help_handler, P(hwndp)); - c->generic.column = 1; - } - } - - /* - * Full-screen mode is a Windows peculiarity; hence - * scrollbar_in_fullscreen is as well. - */ - s = ctrl_getset(b, "Window", "scrollback", - "Control the scrollback in the window"); - ctrl_checkbox(s, "Display scrollbar in full screen mode", 'i', - HELPCTX(window_scrollback), - conf_checkbox_handler, - I(CONF_scrollbar_in_fullscreen)); - /* - * Really this wants to go just after `Display scrollbar'. See - * if we can find that control, and do some shuffling. - */ - { - int i; - for (i = 0; i < s->ncontrols; i++) { - c = s->ctrls[i]; - if (c->generic.type == CTRL_CHECKBOX && - c->generic.context.i == CONF_scrollbar) { - /* - * Control i is the scrollbar checkbox. - * Control s->ncontrols-1 is the scrollbar-in-FS one. - */ - if (i < s->ncontrols-2) { - c = s->ctrls[s->ncontrols-1]; - memmove(s->ctrls+i+2, s->ctrls+i+1, - (s->ncontrols-i-2)*sizeof(union control *)); - s->ctrls[i+1] = c; - } - break; - } - } - } - - /* - * Windows has the AltGr key, which has various Windows- - * specific options. - */ - s = ctrl_getset(b, "Terminal/Keyboard", "features", - "Enable extra keyboard features:"); - ctrl_checkbox(s, "AltGr acts as Compose key", 't', - HELPCTX(keyboard_compose), - conf_checkbox_handler, I(CONF_compose_key)); - ctrl_checkbox(s, "Control-Alt is different from AltGr", 'd', - HELPCTX(keyboard_ctrlalt), - conf_checkbox_handler, I(CONF_ctrlaltkeys)); - - /* - * Windows allows an arbitrary .WAV to be played as a bell, and - * also the use of the PC speaker. For this we must search the - * existing controlset for the radio-button set controlling the - * `beep' option, and add extra buttons to it. - * - * Note that although this _looks_ like a hideous hack, it's - * actually all above board. The well-defined interface to the - * per-platform dialog box code is the _data structures_ `union - * control', `struct controlset' and so on; so code like this - * that reaches into those data structures and changes bits of - * them is perfectly legitimate and crosses no boundaries. All - * the ctrl_* routines that create most of the controls are - * convenient shortcuts provided on the cross-platform side of - * the interface, and template creation code is under no actual - * obligation to use them. - */ - s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell"); - { - int i; - for (i = 0; i < s->ncontrols; i++) { - c = s->ctrls[i]; - if (c->generic.type == CTRL_RADIO && - c->generic.context.i == CONF_beep) { - assert(c->generic.handler == conf_radiobutton_handler); - c->radio.nbuttons += 2; - c->radio.buttons = - sresize(c->radio.buttons, c->radio.nbuttons, char *); - c->radio.buttons[c->radio.nbuttons-1] = - dupstr("Play a custom sound file"); - c->radio.buttons[c->radio.nbuttons-2] = - dupstr("Beep using the PC speaker"); - c->radio.buttondata = - sresize(c->radio.buttondata, c->radio.nbuttons, intorptr); - c->radio.buttondata[c->radio.nbuttons-1] = I(BELL_WAVEFILE); - c->radio.buttondata[c->radio.nbuttons-2] = I(BELL_PCSPEAKER); - if (c->radio.shortcuts) { - c->radio.shortcuts = - sresize(c->radio.shortcuts, c->radio.nbuttons, char); - c->radio.shortcuts[c->radio.nbuttons-1] = NO_SHORTCUT; - c->radio.shortcuts[c->radio.nbuttons-2] = NO_SHORTCUT; - } - break; - } - } - } - ctrl_filesel(s, "Custom sound file to play as a bell:", NO_SHORTCUT, - FILTER_WAVE_FILES, false, "Select bell sound file", - HELPCTX(bell_style), - conf_filesel_handler, I(CONF_bell_wavefile)); - - /* - * While we've got this box open, taskbar flashing on a bell is - * also Windows-specific. - */ - ctrl_radiobuttons(s, "Taskbar/caption indication on bell:", 'i', 3, - HELPCTX(bell_taskbar), - conf_radiobutton_handler, - I(CONF_beep_ind), - "Disabled", I(B_IND_DISABLED), - "Flashing", I(B_IND_FLASH), - "Steady", I(B_IND_STEADY), NULL); - - /* - * The sunken-edge border is a Windows GUI feature. - */ - s = ctrl_getset(b, "Window/Appearance", "border", - "Adjust the window border"); - ctrl_checkbox(s, "Sunken-edge border (slightly thicker)", 's', - HELPCTX(appearance_border), - conf_checkbox_handler, I(CONF_sunken_edge)); - - /* - * Configurable font quality settings for Windows. - */ - s = ctrl_getset(b, "Window/Appearance", "font", - "Font settings"); - ctrl_checkbox(s, "Allow selection of variable-pitch fonts", NO_SHORTCUT, - HELPCTX(appearance_font), variable_pitch_handler, I(0)); - ctrl_radiobuttons(s, "Font quality:", 'q', 2, - HELPCTX(appearance_font), - conf_radiobutton_handler, - I(CONF_font_quality), - "Antialiased", I(FQ_ANTIALIASED), - "Non-Antialiased", I(FQ_NONANTIALIASED), - "ClearType", I(FQ_CLEARTYPE), - "Default", I(FQ_DEFAULT), NULL); - - /* - * Cyrillic Lock is a horrid misfeature even on Windows, and - * the least we can do is ensure it never makes it to any other - * platform (at least unless someone fixes it!). - */ - s = ctrl_getset(b, "Window/Translation", "tweaks", NULL); - ctrl_checkbox(s, "Caps Lock acts as Cyrillic switch", 's', - HELPCTX(translation_cyrillic), - conf_checkbox_handler, - I(CONF_xlat_capslockcyr)); - - /* - * On Windows we can use but not enumerate translation tables - * from the operating system. Briefly document this. - */ - s = ctrl_getset(b, "Window/Translation", "trans", - "Character set translation on received data"); - ctrl_text(s, "(Codepages supported by Windows but not listed here, " - "such as CP866 on many systems, can be entered manually)", - HELPCTX(translation_codepage)); - - /* - * Windows has the weird OEM font mode, which gives us some - * additional options when working with line-drawing - * characters. - */ - str = dupprintf("Adjust how %s displays line drawing characters", appname); - s = ctrl_getset(b, "Window/Translation", "linedraw", str); - sfree(str); - { - int i; - for (i = 0; i < s->ncontrols; i++) { - c = s->ctrls[i]; - if (c->generic.type == CTRL_RADIO && - c->generic.context.i == CONF_vtmode) { - assert(c->generic.handler == conf_radiobutton_handler); - c->radio.nbuttons += 3; - c->radio.buttons = - sresize(c->radio.buttons, c->radio.nbuttons, char *); - c->radio.buttons[c->radio.nbuttons-3] = - dupstr("Font has XWindows encoding"); - c->radio.buttons[c->radio.nbuttons-2] = - dupstr("Use font in both ANSI and OEM modes"); - c->radio.buttons[c->radio.nbuttons-1] = - dupstr("Use font in OEM mode only"); - c->radio.buttondata = - sresize(c->radio.buttondata, c->radio.nbuttons, intorptr); - c->radio.buttondata[c->radio.nbuttons-3] = I(VT_XWINDOWS); - c->radio.buttondata[c->radio.nbuttons-2] = I(VT_OEMANSI); - c->radio.buttondata[c->radio.nbuttons-1] = I(VT_OEMONLY); - if (!c->radio.shortcuts) { - int j; - c->radio.shortcuts = snewn(c->radio.nbuttons, char); - for (j = 0; j < c->radio.nbuttons; j++) - c->radio.shortcuts[j] = NO_SHORTCUT; - } else { - c->radio.shortcuts = sresize(c->radio.shortcuts, - c->radio.nbuttons, char); - } - c->radio.shortcuts[c->radio.nbuttons-3] = 'x'; - c->radio.shortcuts[c->radio.nbuttons-2] = 'b'; - c->radio.shortcuts[c->radio.nbuttons-1] = 'e'; - break; - } - } - } - - /* - * RTF paste is Windows-specific. - */ - s = ctrl_getset(b, "Window/Selection/Copy", "format", - "Formatting of copied characters"); - ctrl_checkbox(s, "Copy to clipboard in RTF as well as plain text", 'f', - HELPCTX(copy_rtf), - conf_checkbox_handler, I(CONF_rtf_paste)); - - /* - * Windows often has no middle button, so we supply a selection - * mode in which the more critical Paste action is available on - * the right button instead. - */ - s = ctrl_getset(b, "Window/Selection", "mouse", - "Control use of mouse"); - ctrl_radiobuttons(s, "Action of mouse buttons:", 'm', 1, - HELPCTX(selection_buttons), - conf_radiobutton_handler, - I(CONF_mouse_is_xterm), - "Windows (Middle extends, Right brings up menu)", I(2), - "Compromise (Middle extends, Right pastes)", I(0), - "xterm (Right extends, Middle pastes)", I(1), NULL); - /* - * This really ought to go at the _top_ of its box, not the - * bottom, so we'll just do some shuffling now we've set it - * up... - */ - c = s->ctrls[s->ncontrols-1]; /* this should be the new control */ - memmove(s->ctrls+1, s->ctrls, (s->ncontrols-1)*sizeof(union control *)); - s->ctrls[0] = c; - - /* - * Logical palettes don't even make sense anywhere except Windows. - */ - s = ctrl_getset(b, "Window/Colours", "general", - "General options for colour usage"); - ctrl_checkbox(s, "Attempt to use logical palettes", 'l', - HELPCTX(colours_logpal), - conf_checkbox_handler, I(CONF_try_palette)); - ctrl_checkbox(s, "Use system colours", 's', - HELPCTX(colours_system), - conf_checkbox_handler, I(CONF_system_colour)); - - - /* - * Resize-by-changing-font is a Windows insanity. - */ - - backvt = backend_vt_from_proto(protocol); - if (backvt) - resize_forbidden = (backvt->flags & BACKEND_RESIZE_FORBIDDEN); - if (!midsession || !resize_forbidden) { - s = ctrl_getset(b, "Window", "size", "Set the size of the window"); - ctrl_radiobuttons(s, "When window is resized:", 'z', 1, - HELPCTX(window_resize), - conf_radiobutton_handler, - I(CONF_resize_action), - "Change the number of rows and columns", I(RESIZE_TERM), - "Change the size of the font", I(RESIZE_FONT), - "Change font size only when maximised", I(RESIZE_EITHER), - "Forbid resizing completely", I(RESIZE_DISABLED), NULL); - } - - /* - * Most of the Window/Behaviour stuff is there to mimic Windows - * conventions which PuTTY can optionally disregard. Hence, - * most of these options are Windows-specific. - */ - s = ctrl_getset(b, "Window/Behaviour", "main", NULL); - ctrl_checkbox(s, "Window closes on ALT-F4", '4', - HELPCTX(behaviour_altf4), - conf_checkbox_handler, I(CONF_alt_f4)); - ctrl_checkbox(s, "System menu appears on ALT-Space", 'y', - HELPCTX(behaviour_altspace), - conf_checkbox_handler, I(CONF_alt_space)); - ctrl_checkbox(s, "System menu appears on ALT alone", 'l', - HELPCTX(behaviour_altonly), - conf_checkbox_handler, I(CONF_alt_only)); - ctrl_checkbox(s, "Ensure window is always on top", 'e', - HELPCTX(behaviour_alwaysontop), - conf_checkbox_handler, I(CONF_alwaysontop)); - ctrl_checkbox(s, "Full screen on Alt-Enter", 'f', - HELPCTX(behaviour_altenter), - conf_checkbox_handler, - I(CONF_fullscreenonaltenter)); - - /* - * Windows supports a local-command proxy. This also means we - * must adjust the text on the `Telnet command' control. - */ - if (!midsession) { - int i; - s = ctrl_getset(b, "Connection/Proxy", "basics", NULL); - for (i = 0; i < s->ncontrols; i++) { - c = s->ctrls[i]; - if (c->generic.type == CTRL_RADIO && - c->generic.context.i == CONF_proxy_type) { - assert(c->generic.handler == conf_radiobutton_handler); - c->radio.nbuttons++; - c->radio.buttons = - sresize(c->radio.buttons, c->radio.nbuttons, char *); - c->radio.buttons[c->radio.nbuttons-1] = - dupstr("Local"); - c->radio.buttondata = - sresize(c->radio.buttondata, c->radio.nbuttons, intorptr); - c->radio.buttondata[c->radio.nbuttons-1] = I(PROXY_CMD); - break; - } - } - - for (i = 0; i < s->ncontrols; i++) { - c = s->ctrls[i]; - if (c->generic.type == CTRL_EDITBOX && - c->generic.context.i == CONF_proxy_telnet_command) { - assert(c->generic.handler == conf_editbox_handler); - sfree(c->generic.label); - c->generic.label = dupstr("Telnet command, or local" - " proxy command"); - break; - } - } - } - - /* - * $XAUTHORITY is not reliable on Windows, so we provide a - * means to override it. - */ - if (!midsession && backend_vt_from_proto(PROT_SSH)) { - s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding"); - ctrl_filesel(s, "X authority file for local display", 't', - NULL, false, "Select X authority file", - HELPCTX(ssh_tunnels_xauthority), - conf_filesel_handler, I(CONF_xauthfile)); - } -} diff --git a/WINDOWS/WINCONS.C b/WINDOWS/WINCONS.C deleted file mode 100644 index 414167b4..00000000 --- a/WINDOWS/WINCONS.C +++ /dev/null @@ -1,452 +0,0 @@ -/* - * wincons.c - various interactive-prompt routines shared between - * the Windows console PuTTY tools - */ - -#include <stdio.h> -#include <stdlib.h> - -#include "putty.h" -#include "storage.h" -#include "ssh.h" -#include "console.h" - -void cleanup_exit(int code) -{ - /* - * Clean up. - */ - sk_cleanup(); - - random_save_seed(); - - exit(code); -} - -void console_print_error_msg(const char *prefix, const char *msg) -{ - fputs(prefix, stderr); - fputs(": ", stderr); - fputs(msg, stderr); - fputc('\n', stderr); - fflush(stderr); -} - -int console_verify_ssh_host_key( - Seat *seat, const char *host, int port, const char *keytype, - char *keystr, const char *keydisp, char **fingerprints, - void (*callback)(void *ctx, int result), void *ctx) -{ - int ret; - HANDLE hin; - DWORD savemode, i; - const char *common_fmt, *intro, *prompt; - - char line[32]; - - /* - * Verify the key against the registry. - */ - ret = verify_host_key(host, port, keytype, keystr); - - if (ret == 0) /* success - key matched OK */ - return 1; - - if (ret == 2) { /* key was different */ - common_fmt = hk_wrongmsg_common_fmt; - intro = hk_wrongmsg_interactive_intro; - prompt = hk_wrongmsg_interactive_prompt; - } else { /* key was absent */ - common_fmt = hk_absentmsg_common_fmt; - intro = hk_absentmsg_interactive_intro; - prompt = hk_absentmsg_interactive_prompt; - } - - FingerprintType fptype_default = - ssh2_pick_default_fingerprint(fingerprints); - - fprintf(stderr, common_fmt, keytype, fingerprints[fptype_default]); - if (console_batch_mode) { - fputs(console_abandoned_msg, stderr); - return 0; - } - - fputs(intro, stderr); - fflush(stderr); - - while (true) { - fputs(prompt, stderr); - fflush(stderr); - - line[0] = '\0'; /* fail safe if ReadFile returns no data */ - - hin = GetStdHandle(STD_INPUT_HANDLE); - GetConsoleMode(hin, &savemode); - SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT | - ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT)); - ReadFile(hin, line, sizeof(line) - 1, &i, NULL); - SetConsoleMode(hin, savemode); - - if (line[0] == 'i' || line[0] == 'I') { - fprintf(stderr, "Full public key:\n%s\n", keydisp); - if (fingerprints[SSH_FPTYPE_SHA256]) - fprintf(stderr, "SHA256 key fingerprint:\n%s\n", - fingerprints[SSH_FPTYPE_SHA256]); - if (fingerprints[SSH_FPTYPE_MD5]) - fprintf(stderr, "MD5 key fingerprint:\n%s\n", - fingerprints[SSH_FPTYPE_MD5]); - } else { - break; - } - } - - /* In case of misplaced reflexes from another program, also recognise 'q' - * as 'abandon connection rather than trust this key' */ - if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n' && - line[0] != 'q' && line[0] != 'Q') { - if (line[0] == 'y' || line[0] == 'Y') - store_host_key(host, port, keytype, keystr); - return 1; - } else { - fputs(console_abandoned_msg, stderr); - return 0; - } -} - -int console_confirm_weak_crypto_primitive( - Seat *seat, const char *algtype, const char *algname, - void (*callback)(void *ctx, int result), void *ctx) -{ - HANDLE hin; - DWORD savemode, i; - - char line[32]; - - fprintf(stderr, weakcrypto_msg_common_fmt, algtype, algname); - - if (console_batch_mode) { - fputs(console_abandoned_msg, stderr); - return 0; - } - - fputs(console_continue_prompt, stderr); - fflush(stderr); - - hin = GetStdHandle(STD_INPUT_HANDLE); - GetConsoleMode(hin, &savemode); - SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT | - ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT)); - ReadFile(hin, line, sizeof(line) - 1, &i, NULL); - SetConsoleMode(hin, savemode); - - if (line[0] == 'y' || line[0] == 'Y') { - return 1; - } else { - fputs(console_abandoned_msg, stderr); - return 0; - } -} - -int console_confirm_weak_cached_hostkey( - Seat *seat, const char *algname, const char *betteralgs, - void (*callback)(void *ctx, int result), void *ctx) -{ - HANDLE hin; - DWORD savemode, i; - - char line[32]; - - fprintf(stderr, weakhk_msg_common_fmt, algname, betteralgs); - - if (console_batch_mode) { - fputs(console_abandoned_msg, stderr); - return 0; - } - - fputs(console_continue_prompt, stderr); - fflush(stderr); - - hin = GetStdHandle(STD_INPUT_HANDLE); - GetConsoleMode(hin, &savemode); - SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT | - ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT)); - ReadFile(hin, line, sizeof(line) - 1, &i, NULL); - SetConsoleMode(hin, savemode); - - if (line[0] == 'y' || line[0] == 'Y') { - return 1; - } else { - fputs(console_abandoned_msg, stderr); - return 0; - } -} - -bool is_interactive(void) -{ - return is_console_handle(GetStdHandle(STD_INPUT_HANDLE)); -} - -bool console_antispoof_prompt = true; -bool console_set_trust_status(Seat *seat, bool trusted) -{ - if (console_batch_mode || !is_interactive() || !console_antispoof_prompt) { - /* - * In batch mode, we don't need to worry about the server - * mimicking our interactive authentication, because the user - * already knows not to expect any. - * - * If standard input isn't connected to a terminal, likewise, - * because even if the server did send a spoof authentication - * prompt, the user couldn't respond to it via the terminal - * anyway. - * - * We also vacuously return success if the user has purposely - * disabled the antispoof prompt. - */ - return true; - } - - return false; -} - -/* - * 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 console_askappend(LogPolicy *lp, Filename *filename, - void (*callback)(void *ctx, int result), void *ctx) -{ - HANDLE hin; - DWORD savemode, i; - - 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" - "Enter \"y\" to wipe the file, \"n\" to append to it,\n" - "or just press Return to disable logging.\n" - "Wipe the log file? (y/n, Return cancels logging) "; - - static const char msgtemplate_batch[] = - "The session log file \"%.*s\" already exists.\n" - "Logging will not be enabled.\n"; - - char line[32]; - - if (console_batch_mode) { - fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename->path); - fflush(stderr); - return 0; - } - fprintf(stderr, msgtemplate, FILENAME_MAX, filename->path); - fflush(stderr); - - hin = GetStdHandle(STD_INPUT_HANDLE); - GetConsoleMode(hin, &savemode); - SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT | - ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT)); - ReadFile(hin, line, sizeof(line) - 1, &i, NULL); - SetConsoleMode(hin, savemode); - - if (line[0] == 'y' || line[0] == 'Y') - return 2; - else if (line[0] == 'n' || line[0] == 'N') - 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 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" - "PuTTY may stop supporting this private key format,\n" - "so we recommend you convert your key to the new\n" - "format.\n" - "\n" - "Once the key is loaded into PuTTYgen, you can perform\n" - "this conversion simply by saving it again.\n"; - - fputs(message, stderr); -} - -/* - * Display the fingerprints of the PGP Master Keys to the user. - */ -void pgp_fingerprints(void) -{ - fputs("These are the fingerprints of the PuTTY PGP Master Keys. They can\n" - "be used to establish a trust path from this executable to another\n" - "one. See the manual for more information.\n" - "(Note: these fingerprints have nothing to do with SSH!)\n" - "\n" - "PuTTY Master Key as of " PGP_MASTER_KEY_YEAR - " (" PGP_MASTER_KEY_DETAILS "):\n" - " " PGP_MASTER_KEY_FP "\n\n" - "Previous Master Key (" PGP_PREV_MASTER_KEY_YEAR - ", " PGP_PREV_MASTER_KEY_DETAILS "):\n" - " " PGP_PREV_MASTER_KEY_FP "\n", stdout); -} - -void console_logging_error(LogPolicy *lp, const char *string) -{ - /* Ordinary Event Log entries are displayed in the same way as - * logging errors, but only in verbose mode */ - fprintf(stderr, "%s\n", string); - fflush(stderr); -} - -void console_eventlog(LogPolicy *lp, const char *string) -{ - /* Ordinary Event Log entries are displayed in the same way as - * logging errors, but only in verbose mode */ - if (lp_verbose(lp)) - console_logging_error(lp, string); -} - -StripCtrlChars *console_stripctrl_new( - Seat *seat, BinarySink *bs_out, SeatInteractionContext sic) -{ - return stripctrl_new(bs_out, false, 0); -} - -static void console_write(HANDLE hout, ptrlen data) -{ - DWORD dummy; - WriteFile(hout, data.ptr, data.len, &dummy, NULL); -} - -int console_get_userpass_input(prompts_t *p) -{ - HANDLE hin = INVALID_HANDLE_VALUE, hout = INVALID_HANDLE_VALUE; - size_t curr_prompt; - - /* - * Zero all the results, in case we abort half-way through. - */ - { - int i; - for (i = 0; i < (int)p->n_prompts; i++) - prompt_set_result(p->prompts[i], ""); - } - - /* - * The prompts_t might contain a message to be displayed but no - * actual prompt. More usually, though, it will contain - * questions that the user needs to answer, in which case we - * need to ensure that we're able to get the answers. - */ - if (p->n_prompts) { - if (console_batch_mode) - return 0; - hin = GetStdHandle(STD_INPUT_HANDLE); - if (hin == INVALID_HANDLE_VALUE) { - fprintf(stderr, "Cannot get standard input handle\n"); - cleanup_exit(1); - } - } - - /* - * And if we have anything to print, we need standard output. - */ - if ((p->name_reqd && p->name) || p->instruction || p->n_prompts) { - hout = GetStdHandle(STD_OUTPUT_HANDLE); - if (hout == INVALID_HANDLE_VALUE) { - fprintf(stderr, "Cannot get standard output handle\n"); - cleanup_exit(1); - } - } - - /* - * Preamble. - */ - /* We only print the `name' caption if we have to... */ - if (p->name_reqd && p->name) { - ptrlen plname = ptrlen_from_asciz(p->name); - console_write(hout, plname); - if (!ptrlen_endswith(plname, PTRLEN_LITERAL("\n"), NULL)) - console_write(hout, PTRLEN_LITERAL("\n")); - } - /* ...but we always print any `instruction'. */ - if (p->instruction) { - ptrlen plinst = ptrlen_from_asciz(p->instruction); - console_write(hout, plinst); - if (!ptrlen_endswith(plinst, PTRLEN_LITERAL("\n"), NULL)) - console_write(hout, PTRLEN_LITERAL("\n")); - } - - for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) { - - DWORD savemode, newmode; - prompt_t *pr = p->prompts[curr_prompt]; - - GetConsoleMode(hin, &savemode); - newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT; - if (!pr->echo) - newmode &= ~ENABLE_ECHO_INPUT; - else - newmode |= ENABLE_ECHO_INPUT; - SetConsoleMode(hin, newmode); - - console_write(hout, ptrlen_from_asciz(pr->prompt)); - - bool failed = false; - while (1) { - /* - * Amount of data to try to read from the console in one - * go. This isn't completely arbitrary: a user reported - * that trying to read more than 31366 bytes at a time - * would fail with ERROR_NOT_ENOUGH_MEMORY on Windows 7, - * and Ruby's Win32 support module has evidence of a - * similar workaround: - * - * https://github.com/ruby/ruby/blob/0aa5195262d4193d3accf3e6b9bad236238b816b/win32/win32.c#L6842 - * - * To keep things simple, I stick with a nice round power - * of 2 rather than trying to go to the very limit of that - * bug. (We're typically reading user passphrases and the - * like here, so even this much is overkill really.) - */ - DWORD toread = 16384; - - size_t prev_result_len = pr->result->len; - void *ptr = strbuf_append(pr->result, toread); - - DWORD ret = 0; - if (!ReadFile(hin, ptr, toread, &ret, NULL) || ret == 0) { - failed = true; - break; - } - - strbuf_shrink_to(pr->result, prev_result_len + ret); - if (strbuf_chomp(pr->result, '\n')) { - strbuf_chomp(pr->result, '\r'); - break; - } - } - - SetConsoleMode(hin, savemode); - - if (!pr->echo) - console_write(hout, PTRLEN_LITERAL("\r\n")); - - if (failed) { - return 0; /* failure due to read error */ - } - } - - return 1; /* success */ -} diff --git a/WINDOWS/WINCTRLS.C b/WINDOWS/WINCTRLS.C deleted file mode 100644 index 59129eab..00000000 --- a/WINDOWS/WINCTRLS.C +++ /dev/null @@ -1,2600 +0,0 @@ -/* - * winctrls.c: routines to self-manage the controls in a dialog - * box. - */ - -/* - * Possible TODO in new cross-platform config box stuff: - * - * - When lining up two controls alongside each other, I wonder if - * we could conveniently arrange to centre them vertically? - * Particularly ugly in the current setup is the `Add new - * forwarded port:' static next to the rather taller `Remove' - * button. - */ - -#include <assert.h> -#include <ctype.h> - -#include "putty.h" -#include "misc.h" -#include "dialog.h" - -#include <commctrl.h> - -#define GAPBETWEEN 3 -#define GAPWITHIN 1 -#define GAPXBOX 7 -#define GAPYBOX 4 -#define DLGWIDTH 168 -#define STATICHEIGHT 8 -#define TITLEHEIGHT 12 -#define CHECKBOXHEIGHT 8 -#define RADIOHEIGHT 8 -#define EDITHEIGHT 12 -#define LISTHEIGHT 11 -#define LISTINCREMENT 8 -#define COMBOHEIGHT 12 -#define PUSHBTNHEIGHT 14 -#define PROGBARHEIGHT 14 - -DECL_WINDOWS_FUNCTION(static, void, InitCommonControls, (void)); -DECL_WINDOWS_FUNCTION(static, BOOL, MakeDragList, (HWND)); -DECL_WINDOWS_FUNCTION(static, int, LBItemFromPt, (HWND, POINT, BOOL)); -DECL_WINDOWS_FUNCTION(static, void, DrawInsert, (HWND, HWND, int)); - -void init_common_controls(void) -{ - HMODULE comctl32_module = load_system32_dll("comctl32.dll"); - GET_WINDOWS_FUNCTION(comctl32_module, InitCommonControls); - GET_WINDOWS_FUNCTION(comctl32_module, MakeDragList); - GET_WINDOWS_FUNCTION(comctl32_module, LBItemFromPt); - GET_WINDOWS_FUNCTION(comctl32_module, DrawInsert); - p_InitCommonControls(); -} - -void ctlposinit(struct ctlpos *cp, HWND hwnd, - int leftborder, int rightborder, int topborder) -{ - RECT r, r2; - cp->hwnd = hwnd; - cp->font = SendMessage(hwnd, WM_GETFONT, 0, 0); - cp->ypos = topborder; - GetClientRect(hwnd, &r); - r2.left = r2.top = 0; - r2.right = 4; - r2.bottom = 8; - MapDialogRect(hwnd, &r2); - cp->dlu4inpix = r2.right; - cp->width = (r.right * 4) / (r2.right) - 2 * GAPBETWEEN; - cp->xoff = leftborder; - cp->width -= leftborder + rightborder; -} - -HWND doctl(struct ctlpos *cp, RECT r, - char *wclass, int wstyle, int exstyle, char *wtext, int wid) -{ - HWND ctl; - /* - * Note nonstandard use of RECT. This is deliberate: by - * transforming the width and height directly we arrange to - * have all supposedly same-sized controls really same-sized. - */ - - r.left += cp->xoff; - MapDialogRect(cp->hwnd, &r); - - /* - * We can pass in cp->hwnd == NULL, to indicate a dry run - * without creating any actual controls. - */ - if (cp->hwnd) { - ctl = CreateWindowEx(exstyle, wclass, wtext, wstyle, - r.left, r.top, r.right, r.bottom, - cp->hwnd, (HMENU)(ULONG_PTR)wid, hinst, NULL); - SendMessage(ctl, WM_SETFONT, cp->font, MAKELPARAM(true, 0)); - - if (!strcmp(wclass, "LISTBOX")) { - /* - * Bizarre Windows bug: the list box calculates its - * number of lines based on the font it has at creation - * time, but sending it WM_SETFONT doesn't cause it to - * recalculate. So now, _after_ we've sent it - * WM_SETFONT, we explicitly resize it (to the same - * size it was already!) to force it to reconsider. - */ - SetWindowPos(ctl, NULL, 0, 0, r.right, r.bottom, - SWP_NOACTIVATE | SWP_NOCOPYBITS | - SWP_NOMOVE | SWP_NOZORDER); - } - } else - ctl = NULL; - return ctl; -} - -/* - * A title bar across the top of a sub-dialog. - */ -void bartitle(struct ctlpos *cp, char *name, int id) -{ - RECT r; - - r.left = GAPBETWEEN; - r.right = cp->width; - r.top = cp->ypos; - r.bottom = STATICHEIGHT; - cp->ypos += r.bottom + GAPBETWEEN; - doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, name, id); -} - -/* - * Begin a grouping box, with or without a group title. - */ -void beginbox(struct ctlpos *cp, char *name, int idbox) -{ - cp->boxystart = cp->ypos; - if (!name) - cp->boxystart -= STATICHEIGHT / 2; - if (name) - cp->ypos += STATICHEIGHT; - cp->ypos += GAPYBOX; - cp->width -= 2 * GAPXBOX; - cp->xoff += GAPXBOX; - cp->boxid = idbox; - cp->boxtext = name; -} - -/* - * End a grouping box. - */ -void endbox(struct ctlpos *cp) -{ - RECT r; - cp->xoff -= GAPXBOX; - cp->width += 2 * GAPXBOX; - cp->ypos += GAPYBOX - GAPBETWEEN; - r.left = GAPBETWEEN; - r.right = cp->width; - r.top = cp->boxystart; - r.bottom = cp->ypos - cp->boxystart; - doctl(cp, r, "BUTTON", BS_GROUPBOX | WS_CHILD | WS_VISIBLE, 0, - cp->boxtext ? cp->boxtext : "", cp->boxid); - cp->ypos += GAPYBOX; -} - -/* - * A static line, followed by a full-width edit box. - */ -void editboxfw(struct ctlpos *cp, bool password, char *text, - int staticid, int editid) -{ - RECT r; - - r.left = GAPBETWEEN; - r.right = cp->width; - - if (text) { - r.top = cp->ypos; - r.bottom = STATICHEIGHT; - doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, staticid); - cp->ypos += STATICHEIGHT + GAPWITHIN; - } - r.top = cp->ypos; - r.bottom = EDITHEIGHT; - doctl(cp, r, "EDIT", - WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL | - (password ? ES_PASSWORD : 0), - WS_EX_CLIENTEDGE, "", editid); - cp->ypos += EDITHEIGHT + GAPBETWEEN; -} - -/* - * A static line, followed by a full-width combo box. - */ -void combobox(struct ctlpos *cp, char *text, int staticid, int listid) -{ - RECT r; - - r.left = GAPBETWEEN; - r.right = cp->width; - - if (text) { - r.top = cp->ypos; - r.bottom = STATICHEIGHT; - doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, staticid); - cp->ypos += STATICHEIGHT + GAPWITHIN; - } - r.top = cp->ypos; - r.bottom = COMBOHEIGHT * 10; - doctl(cp, r, "COMBOBOX", - WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | - CBS_DROPDOWN | CBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", listid); - cp->ypos += COMBOHEIGHT + GAPBETWEEN; -} - -struct radio { char *text; int id; }; - -static void radioline_common(struct ctlpos *cp, char *text, int id, - int nacross, struct radio *buttons, int nbuttons) -{ - RECT r; - int group; - int i; - int j; - - r.left = GAPBETWEEN; - r.top = cp->ypos; - if (text) { - r.right = cp->width; - r.bottom = STATICHEIGHT; - cp->ypos += r.bottom + GAPWITHIN; - doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id); - } else { - r.right = r.bottom = 0; - } - - group = WS_GROUP; - i = 0; - for (j = 0; j < nbuttons; j++) { - char *btext = buttons[j].text; - int bid = buttons[j].id; - - if (i == nacross) { - cp->ypos += r.bottom + (nacross > 1 ? GAPBETWEEN : GAPWITHIN); - i = 0; - } - r.left = GAPBETWEEN + i * (cp->width + GAPBETWEEN) / nacross; - if (j < nbuttons-1) - r.right = - (i + 1) * (cp->width + GAPBETWEEN) / nacross - r.left; - else - r.right = cp->width - r.left; - r.top = cp->ypos; - r.bottom = RADIOHEIGHT; - doctl(cp, r, "BUTTON", - BS_NOTIFY | BS_AUTORADIOBUTTON | WS_CHILD | - WS_VISIBLE | WS_TABSTOP | group, 0, btext, bid); - group = 0; - i++; - } - cp->ypos += r.bottom + GAPBETWEEN; -} - -/* - * A set of radio buttons on the same line, with a static above - * them. `nacross' dictates how many parts the line is divided into - * (you might want this not to equal the number of buttons if you - * needed to line up some 2s and some 3s to look good in the same - * panel). - * - * There's a bit of a hack in here to ensure that if nacross - * exceeds the actual number of buttons, the rightmost button - * really does get all the space right to the edge of the line, so - * you can do things like - * - * (*) Button1 (*) Button2 (*) ButtonWithReallyLongTitle - */ -void radioline(struct ctlpos *cp, char *text, int id, int nacross, ...) -{ - va_list ap; - struct radio *buttons; - int i, nbuttons; - - va_start(ap, nacross); - nbuttons = 0; - while (1) { - char *btext = va_arg(ap, char *); - if (!btext) - break; - (void) va_arg(ap, int); /* id */ - nbuttons++; - } - va_end(ap); - buttons = snewn(nbuttons, struct radio); - va_start(ap, nacross); - for (i = 0; i < nbuttons; i++) { - buttons[i].text = va_arg(ap, char *); - buttons[i].id = va_arg(ap, int); - } - va_end(ap); - radioline_common(cp, text, id, nacross, buttons, nbuttons); - sfree(buttons); -} - -/* - * A set of radio buttons on the same line, without a static above - * them. Otherwise just like radioline. - */ -void bareradioline(struct ctlpos *cp, int nacross, ...) -{ - va_list ap; - struct radio *buttons; - int i, nbuttons; - - va_start(ap, nacross); - nbuttons = 0; - while (1) { - char *btext = va_arg(ap, char *); - if (!btext) - break; - (void) va_arg(ap, int); /* id */ - nbuttons++; - } - va_end(ap); - buttons = snewn(nbuttons, struct radio); - va_start(ap, nacross); - for (i = 0; i < nbuttons; i++) { - buttons[i].text = va_arg(ap, char *); - buttons[i].id = va_arg(ap, int); - } - va_end(ap); - radioline_common(cp, NULL, 0, nacross, buttons, nbuttons); - sfree(buttons); -} - -/* - * A set of radio buttons on multiple lines, with a static above - * them. - */ -void radiobig(struct ctlpos *cp, char *text, int id, ...) -{ - va_list ap; - struct radio *buttons; - int i, nbuttons; - - va_start(ap, id); - nbuttons = 0; - while (1) { - char *btext = va_arg(ap, char *); - if (!btext) - break; - (void) va_arg(ap, int); /* id */ - nbuttons++; - } - va_end(ap); - buttons = snewn(nbuttons, struct radio); - va_start(ap, id); - for (i = 0; i < nbuttons; i++) { - buttons[i].text = va_arg(ap, char *); - buttons[i].id = va_arg(ap, int); - } - va_end(ap); - radioline_common(cp, text, id, 1, buttons, nbuttons); - sfree(buttons); -} - -/* - * A single standalone checkbox. - */ -void checkbox(struct ctlpos *cp, char *text, int id) -{ - RECT r; - - r.left = GAPBETWEEN; - r.top = cp->ypos; - r.right = cp->width; - r.bottom = CHECKBOXHEIGHT; - cp->ypos += r.bottom + GAPBETWEEN; - doctl(cp, r, "BUTTON", - BS_NOTIFY | BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0, - text, id); -} - -/* - * Wrap a piece of text for a static text control. Returns the - * wrapped text (a malloc'ed string containing \ns), and also - * returns the number of lines required. - */ -char *staticwrap(struct ctlpos *cp, HWND hwnd, char *text, int *lines) -{ - HDC hdc = GetDC(hwnd); - int width, nlines, j; - INT *pwidths, nfit; - SIZE size; - char *ret, *p, *q; - RECT r; - HFONT oldfont, newfont; - - ret = snewn(1+strlen(text), char); - p = text; - q = ret; - pwidths = snewn(1+strlen(text), INT); - - /* - * Work out the width the text will need to fit in, by doing - * the same adjustment that the `statictext' function itself - * will perform. - */ - SetMapMode(hdc, MM_TEXT); /* ensure logical units == pixels */ - r.left = r.top = r.bottom = 0; - r.right = cp->width; - MapDialogRect(hwnd, &r); - width = r.right; - - nlines = 1; - - /* - * We must select the correct font into the HDC before calling - * GetTextExtent*, or silly things will happen. - */ - newfont = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0); - oldfont = SelectObject(hdc, newfont); - - while (*p) { - if (!GetTextExtentExPoint(hdc, p, strlen(p), width, - &nfit, pwidths, &size) || - (size_t)nfit >= strlen(p)) { - /* - * Either GetTextExtentExPoint returned failure, or the - * whole of the rest of the text fits on this line. - * Either way, we stop wrapping, copy the remainder of - * the input string unchanged to the output, and leave. - */ - strcpy(q, p); - break; - } - - /* - * Now we search backwards along the string from `nfit', - * looking for a space at which to break the line. If we - * don't find one at all, that's fine - we'll just break - * the line at `nfit'. - */ - for (j = nfit; j > 0; j--) { - if (isspace((unsigned char)p[j])) { - nfit = j; - break; - } - } - - strncpy(q, p, nfit); - q[nfit] = '\n'; - q += nfit+1; - - p += nfit; - while (*p && isspace((unsigned char)*p)) - p++; - - nlines++; - } - - SelectObject(hdc, oldfont); - ReleaseDC(cp->hwnd, hdc); - - if (lines) *lines = nlines; - - sfree(pwidths); - - return ret; -} - -/* - * A single standalone static text control. - */ -void statictext(struct ctlpos *cp, char *text, int lines, int id) -{ - RECT r; - - r.left = GAPBETWEEN; - r.top = cp->ypos; - r.right = cp->width; - r.bottom = STATICHEIGHT * lines; - cp->ypos += r.bottom + GAPBETWEEN; - doctl(cp, r, "STATIC", - WS_CHILD | WS_VISIBLE | SS_LEFTNOWORDWRAP, - 0, text, id); -} - -/* - * An owner-drawn static text control for a panel title. - */ -void paneltitle(struct ctlpos *cp, int id) -{ - RECT r; - - r.left = GAPBETWEEN; - r.top = cp->ypos; - r.right = cp->width; - r.bottom = TITLEHEIGHT; - cp->ypos += r.bottom + GAPBETWEEN; - doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE | SS_OWNERDRAW, - 0, NULL, id); -} - -/* - * A button on the right hand side, with a static to its left. - */ -void staticbtn(struct ctlpos *cp, char *stext, int sid, - char *btext, int bid) -{ - const int height = (PUSHBTNHEIGHT > STATICHEIGHT ? - PUSHBTNHEIGHT : STATICHEIGHT); - RECT r; - int lwid, rwid, rpos; - - rpos = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4; - lwid = rpos - 2 * GAPBETWEEN; - rwid = cp->width + GAPBETWEEN - rpos; - - r.left = GAPBETWEEN; - r.top = cp->ypos + (height - STATICHEIGHT) / 2; - r.right = lwid; - r.bottom = STATICHEIGHT; - doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); - - r.left = rpos; - r.top = cp->ypos + (height - PUSHBTNHEIGHT) / 2; - r.right = rwid; - r.bottom = PUSHBTNHEIGHT; - doctl(cp, r, "BUTTON", - BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, - 0, btext, bid); - - cp->ypos += height + GAPBETWEEN; -} - -/* - * A simple push button. - */ -void button(struct ctlpos *cp, char *btext, int bid, bool defbtn) -{ - RECT r; - - r.left = GAPBETWEEN; - r.top = cp->ypos; - r.right = cp->width; - r.bottom = PUSHBTNHEIGHT; - - /* Q67655: the _dialog box_ must know which button is default - * as well as the button itself knowing */ - if (defbtn && cp->hwnd) - SendMessage(cp->hwnd, DM_SETDEFID, bid, 0); - - doctl(cp, r, "BUTTON", - BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | - (defbtn ? BS_DEFPUSHBUTTON : 0) | BS_PUSHBUTTON, - 0, btext, bid); - - cp->ypos += PUSHBTNHEIGHT + GAPBETWEEN; -} - -/* - * Like staticbtn, but two buttons. - */ -void static2btn(struct ctlpos *cp, char *stext, int sid, - char *btext1, int bid1, char *btext2, int bid2) -{ - const int height = (PUSHBTNHEIGHT > STATICHEIGHT ? - PUSHBTNHEIGHT : STATICHEIGHT); - RECT r; - int lwid, rwid1, rwid2, rpos1, rpos2; - - rpos1 = GAPBETWEEN + (cp->width + GAPBETWEEN) / 2; - rpos2 = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4; - lwid = rpos1 - 2 * GAPBETWEEN; - rwid1 = rpos2 - rpos1 - GAPBETWEEN; - rwid2 = cp->width + GAPBETWEEN - rpos2; - - r.left = GAPBETWEEN; - r.top = cp->ypos + (height - STATICHEIGHT) / 2; - r.right = lwid; - r.bottom = STATICHEIGHT; - doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); - - r.left = rpos1; - r.top = cp->ypos + (height - PUSHBTNHEIGHT) / 2; - r.right = rwid1; - r.bottom = PUSHBTNHEIGHT; - doctl(cp, r, "BUTTON", - BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, - 0, btext1, bid1); - - r.left = rpos2; - r.top = cp->ypos + (height - PUSHBTNHEIGHT) / 2; - r.right = rwid2; - r.bottom = PUSHBTNHEIGHT; - doctl(cp, r, "BUTTON", - BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, - 0, btext2, bid2); - - cp->ypos += height + GAPBETWEEN; -} - -/* - * An edit control on the right hand side, with a static to its left. - */ -static void staticedit_internal(struct ctlpos *cp, char *stext, - int sid, int eid, int percentedit, - int style) -{ - const int height = (EDITHEIGHT > STATICHEIGHT ? - EDITHEIGHT : STATICHEIGHT); - RECT r; - int lwid, rwid, rpos; - - rpos = - GAPBETWEEN + (100 - percentedit) * (cp->width + GAPBETWEEN) / 100; - lwid = rpos - 2 * GAPBETWEEN; - rwid = cp->width + GAPBETWEEN - rpos; - - r.left = GAPBETWEEN; - r.top = cp->ypos + (height - STATICHEIGHT) / 2; - r.right = lwid; - r.bottom = STATICHEIGHT; - doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); - - r.left = rpos; - r.top = cp->ypos + (height - EDITHEIGHT) / 2; - r.right = rwid; - r.bottom = EDITHEIGHT; - doctl(cp, r, "EDIT", - WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL | style, - WS_EX_CLIENTEDGE, "", eid); - - cp->ypos += height + GAPBETWEEN; -} - -void staticedit(struct ctlpos *cp, char *stext, - int sid, int eid, int percentedit) -{ - staticedit_internal(cp, stext, sid, eid, percentedit, 0); -} - -void staticpassedit(struct ctlpos *cp, char *stext, - int sid, int eid, int percentedit) -{ - staticedit_internal(cp, stext, sid, eid, percentedit, ES_PASSWORD); -} - -/* - * A drop-down list box on the right hand side, with a static to - * its left. - */ -void staticddl(struct ctlpos *cp, char *stext, - int sid, int lid, int percentlist) -{ - const int height = (COMBOHEIGHT > STATICHEIGHT ? - COMBOHEIGHT : STATICHEIGHT); - RECT r; - int lwid, rwid, rpos; - - rpos = - GAPBETWEEN + (100 - percentlist) * (cp->width + GAPBETWEEN) / 100; - lwid = rpos - 2 * GAPBETWEEN; - rwid = cp->width + GAPBETWEEN - rpos; - - r.left = GAPBETWEEN; - r.top = cp->ypos + (height - STATICHEIGHT) / 2; - r.right = lwid; - r.bottom = STATICHEIGHT; - doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); - - r.left = rpos; - r.top = cp->ypos + (height - EDITHEIGHT) / 2; - r.right = rwid; - r.bottom = COMBOHEIGHT*4; - doctl(cp, r, "COMBOBOX", - WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | - CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", lid); - - cp->ypos += height + GAPBETWEEN; -} - -/* - * A combo box on the right hand side, with a static to its left. - */ -void staticcombo(struct ctlpos *cp, char *stext, - int sid, int lid, int percentlist) -{ - const int height = (COMBOHEIGHT > STATICHEIGHT ? - COMBOHEIGHT : STATICHEIGHT); - RECT r; - int lwid, rwid, rpos; - - rpos = - GAPBETWEEN + (100 - percentlist) * (cp->width + GAPBETWEEN) / 100; - lwid = rpos - 2 * GAPBETWEEN; - rwid = cp->width + GAPBETWEEN - rpos; - - r.left = GAPBETWEEN; - r.top = cp->ypos + (height - STATICHEIGHT) / 2; - r.right = lwid; - r.bottom = STATICHEIGHT; - doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); - - r.left = rpos; - r.top = cp->ypos + (height - EDITHEIGHT) / 2; - r.right = rwid; - r.bottom = COMBOHEIGHT*10; - doctl(cp, r, "COMBOBOX", - WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | - CBS_DROPDOWN | CBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", lid); - - cp->ypos += height + GAPBETWEEN; -} - -/* - * A static, with a full-width drop-down list box below it. - */ -void staticddlbig(struct ctlpos *cp, char *stext, - int sid, int lid) -{ - RECT r; - - if (stext) { - r.left = GAPBETWEEN; - r.top = cp->ypos; - r.right = cp->width; - r.bottom = STATICHEIGHT; - doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); - cp->ypos += STATICHEIGHT; - } - - r.left = GAPBETWEEN; - r.top = cp->ypos; - r.right = cp->width; - r.bottom = COMBOHEIGHT*4; - doctl(cp, r, "COMBOBOX", - WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | - CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", lid); - cp->ypos += COMBOHEIGHT + GAPBETWEEN; -} - -/* - * A big multiline edit control with a static labelling it. - */ -void bigeditctrl(struct ctlpos *cp, char *stext, - int sid, int eid, int lines) -{ - RECT r; - - if (stext) { - r.left = GAPBETWEEN; - r.top = cp->ypos; - r.right = cp->width; - r.bottom = STATICHEIGHT; - cp->ypos += r.bottom + GAPWITHIN; - doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); - } - - r.left = GAPBETWEEN; - r.top = cp->ypos; - r.right = cp->width; - r.bottom = EDITHEIGHT + (lines - 1) * STATICHEIGHT; - cp->ypos += r.bottom + GAPBETWEEN; - doctl(cp, r, "EDIT", - WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | ES_MULTILINE, - WS_EX_CLIENTEDGE, "", eid); -} - -/* - * A list box with a static labelling it. - */ -void listbox(struct ctlpos *cp, char *stext, - int sid, int lid, int lines, bool multi) -{ - RECT r; - - if (stext != NULL) { - r.left = GAPBETWEEN; - r.top = cp->ypos; - r.right = cp->width; - r.bottom = STATICHEIGHT; - cp->ypos += r.bottom + GAPWITHIN; - doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); - } - - r.left = GAPBETWEEN; - r.top = cp->ypos; - r.right = cp->width; - r.bottom = LISTHEIGHT + (lines - 1) * LISTINCREMENT; - cp->ypos += r.bottom + GAPBETWEEN; - doctl(cp, r, "LISTBOX", - WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | - LBS_NOTIFY | LBS_HASSTRINGS | LBS_USETABSTOPS | - (multi ? LBS_MULTIPLESEL : 0), - WS_EX_CLIENTEDGE, "", lid); -} - -/* - * A tab-control substitute when a real tab control is unavailable. - */ -void ersatztab(struct ctlpos *cp, char *stext, int sid, int lid, int s2id) -{ - const int height = (COMBOHEIGHT > STATICHEIGHT ? - COMBOHEIGHT : STATICHEIGHT); - RECT r; - int bigwid, lwid, rwid, rpos; - static const int BIGGAP = 15; - static const int MEDGAP = 3; - - bigwid = cp->width + 2 * GAPBETWEEN - 2 * BIGGAP; - cp->ypos += MEDGAP; - rpos = BIGGAP + (bigwid + BIGGAP) / 2; - lwid = rpos - 2 * BIGGAP; - rwid = bigwid + BIGGAP - rpos; - - r.left = BIGGAP; - r.top = cp->ypos + (height - STATICHEIGHT) / 2; - r.right = lwid; - r.bottom = STATICHEIGHT; - doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); - - r.left = rpos; - r.top = cp->ypos + (height - COMBOHEIGHT) / 2; - r.right = rwid; - r.bottom = COMBOHEIGHT * 10; - doctl(cp, r, "COMBOBOX", - WS_CHILD | WS_VISIBLE | WS_TABSTOP | - CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", lid); - - cp->ypos += height + MEDGAP + GAPBETWEEN; - - r.left = GAPBETWEEN; - r.top = cp->ypos; - r.right = cp->width; - r.bottom = 2; - doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE | SS_ETCHEDHORZ, - 0, "", s2id); -} - -/* - * A static line, followed by an edit control on the left hand side - * and a button on the right. - */ -void editbutton(struct ctlpos *cp, char *stext, int sid, - int eid, char *btext, int bid) -{ - const int height = (EDITHEIGHT > PUSHBTNHEIGHT ? - EDITHEIGHT : PUSHBTNHEIGHT); - RECT r; - int lwid, rwid, rpos; - - r.left = GAPBETWEEN; - r.top = cp->ypos; - r.right = cp->width; - r.bottom = STATICHEIGHT; - cp->ypos += r.bottom + GAPWITHIN; - doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); - - rpos = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4; - lwid = rpos - 2 * GAPBETWEEN; - rwid = cp->width + GAPBETWEEN - rpos; - - r.left = GAPBETWEEN; - r.top = cp->ypos + (height - EDITHEIGHT) / 2; - r.right = lwid; - r.bottom = EDITHEIGHT; - doctl(cp, r, "EDIT", - WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL, - WS_EX_CLIENTEDGE, "", eid); - - r.left = rpos; - r.top = cp->ypos + (height - PUSHBTNHEIGHT) / 2; - r.right = rwid; - r.bottom = PUSHBTNHEIGHT; - doctl(cp, r, "BUTTON", - BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, - 0, btext, bid); - - cp->ypos += height + GAPBETWEEN; -} - -/* - * A special control for manipulating an ordered preference list - * (eg. for cipher selection). - * XXX: this is a rough hack and could be improved. - */ -void prefslist(struct prefslist *hdl, struct ctlpos *cp, int lines, - char *stext, int sid, int listid, int upbid, int dnbid) -{ - const static int percents[] = { 5, 75, 20 }; - RECT r; - int xpos, percent = 0, i; - int listheight = LISTHEIGHT + (lines - 1) * LISTINCREMENT; - const int BTNSHEIGHT = 2*PUSHBTNHEIGHT + GAPBETWEEN; - int totalheight, buttonpos; - - /* Squirrel away IDs. */ - hdl->listid = listid; - hdl->upbid = upbid; - hdl->dnbid = dnbid; - - /* The static label. */ - if (stext != NULL) { - r.left = GAPBETWEEN; - r.top = cp->ypos; - r.right = cp->width; - r.bottom = STATICHEIGHT; - cp->ypos += r.bottom + GAPWITHIN; - doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); - } - - if (listheight > BTNSHEIGHT) { - totalheight = listheight; - buttonpos = (listheight - BTNSHEIGHT) / 2; - } else { - totalheight = BTNSHEIGHT; - buttonpos = 0; - } - - for (i=0; i<3; i++) { - int left, wid; - xpos = (cp->width + GAPBETWEEN) * percent / 100; - left = xpos + GAPBETWEEN; - percent += percents[i]; - xpos = (cp->width + GAPBETWEEN) * percent / 100; - wid = xpos - left; - - switch (i) { - case 1: { - /* The drag list box. */ - r.left = left; r.right = wid; - r.top = cp->ypos; r.bottom = listheight; - HWND ctl = doctl(cp, r, "LISTBOX", - WS_CHILD | WS_VISIBLE | WS_TABSTOP | - WS_VSCROLL | LBS_HASSTRINGS | LBS_USETABSTOPS, - WS_EX_CLIENTEDGE, - "", listid); - p_MakeDragList(ctl); - break; - } - - case 2: - /* The "Up" and "Down" buttons. */ - /* XXX worry about accelerators if we have more than one - * prefslist on a panel */ - r.left = left; r.right = wid; - r.top = cp->ypos + buttonpos; r.bottom = PUSHBTNHEIGHT; - doctl(cp, r, "BUTTON", - BS_NOTIFY | WS_CHILD | WS_VISIBLE | - WS_TABSTOP | BS_PUSHBUTTON, - 0, "&Up", upbid); - - r.left = left; r.right = wid; - r.top = cp->ypos + buttonpos + PUSHBTNHEIGHT + GAPBETWEEN; - r.bottom = PUSHBTNHEIGHT; - doctl(cp, r, "BUTTON", - BS_NOTIFY | WS_CHILD | WS_VISIBLE | - WS_TABSTOP | BS_PUSHBUTTON, - 0, "&Down", dnbid); - - break; - - } - } - - cp->ypos += totalheight + GAPBETWEEN; - -} - -/* - * Helper function for prefslist: move item in list box. - */ -static void pl_moveitem(HWND hwnd, int listid, int src, int dst) -{ - int tlen, val; - char *txt; - /* Get the item's data. */ - tlen = SendDlgItemMessage (hwnd, listid, LB_GETTEXTLEN, src, 0); - txt = snewn(tlen+1, char); - SendDlgItemMessage (hwnd, listid, LB_GETTEXT, src, (LPARAM) txt); - val = SendDlgItemMessage (hwnd, listid, LB_GETITEMDATA, src, 0); - /* Deselect old location. */ - SendDlgItemMessage (hwnd, listid, LB_SETSEL, false, src); - /* Delete it at the old location. */ - SendDlgItemMessage (hwnd, listid, LB_DELETESTRING, src, 0); - /* Insert it at new location. */ - SendDlgItemMessage (hwnd, listid, LB_INSERTSTRING, dst, - (LPARAM) txt); - SendDlgItemMessage (hwnd, listid, LB_SETITEMDATA, dst, - (LPARAM) val); - /* Set selection. */ - SendDlgItemMessage (hwnd, listid, LB_SETCURSEL, dst, 0); - sfree (txt); -} - -int pl_itemfrompt(HWND hwnd, POINT cursor, bool scroll) -{ - int ret; - POINT uppoint, downpoint; - int updist, downdist, upitem, downitem, i; - - /* - * Ghastly hackery to try to figure out not which - * _item_, but which _gap between items_, the user - * is pointing at. We do this by first working out - * which list item is under the cursor, and then - * working out how far the cursor would have to - * move up or down before the answer was different. - * Then we put the insertion point _above_ the - * current item if the upper edge is closer than - * the lower edge, or _below_ it if vice versa. - */ - ret = p_LBItemFromPt(hwnd, cursor, scroll); - if (ret == -1) - return ret; - ret = p_LBItemFromPt(hwnd, cursor, false); - updist = downdist = 0; - for (i = 1; i < 4096 && (!updist || !downdist); i++) { - uppoint = downpoint = cursor; - uppoint.y -= i; - downpoint.y += i; - upitem = p_LBItemFromPt(hwnd, uppoint, false); - downitem = p_LBItemFromPt(hwnd, downpoint, false); - if (!updist && upitem != ret) - updist = i; - if (!downdist && downitem != ret) - downdist = i; - } - if (downdist < updist) - ret++; - return ret; -} - -/* - * Handler for prefslist above. - * - * Return value has bit 0 set if the dialog box procedure needs to - * return true from handling this message; it has bit 1 set if a - * change may have been made in the contents of the list. - */ -int handle_prefslist(struct prefslist *hdl, - int *array, int maxmemb, - bool is_dlmsg, HWND hwnd, - WPARAM wParam, LPARAM lParam) -{ - int i; - int ret = 0; - - if (is_dlmsg) { - - if ((int)wParam == hdl->listid) { - DRAGLISTINFO *dlm = (DRAGLISTINFO *)lParam; - int dest = 0; /* initialise to placate gcc */ - switch (dlm->uNotification) { - case DL_BEGINDRAG: - /* Add a dummy item to make pl_itemfrompt() work - * better. - * FIXME: this causes scrollbar glitches if the count of - * listbox contains >= its height. */ - hdl->dummyitem = - SendDlgItemMessage(hwnd, hdl->listid, - LB_ADDSTRING, 0, (LPARAM) ""); - - hdl->srcitem = p_LBItemFromPt(dlm->hWnd, dlm->ptCursor, true); - hdl->dragging = false; - /* XXX hack Q183115 */ - SetWindowLongPtr(hwnd, DWLP_MSGRESULT, true); - ret |= 1; break; - case DL_CANCELDRAG: - p_DrawInsert(hwnd, dlm->hWnd, -1); /* Clear arrow */ - SendDlgItemMessage(hwnd, hdl->listid, - LB_DELETESTRING, hdl->dummyitem, 0); - hdl->dragging = false; - ret |= 1; break; - case DL_DRAGGING: - hdl->dragging = true; - dest = pl_itemfrompt(dlm->hWnd, dlm->ptCursor, true); - if (dest > hdl->dummyitem) dest = hdl->dummyitem; - p_DrawInsert (hwnd, dlm->hWnd, dest); - if (dest >= 0) - SetWindowLongPtr(hwnd, DWLP_MSGRESULT, DL_MOVECURSOR); - else - SetWindowLongPtr(hwnd, DWLP_MSGRESULT, DL_STOPCURSOR); - ret |= 1; break; - case DL_DROPPED: - if (hdl->dragging) { - dest = pl_itemfrompt(dlm->hWnd, dlm->ptCursor, true); - if (dest > hdl->dummyitem) dest = hdl->dummyitem; - p_DrawInsert (hwnd, dlm->hWnd, -1); - } - SendDlgItemMessage(hwnd, hdl->listid, - LB_DELETESTRING, hdl->dummyitem, 0); - if (hdl->dragging) { - hdl->dragging = false; - if (dest >= 0) { - /* Correct for "missing" item. */ - if (dest > hdl->srcitem) dest--; - pl_moveitem(hwnd, hdl->listid, hdl->srcitem, dest); - } - ret |= 2; - } - ret |= 1; break; - } - } - - } else { - - if (((LOWORD(wParam) == hdl->upbid) || - (LOWORD(wParam) == hdl->dnbid)) && - ((HIWORD(wParam) == BN_CLICKED) || - (HIWORD(wParam) == BN_DOUBLECLICKED))) { - /* Move an item up or down the list. */ - /* Get the current selection, if any. */ - int selection = SendDlgItemMessage (hwnd, hdl->listid, LB_GETCURSEL, 0, 0); - if (selection == LB_ERR) { - MessageBeep(0); - } else { - int nitems; - /* Get the total number of items. */ - nitems = SendDlgItemMessage (hwnd, hdl->listid, LB_GETCOUNT, 0, 0); - /* Should we do anything? */ - if (LOWORD(wParam) == hdl->upbid && (selection > 0)) - pl_moveitem(hwnd, hdl->listid, selection, selection - 1); - else if (LOWORD(wParam) == hdl->dnbid && (selection < nitems - 1)) - pl_moveitem(hwnd, hdl->listid, selection, selection + 1); - ret |= 2; - } - - } - - } - - if (array) { - /* Update array to match the list box. */ - for (i=0; i < maxmemb; i++) - array[i] = SendDlgItemMessage (hwnd, hdl->listid, LB_GETITEMDATA, - i, 0); - } - - return ret; -} - -/* - * A progress bar (from Common Controls). We like our progress bars - * to be smooth and unbroken, without those ugly divisions; some - * older compilers may not support that, but that's life. - */ -void progressbar(struct ctlpos *cp, int id) -{ - RECT r; - - r.left = GAPBETWEEN; - r.top = cp->ypos; - r.right = cp->width; - r.bottom = PROGBARHEIGHT; - cp->ypos += r.bottom + GAPBETWEEN; - - doctl(cp, r, PROGRESS_CLASS, WS_CHILD | WS_VISIBLE -#ifdef PBS_SMOOTH - | PBS_SMOOTH -#endif - , WS_EX_CLIENTEDGE, "", id); -} - -/* ---------------------------------------------------------------------- - * Platform-specific side of portable dialog-box mechanism. - */ - -/* - * This function takes a string, escapes all the ampersands, and - * places a single (unescaped) ampersand in front of the first - * occurrence of the given shortcut character (which may be - * NO_SHORTCUT). - * - * Return value is a malloc'ed copy of the processed version of the - * string. - */ -static char *shortcut_escape(const char *text, char shortcut) -{ - char *ret; - char const *p; - char *q; - - if (!text) - return NULL; /* sfree won't choke on this */ - - ret = snewn(2*strlen(text)+1, char); /* size potentially doubles! */ - shortcut = tolower((unsigned char)shortcut); - - p = text; - q = ret; - while (*p) { - if (shortcut != NO_SHORTCUT && - tolower((unsigned char)*p) == shortcut) { - *q++ = '&'; - shortcut = NO_SHORTCUT; /* stop it happening twice */ - } else if (*p == '&') { - *q++ = '&'; - } - *q++ = *p++; - } - *q = '\0'; - return ret; -} - -void winctrl_add_shortcuts(struct dlgparam *dp, struct winctrl *c) -{ - int i; - for (i = 0; i < lenof(c->shortcuts); i++) - if (c->shortcuts[i] != NO_SHORTCUT) { - unsigned char s = tolower((unsigned char)c->shortcuts[i]); - assert(!dp->shortcuts[s]); - dp->shortcuts[s] = true; - } -} - -void winctrl_rem_shortcuts(struct dlgparam *dp, struct winctrl *c) -{ - int i; - for (i = 0; i < lenof(c->shortcuts); i++) - if (c->shortcuts[i] != NO_SHORTCUT) { - unsigned char s = tolower((unsigned char)c->shortcuts[i]); - assert(dp->shortcuts[s]); - dp->shortcuts[s] = false; - } -} - -static int winctrl_cmp_byctrl(void *av, void *bv) -{ - struct winctrl *a = (struct winctrl *)av; - struct winctrl *b = (struct winctrl *)bv; - if (a->ctrl < b->ctrl) - return -1; - else if (a->ctrl > b->ctrl) - return +1; - else - return 0; -} -static int winctrl_cmp_byid(void *av, void *bv) -{ - struct winctrl *a = (struct winctrl *)av; - struct winctrl *b = (struct winctrl *)bv; - if (a->base_id < b->base_id) - return -1; - else if (a->base_id > b->base_id) - return +1; - else - return 0; -} -static int winctrl_cmp_byctrl_find(void *av, void *bv) -{ - union control *a = (union control *)av; - struct winctrl *b = (struct winctrl *)bv; - if (a < b->ctrl) - return -1; - else if (a > b->ctrl) - return +1; - else - return 0; -} -static int winctrl_cmp_byid_find(void *av, void *bv) -{ - int *a = (int *)av; - struct winctrl *b = (struct winctrl *)bv; - if (*a < b->base_id) - return -1; - else if (*a >= b->base_id + b->num_ids) - return +1; - else - return 0; -} - -void winctrl_init(struct winctrls *wc) -{ - wc->byctrl = newtree234(winctrl_cmp_byctrl); - wc->byid = newtree234(winctrl_cmp_byid); -} -void winctrl_cleanup(struct winctrls *wc) -{ - struct winctrl *c; - - while ((c = index234(wc->byid, 0)) != NULL) { - winctrl_remove(wc, c); - sfree(c->data); - sfree(c); - } - - freetree234(wc->byctrl); - freetree234(wc->byid); - wc->byctrl = wc->byid = NULL; -} - -void winctrl_add(struct winctrls *wc, struct winctrl *c) -{ - struct winctrl *ret; - if (c->ctrl) { - ret = add234(wc->byctrl, c); - assert(ret == c); - } - ret = add234(wc->byid, c); - assert(ret == c); -} - -void winctrl_remove(struct winctrls *wc, struct winctrl *c) -{ - struct winctrl *ret; - ret = del234(wc->byctrl, c); - ret = del234(wc->byid, c); - assert(ret == c); -} - -struct winctrl *winctrl_findbyctrl(struct winctrls *wc, union control *ctrl) -{ - return find234(wc->byctrl, ctrl, winctrl_cmp_byctrl_find); -} - -struct winctrl *winctrl_findbyid(struct winctrls *wc, int id) -{ - return find234(wc->byid, &id, winctrl_cmp_byid_find); -} - -struct winctrl *winctrl_findbyindex(struct winctrls *wc, int index) -{ - return index234(wc->byid, index); -} - -static void move_windows(HWND hwnd, int base_id, int num_ids, LONG dy) -{ - if (!dy) - return; - for (int i = 0; i < num_ids; i++) { - HWND win = GetDlgItem(hwnd, base_id + i); - - RECT rect; - if (!GetWindowRect(win, &rect)) - continue; - - POINT p; - p.x = rect.left; - p.y = rect.top + dy; - if (!ScreenToClient(hwnd, &p)) - continue; - - SetWindowPos(win, NULL, p.x, p.y, 0, 0, - SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER); - } -} - -void winctrl_layout(struct dlgparam *dp, struct winctrls *wc, - struct ctlpos *cp, struct controlset *s, int *id) -{ - struct ctlpos columns[16]; - int ncols, colstart, colspan; - - struct ctlpos tabdelays[16]; - union control *tabdelayed[16]; - int ntabdelays; - - struct ctlpos pos; - - char shortcuts[MAX_SHORTCUTS_PER_CTRL]; - int nshortcuts; - char *escaped; - int i, actual_base_id, base_id, num_ids, align_id_relative; - void *data; - - base_id = *id; - - /* Start a containing box, if we have a boxname. */ - if (s->boxname && *s->boxname) { - struct winctrl *c = snew(struct winctrl); - c->ctrl = NULL; - c->base_id = c->align_id = base_id; - c->num_ids = 1; - c->data = NULL; - memset(c->shortcuts, NO_SHORTCUT, lenof(c->shortcuts)); - winctrl_add(wc, c); - beginbox(cp, s->boxtitle, base_id); - base_id++; - } - - /* Draw a title, if we have one. */ - if (!s->boxname && s->boxtitle) { - struct winctrl *c = snew(struct winctrl); - c->ctrl = NULL; - c->base_id = c->align_id = base_id; - c->num_ids = 1; - c->data = dupstr(s->boxtitle); - memset(c->shortcuts, NO_SHORTCUT, lenof(c->shortcuts)); - winctrl_add(wc, c); - paneltitle(cp, base_id); - base_id++; - } - - /* Initially we have just one column. */ - ncols = 1; - columns[0] = *cp; /* structure copy */ - - /* And initially, there are no pending tab-delayed controls. */ - ntabdelays = 0; - - /* Loop over each control in the controlset. */ - for (i = 0; i < s->ncontrols; i++) { - union control *ctrl = s->ctrls[i]; - - /* - * Generic processing that pertains to all control types. - * At the end of this if statement, we'll have produced - * `ctrl' (a pointer to the control we have to create, or - * think about creating, in this iteration of the loop), - * `pos' (a suitable ctlpos with which to position it), and - * `c' (a winctrl structure to receive details of the - * dialog IDs). Or we'll have done a `continue', if it was - * CTRL_COLUMNS and doesn't require any control creation at - * all. - */ - if (ctrl->generic.type == CTRL_COLUMNS) { - assert((ctrl->columns.ncols == 1) ^ (ncols == 1)); - - if (ncols == 1) { - /* - * We're splitting into multiple columns. - */ - int lpercent, rpercent, lx, rx, i; - - ncols = ctrl->columns.ncols; - assert(ncols <= lenof(columns)); - for (i = 1; i < ncols; i++) - columns[i] = columns[0]; /* structure copy */ - - lpercent = 0; - for (i = 0; i < ncols; i++) { - rpercent = lpercent + ctrl->columns.percentages[i]; - lx = columns[i].xoff + lpercent * - (columns[i].width + GAPBETWEEN) / 100; - rx = columns[i].xoff + rpercent * - (columns[i].width + GAPBETWEEN) / 100; - columns[i].xoff = lx; - columns[i].width = rx - lx - GAPBETWEEN; - lpercent = rpercent; - } - } else { - /* - * We're recombining the various columns into one. - */ - int maxy = columns[0].ypos; - int i; - for (i = 1; i < ncols; i++) - if (maxy < columns[i].ypos) - maxy = columns[i].ypos; - ncols = 1; - columns[0] = *cp; /* structure copy */ - columns[0].ypos = maxy; - } - - continue; - } else if (ctrl->generic.type == CTRL_TABDELAY) { - int i; - - assert(!ctrl->generic.tabdelay); - ctrl = ctrl->tabdelay.ctrl; - - for (i = 0; i < ntabdelays; i++) - if (tabdelayed[i] == ctrl) - break; - assert(i < ntabdelays); /* we have to have found it */ - - pos = tabdelays[i]; /* structure copy */ - - colstart = colspan = -1; /* indicate this was tab-delayed */ - - } else { - /* - * If it wasn't one of those, it's a genuine control; - * so we'll have to compute a position for it now, by - * checking its column span. - */ - int col; - - colstart = COLUMN_START(ctrl->generic.column); - colspan = COLUMN_SPAN(ctrl->generic.column); - - pos = columns[colstart]; /* structure copy */ - pos.width = columns[colstart+colspan-1].width + - (columns[colstart+colspan-1].xoff - columns[colstart].xoff); - - for (col = colstart; col < colstart+colspan; col++) - if (pos.ypos < columns[col].ypos) - pos.ypos = columns[col].ypos; - - /* - * If this control is to be tabdelayed, add it to the - * tabdelay list, and unset pos.hwnd to inhibit actual - * control creation. - */ - if (ctrl->generic.tabdelay) { - assert(ntabdelays < lenof(tabdelays)); - tabdelays[ntabdelays] = pos; /* structure copy */ - tabdelayed[ntabdelays] = ctrl; - ntabdelays++; - pos.hwnd = NULL; - } - } - - /* Most controls don't need anything in c->data. */ - data = NULL; - - /* And they all start off with no shortcuts registered. */ - memset(shortcuts, NO_SHORTCUT, lenof(shortcuts)); - nshortcuts = 0; - - /* Almost all controls start at base_id. */ - actual_base_id = base_id; - - /* For vertical alignment purposes, the most relevant control - * in a group is usually the last one. But that can be - * overridden occasionally. */ - align_id_relative = -1; - - /* - * Now we're ready to actually create the control, by - * switching on its type. - */ - switch (ctrl->generic.type) { - case CTRL_TEXT: { - char *wrapped, *escaped; - int lines; - num_ids = 1; - wrapped = staticwrap(&pos, cp->hwnd, - ctrl->generic.label, &lines); - escaped = shortcut_escape(wrapped, NO_SHORTCUT); - statictext(&pos, escaped, lines, base_id); - sfree(escaped); - sfree(wrapped); - break; - } - case CTRL_EDITBOX: - num_ids = 2; /* static, edit */ - escaped = shortcut_escape(ctrl->editbox.label, - ctrl->editbox.shortcut); - shortcuts[nshortcuts++] = ctrl->editbox.shortcut; - if (ctrl->editbox.percentwidth == 100) { - if (ctrl->editbox.has_list) - combobox(&pos, escaped, - base_id, base_id+1); - else - editboxfw(&pos, ctrl->editbox.password, escaped, - base_id, base_id+1); - } else { - if (ctrl->editbox.has_list) { - staticcombo(&pos, escaped, base_id, base_id+1, - ctrl->editbox.percentwidth); - } else { - (ctrl->editbox.password ? staticpassedit : staticedit) - (&pos, escaped, base_id, base_id+1, - ctrl->editbox.percentwidth); - } - } - sfree(escaped); - break; - case CTRL_RADIO: { - num_ids = ctrl->radio.nbuttons + 1; /* label as well */ - struct radio *buttons; - int i; - - escaped = shortcut_escape(ctrl->radio.label, - ctrl->radio.shortcut); - shortcuts[nshortcuts++] = ctrl->radio.shortcut; - - buttons = snewn(ctrl->radio.nbuttons, struct radio); - - for (i = 0; i < ctrl->radio.nbuttons; i++) { - buttons[i].text = - shortcut_escape(ctrl->radio.buttons[i], - (char)(ctrl->radio.shortcuts ? - ctrl->radio.shortcuts[i] : - NO_SHORTCUT)); - buttons[i].id = base_id + 1 + i; - if (ctrl->radio.shortcuts) { - assert(nshortcuts < MAX_SHORTCUTS_PER_CTRL); - shortcuts[nshortcuts++] = ctrl->radio.shortcuts[i]; - } - } - - radioline_common(&pos, escaped, base_id, - ctrl->radio.ncolumns, - buttons, ctrl->radio.nbuttons); - - for (i = 0; i < ctrl->radio.nbuttons; i++) { - sfree(buttons[i].text); - } - sfree(buttons); - sfree(escaped); - break; - } - case CTRL_CHECKBOX: - num_ids = 1; - escaped = shortcut_escape(ctrl->checkbox.label, - ctrl->checkbox.shortcut); - shortcuts[nshortcuts++] = ctrl->checkbox.shortcut; - checkbox(&pos, escaped, base_id); - sfree(escaped); - break; - case CTRL_BUTTON: - escaped = shortcut_escape(ctrl->button.label, - ctrl->button.shortcut); - shortcuts[nshortcuts++] = ctrl->button.shortcut; - if (ctrl->button.iscancel) - actual_base_id = IDCANCEL; - num_ids = 1; - button(&pos, escaped, actual_base_id, ctrl->button.isdefault); - sfree(escaped); - break; - case CTRL_LISTBOX: - num_ids = 2; - escaped = shortcut_escape(ctrl->listbox.label, - ctrl->listbox.shortcut); - shortcuts[nshortcuts++] = ctrl->listbox.shortcut; - if (ctrl->listbox.draglist) { - data = snew(struct prefslist); - num_ids = 4; - prefslist(data, &pos, ctrl->listbox.height, escaped, - base_id, base_id+1, base_id+2, base_id+3); - shortcuts[nshortcuts++] = 'u'; /* Up */ - shortcuts[nshortcuts++] = 'd'; /* Down */ - } else if (ctrl->listbox.height == 0) { - /* Drop-down list. */ - if (ctrl->listbox.percentwidth == 100) { - staticddlbig(&pos, escaped, - base_id, base_id+1); - } else { - staticddl(&pos, escaped, base_id, - base_id+1, ctrl->listbox.percentwidth); - } - } else { - /* Ordinary list. */ - listbox(&pos, escaped, base_id, base_id+1, - ctrl->listbox.height, ctrl->listbox.multisel); - } - if (ctrl->listbox.ncols) { - /* - * This method of getting the box width is a bit of - * a hack; we'd do better to try to retrieve the - * actual width in dialog units from doctl() just - * before MapDialogRect. But that's going to be no - * fun, and this should be good enough accuracy. - */ - int width = cp->width * ctrl->listbox.percentwidth; - int *tabarray; - int i, percent; - - tabarray = snewn(ctrl->listbox.ncols-1, int); - percent = 0; - for (i = 0; i < ctrl->listbox.ncols-1; i++) { - percent += ctrl->listbox.percentages[i]; - tabarray[i] = width * percent / 10000; - } - SendDlgItemMessage(cp->hwnd, base_id+1, LB_SETTABSTOPS, - ctrl->listbox.ncols-1, (LPARAM)tabarray); - sfree(tabarray); - } - sfree(escaped); - break; - case CTRL_FILESELECT: - num_ids = 3; - escaped = shortcut_escape(ctrl->fileselect.label, - ctrl->fileselect.shortcut); - shortcuts[nshortcuts++] = ctrl->fileselect.shortcut; - editbutton(&pos, escaped, base_id, base_id+1, - "Bro&wse...", base_id+2); - shortcuts[nshortcuts++] = 'w'; - sfree(escaped); - break; - case CTRL_FONTSELECT: - num_ids = 3; - escaped = shortcut_escape(ctrl->fontselect.label, - ctrl->fontselect.shortcut); - shortcuts[nshortcuts++] = ctrl->fontselect.shortcut; - statictext(&pos, escaped, 1, base_id); - staticbtn(&pos, "", base_id+1, "Change...", base_id+2); - data = fontspec_new("", false, 0, 0); - sfree(escaped); - break; - default: - unreachable("bad control type in winctrl_layout"); - } - - /* Translate the original align_id_relative of -1 into n-1 */ - if (align_id_relative < 0) - align_id_relative += num_ids; - - /* - * Create a `struct winctrl' for this control, and advance - * the dialog ID counter, if it's actually been created - * (and isn't tabdelayed). - */ - if (pos.hwnd) { - struct winctrl *c = snew(struct winctrl); - - c->ctrl = ctrl; - c->base_id = actual_base_id; - c->align_id = c->base_id + align_id_relative; - c->num_ids = num_ids; - c->data = data; - memcpy(c->shortcuts, shortcuts, sizeof(shortcuts)); - winctrl_add(wc, c); - winctrl_add_shortcuts(dp, c); - if (actual_base_id == base_id) - base_id += num_ids; - - if (ctrl->generic.align_next_to) { - /* - * Implement align_next_to by looking at the y extents - * of the two controls now that both are created, and - * moving one or the other downwards so that they're - * centred on a common horizontal line. - */ - struct winctrl *c2 = winctrl_findbyctrl( - wc, ctrl->generic.align_next_to); - HWND win1 = GetDlgItem(pos.hwnd, c->align_id); - HWND win2 = GetDlgItem(pos.hwnd, c2->align_id); - RECT rect1, rect2; - if (win1 && win2 && - GetWindowRect(win1, &rect1) && - GetWindowRect(win2, &rect2)) { - LONG top = (rect1.top < rect2.top ? rect1.top : rect2.top); - LONG bottom = (rect1.bottom > rect2.bottom ? - rect1.bottom : rect2.bottom); - move_windows(pos.hwnd, c->base_id, c->num_ids, - (top + bottom - rect1.top - rect1.bottom)/2); - move_windows(pos.hwnd, c2->base_id, c2->num_ids, - (top + bottom - rect2.top - rect2.bottom)/2); - } - } - } else { - sfree(data); - } - - if (colstart >= 0) { - /* - * Update the ypos in all columns crossed by this - * control. - */ - int i; - for (i = colstart; i < colstart+colspan; i++) - columns[i].ypos = pos.ypos; - } - } - - /* - * We've now finished laying out the controls; so now update - * the ctlpos and control ID that were passed in, terminate - * any containing box, and return. - */ - for (i = 0; i < ncols; i++) - if (cp->ypos < columns[i].ypos) - cp->ypos = columns[i].ypos; - *id = base_id; - - if (s->boxname && *s->boxname) - endbox(cp); -} - -static void winctrl_set_focus(union control *ctrl, struct dlgparam *dp, - bool has_focus) -{ - if (has_focus) { - if (dp->focused) - dp->lastfocused = dp->focused; - dp->focused = ctrl; - } else if (!has_focus && dp->focused == ctrl) { - dp->lastfocused = dp->focused; - dp->focused = NULL; - } -} - -union control *dlg_last_focused(union control *ctrl, dlgparam *dp) -{ - return dp->focused == ctrl ? dp->lastfocused : dp->focused; -} - -/* - * The dialog-box procedure calls this function to handle Windows - * messages on a control we manage. - */ -bool winctrl_handle_command(struct dlgparam *dp, UINT msg, - WPARAM wParam, LPARAM lParam) -{ - struct winctrl *c; - union control *ctrl; - int i, id; - bool ret; - static UINT draglistmsg = WM_NULL; - - /* - * Filter out pointless window messages. Our interest is in - * WM_COMMAND and the drag list message, and nothing else. - */ - if (draglistmsg == WM_NULL) - draglistmsg = RegisterWindowMessage (DRAGLISTMSGSTRING); - - if (msg != draglistmsg && msg != WM_COMMAND && msg != WM_DRAWITEM) - return false; - - /* - * Look up the control ID in our data. - */ - c = NULL; - for (i = 0; i < dp->nctrltrees; i++) { - c = winctrl_findbyid(dp->controltrees[i], LOWORD(wParam)); - if (c) - break; - } - if (!c) - return false; /* we have nothing to do */ - - if (msg == WM_DRAWITEM) { - /* - * Owner-draw request for a panel title. - */ - LPDRAWITEMSTRUCT di = (LPDRAWITEMSTRUCT) lParam; - HDC hdc = di->hDC; - RECT r = di->rcItem; - SIZE s; - - SetMapMode(hdc, MM_TEXT); /* ensure logical units == pixels */ - - GetTextExtentPoint32(hdc, (char *)c->data, - strlen((char *)c->data), &s); - DrawEdge(hdc, &r, EDGE_ETCHED, BF_ADJUST | BF_RECT); - TextOut(hdc, - r.left + (r.right-r.left-s.cx)/2, - r.top + (r.bottom-r.top-s.cy)/2, - (char *)c->data, strlen((char *)c->data)); - - return true; - } - - ctrl = c->ctrl; - id = LOWORD(wParam) - c->base_id; - - if (!ctrl || !ctrl->generic.handler) - return false; /* nothing we can do here */ - - /* - * From here on we do not issue `return' statements until the - * very end of the dialog box: any event handler is entitled to - * ask for a colour selector, so we _must_ always allow control - * to reach the end of this switch statement so that the - * subsequent code can test dp->coloursel_wanted(). - */ - ret = false; - dp->coloursel_wanted = false; - - /* - * Now switch on the control type and the message. - */ - switch (ctrl->generic.type) { - case CTRL_EDITBOX: - if (msg == WM_COMMAND && !ctrl->editbox.has_list && - (HIWORD(wParam) == EN_SETFOCUS || HIWORD(wParam) == EN_KILLFOCUS)) - winctrl_set_focus(ctrl, dp, HIWORD(wParam) == EN_SETFOCUS); - if (msg == WM_COMMAND && ctrl->editbox.has_list && - (HIWORD(wParam)==CBN_SETFOCUS || HIWORD(wParam)==CBN_KILLFOCUS)) - winctrl_set_focus(ctrl, dp, HIWORD(wParam) == CBN_SETFOCUS); - - if (msg == WM_COMMAND && !ctrl->editbox.has_list && - HIWORD(wParam) == EN_CHANGE) - ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE); - if (msg == WM_COMMAND && - ctrl->editbox.has_list) { - if (HIWORD(wParam) == CBN_SELCHANGE) { - int index, len; - char *text; - - index = SendDlgItemMessage(dp->hwnd, c->base_id+1, - CB_GETCURSEL, 0, 0); - len = SendDlgItemMessage(dp->hwnd, c->base_id+1, - CB_GETLBTEXTLEN, index, 0); - text = snewn(len+1, char); - SendDlgItemMessage(dp->hwnd, c->base_id+1, CB_GETLBTEXT, - index, (LPARAM)text); - SetDlgItemText(dp->hwnd, c->base_id+1, text); - sfree(text); - ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE); - } else if (HIWORD(wParam) == CBN_EDITCHANGE) { - ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE); - } else if (HIWORD(wParam) == CBN_KILLFOCUS) { - ctrl->generic.handler(ctrl, dp, dp->data, EVENT_REFRESH); - } - - } - break; - case CTRL_RADIO: - if (msg == WM_COMMAND && - (HIWORD(wParam) == BN_SETFOCUS || HIWORD(wParam) == BN_KILLFOCUS)) - winctrl_set_focus(ctrl, dp, HIWORD(wParam) == BN_SETFOCUS); - /* - * We sometimes get spurious BN_CLICKED messages for the - * radio button that is just about to _lose_ selection, if - * we're switching using the arrow keys. Therefore we - * double-check that the button in wParam is actually - * checked before generating an event. - */ - if (msg == WM_COMMAND && - (HIWORD(wParam) == BN_CLICKED || - HIWORD(wParam) == BN_DOUBLECLICKED) && - IsDlgButtonChecked(dp->hwnd, LOWORD(wParam))) { - ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE); - } - break; - case CTRL_CHECKBOX: - if (msg == WM_COMMAND && - (HIWORD(wParam) == BN_SETFOCUS || HIWORD(wParam) == BN_KILLFOCUS)) - winctrl_set_focus(ctrl, dp, HIWORD(wParam) == BN_SETFOCUS); - if (msg == WM_COMMAND && - (HIWORD(wParam) == BN_CLICKED || - HIWORD(wParam) == BN_DOUBLECLICKED)) { - ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE); - } - break; - case CTRL_BUTTON: - if (msg == WM_COMMAND && - (HIWORD(wParam) == BN_SETFOCUS || HIWORD(wParam) == BN_KILLFOCUS)) - winctrl_set_focus(ctrl, dp, HIWORD(wParam) == BN_SETFOCUS); - if (msg == WM_COMMAND && - (HIWORD(wParam) == BN_CLICKED || - HIWORD(wParam) == BN_DOUBLECLICKED)) { - ctrl->generic.handler(ctrl, dp, dp->data, EVENT_ACTION); - } - break; - case CTRL_LISTBOX: - if (msg == WM_COMMAND && ctrl->listbox.height != 0 && - (HIWORD(wParam)==LBN_SETFOCUS || HIWORD(wParam)==LBN_KILLFOCUS)) - winctrl_set_focus(ctrl, dp, HIWORD(wParam) == LBN_SETFOCUS); - if (msg == WM_COMMAND && ctrl->listbox.height == 0 && - (HIWORD(wParam)==CBN_SETFOCUS || HIWORD(wParam)==CBN_KILLFOCUS)) - winctrl_set_focus(ctrl, dp, HIWORD(wParam) == CBN_SETFOCUS); - if (msg == WM_COMMAND && id >= 2 && - (HIWORD(wParam) == BN_SETFOCUS || HIWORD(wParam) == BN_KILLFOCUS)) - winctrl_set_focus(ctrl, dp, HIWORD(wParam) == BN_SETFOCUS); - if (ctrl->listbox.draglist) { - int pret; - pret = handle_prefslist(c->data, NULL, 0, (msg != WM_COMMAND), - dp->hwnd, wParam, lParam); - if (pret & 2) - ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE); - ret = pret & 1; - } else { - if (msg == WM_COMMAND && HIWORD(wParam) == LBN_DBLCLK) { - SetCapture(dp->hwnd); - ctrl->generic.handler(ctrl, dp, dp->data, EVENT_ACTION); - } else if (msg == WM_COMMAND && HIWORD(wParam) == LBN_SELCHANGE) { - ctrl->generic.handler(ctrl, dp, dp->data, EVENT_SELCHANGE); - } - } - break; - case CTRL_FILESELECT: - if (msg == WM_COMMAND && id == 1 && - (HIWORD(wParam) == EN_SETFOCUS || HIWORD(wParam) == EN_KILLFOCUS)) - winctrl_set_focus(ctrl, dp, HIWORD(wParam) == EN_SETFOCUS); - if (msg == WM_COMMAND && id == 2 && - (HIWORD(wParam) == BN_SETFOCUS || HIWORD(wParam) == BN_KILLFOCUS)) - winctrl_set_focus(ctrl, dp, HIWORD(wParam) == BN_SETFOCUS); - if (msg == WM_COMMAND && id == 1 && HIWORD(wParam) == EN_CHANGE) - ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE); - if (id == 2 && - (msg == WM_COMMAND && - (HIWORD(wParam) == BN_CLICKED || - HIWORD(wParam) == BN_DOUBLECLICKED))) { - OPENFILENAME of; - char filename[FILENAME_MAX]; - - memset(&of, 0, sizeof(of)); - of.hwndOwner = dp->hwnd; - if (ctrl->fileselect.filter) - of.lpstrFilter = ctrl->fileselect.filter; - else - of.lpstrFilter = "All Files (*.*)\0*\0\0\0"; - of.lpstrCustomFilter = NULL; - of.nFilterIndex = 1; - of.lpstrFile = filename; - GetDlgItemText(dp->hwnd, c->base_id+1, filename, lenof(filename)); - filename[lenof(filename)-1] = '\0'; - of.nMaxFile = lenof(filename); - of.lpstrFileTitle = NULL; - of.lpstrTitle = ctrl->fileselect.title; - of.Flags = 0; - if (request_file(NULL, &of, false, ctrl->fileselect.for_writing)) { - SetDlgItemText(dp->hwnd, c->base_id + 1, filename); - ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE); - } - } - break; - case CTRL_FONTSELECT: - if (msg == WM_COMMAND && id == 2 && - (HIWORD(wParam) == BN_SETFOCUS || HIWORD(wParam) == BN_KILLFOCUS)) - winctrl_set_focus(ctrl, dp, HIWORD(wParam) == BN_SETFOCUS); - if (id == 2 && - (msg == WM_COMMAND && - (HIWORD(wParam) == BN_CLICKED || - HIWORD(wParam) == BN_DOUBLECLICKED))) { - CHOOSEFONT cf; - LOGFONT lf; - HDC hdc; - FontSpec *fs = (FontSpec *)c->data; - - hdc = GetDC(0); - lf.lfHeight = -MulDiv(fs->height, - GetDeviceCaps(hdc, LOGPIXELSY), 72); - ReleaseDC(0, hdc); - lf.lfWidth = lf.lfEscapement = lf.lfOrientation = 0; - lf.lfItalic = lf.lfUnderline = lf.lfStrikeOut = 0; - lf.lfWeight = (fs->isbold ? FW_BOLD : 0); - lf.lfCharSet = fs->charset; - lf.lfOutPrecision = OUT_DEFAULT_PRECIS; - lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; - lf.lfQuality = DEFAULT_QUALITY; - lf.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE; - strncpy(lf.lfFaceName, fs->name, - sizeof(lf.lfFaceName) - 1); - lf.lfFaceName[sizeof(lf.lfFaceName) - 1] = '\0'; - - cf.lStructSize = sizeof(cf); - cf.hwndOwner = dp->hwnd; - cf.lpLogFont = &lf; - cf.Flags = (dp->fixed_pitch_fonts ? CF_FIXEDPITCHONLY : 0) | - CF_FORCEFONTEXIST | CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS; - - if (ChooseFont(&cf)) { - fs = fontspec_new(lf.lfFaceName, (lf.lfWeight == FW_BOLD), - cf.iPointSize / 10, lf.lfCharSet); - dlg_fontsel_set(ctrl, dp, fs); - fontspec_free(fs); - - ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE); - } - } - break; - } - - /* - * If the above event handler has asked for a colour selector, - * now is the time to generate one. - */ - if (dp->coloursel_wanted) { - static CHOOSECOLOR cc; - static DWORD custom[16] = { 0 }; /* zero initialisers */ - cc.lStructSize = sizeof(cc); - cc.hwndOwner = dp->hwnd; - cc.hInstance = (HWND) hinst; - cc.lpCustColors = custom; - cc.rgbResult = RGB(dp->coloursel_result.r, - dp->coloursel_result.g, - dp->coloursel_result.b); - cc.Flags = CC_FULLOPEN | CC_RGBINIT; - if (ChooseColor(&cc)) { - dp->coloursel_result.r = - (unsigned char) (cc.rgbResult & 0xFF); - dp->coloursel_result.g = - (unsigned char) (cc.rgbResult >> 8) & 0xFF; - dp->coloursel_result.b = - (unsigned char) (cc.rgbResult >> 16) & 0xFF; - dp->coloursel_result.ok = true; - } else - dp->coloursel_result.ok = false; - ctrl->generic.handler(ctrl, dp, dp->data, EVENT_CALLBACK); - } - - return ret; -} - -/* - * This function can be called to produce context help on a - * control. Returns true if it has actually launched some help. - */ -bool winctrl_context_help(struct dlgparam *dp, HWND hwnd, int id) -{ - int i; - struct winctrl *c; - - /* - * Look up the control ID in our data. - */ - c = NULL; - for (i = 0; i < dp->nctrltrees; i++) { - c = winctrl_findbyid(dp->controltrees[i], id); - if (c) - break; - } - if (!c) - return false; /* we have nothing to do */ - - /* - * This is the Windows front end, so we're allowed to assume - * `helpctx.p' is a context string. - */ - if (!c->ctrl || !c->ctrl->generic.helpctx.p) - return false; /* no help available for this ctrl */ - - launch_help(hwnd, c->ctrl->generic.helpctx.p); - return true; -} - -/* - * Now the various functions that the platform-independent - * mechanism can call to access the dialog box entries. - */ - -static struct winctrl *dlg_findbyctrl(struct dlgparam *dp, union control *ctrl) -{ - int i; - - for (i = 0; i < dp->nctrltrees; i++) { - struct winctrl *c = winctrl_findbyctrl(dp->controltrees[i], ctrl); - if (c) - return c; - } - return NULL; -} - -bool dlg_is_visible(union control *ctrl, dlgparam *dp) -{ - /* - * In this implementation of the dialog box, we physically - * uncreate controls that aren't in a visible panel of the config - * box. So we can tell if a control is visible just by checking if - * it _exists_. - */ - return dlg_findbyctrl(dp, ctrl) != NULL; -} - -void dlg_radiobutton_set(union control *ctrl, dlgparam *dp, int whichbutton) -{ - struct winctrl *c = dlg_findbyctrl(dp, ctrl); - assert(c && c->ctrl->generic.type == CTRL_RADIO); - CheckRadioButton(dp->hwnd, - c->base_id + 1, - c->base_id + c->ctrl->radio.nbuttons, - c->base_id + 1 + whichbutton); -} - -int dlg_radiobutton_get(union control *ctrl, dlgparam *dp) -{ - struct winctrl *c = dlg_findbyctrl(dp, ctrl); - int i; - assert(c && c->ctrl->generic.type == CTRL_RADIO); - for (i = 0; i < c->ctrl->radio.nbuttons; i++) - if (IsDlgButtonChecked(dp->hwnd, c->base_id + 1 + i)) - return i; - unreachable("no radio button was checked"); -} - -void dlg_checkbox_set(union control *ctrl, dlgparam *dp, bool checked) -{ - struct winctrl *c = dlg_findbyctrl(dp, ctrl); - assert(c && c->ctrl->generic.type == CTRL_CHECKBOX); - CheckDlgButton(dp->hwnd, c->base_id, checked); -} - -bool dlg_checkbox_get(union control *ctrl, dlgparam *dp) -{ - struct winctrl *c = dlg_findbyctrl(dp, ctrl); - assert(c && c->ctrl->generic.type == CTRL_CHECKBOX); - return 0 != IsDlgButtonChecked(dp->hwnd, c->base_id); -} - -void dlg_editbox_set(union control *ctrl, dlgparam *dp, char const *text) -{ - struct winctrl *c = dlg_findbyctrl(dp, ctrl); - assert(c && c->ctrl->generic.type == CTRL_EDITBOX); - SetDlgItemText(dp->hwnd, c->base_id+1, text); -} - -char *dlg_editbox_get(union control *ctrl, dlgparam *dp) -{ - struct winctrl *c = dlg_findbyctrl(dp, ctrl); - assert(c && c->ctrl->generic.type == CTRL_EDITBOX); - return GetDlgItemText_alloc(dp->hwnd, c->base_id+1); -} - -/* The `listbox' functions can also apply to combo boxes. */ -void dlg_listbox_clear(union control *ctrl, dlgparam *dp) -{ - struct winctrl *c = dlg_findbyctrl(dp, ctrl); - int msg; - assert(c && - (c->ctrl->generic.type == CTRL_LISTBOX || - (c->ctrl->generic.type == CTRL_EDITBOX && - c->ctrl->editbox.has_list))); - msg = (c->ctrl->generic.type==CTRL_LISTBOX && c->ctrl->listbox.height!=0 ? - LB_RESETCONTENT : CB_RESETCONTENT); - SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, 0, 0); -} - -void dlg_listbox_del(union control *ctrl, dlgparam *dp, int index) -{ - struct winctrl *c = dlg_findbyctrl(dp, ctrl); - int msg; - assert(c && - (c->ctrl->generic.type == CTRL_LISTBOX || - (c->ctrl->generic.type == CTRL_EDITBOX && - c->ctrl->editbox.has_list))); - msg = (c->ctrl->generic.type==CTRL_LISTBOX && c->ctrl->listbox.height!=0 ? - LB_DELETESTRING : CB_DELETESTRING); - SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, index, 0); -} - -void dlg_listbox_add(union control *ctrl, dlgparam *dp, char const *text) -{ - struct winctrl *c = dlg_findbyctrl(dp, ctrl); - int msg; - assert(c && - (c->ctrl->generic.type == CTRL_LISTBOX || - (c->ctrl->generic.type == CTRL_EDITBOX && - c->ctrl->editbox.has_list))); - msg = (c->ctrl->generic.type==CTRL_LISTBOX && c->ctrl->listbox.height!=0 ? - LB_ADDSTRING : CB_ADDSTRING); - SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, 0, (LPARAM)text); -} - -/* - * Each listbox entry may have a numeric id associated with it. - * Note that some front ends only permit a string to be stored at - * each position, which means that _if_ you put two identical - * strings in any listbox then you MUST not assign them different - * IDs and expect to get meaningful results back. - */ -void dlg_listbox_addwithid(union control *ctrl, dlgparam *dp, - char const *text, int id) -{ - struct winctrl *c = dlg_findbyctrl(dp, ctrl); - int msg, msg2, index; - assert(c && - (c->ctrl->generic.type == CTRL_LISTBOX || - (c->ctrl->generic.type == CTRL_EDITBOX && - c->ctrl->editbox.has_list))); - msg = (c->ctrl->generic.type==CTRL_LISTBOX && c->ctrl->listbox.height!=0 ? - LB_ADDSTRING : CB_ADDSTRING); - msg2 = (c->ctrl->generic.type==CTRL_LISTBOX && c->ctrl->listbox.height!=0 ? - LB_SETITEMDATA : CB_SETITEMDATA); - index = SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, 0, (LPARAM)text); - SendDlgItemMessage(dp->hwnd, c->base_id+1, msg2, index, (LPARAM)id); -} - -int dlg_listbox_getid(union control *ctrl, dlgparam *dp, int index) -{ - struct winctrl *c = dlg_findbyctrl(dp, ctrl); - int msg; - assert(c && c->ctrl->generic.type == CTRL_LISTBOX); - msg = (c->ctrl->listbox.height != 0 ? LB_GETITEMDATA : CB_GETITEMDATA); - return - SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, index, 0); -} - -/* dlg_listbox_index returns <0 if no single element is selected. */ -int dlg_listbox_index(union control *ctrl, dlgparam *dp) -{ - struct winctrl *c = dlg_findbyctrl(dp, ctrl); - int msg, ret; - assert(c && c->ctrl->generic.type == CTRL_LISTBOX); - if (c->ctrl->listbox.multisel) { - assert(c->ctrl->listbox.height != 0); /* not combo box */ - ret = SendDlgItemMessage(dp->hwnd, c->base_id+1, LB_GETSELCOUNT, 0, 0); - if (ret == LB_ERR || ret > 1) - return -1; - } - msg = (c->ctrl->listbox.height != 0 ? LB_GETCURSEL : CB_GETCURSEL); - ret = SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, 0, 0); - if (ret == LB_ERR) - return -1; - else - return ret; -} - -bool dlg_listbox_issel(union control *ctrl, dlgparam *dp, int index) -{ - struct winctrl *c = dlg_findbyctrl(dp, ctrl); - assert(c && c->ctrl->generic.type == CTRL_LISTBOX && - c->ctrl->listbox.multisel && - c->ctrl->listbox.height != 0); - return - SendDlgItemMessage(dp->hwnd, c->base_id+1, LB_GETSEL, index, 0); -} - -void dlg_listbox_select(union control *ctrl, dlgparam *dp, int index) -{ - struct winctrl *c = dlg_findbyctrl(dp, ctrl); - int msg; - assert(c && c->ctrl->generic.type == CTRL_LISTBOX && - !c->ctrl->listbox.multisel); - msg = (c->ctrl->listbox.height != 0 ? LB_SETCURSEL : CB_SETCURSEL); - SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, index, 0); -} - -void dlg_text_set(union control *ctrl, dlgparam *dp, char const *text) -{ - struct winctrl *c = dlg_findbyctrl(dp, ctrl); - assert(c && c->ctrl->generic.type == CTRL_TEXT); - SetDlgItemText(dp->hwnd, c->base_id, text); -} - -void dlg_label_change(union control *ctrl, dlgparam *dp, char const *text) -{ - struct winctrl *c = dlg_findbyctrl(dp, ctrl); - char *escaped = NULL; - int id = -1; - - assert(c); - switch (c->ctrl->generic.type) { - case CTRL_EDITBOX: - escaped = shortcut_escape(text, c->ctrl->editbox.shortcut); - id = c->base_id; - break; - case CTRL_RADIO: - escaped = shortcut_escape(text, c->ctrl->radio.shortcut); - id = c->base_id; - break; - case CTRL_CHECKBOX: - escaped = shortcut_escape(text, ctrl->checkbox.shortcut); - id = c->base_id; - break; - case CTRL_BUTTON: - escaped = shortcut_escape(text, ctrl->button.shortcut); - id = c->base_id; - break; - case CTRL_LISTBOX: - escaped = shortcut_escape(text, ctrl->listbox.shortcut); - id = c->base_id; - break; - case CTRL_FILESELECT: - escaped = shortcut_escape(text, ctrl->fileselect.shortcut); - id = c->base_id; - break; - case CTRL_FONTSELECT: - escaped = shortcut_escape(text, ctrl->fontselect.shortcut); - id = c->base_id; - break; - default: - unreachable("bad control type in label_change"); - } - if (escaped) { - SetDlgItemText(dp->hwnd, id, escaped); - sfree(escaped); - } -} - -void dlg_filesel_set(union control *ctrl, dlgparam *dp, Filename *fn) -{ - struct winctrl *c = dlg_findbyctrl(dp, ctrl); - assert(c && c->ctrl->generic.type == CTRL_FILESELECT); - SetDlgItemText(dp->hwnd, c->base_id+1, fn->path); -} - -Filename *dlg_filesel_get(union control *ctrl, dlgparam *dp) -{ - struct winctrl *c = dlg_findbyctrl(dp, ctrl); - char *tmp; - Filename *ret; - assert(c && c->ctrl->generic.type == CTRL_FILESELECT); - tmp = GetDlgItemText_alloc(dp->hwnd, c->base_id+1); - ret = filename_from_str(tmp); - sfree(tmp); - return ret; -} - -void dlg_fontsel_set(union control *ctrl, dlgparam *dp, FontSpec *fs) -{ - char *buf, *boldstr; - struct winctrl *c = dlg_findbyctrl(dp, ctrl); - assert(c && c->ctrl->generic.type == CTRL_FONTSELECT); - - fontspec_free((FontSpec *)c->data); - c->data = fontspec_copy(fs); - - boldstr = (fs->isbold ? "bold, " : ""); - if (fs->height == 0) - buf = dupprintf("Font: %s, %sdefault height", fs->name, boldstr); - else - buf = dupprintf("Font: %s, %s%d-%s", fs->name, boldstr, - (fs->height < 0 ? -fs->height : fs->height), - (fs->height < 0 ? "pixel" : "point")); - SetDlgItemText(dp->hwnd, c->base_id+1, buf); - sfree(buf); - - dlg_auto_set_fixed_pitch_flag(dp); -} - -FontSpec *dlg_fontsel_get(union control *ctrl, dlgparam *dp) -{ - struct winctrl *c = dlg_findbyctrl(dp, ctrl); - assert(c && c->ctrl->generic.type == CTRL_FONTSELECT); - return fontspec_copy((FontSpec *)c->data); -} - -/* - * Bracketing a large set of updates in these two functions will - * cause the front end (if possible) to delay updating the screen - * until it's all complete, thus avoiding flicker. - */ -void dlg_update_start(union control *ctrl, dlgparam *dp) -{ - struct winctrl *c = dlg_findbyctrl(dp, ctrl); - if (c && c->ctrl->generic.type == CTRL_LISTBOX) { - SendDlgItemMessage(dp->hwnd, c->base_id+1, WM_SETREDRAW, false, 0); - } -} - -void dlg_update_done(union control *ctrl, dlgparam *dp) -{ - struct winctrl *c = dlg_findbyctrl(dp, ctrl); - if (c && c->ctrl->generic.type == CTRL_LISTBOX) { - HWND hw = GetDlgItem(dp->hwnd, c->base_id+1); - SendMessage(hw, WM_SETREDRAW, true, 0); - InvalidateRect(hw, NULL, true); - } -} - -void dlg_set_focus(union control *ctrl, dlgparam *dp) -{ - struct winctrl *c = dlg_findbyctrl(dp, ctrl); - int id; - HWND ctl; - if (!c) - return; - switch (ctrl->generic.type) { - case CTRL_EDITBOX: id = c->base_id + 1; break; - case CTRL_RADIO: - for (id = c->base_id + ctrl->radio.nbuttons; id > 1; id--) - if (IsDlgButtonChecked(dp->hwnd, id)) - break; - /* - * In the theoretically-unlikely case that no button was - * selected, id should come out of this as 1, which is a - * reasonable enough choice. - */ - break; - case CTRL_CHECKBOX: id = c->base_id; break; - case CTRL_BUTTON: id = c->base_id; break; - case CTRL_LISTBOX: id = c->base_id + 1; break; - case CTRL_FILESELECT: id = c->base_id + 1; break; - case CTRL_FONTSELECT: id = c->base_id + 2; break; - default: id = c->base_id; break; - } - ctl = GetDlgItem(dp->hwnd, id); - SetFocus(ctl); -} - -/* - * During event processing, you might well want to give an error - * indication to the user. dlg_beep() is a quick and easy generic - * error; dlg_error() puts up a message-box or equivalent. - */ -void dlg_beep(dlgparam *dp) -{ - MessageBeep(0); -} - -void dlg_error_msg(dlgparam *dp, const char *msg) -{ - MessageBox(dp->hwnd, msg, - dp->errtitle ? dp->errtitle : NULL, - MB_OK | MB_ICONERROR); -} - -/* - * This function signals to the front end that the dialog's - * processing is completed, and passes an integer value (typically - * a success status). - */ -void dlg_end(dlgparam *dp, int value) -{ - dp->ended = true; - dp->endresult = value; -} - -void dlg_refresh(union control *ctrl, dlgparam *dp) -{ - int i, j; - struct winctrl *c; - - if (!ctrl) { - /* - * Send EVENT_REFRESH to absolutely everything. - */ - for (j = 0; j < dp->nctrltrees; j++) { - for (i = 0; - (c = winctrl_findbyindex(dp->controltrees[j], i)) != NULL; - i++) { - if (c->ctrl && c->ctrl->generic.handler != NULL) - c->ctrl->generic.handler(c->ctrl, dp, - dp->data, EVENT_REFRESH); - } - } - } else { - /* - * Send EVENT_REFRESH to a specific control. - */ - if (ctrl->generic.handler != NULL) - ctrl->generic.handler(ctrl, dp, dp->data, EVENT_REFRESH); - } -} - -void dlg_coloursel_start(union control *ctrl, dlgparam *dp, int r, int g, int b) -{ - dp->coloursel_wanted = true; - dp->coloursel_result.r = r; - dp->coloursel_result.g = g; - dp->coloursel_result.b = b; -} - -bool dlg_coloursel_results(union control *ctrl, dlgparam *dp, - int *r, int *g, int *b) -{ - if (dp->coloursel_result.ok) { - *r = dp->coloursel_result.r; - *g = dp->coloursel_result.g; - *b = dp->coloursel_result.b; - return true; - } else - return false; -} - -void dlg_auto_set_fixed_pitch_flag(dlgparam *dp) -{ - Conf *conf = (Conf *)dp->data; - FontSpec *fs; - int quality; - HFONT hfont; - HDC hdc; - TEXTMETRIC tm; - bool is_var; - - /* - * Attempt to load the current font, and see if it's - * variable-pitch. If so, start off the fixed-pitch flag for the - * dialog box as false. - * - * We assume here that any client of the dlg_* mechanism which is - * using font selectors at all is also using a normal 'Conf *' - * as dp->data. - */ - - quality = conf_get_int(conf, CONF_font_quality); - fs = conf_get_fontspec(conf, CONF_font); - - hfont = CreateFont(0, 0, 0, 0, FW_DONTCARE, false, false, false, - DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, - CLIP_DEFAULT_PRECIS, FONT_QUALITY(quality), - FIXED_PITCH | FF_DONTCARE, fs->name); - hdc = GetDC(NULL); - if (hdc && SelectObject(hdc, hfont) && GetTextMetrics(hdc, &tm)) { - /* Note that the TMPF_FIXED_PITCH bit is defined upside down :-( */ - is_var = (tm.tmPitchAndFamily & TMPF_FIXED_PITCH); - } else { - is_var = false; /* assume it's basically normal */ - } - if (hdc) - ReleaseDC(NULL, hdc); - if (hfont) - DeleteObject(hfont); - - if (is_var) - dp->fixed_pitch_fonts = false; -} - -bool dlg_get_fixed_pitch_flag(dlgparam *dp) -{ - return dp->fixed_pitch_fonts; -} - -void dlg_set_fixed_pitch_flag(dlgparam *dp, bool flag) -{ - dp->fixed_pitch_fonts = flag; -} - -void dp_init(struct dlgparam *dp) -{ - dp->nctrltrees = 0; - dp->data = NULL; - dp->ended = false; - dp->focused = dp->lastfocused = NULL; - memset(dp->shortcuts, 0, sizeof(dp->shortcuts)); - dp->hwnd = NULL; - dp->wintitle = dp->errtitle = NULL; - dp->fixed_pitch_fonts = true; -} - -void dp_add_tree(struct dlgparam *dp, struct winctrls *wc) -{ - assert(dp->nctrltrees < lenof(dp->controltrees)); - dp->controltrees[dp->nctrltrees++] = wc; -} - -void dp_cleanup(struct dlgparam *dp) -{ - sfree(dp->wintitle); - sfree(dp->errtitle); -} diff --git a/WINDOWS/WINDEFS.C b/WINDOWS/WINDEFS.C deleted file mode 100644 index 006e8dc5..00000000 --- a/WINDOWS/WINDEFS.C +++ /dev/null @@ -1,40 +0,0 @@ -/* - * windefs.c: default settings that are specific to Windows. - */ - -#include "putty.h" - -#include <commctrl.h> - -FontSpec *platform_default_fontspec(const char *name) -{ - if (!strcmp(name, "Font")) - return fontspec_new("Courier New", false, 10, ANSI_CHARSET); - else - return fontspec_new("", false, 0, 0); -} - -Filename *platform_default_filename(const char *name) -{ - if (!strcmp(name, "LogFileName")) - return filename_from_str("putty.log"); - else - return filename_from_str(""); -} - -char *platform_default_s(const char *name) -{ - if (!strcmp(name, "SerialLine")) - return dupstr("COM1"); - return NULL; -} - -bool platform_default_b(const char *name, bool def) -{ - return def; -} - -int platform_default_i(const char *name, int def) -{ - return def; -} diff --git a/WINDOWS/WINDLG.C b/WINDOWS/WINDLG.C deleted file mode 100644 index 9c5fdb76..00000000 --- a/WINDOWS/WINDLG.C +++ /dev/null @@ -1,1156 +0,0 @@ -/* - * windlg.c - dialogs for PuTTY(tel), including the configuration dialog. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <limits.h> -#include <assert.h> -#include <ctype.h> -#include <time.h> - -#include "putty.h" -#include "ssh.h" -#include "win_res.h" -#include "winseat.h" -#include "storage.h" -#include "dialog.h" -#include "licence.h" - -#include <commctrl.h> -#include <commdlg.h> -#include <shellapi.h> - -#ifdef MSVC4 -#define TVINSERTSTRUCT TV_INSERTSTRUCT -#define TVITEM TV_ITEM -#define ICON_BIG 1 -#endif - -/* - * 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; - -#define LOGEVENT_INITIAL_MAX 128 -#define LOGEVENT_CIRCULAR_MAX 128 - -static char *events_initial[LOGEVENT_INITIAL_MAX]; -static char *events_circular[LOGEVENT_CIRCULAR_MAX]; -static int ninitial = 0, ncircular = 0, circular_first = 0; - -#define PRINTER_DISABLED_STRING "None (printing disabled)" - -void force_normal(HWND hwnd) -{ - static bool recurse = false; - - WINDOWPLACEMENT wp; - - if (recurse) - return; - recurse = true; - - wp.length = sizeof(wp); - if (GetWindowPlacement(hwnd, &wp) && wp.showCmd == SW_SHOWMAXIMIZED) { - wp.showCmd = SW_SHOWNORMAL; - SetWindowPlacement(hwnd, &wp); - } - recurse = false; -} - -static char *getevent(int i) -{ - if (i < ninitial) - return events_initial[i]; - if ((i -= ninitial) < ncircular) - return events_circular[(circular_first + i) % LOGEVENT_CIRCULAR_MAX]; - return NULL; -} - -static HWND logbox; -HWND event_log_window(void) { return logbox; } - -static INT_PTR CALLBACK LogProc(HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) -{ - int i; - - switch (msg) { - case WM_INITDIALOG: { - char *str = dupprintf("%s Event Log", appname); - SetWindowText(hwnd, str); - sfree(str); - - static int tabs[4] = { 78, 108 }; - SendDlgItemMessage(hwnd, IDN_LIST, LB_SETTABSTOPS, 2, - (LPARAM) tabs); - - for (i = 0; i < ninitial; i++) - SendDlgItemMessage(hwnd, IDN_LIST, LB_ADDSTRING, - 0, (LPARAM) events_initial[i]); - for (i = 0; i < ncircular; i++) - SendDlgItemMessage(hwnd, IDN_LIST, LB_ADDSTRING, - 0, (LPARAM) events_circular[(circular_first + i) % LOGEVENT_CIRCULAR_MAX]); - return 1; - } - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDOK: - case IDCANCEL: - logbox = NULL; - SetActiveWindow(GetParent(hwnd)); - DestroyWindow(hwnd); - return 0; - case IDN_COPY: - if (HIWORD(wParam) == BN_CLICKED || - HIWORD(wParam) == BN_DOUBLECLICKED) { - int selcount; - int *selitems; - selcount = SendDlgItemMessage(hwnd, IDN_LIST, - LB_GETSELCOUNT, 0, 0); - if (selcount == 0) { /* don't even try to copy zero items */ - MessageBeep(0); - break; - } - - selitems = snewn(selcount, int); - if (selitems) { - int count = SendDlgItemMessage(hwnd, IDN_LIST, - LB_GETSELITEMS, - selcount, - (LPARAM) selitems); - int i; - int size; - char *clipdata; - static unsigned char sel_nl[] = SEL_NL; - - if (count == 0) { /* can't copy zero stuff */ - MessageBeep(0); - break; - } - - size = 0; - for (i = 0; i < count; i++) - size += - strlen(getevent(selitems[i])) + sizeof(sel_nl); - - clipdata = snewn(size, char); - if (clipdata) { - char *p = clipdata; - for (i = 0; i < count; i++) { - char *q = getevent(selitems[i]); - int qlen = strlen(q); - memcpy(p, q, qlen); - p += qlen; - memcpy(p, sel_nl, sizeof(sel_nl)); - p += sizeof(sel_nl); - } - write_aclip(CLIP_SYSTEM, clipdata, size, true); - sfree(clipdata); - } - sfree(selitems); - - for (i = 0; i < (ninitial + ncircular); i++) - SendDlgItemMessage(hwnd, IDN_LIST, LB_SETSEL, - false, i); - } - } - return 0; - } - return 0; - case WM_CLOSE: - logbox = NULL; - SetActiveWindow(GetParent(hwnd)); - DestroyWindow(hwnd); - return 0; - } - return 0; -} - -static INT_PTR CALLBACK LicenceProc(HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_INITDIALOG: { - char *str = dupprintf("%s Licence", appname); - SetWindowText(hwnd, str); - sfree(str); - SetDlgItemText(hwnd, IDA_TEXT, LICENCE_TEXT("\r\n\r\n")); - return 1; - } - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDOK: - case IDCANCEL: - EndDialog(hwnd, 1); - return 0; - } - return 0; - case WM_CLOSE: - EndDialog(hwnd, 1); - return 0; - } - return 0; -} - -static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) -{ - char *str; - - switch (msg) { - case WM_INITDIALOG: { - str = dupprintf("About %s", appname); - SetWindowText(hwnd, str); - sfree(str); - char *buildinfo_text = buildinfo("\r\n"); - char *text = dupprintf - ("%s\r\n\r\n%s\r\n\r\n%s\r\n\r\n%s", - appname, ver, buildinfo_text, - "\251 " SHORT_COPYRIGHT_DETAILS ". All rights reserved."); - sfree(buildinfo_text); - SetDlgItemText(hwnd, IDA_TEXT, text); - MakeDlgItemBorderless(hwnd, IDA_TEXT); - sfree(text); - return 1; - } - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDOK: - case IDCANCEL: - EndDialog(hwnd, true); - return 0; - case IDA_LICENCE: - EnableWindow(hwnd, 0); - DialogBox(hinst, MAKEINTRESOURCE(IDD_LICENCEBOX), - hwnd, LicenceProc); - EnableWindow(hwnd, 1); - SetActiveWindow(hwnd); - return 0; - - case IDA_WEB: - /* Load web browser */ - ShellExecute(hwnd, "open", - "https://www.chiark.greenend.org.uk/~sgtatham/putty/", - 0, 0, SW_SHOWDEFAULT); - return 0; - } - return 0; - case WM_CLOSE: - EndDialog(hwnd, true); - return 0; - } - return 0; -} - -static int SaneDialogBox(HINSTANCE hinst, - LPCTSTR tmpl, - HWND hwndparent, - DLGPROC lpDialogFunc) -{ - WNDCLASS wc; - HWND hwnd; - MSG msg; - int flags; - int ret; - int gm; - - wc.style = CS_DBLCLKS | CS_SAVEBITS | CS_BYTEALIGNWINDOW; - wc.lpfnWndProc = DefDlgProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = DLGWINDOWEXTRA + 2*sizeof(LONG_PTR); - wc.hInstance = hinst; - wc.hIcon = NULL; - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.hbrBackground = (HBRUSH) (COLOR_BACKGROUND +1); - wc.lpszMenuName = NULL; - wc.lpszClassName = "PuTTYConfigBox"; - RegisterClass(&wc); - - hwnd = CreateDialog(hinst, tmpl, hwndparent, lpDialogFunc); - - SetWindowLongPtr(hwnd, BOXFLAGS, 0); /* flags */ - SetWindowLongPtr(hwnd, BOXRESULT, 0); /* result from SaneEndDialog */ - - while ((gm=GetMessage(&msg, NULL, 0, 0)) > 0) { - flags=GetWindowLongPtr(hwnd, BOXFLAGS); - if (!(flags & DF_END) && !IsDialogMessage(hwnd, &msg)) - DispatchMessage(&msg); - if (flags & DF_END) - break; - } - - if (gm == 0) - PostQuitMessage(msg.wParam); /* We got a WM_QUIT, pass it on */ - - ret=GetWindowLongPtr(hwnd, BOXRESULT); - DestroyWindow(hwnd); - return ret; -} - -static void SaneEndDialog(HWND hwnd, int ret) -{ - SetWindowLongPtr(hwnd, BOXRESULT, ret); - SetWindowLongPtr(hwnd, BOXFLAGS, DF_END); -} - -/* - * Null dialog procedure. - */ -static INT_PTR CALLBACK NullDlgProc(HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) -{ - return 0; -} - -enum { - IDCX_ABOUT = IDC_ABOUT, - IDCX_TVSTATIC, - IDCX_TREEVIEW, - IDCX_STDBASE, - IDCX_PANELBASE = IDCX_STDBASE + 32 -}; - -struct treeview_faff { - HWND treeview; - HTREEITEM lastat[4]; -}; - -static HTREEITEM treeview_insert(struct treeview_faff *faff, - int level, char *text, char *path) -{ - TVINSERTSTRUCT ins; - int i; - HTREEITEM newitem; - ins.hParent = (level > 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], - (level > 1 ? TVE_COLLAPSE : 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, 100, 3, 13); - wc = &ctrls_panel; - base_id = IDCX_PANELBASE; - } - - 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); - } -} - -/* - * This function is the configuration box. - * (Being a dialog procedure, in general it returns 0 if the default - * dialog processing should be performed, and 1 if it should not.) - */ -static INT_PTR CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) -{ - HWND hw, treeview; - struct treeview_faff tvfaff; - int ret; - - switch (msg) { - case WM_INITDIALOG: - dp.hwnd = hwnd; - create_controls(hwnd, ""); /* Open and Cancel buttons etc */ - SetWindowText(hwnd, dp.wintitle); - SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); - if (has_help()) - SetWindowLongPtr(hwnd, GWL_EXSTYLE, - GetWindowLongPtr(hwnd, GWL_EXSTYLE) | - WS_EX_CONTEXTHELP); - else { - HWND item = GetDlgItem(hwnd, IDC_HELPBTN); - if (item) - DestroyWindow(item); - } - SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG, - (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(IDI_CFGICON))); - /* - * Centre the window. - */ - { /* centre the window */ - RECT rs, rd; - - 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); - } - - /* - * Create the tree view. - */ - { - RECT r; - WPARAM font; - HWND tvstatic; - - r.left = 3; - r.right = r.left + 95; - 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 + 95; - 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)); - } - - /* - * Set up the tree view contents. - */ - { - HTREEITEM hfirst = NULL; - int i; - char *path = NULL; - char *firstpath = NULL; - - for (i = 0; i < ctrlbox->nctrlsets; i++) { - struct controlset *s = ctrlbox->ctrlsets[i]; - HTREEITEM item; - int j; - char *c; - - 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 */ - - /* - * 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); - - c = strrchr(s->pathname, '/'); - if (!c) - c = s->pathname; - else - c++; - - item = treeview_insert(&tvfaff, j, c, s->pathname); - if (!hfirst) { - hfirst = item; - firstpath = s->pathname; - } - - path = s->pathname; - } - - /* - * Put the treeview selection on to the first panel in the - * ctrlbox. - */ - TreeView_SelectItem(treeview, hfirst); - - /* - * And create the actual control set for that panel, to - * match the initial treeview selection. - */ - assert(firstpath); /* config.c must have given us _something_ */ - create_controls(hwnd, firstpath); - dlg_refresh(NULL, &dp); /* and set up control values */ - } - - /* - * Set focus into the first available control. - */ - { - int i; - struct winctrl *c; - - for (i = 0; (c = winctrl_findbyindex(&ctrls_panel, i)) != NULL; - i++) { - if (c->ctrl) { - dlg_set_focus(c->ctrl, &dp); - break; - } - } - } - - /* - * Now we've finished creating our initial set of controls, - * it's safe to actually show the window without risking setup - * flicker. - */ - ShowWindow(hwnd, SW_SHOWNORMAL); - - /* - * Set the flag that activates a couple of the other message - * handlers below, which were disabled until now to avoid - * spurious firing during the above setup procedure. - */ - SetWindowLongPtr(hwnd, GWLP_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) == IDCX_TREEVIEW && - ((LPNMHDR) lParam)->code == TVN_SELCHANGED) { - /* - * Selection-change events on the treeview cause us to do - * a flurry of control deletion and creation - but only - * after WM_INITDIALOG has finished. The initial - * selection-change event(s) during treeview setup are - * ignored. - */ - HTREEITEM i; - TVITEM item; - char buffer[64]; - - if (GetWindowLongPtr(hwnd, GWLP_USERDATA) != 1) - return 0; - - i = TreeView_GetSelection(((LPNMHDR) lParam)->hwndFrom); - - 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_COMMAND: - case WM_DRAWITEM: - default: /* also handle drag list msg here */ - /* - * Only process WM_COMMAND once the dialog is fully formed. - */ - if (GetWindowLongPtr(hwnd, GWLP_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 (!winctrl_context_help(&dp, hwnd, - ((LPHELPINFO)lParam)->iCtrlId)) - MessageBeep(0); - break; - case WM_CLOSE: - quit_help(hwnd); - SaneEndDialog(hwnd, 0); - return 0; - - /* Grrr Explorer will maximize Dialogs! */ - case WM_SIZE: - if (wParam == SIZE_MAXIMIZED) - force_normal(hwnd); - return 0; - - } - return 0; -} - -void modal_about_box(HWND hwnd) -{ - EnableWindow(hwnd, 0); - DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc); - EnableWindow(hwnd, 1); - SetActiveWindow(hwnd); -} - -void show_help(HWND hwnd) -{ - launch_help(hwnd, NULL); -} - -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); - } -} - -bool do_config(Conf *conf) -{ - bool ret; - - ctrlbox = ctrl_new_box(); - setup_config_box(ctrlbox, false, 0, 0); - win_setup_config_box(ctrlbox, &dp.hwnd, has_help(), false, 0); - 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 = conf; - dlg_auto_set_fixed_pitch_flag(&dp); - dp.shortcuts['g'] = true; /* the treeview: `Cate&gory' */ - - ret = - SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL, - GenericMainDlgProc); - - ctrl_free_box(ctrlbox); - winctrl_cleanup(&ctrls_panel); - winctrl_cleanup(&ctrls_base); - dp_cleanup(&dp); - - return ret; -} - -bool do_reconfig(HWND hwnd, Conf *conf, int protcfginfo) -{ - Conf *backup_conf; - bool ret; - int protocol; - - backup_conf = conf_copy(conf); - - ctrlbox = ctrl_new_box(); - protocol = conf_get_int(conf, CONF_protocol); - setup_config_box(ctrlbox, true, protocol, protcfginfo); - win_setup_config_box(ctrlbox, &dp.hwnd, has_help(), true, protocol); - 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 = conf; - dlg_auto_set_fixed_pitch_flag(&dp); - 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) - conf_copy_into(conf, backup_conf); - - conf_free(backup_conf); - - return ret; -} - -static void win_gui_eventlog(LogPolicy *lp, const char *string) -{ - char timebuf[40]; - char **location; - struct tm tm; - - tm=ltime(); - strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t", &tm); - - if (ninitial < LOGEVENT_INITIAL_MAX) - location = &events_initial[ninitial]; - else - location = &events_circular[(circular_first + ncircular) % LOGEVENT_CIRCULAR_MAX]; - - if (*location) - sfree(*location); - *location = dupcat(timebuf, string); - if (logbox) { - int count; - SendDlgItemMessage(logbox, IDN_LIST, LB_ADDSTRING, - 0, (LPARAM) *location); - count = SendDlgItemMessage(logbox, IDN_LIST, LB_GETCOUNT, 0, 0); - SendDlgItemMessage(logbox, IDN_LIST, LB_SETTOPINDEX, count - 1, 0); - } - if (ninitial < LOGEVENT_INITIAL_MAX) { - ninitial++; - } else if (ncircular < LOGEVENT_CIRCULAR_MAX) { - ncircular++; - } else if (ncircular == LOGEVENT_CIRCULAR_MAX) { - circular_first = (circular_first + 1) % LOGEVENT_CIRCULAR_MAX; - sfree(events_circular[circular_first]); - events_circular[circular_first] = dupstr(".."); - } -} - -static void win_gui_logging_error(LogPolicy *lp, const char *event) -{ - WinGuiSeat *wgs = container_of(lp, WinGuiSeat, logpolicy); - - /* Send 'can't open log file' errors to the terminal window. - * (Marked as stderr, although terminal.c won't care.) */ - seat_stderr_pl(&wgs->seat, ptrlen_from_asciz(event)); - seat_stderr_pl(&wgs->seat, PTRLEN_LITERAL("\r\n")); -} - -void showeventlog(HWND hwnd) -{ - if (!logbox) { - logbox = CreateDialog(hinst, MAKEINTRESOURCE(IDD_LOGBOX), - hwnd, LogProc); - ShowWindow(logbox, SW_SHOWNORMAL); - } - SetActiveWindow(logbox); -} - -void showabout(HWND hwnd) -{ - DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc); -} - -struct hostkey_dialog_ctx { - const char *const *keywords; - const char *const *values; - FingerprintType fptype_default; - char **fingerprints; - const char *keydisp; - LPCTSTR iconid; - const char *helpctx; -}; - -static INT_PTR CALLBACK HostKeyMoreInfoProc(HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_INITDIALOG: { - const struct hostkey_dialog_ctx *ctx = - (const struct hostkey_dialog_ctx *)lParam; - SetWindowLongPtr(hwnd, GWLP_USERDATA, (INT_PTR)ctx); - - if (ctx->fingerprints[SSH_FPTYPE_SHA256]) - SetDlgItemText(hwnd, IDC_HKI_SHA256, - ctx->fingerprints[SSH_FPTYPE_SHA256]); - if (ctx->fingerprints[SSH_FPTYPE_MD5]) - SetDlgItemText(hwnd, IDC_HKI_MD5, - ctx->fingerprints[SSH_FPTYPE_MD5]); - - SetDlgItemText(hwnd, IDA_TEXT, ctx->keydisp); - - return 1; - } - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDOK: - EndDialog(hwnd, 0); - return 0; - } - return 0; - case WM_CLOSE: - EndDialog(hwnd, 0); - return 0; - } - return 0; -} - -static INT_PTR CALLBACK HostKeyDialogProc(HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_INITDIALOG: { - strbuf *sb = strbuf_new(); - const struct hostkey_dialog_ctx *ctx = - (const struct hostkey_dialog_ctx *)lParam; - SetWindowLongPtr(hwnd, GWLP_USERDATA, (INT_PTR)ctx); - for (int id = 100;; id++) { - char buf[256]; - - if (!GetDlgItemText(hwnd, id, buf, (int)lenof(buf))) - break; - - strbuf_clear(sb); - for (const char *p = buf; *p ;) { - if (*p == '{') { - for (size_t i = 0; ctx->keywords[i]; i++) { - if (strstartswith(p, ctx->keywords[i])) { - p += strlen(ctx->keywords[i]); - put_datapl(sb, ptrlen_from_asciz(ctx->values[i])); - goto matched; - } - } - } else { - put_byte(sb, *p++); - } - matched:; - } - - SetDlgItemText(hwnd, id, sb->s); - } - strbuf_free(sb); - - SetDlgItemText(hwnd, IDC_HK_FINGERPRINT, - ctx->fingerprints[ctx->fptype_default]); - MakeDlgItemBorderless(hwnd, IDC_HK_FINGERPRINT); - - HANDLE icon = LoadImage( - NULL, ctx->iconid, IMAGE_ICON, - GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), - LR_SHARED); - SendDlgItemMessage(hwnd, IDC_HK_ICON, STM_SETICON, (WPARAM)icon, 0); - - if (!has_help()) { - HWND item = GetDlgItem(hwnd, IDHELP); - if (item) - DestroyWindow(item); - } - - return 1; - } - case WM_CTLCOLORSTATIC: { - HDC hdc = (HDC)wParam; - HWND control = (HWND)lParam; - - if (GetWindowLongPtr(control, GWLP_ID) == IDC_HK_TITLE) { - SetBkMode(hdc, TRANSPARENT); - HFONT prev_font = (HFONT)SelectObject( - hdc, (HFONT)GetStockObject(SYSTEM_FONT)); - LOGFONT lf; - if (GetObject(prev_font, sizeof(lf), &lf)) { - lf.lfWeight = FW_BOLD; - lf.lfHeight = lf.lfHeight * 3 / 2; - HFONT bold_font = CreateFontIndirect(&lf); - if (bold_font) - SelectObject(hdc, bold_font); - } - return (INT_PTR)GetSysColorBrush(COLOR_BTNFACE); - } - return 0; - } - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDC_HK_ACCEPT: - case IDC_HK_ONCE: - case IDCANCEL: - EndDialog(hwnd, LOWORD(wParam)); - return 0; - case IDHELP: { - const struct hostkey_dialog_ctx *ctx = - (const struct hostkey_dialog_ctx *) - GetWindowLongPtr(hwnd, GWLP_USERDATA); - launch_help(hwnd, ctx->helpctx); - return 0; - } - case IDC_HK_MOREINFO: { - const struct hostkey_dialog_ctx *ctx = - (const struct hostkey_dialog_ctx *) - GetWindowLongPtr(hwnd, GWLP_USERDATA); - DialogBoxParam(hinst, MAKEINTRESOURCE(IDD_HK_MOREINFO), - hwnd, HostKeyMoreInfoProc, (LPARAM)ctx); - } - } - return 0; - case WM_CLOSE: - EndDialog(hwnd, IDCANCEL); - return 0; - } - return 0; -} - -int win_seat_verify_ssh_host_key( - Seat *seat, const char *host, int port, const char *keytype, - char *keystr, const char *keydisp, char **fingerprints, - void (*callback)(void *ctx, int result), void *ctx) -{ - int ret; - - WinGuiSeat *wgs = container_of(seat, WinGuiSeat, seat); - - /* - * Verify the key against the registry. - */ - ret = verify_host_key(host, port, keytype, keystr); - - if (ret == 0) /* success - key matched OK */ - return 1; - else { - static const char *const keywords[] = - { "{KEYTYPE}", "{APPNAME}", NULL }; - - const char *values[2]; - values[0] = keytype; - values[1] = appname; - - struct hostkey_dialog_ctx ctx[1]; - ctx->keywords = keywords; - ctx->values = values; - ctx->fingerprints = fingerprints; - ctx->fptype_default = ssh2_pick_default_fingerprint(fingerprints); - ctx->keydisp = keydisp; - ctx->iconid = (ret == 2 ? IDI_WARNING : IDI_QUESTION); - ctx->helpctx = (ret == 2 ? WINHELP_CTX_errors_hostkey_changed : - WINHELP_CTX_errors_hostkey_absent); - int dlgid = (ret == 2 ? IDD_HK_WRONG : IDD_HK_ABSENT); - int mbret = DialogBoxParam( - hinst, MAKEINTRESOURCE(dlgid), wgs->term_hwnd, - HostKeyDialogProc, (LPARAM)ctx); - assert(mbret==IDC_HK_ACCEPT || mbret==IDC_HK_ONCE || mbret==IDCANCEL); - if (mbret == IDC_HK_ACCEPT) { - store_host_key(host, port, keytype, keystr); - return 1; - } else if (mbret == IDC_HK_ONCE) - return 1; - } - return 0; /* abandon the connection */ -} - -/* - * Ask whether the selected algorithm is acceptable (since it was - * below the configured 'warn' threshold). - */ -int win_seat_confirm_weak_crypto_primitive( - Seat *seat, const char *algtype, const char *algname, - void (*callback)(void *ctx, int result), void *ctx) -{ - static const char mbtitle[] = "%s Security Alert"; - static const char msg[] = - "The first %s supported by the server\n" - "is %s, 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, algtype, algname); - title = dupprintf(mbtitle, appname); - mbret = MessageBox(NULL, message, title, - MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2); - socket_reselect_all(); - sfree(message); - sfree(title); - if (mbret == IDYES) - return 1; - else - return 0; -} - -int win_seat_confirm_weak_cached_hostkey( - Seat *seat, const char *algname, const char *betteralgs, - void (*callback)(void *ctx, int result), void *ctx) -{ - static const char mbtitle[] = "%s Security Alert"; - static const char msg[] = - "The first host key type we have stored for this server\n" - "is %s, which is below the configured warning threshold.\n" - "The server also provides the following types of host key\n" - "above the threshold, which we do not have stored:\n" - "%s\n" - "Do you want to continue with this connection?\n"; - char *message, *title; - int mbret; - - message = dupprintf(msg, algname, betteralgs); - title = dupprintf(mbtitle, appname); - mbret = MessageBox(NULL, message, title, - MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2); - socket_reselect_all(); - sfree(message); - sfree(title); - if (mbret == IDYES) - return 1; - else - return 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). - */ -static int win_gui_askappend(LogPolicy *lp, Filename *filename, - void (*callback)(void *ctx, int result), - void *ctx) -{ - 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 | MB_DEFBUTTON3); - - socket_reselect_all(); - - sfree(message); - sfree(mbtitle); - - if (mbret == IDYES) - return 2; - else if (mbret == IDNO) - return 1; - else - return 0; -} - -const LogPolicyVtable win_gui_logpolicy_vt = { - .eventlog = win_gui_eventlog, - .askappend = win_gui_askappend, - .logging_error = win_gui_logging_error, - .verbose = null_lp_verbose_yes, -}; - -/* - * 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); - - socket_reselect_all(); - - sfree(msg); - sfree(title); -} diff --git a/WINDOWS/WINDOW.C b/WINDOWS/WINDOW.C index 82bbe8e6..979e2ed3 100644 --- a/WINDOWS/WINDOW.C +++ b/WINDOWS/WINDOW.C @@ -121,11 +121,51 @@ static int offset_width, offset_height; static bool was_zoomed = false; static int prev_rows, prev_cols; +<<<<<<< HEAD static void flash_window(WinGuiSeat *wgs, int mode); static void sys_cursor_update(WinGuiSeat *wgs); static bool get_fullscreen_rect(WinGuiSeat *wgs, RECT *ss); static void conf_cache_data(WinGuiSeat *wgs); +======= +static void flash_window(int mode); +static void sys_cursor_update(void); +static bool get_fullscreen_rect(RECT *ss); + +static int caret_x = -1, caret_y = -1; + +static int kbd_codepage; + +static Ldisc *ldisc; +static Backend *backend; + +static cmdline_get_passwd_input_state cmdline_get_passwd_state; + +static struct unicode_data ucsdata; +static bool session_closed; +static bool reconfiguring = false; + +static const SessionSpecial *specials = NULL; +static HMENU specials_menu = NULL; +static int n_specials = 0; + +#define TIMING_TIMER_ID 1234 +static long timing_next_time; + +static struct { + HMENU menu; +} popup_menus[2]; +enum { SYSMENU, CTXMENU }; +static HMENU savedsess_menu; + +static Conf *conf; +static LogContext *logctx; +static Terminal *term; + +static void conf_cache_data(void); +static int cursor_type; +static int vtmode; +>>>>>>> tags/0.78 static struct sesslist sesslist; /* for saved-session menu */ @@ -139,6 +179,24 @@ DECL_WINDOWS_FUNCTION(static, HRESULT, AdjustWindowRectExForDpi, (LPRECT lpRect, static HBITMAP caretbm; +<<<<<<< HEAD +======= +static int dbltime, lasttime, lastact; +static Mouse_Button lastbtn; + +/* this allows xterm-style mouse handling. */ +static bool send_raw_mouse = false; +static int wheel_accumulator = 0; + +static bool pointer_indicates_raw_mouse = false; + +static BusyStatus busy_status = BUSY_NOT; + +static wchar_t *window_name, *icon_name; + +static int compose_state = 0; + +>>>>>>> tags/0.78 static UINT wm_mousewheel = WM_MOUSEWHEEL; struct WinGuiSeatListNode wgslisthead = { @@ -289,7 +347,13 @@ static void start_backend(WinGuiSeat *wgs) char *error, *realhost; int i; +<<<<<<< HEAD wgs->cmdline_get_passwd_state = cmdline_get_passwd_input_state_new; +======= + cmdline_get_passwd_state = cmdline_get_passwd_input_state_new; + + vt = backend_vt_from_conf(conf); +>>>>>>> tags/0.78 vt = backend_vt_from_conf(wgs->conf); @@ -308,7 +372,11 @@ static void start_backend(WinGuiSeat *wgs) msg = dupprintf("Unable to open terminal:\n%s", error); } else { msg = dupprintf("Unable to open connection to\n%s\n%s", +<<<<<<< HEAD conf_dest(wgs->conf), error); +======= + conf_dest(conf), error); +>>>>>>> tags/0.78 } sfree(error); MessageBox(NULL, msg, str, MB_ICONERROR | MB_OK); @@ -351,8 +419,13 @@ static void close_session(void *vctx) wgs->session_closed = true; newtitle = dupprintf("%s (inactive)", appname); +<<<<<<< HEAD win_set_icon_title(&wgs->termwin, newtitle, DEFAULT_CODEPAGE); win_set_title(&wgs->termwin, newtitle, DEFAULT_CODEPAGE); +======= + win_set_icon_title(wintw, newtitle, DEFAULT_CODEPAGE); + win_set_title(wintw, newtitle, DEFAULT_CODEPAGE); +>>>>>>> tags/0.78 sfree(newtitle); if (wgs->ldisc) { @@ -514,11 +587,19 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) /* * Process the command line. */ +<<<<<<< HEAD gui_term_process_cmdline(wgs->conf, cmdline); memset(&wgs->ucsdata, 0, sizeof(wgs->ucsdata)); conf_cache_data(wgs); +======= + gui_term_process_cmdline(conf, cmdline); + + memset(&ucsdata, 0, sizeof(ucsdata)); + + conf_cache_data(); +>>>>>>> tags/0.78 /* * Guess some defaults for the window size. This all gets @@ -553,9 +634,15 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) if (vt && vt->flags & BACKEND_RESIZE_FORBIDDEN) resize_forbidden = true; wchar_t *uappname = dup_mb_to_wc(DEFAULT_CODEPAGE, 0, appname); +<<<<<<< HEAD wgs->window_name = dup_mb_to_wc(DEFAULT_CODEPAGE, 0, appname); wgs->icon_name = dup_mb_to_wc(DEFAULT_CODEPAGE, 0, appname); if (!conf_get_bool(wgs->conf, CONF_scrollbar)) +======= + window_name = dup_mb_to_wc(DEFAULT_CODEPAGE, 0, appname); + icon_name = dup_mb_to_wc(DEFAULT_CODEPAGE, 0, appname); + if (!conf_get_bool(conf, CONF_scrollbar)) +>>>>>>> tags/0.78 winmode &= ~(WS_VSCROLL); if (conf_get_int(wgs->conf, CONF_resize_action) == RESIZE_DISABLED || resize_forbidden) @@ -568,40 +655,65 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) #ifdef TEST_ANSI_WINDOW /* For developer testing of ANSI window support, pretend * CreateWindowExW failed */ +<<<<<<< HEAD wgs->term_hwnd = NULL; +======= + wgs.term_hwnd = NULL; +>>>>>>> tags/0.78 SetLastError(ERROR_CALL_NOT_IMPLEMENTED); #else unicode_window = true; sw_PeekMessage = PeekMessageW; sw_DispatchMessage = DispatchMessageW; sw_DefWindowProc = DefWindowProcW; +<<<<<<< HEAD wgs->term_hwnd = CreateWindowExW( +======= + wgs.term_hwnd = CreateWindowExW( +>>>>>>> tags/0.78 exwinmode, terminal_window_class_w(), uappname, winmode, CW_USEDEFAULT, CW_USEDEFAULT, guess_width, guess_height, NULL, NULL, inst, NULL); #endif #if defined LEGACY_WINDOWS || defined TEST_ANSI_WINDOW +<<<<<<< HEAD if (!wgs->term_hwnd && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) { +======= + if (!wgs.term_hwnd && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) { +>>>>>>> tags/0.78 /* Fall back to an ANSI window, swapping in all the ANSI * window message handling functions */ unicode_window = false; sw_PeekMessage = PeekMessageA; sw_DispatchMessage = DispatchMessageA; sw_DefWindowProc = DefWindowProcA; +<<<<<<< HEAD wgs->term_hwnd = CreateWindowExA( +======= + wgs.term_hwnd = CreateWindowExA( +>>>>>>> tags/0.78 exwinmode, terminal_window_class_a(), appname, winmode, CW_USEDEFAULT, CW_USEDEFAULT, guess_width, guess_height, NULL, NULL, inst, NULL); } #endif +<<<<<<< HEAD if (!wgs->term_hwnd) { modalfatalbox("Unable to create terminal window: %s", win_strerror(GetLastError())); } memset(&wgs->dpi_info, 0, sizeof(struct _dpi_info)); init_dpi_info(wgs); +======= + if (!wgs.term_hwnd) { + modalfatalbox("Unable to create terminal window: %s", + win_strerror(GetLastError())); + } + memset(&dpi_info, 0, sizeof(struct _dpi_info)); + init_dpi_info(); +>>>>>>> tags/0.78 sfree(uappname); } @@ -759,6 +871,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) gui_terminal_ready(wgs->term_hwnd, &wgs->seat, wgs->backend); + gui_terminal_ready(wgs.term_hwnd, &wgs.seat, backend); + while (1) { int n; DWORD timeout; @@ -837,6 +951,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) return msg.wParam; /* ... but optimiser doesn't know */ } +<<<<<<< HEAD static void wgs_cleanup(WinGuiSeat *wgs) { deinit_fonts(wgs); @@ -847,6 +962,8 @@ static void wgs_cleanup(WinGuiSeat *wgs) sfree(wgs); } +======= +>>>>>>> tags/0.78 char *handle_restrict_acl_cmdline_prefix(char *p) { /* @@ -1345,11 +1462,19 @@ static int get_font_width(HDC hdc, const TEXTMETRIC *tm) static void init_dpi_info(WinGuiSeat *wgs) { +<<<<<<< HEAD if (wgs->dpi_info.cur_dpi.x == 0 || wgs->dpi_info.cur_dpi.y == 0) { if (p_GetDpiForMonitor && p_MonitorFromWindow) { UINT dpiX, dpiY; HMONITOR currentMonitor = p_MonitorFromWindow( wgs->term_hwnd, MONITOR_DEFAULTTOPRIMARY); +======= + if (dpi_info.cur_dpi.x == 0 || dpi_info.cur_dpi.y == 0) { + if (p_GetDpiForMonitor && p_MonitorFromWindow) { + UINT dpiX, dpiY; + HMONITOR currentMonitor = p_MonitorFromWindow( + wgs.term_hwnd, MONITOR_DEFAULTTOPRIMARY); +>>>>>>> tags/0.78 if (p_GetDpiForMonitor(currentMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY) == S_OK) { wgs->dpi_info.cur_dpi.x = (int)dpiX; @@ -1472,12 +1597,21 @@ static void init_fonts(WinGuiSeat *wgs, int pick_width, int pick_height) /* !!! Yes the next line is right */ if (cset == OEM_CHARSET) +<<<<<<< HEAD wgs->ucsdata.font_codepage = GetOEMCP(); else if (TranslateCharsetInfo ((DWORD *)(ULONG_PTR)cset, &info, TCI_SRCCHARSET)) wgs->ucsdata.font_codepage = info.ciACP; else wgs->ucsdata.font_codepage = -1; +======= + ucsdata.font_codepage = GetOEMCP(); + else if (TranslateCharsetInfo ((DWORD *)(ULONG_PTR)cset, + &info, TCI_SRCCHARSET)) + ucsdata.font_codepage = info.ciACP; + else + ucsdata.font_codepage = -1; +>>>>>>> tags/0.78 GetCPInfo(wgs->ucsdata.font_codepage, &cpinfo); wgs->ucsdata.dbcs_screenfont = (cpinfo.MaxCharSize > 1); @@ -1658,9 +1792,15 @@ static void wintw_request_resize(TermWin *tw, int w, int h) int width, height; /* If the window is maximized suppress resizing attempts */ +<<<<<<< HEAD if (IsZoomed(wgs->term_hwnd)) { if (conf_get_int(wgs->conf, CONF_resize_action) == RESIZE_TERM) { term_resize_request_completed(wgs->term); +======= + if (IsZoomed(wgs.term_hwnd)) { + if (conf_get_int(conf, CONF_resize_action) == RESIZE_TERM) { + term_resize_request_completed(term); +>>>>>>> tags/0.78 return; } } @@ -1674,13 +1814,21 @@ static void wintw_request_resize(TermWin *tw, int w, int h) /* Sanity checks ... */ { RECT ss; +<<<<<<< HEAD if (get_fullscreen_rect(wgs, &ss)) { +======= + if (get_fullscreen_rect(&ss)) { +>>>>>>> tags/0.78 /* Make sure the values aren't too big */ width = (ss.right - ss.left - extra_width) / 4; height = (ss.bottom - ss.top - extra_height) / 6; if (w > width || h > height) { +<<<<<<< HEAD term_resize_request_completed(wgs->term); +======= + term_resize_request_completed(term); +>>>>>>> tags/0.78 return; } if (w < 15) @@ -1690,12 +1838,21 @@ static void wintw_request_resize(TermWin *tw, int w, int h) } } +<<<<<<< HEAD if (conf_get_int(wgs->conf, CONF_resize_action) != RESIZE_FONT && !IsZoomed(wgs->term_hwnd)) { width = extra_width + font_width * w; height = extra_height + font_height * h; SetWindowPos(wgs->term_hwnd, NULL, 0, 0, width, height, +======= + if (conf_get_int(conf, CONF_resize_action) != RESIZE_FONT && + !IsZoomed(wgs.term_hwnd)) { + width = extra_width + font_width * w; + height = extra_height + font_height * h; + + SetWindowPos(wgs.term_hwnd, NULL, 0, 0, width, height, +>>>>>>> tags/0.78 SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOZORDER); } else { @@ -1704,12 +1861,21 @@ static void wintw_request_resize(TermWin *tw, int w, int h) * terminal the new size immediately, so that reset_window * will know what to do. */ +<<<<<<< HEAD term_size(wgs->term, h, w, conf_get_int(wgs->conf, CONF_savelines)); reset_window(wgs, 0); } term_resize_request_completed(wgs->term); InvalidateRect(wgs->term_hwnd, NULL, true); +======= + term_size(term, h, w, conf_get_int(conf, CONF_savelines)); + reset_window(0); + } + + term_resize_request_completed(term); + InvalidateRect(wgs.term_hwnd, NULL, true); +>>>>>>> tags/0.78 } static void recompute_window_offset(WinGuiSeat *wgs) @@ -1837,6 +2003,7 @@ static void reset_window(WinGuiSeat *wgs, int reinit) wgs->dpi_info.cur_dpi.x); rect.right += (window_border * 2); rect.bottom += (window_border * 2); +<<<<<<< HEAD OffsetRect(&wgs->dpi_info.new_wnd_rect, ((wgs->dpi_info.new_wnd_rect.right - wgs->dpi_info.new_wnd_rect.left) - @@ -1847,6 +2014,15 @@ static void reset_window(WinGuiSeat *wgs, int reinit) SetWindowPos(wgs->term_hwnd, NULL, wgs->dpi_info.new_wnd_rect.left, wgs->dpi_info.new_wnd_rect.top, +======= + OffsetRect(&dpi_info.new_wnd_rect, + ((dpi_info.new_wnd_rect.right - dpi_info.new_wnd_rect.left) - + (rect.right - rect.left)) / 2, + ((dpi_info.new_wnd_rect.bottom - dpi_info.new_wnd_rect.top) - + (rect.bottom - rect.top)) / 2); + SetWindowPos(wgs.term_hwnd, NULL, + dpi_info.new_wnd_rect.left, dpi_info.new_wnd_rect.top, +>>>>>>> tags/0.78 rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER); @@ -1900,7 +2076,11 @@ static void reset_window(WinGuiSeat *wgs, int reinit) static RECT ss; int width, height; +<<<<<<< HEAD get_fullscreen_rect(wgs, &ss); +======= + get_fullscreen_rect(&ss); +>>>>>>> tags/0.78 width = (ss.right - ss.left - extra_width) / font_width; height = (ss.bottom - ss.top - extra_height) / font_height; @@ -2016,10 +2196,17 @@ static Mouse_Button translate_button(WinGuiSeat *wgs, Mouse_Button button) if (button == MBT_LEFT) return MBT_SELECT; if (button == MBT_MIDDLE) +<<<<<<< HEAD return conf_get_int(wgs->conf, CONF_mouse_is_xterm) == 1 ? MBT_PASTE : MBT_EXTEND; if (button == MBT_RIGHT) return conf_get_int(wgs->conf, CONF_mouse_is_xterm) == 1 ? +======= + return conf_get_int(conf, CONF_mouse_is_xterm) == 1 ? + MBT_PASTE : MBT_EXTEND; + if (button == MBT_RIGHT) + return conf_get_int(conf, CONF_mouse_is_xterm) == 1 ? +>>>>>>> tags/0.78 MBT_EXTEND : MBT_PASTE; return 0; /* shouldn't happen */ } @@ -2059,6 +2246,11 @@ static bool is_alt_pressed(void) return false; } +<<<<<<< HEAD +======= +static bool resizing; + +>>>>>>> tags/0.78 static void exit_callback(void *vctx) { WinGuiSeat *wgs = (WinGuiSeat *)vctx; @@ -2088,6 +2280,14 @@ static void exit_callback(void *vctx) } static void win_seat_notify_remote_exit(Seat *seat) +<<<<<<< HEAD +======= +{ + queue_toplevel_callback(exit_callback, NULL); +} + +void timer_change_notify(unsigned long next) +>>>>>>> tags/0.78 { WinGuiSeat *wgs = container_of(seat, WinGuiSeat, seat); queue_toplevel_callback(exit_callback, wgs); @@ -2256,7 +2456,11 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, int size; serbuf = strbuf_new(); +<<<<<<< HEAD conf_serialise(BinarySink_UPCAST(serbuf), wgs->conf); +======= + conf_serialise(BinarySink_UPCAST(serbuf), conf); +>>>>>>> tags/0.78 size = serbuf->len; sa.nLength = sizeof(sa); @@ -2348,8 +2552,13 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, { /* Disable full-screen if resizing forbidden */ int i; +<<<<<<< HEAD for (i = 0; i < lenof(wgs->popup_menus); i++) EnableMenuItem(wgs->popup_menus[i].menu, IDM_FULLSCREEN, +======= + for (i = 0; i < lenof(popup_menus); i++) + EnableMenuItem(popup_menus[i].menu, IDM_FULLSCREEN, +>>>>>>> tags/0.78 MF_BYCOMMAND | (resize_action == RESIZE_DISABLED ? MF_GRAYED : MF_ENABLED)); @@ -2366,9 +2575,15 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, * Flush the line discipline's edit buffer in the * case where local editing has just been disabled. */ +<<<<<<< HEAD if (wgs->ldisc) { ldisc_configure(wgs->ldisc, wgs->conf); ldisc_echoedit_update(wgs->ldisc); +======= + if (ldisc) { + ldisc_configure(ldisc, conf); + ldisc_echoedit_update(ldisc); +>>>>>>> tags/0.78 } if (conf_get_bool(wgs->conf, CONF_system_colour) != @@ -2412,9 +2627,15 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, GetWindowLongPtr(hwnd, GWL_EXSTYLE); nexflag = exflag; +<<<<<<< HEAD if (conf_get_bool(wgs->conf, CONF_alwaysontop) != conf_get_bool(prev_conf, CONF_alwaysontop)) { if (conf_get_bool(wgs->conf, CONF_alwaysontop)) { +======= + if (conf_get_bool(conf, CONF_alwaysontop) != + conf_get_bool(prev_conf, CONF_alwaysontop)) { + if (conf_get_bool(conf, CONF_alwaysontop)) { +>>>>>>> tags/0.78 nexflag |= WS_EX_TOPMOST; SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); @@ -2424,13 +2645,21 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, SWP_NOMOVE | SWP_NOSIZE); } } +<<<<<<< HEAD if (conf_get_bool(wgs->conf, CONF_sunken_edge)) +======= + if (conf_get_bool(conf, CONF_sunken_edge)) +>>>>>>> tags/0.78 nexflag |= WS_EX_CLIENTEDGE; else nexflag &= ~(WS_EX_CLIENTEDGE); nflg = flag; +<<<<<<< HEAD if (conf_get_bool(wgs->conf, is_full_screen(wgs) ? +======= + if (conf_get_bool(conf, is_full_screen() ? +>>>>>>> tags/0.78 CONF_scrollbar_in_fullscreen : CONF_scrollbar)) nflg |= WS_VSCROLL; @@ -2438,7 +2667,11 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, nflg &= ~WS_VSCROLL; if (resize_action == RESIZE_DISABLED || +<<<<<<< HEAD is_full_screen(wgs)) +======= + is_full_screen()) +>>>>>>> tags/0.78 nflg &= ~WS_THICKFRAME; else nflg |= WS_THICKFRAME; @@ -2470,21 +2703,37 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, } { +<<<<<<< HEAD FontSpec *font = conf_get_fontspec(wgs->conf, CONF_font); +======= + FontSpec *font = conf_get_fontspec(conf, CONF_font); +>>>>>>> tags/0.78 FontSpec *prev_font = conf_get_fontspec(prev_conf, CONF_font); if (!strcmp(font->name, prev_font->name) || +<<<<<<< HEAD !strcmp(conf_get_str(wgs->conf, CONF_line_codepage), +======= + !strcmp(conf_get_str(conf, CONF_line_codepage), +>>>>>>> tags/0.78 conf_get_str(prev_conf, CONF_line_codepage)) || font->isbold != prev_font->isbold || font->height != prev_font->height || font->charset != prev_font->charset || +<<<<<<< HEAD conf_get_int(wgs->conf, CONF_font_quality) != conf_get_int(prev_conf, CONF_font_quality) || conf_get_int(wgs->conf, CONF_vtmode) != conf_get_int(prev_conf, CONF_vtmode) || conf_get_int(wgs->conf, CONF_bold_style) != +======= + conf_get_int(conf, CONF_font_quality) != + conf_get_int(prev_conf, CONF_font_quality) || + conf_get_int(conf, CONF_vtmode) != + conf_get_int(prev_conf, CONF_vtmode) || + conf_get_int(conf, CONF_bold_style) != +>>>>>>> tags/0.78 conf_get_int(prev_conf, CONF_bold_style) || resize_action == RESIZE_DISABLED || resize_action == RESIZE_EITHER || @@ -2582,9 +2831,15 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, POINT cursorpos; /* Just in case this happened in mid-select */ +<<<<<<< HEAD term_cancel_selection_drag(wgs->term); show_mouseptr(wgs, true); /* make sure pointer is visible */ +======= + term_cancel_selection_drag(term); + + show_mouseptr(true); /* make sure pointer is visible */ +>>>>>>> tags/0.78 GetCursorPos(&cursorpos); TrackPopupMenu(wgs->popup_menus[CTXMENU].menu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON, @@ -2664,7 +2919,11 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, if (pt.x == 0 && pt.y == 0) { mouse_on_hotspot = true; } +<<<<<<< HEAD if (is_full_screen(wgs) && press && +======= + if (is_full_screen() && press && +>>>>>>> tags/0.78 button == MBT_LEFT && mouse_on_hotspot) { SendMessage(hwnd, WM_SYSCOMMAND, SC_MOUSEMENU, MAKELPARAM(pt.x, pt.y)); @@ -2698,7 +2957,11 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, static LPARAM lp = 0; if (wParam != wp || lParam != lp || last_mousemove != WM_MOUSEMOVE) { +<<<<<<< HEAD show_mouseptr(wgs, true); +======= + show_mouseptr(true); +>>>>>>> tags/0.78 wp = wParam; lp = lParam; last_mousemove = WM_MOUSEMOVE; } @@ -2729,7 +2992,11 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, static LPARAM lp = 0; if (wParam != wp || lParam != lp || last_mousemove != WM_NCMOUSEMOVE) { +<<<<<<< HEAD show_mouseptr(wgs, true); +======= + show_mouseptr(true); +>>>>>>> tags/0.78 wp = wParam; lp = lParam; last_mousemove = WM_NCMOUSEMOVE; } @@ -2749,8 +3016,13 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, HideCaret(hwnd); hdc = BeginPaint(hwnd, &p); +<<<<<<< HEAD if (wgs->pal) { SelectPalette(hdc, wgs->pal, true); +======= + if (pal) { + SelectPalette(hdc, pal, true); +>>>>>>> tags/0.78 RealizePalette(hdc); } @@ -2805,10 +3077,17 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, HBRUSH fillcolour, oldbrush; HPEN edge, oldpen; fillcolour = CreateSolidBrush ( +<<<<<<< HEAD wgs->colours[ATTR_DEFBG>>ATTR_BGSHIFT]); oldbrush = SelectObject(hdc, fillcolour); edge = CreatePen(PS_SOLID, 0, wgs->colours[ATTR_DEFBG>>ATTR_BGSHIFT]); +======= + colours[ATTR_DEFBG>>ATTR_BGSHIFT]); + oldbrush = SelectObject(hdc, fillcolour); + edge = CreatePen(PS_SOLID, 0, + colours[ATTR_DEFBG>>ATTR_BGSHIFT]); +>>>>>>> tags/0.78 oldpen = SelectObject(hdc, edge); /* @@ -2824,8 +3103,13 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, ExcludeClipRect(hdc, offset_width, offset_height, +<<<<<<< HEAD offset_width+font_width*wgs->term->cols, offset_height+font_height*wgs->term->rows); +======= + offset_width+font_width*term->cols, + offset_height+font_height*term->rows); +>>>>>>> tags/0.78 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top, p.rcPaint.right, p.rcPaint.bottom); @@ -3008,10 +3292,17 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, } if (wParam == SIZE_MINIMIZED) sw_SetWindowText(hwnd, +<<<<<<< HEAD conf_get_bool(wgs->conf, CONF_win_name_always) ? wgs->window_name : wgs->icon_name); if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED) sw_SetWindowText(hwnd, wgs->window_name); +======= + conf_get_bool(conf, CONF_win_name_always) ? + window_name : icon_name); + if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED) + sw_SetWindowText(hwnd, window_name); +>>>>>>> tags/0.78 if (wParam == SIZE_RESTORED) { processed_resize = false; clear_full_screen(wgs); @@ -3288,20 +3579,32 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, * instead we send the characters one by one. */ /* don't divide SURROGATE PAIR */ +<<<<<<< HEAD if (wgs->ldisc) { +======= + if (ldisc) { +>>>>>>> tags/0.78 for (i = 0; i < n; i += 2) { WCHAR hs = *(unsigned short *)(buff+i); if (IS_HIGH_SURROGATE(hs) && i+2 < n) { WCHAR ls = *(unsigned short *)(buff+i+2); if (IS_LOW_SURROGATE(ls)) { term_keyinputw( +<<<<<<< HEAD wgs->term, (unsigned short *)(buff+i), 2); +======= + term, (unsigned short *)(buff+i), 2); +>>>>>>> tags/0.78 i += 2; continue; } } term_keyinputw( +<<<<<<< HEAD wgs->term, (unsigned short *)(buff+i), 1); +======= + term, (unsigned short *)(buff+i), 1); +>>>>>>> tags/0.78 } } free(buff); @@ -3347,9 +3650,15 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, } } else { char c = (unsigned char)wParam; +<<<<<<< HEAD term_seen_key_event(wgs->term); if (wgs->ldisc) term_keyinput(wgs->term, CP_ACP, &c, 1); +======= + term_seen_key_event(term); + if (ldisc) + term_keyinput(term, CP_ACP, &c, 1); +>>>>>>> tags/0.78 } return 0; case WM_SYSCOLORCHANGE: @@ -4280,11 +4589,19 @@ static int TranslateKey(WinGuiSeat *wgs, UINT message, WPARAM wParam, } if (wParam == compose_keycode) { +<<<<<<< HEAD if (wgs->compose_state == 0 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) wgs->compose_state = 1; else if (wgs->compose_state == 1 && (HIWORD(lParam) & KF_UP)) wgs->compose_state = 2; +======= + if (compose_state == 0 && + (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) + compose_state = 1; + else if (compose_state == 1 && (HIWORD(lParam) & KF_UP)) + compose_state = 2; +>>>>>>> tags/0.78 else wgs->compose_state = 0; } else if (wgs->compose_state == 1 && wParam != VK_CONTROL) @@ -4592,7 +4909,11 @@ static int TranslateKey(WinGuiSeat *wgs, UINT message, WPARAM wParam, case VK_F20: fkey_number = 20; goto numbered_function_key; numbered_function_key: consumed_alt = false; +<<<<<<< HEAD p += format_function_key((char *)p, wgs->term, fkey_number, +======= + p += format_function_key((char *)p, term, fkey_number, +>>>>>>> tags/0.78 shift_state & 1, shift_state & 2, left_alt, &consumed_alt); if (consumed_alt) @@ -4611,7 +4932,11 @@ static int TranslateKey(WinGuiSeat *wgs, UINT message, WPARAM wParam, if (shift_state & 2) break; +<<<<<<< HEAD p += format_small_keypad_key((char *)p, wgs->term, sk_key, +======= + p += format_small_keypad_key((char *)p, term, sk_key, +>>>>>>> tags/0.78 shift_state & 1, shift_state & 2, left_alt, &consumed_alt); if (consumed_alt) @@ -4626,7 +4951,11 @@ static int TranslateKey(WinGuiSeat *wgs, UINT message, WPARAM wParam, case VK_CLEAR: xkey = 'G'; goto arrow_key; /* close enough */ arrow_key: consumed_alt = false; +<<<<<<< HEAD p += format_arrow_key((char *)p, wgs->term, xkey, shift_state & 1, +======= + p += format_arrow_key((char *)p, term, xkey, shift_state & 1, +>>>>>>> tags/0.78 shift_state & 2, left_alt, &consumed_alt); if (consumed_alt) left_alt = false; /* supersedes the usual prefixing of Esc */ @@ -4807,6 +5136,7 @@ static int TranslateKey(WinGuiSeat *wgs, UINT message, WPARAM wParam, static void wintw_set_title(TermWin *tw, const char *title, int codepage) { +<<<<<<< HEAD WinGuiSeat *wgs = container_of(tw, WinGuiSeat, termwin); wchar_t *new_window_name = dup_mb_to_wc(codepage, 0, title); if (!wcscmp(new_window_name, wgs->window_name)) { @@ -4818,10 +5148,22 @@ static void wintw_set_title(TermWin *tw, const char *title, int codepage) if (conf_get_bool(wgs->conf, CONF_win_name_always) || !IsIconic(wgs->term_hwnd)) sw_SetWindowText(wgs->term_hwnd, wgs->window_name); +======= + wchar_t *new_window_name = dup_mb_to_wc(codepage, 0, title); + if (!wcscmp(new_window_name, window_name)) { + sfree(new_window_name); + return; + } + sfree(window_name); + window_name = new_window_name; + if (conf_get_bool(conf, CONF_win_name_always) || !IsIconic(wgs.term_hwnd)) + sw_SetWindowText(wgs.term_hwnd, window_name); +>>>>>>> tags/0.78 } static void wintw_set_icon_title(TermWin *tw, const char *title, int codepage) { +<<<<<<< HEAD WinGuiSeat *wgs = container_of(tw, WinGuiSeat, termwin); wchar_t *new_icon_name = dup_mb_to_wc(codepage, 0, title); if (!wcscmp(new_icon_name, wgs->icon_name)) { @@ -4833,6 +5175,17 @@ static void wintw_set_icon_title(TermWin *tw, const char *title, int codepage) if (!conf_get_bool(wgs->conf, CONF_win_name_always) && IsIconic(wgs->term_hwnd)) sw_SetWindowText(wgs->term_hwnd, wgs->icon_name); +======= + wchar_t *new_icon_name = dup_mb_to_wc(codepage, 0, title); + if (!wcscmp(new_icon_name, icon_name)) { + sfree(new_icon_name); + return; + } + sfree(icon_name); + icon_name = new_icon_name; + if (!conf_get_bool(conf, CONF_win_name_always) && IsIconic(wgs.term_hwnd)) + sw_SetWindowText(wgs.term_hwnd, icon_name); +>>>>>>> tags/0.78 } static void wintw_set_scrollbar(TermWin *tw, int total, int start, int page) @@ -5134,7 +5487,11 @@ static void wintw_clip_write( for (i = 0; i < OSC4_NCOLOURS; i++) { if (palette[i] != 0) { +<<<<<<< HEAD const PALETTEENTRY *pe = &wgs->logpal->palPalEntry[i]; +======= + const PALETTEENTRY *pe = &logpal->palPalEntry[i]; +>>>>>>> tags/0.78 put_fmt(rtf, "\\red%d\\green%d\\blue%d;", pe->peRed, pe->peGreen, pe->peBlue); } @@ -5684,7 +6041,11 @@ static void wintw_move(TermWin *tw, int x, int y) int resize_action = conf_get_int(wgs->conf, CONF_resize_action); if (resize_action == RESIZE_DISABLED || resize_action == RESIZE_FONT || +<<<<<<< HEAD IsZoomed(wgs->term_hwnd)) +======= + IsZoomed(wgs.term_hwnd)) +>>>>>>> tags/0.78 return; SetWindowPos(wgs->term_hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER); @@ -5743,13 +6104,21 @@ static bool is_full_screen(WinGuiSeat *wgs) /* Get the rect/size of a full screen window using the nearest available * monitor in multimon systems; default to something sensible if only * one monitor is present. */ +<<<<<<< HEAD static bool get_fullscreen_rect(WinGuiSeat *wgs, RECT *ss) +======= +static bool get_fullscreen_rect(RECT *ss) +>>>>>>> tags/0.78 { #if defined(MONITOR_DEFAULTTONEAREST) && !defined(NO_MULTIMON) if (p_GetMonitorInfoA && p_MonitorFromWindow) { HMONITOR mon; MONITORINFO mi; +<<<<<<< HEAD mon = p_MonitorFromWindow(wgs->term_hwnd, MONITOR_DEFAULTTONEAREST); +======= + mon = p_MonitorFromWindow(wgs.term_hwnd, MONITOR_DEFAULTTONEAREST); +>>>>>>> tags/0.78 mi.cbSize = sizeof(mi); p_GetMonitorInfoA(mon, &mi); @@ -5778,7 +6147,11 @@ static void make_full_screen(WinGuiSeat *wgs) assert(IsZoomed(wgs->term_hwnd)); +<<<<<<< HEAD if (is_full_screen(wgs)) +======= + if (is_full_screen()) +>>>>>>> tags/0.78 return; /* Remove the window furniture. */ @@ -5791,8 +6164,13 @@ static void make_full_screen(WinGuiSeat *wgs) SetWindowLongPtr(wgs->term_hwnd, GWL_STYLE, style); /* Resize ourselves to exactly cover the nearest monitor. */ +<<<<<<< HEAD get_fullscreen_rect(wgs, &ss); SetWindowPos(wgs->term_hwnd, HWND_TOP, ss.left, ss.top, +======= + get_fullscreen_rect(&ss); + SetWindowPos(wgs.term_hwnd, HWND_TOP, ss.left, ss.top, +>>>>>>> tags/0.78 ss.right - ss.left, ss.bottom - ss.top, SWP_FRAMECHANGED); /* We may have changed size as a result */ @@ -5860,6 +6238,7 @@ static void flip_full_screen(WinGuiSeat *wgs) static size_t win_seat_output(Seat *seat, SeatOutputType type, const void *data, size_t len) { +<<<<<<< HEAD WinGuiSeat *wgs = container_of(seat, WinGuiSeat, seat); return term_data(wgs->term, data, len); } @@ -5869,6 +6248,15 @@ static void wintw_unthrottle(TermWin *tw, size_t bufsize) WinGuiSeat *wgs = container_of(tw, WinGuiSeat, termwin); if (wgs->backend) backend_unthrottle(wgs->backend, bufsize); +======= + return term_data(term, data, len); +} + +static void wintw_unthrottle(TermWin *win, size_t bufsize) +{ + if (backend) + backend_unthrottle(backend, bufsize); +>>>>>>> tags/0.78 } static bool win_seat_eof(Seat *seat) @@ -5878,15 +6266,23 @@ static bool win_seat_eof(Seat *seat) static SeatPromptResult win_seat_get_userpass_input(Seat *seat, prompts_t *p) { +<<<<<<< HEAD WinGuiSeat *wgs = container_of(seat, WinGuiSeat, seat); SeatPromptResult spr; spr = cmdline_get_passwd_input(p, &wgs->cmdline_get_passwd_state, true); if (spr.kind == SPRK_INCOMPLETE) spr = term_get_userpass_input(wgs->term, p); +======= + SeatPromptResult spr; + spr = cmdline_get_passwd_input(p, &cmdline_get_passwd_state, true); + if (spr.kind == SPRK_INCOMPLETE) + spr = term_get_userpass_input(term, p); +>>>>>>> tags/0.78 return spr; } static void win_seat_set_trust_status(Seat *seat, bool trusted) +<<<<<<< HEAD { WinGuiSeat *wgs = container_of(seat, WinGuiSeat, seat); term_set_trust_status(wgs->term, trusted); @@ -5894,6 +6290,14 @@ static void win_seat_set_trust_status(Seat *seat, bool trusted) static bool win_seat_can_set_trust_status(Seat *seat) { +======= +{ + term_set_trust_status(term, trusted); +} + +static bool win_seat_can_set_trust_status(Seat *seat) +{ +>>>>>>> tags/0.78 return true; } diff --git a/WINDOWS/WINGSS.C b/WINDOWS/WINGSS.C deleted file mode 100644 index 79c4921c..00000000 --- a/WINDOWS/WINGSS.C +++ /dev/null @@ -1,703 +0,0 @@ -#ifndef NO_GSSAPI - -#include <limits.h> -#include "putty.h" - -#define SECURITY_WIN32 -#include <security.h> - -#include "pgssapi.h" -#include "sshgss.h" -#include "sshgssc.h" - -#include "misc.h" - -#define UNIX_EPOCH 11644473600ULL /* Seconds from Windows epoch */ -#define CNS_PERSEC 10000000ULL /* # 100ns per second */ - -/* - * Note, as a special case, 0 relative to the Windows epoch (unspecified) maps - * to 0 relative to the POSIX epoch (unspecified)! - */ -#define TIME_WIN_TO_POSIX(ft, t) do { \ - ULARGE_INTEGER uli; \ - uli.LowPart = (ft).dwLowDateTime; \ - uli.HighPart = (ft).dwHighDateTime; \ - if (uli.QuadPart != 0) \ - uli.QuadPart = uli.QuadPart / CNS_PERSEC - UNIX_EPOCH; \ - (t) = (time_t) uli.QuadPart; \ -} while(0) - -/* Windows code to set up the GSSAPI library list. */ - -#ifdef _WIN64 -#define MIT_KERB_SUFFIX "64" -#else -#define MIT_KERB_SUFFIX "32" -#endif - -const int ngsslibs = 3; -const char *const gsslibnames[3] = { - "MIT Kerberos GSSAPI"MIT_KERB_SUFFIX".DLL", - "Microsoft SSPI SECUR32.DLL", - "User-specified GSSAPI DLL", -}; -const struct keyvalwhere gsslibkeywords[] = { - { "gssapi32", 0, -1, -1 }, - { "sspi", 1, -1, -1 }, - { "custom", 2, -1, -1 }, -}; - -DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS, - AcquireCredentialsHandleA, - (SEC_CHAR *, SEC_CHAR *, ULONG, PVOID, - PVOID, SEC_GET_KEY_FN, PVOID, PCredHandle, PTimeStamp)); -DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS, - InitializeSecurityContextA, - (PCredHandle, PCtxtHandle, SEC_CHAR *, ULONG, ULONG, - ULONG, PSecBufferDesc, ULONG, PCtxtHandle, - PSecBufferDesc, PULONG, PTimeStamp)); -DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS, - FreeContextBuffer, - (PVOID)); -DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS, - FreeCredentialsHandle, - (PCredHandle)); -DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS, - DeleteSecurityContext, - (PCtxtHandle)); -DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS, - QueryContextAttributesA, - (PCtxtHandle, ULONG, PVOID)); -DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS, - MakeSignature, - (PCtxtHandle, ULONG, PSecBufferDesc, ULONG)); -DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS, - VerifySignature, - (PCtxtHandle, PSecBufferDesc, ULONG, PULONG)); -DECL_WINDOWS_FUNCTION(static, DLL_DIRECTORY_COOKIE, - AddDllDirectory, - (PCWSTR)); - -typedef struct winSsh_gss_ctx { - unsigned long maj_stat; - unsigned long min_stat; - CredHandle cred_handle; - CtxtHandle context; - PCtxtHandle context_handle; - TimeStamp expiry; -} winSsh_gss_ctx; - - -const Ssh_gss_buf gss_mech_krb5={9,"\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"}; - -const char *gsslogmsg = NULL; - -static void ssh_sspi_bind_fns(struct ssh_gss_library *lib); - -static tree234 *libraries_to_never_unload; -static int library_to_never_unload_cmp(void *av, void *bv) -{ - uintptr_t a = (uintptr_t)av, b = (uintptr_t)bv; - return a < b ? -1 : a > b ? +1 : 0; -} -static void ensure_library_tree_exists(void) -{ - if (!libraries_to_never_unload) - libraries_to_never_unload = newtree234(library_to_never_unload_cmp); -} -static bool library_is_in_never_unload_tree(HMODULE module) -{ - ensure_library_tree_exists(); - return find234(libraries_to_never_unload, module, NULL); -} -static void add_library_to_never_unload_tree(HMODULE module) -{ - ensure_library_tree_exists(); - add234(libraries_to_never_unload, module); -} - -struct ssh_gss_liblist *ssh_gss_setup(Conf *conf) -{ - HMODULE module; - HKEY regkey; - struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist); - char *path; - static HMODULE kernel32_module; - if (!kernel32_module) { - kernel32_module = load_system32_dll("kernel32.dll"); - } -#if defined _MSC_VER && _MSC_VER < 1900 - /* Omit the type-check because older MSVCs don't have this function */ - GET_WINDOWS_FUNCTION_NO_TYPECHECK(kernel32_module, AddDllDirectory); -#else - GET_WINDOWS_FUNCTION(kernel32_module, AddDllDirectory); -#endif - - list->libraries = snewn(3, struct ssh_gss_library); - list->nlibraries = 0; - - /* MIT Kerberos GSSAPI implementation */ - module = NULL; - if (RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\MIT\\Kerberos", ®key) - == ERROR_SUCCESS) { - DWORD type, size; - LONG ret; - char *buffer; - - /* Find out the string length */ - ret = RegQueryValueEx(regkey, "InstallDir", NULL, &type, NULL, &size); - - if (ret == ERROR_SUCCESS && type == REG_SZ) { - buffer = snewn(size + 20, char); - ret = RegQueryValueEx(regkey, "InstallDir", NULL, - &type, (LPBYTE)buffer, &size); - if (ret == ERROR_SUCCESS && type == REG_SZ) { - strcat (buffer, "\\bin"); - if(p_AddDllDirectory) { - /* Add MIT Kerberos' path to the DLL search path, - * it loads its own DLLs further down the road */ - wchar_t *dllPath = - dup_mb_to_wc(DEFAULT_CODEPAGE, 0, buffer); - p_AddDllDirectory(dllPath); - sfree(dllPath); - } - strcat (buffer, "\\gssapi"MIT_KERB_SUFFIX".dll"); - module = LoadLibraryEx (buffer, NULL, - LOAD_LIBRARY_SEARCH_SYSTEM32 | - LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | - LOAD_LIBRARY_SEARCH_USER_DIRS); - - /* - * The MIT Kerberos DLL suffers an internal segfault - * for some reason if you unload and reload one within - * the same process. So, make sure that after we load - * this library, we never free it. - * - * Or rather: after we've loaded it once, if any - * _further_ load returns the same module handle, we - * immediately free it again (to prevent the Windows - * API's internal reference count growing without - * bound). But on the other hand we never free it in - * ssh_gss_cleanup. - */ - if (library_is_in_never_unload_tree(module)) - FreeLibrary(module); - add_library_to_never_unload_tree(module); - } - sfree(buffer); - } - RegCloseKey(regkey); - } - if (module) { - struct ssh_gss_library *lib = - &list->libraries[list->nlibraries++]; - - lib->id = 0; - lib->gsslogmsg = "Using GSSAPI from GSSAPI"MIT_KERB_SUFFIX".DLL"; - lib->handle = (void *)module; - -#define BIND_GSS_FN(name) \ - lib->u.gssapi.name = (t_gss_##name) GetProcAddress(module, "gss_" #name) - - BIND_GSS_FN(delete_sec_context); - BIND_GSS_FN(display_status); - BIND_GSS_FN(get_mic); - BIND_GSS_FN(verify_mic); - BIND_GSS_FN(import_name); - BIND_GSS_FN(init_sec_context); - BIND_GSS_FN(release_buffer); - BIND_GSS_FN(release_cred); - BIND_GSS_FN(release_name); - BIND_GSS_FN(acquire_cred); - BIND_GSS_FN(inquire_cred_by_mech); - -#undef BIND_GSS_FN - - ssh_gssapi_bind_fns(lib); - } - - /* Microsoft SSPI Implementation */ - module = load_system32_dll("secur32.dll"); - if (module) { - struct ssh_gss_library *lib = - &list->libraries[list->nlibraries++]; - - lib->id = 1; - lib->gsslogmsg = "Using SSPI from SECUR32.DLL"; - lib->handle = (void *)module; - - GET_WINDOWS_FUNCTION(module, AcquireCredentialsHandleA); - GET_WINDOWS_FUNCTION(module, InitializeSecurityContextA); - GET_WINDOWS_FUNCTION(module, FreeContextBuffer); - GET_WINDOWS_FUNCTION(module, FreeCredentialsHandle); - GET_WINDOWS_FUNCTION(module, DeleteSecurityContext); - GET_WINDOWS_FUNCTION(module, QueryContextAttributesA); - GET_WINDOWS_FUNCTION(module, MakeSignature); - GET_WINDOWS_FUNCTION(module, VerifySignature); - - ssh_sspi_bind_fns(lib); - } - - /* - * Custom GSSAPI DLL. - */ - module = NULL; - path = conf_get_filename(conf, CONF_ssh_gss_custom)->path; - if (*path) { - if(p_AddDllDirectory) { - /* Add the custom directory as well in case it chainloads - * some other DLLs (e.g a non-installed MIT Kerberos - * instance) */ - int pathlen = strlen(path); - - while (pathlen > 0 && path[pathlen-1] != ':' && - path[pathlen-1] != '\\') - pathlen--; - - if (pathlen > 0 && path[pathlen-1] != '\\') - pathlen--; - - if (pathlen > 0) { - char *dirpath = dupprintf("%.*s", pathlen, path); - wchar_t *dllPath = dup_mb_to_wc(DEFAULT_CODEPAGE, 0, dirpath); - p_AddDllDirectory(dllPath); - sfree(dllPath); - sfree(dirpath); - } - } - - module = LoadLibraryEx(path, NULL, - LOAD_LIBRARY_SEARCH_SYSTEM32 | - LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | - LOAD_LIBRARY_SEARCH_USER_DIRS); - } - if (module) { - struct ssh_gss_library *lib = - &list->libraries[list->nlibraries++]; - - lib->id = 2; - lib->gsslogmsg = dupprintf("Using GSSAPI from user-specified" - " library '%s'", path); - lib->handle = (void *)module; - -#define BIND_GSS_FN(name) \ - lib->u.gssapi.name = (t_gss_##name) GetProcAddress(module, "gss_" #name) - - BIND_GSS_FN(delete_sec_context); - BIND_GSS_FN(display_status); - BIND_GSS_FN(get_mic); - BIND_GSS_FN(verify_mic); - BIND_GSS_FN(import_name); - BIND_GSS_FN(init_sec_context); - BIND_GSS_FN(release_buffer); - BIND_GSS_FN(release_cred); - BIND_GSS_FN(release_name); - BIND_GSS_FN(acquire_cred); - BIND_GSS_FN(inquire_cred_by_mech); - -#undef BIND_GSS_FN - - ssh_gssapi_bind_fns(lib); - } - - - return list; -} - -void ssh_gss_cleanup(struct ssh_gss_liblist *list) -{ - int i; - - /* - * LoadLibrary and FreeLibrary are defined to employ reference - * counting in the case where the same library is repeatedly - * loaded, so even in a multiple-sessions-per-process context - * (not that we currently expect ever to have such a thing on - * Windows) it's safe to naively FreeLibrary everything here - * without worrying about destroying it under the feet of - * another SSH instance still using it. - */ - for (i = 0; i < list->nlibraries; i++) { - if (list->libraries[i].id != 0) { - HMODULE module = (HMODULE)list->libraries[i].handle; - if (!library_is_in_never_unload_tree(module)) - FreeLibrary(module); - } - if (list->libraries[i].id == 2) { - /* The 'custom' id involves a dynamically allocated message. - * Note that we must cast away the 'const' to free it. */ - sfree((char *)list->libraries[i].gsslogmsg); - } - } - sfree(list->libraries); - sfree(list); -} - -static Ssh_gss_stat ssh_sspi_indicate_mech(struct ssh_gss_library *lib, - Ssh_gss_buf *mech) -{ - *mech = gss_mech_krb5; - return SSH_GSS_OK; -} - - -static Ssh_gss_stat ssh_sspi_import_name(struct ssh_gss_library *lib, - char *host, Ssh_gss_name *srv_name) -{ - char *pStr; - - /* Check hostname */ - if (host == NULL) return SSH_GSS_FAILURE; - - /* copy it into form host/FQDN */ - pStr = dupcat("host/", host); - - *srv_name = (Ssh_gss_name) pStr; - - return SSH_GSS_OK; -} - -static Ssh_gss_stat ssh_sspi_acquire_cred(struct ssh_gss_library *lib, - Ssh_gss_ctx *ctx, - time_t *expiry) -{ - winSsh_gss_ctx *winctx = snew(winSsh_gss_ctx); - memset(winctx, 0, sizeof(winSsh_gss_ctx)); - - /* prepare our "wrapper" structure */ - winctx->maj_stat = winctx->min_stat = SEC_E_OK; - winctx->context_handle = NULL; - - /* Specifying no principal name here means use the credentials of - the current logged-in user */ - - winctx->maj_stat = p_AcquireCredentialsHandleA(NULL, - "Kerberos", - SECPKG_CRED_OUTBOUND, - NULL, - NULL, - NULL, - NULL, - &winctx->cred_handle, - NULL); - - if (winctx->maj_stat != SEC_E_OK) { - p_FreeCredentialsHandle(&winctx->cred_handle); - sfree(winctx); - return SSH_GSS_FAILURE; - } - - /* Windows does not return a valid expiration from AcquireCredentials */ - if (expiry) - *expiry = GSS_NO_EXPIRATION; - - *ctx = (Ssh_gss_ctx) winctx; - return SSH_GSS_OK; -} - -static void localexp_to_exp_lifetime(TimeStamp *localexp, - time_t *expiry, unsigned long *lifetime) -{ - FILETIME nowUTC; - FILETIME expUTC; - time_t now; - time_t exp; - time_t delta; - - if (!lifetime && !expiry) - return; - - GetSystemTimeAsFileTime(&nowUTC); - TIME_WIN_TO_POSIX(nowUTC, now); - - if (lifetime) - *lifetime = 0; - if (expiry) - *expiry = GSS_NO_EXPIRATION; - - /* - * Type oddity: localexp is a pointer to 'TimeStamp', whereas - * LocalFileTimeToFileTime expects a pointer to FILETIME. However, - * despite having different formal type names from the compiler's - * point of view, these two structures are specified to be - * isomorphic in the MS documentation, so it's legitimate to copy - * between them: - * - * https://msdn.microsoft.com/en-us/library/windows/desktop/aa380511(v=vs.85).aspx - */ - { - FILETIME localexp_ft; - enum { vorpal_sword = 1 / (sizeof(*localexp) == sizeof(localexp_ft)) }; - memcpy(&localexp_ft, localexp, sizeof(localexp_ft)); - if (!LocalFileTimeToFileTime(&localexp_ft, &expUTC)) - return; - } - - TIME_WIN_TO_POSIX(expUTC, exp); - delta = exp - now; - if (exp == 0 || delta <= 0) - return; - - if (expiry) - *expiry = exp; - if (lifetime) { - if (delta <= ULONG_MAX) - *lifetime = (unsigned long)delta; - else - *lifetime = ULONG_MAX; - } -} - -static Ssh_gss_stat ssh_sspi_init_sec_context(struct ssh_gss_library *lib, - Ssh_gss_ctx *ctx, - Ssh_gss_name srv_name, - int to_deleg, - Ssh_gss_buf *recv_tok, - Ssh_gss_buf *send_tok, - time_t *expiry, - unsigned long *lifetime) -{ - winSsh_gss_ctx *winctx = (winSsh_gss_ctx *) *ctx; - SecBuffer wsend_tok = {send_tok->length,SECBUFFER_TOKEN,send_tok->value}; - SecBuffer wrecv_tok = {recv_tok->length,SECBUFFER_TOKEN,recv_tok->value}; - SecBufferDesc output_desc={SECBUFFER_VERSION,1,&wsend_tok}; - SecBufferDesc input_desc ={SECBUFFER_VERSION,1,&wrecv_tok}; - unsigned long flags=ISC_REQ_MUTUAL_AUTH|ISC_REQ_REPLAY_DETECT| - ISC_REQ_CONFIDENTIALITY|ISC_REQ_ALLOCATE_MEMORY; - unsigned long ret_flags=0; - TimeStamp localexp; - - /* check if we have to delegate ... */ - if (to_deleg) flags |= ISC_REQ_DELEGATE; - winctx->maj_stat = p_InitializeSecurityContextA(&winctx->cred_handle, - winctx->context_handle, - (char*) srv_name, - flags, - 0, /* reserved */ - SECURITY_NATIVE_DREP, - &input_desc, - 0, /* reserved */ - &winctx->context, - &output_desc, - &ret_flags, - &localexp); - - localexp_to_exp_lifetime(&localexp, expiry, lifetime); - - /* prepare for the next round */ - winctx->context_handle = &winctx->context; - send_tok->value = wsend_tok.pvBuffer; - send_tok->length = wsend_tok.cbBuffer; - - /* check & return our status */ - if (winctx->maj_stat==SEC_E_OK) return SSH_GSS_S_COMPLETE; - if (winctx->maj_stat==SEC_I_CONTINUE_NEEDED) return SSH_GSS_S_CONTINUE_NEEDED; - - return SSH_GSS_FAILURE; -} - -static Ssh_gss_stat ssh_sspi_free_tok(struct ssh_gss_library *lib, - Ssh_gss_buf *send_tok) -{ - /* check input */ - if (send_tok == NULL) return SSH_GSS_FAILURE; - - /* free Windows buffer */ - p_FreeContextBuffer(send_tok->value); - SSH_GSS_CLEAR_BUF(send_tok); - - return SSH_GSS_OK; -} - -static Ssh_gss_stat ssh_sspi_release_cred(struct ssh_gss_library *lib, - Ssh_gss_ctx *ctx) -{ - winSsh_gss_ctx *winctx= (winSsh_gss_ctx *) *ctx; - - /* check input */ - if (winctx == NULL) return SSH_GSS_FAILURE; - - /* free Windows data */ - p_FreeCredentialsHandle(&winctx->cred_handle); - p_DeleteSecurityContext(&winctx->context); - - /* delete our "wrapper" structure */ - sfree(winctx); - *ctx = (Ssh_gss_ctx) NULL; - - return SSH_GSS_OK; -} - - -static Ssh_gss_stat ssh_sspi_release_name(struct ssh_gss_library *lib, - Ssh_gss_name *srv_name) -{ - char *pStr= (char *) *srv_name; - - if (pStr == NULL) return SSH_GSS_FAILURE; - sfree(pStr); - *srv_name = (Ssh_gss_name) NULL; - - return SSH_GSS_OK; -} - -static Ssh_gss_stat ssh_sspi_display_status(struct ssh_gss_library *lib, - Ssh_gss_ctx ctx, Ssh_gss_buf *buf) -{ - winSsh_gss_ctx *winctx = (winSsh_gss_ctx *) ctx; - const char *msg; - - if (winctx == NULL) return SSH_GSS_FAILURE; - - /* decode the error code */ - switch (winctx->maj_stat) { - case SEC_E_OK: msg="SSPI status OK"; break; - case SEC_E_INVALID_HANDLE: msg="The handle passed to the function" - " is invalid."; - break; - case SEC_E_TARGET_UNKNOWN: msg="The target was not recognized."; break; - case SEC_E_LOGON_DENIED: msg="The logon failed."; break; - case SEC_E_INTERNAL_ERROR: msg="The Local Security Authority cannot" - " be contacted."; - break; - case SEC_E_NO_CREDENTIALS: msg="No credentials are available in the" - " security package."; - break; - case SEC_E_NO_AUTHENTICATING_AUTHORITY: - msg="No authority could be contacted for authentication." - "The domain name of the authenticating party could be wrong," - " the domain could be unreachable, or there might have been" - " a trust relationship failure."; - break; - case SEC_E_INSUFFICIENT_MEMORY: - msg="One or more of the SecBufferDesc structures passed as" - " an OUT parameter has a buffer that is too small."; - break; - case SEC_E_INVALID_TOKEN: - msg="The error is due to a malformed input token, such as a" - " token corrupted in transit, a token" - " of incorrect size, or a token passed into the wrong" - " security package. Passing a token to" - " the wrong package can happen if client and server did not" - " negotiate the proper security package."; - break; - default: - msg = "Internal SSPI error"; - break; - } - - buf->value = dupstr(msg); - buf->length = strlen(buf->value); - - return SSH_GSS_OK; -} - -static Ssh_gss_stat ssh_sspi_get_mic(struct ssh_gss_library *lib, - Ssh_gss_ctx ctx, Ssh_gss_buf *buf, - Ssh_gss_buf *hash) -{ - winSsh_gss_ctx *winctx= (winSsh_gss_ctx *) ctx; - SecPkgContext_Sizes ContextSizes; - SecBufferDesc InputBufferDescriptor; - SecBuffer InputSecurityToken[2]; - - if (winctx == NULL) return SSH_GSS_FAILURE; - - winctx->maj_stat = 0; - - memset(&ContextSizes, 0, sizeof(ContextSizes)); - - winctx->maj_stat = p_QueryContextAttributesA(&winctx->context, - SECPKG_ATTR_SIZES, - &ContextSizes); - - if (winctx->maj_stat != SEC_E_OK || - ContextSizes.cbMaxSignature == 0) - return winctx->maj_stat; - - InputBufferDescriptor.cBuffers = 2; - InputBufferDescriptor.pBuffers = InputSecurityToken; - InputBufferDescriptor.ulVersion = SECBUFFER_VERSION; - InputSecurityToken[0].BufferType = SECBUFFER_DATA; - InputSecurityToken[0].cbBuffer = buf->length; - InputSecurityToken[0].pvBuffer = buf->value; - InputSecurityToken[1].BufferType = SECBUFFER_TOKEN; - InputSecurityToken[1].cbBuffer = ContextSizes.cbMaxSignature; - InputSecurityToken[1].pvBuffer = snewn(ContextSizes.cbMaxSignature, char); - - winctx->maj_stat = p_MakeSignature(&winctx->context, - 0, - &InputBufferDescriptor, - 0); - - if (winctx->maj_stat == SEC_E_OK) { - hash->length = InputSecurityToken[1].cbBuffer; - hash->value = InputSecurityToken[1].pvBuffer; - } - - return winctx->maj_stat; -} - -static Ssh_gss_stat ssh_sspi_verify_mic(struct ssh_gss_library *lib, - Ssh_gss_ctx ctx, - Ssh_gss_buf *buf, - Ssh_gss_buf *mic) -{ - winSsh_gss_ctx *winctx= (winSsh_gss_ctx *) ctx; - SecBufferDesc InputBufferDescriptor; - SecBuffer InputSecurityToken[2]; - ULONG qop; - - if (winctx == NULL) return SSH_GSS_FAILURE; - - winctx->maj_stat = 0; - - InputBufferDescriptor.cBuffers = 2; - InputBufferDescriptor.pBuffers = InputSecurityToken; - InputBufferDescriptor.ulVersion = SECBUFFER_VERSION; - InputSecurityToken[0].BufferType = SECBUFFER_DATA; - InputSecurityToken[0].cbBuffer = buf->length; - InputSecurityToken[0].pvBuffer = buf->value; - InputSecurityToken[1].BufferType = SECBUFFER_TOKEN; - InputSecurityToken[1].cbBuffer = mic->length; - InputSecurityToken[1].pvBuffer = mic->value; - - winctx->maj_stat = p_VerifySignature(&winctx->context, - &InputBufferDescriptor, - 0, &qop); - return winctx->maj_stat; -} - -static Ssh_gss_stat ssh_sspi_free_mic(struct ssh_gss_library *lib, - Ssh_gss_buf *hash) -{ - sfree(hash->value); - return SSH_GSS_OK; -} - -static void ssh_sspi_bind_fns(struct ssh_gss_library *lib) -{ - lib->indicate_mech = ssh_sspi_indicate_mech; - lib->import_name = ssh_sspi_import_name; - lib->release_name = ssh_sspi_release_name; - lib->init_sec_context = ssh_sspi_init_sec_context; - lib->free_tok = ssh_sspi_free_tok; - lib->acquire_cred = ssh_sspi_acquire_cred; - lib->release_cred = ssh_sspi_release_cred; - lib->get_mic = ssh_sspi_get_mic; - lib->verify_mic = ssh_sspi_verify_mic; - lib->free_mic = ssh_sspi_free_mic; - lib->display_status = ssh_sspi_display_status; -} - -#else - -/* Dummy function so this source file defines something if NO_GSSAPI - is defined. */ - -void ssh_gss_init(void) -{ -} - -#endif diff --git a/WINDOWS/WINHANDL.C b/WINDOWS/WINHANDL.C deleted file mode 100644 index 82d2aded..00000000 --- a/WINDOWS/WINHANDL.C +++ /dev/null @@ -1,731 +0,0 @@ -/* - * winhandl.c: Module to give Windows front ends the general - * ability to deal with consoles, pipes, serial ports, or any other - * type of data stream accessed through a Windows API HANDLE rather - * than a WinSock SOCKET. - * - * We do this by spawning a subthread to continuously try to read - * from the handle. Every time a read successfully returns some - * data, the subthread sets an event object which is picked up by - * the main thread, and the main thread then sets an event in - * return to instruct the subthread to resume reading. - * - * Output works precisely the other way round, in a second - * subthread. The output subthread should not be attempting to - * write all the time, because it hasn't always got data _to_ - * write; so the output thread waits for an event object notifying - * it to _attempt_ a write, and then it sets an event in return - * when one completes. - * - * (It's terribly annoying having to spawn a subthread for each - * direction of each handle. Technically it isn't necessary for - * serial ports, since we could use overlapped I/O within the main - * thread and wait directly on the event objects in the OVERLAPPED - * structures. However, we can't use this trick for some types of - * file handle at all - for some reason Windows restricts use of - * OVERLAPPED to files which were opened with the overlapped flag - - * and so we must use threads for those. This being the case, it's - * simplest just to use threads for everything rather than trying - * to keep track of multiple completely separate mechanisms.) - */ - -#include <assert.h> - -#include "putty.h" - -/* ---------------------------------------------------------------------- - * Generic definitions. - */ - -/* - * Maximum amount of backlog we will allow to build up on an input - * handle before we stop reading from it. - */ -#define MAX_BACKLOG 32768 - -struct handle_generic { - /* - * Initial fields common to both handle_input and handle_output - * structures. - * - * The three HANDLEs are set up at initialisation time and are - * thereafter read-only to both main thread and subthread. - * `moribund' is only used by the main thread; `done' is - * written by the main thread before signalling to the - * subthread. `defunct' and `busy' are used only by the main - * thread. - */ - HANDLE h; /* the handle itself */ - HANDLE ev_to_main; /* event used to signal main thread */ - HANDLE ev_from_main; /* event used to signal back to us */ - bool moribund; /* are we going to kill this soon? */ - bool done; /* request subthread to terminate */ - bool defunct; /* has the subthread already gone? */ - bool busy; /* operation currently in progress? */ - void *privdata; /* for client to remember who they are */ -}; - -typedef enum { HT_INPUT, HT_OUTPUT, HT_FOREIGN } HandleType; - -/* ---------------------------------------------------------------------- - * Input threads. - */ - -/* - * Data required by an input thread. - */ -struct handle_input { - /* - * Copy of the handle_generic structure. - */ - HANDLE h; /* the handle itself */ - HANDLE ev_to_main; /* event used to signal main thread */ - HANDLE ev_from_main; /* event used to signal back to us */ - bool moribund; /* are we going to kill this soon? */ - bool done; /* request subthread to terminate */ - bool defunct; /* has the subthread already gone? */ - bool busy; /* operation currently in progress? */ - void *privdata; /* for client to remember who they are */ - - /* - * Data set at initialisation and then read-only. - */ - int flags; - - /* - * Data set by the input thread before signalling ev_to_main, - * and read by the main thread after receiving that signal. - */ - char buffer[4096]; /* the data read from the handle */ - DWORD len; /* how much data that was */ - int readerr; /* lets us know about read errors */ - - /* - * Callback function called by this module when data arrives on - * an input handle. - */ - handle_inputfn_t gotdata; -}; - -/* - * The actual thread procedure for an input thread. - */ -static DWORD WINAPI handle_input_threadfunc(void *param) -{ - struct handle_input *ctx = (struct handle_input *) param; - OVERLAPPED ovl, *povl; - HANDLE oev; - bool readret, finished; - int readlen; - - if (ctx->flags & HANDLE_FLAG_OVERLAPPED) { - povl = &ovl; - oev = CreateEvent(NULL, true, false, NULL); - } else { - povl = NULL; - } - - if (ctx->flags & HANDLE_FLAG_UNITBUFFER) - readlen = 1; - else - readlen = sizeof(ctx->buffer); - - while (1) { - if (povl) { - memset(povl, 0, sizeof(OVERLAPPED)); - povl->hEvent = oev; - } - readret = ReadFile(ctx->h, ctx->buffer,readlen, &ctx->len, povl); - if (!readret) - ctx->readerr = GetLastError(); - else - ctx->readerr = 0; - if (povl && !readret && ctx->readerr == ERROR_IO_PENDING) { - WaitForSingleObject(povl->hEvent, INFINITE); - readret = GetOverlappedResult(ctx->h, povl, &ctx->len, false); - if (!readret) - ctx->readerr = GetLastError(); - else - ctx->readerr = 0; - } - - if (!readret) { - /* - * Windows apparently sends ERROR_BROKEN_PIPE when a - * pipe we're reading from is closed normally from the - * writing end. This is ludicrous; if that situation - * isn't a natural EOF, _nothing_ is. So if we get that - * particular error, we pretend it's EOF. - */ - if (ctx->readerr == ERROR_BROKEN_PIPE) - ctx->readerr = 0; - ctx->len = 0; - } - - if (readret && ctx->len == 0 && - (ctx->flags & HANDLE_FLAG_IGNOREEOF)) - continue; - - /* - * If we just set ctx->len to 0, that means the read operation - * has returned end-of-file. Telling that to the main thread - * will cause it to set its 'defunct' flag and dispose of the - * handle structure at the next opportunity, in which case we - * mustn't touch ctx at all after the SetEvent. (Hence we do - * even _this_ check before the SetEvent.) - */ - finished = (ctx->len == 0); - - SetEvent(ctx->ev_to_main); - - if (finished) - break; - - WaitForSingleObject(ctx->ev_from_main, INFINITE); - if (ctx->done) { - /* - * The main thread has asked us to shut down. Send back an - * event indicating that we've done so. Hereafter we must - * not touch ctx at all, because the main thread might - * have freed it. - */ - SetEvent(ctx->ev_to_main); - break; - } - } - - if (povl) - CloseHandle(oev); - - return 0; -} - -/* - * This is called after a successful read, or from the - * `unthrottle' function. It decides whether or not to begin a new - * read operation. - */ -static void handle_throttle(struct handle_input *ctx, int backlog) -{ - if (ctx->defunct) - return; - - /* - * If there's a read operation already in progress, do nothing: - * when that completes, we'll come back here and be in a - * position to make a better decision. - */ - if (ctx->busy) - return; - - /* - * Otherwise, we must decide whether to start a new read based - * on the size of the backlog. - */ - if (backlog < MAX_BACKLOG) { - SetEvent(ctx->ev_from_main); - ctx->busy = true; - } -} - -/* ---------------------------------------------------------------------- - * Output threads. - */ - -/* - * Data required by an output thread. - */ -struct handle_output { - /* - * Copy of the handle_generic structure. - */ - HANDLE h; /* the handle itself */ - HANDLE ev_to_main; /* event used to signal main thread */ - HANDLE ev_from_main; /* event used to signal back to us */ - bool moribund; /* are we going to kill this soon? */ - bool done; /* request subthread to terminate */ - bool defunct; /* has the subthread already gone? */ - bool busy; /* operation currently in progress? */ - void *privdata; /* for client to remember who they are */ - - /* - * Data set at initialisation and then read-only. - */ - int flags; - - /* - * Data set by the main thread before signalling ev_from_main, - * and read by the input thread after receiving that signal. - */ - const char *buffer; /* the data to write */ - DWORD len; /* how much data there is */ - - /* - * Data set by the input thread before signalling ev_to_main, - * and read by the main thread after receiving that signal. - */ - DWORD lenwritten; /* how much data we actually wrote */ - int writeerr; /* return value from WriteFile */ - - /* - * Data only ever read or written by the main thread. - */ - bufchain queued_data; /* data still waiting to be written */ - enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof; - - /* - * Callback function called when the backlog in the bufchain - * drops. - */ - handle_outputfn_t sentdata; -}; - -static DWORD WINAPI handle_output_threadfunc(void *param) -{ - struct handle_output *ctx = (struct handle_output *) param; - OVERLAPPED ovl, *povl; - HANDLE oev; - bool writeret; - - if (ctx->flags & HANDLE_FLAG_OVERLAPPED) { - povl = &ovl; - oev = CreateEvent(NULL, true, false, NULL); - } else { - povl = NULL; - } - - while (1) { - WaitForSingleObject(ctx->ev_from_main, INFINITE); - if (ctx->done) { - /* - * The main thread has asked us to shut down. Send back an - * event indicating that we've done so. Hereafter we must - * not touch ctx at all, because the main thread might - * have freed it. - */ - SetEvent(ctx->ev_to_main); - break; - } - if (povl) { - memset(povl, 0, sizeof(OVERLAPPED)); - povl->hEvent = oev; - } - - writeret = WriteFile(ctx->h, ctx->buffer, ctx->len, - &ctx->lenwritten, povl); - if (!writeret) - ctx->writeerr = GetLastError(); - else - ctx->writeerr = 0; - if (povl && !writeret && GetLastError() == ERROR_IO_PENDING) { - writeret = GetOverlappedResult(ctx->h, povl, - &ctx->lenwritten, true); - if (!writeret) - ctx->writeerr = GetLastError(); - else - ctx->writeerr = 0; - } - - SetEvent(ctx->ev_to_main); - if (!writeret) { - /* - * The write operation has suffered an error. Telling that - * to the main thread will cause it to set its 'defunct' - * flag and dispose of the handle structure at the next - * opportunity, so we must not touch ctx at all after - * this. - */ - break; - } - } - - if (povl) - CloseHandle(oev); - - return 0; -} - -static void handle_try_output(struct handle_output *ctx) -{ - if (!ctx->busy && bufchain_size(&ctx->queued_data)) { - ptrlen data = bufchain_prefix(&ctx->queued_data); - ctx->buffer = data.ptr; - ctx->len = min(data.len, ~(DWORD)0); - SetEvent(ctx->ev_from_main); - ctx->busy = true; - } else if (!ctx->busy && bufchain_size(&ctx->queued_data) == 0 && - ctx->outgoingeof == EOF_PENDING) { - CloseHandle(ctx->h); - ctx->h = INVALID_HANDLE_VALUE; - ctx->outgoingeof = EOF_SENT; - } -} - -/* ---------------------------------------------------------------------- - * 'Foreign events'. These are handle structures which just contain a - * single event object passed to us by another module such as - * winnps.c, so that they can make use of our handle_get_events / - * handle_got_event mechanism for communicating with application main - * loops. - */ -struct handle_foreign { - /* - * Copy of the handle_generic structure. - */ - HANDLE h; /* the handle itself */ - HANDLE ev_to_main; /* event used to signal main thread */ - HANDLE ev_from_main; /* event used to signal back to us */ - bool moribund; /* are we going to kill this soon? */ - bool done; /* request subthread to terminate */ - bool defunct; /* has the subthread already gone? */ - bool busy; /* operation currently in progress? */ - void *privdata; /* for client to remember who they are */ - - /* - * Our own data, just consisting of knowledge of who to call back. - */ - void (*callback)(void *); - void *ctx; -}; - -/* ---------------------------------------------------------------------- - * Unified code handling both input and output threads. - */ - -struct handle { - HandleType type; - union { - struct handle_generic g; - struct handle_input i; - struct handle_output o; - struct handle_foreign f; - } u; -}; - -static tree234 *handles_by_evtomain; - -static int handle_cmp_evtomain(void *av, void *bv) -{ - struct handle *a = (struct handle *)av; - struct handle *b = (struct handle *)bv; - - if ((uintptr_t)a->u.g.ev_to_main < (uintptr_t)b->u.g.ev_to_main) - return -1; - else if ((uintptr_t)a->u.g.ev_to_main > (uintptr_t)b->u.g.ev_to_main) - return +1; - else - return 0; -} - -static int handle_find_evtomain(void *av, void *bv) -{ - HANDLE *a = (HANDLE *)av; - struct handle *b = (struct handle *)bv; - - if ((uintptr_t)*a < (uintptr_t)b->u.g.ev_to_main) - return -1; - else if ((uintptr_t)*a > (uintptr_t)b->u.g.ev_to_main) - return +1; - else - return 0; -} - -struct handle *handle_input_new(HANDLE handle, handle_inputfn_t gotdata, - void *privdata, int flags) -{ - struct handle *h = snew(struct handle); - DWORD in_threadid; /* required for Win9x */ - - h->type = HT_INPUT; - h->u.i.h = handle; - h->u.i.ev_to_main = CreateEvent(NULL, false, false, NULL); - h->u.i.ev_from_main = CreateEvent(NULL, false, false, NULL); - h->u.i.gotdata = gotdata; - h->u.i.defunct = false; - h->u.i.moribund = false; - h->u.i.done = false; - h->u.i.privdata = privdata; - h->u.i.flags = flags; - - if (!handles_by_evtomain) - handles_by_evtomain = newtree234(handle_cmp_evtomain); - add234(handles_by_evtomain, h); - - HANDLE hThread = CreateThread(NULL, 0, handle_input_threadfunc, - &h->u.i, 0, &in_threadid); - if (hThread) - CloseHandle(hThread); /* we don't need the thread handle */ - h->u.i.busy = true; - - return h; -} - -struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata, - void *privdata, int flags) -{ - struct handle *h = snew(struct handle); - DWORD out_threadid; /* required for Win9x */ - - h->type = HT_OUTPUT; - h->u.o.h = handle; - h->u.o.ev_to_main = CreateEvent(NULL, false, false, NULL); - h->u.o.ev_from_main = CreateEvent(NULL, false, false, NULL); - h->u.o.busy = false; - h->u.o.defunct = false; - h->u.o.moribund = false; - h->u.o.done = false; - h->u.o.privdata = privdata; - bufchain_init(&h->u.o.queued_data); - h->u.o.outgoingeof = EOF_NO; - h->u.o.sentdata = sentdata; - h->u.o.flags = flags; - - if (!handles_by_evtomain) - handles_by_evtomain = newtree234(handle_cmp_evtomain); - add234(handles_by_evtomain, h); - - HANDLE hThread = CreateThread(NULL, 0, handle_output_threadfunc, - &h->u.o, 0, &out_threadid); - if (hThread) - CloseHandle(hThread); /* we don't need the thread handle */ - - return h; -} - -struct handle *handle_add_foreign_event(HANDLE event, - void (*callback)(void *), void *ctx) -{ - struct handle *h = snew(struct handle); - - h->type = HT_FOREIGN; - h->u.f.h = INVALID_HANDLE_VALUE; - h->u.f.ev_to_main = event; - h->u.f.ev_from_main = INVALID_HANDLE_VALUE; - h->u.f.defunct = true; /* we have no thread in the first place */ - h->u.f.moribund = false; - h->u.f.done = false; - h->u.f.privdata = NULL; - h->u.f.callback = callback; - h->u.f.ctx = ctx; - h->u.f.busy = true; - - if (!handles_by_evtomain) - handles_by_evtomain = newtree234(handle_cmp_evtomain); - add234(handles_by_evtomain, h); - - return h; -} - -size_t handle_write(struct handle *h, const void *data, size_t len) -{ - assert(h->type == HT_OUTPUT); - assert(h->u.o.outgoingeof == EOF_NO); - bufchain_add(&h->u.o.queued_data, data, len); - handle_try_output(&h->u.o); - return bufchain_size(&h->u.o.queued_data); -} - -void handle_write_eof(struct handle *h) -{ - /* - * This function is called when we want to proactively send an - * end-of-file notification on the handle. We can only do this by - * actually closing the handle - so never call this on a - * bidirectional handle if we're still interested in its incoming - * direction! - */ - assert(h->type == HT_OUTPUT); - if (h->u.o.outgoingeof == EOF_NO) { - h->u.o.outgoingeof = EOF_PENDING; - handle_try_output(&h->u.o); - } -} - -HANDLE *handle_get_events(int *nevents) -{ - HANDLE *ret; - struct handle *h; - int i; - size_t n, size; - - /* - * Go through our tree counting the handle objects currently - * engaged in useful activity. - */ - ret = NULL; - n = size = 0; - if (handles_by_evtomain) { - for (i = 0; (h = index234(handles_by_evtomain, i)) != NULL; i++) { - if (h->u.g.busy) { - sgrowarray(ret, size, n); - ret[n++] = h->u.g.ev_to_main; - } - } - } - - *nevents = n; - return ret; -} - -static void handle_destroy(struct handle *h) -{ - if (h->type == HT_OUTPUT) - bufchain_clear(&h->u.o.queued_data); - CloseHandle(h->u.g.ev_from_main); - CloseHandle(h->u.g.ev_to_main); - del234(handles_by_evtomain, h); - sfree(h); -} - -void handle_free(struct handle *h) -{ - assert(h && !h->u.g.moribund); - if (h->u.g.busy && h->type != HT_FOREIGN) { - /* - * If the handle is currently busy, we cannot immediately free - * it, because its subthread is in the middle of something. - * (Exception: foreign handles don't have a subthread.) - * - * Instead we must wait until it's finished its current - * operation, because otherwise the subthread will write to - * invalid memory after we free its context from under it. So - * we set the moribund flag, which will be noticed next time - * an operation completes. - */ - h->u.g.moribund = true; - } else if (h->u.g.defunct) { - /* - * There isn't even a subthread; we can go straight to - * handle_destroy. - */ - handle_destroy(h); - } else { - /* - * The subthread is alive but not busy, so we now signal it - * to die. Set the moribund flag to indicate that it will - * want destroying after that. - */ - h->u.g.moribund = true; - h->u.g.done = true; - h->u.g.busy = true; - SetEvent(h->u.g.ev_from_main); - } -} - -void handle_got_event(HANDLE event) -{ - struct handle *h; - - assert(handles_by_evtomain); - h = find234(handles_by_evtomain, &event, handle_find_evtomain); - if (!h) { - /* - * This isn't an error condition. If two or more event - * objects were signalled during the same select operation, - * and processing of the first caused the second handle to - * be closed, then it will sometimes happen that we receive - * an event notification here for a handle which is already - * deceased. In that situation we simply do nothing. - */ - return; - } - - if (h->u.g.moribund) { - /* - * A moribund handle is one which we have either already - * signalled to die, or are waiting until its current I/O op - * completes to do so. Either way, it's treated as already - * dead from the external user's point of view, so we ignore - * the actual I/O result. We just signal the thread to die if - * we haven't yet done so, or destroy the handle if not. - */ - if (h->u.g.done) { - handle_destroy(h); - } else { - h->u.g.done = true; - h->u.g.busy = true; - SetEvent(h->u.g.ev_from_main); - } - return; - } - - switch (h->type) { - int backlog; - - case HT_INPUT: - h->u.i.busy = false; - - /* - * A signal on an input handle means data has arrived. - */ - if (h->u.i.len == 0) { - /* - * EOF, or (nearly equivalently) read error. - */ - h->u.i.defunct = true; - h->u.i.gotdata(h, NULL, 0, h->u.i.readerr); - } else { - backlog = h->u.i.gotdata(h, h->u.i.buffer, h->u.i.len, 0); - handle_throttle(&h->u.i, backlog); - } - break; - - case HT_OUTPUT: - h->u.o.busy = false; - - /* - * A signal on an output handle means we have completed a - * write. Call the callback to indicate that the output - * buffer size has decreased, or to indicate an error. - */ - if (h->u.o.writeerr) { - /* - * Write error. Send a negative value to the callback, - * and mark the thread as defunct (because the output - * thread is terminating by now). - */ - h->u.o.defunct = true; - h->u.o.sentdata(h, 0, h->u.o.writeerr); - } else { - bufchain_consume(&h->u.o.queued_data, h->u.o.lenwritten); - noise_ultralight(NOISE_SOURCE_IOLEN, h->u.o.lenwritten); - h->u.o.sentdata(h, bufchain_size(&h->u.o.queued_data), 0); - handle_try_output(&h->u.o); - } - break; - - case HT_FOREIGN: - /* Just call the callback. */ - h->u.f.callback(h->u.f.ctx); - break; - } -} - -void handle_unthrottle(struct handle *h, size_t backlog) -{ - assert(h->type == HT_INPUT); - handle_throttle(&h->u.i, backlog); -} - -size_t handle_backlog(struct handle *h) -{ - assert(h->type == HT_OUTPUT); - return bufchain_size(&h->u.o.queued_data); -} - -void *handle_get_privdata(struct handle *h) -{ - return h->u.g.privdata; -} - -static void handle_sink_write(BinarySink *bs, const void *data, size_t len) -{ - handle_sink *sink = BinarySink_DOWNCAST(bs, handle_sink); - handle_write(sink->h, data, len); -} - -void handle_sink_init(handle_sink *sink, struct handle *h) -{ - sink->h = h; - BinarySink_INIT(sink, handle_sink_write); -} diff --git a/WINDOWS/WINHELP.C b/WINDOWS/WINHELP.C deleted file mode 100644 index df6ac37b..00000000 --- a/WINDOWS/WINHELP.C +++ /dev/null @@ -1,250 +0,0 @@ -/* - * winhelp.c: centralised functions to launch Windows HTML Help files. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <assert.h> - -#include "putty.h" -#include "win_res.h" - -#ifdef NO_HTMLHELP - -/* If htmlhelp.h is not available, we can't do any of this at all */ -bool has_help(void) { return false; } -void init_help(void) { } -void shutdown_help(void) { } -void launch_help(HWND hwnd, const char *topic) { } -void quit_help(HWND hwnd) { } - -#else - -#include <htmlhelp.h> - -static char *chm_path = NULL; -static bool chm_created_by_us = false; - -static bool requested_help; -DECL_WINDOWS_FUNCTION(static, HWND, HtmlHelpA, (HWND, LPCSTR, UINT, DWORD_PTR)); - -static HRSRC chm_hrsrc; -static DWORD chm_resource_size = 0; -static const void *chm_resource = NULL; - -int has_embedded_chm(void) -{ - static bool checked = false; - if (!checked) { - checked = true; - - chm_hrsrc = FindResource( - NULL, MAKEINTRESOURCE(ID_CUSTOM_CHMFILE), - MAKEINTRESOURCE(TYPE_CUSTOM_CHMFILE)); - } - return chm_hrsrc != NULL ? 1 : 0; -} - -static bool find_chm_resource(void) -{ - static bool checked = false; - if (checked) /* we've been here already */ - goto out; - checked = true; - - /* - * Look for a CHM file embedded in this executable as a custom - * resource. - */ - if (!has_embedded_chm()) /* set up chm_hrsrc and check if it's NULL */ - goto out; - - chm_resource_size = SizeofResource(NULL, chm_hrsrc); - if (chm_resource_size == 0) - goto out; - - HGLOBAL chm_hglobal = LoadResource(NULL, chm_hrsrc); - if (chm_hglobal == NULL) - goto out; - - chm_resource = (const uint8_t *)LockResource(chm_hglobal); - - out: - return chm_resource != NULL; -} - -static bool load_chm_resource(void) -{ - bool toret = false; - char *filename = NULL; - HANDLE filehandle = INVALID_HANDLE_VALUE; - bool created = false; - - static bool tried_to_load = false; - if (tried_to_load) - goto out; - tried_to_load = true; - - /* - * We've found it! Now write it out into a separate file, so that - * htmlhelp.exe can handle it. - */ - - /* GetTempPath is documented as returning a size of up to - * MAX_PATH+1 which does not count the NUL */ - char tempdir[MAX_PATH + 2]; - if (GetTempPath(sizeof(tempdir), tempdir) == 0) - goto out; - - unsigned long pid = GetCurrentProcessId(); - - for (uint64_t counter = 0;; counter++) { - filename = dupprintf( - "%s\\putty_%lu_%"PRIu64".chm", tempdir, pid, counter); - filehandle = CreateFile( - filename, GENERIC_WRITE, FILE_SHARE_READ, - NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); - - if (filehandle != INVALID_HANDLE_VALUE) - break; /* success! */ - - if (GetLastError() != ERROR_FILE_EXISTS) - goto out; /* failed for some other reason! */ - - sfree(filename); - filename = NULL; - } - created = true; - - const uint8_t *p = (const uint8_t *)chm_resource; - for (DWORD pos = 0; pos < chm_resource_size; pos++) { - DWORD to_write = chm_resource_size - pos; - DWORD written = 0; - - if (!WriteFile(filehandle, p + pos, to_write, &written, NULL)) - goto out; - pos += written; - } - - chm_path = filename; - filename = NULL; - chm_created_by_us = true; - toret = true; - - out: - if (created && !toret) - DeleteFile(filename); - sfree(filename); - if (filehandle != INVALID_HANDLE_VALUE) - CloseHandle(filehandle); - return toret; -} - -static bool find_chm_from_installation(void) -{ - static const char *const reg_paths[] = { - "Software\\SimonTatham\\PuTTY64\\CHMPath", - "Software\\SimonTatham\\PuTTY\\CHMPath", - }; - - for (size_t i = 0; i < lenof(reg_paths); i++) { - char *filename = registry_get_string( - HKEY_LOCAL_MACHINE, reg_paths[i], NULL); - - if (filename) { - chm_path = filename; - chm_created_by_us = false; - return true; - } - } - - return false; -} - -void init_help(void) -{ - /* Just in case of multiple calls */ - static bool already_called = false; - if (already_called) - return; - already_called = true; - - /* - * Don't even try looking for the CHM file if we can't even find - * the HtmlHelp() API function. - */ - HINSTANCE dllHH = load_system32_dll("hhctrl.ocx"); - GET_WINDOWS_FUNCTION(dllHH, HtmlHelpA); - if (!p_HtmlHelpA) { - FreeLibrary(dllHH); - return; - } - - /* - * If there's a CHM file embedded in this executable, we should - * use that as the first choice. - */ - if (find_chm_resource()) - return; - - /* - * Otherwise, try looking for the CHM in the location that the - * installer marked in the registry. - */ - if (find_chm_from_installation()) - return; -} - -void shutdown_help(void) -{ - if (chm_path && chm_created_by_us) { - p_HtmlHelpA(NULL, NULL, HH_CLOSE_ALL, 0); - DeleteFile(chm_path); - } - sfree(chm_path); - chm_path = NULL; - chm_created_by_us = false; -} - -bool has_help(void) -{ - return chm_path != NULL || chm_resource != NULL; -} - -void launch_help(HWND hwnd, const char *topic) -{ - if (!chm_path && chm_resource) { - /* - * If we've been called without already having a file name for - * the CHM file, that might be because we've located it in our - * resource section but not written it to a temp file yet. Do - * so now, on first use. - */ - load_chm_resource(); - } - - /* If we _still_ don't have a CHM pathname, we just can't display help. */ - if (!chm_path) - return; - - if (topic) { - char *fname = dupprintf( - "%s::/%s.html>main", chm_path, topic); - p_HtmlHelpA(hwnd, fname, HH_DISPLAY_TOPIC, 0); - sfree(fname); - } else { - p_HtmlHelpA(hwnd, chm_path, HH_DISPLAY_TOPIC, 0); - } - requested_help = true; -} - -void quit_help(HWND hwnd) -{ - if (requested_help) - p_HtmlHelpA(NULL, NULL, HH_CLOSE_ALL, 0); - if (chm_path && chm_created_by_us) - DeleteFile(chm_path); -} - -#endif /* NO_HTMLHELP */ diff --git a/WINDOWS/WINHELP.H b/WINDOWS/WINHELP.H deleted file mode 100644 index 9011df45..00000000 --- a/WINDOWS/WINHELP.H +++ /dev/null @@ -1,208 +0,0 @@ -/* - * winhelp.h - define Windows Help context names. - * Each definition is simply a string which matches up with the - * section names in the Halibut source, and is used for HTML Help. - */ - -/* Maximum length for WINHELP_CTX_foo strings */ -#define WINHELP_CTX_MAXLEN 80 - -/* These are used in the cross-platform configuration dialog code. */ - -#define HELPCTX(x) P(WINHELP_CTX_ ## x) - -#define WINHELP_CTX_no_help NULL - -#define WINHELP_CTX_session_hostname "config-hostname" -#define WINHELP_CTX_session_saved "config-saving" -#define WINHELP_CTX_session_coe "config-closeonexit" -#define WINHELP_CTX_logging_main "config-logging" -#define WINHELP_CTX_logging_filename "config-logfilename" -#define WINHELP_CTX_logging_exists "config-logfileexists" -#define WINHELP_CTX_logging_flush "config-logflush" -#define WINHELP_CTX_logging_header "config-logheader" -#define WINHELP_CTX_logging_ssh_omit_password "config-logssh" -#define WINHELP_CTX_logging_ssh_omit_data "config-logssh" -#define WINHELP_CTX_keyboard_backspace "config-backspace" -#define WINHELP_CTX_keyboard_homeend "config-homeend" -#define WINHELP_CTX_keyboard_funkeys "config-funkeys" -#define WINHELP_CTX_keyboard_appkeypad "config-appkeypad" -#define WINHELP_CTX_keyboard_appcursor "config-appcursor" -#define WINHELP_CTX_keyboard_nethack "config-nethack" -#define WINHELP_CTX_keyboard_compose "config-compose" -#define WINHELP_CTX_keyboard_ctrlalt "config-ctrlalt" -#define WINHELP_CTX_features_application "config-features-application" -#define WINHELP_CTX_features_mouse "config-features-mouse" -#define WINHELP_CTX_features_resize "config-features-resize" -#define WINHELP_CTX_features_altscreen "config-features-altscreen" -#define WINHELP_CTX_features_retitle "config-features-retitle" -#define WINHELP_CTX_features_qtitle "config-features-qtitle" -#define WINHELP_CTX_features_dbackspace "config-features-dbackspace" -#define WINHELP_CTX_features_charset "config-features-charset" -#define WINHELP_CTX_features_clearscroll "config-features-clearscroll" -#define WINHELP_CTX_features_arabicshaping "config-features-shaping" -#define WINHELP_CTX_features_bidi "config-features-bidi" -#define WINHELP_CTX_terminal_autowrap "config-autowrap" -#define WINHELP_CTX_terminal_decom "config-decom" -#define WINHELP_CTX_terminal_lfhascr "config-crlf" -#define WINHELP_CTX_terminal_crhaslf "config-lfcr" -#define WINHELP_CTX_terminal_bce "config-erase" -#define WINHELP_CTX_terminal_blink "config-blink" -#define WINHELP_CTX_terminal_answerback "config-answerback" -#define WINHELP_CTX_terminal_localecho "config-localecho" -#define WINHELP_CTX_terminal_localedit "config-localedit" -#define WINHELP_CTX_terminal_printing "config-printing" -#define WINHELP_CTX_supdup_location "supdup-location" -#define WINHELP_CTX_supdup_ascii "supdup-ascii" -#define WINHELP_CTX_supdup_more "supdup-more" -#define WINHELP_CTX_supdup_scroll "supdup-scroll" -#define WINHELP_CTX_bell_style "config-bellstyle" -#define WINHELP_CTX_bell_taskbar "config-belltaskbar" -#define WINHELP_CTX_bell_overload "config-bellovl" -#define WINHELP_CTX_window_size "config-winsize" -#define WINHELP_CTX_window_resize "config-winsizelock" -#define WINHELP_CTX_window_scrollback "config-scrollback" -#define WINHELP_CTX_window_erased "config-erasetoscrollback" -#define WINHELP_CTX_behaviour_closewarn "config-warnonclose" -#define WINHELP_CTX_behaviour_altf4 "config-altf4" -#define WINHELP_CTX_behaviour_altspace "config-altspace" -#define WINHELP_CTX_behaviour_altonly "config-altonly" -#define WINHELP_CTX_behaviour_alwaysontop "config-alwaysontop" -#define WINHELP_CTX_behaviour_altenter "config-fullscreen" -#define WINHELP_CTX_appearance_cursor "config-cursor" -#define WINHELP_CTX_appearance_font "config-font" -#define WINHELP_CTX_appearance_title "config-title" -#define WINHELP_CTX_appearance_hidemouse "config-mouseptr" -#define WINHELP_CTX_appearance_border "config-winborder" -#define WINHELP_CTX_connection_termtype "config-termtype" -#define WINHELP_CTX_connection_termspeed "config-termspeed" -#define WINHELP_CTX_connection_username "config-username" -#define WINHELP_CTX_connection_username_from_env "config-username-from-env" -#define WINHELP_CTX_connection_keepalive "config-keepalive" -#define WINHELP_CTX_connection_nodelay "config-nodelay" -#define WINHELP_CTX_connection_ipversion "config-address-family" -#define WINHELP_CTX_connection_tcpkeepalive "config-tcp-keepalives" -#define WINHELP_CTX_connection_loghost "config-loghost" -#define WINHELP_CTX_proxy_type "config-proxy-type" -#define WINHELP_CTX_proxy_main "config-proxy" -#define WINHELP_CTX_proxy_exclude "config-proxy-exclude" -#define WINHELP_CTX_proxy_dns "config-proxy-dns" -#define WINHELP_CTX_proxy_auth "config-proxy-auth" -#define WINHELP_CTX_proxy_command "config-proxy-command" -#define WINHELP_CTX_proxy_logging "config-proxy-logging" -#define WINHELP_CTX_telnet_environ "config-environ" -#define WINHELP_CTX_telnet_oldenviron "config-oldenviron" -#define WINHELP_CTX_telnet_passive "config-ptelnet" -#define WINHELP_CTX_telnet_specialkeys "config-telnetkey" -#define WINHELP_CTX_telnet_newline "config-telnetnl" -#define WINHELP_CTX_rlogin_localuser "config-rlogin-localuser" -#define WINHELP_CTX_ssh_nopty "config-ssh-pty" -#define WINHELP_CTX_ssh_ttymodes "config-ttymodes" -#define WINHELP_CTX_ssh_noshell "config-ssh-noshell" -#define WINHELP_CTX_ssh_ciphers "config-ssh-encryption" -#define WINHELP_CTX_ssh_protocol "config-ssh-prot" -#define WINHELP_CTX_ssh_command "config-command" -#define WINHELP_CTX_ssh_compress "config-ssh-comp" -#define WINHELP_CTX_ssh_share "config-ssh-sharing" -#define WINHELP_CTX_ssh_kexlist "config-ssh-kex-order" -#define WINHELP_CTX_ssh_hklist "config-ssh-hostkey-order" -#define WINHELP_CTX_ssh_hk_known "config-ssh-prefer-known-hostkeys" -#define WINHELP_CTX_ssh_gssapi_kex_delegation "config-ssh-kex-gssapi-delegation" -#define WINHELP_CTX_ssh_kex_repeat "config-ssh-kex-rekey" -#define WINHELP_CTX_ssh_kex_manual_hostkeys "config-ssh-kex-manual-hostkeys" -#define WINHELP_CTX_ssh_auth_bypass "config-ssh-noauth" -#define WINHELP_CTX_ssh_no_trivial_userauth "config-ssh-notrivialauth" -#define WINHELP_CTX_ssh_auth_banner "config-ssh-banner" -#define WINHELP_CTX_ssh_auth_privkey "config-ssh-privkey" -#define WINHELP_CTX_ssh_auth_agentfwd "config-ssh-agentfwd" -#define WINHELP_CTX_ssh_auth_changeuser "config-ssh-changeuser" -#define WINHELP_CTX_ssh_auth_pageant "config-ssh-tryagent" -#define WINHELP_CTX_ssh_auth_tis "config-ssh-tis" -#define WINHELP_CTX_ssh_auth_ki "config-ssh-ki" -#define WINHELP_CTX_ssh_gssapi "config-ssh-auth-gssapi" -#define WINHELP_CTX_ssh_gssapi_delegation "config-ssh-auth-gssapi-delegation" -#define WINHELP_CTX_ssh_gssapi_libraries "config-ssh-auth-gssapi-libraries" -#define WINHELP_CTX_selection_buttons "config-mouse" -#define WINHELP_CTX_selection_shiftdrag "config-mouseshift" -#define WINHELP_CTX_selection_rect "config-rectselect" -#define WINHELP_CTX_selection_linedraw "config-linedrawpaste" -#define WINHELP_CTX_selection_autocopy "config-selection-autocopy" -#define WINHELP_CTX_selection_clipactions "config-selection-clipactions" -#define WINHELP_CTX_selection_pastectrl "config-paste-ctrl-char" -#define WINHELP_CTX_copy_charclasses "config-charclasses" -#define WINHELP_CTX_copy_rtf "config-rtfcopy" -#define WINHELP_CTX_colours_ansi "config-ansicolour" -#define WINHELP_CTX_colours_xterm256 "config-xtermcolour" -#define WINHELP_CTX_colours_truecolour "config-truecolour" -#define WINHELP_CTX_colours_bold "config-boldcolour" -#define WINHELP_CTX_colours_system "config-syscolour" -#define WINHELP_CTX_colours_logpal "config-logpalette" -#define WINHELP_CTX_colours_config "config-colourcfg" -#define WINHELP_CTX_translation_codepage "config-charset" -#define WINHELP_CTX_translation_cjk_ambig_wide "config-cjk-ambig-wide" -#define WINHELP_CTX_translation_cyrillic "config-cyr" -#define WINHELP_CTX_translation_linedraw "config-linedraw" -#define WINHELP_CTX_translation_utf8linedraw "config-utf8linedraw" -#define WINHELP_CTX_ssh_tunnels_x11 "config-ssh-x11" -#define WINHELP_CTX_ssh_tunnels_x11auth "config-ssh-x11auth" -#define WINHELP_CTX_ssh_tunnels_xauthority "config-ssh-xauthority" -#define WINHELP_CTX_ssh_tunnels_portfwd "config-ssh-portfwd" -#define WINHELP_CTX_ssh_tunnels_portfwd_localhost "config-ssh-portfwd-localhost" -#define WINHELP_CTX_ssh_tunnels_portfwd_ipversion "config-ssh-portfwd-address-family" -#define WINHELP_CTX_ssh_bugs_ignore1 "config-ssh-bug-ignore1" -#define WINHELP_CTX_ssh_bugs_plainpw1 "config-ssh-bug-plainpw1" -#define WINHELP_CTX_ssh_bugs_rsa1 "config-ssh-bug-rsa1" -#define WINHELP_CTX_ssh_bugs_ignore2 "config-ssh-bug-ignore2" -#define WINHELP_CTX_ssh_bugs_hmac2 "config-ssh-bug-hmac2" -#define WINHELP_CTX_ssh_bugs_derivekey2 "config-ssh-bug-derivekey2" -#define WINHELP_CTX_ssh_bugs_rsapad2 "config-ssh-bug-sig" -#define WINHELP_CTX_ssh_bugs_pksessid2 "config-ssh-bug-pksessid2" -#define WINHELP_CTX_ssh_bugs_rekey2 "config-ssh-bug-rekey" -#define WINHELP_CTX_ssh_bugs_maxpkt2 "config-ssh-bug-maxpkt2" -#define WINHELP_CTX_ssh_bugs_winadj "config-ssh-bug-winadj" -#define WINHELP_CTX_ssh_bugs_chanreq "config-ssh-bug-chanreq" -#define WINHELP_CTX_ssh_bugs_oldgex2 "config-ssh-bug-oldgex2" -#define WINHELP_CTX_serial_line "config-serial-line" -#define WINHELP_CTX_serial_speed "config-serial-speed" -#define WINHELP_CTX_serial_databits "config-serial-databits" -#define WINHELP_CTX_serial_stopbits "config-serial-stopbits" -#define WINHELP_CTX_serial_parity "config-serial-parity" -#define WINHELP_CTX_serial_flow "config-serial-flow" - -#define WINHELP_CTX_pageant_general "pageant" -#define WINHELP_CTX_pageant_keylist "pageant-mainwin-keylist" -#define WINHELP_CTX_pageant_addkey "pageant-mainwin-addkey" -#define WINHELP_CTX_pageant_remkey "pageant-mainwin-remkey" -#define WINHELP_CTX_pageant_deferred "pageant-deferred-decryption" -#define WINHELP_CTX_pgpfingerprints "pgpkeys" -#define WINHELP_CTX_puttygen_general "pubkey-puttygen" -#define WINHELP_CTX_puttygen_keytype "puttygen-keytype" -#define WINHELP_CTX_puttygen_bits "puttygen-strength" -#define WINHELP_CTX_puttygen_generate "puttygen-generate" -#define WINHELP_CTX_puttygen_fingerprint "puttygen-fingerprint" -#define WINHELP_CTX_puttygen_comment "puttygen-comment" -#define WINHELP_CTX_puttygen_passphrase "puttygen-passphrase" -#define WINHELP_CTX_puttygen_savepriv "puttygen-savepriv" -#define WINHELP_CTX_puttygen_savepub "puttygen-savepub" -#define WINHELP_CTX_puttygen_pastekey "puttygen-pastekey" -#define WINHELP_CTX_puttygen_load "puttygen-load" -#define WINHELP_CTX_puttygen_conversions "puttygen-conversions" -#define WINHELP_CTX_puttygen_ppkver "puttygen-save-ppk-version" -#define WINHELP_CTX_puttygen_kdfparam "puttygen-save-passphrase-hashing" - -/* These are used in Windows-specific bits of the frontend. - * We (ab)use "help context identifiers" (dwContextId) to identify them. */ - -#define HELPCTXID(x) WINHELP_CTXID_ ## x - -#define WINHELP_CTXID_no_help 0 -#define WINHELP_CTX_errors_hostkey_absent "errors-hostkey-absent" -#define WINHELP_CTXID_errors_hostkey_absent 1 -#define WINHELP_CTX_errors_hostkey_changed "errors-hostkey-wrong" -#define WINHELP_CTXID_errors_hostkey_changed 2 -#define WINHELP_CTX_errors_cantloadkey "errors-cant-load-key" -#define WINHELP_CTXID_errors_cantloadkey 3 -#define WINHELP_CTX_option_cleanup "using-cleanup" -#define WINHELP_CTXID_option_cleanup 4 -#define WINHELP_CTX_pgp_fingerprints "pgpkeys" -#define WINHELP_CTXID_pgp_fingerprints 5 diff --git a/WINDOWS/WINJUMP.C b/WINDOWS/WINJUMP.C deleted file mode 100644 index 358504fd..00000000 --- a/WINDOWS/WINJUMP.C +++ /dev/null @@ -1,748 +0,0 @@ -/* - * winjump.c: support for Windows 7 jump lists. - * - * The Windows 7 jumplist is a customizable list defined by the - * application. It is persistent across application restarts: the OS - * maintains the list when the app is not running. The list is shown - * when the user right-clicks on the taskbar button of a running app - * or a pinned non-running application. We use the jumplist to - * maintain a list of recently started saved sessions, started either - * by doubleclicking on a saved session, or with the command line - * "-load" parameter. - * - * Since the jumplist is write-only: it can only be replaced and the - * current list cannot be read, we must maintain the contents of the - * list persistantly in the registry. The file winstore.h contains - * functions to directly manipulate these registry entries. This file - * contains higher level functions to manipulate the jumplist. - */ - -#include <assert.h> - -#include "putty.h" -#include "storage.h" - -#define MAX_JUMPLIST_ITEMS 30 /* PuTTY will never show more items in - * the jumplist than this, regardless of - * user preferences. */ - -/* - * COM structures and functions. - */ -#ifndef PROPERTYKEY_DEFINED -#define PROPERTYKEY_DEFINED -typedef struct _tagpropertykey { - GUID fmtid; - DWORD pid; -} PROPERTYKEY; -#endif -#ifndef _REFPROPVARIANT_DEFINED -#define _REFPROPVARIANT_DEFINED -typedef PROPVARIANT *REFPROPVARIANT; -#endif -/* MinGW doesn't define this yet: */ -#ifndef _PROPVARIANTINIT_DEFINED_ -#define _PROPVARIANTINIT_DEFINED_ -#define PropVariantInit(pvar) memset((pvar),0,sizeof(PROPVARIANT)) -#endif - -#define IID_IShellLink IID_IShellLinkA - -typedef struct ICustomDestinationListVtbl { - HRESULT ( __stdcall *QueryInterface ) ( - /* [in] ICustomDestinationList*/ void *This, - /* [in] */ const GUID * const riid, - /* [out] */ void **ppvObject); - - ULONG ( __stdcall *AddRef )( - /* [in] ICustomDestinationList*/ void *This); - - ULONG ( __stdcall *Release )( - /* [in] ICustomDestinationList*/ void *This); - - HRESULT ( __stdcall *SetAppID )( - /* [in] ICustomDestinationList*/ void *This, - /* [string][in] */ LPCWSTR pszAppID); - - HRESULT ( __stdcall *BeginList )( - /* [in] ICustomDestinationList*/ void *This, - /* [out] */ UINT *pcMinSlots, - /* [in] */ const GUID * const riid, - /* [out] */ void **ppv); - - HRESULT ( __stdcall *AppendCategory )( - /* [in] ICustomDestinationList*/ void *This, - /* [string][in] */ LPCWSTR pszCategory, - /* [in] IObjectArray*/ void *poa); - - HRESULT ( __stdcall *AppendKnownCategory )( - /* [in] ICustomDestinationList*/ void *This, - /* [in] KNOWNDESTCATEGORY*/ int category); - - HRESULT ( __stdcall *AddUserTasks )( - /* [in] ICustomDestinationList*/ void *This, - /* [in] IObjectArray*/ void *poa); - - HRESULT ( __stdcall *CommitList )( - /* [in] ICustomDestinationList*/ void *This); - - HRESULT ( __stdcall *GetRemovedDestinations )( - /* [in] ICustomDestinationList*/ void *This, - /* [in] */ const IID * const riid, - /* [out] */ void **ppv); - - HRESULT ( __stdcall *DeleteList )( - /* [in] ICustomDestinationList*/ void *This, - /* [string][unique][in] */ LPCWSTR pszAppID); - - HRESULT ( __stdcall *AbortList )( - /* [in] ICustomDestinationList*/ void *This); - -} ICustomDestinationListVtbl; - -typedef struct ICustomDestinationList -{ - ICustomDestinationListVtbl *lpVtbl; -} ICustomDestinationList; - -typedef struct IObjectArrayVtbl -{ - HRESULT ( __stdcall *QueryInterface )( - /* [in] IObjectArray*/ void *This, - /* [in] */ const GUID * const riid, - /* [out] */ void **ppvObject); - - ULONG ( __stdcall *AddRef )( - /* [in] IObjectArray*/ void *This); - - ULONG ( __stdcall *Release )( - /* [in] IObjectArray*/ void *This); - - HRESULT ( __stdcall *GetCount )( - /* [in] IObjectArray*/ void *This, - /* [out] */ UINT *pcObjects); - - HRESULT ( __stdcall *GetAt )( - /* [in] IObjectArray*/ void *This, - /* [in] */ UINT uiIndex, - /* [in] */ const GUID * const riid, - /* [out] */ void **ppv); - -} IObjectArrayVtbl; - -typedef struct IObjectArray -{ - IObjectArrayVtbl *lpVtbl; -} IObjectArray; - -typedef struct IShellLinkVtbl -{ - HRESULT ( __stdcall *QueryInterface )( - /* [in] IShellLink*/ void *This, - /* [in] */ const GUID * const riid, - /* [out] */ void **ppvObject); - - ULONG ( __stdcall *AddRef )( - /* [in] IShellLink*/ void *This); - - ULONG ( __stdcall *Release )( - /* [in] IShellLink*/ void *This); - - HRESULT ( __stdcall *GetPath )( - /* [in] IShellLink*/ void *This, - /* [string][out] */ LPSTR pszFile, - /* [in] */ int cch, - /* [unique][out][in] */ WIN32_FIND_DATAA *pfd, - /* [in] */ DWORD fFlags); - - HRESULT ( __stdcall *GetIDList )( - /* [in] IShellLink*/ void *This, - /* [out] LPITEMIDLIST*/ void **ppidl); - - HRESULT ( __stdcall *SetIDList )( - /* [in] IShellLink*/ void *This, - /* [in] LPITEMIDLIST*/ void *pidl); - - HRESULT ( __stdcall *GetDescription )( - /* [in] IShellLink*/ void *This, - /* [string][out] */ LPSTR pszName, - /* [in] */ int cch); - - HRESULT ( __stdcall *SetDescription )( - /* [in] IShellLink*/ void *This, - /* [string][in] */ LPCSTR pszName); - - HRESULT ( __stdcall *GetWorkingDirectory )( - /* [in] IShellLink*/ void *This, - /* [string][out] */ LPSTR pszDir, - /* [in] */ int cch); - - HRESULT ( __stdcall *SetWorkingDirectory )( - /* [in] IShellLink*/ void *This, - /* [string][in] */ LPCSTR pszDir); - - HRESULT ( __stdcall *GetArguments )( - /* [in] IShellLink*/ void *This, - /* [string][out] */ LPSTR pszArgs, - /* [in] */ int cch); - - HRESULT ( __stdcall *SetArguments )( - /* [in] IShellLink*/ void *This, - /* [string][in] */ LPCSTR pszArgs); - - HRESULT ( __stdcall *GetHotkey )( - /* [in] IShellLink*/ void *This, - /* [out] */ WORD *pwHotkey); - - HRESULT ( __stdcall *SetHotkey )( - /* [in] IShellLink*/ void *This, - /* [in] */ WORD wHotkey); - - HRESULT ( __stdcall *GetShowCmd )( - /* [in] IShellLink*/ void *This, - /* [out] */ int *piShowCmd); - - HRESULT ( __stdcall *SetShowCmd )( - /* [in] IShellLink*/ void *This, - /* [in] */ int iShowCmd); - - HRESULT ( __stdcall *GetIconLocation )( - /* [in] IShellLink*/ void *This, - /* [string][out] */ LPSTR pszIconPath, - /* [in] */ int cch, - /* [out] */ int *piIcon); - - HRESULT ( __stdcall *SetIconLocation )( - /* [in] IShellLink*/ void *This, - /* [string][in] */ LPCSTR pszIconPath, - /* [in] */ int iIcon); - - HRESULT ( __stdcall *SetRelativePath )( - /* [in] IShellLink*/ void *This, - /* [string][in] */ LPCSTR pszPathRel, - /* [in] */ DWORD dwReserved); - - HRESULT ( __stdcall *Resolve )( - /* [in] IShellLink*/ void *This, - /* [unique][in] */ HWND hwnd, - /* [in] */ DWORD fFlags); - - HRESULT ( __stdcall *SetPath )( - /* [in] IShellLink*/ void *This, - /* [string][in] */ LPCSTR pszFile); - -} IShellLinkVtbl; - -typedef struct IShellLink -{ - IShellLinkVtbl *lpVtbl; -} IShellLink; - -typedef struct IObjectCollectionVtbl -{ - HRESULT ( __stdcall *QueryInterface )( - /* [in] IShellLink*/ void *This, - /* [in] */ const GUID * const riid, - /* [out] */ void **ppvObject); - - ULONG ( __stdcall *AddRef )( - /* [in] IShellLink*/ void *This); - - ULONG ( __stdcall *Release )( - /* [in] IShellLink*/ void *This); - - HRESULT ( __stdcall *GetCount )( - /* [in] IShellLink*/ void *This, - /* [out] */ UINT *pcObjects); - - HRESULT ( __stdcall *GetAt )( - /* [in] IShellLink*/ void *This, - /* [in] */ UINT uiIndex, - /* [in] */ const GUID * const riid, - /* [iid_is][out] */ void **ppv); - - HRESULT ( __stdcall *AddObject )( - /* [in] IShellLink*/ void *This, - /* [in] */ void *punk); - - HRESULT ( __stdcall *AddFromArray )( - /* [in] IShellLink*/ void *This, - /* [in] */ IObjectArray *poaSource); - - HRESULT ( __stdcall *RemoveObjectAt )( - /* [in] IShellLink*/ void *This, - /* [in] */ UINT uiIndex); - - HRESULT ( __stdcall *Clear )( - /* [in] IShellLink*/ void *This); - -} IObjectCollectionVtbl; - -typedef struct IObjectCollection -{ - IObjectCollectionVtbl *lpVtbl; -} IObjectCollection; - -typedef struct IPropertyStoreVtbl -{ - HRESULT ( __stdcall *QueryInterface )( - /* [in] IPropertyStore*/ void *This, - /* [in] */ const GUID * const riid, - /* [iid_is][out] */ void **ppvObject); - - ULONG ( __stdcall *AddRef )( - /* [in] IPropertyStore*/ void *This); - - ULONG ( __stdcall *Release )( - /* [in] IPropertyStore*/ void *This); - - HRESULT ( __stdcall *GetCount )( - /* [in] IPropertyStore*/ void *This, - /* [out] */ DWORD *cProps); - - HRESULT ( __stdcall *GetAt )( - /* [in] IPropertyStore*/ void *This, - /* [in] */ DWORD iProp, - /* [out] */ PROPERTYKEY *pkey); - - HRESULT ( __stdcall *GetValue )( - /* [in] IPropertyStore*/ void *This, - /* [in] */ const PROPERTYKEY * const key, - /* [out] */ PROPVARIANT *pv); - - HRESULT ( __stdcall *SetValue )( - /* [in] IPropertyStore*/ void *This, - /* [in] */ const PROPERTYKEY * const key, - /* [in] */ REFPROPVARIANT propvar); - - HRESULT ( __stdcall *Commit )( - /* [in] IPropertyStore*/ void *This); -} IPropertyStoreVtbl; - -typedef struct IPropertyStore -{ - IPropertyStoreVtbl *lpVtbl; -} IPropertyStore; - -static const CLSID CLSID_DestinationList = { - 0x77f10cf0, 0x3db5, 0x4966, {0xb5,0x20,0xb7,0xc5,0x4f,0xd3,0x5e,0xd6} -}; -static const CLSID CLSID_ShellLink = { - 0x00021401, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46} -}; -static const CLSID CLSID_EnumerableObjectCollection = { - 0x2d3468c1, 0x36a7, 0x43b6, {0xac,0x24,0xd3,0xf0,0x2f,0xd9,0x60,0x7a} -}; -static const IID IID_IObjectCollection = { - 0x5632b1a4, 0xe38a, 0x400a, {0x92,0x8a,0xd4,0xcd,0x63,0x23,0x02,0x95} -}; -static const IID IID_IShellLink = { - 0x000214ee, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46} -}; -static const IID IID_ICustomDestinationList = { - 0x6332debf, 0x87b5, 0x4670, {0x90,0xc0,0x5e,0x57,0xb4,0x08,0xa4,0x9e} -}; -static const IID IID_IObjectArray = { - 0x92ca9dcd, 0x5622, 0x4bba, {0xa8,0x05,0x5e,0x9f,0x54,0x1b,0xd8,0xc9} -}; -static const IID IID_IPropertyStore = { - 0x886d8eeb, 0x8cf2, 0x4446, {0x8d,0x02,0xcd,0xba,0x1d,0xbd,0xcf,0x99} -}; -static const PROPERTYKEY PKEY_Title = { - {0xf29f85e0, 0x4ff9, 0x1068, {0xab,0x91,0x08,0x00,0x2b,0x27,0xb3,0xd9}}, - 0x00000002 -}; - -/* Type-checking macro to provide arguments for CoCreateInstance() - * etc, ensuring that 'obj' really is a 'type **'. */ -#define typecheck(checkexpr, result) \ - (sizeof(checkexpr) ? (result) : (result)) -#define COMPTR(type, obj) &IID_##type, \ - typecheck((obj)-(type **)(obj), (void **)(void *)(obj)) - -static char putty_path[2048]; - -/* - * Function to make an IShellLink describing a particular PuTTY - * command. If 'appname' is null, the command run will be the one - * returned by GetModuleFileName, i.e. our own executable; if it's - * non-null then it will be assumed to be a filename in the same - * directory as our own executable, and the return value will be NULL - * if that file doesn't exist. - * - * If 'sessionname' is null then no command line will be passed to the - * program. If it's non-null, the command line will be that text - * prefixed with an @ (to load a PuTTY saved session). - * - * Hence, you can launch a saved session using make_shell_link(NULL, - * sessionname), and launch another app using e.g. - * make_shell_link("puttygen.exe", NULL). - */ -static IShellLink *make_shell_link(const char *appname, - const char *sessionname) -{ - IShellLink *ret; - char *app_path, *param_string, *desc_string; - IPropertyStore *pPS; - PROPVARIANT pv; - - /* Retrieve path to executable. */ - if (!putty_path[0]) - GetModuleFileName(NULL, putty_path, sizeof(putty_path) - 1); - if (appname) { - char *p, *q = putty_path; - FILE *fp; - - if ((p = strrchr(q, '\\')) != NULL) q = p+1; - if ((p = strrchr(q, ':')) != NULL) q = p+1; - app_path = dupprintf("%.*s%s", (int)(q - putty_path), putty_path, - appname); - if ((fp = fopen(app_path, "r")) == NULL) { - sfree(app_path); - return NULL; - } - fclose(fp); - } else { - app_path = dupstr(putty_path); - } - - /* Check if this is a valid session, otherwise don't add. */ - if (sessionname) { - settings_r *psettings_tmp = open_settings_r(sessionname); - if (!psettings_tmp) { - sfree(app_path); - return NULL; - } - close_settings_r(psettings_tmp); - } - - /* Create the new item. */ - if (!SUCCEEDED(CoCreateInstance(&CLSID_ShellLink, NULL, - CLSCTX_INPROC_SERVER, - COMPTR(IShellLink, &ret)))) { - sfree(app_path); - return NULL; - } - - /* Set path, parameters, icon and description. */ - ret->lpVtbl->SetPath(ret, app_path); - - if (sessionname) { - /* The leading space is reported to work around a Windows 10 - * behaviour change in which an argument string starting with - * '@' causes the SetArguments method to silently do the wrong - * thing. */ - param_string = dupcat(" @", sessionname); - } else { - param_string = dupstr(""); - } - ret->lpVtbl->SetArguments(ret, param_string); - sfree(param_string); - - if (sessionname) { - desc_string = dupcat("Connect to PuTTY session '", sessionname, "'"); - } else { - assert(appname); - desc_string = dupprintf("Run %.*s", - (int)strcspn(appname, "."), appname); - } - ret->lpVtbl->SetDescription(ret, desc_string); - sfree(desc_string); - - ret->lpVtbl->SetIconLocation(ret, app_path, 0); - - /* To set the link title, we require the property store of the link. */ - if (SUCCEEDED(ret->lpVtbl->QueryInterface(ret, - COMPTR(IPropertyStore, &pPS)))) { - PropVariantInit(&pv); - pv.vt = VT_LPSTR; - if (sessionname) { - pv.pszVal = dupstr(sessionname); - } else { - assert(appname); - pv.pszVal = dupprintf("Run %.*s", - (int)strcspn(appname, "."), appname); - } - pPS->lpVtbl->SetValue(pPS, &PKEY_Title, &pv); - sfree(pv.pszVal); - pPS->lpVtbl->Commit(pPS); - pPS->lpVtbl->Release(pPS); - } - - sfree(app_path); - - return ret; -} - -/* Updates jumplist from registry. */ -static void update_jumplist_from_registry(void) -{ - const char *piterator; - UINT num_items; - int jumplist_counter; - UINT nremoved; - - /* Variables used by the cleanup code must be initialised to NULL, - * so that we don't try to free or release them if they were never - * set up. */ - ICustomDestinationList *pCDL = NULL; - char *pjumplist_reg_entries = NULL; - IObjectCollection *collection = NULL; - IObjectArray *array = NULL; - IShellLink *link = NULL; - IObjectArray *pRemoved = NULL; - bool need_abort = false; - - /* - * Create an ICustomDestinationList: the top-level object which - * deals with jump list management. - */ - if (!SUCCEEDED(CoCreateInstance(&CLSID_DestinationList, NULL, - CLSCTX_INPROC_SERVER, - COMPTR(ICustomDestinationList, &pCDL)))) - goto cleanup; - - /* - * Call its BeginList method to start compiling a list. This gives - * us back 'num_items' (a hint derived from systemwide - * configuration about how many things to put on the list) and - * 'pRemoved' (user configuration about things to leave off the - * list). - */ - if (!SUCCEEDED(pCDL->lpVtbl->BeginList(pCDL, &num_items, - COMPTR(IObjectArray, &pRemoved)))) - goto cleanup; - need_abort = true; - if (!SUCCEEDED(pRemoved->lpVtbl->GetCount(pRemoved, &nremoved))) - nremoved = 0; - - /* - * Create an object collection to form the 'Recent Sessions' - * category on the jump list. - */ - if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection, - NULL, CLSCTX_INPROC_SERVER, - COMPTR(IObjectCollection, &collection)))) - goto cleanup; - - /* - * Go through the jump list entries from the registry and add each - * one to the collection. - */ - pjumplist_reg_entries = get_jumplist_registry_entries(); - piterator = pjumplist_reg_entries; - jumplist_counter = 0; - while (*piterator != '\0' && - (jumplist_counter < min(MAX_JUMPLIST_ITEMS, (int) num_items))) { - link = make_shell_link(NULL, piterator); - if (link) { - UINT i; - bool found; - - /* - * Check that the link isn't in the user-removed list. - */ - for (i = 0, found = false; i < nremoved && !found; i++) { - IShellLink *rlink; - if (SUCCEEDED(pRemoved->lpVtbl->GetAt - (pRemoved, i, COMPTR(IShellLink, &rlink)))) { - char desc1[2048], desc2[2048]; - if (SUCCEEDED(link->lpVtbl->GetDescription - (link, desc1, sizeof(desc1)-1)) && - SUCCEEDED(rlink->lpVtbl->GetDescription - (rlink, desc2, sizeof(desc2)-1)) && - !strcmp(desc1, desc2)) { - found = true; - } - rlink->lpVtbl->Release(rlink); - } - } - - if (!found) { - collection->lpVtbl->AddObject(collection, link); - jumplist_counter++; - } - - link->lpVtbl->Release(link); - link = NULL; - } - piterator += strlen(piterator) + 1; - } - sfree(pjumplist_reg_entries); - pjumplist_reg_entries = NULL; - - /* - * Get the array form of the collection we've just constructed, - * and put it in the jump list. - */ - if (!SUCCEEDED(collection->lpVtbl->QueryInterface - (collection, COMPTR(IObjectArray, &array)))) - goto cleanup; - - pCDL->lpVtbl->AppendCategory(pCDL, L"Recent Sessions", array); - - /* - * Create an object collection to form the 'Tasks' category on the - * jump list. - */ - if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection, - NULL, CLSCTX_INPROC_SERVER, - COMPTR(IObjectCollection, &collection)))) - goto cleanup; - - /* - * Add task entries for PuTTYgen and Pageant. - */ - piterator = "Pageant.exe\0PuTTYgen.exe\0\0"; - while (*piterator != '\0') { - link = make_shell_link(piterator, NULL); - if (link) { - collection->lpVtbl->AddObject(collection, link); - link->lpVtbl->Release(link); - link = NULL; - } - piterator += strlen(piterator) + 1; - } - - /* - * Get the array form of the collection we've just constructed, - * and put it in the jump list. - */ - if (!SUCCEEDED(collection->lpVtbl->QueryInterface - (collection, COMPTR(IObjectArray, &array)))) - goto cleanup; - - pCDL->lpVtbl->AddUserTasks(pCDL, array); - - /* - * Now we can clean up the array and collection variables, so as - * to be able to reuse them. - */ - array->lpVtbl->Release(array); - array = NULL; - collection->lpVtbl->Release(collection); - collection = NULL; - - /* - * Create another object collection to form the user tasks - * category. - */ - if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection, - NULL, CLSCTX_INPROC_SERVER, - COMPTR(IObjectCollection, &collection)))) - goto cleanup; - - /* - * Get the array form of the collection we've just constructed, - * and put it in the jump list. - */ - if (!SUCCEEDED(collection->lpVtbl->QueryInterface - (collection, COMPTR(IObjectArray, &array)))) - goto cleanup; - - pCDL->lpVtbl->AddUserTasks(pCDL, array); - - /* - * Now we can clean up the array and collection variables, so as - * to be able to reuse them. - */ - array->lpVtbl->Release(array); - array = NULL; - collection->lpVtbl->Release(collection); - collection = NULL; - - /* - * Commit the jump list. - */ - pCDL->lpVtbl->CommitList(pCDL); - need_abort = false; - - /* - * Clean up. - */ - cleanup: - if (pRemoved) pRemoved->lpVtbl->Release(pRemoved); - if (pCDL && need_abort) pCDL->lpVtbl->AbortList(pCDL); - if (pCDL) pCDL->lpVtbl->Release(pCDL); - if (collection) collection->lpVtbl->Release(collection); - if (array) array->lpVtbl->Release(array); - if (link) link->lpVtbl->Release(link); - sfree(pjumplist_reg_entries); -} - -/* Clears the entire jumplist. */ -void clear_jumplist(void) -{ - ICustomDestinationList *pCDL; - - if (CoCreateInstance(&CLSID_DestinationList, NULL, CLSCTX_INPROC_SERVER, - COMPTR(ICustomDestinationList, &pCDL)) == S_OK) { - pCDL->lpVtbl->DeleteList(pCDL, NULL); - pCDL->lpVtbl->Release(pCDL); - } - -} - -/* Adds a saved session to the Windows 7 jumplist. */ -void add_session_to_jumplist(const char * const sessionname) -{ - if ((osMajorVersion < 6) || (osMajorVersion == 6 && osMinorVersion < 1)) - return; /* do nothing on pre-Win7 systems */ - - if (add_to_jumplist_registry(sessionname) == JUMPLISTREG_OK) { - update_jumplist_from_registry(); - } else { - /* Make sure we don't leave the jumplist dangling. */ - clear_jumplist(); - } -} - -/* Removes a saved session from the Windows jumplist. */ -void remove_session_from_jumplist(const char * const sessionname) -{ - if ((osMajorVersion < 6) || (osMajorVersion == 6 && osMinorVersion < 1)) - return; /* do nothing on pre-Win7 systems */ - - if (remove_from_jumplist_registry(sessionname) == JUMPLISTREG_OK) { - update_jumplist_from_registry(); - } else { - /* Make sure we don't leave the jumplist dangling. */ - clear_jumplist(); - } -} - -/* Set Explicit App User Model Id to fix removable media error with - jump lists */ - -bool set_explicit_app_user_model_id(void) -{ - DECL_WINDOWS_FUNCTION(static, HRESULT, SetCurrentProcessExplicitAppUserModelID, - (PCWSTR)); - - static HMODULE shell32_module = 0; - - if (!shell32_module) - { - shell32_module = load_system32_dll("Shell32.dll"); - /* - * We can't typecheck this function here, because it's defined - * in <shobjidl.h>, which we're not including due to clashes - * with all the manual-COM machinery above. - */ - GET_WINDOWS_FUNCTION_NO_TYPECHECK( - shell32_module, SetCurrentProcessExplicitAppUserModelID); - } - - if (p_SetCurrentProcessExplicitAppUserModelID) - { - if (p_SetCurrentProcessExplicitAppUserModelID(L"SimonTatham.PuTTY") == S_OK) - { - return true; - } - return false; - } - /* Function doesn't exist, which is ok for Pre-7 systems */ - - return true; - -} diff --git a/WINDOWS/WINMISC.C b/WINDOWS/WINMISC.C deleted file mode 100644 index 759df011..00000000 --- a/WINDOWS/WINMISC.C +++ /dev/null @@ -1,467 +0,0 @@ -/* - * winmisc.c: miscellaneous Windows-specific things - */ - -#include <stdio.h> -#include <stdlib.h> -#include <limits.h> -#include "putty.h" -#ifndef SECURITY_WIN32 -#define SECURITY_WIN32 -#endif -#include <security.h> - -DWORD osMajorVersion, osMinorVersion, osPlatformId; - -char *platform_get_x_display(void) { - /* We may as well check for DISPLAY in case it's useful. */ - return dupstr(getenv("DISPLAY")); -} - -Filename *filename_from_str(const char *str) -{ - Filename *ret = snew(Filename); - ret->path = dupstr(str); - return ret; -} - -Filename *filename_copy(const Filename *fn) -{ - return filename_from_str(fn->path); -} - -const char *filename_to_str(const Filename *fn) -{ - return fn->path; -} - -bool filename_equal(const Filename *f1, const Filename *f2) -{ - return !strcmp(f1->path, f2->path); -} - -bool filename_is_null(const Filename *fn) -{ - return !*fn->path; -} - -void filename_free(Filename *fn) -{ - sfree(fn->path); - sfree(fn); -} - -void filename_serialise(BinarySink *bs, const Filename *f) -{ - put_asciz(bs, f->path); -} -Filename *filename_deserialise(BinarySource *src) -{ - return filename_from_str(get_asciz(src)); -} - -char filename_char_sanitise(char c) -{ - if (strchr("<>:\"/\\|?*", c)) - return '.'; - return c; -} - -char *get_username(void) -{ - DWORD namelen; - char *user; - bool got_username = false; - DECL_WINDOWS_FUNCTION(static, BOOLEAN, GetUserNameExA, - (EXTENDED_NAME_FORMAT, LPSTR, PULONG)); - - { - static bool tried_usernameex = false; - if (!tried_usernameex) { - /* Not available on Win9x, so load dynamically */ - HMODULE secur32 = load_system32_dll("secur32.dll"); - /* If MIT Kerberos is installed, the following call to - GET_WINDOWS_FUNCTION makes Windows implicitly load - sspicli.dll WITHOUT proper path sanitizing, so better - load it properly before */ - HMODULE sspicli = load_system32_dll("sspicli.dll"); - (void)sspicli; /* squash compiler warning about unused variable */ - GET_WINDOWS_FUNCTION(secur32, GetUserNameExA); - tried_usernameex = true; - } - } - - if (p_GetUserNameExA) { - /* - * If available, use the principal -- this avoids the problem - * that the local username is case-insensitive but Kerberos - * usernames are case-sensitive. - */ - - /* Get the length */ - namelen = 0; - (void) p_GetUserNameExA(NameUserPrincipal, NULL, &namelen); - - user = snewn(namelen, char); - got_username = p_GetUserNameExA(NameUserPrincipal, user, &namelen); - if (got_username) { - char *p = strchr(user, '@'); - if (p) *p = 0; - } else { - sfree(user); - } - } - - if (!got_username) { - /* Fall back to local user name */ - namelen = 0; - if (!GetUserName(NULL, &namelen)) { - /* - * Apparently this doesn't work at least on Windows XP SP2. - * Thus assume a maximum of 256. It will fail again if it - * doesn't fit. - */ - namelen = 256; - } - - user = snewn(namelen, char); - got_username = GetUserName(user, &namelen); - if (!got_username) { - sfree(user); - } - } - - return got_username ? user : NULL; -} - -void dll_hijacking_protection(void) -{ - /* - * If the OS provides it, call SetDefaultDllDirectories() to - * prevent DLLs from being loaded from the directory containing - * our own binary, and instead only load from system32. - * - * This is a protection against hijacking attacks, if someone runs - * PuTTY directly from their web browser's download directory - * having previously been enticed into clicking on an unwise link - * that downloaded a malicious DLL to the same directory under one - * of various magic names that seem to be things that standard - * Windows DLLs delegate to. - * - * It shouldn't break deliberate loading of user-provided DLLs - * such as GSSAPI providers, because those are specified by their - * full pathname by the user-provided configuration. - */ - static HMODULE kernel32_module; - DECL_WINDOWS_FUNCTION(static, BOOL, SetDefaultDllDirectories, (DWORD)); - - if (!kernel32_module) { - kernel32_module = load_system32_dll("kernel32.dll"); -#if (defined _MSC_VER && _MSC_VER < 1900) - /* For older Visual Studio, this function isn't available in - * the header files to type-check */ - GET_WINDOWS_FUNCTION_NO_TYPECHECK( - kernel32_module, SetDefaultDllDirectories); -#else - GET_WINDOWS_FUNCTION(kernel32_module, SetDefaultDllDirectories); -#endif - } - - if (p_SetDefaultDllDirectories) { - /* LOAD_LIBRARY_SEARCH_SYSTEM32 and explicitly specified - * directories only */ - p_SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32 | - LOAD_LIBRARY_SEARCH_USER_DIRS); - } -} - -void init_winver(void) -{ - OSVERSIONINFO osVersion; - static HMODULE kernel32_module; - DECL_WINDOWS_FUNCTION(static, BOOL, GetVersionExA, (LPOSVERSIONINFO)); - - if (!kernel32_module) { - kernel32_module = load_system32_dll("kernel32.dll"); - /* Deliberately don't type-check this function, because that - * would involve using its declaration in a header file which - * triggers a deprecation warning. I know it's deprecated (see - * below) and don't need telling. */ - GET_WINDOWS_FUNCTION_NO_TYPECHECK(kernel32_module, GetVersionExA); - } - - ZeroMemory(&osVersion, sizeof(osVersion)); - osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); - if (p_GetVersionExA && p_GetVersionExA(&osVersion)) { - osMajorVersion = osVersion.dwMajorVersion; - osMinorVersion = osVersion.dwMinorVersion; - osPlatformId = osVersion.dwPlatformId; - } else { - /* - * GetVersionEx is deprecated, so allow for it perhaps going - * away in future API versions. If it's not there, simply - * assume that's because Windows is too _new_, so fill in the - * variables we care about to a value that will always compare - * higher than any given test threshold. - * - * Normally we should be checking against the presence of a - * specific function if possible in any case. - */ - osMajorVersion = osMinorVersion = UINT_MAX; /* a very high number */ - osPlatformId = VER_PLATFORM_WIN32_NT; /* not Win32s or Win95-like */ - } -} - -HMODULE load_system32_dll(const char *libname) -{ - /* - * Wrapper function to load a DLL out of c:\windows\system32 - * without going through the full DLL search path. (Hence no - * attack is possible by placing a substitute DLL earlier on that - * path.) - */ - static char *sysdir = NULL; - static size_t sysdirsize = 0; - char *fullpath; - HMODULE ret; - - if (!sysdir) { - size_t len; - while ((len = GetSystemDirectory(sysdir, sysdirsize)) >= sysdirsize) - sgrowarray(sysdir, sysdirsize, len); - } - - fullpath = dupcat(sysdir, "\\", libname); - ret = LoadLibrary(fullpath); - sfree(fullpath); - return ret; -} - -/* - * A tree234 containing mappings from system error codes to strings. - */ - -struct errstring { - int error; - char *text; -}; - -static int errstring_find(void *av, void *bv) -{ - int *a = (int *)av; - struct errstring *b = (struct errstring *)bv; - if (*a < b->error) - return -1; - if (*a > b->error) - return +1; - return 0; -} -static int errstring_compare(void *av, void *bv) -{ - struct errstring *a = (struct errstring *)av; - return errstring_find(&a->error, bv); -} - -static tree234 *errstrings = NULL; - -const char *win_strerror(int error) -{ - struct errstring *es; - - if (!errstrings) - errstrings = newtree234(errstring_compare); - - es = find234(errstrings, &error, errstring_find); - - if (!es) { - char msgtext[65536]; /* maximum size for FormatMessage is 64K */ - - es = snew(struct errstring); - es->error = error; - if (!FormatMessage((FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS), NULL, error, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - msgtext, lenof(msgtext)-1, NULL)) { - sprintf(msgtext, - "(unable to format: FormatMessage returned %u)", - (unsigned int)GetLastError()); - } else { - int len = strlen(msgtext); - if (len > 0 && msgtext[len-1] == '\n') - msgtext[len-1] = '\0'; - } - es->text = dupprintf("Error %d: %s", error, msgtext); - add234(errstrings, es); - } - - return es->text; -} - -FontSpec *fontspec_new(const char *name, bool bold, int height, int charset) -{ - FontSpec *f = snew(FontSpec); - f->name = dupstr(name); - f->isbold = bold; - f->height = height; - f->charset = charset; - return f; -} -FontSpec *fontspec_copy(const FontSpec *f) -{ - return fontspec_new(f->name, f->isbold, f->height, f->charset); -} -void fontspec_free(FontSpec *f) -{ - sfree(f->name); - sfree(f); -} -void fontspec_serialise(BinarySink *bs, FontSpec *f) -{ - put_asciz(bs, f->name); - put_uint32(bs, f->isbold); - put_uint32(bs, f->height); - put_uint32(bs, f->charset); -} -FontSpec *fontspec_deserialise(BinarySource *src) -{ - const char *name = get_asciz(src); - unsigned isbold = get_uint32(src); - unsigned height = get_uint32(src); - unsigned charset = get_uint32(src); - return fontspec_new(name, isbold, height, charset); -} - -bool open_for_write_would_lose_data(const Filename *fn) -{ - WIN32_FILE_ATTRIBUTE_DATA attrs; - if (!GetFileAttributesEx(fn->path, GetFileExInfoStandard, &attrs)) { - /* - * Generally, if we don't identify a specific reason why we - * should return true from this function, we return false, and - * let the subsequent attempt to open the file for real give a - * more useful error message. - */ - return false; - } - if (attrs.dwFileAttributes & (FILE_ATTRIBUTE_DEVICE | - FILE_ATTRIBUTE_DIRECTORY)) { - /* - * File is something other than an ordinary disk file, so - * opening it for writing will not cause truncation. (It may - * not _succeed_ either, but that's not our problem here!) - */ - return false; - } - if (attrs.nFileSizeHigh == 0 && attrs.nFileSizeLow == 0) { - /* - * File is zero-length (or may be a named pipe, which - * dwFileAttributes can't tell apart from a regular file), so - * opening it for writing won't truncate any data away because - * there's nothing to truncate anyway. - */ - return false; - } - return true; -} - -void escape_registry_key(const char *in, strbuf *out) -{ - bool candot = false; - static const char hex[16] = "0123456789ABCDEF"; - - while (*in) { - if (*in == ' ' || *in == '\\' || *in == '*' || *in == '?' || - *in == '%' || *in < ' ' || *in > '~' || (*in == '.' - && !candot)) { - put_byte(out, '%'); - put_byte(out, hex[((unsigned char) *in) >> 4]); - put_byte(out, hex[((unsigned char) *in) & 15]); - } else - put_byte(out, *in); - in++; - candot = true; - } -} - -void unescape_registry_key(const char *in, strbuf *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); - - put_byte(out, (i << 4) + j); - in += 3; - } else { - put_byte(out, *in++); - } - } -} - -#ifdef DEBUG -static FILE *debug_fp = NULL; -static HANDLE debug_hdl = INVALID_HANDLE_VALUE; -static int debug_got_console = 0; - -void dputs(const char *buf) -{ - DWORD dw; - - if (!debug_got_console) { - if (AllocConsole()) { - debug_got_console = 1; - debug_hdl = GetStdHandle(STD_OUTPUT_HANDLE); - } - } - if (!debug_fp) { - debug_fp = fopen("debug.log", "w"); - } - - if (debug_hdl != INVALID_HANDLE_VALUE) { - WriteFile(debug_hdl, buf, strlen(buf), &dw, NULL); - } - fputs(buf, debug_fp); - fflush(debug_fp); -} -#endif - -char *registry_get_string(HKEY root, const char *path, const char *leaf) -{ - HKEY key = root; - bool need_close_key = false; - char *toret = NULL, *str = NULL; - - if (path) { - if (RegCreateKey(key, path, &key) != ERROR_SUCCESS) - goto out; - need_close_key = true; - } - - DWORD type, size; - if (RegQueryValueEx(key, leaf, 0, &type, NULL, &size) != ERROR_SUCCESS) - goto out; - if (type != REG_SZ) - goto out; - - str = snewn(size + 1, char); - DWORD size_got = size; - if (RegQueryValueEx(key, leaf, 0, &type, (LPBYTE)str, - &size_got) != ERROR_SUCCESS) - goto out; - if (type != REG_SZ || size_got > size) - goto out; - str[size_got] = '\0'; - - toret = str; - str = NULL; - - out: - if (need_close_key) - RegCloseKey(key); - sfree(str); - return toret; -} diff --git a/WINDOWS/WINNET.C b/WINDOWS/WINNET.C deleted file mode 100644 index 3b4da3cc..00000000 --- a/WINDOWS/WINNET.C +++ /dev/null @@ -1,1825 +0,0 @@ -/* - * Windows networking abstraction. - * - * For the IPv6 code in here I am indebted to Jeroen Massar and - * unfix.org. - */ - -#include <winsock2.h> /* need to put this first, for winelib builds */ - -#include <stdio.h> -#include <stdlib.h> -#include <assert.h> - -#define NEED_DECLARATION_OF_SELECT /* in order to initialise it */ - -#include "putty.h" -#include "network.h" -#include "tree234.h" -#include "ssh.h" - -#include <ws2tcpip.h> - -#ifndef NO_IPV6 -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wmissing-braces" -#endif -const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; -const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; -#ifdef __clang__ -#pragma clang diagnostic pop -#endif -#endif - -#define ipv4_is_loopback(addr) \ - ((p_ntohl(addr.s_addr) & 0xFF000000L) == 0x7F000000L) - -/* - * Mutable state that goes with a SockAddr: stores information - * about where in the list of candidate IP(v*) addresses we've - * currently got to. - */ -typedef struct SockAddrStep_tag SockAddrStep; -struct SockAddrStep_tag { -#ifndef NO_IPV6 - struct addrinfo *ai; /* steps along addr->ais */ -#endif - int curraddr; -}; - -typedef struct NetSocket NetSocket; -struct NetSocket { - const char *error; - SOCKET s; - Plug *plug; - bufchain output_data; - bool connected; - bool writable; - bool frozen; /* this causes readability notifications to be ignored */ - bool frozen_readable; /* this means we missed at least one readability - * notification while we were frozen */ - bool localhost_only; /* for listening sockets */ - char oobdata[1]; - size_t sending_oob; - bool oobinline, nodelay, keepalive, privport; - enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof; - SockAddr *addr; - SockAddrStep step; - int port; - int pending_error; /* in case send() returns error */ - /* - * We sometimes need pairs of Socket structures to be linked: - * if we are listening on the same IPv6 and v4 port, for - * example. So here we define `parent' and `child' pointers to - * track this link. - */ - NetSocket *parent, *child; - - Socket sock; -}; - -struct SockAddr { - int refcount; - char *error; - bool resolved; - bool namedpipe; /* indicates that this SockAddr is phony, holding a Windows - * named pipe pathname instead of a network address */ -#ifndef NO_IPV6 - struct addrinfo *ais; /* Addresses IPv6 style. */ -#endif - unsigned long *addresses; /* Addresses IPv4 style. */ - int naddresses; - char hostname[512]; /* Store an unresolved host name. */ -}; - -/* - * Which address family this address belongs to. AF_INET for IPv4; - * AF_INET6 for IPv6; AF_UNSPEC indicates that name resolution has - * not been done and a simple host name is held in this SockAddr - * structure. - */ -#ifndef NO_IPV6 -#define SOCKADDR_FAMILY(addr, step) \ - (!(addr)->resolved ? AF_UNSPEC : \ - (step).ai ? (step).ai->ai_family : AF_INET) -#else -#define SOCKADDR_FAMILY(addr, step) \ - (!(addr)->resolved ? AF_UNSPEC : AF_INET) -#endif - -/* - * Start a SockAddrStep structure to step through multiple - * addresses. - */ -#ifndef NO_IPV6 -#define START_STEP(addr, step) \ - ((step).ai = (addr)->ais, (step).curraddr = 0) -#else -#define START_STEP(addr, step) \ - ((step).curraddr = 0) -#endif - -static tree234 *sktree; - -static int cmpfortree(void *av, void *bv) -{ - NetSocket *a = (NetSocket *)av, *b = (NetSocket *)bv; - uintptr_t as = (uintptr_t) a->s, bs = (uintptr_t) b->s; - if (as < bs) - return -1; - if (as > bs) - return +1; - if (a < b) - return -1; - if (a > b) - return +1; - return 0; -} - -static int cmpforsearch(void *av, void *bv) -{ - NetSocket *b = (NetSocket *)bv; - uintptr_t as = (uintptr_t) av, bs = (uintptr_t) b->s; - if (as < bs) - return -1; - if (as > bs) - return +1; - return 0; -} - -DECL_WINDOWS_FUNCTION(static, int, WSAStartup, (WORD, LPWSADATA)); -DECL_WINDOWS_FUNCTION(static, int, WSACleanup, (void)); -DECL_WINDOWS_FUNCTION(static, int, closesocket, (SOCKET)); -DECL_WINDOWS_FUNCTION(static, u_long, ntohl, (u_long)); -DECL_WINDOWS_FUNCTION(static, u_long, htonl, (u_long)); -DECL_WINDOWS_FUNCTION(static, u_short, htons, (u_short)); -DECL_WINDOWS_FUNCTION(static, u_short, ntohs, (u_short)); -DECL_WINDOWS_FUNCTION(static, int, gethostname, (char *, int)); -DECL_WINDOWS_FUNCTION(static, struct hostent FAR *, gethostbyname, - (const char FAR *)); -DECL_WINDOWS_FUNCTION(static, struct servent FAR *, getservbyname, - (const char FAR *, const char FAR *)); -DECL_WINDOWS_FUNCTION(static, unsigned long, inet_addr, (const char FAR *)); -DECL_WINDOWS_FUNCTION(static, char FAR *, inet_ntoa, (struct in_addr)); -DECL_WINDOWS_FUNCTION(static, const char FAR *, inet_ntop, - (int, void FAR *, char *, size_t)); -DECL_WINDOWS_FUNCTION(static, int, connect, - (SOCKET, const struct sockaddr FAR *, int)); -DECL_WINDOWS_FUNCTION(static, int, bind, - (SOCKET, const struct sockaddr FAR *, int)); -DECL_WINDOWS_FUNCTION(static, int, setsockopt, - (SOCKET, int, int, const char FAR *, int)); -DECL_WINDOWS_FUNCTION(static, SOCKET, socket, (int, int, int)); -DECL_WINDOWS_FUNCTION(static, int, listen, (SOCKET, int)); -DECL_WINDOWS_FUNCTION(static, int, send, (SOCKET, const char FAR *, int, int)); -DECL_WINDOWS_FUNCTION(static, int, shutdown, (SOCKET, int)); -DECL_WINDOWS_FUNCTION(static, int, ioctlsocket, - (SOCKET, long, u_long FAR *)); -DECL_WINDOWS_FUNCTION(static, SOCKET, accept, - (SOCKET, struct sockaddr FAR *, int FAR *)); -DECL_WINDOWS_FUNCTION(static, int, getpeername, - (SOCKET, struct sockaddr FAR *, int FAR *)); -DECL_WINDOWS_FUNCTION(static, int, recv, (SOCKET, char FAR *, int, int)); -DECL_WINDOWS_FUNCTION(static, int, WSAIoctl, - (SOCKET, DWORD, LPVOID, DWORD, LPVOID, DWORD, - LPDWORD, LPWSAOVERLAPPED, - LPWSAOVERLAPPED_COMPLETION_ROUTINE)); -#ifndef NO_IPV6 -DECL_WINDOWS_FUNCTION(static, int, getaddrinfo, - (const char *nodename, const char *servname, - const struct addrinfo *hints, struct addrinfo **res)); -DECL_WINDOWS_FUNCTION(static, void, freeaddrinfo, (struct addrinfo *res)); -DECL_WINDOWS_FUNCTION(static, int, getnameinfo, - (const struct sockaddr FAR * sa, socklen_t salen, - char FAR * host, DWORD hostlen, char FAR * serv, - DWORD servlen, int flags)); -DECL_WINDOWS_FUNCTION(static, char *, gai_strerror, (int ecode)); -DECL_WINDOWS_FUNCTION(static, int, WSAAddressToStringA, - (LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFO, - LPSTR, LPDWORD)); -#endif - -static HMODULE winsock_module = NULL; -static WSADATA wsadata; -#ifndef NO_IPV6 -static HMODULE winsock2_module = NULL; -static HMODULE wship6_module = NULL; -#endif - -static bool sk_startup(int hi, int lo) -{ - WORD winsock_ver; - - winsock_ver = MAKEWORD(hi, lo); - - if (p_WSAStartup(winsock_ver, &wsadata)) { - return false; - } - - if (LOBYTE(wsadata.wVersion) != LOBYTE(winsock_ver)) { - return false; - } - - return true; -} - -DEF_WINDOWS_FUNCTION(WSAAsyncSelect); -DEF_WINDOWS_FUNCTION(WSAEventSelect); -DEF_WINDOWS_FUNCTION(WSAGetLastError); -DEF_WINDOWS_FUNCTION(WSAEnumNetworkEvents); -DEF_WINDOWS_FUNCTION(select); - -void sk_init(void) -{ -#ifndef NO_IPV6 - winsock2_module = -#endif - winsock_module = load_system32_dll("ws2_32.dll"); - if (!winsock_module) { - winsock_module = load_system32_dll("wsock32.dll"); - } - if (!winsock_module) - modalfatalbox("Unable to load any WinSock library"); - -#ifndef NO_IPV6 - /* Check if we have getaddrinfo in Winsock */ - if (GetProcAddress(winsock_module, "getaddrinfo") != NULL) { - GET_WINDOWS_FUNCTION(winsock_module, getaddrinfo); - GET_WINDOWS_FUNCTION(winsock_module, freeaddrinfo); - GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, getnameinfo); - /* This function would fail its type-check if we did one, - * because the VS header file provides an inline definition - * which is __cdecl instead of WINAPI. */ - GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, gai_strerror); - } else { - /* Fall back to wship6.dll for Windows 2000 */ - wship6_module = load_system32_dll("wship6.dll"); - if (wship6_module) { - GET_WINDOWS_FUNCTION(wship6_module, getaddrinfo); - GET_WINDOWS_FUNCTION(wship6_module, freeaddrinfo); - /* See comment above about type check */ - GET_WINDOWS_FUNCTION_NO_TYPECHECK(wship6_module, getnameinfo); - GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, gai_strerror); - } else { - } - } - GET_WINDOWS_FUNCTION(winsock2_module, WSAAddressToStringA); -#endif - - GET_WINDOWS_FUNCTION(winsock_module, WSAAsyncSelect); - GET_WINDOWS_FUNCTION(winsock_module, WSAEventSelect); - /* We don't type-check select because at least some MinGW versions - * of the Windows API headers seem to disagree with the - * documentation on whether the 'struct timeval *' pointer is - * const or not. */ - GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, select); - GET_WINDOWS_FUNCTION(winsock_module, WSAGetLastError); - GET_WINDOWS_FUNCTION(winsock_module, WSAEnumNetworkEvents); - GET_WINDOWS_FUNCTION(winsock_module, WSAStartup); - GET_WINDOWS_FUNCTION(winsock_module, WSACleanup); - GET_WINDOWS_FUNCTION(winsock_module, closesocket); - GET_WINDOWS_FUNCTION(winsock_module, ntohl); - GET_WINDOWS_FUNCTION(winsock_module, htonl); - GET_WINDOWS_FUNCTION(winsock_module, htons); - GET_WINDOWS_FUNCTION(winsock_module, ntohs); - GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, gethostname); - GET_WINDOWS_FUNCTION(winsock_module, gethostbyname); - GET_WINDOWS_FUNCTION(winsock_module, getservbyname); - GET_WINDOWS_FUNCTION(winsock_module, inet_addr); - GET_WINDOWS_FUNCTION(winsock_module, inet_ntoa); - /* Older Visual Studio, and MinGW as of Ubuntu 16.04, don't know - * about this function at all, so can't type-check it. Also there - * seems to be some disagreement in the VS headers about whether - * the second argument is void * or const void *, so I omit the - * type check. */ - GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, inet_ntop); - GET_WINDOWS_FUNCTION(winsock_module, connect); - GET_WINDOWS_FUNCTION(winsock_module, bind); - GET_WINDOWS_FUNCTION(winsock_module, setsockopt); - GET_WINDOWS_FUNCTION(winsock_module, socket); - GET_WINDOWS_FUNCTION(winsock_module, listen); - GET_WINDOWS_FUNCTION(winsock_module, send); - GET_WINDOWS_FUNCTION(winsock_module, shutdown); - GET_WINDOWS_FUNCTION(winsock_module, ioctlsocket); - GET_WINDOWS_FUNCTION(winsock_module, accept); - GET_WINDOWS_FUNCTION(winsock_module, getpeername); - GET_WINDOWS_FUNCTION(winsock_module, recv); - GET_WINDOWS_FUNCTION(winsock_module, WSAIoctl); - - /* Try to get the best WinSock version we can get */ - if (!sk_startup(2,2) && - !sk_startup(2,0) && - !sk_startup(1,1)) { - modalfatalbox("Unable to initialise WinSock"); - } - - sktree = newtree234(cmpfortree); -} - -void sk_cleanup(void) -{ - NetSocket *s; - int i; - - if (sktree) { - for (i = 0; (s = index234(sktree, i)) != NULL; i++) { - p_closesocket(s->s); - } - freetree234(sktree); - sktree = NULL; - } - - if (p_WSACleanup) - p_WSACleanup(); - if (winsock_module) - FreeLibrary(winsock_module); -#ifndef NO_IPV6 - if (wship6_module) - FreeLibrary(wship6_module); -#endif -} - -const char *winsock_error_string(int error) -{ - /* - * Error codes we know about and have historically had reasonably - * sensible error messages for. - */ - switch (error) { - case WSAEACCES: - return "Network error: Permission denied"; - case WSAEADDRINUSE: - return "Network error: Address already in use"; - case WSAEADDRNOTAVAIL: - return "Network error: Cannot assign requested address"; - case WSAEAFNOSUPPORT: - return - "Network error: Address family not supported by protocol family"; - case WSAEALREADY: - return "Network error: Operation already in progress"; - case WSAECONNABORTED: - return "Network error: Software caused connection abort"; - case WSAECONNREFUSED: - return "Network error: Connection refused"; - case WSAECONNRESET: - return "Network error: Connection reset by peer"; - case WSAEDESTADDRREQ: - return "Network error: Destination address required"; - case WSAEFAULT: - return "Network error: Bad address"; - case WSAEHOSTDOWN: - return "Network error: Host is down"; - case WSAEHOSTUNREACH: - return "Network error: No route to host"; - case WSAEINPROGRESS: - return "Network error: Operation now in progress"; - case WSAEINTR: - return "Network error: Interrupted function call"; - case WSAEINVAL: - return "Network error: Invalid argument"; - case WSAEISCONN: - return "Network error: Socket is already connected"; - case WSAEMFILE: - return "Network error: Too many open files"; - case WSAEMSGSIZE: - return "Network error: Message too long"; - case WSAENETDOWN: - return "Network error: Network is down"; - case WSAENETRESET: - return "Network error: Network dropped connection on reset"; - case WSAENETUNREACH: - return "Network error: Network is unreachable"; - case WSAENOBUFS: - return "Network error: No buffer space available"; - case WSAENOPROTOOPT: - return "Network error: Bad protocol option"; - case WSAENOTCONN: - return "Network error: Socket is not connected"; - case WSAENOTSOCK: - return "Network error: Socket operation on non-socket"; - case WSAEOPNOTSUPP: - return "Network error: Operation not supported"; - case WSAEPFNOSUPPORT: - return "Network error: Protocol family not supported"; - case WSAEPROCLIM: - return "Network error: Too many processes"; - case WSAEPROTONOSUPPORT: - return "Network error: Protocol not supported"; - case WSAEPROTOTYPE: - return "Network error: Protocol wrong type for socket"; - case WSAESHUTDOWN: - return "Network error: Cannot send after socket shutdown"; - case WSAESOCKTNOSUPPORT: - return "Network error: Socket type not supported"; - case WSAETIMEDOUT: - return "Network error: Connection timed out"; - case WSAEWOULDBLOCK: - return "Network error: Resource temporarily unavailable"; - case WSAEDISCON: - return "Network error: Graceful shutdown in progress"; - } - - /* - * Handle any other error code by delegating to win_strerror. - */ - return win_strerror(error); -} - -SockAddr *sk_namelookup(const char *host, char **canonicalname, - int address_family) -{ - SockAddr *ret = snew(SockAddr); - unsigned long a; - char realhost[8192]; - int hint_family; - - /* Default to IPv4. */ - hint_family = (address_family == ADDRTYPE_IPV4 ? AF_INET : -#ifndef NO_IPV6 - address_family == ADDRTYPE_IPV6 ? AF_INET6 : -#endif - AF_UNSPEC); - - /* Clear the structure and default to IPv4. */ - memset(ret, 0, sizeof(SockAddr)); -#ifndef NO_IPV6 - ret->ais = NULL; -#endif - ret->namedpipe = false; - ret->addresses = NULL; - ret->resolved = false; - ret->refcount = 1; - *realhost = '\0'; - - if ((a = p_inet_addr(host)) == (unsigned long) INADDR_NONE) { - struct hostent *h = NULL; - int err = 0; -#ifndef NO_IPV6 - /* - * Use getaddrinfo when it's available - */ - if (p_getaddrinfo) { - struct addrinfo hints; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = hint_family; - hints.ai_flags = AI_CANONNAME; - { - /* strip [] on IPv6 address literals */ - char *trimmed_host = host_strduptrim(host); - err = p_getaddrinfo(trimmed_host, NULL, &hints, &ret->ais); - sfree(trimmed_host); - } - if (err == 0) - ret->resolved = true; - } else -#endif - { - /* - * Otherwise use the IPv4-only gethostbyname... - * (NOTE: we don't use gethostbyname as a fallback!) - */ - if ( (h = p_gethostbyname(host)) ) - ret->resolved = true; - else - err = p_WSAGetLastError(); - } - - if (!ret->resolved) { - ret->error = (err == WSAENETDOWN ? "Network is down" : - err == WSAHOST_NOT_FOUND ? "Host does not exist" : - err == WSATRY_AGAIN ? "Host not found" : -#ifndef NO_IPV6 - p_getaddrinfo&&p_gai_strerror ? p_gai_strerror(err) : -#endif - "gethostbyname: unknown error"); - } else { - ret->error = NULL; - -#ifndef NO_IPV6 - /* If we got an address info use that... */ - if (ret->ais) { - /* Are we in IPv4 fallback mode? */ - /* We put the IPv4 address into the a variable so we can further-on use the IPv4 code... */ - if (ret->ais->ai_family == AF_INET) - memcpy(&a, - (char *) &((SOCKADDR_IN *) ret->ais-> - ai_addr)->sin_addr, sizeof(a)); - - if (ret->ais->ai_canonname) - strncpy(realhost, ret->ais->ai_canonname, lenof(realhost)); - else - strncpy(realhost, host, lenof(realhost)); - } - /* We used the IPv4-only gethostbyname()... */ - else -#endif - { - int n; - for (n = 0; h->h_addr_list[n]; n++); - ret->addresses = snewn(n, unsigned long); - ret->naddresses = n; - for (n = 0; n < ret->naddresses; n++) { - memcpy(&a, h->h_addr_list[n], sizeof(a)); - ret->addresses[n] = p_ntohl(a); - } - memcpy(&a, h->h_addr, sizeof(a)); - /* This way we are always sure the h->h_name is valid :) */ - strncpy(realhost, h->h_name, sizeof(realhost)); - } - } - } else { - /* - * This must be a numeric IPv4 address because it caused a - * success return from inet_addr. - */ - ret->addresses = snewn(1, unsigned long); - ret->naddresses = 1; - ret->addresses[0] = p_ntohl(a); - ret->resolved = true; - strncpy(realhost, host, sizeof(realhost)); - } - realhost[lenof(realhost)-1] = '\0'; - *canonicalname = dupstr(realhost); - return ret; -} - -SockAddr *sk_nonamelookup(const char *host) -{ - SockAddr *ret = snew(SockAddr); - ret->error = NULL; - ret->resolved = false; -#ifndef NO_IPV6 - ret->ais = NULL; -#endif - ret->namedpipe = false; - ret->addresses = NULL; - ret->naddresses = 0; - ret->refcount = 1; - strncpy(ret->hostname, host, lenof(ret->hostname)); - ret->hostname[lenof(ret->hostname)-1] = '\0'; - return ret; -} - -SockAddr *sk_namedpipe_addr(const char *pipename) -{ - SockAddr *ret = snew(SockAddr); - ret->error = NULL; - ret->resolved = false; -#ifndef NO_IPV6 - ret->ais = NULL; -#endif - ret->namedpipe = true; - ret->addresses = NULL; - ret->naddresses = 0; - ret->refcount = 1; - strncpy(ret->hostname, pipename, lenof(ret->hostname)); - ret->hostname[lenof(ret->hostname)-1] = '\0'; - return ret; -} - -static bool sk_nextaddr(SockAddr *addr, SockAddrStep *step) -{ -#ifndef NO_IPV6 - if (step->ai) { - if (step->ai->ai_next) { - step->ai = step->ai->ai_next; - return true; - } else - return false; - } -#endif - if (step->curraddr+1 < addr->naddresses) { - step->curraddr++; - return true; - } else { - return false; - } -} - -void sk_getaddr(SockAddr *addr, char *buf, int buflen) -{ - SockAddrStep step; - START_STEP(addr, step); - -#ifndef NO_IPV6 - if (step.ai) { - int err = 0; - if (p_WSAAddressToStringA) { - DWORD dwbuflen = buflen; - err = p_WSAAddressToStringA(step.ai->ai_addr, step.ai->ai_addrlen, - NULL, buf, &dwbuflen); - } else - err = -1; - if (err) { - strncpy(buf, addr->hostname, buflen); - if (!buf[0]) - strncpy(buf, "<unknown>", buflen); - buf[buflen-1] = '\0'; - } - } else -#endif - if (SOCKADDR_FAMILY(addr, step) == AF_INET) { - struct in_addr a; - assert(addr->addresses && step.curraddr < addr->naddresses); - a.s_addr = p_htonl(addr->addresses[step.curraddr]); - strncpy(buf, p_inet_ntoa(a), buflen); - buf[buflen-1] = '\0'; - } else { - strncpy(buf, addr->hostname, buflen); - buf[buflen-1] = '\0'; - } -} - -/* - * This constructs a SockAddr that points at one specific sub-address - * of a parent SockAddr. The returned SockAddr does not own all its - * own memory: it points into the old one's data structures, so it - * MUST NOT be used after the old one is freed, and it MUST NOT be - * passed to sk_addr_free. (The latter is why it's returned by value - * rather than dynamically allocated - that should clue in anyone - * writing a call to it that something is weird about it.) - */ -static SockAddr sk_extractaddr_tmp( - SockAddr *addr, const SockAddrStep *step) -{ - SockAddr toret; - toret = *addr; /* structure copy */ - toret.refcount = 1; - -#ifndef NO_IPV6 - toret.ais = step->ai; -#endif - if (SOCKADDR_FAMILY(addr, *step) == AF_INET -#ifndef NO_IPV6 - && !toret.ais -#endif - ) - toret.addresses += step->curraddr; - - return toret; -} - -bool sk_addr_needs_port(SockAddr *addr) -{ - return !addr->namedpipe; -} - -bool sk_hostname_is_local(const char *name) -{ - return !strcmp(name, "localhost") || - !strcmp(name, "::1") || - !strncmp(name, "127.", 4); -} - -static INTERFACE_INFO local_interfaces[16]; -static int n_local_interfaces; /* 0=not yet, -1=failed, >0=number */ - -static bool ipv4_is_local_addr(struct in_addr addr) -{ - if (ipv4_is_loopback(addr)) - return true; /* loopback addresses are local */ - if (!n_local_interfaces) { - SOCKET s = p_socket(AF_INET, SOCK_DGRAM, 0); - DWORD retbytes; - - SetHandleInformation((HANDLE)s, HANDLE_FLAG_INHERIT, 0); - - if (p_WSAIoctl && - p_WSAIoctl(s, SIO_GET_INTERFACE_LIST, NULL, 0, - local_interfaces, sizeof(local_interfaces), - &retbytes, NULL, NULL) == 0) - n_local_interfaces = retbytes / sizeof(INTERFACE_INFO); - else - n_local_interfaces = -1; - } - if (n_local_interfaces > 0) { - int i; - for (i = 0; i < n_local_interfaces; i++) { - SOCKADDR_IN *address = - (SOCKADDR_IN *)&local_interfaces[i].iiAddress; - if (address->sin_addr.s_addr == addr.s_addr) - return true; /* this address is local */ - } - } - return false; /* this address is not local */ -} - -bool sk_address_is_local(SockAddr *addr) -{ - SockAddrStep step; - int family; - START_STEP(addr, step); - family = SOCKADDR_FAMILY(addr, step); - -#ifndef NO_IPV6 - if (family == AF_INET6) { - return IN6_IS_ADDR_LOOPBACK(&((const struct sockaddr_in6 *)step.ai->ai_addr)->sin6_addr); - } else -#endif - if (family == AF_INET) { -#ifndef NO_IPV6 - if (step.ai) { - return ipv4_is_local_addr(((struct sockaddr_in *)step.ai->ai_addr) - ->sin_addr); - } else -#endif - { - struct in_addr a; - assert(addr->addresses && step.curraddr < addr->naddresses); - a.s_addr = p_htonl(addr->addresses[step.curraddr]); - return ipv4_is_local_addr(a); - } - } else { - assert(family == AF_UNSPEC); - return false; /* we don't know; assume not */ - } -} - -bool sk_address_is_special_local(SockAddr *addr) -{ - return false; /* no Unix-domain socket analogue here */ -} - -int sk_addrtype(SockAddr *addr) -{ - SockAddrStep step; - int family; - START_STEP(addr, step); - family = SOCKADDR_FAMILY(addr, step); - - return (family == AF_INET ? ADDRTYPE_IPV4 : -#ifndef NO_IPV6 - family == AF_INET6 ? ADDRTYPE_IPV6 : -#endif - ADDRTYPE_NAME); -} - -void sk_addrcopy(SockAddr *addr, char *buf) -{ - SockAddrStep step; - int family; - START_STEP(addr, step); - family = SOCKADDR_FAMILY(addr, step); - - assert(family != AF_UNSPEC); -#ifndef NO_IPV6 - if (step.ai) { - if (family == AF_INET) - memcpy(buf, &((struct sockaddr_in *)step.ai->ai_addr)->sin_addr, - sizeof(struct in_addr)); - else if (family == AF_INET6) - memcpy(buf, &((struct sockaddr_in6 *)step.ai->ai_addr)->sin6_addr, - sizeof(struct in6_addr)); - else - unreachable("bad address family in sk_addrcopy"); - } else -#endif - if (family == AF_INET) { - struct in_addr a; - assert(addr->addresses && step.curraddr < addr->naddresses); - a.s_addr = p_htonl(addr->addresses[step.curraddr]); - memcpy(buf, (char*) &a.s_addr, 4); - } -} - -void sk_addr_free(SockAddr *addr) -{ - if (--addr->refcount > 0) - return; -#ifndef NO_IPV6 - if (addr->ais && p_freeaddrinfo) - p_freeaddrinfo(addr->ais); -#endif - if (addr->addresses) - sfree(addr->addresses); - sfree(addr); -} - -SockAddr *sk_addr_dup(SockAddr *addr) -{ - addr->refcount++; - return addr; -} - -static Plug *sk_net_plug(Socket *sock, Plug *p) -{ - NetSocket *s = container_of(sock, NetSocket, sock); - Plug *ret = s->plug; - if (p) - s->plug = p; - return ret; -} - -static void sk_net_close(Socket *s); -static size_t sk_net_write(Socket *s, const void *data, size_t len); -static size_t sk_net_write_oob(Socket *s, const void *data, size_t len); -static void sk_net_write_eof(Socket *s); -static void sk_net_set_frozen(Socket *s, bool is_frozen); -static const char *sk_net_socket_error(Socket *s); -static SocketPeerInfo *sk_net_peer_info(Socket *s); - -static const SocketVtable NetSocket_sockvt = { - .plug = sk_net_plug, - .close = sk_net_close, - .write = sk_net_write, - .write_oob = sk_net_write_oob, - .write_eof = sk_net_write_eof, - .set_frozen = sk_net_set_frozen, - .socket_error = sk_net_socket_error, - .peer_info = sk_net_peer_info, -}; - -static Socket *sk_net_accept(accept_ctx_t ctx, Plug *plug) -{ - DWORD err; - const char *errstr; - NetSocket *ret; - - /* - * Create NetSocket structure. - */ - ret = snew(NetSocket); - ret->sock.vt = &NetSocket_sockvt; - ret->error = NULL; - ret->plug = plug; - bufchain_init(&ret->output_data); - ret->writable = true; /* to start with */ - ret->sending_oob = 0; - ret->outgoingeof = EOF_NO; - ret->frozen = true; - ret->frozen_readable = false; - ret->localhost_only = false; /* unused, but best init anyway */ - ret->pending_error = 0; - ret->parent = ret->child = NULL; - ret->addr = NULL; - - ret->s = (SOCKET)ctx.p; - - if (ret->s == INVALID_SOCKET) { - err = p_WSAGetLastError(); - ret->error = winsock_error_string(err); - return &ret->sock; - } - - ret->oobinline = false; - - /* Set up a select mechanism. This could be an AsyncSelect on a - * window, or an EventSelect on an event object. */ - errstr = do_select(ret->s, true); - if (errstr) { - ret->error = errstr; - return &ret->sock; - } - - add234(sktree, ret); - - return &ret->sock; -} - -static DWORD try_connect(NetSocket *sock) -{ - SOCKET s; -#ifndef NO_IPV6 - SOCKADDR_IN6 a6; -#endif - SOCKADDR_IN a; - DWORD err; - const char *errstr; - short localport; - int family; - - if (sock->s != INVALID_SOCKET) { - do_select(sock->s, false); - p_closesocket(sock->s); - } - - { - SockAddr thisaddr = sk_extractaddr_tmp( - sock->addr, &sock->step); - plug_log(sock->plug, PLUGLOG_CONNECT_TRYING, - &thisaddr, sock->port, NULL, 0); - } - - /* - * Open socket. - */ - family = SOCKADDR_FAMILY(sock->addr, sock->step); - - /* - * Remove the socket from the tree before we overwrite its - * internal socket id, because that forms part of the tree's - * sorting criterion. We'll add it back before exiting this - * function, whether we changed anything or not. - */ - del234(sktree, sock); - - s = p_socket(family, SOCK_STREAM, 0); - sock->s = s; - - if (s == INVALID_SOCKET) { - err = p_WSAGetLastError(); - sock->error = winsock_error_string(err); - goto ret; - } - - SetHandleInformation((HANDLE)s, HANDLE_FLAG_INHERIT, 0); - - if (sock->oobinline) { - BOOL b = true; - p_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (void *) &b, sizeof(b)); - } - - if (sock->nodelay) { - BOOL b = true; - p_setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (void *) &b, sizeof(b)); - } - - if (sock->keepalive) { - BOOL b = true; - p_setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *) &b, sizeof(b)); - } - - /* - * Bind to local address. - */ - if (sock->privport) - localport = 1023; /* count from 1023 downwards */ - else - localport = 0; /* just use port 0 (ie winsock picks) */ - - /* Loop round trying to bind */ - while (1) { - int sockcode; - -#ifndef NO_IPV6 - if (family == AF_INET6) { - memset(&a6, 0, sizeof(a6)); - a6.sin6_family = AF_INET6; - /*a6.sin6_addr = in6addr_any; */ /* == 0 done by memset() */ - a6.sin6_port = p_htons(localport); - } else -#endif - { - a.sin_family = AF_INET; - a.sin_addr.s_addr = p_htonl(INADDR_ANY); - a.sin_port = p_htons(localport); - } -#ifndef NO_IPV6 - sockcode = p_bind(s, (family == AF_INET6 ? - (struct sockaddr *) &a6 : - (struct sockaddr *) &a), - (family == AF_INET6 ? sizeof(a6) : sizeof(a))); -#else - sockcode = p_bind(s, (struct sockaddr *) &a, sizeof(a)); -#endif - if (sockcode != SOCKET_ERROR) { - err = 0; - break; /* done */ - } else { - err = p_WSAGetLastError(); - if (err != WSAEADDRINUSE) /* failed, for a bad reason */ - break; - } - - if (localport == 0) - break; /* we're only looping once */ - localport--; - if (localport == 0) - break; /* we might have got to the end */ - } - - if (err) { - sock->error = winsock_error_string(err); - goto ret; - } - - /* - * Connect to remote address. - */ -#ifndef NO_IPV6 - if (sock->step.ai) { - if (family == AF_INET6) { - a6.sin6_family = AF_INET6; - a6.sin6_port = p_htons((short) sock->port); - a6.sin6_addr = - ((struct sockaddr_in6 *) sock->step.ai->ai_addr)->sin6_addr; - a6.sin6_flowinfo = ((struct sockaddr_in6 *) sock->step.ai->ai_addr)->sin6_flowinfo; - a6.sin6_scope_id = ((struct sockaddr_in6 *) sock->step.ai->ai_addr)->sin6_scope_id; - } else { - a.sin_family = AF_INET; - a.sin_addr = - ((struct sockaddr_in *) sock->step.ai->ai_addr)->sin_addr; - a.sin_port = p_htons((short) sock->port); - } - } else -#endif - { - assert(sock->addr->addresses && sock->step.curraddr < sock->addr->naddresses); - a.sin_family = AF_INET; - a.sin_addr.s_addr = p_htonl(sock->addr->addresses[sock->step.curraddr]); - a.sin_port = p_htons((short) sock->port); - } - - /* Set up a select mechanism. This could be an AsyncSelect on a - * window, or an EventSelect on an event object. */ - errstr = do_select(s, true); - if (errstr) { - sock->error = errstr; - err = 1; - goto ret; - } - - if (( -#ifndef NO_IPV6 - p_connect(s, - ((family == AF_INET6) ? (struct sockaddr *) &a6 : - (struct sockaddr *) &a), - (family == AF_INET6) ? sizeof(a6) : sizeof(a)) -#else - p_connect(s, (struct sockaddr *) &a, sizeof(a)) -#endif - ) == SOCKET_ERROR) { - err = p_WSAGetLastError(); - /* - * We expect a potential EWOULDBLOCK here, because the - * chances are the front end has done a select for - * FD_CONNECT, so that connect() will complete - * asynchronously. - */ - if ( err != WSAEWOULDBLOCK ) { - sock->error = winsock_error_string(err); - goto ret; - } - } else { - /* - * If we _don't_ get EWOULDBLOCK, the connect has completed - * and we should set the socket as writable. - */ - sock->writable = true; - SockAddr thisaddr = sk_extractaddr_tmp(sock->addr, &sock->step); - plug_log(sock->plug, PLUGLOG_CONNECT_SUCCESS, - &thisaddr, sock->port, NULL, 0); - } - - err = 0; - - ret: - - /* - * No matter what happened, put the socket back in the tree. - */ - add234(sktree, sock); - - if (err) { - SockAddr thisaddr = sk_extractaddr_tmp( - sock->addr, &sock->step); - plug_log(sock->plug, PLUGLOG_CONNECT_FAILED, - &thisaddr, sock->port, sock->error, err); - } - return err; -} - -Socket *sk_new(SockAddr *addr, int port, bool privport, bool oobinline, - bool nodelay, bool keepalive, Plug *plug) -{ - NetSocket *ret; - DWORD err; - - /* - * Create NetSocket structure. - */ - ret = snew(NetSocket); - ret->sock.vt = &NetSocket_sockvt; - ret->error = NULL; - ret->plug = plug; - bufchain_init(&ret->output_data); - ret->connected = false; /* to start with */ - ret->writable = false; /* to start with */ - ret->sending_oob = 0; - ret->outgoingeof = EOF_NO; - ret->frozen = false; - ret->frozen_readable = false; - ret->localhost_only = false; /* unused, but best init anyway */ - ret->pending_error = 0; - ret->parent = ret->child = NULL; - ret->oobinline = oobinline; - ret->nodelay = nodelay; - ret->keepalive = keepalive; - ret->privport = privport; - ret->port = port; - ret->addr = addr; - START_STEP(ret->addr, ret->step); - ret->s = INVALID_SOCKET; - - err = 0; - do { - err = try_connect(ret); - } while (err && sk_nextaddr(ret->addr, &ret->step)); - - return &ret->sock; -} - -Socket *sk_newlistener(const char *srcaddr, int port, Plug *plug, - bool local_host_only, int orig_address_family) -{ - SOCKET s; -#ifndef NO_IPV6 - SOCKADDR_IN6 a6; -#endif - SOCKADDR_IN a; - - DWORD err; - const char *errstr; - NetSocket *ret; - int retcode; - - int address_family; - - /* - * Create NetSocket structure. - */ - ret = snew(NetSocket); - ret->sock.vt = &NetSocket_sockvt; - ret->error = NULL; - ret->plug = plug; - bufchain_init(&ret->output_data); - ret->writable = false; /* to start with */ - ret->sending_oob = 0; - ret->outgoingeof = EOF_NO; - ret->frozen = false; - ret->frozen_readable = false; - ret->localhost_only = local_host_only; - ret->pending_error = 0; - ret->parent = ret->child = NULL; - ret->addr = NULL; - - /* - * Translate address_family from platform-independent constants - * into local reality. - */ - address_family = (orig_address_family == ADDRTYPE_IPV4 ? AF_INET : -#ifndef NO_IPV6 - orig_address_family == ADDRTYPE_IPV6 ? AF_INET6 : -#endif - AF_UNSPEC); - - /* - * Our default, if passed the `don't care' value - * ADDRTYPE_UNSPEC, is to listen on IPv4. If IPv6 is supported, - * we will also set up a second socket listening on IPv6, but - * the v4 one is primary since that ought to work even on - * non-v6-supporting systems. - */ - if (address_family == AF_UNSPEC) address_family = AF_INET; - - /* - * Open socket. - */ - s = p_socket(address_family, SOCK_STREAM, 0); - ret->s = s; - - if (s == INVALID_SOCKET) { - err = p_WSAGetLastError(); - ret->error = winsock_error_string(err); - return &ret->sock; - } - - SetHandleInformation((HANDLE)s, HANDLE_FLAG_INHERIT, 0); - - ret->oobinline = false; - - { - BOOL on = true; - p_setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, - (const char *)&on, sizeof(on)); - } - -#ifndef NO_IPV6 - if (address_family == AF_INET6) { - memset(&a6, 0, sizeof(a6)); - a6.sin6_family = AF_INET6; - if (local_host_only) - a6.sin6_addr = in6addr_loopback; - else - a6.sin6_addr = in6addr_any; - if (srcaddr != NULL && p_getaddrinfo) { - struct addrinfo hints; - struct addrinfo *ai; - int err; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_INET6; - hints.ai_flags = 0; - { - /* strip [] on IPv6 address literals */ - char *trimmed_addr = host_strduptrim(srcaddr); - err = p_getaddrinfo(trimmed_addr, NULL, &hints, &ai); - sfree(trimmed_addr); - } - if (err == 0 && ai->ai_family == AF_INET6) { - a6.sin6_addr = - ((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr; - } - } - a6.sin6_port = p_htons(port); - } else -#endif - { - bool got_addr = false; - a.sin_family = AF_INET; - - /* - * Bind to source address. First try an explicitly - * specified one... - */ - if (srcaddr) { - a.sin_addr.s_addr = p_inet_addr(srcaddr); - if (a.sin_addr.s_addr != INADDR_NONE) { - /* Override localhost_only with specified listen addr. */ - ret->localhost_only = ipv4_is_loopback(a.sin_addr); - got_addr = true; - } - } - - /* - * ... and failing that, go with one of the standard ones. - */ - if (!got_addr) { - if (local_host_only) - a.sin_addr.s_addr = p_htonl(INADDR_LOOPBACK); - else - a.sin_addr.s_addr = p_htonl(INADDR_ANY); - } - - a.sin_port = p_htons((short)port); - } -#ifndef NO_IPV6 - retcode = p_bind(s, (address_family == AF_INET6 ? - (struct sockaddr *) &a6 : - (struct sockaddr *) &a), - (address_family == - AF_INET6 ? sizeof(a6) : sizeof(a))); -#else - retcode = p_bind(s, (struct sockaddr *) &a, sizeof(a)); -#endif - if (retcode != SOCKET_ERROR) { - err = 0; - } else { - err = p_WSAGetLastError(); - } - - if (err) { - p_closesocket(s); - ret->error = winsock_error_string(err); - return &ret->sock; - } - - - if (p_listen(s, SOMAXCONN) == SOCKET_ERROR) { - p_closesocket(s); - ret->error = winsock_error_string(p_WSAGetLastError()); - return &ret->sock; - } - - /* Set up a select mechanism. This could be an AsyncSelect on a - * window, or an EventSelect on an event object. */ - errstr = do_select(s, true); - if (errstr) { - p_closesocket(s); - ret->error = errstr; - return &ret->sock; - } - - add234(sktree, ret); - -#ifndef NO_IPV6 - /* - * If we were given ADDRTYPE_UNSPEC, we must also create an - * IPv6 listening socket and link it to this one. - */ - if (address_family == AF_INET && orig_address_family == ADDRTYPE_UNSPEC) { - Socket *other = sk_newlistener(srcaddr, port, plug, - local_host_only, ADDRTYPE_IPV6); - - if (other) { - NetSocket *ns = container_of(other, NetSocket, sock); - if (!ns->error) { - ns->parent = ret; - ret->child = ns; - } else { - sfree(ns); - } - } - } -#endif - - return &ret->sock; -} - -static void sk_net_close(Socket *sock) -{ - NetSocket *s = container_of(sock, NetSocket, sock); - - if (s->child) - sk_net_close(&s->child->sock); - - bufchain_clear(&s->output_data); - - del234(sktree, s); - do_select(s->s, false); - p_closesocket(s->s); - if (s->addr) - sk_addr_free(s->addr); - delete_callbacks_for_context(s); - sfree(s); -} - -/* - * Deal with socket errors detected in try_send(). - */ -static void socket_error_callback(void *vs) -{ - NetSocket *s = (NetSocket *)vs; - - /* - * Just in case other socket work has caused this socket to vanish - * or become somehow non-erroneous before this callback arrived... - */ - if (!find234(sktree, s, NULL) || !s->pending_error) - return; - - /* - * An error has occurred on this socket. Pass it to the plug. - */ - plug_closing(s->plug, winsock_error_string(s->pending_error), - s->pending_error, 0); -} - -/* - * The function which tries to send on a socket once it's deemed - * writable. - */ -void try_send(NetSocket *s) -{ - while (s->sending_oob || bufchain_size(&s->output_data) > 0) { - int nsent; - DWORD err; - const void *data; - size_t len; - int urgentflag; - - if (s->sending_oob) { - urgentflag = MSG_OOB; - len = s->sending_oob; - data = &s->oobdata; - } else { - urgentflag = 0; - ptrlen bufdata = bufchain_prefix(&s->output_data); - data = bufdata.ptr; - len = bufdata.len; - } - len = min(len, INT_MAX); /* WinSock send() takes an int */ - nsent = p_send(s->s, data, len, urgentflag); - noise_ultralight(NOISE_SOURCE_IOLEN, nsent); - if (nsent <= 0) { - err = (nsent < 0 ? p_WSAGetLastError() : 0); - if ((err < WSABASEERR && nsent < 0) || err == WSAEWOULDBLOCK) { - /* - * Perfectly normal: we've sent all we can for the moment. - * - * (Some WinSock send() implementations can return - * <0 but leave no sensible error indication - - * WSAGetLastError() is called but returns zero or - * a small number - so we check that case and treat - * it just like WSAEWOULDBLOCK.) - */ - s->writable = false; - return; - } else { - /* - * If send() returns a socket error, we unfortunately - * can't just call plug_closing(), because it's quite - * likely that we're currently _in_ a call from the - * code we'd be calling back to, so we'd have to make - * half the SSH code reentrant. Instead we flag a - * pending error on the socket, to be dealt with (by - * calling plug_closing()) at some suitable future - * moment. - */ - s->pending_error = err; - queue_toplevel_callback(socket_error_callback, s); - return; - } - } else { - if (s->sending_oob) { - if (nsent < len) { - memmove(s->oobdata, s->oobdata+nsent, len-nsent); - s->sending_oob = len - nsent; - } else { - s->sending_oob = 0; - } - } else { - bufchain_consume(&s->output_data, nsent); - } - } - } - - /* - * If we reach here, we've finished sending everything we might - * have needed to send. Send EOF, if we need to. - */ - if (s->outgoingeof == EOF_PENDING) { - p_shutdown(s->s, SD_SEND); - s->outgoingeof = EOF_SENT; - } -} - -static size_t sk_net_write(Socket *sock, const void *buf, size_t len) -{ - NetSocket *s = container_of(sock, NetSocket, sock); - - assert(s->outgoingeof == EOF_NO); - - /* - * Add the data to the buffer list on the socket. - */ - bufchain_add(&s->output_data, buf, len); - - /* - * Now try sending from the start of the buffer list. - */ - if (s->writable) - try_send(s); - - return bufchain_size(&s->output_data); -} - -static size_t sk_net_write_oob(Socket *sock, const void *buf, size_t len) -{ - NetSocket *s = container_of(sock, NetSocket, sock); - - assert(s->outgoingeof == EOF_NO); - - /* - * Replace the buffer list on the socket with the data. - */ - bufchain_clear(&s->output_data); - assert(len <= sizeof(s->oobdata)); - memcpy(s->oobdata, buf, len); - s->sending_oob = len; - - /* - * Now try sending from the start of the buffer list. - */ - if (s->writable) - try_send(s); - - return s->sending_oob; -} - -static void sk_net_write_eof(Socket *sock) -{ - NetSocket *s = container_of(sock, NetSocket, sock); - - assert(s->outgoingeof == EOF_NO); - - /* - * Mark the socket as pending outgoing EOF. - */ - s->outgoingeof = EOF_PENDING; - - /* - * Now try sending from the start of the buffer list. - */ - if (s->writable) - try_send(s); -} - -void select_result(WPARAM wParam, LPARAM lParam) -{ - int ret; - DWORD err; - char buf[20480]; /* nice big buffer for plenty of speed */ - NetSocket *s; - bool atmark; - - /* wParam is the socket itself */ - - if (wParam == 0) - return; /* boggle */ - - s = find234(sktree, (void *) wParam, cmpforsearch); - if (!s) - return; /* boggle */ - - if ((err = WSAGETSELECTERROR(lParam)) != 0) { - /* - * An error has occurred on this socket. Pass it to the - * plug. - */ - if (s->addr) { - SockAddr thisaddr = sk_extractaddr_tmp( - s->addr, &s->step); - plug_log(s->plug, PLUGLOG_CONNECT_FAILED, &thisaddr, s->port, - winsock_error_string(err), err); - while (err && s->addr && sk_nextaddr(s->addr, &s->step)) { - err = try_connect(s); - } - } - if (err != 0) - plug_closing(s->plug, winsock_error_string(err), err, 0); - return; - } - - noise_ultralight(NOISE_SOURCE_IOID, wParam); - - switch (WSAGETSELECTEVENT(lParam)) { - case FD_CONNECT: - s->connected = true; - s->writable = true; - - /* - * Once a socket is connected, we can stop falling back - * through the candidate addresses to connect to. But first, - * let the plug know we were successful. - */ - if (s->addr) { - SockAddr thisaddr = sk_extractaddr_tmp( - s->addr, &s->step); - plug_log(s->plug, PLUGLOG_CONNECT_SUCCESS, - &thisaddr, s->port, NULL, 0); - - sk_addr_free(s->addr); - s->addr = NULL; - } - break; - case FD_READ: - /* In the case the socket is still frozen, we don't even bother */ - if (s->frozen) { - s->frozen_readable = true; - break; - } - - /* - * We have received data on the socket. For an oobinline - * socket, this might be data _before_ an urgent pointer, - * in which case we send it to the back end with type==1 - * (data prior to urgent). - */ - if (s->oobinline) { - u_long atmark_from_ioctl = 1; - p_ioctlsocket(s->s, SIOCATMARK, &atmark_from_ioctl); - /* - * Avoid checking the return value from ioctlsocket(), - * on the grounds that some WinSock wrappers don't - * support it. If it does nothing, we get atmark==1, - * which is equivalent to `no OOB pending', so the - * effect will be to non-OOB-ify any OOB data. - */ - atmark = atmark_from_ioctl; - } else - atmark = true; - - ret = p_recv(s->s, buf, sizeof(buf), 0); - noise_ultralight(NOISE_SOURCE_IOLEN, ret); - if (ret < 0) { - err = p_WSAGetLastError(); - if (err == WSAEWOULDBLOCK) { - break; - } - } - if (ret < 0) { - plug_closing(s->plug, winsock_error_string(err), err, 0); - } else if (0 == ret) { - plug_closing(s->plug, NULL, 0, 0); - } else { - plug_receive(s->plug, atmark ? 0 : 1, buf, ret); - } - break; - case FD_OOB: - /* - * This will only happen on a non-oobinline socket. It - * indicates that we can immediately perform an OOB read - * and get back OOB data, which we will send to the back - * end with type==2 (urgent data). - */ - ret = p_recv(s->s, buf, sizeof(buf), MSG_OOB); - noise_ultralight(NOISE_SOURCE_IOLEN, ret); - if (ret <= 0) { - int err = p_WSAGetLastError(); - plug_closing(s->plug, winsock_error_string(err), err, 0); - } else { - plug_receive(s->plug, 2, buf, ret); - } - break; - case FD_WRITE: { - int bufsize_before, bufsize_after; - s->writable = true; - bufsize_before = s->sending_oob + bufchain_size(&s->output_data); - try_send(s); - bufsize_after = s->sending_oob + bufchain_size(&s->output_data); - if (bufsize_after < bufsize_before) - plug_sent(s->plug, bufsize_after); - break; - } - case FD_CLOSE: - /* Signal a close on the socket. First read any outstanding data. */ - do { - ret = p_recv(s->s, buf, sizeof(buf), 0); - if (ret < 0) { - err = p_WSAGetLastError(); - if (err == WSAEWOULDBLOCK) - break; - plug_closing(s->plug, winsock_error_string(err), err, 0); - } else { - if (ret) - plug_receive(s->plug, 0, buf, ret); - else - plug_closing(s->plug, NULL, 0, 0); - } - } while (ret > 0); - return; - case FD_ACCEPT: { -#ifdef NO_IPV6 - struct sockaddr_in isa; -#else - struct sockaddr_storage isa; -#endif - int addrlen = sizeof(isa); - SOCKET t; /* socket of connection */ - accept_ctx_t actx; - - memset(&isa, 0, sizeof(isa)); - err = 0; - t = p_accept(s->s,(struct sockaddr *)&isa,&addrlen); - if (t == INVALID_SOCKET) - { - err = p_WSAGetLastError(); - if (err == WSATRY_AGAIN) - break; - } - - actx.p = (void *)t; - -#ifndef NO_IPV6 - if (isa.ss_family == AF_INET && - s->localhost_only && - !ipv4_is_local_addr(((struct sockaddr_in *)&isa)->sin_addr)) -#else - if (s->localhost_only && !ipv4_is_local_addr(isa.sin_addr)) -#endif - { - p_closesocket(t); /* dodgy WinSock let nonlocal through */ - } else if (plug_accepting(s->plug, sk_net_accept, actx)) { - p_closesocket(t); /* denied or error */ - } - break; - } - } -} - -/* - * Special error values are returned from sk_namelookup and sk_new - * if there's a problem. These functions extract an error message, - * or return NULL if there's no problem. - */ -const char *sk_addr_error(SockAddr *addr) -{ - return addr->error; -} -static const char *sk_net_socket_error(Socket *sock) -{ - NetSocket *s = container_of(sock, NetSocket, sock); - return s->error; -} - -static SocketPeerInfo *sk_net_peer_info(Socket *sock) -{ - NetSocket *s = container_of(sock, NetSocket, sock); -#ifdef NO_IPV6 - struct sockaddr_in addr; -#else - struct sockaddr_storage addr; - char buf[INET6_ADDRSTRLEN]; -#endif - int addrlen = sizeof(addr); - SocketPeerInfo *pi; - - if (p_getpeername(s->s, (struct sockaddr *)&addr, &addrlen) < 0) - return NULL; - - pi = snew(SocketPeerInfo); - pi->addressfamily = ADDRTYPE_UNSPEC; - pi->addr_text = NULL; - pi->port = -1; - pi->log_text = NULL; - - if (((struct sockaddr *)&addr)->sa_family == AF_INET) { - pi->addressfamily = ADDRTYPE_IPV4; - memcpy(pi->addr_bin.ipv4, &((struct sockaddr_in *)&addr)->sin_addr, 4); - pi->port = p_ntohs(((struct sockaddr_in *)&addr)->sin_port); - pi->addr_text = dupstr( - p_inet_ntoa(((struct sockaddr_in *)&addr)->sin_addr)); - pi->log_text = dupprintf("%s:%d", pi->addr_text, pi->port); - -#ifndef NO_IPV6 - } else if (((struct sockaddr *)&addr)->sa_family == AF_INET6) { - pi->addressfamily = ADDRTYPE_IPV6; - memcpy(pi->addr_bin.ipv6, - &((struct sockaddr_in6 *)&addr)->sin6_addr, 16); - pi->port = p_ntohs(((struct sockaddr_in6 *)&addr)->sin6_port); - pi->addr_text = dupstr( - p_inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&addr)->sin6_addr, - buf, sizeof(buf))); - pi->log_text = dupprintf("[%s]:%d", pi->addr_text, pi->port); - -#endif - } else { - sfree(pi); - return NULL; - } - - return pi; -} - -static void sk_net_set_frozen(Socket *sock, bool is_frozen) -{ - NetSocket *s = container_of(sock, NetSocket, sock); - if (s->frozen == is_frozen) - return; - s->frozen = is_frozen; - if (!is_frozen) { - do_select(s->s, true); - if (s->frozen_readable) { - char c; - p_recv(s->s, &c, 1, MSG_PEEK); - } - } - s->frozen_readable = false; -} - -void socket_reselect_all(void) -{ - NetSocket *s; - int i; - - for (i = 0; (s = index234(sktree, i)) != NULL; i++) { - if (!s->frozen) - do_select(s->s, true); - } -} - -/* - * For Plink: enumerate all sockets currently active. - */ -SOCKET first_socket(int *state) -{ - NetSocket *s; - *state = 0; - s = index234(sktree, (*state)++); - return s ? s->s : INVALID_SOCKET; -} - -SOCKET next_socket(int *state) -{ - NetSocket *s = index234(sktree, (*state)++); - return s ? s->s : INVALID_SOCKET; -} - -bool socket_writable(SOCKET skt) -{ - NetSocket *s = find234(sktree, (void *)skt, cmpforsearch); - - if (s) - return bufchain_size(&s->output_data) > 0; - else - return false; -} - -int net_service_lookup(char *service) -{ - struct servent *se; - se = p_getservbyname(service, NULL); - if (se != NULL) - return p_ntohs(se->s_port); - else - return 0; -} - -char *get_hostname(void) -{ - char hostbuf[256]; /* MSDN docs for gethostname() promise this is enough */ - if (p_gethostname(hostbuf, sizeof(hostbuf)) < 0) - return NULL; - return dupstr(hostbuf); -} - -SockAddr *platform_get_x11_unix_address(const char *display, int displaynum) -{ - SockAddr *ret = snew(SockAddr); - memset(ret, 0, sizeof(SockAddr)); - ret->error = "unix sockets not supported on this platform"; - ret->refcount = 1; - return ret; -} diff --git a/WINDOWS/WINNOISE.C b/WINDOWS/WINNOISE.C deleted file mode 100644 index 65c4c92d..00000000 --- a/WINDOWS/WINNOISE.C +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Noise generation for PuTTY's cryptographic random number - * generator. - */ - -#include <stdio.h> - -#include "putty.h" -#include "ssh.h" -#include "storage.h" - -#include <wincrypt.h> - -DECL_WINDOWS_FUNCTION(static, BOOL, CryptAcquireContextA, - (HCRYPTPROV *, LPCTSTR, LPCTSTR, DWORD, DWORD)); -DECL_WINDOWS_FUNCTION(static, BOOL, CryptGenRandom, - (HCRYPTPROV, DWORD, BYTE *)); -DECL_WINDOWS_FUNCTION(static, BOOL, CryptReleaseContext, - (HCRYPTPROV, DWORD)); -static HMODULE wincrypt_module = NULL; - -bool win_read_random(void *buf, unsigned wanted) -{ - bool toret = false; - HCRYPTPROV crypt_provider; - - if (!wincrypt_module) { - wincrypt_module = load_system32_dll("advapi32.dll"); - GET_WINDOWS_FUNCTION(wincrypt_module, CryptAcquireContextA); - GET_WINDOWS_FUNCTION(wincrypt_module, CryptGenRandom); - GET_WINDOWS_FUNCTION(wincrypt_module, CryptReleaseContext); - } - - if (wincrypt_module && p_CryptAcquireContextA && - p_CryptGenRandom && p_CryptReleaseContext && - p_CryptAcquireContextA(&crypt_provider, NULL, NULL, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT)) { - toret = p_CryptGenRandom(crypt_provider, wanted, buf); - p_CryptReleaseContext(crypt_provider, 0); - } - - return toret; -} - -/* - * This function is called once, at PuTTY startup. - */ - -void noise_get_heavy(void (*func) (void *, int)) -{ - HANDLE srch; - WIN32_FIND_DATA finddata; - DWORD pid; - char winpath[MAX_PATH + 3]; - BYTE buf[32]; - - GetWindowsDirectory(winpath, sizeof(winpath)); - strcat(winpath, "\\*"); - srch = FindFirstFile(winpath, &finddata); - if (srch != INVALID_HANDLE_VALUE) { - do { - func(&finddata, sizeof(finddata)); - } while (FindNextFile(srch, &finddata)); - FindClose(srch); - } - - pid = GetCurrentProcessId(); - func(&pid, sizeof(pid)); - - if (win_read_random(buf, sizeof(buf))) { - func(buf, sizeof(buf)); - smemclr(buf, sizeof(buf)); - } - - read_random_seed(func); -} - -/* - * This function is called on a timer, and it will monitor - * frequently changing quantities such as the state of physical and - * virtual memory, the state of the process's message queue, which - * window is in the foreground, which owns the clipboard, etc. - */ -void noise_regular(void) -{ - HWND w; - DWORD z; - POINT pt; - MEMORYSTATUS memstat; - FILETIME times[4]; - - w = GetForegroundWindow(); - random_add_noise(NOISE_SOURCE_FGWINDOW, &w, sizeof(w)); - w = GetCapture(); - random_add_noise(NOISE_SOURCE_CAPTURE, &w, sizeof(w)); - w = GetClipboardOwner(); - random_add_noise(NOISE_SOURCE_CLIPBOARD, &w, sizeof(w)); - z = GetQueueStatus(QS_ALLEVENTS); - random_add_noise(NOISE_SOURCE_QUEUE, &z, sizeof(z)); - - GetCursorPos(&pt); - random_add_noise(NOISE_SOURCE_CURSORPOS, &pt, sizeof(pt)); - - GlobalMemoryStatus(&memstat); - random_add_noise(NOISE_SOURCE_MEMINFO, &memstat, sizeof(memstat)); - - GetThreadTimes(GetCurrentThread(), times, times + 1, times + 2, - times + 3); - random_add_noise(NOISE_SOURCE_THREADTIME, ×, sizeof(times)); - GetProcessTimes(GetCurrentProcess(), times, times + 1, times + 2, - times + 3); - random_add_noise(NOISE_SOURCE_PROCTIME, ×, sizeof(times)); -} - -/* - * This function is called on every keypress or mouse move, and - * will add the current Windows time and performance monitor - * counter to the noise pool. It gets the scan code or mouse - * position passed in. - */ -void noise_ultralight(NoiseSourceId id, unsigned long data) -{ - DWORD wintime; - LARGE_INTEGER perftime; - - random_add_noise(id, &data, sizeof(DWORD)); - - wintime = GetTickCount(); - random_add_noise(NOISE_SOURCE_TIME, &wintime, sizeof(DWORD)); - - if (QueryPerformanceCounter(&perftime)) - random_add_noise(NOISE_SOURCE_PERFCOUNT, &perftime, sizeof(perftime)); -} - -uint64_t prng_reseed_time_ms(void) -{ - FILETIME ft; - GetSystemTimeAsFileTime(&ft); - uint64_t value = ft.dwHighDateTime; - value = (value << 32) + ft.dwLowDateTime; - return value / 10000; /* 1 millisecond / 100ns */ -} diff --git a/WINDOWS/WINNOJMP.C b/WINDOWS/WINNOJMP.C deleted file mode 100644 index dd61dc69..00000000 --- a/WINDOWS/WINNOJMP.C +++ /dev/null @@ -1,8 +0,0 @@ -/* - * winnojmp.c: stub jump list functions for Windows executables that - * don't update the jump list. - */ - -void add_session_to_jumplist(const char * const sessionname) {} -void remove_session_from_jumplist(const char * const sessionname) {} -void clear_jumplist(void) {} diff --git a/WINDOWS/WINPGEN.C b/WINDOWS/WINPGEN.C deleted file mode 100644 index 56c6a8db..00000000 --- a/WINDOWS/WINPGEN.C +++ /dev/null @@ -1,2026 +0,0 @@ -/* - * PuTTY key generation front end (Windows). - */ - -#include <time.h> -#include <stdio.h> -#include <stdlib.h> -#include <assert.h> - -#include "putty.h" -#include "ssh.h" -#include "sshkeygen.h" -#include "licence.h" -#include "winsecur.h" -#include "puttygen-rc.h" - -#include <commctrl.h> - -#ifdef MSVC4 -#define ICON_BIG 1 -#endif - -#define WM_DONEKEY (WM_APP + 1) - -#define DEFAULT_KEY_BITS 2048 -#define DEFAULT_ECCURVE_INDEX 0 -#define DEFAULT_EDCURVE_INDEX 0 - -static char *cmdline_keyfile = NULL; - -/* - * Print a modal (Really Bad) message box and perform a fatal exit. - */ -void modalfatalbox(const char *fmt, ...) -{ - va_list ap; - char *stuff; - - va_start(ap, fmt); - stuff = dupvprintf(fmt, ap); - va_end(ap); - MessageBox(NULL, stuff, "PuTTYgen Fatal Error", - MB_SYSTEMMODAL | MB_ICONERROR | MB_OK); - sfree(stuff); - exit(1); -} - -/* - * Print a non-fatal message box and do not exit. - */ -void nonfatal(const char *fmt, ...) -{ - va_list ap; - char *stuff; - - va_start(ap, fmt); - stuff = dupvprintf(fmt, ap); - va_end(ap); - MessageBox(NULL, stuff, "PuTTYgen Error", - MB_SYSTEMMODAL | MB_ICONERROR | MB_OK); - sfree(stuff); -} - -/* ---------------------------------------------------------------------- - * ProgressReceiver implementation. - */ - -#define PROGRESSRANGE 65535 - -struct progressphase { - double startpoint, total; - /* For exponential phases */ - double exp_probability, exp_current_value; -}; - -struct progress { - size_t nphases, phasessize; - struct progressphase *phases, *currphase; - - double scale; - HWND progbar; - - ProgressReceiver rec; -}; - -static ProgressPhase win_progress_add_linear( - ProgressReceiver *prog, double overall_cost) { - struct progress *p = container_of(prog, struct progress, rec); - - sgrowarray(p->phases, p->phasessize, p->nphases); - int phase = p->nphases++; - - p->phases[phase].total = overall_cost; - - ProgressPhase ph = { .n = phase }; - return ph; -} - -static ProgressPhase win_progress_add_probabilistic( - ProgressReceiver *prog, double cost_per_attempt, double probability) { - struct progress *p = container_of(prog, struct progress, rec); - - sgrowarray(p->phases, p->phasessize, p->nphases); - int phase = p->nphases++; - - p->phases[phase].exp_probability = 1.0 - probability; - p->phases[phase].exp_current_value = 1.0; - /* Expected number of attempts = 1 / probability of attempt succeeding */ - p->phases[phase].total = cost_per_attempt / probability; - - ProgressPhase ph = { .n = phase }; - return ph; -} - -static void win_progress_ready(ProgressReceiver *prog) -{ - struct progress *p = container_of(prog, struct progress, rec); - - double total = 0; - for (int i = 0; i < p->nphases; i++) { - p->phases[i].startpoint = total; - total += p->phases[i].total; - } - p->scale = PROGRESSRANGE / total; - - SendMessage(p->progbar, PBM_SETRANGE, 0, MAKELPARAM(0, PROGRESSRANGE)); -} - -static void win_progress_start_phase(ProgressReceiver *prog, - ProgressPhase phase) -{ - struct progress *p = container_of(prog, struct progress, rec); - - assert(phase.n < p->nphases); - p->currphase = &p->phases[phase.n]; -} - -static void win_progress_update(struct progress *p, double phasepos) -{ - double position = (p->currphase->startpoint + - p->currphase->total * phasepos); - position *= p->scale; - if (position < 0) - position = 0; - if (position > PROGRESSRANGE) - position = PROGRESSRANGE; - - SendMessage(p->progbar, PBM_SETPOS, (WPARAM)position, 0); -} - -static void win_progress_report(ProgressReceiver *prog, double progress) -{ - struct progress *p = container_of(prog, struct progress, rec); - - win_progress_update(p, progress); -} - -static void win_progress_report_attempt(ProgressReceiver *prog) -{ - struct progress *p = container_of(prog, struct progress, rec); - - p->currphase->exp_current_value *= p->currphase->exp_probability; - win_progress_update(p, 1.0 - p->currphase->exp_current_value); -} - -static void win_progress_report_phase_complete(ProgressReceiver *prog) -{ - struct progress *p = container_of(prog, struct progress, rec); - - win_progress_update(p, 1.0); -} - -static const ProgressReceiverVtable win_progress_vt = { - .add_linear = win_progress_add_linear, - .add_probabilistic = win_progress_add_probabilistic, - .ready = win_progress_ready, - .start_phase = win_progress_start_phase, - .report = win_progress_report, - .report_attempt = win_progress_report_attempt, - .report_phase_complete = win_progress_report_phase_complete, -}; - -static void win_progress_initialise(struct progress *p) -{ - p->nphases = p->phasessize = 0; - p->phases = p->currphase = NULL; - p->rec.vt = &win_progress_vt; -} - -static void win_progress_cleanup(struct progress *p) -{ - sfree(p->phases); -} - -struct PassphraseProcStruct { - char **passphrase; - char *comment; -}; - -/* - * Dialog-box function for the passphrase box. - */ -static INT_PTR CALLBACK PassphraseProc(HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) -{ - static char **passphrase = NULL; - struct PassphraseProcStruct *p; - - switch (msg) { - case WM_INITDIALOG: - SetForegroundWindow(hwnd); - SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, - SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); - - /* - * Centre the window. - */ - { /* centre the window */ - RECT rs, rd; - HWND hw; - - 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); - } - - p = (struct PassphraseProcStruct *) lParam; - passphrase = p->passphrase; - if (p->comment) - SetDlgItemText(hwnd, 101, p->comment); - burnstr(*passphrase); - *passphrase = dupstr(""); - SetDlgItemText(hwnd, 102, *passphrase); - return 0; - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDOK: - if (*passphrase) - EndDialog(hwnd, 1); - else - MessageBeep(0); - return 0; - case IDCANCEL: - EndDialog(hwnd, 0); - return 0; - case 102: /* edit box */ - if ((HIWORD(wParam) == EN_CHANGE) && passphrase) { - burnstr(*passphrase); - *passphrase = GetDlgItemText_alloc(hwnd, 102); - } - return 0; - } - return 0; - case WM_CLOSE: - EndDialog(hwnd, 0); - return 0; - } - return 0; -} - -static void try_get_dlg_item_uint32(HWND hwnd, int id, uint32_t *out) -{ - char buf[128]; - if (!GetDlgItemText(hwnd, id, buf, sizeof(buf))) - return; - - if (!*buf) - return; - - char *end; - unsigned long val = strtoul(buf, &end, 10); - if (*end) - return; - - if ((val >> 16) >> 16) - return; - - *out = val; -} - -static ppk_save_parameters save_params; - -struct PPKParams { - ppk_save_parameters params; - uint32_t time_passes, time_ms; -}; - -/* - * Dialog-box function for the passphrase box. - */ -static INT_PTR CALLBACK PPKParamsProc(HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) -{ - struct PPKParams *pp; - char *buf; - - if (msg == WM_INITDIALOG) { - pp = (struct PPKParams *)lParam; - SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pp); - } else { - pp = (struct PPKParams *)GetWindowLongPtr(hwnd, GWLP_USERDATA); - } - - switch (msg) { - case WM_INITDIALOG: - SetForegroundWindow(hwnd); - SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, - SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); - - if (has_help()) - SetWindowLongPtr(hwnd, GWL_EXSTYLE, - GetWindowLongPtr(hwnd, GWL_EXSTYLE) | - WS_EX_CONTEXTHELP); - - /* - * Centre the window. - */ - { /* centre the window */ - RECT rs, rd; - HWND hw; - - 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); - } - - CheckRadioButton(hwnd, IDC_PPKVER_2, IDC_PPKVER_3, - IDC_PPKVER_2 + (pp->params.fmt_version - 2)); - - CheckRadioButton( - hwnd, IDC_KDF_ARGON2ID, IDC_KDF_ARGON2D, - (pp->params.argon2_flavour == Argon2id ? IDC_KDF_ARGON2ID : - pp->params.argon2_flavour == Argon2i ? IDC_KDF_ARGON2I : - /* pp->params.argon2_flavour == Argon2d ? */ IDC_KDF_ARGON2D)); - - buf = dupprintf("%"PRIu32, pp->params.argon2_mem); - SetDlgItemText(hwnd, IDC_ARGON2_MEM, buf); - sfree(buf); - - if (pp->params.argon2_passes_auto) { - CheckRadioButton(hwnd, IDC_PPK_AUTO_YES, IDC_PPK_AUTO_NO, - IDC_PPK_AUTO_YES); - buf = dupprintf("%"PRIu32, pp->time_ms); - SetDlgItemText(hwnd, IDC_ARGON2_TIME, buf); - sfree(buf); - } else { - CheckRadioButton(hwnd, IDC_PPK_AUTO_YES, IDC_PPK_AUTO_NO, - IDC_PPK_AUTO_NO); - buf = dupprintf("%"PRIu32, pp->time_passes); - SetDlgItemText(hwnd, IDC_ARGON2_TIME, buf); - sfree(buf); - } - - buf = dupprintf("%"PRIu32, pp->params.argon2_parallelism); - SetDlgItemText(hwnd, IDC_ARGON2_PARALLEL, buf); - sfree(buf); - - return 0; - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDOK: - EndDialog(hwnd, 1); - return 0; - case IDCANCEL: - EndDialog(hwnd, 0); - return 0; - case IDC_PPKVER_2: - pp->params.fmt_version = 2; - return 0; - case IDC_PPKVER_3: - pp->params.fmt_version = 3; - return 0; - case IDC_KDF_ARGON2ID: - pp->params.argon2_flavour = Argon2id; - return 0; - case IDC_KDF_ARGON2I: - pp->params.argon2_flavour = Argon2i; - return 0; - case IDC_KDF_ARGON2D: - pp->params.argon2_flavour = Argon2d; - return 0; - case IDC_ARGON2_MEM: - try_get_dlg_item_uint32(hwnd, IDC_ARGON2_MEM, - &pp->params.argon2_mem); - return 0; - case IDC_PPK_AUTO_YES: - pp->params.argon2_passes_auto = true; - buf = dupprintf("%"PRIu32, pp->time_ms); - SetDlgItemText(hwnd, IDC_ARGON2_TIME, buf); - sfree(buf); - return 0; - case IDC_PPK_AUTO_NO: - pp->params.argon2_passes_auto = false; - buf = dupprintf("%"PRIu32, pp->time_passes); - SetDlgItemText(hwnd, IDC_ARGON2_TIME, buf); - sfree(buf); - return 0; - case IDC_ARGON2_TIME: - try_get_dlg_item_uint32(hwnd, IDC_ARGON2_TIME, - pp->params.argon2_passes_auto ? - &pp->time_ms : &pp->time_passes); - return 0; - case IDC_ARGON2_PARALLEL: - try_get_dlg_item_uint32(hwnd, IDC_ARGON2_PARALLEL, - &pp->params.argon2_parallelism); - return 0; - } - return 0; - case WM_HELP: { - int id = ((LPHELPINFO)lParam)->iCtrlId; - const char *topic = NULL; - switch (id) { - case IDC_PPKVER_STATIC: - case IDC_PPKVER_2: - case IDC_PPKVER_3: - topic = WINHELP_CTX_puttygen_ppkver; break; - case IDC_KDF_STATIC: - case IDC_KDF_ARGON2ID: - case IDC_KDF_ARGON2I: - case IDC_KDF_ARGON2D: - case IDC_ARGON2_MEM_STATIC: - case IDC_ARGON2_MEM: - case IDC_ARGON2_MEM_STATIC2: - case IDC_ARGON2_TIME_STATIC: - case IDC_ARGON2_TIME: - case IDC_PPK_AUTO_YES: - case IDC_PPK_AUTO_NO: - case IDC_ARGON2_PARALLEL_STATIC: - case IDC_ARGON2_PARALLEL: - topic = WINHELP_CTX_puttygen_kdfparam; break; - } - if (topic) { - launch_help(hwnd, topic); - } else { - MessageBeep(0); - } - break; - } - case WM_CLOSE: - EndDialog(hwnd, 0); - return 0; - } - return 0; -} - -/* - * Prompt for a key file. Assumes the filename buffer is of size - * FILENAME_MAX. - */ -static bool prompt_keyfile(HWND hwnd, char *dlgtitle, - char *filename, bool save, bool ppk) -{ - OPENFILENAME of; - memset(&of, 0, sizeof(of)); - of.hwndOwner = hwnd; - if (ppk) { - of.lpstrFilter = "PuTTY Private Key Files (*.ppk)\0*.ppk\0" - "All Files (*.*)\0*\0\0\0"; - of.lpstrDefExt = ".ppk"; - } else { - of.lpstrFilter = "All Files (*.*)\0*\0\0\0"; - } - of.lpstrCustomFilter = NULL; - of.nFilterIndex = 1; - of.lpstrFile = filename; - *filename = '\0'; - of.nMaxFile = FILENAME_MAX; - of.lpstrFileTitle = NULL; - of.lpstrTitle = dlgtitle; - of.Flags = 0; - return request_file(NULL, &of, false, save); -} - -/* - * Dialog-box function for the Licence box. - */ -static INT_PTR CALLBACK LicenceProc(HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_INITDIALOG: { - /* - * Centre the window. - */ - RECT rs, rd; - HWND hw; - - 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); - - SetDlgItemText(hwnd, 1000, LICENCE_TEXT("\r\n\r\n")); - return 1; - } - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDOK: - case IDCANCEL: - EndDialog(hwnd, 1); - return 0; - } - return 0; - case WM_CLOSE: - EndDialog(hwnd, 1); - return 0; - } - return 0; -} - -/* - * Dialog-box function for the About box. - */ -static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_INITDIALOG: - /* - * Centre the window. - */ - { /* centre the window */ - RECT rs, rd; - HWND hw; - - 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); - } - - { - char *buildinfo_text = buildinfo("\r\n"); - char *text = dupprintf - ("PuTTYgen\r\n\r\n%s\r\n\r\n%s\r\n\r\n%s", - ver, buildinfo_text, - "\251 " SHORT_COPYRIGHT_DETAILS ". All rights reserved."); - sfree(buildinfo_text); - SetDlgItemText(hwnd, 1000, text); - MakeDlgItemBorderless(hwnd, 1000); - sfree(text); - } - return 1; - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDOK: - case IDCANCEL: - EndDialog(hwnd, 1); - return 0; - case 101: - EnableWindow(hwnd, 0); - DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc); - EnableWindow(hwnd, 1); - SetActiveWindow(hwnd); - return 0; - case 102: - /* Load web browser */ - ShellExecute(hwnd, "open", - "https://www.chiark.greenend.org.uk/~sgtatham/putty/", - 0, 0, SW_SHOWDEFAULT); - return 0; - } - return 0; - case WM_CLOSE: - EndDialog(hwnd, 1); - return 0; - } - return 0; -} - -typedef enum {RSA, DSA, ECDSA, EDDSA} keytype; - -/* - * Thread to generate a key. - */ -struct rsa_key_thread_params { - HWND progressbar; /* notify this with progress */ - HWND dialog; /* notify this on completion */ - int key_bits; /* bits in key modulus (RSA, DSA) */ - int curve_bits; /* bits in elliptic curve (ECDSA) */ - keytype keytype; - const PrimeGenerationPolicy *primepolicy; - bool rsa_strong; - union { - RSAKey *key; - struct dss_key *dsskey; - struct ecdsa_key *eckey; - struct eddsa_key *edkey; - }; -}; -static DWORD WINAPI generate_key_thread(void *param) -{ - struct rsa_key_thread_params *params = - (struct rsa_key_thread_params *) param; - struct progress prog; - prog.progbar = params->progressbar; - - win_progress_initialise(&prog); - - PrimeGenerationContext *pgc = primegen_new_context(params->primepolicy); - - if (params->keytype == DSA) - dsa_generate(params->dsskey, params->key_bits, pgc, &prog.rec); - else if (params->keytype == ECDSA) - ecdsa_generate(params->eckey, params->curve_bits); - else if (params->keytype == EDDSA) - eddsa_generate(params->edkey, params->curve_bits); - else - rsa_generate(params->key, params->key_bits, params->rsa_strong, - pgc, &prog.rec); - - primegen_free_context(pgc); - - PostMessage(params->dialog, WM_DONEKEY, 0, 0); - - win_progress_cleanup(&prog); - - sfree(params); - return 0; -} - -struct MainDlgState { - bool collecting_entropy; - bool generation_thread_exists; - bool key_exists; - int entropy_got, entropy_required, entropy_size; - int key_bits, curve_bits; - bool ssh2; - keytype keytype; - const PrimeGenerationPolicy *primepolicy; - bool rsa_strong; - FingerprintType fptype; - char **commentptr; /* points to key.comment or ssh2key.comment */ - ssh2_userkey ssh2key; - unsigned *entropy; - union { - RSAKey key; - struct dss_key dsskey; - struct ecdsa_key eckey; - struct eddsa_key edkey; - }; - HMENU filemenu, keymenu, cvtmenu; -}; - -static void hidemany(HWND hwnd, const int *ids, bool hideit) -{ - while (*ids) { - ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW)); - } -} - -static void setupbigedit1(HWND hwnd, int id, int idstatic, RSAKey *key) -{ - char *buffer = ssh1_pubkey_str(key); - SetDlgItemText(hwnd, id, buffer); - SetDlgItemText(hwnd, idstatic, - "&Public key for pasting into authorized_keys file:"); - sfree(buffer); -} - -static void setupbigedit2(HWND hwnd, int id, int idstatic, - ssh2_userkey *key) -{ - char *buffer = ssh2_pubkey_openssh_str(key); - SetDlgItemText(hwnd, id, buffer); - SetDlgItemText(hwnd, idstatic, "&Public key for pasting into " - "OpenSSH authorized_keys file:"); - sfree(buffer); -} - -/* - * Warn about the obsolescent key file format. - */ -void old_keyfile_warning(void) -{ - static const char mbtitle[] = "PuTTY 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" - "PuTTY may stop supporting this private key format,\n" - "so we recommend you convert your key to the new\n" - "format.\n" - "\n" - "Once the key is loaded into PuTTYgen, you can perform\n" - "this conversion simply by saving it again."; - - MessageBox(NULL, message, mbtitle, MB_OK); -} - -enum { - controlidstart = 100, - IDC_QUIT, - IDC_TITLE, - IDC_BOX_KEY, - IDC_NOKEY, - IDC_GENERATING, - IDC_PROGRESS, - IDC_PKSTATIC, IDC_KEYDISPLAY, - IDC_FPSTATIC, IDC_FINGERPRINT, - IDC_COMMENTSTATIC, IDC_COMMENTEDIT, - IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT, - IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, - IDC_BOX_ACTIONS, - IDC_GENSTATIC, IDC_GENERATE, - IDC_LOADSTATIC, IDC_LOAD, - IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB, - IDC_BOX_PARAMS, - IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA, - IDC_KEYSSH2ECDSA, IDC_KEYSSH2EDDSA, - IDC_PRIMEGEN_PROB, IDC_PRIMEGEN_MAURER_SIMPLE, IDC_PRIMEGEN_MAURER_COMPLEX, - IDC_RSA_STRONG, - IDC_FPTYPE_SHA256, IDC_FPTYPE_MD5, - IDC_PPK_PARAMS, - IDC_BITSSTATIC, IDC_BITS, - IDC_ECCURVESTATIC, IDC_ECCURVE, - IDC_EDCURVESTATIC, IDC_EDCURVE, - IDC_NOTHINGSTATIC, - IDC_ABOUT, - IDC_GIVEHELP, - IDC_IMPORT, - IDC_EXPORT_OPENSSH_AUTO, IDC_EXPORT_OPENSSH_NEW, - IDC_EXPORT_SSHCOM -}; - -static const int nokey_ids[] = { IDC_NOKEY, 0 }; -static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 }; -static const int gotkey_ids[] = { - IDC_PKSTATIC, IDC_KEYDISPLAY, - IDC_FPSTATIC, IDC_FINGERPRINT, - IDC_COMMENTSTATIC, IDC_COMMENTEDIT, - IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT, - IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0 -}; - -/* - * Small UI helper function to switch the state of the main dialog - * by enabling and disabling controls and menu items. - */ -void ui_set_state(HWND hwnd, struct MainDlgState *state, int status) -{ - int type; - - switch (status) { - case 0: /* no key */ - hidemany(hwnd, nokey_ids, false); - hidemany(hwnd, generating_ids, true); - hidemany(hwnd, gotkey_ids, true); - EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1); - EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1); - EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0); - EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0); - EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1); - EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1); - EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1); - EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1); - EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2EDDSA), 1); - EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1); - EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND); - EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND); - EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND); - EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND); - EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND); - EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND); - EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND); - EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA, - MF_ENABLED|MF_BYCOMMAND); - EnableMenuItem(state->keymenu, IDC_KEYSSH2EDDSA, - MF_ENABLED|MF_BYCOMMAND); - EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND); - EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_AUTO, - MF_GRAYED|MF_BYCOMMAND); - EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_NEW, - MF_GRAYED|MF_BYCOMMAND); - EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM, - MF_GRAYED|MF_BYCOMMAND); - break; - case 1: /* generating key */ - hidemany(hwnd, nokey_ids, true); - hidemany(hwnd, generating_ids, false); - hidemany(hwnd, gotkey_ids, true); - EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0); - EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0); - EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0); - EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0); - EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0); - EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0); - EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0); - EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 0); - EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2EDDSA), 0); - EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0); - EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND); - EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND); - EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND); - EnableMenuItem(state->keymenu, IDC_GENERATE, MF_GRAYED|MF_BYCOMMAND); - EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND); - EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND); - EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND); - EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA, - MF_GRAYED|MF_BYCOMMAND); - EnableMenuItem(state->keymenu, IDC_KEYSSH2EDDSA, - MF_GRAYED|MF_BYCOMMAND); - EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND); - EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_AUTO, - MF_GRAYED|MF_BYCOMMAND); - EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_NEW, - MF_GRAYED|MF_BYCOMMAND); - EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM, - MF_GRAYED|MF_BYCOMMAND); - break; - case 2: - hidemany(hwnd, nokey_ids, true); - hidemany(hwnd, generating_ids, true); - hidemany(hwnd, gotkey_ids, false); - EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1); - EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1); - EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1); - EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1); - EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1); - EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1); - EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1); - EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1); - EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2EDDSA), 1); - EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1); - EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND); - EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND); - EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|MF_BYCOMMAND); - EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND); - EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND); - EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND); - EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND); - EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA, - MF_ENABLED|MF_BYCOMMAND); - EnableMenuItem(state->keymenu, IDC_KEYSSH2EDDSA, - MF_ENABLED|MF_BYCOMMAND); - EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND); - /* - * Enable export menu items if and only if the key type - * supports this kind of export. - */ - type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1; -#define do_export_menuitem(x,y) \ - EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \ - (import_target_type(y)==type?MF_ENABLED:MF_GRAYED)) - do_export_menuitem(IDC_EXPORT_OPENSSH_AUTO, SSH_KEYTYPE_OPENSSH_AUTO); - do_export_menuitem(IDC_EXPORT_OPENSSH_NEW, SSH_KEYTYPE_OPENSSH_NEW); - do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM); -#undef do_export_menuitem - break; - } -} - -/* - * Helper functions to set the key type, taking care of keeping the - * menu and radio button selections in sync and also showing/hiding - * the appropriate size/curve control for the current key type. - */ -void ui_update_key_type_ctrls(HWND hwnd) -{ - enum { BITS, ECCURVE, EDCURVE, NOTHING } which; - static const int bits_ids[] = { - IDC_BITSSTATIC, IDC_BITS, 0 - }; - static const int eccurve_ids[] = { - IDC_ECCURVESTATIC, IDC_ECCURVE, 0 - }; - static const int edcurve_ids[] = { - IDC_EDCURVESTATIC, IDC_EDCURVE, 0 - }; - static const int nothing_ids[] = { - IDC_NOTHINGSTATIC, 0 - }; - - if (IsDlgButtonChecked(hwnd, IDC_KEYSSH1) || - IsDlgButtonChecked(hwnd, IDC_KEYSSH2RSA) || - IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) { - which = BITS; - } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ECDSA)) { - which = ECCURVE; - } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2EDDSA)) { - which = EDCURVE; - } else { - /* Currently not used since Ed25519 stopped being the only - * thing in its class, but I'll keep it here in case it comes - * in useful again */ - which = NOTHING; - } - - hidemany(hwnd, bits_ids, which != BITS); - hidemany(hwnd, eccurve_ids, which != ECCURVE); - hidemany(hwnd, edcurve_ids, which != EDCURVE); - hidemany(hwnd, nothing_ids, which != NOTHING); -} -void ui_set_key_type(HWND hwnd, struct MainDlgState *state, int button) -{ - CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2EDDSA, button); - CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2EDDSA, - button, MF_BYCOMMAND); - ui_update_key_type_ctrls(hwnd); -} -void ui_set_primepolicy(HWND hwnd, struct MainDlgState *state, int option) -{ - CheckMenuRadioItem(state->keymenu, IDC_PRIMEGEN_PROB, - IDC_PRIMEGEN_MAURER_COMPLEX, option, MF_BYCOMMAND); - switch (option) { - case IDC_PRIMEGEN_PROB: - state->primepolicy = &primegen_probabilistic; - break; - case IDC_PRIMEGEN_MAURER_SIMPLE: - state->primepolicy = &primegen_provable_maurer_simple; - break; - case IDC_PRIMEGEN_MAURER_COMPLEX: - state->primepolicy = &primegen_provable_maurer_complex; - break; - } -} -void ui_set_rsa_strong(HWND hwnd, struct MainDlgState *state, bool enable) -{ - state->rsa_strong = enable; - CheckMenuItem(state->keymenu, IDC_RSA_STRONG, - (enable ? MF_CHECKED : 0) | MF_BYCOMMAND); -} -static FingerprintType idc_to_fptype(int option) -{ - switch (option) { - case IDC_FPTYPE_SHA256: - return SSH_FPTYPE_SHA256; - case IDC_FPTYPE_MD5: - return SSH_FPTYPE_MD5; - default: - unreachable("bad control id in idc_to_fptype"); - } -} -static int fptype_to_idc(FingerprintType fptype) -{ - switch (fptype) { - case SSH_FPTYPE_SHA256: - return IDC_FPTYPE_SHA256; - case SSH_FPTYPE_MD5: - return IDC_FPTYPE_MD5; - default: - unreachable("bad fptype in fptype_to_idc"); - } -} -void ui_set_fptype(HWND hwnd, struct MainDlgState *state, int option) -{ - CheckMenuRadioItem(state->keymenu, IDC_FPTYPE_SHA256, - IDC_FPTYPE_MD5, option, MF_BYCOMMAND); - - state->fptype = idc_to_fptype(option); - - if (state->key_exists && state->ssh2) { - char *fp = ssh2_fingerprint(state->ssh2key.key, state->fptype); - SetDlgItemText(hwnd, IDC_FINGERPRINT, fp); - sfree(fp); - } -} - -void load_key_file(HWND hwnd, struct MainDlgState *state, - Filename *filename, bool was_import_cmd) -{ - char *passphrase; - bool needs_pass; - int type, realtype; - int ret; - const char *errmsg = NULL; - char *comment; - RSAKey newkey1; - ssh2_userkey *newkey2 = NULL; - - type = realtype = key_type(filename); - if (type != SSH_KEYTYPE_SSH1 && - type != SSH_KEYTYPE_SSH2 && - !import_possible(type)) { - char *msg = dupprintf("Couldn't load private key (%s)", - key_type_to_str(type)); - message_box(hwnd, msg, "PuTTYgen Error", MB_OK | MB_ICONERROR, - HELPCTXID(errors_cantloadkey)); - sfree(msg); - return; - } - - if (type != SSH_KEYTYPE_SSH1 && - type != SSH_KEYTYPE_SSH2) { - realtype = type; - type = import_target_type(type); - } - - comment = NULL; - passphrase = NULL; - if (realtype == SSH_KEYTYPE_SSH1) - needs_pass = rsa1_encrypted_f(filename, &comment); - else if (realtype == SSH_KEYTYPE_SSH2) - needs_pass = ppk_encrypted_f(filename, &comment); - else - needs_pass = import_encrypted(filename, realtype, &comment); - do { - burnstr(passphrase); - passphrase = NULL; - - if (needs_pass) { - int dlgret; - struct PassphraseProcStruct pps; - pps.passphrase = &passphrase; - pps.comment = comment; - dlgret = DialogBoxParam(hinst, - MAKEINTRESOURCE(210), - NULL, PassphraseProc, - (LPARAM) &pps); - if (!dlgret) { - ret = -2; - break; - } - assert(passphrase != NULL); - } else - passphrase = dupstr(""); - if (type == SSH_KEYTYPE_SSH1) { - if (realtype == type) - ret = rsa1_load_f(filename, &newkey1, passphrase, &errmsg); - else - ret = import_ssh1(filename, realtype, &newkey1, - passphrase, &errmsg); - } else { - if (realtype == type) - newkey2 = ppk_load_f(filename, passphrase, &errmsg); - else - newkey2 = import_ssh2(filename, realtype, passphrase, &errmsg); - if (newkey2 == SSH2_WRONG_PASSPHRASE) - ret = -1; - else if (!newkey2) - ret = 0; - else - ret = 1; - } - } while (ret == -1); - if (comment) - sfree(comment); - if (ret == 0) { - char *msg = dupprintf("Couldn't load private key (%s)", errmsg); - message_box(hwnd, msg, "PuTTYgen Error", MB_OK | MB_ICONERROR, - HELPCTXID(errors_cantloadkey)); - sfree(msg); - } else if (ret == 1) { - /* - * Now update the key controls with all the - * key data. - */ - { - SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, - passphrase); - SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, - passphrase); - if (type == SSH_KEYTYPE_SSH1) { - char *fingerprint, *savecomment; - - state->ssh2 = false; - state->commentptr = &state->key.comment; - state->key = newkey1; - - /* - * Set the key fingerprint. - */ - savecomment = state->key.comment; - state->key.comment = NULL; - fingerprint = rsa_ssh1_fingerprint(&state->key); - state->key.comment = savecomment; - SetDlgItemText(hwnd, IDC_FINGERPRINT, fingerprint); - sfree(fingerprint); - - /* - * Construct a decimal representation - * of the key, for pasting into - * .ssh/authorized_keys on a Unix box. - */ - setupbigedit1(hwnd, IDC_KEYDISPLAY, - IDC_PKSTATIC, &state->key); - } else { - char *fp; - char *savecomment; - - state->ssh2 = true; - state->commentptr = - &state->ssh2key.comment; - state->ssh2key = *newkey2; /* structure copy */ - sfree(newkey2); - - savecomment = state->ssh2key.comment; - state->ssh2key.comment = NULL; - fp = ssh2_fingerprint(state->ssh2key.key, state->fptype); - state->ssh2key.comment = savecomment; - - SetDlgItemText(hwnd, IDC_FINGERPRINT, fp); - sfree(fp); - - setupbigedit2(hwnd, IDC_KEYDISPLAY, - IDC_PKSTATIC, &state->ssh2key); - } - SetDlgItemText(hwnd, IDC_COMMENTEDIT, - *state->commentptr); - } - /* - * Finally, hide the progress bar and show - * the key data. - */ - ui_set_state(hwnd, state, 2); - state->key_exists = true; - - /* - * If the user has imported a foreign key - * using the Load command, let them know. - * If they've used the Import command, be - * silent. - */ - if (realtype != type && !was_import_cmd) { - char msg[512]; - sprintf(msg, "Successfully imported foreign key\n" - "(%s).\n" - "To use this key with PuTTY, you need to\n" - "use the \"Save private key\" command to\n" - "save it in PuTTY's own format.", - key_type_to_str(realtype)); - MessageBox(NULL, msg, "PuTTYgen Notice", - MB_OK | MB_ICONINFORMATION); - } - } - burnstr(passphrase); -} - -static void start_generating_key(HWND hwnd, struct MainDlgState *state) -{ - static const char generating_msg[] = - "Please wait while a key is generated..."; - - struct rsa_key_thread_params *params; - DWORD threadid; - - SetDlgItemText(hwnd, IDC_GENERATING, generating_msg); - SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0, - MAKELPARAM(0, PROGRESSRANGE)); - SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0); - - params = snew(struct rsa_key_thread_params); - params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS); - params->dialog = hwnd; - params->key_bits = state->key_bits; - params->curve_bits = state->curve_bits; - params->keytype = state->keytype; - params->primepolicy = state->primepolicy; - params->rsa_strong = state->rsa_strong; - params->key = &state->key; - params->dsskey = &state->dsskey; - - HANDLE hThread = CreateThread(NULL, 0, generate_key_thread, - params, 0, &threadid); - if (!hThread) { - MessageBox(hwnd, "Out of thread resources", - "Key generation error", - MB_OK | MB_ICONERROR); - sfree(params); - } else { - CloseHandle(hThread); /* we don't need the thread handle */ - state->generation_thread_exists = true; - } -} - -/* - * Dialog-box function for the main PuTTYgen dialog box. - */ -static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) -{ - static const char entropy_msg[] = - "Please generate some randomness by moving the mouse over the blank area."; - struct MainDlgState *state; - - switch (msg) { - case WM_INITDIALOG: - if (has_help()) - SetWindowLongPtr(hwnd, GWL_EXSTYLE, - GetWindowLongPtr(hwnd, GWL_EXSTYLE) | - WS_EX_CONTEXTHELP); - else { - /* - * If we add a Help button, this is where we destroy it - * if the help file isn't present. - */ - } - SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG, - (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(200))); - - state = snew(struct MainDlgState); - state->generation_thread_exists = false; - state->collecting_entropy = false; - state->entropy = NULL; - state->key_exists = false; - SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) state); - { - HMENU menu, menu1; - - menu = CreateMenu(); - - menu1 = CreateMenu(); - AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key"); - AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key"); - AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key"); - AppendMenu(menu1, MF_SEPARATOR, 0, 0); - AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit"); - AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&File"); - state->filemenu = menu1; - - menu1 = CreateMenu(); - AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair"); - AppendMenu(menu1, MF_SEPARATOR, 0, 0); - AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH-&1 key (RSA)"); - AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH-2 &RSA key"); - AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH-2 &DSA key"); - AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2ECDSA, "SSH-2 &ECDSA key"); - AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2EDDSA, "SSH-2 EdD&SA key"); - AppendMenu(menu1, MF_SEPARATOR, 0, 0); - AppendMenu(menu1, MF_ENABLED, IDC_PRIMEGEN_PROB, - "Use probable primes (fast)"); - AppendMenu(menu1, MF_ENABLED, IDC_PRIMEGEN_MAURER_SIMPLE, - "Use proven primes (slower)"); - AppendMenu(menu1, MF_ENABLED, IDC_PRIMEGEN_MAURER_COMPLEX, - "Use proven primes with even distribution (slowest)"); - AppendMenu(menu1, MF_SEPARATOR, 0, 0); - AppendMenu(menu1, MF_ENABLED, IDC_RSA_STRONG, - "Use \"strong\" primes as RSA key factors"); - AppendMenu(menu1, MF_SEPARATOR, 0, 0); - AppendMenu(menu1, MF_ENABLED, IDC_PPK_PARAMS, - "Parameters for saving key files..."); - AppendMenu(menu1, MF_SEPARATOR, 0, 0); - AppendMenu(menu1, MF_ENABLED, IDC_FPTYPE_SHA256, - "Show fingerprint as SHA256"); - AppendMenu(menu1, MF_ENABLED, IDC_FPTYPE_MD5, - "Show fingerprint as MD5"); - AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&Key"); - state->keymenu = menu1; - - menu1 = CreateMenu(); - AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key"); - AppendMenu(menu1, MF_SEPARATOR, 0, 0); - AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH_AUTO, - "Export &OpenSSH key"); - AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH_NEW, - "Export &OpenSSH key (force new file format)"); - AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM, - "Export &ssh.com key"); - AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, - "Con&versions"); - state->cvtmenu = menu1; - - menu1 = CreateMenu(); - AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About"); - if (has_help()) - AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help"); - AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&Help"); - - SetMenu(hwnd, menu); - } - - /* - * Centre the window. - */ - { /* centre the window */ - RECT rs, rd; - HWND hw; - - 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); - } - - { - struct ctlpos cp, cp2; - int ymax; - - /* Accelerators used: acglops1rbvde */ - - ctlposinit(&cp, hwnd, 4, 4, 4); - beginbox(&cp, "Key", IDC_BOX_KEY); - cp2 = cp; - statictext(&cp2, "No key.", 1, IDC_NOKEY); - cp2 = cp; - statictext(&cp2, "", 1, IDC_GENERATING); - progressbar(&cp2, IDC_PROGRESS); - bigeditctrl(&cp, - "&Public key for pasting into authorized_keys file:", - IDC_PKSTATIC, IDC_KEYDISPLAY, 5); - SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0); - staticedit(&cp, "Key f&ingerprint:", IDC_FPSTATIC, - IDC_FINGERPRINT, 82); - SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1, - 0); - staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC, - IDC_COMMENTEDIT, 82); - staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC, - IDC_PASSPHRASE1EDIT, 82); - staticpassedit(&cp, "C&onfirm passphrase:", - IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 82); - endbox(&cp); - beginbox(&cp, "Actions", IDC_BOX_ACTIONS); - staticbtn(&cp, "Generate a public/private key pair", - IDC_GENSTATIC, "&Generate", IDC_GENERATE); - staticbtn(&cp, "Load an existing private key file", - IDC_LOADSTATIC, "&Load", IDC_LOAD); - static2btn(&cp, "Save the generated key", IDC_SAVESTATIC, - "Save p&ublic key", IDC_SAVEPUB, - "&Save private key", IDC_SAVE); - endbox(&cp); - beginbox(&cp, "Parameters", IDC_BOX_PARAMS); - radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 5, - "&RSA", IDC_KEYSSH2RSA, - "&DSA", IDC_KEYSSH2DSA, - "&ECDSA", IDC_KEYSSH2ECDSA, - "EdD&SA", IDC_KEYSSH2EDDSA, - "SSH-&1 (RSA)", IDC_KEYSSH1, - NULL); - cp2 = cp; - staticedit(&cp2, "Number of &bits in a generated key:", - IDC_BITSSTATIC, IDC_BITS, 20); - ymax = cp2.ypos; - cp2 = cp; - staticddl(&cp2, "Cur&ve to use for generating this key:", - IDC_ECCURVESTATIC, IDC_ECCURVE, 30); - SendDlgItemMessage(hwnd, IDC_ECCURVE, CB_RESETCONTENT, 0, 0); - { - int i, bits; - const struct ec_curve *curve; - const ssh_keyalg *alg; - - for (i = 0; i < n_ec_nist_curve_lengths; i++) { - bits = ec_nist_curve_lengths[i]; - ec_nist_alg_and_curve_by_bits(bits, &curve, &alg); - SendDlgItemMessage(hwnd, IDC_ECCURVE, CB_ADDSTRING, 0, - (LPARAM)curve->textname); - } - } - ymax = ymax > cp2.ypos ? ymax : cp2.ypos; - cp2 = cp; - staticddl(&cp2, "Cur&ve to use for generating this key:", - IDC_EDCURVESTATIC, IDC_EDCURVE, 30); - SendDlgItemMessage(hwnd, IDC_EDCURVE, CB_RESETCONTENT, 0, 0); - { - int i, bits; - const struct ec_curve *curve; - const ssh_keyalg *alg; - - for (i = 0; i < n_ec_ed_curve_lengths; i++) { - bits = ec_ed_curve_lengths[i]; - ec_ed_alg_and_curve_by_bits(bits, &curve, &alg); - char *desc = dupprintf("%s (%d bits)", - curve->textname, bits); - SendDlgItemMessage(hwnd, IDC_EDCURVE, CB_ADDSTRING, 0, - (LPARAM)desc); - sfree(desc); - } - } - ymax = ymax > cp2.ypos ? ymax : cp2.ypos; - cp2 = cp; - statictext(&cp2, "(nothing to configure for this key type)", - 1, IDC_NOTHINGSTATIC); - ymax = ymax > cp2.ypos ? ymax : cp2.ypos; - cp.ypos = ymax; - endbox(&cp); - } - ui_set_key_type(hwnd, state, IDC_KEYSSH2RSA); - ui_set_primepolicy(hwnd, state, IDC_PRIMEGEN_PROB); - ui_set_rsa_strong(hwnd, state, false); - ui_set_fptype(hwnd, state, fptype_to_idc(SSH_FPTYPE_DEFAULT)); - SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEY_BITS, false); - SendDlgItemMessage(hwnd, IDC_ECCURVE, CB_SETCURSEL, - DEFAULT_ECCURVE_INDEX, 0); - SendDlgItemMessage(hwnd, IDC_EDCURVE, CB_SETCURSEL, - DEFAULT_EDCURVE_INDEX, 0); - - /* - * Initially, hide the progress bar and the key display, - * and show the no-key display. Also disable the Save - * buttons, because with no key we obviously can't save - * anything. - */ - ui_set_state(hwnd, state, 0); - - /* - * Load a key file if one was provided on the command line. - */ - if (cmdline_keyfile) { - Filename *fn = filename_from_str(cmdline_keyfile); - load_key_file(hwnd, state, fn, false); - filename_free(fn); - } - - return 1; - case WM_MOUSEMOVE: - state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA); - if (state->collecting_entropy && - state->entropy && state->entropy_got < state->entropy_required) { - state->entropy[state->entropy_got++] = lParam; - state->entropy[state->entropy_got++] = GetMessageTime(); - SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, - state->entropy_got, 0); - if (state->entropy_got >= state->entropy_required) { - /* - * Seed the entropy pool - */ - random_reseed( - make_ptrlen(state->entropy, state->entropy_size)); - smemclr(state->entropy, state->entropy_size); - sfree(state->entropy); - state->collecting_entropy = false; - - start_generating_key(hwnd, state); - } - } - break; - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDC_KEYSSH1: - case IDC_KEYSSH2RSA: - case IDC_KEYSSH2DSA: - case IDC_KEYSSH2ECDSA: - case IDC_KEYSSH2EDDSA: { - state = (struct MainDlgState *) - GetWindowLongPtr(hwnd, GWLP_USERDATA); - ui_set_key_type(hwnd, state, LOWORD(wParam)); - break; - } - case IDC_PRIMEGEN_PROB: - case IDC_PRIMEGEN_MAURER_SIMPLE: - case IDC_PRIMEGEN_MAURER_COMPLEX: { - state = (struct MainDlgState *) - GetWindowLongPtr(hwnd, GWLP_USERDATA); - ui_set_primepolicy(hwnd, state, LOWORD(wParam)); - break; - } - case IDC_FPTYPE_SHA256: - case IDC_FPTYPE_MD5: { - state = (struct MainDlgState *) - GetWindowLongPtr(hwnd, GWLP_USERDATA); - ui_set_fptype(hwnd, state, LOWORD(wParam)); - break; - } - case IDC_RSA_STRONG: { - state = (struct MainDlgState *) - GetWindowLongPtr(hwnd, GWLP_USERDATA); - ui_set_rsa_strong(hwnd, state, !state->rsa_strong); - break; - } - case IDC_PPK_PARAMS: { - struct PPKParams pp[1]; - pp->params = save_params; - if (pp->params.argon2_passes_auto) { - pp->time_ms = pp->params.argon2_milliseconds; - pp->time_passes = 13; - } else { - pp->time_ms = 100; - pp->time_passes = pp->params.argon2_passes; - } - int dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(215), - NULL, PPKParamsProc, (LPARAM)pp); - if (dlgret) { - if (pp->params.argon2_passes_auto) { - pp->params.argon2_milliseconds = pp->time_ms; - } else { - pp->params.argon2_passes = pp->time_passes; - } - save_params = pp->params; - } - break; - } - case IDC_QUIT: - PostMessage(hwnd, WM_CLOSE, 0, 0); - break; - case IDC_COMMENTEDIT: - if (HIWORD(wParam) == EN_CHANGE) { - state = (struct MainDlgState *) - GetWindowLongPtr(hwnd, GWLP_USERDATA); - if (state->key_exists) { - HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT); - int len = GetWindowTextLength(editctl); - if (*state->commentptr) - sfree(*state->commentptr); - *state->commentptr = snewn(len + 1, char); - GetWindowText(editctl, *state->commentptr, len + 1); - if (state->ssh2) { - setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC, - &state->ssh2key); - } else { - setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC, - &state->key); - } - } - } - break; - case IDC_ABOUT: - EnableWindow(hwnd, 0); - DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc); - EnableWindow(hwnd, 1); - SetActiveWindow(hwnd); - return 0; - case IDC_GIVEHELP: - if (HIWORD(wParam) == BN_CLICKED || - HIWORD(wParam) == BN_DOUBLECLICKED) { - launch_help(hwnd, WINHELP_CTX_puttygen_general); - } - return 0; - case IDC_GENERATE: - if (HIWORD(wParam) != BN_CLICKED && - HIWORD(wParam) != BN_DOUBLECLICKED) - break; - state = - (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA); - if (!state->generation_thread_exists) { - unsigned raw_entropy_required; - unsigned char *raw_entropy_buf; - BOOL ok; - - state->key_bits = GetDlgItemInt(hwnd, IDC_BITS, &ok, false); - if (!ok) - state->key_bits = DEFAULT_KEY_BITS; - state->ssh2 = true; - - if (IsDlgButtonChecked(hwnd, IDC_KEYSSH1)) { - state->ssh2 = false; - state->keytype = RSA; - } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2RSA)) { - state->keytype = RSA; - } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) { - state->keytype = DSA; - } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ECDSA)) { - state->keytype = ECDSA; - int curveindex = SendDlgItemMessage(hwnd, IDC_ECCURVE, - CB_GETCURSEL, 0, 0); - assert(curveindex >= 0); - assert(curveindex < n_ec_nist_curve_lengths); - state->curve_bits = ec_nist_curve_lengths[curveindex]; - } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2EDDSA)) { - state->keytype = EDDSA; - int curveindex = SendDlgItemMessage(hwnd, IDC_EDCURVE, - CB_GETCURSEL, 0, 0); - assert(curveindex >= 0); - assert(curveindex < n_ec_ed_curve_lengths); - state->curve_bits = ec_ed_curve_lengths[curveindex]; - } else { - /* Somehow, no button was checked */ - break; - } - - if ((state->keytype == RSA || state->keytype == DSA) && - state->key_bits < 256) { - char *message = dupprintf - ("PuTTYgen will not generate a key smaller than 256" - " bits.\nKey length reset to default %d. Continue?", - DEFAULT_KEY_BITS); - int ret = MessageBox(hwnd, message, "PuTTYgen Warning", - MB_ICONWARNING | MB_OKCANCEL); - sfree(message); - if (ret != IDOK) - break; - state->key_bits = DEFAULT_KEY_BITS; - SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEY_BITS, false); - } else if ((state->keytype == RSA || state->keytype == DSA) && - state->key_bits < DEFAULT_KEY_BITS) { - char *message = dupprintf - ("Keys shorter than %d bits are not recommended. " - "Really generate this key?", DEFAULT_KEY_BITS); - int ret = MessageBox(hwnd, message, "PuTTYgen Warning", - MB_ICONWARNING | MB_OKCANCEL); - sfree(message); - if (ret != IDOK) - break; - } - - if (state->keytype == RSA || state->keytype == DSA) - raw_entropy_required = (state->key_bits / 2) * 2; - else if (state->keytype == ECDSA || state->keytype == EDDSA) - raw_entropy_required = (state->curve_bits / 2) * 2; - else - unreachable("we must have initialised keytype by now"); - - /* Bound the entropy collection above by the amount of - * data we can actually fit into the PRNG. Any more - * than that and it's doing no more good. */ - if (raw_entropy_required > random_seed_bits()) - raw_entropy_required = random_seed_bits(); - - raw_entropy_buf = snewn(raw_entropy_required, unsigned char); - if (win_read_random(raw_entropy_buf, raw_entropy_required)) { - /* - * If we can get entropy from CryptGenRandom, use - * it. But CryptGenRandom isn't a kernel-level - * CPRNG (according to Wikipedia), and papers have - * been published cryptanalysing it. So we'll - * still do manual entropy collection; we'll just - * do it _as well_ as this. - */ - random_reseed( - make_ptrlen(raw_entropy_buf, raw_entropy_required)); - } - - /* - * Manual entropy input, by making the user wave the - * mouse over the window a lot. - * - * My brief statistical tests on mouse movements - * suggest that there are about 2.5 bits of randomness - * in the x position, 2.5 in the y position, and 1.7 - * in the message time, making 5.7 bits of - * unpredictability per mouse movement. However, other - * people have told me it's far less than that, so I'm - * going to be stupidly cautious and knock that down - * to a nice round 2. With this method, we require two - * words per mouse movement, so with 2 bits per mouse - * movement we expect 2 bits every 2 words, i.e. the - * number of _words_ of mouse data we want to collect - * is just the same as the number of _bits_ of entropy - * we want. - */ - state->entropy_required = raw_entropy_required; - - ui_set_state(hwnd, state, 1); - SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg); - state->key_exists = false; - state->collecting_entropy = true; - - state->entropy_got = 0; - state->entropy_size = (state->entropy_required * - sizeof(unsigned)); - state->entropy = snewn(state->entropy_required, unsigned); - - SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0, - MAKELPARAM(0, state->entropy_required)); - SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0); - - smemclr(raw_entropy_buf, raw_entropy_required); - sfree(raw_entropy_buf); - } - break; - case IDC_SAVE: - case IDC_EXPORT_OPENSSH_AUTO: - case IDC_EXPORT_OPENSSH_NEW: - case IDC_EXPORT_SSHCOM: - if (HIWORD(wParam) != BN_CLICKED) - break; - state = - (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA); - if (state->key_exists) { - char filename[FILENAME_MAX]; - char *passphrase, *passphrase2; - int type, realtype; - - if (state->ssh2) - realtype = SSH_KEYTYPE_SSH2; - else - realtype = SSH_KEYTYPE_SSH1; - - if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_AUTO) - type = SSH_KEYTYPE_OPENSSH_AUTO; - else if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_NEW) - type = SSH_KEYTYPE_OPENSSH_NEW; - else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM) - type = SSH_KEYTYPE_SSHCOM; - else - type = realtype; - - if (type != realtype && - import_target_type(type) != realtype) { - char msg[256]; - sprintf(msg, "Cannot export an SSH-%d key in an SSH-%d" - " format", (state->ssh2 ? 2 : 1), - (state->ssh2 ? 1 : 2)); - MessageBox(hwnd, msg, - "PuTTYgen Error", MB_OK | MB_ICONERROR); - break; - } - - passphrase = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE1EDIT); - passphrase2 = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE2EDIT); - if (strcmp(passphrase, passphrase2)) { - MessageBox(hwnd, - "The two passphrases given do not match.", - "PuTTYgen Error", MB_OK | MB_ICONERROR); - burnstr(passphrase); - burnstr(passphrase2); - break; - } - burnstr(passphrase2); - if (!*passphrase) { - int ret; - ret = MessageBox(hwnd, - "Are you sure you want to save this key\n" - "without a passphrase to protect it?", - "PuTTYgen Warning", - MB_YESNO | MB_ICONWARNING); - if (ret != IDYES) { - burnstr(passphrase); - break; - } - } - if (prompt_keyfile(hwnd, "Save private key as:", - filename, true, (type == realtype))) { - int ret; - FILE *fp = fopen(filename, "r"); - if (fp) { - char *buffer; - fclose(fp); - buffer = dupprintf("Overwrite existing file\n%s?", - filename); - ret = MessageBox(hwnd, buffer, "PuTTYgen Warning", - MB_YESNO | MB_ICONWARNING); - sfree(buffer); - if (ret != IDYES) { - burnstr(passphrase); - break; - } - } - - if (state->ssh2) { - Filename *fn = filename_from_str(filename); - if (type != realtype) - ret = export_ssh2(fn, type, &state->ssh2key, - *passphrase ? passphrase : NULL); - else - ret = ppk_save_f(fn, &state->ssh2key, - *passphrase ? passphrase : NULL, - &save_params); - filename_free(fn); - } else { - Filename *fn = filename_from_str(filename); - if (type != realtype) - ret = export_ssh1(fn, type, &state->key, - *passphrase ? passphrase : NULL); - else - ret = rsa1_save_f(fn, &state->key, - *passphrase ? passphrase : NULL); - filename_free(fn); - } - if (ret <= 0) { - MessageBox(hwnd, "Unable to save key file", - "PuTTYgen Error", MB_OK | MB_ICONERROR); - } - } - burnstr(passphrase); - } - break; - case IDC_SAVEPUB: - if (HIWORD(wParam) != BN_CLICKED) - break; - state = - (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA); - if (state->key_exists) { - char filename[FILENAME_MAX]; - if (prompt_keyfile(hwnd, "Save public key as:", - filename, true, false)) { - int ret; - FILE *fp = fopen(filename, "r"); - if (fp) { - char *buffer; - fclose(fp); - buffer = dupprintf("Overwrite existing file\n%s?", - filename); - ret = MessageBox(hwnd, buffer, "PuTTYgen Warning", - MB_YESNO | MB_ICONWARNING); - sfree(buffer); - if (ret != IDYES) - break; - } - fp = fopen(filename, "w"); - if (!fp) { - MessageBox(hwnd, "Unable to open key file", - "PuTTYgen Error", MB_OK | MB_ICONERROR); - } else { - if (state->ssh2) { - strbuf *blob = strbuf_new(); - ssh_key_public_blob( - state->ssh2key.key, BinarySink_UPCAST(blob)); - ssh2_write_pubkey(fp, state->ssh2key.comment, - blob->u, blob->len, - SSH_KEYTYPE_SSH2_PUBLIC_RFC4716); - strbuf_free(blob); - } else { - ssh1_write_pubkey(fp, &state->key); - } - if (fclose(fp) < 0) { - MessageBox(hwnd, "Unable to save key file", - "PuTTYgen Error", MB_OK | MB_ICONERROR); - } - } - } - } - break; - case IDC_LOAD: - case IDC_IMPORT: - if (HIWORD(wParam) != BN_CLICKED) - break; - state = - (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA); - if (!state->generation_thread_exists) { - char filename[FILENAME_MAX]; - if (prompt_keyfile(hwnd, "Load private key:", filename, false, - LOWORD(wParam) == IDC_LOAD)) { - Filename *fn = filename_from_str(filename); - load_key_file(hwnd, state, fn, LOWORD(wParam) != IDC_LOAD); - filename_free(fn); - } - } - break; - } - return 0; - case WM_DONEKEY: - state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA); - state->generation_thread_exists = false; - state->key_exists = true; - SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0, - MAKELPARAM(0, PROGRESSRANGE)); - SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0); - if (state->ssh2) { - if (state->keytype == DSA) { - state->ssh2key.key = &state->dsskey.sshk; - } else if (state->keytype == ECDSA) { - state->ssh2key.key = &state->eckey.sshk; - } else if (state->keytype == EDDSA) { - state->ssh2key.key = &state->edkey.sshk; - } else { - state->ssh2key.key = &state->key.sshk; - } - state->commentptr = &state->ssh2key.comment; - } else { - state->commentptr = &state->key.comment; - } - /* - * Invent a comment for the key. We'll do this by including - * the date in it. This will be so horrifyingly ugly that - * the user will immediately want to change it, which is - * what we want :-) - */ - *state->commentptr = snewn(30, char); - { - struct tm tm; - tm = ltime(); - if (state->keytype == DSA) - strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm); - else if (state->keytype == ECDSA) - strftime(*state->commentptr, 30, "ecdsa-key-%Y%m%d", &tm); - else if (state->keytype == EDDSA) - strftime(*state->commentptr, 30, "eddsa-key-%Y%m%d", &tm); - else - strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm); - } - - /* - * Now update the key controls with all the key data. - */ - { - char *fp, *savecomment; - /* - * Blank passphrase, initially. This isn't dangerous, - * because we will warn (Are You Sure?) before allowing - * the user to save an unprotected private key. - */ - SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, ""); - SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, ""); - /* - * Set the comment. - */ - SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr); - /* - * Set the key fingerprint. - */ - savecomment = *state->commentptr; - *state->commentptr = NULL; - if (state->ssh2) - fp = ssh2_fingerprint(state->ssh2key.key, state->fptype); - else - fp = rsa_ssh1_fingerprint(&state->key); - SetDlgItemText(hwnd, IDC_FINGERPRINT, fp); - sfree(fp); - *state->commentptr = savecomment; - /* - * Construct a decimal representation of the key, for - * pasting into .ssh/authorized_keys or - * .ssh/authorized_keys2 on a Unix box. - */ - if (state->ssh2) { - setupbigedit2(hwnd, IDC_KEYDISPLAY, - IDC_PKSTATIC, &state->ssh2key); - } else { - setupbigedit1(hwnd, IDC_KEYDISPLAY, - IDC_PKSTATIC, &state->key); - } - } - /* - * Finally, hide the progress bar and show the key data. - */ - ui_set_state(hwnd, state, 2); - break; - case WM_HELP: { - int id = ((LPHELPINFO)lParam)->iCtrlId; - const char *topic = NULL; - switch (id) { - case IDC_GENERATING: - case IDC_PROGRESS: - case IDC_GENSTATIC: - case IDC_GENERATE: - topic = WINHELP_CTX_puttygen_generate; break; - case IDC_PKSTATIC: - case IDC_KEYDISPLAY: - topic = WINHELP_CTX_puttygen_pastekey; break; - case IDC_FPSTATIC: - case IDC_FINGERPRINT: - topic = WINHELP_CTX_puttygen_fingerprint; break; - case IDC_COMMENTSTATIC: - case IDC_COMMENTEDIT: - topic = WINHELP_CTX_puttygen_comment; break; - case IDC_PASSPHRASE1STATIC: - case IDC_PASSPHRASE1EDIT: - case IDC_PASSPHRASE2STATIC: - case IDC_PASSPHRASE2EDIT: - topic = WINHELP_CTX_puttygen_passphrase; break; - case IDC_LOADSTATIC: - case IDC_LOAD: - topic = WINHELP_CTX_puttygen_load; break; - case IDC_SAVESTATIC: - case IDC_SAVE: - topic = WINHELP_CTX_puttygen_savepriv; break; - case IDC_SAVEPUB: - topic = WINHELP_CTX_puttygen_savepub; break; - case IDC_TYPESTATIC: - case IDC_KEYSSH1: - case IDC_KEYSSH2RSA: - case IDC_KEYSSH2DSA: - case IDC_KEYSSH2ECDSA: - case IDC_KEYSSH2EDDSA: - topic = WINHELP_CTX_puttygen_keytype; break; - case IDC_BITSSTATIC: - case IDC_BITS: - topic = WINHELP_CTX_puttygen_bits; break; - case IDC_IMPORT: - case IDC_EXPORT_OPENSSH_AUTO: - case IDC_EXPORT_OPENSSH_NEW: - case IDC_EXPORT_SSHCOM: - topic = WINHELP_CTX_puttygen_conversions; break; - } - if (topic) { - launch_help(hwnd, topic); - } else { - MessageBeep(0); - } - break; - } - case WM_CLOSE: - state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA); - sfree(state); - quit_help(hwnd); - EndDialog(hwnd, 1); - return 0; - } - return 0; -} - -void cleanup_exit(int code) -{ - shutdown_help(); - exit(code); -} - -HINSTANCE hinst; - -int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) -{ - int argc, i; - char **argv; - int ret; - - dll_hijacking_protection(); - - init_common_controls(); - hinst = inst; - - /* - * See if we can find our Help file. - */ - init_help(); - - split_into_argv(cmdline, &argc, &argv, NULL); - - for (i = 0; i < argc; i++) { - if (!strcmp(argv[i], "-pgpfp")) { - pgp_fingerprints_msgbox(NULL); - return 1; - } else if (!strcmp(argv[i], "-restrict-acl") || - !strcmp(argv[i], "-restrict_acl") || - !strcmp(argv[i], "-restrictacl")) { - restrict_process_acl(); - } else { - /* - * Assume the first argument to be a private key file, and - * attempt to load it. - */ - cmdline_keyfile = argv[i]; - break; - } - } - - save_params = ppk_save_default_parameters; - - random_setup_special(); - ret = DialogBox(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc) != IDOK; - - cleanup_exit(ret); - return ret; /* just in case optimiser complains */ -} diff --git a/WINDOWS/WINPGNT.C b/WINDOWS/WINPGNT.C deleted file mode 100644 index d6e960b6..00000000 --- a/WINDOWS/WINPGNT.C +++ /dev/null @@ -1,1723 +0,0 @@ -/* - * Pageant: the PuTTY Authentication Agent. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <stddef.h> -#include <ctype.h> -#include <assert.h> -#include <tchar.h> - -#include "putty.h" -#include "ssh.h" -#include "misc.h" -#include "tree234.h" -#include "winsecur.h" -#include "wincapi.h" -#include "pageant.h" -#include "licence.h" -#include "pageant-rc.h" - -#include <shellapi.h> - -#ifndef NO_SECURITY -#include <aclapi.h> -#ifdef DEBUG_IPC -#define _WIN32_WINNT 0x0500 /* for ConvertSidToStringSid */ -#include <sddl.h> -#endif -#endif - -#define WM_SYSTRAY (WM_APP + 6) -#define WM_SYSTRAY2 (WM_APP + 7) - -#define AGENT_COPYDATA_ID 0x804e50ba /* random goop */ - -#define APPNAME "Pageant" - -/* Titles and class names for invisible windows. IPCWINTITLE and - * IPCCLASSNAME are critical to backwards compatibility: WM_COPYDATA - * based Pageant clients will call FindWindow with those parameters - * and expect to find the Pageant IPC receiver. */ -#define TRAYWINTITLE "Pageant" -#define TRAYCLASSNAME "PageantSysTray" -#define IPCWINTITLE "Pageant" -#define IPCCLASSNAME "Pageant" - -static HWND traywindow; -static HWND keylist; -static HWND aboutbox; -static HMENU systray_menu, session_menu; -static bool already_running; -static FingerprintType fptype = SSH_FPTYPE_DEFAULT; - -static char *putty_path; -static bool restrict_putty_acl = false; - -/* CWD for "add key" file requester. */ -static filereq *keypath = NULL; - -/* From MSDN: In the WM_SYSCOMMAND message, the four low-order bits of - * wParam are used by Windows, and should be masked off, so we shouldn't - * attempt to store information in them. Hence all these identifiers have - * the low 4 bits clear. Also, identifiers should < 0xF000. */ - -#define IDM_CLOSE 0x0010 -#define IDM_VIEWKEYS 0x0020 -#define IDM_ADDKEY 0x0030 -#define IDM_ADDKEY_ENCRYPTED 0x0040 -#define IDM_REMOVE_ALL 0x0050 -#define IDM_REENCRYPT_ALL 0x0060 -#define IDM_HELP 0x0070 -#define IDM_ABOUT 0x0080 -#define IDM_PUTTY 0x0090 -#define IDM_SESSIONS_BASE 0x1000 -#define IDM_SESSIONS_MAX 0x2000 -#define PUTTY_REGKEY "Software\\SimonTatham\\PuTTY\\Sessions" -#define PUTTY_DEFAULT "Default%20Settings" -static int initial_menuitems_count; - -/* - * Print a modal (Really Bad) message box and perform a fatal exit. - */ -void modalfatalbox(const char *fmt, ...) -{ - va_list ap; - char *buf; - - va_start(ap, fmt); - buf = dupvprintf(fmt, ap); - va_end(ap); - MessageBox(traywindow, buf, "Pageant Fatal Error", - MB_SYSTEMMODAL | MB_ICONERROR | MB_OK); - sfree(buf); - exit(1); -} - -static bool has_security; - -struct PassphraseProcStruct { - bool modal; - const char *help_topic; - PageantClientDialogId *dlgid; - char *passphrase; - const char *comment; -}; - -/* - * Dialog-box function for the Licence box. - */ -static INT_PTR CALLBACK LicenceProc(HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_INITDIALOG: - SetDlgItemText(hwnd, IDC_LICENCE_TEXTBOX, LICENCE_TEXT("\r\n\r\n")); - return 1; - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDOK: - case IDCANCEL: - EndDialog(hwnd, 1); - return 0; - } - return 0; - case WM_CLOSE: - EndDialog(hwnd, 1); - return 0; - } - return 0; -} - -/* - * Dialog-box function for the About box. - */ -static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_INITDIALOG: { - char *buildinfo_text = buildinfo("\r\n"); - char *text = dupprintf - ("Pageant\r\n\r\n%s\r\n\r\n%s\r\n\r\n%s", - ver, buildinfo_text, - "\251 " SHORT_COPYRIGHT_DETAILS ". All rights reserved."); - sfree(buildinfo_text); - SetDlgItemText(hwnd, IDC_ABOUT_TEXTBOX, text); - MakeDlgItemBorderless(hwnd, IDC_ABOUT_TEXTBOX); - sfree(text); - return 1; - } - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDOK: - case IDCANCEL: - aboutbox = NULL; - DestroyWindow(hwnd); - return 0; - case IDC_ABOUT_LICENCE: - EnableWindow(hwnd, 0); - DialogBox(hinst, MAKEINTRESOURCE(IDD_LICENCE), hwnd, LicenceProc); - EnableWindow(hwnd, 1); - SetActiveWindow(hwnd); - return 0; - case IDC_ABOUT_WEBSITE: - /* Load web browser */ - ShellExecute(hwnd, "open", - "https://www.chiark.greenend.org.uk/~sgtatham/putty/", - 0, 0, SW_SHOWDEFAULT); - return 0; - } - return 0; - case WM_CLOSE: - aboutbox = NULL; - DestroyWindow(hwnd); - return 0; - } - return 0; -} - -static HWND modal_passphrase_hwnd = NULL; -static HWND nonmodal_passphrase_hwnd = NULL; - -static void end_passphrase_dialog(HWND hwnd, INT_PTR result) -{ - struct PassphraseProcStruct *p = (struct PassphraseProcStruct *) - GetWindowLongPtr(hwnd, GWLP_USERDATA); - - if (p->modal) { - EndDialog(hwnd, result); - } else { - /* - * Destroy this passphrase dialog box before passing the - * results back to pageant.c, to avoid re-entrancy issues. - * - * If we successfully got a passphrase from the user, but it - * was _wrong_, then pageant_passphrase_request_success will - * respond by calling back - synchronously - to our - * ask_passphrase() implementation, which will expect the - * previous value of nonmodal_passphrase_hwnd to have already - * been cleaned up. - */ - SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) NULL); - DestroyWindow(hwnd); - nonmodal_passphrase_hwnd = NULL; - - if (result) - pageant_passphrase_request_success( - p->dlgid, ptrlen_from_asciz(p->passphrase)); - else - pageant_passphrase_request_refused(p->dlgid); - - burnstr(p->passphrase); - sfree(p); - } -} - -/* - * Dialog-box function for the passphrase box. - */ -static INT_PTR CALLBACK PassphraseProc(HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) -{ - struct PassphraseProcStruct *p; - - if (msg == WM_INITDIALOG) { - p = (struct PassphraseProcStruct *) lParam; - SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) p); - } else { - p = (struct PassphraseProcStruct *) - GetWindowLongPtr(hwnd, GWLP_USERDATA); - } - - switch (msg) { - case WM_INITDIALOG: { - if (p->modal) - modal_passphrase_hwnd = hwnd; - - /* - * Centre the window. - */ - RECT rs, rd; - HWND hw; - - 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); - - SetForegroundWindow(hwnd); - SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, - SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); - if (!p->modal) - SetActiveWindow(hwnd); /* this won't have happened automatically */ - if (p->comment) - SetDlgItemText(hwnd, IDC_PASSPHRASE_FINGERPRINT, p->comment); - burnstr(p->passphrase); - p->passphrase = dupstr(""); - SetDlgItemText(hwnd, IDC_PASSPHRASE_EDITBOX, p->passphrase); - if (!p->help_topic || !has_help()) { - HWND item = GetDlgItem(hwnd, IDHELP); - if (item) - DestroyWindow(item); - } - return 0; - } - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDOK: - if (p->passphrase) - end_passphrase_dialog(hwnd, 1); - else - MessageBeep(0); - return 0; - case IDCANCEL: - end_passphrase_dialog(hwnd, 0); - return 0; - case IDHELP: - if (p->help_topic) - launch_help(hwnd, p->help_topic); - return 0; - case IDC_PASSPHRASE_EDITBOX: - if ((HIWORD(wParam) == EN_CHANGE) && p->passphrase) { - burnstr(p->passphrase); - p->passphrase = GetDlgItemText_alloc( - hwnd, IDC_PASSPHRASE_EDITBOX); - } - return 0; - } - return 0; - case WM_CLOSE: - end_passphrase_dialog(hwnd, 0); - return 0; - } - return 0; -} - -/* - * Warn about the obsolescent key file format. - */ -void old_keyfile_warning(void) -{ - static const char mbtitle[] = "PuTTY 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" - "PuTTY 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."; - - MessageBox(NULL, message, mbtitle, MB_OK); -} - -struct keylist_update_ctx { - bool enable_remove_controls; - bool enable_reencrypt_controls; -}; - -static void keylist_update_callback( - void *vctx, char **fingerprints, const char *comment, uint32_t ext_flags, - struct pageant_pubkey *key) -{ - struct keylist_update_ctx *ctx = (struct keylist_update_ctx *)vctx; - FingerprintType this_type = ssh2_pick_fingerprint(fingerprints, fptype); - const char *fingerprint = fingerprints[this_type]; - strbuf *listentry = strbuf_new(); - - /* There is at least one key, so the controls for removing keys - * should be enabled */ - ctx->enable_remove_controls = true; - - switch (key->ssh_version) { - case 1: { - strbuf_catf(listentry, "ssh1\t%s\t%s", fingerprint, comment); - - /* - * Replace the space in the fingerprint (between bit count and - * hash) with a tab, for nice alignment in the box. - */ - char *p = strchr(listentry->s, ' '); - if (p) - *p = '\t'; - break; - } - - case 2: { - /* - * For nice alignment in the list box, we would ideally want - * every entry to align to the tab stop settings, and have a - * column for algorithm name, one for bit count, one for hex - * fingerprint, and one for key comment. - * - * Unfortunately, some of the algorithm names are so long that - * they overflow into the bit-count field. Fortunately, at the - * moment, those are _precisely_ the algorithm names that - * don't need a bit count displayed anyway (because for - * NIST-style ECDSA the bit count is mentioned in the - * algorithm name, and for ssh-ed25519 there is only one - * possible value anyway). So we fudge this by simply omitting - * the bit count field in that situation. - * - * This is fragile not only in the face of further key types - * that don't follow this pattern, but also in the face of - * font metrics changes - the Windows semantics for list box - * tab stops is that \t aligns to the next one you haven't - * already exceeded, so I have to guess when the key type will - * overflow past the bit-count tab stop and leave out a tab - * character. Urgh. - */ - BinarySource src[1]; - BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(key->blob)); - ptrlen algname = get_string(src); - const ssh_keyalg *alg = find_pubkey_alg_len(algname); - - bool include_bit_count = (alg == &ssh_dss || alg == &ssh_rsa); - - int wordnumber = 0; - for (const char *p = fingerprint; *p; p++) { - char c = *p; - if (c == ' ') { - if (wordnumber < 2) - c = '\t'; - wordnumber++; - } - if (include_bit_count || wordnumber != 1) - put_byte(listentry, c); - } - - strbuf_catf(listentry, "\t%s", comment); - break; - } - } - - if (ext_flags & LIST_EXTENDED_FLAG_HAS_NO_CLEARTEXT_KEY) { - strbuf_catf(listentry, "\t(encrypted)"); - } else if (ext_flags & LIST_EXTENDED_FLAG_HAS_ENCRYPTED_KEY_FILE) { - strbuf_catf(listentry, "\t(re-encryptable)"); - - /* At least one key can be re-encrypted */ - ctx->enable_reencrypt_controls = true; - } - - SendDlgItemMessage(keylist, IDC_KEYLIST_LISTBOX, - LB_ADDSTRING, 0, (LPARAM)listentry->s); - strbuf_free(listentry); -} - -/* - * Update the visible key list. - */ -void keylist_update(void) -{ - if (keylist) { - SendDlgItemMessage(keylist, IDC_KEYLIST_LISTBOX, - LB_RESETCONTENT, 0, 0); - - char *errmsg; - struct keylist_update_ctx ctx[1]; - ctx->enable_remove_controls = false; - ctx->enable_reencrypt_controls = false; - int status = pageant_enum_keys(keylist_update_callback, ctx, &errmsg); - assert(status == PAGEANT_ACTION_OK); - assert(!errmsg); - - SendDlgItemMessage(keylist, IDC_KEYLIST_LISTBOX, - LB_SETCURSEL, (WPARAM) - 1, 0); - - EnableWindow(GetDlgItem(keylist, IDC_KEYLIST_REMOVE), - ctx->enable_remove_controls); - EnableWindow(GetDlgItem(keylist, IDC_KEYLIST_REENCRYPT), - ctx->enable_reencrypt_controls); - } -} - -static void win_add_keyfile(Filename *filename, bool encrypted) -{ - char *err; - int ret; - - /* - * Try loading the key without a passphrase. (Or rather, without a - * _new_ passphrase; pageant_add_keyfile will take care of trying - * all the passphrases we've already stored.) - */ - ret = pageant_add_keyfile(filename, NULL, &err, encrypted); - if (ret == PAGEANT_ACTION_OK) { - goto done; - } else if (ret == PAGEANT_ACTION_FAILURE) { - goto error; - } - - /* - * OK, a passphrase is needed, and we've been given the key - * comment to use in the passphrase prompt. - */ - while (1) { - INT_PTR dlgret; - struct PassphraseProcStruct pps; - pps.modal = true; - pps.help_topic = NULL; /* this dialog has no help button */ - pps.dlgid = NULL; - pps.passphrase = NULL; - pps.comment = err; - dlgret = DialogBoxParam( - hinst, MAKEINTRESOURCE(IDD_LOAD_PASSPHRASE), - NULL, PassphraseProc, (LPARAM) &pps); - modal_passphrase_hwnd = NULL; - - if (!dlgret) { - burnstr(pps.passphrase); - goto done; /* operation cancelled */ - } - - sfree(err); - - assert(pps.passphrase != NULL); - - ret = pageant_add_keyfile(filename, pps.passphrase, &err, false); - burnstr(pps.passphrase); - - if (ret == PAGEANT_ACTION_OK) { - goto done; - } else if (ret == PAGEANT_ACTION_FAILURE) { - goto error; - } - } - - error: - message_box(traywindow, err, APPNAME, MB_OK | MB_ICONERROR, - HELPCTXID(errors_cantloadkey)); - done: - sfree(err); - return; -} - -/* - * Prompt for a key file to add, and add it. - */ -static void prompt_add_keyfile(bool encrypted) -{ - OPENFILENAME of; - char *filelist = snewn(8192, char); - - if (!keypath) keypath = filereq_new(); - memset(&of, 0, sizeof(of)); - of.hwndOwner = traywindow; - of.lpstrFilter = FILTER_KEY_FILES; - of.lpstrCustomFilter = NULL; - of.nFilterIndex = 1; - of.lpstrFile = filelist; - *filelist = '\0'; - of.nMaxFile = 8192; - of.lpstrFileTitle = NULL; - of.lpstrTitle = "Select Private Key File"; - of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER; - if (request_file(keypath, &of, true, false)) { - if(strlen(filelist) > of.nFileOffset) { - /* Only one filename returned? */ - Filename *fn = filename_from_str(filelist); - win_add_keyfile(fn, encrypted); - filename_free(fn); - } else { - /* we are returned a bunch of strings, end to - * end. first string is the directory, the - * rest the filenames. terminated with an - * empty string. - */ - char *dir = filelist; - char *filewalker = filelist + strlen(dir) + 1; - while (*filewalker != '\0') { - char *filename = dupcat(dir, "\\", filewalker); - Filename *fn = filename_from_str(filename); - win_add_keyfile(fn, encrypted); - filename_free(fn); - sfree(filename); - filewalker += strlen(filewalker) + 1; - } - } - - keylist_update(); - pageant_forget_passphrases(); - } - sfree(filelist); -} - -/* - * Dialog-box function for the key list box. - */ -static INT_PTR CALLBACK KeyListProc(HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) -{ - static const struct { - const char *name; - FingerprintType value; - } fptypes[] = { - {"SHA256", SSH_FPTYPE_SHA256}, - {"MD5", SSH_FPTYPE_MD5}, - }; - - switch (msg) { - case WM_INITDIALOG: { - /* - * Centre the window. - */ - RECT rs, rd; - HWND hw; - - 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); - - if (has_help()) - SetWindowLongPtr(hwnd, GWL_EXSTYLE, - GetWindowLongPtr(hwnd, GWL_EXSTYLE) | - WS_EX_CONTEXTHELP); - else { - HWND item = GetDlgItem(hwnd, IDC_KEYLIST_HELP); - if (item) - DestroyWindow(item); - } - - keylist = hwnd; - { - static int tabs[] = { 35, 75, 300 }; - SendDlgItemMessage(hwnd, IDC_KEYLIST_LISTBOX, LB_SETTABSTOPS, - sizeof(tabs) / sizeof(*tabs), - (LPARAM) tabs); - } - - int selection = 0; - for (size_t i = 0; i < lenof(fptypes); i++) { - SendDlgItemMessage(hwnd, IDC_KEYLIST_FPTYPE, CB_ADDSTRING, - 0, (LPARAM)fptypes[i].name); - if (fptype == fptypes[i].value) - selection = (int)i; - } - SendDlgItemMessage(hwnd, IDC_KEYLIST_FPTYPE, - CB_SETCURSEL, 0, selection); - - keylist_update(); - return 0; - } - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDOK: - case IDCANCEL: - keylist = NULL; - DestroyWindow(hwnd); - return 0; - case IDC_KEYLIST_ADDKEY: - case IDC_KEYLIST_ADDKEY_ENC: - if (HIWORD(wParam) == BN_CLICKED || - HIWORD(wParam) == BN_DOUBLECLICKED) { - if (modal_passphrase_hwnd) { - MessageBeep(MB_ICONERROR); - SetForegroundWindow(modal_passphrase_hwnd); - break; - } - prompt_add_keyfile(LOWORD(wParam) == IDC_KEYLIST_ADDKEY_ENC); - } - return 0; - case IDC_KEYLIST_REMOVE: - case IDC_KEYLIST_REENCRYPT: - if (HIWORD(wParam) == BN_CLICKED || - HIWORD(wParam) == BN_DOUBLECLICKED) { - int i; - int rCount, sCount; - int *selectedArray; - - /* our counter within the array of selected items */ - int itemNum; - - /* get the number of items selected in the list */ - int numSelected = SendDlgItemMessage( - hwnd, IDC_KEYLIST_LISTBOX, LB_GETSELCOUNT, 0, 0); - - /* none selected? that was silly */ - if (numSelected == 0) { - MessageBeep(0); - break; - } - - /* get item indices in an array */ - selectedArray = snewn(numSelected, int); - SendDlgItemMessage(hwnd, IDC_KEYLIST_LISTBOX, LB_GETSELITEMS, - numSelected, (WPARAM)selectedArray); - - itemNum = numSelected - 1; - rCount = pageant_count_ssh1_keys(); - sCount = pageant_count_ssh2_keys(); - - /* go through the non-rsakeys until we've covered them all, - * and/or we're out of selected items to check. note that - * we go *backwards*, to avoid complications from deleting - * things hence altering the offset of subsequent items - */ - for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) { - if (selectedArray[itemNum] == rCount + i) { - switch (LOWORD(wParam)) { - case IDC_KEYLIST_REMOVE: - pageant_delete_nth_ssh2_key(i); - break; - case IDC_KEYLIST_REENCRYPT: - pageant_reencrypt_nth_ssh2_key(i); - break; - } - itemNum--; - } - } - - /* do the same for the rsa keys */ - for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) { - if(selectedArray[itemNum] == i) { - switch (LOWORD(wParam)) { - case IDC_KEYLIST_REMOVE: - pageant_delete_nth_ssh1_key(i); - break; - case IDC_KEYLIST_REENCRYPT: - /* SSH-1 keys can't be re-encrypted */ - break; - } - itemNum--; - } - } - - sfree(selectedArray); - keylist_update(); - } - return 0; - case IDC_KEYLIST_HELP: - if (HIWORD(wParam) == BN_CLICKED || - HIWORD(wParam) == BN_DOUBLECLICKED) { - launch_help(hwnd, WINHELP_CTX_pageant_general); - } - return 0; - case IDC_KEYLIST_FPTYPE: - if (HIWORD(wParam) == CBN_SELCHANGE) { - int selection = SendDlgItemMessage( - hwnd, IDC_KEYLIST_FPTYPE, CB_GETCURSEL, 0, 0); - if (selection >= 0 && (size_t)selection < lenof(fptypes)) { - fptype = fptypes[selection].value; - keylist_update(); - } - } - return 0; - } - return 0; - case WM_HELP: { - int id = ((LPHELPINFO)lParam)->iCtrlId; - const char *topic = NULL; - switch (id) { - case IDC_KEYLIST_LISTBOX: - case IDC_KEYLIST_FPTYPE: - case IDC_KEYLIST_FPTYPE_STATIC: - topic = WINHELP_CTX_pageant_keylist; break; - case IDC_KEYLIST_ADDKEY: topic = WINHELP_CTX_pageant_addkey; break; - case IDC_KEYLIST_REMOVE: topic = WINHELP_CTX_pageant_remkey; break; - case IDC_KEYLIST_ADDKEY_ENC: - case IDC_KEYLIST_REENCRYPT: - topic = WINHELP_CTX_pageant_deferred; break; - } - if (topic) { - launch_help(hwnd, topic); - } else { - MessageBeep(0); - } - break; - } - case WM_CLOSE: - keylist = NULL; - DestroyWindow(hwnd); - return 0; - } - return 0; -} - -/* Set up a system tray icon */ -static BOOL AddTrayIcon(HWND hwnd) -{ - BOOL res; - NOTIFYICONDATA tnid; - HICON hicon; - -#ifdef NIM_SETVERSION - tnid.uVersion = 0; - res = Shell_NotifyIcon(NIM_SETVERSION, &tnid); -#endif - - tnid.cbSize = sizeof(NOTIFYICONDATA); - tnid.hWnd = hwnd; - tnid.uID = 1; /* unique within this systray use */ - tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; - tnid.uCallbackMessage = WM_SYSTRAY; - tnid.hIcon = hicon = LoadIcon(hinst, MAKEINTRESOURCE(201)); - strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)"); - - res = Shell_NotifyIcon(NIM_ADD, &tnid); - - if (hicon) DestroyIcon(hicon); - - return res; -} - -/* Update the saved-sessions menu. */ -static void update_sessions(void) -{ - int num_entries; - HKEY hkey; - TCHAR buf[MAX_PATH + 1]; - MENUITEMINFO mii; - strbuf *sb; - - int index_key, index_menu; - - if (!putty_path) - return; - - if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, PUTTY_REGKEY, &hkey)) - return; - - for(num_entries = GetMenuItemCount(session_menu); - num_entries > initial_menuitems_count; - num_entries--) - RemoveMenu(session_menu, 0, MF_BYPOSITION); - - index_key = 0; - index_menu = 0; - - sb = strbuf_new(); - while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) { - if(strcmp(buf, PUTTY_DEFAULT) != 0) { - strbuf_clear(sb); - unescape_registry_key(buf, sb); - - memset(&mii, 0, sizeof(mii)); - mii.cbSize = sizeof(mii); - mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID; - mii.fType = MFT_STRING; - mii.fState = MFS_ENABLED; - mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE; - mii.dwTypeData = sb->s; - InsertMenuItem(session_menu, index_menu, true, &mii); - index_menu++; - } - index_key++; - } - strbuf_free(sb); - - RegCloseKey(hkey); - - if(index_menu == 0) { - mii.cbSize = sizeof(mii); - mii.fMask = MIIM_TYPE | MIIM_STATE; - mii.fType = MFT_STRING; - mii.fState = MFS_GRAYED; - mii.dwTypeData = _T("(No sessions)"); - InsertMenuItem(session_menu, index_menu, true, &mii); - } -} - -#ifndef NO_SECURITY -/* - * Versions of Pageant prior to 0.61 expected this SID on incoming - * communications. For backwards compatibility, and more particularly - * for compatibility with derived works of PuTTY still using the old - * Pageant client code, we accept it as an alternative to the one - * returned from get_user_sid() in winpgntc.c. - */ -PSID get_default_sid(void) -{ - HANDLE proc = NULL; - DWORD sidlen; - PSECURITY_DESCRIPTOR psd = NULL; - PSID sid = NULL, copy = NULL, ret = NULL; - - if ((proc = OpenProcess(MAXIMUM_ALLOWED, false, - GetCurrentProcessId())) == NULL) - goto cleanup; - - if (p_GetSecurityInfo(proc, SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION, - &sid, NULL, NULL, NULL, &psd) != ERROR_SUCCESS) - goto cleanup; - - sidlen = GetLengthSid(sid); - - copy = (PSID)smalloc(sidlen); - - if (!CopySid(sidlen, copy, sid)) - goto cleanup; - - /* Success. Move sid into the return value slot, and null it out - * to stop the cleanup code freeing it. */ - ret = copy; - copy = NULL; - - cleanup: - if (proc != NULL) - CloseHandle(proc); - if (psd != NULL) - LocalFree(psd); - if (copy != NULL) - sfree(copy); - - return ret; -} -#endif - -struct WmCopydataTransaction { - char *length, *body; - size_t bodysize, bodylen; - HANDLE ev_msg_ready, ev_reply_ready; -} wmct; - -static struct PageantClient wmcpc; - -static void wm_copydata_got_msg(void *vctx) -{ - pageant_handle_msg(&wmcpc, NULL, make_ptrlen(wmct.body, wmct.bodylen)); -} - -static void wm_copydata_got_response( - PageantClient *pc, PageantClientRequestId *reqid, ptrlen response) -{ - if (response.len > wmct.bodysize) { - /* Output would overflow message buffer. Replace with a - * failure message. */ - static const unsigned char failure[] = { SSH_AGENT_FAILURE }; - response = make_ptrlen(failure, lenof(failure)); - assert(response.len <= wmct.bodysize); - } - - PUT_32BIT_MSB_FIRST(wmct.length, response.len); - memcpy(wmct.body, response.ptr, response.len); - - SetEvent(wmct.ev_reply_ready); -} - -static bool ask_passphrase_common(PageantClientDialogId *dlgid, - const char *comment) -{ - /* Pageant core should be serialising requests, so we never expect - * a passphrase prompt to exist already at this point */ - assert(!nonmodal_passphrase_hwnd); - - struct PassphraseProcStruct *pps = snew(struct PassphraseProcStruct); - pps->modal = false; - pps->help_topic = WINHELP_CTX_pageant_deferred; - pps->dlgid = dlgid; - pps->passphrase = NULL; - pps->comment = comment; - - nonmodal_passphrase_hwnd = CreateDialogParam( - hinst, MAKEINTRESOURCE(IDD_ONDEMAND_PASSPHRASE), - NULL, PassphraseProc, (LPARAM)pps); - - /* - * Try to put this passphrase prompt into the foreground. - * - * This will probably not succeed in giving it the actual keyboard - * focus, because Windows is quite opposed to applications being - * able to suddenly steal the focus on their own initiative. - * - * That makes sense in a lot of situations, as a defensive - * measure. If you were about to type a password or other secret - * data into the window you already had focused, and some - * malicious app stole the focus, it might manage to trick you - * into typing your secrets into _it_ instead. - * - * In this case it's possible to regard the same defensive measure - * as counterproductive, because the effect if we _do_ steal focus - * is that you type something into our passphrase prompt that - * isn't the passphrase, and we fail to decrypt the key, and no - * harm is done. Whereas the effect of the user wrongly _assuming_ - * the new passphrase prompt has the focus is much worse: now you - * type your highly secret passphrase into some other window you - * didn't mean to trust with that information - such as the - * agent-forwarded PuTTY in which you just ran an ssh command, - * which the _whole point_ was to avoid telling your passphrase to! - * - * On the other hand, I'm sure _every_ application author can come - * up with an argument for why they think _they_ should be allowed - * to steal the focus. Probably most of them include the claim - * that no harm is done if their application receives data - * intended for something else, and of course that's not always - * true! - * - * In any case, I don't know of anything I can do about it, or - * anything I _should_ do about it if I could. If anyone thinks - * they can improve on all this, patches are welcome. - */ - SetForegroundWindow(nonmodal_passphrase_hwnd); - - return true; -} - -static bool wm_copydata_ask_passphrase( - PageantClient *pc, PageantClientDialogId *dlgid, const char *comment) -{ - return ask_passphrase_common(dlgid, comment); -} - -static const PageantClientVtable wmcpc_vtable = { - .log = NULL, /* no logging in this client */ - .got_response = wm_copydata_got_response, - .ask_passphrase = wm_copydata_ask_passphrase, -}; - -static char *answer_filemapping_message(const char *mapname) -{ - HANDLE maphandle = INVALID_HANDLE_VALUE; - void *mapaddr = NULL; - char *err = NULL; - size_t mapsize; - unsigned msglen; - -#ifndef NO_SECURITY - PSID mapsid = NULL; - PSID expectedsid = NULL; - PSID expectedsid_bc = NULL; - PSECURITY_DESCRIPTOR psd = NULL; -#endif - - wmct.length = wmct.body = NULL; - -#ifdef DEBUG_IPC - debug("mapname = \"%s\"\n", mapname); -#endif - - maphandle = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, mapname); - if (maphandle == NULL || maphandle == INVALID_HANDLE_VALUE) { - err = dupprintf("OpenFileMapping(\"%s\"): %s", - mapname, win_strerror(GetLastError())); - goto cleanup; - } - -#ifdef DEBUG_IPC - debug("maphandle = %p\n", maphandle); -#endif - -#ifndef NO_SECURITY - if (has_security) { - DWORD retd; - - if ((expectedsid = get_user_sid()) == NULL) { - err = dupstr("unable to get user SID"); - goto cleanup; - } - - if ((expectedsid_bc = get_default_sid()) == NULL) { - err = dupstr("unable to get default SID"); - goto cleanup; - } - - if ((retd = p_GetSecurityInfo( - maphandle, SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION, - &mapsid, NULL, NULL, NULL, &psd) != ERROR_SUCCESS)) { - err = dupprintf("unable to get owner of file mapping: " - "GetSecurityInfo returned: %s", - win_strerror(retd)); - goto cleanup; - } - -#ifdef DEBUG_IPC - { - LPTSTR ours, ours2, theirs; - ConvertSidToStringSid(mapsid, &theirs); - ConvertSidToStringSid(expectedsid, &ours); - ConvertSidToStringSid(expectedsid_bc, &ours2); - debug("got sids:\n oursnew=%s\n oursold=%s\n" - " theirs=%s\n", ours, ours2, theirs); - LocalFree(ours); - LocalFree(ours2); - LocalFree(theirs); - } -#endif - - if (!EqualSid(mapsid, expectedsid) && - !EqualSid(mapsid, expectedsid_bc)) { - err = dupstr("wrong owning SID of file mapping"); - goto cleanup; - } - } else -#endif /* NO_SECURITY */ - { -#ifdef DEBUG_IPC - debug("security APIs not present\n"); -#endif - } - - mapaddr = MapViewOfFile(maphandle, FILE_MAP_WRITE, 0, 0, 0); - if (!mapaddr) { - err = dupprintf("unable to obtain view of file mapping: %s", - win_strerror(GetLastError())); - goto cleanup; - } - -#ifdef DEBUG_IPC - debug("mapped address = %p\n", mapaddr); -#endif - - { - MEMORY_BASIC_INFORMATION mbi; - size_t mbiSize = VirtualQuery(mapaddr, &mbi, sizeof(mbi)); - if (mbiSize == 0) { - err = dupprintf("unable to query view of file mapping: %s", - win_strerror(GetLastError())); - goto cleanup; - } - if (mbiSize < (offsetof(MEMORY_BASIC_INFORMATION, RegionSize) + - sizeof(mbi.RegionSize))) { - err = dupstr("VirtualQuery returned too little data to get " - "region size"); - goto cleanup; - } - - mapsize = mbi.RegionSize; - } -#ifdef DEBUG_IPC - debug("region size = %"SIZEu"\n", mapsize); -#endif - if (mapsize < 5) { - err = dupstr("mapping smaller than smallest possible request"); - goto cleanup; - } - - wmct.length = (char *)mapaddr; - msglen = GET_32BIT_MSB_FIRST(wmct.length); - -#ifdef DEBUG_IPC - debug("msg length=%08x, msg type=%02x\n", - msglen, (unsigned)((unsigned char *) mapaddr)[4]); -#endif - - wmct.body = wmct.length + 4; - wmct.bodysize = mapsize - 4; - - if (msglen > wmct.bodysize) { - /* Incoming length field is too large. Emit a failure response - * without even trying to handle the request. - * - * (We know this must fit, because we checked mapsize >= 5 - * above.) */ - PUT_32BIT_MSB_FIRST(wmct.length, 1); - *wmct.body = SSH_AGENT_FAILURE; - } else { - wmct.bodylen = msglen; - SetEvent(wmct.ev_msg_ready); - WaitForSingleObject(wmct.ev_reply_ready, INFINITE); - } - - cleanup: - /* expectedsid has the lifetime of the program, so we don't free it */ - sfree(expectedsid_bc); - if (psd) - LocalFree(psd); - if (mapaddr) - UnmapViewOfFile(mapaddr); - if (maphandle != NULL && maphandle != INVALID_HANDLE_VALUE) - CloseHandle(maphandle); - return err; -} - -static void create_keylist_window(void) -{ - if (keylist) - return; - - keylist = CreateDialog(hinst, MAKEINTRESOURCE(IDD_KEYLIST), - NULL, KeyListProc); - ShowWindow(keylist, SW_SHOWNORMAL); -} - -static LRESULT CALLBACK TrayWndProc(HWND hwnd, UINT message, - WPARAM wParam, LPARAM lParam) -{ - static bool menuinprogress; - static UINT msgTaskbarCreated = 0; - - switch (message) { - case WM_CREATE: - msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated")); - break; - default: - if (message==msgTaskbarCreated) { - /* - * Explorer has been restarted, so the tray icon will - * have been lost. - */ - AddTrayIcon(hwnd); - } - break; - - case WM_SYSTRAY: - if (lParam == WM_RBUTTONUP) { - POINT cursorpos; - GetCursorPos(&cursorpos); - PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y); - } else if (lParam == WM_LBUTTONDBLCLK) { - /* Run the default menu item. */ - UINT menuitem = GetMenuDefaultItem(systray_menu, false, 0); - if (menuitem != -1) - PostMessage(hwnd, WM_COMMAND, menuitem, 0); - } - break; - case WM_SYSTRAY2: - if (!menuinprogress) { - menuinprogress = true; - update_sessions(); - SetForegroundWindow(hwnd); - TrackPopupMenu(systray_menu, - TPM_RIGHTALIGN | TPM_BOTTOMALIGN | - TPM_RIGHTBUTTON, - wParam, lParam, 0, hwnd, NULL); - menuinprogress = false; - } - break; - case WM_COMMAND: - case WM_SYSCOMMAND: { - unsigned command = wParam & ~0xF; /* low 4 bits reserved to Windows */ - switch (command) { - case IDM_PUTTY: { - TCHAR cmdline[10]; - cmdline[0] = '\0'; - if (restrict_putty_acl) - strcat(cmdline, "&R"); - - if((INT_PTR)ShellExecute(hwnd, NULL, putty_path, cmdline, - _T(""), SW_SHOW) <= 32) { - MessageBox(NULL, "Unable to execute PuTTY!", - "Error", MB_OK | MB_ICONERROR); - } - break; - } - case IDM_CLOSE: - if (modal_passphrase_hwnd) - SendMessage(modal_passphrase_hwnd, WM_CLOSE, 0, 0); - SendMessage(hwnd, WM_CLOSE, 0, 0); - break; - case IDM_VIEWKEYS: - create_keylist_window(); - /* - * Sometimes the window comes up minimised / hidden for - * no obvious reason. Prevent this. This also brings it - * to the front if it's already present (the user - * selected View Keys because they wanted to _see_ the - * thing). - */ - SetForegroundWindow(keylist); - SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0, - SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); - break; - case IDM_ADDKEY: - case IDM_ADDKEY_ENCRYPTED: - if (modal_passphrase_hwnd) { - MessageBeep(MB_ICONERROR); - SetForegroundWindow(modal_passphrase_hwnd); - break; - } - prompt_add_keyfile(command == IDM_ADDKEY_ENCRYPTED); - break; - case IDM_REMOVE_ALL: - pageant_delete_all(); - keylist_update(); - break; - case IDM_REENCRYPT_ALL: - pageant_reencrypt_all(); - keylist_update(); - break; - case IDM_ABOUT: - if (!aboutbox) { - aboutbox = CreateDialog(hinst, MAKEINTRESOURCE(IDD_ABOUT), - NULL, AboutProc); - ShowWindow(aboutbox, SW_SHOWNORMAL); - /* - * Sometimes the window comes up minimised / hidden - * for no obvious reason. Prevent this. - */ - SetForegroundWindow(aboutbox); - SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0, - SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); - } - break; - case IDM_HELP: - launch_help(hwnd, WINHELP_CTX_pageant_general); - break; - default: { - if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) { - MENUITEMINFO mii; - TCHAR buf[MAX_PATH + 1]; - TCHAR param[MAX_PATH + 1]; - memset(&mii, 0, sizeof(mii)); - mii.cbSize = sizeof(mii); - mii.fMask = MIIM_TYPE; - mii.cch = MAX_PATH; - mii.dwTypeData = buf; - GetMenuItemInfo(session_menu, wParam, false, &mii); - param[0] = '\0'; - if (restrict_putty_acl) - strcat(param, "&R"); - strcat(param, "@"); - strcat(param, mii.dwTypeData); - if((INT_PTR)ShellExecute(hwnd, NULL, putty_path, param, - _T(""), SW_SHOW) <= 32) { - MessageBox(NULL, "Unable to execute PuTTY!", "Error", - MB_OK | MB_ICONERROR); - } - } - break; - } - } - break; - } - case WM_DESTROY: - quit_help(hwnd); - PostQuitMessage(0); - return 0; - } - - return DefWindowProc(hwnd, message, wParam, lParam); -} - -static LRESULT CALLBACK wm_copydata_WndProc(HWND hwnd, UINT message, - WPARAM wParam, LPARAM lParam) -{ - switch (message) { - case WM_COPYDATA: { - COPYDATASTRUCT *cds; - char *mapname, *err; - - cds = (COPYDATASTRUCT *) lParam; - if (cds->dwData != AGENT_COPYDATA_ID) - return 0; /* not our message, mate */ - mapname = (char *) cds->lpData; - if (mapname[cds->cbData - 1] != '\0') - return 0; /* failure to be ASCIZ! */ - err = answer_filemapping_message(mapname); - if (err) { -#ifdef DEBUG_IPC - debug("IPC failed: %s\n", err); -#endif - sfree(err); - return 0; - } - return 1; - } - } - - return DefWindowProc(hwnd, message, wParam, lParam); -} - -static DWORD WINAPI wm_copydata_threadfunc(void *param) -{ - HINSTANCE inst = *(HINSTANCE *)param; - - HWND ipchwnd = CreateWindow(IPCCLASSNAME, IPCWINTITLE, - WS_OVERLAPPEDWINDOW | WS_VSCROLL, - CW_USEDEFAULT, CW_USEDEFAULT, - 100, 100, NULL, NULL, inst, NULL); - ShowWindow(ipchwnd, SW_HIDE); - - MSG msg; - while (GetMessage(&msg, NULL, 0, 0) == 1) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - - return 0; -} - -/* - * Fork and Exec the command in cmdline. [DBW] - */ -void spawn_cmd(const char *cmdline, const char *args, int show) -{ - if (ShellExecute(NULL, _T("open"), cmdline, - args, NULL, show) <= (HINSTANCE) 32) { - char *msg; - msg = dupprintf("Failed to run \"%s\": %s", cmdline, - win_strerror(GetLastError())); - MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION); - sfree(msg); - } -} - -void logevent(LogContext *logctx, const char *event) -{ - unreachable("Pageant can't create a LogContext, so this can't be called"); -} - -void noise_ultralight(NoiseSourceId id, unsigned long data) -{ - /* Pageant doesn't use random numbers, so we ignore this */ -} - -void cleanup_exit(int code) -{ - shutdown_help(); - exit(code); -} - -static bool winpgnt_listener_ask_passphrase( - PageantListenerClient *plc, PageantClientDialogId *dlgid, - const char *comment) -{ - return ask_passphrase_common(dlgid, comment); -} - -struct winpgnt_client { - PageantListenerClient plc; -}; -static const PageantListenerClientVtable winpgnt_vtable = { - .log = NULL, /* no logging */ - .ask_passphrase = winpgnt_listener_ask_passphrase, -}; - -static struct winpgnt_client wpc[1]; - -HINSTANCE hinst; - -int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) -{ - MSG msg; - const char *command = NULL; - bool added_keys = false; - bool show_keylist_on_startup = false; - int argc, i; - char **argv, **argstart; - - dll_hijacking_protection(); - - hinst = inst; - - /* - * Determine whether we're an NT system (should have security - * APIs) or a non-NT system (don't do security). - */ - init_winver(); - has_security = (osPlatformId == VER_PLATFORM_WIN32_NT); - - if (has_security) { -#ifndef NO_SECURITY - /* - * Attempt to get the security API we need. - */ - if (!got_advapi()) { - MessageBox(NULL, - "Unable to access security APIs. Pageant will\n" - "not run, in case it causes a security breach.", - "Pageant Fatal Error", MB_ICONERROR | MB_OK); - return 1; - } -#else - MessageBox(NULL, - "This program has been compiled for Win9X and will\n" - "not run on NT, in case it causes a security breach.", - "Pageant Fatal Error", MB_ICONERROR | MB_OK); - return 1; -#endif - } - - /* - * See if we can find our Help file. - */ - init_help(); - - /* - * Look for the PuTTY binary (we will enable the saved session - * submenu if we find it). - */ - { - char b[2048], *p, *q, *r; - FILE *fp; - GetModuleFileName(NULL, b, sizeof(b) - 16); - r = b; - p = strrchr(b, '\\'); - if (p && p >= r) r = p+1; - q = strrchr(b, ':'); - if (q && q >= r) r = q+1; - strcpy(r, "putty.exe"); - if ( (fp = fopen(b, "r")) != NULL) { - putty_path = dupstr(b); - fclose(fp); - } else - putty_path = NULL; - } - - /* - * Find out if Pageant is already running. - */ - already_running = agent_exists(); - - /* - * Initialise the cross-platform Pageant code. - */ - if (!already_running) { - pageant_init(); - } - - /* - * Process the command line and add keys as listed on it. - */ - split_into_argv(cmdline, &argc, &argv, &argstart); - bool doing_opts = true; - bool add_keys_encrypted = false; - for (i = 0; i < argc; i++) { - char *p = argv[i]; - if (*p == '-' && doing_opts) { - if (!strcmp(p, "-pgpfp")) { - pgp_fingerprints_msgbox(NULL); - return 1; - } else if (!strcmp(p, "-restrict-acl") || - !strcmp(p, "-restrict_acl") || - !strcmp(p, "-restrictacl")) { - restrict_process_acl(); - } else if (!strcmp(p, "-restrict-putty-acl") || - !strcmp(p, "-restrict_putty_acl")) { - restrict_putty_acl = true; - } else if (!strcmp(p, "--no-decrypt") || - !strcmp(p, "-no-decrypt") || - !strcmp(p, "--no_decrypt") || - !strcmp(p, "-no_decrypt") || - !strcmp(p, "--nodecrypt") || - !strcmp(p, "-nodecrypt") || - !strcmp(p, "--encrypted") || - !strcmp(p, "-encrypted")) { - add_keys_encrypted = true; - } else if (!strcmp(p, "-keylist") || !strcmp(p, "--keylist")) { - show_keylist_on_startup = true; - } else if (!strcmp(p, "-c")) { - /* - * If we see `-c', then the rest of the - * command line should be treated as a - * command to be spawned. - */ - if (i < argc-1) - command = argstart[i+1]; - else - command = ""; - break; - } else if (!strcmp(p, "--")) { - doing_opts = false; - } else { - char *msg = dupprintf("unrecognised command-line option\n" - "'%s'", p); - MessageBox(NULL, msg, "Pageant command-line syntax error", - MB_ICONERROR | MB_OK); - exit(1); - } - } else { - Filename *fn = filename_from_str(p); - win_add_keyfile(fn, add_keys_encrypted); - filename_free(fn); - added_keys = true; - } - } - - /* - * Forget any passphrase that we retained while going over - * command line keyfiles. - */ - pageant_forget_passphrases(); - - if (command) { - char *args; - if (command[0] == '"') - args = strchr(++command, '"'); - else - args = strchr(command, ' '); - if (args) { - *args++ = 0; - while(*args && isspace(*args)) args++; - } - spawn_cmd(command, args, show); - } - - /* - * If Pageant was already running, we leave now. If we haven't - * even taken any auxiliary action (spawned a command or added - * keys), complain. - */ - if (already_running) { - if (!command && !added_keys) { - MessageBox(NULL, "Pageant is already running", "Pageant Error", - MB_ICONERROR | MB_OK); - } - return 0; - } - -#if !defined NO_SECURITY - - /* - * Set up a named-pipe listener. - */ - { - Plug *pl_plug; - wpc->plc.vt = &winpgnt_vtable; - wpc->plc.suppress_logging = true; - struct pageant_listen_state *pl = - pageant_listener_new(&pl_plug, &wpc->plc); - char *pipename = agent_named_pipe_name(); - Socket *sock = new_named_pipe_listener(pipename, pl_plug); - if (sk_socket_error(sock)) { - char *err = dupprintf("Unable to open named pipe at %s " - "for SSH agent:\n%s", pipename, - sk_socket_error(sock)); - MessageBox(NULL, err, "Pageant Error", MB_ICONERROR | MB_OK); - return 1; - } - pageant_listener_got_socket(pl, sock); - sfree(pipename); - } - -#endif /* !defined NO_SECURITY */ - - /* - * Set up window classes for two hidden windows: one that receives - * all the messages to do with our presence in the system tray, - * and one that receives the WM_COPYDATA message used by the - * old-style Pageant IPC system. - */ - - if (!prev) { - WNDCLASS wndclass; - - memset(&wndclass, 0, sizeof(wndclass)); - wndclass.lpfnWndProc = TrayWndProc; - wndclass.hInstance = inst; - wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON)); - wndclass.lpszClassName = TRAYCLASSNAME; - - RegisterClass(&wndclass); - - memset(&wndclass, 0, sizeof(wndclass)); - wndclass.lpfnWndProc = wm_copydata_WndProc; - wndclass.hInstance = inst; - wndclass.lpszClassName = IPCCLASSNAME; - - RegisterClass(&wndclass); - } - - keylist = NULL; - - traywindow = CreateWindow(TRAYCLASSNAME, TRAYWINTITLE, - WS_OVERLAPPEDWINDOW | WS_VSCROLL, - CW_USEDEFAULT, CW_USEDEFAULT, - 100, 100, NULL, NULL, inst, NULL); - winselgui_set_hwnd(traywindow); - - /* Set up a system tray icon */ - AddTrayIcon(traywindow); - - /* Accelerators used: nsvkxa */ - systray_menu = CreatePopupMenu(); - if (putty_path) { - session_menu = CreateMenu(); - AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session"); - AppendMenu(systray_menu, MF_POPUP | MF_ENABLED, - (UINT_PTR) session_menu, "&Saved Sessions"); - AppendMenu(systray_menu, MF_SEPARATOR, 0, 0); - } - AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS, - "&View Keys"); - AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key"); - AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY_ENCRYPTED, - "Add key (encrypted)"); - AppendMenu(systray_menu, MF_SEPARATOR, 0, 0); - AppendMenu(systray_menu, MF_ENABLED, IDM_REMOVE_ALL, - "Remove All Keys"); - AppendMenu(systray_menu, MF_ENABLED, IDM_REENCRYPT_ALL, - "Re-encrypt All Keys"); - AppendMenu(systray_menu, MF_SEPARATOR, 0, 0); - if (has_help()) - AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help"); - AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About"); - AppendMenu(systray_menu, MF_SEPARATOR, 0, 0); - AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit"); - initial_menuitems_count = GetMenuItemCount(session_menu); - - /* Set the default menu item. */ - SetMenuDefaultItem(systray_menu, IDM_VIEWKEYS, false); - - ShowWindow(traywindow, SW_HIDE); - - wmcpc.vt = &wmcpc_vtable; - wmcpc.suppress_logging = true; - pageant_register_client(&wmcpc); - DWORD wm_copydata_threadid; - wmct.ev_msg_ready = CreateEvent(NULL, false, false, NULL); - wmct.ev_reply_ready = CreateEvent(NULL, false, false, NULL); - HANDLE hThread = CreateThread(NULL, 0, wm_copydata_threadfunc, - &inst, 0, &wm_copydata_threadid); - if (hThread) - CloseHandle(hThread); /* we don't need the thread handle */ - handle_add_foreign_event(wmct.ev_msg_ready, wm_copydata_got_msg, NULL); - - if (show_keylist_on_startup) - create_keylist_window(); - - /* - * Main message loop. - */ - while (true) { - HANDLE *handles; - int nhandles, n; - - handles = handle_get_events(&nhandles); - - n = MsgWaitForMultipleObjects(nhandles, handles, false, - INFINITE, QS_ALLINPUT); - - if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) { - handle_got_event(handles[n - WAIT_OBJECT_0]); - sfree(handles); - } else - sfree(handles); - - while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { - if (msg.message == WM_QUIT) - goto finished; /* two-level break */ - - if (IsWindow(keylist) && IsDialogMessage(keylist, &msg)) - continue; - if (IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg)) - continue; - if (IsWindow(nonmodal_passphrase_hwnd) && - IsDialogMessage(nonmodal_passphrase_hwnd, &msg)) - continue; - - TranslateMessage(&msg); - DispatchMessage(&msg); - } - - run_toplevel_callbacks(); - } - finished: - - /* Clean up the system tray icon */ - { - NOTIFYICONDATA tnid; - - tnid.cbSize = sizeof(NOTIFYICONDATA); - tnid.hWnd = traywindow; - tnid.uID = 1; - - Shell_NotifyIcon(NIM_DELETE, &tnid); - - DestroyMenu(systray_menu); - } - - if (keypath) filereq_free(keypath); - - cleanup_exit(msg.wParam); - return msg.wParam; /* just in case optimiser complains */ -} diff --git a/WINDOWS/WINPGNTC.C b/WINDOWS/WINPGNTC.C deleted file mode 100644 index 557dc532..00000000 --- a/WINDOWS/WINPGNTC.C +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Pageant client code. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <assert.h> - -#include "putty.h" -#include "pageant.h" /* for AGENT_MAX_MSGLEN */ - -#ifndef NO_SECURITY -#include "winsecur.h" -#include "wincapi.h" -#endif - -#define AGENT_COPYDATA_ID 0x804e50ba /* random goop */ - -static bool wm_copydata_agent_exists(void) -{ - HWND hwnd; - hwnd = FindWindow("Pageant", "Pageant"); - if (!hwnd) - return false; - else - return true; -} - -static void wm_copydata_agent_query(strbuf *query, void **out, int *outlen) -{ - HWND hwnd; - char *mapname; - HANDLE filemap; - unsigned char *p, *ret; - int id, retlen; - COPYDATASTRUCT cds; - SECURITY_ATTRIBUTES sa, *psa; - PSECURITY_DESCRIPTOR psd = NULL; - PSID usersid = NULL; - - *out = NULL; - *outlen = 0; - - if (query->len > AGENT_MAX_MSGLEN) - return; /* query too large */ - - hwnd = FindWindow("Pageant", "Pageant"); - if (!hwnd) - return; /* *out == NULL, so failure */ - mapname = dupprintf("PageantRequest%08x", (unsigned)GetCurrentThreadId()); - - psa = NULL; -#ifndef NO_SECURITY - if (got_advapi()) { - /* - * Make the file mapping we create for communication with - * Pageant owned by the user SID rather than the default. This - * should make communication between processes with slightly - * different contexts more reliable: in particular, command - * prompts launched as administrator should still be able to - * run PSFTPs which refer back to the owning user's - * unprivileged Pageant. - */ - usersid = get_user_sid(); - - if (usersid) { - psd = (PSECURITY_DESCRIPTOR) - LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); - if (psd) { - if (p_InitializeSecurityDescriptor - (psd, SECURITY_DESCRIPTOR_REVISION) && - p_SetSecurityDescriptorOwner(psd, usersid, false)) { - sa.nLength = sizeof(sa); - sa.bInheritHandle = true; - sa.lpSecurityDescriptor = psd; - psa = &sa; - } else { - LocalFree(psd); - psd = NULL; - } - } - } - } -#endif /* NO_SECURITY */ - - filemap = CreateFileMapping(INVALID_HANDLE_VALUE, psa, PAGE_READWRITE, - 0, AGENT_MAX_MSGLEN, mapname); - if (filemap == NULL || filemap == INVALID_HANDLE_VALUE) { - sfree(mapname); - return; /* *out == NULL, so failure */ - } - p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0); - strbuf_finalise_agent_query(query); - memcpy(p, query->s, query->len); - cds.dwData = AGENT_COPYDATA_ID; - cds.cbData = 1 + strlen(mapname); - cds.lpData = mapname; - - /* - * The user either passed a null callback (indicating that the - * query is required to be synchronous) or CreateThread failed. - * Either way, we need a synchronous request. - */ - id = SendMessage(hwnd, WM_COPYDATA, (WPARAM) NULL, (LPARAM) &cds); - if (id > 0) { - uint32_t length_field = GET_32BIT_MSB_FIRST(p); - if (length_field > 0 && length_field <= AGENT_MAX_MSGLEN - 4) { - retlen = length_field + 4; - ret = snewn(retlen, unsigned char); - memcpy(ret, p, retlen); - *out = ret; - *outlen = retlen; - } else { - /* - * If we get here, we received an out-of-range length - * field, either without space for a message type code or - * overflowing the FileMapping. - * - * Treat this as if Pageant didn't answer at all - which - * actually means we do nothing, and just don't fill in - * out and outlen. - */ - } - } - UnmapViewOfFile(p); - CloseHandle(filemap); - sfree(mapname); - if (psd) - LocalFree(psd); -} - -#ifndef NO_SECURITY - -char *agent_named_pipe_name(void) -{ - char *username, *suffix, *pipename; - username = get_username(); - suffix = capi_obfuscate_string("Pageant"); - pipename = dupprintf("\\\\.\\pipe\\pageant.%s.%s", username, suffix); - sfree(username); - sfree(suffix); - return pipename; -} - -Socket *agent_connect(Plug *plug) -{ - char *pipename = agent_named_pipe_name(); - Socket *s = new_named_pipe_client(pipename, plug); - sfree(pipename); - return s; -} - -static bool named_pipe_agent_exists(void) -{ - char *pipename = agent_named_pipe_name(); - WIN32_FIND_DATA data; - HANDLE ffh = FindFirstFile(pipename, &data); - sfree(pipename); - if (ffh == INVALID_HANDLE_VALUE) - return false; - FindClose(ffh); - return true; -} - -bool agent_exists(void) -{ - return named_pipe_agent_exists() || wm_copydata_agent_exists(); -} - -struct agent_pending_query { - struct handle *handle; - HANDLE os_handle; - strbuf *response; - void (*callback)(void *, void *, int); - void *callback_ctx; -}; - -static int named_pipe_agent_accumulate_response( - strbuf *sb, const void *data, size_t len) -{ - put_data(sb, data, len); - if (sb->len >= 4) { - uint32_t length_field = GET_32BIT_MSB_FIRST(sb->u); - if (length_field > AGENT_MAX_MSGLEN) - return -1; /* badly formatted message */ - - int overall_length = length_field + 4; - if (sb->len >= overall_length) - return overall_length; - } - - return 0; /* not done yet */ -} - -static size_t named_pipe_agent_gotdata( - struct handle *h, const void *data, size_t len, int err) -{ - agent_pending_query *pq = handle_get_privdata(h); - - if (err || len == 0) { - pq->callback(pq->callback_ctx, NULL, 0); - agent_cancel_query(pq); - return 0; - } - - int status = named_pipe_agent_accumulate_response(pq->response, data, len); - if (status == -1) { - pq->callback(pq->callback_ctx, NULL, 0); - agent_cancel_query(pq); - } else if (status > 0) { - void *response_buf = strbuf_to_str(pq->response); - pq->response = NULL; - pq->callback(pq->callback_ctx, response_buf, status); - agent_cancel_query(pq); - } - return 0; -} - -static agent_pending_query *named_pipe_agent_query( - strbuf *query, void **out, int *outlen, - void (*callback)(void *, void *, int), void *callback_ctx) -{ - agent_pending_query *pq = NULL; - char *err = NULL, *pipename = NULL; - strbuf *sb = NULL; - HANDLE pipehandle; - - pipename = agent_named_pipe_name(); - pipehandle = connect_to_named_pipe(pipename, &err); - if (pipehandle == INVALID_HANDLE_VALUE) - goto failure; - - strbuf_finalise_agent_query(query); - - for (DWORD done = 0; done < query->len ;) { - DWORD nwritten; - bool ret = WriteFile(pipehandle, query->s + done, query->len - done, - &nwritten, NULL); - if (!ret) - goto failure; - - done += nwritten; - } - - if (!callback) { - int status; - - sb = strbuf_new_nm(); - do { - char buf[1024]; - DWORD nread; - bool ret = ReadFile(pipehandle, buf, sizeof(buf), &nread, NULL); - if (!ret) - goto failure; - status = named_pipe_agent_accumulate_response(sb, buf, nread); - } while (status == 0); - - if (status == -1) - goto failure; - - *out = strbuf_to_str(sb); - *outlen = status; - sb = NULL; - pq = NULL; - goto out; - } - - pq = snew(agent_pending_query); - pq->handle = handle_input_new(pipehandle, named_pipe_agent_gotdata, pq, 0); - pq->os_handle = pipehandle; - pipehandle = INVALID_HANDLE_VALUE; /* prevent it being closed below */ - pq->response = strbuf_new_nm(); - pq->callback = callback; - pq->callback_ctx = callback_ctx; - goto out; - - failure: - *out = NULL; - *outlen = 0; - pq = NULL; - - out: - sfree(err); - sfree(pipename); - if (pipehandle != INVALID_HANDLE_VALUE) - CloseHandle(pipehandle); - if (sb) - strbuf_free(sb); - return pq; -} - -void agent_cancel_query(agent_pending_query *pq) -{ - handle_free(pq->handle); - CloseHandle(pq->os_handle); - if (pq->response) - strbuf_free(pq->response); - sfree(pq); -} - -agent_pending_query *agent_query( - strbuf *query, void **out, int *outlen, - void (*callback)(void *, void *, int), void *callback_ctx) -{ - agent_pending_query *pq = named_pipe_agent_query( - query, out, outlen, callback, callback_ctx); - if (pq || *out) - return pq; - - wm_copydata_agent_query(query, out, outlen); - return NULL; -} - -#else /* NO_SECURITY */ - -Socket *agent_connect(void *vctx, Plug *plug) -{ - unreachable("no agent_connect_ctx can be constructed on this platform"); -} - -agent_connect_ctx *agent_get_connect_ctx(void) -{ - return NULL; -} - -void agent_free_connect_ctx(agent_connect_ctx *ctx) -{ -} - -bool agent_exists(void) -{ - return wm_copydata_agent_exists(); -} - -agent_pending_query *agent_query( - strbuf *query, void **out, int *outlen, - void (*callback)(void *, void *, int), void *callback_ctx) -{ - wm_copydata_agent_query(query, out, outlen); - return NULL; -} - -void agent_cancel_query(agent_pending_query *q) -{ - unreachable("Windows agent queries are never asynchronous!"); -} - -#endif /* NO_SECURITY */ diff --git a/WINDOWS/WINPLINK.C b/WINDOWS/WINPLINK.C deleted file mode 100644 index 58d43e6d..00000000 --- a/WINDOWS/WINPLINK.C +++ /dev/null @@ -1,535 +0,0 @@ -/* - * PLink - a Windows command-line (stdin/stdout) variant of PuTTY. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <assert.h> -#include <stdarg.h> - -#include "putty.h" -#include "storage.h" -#include "tree234.h" -#include "winsecur.h" - -void cmdline_error(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - console_print_error_msg_fmt_v("plink", fmt, ap); - va_end(ap); - exit(1); -} - -static HANDLE inhandle, outhandle, errhandle; -static struct handle *stdin_handle, *stdout_handle, *stderr_handle; -static handle_sink stdout_hs, stderr_hs; -static StripCtrlChars *stdout_scc, *stderr_scc; -static BinarySink *stdout_bs, *stderr_bs; -static DWORD orig_console_mode; - -static Backend *backend; -static LogContext *logctx; -static Conf *conf; - -static void plink_echoedit_update(Seat *seat, bool echo, bool edit) -{ - /* Update stdin read mode to reflect changes in line discipline. */ - DWORD mode; - - mode = ENABLE_PROCESSED_INPUT; - if (echo) - mode = mode | ENABLE_ECHO_INPUT; - else - mode = mode & ~ENABLE_ECHO_INPUT; - if (edit) - mode = mode | ENABLE_LINE_INPUT; - else - mode = mode & ~ENABLE_LINE_INPUT; - SetConsoleMode(inhandle, mode); -} - -static size_t plink_output( - Seat *seat, bool is_stderr, const void *data, size_t len) -{ - BinarySink *bs = is_stderr ? stderr_bs : stdout_bs; - put_data(bs, data, len); - - return handle_backlog(stdout_handle) + handle_backlog(stderr_handle); -} - -static bool plink_eof(Seat *seat) -{ - handle_write_eof(stdout_handle); - return false; /* do not respond to incoming EOF with outgoing */ -} - -static int plink_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input) -{ - int ret; - ret = cmdline_get_passwd_input(p); - if (ret == -1) - ret = console_get_userpass_input(p); - return ret; -} - -static bool plink_seat_interactive(Seat *seat) -{ - return (!*conf_get_str(conf, CONF_remote_cmd) && - !*conf_get_str(conf, CONF_remote_cmd2) && - !*conf_get_str(conf, CONF_ssh_nc_host)); -} - -static const SeatVtable plink_seat_vt = { - .output = plink_output, - .eof = plink_eof, - .get_userpass_input = plink_get_userpass_input, - .notify_remote_exit = nullseat_notify_remote_exit, - .connection_fatal = console_connection_fatal, - .update_specials_menu = nullseat_update_specials_menu, - .get_ttymode = nullseat_get_ttymode, - .set_busy_status = nullseat_set_busy_status, - .verify_ssh_host_key = console_verify_ssh_host_key, - .confirm_weak_crypto_primitive = console_confirm_weak_crypto_primitive, - .confirm_weak_cached_hostkey = console_confirm_weak_cached_hostkey, - .is_utf8 = nullseat_is_never_utf8, - .echoedit_update = plink_echoedit_update, - .get_x_display = nullseat_get_x_display, - .get_windowid = nullseat_get_windowid, - .get_window_pixel_size = nullseat_get_window_pixel_size, - .stripctrl_new = console_stripctrl_new, - .set_trust_status = console_set_trust_status, - .verbose = cmdline_seat_verbose, - .interactive = plink_seat_interactive, - .get_cursor_position = nullseat_get_cursor_position, -}; -static Seat plink_seat[1] = {{ &plink_seat_vt }}; - -static DWORD main_thread_id; - -/* - * Short description of parameters. - */ -static void usage(void) -{ - printf("Plink: command-line connection utility\n"); - printf("%s\n", ver); - printf("Usage: plink [options] [user@]host [command]\n"); - printf(" (\"host\" can also be a PuTTY saved session name)\n"); - printf("Options:\n"); - printf(" -V print version information and exit\n"); - printf(" -pgpfp print PGP key fingerprints and exit\n"); - printf(" -v show verbose messages\n"); - printf(" -load sessname Load settings from saved session\n"); - printf(" -ssh -telnet -rlogin -raw -serial\n"); - printf(" force use of a particular protocol\n"); - printf(" -ssh-connection\n"); - printf(" force use of the bare ssh-connection protocol\n"); - printf(" -P port connect to specified port\n"); - printf(" -l user connect with specified username\n"); - printf(" -batch disable all interactive prompts\n"); - printf(" -proxycmd command\n"); - printf(" use 'command' as local proxy\n"); - printf(" -sercfg configuration-string (e.g. 19200,8,n,1,X)\n"); - printf(" Specify the serial configuration (serial only)\n"); - printf("The following options only apply to SSH connections:\n"); - printf(" -pw passw login with specified password\n"); - printf(" -D [listen-IP:]listen-port\n"); - printf(" Dynamic SOCKS-based port forwarding\n"); - printf(" -L [listen-IP:]listen-port:host:port\n"); - printf(" Forward local port to remote address\n"); - printf(" -R [listen-IP:]listen-port:host:port\n"); - printf(" Forward remote port to local address\n"); - printf(" -X -x enable / disable X11 forwarding\n"); - printf(" -A -a enable / disable agent forwarding\n"); - printf(" -t -T enable / disable pty allocation\n"); - printf(" -1 -2 force use of particular SSH protocol version\n"); - printf(" -4 -6 force use of IPv4 or IPv6\n"); - printf(" -C enable compression\n"); - printf(" -i key private key file for user authentication\n"); - printf(" -noagent disable use of Pageant\n"); - printf(" -agent enable use of Pageant\n"); - printf(" -no-trivial-auth\n"); - printf(" disconnect if SSH authentication succeeds trivially\n"); - printf(" -noshare disable use of connection sharing\n"); - printf(" -share enable use of connection sharing\n"); - printf(" -hostkey keyid\n"); - printf(" manually specify a host key (may be repeated)\n"); - printf(" -sanitise-stderr, -sanitise-stdout, " - "-no-sanitise-stderr, -no-sanitise-stdout\n"); - printf(" do/don't strip control chars from standard " - "output/error\n"); - printf(" -no-antispoof omit anti-spoofing prompt after " - "authentication\n"); - printf(" -m file read remote command(s) from file\n"); - printf(" -s remote command is an SSH subsystem (SSH-2 only)\n"); - printf(" -N don't start a shell/command (SSH-2 only)\n"); - printf(" -nc host:port\n"); - printf(" open tunnel in place of session (SSH-2 only)\n"); - printf(" -sshlog file\n"); - printf(" -sshrawlog file\n"); - printf(" log protocol details to a file\n"); - printf(" -logoverwrite\n"); - printf(" -logappend\n"); - printf(" control what happens when a log file already exists\n"); - printf(" -shareexists\n"); - printf(" test whether a connection-sharing upstream exists\n"); - exit(1); -} - -static void version(void) -{ - char *buildinfo_text = buildinfo("\n"); - printf("plink: %s\n%s\n", ver, buildinfo_text); - sfree(buildinfo_text); - exit(0); -} - -size_t stdin_gotdata(struct handle *h, const void *data, size_t len, int err) -{ - if (err) { - char buf[4096]; - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0, - buf, lenof(buf), NULL); - buf[lenof(buf)-1] = '\0'; - if (buf[strlen(buf)-1] == '\n') - buf[strlen(buf)-1] = '\0'; - fprintf(stderr, "Unable to read from standard input: %s\n", buf); - cleanup_exit(0); - } - - noise_ultralight(NOISE_SOURCE_IOLEN, len); - if (backend_connected(backend)) { - if (len > 0) { - return backend_send(backend, data, len); - } else { - backend_special(backend, SS_EOF, 0); - return 0; - } - } else - return 0; -} - -void stdouterr_sent(struct handle *h, size_t new_backlog, int err) -{ - if (err) { - char buf[4096]; - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0, - buf, lenof(buf), NULL); - buf[lenof(buf)-1] = '\0'; - if (buf[strlen(buf)-1] == '\n') - buf[strlen(buf)-1] = '\0'; - fprintf(stderr, "Unable to write to standard %s: %s\n", - (h == stdout_handle ? "output" : "error"), buf); - cleanup_exit(0); - } - - if (backend_connected(backend)) { - backend_unthrottle(backend, (handle_backlog(stdout_handle) + - handle_backlog(stderr_handle))); - } -} - -const bool share_can_be_downstream = true; -const bool share_can_be_upstream = true; - -const unsigned cmdline_tooltype = - TOOLTYPE_HOST_ARG | - TOOLTYPE_HOST_ARG_CAN_BE_SESSION | - TOOLTYPE_HOST_ARG_PROTOCOL_PREFIX | - TOOLTYPE_HOST_ARG_FROM_LAUNCHABLE_LOAD; - -static bool sending; - -static bool plink_mainloop_pre(void *vctx, const HANDLE **extra_handles, - size_t *n_extra_handles) -{ - if (!sending && backend_sendok(backend)) { - stdin_handle = handle_input_new(inhandle, stdin_gotdata, NULL, - 0); - sending = true; - } - - return true; -} - -static bool plink_mainloop_post(void *vctx, size_t extra_handle_index) -{ - if (sending) - handle_unthrottle(stdin_handle, backend_sendbuffer(backend)); - - if (!backend_connected(backend) && - handle_backlog(stdout_handle) + handle_backlog(stderr_handle) == 0) - return false; /* we closed the connection */ - - return true; -} - -int main(int argc, char **argv) -{ - int exitcode; - bool errors; - bool use_subsystem = false; - bool just_test_share_exists = false; - enum TriState sanitise_stdout = AUTO, sanitise_stderr = AUTO; - const struct BackendVtable *vt; - - dll_hijacking_protection(); - - /* - * Initialise port and protocol to sensible defaults. (These - * will be overridden by more or less anything.) - */ - settings_set_default_protocol(PROT_SSH); - settings_set_default_port(22); - - /* - * Process the command line. - */ - conf = conf_new(); - do_defaults(NULL, conf); - settings_set_default_protocol(conf_get_int(conf, CONF_protocol)); - settings_set_default_port(conf_get_int(conf, CONF_port)); - errors = false; - { - /* - * Override the default protocol if PLINK_PROTOCOL is set. - */ - char *p = getenv("PLINK_PROTOCOL"); - if (p) { - const struct BackendVtable *vt = backend_vt_from_name(p); - if (vt) { - settings_set_default_protocol(vt->protocol); - settings_set_default_port(vt->default_port); - conf_set_int(conf, CONF_protocol, vt->protocol); - conf_set_int(conf, CONF_port, vt->default_port); - } - } - } - while (--argc) { - char *p = *++argv; - int ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL), - 1, conf); - if (ret == -2) { - fprintf(stderr, - "plink: option \"%s\" requires an argument\n", p); - errors = true; - } else if (ret == 2) { - --argc, ++argv; - } else if (ret == 1) { - continue; - } else if (!strcmp(p, "-batch")) { - console_batch_mode = true; - } else if (!strcmp(p, "-s")) { - /* Save status to write to conf later. */ - use_subsystem = true; - } else if (!strcmp(p, "-V") || !strcmp(p, "--version")) { - version(); - } else if (!strcmp(p, "--help")) { - usage(); - } else if (!strcmp(p, "-pgpfp")) { - pgp_fingerprints(); - exit(1); - } else if (!strcmp(p, "-shareexists")) { - just_test_share_exists = true; - } else if (!strcmp(p, "-sanitise-stdout") || - !strcmp(p, "-sanitize-stdout")) { - sanitise_stdout = FORCE_ON; - } else if (!strcmp(p, "-no-sanitise-stdout") || - !strcmp(p, "-no-sanitize-stdout")) { - sanitise_stdout = FORCE_OFF; - } else if (!strcmp(p, "-sanitise-stderr") || - !strcmp(p, "-sanitize-stderr")) { - sanitise_stderr = FORCE_ON; - } else if (!strcmp(p, "-no-sanitise-stderr") || - !strcmp(p, "-no-sanitize-stderr")) { - sanitise_stderr = FORCE_OFF; - } else if (!strcmp(p, "-no-antispoof")) { - console_antispoof_prompt = false; - } else if (*p != '-') { - strbuf *cmdbuf = strbuf_new(); - - while (argc > 0) { - if (cmdbuf->len > 0) - put_byte(cmdbuf, ' '); /* add space separator */ - put_datapl(cmdbuf, ptrlen_from_asciz(p)); - if (--argc > 0) - p = *++argv; - } - - conf_set_str(conf, CONF_remote_cmd, cmdbuf->s); - conf_set_str(conf, CONF_remote_cmd2, ""); - conf_set_bool(conf, CONF_nopty, true); /* command => no tty */ - - strbuf_free(cmdbuf); - break; /* done with cmdline */ - } else { - fprintf(stderr, "plink: unknown option \"%s\"\n", p); - errors = true; - } - } - - if (errors) - return 1; - - if (!cmdline_host_ok(conf)) { - usage(); - } - - prepare_session(conf); - - /* - * Perform command-line overrides on session configuration. - */ - cmdline_run_saved(conf); - - /* - * Apply subsystem status. - */ - if (use_subsystem) - conf_set_bool(conf, CONF_ssh_subsys, true); - - /* - * Select protocol. This is farmed out into a table in a - * separate file to enable an ssh-free variant. - */ - vt = backend_vt_from_proto(conf_get_int(conf, CONF_protocol)); - if (vt == NULL) { - fprintf(stderr, - "Internal fault: Unsupported protocol found\n"); - return 1; - } - - if (vt->flags & BACKEND_NEEDS_TERMINAL) { - fprintf(stderr, - "Plink doesn't support %s, which needs terminal emulation\n", - vt->displayname); - return 1; - } - - sk_init(); - if (p_WSAEventSelect == NULL) { - fprintf(stderr, "Plink requires WinSock 2\n"); - return 1; - } - - /* - * Plink doesn't provide any way to add forwardings after the - * connection is set up, so if there are none now, we can safely set - * the "simple" flag. - */ - if (conf_get_int(conf, CONF_protocol) == PROT_SSH && - !conf_get_bool(conf, CONF_x11_forward) && - !conf_get_bool(conf, CONF_agentfwd) && - !conf_get_str_nthstrkey(conf, CONF_portfwd, 0)) - conf_set_bool(conf, CONF_ssh_simple, true); - - logctx = log_init(console_cli_logpolicy, conf); - - if (just_test_share_exists) { - if (!vt->test_for_upstream) { - fprintf(stderr, "Connection sharing not supported for this " - "connection type (%s)'\n", vt->displayname); - return 1; - } - if (vt->test_for_upstream(conf_get_str(conf, CONF_host), - conf_get_int(conf, CONF_port), conf)) - return 0; - else - return 1; - } - - if (restricted_acl()) { - lp_eventlog(console_cli_logpolicy, - "Running with restricted process ACL"); - } - - inhandle = GetStdHandle(STD_INPUT_HANDLE); - outhandle = GetStdHandle(STD_OUTPUT_HANDLE); - errhandle = GetStdHandle(STD_ERROR_HANDLE); - - /* - * Turn off ECHO and LINE input modes. We don't care if this - * call fails, because we know we aren't necessarily running in - * a console. - */ - GetConsoleMode(inhandle, &orig_console_mode); - SetConsoleMode(inhandle, ENABLE_PROCESSED_INPUT); - - /* - * Pass the output handles to the handle-handling subsystem. - * (The input one we leave until we're through the - * authentication process.) - */ - stdout_handle = handle_output_new(outhandle, stdouterr_sent, NULL, 0); - stderr_handle = handle_output_new(errhandle, stdouterr_sent, NULL, 0); - handle_sink_init(&stdout_hs, stdout_handle); - handle_sink_init(&stderr_hs, stderr_handle); - stdout_bs = BinarySink_UPCAST(&stdout_hs); - stderr_bs = BinarySink_UPCAST(&stderr_hs); - - /* - * Decide whether to sanitise control sequences out of standard - * output and standard error. - * - * If we weren't given a command-line override, we do this if (a) - * the fd in question is pointing at a console, and (b) we aren't - * trying to allocate a terminal as part of the session. - * - * (Rationale: the risk of control sequences is that they cause - * confusion when sent to a local console, so if there isn't one, - * no problem. Also, if we allocate a remote terminal, then we - * sent a terminal type, i.e. we told it what kind of escape - * sequences we _like_, i.e. we were expecting to receive some.) - */ - if (sanitise_stdout == FORCE_ON || - (sanitise_stdout == AUTO && is_console_handle(outhandle) && - conf_get_bool(conf, CONF_nopty))) { - stdout_scc = stripctrl_new(stdout_bs, true, L'\0'); - stdout_bs = BinarySink_UPCAST(stdout_scc); - } - if (sanitise_stderr == FORCE_ON || - (sanitise_stderr == AUTO && is_console_handle(errhandle) && - conf_get_bool(conf, CONF_nopty))) { - stderr_scc = stripctrl_new(stderr_bs, true, L'\0'); - stderr_bs = BinarySink_UPCAST(stderr_scc); - } - - /* - * Start up the connection. - */ - winselcli_setup(); /* ensure event object exists */ - { - char *error, *realhost; - /* nodelay is only useful if stdin is a character device (console) */ - bool nodelay = conf_get_bool(conf, CONF_tcp_nodelay) && - (GetFileType(GetStdHandle(STD_INPUT_HANDLE)) == FILE_TYPE_CHAR); - - error = backend_init(vt, plink_seat, &backend, logctx, conf, - conf_get_str(conf, CONF_host), - conf_get_int(conf, CONF_port), - &realhost, nodelay, - conf_get_bool(conf, CONF_tcp_keepalives)); - if (error) { - fprintf(stderr, "Unable to open connection:\n%s", error); - sfree(error); - return 1; - } - ldisc_create(conf, NULL, backend, plink_seat); - sfree(realhost); - } - - main_thread_id = GetCurrentThreadId(); - - sending = false; - - cli_main_loop(plink_mainloop_pre, plink_mainloop_post, NULL); - - exitcode = backend_exitcode(backend); - if (exitcode < 0) { - fprintf(stderr, "Remote process exit code unavailable\n"); - exitcode = 1; /* this is an error condition */ - } - cleanup_exit(exitcode); - return 0; /* placate compiler warning */ -} diff --git a/WINDOWS/WINPRINT.C b/WINDOWS/WINPRINT.C deleted file mode 100644 index e6b3531d..00000000 --- a/WINDOWS/WINPRINT.C +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Printing interface for PuTTY. - */ - -#include "putty.h" -#include <winspool.h> - -struct printer_enum_tag { - int nprinters; - DWORD enum_level; - union { - LPPRINTER_INFO_4 i4; - LPPRINTER_INFO_5 i5; - } info; -}; - -struct printer_job_tag { - HANDLE hprinter; -}; - -DECL_WINDOWS_FUNCTION(static, BOOL, EnumPrinters, - (DWORD, LPTSTR, DWORD, LPBYTE, DWORD, LPDWORD, LPDWORD)); -DECL_WINDOWS_FUNCTION(static, BOOL, OpenPrinter, - (LPTSTR, LPHANDLE, LPPRINTER_DEFAULTS)); -DECL_WINDOWS_FUNCTION(static, BOOL, ClosePrinter, (HANDLE)); -DECL_WINDOWS_FUNCTION(static, DWORD, StartDocPrinter, (HANDLE, DWORD, LPBYTE)); -DECL_WINDOWS_FUNCTION(static, BOOL, EndDocPrinter, (HANDLE)); -DECL_WINDOWS_FUNCTION(static, BOOL, StartPagePrinter, (HANDLE)); -DECL_WINDOWS_FUNCTION(static, BOOL, EndPagePrinter, (HANDLE)); -DECL_WINDOWS_FUNCTION(static, BOOL, WritePrinter, - (HANDLE, LPVOID, DWORD, LPDWORD)); - -static void init_winfuncs(void) -{ - static bool initialised = false; - if (initialised) - return; - { - HMODULE winspool_module = load_system32_dll("winspool.drv"); - /* Some MSDN documentation claims that some of the below functions - * should be loaded from spoolss.dll, but this doesn't seem to - * be reliable in practice. - * Nevertheless, we load spoolss.dll ourselves using our safe - * loading method, against the possibility that winspool.drv - * later loads it unsafely. */ - (void) load_system32_dll("spoolss.dll"); - GET_WINDOWS_FUNCTION_PP(winspool_module, EnumPrinters); - GET_WINDOWS_FUNCTION_PP(winspool_module, OpenPrinter); - GET_WINDOWS_FUNCTION_PP(winspool_module, ClosePrinter); - GET_WINDOWS_FUNCTION_PP(winspool_module, StartDocPrinter); - GET_WINDOWS_FUNCTION_PP(winspool_module, EndDocPrinter); - GET_WINDOWS_FUNCTION_PP(winspool_module, StartPagePrinter); - GET_WINDOWS_FUNCTION_PP(winspool_module, EndPagePrinter); - GET_WINDOWS_FUNCTION_PP(winspool_module, WritePrinter); - } - initialised = true; -} - -static bool printer_add_enum(int param, DWORD level, char **buffer, - int offset, int *nprinters_ptr) -{ - DWORD needed = 0, nprinters = 0; - - init_winfuncs(); - - *buffer = sresize(*buffer, offset+512, char); - - /* - * Exploratory call to EnumPrinters to determine how much space - * we'll need for the output. Discard the return value since it - * will almost certainly be a failure due to lack of space. - */ - p_EnumPrinters(param, NULL, level, (LPBYTE)((*buffer)+offset), 512, - &needed, &nprinters); - - if (needed < 512) - needed = 512; - - *buffer = sresize(*buffer, offset+needed, char); - - if (p_EnumPrinters(param, NULL, level, (LPBYTE)((*buffer)+offset), - needed, &needed, &nprinters) == 0) - return false; - - *nprinters_ptr += nprinters; - - return true; -} - -printer_enum *printer_start_enum(int *nprinters_ptr) -{ - printer_enum *ret = snew(printer_enum); - char *buffer = NULL; - - *nprinters_ptr = 0; /* default return value */ - buffer = snewn(512, char); - - /* - * Determine what enumeration level to use. - * When enumerating printers, we need to use PRINTER_INFO_4 on - * NT-class systems to avoid Windows looking too hard for them and - * slowing things down; and we need to avoid PRINTER_INFO_5 as - * we've seen network printers not show up. - * On 9x-class systems, PRINTER_INFO_4 isn't available and - * PRINTER_INFO_5 is recommended. - * Bletch. - */ - if (osPlatformId != VER_PLATFORM_WIN32_NT) { - ret->enum_level = 5; - } else { - ret->enum_level = 4; - } - - if (!printer_add_enum(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, - ret->enum_level, &buffer, 0, nprinters_ptr)) - goto error; - - switch (ret->enum_level) { - case 4: - ret->info.i4 = (LPPRINTER_INFO_4)buffer; - break; - case 5: - ret->info.i5 = (LPPRINTER_INFO_5)buffer; - break; - } - ret->nprinters = *nprinters_ptr; - - return ret; - - error: - sfree(buffer); - sfree(ret); - *nprinters_ptr = 0; - return NULL; -} - -char *printer_get_name(printer_enum *pe, int i) -{ - if (!pe) - return NULL; - if (i < 0 || i >= pe->nprinters) - return NULL; - switch (pe->enum_level) { - case 4: - return pe->info.i4[i].pPrinterName; - case 5: - return pe->info.i5[i].pPrinterName; - default: - return NULL; - } -} - -void printer_finish_enum(printer_enum *pe) -{ - if (!pe) - return; - switch (pe->enum_level) { - case 4: - sfree(pe->info.i4); - break; - case 5: - sfree(pe->info.i5); - break; - } - sfree(pe); -} - -printer_job *printer_start_job(char *printer) -{ - printer_job *ret = snew(printer_job); - DOC_INFO_1 docinfo; - bool jobstarted = false, pagestarted = false; - - init_winfuncs(); - - ret->hprinter = NULL; - if (!p_OpenPrinter(printer, &ret->hprinter, NULL)) - goto error; - - docinfo.pDocName = "PuTTY remote printer output"; - docinfo.pOutputFile = NULL; - docinfo.pDatatype = "RAW"; - - if (!p_StartDocPrinter(ret->hprinter, 1, (LPBYTE)&docinfo)) - goto error; - jobstarted = true; - - if (!p_StartPagePrinter(ret->hprinter)) - goto error; - pagestarted = true; - - return ret; - - error: - if (pagestarted) - p_EndPagePrinter(ret->hprinter); - if (jobstarted) - p_EndDocPrinter(ret->hprinter); - if (ret->hprinter) - p_ClosePrinter(ret->hprinter); - sfree(ret); - return NULL; -} - -void printer_job_data(printer_job *pj, const void *data, size_t len) -{ - DWORD written; - - if (!pj) - return; - - p_WritePrinter(pj->hprinter, (void *)data, len, &written); -} - -void printer_finish_job(printer_job *pj) -{ - if (!pj) - return; - - p_EndPagePrinter(pj->hprinter); - p_EndDocPrinter(pj->hprinter); - p_ClosePrinter(pj->hprinter); - sfree(pj); -} diff --git a/WINDOWS/WINPROXY.C b/WINDOWS/WINPROXY.C deleted file mode 100644 index 94e31fcb..00000000 --- a/WINDOWS/WINPROXY.C +++ /dev/null @@ -1,107 +0,0 @@ -/* - * winproxy.c: Windows implementation of platform_new_connection(), - * supporting an OpenSSH-like proxy command via the winhandl.c - * mechanism. - */ - -#include <stdio.h> -#include <assert.h> - -#include "tree234.h" -#include "putty.h" -#include "network.h" -#include "proxy.h" - -Socket *platform_new_connection(SockAddr *addr, const char *hostname, - int port, bool privport, - bool oobinline, bool nodelay, bool keepalive, - Plug *plug, Conf *conf) -{ - char *cmd; - HANDLE us_to_cmd, cmd_from_us; - HANDLE us_from_cmd, cmd_to_us; - HANDLE us_from_cmd_err, cmd_err_to_us; - SECURITY_ATTRIBUTES sa; - STARTUPINFO si; - PROCESS_INFORMATION pi; - - if (conf_get_int(conf, CONF_proxy_type) != PROXY_CMD) - return NULL; - - cmd = format_telnet_command(addr, port, conf); - - /* We are responsible for this and don't need it any more */ - sk_addr_free(addr); - - { - char *msg = dupprintf("Starting local proxy command: %s", cmd); - plug_log(plug, PLUGLOG_PROXY_MSG, NULL, 0, msg, 0); - sfree(msg); - } - - /* - * Create the pipes to the proxy command, and spawn the proxy - * command process. - */ - sa.nLength = sizeof(sa); - sa.lpSecurityDescriptor = NULL; /* default */ - sa.bInheritHandle = true; - if (!CreatePipe(&us_from_cmd, &cmd_to_us, &sa, 0)) { - sfree(cmd); - return new_error_socket_fmt( - plug, "Unable to create pipes for proxy command: %s", - win_strerror(GetLastError())); - } - - if (!CreatePipe(&cmd_from_us, &us_to_cmd, &sa, 0)) { - sfree(cmd); - CloseHandle(us_from_cmd); - CloseHandle(cmd_to_us); - return new_error_socket_fmt( - plug, "Unable to create pipes for proxy command: %s", - win_strerror(GetLastError())); - } - - if (!CreatePipe(&us_from_cmd_err, &cmd_err_to_us, &sa, 0)) { - sfree(cmd); - CloseHandle(us_from_cmd); - CloseHandle(cmd_to_us); - CloseHandle(us_to_cmd); - CloseHandle(cmd_from_us); - return new_error_socket_fmt( - plug, "Unable to create pipes for proxy command: %s", - win_strerror(GetLastError())); - } - - SetHandleInformation(us_to_cmd, HANDLE_FLAG_INHERIT, 0); - SetHandleInformation(us_from_cmd, HANDLE_FLAG_INHERIT, 0); - if (us_from_cmd_err != NULL) - SetHandleInformation(us_from_cmd_err, HANDLE_FLAG_INHERIT, 0); - - si.cb = sizeof(si); - si.lpReserved = NULL; - si.lpDesktop = NULL; - si.lpTitle = NULL; - si.dwFlags = STARTF_USESTDHANDLES; - si.cbReserved2 = 0; - si.lpReserved2 = NULL; - si.hStdInput = cmd_from_us; - si.hStdOutput = cmd_to_us; - si.hStdError = cmd_err_to_us; - CreateProcess(NULL, cmd, NULL, NULL, true, - CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS, - NULL, NULL, &si, &pi); - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - - sfree(cmd); - - CloseHandle(cmd_from_us); - CloseHandle(cmd_to_us); - - if (cmd_err_to_us != NULL) - CloseHandle(cmd_err_to_us); - - return make_handle_socket(us_to_cmd, us_from_cmd, us_from_cmd_err, - plug, false); -} diff --git a/WINDOWS/WINSER.C b/WINDOWS/WINSER.C deleted file mode 100644 index 7f4bcf2e..00000000 --- a/WINDOWS/WINSER.C +++ /dev/null @@ -1,465 +0,0 @@ -/* - * Serial back end (Windows-specific). - */ - -#include <stdio.h> -#include <stdlib.h> -#include <limits.h> - -#include "putty.h" - -#define SERIAL_MAX_BACKLOG 4096 - -typedef struct Serial Serial; -struct Serial { - HANDLE port; - struct handle *out, *in; - Seat *seat; - LogContext *logctx; - int bufsize; - long clearbreak_time; - bool break_in_progress; - Backend backend; -}; - -static void serial_terminate(Serial *serial) -{ - if (serial->out) { - handle_free(serial->out); - serial->out = NULL; - } - if (serial->in) { - handle_free(serial->in); - serial->in = NULL; - } - if (serial->port != INVALID_HANDLE_VALUE) { - if (serial->break_in_progress) - ClearCommBreak(serial->port); - CloseHandle(serial->port); - serial->port = INVALID_HANDLE_VALUE; - } -} - -static size_t serial_gotdata( - struct handle *h, const void *data, size_t len, int err) -{ - Serial *serial = (Serial *)handle_get_privdata(h); - if (err || len == 0) { - const char *error_msg; - - /* - * Currently, len==0 should never happen because we're - * ignoring EOFs. However, it seems not totally impossible - * that this same back end might be usable to talk to named - * pipes or some other non-serial device, in which case EOF - * may become meaningful here. - */ - if (!err) - error_msg = "End of file reading from serial device"; - else - error_msg = "Error reading from serial device"; - - serial_terminate(serial); - - seat_notify_remote_exit(serial->seat); - - logevent(serial->logctx, error_msg); - - seat_connection_fatal(serial->seat, "%s", error_msg); - - return 0; - } else { - return seat_stdout(serial->seat, data, len); - } -} - -static void serial_sentdata(struct handle *h, size_t new_backlog, int err) -{ - Serial *serial = (Serial *)handle_get_privdata(h); - if (err) { - const char *error_msg = "Error writing to serial device"; - - serial_terminate(serial); - - seat_notify_remote_exit(serial->seat); - - logevent(serial->logctx, error_msg); - - seat_connection_fatal(serial->seat, "%s", error_msg); - } else { - serial->bufsize = new_backlog; - } -} - -static char *serial_configure(Serial *serial, HANDLE serport, Conf *conf) -{ - DCB dcb; - COMMTIMEOUTS timeouts; - - /* - * Set up the serial port parameters. If we can't even - * GetCommState, we ignore the problem on the grounds that the - * user might have pointed us at some other type of two-way - * device instead of a serial port. - */ - if (GetCommState(serport, &dcb)) { - const char *str; - - /* - * Boilerplate. - */ - dcb.fBinary = true; - dcb.fDtrControl = DTR_CONTROL_ENABLE; - dcb.fDsrSensitivity = false; - dcb.fTXContinueOnXoff = false; - dcb.fOutX = false; - dcb.fInX = false; - dcb.fErrorChar = false; - dcb.fNull = false; - dcb.fRtsControl = RTS_CONTROL_ENABLE; - dcb.fAbortOnError = false; - dcb.fOutxCtsFlow = false; - dcb.fOutxDsrFlow = false; - - /* - * Configurable parameters. - */ - dcb.BaudRate = conf_get_int(conf, CONF_serspeed); - logeventf(serial->logctx, "Configuring baud rate %lu", - (unsigned long)dcb.BaudRate); - - dcb.ByteSize = conf_get_int(conf, CONF_serdatabits); - logeventf(serial->logctx, "Configuring %u data bits", - (unsigned)dcb.ByteSize); - - switch (conf_get_int(conf, CONF_serstopbits)) { - case 2: dcb.StopBits = ONESTOPBIT; str = "1 stop bit"; break; - case 3: dcb.StopBits = ONE5STOPBITS; str = "1.5 stop bits"; break; - case 4: dcb.StopBits = TWOSTOPBITS; str = "2 stop bits"; break; - default: return dupstr("Invalid number of stop bits " - "(need 1, 1.5 or 2)"); - } - logeventf(serial->logctx, "Configuring %s", str); - - switch (conf_get_int(conf, CONF_serparity)) { - case SER_PAR_NONE: dcb.Parity = NOPARITY; str = "no"; break; - case SER_PAR_ODD: dcb.Parity = ODDPARITY; str = "odd"; break; - case SER_PAR_EVEN: dcb.Parity = EVENPARITY; str = "even"; break; - case SER_PAR_MARK: dcb.Parity = MARKPARITY; str = "mark"; break; - case SER_PAR_SPACE: dcb.Parity = SPACEPARITY; str = "space"; break; - } - logeventf(serial->logctx, "Configuring %s parity", str); - - switch (conf_get_int(conf, CONF_serflow)) { - case SER_FLOW_NONE: - str = "no"; - break; - case SER_FLOW_XONXOFF: - dcb.fOutX = dcb.fInX = true; - str = "XON/XOFF"; - break; - case SER_FLOW_RTSCTS: - dcb.fRtsControl = RTS_CONTROL_HANDSHAKE; - dcb.fOutxCtsFlow = true; - str = "RTS/CTS"; - break; - case SER_FLOW_DSRDTR: - dcb.fDtrControl = DTR_CONTROL_HANDSHAKE; - dcb.fOutxDsrFlow = true; - str = "DSR/DTR"; - break; - } - logeventf(serial->logctx, "Configuring %s flow control", str); - - if (!SetCommState(serport, &dcb)) - return dupprintf("Configuring serial port: %s", - win_strerror(GetLastError())); - - timeouts.ReadIntervalTimeout = 1; - timeouts.ReadTotalTimeoutMultiplier = 0; - timeouts.ReadTotalTimeoutConstant = 0; - timeouts.WriteTotalTimeoutMultiplier = 0; - timeouts.WriteTotalTimeoutConstant = 0; - if (!SetCommTimeouts(serport, &timeouts)) - return dupprintf("Configuring serial timeouts: %s", - win_strerror(GetLastError())); - } - - return NULL; -} - -/* - * Called to set up the serial connection. - * - * Returns an error message, or NULL on success. - * - * Also places the canonical host name into `realhost'. It must be - * freed by the caller. - */ -static char *serial_init(const BackendVtable *vt, Seat *seat, - Backend **backend_handle, LogContext *logctx, - Conf *conf, const char *host, int port, - char **realhost, bool nodelay, bool keepalive) -{ - Serial *serial; - HANDLE serport; - char *err; - char *serline; - - /* No local authentication phase in this protocol */ - seat_set_trust_status(seat, false); - - serial = snew(Serial); - serial->port = INVALID_HANDLE_VALUE; - serial->out = serial->in = NULL; - serial->bufsize = 0; - serial->break_in_progress = false; - serial->backend.vt = vt; - *backend_handle = &serial->backend; - - serial->seat = seat; - serial->logctx = logctx; - - serline = conf_get_str(conf, CONF_serline); - logeventf(serial->logctx, "Opening serial device %s", serline); - - /* - * Munge the string supplied by the user into a Windows filename. - * - * Windows supports opening a few "legacy" devices (including - * COM1-9) by specifying their names verbatim as a filename to - * open. (Thus, no files can ever have these names. See - * <http://msdn2.microsoft.com/en-us/library/aa365247.aspx> - * ("Naming a File") for the complete list of reserved names.) - * - * However, this doesn't let you get at devices COM10 and above. - * For that, you need to specify a filename like "\\.\COM10". - * This is also necessary for special serial and serial-like - * devices such as \\.\WCEUSBSH001. It also works for the "legacy" - * names, so you can do \\.\COM1 (verified as far back as Win95). - * See <http://msdn2.microsoft.com/en-us/library/aa363858.aspx> - * (CreateFile() docs). - * - * So, we believe that prepending "\\.\" should always be the - * Right Thing. However, just in case someone finds something to - * talk to that doesn't exist under there, if the serial line - * contains a backslash, we use it verbatim. (This also lets - * existing configurations using \\.\ continue working.) - */ - char *serfilename = - dupprintf("%s%s", strchr(serline, '\\') ? "" : "\\\\.\\", serline); - serport = CreateFile(serfilename, GENERIC_READ | GENERIC_WRITE, 0, NULL, - OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); - if (serport == INVALID_HANDLE_VALUE) { - err = dupprintf("Opening '%s': %s", - serfilename, win_strerror(GetLastError())); - sfree(serfilename); - return err; - } - - sfree(serfilename); - - err = serial_configure(serial, serport, conf); - if (err) - return err; - - serial->port = serport; - serial->out = handle_output_new(serport, serial_sentdata, serial, - HANDLE_FLAG_OVERLAPPED); - serial->in = handle_input_new(serport, serial_gotdata, serial, - HANDLE_FLAG_OVERLAPPED | - HANDLE_FLAG_IGNOREEOF | - HANDLE_FLAG_UNITBUFFER); - - *realhost = dupstr(serline); - - /* - * Specials are always available. - */ - seat_update_specials_menu(serial->seat); - - return NULL; -} - -static void serial_free(Backend *be) -{ - Serial *serial = container_of(be, Serial, backend); - - serial_terminate(serial); - expire_timer_context(serial); - sfree(serial); -} - -static void serial_reconfig(Backend *be, Conf *conf) -{ - Serial *serial = container_of(be, Serial, backend); - - serial_configure(serial, serial->port, conf); - - /* - * FIXME: what should we do if that call returned a non-NULL error - * message? - */ -} - -/* - * Called to send data down the serial connection. - */ -static size_t serial_send(Backend *be, const char *buf, size_t len) -{ - Serial *serial = container_of(be, Serial, backend); - - if (serial->out == NULL) - return 0; - - serial->bufsize = handle_write(serial->out, buf, len); - return serial->bufsize; -} - -/* - * Called to query the current sendability status. - */ -static size_t serial_sendbuffer(Backend *be) -{ - Serial *serial = container_of(be, Serial, backend); - return serial->bufsize; -} - -/* - * Called to set the size of the window - */ -static void serial_size(Backend *be, int width, int height) -{ - /* Do nothing! */ - return; -} - -static void serbreak_timer(void *ctx, unsigned long now) -{ - Serial *serial = (Serial *)ctx; - - if (now == serial->clearbreak_time && serial->port) { - ClearCommBreak(serial->port); - serial->break_in_progress = false; - logevent(serial->logctx, "Finished serial break"); - } -} - -/* - * Send serial special codes. - */ -static void serial_special(Backend *be, SessionSpecialCode code, int arg) -{ - Serial *serial = container_of(be, Serial, backend); - - if (serial->port && code == SS_BRK) { - logevent(serial->logctx, "Starting serial break at user request"); - SetCommBreak(serial->port); - /* - * To send a serial break on Windows, we call SetCommBreak - * to begin the break, then wait a bit, and then call - * ClearCommBreak to finish it. Hence, I must use timing.c - * to arrange a callback when it's time to do the latter. - * - * SUS says that a default break length must be between 1/4 - * and 1/2 second. FreeBSD apparently goes with 2/5 second, - * and so will I. - */ - serial->clearbreak_time = - schedule_timer(TICKSPERSEC * 2 / 5, serbreak_timer, serial); - serial->break_in_progress = true; - } - - return; -} - -/* - * Return a list of the special codes that make sense in this - * protocol. - */ -static const SessionSpecial *serial_get_specials(Backend *be) -{ - static const SessionSpecial specials[] = { - {"Break", SS_BRK}, - {NULL, SS_EXITMENU} - }; - return specials; -} - -static bool serial_connected(Backend *be) -{ - return true; /* always connected */ -} - -static bool serial_sendok(Backend *be) -{ - return true; -} - -static void serial_unthrottle(Backend *be, size_t backlog) -{ - Serial *serial = container_of(be, Serial, backend); - if (serial->in) - handle_unthrottle(serial->in, backlog); -} - -static bool serial_ldisc(Backend *be, int option) -{ - /* - * Local editing and local echo are off by default. - */ - return false; -} - -static void serial_provide_ldisc(Backend *be, Ldisc *ldisc) -{ - /* This is a stub. */ -} - -static int serial_exitcode(Backend *be) -{ - Serial *serial = container_of(be, Serial, backend); - if (serial->port != INVALID_HANDLE_VALUE) - return -1; /* still connected */ - else - /* Exit codes are a meaningless concept with serial ports */ - return INT_MAX; -} - -/* - * cfg_info for Serial does nothing at all. - */ -static int serial_cfg_info(Backend *be) -{ - return 0; -} - -const BackendVtable serial_backend = { - .init = serial_init, - .free = serial_free, - .reconfig = serial_reconfig, - .send = serial_send, - .sendbuffer = serial_sendbuffer, - .size = serial_size, - .special = serial_special, - .get_specials = serial_get_specials, - .connected = serial_connected, - .exitcode = serial_exitcode, - .sendok = serial_sendok, - .ldisc_option_state = serial_ldisc, - .provide_ldisc = serial_provide_ldisc, - .unthrottle = serial_unthrottle, - .cfg_info = serial_cfg_info, - .id = "serial", - .displayname = "Serial", - .protocol = PROT_SERIAL, - .serial_parity_mask = ((1 << SER_PAR_NONE) | - (1 << SER_PAR_ODD) | - (1 << SER_PAR_EVEN) | - (1 << SER_PAR_MARK) | - (1 << SER_PAR_SPACE)), - .serial_flow_mask = ((1 << SER_FLOW_NONE) | - (1 << SER_FLOW_XONXOFF) | - (1 << SER_FLOW_RTSCTS) | - (1 << SER_FLOW_DSRDTR)), -}; diff --git a/WINDOWS/WINSFTP.C b/WINDOWS/WINSFTP.C deleted file mode 100644 index 0c695d2e..00000000 --- a/WINDOWS/WINSFTP.C +++ /dev/null @@ -1,650 +0,0 @@ -/* - * winsftp.c: the Windows-specific parts of PSFTP and PSCP. - */ - -#include <winsock2.h> /* need to put this first, for winelib builds */ -#include <assert.h> - -#define NEED_DECLARATION_OF_SELECT - -#include "putty.h" -#include "psftp.h" -#include "ssh.h" -#include "winsecur.h" - -int filexfer_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input) -{ - int ret; - ret = cmdline_get_passwd_input(p); - if (ret == -1) - ret = console_get_userpass_input(p); - return ret; -} - -void platform_get_x11_auth(struct X11Display *display, Conf *conf) -{ - /* Do nothing, therefore no auth. */ -} -const bool platform_uses_x11_unix_by_default = true; - -/* ---------------------------------------------------------------------- - * File access abstraction. - */ - -/* - * Set local current directory. Returns NULL on success, or else an - * error message which must be freed after printing. - */ -char *psftp_lcd(char *dir) -{ - char *ret = NULL; - - if (!SetCurrentDirectory(dir)) { - LPVOID message; - int i; - FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, GetLastError(), - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR)&message, 0, NULL); - i = strcspn((char *)message, "\n"); - ret = dupprintf("%.*s", i, (LPCTSTR)message); - LocalFree(message); - } - - return ret; -} - -/* - * Get local current directory. Returns a string which must be - * freed. - */ -char *psftp_getcwd(void) -{ - char *ret = snewn(256, char); - size_t len = GetCurrentDirectory(256, ret); - if (len > 256) - ret = sresize(ret, len, char); - GetCurrentDirectory(len, ret); - return ret; -} - -static inline uint64_t uint64_from_words(uint32_t hi, uint32_t lo) -{ - return (((uint64_t)hi) << 32) | lo; -} - -#define TIME_POSIX_TO_WIN(t, ft) do { \ - ULARGE_INTEGER uli; \ - uli.QuadPart = ((ULONGLONG)(t) + 11644473600ull) * 10000000ull; \ - (ft).dwLowDateTime = uli.LowPart; \ - (ft).dwHighDateTime = uli.HighPart; \ -} while(0) -#define TIME_WIN_TO_POSIX(ft, t) do { \ - ULARGE_INTEGER uli; \ - uli.LowPart = (ft).dwLowDateTime; \ - uli.HighPart = (ft).dwHighDateTime; \ - uli.QuadPart = uli.QuadPart / 10000000ull - 11644473600ull; \ - (t) = (unsigned long) uli.QuadPart; \ -} while(0) - -struct RFile { - HANDLE h; -}; - -RFile *open_existing_file(const char *name, uint64_t *size, - unsigned long *mtime, unsigned long *atime, - long *perms) -{ - HANDLE h; - RFile *ret; - - h = CreateFile(name, GENERIC_READ, FILE_SHARE_READ, NULL, - OPEN_EXISTING, 0, 0); - if (h == INVALID_HANDLE_VALUE) - return NULL; - - ret = snew(RFile); - ret->h = h; - - if (size) { - DWORD lo, hi; - lo = GetFileSize(h, &hi); - *size = uint64_from_words(hi, lo); - } - - if (mtime || atime) { - FILETIME actime, wrtime; - GetFileTime(h, NULL, &actime, &wrtime); - if (atime) - TIME_WIN_TO_POSIX(actime, *atime); - if (mtime) - TIME_WIN_TO_POSIX(wrtime, *mtime); - } - - if (perms) - *perms = -1; - - return ret; -} - -int read_from_file(RFile *f, void *buffer, int length) -{ - DWORD read; - if (!ReadFile(f->h, buffer, length, &read, NULL)) - return -1; /* error */ - else - return read; -} - -void close_rfile(RFile *f) -{ - CloseHandle(f->h); - sfree(f); -} - -struct WFile { - HANDLE h; -}; - -WFile *open_new_file(const char *name, long perms) -{ - HANDLE h; - WFile *ret; - - h = CreateFile(name, GENERIC_WRITE, 0, NULL, - CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); - if (h == INVALID_HANDLE_VALUE) - return NULL; - - ret = snew(WFile); - ret->h = h; - - return ret; -} - -WFile *open_existing_wfile(const char *name, uint64_t *size) -{ - HANDLE h; - WFile *ret; - - h = CreateFile(name, GENERIC_WRITE, FILE_SHARE_READ, NULL, - OPEN_EXISTING, 0, 0); - if (h == INVALID_HANDLE_VALUE) - return NULL; - - ret = snew(WFile); - ret->h = h; - - if (size) { - DWORD lo, hi; - lo = GetFileSize(h, &hi); - *size = uint64_from_words(hi, lo); - } - - return ret; -} - -int write_to_file(WFile *f, void *buffer, int length) -{ - DWORD written; - if (!WriteFile(f->h, buffer, length, &written, NULL)) - return -1; /* error */ - else - return written; -} - -void set_file_times(WFile *f, unsigned long mtime, unsigned long atime) -{ - FILETIME actime, wrtime; - TIME_POSIX_TO_WIN(atime, actime); - TIME_POSIX_TO_WIN(mtime, wrtime); - SetFileTime(f->h, NULL, &actime, &wrtime); -} - -void close_wfile(WFile *f) -{ - CloseHandle(f->h); - sfree(f); -} - -/* Seek offset bytes through file, from whence, where whence is - FROM_START, FROM_CURRENT, or FROM_END */ -int seek_file(WFile *f, uint64_t offset, int whence) -{ - DWORD movemethod; - - switch (whence) { - case FROM_START: - movemethod = FILE_BEGIN; - break; - case FROM_CURRENT: - movemethod = FILE_CURRENT; - break; - case FROM_END: - movemethod = FILE_END; - break; - default: - return -1; - } - - { - LONG lo = offset & 0xFFFFFFFFU, hi = offset >> 32; - SetFilePointer(f->h, lo, &hi, movemethod); - } - - if (GetLastError() != NO_ERROR) - return -1; - else - return 0; -} - -uint64_t get_file_posn(WFile *f) -{ - LONG lo, hi = 0; - - lo = SetFilePointer(f->h, 0L, &hi, FILE_CURRENT); - return uint64_from_words(hi, lo); -} - -int file_type(const char *name) -{ - DWORD attr; - attr = GetFileAttributes(name); - /* We know of no `weird' files under Windows. */ - if (attr == (DWORD)-1) - return FILE_TYPE_NONEXISTENT; - else if (attr & FILE_ATTRIBUTE_DIRECTORY) - return FILE_TYPE_DIRECTORY; - else - return FILE_TYPE_FILE; -} - -struct DirHandle { - HANDLE h; - char *name; -}; - -DirHandle *open_directory(const char *name, const char **errmsg) -{ - HANDLE h; - WIN32_FIND_DATA fdat; - char *findfile; - DirHandle *ret; - - /* Enumerate files in dir `foo'. */ - findfile = dupcat(name, "/*"); - h = FindFirstFile(findfile, &fdat); - if (h == INVALID_HANDLE_VALUE) { - *errmsg = win_strerror(GetLastError()); - return NULL; - } - sfree(findfile); - - ret = snew(DirHandle); - ret->h = h; - ret->name = dupstr(fdat.cFileName); - return ret; -} - -char *read_filename(DirHandle *dir) -{ - do { - - if (!dir->name) { - WIN32_FIND_DATA fdat; - if (!FindNextFile(dir->h, &fdat)) - return NULL; - else - dir->name = dupstr(fdat.cFileName); - } - - assert(dir->name); - if (dir->name[0] == '.' && - (dir->name[1] == '\0' || - (dir->name[1] == '.' && dir->name[2] == '\0'))) { - sfree(dir->name); - dir->name = NULL; - } - - } while (!dir->name); - - if (dir->name) { - char *ret = dir->name; - dir->name = NULL; - return ret; - } else - return NULL; -} - -void close_directory(DirHandle *dir) -{ - FindClose(dir->h); - if (dir->name) - sfree(dir->name); - sfree(dir); -} - -int test_wildcard(const char *name, bool cmdline) -{ - HANDLE fh; - WIN32_FIND_DATA fdat; - - /* First see if the exact name exists. */ - if (GetFileAttributes(name) != (DWORD)-1) - return WCTYPE_FILENAME; - - /* Otherwise see if a wildcard match finds anything. */ - fh = FindFirstFile(name, &fdat); - if (fh == INVALID_HANDLE_VALUE) - return WCTYPE_NONEXISTENT; - - FindClose(fh); - return WCTYPE_WILDCARD; -} - -struct WildcardMatcher { - HANDLE h; - char *name; - char *srcpath; -}; - -char *stripslashes(const char *str, bool local) -{ - char *p; - - /* - * On Windows, \ / : are all path component separators. - */ - - if (local) { - p = strchr(str, ':'); - if (p) str = p+1; - } - - p = strrchr(str, '/'); - if (p) str = p+1; - - if (local) { - p = strrchr(str, '\\'); - if (p) str = p+1; - } - - return (char *)str; -} - -WildcardMatcher *begin_wildcard_matching(const char *name) -{ - HANDLE h; - WIN32_FIND_DATA fdat; - WildcardMatcher *ret; - char *last; - - h = FindFirstFile(name, &fdat); - if (h == INVALID_HANDLE_VALUE) - return NULL; - - ret = snew(WildcardMatcher); - ret->h = h; - ret->srcpath = dupstr(name); - last = stripslashes(ret->srcpath, true); - *last = '\0'; - if (fdat.cFileName[0] == '.' && - (fdat.cFileName[1] == '\0' || - (fdat.cFileName[1] == '.' && fdat.cFileName[2] == '\0'))) - ret->name = NULL; - else - ret->name = dupcat(ret->srcpath, fdat.cFileName); - - return ret; -} - -char *wildcard_get_filename(WildcardMatcher *dir) -{ - while (!dir->name) { - WIN32_FIND_DATA fdat; - - if (!FindNextFile(dir->h, &fdat)) - return NULL; - - if (fdat.cFileName[0] == '.' && - (fdat.cFileName[1] == '\0' || - (fdat.cFileName[1] == '.' && fdat.cFileName[2] == '\0'))) - dir->name = NULL; - else - dir->name = dupcat(dir->srcpath, fdat.cFileName); - } - - if (dir->name) { - char *ret = dir->name; - dir->name = NULL; - return ret; - } else - return NULL; -} - -void finish_wildcard_matching(WildcardMatcher *dir) -{ - FindClose(dir->h); - if (dir->name) - sfree(dir->name); - sfree(dir->srcpath); - sfree(dir); -} - -bool vet_filename(const char *name) -{ - if (strchr(name, '/') || strchr(name, '\\') || strchr(name, ':')) - return false; - - if (!name[strspn(name, ".")]) /* entirely composed of dots */ - return false; - - return true; -} - -bool create_directory(const char *name) -{ - return CreateDirectory(name, NULL) != 0; -} - -char *dir_file_cat(const char *dir, const char *file) -{ - ptrlen dir_pl = ptrlen_from_asciz(dir); - return dupcat( - dir, (ptrlen_endswith(dir_pl, PTRLEN_LITERAL("\\"), NULL) || - ptrlen_endswith(dir_pl, PTRLEN_LITERAL("/"), NULL)) ? "" : "\\", - file); -} - -/* ---------------------------------------------------------------------- - * Platform-specific network handling. - */ -struct winsftp_cliloop_ctx { - HANDLE other_event; - int toret; -}; -static bool winsftp_cliloop_pre(void *vctx, const HANDLE **extra_handles, - size_t *n_extra_handles) -{ - struct winsftp_cliloop_ctx *ctx = (struct winsftp_cliloop_ctx *)vctx; - - if (ctx->other_event != INVALID_HANDLE_VALUE) { - *extra_handles = &ctx->other_event; - *n_extra_handles = 1; - } - - return true; -} -static bool winsftp_cliloop_post(void *vctx, size_t extra_handle_index) -{ - struct winsftp_cliloop_ctx *ctx = (struct winsftp_cliloop_ctx *)vctx; - - if (ctx->other_event != INVALID_HANDLE_VALUE && - extra_handle_index == 0) - ctx->toret = 1; /* other_event was set */ - - return false; /* always run only one loop iteration */ -} -int do_eventsel_loop(HANDLE other_event) -{ - struct winsftp_cliloop_ctx ctx[1]; - ctx->other_event = other_event; - ctx->toret = 0; - cli_main_loop(winsftp_cliloop_pre, winsftp_cliloop_post, ctx); - return ctx->toret; -} - -/* - * Wait for some network data and process it. - * - * We have two variants of this function. One uses select() so that - * it's compatible with WinSock 1. The other uses WSAEventSelect - * and MsgWaitForMultipleObjects, so that we can consistently use - * WSAEventSelect throughout; this enables us to also implement - * ssh_sftp_get_cmdline() using a parallel mechanism. - */ -int ssh_sftp_loop_iteration(void) -{ - if (p_WSAEventSelect == NULL) { - fd_set readfds; - int ret; - unsigned long now = GETTICKCOUNT(), then; - SOCKET skt = winselcli_unique_socket(); - - if (skt == INVALID_SOCKET) - return -1; /* doom */ - - if (socket_writable(skt)) - select_result((WPARAM) skt, (LPARAM) FD_WRITE); - - do { - unsigned long next; - long ticks; - struct timeval tv, *ptv; - - if (run_timers(now, &next)) { - then = now; - now = GETTICKCOUNT(); - if (now - then > next - then) - ticks = 0; - else - ticks = next - now; - tv.tv_sec = ticks / 1000; - tv.tv_usec = ticks % 1000 * 1000; - ptv = &tv; - } else { - ptv = NULL; - } - - FD_ZERO(&readfds); - FD_SET(skt, &readfds); - ret = p_select(1, &readfds, NULL, NULL, ptv); - - if (ret < 0) - return -1; /* doom */ - else if (ret == 0) - now = next; - else - now = GETTICKCOUNT(); - - } while (ret == 0); - - select_result((WPARAM) skt, (LPARAM) FD_READ); - - return 0; - } else { - return do_eventsel_loop(INVALID_HANDLE_VALUE); - } -} - -/* - * Read a command line from standard input. - * - * In the presence of WinSock 2, we can use WSAEventSelect to - * mediate between the socket and stdin, meaning we can send - * keepalives and respond to server events even while waiting at - * the PSFTP command prompt. Without WS2, we fall back to a simple - * fgets. - */ -struct command_read_ctx { - HANDLE event; - char *line; -}; - -static DWORD WINAPI command_read_thread(void *param) -{ - struct command_read_ctx *ctx = (struct command_read_ctx *) param; - - ctx->line = fgetline(stdin); - - SetEvent(ctx->event); - - return 0; -} - -char *ssh_sftp_get_cmdline(const char *prompt, bool no_fds_ok) -{ - int ret; - struct command_read_ctx ctx[1]; - DWORD threadid; - HANDLE hThread; - - fputs(prompt, stdout); - fflush(stdout); - - if ((winselcli_unique_socket() == INVALID_SOCKET && no_fds_ok) || - p_WSAEventSelect == NULL) { - return fgetline(stdin); /* very simple */ - } - - /* - * Create a second thread to read from stdin. Process network - * and timing events until it terminates. - */ - ctx->event = CreateEvent(NULL, false, false, NULL); - ctx->line = NULL; - - hThread = CreateThread(NULL, 0, command_read_thread, ctx, 0, &threadid); - if (!hThread) { - CloseHandle(ctx->event); - fprintf(stderr, "Unable to create command input thread\n"); - cleanup_exit(1); - } - - do { - ret = do_eventsel_loop(ctx->event); - - /* do_eventsel_loop can't return an error (unlike - * ssh_sftp_loop_iteration, which can return -1 if select goes - * wrong or if the socket doesn't exist). */ - assert(ret >= 0); - } while (ret == 0); - - CloseHandle(hThread); - CloseHandle(ctx->event); - - return ctx->line; -} - -void platform_psftp_pre_conn_setup(LogPolicy *lp) -{ - if (restricted_acl()) { - lp_eventlog(lp, "Running with restricted process ACL"); - } -} - -/* ---------------------------------------------------------------------- - * Main program. Parse arguments etc. - */ -int main(int argc, char *argv[]) -{ - int ret; - - dll_hijacking_protection(); - - ret = psftp_main(argc, argv); - - return ret; -} diff --git a/WINDOWS/WINSTORE.C b/WINDOWS/WINSTORE.C deleted file mode 100644 index 09e5c028..00000000 --- a/WINDOWS/WINSTORE.C +++ /dev/null @@ -1,873 +0,0 @@ -/* - * winstore.c: Windows-specific implementation of the interface - * defined in storage.h. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <limits.h> -#include <assert.h> -#include "putty.h" -#include "storage.h" - -#include <shlobj.h> -#ifndef CSIDL_APPDATA -#define CSIDL_APPDATA 0x001a -#endif -#ifndef CSIDL_LOCAL_APPDATA -#define CSIDL_LOCAL_APPDATA 0x001c -#endif - -static const char *const reg_jumplist_key = PUTTY_REG_POS "\\Jumplist"; -static const char *const reg_jumplist_value = "Recent sessions"; -static const char *const puttystr = PUTTY_REG_POS "\\Sessions"; - -static bool tried_shgetfolderpath = false; -static HMODULE shell32_module = NULL; -DECL_WINDOWS_FUNCTION(static, HRESULT, SHGetFolderPathA, - (HWND, int, HANDLE, DWORD, LPSTR)); - -struct settings_w { - HKEY sesskey; -}; - -settings_w *open_settings_w(const char *sessionname, char **errmsg) -{ - HKEY subkey1, sesskey; - int ret; - strbuf *sb; - - *errmsg = NULL; - - if (!sessionname || !*sessionname) - sessionname = "Default Settings"; - - sb = strbuf_new(); - escape_registry_key(sessionname, sb); - - ret = RegCreateKey(HKEY_CURRENT_USER, puttystr, &subkey1); - if (ret != ERROR_SUCCESS) { - strbuf_free(sb); - *errmsg = dupprintf("Unable to create registry key\n" - "HKEY_CURRENT_USER\\%s", puttystr); - return NULL; - } - ret = RegCreateKey(subkey1, sb->s, &sesskey); - RegCloseKey(subkey1); - if (ret != ERROR_SUCCESS) { - *errmsg = dupprintf("Unable to create registry key\n" - "HKEY_CURRENT_USER\\%s\\%s", puttystr, sb->s); - strbuf_free(sb); - return NULL; - } - strbuf_free(sb); - - settings_w *toret = snew(settings_w); - toret->sesskey = sesskey; - return toret; -} - -void write_setting_s(settings_w *handle, const char *key, const char *value) -{ - if (handle) - RegSetValueEx(handle->sesskey, key, 0, REG_SZ, (CONST BYTE *)value, - 1 + strlen(value)); -} - -void write_setting_i(settings_w *handle, const char *key, int value) -{ - if (handle) - RegSetValueEx(handle->sesskey, key, 0, REG_DWORD, - (CONST BYTE *) &value, sizeof(value)); -} - -void close_settings_w(settings_w *handle) -{ - RegCloseKey(handle->sesskey); - sfree(handle); -} - -struct settings_r { - HKEY sesskey; -}; - -settings_r *open_settings_r(const char *sessionname) -{ - HKEY subkey1, sesskey; - strbuf *sb; - - if (!sessionname || !*sessionname) - sessionname = "Default Settings"; - - sb = strbuf_new(); - escape_registry_key(sessionname, sb); - - if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &subkey1) != ERROR_SUCCESS) { - sesskey = NULL; - } else { - if (RegOpenKey(subkey1, sb->s, &sesskey) != ERROR_SUCCESS) { - sesskey = NULL; - } - RegCloseKey(subkey1); - } - - strbuf_free(sb); - - if (!sesskey) - return NULL; - - settings_r *toret = snew(settings_r); - toret->sesskey = sesskey; - return toret; -} - -char *read_setting_s(settings_r *handle, const char *key) -{ - DWORD type, allocsize, size; - char *ret; - - if (!handle) - return NULL; - - /* Find out the type and size of the data. */ - if (RegQueryValueEx(handle->sesskey, key, 0, - &type, NULL, &size) != ERROR_SUCCESS || - type != REG_SZ) - return NULL; - - allocsize = size+1; /* allow for an extra NUL if needed */ - ret = snewn(allocsize, char); - if (RegQueryValueEx(handle->sesskey, key, 0, - &type, (BYTE *)ret, &size) != ERROR_SUCCESS || - type != REG_SZ) { - sfree(ret); - return NULL; - } - assert(size < allocsize); - ret[size] = '\0'; /* add an extra NUL in case RegQueryValueEx - * didn't supply one */ - - return ret; -} - -int read_setting_i(settings_r *handle, const char *key, int defvalue) -{ - DWORD type, val, size; - size = sizeof(val); - - if (!handle || - RegQueryValueEx(handle->sesskey, key, 0, &type, - (BYTE *) &val, &size) != ERROR_SUCCESS || - size != sizeof(val) || type != REG_DWORD) - return defvalue; - else - return val; -} - -FontSpec *read_setting_fontspec(settings_r *handle, const char *name) -{ - char *settingname; - char *fontname; - FontSpec *ret; - int isbold, height, charset; - - fontname = read_setting_s(handle, name); - if (!fontname) - return NULL; - - settingname = dupcat(name, "IsBold"); - isbold = read_setting_i(handle, settingname, -1); - sfree(settingname); - if (isbold == -1) { - sfree(fontname); - return NULL; - } - - settingname = dupcat(name, "CharSet"); - charset = read_setting_i(handle, settingname, -1); - sfree(settingname); - if (charset == -1) { - sfree(fontname); - return NULL; - } - - settingname = dupcat(name, "Height"); - height = read_setting_i(handle, settingname, INT_MIN); - sfree(settingname); - if (height == INT_MIN) { - sfree(fontname); - return NULL; - } - - ret = fontspec_new(fontname, isbold, height, charset); - sfree(fontname); - return ret; -} - -void write_setting_fontspec(settings_w *handle, - const char *name, FontSpec *font) -{ - char *settingname; - - write_setting_s(handle, name, font->name); - settingname = dupcat(name, "IsBold"); - write_setting_i(handle, settingname, font->isbold); - sfree(settingname); - settingname = dupcat(name, "CharSet"); - write_setting_i(handle, settingname, font->charset); - sfree(settingname); - settingname = dupcat(name, "Height"); - write_setting_i(handle, settingname, font->height); - sfree(settingname); -} - -Filename *read_setting_filename(settings_r *handle, const char *name) -{ - char *tmp = read_setting_s(handle, name); - if (tmp) { - Filename *ret = filename_from_str(tmp); - sfree(tmp); - return ret; - } else - return NULL; -} - -void write_setting_filename(settings_w *handle, - const char *name, Filename *result) -{ - write_setting_s(handle, name, result->path); -} - -void close_settings_r(settings_r *handle) -{ - if (handle) { - RegCloseKey(handle->sesskey); - sfree(handle); - } -} - -void del_settings(const char *sessionname) -{ - HKEY subkey1; - strbuf *sb; - - if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &subkey1) != ERROR_SUCCESS) - return; - - sb = strbuf_new(); - escape_registry_key(sessionname, sb); - RegDeleteKey(subkey1, sb->s); - strbuf_free(sb); - - RegCloseKey(subkey1); - - remove_session_from_jumplist(sessionname); -} - -struct settings_e { - HKEY key; - int i; -}; - -settings_e *enum_settings_start(void) -{ - settings_e *ret; - HKEY key; - - if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &key) != ERROR_SUCCESS) - return NULL; - - ret = snew(settings_e); - if (ret) { - ret->key = key; - ret->i = 0; - } - - return ret; -} - -bool enum_settings_next(settings_e *e, strbuf *sb) -{ - size_t regbuf_size = MAX_PATH + 1; - char *regbuf = snewn(regbuf_size, char); - bool success; - - while (1) { - DWORD retd = RegEnumKey(e->key, e->i, regbuf, regbuf_size); - if (retd != ERROR_MORE_DATA) { - success = (retd == ERROR_SUCCESS); - break; - } - sgrowarray(regbuf, regbuf_size, regbuf_size); - } - - if (success) - unescape_registry_key(regbuf, sb); - - e->i++; - sfree(regbuf); - return success; -} - -void enum_settings_finish(settings_e *e) -{ - RegCloseKey(e->key); - sfree(e); -} - -static void hostkey_regname(strbuf *sb, const char *hostname, - int port, const char *keytype) -{ - strbuf_catf(sb, "%s@%d:", keytype, port); - escape_registry_key(hostname, sb); -} - -int verify_host_key(const char *hostname, int port, - const char *keytype, const char *key) -{ - char *otherstr; - strbuf *regname; - int len; - HKEY rkey; - DWORD readlen; - DWORD type; - int ret, compare; - - len = 1 + strlen(key); - - /* - * Now read a saved key in from the registry and see what it - * says. - */ - regname = strbuf_new(); - hostkey_regname(regname, hostname, port, keytype); - - if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys", - &rkey) != ERROR_SUCCESS) { - strbuf_free(regname); - return 1; /* key does not exist in registry */ - } - - readlen = len; - otherstr = snewn(len, char); - ret = RegQueryValueEx(rkey, regname->s, NULL, - &type, (BYTE *)otherstr, &readlen); - - if (ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA && - !strcmp(keytype, "rsa")) { - /* - * Key didn't exist. If the key type is RSA, we'll try - * another trick, which is to look up the _old_ key format - * under just the hostname and translate that. - */ - char *justhost = regname->s + 1 + strcspn(regname->s, ":"); - char *oldstyle = snewn(len + 10, char); /* safety margin */ - readlen = len; - ret = RegQueryValueEx(rkey, justhost, NULL, &type, - (BYTE *)oldstyle, &readlen); - - if (ret == ERROR_SUCCESS && type == REG_SZ) { - /* - * The old format is two old-style bignums separated by - * a slash. An old-style bignum is made of groups of - * four hex digits: digits are ordered in sensible - * (most to least significant) order within each group, - * but groups are ordered in silly (least to most) - * order within the bignum. The new format is two - * ordinary C-format hex numbers (0xABCDEFG...XYZ, with - * A nonzero except in the special case 0x0, which - * doesn't appear anyway in RSA keys) separated by a - * comma. All hex digits are lowercase in both formats. - */ - char *p = otherstr; - char *q = oldstyle; - int i, j; - - for (i = 0; i < 2; i++) { - int ndigits, nwords; - *p++ = '0'; - *p++ = 'x'; - ndigits = strcspn(q, "/"); /* find / or end of string */ - nwords = ndigits / 4; - /* now trim ndigits to remove leading zeros */ - while (q[(ndigits - 1) ^ 3] == '0' && ndigits > 1) - ndigits--; - /* now move digits over to new string */ - for (j = 0; j < ndigits; j++) - p[ndigits - 1 - j] = q[j ^ 3]; - p += ndigits; - q += nwords * 4; - if (*q) { - q++; /* eat the slash */ - *p++ = ','; /* add a comma */ - } - *p = '\0'; /* terminate the string */ - } - - /* - * Now _if_ this key matches, we'll enter it in the new - * format. If not, we'll assume something odd went - * wrong, and hyper-cautiously do nothing. - */ - if (!strcmp(otherstr, key)) - RegSetValueEx(rkey, regname->s, 0, REG_SZ, (BYTE *)otherstr, - strlen(otherstr) + 1); - } - - sfree(oldstyle); - } - - RegCloseKey(rkey); - - compare = strcmp(otherstr, key); - - sfree(otherstr); - strbuf_free(regname); - - if (ret == ERROR_MORE_DATA || - (ret == ERROR_SUCCESS && type == REG_SZ && compare)) - return 2; /* key is different in registry */ - else if (ret != ERROR_SUCCESS || type != REG_SZ) - return 1; /* key does not exist in registry */ - else - return 0; /* key matched OK in registry */ -} - -bool have_ssh_host_key(const char *hostname, int port, - const char *keytype) -{ - /* - * If we have a host key, verify_host_key will return 0 or 2. - * If we don't have one, it'll return 1. - */ - return verify_host_key(hostname, port, keytype, "") != 1; -} - -void store_host_key(const char *hostname, int port, - const char *keytype, const char *key) -{ - strbuf *regname; - HKEY rkey; - - regname = strbuf_new(); - hostkey_regname(regname, hostname, port, keytype); - - if (RegCreateKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys", - &rkey) == ERROR_SUCCESS) { - RegSetValueEx(rkey, regname->s, 0, REG_SZ, - (BYTE *)key, strlen(key) + 1); - RegCloseKey(rkey); - } /* else key does not exist in registry */ - - strbuf_free(regname); -} - -/* - * Open (or delete) the random seed file. - */ -enum { DEL, OPEN_R, OPEN_W }; -static bool try_random_seed(char const *path, int action, HANDLE *ret) -{ - if (action == DEL) { - if (!DeleteFile(path) && GetLastError() != ERROR_FILE_NOT_FOUND) { - nonfatal("Unable to delete '%s': %s", path, - win_strerror(GetLastError())); - } - *ret = INVALID_HANDLE_VALUE; - return false; /* so we'll do the next ones too */ - } - - *ret = CreateFile(path, - action == OPEN_W ? GENERIC_WRITE : GENERIC_READ, - action == OPEN_W ? 0 : (FILE_SHARE_READ | - FILE_SHARE_WRITE), - NULL, - action == OPEN_W ? CREATE_ALWAYS : OPEN_EXISTING, - action == OPEN_W ? FILE_ATTRIBUTE_NORMAL : 0, - NULL); - - return (*ret != INVALID_HANDLE_VALUE); -} - -static bool try_random_seed_and_free(char *path, int action, HANDLE *hout) -{ - bool retd = try_random_seed(path, action, hout); - sfree(path); - return retd; -} - -static HANDLE access_random_seed(int action) -{ - HKEY rkey; - HANDLE rethandle; - - /* - * Iterate over a selection of possible random seed paths until - * we find one that works. - * - * We do this iteration separately for reading and writing, - * meaning that we will automatically migrate random seed files - * if a better location becomes available (by reading from the - * best location in which we actually find one, and then - * writing to the best location in which we can _create_ one). - */ - - /* - * First, try the location specified by the user in the - * Registry, if any. - */ - { - char regpath[MAX_PATH + 1]; - DWORD type, size = sizeof(regpath); - if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS, &rkey) == - ERROR_SUCCESS) { - int ret = RegQueryValueEx(rkey, "RandSeedFile", - 0, &type, (BYTE *)regpath, &size); - RegCloseKey(rkey); - if (ret == ERROR_SUCCESS && type == REG_SZ && - try_random_seed(regpath, action, &rethandle)) - return rethandle; - } - } - - /* - * Next, try the user's local Application Data directory, - * followed by their non-local one. This is found using the - * SHGetFolderPath function, which won't be present on all - * versions of Windows. - */ - if (!tried_shgetfolderpath) { - /* This is likely only to bear fruit on systems with IE5+ - * installed, or WinMe/2K+. There is some faffing with - * SHFOLDER.DLL we could do to try to find an equivalent - * on older versions of Windows if we cared enough. - * However, the invocation below requires IE5+ anyway, - * so stuff that. */ - shell32_module = load_system32_dll("shell32.dll"); - GET_WINDOWS_FUNCTION(shell32_module, SHGetFolderPathA); - tried_shgetfolderpath = true; - } - if (p_SHGetFolderPathA) { - char profile[MAX_PATH + 1]; - if (SUCCEEDED(p_SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, - NULL, SHGFP_TYPE_CURRENT, profile)) && - try_random_seed_and_free(dupcat(profile, "\\PUTTY.RND"), - action, &rethandle)) - return rethandle; - - if (SUCCEEDED(p_SHGetFolderPathA(NULL, CSIDL_APPDATA, - NULL, SHGFP_TYPE_CURRENT, profile)) && - try_random_seed_and_free(dupcat(profile, "\\PUTTY.RND"), - action, &rethandle)) - return rethandle; - } - - /* - * Failing that, try %HOMEDRIVE%%HOMEPATH% as a guess at the - * user's home directory. - */ - { - char drv[MAX_PATH], path[MAX_PATH]; - - DWORD drvlen = GetEnvironmentVariable("HOMEDRIVE", drv, sizeof(drv)); - DWORD pathlen = GetEnvironmentVariable("HOMEPATH", path, sizeof(path)); - - /* We permit %HOMEDRIVE% to expand to an empty string, but if - * %HOMEPATH% does that, we abort the attempt. Same if either - * variable overflows its buffer. */ - if (drvlen == 0) - drv[0] = '\0'; - - if (drvlen < lenof(drv) && pathlen < lenof(path) && pathlen > 0 && - try_random_seed_and_free( - dupcat(drv, path, "\\PUTTY.RND"), action, &rethandle)) - return rethandle; - } - - /* - * And finally, fall back to C:\WINDOWS. - */ - { - char windir[MAX_PATH]; - DWORD len = GetWindowsDirectory(windir, sizeof(windir)); - if (len < lenof(windir) && - try_random_seed_and_free( - dupcat(windir, "\\PUTTY.RND"), action, &rethandle)) - return rethandle; - } - - /* - * If even that failed, give up. - */ - return INVALID_HANDLE_VALUE; -} - -void read_random_seed(noise_consumer_t consumer) -{ - HANDLE seedf = access_random_seed(OPEN_R); - - if (seedf != INVALID_HANDLE_VALUE) { - while (1) { - char buf[1024]; - DWORD len; - - if (ReadFile(seedf, buf, sizeof(buf), &len, NULL) && len) - consumer(buf, len); - else - break; - } - CloseHandle(seedf); - } -} - -void write_random_seed(void *data, int len) -{ - HANDLE seedf = access_random_seed(OPEN_W); - - if (seedf != INVALID_HANDLE_VALUE) { - DWORD lenwritten; - - WriteFile(seedf, data, len, &lenwritten, NULL); - CloseHandle(seedf); - } -} - -/* - * Internal function supporting the jump list registry code. All the - * functions to add, remove and read the list have substantially - * similar content, so this is a generalisation of all of them which - * transforms the list in the registry by prepending 'add' (if - * non-null), removing 'rem' from what's left (if non-null), and - * returning the resulting concatenated list of strings in 'out' (if - * non-null). - */ -static int transform_jumplist_registry - (const char *add, const char *rem, char **out) -{ - int ret; - HKEY pjumplist_key; - DWORD type; - DWORD value_length; - char *old_value, *new_value; - char *piterator_old, *piterator_new, *piterator_tmp; - - ret = RegCreateKeyEx(HKEY_CURRENT_USER, reg_jumplist_key, 0, NULL, - REG_OPTION_NON_VOLATILE, (KEY_READ | KEY_WRITE), NULL, - &pjumplist_key, NULL); - if (ret != ERROR_SUCCESS) { - return JUMPLISTREG_ERROR_KEYOPENCREATE_FAILURE; - } - - /* Get current list of saved sessions in the registry. */ - value_length = 200; - old_value = snewn(value_length, char); - ret = RegQueryValueEx(pjumplist_key, reg_jumplist_value, NULL, &type, - (BYTE *)old_value, &value_length); - /* When the passed buffer is too small, ERROR_MORE_DATA is - * returned and the required size is returned in the length - * argument. */ - if (ret == ERROR_MORE_DATA) { - sfree(old_value); - old_value = snewn(value_length, char); - ret = RegQueryValueEx(pjumplist_key, reg_jumplist_value, NULL, &type, - (BYTE *)old_value, &value_length); - } - - if (ret == ERROR_FILE_NOT_FOUND) { - /* Value doesn't exist yet. Start from an empty value. */ - *old_value = '\0'; - *(old_value + 1) = '\0'; - } else if (ret != ERROR_SUCCESS) { - /* Some non-recoverable error occurred. */ - sfree(old_value); - RegCloseKey(pjumplist_key); - return JUMPLISTREG_ERROR_VALUEREAD_FAILURE; - } else if (type != REG_MULTI_SZ) { - /* The value present in the registry has the wrong type: we - * try to delete it and start from an empty value. */ - ret = RegDeleteValue(pjumplist_key, reg_jumplist_value); - if (ret != ERROR_SUCCESS) { - sfree(old_value); - RegCloseKey(pjumplist_key); - return JUMPLISTREG_ERROR_VALUEREAD_FAILURE; - } - - *old_value = '\0'; - *(old_value + 1) = '\0'; - } - - /* Check validity of registry data: REG_MULTI_SZ value must end - * with \0\0. */ - piterator_tmp = old_value; - while (((piterator_tmp - old_value) < (value_length - 1)) && - !(*piterator_tmp == '\0' && *(piterator_tmp+1) == '\0')) { - ++piterator_tmp; - } - - if ((piterator_tmp - old_value) >= (value_length-1)) { - /* Invalid value. Start from an empty value. */ - *old_value = '\0'; - *(old_value + 1) = '\0'; - } - - /* - * Modify the list, if we're modifying. - */ - if (add || rem) { - /* Walk through the existing list and construct the new list of - * saved sessions. */ - new_value = snewn(value_length + (add ? strlen(add) + 1 : 0), char); - piterator_new = new_value; - piterator_old = old_value; - - /* First add the new item to the beginning of the list. */ - if (add) { - strcpy(piterator_new, add); - piterator_new += strlen(piterator_new) + 1; - } - /* Now add the existing list, taking care to leave out the removed - * item, if it was already in the existing list. */ - while (*piterator_old != '\0') { - if (!rem || strcmp(piterator_old, rem) != 0) { - /* Check if this is a valid session, otherwise don't add. */ - settings_r *psettings_tmp = open_settings_r(piterator_old); - if (psettings_tmp != NULL) { - close_settings_r(psettings_tmp); - strcpy(piterator_new, piterator_old); - piterator_new += strlen(piterator_new) + 1; - } - } - piterator_old += strlen(piterator_old) + 1; - } - *piterator_new = '\0'; - ++piterator_new; - - /* Save the new list to the registry. */ - ret = RegSetValueEx(pjumplist_key, reg_jumplist_value, 0, REG_MULTI_SZ, - (BYTE *)new_value, piterator_new - new_value); - - sfree(old_value); - old_value = new_value; - } else - ret = ERROR_SUCCESS; - - /* - * Either return or free the result. - */ - if (out && ret == ERROR_SUCCESS) - *out = old_value; - else - sfree(old_value); - - /* Clean up and return. */ - RegCloseKey(pjumplist_key); - - if (ret != ERROR_SUCCESS) { - return JUMPLISTREG_ERROR_VALUEWRITE_FAILURE; - } else { - return JUMPLISTREG_OK; - } -} - -/* Adds a new entry to the jumplist entries in the registry. */ -int add_to_jumplist_registry(const char *item) -{ - return transform_jumplist_registry(item, item, NULL); -} - -/* Removes an item from the jumplist entries in the registry. */ -int remove_from_jumplist_registry(const char *item) -{ - return transform_jumplist_registry(NULL, item, NULL); -} - -/* Returns the jumplist entries from the registry. Caller must free - * the returned pointer. */ -char *get_jumplist_registry_entries (void) -{ - char *list_value; - - if (transform_jumplist_registry(NULL,NULL,&list_value) != JUMPLISTREG_OK) { - list_value = snewn(2, char); - *list_value = '\0'; - *(list_value + 1) = '\0'; - } - return list_value; -} - -/* - * Recursively delete a registry key and everything under it. - */ -static void registry_recursive_remove(HKEY key) -{ - DWORD i; - char name[MAX_PATH + 1]; - HKEY subkey; - - i = 0; - while (RegEnumKey(key, i, name, sizeof(name)) == ERROR_SUCCESS) { - if (RegOpenKey(key, name, &subkey) == ERROR_SUCCESS) { - registry_recursive_remove(subkey); - RegCloseKey(subkey); - } - RegDeleteKey(key, name); - } -} - -void cleanup_all(void) -{ - HKEY key; - int ret; - char name[MAX_PATH + 1]; - - /* ------------------------------------------------------------ - * Wipe out the random seed file, in all of its possible - * locations. - */ - access_random_seed(DEL); - - /* ------------------------------------------------------------ - * Ask Windows to delete any jump list information associated - * with this installation of PuTTY. - */ - clear_jumplist(); - - /* ------------------------------------------------------------ - * Destroy all registry information associated with PuTTY. - */ - - /* - * Open the main PuTTY registry key and remove everything in it. - */ - if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS, &key) == - ERROR_SUCCESS) { - registry_recursive_remove(key); - RegCloseKey(key); - } - /* - * Now open the parent key and remove the PuTTY main key. Once - * we've done that, see if the parent key has any other - * children. - */ - if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_PARENT, - &key) == ERROR_SUCCESS) { - RegDeleteKey(key, PUTTY_REG_PARENT_CHILD); - ret = RegEnumKey(key, 0, name, sizeof(name)); - RegCloseKey(key); - /* - * If the parent key had no other children, we must delete - * it in its turn. That means opening the _grandparent_ - * key. - */ - if (ret != ERROR_SUCCESS) { - if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_GPARENT, - &key) == ERROR_SUCCESS) { - RegDeleteKey(key, PUTTY_REG_GPARENT_CHILD); - RegCloseKey(key); - } - } - } - /* - * Now we're done. - */ -} diff --git a/WINDOWS/WINSTUFF.H b/WINDOWS/WINSTUFF.H deleted file mode 100644 index c0df5a31..00000000 --- a/WINDOWS/WINSTUFF.H +++ /dev/null @@ -1,704 +0,0 @@ -/* - * winstuff.h: Windows-specific inter-module stuff. - */ - -#ifndef PUTTY_WINSTUFF_H -#define PUTTY_WINSTUFF_H - -#ifndef AUTO_WINSOCK -#include <winsock2.h> -#endif -#include <windows.h> -#include <stdio.h> /* for FILENAME_MAX */ - -/* We use uintptr_t for Win32/Win64 portability, so we should in - * principle include stdint.h, which defines it according to the C - * standard. But older versions of Visual Studio - including the one - * used for official PuTTY builds as of 2015-09-28 - don't provide - * stdint.h at all, but do (non-standardly) define uintptr_t in - * stddef.h. So here we try to make sure _some_ standard header is - * included which defines uintptr_t. */ -#include <stddef.h> -#if !defined _MSC_VER || _MSC_VER >= 1600 || defined __clang__ -#include <stdint.h> -#endif - -#include "defs.h" -#include "marshal.h" - -#include "tree234.h" - -#include "winhelp.h" - -#if defined _M_IX86 || defined _M_AMD64 -#define BUILDINFO_PLATFORM "x86 Windows" -#elif defined _M_ARM || defined _M_ARM64 -#define BUILDINFO_PLATFORM "Arm Windows" -#else -#define BUILDINFO_PLATFORM "Windows" -#endif - -struct Filename { - char *path; -}; -static inline FILE *f_open(const Filename *filename, const char *mode, - bool isprivate) -{ - return fopen(filename->path, mode); -} - -struct FontSpec { - char *name; - bool isbold; - int height; - int charset; -}; -struct FontSpec *fontspec_new( - const char *name, bool bold, int height, int charset); - -#ifndef CLEARTYPE_QUALITY -#define CLEARTYPE_QUALITY 5 -#endif -#define FONT_QUALITY(fq) ( \ - (fq) == FQ_DEFAULT ? DEFAULT_QUALITY : \ - (fq) == FQ_ANTIALIASED ? ANTIALIASED_QUALITY : \ - (fq) == FQ_NONANTIALIASED ? NONANTIALIASED_QUALITY : \ - CLEARTYPE_QUALITY) - -#define PLATFORM_IS_UTF16 /* enable UTF-16 processing when exchanging - * wchar_t strings with environment */ - -#define PLATFORM_CLIPBOARDS(X) \ - X(CLIP_SYSTEM, "system clipboard") \ - /* end of list */ - -/* - * Where we can, we use GetWindowLongPtr and friends because they're - * more useful on 64-bit platforms, but they're a relatively recent - * innovation, missing from VC++ 6 and older MinGW. Degrade nicely. - * (NB that on some systems, some of these things are available but - * not others...) - */ - -#ifndef GCLP_HCURSOR -/* GetClassLongPtr and friends */ -#undef GetClassLongPtr -#define GetClassLongPtr GetClassLong -#undef SetClassLongPtr -#define SetClassLongPtr SetClassLong -#define GCLP_HCURSOR GCL_HCURSOR -/* GetWindowLongPtr and friends */ -#undef GetWindowLongPtr -#define GetWindowLongPtr GetWindowLong -#undef SetWindowLongPtr -#define SetWindowLongPtr SetWindowLong -#undef GWLP_USERDATA -#define GWLP_USERDATA GWL_USERDATA -#undef DWLP_MSGRESULT -#define DWLP_MSGRESULT DWL_MSGRESULT -/* Since we've clobbered the above functions, we should clobber the - * associated type regardless of whether it's defined. */ -#undef LONG_PTR -#define LONG_PTR LONG -#endif - -#define BOXFLAGS DLGWINDOWEXTRA -#define BOXRESULT (DLGWINDOWEXTRA + sizeof(LONG_PTR)) -#define DF_END 0x0001 - -#ifndef __WINE__ -/* Up-to-date Windows headers warn that the unprefixed versions of - * these names are deprecated. */ -#define stricmp _stricmp -#define strnicmp _strnicmp -#else -/* Compiling with winegcc, _neither_ version of these functions - * exists. Use the POSIX names. */ -#define stricmp strcasecmp -#define strnicmp strncasecmp -#endif - -#define BROKEN_PIPE_ERROR_CODE ERROR_BROKEN_PIPE /* used in sshshare.c */ - -/* - * Dynamically linked functions. These come in two flavours: - * - * - GET_WINDOWS_FUNCTION does not expose "name" to the preprocessor, - * so will always dynamically link against exactly what is specified - * in "name". If you're not sure, use this one. - * - * - GET_WINDOWS_FUNCTION_PP allows "name" to be redirected via - * preprocessor definitions like "#define foo bar"; this is principally - * intended for the ANSI/Unicode DoSomething/DoSomethingA/DoSomethingW. - * If your function has an argument of type "LPTSTR" or similar, this - * is the variant to use. - * (However, it can't always be used, as it trips over more complicated - * macro trickery such as the WspiapiGetAddrInfo wrapper for getaddrinfo.) - * - * (DECL_WINDOWS_FUNCTION works with both these variants.) - */ -#define DECL_WINDOWS_FUNCTION(linkage, rettype, name, params) \ - typedef rettype (WINAPI *t_##name) params; \ - linkage t_##name p_##name -/* If you DECL_WINDOWS_FUNCTION as extern in a header file, use this to - * define the function pointer in a source file */ -#define DEF_WINDOWS_FUNCTION(name) t_##name p_##name -#define STR1(x) #x -#define STR(x) STR1(x) -#define GET_WINDOWS_FUNCTION_PP(module, name) \ - TYPECHECK((t_##name)NULL == name, \ - (p_##name = module ? \ - (t_##name) GetProcAddress(module, STR(name)) : NULL)) -#define GET_WINDOWS_FUNCTION(module, name) \ - TYPECHECK((t_##name)NULL == name, \ - (p_##name = module ? \ - (t_##name) GetProcAddress(module, #name) : NULL)) -#define GET_WINDOWS_FUNCTION_NO_TYPECHECK(module, name) \ - (p_##name = module ? \ - (t_##name) GetProcAddress(module, #name) : NULL) - -#define PUTTY_REG_POS "Software\\SimonTatham\\PuTTY" -#define PUTTY_REG_PARENT "Software\\SimonTatham" -#define PUTTY_REG_PARENT_CHILD "PuTTY" -#define PUTTY_REG_GPARENT "Software" -#define PUTTY_REG_GPARENT_CHILD "SimonTatham" - -/* Result values for the jumplist registry functions. */ -#define JUMPLISTREG_OK 0 -#define JUMPLISTREG_ERROR_INVALID_PARAMETER 1 -#define JUMPLISTREG_ERROR_KEYOPENCREATE_FAILURE 2 -#define JUMPLISTREG_ERROR_VALUEREAD_FAILURE 3 -#define JUMPLISTREG_ERROR_VALUEWRITE_FAILURE 4 -#define JUMPLISTREG_ERROR_INVALID_VALUE 5 - -#define PUTTY_CHM_FILE "putty.chm" - -#define GETTICKCOUNT GetTickCount -#define CURSORBLINK GetCaretBlinkTime() -#define TICKSPERSEC 1000 /* GetTickCount returns milliseconds */ - -#define DEFAULT_CODEPAGE CP_ACP -#define USES_VTLINE_HACK - -#ifndef NO_GSSAPI -/* - * GSS-API stuff - */ -#define GSS_CC CALLBACK -/* -typedef struct Ssh_gss_buf { - size_t length; - char *value; -} Ssh_gss_buf; - -#define SSH_GSS_EMPTY_BUF (Ssh_gss_buf) {0,NULL} -typedef void *Ssh_gss_name; -*/ -#endif - -/* - * The all-important instance handle, saved from WinMain in every GUI - * program and exported for other GUI code to pass back to the Windows - * API. - */ -extern HINSTANCE hinst; - -/* - * Help file stuff in winhelp.c. - */ -void init_help(void); -void shutdown_help(void); -bool has_help(void); -void launch_help(HWND hwnd, const char *topic); -void quit_help(HWND hwnd); -int has_embedded_chm(void); /* 1 = yes, 0 = no, -1 = N/A */ - -/* - * GUI seat methods in windlg.c, so that the vtable definition in - * window.c can refer to them. - */ -int win_seat_verify_ssh_host_key( - Seat *seat, const char *host, int port, const char *keytype, - char *keystr, const char *keydisp, char **key_fingerprints, - void (*callback)(void *ctx, int result), void *ctx); -int win_seat_confirm_weak_crypto_primitive( - Seat *seat, const char *algtype, const char *algname, - void (*callback)(void *ctx, int result), void *ctx); -int win_seat_confirm_weak_cached_hostkey( - Seat *seat, const char *algname, const char *betteralgs, - void (*callback)(void *ctx, int result), void *ctx); - -/* - * Windows-specific clipboard helper function shared with windlg.c, - * which takes the data string in the system code page instead of - * Unicode. - */ -void write_aclip(int clipboard, char *, int, bool); - -#define WM_NETEVENT (WM_APP + 5) - -/* - * On Windows, we send MA_2CLK as the only event marking the second - * press of a mouse button. Compare unix.h. - */ -#define MULTICLICK_ONLY_EVENT 1 - -/* - * On Windows, data written to the clipboard must be NUL-terminated. - */ -#define SELECTION_NUL_TERMINATED 1 - -/* - * On Windows, copying to the clipboard terminates lines with CRLF. - */ -#define SEL_NL { 13, 10 } - -/* - * sk_getxdmdata() does not exist under Windows (not that I - * couldn't write it if I wanted to, but I haven't bothered), so - * it's a macro which always returns NULL. With any luck this will - * cause the compiler to notice it can optimise away the - * implementation of XDM-AUTHORIZATION-1 in x11fwd.c :-) - */ -#define sk_getxdmdata(socket, lenp) (NULL) - -/* - * File-selector filter strings used in the config box. On Windows, - * these strings are of exactly the type needed to go in - * `lpstrFilter' in an OPENFILENAME structure. - */ -#define FILTER_KEY_FILES ("PuTTY Private Key Files (*.ppk)\0*.ppk\0" \ - "All Files (*.*)\0*\0\0\0") -#define FILTER_WAVE_FILES ("Wave Files (*.wav)\0*.WAV\0" \ - "All Files (*.*)\0*\0\0\0") -#define FILTER_DYNLIB_FILES ("Dynamic Library Files (*.dll)\0*.dll\0" \ - "All Files (*.*)\0*\0\0\0") - -/* - * Exports from winnet.c. - */ -/* Report an event notification from WSA*Select */ -void select_result(WPARAM, LPARAM); -/* Enumerate all currently live OS-level SOCKETs */ -SOCKET first_socket(int *); -SOCKET next_socket(int *); -/* Ask winnet.c whether we currently want to try to write to a SOCKET */ -bool socket_writable(SOCKET skt); -/* Force a refresh of the SOCKET list by re-calling do_select for each one */ -void socket_reselect_all(void); -/* Make a SockAddr which just holds a named pipe address. */ -SockAddr *sk_namedpipe_addr(const char *pipename); -/* Turn a WinSock error code into a string. */ -const char *winsock_error_string(int error); - -/* - * winnet.c dynamically loads WinSock 2 or WinSock 1 depending on - * what it can get, which means any WinSock routines used outside - * that module must be exported from it as function pointers. So - * here they are. - */ -DECL_WINDOWS_FUNCTION(extern, int, WSAAsyncSelect, - (SOCKET, HWND, u_int, long)); -DECL_WINDOWS_FUNCTION(extern, int, WSAEventSelect, - (SOCKET, WSAEVENT, long)); -DECL_WINDOWS_FUNCTION(extern, int, WSAGetLastError, (void)); -DECL_WINDOWS_FUNCTION(extern, int, WSAEnumNetworkEvents, - (SOCKET, WSAEVENT, LPWSANETWORKEVENTS)); -#ifdef NEED_DECLARATION_OF_SELECT -/* This declaration is protected by an ifdef for the sake of building - * against winelib, in which you have to include winsock2.h before - * stdlib.h so that the right fd_set type gets defined. It would be a - * pain to do that throughout this codebase, so instead I arrange that - * only a modules actually needing to use (or define, or initialise) - * this function pointer will see its declaration, and _those_ modules - * - which will be Windows-specific anyway - can take more care. */ -DECL_WINDOWS_FUNCTION(extern, int, select, - (int, fd_set FAR *, fd_set FAR *, - fd_set FAR *, const struct timeval FAR *)); -#endif - -/* - * Implemented differently depending on the client of winnet.c, and - * called by winnet.c to turn on or off WSA*Select for a given socket. - */ -const char *do_select(SOCKET skt, bool enable); - -/* - * Exports from winselgui.c and winselcli.c, each of which provides an - * implementation of do_select. - */ -void winselgui_set_hwnd(HWND hwnd); -void winselgui_clear_hwnd(void); - -void winselcli_setup(void); -SOCKET winselcli_unique_socket(void); -extern HANDLE winselcli_event; - -/* - * Network-subsystem-related functions provided in other Windows modules. - */ -Socket *make_handle_socket(HANDLE send_H, HANDLE recv_H, HANDLE stderr_H, - Plug *plug, bool overlapped); /* winhsock */ -Socket *new_named_pipe_client(const char *pipename, Plug *plug); /* winnpc */ -Socket *new_named_pipe_listener(const char *pipename, Plug *plug); /* winnps */ - -/* A lower-level function in winnpc.c, which does most of the work of - * new_named_pipe_client (including checking the ownership of what - * it's connected to), but returns a plain HANDLE instead of wrapping - * it into a Socket. */ -HANDLE connect_to_named_pipe(const char *pipename, char **err); - -/* - * Exports from winctrls.c. - */ - -struct ctlpos { - HWND hwnd; - WPARAM font; - int dlu4inpix; - int ypos, width; - int xoff; - int boxystart, boxid; - char *boxtext; -}; -void init_common_controls(void); /* also does some DLL-loading */ - -/* - * Exports from winutils.c. - */ -typedef struct filereq_tag filereq; /* cwd for file requester */ -bool request_file(filereq *state, OPENFILENAME *of, bool preserve, bool save); -filereq *filereq_new(void); -void filereq_free(filereq *state); -void pgp_fingerprints_msgbox(HWND owner); -int message_box(HWND owner, LPCTSTR text, LPCTSTR caption, - DWORD style, DWORD helpctxid); -void MakeDlgItemBorderless(HWND parent, int id); -char *GetDlgItemText_alloc(HWND hwnd, int id); -void split_into_argv(char *, int *, char ***, char ***); - -/* - * Private structure for prefslist state. Only in the header file - * so that we can delegate allocation to callers. - */ -struct prefslist { - int listid, upbid, dnbid; - int srcitem; - int dummyitem; - bool dragging; -}; - -/* - * This structure is passed to event handler functions as the `dlg' - * parameter, and hence is passed back to winctrls access functions. - */ -struct dlgparam { - HWND hwnd; /* the hwnd of the dialog box */ - struct winctrls *controltrees[8]; /* can have several of these */ - int nctrltrees; - char *wintitle; /* title of actual window */ - char *errtitle; /* title of error sub-messageboxes */ - void *data; /* data to pass in refresh events */ - union control *focused, *lastfocused; /* which ctrl has focus now/before */ - bool shortcuts[128]; /* track which shortcuts in use */ - bool coloursel_wanted; /* has an event handler asked for - * a colour selector? */ - struct { - unsigned char r, g, b; /* 0-255 */ - bool ok; - } coloursel_result; - tree234 *privdata; /* stores per-control private data */ - bool ended; /* has the dialog been ended? */ - int endresult; /* and if so, what was the result? */ - bool fixed_pitch_fonts; /* are we constrained to fixed fonts? */ -}; - -/* - * Exports from winctrls.c. - */ -void ctlposinit(struct ctlpos *cp, HWND hwnd, - int leftborder, int rightborder, int topborder); -HWND doctl(struct ctlpos *cp, RECT r, - char *wclass, int wstyle, int exstyle, char *wtext, int wid); -void bartitle(struct ctlpos *cp, char *name, int id); -void beginbox(struct ctlpos *cp, char *name, int idbox); -void endbox(struct ctlpos *cp); -void editboxfw(struct ctlpos *cp, bool password, char *text, - int staticid, int editid); -void radioline(struct ctlpos *cp, char *text, int id, int nacross, ...); -void bareradioline(struct ctlpos *cp, int nacross, ...); -void radiobig(struct ctlpos *cp, char *text, int id, ...); -void checkbox(struct ctlpos *cp, char *text, int id); -void statictext(struct ctlpos *cp, char *text, int lines, int id); -void staticbtn(struct ctlpos *cp, char *stext, int sid, - char *btext, int bid); -void static2btn(struct ctlpos *cp, char *stext, int sid, - char *btext1, int bid1, char *btext2, int bid2); -void staticedit(struct ctlpos *cp, char *stext, - int sid, int eid, int percentedit); -void staticddl(struct ctlpos *cp, char *stext, - int sid, int lid, int percentlist); -void combobox(struct ctlpos *cp, char *text, int staticid, int listid); -void staticpassedit(struct ctlpos *cp, char *stext, - int sid, int eid, int percentedit); -void bigeditctrl(struct ctlpos *cp, char *stext, - int sid, int eid, int lines); -void ersatztab(struct ctlpos *cp, char *stext, int sid, int lid, int s2id); -void editbutton(struct ctlpos *cp, char *stext, int sid, - int eid, char *btext, int bid); -void sesssaver(struct ctlpos *cp, char *text, - int staticid, int editid, int listid, ...); -void envsetter(struct ctlpos *cp, char *stext, int sid, - char *e1stext, int e1sid, int e1id, - char *e2stext, int e2sid, int e2id, - int listid, char *b1text, int b1id, char *b2text, int b2id); -void charclass(struct ctlpos *cp, char *stext, int sid, int listid, - char *btext, int bid, int eid, char *s2text, int s2id); -void colouredit(struct ctlpos *cp, char *stext, int sid, int listid, - char *btext, int bid, ...); -void prefslist(struct prefslist *hdl, struct ctlpos *cp, int lines, - char *stext, int sid, int listid, int upbid, int dnbid); -int handle_prefslist(struct prefslist *hdl, - int *array, int maxmemb, - bool is_dlmsg, HWND hwnd, - WPARAM wParam, LPARAM lParam); -void progressbar(struct ctlpos *cp, int id); -void fwdsetter(struct ctlpos *cp, int listid, char *stext, int sid, - char *e1stext, int e1sid, int e1id, - char *e2stext, int e2sid, int e2id, - char *btext, int bid, - char *r1text, int r1id, char *r2text, int r2id); - -void dlg_auto_set_fixed_pitch_flag(dlgparam *dlg); -bool dlg_get_fixed_pitch_flag(dlgparam *dlg); -void dlg_set_fixed_pitch_flag(dlgparam *dlg, bool flag); - -#define MAX_SHORTCUTS_PER_CTRL 16 - -/* - * This structure is what's stored for each `union control' in the - * portable-dialog interface. - */ -struct winctrl { - union control *ctrl; - /* - * The control may have several components at the Windows - * level, with different dialog IDs. To avoid needing N - * separate platformsidectrl structures (which could be stored - * separately in a tree234 so that lookup by ID worked), we - * impose the constraint that those IDs must be in a contiguous - * block. - */ - int base_id; - int num_ids; - /* - * For vertical alignment, the id of a particular representative - * control that has the y-extent of the sensible part of the - * control. - */ - int align_id; - /* - * Remember what keyboard shortcuts were used by this control, - * so that when we remove it again we can take them out of the - * list in the dlgparam. - */ - char shortcuts[MAX_SHORTCUTS_PER_CTRL]; - /* - * Some controls need a piece of allocated memory in which to - * store temporary data about the control. - */ - void *data; -}; -/* - * And this structure holds a set of the above, in two separate - * tree234s so that it can find an item by `union control' or by - * dialog ID. - */ -struct winctrls { - tree234 *byctrl, *byid; -}; -struct controlset; -struct controlbox; - -void winctrl_init(struct winctrls *); -void winctrl_cleanup(struct winctrls *); -void winctrl_add(struct winctrls *, struct winctrl *); -void winctrl_remove(struct winctrls *, struct winctrl *); -struct winctrl *winctrl_findbyctrl(struct winctrls *, union control *); -struct winctrl *winctrl_findbyid(struct winctrls *, int); -struct winctrl *winctrl_findbyindex(struct winctrls *, int); -void winctrl_layout(struct dlgparam *dp, struct winctrls *wc, - struct ctlpos *cp, struct controlset *s, int *id); -bool winctrl_handle_command(struct dlgparam *dp, UINT msg, - WPARAM wParam, LPARAM lParam); -void winctrl_rem_shortcuts(struct dlgparam *dp, struct winctrl *c); -bool winctrl_context_help(struct dlgparam *dp, HWND hwnd, int id); - -void dp_init(struct dlgparam *dp); -void dp_add_tree(struct dlgparam *dp, struct winctrls *tree); -void dp_cleanup(struct dlgparam *dp); - -/* - * Exports from wincfg.c. - */ -void win_setup_config_box(struct controlbox *b, HWND *hwndp, bool has_help, - bool midsession, int protocol); - -/* - * Exports from windlg.c. - */ -void defuse_showwindow(void); -bool do_config(Conf *); -bool do_reconfig(HWND, Conf *, int); -void showeventlog(HWND); -void showabout(HWND); -void force_normal(HWND hwnd); -void modal_about_box(HWND hwnd); -void show_help(HWND hwnd); -HWND event_log_window(void); - -/* - * Exports from winmisc.c. - */ -extern DWORD osMajorVersion, osMinorVersion, osPlatformId; -void init_winver(void); -void dll_hijacking_protection(void); -HMODULE load_system32_dll(const char *libname); -const char *win_strerror(int error); -void restrict_process_acl(void); -bool restricted_acl(void); -void escape_registry_key(const char *in, strbuf *out); -void unescape_registry_key(const char *in, strbuf *out); - -bool is_console_handle(HANDLE); - -/* A few pieces of up-to-date Windows API definition needed for older - * compilers. */ -#ifndef LOAD_LIBRARY_SEARCH_SYSTEM32 -#define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800 -#endif -#ifndef LOAD_LIBRARY_SEARCH_USER_DIRS -#define LOAD_LIBRARY_SEARCH_USER_DIRS 0x00000400 -#endif -#ifndef LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR -#define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR 0x00000100 -#endif -#ifndef DLL_DIRECTORY_COOKIE -typedef PVOID DLL_DIRECTORY_COOKIE; -DECLSPEC_IMPORT DLL_DIRECTORY_COOKIE WINAPI AddDllDirectory (PCWSTR NewDirectory); -#endif - -/* - * Exports from sizetip.c. - */ -void UpdateSizeTip(HWND src, int cx, int cy); -void EnableSizeTip(bool bEnable); - -/* - * Exports from unicode.c. - */ -struct unicode_data; -void init_ucs(Conf *, struct unicode_data *); - -/* - * Exports from winhandl.c. - */ -#define HANDLE_FLAG_OVERLAPPED 1 -#define HANDLE_FLAG_IGNOREEOF 2 -#define HANDLE_FLAG_UNITBUFFER 4 -struct handle; -typedef size_t (*handle_inputfn_t)( - struct handle *h, const void *data, size_t len, int err); -typedef void (*handle_outputfn_t)( - struct handle *h, size_t new_backlog, int err); -struct handle *handle_input_new(HANDLE handle, handle_inputfn_t gotdata, - void *privdata, int flags); -struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata, - void *privdata, int flags); -size_t handle_write(struct handle *h, const void *data, size_t len); -void handle_write_eof(struct handle *h); -HANDLE *handle_get_events(int *nevents); -void handle_free(struct handle *h); -void handle_got_event(HANDLE event); -void handle_unthrottle(struct handle *h, size_t backlog); -size_t handle_backlog(struct handle *h); -void *handle_get_privdata(struct handle *h); -struct handle *handle_add_foreign_event(HANDLE event, - void (*callback)(void *), void *ctx); -/* Analogue of stdio_sink in marshal.h, for a Windows handle */ -struct handle_sink { - struct handle *h; - BinarySink_IMPLEMENTATION; -}; -void handle_sink_init(handle_sink *sink, struct handle *h); - -/* - * Exports from winpgntc.c. - */ -char *agent_named_pipe_name(void); - -/* - * Exports from winser.c. - */ -extern const struct BackendVtable serial_backend; - -/* - * Exports from winjump.c. - */ -#define JUMPLIST_SUPPORTED /* suppress #defines in putty.h */ -void add_session_to_jumplist(const char * const sessionname); -void remove_session_from_jumplist(const char * const sessionname); -void clear_jumplist(void); -bool set_explicit_app_user_model_id(void); - -/* - * Exports from winnoise.c. - */ -bool win_read_random(void *buf, unsigned wanted); /* returns true on success */ - -/* - * Extra functions in winstore.c over and above the interface in - * storage.h. - * - * These functions manipulate the Registry section which mirrors the - * current Windows 7 jump list. (Because the real jump list storage is - * write-only, we need to keep another copy of whatever we put in it, - * so that we can put in a slightly modified version the next time.) - */ - -/* Adds a saved session to the registry jump list mirror. 'item' is a - * string naming a saved session. */ -int add_to_jumplist_registry(const char *item); - -/* Removes an item from the registry jump list mirror. */ -int remove_from_jumplist_registry(const char *item); - -/* Returns the current jump list entries from the registry. Caller - * must free the returned pointer, which points to a contiguous - * sequence of NUL-terminated strings in memory, terminated with an - * empty one. */ -char *get_jumplist_registry_entries(void); - -/* - * Windows clipboard-UI wording. - */ -#define CLIPNAME_IMPLICIT "Last selected text" -#define CLIPNAME_EXPLICIT "System clipboard" -#define CLIPNAME_EXPLICIT_OBJECT "system clipboard" -/* These defaults are the ones PuTTY has historically had */ -#define CLIPUI_DEFAULT_AUTOCOPY true -#define CLIPUI_DEFAULT_MOUSE CLIPUI_EXPLICIT -#define CLIPUI_DEFAULT_INS CLIPUI_EXPLICIT - -/* In winmisc.c */ -char *registry_get_string(HKEY root, const char *path, const char *leaf); - -/* In wincliloop.c */ -typedef bool (*cliloop_pre_t)(void *vctx, const HANDLE **extra_handles, - size_t *n_extra_handles); -typedef bool (*cliloop_post_t)(void *vctx, size_t extra_handle_index); -void cli_main_loop(cliloop_pre_t pre, cliloop_post_t post, void *ctx); -bool cliloop_null_pre(void *vctx, const HANDLE **, size_t *); -bool cliloop_null_post(void *vctx, size_t); - -#endif diff --git a/WINDOWS/WINTIME.C b/WINDOWS/WINTIME.C deleted file mode 100644 index 5fa3b0de..00000000 --- a/WINDOWS/WINTIME.C +++ /dev/null @@ -1,26 +0,0 @@ -/* - * wintime.c - Avoid trouble with time() returning (time_t)-1 on Windows. - */ - -#include "putty.h" -#include <time.h> - -struct tm ltime(void) -{ - SYSTEMTIME st; - struct tm tm; - - memset(&tm, 0, sizeof(tm)); /* in case there are any other fields */ - - GetLocalTime(&st); - tm.tm_sec=st.wSecond; - tm.tm_min=st.wMinute; - tm.tm_hour=st.wHour; - tm.tm_mday=st.wDay; - tm.tm_mon=st.wMonth-1; - tm.tm_year=(st.wYear>=1900?st.wYear-1900:0); - tm.tm_wday=st.wDayOfWeek; - tm.tm_yday=-1; /* GetLocalTime doesn't tell us */ - tm.tm_isdst=0; /* GetLocalTime doesn't tell us */ - return tm; -} diff --git a/WINDOWS/WINUCS.C b/WINDOWS/WINUCS.C deleted file mode 100644 index 9ffff5e9..00000000 --- a/WINDOWS/WINUCS.C +++ /dev/null @@ -1,1213 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <ctype.h> -#include <time.h> -#include <assert.h> - -#include "putty.h" -#include "terminal.h" -#include "misc.h" - -/* Character conversion arrays; they are usually taken from windows, - * the xterm one has the four scanlines that have no unicode 2.0 - * equivalents mapped to their unicode 3.0 locations. - */ -static const WCHAR unitab_xterm_std[32] = { - 0x2666, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, - 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba, - 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c, - 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, 0x0020 -}; - -/* - * If the codepage is non-zero it's a window codepage, zero means use a - * local codepage. The name is always converted to the first of any - * duplicate definitions. - */ - -/* - * Tables for ISO-8859-{1-10,13-16} derived from those downloaded - * 2001-10-02 from <http://www.unicode.org/Public/MAPPINGS/> -- jtn - * Table for ISO-8859-11 derived from same on 2002-11-18. -- bjh21 - */ - -/* XXX: This could be done algorithmically, but I'm not sure it's - * worth the hassle -- jtn */ -/* ISO/IEC 8859-1:1998 (Latin-1, "Western", "West European") */ -static const wchar_t iso_8859_1[] = { - 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, - 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, - 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, - 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, - 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, - 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, - 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, - 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, - 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, - 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, - 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, - 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF -}; - -/* ISO/IEC 8859-2:1999 (Latin-2, "Central European", "East European") */ -static const wchar_t iso_8859_2[] = { - 0x00A0, 0x0104, 0x02D8, 0x0141, 0x00A4, 0x013D, 0x015A, 0x00A7, - 0x00A8, 0x0160, 0x015E, 0x0164, 0x0179, 0x00AD, 0x017D, 0x017B, - 0x00B0, 0x0105, 0x02DB, 0x0142, 0x00B4, 0x013E, 0x015B, 0x02C7, - 0x00B8, 0x0161, 0x015F, 0x0165, 0x017A, 0x02DD, 0x017E, 0x017C, - 0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7, - 0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E, - 0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7, - 0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF, - 0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7, - 0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F, - 0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7, - 0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9 -}; - -/* ISO/IEC 8859-3:1999 (Latin-3, "South European", "Maltese & Esperanto") */ -static const wchar_t iso_8859_3[] = { - 0x00A0, 0x0126, 0x02D8, 0x00A3, 0x00A4, 0xFFFD, 0x0124, 0x00A7, - 0x00A8, 0x0130, 0x015E, 0x011E, 0x0134, 0x00AD, 0xFFFD, 0x017B, - 0x00B0, 0x0127, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x0125, 0x00B7, - 0x00B8, 0x0131, 0x015F, 0x011F, 0x0135, 0x00BD, 0xFFFD, 0x017C, - 0x00C0, 0x00C1, 0x00C2, 0xFFFD, 0x00C4, 0x010A, 0x0108, 0x00C7, - 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, - 0xFFFD, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x0120, 0x00D6, 0x00D7, - 0x011C, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x016C, 0x015C, 0x00DF, - 0x00E0, 0x00E1, 0x00E2, 0xFFFD, 0x00E4, 0x010B, 0x0109, 0x00E7, - 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, - 0xFFFD, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x0121, 0x00F6, 0x00F7, - 0x011D, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x016D, 0x015D, 0x02D9 -}; - -/* ISO/IEC 8859-4:1998 (Latin-4, "North European") */ -static const wchar_t iso_8859_4[] = { - 0x00A0, 0x0104, 0x0138, 0x0156, 0x00A4, 0x0128, 0x013B, 0x00A7, - 0x00A8, 0x0160, 0x0112, 0x0122, 0x0166, 0x00AD, 0x017D, 0x00AF, - 0x00B0, 0x0105, 0x02DB, 0x0157, 0x00B4, 0x0129, 0x013C, 0x02C7, - 0x00B8, 0x0161, 0x0113, 0x0123, 0x0167, 0x014A, 0x017E, 0x014B, - 0x0100, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x012E, - 0x010C, 0x00C9, 0x0118, 0x00CB, 0x0116, 0x00CD, 0x00CE, 0x012A, - 0x0110, 0x0145, 0x014C, 0x0136, 0x00D4, 0x00D5, 0x00D6, 0x00D7, - 0x00D8, 0x0172, 0x00DA, 0x00DB, 0x00DC, 0x0168, 0x016A, 0x00DF, - 0x0101, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x012F, - 0x010D, 0x00E9, 0x0119, 0x00EB, 0x0117, 0x00ED, 0x00EE, 0x012B, - 0x0111, 0x0146, 0x014D, 0x0137, 0x00F4, 0x00F5, 0x00F6, 0x00F7, - 0x00F8, 0x0173, 0x00FA, 0x00FB, 0x00FC, 0x0169, 0x016B, 0x02D9 -}; - -/* ISO/IEC 8859-5:1999 (Latin/Cyrillic) */ -static const wchar_t iso_8859_5[] = { - 0x00A0, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, - 0x0408, 0x0409, 0x040A, 0x040B, 0x040C, 0x00AD, 0x040E, 0x040F, - 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, - 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, - 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, - 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, - 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, - 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, - 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, - 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, - 0x2116, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, - 0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x00A7, 0x045E, 0x045F -}; - -/* ISO/IEC 8859-6:1999 (Latin/Arabic) */ -static const wchar_t iso_8859_6[] = { - 0x00A0, 0xFFFD, 0xFFFD, 0xFFFD, 0x00A4, 0xFFFD, 0xFFFD, 0xFFFD, - 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x060C, 0x00AD, 0xFFFD, 0xFFFD, - 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, - 0xFFFD, 0xFFFD, 0xFFFD, 0x061B, 0xFFFD, 0xFFFD, 0xFFFD, 0x061F, - 0xFFFD, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, - 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, - 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637, - 0x0638, 0x0639, 0x063A, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, - 0x0640, 0x0641, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, - 0x0648, 0x0649, 0x064A, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, - 0x0650, 0x0651, 0x0652, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, - 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD -}; - -/* ISO 8859-7:1987 (Latin/Greek) */ -static const wchar_t iso_8859_7[] = { - 0x00A0, 0x2018, 0x2019, 0x00A3, 0xFFFD, 0xFFFD, 0x00A6, 0x00A7, - 0x00A8, 0x00A9, 0xFFFD, 0x00AB, 0x00AC, 0x00AD, 0xFFFD, 0x2015, - 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x0385, 0x0386, 0x00B7, - 0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F, - 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, - 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, - 0x03A0, 0x03A1, 0xFFFD, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, - 0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF, - 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, - 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, - 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, - 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0xFFFD -}; - -/* ISO/IEC 8859-8:1999 (Latin/Hebrew) */ -static const wchar_t iso_8859_8[] = { - 0x00A0, 0xFFFD, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, - 0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, - 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, - 0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0xFFFD, - 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, - 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, - 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, - 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x2017, - 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, - 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, - 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, - 0x05E8, 0x05E9, 0x05EA, 0xFFFD, 0xFFFD, 0x200E, 0x200F, 0xFFFD -}; - -/* ISO/IEC 8859-9:1999 (Latin-5, "Turkish") */ -static const wchar_t iso_8859_9[] = { - 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, - 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, - 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, - 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, - 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, - 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, - 0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, - 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0130, 0x015E, 0x00DF, - 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, - 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, - 0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, - 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015F, 0x00FF -}; - -/* ISO/IEC 8859-10:1998 (Latin-6, "Nordic" [Sami, Inuit, Icelandic]) */ -static const wchar_t iso_8859_10[] = { - 0x00A0, 0x0104, 0x0112, 0x0122, 0x012A, 0x0128, 0x0136, 0x00A7, - 0x013B, 0x0110, 0x0160, 0x0166, 0x017D, 0x00AD, 0x016A, 0x014A, - 0x00B0, 0x0105, 0x0113, 0x0123, 0x012B, 0x0129, 0x0137, 0x00B7, - 0x013C, 0x0111, 0x0161, 0x0167, 0x017E, 0x2015, 0x016B, 0x014B, - 0x0100, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x012E, - 0x010C, 0x00C9, 0x0118, 0x00CB, 0x0116, 0x00CD, 0x00CE, 0x00CF, - 0x00D0, 0x0145, 0x014C, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x0168, - 0x00D8, 0x0172, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, - 0x0101, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x012F, - 0x010D, 0x00E9, 0x0119, 0x00EB, 0x0117, 0x00ED, 0x00EE, 0x00EF, - 0x00F0, 0x0146, 0x014D, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x0169, - 0x00F8, 0x0173, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x0138 -}; - -/* ISO/IEC 8859-11:2001 ("Thai", "TIS620") */ -static const wchar_t iso_8859_11[] = { - 0x00A0, 0x0E01, 0x0E02, 0x0E03, 0x0E04, 0x0E05, 0x0E06, 0x0E07, - 0x0E08, 0x0E09, 0x0E0A, 0x0E0B, 0x0E0C, 0x0E0D, 0x0E0E, 0x0E0F, - 0x0E10, 0x0E11, 0x0E12, 0x0E13, 0x0E14, 0x0E15, 0x0E16, 0x0E17, - 0x0E18, 0x0E19, 0x0E1A, 0x0E1B, 0x0E1C, 0x0E1D, 0x0E1E, 0x0E1F, - 0x0E20, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27, - 0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F, - 0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37, - 0x0E38, 0x0E39, 0x0E3A, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x0E3F, - 0x0E40, 0x0E41, 0x0E42, 0x0E43, 0x0E44, 0x0E45, 0x0E46, 0x0E47, - 0x0E48, 0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, 0x0E4E, 0x0E4F, - 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, - 0x0E58, 0x0E59, 0x0E5A, 0x0E5B, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD -}; - -/* ISO/IEC 8859-13:1998 (Latin-7, "Baltic Rim") */ -static const wchar_t iso_8859_13[] = { - 0x00A0, 0x201D, 0x00A2, 0x00A3, 0x00A4, 0x201E, 0x00A6, 0x00A7, - 0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00C6, - 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x201C, 0x00B5, 0x00B6, 0x00B7, - 0x00F8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00E6, - 0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112, - 0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B, - 0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7, - 0x0172, 0x0141, 0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF, - 0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113, - 0x010D, 0x00E9, 0x017A, 0x0117, 0x0123, 0x0137, 0x012B, 0x013C, - 0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7, - 0x0173, 0x0142, 0x015B, 0x016B, 0x00FC, 0x017C, 0x017E, 0x2019 -}; - -/* ISO/IEC 8859-14:1998 (Latin-8, "Celtic", "Gaelic/Welsh") */ -static const wchar_t iso_8859_14[] = { - 0x00A0, 0x1E02, 0x1E03, 0x00A3, 0x010A, 0x010B, 0x1E0A, 0x00A7, - 0x1E80, 0x00A9, 0x1E82, 0x1E0B, 0x1EF2, 0x00AD, 0x00AE, 0x0178, - 0x1E1E, 0x1E1F, 0x0120, 0x0121, 0x1E40, 0x1E41, 0x00B6, 0x1E56, - 0x1E81, 0x1E57, 0x1E83, 0x1E60, 0x1EF3, 0x1E84, 0x1E85, 0x1E61, - 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, - 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, - 0x0174, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x1E6A, - 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x0176, 0x00DF, - 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, - 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, - 0x0175, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x1E6B, - 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x0177, 0x00FF -}; - -/* ISO/IEC 8859-15:1999 (Latin-9 aka -0, "euro") */ -static const wchar_t iso_8859_15[] = { - 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x20AC, 0x00A5, 0x0160, 0x00A7, - 0x0161, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, - 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x017D, 0x00B5, 0x00B6, 0x00B7, - 0x017E, 0x00B9, 0x00BA, 0x00BB, 0x0152, 0x0153, 0x0178, 0x00BF, - 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, - 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, - 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, - 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, - 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, - 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, - 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, - 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF -}; - -/* ISO/IEC 8859-16:2001 (Latin-10, "Balkan") */ -static const wchar_t iso_8859_16[] = { - 0x00A0, 0x0104, 0x0105, 0x0141, 0x20AC, 0x201E, 0x0160, 0x00A7, - 0x0161, 0x00A9, 0x0218, 0x00AB, 0x0179, 0x00AD, 0x017A, 0x017B, - 0x00B0, 0x00B1, 0x010C, 0x0142, 0x017D, 0x201D, 0x00B6, 0x00B7, - 0x017E, 0x010D, 0x0219, 0x00BB, 0x0152, 0x0153, 0x0178, 0x017C, - 0x00C0, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0106, 0x00C6, 0x00C7, - 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, - 0x0110, 0x0143, 0x00D2, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x015A, - 0x0170, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0118, 0x021A, 0x00DF, - 0x00E0, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x0107, 0x00E6, 0x00E7, - 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, - 0x0111, 0x0144, 0x00F2, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x015B, - 0x0171, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0119, 0x021B, 0x00FF -}; - -static const wchar_t roman8[] = { - 0x00A0, 0x00C0, 0x00C2, 0x00C8, 0x00CA, 0x00CB, 0x00CE, 0x00CF, - 0x00B4, 0x02CB, 0x02C6, 0x00A8, 0x02DC, 0x00D9, 0x00DB, 0x20A4, - 0x00AF, 0x00DD, 0x00FD, 0x00B0, 0x00C7, 0x00E7, 0x00D1, 0x00F1, - 0x00A1, 0x00BF, 0x00A4, 0x00A3, 0x00A5, 0x00A7, 0x0192, 0x00A2, - 0x00E2, 0x00EA, 0x00F4, 0x00FB, 0x00E1, 0x00E9, 0x00F3, 0x00FA, - 0x00E0, 0x00E8, 0x00F2, 0x00F9, 0x00E4, 0x00EB, 0x00F6, 0x00FC, - 0x00C5, 0x00EE, 0x00D8, 0x00C6, 0x00E5, 0x00ED, 0x00F8, 0x00E6, - 0x00C4, 0x00EC, 0x00D6, 0x00DC, 0x00C9, 0x00EF, 0x00DF, 0x00D4, - 0x00C1, 0x00C3, 0x00E3, 0x00D0, 0x00F0, 0x00CD, 0x00CC, 0x00D3, - 0x00D2, 0x00D5, 0x00F5, 0x0160, 0x0161, 0x00DA, 0x0178, 0x00FF, - 0x00DE, 0x00FE, 0x00B7, 0x00B5, 0x00B6, 0x00BE, 0x2014, 0x00BC, - 0x00BD, 0x00AA, 0x00BA, 0x00AB, 0x25A0, 0x00BB, 0x00B1, 0xFFFD -}; - -static const wchar_t koi8_u[] = { - 0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524, - 0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590, - 0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2022, 0x221A, 0x2248, - 0x2264, 0x2265, 0x00A0, 0x2321, 0x00B0, 0x00B2, 0x00B7, 0x00F7, - 0x2550, 0x2551, 0x2552, 0x0451, 0x0454, 0x2554, 0x0456, 0x0457, - 0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x0491, 0x255D, 0x255E, - 0x255F, 0x2560, 0x2561, 0x0401, 0x0404, 0x2563, 0x0406, 0x0407, - 0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x0490, 0x256C, 0x00A9, - 0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, - 0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, - 0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, - 0x044C, 0x044B, 0x0437, 0x0448, 0x044D, 0x0449, 0x0447, 0x044A, - 0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, - 0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, - 0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, - 0x042C, 0x042B, 0x0417, 0x0428, 0x042D, 0x0429, 0x0427, 0x042A -}; - -static const wchar_t vscii[] = { - 0x0000, 0x0001, 0x1EB2, 0x0003, 0x0004, 0x1EB4, 0x1EAA, 0x0007, - 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, - 0x0010, 0x0011, 0x0012, 0x0013, 0x1EF6, 0x0015, 0x0016, 0x0017, - 0x0018, 0x1EF8, 0x001a, 0x001b, 0x001c, 0x001d, 0x1EF4, 0x001f, - 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, - 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, - 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, - 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, - 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, - 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, - 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, - 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, - 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, - 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, - 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, - 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007f, - 0x1EA0, 0x1EAE, 0x1EB0, 0x1EB6, 0x1EA4, 0x1EA6, 0x1EA8, 0x1EAC, - 0x1EBC, 0x1EB8, 0x1EBE, 0x1EC0, 0x1EC2, 0x1EC4, 0x1EC6, 0x1ED0, - 0x1ED2, 0x1ED4, 0x1ED6, 0x1ED8, 0x1EE2, 0x1EDA, 0x1EDC, 0x1EDE, - 0x1ECA, 0x1ECE, 0x1ECC, 0x1EC8, 0x1EE6, 0x0168, 0x1EE4, 0x1EF2, - 0x00D5, 0x1EAF, 0x1EB1, 0x1EB7, 0x1EA5, 0x1EA7, 0x1EA8, 0x1EAD, - 0x1EBD, 0x1EB9, 0x1EBF, 0x1EC1, 0x1EC3, 0x1EC5, 0x1EC7, 0x1ED1, - 0x1ED3, 0x1ED5, 0x1ED7, 0x1EE0, 0x01A0, 0x1ED9, 0x1EDD, 0x1EDF, - 0x1ECB, 0x1EF0, 0x1EE8, 0x1EEA, 0x1EEC, 0x01A1, 0x1EDB, 0x01AF, - 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x1EA2, 0x0102, 0x1EB3, 0x1EB5, - 0x00C8, 0x00C9, 0x00CA, 0x1EBA, 0x00CC, 0x00CD, 0x0128, 0x1EF3, - 0x0110, 0x1EE9, 0x00D2, 0x00D3, 0x00D4, 0x1EA1, 0x1EF7, 0x1EEB, - 0x1EED, 0x00D9, 0x00DA, 0x1EF9, 0x1EF5, 0x00DD, 0x1EE1, 0x01B0, - 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x1EA3, 0x0103, 0x1EEF, 0x1EAB, - 0x00E8, 0x00E9, 0x00EA, 0x1EBB, 0x00EC, 0x00ED, 0x0129, 0x1EC9, - 0x0111, 0x1EF1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x1ECF, 0x1ECD, - 0x1EE5, 0x00F9, 0x00FA, 0x0169, 0x1EE7, 0x00FD, 0x1EE3, 0x1EEE -}; - -static const wchar_t dec_mcs[] = { - 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0xFFFD, 0x00A5, 0xFFFD, 0x00A7, - 0x00A4, 0x00A9, 0x00AA, 0x00AB, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, - 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0xFFFD, 0x00B5, 0x00B6, 0x00B7, - 0xFFFD, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0xFFFD, 0x00BF, - 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, - 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, - 0xFFFD, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x0152, - 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0178, 0xFFFD, 0x00DF, - 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, - 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, - 0xFFFD, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x0153, - 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FF, 0xFFFD, 0xFFFD -}; - -/* Mazovia (Polish) aka CP620 - * from "Mazowia to Unicode table", 04/24/96, Mikolaj Jedrzejak */ -static const wchar_t mazovia[] = { - /* Code point 0x9B is "zloty" symbol (zŽ), which is not - * widely used and for which there is no Unicode equivalent. - * One reference shows 0xA8 as U+00A7 SECTION SIGN, but we're - * told that's incorrect. */ - 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x0105, 0x00E7, - 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0107, 0x00C4, 0x0104, - 0x0118, 0x0119, 0x0142, 0x00F4, 0x00F6, 0x0106, 0x00FB, 0x00F9, - 0x015a, 0x00D6, 0x00DC, 0xFFFD, 0x0141, 0x00A5, 0x015b, 0x0192, - 0x0179, 0x017b, 0x00F3, 0x00d3, 0x0144, 0x0143, 0x017a, 0x017c, - 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, - 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, - 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, - 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, - 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, - 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, - 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, - 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, - 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, - 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, - 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 -}; - -struct cp_list_item { - char *name; - int codepage; - int cp_size; - const wchar_t *cp_table; -}; - -static const struct cp_list_item cp_list[] = { - {"UTF-8", CP_UTF8}, - - {"ISO-8859-1:1998 (Latin-1, West Europe)", 0, 96, iso_8859_1}, - {"ISO-8859-2:1999 (Latin-2, East Europe)", 0, 96, iso_8859_2}, - {"ISO-8859-3:1999 (Latin-3, South Europe)", 0, 96, iso_8859_3}, - {"ISO-8859-4:1998 (Latin-4, North Europe)", 0, 96, iso_8859_4}, - {"ISO-8859-5:1999 (Latin/Cyrillic)", 0, 96, iso_8859_5}, - {"ISO-8859-6:1999 (Latin/Arabic)", 0, 96, iso_8859_6}, - {"ISO-8859-7:1987 (Latin/Greek)", 0, 96, iso_8859_7}, - {"ISO-8859-8:1999 (Latin/Hebrew)", 0, 96, iso_8859_8}, - {"ISO-8859-9:1999 (Latin-5, Turkish)", 0, 96, iso_8859_9}, - {"ISO-8859-10:1998 (Latin-6, Nordic)", 0, 96, iso_8859_10}, - {"ISO-8859-11:2001 (Latin/Thai)", 0, 96, iso_8859_11}, - {"ISO-8859-13:1998 (Latin-7, Baltic)", 0, 96, iso_8859_13}, - {"ISO-8859-14:1998 (Latin-8, Celtic)", 0, 96, iso_8859_14}, - {"ISO-8859-15:1999 (Latin-9, \"euro\")", 0, 96, iso_8859_15}, - {"ISO-8859-16:2001 (Latin-10, Balkan)", 0, 96, iso_8859_16}, - - {"KOI8-U", 0, 128, koi8_u}, - {"KOI8-R", 20866}, - {"HP-ROMAN8", 0, 96, roman8}, - {"VSCII", 0, 256, vscii}, - {"DEC-MCS", 0, 96, dec_mcs}, - - {"Win1250 (Central European)", 1250}, - {"Win1251 (Cyrillic)", 1251}, - {"Win1252 (Western)", 1252}, - {"Win1253 (Greek)", 1253}, - {"Win1254 (Turkish)", 1254}, - {"Win1255 (Hebrew)", 1255}, - {"Win1256 (Arabic)", 1256}, - {"Win1257 (Baltic)", 1257}, - {"Win1258 (Vietnamese)", 1258}, - - {"CP437", 437}, - {"CP620 (Mazovia)", 0, 128, mazovia}, - {"CP819", 28591}, - {"CP852", 852}, - {"CP878", 20866}, - - {"Use font encoding", -1}, - - {0, 0} -}; - -static void link_font(WCHAR * line_tbl, WCHAR * font_tbl, WCHAR attr); - -void init_ucs(Conf *conf, struct unicode_data *ucsdata) -{ - int i, j; - bool used_dtf = false; - int vtmode; - - /* Decide on the Line and Font codepages */ - ucsdata->line_codepage = decode_codepage(conf_get_str(conf, - CONF_line_codepage)); - - if (ucsdata->font_codepage <= 0) { - ucsdata->font_codepage=0; - ucsdata->dbcs_screenfont=false; - } - - vtmode = conf_get_int(conf, CONF_vtmode); - if (vtmode == VT_OEMONLY) { - ucsdata->font_codepage = 437; - ucsdata->dbcs_screenfont = false; - if (ucsdata->line_codepage <= 0) - ucsdata->line_codepage = GetACP(); - } else if (ucsdata->line_codepage <= 0) - ucsdata->line_codepage = ucsdata->font_codepage; - - /* Collect screen font ucs table */ - if (ucsdata->dbcs_screenfont || ucsdata->font_codepage == 0) { - get_unitab(ucsdata->font_codepage, ucsdata->unitab_font, 2); - for (i = 128; i < 256; i++) - ucsdata->unitab_font[i] = (WCHAR) (CSET_ACP + i); - } else { - get_unitab(ucsdata->font_codepage, ucsdata->unitab_font, 1); - - /* CP437 fonts are often broken ... */ - if (ucsdata->font_codepage == 437) - ucsdata->unitab_font[0] = ucsdata->unitab_font[255] = 0xFFFF; - } - if (vtmode == VT_XWINDOWS) - memcpy(ucsdata->unitab_font + 1, unitab_xterm_std, - sizeof(unitab_xterm_std)); - - /* Collect OEMCP ucs table */ - get_unitab(CP_OEMCP, ucsdata->unitab_oemcp, 1); - - /* Collect CP437 ucs table for SCO acs */ - if (vtmode == VT_OEMANSI || vtmode == VT_XWINDOWS) - memcpy(ucsdata->unitab_scoacs, ucsdata->unitab_oemcp, - sizeof(ucsdata->unitab_scoacs)); - else - get_unitab(437, ucsdata->unitab_scoacs, 1); - - /* Collect line set ucs table */ - if (ucsdata->line_codepage == ucsdata->font_codepage && - (ucsdata->dbcs_screenfont || - vtmode == VT_POORMAN || ucsdata->font_codepage==0)) { - - /* For DBCS and POOR fonts force direct to font */ - used_dtf = true; - for (i = 0; i < 32; i++) - ucsdata->unitab_line[i] = (WCHAR) i; - for (i = 32; i < 256; i++) - ucsdata->unitab_line[i] = (WCHAR) (CSET_ACP + i); - ucsdata->unitab_line[127] = (WCHAR) 127; - } else { - get_unitab(ucsdata->line_codepage, ucsdata->unitab_line, 0); - } - -#if 0 - debug("Line cp%d, Font cp%d%s\n", ucsdata->line_codepage, - ucsdata->font_codepage, ucsdata->dbcs_screenfont ? " DBCS" : ""); - - for (i = 0; i < 256; i += 16) { - for (j = 0; j < 16; j++) { - debug("%04x%s", ucsdata->unitab_line[i + j], j == 15 ? "" : ","); - } - debug("\n"); - } -#endif - - /* VT100 graphics - NB: Broken for non-ascii CP's */ - memcpy(ucsdata->unitab_xterm, ucsdata->unitab_line, - sizeof(ucsdata->unitab_xterm)); - memcpy(ucsdata->unitab_xterm + '`', unitab_xterm_std, - sizeof(unitab_xterm_std)); - ucsdata->unitab_xterm['_'] = ' '; - - /* Generate UCS ->line page table. */ - if (ucsdata->uni_tbl) { - for (i = 0; i < 256; i++) - if (ucsdata->uni_tbl[i]) - sfree(ucsdata->uni_tbl[i]); - sfree(ucsdata->uni_tbl); - ucsdata->uni_tbl = 0; - } - if (!used_dtf) { - for (i = 0; i < 256; i++) { - if (DIRECT_CHAR(ucsdata->unitab_line[i])) - continue; - if (DIRECT_FONT(ucsdata->unitab_line[i])) - continue; - if (!ucsdata->uni_tbl) { - ucsdata->uni_tbl = snewn(256, char *); - memset(ucsdata->uni_tbl, 0, 256 * sizeof(char *)); - } - j = ((ucsdata->unitab_line[i] >> 8) & 0xFF); - if (!ucsdata->uni_tbl[j]) { - ucsdata->uni_tbl[j] = snewn(256, char); - memset(ucsdata->uni_tbl[j], 0, 256 * sizeof(char)); - } - ucsdata->uni_tbl[j][ucsdata->unitab_line[i] & 0xFF] = i; - } - } - - /* Find the line control characters. */ - for (i = 0; i < 256; i++) - if (ucsdata->unitab_line[i] < ' ' - || (ucsdata->unitab_line[i] >= 0x7F && - ucsdata->unitab_line[i] < 0xA0)) - ucsdata->unitab_ctrl[i] = i; - else - ucsdata->unitab_ctrl[i] = 0xFF; - - /* Generate line->screen direct conversion links. */ - if (vtmode == VT_OEMANSI || vtmode == VT_XWINDOWS) - link_font(ucsdata->unitab_scoacs, ucsdata->unitab_oemcp, CSET_OEMCP); - - link_font(ucsdata->unitab_line, ucsdata->unitab_font, CSET_ACP); - link_font(ucsdata->unitab_scoacs, ucsdata->unitab_font, CSET_ACP); - link_font(ucsdata->unitab_xterm, ucsdata->unitab_font, CSET_ACP); - - if (vtmode == VT_OEMANSI || vtmode == VT_XWINDOWS) { - link_font(ucsdata->unitab_line, ucsdata->unitab_oemcp, CSET_OEMCP); - link_font(ucsdata->unitab_xterm, ucsdata->unitab_oemcp, CSET_OEMCP); - } - - if (ucsdata->dbcs_screenfont && - ucsdata->font_codepage != ucsdata->line_codepage) { - /* F***ing Microsoft fonts, Japanese and Korean codepage fonts - * have a currency symbol at 0x5C but their unicode value is - * still given as U+005C not the correct U+00A5. */ - ucsdata->unitab_line['\\'] = CSET_OEMCP + '\\'; - } - - /* Last chance, if !unicode then try poorman links. */ - if (vtmode != VT_UNICODE) { - static const char poorman_scoacs[] = - "CueaaaaceeeiiiAAE**ooouuyOUc$YPsaiounNao?++**!<>###||||++||++++++--|-+||++--|-+----++++++++##||#aBTPEsyt******EN=+><++-=... n2* "; - static const char poorman_latin1[] = - " !cL.Y|S\"Ca<--R~o+23'u|.,1o>///?AAAAAAACEEEEIIIIDNOOOOOxOUUUUYPBaaaaaaaceeeeiiiionooooo/ouuuuypy"; - static const char poorman_vt100[] = "*#****o~**+++++-----++++|****L."; - - for (i = 160; i < 256; i++) - if (!DIRECT_FONT(ucsdata->unitab_line[i]) && - ucsdata->unitab_line[i] >= 160 && - ucsdata->unitab_line[i] < 256) { - ucsdata->unitab_line[i] = - (WCHAR) (CSET_ACP + - poorman_latin1[ucsdata->unitab_line[i] - 160]); - } - for (i = 96; i < 127; i++) - if (!DIRECT_FONT(ucsdata->unitab_xterm[i])) - ucsdata->unitab_xterm[i] = - (WCHAR) (CSET_ACP + poorman_vt100[i - 96]); - for(i=128;i<256;i++) - if (!DIRECT_FONT(ucsdata->unitab_scoacs[i])) - ucsdata->unitab_scoacs[i] = - (WCHAR) (CSET_ACP + poorman_scoacs[i - 128]); - } -} - -static void link_font(WCHAR * line_tbl, WCHAR * font_tbl, WCHAR attr) -{ - int font_index, line_index, i; - for (line_index = 0; line_index < 256; line_index++) { - if (DIRECT_FONT(line_tbl[line_index])) - continue; - for(i = 0; i < 256; i++) { - font_index = ((32 + i) & 0xFF); - if (line_tbl[line_index] == font_tbl[font_index]) { - line_tbl[line_index] = (WCHAR) (attr + font_index); - break; - } - } - } -} - -wchar_t xlat_uskbd2cyrllic(int ch) -{ - static const wchar_t cyrtab[] = { - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 0x042d, 35, 36, 37, 38, 0x044d, - 40, 41, 42, 0x0406, 0x0431, 0x0454, 0x044e, 0x002e, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 0x0416, 0x0436, 0x0411, 0x0456, 0x042e, 0x002c, - 64, 0x0424, 0x0418, 0x0421, 0x0412, 0x0423, 0x0410, 0x041f, - 0x0420, 0x0428, 0x041e, 0x041b, 0x0414, 0x042c, 0x0422, 0x0429, - 0x0417, 0x0419, 0x041a, 0x042b, 0x0415, 0x0413, 0x041c, 0x0426, - 0x0427, 0x041d, 0x042f, 0x0445, 0x0457, 0x044a, 94, 0x0404, - 96, 0x0444, 0x0438, 0x0441, 0x0432, 0x0443, 0x0430, 0x043f, - 0x0440, 0x0448, 0x043e, 0x043b, 0x0434, 0x044c, 0x0442, 0x0449, - 0x0437, 0x0439, 0x043a, 0x044b, 0x0435, 0x0433, 0x043c, 0x0446, - 0x0447, 0x043d, 0x044f, 0x0425, 0x0407, 0x042a, 126, 127 - }; - return cyrtab[ch&0x7F]; -} - -int check_compose_internal(int first, int second, int recurse) -{ - - static const struct { - char first, second; - wchar_t composed; - } composetbl[] = { - {0x2b, 0x2b, 0x0023}, - {0x41, 0x41, 0x0040}, - {0x28, 0x28, 0x005b}, - {0x2f, 0x2f, 0x005c}, - {0x29, 0x29, 0x005d}, - {0x28, 0x2d, 0x007b}, - {0x2d, 0x29, 0x007d}, - {0x2f, 0x5e, 0x007c}, - {0x21, 0x21, 0x00a1}, - {0x43, 0x2f, 0x00a2}, - {0x43, 0x7c, 0x00a2}, - {0x4c, 0x2d, 0x00a3}, - {0x4c, 0x3d, 0x20a4}, - {0x58, 0x4f, 0x00a4}, - {0x58, 0x30, 0x00a4}, - {0x59, 0x2d, 0x00a5}, - {0x59, 0x3d, 0x00a5}, - {0x7c, 0x7c, 0x00a6}, - {0x53, 0x4f, 0x00a7}, - {0x53, 0x21, 0x00a7}, - {0x53, 0x30, 0x00a7}, - {0x22, 0x22, 0x00a8}, - {0x43, 0x4f, 0x00a9}, - {0x43, 0x30, 0x00a9}, - {0x41, 0x5f, 0x00aa}, - {0x3c, 0x3c, 0x00ab}, - {0x2c, 0x2d, 0x00ac}, - {0x2d, 0x2d, 0x00ad}, - {0x52, 0x4f, 0x00ae}, - {0x2d, 0x5e, 0x00af}, - {0x30, 0x5e, 0x00b0}, - {0x2b, 0x2d, 0x00b1}, - {0x32, 0x5e, 0x00b2}, - {0x33, 0x5e, 0x00b3}, - {0x27, 0x27, 0x00b4}, - {0x2f, 0x55, 0x00b5}, - {0x50, 0x21, 0x00b6}, - {0x2e, 0x5e, 0x00b7}, - {0x2c, 0x2c, 0x00b8}, - {0x31, 0x5e, 0x00b9}, - {0x4f, 0x5f, 0x00ba}, - {0x3e, 0x3e, 0x00bb}, - {0x31, 0x34, 0x00bc}, - {0x31, 0x32, 0x00bd}, - {0x33, 0x34, 0x00be}, - {0x3f, 0x3f, 0x00bf}, - {0x60, 0x41, 0x00c0}, - {0x27, 0x41, 0x00c1}, - {0x5e, 0x41, 0x00c2}, - {0x7e, 0x41, 0x00c3}, - {0x22, 0x41, 0x00c4}, - {0x2a, 0x41, 0x00c5}, - {0x41, 0x45, 0x00c6}, - {0x2c, 0x43, 0x00c7}, - {0x60, 0x45, 0x00c8}, - {0x27, 0x45, 0x00c9}, - {0x5e, 0x45, 0x00ca}, - {0x22, 0x45, 0x00cb}, - {0x60, 0x49, 0x00cc}, - {0x27, 0x49, 0x00cd}, - {0x5e, 0x49, 0x00ce}, - {0x22, 0x49, 0x00cf}, - {0x2d, 0x44, 0x00d0}, - {0x7e, 0x4e, 0x00d1}, - {0x60, 0x4f, 0x00d2}, - {0x27, 0x4f, 0x00d3}, - {0x5e, 0x4f, 0x00d4}, - {0x7e, 0x4f, 0x00d5}, - {0x22, 0x4f, 0x00d6}, - {0x58, 0x58, 0x00d7}, - {0x2f, 0x4f, 0x00d8}, - {0x60, 0x55, 0x00d9}, - {0x27, 0x55, 0x00da}, - {0x5e, 0x55, 0x00db}, - {0x22, 0x55, 0x00dc}, - {0x27, 0x59, 0x00dd}, - {0x48, 0x54, 0x00de}, - {0x73, 0x73, 0x00df}, - {0x60, 0x61, 0x00e0}, - {0x27, 0x61, 0x00e1}, - {0x5e, 0x61, 0x00e2}, - {0x7e, 0x61, 0x00e3}, - {0x22, 0x61, 0x00e4}, - {0x2a, 0x61, 0x00e5}, - {0x61, 0x65, 0x00e6}, - {0x2c, 0x63, 0x00e7}, - {0x60, 0x65, 0x00e8}, - {0x27, 0x65, 0x00e9}, - {0x5e, 0x65, 0x00ea}, - {0x22, 0x65, 0x00eb}, - {0x60, 0x69, 0x00ec}, - {0x27, 0x69, 0x00ed}, - {0x5e, 0x69, 0x00ee}, - {0x22, 0x69, 0x00ef}, - {0x2d, 0x64, 0x00f0}, - {0x7e, 0x6e, 0x00f1}, - {0x60, 0x6f, 0x00f2}, - {0x27, 0x6f, 0x00f3}, - {0x5e, 0x6f, 0x00f4}, - {0x7e, 0x6f, 0x00f5}, - {0x22, 0x6f, 0x00f6}, - {0x3a, 0x2d, 0x00f7}, - {0x6f, 0x2f, 0x00f8}, - {0x60, 0x75, 0x00f9}, - {0x27, 0x75, 0x00fa}, - {0x5e, 0x75, 0x00fb}, - {0x22, 0x75, 0x00fc}, - {0x27, 0x79, 0x00fd}, - {0x68, 0x74, 0x00fe}, - {0x22, 0x79, 0x00ff}, - /* Unicode extras. */ - {0x6f, 0x65, 0x0153}, - {0x4f, 0x45, 0x0152}, - /* Compose pairs from UCS */ - {0x41, 0x2D, 0x0100}, - {0x61, 0x2D, 0x0101}, - {0x43, 0x27, 0x0106}, - {0x63, 0x27, 0x0107}, - {0x43, 0x5E, 0x0108}, - {0x63, 0x5E, 0x0109}, - {0x45, 0x2D, 0x0112}, - {0x65, 0x2D, 0x0113}, - {0x47, 0x5E, 0x011C}, - {0x67, 0x5E, 0x011D}, - {0x47, 0x2C, 0x0122}, - {0x67, 0x2C, 0x0123}, - {0x48, 0x5E, 0x0124}, - {0x68, 0x5E, 0x0125}, - {0x49, 0x7E, 0x0128}, - {0x69, 0x7E, 0x0129}, - {0x49, 0x2D, 0x012A}, - {0x69, 0x2D, 0x012B}, - {0x4A, 0x5E, 0x0134}, - {0x6A, 0x5E, 0x0135}, - {0x4B, 0x2C, 0x0136}, - {0x6B, 0x2C, 0x0137}, - {0x4C, 0x27, 0x0139}, - {0x6C, 0x27, 0x013A}, - {0x4C, 0x2C, 0x013B}, - {0x6C, 0x2C, 0x013C}, - {0x4E, 0x27, 0x0143}, - {0x6E, 0x27, 0x0144}, - {0x4E, 0x2C, 0x0145}, - {0x6E, 0x2C, 0x0146}, - {0x4F, 0x2D, 0x014C}, - {0x6F, 0x2D, 0x014D}, - {0x52, 0x27, 0x0154}, - {0x72, 0x27, 0x0155}, - {0x52, 0x2C, 0x0156}, - {0x72, 0x2C, 0x0157}, - {0x53, 0x27, 0x015A}, - {0x73, 0x27, 0x015B}, - {0x53, 0x5E, 0x015C}, - {0x73, 0x5E, 0x015D}, - {0x53, 0x2C, 0x015E}, - {0x73, 0x2C, 0x015F}, - {0x54, 0x2C, 0x0162}, - {0x74, 0x2C, 0x0163}, - {0x55, 0x7E, 0x0168}, - {0x75, 0x7E, 0x0169}, - {0x55, 0x2D, 0x016A}, - {0x75, 0x2D, 0x016B}, - {0x55, 0x2A, 0x016E}, - {0x75, 0x2A, 0x016F}, - {0x57, 0x5E, 0x0174}, - {0x77, 0x5E, 0x0175}, - {0x59, 0x5E, 0x0176}, - {0x79, 0x5E, 0x0177}, - {0x59, 0x22, 0x0178}, - {0x5A, 0x27, 0x0179}, - {0x7A, 0x27, 0x017A}, - {0x47, 0x27, 0x01F4}, - {0x67, 0x27, 0x01F5}, - {0x4E, 0x60, 0x01F8}, - {0x6E, 0x60, 0x01F9}, - {0x45, 0x2C, 0x0228}, - {0x65, 0x2C, 0x0229}, - {0x59, 0x2D, 0x0232}, - {0x79, 0x2D, 0x0233}, - {0x44, 0x2C, 0x1E10}, - {0x64, 0x2C, 0x1E11}, - {0x47, 0x2D, 0x1E20}, - {0x67, 0x2D, 0x1E21}, - {0x48, 0x22, 0x1E26}, - {0x68, 0x22, 0x1E27}, - {0x48, 0x2C, 0x1E28}, - {0x68, 0x2C, 0x1E29}, - {0x4B, 0x27, 0x1E30}, - {0x6B, 0x27, 0x1E31}, - {0x4D, 0x27, 0x1E3E}, - {0x6D, 0x27, 0x1E3F}, - {0x50, 0x27, 0x1E54}, - {0x70, 0x27, 0x1E55}, - {0x56, 0x7E, 0x1E7C}, - {0x76, 0x7E, 0x1E7D}, - {0x57, 0x60, 0x1E80}, - {0x77, 0x60, 0x1E81}, - {0x57, 0x27, 0x1E82}, - {0x77, 0x27, 0x1E83}, - {0x57, 0x22, 0x1E84}, - {0x77, 0x22, 0x1E85}, - {0x58, 0x22, 0x1E8C}, - {0x78, 0x22, 0x1E8D}, - {0x5A, 0x5E, 0x1E90}, - {0x7A, 0x5E, 0x1E91}, - {0x74, 0x22, 0x1E97}, - {0x77, 0x2A, 0x1E98}, - {0x79, 0x2A, 0x1E99}, - {0x45, 0x7E, 0x1EBC}, - {0x65, 0x7E, 0x1EBD}, - {0x59, 0x60, 0x1EF2}, - {0x79, 0x60, 0x1EF3}, - {0x59, 0x7E, 0x1EF8}, - {0x79, 0x7E, 0x1EF9}, - /* Compatible/possibles from UCS */ - {0x49, 0x4A, 0x0132}, - {0x69, 0x6A, 0x0133}, - {0x4C, 0x4A, 0x01C7}, - {0x4C, 0x6A, 0x01C8}, - {0x6C, 0x6A, 0x01C9}, - {0x4E, 0x4A, 0x01CA}, - {0x4E, 0x6A, 0x01CB}, - {0x6E, 0x6A, 0x01CC}, - {0x44, 0x5A, 0x01F1}, - {0x44, 0x7A, 0x01F2}, - {0x64, 0x7A, 0x01F3}, - {0x2E, 0x2E, 0x2025}, - {0x21, 0x21, 0x203C}, - {0x3F, 0x21, 0x2048}, - {0x21, 0x3F, 0x2049}, - {0x52, 0x73, 0x20A8}, - {0x4E, 0x6F, 0x2116}, - {0x53, 0x4D, 0x2120}, - {0x54, 0x4D, 0x2122}, - {0x49, 0x49, 0x2161}, - {0x49, 0x56, 0x2163}, - {0x56, 0x49, 0x2165}, - {0x49, 0x58, 0x2168}, - {0x58, 0x49, 0x216A}, - {0x69, 0x69, 0x2171}, - {0x69, 0x76, 0x2173}, - {0x76, 0x69, 0x2175}, - {0x69, 0x78, 0x2178}, - {0x78, 0x69, 0x217A}, - {0x31, 0x30, 0x2469}, - {0x31, 0x31, 0x246A}, - {0x31, 0x32, 0x246B}, - {0x31, 0x33, 0x246C}, - {0x31, 0x34, 0x246D}, - {0x31, 0x35, 0x246E}, - {0x31, 0x36, 0x246F}, - {0x31, 0x37, 0x2470}, - {0x31, 0x38, 0x2471}, - {0x31, 0x39, 0x2472}, - {0x32, 0x30, 0x2473}, - {0x31, 0x2E, 0x2488}, - {0x32, 0x2E, 0x2489}, - {0x33, 0x2E, 0x248A}, - {0x34, 0x2E, 0x248B}, - {0x35, 0x2E, 0x248C}, - {0x36, 0x2E, 0x248D}, - {0x37, 0x2E, 0x248E}, - {0x38, 0x2E, 0x248F}, - {0x39, 0x2E, 0x2490}, - {0x64, 0x61, 0x3372}, - {0x41, 0x55, 0x3373}, - {0x6F, 0x56, 0x3375}, - {0x70, 0x63, 0x3376}, - {0x70, 0x41, 0x3380}, - {0x6E, 0x41, 0x3381}, - {0x6D, 0x41, 0x3383}, - {0x6B, 0x41, 0x3384}, - {0x4B, 0x42, 0x3385}, - {0x4D, 0x42, 0x3386}, - {0x47, 0x42, 0x3387}, - {0x70, 0x46, 0x338A}, - {0x6E, 0x46, 0x338B}, - {0x6D, 0x67, 0x338E}, - {0x6B, 0x67, 0x338F}, - {0x48, 0x7A, 0x3390}, - {0x66, 0x6D, 0x3399}, - {0x6E, 0x6D, 0x339A}, - {0x6D, 0x6D, 0x339C}, - {0x63, 0x6D, 0x339D}, - {0x6B, 0x6D, 0x339E}, - {0x50, 0x61, 0x33A9}, - {0x70, 0x73, 0x33B0}, - {0x6E, 0x73, 0x33B1}, - {0x6D, 0x73, 0x33B3}, - {0x70, 0x56, 0x33B4}, - {0x6E, 0x56, 0x33B5}, - {0x6D, 0x56, 0x33B7}, - {0x6B, 0x56, 0x33B8}, - {0x4D, 0x56, 0x33B9}, - {0x70, 0x57, 0x33BA}, - {0x6E, 0x57, 0x33BB}, - {0x6D, 0x57, 0x33BD}, - {0x6B, 0x57, 0x33BE}, - {0x4D, 0x57, 0x33BF}, - {0x42, 0x71, 0x33C3}, - {0x63, 0x63, 0x33C4}, - {0x63, 0x64, 0x33C5}, - {0x64, 0x42, 0x33C8}, - {0x47, 0x79, 0x33C9}, - {0x68, 0x61, 0x33CA}, - {0x48, 0x50, 0x33CB}, - {0x69, 0x6E, 0x33CC}, - {0x4B, 0x4B, 0x33CD}, - {0x4B, 0x4D, 0x33CE}, - {0x6B, 0x74, 0x33CF}, - {0x6C, 0x6D, 0x33D0}, - {0x6C, 0x6E, 0x33D1}, - {0x6C, 0x78, 0x33D3}, - {0x6D, 0x62, 0x33D4}, - {0x50, 0x48, 0x33D7}, - {0x50, 0x52, 0x33DA}, - {0x73, 0x72, 0x33DB}, - {0x53, 0x76, 0x33DC}, - {0x57, 0x62, 0x33DD}, - {0x66, 0x66, 0xFB00}, - {0x66, 0x69, 0xFB01}, - {0x66, 0x6C, 0xFB02}, - {0x73, 0x74, 0xFB06}, - {0, 0, 0} - }, *c; - - int nc = -1; - - for (c = composetbl; c->first; c++) { - if (c->first == first && c->second == second) - return c->composed; - } - - if (recurse == 0) { - nc = check_compose_internal(second, first, 1); - if (nc == -1) - nc = check_compose_internal(toupper(first), toupper(second), 1); - if (nc == -1) - nc = check_compose_internal(toupper(second), toupper(first), 1); - } - return nc; -} - -int check_compose(int first, int second) -{ - return check_compose_internal(first, second, 0); -} - -int decode_codepage(char *cp_name) -{ - char *s, *d; - const struct cp_list_item *cpi; - int codepage = -1; - CPINFO cpinfo; - - if (!cp_name || !*cp_name) - return CP_UTF8; /* default */ - - for (cpi = cp_list; cpi->name; cpi++) { - s = cp_name; - d = cpi->name; - for (;;) { - while (*s && !isalnum(*s) && *s != ':') - s++; - while (*d && !isalnum(*d) && *d != ':') - d++; - if (*s == 0) { - codepage = cpi->codepage; - if (codepage == CP_UTF8) - goto break_break; - if (codepage == -1) - return codepage; - if (codepage == 0) { - codepage = 65536 + (cpi - cp_list); - goto break_break; - } - - if (GetCPInfo(codepage, &cpinfo) != 0) - goto break_break; - } - if (tolower((unsigned char)*s++) != tolower((unsigned char)*d++)) - break; - } - } - - d = cp_name; - if (tolower((unsigned char)d[0]) == 'c' && - tolower((unsigned char)d[1]) == 'p') - d += 2; - if (tolower((unsigned char)d[0]) == 'i' && - tolower((unsigned char)d[1]) == 'b' && - tolower((unsigned char)d[2]) == 'm') - d += 3; - for (s = d; *s >= '0' && *s <= '9'; s++); - if (*s == 0 && s != d) - codepage = atoi(d); /* CP999 or IBM999 */ - - if (codepage == CP_ACP) - codepage = GetACP(); - if (codepage == CP_OEMCP) - codepage = GetOEMCP(); - if (codepage > 65535) - codepage = -2; - - break_break:; - if (codepage != -1) { - if (codepage != CP_UTF8 && codepage < 65536) { - if (GetCPInfo(codepage, &cpinfo) == 0) { - codepage = -2; - } else if (cpinfo.MaxCharSize > 1) - codepage = -3; - } - } - if (codepage == -1 && *cp_name) - codepage = -2; - return codepage; -} - -const char *cp_name(int codepage) -{ - const struct cp_list_item *cpi, *cpno; - static char buf[32]; - - if (codepage == -1) { - sprintf(buf, "Use font encoding"); - return buf; - } - - if (codepage > 0 && codepage < 65536) - sprintf(buf, "CP%03d", codepage); - else - *buf = 0; - - if (codepage >= 65536) { - cpno = 0; - for (cpi = cp_list; cpi->name; cpi++) - if (cpi == cp_list + (codepage - 65536)) { - cpno = cpi; - break; - } - if (cpno) - for (cpi = cp_list; cpi->name; cpi++) { - if (cpno->cp_table == cpi->cp_table) - return cpi->name; - } - } else { - for (cpi = cp_list; cpi->name; cpi++) { - if (codepage == cpi->codepage) - return cpi->name; - } - } - return buf; -} - -/* - * Return the nth code page in the list, for use in the GUI - * configurer. - */ -const char *cp_enumerate(int index) -{ - if (index < 0 || index >= lenof(cp_list)) - return NULL; - return cp_list[index].name; -} - -void get_unitab(int codepage, wchar_t * unitab, int ftype) -{ - char tbuf[4]; - int i, max = 256, flg = MB_ERR_INVALID_CHARS; - - if (ftype) - flg |= MB_USEGLYPHCHARS; - if (ftype == 2) - max = 128; - - if (codepage == CP_UTF8) { - for (i = 0; i < max; i++) - unitab[i] = i; - return; - } - - if (codepage == CP_ACP) - codepage = GetACP(); - else if (codepage == CP_OEMCP) - codepage = GetOEMCP(); - - if (codepage > 0 && codepage < 65536) { - for (i = 0; i < max; i++) { - tbuf[0] = i; - - if (mb_to_wc(codepage, flg, tbuf, 1, unitab + i, 1) - != 1) - unitab[i] = 0xFFFD; - } - } else { - int j = 256 - cp_list[codepage & 0xFFFF].cp_size; - for (i = 0; i < max; i++) - unitab[i] = i; - for (i = j; i < max; i++) - unitab[i] = cp_list[codepage & 0xFFFF].cp_table[i - j]; - } -} - -int wc_to_mb(int codepage, int flags, const wchar_t *wcstr, int wclen, - char *mbstr, int mblen, const char *defchr, - struct unicode_data *ucsdata) -{ - char *p; - int i; - if (ucsdata && codepage == ucsdata->line_codepage && ucsdata->uni_tbl) { - /* Do this by array lookup if we can. */ - if (wclen < 0) { - for (wclen = 0; wcstr[wclen++] ;); /* will include the NUL */ - } - for (p = mbstr, i = 0; i < wclen; i++) { - wchar_t ch = wcstr[i]; - int by; - char *p1; - - #define WRITECH(chr) do \ - { \ - assert(p - mbstr < mblen); \ - *p++ = (char)(chr); \ - } while (0) - - if (ucsdata->uni_tbl && - (p1 = ucsdata->uni_tbl[(ch >> 8) & 0xFF]) != NULL && - (by = p1[ch & 0xFF]) != '\0') - WRITECH(by); - else if (ch < 0x80) - WRITECH(ch); - else if (defchr) - for (const char *q = defchr; *q; q++) - WRITECH(*q); -#if 1 - else - WRITECH('.'); -#endif - - #undef WRITECH - } - return p - mbstr; - } else { - int defused; - return WideCharToMultiByte(codepage, flags, wcstr, wclen, - mbstr, mblen, defchr, &defused); - } -} - -int mb_to_wc(int codepage, int flags, const char *mbstr, int mblen, - wchar_t *wcstr, int wclen) -{ - return MultiByteToWideChar(codepage, flags, mbstr, mblen, wcstr, wclen); -} - -bool is_dbcs_leadbyte(int codepage, char byte) -{ - return IsDBCSLeadByteEx(codepage, byte); -} diff --git a/WINDOWS/WINUTILS.C b/WINDOWS/WINUTILS.C deleted file mode 100644 index dec8984b..00000000 --- a/WINDOWS/WINUTILS.C +++ /dev/null @@ -1,788 +0,0 @@ -/* - * winutils.c: miscellaneous Windows utilities for GUI apps - */ - -#include <stdio.h> -#include <stdlib.h> -#include <ctype.h> - -#include "putty.h" -#include "misc.h" - -#ifdef TESTMODE -/* Definitions to allow this module to be compiled standalone for testing - * split_into_argv(). */ -#define smalloc malloc -#define srealloc realloc -#define sfree free -#endif - -/* - * GetOpenFileName/GetSaveFileName tend to muck around with the process' - * working directory on at least some versions of Windows. - * Here's a wrapper that gives more control over this, and hides a little - * bit of other grottiness. - */ - -struct filereq_tag { - TCHAR cwd[MAX_PATH]; -}; - -/* - * `of' is expected to be initialised with most interesting fields, but - * this function does some administrivia. (assume `of' was memset to 0) - * save==1 -> GetSaveFileName; save==0 -> GetOpenFileName - * `state' is optional. - */ -bool request_file(filereq *state, OPENFILENAME *of, bool preserve, bool save) -{ - TCHAR cwd[MAX_PATH]; /* process CWD */ - bool ret; - - /* Get process CWD */ - if (preserve) { - DWORD r = GetCurrentDirectory(lenof(cwd), cwd); - if (r == 0 || r >= lenof(cwd)) - /* Didn't work, oh well. Stop trying to be clever. */ - preserve = false; - } - - /* Open the file requester, maybe setting lpstrInitialDir */ - { -#ifdef OPENFILENAME_SIZE_VERSION_400 - of->lStructSize = OPENFILENAME_SIZE_VERSION_400; -#else - of->lStructSize = sizeof(*of); -#endif - of->lpstrInitialDir = (state && state->cwd[0]) ? state->cwd : NULL; - /* Actually put up the requester. */ - ret = save ? GetSaveFileName(of) : GetOpenFileName(of); - } - - /* Get CWD left by requester */ - if (state) { - DWORD r = GetCurrentDirectory(lenof(state->cwd), state->cwd); - if (r == 0 || r >= lenof(state->cwd)) - /* Didn't work, oh well. */ - state->cwd[0] = '\0'; - } - - /* Restore process CWD */ - if (preserve) - /* If it fails, there's not much we can do. */ - (void) SetCurrentDirectory(cwd); - - return ret; -} - -filereq *filereq_new(void) -{ - filereq *ret = snew(filereq); - ret->cwd[0] = '\0'; - return ret; -} - -void filereq_free(filereq *state) -{ - sfree(state); -} - -/* - * Message box with optional context help. - */ - -static HWND message_box_owner; - -/* Callback function to launch context help. */ -static VOID CALLBACK message_box_help_callback(LPHELPINFO lpHelpInfo) -{ - const char *context = NULL; -#define CHECK_CTX(name) \ - do { \ - if (lpHelpInfo->dwContextId == WINHELP_CTXID_ ## name) \ - context = WINHELP_CTX_ ## name; \ - } while (0) - CHECK_CTX(errors_hostkey_absent); - CHECK_CTX(errors_hostkey_changed); - CHECK_CTX(errors_cantloadkey); - CHECK_CTX(option_cleanup); - CHECK_CTX(pgp_fingerprints); -#undef CHECK_CTX - if (context) - launch_help(message_box_owner, context); -} - -int message_box(HWND owner, LPCTSTR text, LPCTSTR caption, - DWORD style, DWORD helpctxid) -{ - MSGBOXPARAMS mbox; - - /* - * We use MessageBoxIndirect() because it allows us to specify a - * callback function for the Help button. - */ - mbox.cbSize = sizeof(mbox); - /* Assumes the globals `hinst' and `hwnd' have sensible values. */ - mbox.hInstance = hinst; - mbox.hwndOwner = message_box_owner = owner; - mbox.lpfnMsgBoxCallback = &message_box_help_callback; - mbox.dwLanguageId = LANG_NEUTRAL; - mbox.lpszText = text; - mbox.lpszCaption = caption; - mbox.dwContextHelpId = helpctxid; - mbox.dwStyle = style; - if (helpctxid != 0 && has_help()) mbox.dwStyle |= MB_HELP; - return MessageBoxIndirect(&mbox); -} - -/* - * Display the fingerprints of the PGP Master Keys to the user. - */ -void pgp_fingerprints_msgbox(HWND owner) -{ - message_box( - owner, - "These are the fingerprints of the PuTTY PGP Master Keys. They can\n" - "be used to establish a trust path from this executable to another\n" - "one. See the manual for more information.\n" - "(Note: these fingerprints have nothing to do with SSH!)\n" - "\n" - "PuTTY Master Key as of " PGP_MASTER_KEY_YEAR - " (" PGP_MASTER_KEY_DETAILS "):\n" - " " PGP_MASTER_KEY_FP "\n\n" - "Previous Master Key (" PGP_PREV_MASTER_KEY_YEAR - ", " PGP_PREV_MASTER_KEY_DETAILS "):\n" - " " PGP_PREV_MASTER_KEY_FP, - "PGP fingerprints", MB_ICONINFORMATION | MB_OK, - HELPCTXID(pgp_fingerprints)); -} - -/* - * Helper function to remove the border around a dialog item such as - * a read-only edit control. - */ -void MakeDlgItemBorderless(HWND parent, int id) -{ - HWND child = GetDlgItem(parent, id); - LONG_PTR style = GetWindowLongPtr(child, GWL_STYLE); - LONG_PTR exstyle = GetWindowLongPtr(child, GWL_EXSTYLE); - style &= ~WS_BORDER; - exstyle &= ~(WS_EX_CLIENTEDGE | WS_EX_STATICEDGE | WS_EX_WINDOWEDGE); - SetWindowLongPtr(child, GWL_STYLE, style); - SetWindowLongPtr(child, GWL_EXSTYLE, exstyle); - SetWindowPos(child, NULL, 0, 0, 0, 0, - SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); -} - -/* - * Handy wrapper around GetDlgItemText which doesn't make you invent - * an arbitrary length limit on the output string. Returned string is - * dynamically allocated; caller must free. - */ -char *GetDlgItemText_alloc(HWND hwnd, int id) -{ - char *ret = NULL; - size_t size = 0; - - do { - sgrowarray_nm(ret, size, size); - GetDlgItemText(hwnd, id, ret, size); - } while (!memchr(ret, '\0', size-1)); - - return ret; -} - -/* - * Split a complete command line into argc/argv, attempting to do it - * exactly the same way the Visual Studio C library would do it (so - * that our console utilities, which receive argc and argv already - * broken apart by the C library, will have their command lines - * processed in the same way as the GUI utilities which get a whole - * command line and must call this function). - * - * Does not modify the input command line. - * - * The final parameter (argstart) is used to return a second array - * of char * pointers, the same length as argv, each one pointing - * at the start of the corresponding element of argv in the - * original command line. So if you get half way through processing - * your command line in argc/argv form and then decide you want to - * treat the rest as a raw string, you can. If you don't want to, - * `argstart' can be safely left NULL. - */ - -/* - * The precise argument-breaking rules vary with compiler version, or - * rather, with the crt0-type startup code that comes with each - * compiler's C library. We do our best to match the compiler version, - * so that we faithfully imitate in our GUI utilities what the - * corresponding set of CLI utilities can't be prevented from doing. - * - * The basic rules are: - * - * - Single quotes are not special characters. - * - * - Double quotes are removed, but within them spaces cease to be - * special. - * - * - Backslashes are _only_ special when a sequence of them appear - * just before a double quote. In this situation, they are treated - * like C backslashes: so \" just gives a literal quote, \\" gives - * a literal backslash and then opens or closes a double-quoted - * segment, \\\" gives a literal backslash and then a literal - * quote, \\\\" gives two literal backslashes and then opens/closes - * a double-quoted segment, and so forth. Note that this behaviour - * is identical inside and outside double quotes. - * - * - Two successive double quotes become one literal double quote, - * but only _inside_ a double-quoted segment. Outside, they just - * form an empty double-quoted segment (which may cause an empty - * argument word). - * - * That only leaves the interesting question of what happens when one - * or more backslashes precedes two or more double quotes, starting - * inside a double-quoted string. - * - * I investigated this in an ordinary CLI program, using the - * toolchain's crt0 to split a command line of the form - * - * "a\\\"""b c" d - * - * Here I tabulate number of backslashes (across the top) against - * number of quotes (down the left), and indicate how many backslashes - * are output, how many quotes are output, and whether a quoted - * segment is open at the end of the sequence: - * - * backslashes - * - * 0 1 2 3 4 - * - * 0 0,0,y | 1,0,y 2,0,y 3,0,y 4,0,y - * --------+----------------------------- - * 1 0,0,n | 0,1,y 1,0,n 1,1,y 2,0,n - * q 2 0,1,y | 0,1,n 1,1,y 1,1,n 2,1,y - * u 3 0,1,n | 0,2,y 1,1,n 1,2,y 2,1,n - * o 4 0,2,y | 0,2,n 1,2,y 1,2,n 2,2,y - * t 5 0,2,n | 0,3,y 1,2,n 1,3,y 2,2,n - * e 6 0,3,y | 0,3,n 1,3,y 1,3,n 2,3,y - * s 7 0,3,n | 0,4,y 1,3,n 1,4,y 2,3,n - * 8 0,4,y | 0,4,n 1,4,y 1,4,n 2,4,y - * - * The row at the top of this table, with quotes=0, demonstrates what - * I claimed above, that when a sequence of backslashes are not - * followed by a double quote, they don't act specially at all. The - * rest of the table shows that the backslashes escape each other in - * pairs (so that with 2n or 2n+1 input backslashes you get n output - * ones); if there's an odd number of input backslashes then the last - * one escapes the first double quote (so you get a literal quote and - * enter a quoted string); thereafter, each input quote character - * either opens or closes a quoted string, and if it closes one, it - * generates a literal " as a side effect. - * - * But here's the corresponding table from the older Visual Studio 7: - * - * backslashes - * - * 0 1 2 3 4 - * - * 0 0,0,y | 1,0,y 2,0,y 3,0,y 4,0,y - * --------+----------------------------- - * 1 0,0,n | 0,1,y 1,0,n 1,1,y 2,0,n - * q 2 0,1,n | 0,1,n 1,1,n 1,1,n 2,1,n - * u 3 0,1,y | 0,2,n 1,1,y 1,2,n 2,1,y - * o 4 0,1,n | 0,2,y 1,1,n 1,2,y 2,1,n - * t 5 0,2,n | 0,2,n 1,2,n 1,2,n 2,2,n - * e 6 0,2,y | 0,3,n 1,2,y 1,3,n 2,2,y - * s 7 0,2,n | 0,3,y 1,2,n 1,3,y 2,2,n - * 8 0,3,n | 0,3,n 1,3,n 1,3,n 2,3,n - * 9 0,3,y | 0,4,n 1,3,y 1,4,n 2,3,y - * 10 0,3,n | 0,4,y 1,3,n 1,4,y 2,3,n - * 11 0,4,n | 0,4,n 1,4,n 1,4,n 2,4,n - * - * There is very weird mod-3 behaviour going on here in the - * number of quotes, and it even applies when there aren't any - * backslashes! How ghastly. - * - * With a bit of thought, this extremely odd diagram suddenly - * coalesced itself into a coherent, if still ghastly, model of - * how things work: - * - * - As before, backslashes are only special when one or more - * of them appear contiguously before at least one double - * quote. In this situation the backslashes do exactly what - * you'd expect: each one quotes the next thing in front of - * it, so you end up with n/2 literal backslashes (if n is - * even) or (n-1)/2 literal backslashes and a literal quote - * (if n is odd). In the latter case the double quote - * character right after the backslashes is used up. - * - * - After that, any remaining double quotes are processed. A - * string of contiguous unescaped double quotes has a mod-3 - * behaviour: - * - * * inside a quoted segment, a quote ends the segment. - * * _immediately_ after ending a quoted segment, a quote - * simply produces a literal quote. - * * otherwise, outside a quoted segment, a quote begins a - * quoted segment. - * - * So, for example, if we started inside a quoted segment - * then two contiguous quotes would close the segment and - * produce a literal quote; three would close the segment, - * produce a literal quote, and open a new segment. If we - * started outside a quoted segment, then two contiguous - * quotes would open and then close a segment, producing no - * output (but potentially creating a zero-length argument); - * but three quotes would open and close a segment and then - * produce a literal quote. - */ - -/* - * We select between two behaviours depending on the version of Visual - * Studio (see large comment below). I don't know exactly when the bug - * fix happened, but I know that VS7 had the odd mod-3 behaviour. - */ -#if _MSC_VER < 1400 -#define MOD3 1 -#else -#define MOD3 0 -#endif - -void split_into_argv(char *cmdline, int *argc, char ***argv, - char ***argstart) -{ - char *p; - char *outputline, *q; - char **outputargv, **outputargstart; - int outputargc; - - /* - * First deal with the simplest of all special cases: if there - * aren't any arguments, return 0,NULL,NULL. - */ - while (*cmdline && isspace(*cmdline)) cmdline++; - if (!*cmdline) { - if (argc) *argc = 0; - if (argv) *argv = NULL; - if (argstart) *argstart = NULL; - return; - } - - /* - * This will guaranteeably be big enough; we can realloc it - * down later. - */ - outputline = snewn(1+strlen(cmdline), char); - outputargv = snewn(strlen(cmdline)+1 / 2, char *); - outputargstart = snewn(strlen(cmdline)+1 / 2, char *); - - p = cmdline; q = outputline; outputargc = 0; - - while (*p) { - bool quote; - - /* Skip whitespace searching for start of argument. */ - while (*p && isspace(*p)) p++; - if (!*p) break; - - /* We have an argument; start it. */ - outputargv[outputargc] = q; - outputargstart[outputargc] = p; - outputargc++; - quote = false; - - /* Copy data into the argument until it's finished. */ - while (*p) { - if (!quote && isspace(*p)) - break; /* argument is finished */ - - if (*p == '"' || *p == '\\') { - /* - * We have a sequence of zero or more backslashes - * followed by a sequence of zero or more quotes. - * Count up how many of each, and then deal with - * them as appropriate. - */ - int i, slashes = 0, quotes = 0; - while (*p == '\\') slashes++, p++; - while (*p == '"') quotes++, p++; - - if (!quotes) { - /* - * Special case: if there are no quotes, - * slashes are not special at all, so just copy - * n slashes to the output string. - */ - while (slashes--) *q++ = '\\'; - } else { - /* Slashes annihilate in pairs. */ - while (slashes >= 2) slashes -= 2, *q++ = '\\'; - - /* One remaining slash takes out the first quote. */ - if (slashes) quotes--, *q++ = '"'; - - if (quotes > 0) { - /* Outside a quote segment, a quote starts one. */ - if (!quote) quotes--; - -#if !MOD3 - /* New behaviour: produce n/2 literal quotes... */ - for (i = 2; i <= quotes; i += 2) *q++ = '"'; - /* ... and end in a quote segment iff 2 divides n. */ - quote = (quotes % 2 == 0); -#else - /* Old behaviour: produce (n+1)/3 literal quotes... */ - for (i = 3; i <= quotes+1; i += 3) *q++ = '"'; - /* ... and end in a quote segment iff 3 divides n. */ - quote = (quotes % 3 == 0); -#endif - } - } - } else { - *q++ = *p++; - } - } - - /* At the end of an argument, just append a trailing NUL. */ - *q++ = '\0'; - } - - outputargv = sresize(outputargv, outputargc, char *); - outputargstart = sresize(outputargstart, outputargc, char *); - - if (argc) *argc = outputargc; - if (argv) *argv = outputargv; else sfree(outputargv); - if (argstart) *argstart = outputargstart; else sfree(outputargstart); -} - -#ifdef TESTMODE - -const struct argv_test { - const char *cmdline; - const char *argv[10]; -} argv_tests[] = { - /* - * We generate this set of tests by invoking ourself with - * `-generate'. - */ -#if !MOD3 - /* Newer behaviour, with no weird mod-3 glitch. */ - {"ab c\" d", {"ab", "c d", NULL}}, - {"a\"b c\" d", {"ab c", "d", NULL}}, - {"a\"\"b c\" d", {"ab", "c d", NULL}}, - {"a\"\"\"b c\" d", {"a\"b c", "d", NULL}}, - {"a\"\"\"\"b c\" d", {"a\"b", "c d", NULL}}, - {"a\"\"\"\"\"b c\" d", {"a\"\"b c", "d", NULL}}, - {"a\"\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}}, - {"a\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b c", "d", NULL}}, - {"a\"\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b", "c d", NULL}}, - {"a\\b c\" d", {"a\\b", "c d", NULL}}, - {"a\\\"b c\" d", {"a\"b", "c d", NULL}}, - {"a\\\"\"b c\" d", {"a\"b c", "d", NULL}}, - {"a\\\"\"\"b c\" d", {"a\"b", "c d", NULL}}, - {"a\\\"\"\"\"b c\" d", {"a\"\"b c", "d", NULL}}, - {"a\\\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}}, - {"a\\\"\"\"\"\"\"b c\" d", {"a\"\"\"b c", "d", NULL}}, - {"a\\\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b", "c d", NULL}}, - {"a\\\"\"\"\"\"\"\"\"b c\" d", {"a\"\"\"\"b c", "d", NULL}}, - {"a\\\\b c\" d", {"a\\\\b", "c d", NULL}}, - {"a\\\\\"b c\" d", {"a\\b c", "d", NULL}}, - {"a\\\\\"\"b c\" d", {"a\\b", "c d", NULL}}, - {"a\\\\\"\"\"b c\" d", {"a\\\"b c", "d", NULL}}, - {"a\\\\\"\"\"\"b c\" d", {"a\\\"b", "c d", NULL}}, - {"a\\\\\"\"\"\"\"b c\" d", {"a\\\"\"b c", "d", NULL}}, - {"a\\\\\"\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}}, - {"a\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b c", "d", NULL}}, - {"a\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b", "c d", NULL}}, - {"a\\\\\\b c\" d", {"a\\\\\\b", "c d", NULL}}, - {"a\\\\\\\"b c\" d", {"a\\\"b", "c d", NULL}}, - {"a\\\\\\\"\"b c\" d", {"a\\\"b c", "d", NULL}}, - {"a\\\\\\\"\"\"b c\" d", {"a\\\"b", "c d", NULL}}, - {"a\\\\\\\"\"\"\"b c\" d", {"a\\\"\"b c", "d", NULL}}, - {"a\\\\\\\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}}, - {"a\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b c", "d", NULL}}, - {"a\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b", "c d", NULL}}, - {"a\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"\"b c", "d", NULL}}, - {"a\\\\\\\\b c\" d", {"a\\\\\\\\b", "c d", NULL}}, - {"a\\\\\\\\\"b c\" d", {"a\\\\b c", "d", NULL}}, - {"a\\\\\\\\\"\"b c\" d", {"a\\\\b", "c d", NULL}}, - {"a\\\\\\\\\"\"\"b c\" d", {"a\\\\\"b c", "d", NULL}}, - {"a\\\\\\\\\"\"\"\"b c\" d", {"a\\\\\"b", "c d", NULL}}, - {"a\\\\\\\\\"\"\"\"\"b c\" d", {"a\\\\\"\"b c", "d", NULL}}, - {"a\\\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\\\"\"b", "c d", NULL}}, - {"a\\\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"\"b c", "d", NULL}}, - {"a\\\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"\"b", "c d", NULL}}, - {"\"ab c\" d", {"ab c", "d", NULL}}, - {"\"a\"b c\" d", {"ab", "c d", NULL}}, - {"\"a\"\"b c\" d", {"a\"b c", "d", NULL}}, - {"\"a\"\"\"b c\" d", {"a\"b", "c d", NULL}}, - {"\"a\"\"\"\"b c\" d", {"a\"\"b c", "d", NULL}}, - {"\"a\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}}, - {"\"a\"\"\"\"\"\"b c\" d", {"a\"\"\"b c", "d", NULL}}, - {"\"a\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b", "c d", NULL}}, - {"\"a\"\"\"\"\"\"\"\"b c\" d", {"a\"\"\"\"b c", "d", NULL}}, - {"\"a\\b c\" d", {"a\\b c", "d", NULL}}, - {"\"a\\\"b c\" d", {"a\"b c", "d", NULL}}, - {"\"a\\\"\"b c\" d", {"a\"b", "c d", NULL}}, - {"\"a\\\"\"\"b c\" d", {"a\"\"b c", "d", NULL}}, - {"\"a\\\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}}, - {"\"a\\\"\"\"\"\"b c\" d", {"a\"\"\"b c", "d", NULL}}, - {"\"a\\\"\"\"\"\"\"b c\" d", {"a\"\"\"b", "c d", NULL}}, - {"\"a\\\"\"\"\"\"\"\"b c\" d", {"a\"\"\"\"b c", "d", NULL}}, - {"\"a\\\"\"\"\"\"\"\"\"b c\" d", {"a\"\"\"\"b", "c d", NULL}}, - {"\"a\\\\b c\" d", {"a\\\\b c", "d", NULL}}, - {"\"a\\\\\"b c\" d", {"a\\b", "c d", NULL}}, - {"\"a\\\\\"\"b c\" d", {"a\\\"b c", "d", NULL}}, - {"\"a\\\\\"\"\"b c\" d", {"a\\\"b", "c d", NULL}}, - {"\"a\\\\\"\"\"\"b c\" d", {"a\\\"\"b c", "d", NULL}}, - {"\"a\\\\\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}}, - {"\"a\\\\\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b c", "d", NULL}}, - {"\"a\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b", "c d", NULL}}, - {"\"a\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"\"b c", "d", NULL}}, - {"\"a\\\\\\b c\" d", {"a\\\\\\b c", "d", NULL}}, - {"\"a\\\\\\\"b c\" d", {"a\\\"b c", "d", NULL}}, - {"\"a\\\\\\\"\"b c\" d", {"a\\\"b", "c d", NULL}}, - {"\"a\\\\\\\"\"\"b c\" d", {"a\\\"\"b c", "d", NULL}}, - {"\"a\\\\\\\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}}, - {"\"a\\\\\\\"\"\"\"\"b c\" d", {"a\\\"\"\"b c", "d", NULL}}, - {"\"a\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b", "c d", NULL}}, - {"\"a\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"\"b c", "d", NULL}}, - {"\"a\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"\"b", "c d", NULL}}, - {"\"a\\\\\\\\b c\" d", {"a\\\\\\\\b c", "d", NULL}}, - {"\"a\\\\\\\\\"b c\" d", {"a\\\\b", "c d", NULL}}, - {"\"a\\\\\\\\\"\"b c\" d", {"a\\\\\"b c", "d", NULL}}, - {"\"a\\\\\\\\\"\"\"b c\" d", {"a\\\\\"b", "c d", NULL}}, - {"\"a\\\\\\\\\"\"\"\"b c\" d", {"a\\\\\"\"b c", "d", NULL}}, - {"\"a\\\\\\\\\"\"\"\"\"b c\" d", {"a\\\\\"\"b", "c d", NULL}}, - {"\"a\\\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\\\"\"\"b c", "d", NULL}}, - {"\"a\\\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"\"b", "c d", NULL}}, - {"\"a\\\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"\"\"b c", "d", NULL}}, -#else /* MOD3 */ - /* VS7 mod-3 behaviour. */ - {"ab c\" d", {"ab", "c d", NULL}}, - {"a\"b c\" d", {"ab c", "d", NULL}}, - {"a\"\"b c\" d", {"ab", "c d", NULL}}, - {"a\"\"\"b c\" d", {"a\"b", "c d", NULL}}, - {"a\"\"\"\"b c\" d", {"a\"b c", "d", NULL}}, - {"a\"\"\"\"\"b c\" d", {"a\"b", "c d", NULL}}, - {"a\"\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}}, - {"a\"\"\"\"\"\"\"b c\" d", {"a\"\"b c", "d", NULL}}, - {"a\"\"\"\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}}, - {"a\\b c\" d", {"a\\b", "c d", NULL}}, - {"a\\\"b c\" d", {"a\"b", "c d", NULL}}, - {"a\\\"\"b c\" d", {"a\"b c", "d", NULL}}, - {"a\\\"\"\"b c\" d", {"a\"b", "c d", NULL}}, - {"a\\\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}}, - {"a\\\"\"\"\"\"b c\" d", {"a\"\"b c", "d", NULL}}, - {"a\\\"\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}}, - {"a\\\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b", "c d", NULL}}, - {"a\\\"\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b c", "d", NULL}}, - {"a\\\\b c\" d", {"a\\\\b", "c d", NULL}}, - {"a\\\\\"b c\" d", {"a\\b c", "d", NULL}}, - {"a\\\\\"\"b c\" d", {"a\\b", "c d", NULL}}, - {"a\\\\\"\"\"b c\" d", {"a\\\"b", "c d", NULL}}, - {"a\\\\\"\"\"\"b c\" d", {"a\\\"b c", "d", NULL}}, - {"a\\\\\"\"\"\"\"b c\" d", {"a\\\"b", "c d", NULL}}, - {"a\\\\\"\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}}, - {"a\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\"\"b c", "d", NULL}}, - {"a\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}}, - {"a\\\\\\b c\" d", {"a\\\\\\b", "c d", NULL}}, - {"a\\\\\\\"b c\" d", {"a\\\"b", "c d", NULL}}, - {"a\\\\\\\"\"b c\" d", {"a\\\"b c", "d", NULL}}, - {"a\\\\\\\"\"\"b c\" d", {"a\\\"b", "c d", NULL}}, - {"a\\\\\\\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}}, - {"a\\\\\\\"\"\"\"\"b c\" d", {"a\\\"\"b c", "d", NULL}}, - {"a\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}}, - {"a\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b", "c d", NULL}}, - {"a\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b c", "d", NULL}}, - {"a\\\\\\\\b c\" d", {"a\\\\\\\\b", "c d", NULL}}, - {"a\\\\\\\\\"b c\" d", {"a\\\\b c", "d", NULL}}, - {"a\\\\\\\\\"\"b c\" d", {"a\\\\b", "c d", NULL}}, - {"a\\\\\\\\\"\"\"b c\" d", {"a\\\\\"b", "c d", NULL}}, - {"a\\\\\\\\\"\"\"\"b c\" d", {"a\\\\\"b c", "d", NULL}}, - {"a\\\\\\\\\"\"\"\"\"b c\" d", {"a\\\\\"b", "c d", NULL}}, - {"a\\\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\\\"\"b", "c d", NULL}}, - {"a\\\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"b c", "d", NULL}}, - {"a\\\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"b", "c d", NULL}}, - {"\"ab c\" d", {"ab c", "d", NULL}}, - {"\"a\"b c\" d", {"ab", "c d", NULL}}, - {"\"a\"\"b c\" d", {"a\"b", "c d", NULL}}, - {"\"a\"\"\"b c\" d", {"a\"b c", "d", NULL}}, - {"\"a\"\"\"\"b c\" d", {"a\"b", "c d", NULL}}, - {"\"a\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}}, - {"\"a\"\"\"\"\"\"b c\" d", {"a\"\"b c", "d", NULL}}, - {"\"a\"\"\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}}, - {"\"a\"\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b", "c d", NULL}}, - {"\"a\\b c\" d", {"a\\b c", "d", NULL}}, - {"\"a\\\"b c\" d", {"a\"b c", "d", NULL}}, - {"\"a\\\"\"b c\" d", {"a\"b", "c d", NULL}}, - {"\"a\\\"\"\"b c\" d", {"a\"\"b", "c d", NULL}}, - {"\"a\\\"\"\"\"b c\" d", {"a\"\"b c", "d", NULL}}, - {"\"a\\\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}}, - {"\"a\\\"\"\"\"\"\"b c\" d", {"a\"\"\"b", "c d", NULL}}, - {"\"a\\\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b c", "d", NULL}}, - {"\"a\\\"\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b", "c d", NULL}}, - {"\"a\\\\b c\" d", {"a\\\\b c", "d", NULL}}, - {"\"a\\\\\"b c\" d", {"a\\b", "c d", NULL}}, - {"\"a\\\\\"\"b c\" d", {"a\\\"b", "c d", NULL}}, - {"\"a\\\\\"\"\"b c\" d", {"a\\\"b c", "d", NULL}}, - {"\"a\\\\\"\"\"\"b c\" d", {"a\\\"b", "c d", NULL}}, - {"\"a\\\\\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}}, - {"\"a\\\\\"\"\"\"\"\"b c\" d", {"a\\\"\"b c", "d", NULL}}, - {"\"a\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}}, - {"\"a\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b", "c d", NULL}}, - {"\"a\\\\\\b c\" d", {"a\\\\\\b c", "d", NULL}}, - {"\"a\\\\\\\"b c\" d", {"a\\\"b c", "d", NULL}}, - {"\"a\\\\\\\"\"b c\" d", {"a\\\"b", "c d", NULL}}, - {"\"a\\\\\\\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}}, - {"\"a\\\\\\\"\"\"\"b c\" d", {"a\\\"\"b c", "d", NULL}}, - {"\"a\\\\\\\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}}, - {"\"a\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b", "c d", NULL}}, - {"\"a\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b c", "d", NULL}}, - {"\"a\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b", "c d", NULL}}, - {"\"a\\\\\\\\b c\" d", {"a\\\\\\\\b c", "d", NULL}}, - {"\"a\\\\\\\\\"b c\" d", {"a\\\\b", "c d", NULL}}, - {"\"a\\\\\\\\\"\"b c\" d", {"a\\\\\"b", "c d", NULL}}, - {"\"a\\\\\\\\\"\"\"b c\" d", {"a\\\\\"b c", "d", NULL}}, - {"\"a\\\\\\\\\"\"\"\"b c\" d", {"a\\\\\"b", "c d", NULL}}, - {"\"a\\\\\\\\\"\"\"\"\"b c\" d", {"a\\\\\"\"b", "c d", NULL}}, - {"\"a\\\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\\\"\"b c", "d", NULL}}, - {"\"a\\\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"b", "c d", NULL}}, - {"\"a\\\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"\"b", "c d", NULL}}, -#endif /* MOD3 */ -}; - -int main(int argc, char **argv) -{ - int i, j; - - if (argc > 1) { - /* - * Generation of tests. - * - * Given `-splat <args>', we print out a C-style - * representation of each argument (in the form "a", "b", - * NULL), backslash-escaping each backslash and double - * quote. - * - * Given `-split <string>', we first doctor `string' by - * turning forward slashes into backslashes, single quotes - * into double quotes and underscores into spaces; and then - * we feed the resulting string to ourself with `-splat'. - * - * Given `-generate', we concoct a variety of fun test - * cases, encode them in quote-safe form (mapping \, " and - * space to /, ' and _ respectively) and feed each one to - * `-split'. - */ - if (!strcmp(argv[1], "-splat")) { - int i; - char *p; - for (i = 2; i < argc; i++) { - putchar('"'); - for (p = argv[i]; *p; p++) { - if (*p == '\\' || *p == '"') - putchar('\\'); - putchar(*p); - } - printf("\", "); - } - printf("NULL"); - return 0; - } - - if (!strcmp(argv[1], "-split") && argc > 2) { - char *str = malloc(20 + strlen(argv[0]) + strlen(argv[2])); - char *p, *q; - - q = str + sprintf(str, "%s -splat ", argv[0]); - printf(" {\""); - for (p = argv[2]; *p; p++, q++) { - switch (*p) { - case '/': printf("\\\\"); *q = '\\'; break; - case '\'': printf("\\\""); *q = '"'; break; - case '_': printf(" "); *q = ' '; break; - default: putchar(*p); *q = *p; break; - } - } - *p = '\0'; - printf("\", {"); - fflush(stdout); - - system(str); - - printf("}},\n"); - - return 0; - } - - if (!strcmp(argv[1], "-generate")) { - char *teststr, *p; - int i, initialquote, backslashes, quotes; - - teststr = malloc(200 + strlen(argv[0])); - - for (initialquote = 0; initialquote <= 1; initialquote++) { - for (backslashes = 0; backslashes < 5; backslashes++) { - for (quotes = 0; quotes < 9; quotes++) { - p = teststr + sprintf(teststr, "%s -split ", argv[0]); - if (initialquote) *p++ = '\''; - *p++ = 'a'; - for (i = 0; i < backslashes; i++) *p++ = '/'; - for (i = 0; i < quotes; i++) *p++ = '\''; - *p++ = 'b'; - *p++ = '_'; - *p++ = 'c'; - *p++ = '\''; - *p++ = '_'; - *p++ = 'd'; - *p = '\0'; - - system(teststr); - } - } - } - return 0; - } - - fprintf(stderr, "unrecognised option: \"%s\"\n", argv[1]); - return 1; - } - - /* - * If we get here, we were invoked with no arguments, so just - * run the tests. - */ - - for (i = 0; i < lenof(argv_tests); i++) { - int ac; - char **av; - - split_into_argv(argv_tests[i].cmdline, &ac, &av); - - for (j = 0; j < ac && argv_tests[i].argv[j]; j++) { - if (strcmp(av[j], argv_tests[i].argv[j])) { - printf("failed test %d (|%s|) arg %d: |%s| should be |%s|\n", - i, argv_tests[i].cmdline, - j, av[j], argv_tests[i].argv[j]); - } -#ifdef VERBOSE - else { - printf("test %d (|%s|) arg %d: |%s| == |%s|\n", - i, argv_tests[i].cmdline, - j, av[j], argv_tests[i].argv[j]); - } -#endif - } - if (j < ac) - printf("failed test %d (|%s|): %d args returned, should be %d\n", - i, argv_tests[i].cmdline, ac, j); - if (argv_tests[i].argv[j]) - printf("failed test %d (|%s|): %d args returned, should be more\n", - i, argv_tests[i].cmdline, ac); - } - - return 0; -} - -#endif diff --git a/WINDOWS/WINX11.C b/WINDOWS/WINX11.C deleted file mode 100644 index 800d8509..00000000 --- a/WINDOWS/WINX11.C +++ /dev/null @@ -1,19 +0,0 @@ -/* - * winx11.c: fetch local auth data for X forwarding. - */ - -#include <ctype.h> -#include <assert.h> -#include <stdlib.h> - -#include "putty.h" -#include "ssh.h" - -void platform_get_x11_auth(struct X11Display *disp, Conf *conf) -{ - char *xauthpath = conf_get_filename(conf, CONF_xauthfile)->path; - if (xauthpath[0]) - x11_get_auth_from_authfile(disp, xauthpath); -} - -const bool platform_uses_x11_unix_by_default = false; diff --git a/WINDOWS/WIN_RES.H b/WINDOWS/WIN_RES.H deleted file mode 100644 index d34f6852..00000000 --- a/WINDOWS/WIN_RES.H +++ /dev/null @@ -1,49 +0,0 @@ -/* - * win_res.h - constants shared between win_res.rc2 and the C code. - */ - -#ifndef PUTTY_WIN_RES_H -#define PUTTY_WIN_RES_H - -#define IDI_MAINICON 200 -#define IDI_CFGICON 201 - -#define IDD_MAINBOX 102 -#define IDD_LOGBOX 110 -#define IDD_ABOUTBOX 111 -#define IDD_RECONF 112 -#define IDD_LICENCEBOX 113 -#define IDD_HK_ABSENT 114 -#define IDD_HK_WRONG 115 -#define IDD_HK_MOREINFO 116 - -#define IDN_LIST 1001 -#define IDN_COPY 1002 - -#define IDA_ICON 1001 -#define IDA_TEXT 1002 -#define IDA_LICENCE 1003 -#define IDA_WEB 1004 - -#define IDC_TAB 1001 -#define IDC_TABSTATIC1 1002 -#define IDC_TABSTATIC2 1003 -#define IDC_TABLIST 1004 -#define IDC_HELPBTN 1005 -#define IDC_ABOUT 1006 - -#define IDC_HK_ICON 98 -#define IDC_HK_TITLE 99 -#define IDC_HK_ACCEPT 1001 -#define IDC_HK_ONCE 1000 -#define IDC_HK_FINGERPRINT 1002 -#define IDC_HK_MOREINFO 1003 - -#define IDC_HKI_SHA256 1000 -#define IDC_HKI_MD5 1001 -#define IDC_HKI_PUBKEY 1002 - -#define ID_CUSTOM_CHMFILE 2000 -#define TYPE_CUSTOM_CHMFILE 2000 - -#endif diff --git a/WINDOWS/WIN_RES.RC2 b/WINDOWS/WIN_RES.RC2 deleted file mode 100644 index ccec3122..00000000 --- a/WINDOWS/WIN_RES.RC2 +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Windows resources shared between PuTTY and PuTTYtel, to be #include'd - * after defining appropriate macros. - * - * Note that many of these strings mention PuTTY. Due to restrictions in - * VC's handling of string concatenation, this can't easily be fixed. - * It's fixed up at runtime. - * - * This file has the more or less arbitrary extension '.rc2' to avoid - * IDEs taking it to be a top-level resource script in its own right - * (which has been known to happen if the extension was '.rc'), and - * also to avoid the resource compiler ignoring everything included - * from it (which happens if the extension is '.h'). - */ - -#include "win_res.h" - -IDI_MAINICON ICON "putty.ico" - -IDI_CFGICON ICON "puttycfg.ico" - -/* Accelerators used: clw */ -IDD_ABOUTBOX DIALOG DISCARDABLE 140, 40, 270, 136 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "About PuTTY" -FONT 8, "MS Shell Dlg" -BEGIN - DEFPUSHBUTTON "&Close", IDOK, 216, 118, 48, 14 - PUSHBUTTON "View &Licence", IDA_LICENCE, 6, 118, 70, 14 - PUSHBUTTON "Visit &Web Site", IDA_WEB, 140, 118, 70, 14 - EDITTEXT IDA_TEXT, 10, 6, 250, 110, ES_READONLY | ES_MULTILINE | ES_CENTER, WS_EX_STATICEDGE -END - -/* Accelerators used: aco */ -IDD_MAINBOX DIALOG DISCARDABLE 0, 0, 300, 252 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "PuTTY Configuration" -FONT 8, "MS Shell Dlg" -CLASS "PuTTYConfigBox" -BEGIN -END - -/* Accelerators used: co */ -IDD_LOGBOX DIALOG DISCARDABLE 100, 20, 300, 119 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "PuTTY Event Log" -FONT 8, "MS Shell Dlg" -BEGIN - DEFPUSHBUTTON "&Close", IDOK, 135, 102, 44, 14 - PUSHBUTTON "C&opy", IDN_COPY, 81, 102, 44, 14 - LISTBOX IDN_LIST, 3, 3, 294, 95, LBS_HASSTRINGS | LBS_USETABSTOPS | WS_VSCROLL | LBS_EXTENDEDSEL -END - -/* No accelerators used */ -IDD_LICENCEBOX DIALOG DISCARDABLE 50, 50, 326, 239 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "PuTTY Licence" -FONT 8, "MS Shell Dlg" -BEGIN - DEFPUSHBUTTON "OK", IDOK, 148, 219, 44, 14 - - EDITTEXT IDA_TEXT, 10, 10, 306, 200, ES_READONLY | ES_MULTILINE | ES_LEFT, WS_EX_STATICEDGE -END - -/* No accelerators used */ -IDD_HK_ABSENT DIALOG DISCARDABLE 50, 50, 340, 148 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "PuTTY Security Alert" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "The server's host key is not cached in the registry. You have no", 100, 40, 20, 300, 8 - LTEXT "guarantee that the server is the computer you think it is.", 101, 40, 28, 300, 8 - LTEXT "The server's {KEYTYPE} key fingerprint is:", 102, 40, 40, 300, 8 - LTEXT "If you trust this host, press ""Accept"" to add the key to {APPNAME}'s", 103, 40, 60, 300, 8 - LTEXT "cache and carry on connecting.", 104, 40, 68, 300, 8 - LTEXT "If you want to carry on connecting just once, without adding the key", 105, 40, 80, 300, 8 - LTEXT "to the cache, press ""Connect Once"".", 106, 40, 88, 300, 8 - LTEXT "If you do not trust this host, press ""Cancel"" to abandon the connection.", 107, 40, 100, 300, 8 - - ICON "", IDC_HK_ICON, 10, 18, 0, 0 - - PUSHBUTTON "Cancel", IDCANCEL, 288, 128, 40, 14 - PUSHBUTTON "Accept", IDC_HK_ACCEPT, 168, 128, 40, 14 - PUSHBUTTON "Connect Once", IDC_HK_ONCE, 216, 128, 64, 14 - PUSHBUTTON "More info...", IDC_HK_MOREINFO, 60, 128, 64, 14 - PUSHBUTTON "Help", IDHELP, 12, 128, 40, 14 - - EDITTEXT IDC_HK_FINGERPRINT, 40, 48, 300, 12, ES_READONLY | ES_LEFT, 0 -END - -/* No accelerators used */ -IDD_HK_WRONG DIALOG DISCARDABLE 50, 50, 340, 188 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "PuTTY Security Alert" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "WARNING - POTENTIAL SECURITY BREACH!", IDC_HK_TITLE, 40, 20, 300, 12 - - LTEXT "The server's host key does not match the one {APPNAME} has cached in", 100, 40, 36, 300, 8 - LTEXT "the registry. This means that either the server administrator has", 101, 40, 44, 300, 8 - LTEXT "changed the host key, or you have actually connected to another", 102, 40, 52, 300, 8 - LTEXT "computer pretending to be the server.", 103, 40, 60, 300, 8 - LTEXT "The new {KEYTYPE} key fingerprint is:", 104, 40, 72, 300, 8 - LTEXT "If you were expecting this change and trust the new key, press", 105, 40, 92, 300, 8 - LTEXT """Accept"" to update {APPNAME}'s cache and continue connecting.", 106, 40, 100, 300, 8 - LTEXT "If you want to carry on connecting but without updating the cache,", 107, 40, 112, 300, 8 - LTEXT "press ""Connect Once"".", 108, 40, 120, 300, 8 - LTEXT "If you want to abandon the connection completely, press ""Cancel"".", 109, 40, 132, 300, 8 - LTEXT "Pressing ""Cancel"" is the ONLY guaranteed safe choice.", 110, 40, 140, 300, 8 - - ICON "", IDC_HK_ICON, 10, 16, 0, 0 - - PUSHBUTTON "Cancel", IDCANCEL, 288, 168, 40, 14 - PUSHBUTTON "Accept", IDC_HK_ACCEPT, 168, 168, 40, 14 - PUSHBUTTON "Connect Once", IDC_HK_ONCE, 216, 168, 64, 14 - PUSHBUTTON "More info...", IDC_HK_MOREINFO, 60, 168, 64, 14 - PUSHBUTTON "Help", IDHELP, 12, 168, 40, 14 - - EDITTEXT IDC_HK_FINGERPRINT, 40, 80, 300, 12, ES_READONLY | ES_LEFT, 0 -END - -/* Accelerators used: clw */ -IDD_HK_MOREINFO DIALOG DISCARDABLE 140, 40, 400, 156 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "PuTTY: information about the server's host key" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "SHA256 fingerprint:", 100, 12, 12, 80, 8 - EDITTEXT IDC_HKI_SHA256, 100, 10, 288, 12, ES_READONLY - LTEXT "MD5 fingerprint:", 101, 12, 28, 80, 8 - EDITTEXT IDC_HKI_MD5, 100, 26, 288, 12, ES_READONLY - LTEXT "Full public key:", 102, 12, 44, 376, 8 - EDITTEXT IDC_HKI_PUBKEY, 12, 54, 376, 64, ES_READONLY | ES_MULTILINE | ES_LEFT | ES_AUTOVSCROLL, WS_EX_STATICEDGE - DEFPUSHBUTTON "&Close", IDOK, 176, 130, 48, 14 -END - -#include "version.rc2" diff --git a/WINDOWS/installer.wxs b/WINDOWS/installer.wxs index b221320e..65a7f379 100644 --- a/WINDOWS/installer.wxs +++ b/WINDOWS/installer.wxs @@ -91,6 +91,10 @@ <?define Desktop_Shortcut_Component_GUID = "8999BBE1-F99E-4301-B7A6-480C19DE13B9" ?> <?endif ?> +<?ifndef HelpFilePath ?> + <?define HelpFilePath = "../doc/putty.chm" ?> +<?endif ?> + <?define ProgramName = "PuTTY$(var.Bitness)" ?> <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> @@ -117,12 +121,6 @@ Language="1033" Codepage="1252" Version="$(var.Winver)"> <!-- - We force the install scope to perMachine, largely because I - don't really understand how to make it usefully switchable - between the two. If anyone is a WiX expert and does want to - install PuTTY locally in a user account, I hope they'll send a - well explained patch! - $(var.Puttytextver) is again defined on the candle command line, and describes the version of PuTTY in human-readable form, e.g. "PuTTY 0.67" or "PuTTY development snapshot [foo]". @@ -131,8 +129,7 @@ Description="$(var.Puttytextver) installer" Manufacturer="Simon Tatham" InstallerVersion="$(var.InstallerVersion)" Languages="1033" - Compressed="yes" SummaryCodepage="1252" - InstallScope="perMachine" /> + Compressed="yes" SummaryCodepage="1252" /> <!-- Permit installing an arbitrary one of these PuTTY installers @@ -238,7 +235,7 @@ https://msdn.microsoft.com/en-us/library/windows/desktop/dd391569(v=vs.85).aspx <Component Id="HelpFile_Component" Guid="$(var.HelpFile_Component_GUID)"> <File Id="HelpFile_File" - Source="../doc/putty.chm" KeyPath="yes"> + Source="$(var.HelpFilePath)" KeyPath="yes"> <Shortcut Id="startmenuManual" Directory="ProgramMenuDir" Name="PuTTY Manual" Advertise="no" /> diff --git a/WINDOWS/wincapi.c b/WINDOWS/wincapi.c deleted file mode 100644 index de78988b..00000000 --- a/WINDOWS/wincapi.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * wincapi.c: implementation of wincapi.h. - */ - -#include "putty.h" - -#if !defined NO_SECURITY - -#include "putty.h" -#include "ssh.h" - -#include "wincapi.h" - -DEF_WINDOWS_FUNCTION(CryptProtectMemory); - -bool got_crypt(void) -{ - static bool attempted = false; - static bool successful; - static HMODULE crypt; - - if (!attempted) { - attempted = true; - crypt = load_system32_dll("crypt32.dll"); - successful = crypt && - GET_WINDOWS_FUNCTION(crypt, CryptProtectMemory); - } - return successful; -} - -char *capi_obfuscate_string(const char *realname) -{ - char *cryptdata; - int cryptlen; - unsigned char digest[32]; - char retbuf[65]; - int i; - - cryptlen = strlen(realname) + 1; - cryptlen += CRYPTPROTECTMEMORY_BLOCK_SIZE - 1; - cryptlen /= CRYPTPROTECTMEMORY_BLOCK_SIZE; - cryptlen *= CRYPTPROTECTMEMORY_BLOCK_SIZE; - - cryptdata = snewn(cryptlen, char); - memset(cryptdata, 0, cryptlen); - strcpy(cryptdata, realname); - - /* - * CRYPTPROTECTMEMORY_CROSS_PROCESS causes CryptProtectMemory to - * use the same key in all processes with this user id, meaning - * that the next PuTTY process calling this function with the same - * input will get the same data. - * - * (Contrast with CryptProtectData, which invents a new session - * key every time since its API permits returning more data than - * was input, so calling _that_ and hashing the output would not - * be stable.) - * - * We don't worry too much if this doesn't work for some reason. - * Omitting this step still has _some_ privacy value (in that - * another user can test-hash things to confirm guesses as to - * where you might be connecting to, but cannot invert SHA-256 in - * the absence of any plausible guess). So we don't abort if we - * can't call CryptProtectMemory at all, or if it fails. - */ - if (got_crypt()) - p_CryptProtectMemory(cryptdata, cryptlen, - CRYPTPROTECTMEMORY_CROSS_PROCESS); - - /* - * We don't want to give away the length of the hostname either, - * so having got it back out of CryptProtectMemory we now hash it. - */ - { - ssh_hash *h = ssh_hash_new(&ssh_sha256); - put_string(h, cryptdata, cryptlen); - ssh_hash_final(h, digest); - } - - sfree(cryptdata); - - /* - * Finally, make printable. - */ - for (i = 0; i < 32; i++) { - sprintf(retbuf + 2*i, "%02x", digest[i]); - /* the last of those will also write the trailing NUL */ - } - - return dupstr(retbuf); -} - -#endif /* !defined NO_SECURITY */ diff --git a/WINDOWS/wincapi.h b/WINDOWS/wincapi.h deleted file mode 100644 index 732412e2..00000000 --- a/WINDOWS/wincapi.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * wincapi.h: Windows Crypto API functions defined in wincapi.c that - * use the crypt32 library. Also centralises the machinery for - * dynamically loading that library, and our own functions using that - * in turn. - */ - -#if !defined NO_SECURITY - -DECL_WINDOWS_FUNCTION(extern, BOOL, CryptProtectMemory, (LPVOID,DWORD,DWORD)); - -bool got_crypt(void); - -/* - * Function to obfuscate an input string into something usable as a - * pathname for a Windows named pipe. Uses CryptProtectMemory to make - * the obfuscation depend on a key Windows stores for the owning user, - * and then hashes the string as well to make it have a manageable - * length and be composed of filename-legal characters. - * - * Rationale: Windows's named pipes all live in the same namespace, so - * one user can see what pipes another user has open. This is an - * undesirable privacy leak: in particular, if we used unobfuscated - * names for the connection-sharing pipe names, it would permit one - * user to know what username@host another user is SSHing to. - * - * The returned string is dynamically allocated. - */ -char *capi_obfuscate_string(const char *realname); - -#endif diff --git a/WINDOWS/wincliloop.c b/WINDOWS/wincliloop.c deleted file mode 100644 index 26a4d3aa..00000000 --- a/WINDOWS/wincliloop.c +++ /dev/null @@ -1,136 +0,0 @@ -#include "putty.h" - -void cli_main_loop(cliloop_pre_t pre, cliloop_post_t post, void *ctx) -{ - SOCKET *sklist = NULL; - size_t skcount = 0, sksize = 0; - unsigned long now, next, then; - now = GETTICKCOUNT(); - - while (true) { - int nhandles; - HANDLE *handles; - DWORD n; - DWORD ticks; - - const HANDLE *extra_handles = NULL; - size_t n_extra_handles = 0; - if (!pre(ctx, &extra_handles, &n_extra_handles)) - break; - - if (toplevel_callback_pending()) { - ticks = 0; - next = now; - } else if (run_timers(now, &next)) { - then = now; - now = GETTICKCOUNT(); - if (now - then > next - then) - ticks = 0; - else - ticks = next - now; - } else { - ticks = INFINITE; - /* no need to initialise next here because we can never - * get WAIT_TIMEOUT */ - } - - handles = handle_get_events(&nhandles); - size_t winselcli_index = -(size_t)1; - size_t extra_base = nhandles; - if (winselcli_event != INVALID_HANDLE_VALUE) { - winselcli_index = extra_base++; - handles = sresize(handles, extra_base, HANDLE); - handles[winselcli_index] = winselcli_event; - } - size_t total_handles = extra_base + n_extra_handles; - handles = sresize(handles, total_handles, HANDLE); - for (size_t i = 0; i < n_extra_handles; i++) - handles[extra_base + i] = extra_handles[i]; - - n = WaitForMultipleObjects(total_handles, handles, false, ticks); - - size_t extra_handle_index = n_extra_handles; - - if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) { - handle_got_event(handles[n - WAIT_OBJECT_0]); - } else if (winselcli_event != INVALID_HANDLE_VALUE && - n == WAIT_OBJECT_0 + winselcli_index) { - WSANETWORKEVENTS things; - SOCKET socket; - int i, socketstate; - - /* - * We must not call select_result() for any socket - * until we have finished enumerating within the tree. - * This is because select_result() may close the socket - * and modify the tree. - */ - /* Count the active sockets. */ - i = 0; - for (socket = first_socket(&socketstate); - socket != INVALID_SOCKET; - socket = next_socket(&socketstate)) i++; - - /* Expand the buffer if necessary. */ - sgrowarray(sklist, sksize, i); - - /* Retrieve the sockets into sklist. */ - skcount = 0; - for (socket = first_socket(&socketstate); - socket != INVALID_SOCKET; - socket = next_socket(&socketstate)) { - sklist[skcount++] = socket; - } - - /* Now we're done enumerating; go through the list. */ - for (i = 0; i < skcount; i++) { - WPARAM wp; - socket = sklist[i]; - wp = (WPARAM) socket; - if (!p_WSAEnumNetworkEvents(socket, NULL, &things)) { - static const struct { int bit, mask; } eventtypes[] = { - {FD_CONNECT_BIT, FD_CONNECT}, - {FD_READ_BIT, FD_READ}, - {FD_CLOSE_BIT, FD_CLOSE}, - {FD_OOB_BIT, FD_OOB}, - {FD_WRITE_BIT, FD_WRITE}, - {FD_ACCEPT_BIT, FD_ACCEPT}, - }; - int e; - - noise_ultralight(NOISE_SOURCE_IOID, socket); - - for (e = 0; e < lenof(eventtypes); e++) - if (things.lNetworkEvents & eventtypes[e].mask) { - LPARAM lp; - int err = things.iErrorCode[eventtypes[e].bit]; - lp = WSAMAKESELECTREPLY(eventtypes[e].mask, err); - select_result(wp, lp); - } - } - } - } else if (n >= WAIT_OBJECT_0 + extra_base && - n < WAIT_OBJECT_0 + extra_base + n_extra_handles) { - extra_handle_index = n - (WAIT_OBJECT_0 + extra_base); - } - - run_toplevel_callbacks(); - - if (n == WAIT_TIMEOUT) { - now = next; - } else { - now = GETTICKCOUNT(); - } - - sfree(handles); - - if (!post(ctx, extra_handle_index)) - break; - } - - sfree(sklist); -} - -bool cliloop_null_pre(void *vctx, const HANDLE **eh, size_t *neh) -{ return true; } -bool cliloop_null_post(void *vctx, size_t ehi) { return true; } diff --git a/WINDOWS/winhelp.rc2 b/WINDOWS/winhelp.rc2 deleted file mode 100644 index 3499d25e..00000000 --- a/WINDOWS/winhelp.rc2 +++ /dev/null @@ -1,8 +0,0 @@ -#include "win_res.h" - -#ifdef EMBED_CHM -ID_CUSTOM_CHMFILE TYPE_CUSTOM_CHMFILE "../doc/putty.chm" -#define HELPVER " (with embedded help)" -#else -#define HELPVER " (without embedded help)" -#endif diff --git a/WINDOWS/winhsock.c b/WINDOWS/winhsock.c deleted file mode 100644 index 543b77b6..00000000 --- a/WINDOWS/winhsock.c +++ /dev/null @@ -1,343 +0,0 @@ -/* - * General mechanism for wrapping up reading/writing of Windows - * HANDLEs into a PuTTY Socket abstraction. - */ - -#include <stdio.h> -#include <assert.h> -#include <limits.h> - -#include "tree234.h" -#include "putty.h" -#include "network.h" - -typedef struct HandleSocket { - HANDLE send_H, recv_H, stderr_H; - struct handle *send_h, *recv_h, *stderr_h; - - /* - * Freezing one of these sockets is a slightly fiddly business, - * because the reads from the handle are happening in a separate - * thread as blocking system calls and so once one is in progress - * it can't sensibly be interrupted. Hence, after the user tries - * to freeze one of these sockets, it's unavoidable that we may - * receive one more load of data before we manage to get - * winhandl.c to stop reading. - */ - enum { - UNFROZEN, /* reading as normal */ - FREEZING, /* have been set to frozen but winhandl is still reading */ - FROZEN, /* really frozen - winhandl has been throttled */ - THAWING /* we're gradually releasing our remaining data */ - } frozen; - /* We buffer data here if we receive it from winhandl while frozen. */ - bufchain inputdata; - - /* Handle logging proxy error messages from stderr_H, if we have one. */ - ProxyStderrBuf psb; - - bool defer_close, deferred_close; /* in case of re-entrance */ - - char *error; - - Plug *plug; - - Socket sock; -} HandleSocket; - -static size_t handle_gotdata( - struct handle *h, const void *data, size_t len, int err) -{ - HandleSocket *hs = (HandleSocket *)handle_get_privdata(h); - - if (err) { - plug_closing(hs->plug, "Read error from handle", 0, 0); - return 0; - } else if (len == 0) { - plug_closing(hs->plug, NULL, 0, 0); - return 0; - } else { - assert(hs->frozen != FROZEN && hs->frozen != THAWING); - if (hs->frozen == FREEZING) { - /* - * If we've received data while this socket is supposed to - * be frozen (because the read winhandl.c started before - * sk_set_frozen was called has now returned) then buffer - * the data for when we unfreeze. - */ - bufchain_add(&hs->inputdata, data, len); - hs->frozen = FROZEN; - - /* - * And return a very large backlog, to prevent further - * data arriving from winhandl until we unfreeze. - */ - return INT_MAX; - } else { - plug_receive(hs->plug, 0, data, len); - return 0; - } - } -} - -static size_t handle_stderr( - struct handle *h, const void *data, size_t len, int err) -{ - HandleSocket *hs = (HandleSocket *)handle_get_privdata(h); - - if (!err && len > 0) - log_proxy_stderr(hs->plug, &hs->psb, data, len); - - return 0; -} - -static void handle_sentdata(struct handle *h, size_t new_backlog, int err) -{ - HandleSocket *hs = (HandleSocket *)handle_get_privdata(h); - - if (err) { - plug_closing(hs->plug, win_strerror(err), err, 0); - return; - } - - plug_sent(hs->plug, new_backlog); -} - -static Plug *sk_handle_plug(Socket *s, Plug *p) -{ - HandleSocket *hs = container_of(s, HandleSocket, sock); - Plug *ret = hs->plug; - if (p) - hs->plug = p; - return ret; -} - -static void sk_handle_close(Socket *s) -{ - HandleSocket *hs = container_of(s, HandleSocket, sock); - - if (hs->defer_close) { - hs->deferred_close = true; - return; - } - - handle_free(hs->send_h); - handle_free(hs->recv_h); - CloseHandle(hs->send_H); - if (hs->recv_H != hs->send_H) - CloseHandle(hs->recv_H); - bufchain_clear(&hs->inputdata); - - delete_callbacks_for_context(hs); - - sfree(hs); -} - -static size_t sk_handle_write(Socket *s, const void *data, size_t len) -{ - HandleSocket *hs = container_of(s, HandleSocket, sock); - - return handle_write(hs->send_h, data, len); -} - -static size_t sk_handle_write_oob(Socket *s, const void *data, size_t len) -{ - /* - * oob data is treated as inband; nasty, but nothing really - * better we can do - */ - return sk_handle_write(s, data, len); -} - -static void sk_handle_write_eof(Socket *s) -{ - HandleSocket *hs = container_of(s, HandleSocket, sock); - - handle_write_eof(hs->send_h); -} - -static void handle_socket_unfreeze(void *hsv) -{ - HandleSocket *hs = (HandleSocket *)hsv; - - /* - * If we've been put into a state other than THAWING since the - * last callback, then we're done. - */ - if (hs->frozen != THAWING) - return; - - /* - * Get some of the data we've buffered. - */ - ptrlen data = bufchain_prefix(&hs->inputdata); - assert(data.len > 0); - - /* - * Hand it off to the plug. Be careful of re-entrance - that might - * have the effect of trying to close this socket. - */ - hs->defer_close = true; - plug_receive(hs->plug, 0, data.ptr, data.len); - bufchain_consume(&hs->inputdata, data.len); - hs->defer_close = false; - if (hs->deferred_close) { - sk_handle_close(&hs->sock); - return; - } - - if (bufchain_size(&hs->inputdata) > 0) { - /* - * If there's still data in our buffer, stay in THAWING state, - * and reschedule ourself. - */ - queue_toplevel_callback(handle_socket_unfreeze, hs); - } else { - /* - * Otherwise, we've successfully thawed! - */ - hs->frozen = UNFROZEN; - handle_unthrottle(hs->recv_h, 0); - } -} - -static void sk_handle_set_frozen(Socket *s, bool is_frozen) -{ - HandleSocket *hs = container_of(s, HandleSocket, sock); - - if (is_frozen) { - switch (hs->frozen) { - case FREEZING: - case FROZEN: - return; /* nothing to do */ - - case THAWING: - /* - * We were in the middle of emptying our bufchain, and got - * frozen again. In that case, winhandl.c is already - * throttled, so just return to FROZEN state. The toplevel - * callback will notice and disable itself. - */ - hs->frozen = FROZEN; - break; - - case UNFROZEN: - /* - * The normal case. Go to FREEZING, and expect one more - * load of data from winhandl if we're unlucky. - */ - hs->frozen = FREEZING; - break; - } - } else { - switch (hs->frozen) { - case UNFROZEN: - case THAWING: - return; /* nothing to do */ - - case FREEZING: - /* - * If winhandl didn't send us any data throughout the time - * we were frozen, then we'll still be in this state and - * can just unfreeze in the trivial way. - */ - assert(bufchain_size(&hs->inputdata) == 0); - hs->frozen = UNFROZEN; - break; - - case FROZEN: - /* - * If we have buffered data, go to THAWING and start - * releasing it in top-level callbacks. - */ - hs->frozen = THAWING; - queue_toplevel_callback(handle_socket_unfreeze, hs); - } - } -} - -static const char *sk_handle_socket_error(Socket *s) -{ - HandleSocket *hs = container_of(s, HandleSocket, sock); - return hs->error; -} - -static SocketPeerInfo *sk_handle_peer_info(Socket *s) -{ - HandleSocket *hs = container_of(s, HandleSocket, sock); - ULONG pid; - static HMODULE kernel32_module; - DECL_WINDOWS_FUNCTION(static, BOOL, GetNamedPipeClientProcessId, - (HANDLE, PULONG)); - - if (!kernel32_module) { - kernel32_module = load_system32_dll("kernel32.dll"); -#if (defined _MSC_VER && _MSC_VER < 1900) || defined __MINGW32__ - /* For older Visual Studio, and MinGW too (at least as of - * Ubuntu 16.04), this function isn't available in the header - * files to type-check. Ditto the toolchain I use for - * Coveritying the Windows code. */ - GET_WINDOWS_FUNCTION_NO_TYPECHECK( - kernel32_module, GetNamedPipeClientProcessId); -#else - GET_WINDOWS_FUNCTION( - kernel32_module, GetNamedPipeClientProcessId); -#endif - } - - /* - * Of course, not all handles managed by this module will be - * server ends of named pipes, but if they are, then it's useful - * to log what we can find out about the client end. - */ - if (p_GetNamedPipeClientProcessId && - p_GetNamedPipeClientProcessId(hs->send_H, &pid)) { - SocketPeerInfo *pi = snew(SocketPeerInfo); - pi->addressfamily = ADDRTYPE_LOCAL; - pi->addr_text = NULL; - pi->port = -1; - pi->log_text = dupprintf("process id %lu", (unsigned long)pid); - return pi; - } - - return NULL; -} - -static const SocketVtable HandleSocket_sockvt = { - .plug = sk_handle_plug, - .close = sk_handle_close, - .write = sk_handle_write, - .write_oob = sk_handle_write_oob, - .write_eof = sk_handle_write_eof, - .set_frozen = sk_handle_set_frozen, - .socket_error = sk_handle_socket_error, - .peer_info = sk_handle_peer_info, -}; - -Socket *make_handle_socket(HANDLE send_H, HANDLE recv_H, HANDLE stderr_H, - Plug *plug, bool overlapped) -{ - HandleSocket *hs; - int flags = (overlapped ? HANDLE_FLAG_OVERLAPPED : 0); - - hs = snew(HandleSocket); - hs->sock.vt = &HandleSocket_sockvt; - hs->plug = plug; - hs->error = NULL; - hs->frozen = UNFROZEN; - bufchain_init(&hs->inputdata); - psb_init(&hs->psb); - - hs->recv_H = recv_H; - hs->recv_h = handle_input_new(hs->recv_H, handle_gotdata, hs, flags); - hs->send_H = send_H; - hs->send_h = handle_output_new(hs->send_H, handle_sentdata, hs, flags); - hs->stderr_H = stderr_H; - if (hs->stderr_H) - hs->stderr_h = handle_input_new(hs->stderr_H, handle_stderr, - hs, flags); - - hs->defer_close = hs->deferred_close = false; - - return &hs->sock; -} diff --git a/WINDOWS/winmiscs.c b/WINDOWS/winmiscs.c deleted file mode 100644 index 571a9122..00000000 --- a/WINDOWS/winmiscs.c +++ /dev/null @@ -1,285 +0,0 @@ -/* - * winmiscs.c: Windows-specific standalone functions. Has the same - * relationship to winmisc.c that utils.c does to misc.c, but the - * corresponding name 'winutils.c' was already taken. - */ - -#include "putty.h" - -#ifndef NO_SECUREZEROMEMORY -/* - * Windows implementation of smemclr (see misc.c) using SecureZeroMemory. - */ -void smemclr(void *b, size_t n) { - if (b && n > 0) - SecureZeroMemory(b, n); -} -#endif - -#ifdef MINEFIELD -/* - * Minefield - a Windows equivalent for Electric Fence - */ - -#define PAGESIZE 4096 - -/* - * Design: - * - * We start by reserving as much virtual address space as Windows - * will sensibly (or not sensibly) let us have. We flag it all as - * invalid memory. - * - * Any allocation attempt is satisfied by committing one or more - * pages, with an uncommitted page on either side. The returned - * memory region is jammed up against the _end_ of the pages. - * - * Freeing anything causes instantaneous decommitment of the pages - * involved, so stale pointers are caught as soon as possible. - */ - -static int minefield_initialised = 0; -static void *minefield_region = NULL; -static long minefield_size = 0; -static long minefield_npages = 0; -static long minefield_curpos = 0; -static unsigned short *minefield_admin = NULL; -static void *minefield_pages = NULL; - -static void minefield_admin_hide(int hide) -{ - int access = hide ? PAGE_NOACCESS : PAGE_READWRITE; - VirtualProtect(minefield_admin, minefield_npages * 2, access, NULL); -} - -static void minefield_init(void) -{ - int size; - int admin_size; - int i; - - for (size = 0x40000000; size > 0; size = ((size >> 3) * 7) & ~0xFFF) { - minefield_region = VirtualAlloc(NULL, size, - MEM_RESERVE, PAGE_NOACCESS); - if (minefield_region) - break; - } - minefield_size = size; - - /* - * Firstly, allocate a section of that to be the admin block. - * We'll need a two-byte field for each page. - */ - minefield_admin = minefield_region; - minefield_npages = minefield_size / PAGESIZE; - admin_size = (minefield_npages * 2 + PAGESIZE - 1) & ~(PAGESIZE - 1); - minefield_npages = (minefield_size - admin_size) / PAGESIZE; - minefield_pages = (char *) minefield_region + admin_size; - - /* - * Commit the admin region. - */ - VirtualAlloc(minefield_admin, minefield_npages * 2, - MEM_COMMIT, PAGE_READWRITE); - - /* - * Mark all pages as unused (0xFFFF). - */ - for (i = 0; i < minefield_npages; i++) - minefield_admin[i] = 0xFFFF; - - /* - * Hide the admin region. - */ - minefield_admin_hide(1); - - minefield_initialised = 1; -} - -static void minefield_bomb(void) -{ - div(1, *(int *) minefield_pages); -} - -static void *minefield_alloc(int size) -{ - int npages; - int pos, lim, region_end, region_start; - int start; - int i; - - npages = (size + PAGESIZE - 1) / PAGESIZE; - - minefield_admin_hide(0); - - /* - * Search from current position until we find a contiguous - * bunch of npages+2 unused pages. - */ - pos = minefield_curpos; - lim = minefield_npages; - while (1) { - /* Skip over used pages. */ - while (pos < lim && minefield_admin[pos] != 0xFFFF) - pos++; - /* Count unused pages. */ - start = pos; - while (pos < lim && pos - start < npages + 2 && - minefield_admin[pos] == 0xFFFF) - pos++; - if (pos - start == npages + 2) - break; - /* If we've reached the limit, reset the limit or stop. */ - if (pos >= lim) { - if (lim == minefield_npages) { - /* go round and start again at zero */ - lim = minefield_curpos; - pos = 0; - } else { - minefield_admin_hide(1); - return NULL; - } - } - } - - minefield_curpos = pos - 1; - - /* - * We have npages+2 unused pages starting at start. We leave - * the first and last of these alone and use the rest. - */ - region_end = (start + npages + 1) * PAGESIZE; - region_start = region_end - size; - /* FIXME: could align here if we wanted */ - - /* - * Update the admin region. - */ - for (i = start + 2; i < start + npages + 1; i++) - minefield_admin[i] = 0xFFFE; /* used but no region starts here */ - minefield_admin[start + 1] = region_start % PAGESIZE; - - minefield_admin_hide(1); - - VirtualAlloc((char *) minefield_pages + region_start, size, - MEM_COMMIT, PAGE_READWRITE); - return (char *) minefield_pages + region_start; -} - -static void minefield_free(void *ptr) -{ - int region_start, i, j; - - minefield_admin_hide(0); - - region_start = (char *) ptr - (char *) minefield_pages; - i = region_start / PAGESIZE; - if (i < 0 || i >= minefield_npages || - minefield_admin[i] != region_start % PAGESIZE) - minefield_bomb(); - for (j = i; j < minefield_npages && minefield_admin[j] != 0xFFFF; j++) { - minefield_admin[j] = 0xFFFF; - } - - VirtualFree(ptr, j * PAGESIZE - region_start, MEM_DECOMMIT); - - minefield_admin_hide(1); -} - -static int minefield_get_size(void *ptr) -{ - int region_start, i, j; - - minefield_admin_hide(0); - - region_start = (char *) ptr - (char *) minefield_pages; - i = region_start / PAGESIZE; - if (i < 0 || i >= minefield_npages || - minefield_admin[i] != region_start % PAGESIZE) - minefield_bomb(); - for (j = i; j < minefield_npages && minefield_admin[j] != 0xFFFF; j++); - - minefield_admin_hide(1); - - return j * PAGESIZE - region_start; -} - -void *minefield_c_malloc(size_t size) -{ - if (!minefield_initialised) - minefield_init(); - return minefield_alloc(size); -} - -void minefield_c_free(void *p) -{ - if (!minefield_initialised) - minefield_init(); - minefield_free(p); -} - -/* - * realloc _always_ moves the chunk, for rapid detection of code - * that assumes it won't. - */ -void *minefield_c_realloc(void *p, size_t size) -{ - size_t oldsize; - void *q; - if (!minefield_initialised) - minefield_init(); - q = minefield_alloc(size); - oldsize = minefield_get_size(p); - memcpy(q, p, (oldsize < size ? oldsize : size)); - minefield_free(p); - return q; -} - -#endif /* MINEFIELD */ - -#if defined _MSC_VER && _MSC_VER < 1800 - -/* - * Work around lack of strtoumax in older MSVC libraries - */ -uintmax_t strtoumax(const char *nptr, char **endptr, int base) -{ - return _strtoui64(nptr, endptr, base); -} - -#endif - -#if defined _M_ARM || defined _M_ARM64 - -bool platform_aes_hw_available(void) -{ - return IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE); -} - -bool platform_sha256_hw_available(void) -{ - return IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE); -} - -bool platform_sha1_hw_available(void) -{ - return IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE); -} - -bool platform_sha512_hw_available(void) -{ - /* As of 2020-12-24, as far as I can tell from docs.microsoft.com, - * Windows on Arm does not yet provide a PF_ARM_V8_* flag for the - * SHA-512 architecture extension. */ - return false; -} - -#endif - -bool is_console_handle(HANDLE handle) -{ - DWORD ignored_output; - if (GetConsoleMode(handle, &ignored_output)) - return true; - return false; -} diff --git a/WINDOWS/winnohlp.c b/WINDOWS/winnohlp.c deleted file mode 100644 index 62ddc65c..00000000 --- a/WINDOWS/winnohlp.c +++ /dev/null @@ -1,15 +0,0 @@ -/* - * nohelp.c: implement the has_embedded_chm() function for - * applications that have no help file at all, so that misc.c's - * buildinfo string knows not to talk meaninglessly about whether the - * nonexistent help file is present. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <assert.h> - -#include "putty.h" - -int has_embedded_chm(void) { return -1; } diff --git a/WINDOWS/winnpc.c b/WINDOWS/winnpc.c deleted file mode 100644 index eabfb4bc..00000000 --- a/WINDOWS/winnpc.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Windows support module which deals with being a named-pipe client. - */ - -#include <stdio.h> -#include <assert.h> - -#include "tree234.h" -#include "putty.h" -#include "network.h" -#include "proxy.h" -#include "ssh.h" - -#if !defined NO_SECURITY - -#include "winsecur.h" - -HANDLE connect_to_named_pipe(const char *pipename, char **err) -{ - HANDLE pipehandle; - PSID usersid, pipeowner; - PSECURITY_DESCRIPTOR psd; - - assert(strncmp(pipename, "\\\\.\\pipe\\", 9) == 0); - assert(strchr(pipename + 9, '\\') == NULL); - - while (1) { - pipehandle = CreateFile(pipename, GENERIC_READ | GENERIC_WRITE, - 0, NULL, OPEN_EXISTING, - FILE_FLAG_OVERLAPPED, NULL); - - if (pipehandle != INVALID_HANDLE_VALUE) - break; - - if (GetLastError() != ERROR_PIPE_BUSY) { - *err = dupprintf( - "Unable to open named pipe '%s': %s", - pipename, win_strerror(GetLastError())); - return INVALID_HANDLE_VALUE; - } - - /* - * If we got ERROR_PIPE_BUSY, wait for the server to - * create a new pipe instance. (Since the server is - * expected to be winnps.c, which will do that immediately - * after a previous connection is accepted, that shouldn't - * take excessively long.) - */ - if (!WaitNamedPipe(pipename, NMPWAIT_USE_DEFAULT_WAIT)) { - *err = dupprintf( - "Error waiting for named pipe '%s': %s", - pipename, win_strerror(GetLastError())); - return INVALID_HANDLE_VALUE; - } - } - - if ((usersid = get_user_sid()) == NULL) { - CloseHandle(pipehandle); - *err = dupprintf( - "Unable to get user SID: %s", win_strerror(GetLastError())); - return INVALID_HANDLE_VALUE; - } - - if (p_GetSecurityInfo(pipehandle, SE_KERNEL_OBJECT, - OWNER_SECURITY_INFORMATION, - &pipeowner, NULL, NULL, NULL, - &psd) != ERROR_SUCCESS) { - CloseHandle(pipehandle); - *err = dupprintf( - "Unable to get named pipe security information: %s", - win_strerror(GetLastError())); - return INVALID_HANDLE_VALUE; - } - - if (!EqualSid(pipeowner, usersid)) { - CloseHandle(pipehandle); - LocalFree(psd); - *err = dupprintf( - "Owner of named pipe '%s' is not us", pipename); - return INVALID_HANDLE_VALUE; - } - - LocalFree(psd); - - return pipehandle; -} - -Socket *new_named_pipe_client(const char *pipename, Plug *plug) -{ - char *err = NULL; - HANDLE pipehandle = connect_to_named_pipe(pipename, &err); - if (pipehandle == INVALID_HANDLE_VALUE) - return new_error_socket_consume_string(plug, err); - else - return make_handle_socket(pipehandle, pipehandle, NULL, plug, true); -} - -#endif /* !defined NO_SECURITY */ diff --git a/WINDOWS/winnps.c b/WINDOWS/winnps.c deleted file mode 100644 index 1757cdbb..00000000 --- a/WINDOWS/winnps.c +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Windows support module which deals with being a named-pipe server. - */ - -#include <stdio.h> -#include <assert.h> - -#include "tree234.h" -#include "putty.h" -#include "network.h" -#include "proxy.h" -#include "ssh.h" - -#if !defined NO_SECURITY - -#include "winsecur.h" - -typedef struct NamedPipeServerSocket { - /* Parameters for (repeated) creation of named pipe objects */ - PSECURITY_DESCRIPTOR psd; - PACL acl; - char *pipename; - - /* The current named pipe object + attempt to connect to it */ - HANDLE pipehandle; - OVERLAPPED connect_ovl; - struct handle *callback_handle; /* winhandl.c's reference */ - - /* PuTTY Socket machinery */ - Plug *plug; - char *error; - - Socket sock; -} NamedPipeServerSocket; - -static Plug *sk_namedpipeserver_plug(Socket *s, Plug *p) -{ - NamedPipeServerSocket *ps = container_of(s, NamedPipeServerSocket, sock); - Plug *ret = ps->plug; - if (p) - ps->plug = p; - return ret; -} - -static void sk_namedpipeserver_close(Socket *s) -{ - NamedPipeServerSocket *ps = container_of(s, NamedPipeServerSocket, sock); - - if (ps->callback_handle) - handle_free(ps->callback_handle); - CloseHandle(ps->pipehandle); - CloseHandle(ps->connect_ovl.hEvent); - sfree(ps->error); - sfree(ps->pipename); - if (ps->acl) - LocalFree(ps->acl); - if (ps->psd) - LocalFree(ps->psd); - sfree(ps); -} - -static const char *sk_namedpipeserver_socket_error(Socket *s) -{ - NamedPipeServerSocket *ps = container_of(s, NamedPipeServerSocket, sock); - return ps->error; -} - -static SocketPeerInfo *sk_namedpipeserver_peer_info(Socket *s) -{ - return NULL; -} - -static bool create_named_pipe(NamedPipeServerSocket *ps, bool first_instance) -{ - SECURITY_ATTRIBUTES sa; - - memset(&sa, 0, sizeof(sa)); - sa.nLength = sizeof(sa); - sa.lpSecurityDescriptor = ps->psd; - sa.bInheritHandle = false; - - ps->pipehandle = CreateNamedPipe - (/* lpName */ - ps->pipename, - - /* dwOpenMode */ - PIPE_ACCESS_DUPLEX | - FILE_FLAG_OVERLAPPED | - (first_instance ? FILE_FLAG_FIRST_PIPE_INSTANCE : 0), - - /* dwPipeMode */ - PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT -#ifdef PIPE_REJECT_REMOTE_CLIENTS - | PIPE_REJECT_REMOTE_CLIENTS -#endif - , - - /* nMaxInstances */ - PIPE_UNLIMITED_INSTANCES, - - /* nOutBufferSize, nInBufferSize */ - 4096, 4096, /* FIXME: think harder about buffer sizes? */ - - /* nDefaultTimeOut */ - 0 /* default timeout */, - - /* lpSecurityAttributes */ - &sa); - - return ps->pipehandle != INVALID_HANDLE_VALUE; -} - -static Socket *named_pipe_accept(accept_ctx_t ctx, Plug *plug) -{ - HANDLE conn = (HANDLE)ctx.p; - - return make_handle_socket(conn, conn, NULL, plug, true); -} - -static void named_pipe_accept_loop(NamedPipeServerSocket *ps, - bool got_one_already) -{ - while (1) { - int error; - char *errmsg; - - if (got_one_already) { - /* If we were called with a connection already waiting, - * skip this step. */ - got_one_already = false; - error = 0; - } else { - /* - * Call ConnectNamedPipe, which might succeed or might - * tell us that an overlapped operation is in progress and - * we should wait for our event object. - */ - if (ConnectNamedPipe(ps->pipehandle, &ps->connect_ovl)) - error = 0; - else - error = GetLastError(); - - if (error == ERROR_IO_PENDING) - return; - } - - if (error == 0 || error == ERROR_PIPE_CONNECTED) { - /* - * We've successfully retrieved an incoming connection, so - * ps->pipehandle now refers to that connection. So - * convert that handle into a separate connection-type - * Socket, and create a fresh one to be the new listening - * pipe. - */ - HANDLE conn = ps->pipehandle; - accept_ctx_t actx; - - actx.p = (void *)conn; - if (plug_accepting(ps->plug, named_pipe_accept, actx)) { - /* - * If the plug didn't want the connection, might as - * well close this handle. - */ - CloseHandle(conn); - } - - if (!create_named_pipe(ps, false)) { - error = GetLastError(); - } else { - /* - * Go round again to see if more connections can be - * got, or to begin waiting on the event object. - */ - continue; - } - } - - errmsg = dupprintf("Error while listening to named pipe: %s", - win_strerror(error)); - plug_log(ps->plug, 1, sk_namedpipe_addr(ps->pipename), 0, - errmsg, error); - sfree(errmsg); - break; - } -} - -static void named_pipe_connect_callback(void *vps) -{ - NamedPipeServerSocket *ps = (NamedPipeServerSocket *)vps; - named_pipe_accept_loop(ps, true); -} - -/* - * This socket type is only used for listening, so it should never - * be asked to write or set_frozen. - */ -static const SocketVtable NamedPipeServerSocket_sockvt = { - .plug = sk_namedpipeserver_plug, - .close = sk_namedpipeserver_close, - .socket_error = sk_namedpipeserver_socket_error, - .peer_info = sk_namedpipeserver_peer_info, -}; - -Socket *new_named_pipe_listener(const char *pipename, Plug *plug) -{ - NamedPipeServerSocket *ret = snew(NamedPipeServerSocket); - ret->sock.vt = &NamedPipeServerSocket_sockvt; - ret->plug = plug; - ret->error = NULL; - ret->psd = NULL; - ret->pipename = dupstr(pipename); - ret->acl = NULL; - ret->callback_handle = NULL; - - assert(strncmp(pipename, "\\\\.\\pipe\\", 9) == 0); - assert(strchr(pipename + 9, '\\') == NULL); - - if (!make_private_security_descriptor(GENERIC_READ | GENERIC_WRITE, - &ret->psd, &ret->acl, &ret->error)) { - goto cleanup; - } - - if (!create_named_pipe(ret, true)) { - ret->error = dupprintf("unable to create named pipe '%s': %s", - pipename, win_strerror(GetLastError())); - goto cleanup; - } - - memset(&ret->connect_ovl, 0, sizeof(ret->connect_ovl)); - ret->connect_ovl.hEvent = CreateEvent(NULL, true, false, NULL); - ret->callback_handle = - handle_add_foreign_event(ret->connect_ovl.hEvent, - named_pipe_connect_callback, ret); - named_pipe_accept_loop(ret, false); - - cleanup: - return &ret->sock; -} - -#endif /* !defined NO_SECURITY */ diff --git a/WINDOWS/winseat.h b/WINDOWS/winseat.h deleted file mode 100644 index c6b5fa96..00000000 --- a/WINDOWS/winseat.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Small implementation of Seat and LogPolicy shared between window.c - * and windlg.c. - */ - -typedef struct WinGuiSeat WinGuiSeat; - -struct WinGuiSeat { - HWND term_hwnd; - Seat seat; - LogPolicy logpolicy; -}; - -extern const LogPolicyVtable win_gui_logpolicy_vt; /* in windlg.c */ diff --git a/WINDOWS/winsecur.c b/WINDOWS/winsecur.c deleted file mode 100644 index a1164af5..00000000 --- a/WINDOWS/winsecur.c +++ /dev/null @@ -1,334 +0,0 @@ -/* - * winsecur.c: implementation of winsecur.h. - */ - -#include <stdio.h> -#include <stdlib.h> - -#include "putty.h" - -#if !defined NO_SECURITY - -#include "winsecur.h" - -/* Initialised once, then kept around to reuse forever */ -static PSID worldsid, networksid, usersid; - -DEF_WINDOWS_FUNCTION(OpenProcessToken); -DEF_WINDOWS_FUNCTION(GetTokenInformation); -DEF_WINDOWS_FUNCTION(InitializeSecurityDescriptor); -DEF_WINDOWS_FUNCTION(SetSecurityDescriptorOwner); -DEF_WINDOWS_FUNCTION(GetSecurityInfo); -DEF_WINDOWS_FUNCTION(SetSecurityInfo); -DEF_WINDOWS_FUNCTION(SetEntriesInAclA); - -bool got_advapi(void) -{ - static bool attempted = false; - static bool successful; - static HMODULE advapi; - - if (!attempted) { - attempted = true; - advapi = load_system32_dll("advapi32.dll"); - successful = advapi && - GET_WINDOWS_FUNCTION(advapi, GetSecurityInfo) && - GET_WINDOWS_FUNCTION(advapi, SetSecurityInfo) && - GET_WINDOWS_FUNCTION(advapi, OpenProcessToken) && - GET_WINDOWS_FUNCTION(advapi, GetTokenInformation) && - GET_WINDOWS_FUNCTION(advapi, InitializeSecurityDescriptor) && - GET_WINDOWS_FUNCTION(advapi, SetSecurityDescriptorOwner) && - GET_WINDOWS_FUNCTION(advapi, SetEntriesInAclA); - } - return successful; -} - -PSID get_user_sid(void) -{ - HANDLE proc = NULL, tok = NULL; - TOKEN_USER *user = NULL; - DWORD toklen, sidlen; - PSID sid = NULL, ret = NULL; - - if (usersid) - return usersid; - - if (!got_advapi()) - goto cleanup; - - if ((proc = OpenProcess(MAXIMUM_ALLOWED, false, - GetCurrentProcessId())) == NULL) - goto cleanup; - - if (!p_OpenProcessToken(proc, TOKEN_QUERY, &tok)) - goto cleanup; - - if (!p_GetTokenInformation(tok, TokenUser, NULL, 0, &toklen) && - GetLastError() != ERROR_INSUFFICIENT_BUFFER) - goto cleanup; - - if ((user = (TOKEN_USER *)LocalAlloc(LPTR, toklen)) == NULL) - goto cleanup; - - if (!p_GetTokenInformation(tok, TokenUser, user, toklen, &toklen)) - goto cleanup; - - sidlen = GetLengthSid(user->User.Sid); - - sid = (PSID)smalloc(sidlen); - - if (!CopySid(sidlen, sid, user->User.Sid)) - goto cleanup; - - /* Success. Move sid into the return value slot, and null it out - * to stop the cleanup code freeing it. */ - ret = usersid = sid; - sid = NULL; - - cleanup: - if (proc != NULL) - CloseHandle(proc); - if (tok != NULL) - CloseHandle(tok); - if (user != NULL) - LocalFree(user); - if (sid != NULL) - sfree(sid); - - return ret; -} - -static bool getsids(char **error) -{ -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wmissing-braces" -#endif - SID_IDENTIFIER_AUTHORITY world_auth = SECURITY_WORLD_SID_AUTHORITY; - SID_IDENTIFIER_AUTHORITY nt_auth = SECURITY_NT_AUTHORITY; -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - - bool ret = false; - - *error = NULL; - - if (!usersid) { - if ((usersid = get_user_sid()) == NULL) { - *error = dupprintf("unable to construct SID for current user: %s", - win_strerror(GetLastError())); - goto cleanup; - } - } - - if (!worldsid) { - if (!AllocateAndInitializeSid(&world_auth, 1, SECURITY_WORLD_RID, - 0, 0, 0, 0, 0, 0, 0, &worldsid)) { - *error = dupprintf("unable to construct SID for world: %s", - win_strerror(GetLastError())); - goto cleanup; - } - } - - if (!networksid) { - if (!AllocateAndInitializeSid(&nt_auth, 1, SECURITY_NETWORK_RID, - 0, 0, 0, 0, 0, 0, 0, &networksid)) { - *error = dupprintf("unable to construct SID for " - "local same-user access only: %s", - win_strerror(GetLastError())); - goto cleanup; - } - } - - ret = true; - - cleanup: - return ret; -} - - -bool make_private_security_descriptor(DWORD permissions, - PSECURITY_DESCRIPTOR *psd, - PACL *acl, - char **error) -{ - EXPLICIT_ACCESS ea[3]; - int acl_err; - bool ret = false; - - - *psd = NULL; - *acl = NULL; - *error = NULL; - - if (!getsids(error)) - goto cleanup; - - memset(ea, 0, sizeof(ea)); - ea[0].grfAccessPermissions = permissions; - ea[0].grfAccessMode = REVOKE_ACCESS; - ea[0].grfInheritance = NO_INHERITANCE; - ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; - ea[0].Trustee.ptstrName = (LPTSTR)worldsid; - ea[1].grfAccessPermissions = permissions; - ea[1].grfAccessMode = GRANT_ACCESS; - ea[1].grfInheritance = NO_INHERITANCE; - ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; - ea[1].Trustee.ptstrName = (LPTSTR)usersid; - ea[2].grfAccessPermissions = permissions; - ea[2].grfAccessMode = REVOKE_ACCESS; - ea[2].grfInheritance = NO_INHERITANCE; - ea[2].Trustee.TrusteeForm = TRUSTEE_IS_SID; - ea[2].Trustee.ptstrName = (LPTSTR)networksid; - - acl_err = p_SetEntriesInAclA(3, ea, NULL, acl); - if (acl_err != ERROR_SUCCESS || *acl == NULL) { - *error = dupprintf("unable to construct ACL: %s", - win_strerror(acl_err)); - goto cleanup; - } - - *psd = (PSECURITY_DESCRIPTOR) - LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); - if (!*psd) { - *error = dupprintf("unable to allocate security descriptor: %s", - win_strerror(GetLastError())); - goto cleanup; - } - - if (!InitializeSecurityDescriptor(*psd, SECURITY_DESCRIPTOR_REVISION)) { - *error = dupprintf("unable to initialise security descriptor: %s", - win_strerror(GetLastError())); - goto cleanup; - } - - if (!SetSecurityDescriptorOwner(*psd, usersid, false)) { - *error = dupprintf("unable to set owner in security descriptor: %s", - win_strerror(GetLastError())); - goto cleanup; - } - - if (!SetSecurityDescriptorDacl(*psd, true, *acl, false)) { - *error = dupprintf("unable to set DACL in security descriptor: %s", - win_strerror(GetLastError())); - goto cleanup; - } - - ret = true; - - cleanup: - if (!ret) { - if (*psd) { - LocalFree(*psd); - *psd = NULL; - } - if (*acl) { - LocalFree(*acl); - *acl = NULL; - } - } else { - sfree(*error); - *error = NULL; - } - return ret; -} - -static bool acl_restricted = false; -bool restricted_acl(void) { return acl_restricted; } - -static bool really_restrict_process_acl(char **error) -{ - EXPLICIT_ACCESS ea[2]; - int acl_err; - bool ret = false; - PACL acl = NULL; - - static const DWORD nastyace=WRITE_DAC | WRITE_OWNER | - PROCESS_CREATE_PROCESS | PROCESS_CREATE_THREAD | - PROCESS_DUP_HANDLE | - PROCESS_SET_QUOTA | PROCESS_SET_INFORMATION | - PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE | - PROCESS_SUSPEND_RESUME; - - if (!getsids(error)) - goto cleanup; - - memset(ea, 0, sizeof(ea)); - - /* Everyone: deny */ - ea[0].grfAccessPermissions = nastyace; - ea[0].grfAccessMode = DENY_ACCESS; - ea[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; - ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; - ea[0].Trustee.ptstrName = (LPTSTR)worldsid; - - /* User: user ace */ - ea[1].grfAccessPermissions = ~nastyace & 0x1fff; - ea[1].grfAccessMode = GRANT_ACCESS; - ea[1].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; - ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; - ea[1].Trustee.ptstrName = (LPTSTR)usersid; - - acl_err = p_SetEntriesInAclA(2, ea, NULL, &acl); - - if (acl_err != ERROR_SUCCESS || acl == NULL) { - *error = dupprintf("unable to construct ACL: %s", - win_strerror(acl_err)); - goto cleanup; - } - - if (ERROR_SUCCESS != p_SetSecurityInfo - (GetCurrentProcess(), SE_KERNEL_OBJECT, - OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, - usersid, NULL, acl, NULL)) { - *error = dupprintf("Unable to set process ACL: %s", - win_strerror(GetLastError())); - goto cleanup; - } - - acl_restricted = true; - ret=true; - - cleanup: - if (!ret) { - if (acl) { - LocalFree(acl); - acl = NULL; - } - } - return ret; -} -#endif /* !defined NO_SECURITY */ - -/* - * Lock down our process's ACL, to present an obstacle to malware - * trying to write into its memory. This can't be a full defence, - * because well timed malware could attack us before this code runs - - * even if it was unconditionally run at the very start of main(), - * which we wouldn't want to do anyway because it turns out in practie - * that interfering with other processes in this way has significant - * non-infringing uses on Windows (e.g. screen reader software). - * - * If we've been requested to do this and are unsuccessful, bomb out - * via modalfatalbox rather than continue in a less protected mode. - * - * This function is intentionally outside the #ifndef NO_SECURITY that - * covers the rest of this file, because when PuTTY is compiled - * without the ability to restrict its ACL, we don't want it to - * silently pretend to honour the instruction to do so. - */ -void restrict_process_acl(void) -{ - char *error = NULL; - bool ret; - -#if !defined NO_SECURITY - ret = really_restrict_process_acl(&error); -#else - ret = false; - error = dupstr("ACL restrictions not compiled into this binary"); -#endif - if (!ret) - modalfatalbox("Could not restrict process ACL: %s", error); -} diff --git a/WINDOWS/winsecur.h b/WINDOWS/winsecur.h deleted file mode 100644 index fdd39d81..00000000 --- a/WINDOWS/winsecur.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * winsecur.h: some miscellaneous security-related helper functions, - * defined in winsecur.c, that use the advapi32 library. Also - * centralises the machinery for dynamically loading that library. - */ - -#if !defined NO_SECURITY - -#include <aclapi.h> - -/* - * Functions loaded from advapi32.dll. - */ -DECL_WINDOWS_FUNCTION(extern, BOOL, OpenProcessToken, - (HANDLE, DWORD, PHANDLE)); -DECL_WINDOWS_FUNCTION(extern, BOOL, GetTokenInformation, - (HANDLE, TOKEN_INFORMATION_CLASS, - LPVOID, DWORD, PDWORD)); -DECL_WINDOWS_FUNCTION(extern, BOOL, InitializeSecurityDescriptor, - (PSECURITY_DESCRIPTOR, DWORD)); -DECL_WINDOWS_FUNCTION(extern, BOOL, SetSecurityDescriptorOwner, - (PSECURITY_DESCRIPTOR, PSID, BOOL)); -DECL_WINDOWS_FUNCTION(extern, DWORD, GetSecurityInfo, - (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION, - PSID *, PSID *, PACL *, PACL *, - PSECURITY_DESCRIPTOR *)); -DECL_WINDOWS_FUNCTION(extern, DWORD, SetSecurityInfo, - (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION, - PSID, PSID, PACL, PACL)); -DECL_WINDOWS_FUNCTION(extern, DWORD, SetEntriesInAclA, - (ULONG, PEXPLICIT_ACCESS, PACL, PACL *)); -bool got_advapi(void); - -/* - * Find the SID describing the current user. The return value (if not - * NULL for some error-related reason) is smalloced. - */ -PSID get_user_sid(void); - -/* - * Construct a PSECURITY_DESCRIPTOR of the type used for named pipe - * servers, i.e. allowing access only to the current user id and also - * only local (i.e. not over SMB) connections. - * - * If this function returns true, then 'psd' and 'acl' will have been - * filled in with memory allocated using LocalAlloc (and hence must be - * freed later using LocalFree). If it returns false, then instead - * 'error' has been filled with a dynamically allocated error message. - */ -bool make_private_security_descriptor( - DWORD permissions, PSECURITY_DESCRIPTOR *psd, PACL *acl, char **error); - -#endif diff --git a/WINDOWS/winselcli.c b/WINDOWS/winselcli.c deleted file mode 100644 index f19a0bbe..00000000 --- a/WINDOWS/winselcli.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Implementation of do_select() for winnet.c to use, suitable for use - * when there's no GUI window to have network activity reported to. - * - * It uses WSAEventSelect, where available, to convert network - * activity into activity on an event object, for integration into an - * event loop that includes WaitForMultipleObjects. - * - * It also maintains a list of currently active sockets, which can be - * retrieved by a front end that wants to use WinSock's synchronous - * select() function. - */ - -#include "putty.h" - -static tree234 *winselcli_sockets; - -static int socket_cmp(void *av, void *bv) -{ - return memcmp(av, bv, sizeof(SOCKET)); -} - -HANDLE winselcli_event = INVALID_HANDLE_VALUE; - -void winselcli_setup(void) -{ - if (!winselcli_sockets) - winselcli_sockets = newtree234(socket_cmp); - - if (p_WSAEventSelect && winselcli_event == INVALID_HANDLE_VALUE) - winselcli_event = CreateEvent(NULL, false, false, NULL); -} - -SOCKET winselcli_unique_socket(void) -{ - if (!winselcli_sockets) - return INVALID_SOCKET; - - assert(count234(winselcli_sockets) <= 1); - - SOCKET *p = index234(winselcli_sockets, 0); - if (!p) - return INVALID_SOCKET; - - return *p; -} - -const char *do_select(SOCKET skt, bool enable) -{ - /* Check everything's been set up, for convenience of callers. */ - winselcli_setup(); - - if (enable) { - SOCKET *ptr = snew(SOCKET); - *ptr = skt; - if (add234(winselcli_sockets, ptr) != ptr) - sfree(ptr); /* already there */ - } else { - SOCKET *ptr = del234(winselcli_sockets, &skt); - if (ptr) - sfree(ptr); - } - - if (p_WSAEventSelect) { - int events; - if (enable) { - events = (FD_CONNECT | FD_READ | FD_WRITE | - FD_OOB | FD_CLOSE | FD_ACCEPT); - } else { - events = 0; - } - - if (p_WSAEventSelect(skt, winselcli_event, events) == SOCKET_ERROR) - return winsock_error_string(p_WSAGetLastError()); - } - - return NULL; -} diff --git a/WINDOWS/winselgui.c b/WINDOWS/winselgui.c deleted file mode 100644 index 48a15212..00000000 --- a/WINDOWS/winselgui.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Implementation of do_select() for winnet.c to use, that uses - * WSAAsyncSelect to convert network activity into window messages, - * for integration into a GUI event loop. - */ - -#include "putty.h" - -static HWND winsel_hwnd = NULL; - -void winselgui_set_hwnd(HWND hwnd) -{ - winsel_hwnd = hwnd; -} - -void winselgui_clear_hwnd(void) -{ - winsel_hwnd = NULL; -} - -const char *do_select(SOCKET skt, bool enable) -{ - int msg, events; - if (enable) { - msg = WM_NETEVENT; - events = (FD_CONNECT | FD_READ | FD_WRITE | - FD_OOB | FD_CLOSE | FD_ACCEPT); - } else { - msg = events = 0; - } - - assert(winsel_hwnd); - - if (p_WSAAsyncSelect(skt, winsel_hwnd, msg, events) == SOCKET_ERROR) - return winsock_error_string(p_WSAGetLastError()); - - return NULL; -} diff --git a/WINDOWS/winshare.c b/WINDOWS/winshare.c deleted file mode 100644 index f0e409ac..00000000 --- a/WINDOWS/winshare.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Windows implementation of SSH connection-sharing IPC setup. - */ - -#include <stdio.h> -#include <assert.h> - -#if !defined NO_SECURITY - -#include "tree234.h" -#include "putty.h" -#include "network.h" -#include "proxy.h" -#include "ssh.h" - -#include "wincapi.h" -#include "winsecur.h" - -#define CONNSHARE_PIPE_PREFIX "\\\\.\\pipe\\putty-connshare" -#define CONNSHARE_MUTEX_PREFIX "Local\\putty-connshare-mutex" - -static char *make_name(const char *prefix, const char *name) -{ - char *username, *retname; - - username = get_username(); - retname = dupprintf("%s.%s.%s", prefix, username, name); - sfree(username); - - return retname; -} - -int platform_ssh_share(const char *pi_name, Conf *conf, - Plug *downplug, Plug *upplug, Socket **sock, - char **logtext, char **ds_err, char **us_err, - bool can_upstream, bool can_downstream) -{ - char *name, *mutexname, *pipename; - HANDLE mutex; - Socket *retsock; - PSECURITY_DESCRIPTOR psd; - PACL acl; - - /* - * Transform the platform-independent version of the connection - * identifier into the obfuscated version we'll use for our - * Windows named pipe and mutex. A side effect of doing this is - * that it also eliminates any characters illegal in Windows pipe - * names. - */ - name = capi_obfuscate_string(pi_name); - if (!name) { - *logtext = dupprintf("Unable to call CryptProtectMemory: %s", - win_strerror(GetLastError())); - return SHARE_NONE; - } - - /* - * Make a mutex name out of the connection identifier, and lock it - * while we decide whether to be upstream or downstream. - */ - { - SECURITY_ATTRIBUTES sa; - - mutexname = make_name(CONNSHARE_MUTEX_PREFIX, name); - if (!make_private_security_descriptor(MUTEX_ALL_ACCESS, - &psd, &acl, logtext)) { - sfree(mutexname); - sfree(name); - return SHARE_NONE; - } - - memset(&sa, 0, sizeof(sa)); - sa.nLength = sizeof(sa); - sa.lpSecurityDescriptor = psd; - sa.bInheritHandle = false; - - mutex = CreateMutex(&sa, false, mutexname); - - if (!mutex) { - *logtext = dupprintf("CreateMutex(\"%s\") failed: %s", - mutexname, win_strerror(GetLastError())); - sfree(mutexname); - sfree(name); - LocalFree(psd); - LocalFree(acl); - return SHARE_NONE; - } - - sfree(mutexname); - LocalFree(psd); - LocalFree(acl); - - WaitForSingleObject(mutex, INFINITE); - } - - pipename = make_name(CONNSHARE_PIPE_PREFIX, name); - - *logtext = NULL; - - if (can_downstream) { - retsock = new_named_pipe_client(pipename, downplug); - if (sk_socket_error(retsock) == NULL) { - sfree(*logtext); - *logtext = pipename; - *sock = retsock; - sfree(name); - ReleaseMutex(mutex); - CloseHandle(mutex); - return SHARE_DOWNSTREAM; - } - sfree(*ds_err); - *ds_err = dupprintf("%s: %s", pipename, sk_socket_error(retsock)); - sk_close(retsock); - } - - if (can_upstream) { - retsock = new_named_pipe_listener(pipename, upplug); - if (sk_socket_error(retsock) == NULL) { - sfree(*logtext); - *logtext = pipename; - *sock = retsock; - sfree(name); - ReleaseMutex(mutex); - CloseHandle(mutex); - return SHARE_UPSTREAM; - } - sfree(*us_err); - *us_err = dupprintf("%s: %s", pipename, sk_socket_error(retsock)); - sk_close(retsock); - } - - /* One of the above clauses ought to have happened. */ - assert(*logtext || *ds_err || *us_err); - - sfree(pipename); - sfree(name); - ReleaseMutex(mutex); - CloseHandle(mutex); - return SHARE_NONE; -} - -void platform_ssh_share_cleanup(const char *name) -{ -} - -#else /* !defined NO_SECURITY */ - -#include "noshare.c" - -#endif /* !defined NO_SECURITY */ diff --git a/WINDOWS/winsocks.c b/WINDOWS/winsocks.c deleted file mode 100644 index 83ba364c..00000000 --- a/WINDOWS/winsocks.c +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Main program for Windows psocks. - */ - -#include "putty.h" -#include "ssh.h" -#include "psocks.h" - -static const PsocksPlatform platform = { - NULL /* open_pipes */, - NULL /* start_subcommand */, -}; - -int main(int argc, char **argv) -{ - psocks_state *ps = psocks_new(&platform); - psocks_cmdline(ps, argc, argv); - - sk_init(); - winselcli_setup(); - psocks_start(ps); - - cli_main_loop(cliloop_null_pre, cliloop_null_post, NULL); -} |