Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mRemoteNG/PuTTYNG.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDimitrij <kvarkas@gmail.com>2022-10-31 00:45:23 +0300
committerDimitrij <kvarkas@gmail.com>2022-10-31 00:45:23 +0300
commit302fb2e8ddea1c993552c9a30c02f41d01ca54a9 (patch)
treed6cf1b32664296ef2cecda33caeafbe39e6695c1 /windows
parent59105d9b26363e47f00676bd365b2ac8d4cb536a (diff)
parent4ff82ab29a22936b78510c68f544a99e677efed3 (diff)
Merge tag 'tags/0.78'HEADmaster
Diffstat (limited to 'windows')
-rw-r--r--windows/CMakeLists.txt186
-rw-r--r--windows/agent-client.c (renamed from windows/winpgntc.c)63
-rw-r--r--windows/cliloop.c (renamed from windows/wincliloop.c)22
-rw-r--r--windows/config.c (renamed from windows/wincfg.c)75
-rw-r--r--windows/conpty.c433
-rw-r--r--windows/console.c (renamed from windows/wincons.c)186
-rw-r--r--windows/controls.c (renamed from windows/winctrls.c)458
-rw-r--r--windows/cryptoapi.h (renamed from windows/wincapi.h)6
-rw-r--r--windows/dialog.c (renamed from windows/windlg.c)906
-rw-r--r--windows/gss.c (renamed from windows/wingss.c)19
-rw-r--r--windows/handle-io.c (renamed from windows/winhandl.c)250
-rw-r--r--windows/handle-socket.c (renamed from windows/winhsock.c)233
-rw-r--r--windows/handle-wait.c143
-rw-r--r--windows/help.c (renamed from windows/winhelp.c)6
-rw-r--r--windows/help.h (renamed from windows/winhelp.h)15
-rw-r--r--windows/help.rc28
-rw-r--r--windows/installer.wxs15
-rw-r--r--windows/jump-list.c (renamed from windows/winjump.c)48
-rw-r--r--windows/local-proxy.c (renamed from windows/winproxy.c)89
-rw-r--r--windows/named-pipe-client.c (renamed from windows/winnpc.c)21
-rw-r--r--windows/named-pipe-server.c (renamed from windows/winnps.c)57
-rw-r--r--windows/network.c (renamed from windows/winnet.c)568
-rw-r--r--windows/no-jump-list.c (renamed from windows/winnojmp.c)6
-rw-r--r--windows/nohelp.c (renamed from windows/winnohlp.c)2
-rw-r--r--windows/noise.c (renamed from windows/winnoise.c)0
-rw-r--r--windows/pageant.c (renamed from windows/winpgnt.c)795
-rw-r--r--windows/pageant.rc8
-rw-r--r--windows/platform.h (renamed from windows/winstuff.h)306
-rw-r--r--windows/plink.c (renamed from windows/winplink.c)51
-rw-r--r--windows/printing.c (renamed from windows/winprint.c)16
-rw-r--r--windows/psocks.c (renamed from windows/winsocks.c)0
-rw-r--r--windows/pterm.c65
-rw-r--r--windows/pterm.icobin0 -> 4078 bytes
-rw-r--r--windows/pterm.rc15
-rw-r--r--windows/ptermcfg.icobin0 -> 4078 bytes
-rw-r--r--windows/putty-common.rc298
-rw-r--r--windows/putty-rc.h (renamed from windows/win_res.h)12
-rw-r--r--windows/putty.c203
-rw-r--r--windows/putty.rc8
-rw-r--r--windows/puttygen.c (renamed from windows/winpgen.c)936
-rw-r--r--windows/puttygen.rc12
-rw-r--r--windows/puttytel.rc8
-rw-r--r--windows/rcstuff.h15
-rw-r--r--windows/security-api.h (renamed from windows/winsecur.h)8
-rw-r--r--windows/select-cli.c (renamed from windows/winselcli.c)2
-rw-r--r--windows/select-gui.c65
-rw-r--r--windows/serial.c (renamed from windows/winser.c)13
-rw-r--r--windows/sftp.c (renamed from windows/winsftp.c)30
-rw-r--r--windows/sharing.c (renamed from windows/winshare.c)56
-rw-r--r--windows/storage.c (renamed from windows/winstore.c)564
-rw-r--r--windows/test_screenshot.c45
-rw-r--r--windows/unicode.c (renamed from windows/winucs.c)279
-rw-r--r--windows/utils/agent_mutex_name.c14
-rw-r--r--windows/utils/agent_named_pipe_name.c17
-rw-r--r--windows/utils/arm_arch_queries.c45
-rw-r--r--windows/utils/aux_match_opt.c117
-rw-r--r--windows/utils/centre_window.c20
-rw-r--r--windows/utils/cryptoapi.c (renamed from windows/wincapi.c)8
-rw-r--r--windows/utils/defaults.c (renamed from windows/windefs.c)2
-rw-r--r--windows/utils/dll_hijacking_protection.c43
-rw-r--r--windows/utils/dputs.c39
-rw-r--r--windows/utils/escape_registry_key.c48
-rw-r--r--windows/utils/filename.c54
-rw-r--r--windows/utils/fontspec.c43
-rw-r--r--windows/utils/get_system_dir.c21
-rw-r--r--windows/utils/get_username.c77
-rw-r--r--windows/utils/getdlgitemtext_alloc.c20
-rw-r--r--windows/utils/interprocess_mutex.c48
-rw-r--r--windows/utils/is_console_handle.c13
-rw-r--r--windows/utils/load_system32_dll.c18
-rw-r--r--windows/utils/ltime.c (renamed from windows/wintime.c)3
-rw-r--r--windows/utils/make_spr_sw_abort_winerror.c22
-rw-r--r--windows/utils/makedlgitemborderless.c19
-rw-r--r--windows/utils/message_box.c49
-rw-r--r--windows/utils/minefield.c (renamed from windows/winmiscs.c)77
-rw-r--r--windows/utils/open_for_write_would_lose_data.c76
-rw-r--r--windows/utils/pgp_fingerprints_msgbox.c25
-rw-r--r--windows/utils/platform_get_x_display.c13
-rw-r--r--windows/utils/registry.c184
-rw-r--r--windows/utils/request_file.c71
-rw-r--r--windows/utils/screenshot.c126
-rw-r--r--windows/utils/security.c (renamed from windows/winsecur.c)38
-rw-r--r--windows/utils/shinydialogbox.c111
-rw-r--r--windows/utils/split_into_argv.c (renamed from windows/winutils.c)413
-rw-r--r--windows/utils/strtoumax.c12
-rw-r--r--windows/utils/version.c45
-rw-r--r--windows/utils/win_strerror.c72
-rw-r--r--windows/win-gui-seat.h (renamed from windows/winseat.h)4
-rw-r--r--windows/win_res.rc2137
-rw-r--r--windows/window.c404
-rw-r--r--windows/winhelp.rc28
-rw-r--r--windows/winmisc.c467
-rw-r--r--windows/winselgui.c38
-rw-r--r--windows/x11.c (renamed from windows/winx11.c)2
94 files changed, 7157 insertions, 3331 deletions
diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt
new file mode 100644
index 00000000..d34b4106
--- /dev/null
+++ b/windows/CMakeLists.txt
@@ -0,0 +1,186 @@
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
+
+add_sources_from_current_dir(utils
+ utils/agent_mutex_name.c
+ utils/agent_named_pipe_name.c
+ utils/arm_arch_queries.c
+ utils/aux_match_opt.c
+ utils/centre_window.c
+ utils/cryptoapi.c
+ utils/defaults.c
+ utils/dll_hijacking_protection.c
+ utils/dputs.c
+ utils/escape_registry_key.c
+ utils/filename.c
+ utils/fontspec.c
+ utils/getdlgitemtext_alloc.c
+ utils/get_system_dir.c
+ utils/get_username.c
+ utils/interprocess_mutex.c
+ utils/is_console_handle.c
+ utils/load_system32_dll.c
+ utils/ltime.c
+ utils/makedlgitemborderless.c
+ utils/make_spr_sw_abort_winerror.c
+ utils/message_box.c
+ utils/minefield.c
+ utils/open_for_write_would_lose_data.c
+ utils/pgp_fingerprints_msgbox.c
+ utils/platform_get_x_display.c
+ utils/registry.c
+ utils/request_file.c
+ utils/screenshot.c
+ utils/security.c
+ utils/shinydialogbox.c
+ utils/split_into_argv.c
+ utils/version.c
+ utils/win_strerror.c
+ unicode.c)
+if(NOT HAVE_STRTOUMAX)
+ add_sources_from_current_dir(utils utils/strtoumax.c)
+endif()
+add_sources_from_current_dir(eventloop
+ cliloop.c handle-wait.c)
+add_sources_from_current_dir(console
+ select-cli.c nohelp.c console.c)
+add_sources_from_current_dir(settings
+ storage.c)
+add_sources_from_current_dir(network
+ network.c handle-socket.c named-pipe-client.c named-pipe-server.c local-proxy.c x11.c)
+add_sources_from_current_dir(sshcommon
+ noise.c)
+add_sources_from_current_dir(sshclient
+ agent-client.c gss.c sharing.c)
+add_sources_from_current_dir(sftpclient
+ sftp.c)
+add_sources_from_current_dir(otherbackends
+ serial.c)
+add_sources_from_current_dir(agent
+ agent-client.c)
+add_sources_from_current_dir(guiterminal
+ dialog.c controls.c config.c printing.c jump-list.c sizetip.c)
+add_dependencies(guiterminal generated_licence_h) # dialog.c uses licence.h
+
+# This object awkwardly needs to live in the network library as well
+# as the eventloop library, in case it didn't get pulled in from the
+# latter before handle-socket.c needed it.
+add_library(handle-io OBJECT
+ handle-io.c)
+target_sources(eventloop PRIVATE $<TARGET_OBJECTS:handle-io>)
+target_sources(network PRIVATE $<TARGET_OBJECTS:handle-io>)
+
+add_library(guimisc STATIC
+ select-gui.c)
+
+add_executable(pageant
+ pageant.c
+ help.c
+ pageant.rc)
+add_dependencies(pageant generated_licence_h)
+target_link_libraries(pageant
+ guimisc eventloop agent network crypto utils
+ ${platform_libraries})
+set_target_properties(pageant PROPERTIES
+ WIN32_EXECUTABLE ON
+ LINK_FLAGS "${LFLAG_MANIFEST_NO}")
+installed_program(pageant)
+
+add_sources_from_current_dir(plink no-jump-list.c nohelp.c plink.rc)
+add_dependencies(plink generated_licence_h)
+
+add_sources_from_current_dir(pscp no-jump-list.c nohelp.c pscp.rc)
+add_dependencies(pscp generated_licence_h)
+
+add_sources_from_current_dir(psftp no-jump-list.c nohelp.c psftp.rc)
+add_dependencies(psftp generated_licence_h)
+
+add_sources_from_current_dir(psocks nohelp.c)
+
+add_executable(putty
+ window.c
+ putty.c
+ help.c
+ putty.rc)
+be_list(putty PuTTY SSH SERIAL OTHERBACKENDS)
+add_dependencies(putty generated_licence_h)
+target_link_libraries(putty
+ guiterminal guimisc eventloop sshclient otherbackends settings network crypto
+ utils
+ ${platform_libraries})
+set_target_properties(putty PROPERTIES
+ WIN32_EXECUTABLE ON
+ LINK_FLAGS "${LFLAG_MANIFEST_NO}")
+installed_program(putty)
+
+add_executable(puttytel
+ window.c
+ putty.c
+ help.c
+ ${CMAKE_SOURCE_DIR}/stubs/no-gss.c
+ ${CMAKE_SOURCE_DIR}/stubs/no-ca-config.c
+ ${CMAKE_SOURCE_DIR}/stubs/no-rand.c
+ ${CMAKE_SOURCE_DIR}/proxy/nocproxy.c
+ ${CMAKE_SOURCE_DIR}/proxy/nosshproxy.c
+ puttytel.rc)
+be_list(puttytel PuTTYtel SERIAL OTHERBACKENDS)
+add_dependencies(puttytel generated_licence_h)
+target_link_libraries(puttytel
+ guiterminal guimisc eventloop otherbackends settings network utils
+ ${platform_libraries})
+set_target_properties(puttytel PROPERTIES
+ WIN32_EXECUTABLE ON
+ LINK_FLAGS "${LFLAG_MANIFEST_NO}")
+installed_program(puttytel)
+
+add_executable(puttygen
+ puttygen.c
+ ${CMAKE_SOURCE_DIR}/stubs/no-timing.c
+ noise.c
+ no-jump-list.c
+ storage.c
+ help.c
+ ${CMAKE_SOURCE_DIR}/sshpubk.c
+ ${CMAKE_SOURCE_DIR}/sshrand.c
+ controls.c
+ puttygen.rc)
+add_dependencies(puttygen generated_licence_h)
+target_link_libraries(puttygen
+ keygen guimisc crypto utils
+ ${platform_libraries})
+set_target_properties(puttygen PROPERTIES
+ WIN32_EXECUTABLE ON
+ LINK_FLAGS "${LFLAG_MANIFEST_NO}")
+installed_program(puttygen)
+
+if(HAVE_CONPTY)
+ add_executable(pterm
+ window.c
+ pterm.c
+ help.c
+ conpty.c
+ ${CMAKE_SOURCE_DIR}/stubs/no-gss.c
+ ${CMAKE_SOURCE_DIR}/stubs/no-ca-config.c
+ ${CMAKE_SOURCE_DIR}/stubs/no-rand.c
+ ${CMAKE_SOURCE_DIR}/proxy/nosshproxy.c
+ pterm.rc)
+ be_list(pterm pterm)
+ add_dependencies(pterm generated_licence_h)
+ target_link_libraries(pterm
+ guiterminal guimisc eventloop settings network utils
+ ${platform_libraries})
+ set_target_properties(pterm PROPERTIES
+ WIN32_EXECUTABLE ON
+ LINK_FLAGS "${LFLAG_MANIFEST_NO}")
+ installed_program(pterm)
+else()
+ message("ConPTY not available; cannot build Windows pterm")
+endif()
+
+add_executable(test_split_into_argv
+ utils/split_into_argv.c)
+target_compile_definitions(test_split_into_argv PRIVATE TEST)
+target_link_libraries(test_split_into_argv utils ${platform_libraries})
+
+add_executable(test_screenshot
+ test_screenshot.c)
+target_link_libraries(test_screenshot utils ${platform_libraries})
diff --git a/windows/winpgntc.c b/windows/agent-client.c
index 557dc532..168465e5 100644
--- a/windows/winpgntc.c
+++ b/windows/agent-client.c
@@ -9,12 +9,8 @@
#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 */
+#include "security-api.h"
+#include "cryptoapi.h"
static bool wm_copydata_agent_exists(void)
{
@@ -50,7 +46,6 @@ static void wm_copydata_agent_query(strbuf *query, void **out, int *outlen)
mapname = dupprintf("PageantRequest%08x", (unsigned)GetCurrentThreadId());
psa = NULL;
-#ifndef NO_SECURITY
if (got_advapi()) {
/*
* Make the file mapping we create for communication with
@@ -67,8 +62,8 @@ static void wm_copydata_agent_query(strbuf *query, void **out, int *outlen)
psd = (PSECURITY_DESCRIPTOR)
LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
if (psd) {
- if (p_InitializeSecurityDescriptor
- (psd, SECURITY_DESCRIPTOR_REVISION) &&
+ if (p_InitializeSecurityDescriptor(
+ psd, SECURITY_DESCRIPTOR_REVISION) &&
p_SetSecurityDescriptorOwner(psd, usersid, false)) {
sa.nLength = sizeof(sa);
sa.bInheritHandle = true;
@@ -81,7 +76,6 @@ static void wm_copydata_agent_query(strbuf *query, void **out, int *outlen)
}
}
}
-#endif /* NO_SECURITY */
filemap = CreateFileMapping(INVALID_HANDLE_VALUE, psa, PAGE_READWRITE,
0, AGENT_MAX_MSGLEN, mapname);
@@ -129,19 +123,6 @@ static void wm_copydata_agent_query(strbuf *query, void **out, int *outlen)
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();
@@ -310,39 +291,3 @@ agent_pending_query *agent_query(
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/wincliloop.c b/windows/cliloop.c
index 26a4d3aa..eced54ca 100644
--- a/windows/wincliloop.c
+++ b/windows/cliloop.c
@@ -8,8 +8,6 @@ void cli_main_loop(cliloop_pre_t pre, cliloop_post_t post, void *ctx)
now = GETTICKCOUNT();
while (true) {
- int nhandles;
- HANDLE *handles;
DWORD n;
DWORD ticks;
@@ -34,25 +32,25 @@ void cli_main_loop(cliloop_pre_t pre, cliloop_post_t post, void *ctx)
* get WAIT_TIMEOUT */
}
- handles = handle_get_events(&nhandles);
+ HandleWaitList *hwl = get_handle_wait_list();
size_t winselcli_index = -(size_t)1;
- size_t extra_base = nhandles;
+ size_t extra_base = hwl->nhandles;
if (winselcli_event != INVALID_HANDLE_VALUE) {
+ assert(extra_base < MAXIMUM_WAIT_OBJECTS);
winselcli_index = extra_base++;
- handles = sresize(handles, extra_base, HANDLE);
- handles[winselcli_index] = winselcli_event;
+ hwl->handles[winselcli_index] = winselcli_event;
}
size_t total_handles = extra_base + n_extra_handles;
- handles = sresize(handles, total_handles, HANDLE);
+ assert(total_handles < MAXIMUM_WAIT_OBJECTS);
for (size_t i = 0; i < n_extra_handles; i++)
- handles[extra_base + i] = extra_handles[i];
+ hwl->handles[extra_base + i] = extra_handles[i];
- n = WaitForMultipleObjects(total_handles, handles, false, ticks);
+ n = WaitForMultipleObjects(total_handles, hwl->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]);
+ if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)hwl->nhandles) {
+ handle_wait_activate(hwl, n - WAIT_OBJECT_0);
} else if (winselcli_event != INVALID_HANDLE_VALUE &&
n == WAIT_OBJECT_0 + winselcli_index) {
WSANETWORKEVENTS things;
@@ -122,7 +120,7 @@ void cli_main_loop(cliloop_pre_t pre, cliloop_post_t post, void *ctx)
now = GETTICKCOUNT();
}
- sfree(handles);
+ handle_wait_list_free(hwl);
if (!post(ctx, extra_handle_index))
break;
diff --git a/windows/wincfg.c b/windows/config.c
index fab3240f..fc9070bf 100644
--- a/windows/wincfg.c
+++ b/windows/config.c
@@ -1,5 +1,5 @@
/*
- * wincfg.c - the Windows-specific parts of the PuTTY configuration
+ * config.c - the Windows-specific parts of the PuTTY configuration
* box.
*/
@@ -10,27 +10,27 @@
#include "dialog.h"
#include "storage.h"
-static void about_handler(union control *ctrl, dlgparam *dlg,
+static void about_handler(dlgcontrol *ctrl, dlgparam *dlg,
void *data, int event)
{
- HWND *hwndp = (HWND *)ctrl->generic.context.p;
+ HWND *hwndp = (HWND *)ctrl->context.p;
if (event == EVENT_ACTION) {
modal_about_box(*hwndp);
}
}
-static void help_handler(union control *ctrl, dlgparam *dlg,
+static void help_handler(dlgcontrol *ctrl, dlgparam *dlg,
void *data, int event)
{
- HWND *hwndp = (HWND *)ctrl->generic.context.p;
+ HWND *hwndp = (HWND *)ctrl->context.p;
if (event == EVENT_ACTION) {
show_help(*hwndp);
}
}
-static void variable_pitch_handler(union control *ctrl, dlgparam *dlg,
+static void variable_pitch_handler(dlgcontrol *ctrl, dlgparam *dlg,
void *data, int event)
{
if (event == EVENT_REFRESH) {
@@ -46,7 +46,7 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, bool has_help,
const struct BackendVtable *backvt;
bool resize_forbidden = false;
struct controlset *s;
- union control *c;
+ dlgcontrol *c;
char *str;
if (!midsession) {
@@ -56,11 +56,11 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, bool has_help,
s = ctrl_getset(b, "", "", "");
c = ctrl_pushbutton(s, "About", 'a', HELPCTX(no_help),
about_handler, P(hwndp));
- c->generic.column = 0;
+ c->column = 0;
if (has_help) {
c = ctrl_pushbutton(s, "Help", 'h', HELPCTX(no_help),
help_handler, P(hwndp));
- c->generic.column = 1;
+ c->column = 1;
}
}
@@ -82,8 +82,8 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, bool has_help,
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) {
+ if (c->type == CTRL_CHECKBOX &&
+ c->context.i == CONF_scrollbar) {
/*
* Control i is the scrollbar checkbox.
* Control s->ncontrols-1 is the scrollbar-in-FS one.
@@ -91,7 +91,7 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, bool has_help,
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->ncontrols-i-2)*sizeof(dlgcontrol *));
s->ctrls[i+1] = c;
}
break;
@@ -134,9 +134,9 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, bool has_help,
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);
+ if (c->type == CTRL_RADIO &&
+ c->context.i == CONF_beep) {
+ assert(c->handler == conf_radiobutton_handler);
c->radio.nbuttons += 2;
c->radio.buttons =
sresize(c->radio.buttons, c->radio.nbuttons, char *);
@@ -173,7 +173,7 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, bool has_help,
I(CONF_beep_ind),
"Disabled", I(B_IND_DISABLED),
"Flashing", I(B_IND_FLASH),
- "Steady", I(B_IND_STEADY), NULL);
+ "Steady", I(B_IND_STEADY));
/*
* The sunken-edge border is a Windows GUI feature.
@@ -198,7 +198,7 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, bool has_help,
"Antialiased", I(FQ_ANTIALIASED),
"Non-Antialiased", I(FQ_NONANTIALIASED),
"ClearType", I(FQ_CLEARTYPE),
- "Default", I(FQ_DEFAULT), NULL);
+ "Default", I(FQ_DEFAULT));
/*
* Cyrillic Lock is a horrid misfeature even on Windows, and
@@ -233,9 +233,9 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, bool has_help,
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);
+ if (c->type == CTRL_RADIO &&
+ c->context.i == CONF_vtmode) {
+ assert(c->handler == conf_radiobutton_handler);
c->radio.nbuttons += 3;
c->radio.buttons =
sresize(c->radio.buttons, c->radio.nbuttons, char *);
@@ -289,14 +289,14 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, bool has_help,
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);
+ "xterm (Right extends, Middle pastes)", I(1));
/*
* 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 *));
+ memmove(s->ctrls+1, s->ctrls, (s->ncontrols-1)*sizeof(dlgcontrol *));
s->ctrls[0] = c;
/*
@@ -328,7 +328,7 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, bool has_help,
"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);
+ "Forbid resizing completely", I(RESIZE_DISABLED));
}
/*
@@ -355,37 +355,16 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, bool has_help,
I(CONF_fullscreenonaltenter));
/*
- * Windows supports a local-command proxy. This also means we
- * must adjust the text on the `Telnet command' control.
+ * Windows supports a local-command proxy.
*/
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");
+ if (c->type == CTRL_LISTBOX &&
+ c->handler == proxy_type_handler) {
+ c->context.i |= PROXY_UI_FLAG_LOCAL;
break;
}
}
diff --git a/windows/conpty.c b/windows/conpty.c
new file mode 100644
index 00000000..c23c81e1
--- /dev/null
+++ b/windows/conpty.c
@@ -0,0 +1,433 @@
+/*
+ * Backend to run a Windows console session using ConPTY.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include "putty.h"
+
+#include <windows.h>
+#include <consoleapi.h>
+
+typedef struct ConPTY ConPTY;
+struct ConPTY {
+ HPCON pseudoconsole;
+ HANDLE outpipe, inpipe, hprocess;
+ struct handle *out, *in;
+ HandleWait *subprocess;
+ bool exited;
+ DWORD exitstatus;
+ Seat *seat;
+ LogContext *logctx;
+ int bufsize;
+ Backend backend;
+};
+
+DECL_WINDOWS_FUNCTION(static, HRESULT, CreatePseudoConsole,
+ (COORD, HANDLE, HANDLE, DWORD, HPCON *));
+DECL_WINDOWS_FUNCTION(static, void, ClosePseudoConsole, (HPCON));
+DECL_WINDOWS_FUNCTION(static, HRESULT, ResizePseudoConsole, (HPCON, COORD));
+
+static bool init_conpty_api(void)
+{
+ static bool tried = false;
+ if (!tried) {
+ tried = true;
+ HMODULE kernel32_module = load_system32_dll("kernel32.dll");
+ GET_WINDOWS_FUNCTION(kernel32_module, CreatePseudoConsole);
+ GET_WINDOWS_FUNCTION(kernel32_module, ClosePseudoConsole);
+ GET_WINDOWS_FUNCTION(kernel32_module, ResizePseudoConsole);
+ }
+
+ return (p_CreatePseudoConsole != NULL &&
+ p_ClosePseudoConsole != NULL &&
+ p_ResizePseudoConsole != NULL);
+}
+
+static void conpty_terminate(ConPTY *conpty)
+{
+ if (conpty->out) {
+ handle_free(conpty->out);
+ conpty->out = NULL;
+ }
+ if (conpty->outpipe != INVALID_HANDLE_VALUE) {
+ CloseHandle(conpty->outpipe);
+ conpty->outpipe = INVALID_HANDLE_VALUE;
+ }
+ if (conpty->in) {
+ handle_free(conpty->in);
+ conpty->in = NULL;
+ }
+ if (conpty->inpipe != INVALID_HANDLE_VALUE) {
+ CloseHandle(conpty->inpipe);
+ conpty->inpipe = INVALID_HANDLE_VALUE;
+ }
+ if (conpty->subprocess) {
+ delete_handle_wait(conpty->subprocess);
+ conpty->subprocess = NULL;
+ conpty->hprocess = INVALID_HANDLE_VALUE;
+ }
+ if (conpty->pseudoconsole != INVALID_HANDLE_VALUE) {
+ p_ClosePseudoConsole(conpty->pseudoconsole);
+ conpty->pseudoconsole = INVALID_HANDLE_VALUE;
+ }
+}
+
+static void conpty_process_wait_callback(void *vctx)
+{
+ ConPTY *conpty = (ConPTY *)vctx;
+
+ if (!GetExitCodeProcess(conpty->hprocess, &conpty->exitstatus))
+ return;
+ conpty->exited = true;
+
+ /*
+ * We can stop waiting for the process now.
+ */
+ if (conpty->subprocess) {
+ delete_handle_wait(conpty->subprocess);
+ conpty->subprocess = NULL;
+ conpty->hprocess = INVALID_HANDLE_VALUE;
+ }
+
+ /*
+ * Once the contained process exits, close the pseudo-console as
+ * well. But don't close the pipes yet, since apparently
+ * ClosePseudoConsole can trigger a final bout of terminal output
+ * as things clean themselves up.
+ */
+ if (conpty->pseudoconsole != INVALID_HANDLE_VALUE) {
+ p_ClosePseudoConsole(conpty->pseudoconsole);
+ conpty->pseudoconsole = INVALID_HANDLE_VALUE;
+ }
+}
+
+static size_t conpty_gotdata(
+ struct handle *h, const void *data, size_t len, int err)
+{
+ ConPTY *conpty = (ConPTY *)handle_get_privdata(h);
+ if (err || len == 0) {
+ char *error_msg;
+
+ conpty_terminate(conpty);
+
+ seat_notify_remote_exit(conpty->seat);
+
+ if (!err && conpty->exited) {
+ /*
+ * The clean-exit case: our subprocess terminated, we
+ * deleted the PseudoConsole ourself, and now we got the
+ * expected EOF on the pipe.
+ */
+ return 0;
+ }
+
+ if (err)
+ error_msg = dupprintf("Error reading from console pty: %s",
+ win_strerror(err));
+ else
+ error_msg = dupprintf(
+ "Unexpected end of file reading from console pty");
+
+ logevent(conpty->logctx, error_msg);
+ seat_connection_fatal(conpty->seat, "%s", error_msg);
+ sfree(error_msg);
+
+ return 0;
+ } else {
+ return seat_stdout(conpty->seat, data, len);
+ }
+}
+
+static void conpty_sentdata(struct handle *h, size_t new_backlog, int err,
+ bool close)
+{
+ ConPTY *conpty = (ConPTY *)handle_get_privdata(h);
+ if (err) {
+ const char *error_msg = "Error writing to conpty device";
+
+ conpty_terminate(conpty);
+
+ seat_notify_remote_exit(conpty->seat);
+
+ logevent(conpty->logctx, error_msg);
+
+ seat_connection_fatal(conpty->seat, "%s", error_msg);
+ } else {
+ conpty->bufsize = new_backlog;
+ }
+}
+
+static char *conpty_init(const BackendVtable *vt, Seat *seat,
+ Backend **backend_handle, LogContext *logctx,
+ Conf *conf, const char *host, int port,
+ char **realhost, bool nodelay, bool keepalive)
+{
+ ConPTY *conpty;
+ char *err = NULL;
+
+ HANDLE in_r = INVALID_HANDLE_VALUE;
+ HANDLE in_w = INVALID_HANDLE_VALUE;
+ HANDLE out_r = INVALID_HANDLE_VALUE;
+ HANDLE out_w = INVALID_HANDLE_VALUE;
+
+ HPCON pcon;
+ bool pcon_needs_cleanup = false;
+
+ STARTUPINFOEX si;
+ memset(&si, 0, sizeof(si));
+
+ if (!init_conpty_api()) {
+ err = dupprintf("Pseudo-console API is not available on this "
+ "Windows system");
+ goto out;
+ }
+
+ if (!CreatePipe(&in_r, &in_w, NULL, 0)) {
+ err = dupprintf("CreatePipe: %s", win_strerror(GetLastError()));
+ goto out;
+ }
+ if (!CreatePipe(&out_r, &out_w, NULL, 0)) {
+ err = dupprintf("CreatePipe: %s", win_strerror(GetLastError()));
+ goto out;
+ }
+
+ COORD size;
+ size.X = conf_get_int(conf, CONF_width);
+ size.Y = conf_get_int(conf, CONF_height);
+
+ HRESULT result = p_CreatePseudoConsole(size, in_r, out_w, 0, &pcon);
+ if (FAILED(result)) {
+ if (HRESULT_FACILITY(result) == FACILITY_WIN32)
+ err = dupprintf("CreatePseudoConsole: %s",
+ win_strerror(HRESULT_CODE(result)));
+ else
+ err = dupprintf("CreatePseudoConsole failed: HRESULT=0x%08x",
+ (unsigned)result);
+ goto out;
+ }
+ pcon_needs_cleanup = true;
+
+ CloseHandle(in_r);
+ in_r = INVALID_HANDLE_VALUE;
+ CloseHandle(out_w);
+ out_w = INVALID_HANDLE_VALUE;
+
+ si.StartupInfo.cb = sizeof(si);
+
+ SIZE_T attrsize = 0;
+ InitializeProcThreadAttributeList(NULL, 1, 0, &attrsize);
+ si.lpAttributeList = smalloc(attrsize);
+ if (!InitializeProcThreadAttributeList(
+ si.lpAttributeList, 1, 0, &attrsize)) {
+ err = dupprintf("InitializeProcThreadAttributeList: %s",
+ win_strerror(GetLastError()));
+ goto out;
+ }
+ if (!UpdateProcThreadAttribute(
+ si.lpAttributeList,
+ 0, PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
+ pcon, sizeof(pcon), NULL, NULL)) {
+ err = dupprintf("UpdateProcThreadAttribute: %s",
+ win_strerror(GetLastError()));
+ goto out;
+ }
+
+ PROCESS_INFORMATION pi;
+ memset(&pi, 0, sizeof(pi));
+
+ char *command;
+ const char *conf_cmd = conf_get_str(conf, CONF_remote_cmd);
+ if (*conf_cmd) {
+ command = dupstr(conf_cmd);
+ } else {
+ command = dupcat(get_system_dir(), "\\cmd.exe");
+ }
+ bool created_ok = CreateProcess(NULL, command, NULL, NULL,
+ false, EXTENDED_STARTUPINFO_PRESENT,
+ NULL, NULL, &si.StartupInfo, &pi);
+ sfree(command);
+ if (!created_ok) {
+ err = dupprintf("CreateProcess: %s",
+ win_strerror(GetLastError()));
+ goto out;
+ }
+
+ /* No local authentication phase in this protocol */
+ seat_set_trust_status(seat, false);
+
+ conpty = snew(ConPTY);
+ memset(conpty, 0, sizeof(ConPTY));
+ conpty->pseudoconsole = pcon;
+ pcon_needs_cleanup = false;
+ conpty->outpipe = in_w;
+ conpty->out = handle_output_new(in_w, conpty_sentdata, conpty, 0);
+ in_w = INVALID_HANDLE_VALUE;
+ conpty->inpipe = out_r;
+ conpty->in = handle_input_new(out_r, conpty_gotdata, conpty, 0);
+ out_r = INVALID_HANDLE_VALUE;
+ conpty->subprocess = add_handle_wait(
+ pi.hProcess, conpty_process_wait_callback, conpty);
+ conpty->hprocess = pi.hProcess;
+ CloseHandle(pi.hThread);
+ conpty->exited = false;
+ conpty->exitstatus = 0;
+ conpty->bufsize = 0;
+ conpty->backend.vt = vt;
+ *backend_handle = &conpty->backend;
+
+ conpty->seat = seat;
+ conpty->logctx = logctx;
+
+ *realhost = dupstr("");
+
+ /*
+ * Specials are always available.
+ */
+ seat_update_specials_menu(conpty->seat);
+
+ out:
+ if (in_r != INVALID_HANDLE_VALUE)
+ CloseHandle(in_r);
+ if (in_w != INVALID_HANDLE_VALUE)
+ CloseHandle(in_w);
+ if (out_r != INVALID_HANDLE_VALUE)
+ CloseHandle(out_r);
+ if (out_w != INVALID_HANDLE_VALUE)
+ CloseHandle(out_w);
+ if (pcon_needs_cleanup)
+ p_ClosePseudoConsole(pcon);
+ sfree(si.lpAttributeList);
+ return err;
+}
+
+static void conpty_free(Backend *be)
+{
+ ConPTY *conpty = container_of(be, ConPTY, backend);
+
+ conpty_terminate(conpty);
+ expire_timer_context(conpty);
+ sfree(conpty);
+}
+
+static void conpty_reconfig(Backend *be, Conf *conf)
+{
+}
+
+static void conpty_send(Backend *be, const char *buf, size_t len)
+{
+ ConPTY *conpty = container_of(be, ConPTY, backend);
+
+ if (conpty->out == NULL)
+ return;
+
+ conpty->bufsize = handle_write(conpty->out, buf, len);
+}
+
+static size_t conpty_sendbuffer(Backend *be)
+{
+ ConPTY *conpty = container_of(be, ConPTY, backend);
+ return conpty->bufsize;
+}
+
+static void conpty_size(Backend *be, int width, int height)
+{
+ ConPTY *conpty = container_of(be, ConPTY, backend);
+ COORD size;
+ size.X = width;
+ size.Y = height;
+ p_ResizePseudoConsole(conpty->pseudoconsole, size);
+}
+
+static void conpty_special(Backend *be, SessionSpecialCode code, int arg)
+{
+}
+
+static const SessionSpecial *conpty_get_specials(Backend *be)
+{
+ static const SessionSpecial specials[] = {
+ {NULL, SS_EXITMENU}
+ };
+ return specials;
+}
+
+static bool conpty_connected(Backend *be)
+{
+ return true; /* always connected */
+}
+
+static bool conpty_sendok(Backend *be)
+{
+ return true;
+}
+
+static void conpty_unthrottle(Backend *be, size_t backlog)
+{
+ ConPTY *conpty = container_of(be, ConPTY, backend);
+ if (conpty->in)
+ handle_unthrottle(conpty->in, backlog);
+}
+
+static bool conpty_ldisc(Backend *be, int option)
+{
+ return false;
+}
+
+static void conpty_provide_ldisc(Backend *be, Ldisc *ldisc)
+{
+}
+
+static int conpty_exitcode(Backend *be)
+{
+ ConPTY *conpty = container_of(be, ConPTY, backend);
+
+ if (conpty->exited) {
+ /*
+ * PuTTY's representation of exit statuses expects them to be
+ * non-negative 'int' values. But Windows exit statuses can
+ * include all those exception codes like 0xC000001D which
+ * convert to negative 32-bit ints.
+ *
+ * I don't think there's a great deal of use for returning
+ * those in full detail, right now. (Though if we ever
+ * connected this system up to a Windows version of psusan or
+ * Uppity, perhaps there might be?)
+ *
+ * So we clip them at INT_MAX-1, since INT_MAX is reserved for
+ * 'exit so unclean as to inhibit Close On Clean Exit'.
+ */
+ return (0 <= conpty->exitstatus && conpty->exitstatus < INT_MAX) ?
+ conpty->exitstatus : INT_MAX-1;
+ } else {
+ return -1;
+ }
+}
+
+static int conpty_cfg_info(Backend *be)
+{
+ return 0;
+}
+
+const BackendVtable conpty_backend = {
+ .init = conpty_init,
+ .free = conpty_free,
+ .reconfig = conpty_reconfig,
+ .send = conpty_send,
+ .sendbuffer = conpty_sendbuffer,
+ .size = conpty_size,
+ .special = conpty_special,
+ .get_specials = conpty_get_specials,
+ .connected = conpty_connected,
+ .exitcode = conpty_exitcode,
+ .sendok = conpty_sendok,
+ .ldisc_option_state = conpty_ldisc,
+ .provide_ldisc = conpty_provide_ldisc,
+ .unthrottle = conpty_unthrottle,
+ .cfg_info = conpty_cfg_info,
+ .id = "conpty",
+ .displayname_tc = "ConPTY",
+ .displayname_lc = "ConPTY", /* proper name, so capitalise it anyway */
+ .protocol = -1,
+};
diff --git a/windows/wincons.c b/windows/console.c
index 414167b4..2fd572b4 100644
--- a/windows/wincons.c
+++ b/windows/console.c
@@ -1,5 +1,5 @@
/*
- * wincons.c - various interactive-prompt routines shared between
+ * console.c - various interactive-prompt routines shared between
* the Windows console PuTTY tools
*/
@@ -32,50 +32,55 @@ void console_print_error_msg(const char *prefix, const char *msg)
fflush(stderr);
}
-int console_verify_ssh_host_key(
+SeatPromptResult console_confirm_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)
+ char *keystr, SeatDialogText *text, HelpCtx helpctx,
+ void (*callback)(void *ctx, SeatPromptResult result), void *ctx)
{
- int ret;
HANDLE hin;
DWORD savemode, i;
- const char *common_fmt, *intro, *prompt;
+ const char *prompt = NULL;
- char line[32];
-
- /*
- * Verify the key against the registry.
- */
- ret = verify_host_key(host, port, keytype, keystr);
+ stdio_sink errsink[1];
+ stdio_sink_init(errsink, stderr);
- 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);
+ char line[32];
- fprintf(stderr, common_fmt, keytype, fingerprints[fptype_default]);
- if (console_batch_mode) {
- fputs(console_abandoned_msg, stderr);
- return 0;
+ for (SeatDialogTextItem *item = text->items,
+ *end = item+text->nitems; item < end; item++) {
+ switch (item->type) {
+ case SDT_PARA:
+ wordwrap(BinarySink_UPCAST(errsink),
+ ptrlen_from_asciz(item->text), 60);
+ fputc('\n', stderr);
+ break;
+ case SDT_DISPLAY:
+ fprintf(stderr, " %s\n", item->text);
+ break;
+ case SDT_SCARY_HEADING:
+ /* Can't change font size or weight in this context */
+ fprintf(stderr, "%s\n", item->text);
+ break;
+ case SDT_BATCH_ABORT:
+ if (console_batch_mode) {
+ fprintf(stderr, "%s\n", item->text);
+ fflush(stderr);
+ return SPR_SW_ABORT("Cannot confirm a host key in batch mode");
+ }
+ break;
+ case SDT_PROMPT:
+ prompt = item->text;
+ break;
+ default:
+ break;
+ }
}
-
- fputs(intro, stderr);
- fflush(stderr);
+ assert(prompt); /* something in the SeatDialogText should have set this */
while (true) {
- fputs(prompt, stderr);
+ fprintf(stderr,
+ "%s (y/n, Return cancels connection, i for more info) ",
+ prompt);
fflush(stderr);
line[0] = '\0'; /* fail safe if ReadFile returns no data */
@@ -88,13 +93,22 @@ int console_verify_ssh_host_key(
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]);
+ for (SeatDialogTextItem *item = text->items,
+ *end = item+text->nitems; item < end; item++) {
+ switch (item->type) {
+ case SDT_MORE_INFO_KEY:
+ fprintf(stderr, "%s", item->text);
+ break;
+ case SDT_MORE_INFO_VALUE_SHORT:
+ fprintf(stderr, ": %s\n", item->text);
+ break;
+ case SDT_MORE_INFO_VALUE_BLOB:
+ fprintf(stderr, ":\n%s\n", item->text);
+ break;
+ default:
+ break;
+ }
+ }
} else {
break;
}
@@ -106,16 +120,16 @@ int console_verify_ssh_host_key(
line[0] != 'q' && line[0] != 'Q') {
if (line[0] == 'y' || line[0] == 'Y')
store_host_key(host, port, keytype, keystr);
- return 1;
+ return SPR_OK;
} else {
fputs(console_abandoned_msg, stderr);
- return 0;
+ return SPR_USER_ABORT;
}
}
-int console_confirm_weak_crypto_primitive(
+SeatPromptResult console_confirm_weak_crypto_primitive(
Seat *seat, const char *algtype, const char *algname,
- void (*callback)(void *ctx, int result), void *ctx)
+ void (*callback)(void *ctx, SeatPromptResult result), void *ctx)
{
HANDLE hin;
DWORD savemode, i;
@@ -126,7 +140,8 @@ int console_confirm_weak_crypto_primitive(
if (console_batch_mode) {
fputs(console_abandoned_msg, stderr);
- return 0;
+ return SPR_SW_ABORT("Cannot confirm a weak crypto primitive "
+ "in batch mode");
}
fputs(console_continue_prompt, stderr);
@@ -140,16 +155,16 @@ int console_confirm_weak_crypto_primitive(
SetConsoleMode(hin, savemode);
if (line[0] == 'y' || line[0] == 'Y') {
- return 1;
+ return SPR_OK;
} else {
fputs(console_abandoned_msg, stderr);
- return 0;
+ return SPR_USER_ABORT;
}
}
-int console_confirm_weak_cached_hostkey(
+SeatPromptResult console_confirm_weak_cached_hostkey(
Seat *seat, const char *algname, const char *betteralgs,
- void (*callback)(void *ctx, int result), void *ctx)
+ void (*callback)(void *ctx, SeatPromptResult result), void *ctx)
{
HANDLE hin;
DWORD savemode, i;
@@ -160,7 +175,8 @@ int console_confirm_weak_cached_hostkey(
if (console_batch_mode) {
fputs(console_abandoned_msg, stderr);
- return 0;
+ return SPR_SW_ABORT("Cannot confirm a weak cached host key "
+ "in batch mode");
}
fputs(console_continue_prompt, stderr);
@@ -174,10 +190,10 @@ int console_confirm_weak_cached_hostkey(
SetConsoleMode(hin, savemode);
if (line[0] == 'y' || line[0] == 'Y') {
- return 1;
+ return SPR_OK;
} else {
fputs(console_abandoned_msg, stderr);
- return 0;
+ return SPR_USER_ABORT;
}
}
@@ -187,21 +203,22 @@ bool is_interactive(void)
}
bool console_antispoof_prompt = true;
-bool console_set_trust_status(Seat *seat, bool trusted)
+
+void console_set_trust_status(Seat *seat, bool trusted)
{
- if (console_batch_mode || !is_interactive() || !console_antispoof_prompt) {
+ /* Do nothing in response to a change of trust status, because
+ * there's nothing we can do in a console environment. However,
+ * the query function below will make a fiddly decision about
+ * whether to tell the backend to enable fallback handling. */
+}
+
+bool console_can_set_trust_status(Seat *seat)
+{
+ if (console_batch_mode) {
/*
* 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;
}
@@ -209,6 +226,23 @@ bool console_set_trust_status(Seat *seat, bool trusted)
return false;
}
+bool console_has_mixed_input_stream(Seat *seat)
+{
+ if (!is_interactive() || !console_antispoof_prompt) {
+ /*
+ * If standard input isn't connected to a terminal, then even
+ * if the server did send a spoof authentication prompt, the
+ * user couldn't respond to it via the terminal anyway.
+ *
+ * We also pretend this is true if the user has purposely
+ * disabled the antispoof prompt.
+ */
+ return false;
+ }
+
+ return true;
+}
+
/*
* 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).
@@ -329,7 +363,7 @@ static void console_write(HANDLE hout, ptrlen data)
WriteFile(hout, data.ptr, data.len, &dummy, NULL);
}
-int console_get_userpass_input(prompts_t *p)
+SeatPromptResult console_get_userpass_input(prompts_t *p)
{
HANDLE hin = INVALID_HANDLE_VALUE, hout = INVALID_HANDLE_VALUE;
size_t curr_prompt;
@@ -351,7 +385,8 @@ int console_get_userpass_input(prompts_t *p)
*/
if (p->n_prompts) {
if (console_batch_mode)
- return 0;
+ return SPR_SW_ABORT("Cannot answer interactive prompts "
+ "in batch mode");
hin = GetStdHandle(STD_INPUT_HANDLE);
if (hin == INVALID_HANDLE_VALUE) {
fprintf(stderr, "Cannot get standard input handle\n");
@@ -404,6 +439,7 @@ int console_get_userpass_input(prompts_t *p)
console_write(hout, ptrlen_from_asciz(pr->prompt));
bool failed = false;
+ SeatPromptResult spr;
while (1) {
/*
* Amount of data to try to read from the console in one
@@ -426,8 +462,17 @@ int console_get_userpass_input(prompts_t *p)
void *ptr = strbuf_append(pr->result, toread);
DWORD ret = 0;
- if (!ReadFile(hin, ptr, toread, &ret, NULL) || ret == 0) {
+ if (!ReadFile(hin, ptr, toread, &ret, NULL)) {
+ /* An OS error when reading from the console is treated as an
+ * unexpected error and reported to the user. */
failed = true;
+ spr = make_spr_sw_abort_winerror(
+ "Error reading from console", GetLastError());
+ break;
+ } else if (ret == 0) {
+ /* Regard EOF on the terminal as a deliberate user-abort */
+ failed = true;
+ spr = SPR_USER_ABORT;
break;
}
@@ -443,10 +488,9 @@ int console_get_userpass_input(prompts_t *p)
if (!pr->echo)
console_write(hout, PTRLEN_LITERAL("\r\n"));
- if (failed) {
- return 0; /* failure due to read error */
- }
+ if (failed)
+ return spr;
}
- return 1; /* success */
+ return SPR_OK;
}
diff --git a/windows/winctrls.c b/windows/controls.c
index 59129eab..ce3638e4 100644
--- a/windows/winctrls.c
+++ b/windows/controls.c
@@ -1,5 +1,5 @@
/*
- * winctrls.c: routines to self-manage the controls in a dialog
+ * controls.c: routines to self-manage the controls in a dialog
* box.
*/
@@ -71,8 +71,8 @@ void ctlposinit(struct ctlpos *cp, HWND hwnd,
cp->width -= leftborder + rightborder;
}
-HWND doctl(struct ctlpos *cp, RECT r,
- char *wclass, int wstyle, int exstyle, char *wtext, int wid)
+HWND doctl(struct ctlpos *cp, RECT r, const char *wclass, int wstyle,
+ int exstyle, const char *wtext, int wid)
{
HWND ctl;
/*
@@ -115,7 +115,7 @@ HWND doctl(struct ctlpos *cp, RECT r,
/*
* A title bar across the top of a sub-dialog.
*/
-void bartitle(struct ctlpos *cp, char *name, int id)
+void bartitle(struct ctlpos *cp, const char *name, int id)
{
RECT r;
@@ -130,7 +130,7 @@ void bartitle(struct ctlpos *cp, char *name, int id)
/*
* Begin a grouping box, with or without a group title.
*/
-void beginbox(struct ctlpos *cp, char *name, int idbox)
+void beginbox(struct ctlpos *cp, const char *name, int idbox)
{
cp->boxystart = cp->ypos;
if (!name)
@@ -165,8 +165,8 @@ void endbox(struct ctlpos *cp)
/*
* A static line, followed by a full-width edit box.
*/
-void editboxfw(struct ctlpos *cp, bool password, char *text,
- int staticid, int editid)
+void editboxfw(struct ctlpos *cp, bool password, bool readonly,
+ const char *text, int staticid, int editid)
{
RECT r;
@@ -183,7 +183,8 @@ void editboxfw(struct ctlpos *cp, bool password, char *text,
r.bottom = EDITHEIGHT;
doctl(cp, r, "EDIT",
WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL |
- (password ? ES_PASSWORD : 0),
+ (password ? ES_PASSWORD : 0) |
+ (readonly ? ES_READONLY : 0),
WS_EX_CLIENTEDGE, "", editid);
cp->ypos += EDITHEIGHT + GAPBETWEEN;
}
@@ -191,7 +192,7 @@ void editboxfw(struct ctlpos *cp, bool password, char *text,
/*
* A static line, followed by a full-width combo box.
*/
-void combobox(struct ctlpos *cp, char *text, int staticid, int listid)
+void combobox(struct ctlpos *cp, const char *text, int staticid, int listid)
{
RECT r;
@@ -212,9 +213,9 @@ void combobox(struct ctlpos *cp, char *text, int staticid, int listid)
cp->ypos += COMBOHEIGHT + GAPBETWEEN;
}
-struct radio { char *text; int id; };
+struct radio { const char *text; int id; };
-static void radioline_common(struct ctlpos *cp, char *text, int id,
+static void radioline_common(struct ctlpos *cp, const char *text, int id,
int nacross, struct radio *buttons, int nbuttons)
{
RECT r;
@@ -236,7 +237,7 @@ static void radioline_common(struct ctlpos *cp, char *text, int id,
group = WS_GROUP;
i = 0;
for (j = 0; j < nbuttons; j++) {
- char *btext = buttons[j].text;
+ const char *btext = buttons[j].text;
int bid = buttons[j].id;
if (i == nacross) {
@@ -274,7 +275,7 @@ static void radioline_common(struct ctlpos *cp, char *text, int id,
*
* (*) Button1 (*) Button2 (*) ButtonWithReallyLongTitle
*/
-void radioline(struct ctlpos *cp, char *text, int id, int nacross, ...)
+void radioline(struct ctlpos *cp, const char *text, int id, int nacross, ...)
{
va_list ap;
struct radio *buttons;
@@ -283,7 +284,7 @@ void radioline(struct ctlpos *cp, char *text, int id, int nacross, ...)
va_start(ap, nacross);
nbuttons = 0;
while (1) {
- char *btext = va_arg(ap, char *);
+ const char *btext = va_arg(ap, const char *);
if (!btext)
break;
(void) va_arg(ap, int); /* id */
@@ -293,7 +294,7 @@ void radioline(struct ctlpos *cp, char *text, int id, int nacross, ...)
buttons = snewn(nbuttons, struct radio);
va_start(ap, nacross);
for (i = 0; i < nbuttons; i++) {
- buttons[i].text = va_arg(ap, char *);
+ buttons[i].text = va_arg(ap, const char *);
buttons[i].id = va_arg(ap, int);
}
va_end(ap);
@@ -314,7 +315,7 @@ void bareradioline(struct ctlpos *cp, int nacross, ...)
va_start(ap, nacross);
nbuttons = 0;
while (1) {
- char *btext = va_arg(ap, char *);
+ const char *btext = va_arg(ap, const char *);
if (!btext)
break;
(void) va_arg(ap, int); /* id */
@@ -324,7 +325,7 @@ void bareradioline(struct ctlpos *cp, int nacross, ...)
buttons = snewn(nbuttons, struct radio);
va_start(ap, nacross);
for (i = 0; i < nbuttons; i++) {
- buttons[i].text = va_arg(ap, char *);
+ buttons[i].text = va_arg(ap, const char *);
buttons[i].id = va_arg(ap, int);
}
va_end(ap);
@@ -336,7 +337,7 @@ void bareradioline(struct ctlpos *cp, int nacross, ...)
* A set of radio buttons on multiple lines, with a static above
* them.
*/
-void radiobig(struct ctlpos *cp, char *text, int id, ...)
+void radiobig(struct ctlpos *cp, const char *text, int id, ...)
{
va_list ap;
struct radio *buttons;
@@ -345,7 +346,7 @@ void radiobig(struct ctlpos *cp, char *text, int id, ...)
va_start(ap, id);
nbuttons = 0;
while (1) {
- char *btext = va_arg(ap, char *);
+ const char *btext = va_arg(ap, const char *);
if (!btext)
break;
(void) va_arg(ap, int); /* id */
@@ -355,7 +356,7 @@ void radiobig(struct ctlpos *cp, char *text, int id, ...)
buttons = snewn(nbuttons, struct radio);
va_start(ap, id);
for (i = 0; i < nbuttons; i++) {
- buttons[i].text = va_arg(ap, char *);
+ buttons[i].text = va_arg(ap, const char *);
buttons[i].id = va_arg(ap, int);
}
va_end(ap);
@@ -366,7 +367,7 @@ void radiobig(struct ctlpos *cp, char *text, int id, ...)
/*
* A single standalone checkbox.
*/
-void checkbox(struct ctlpos *cp, char *text, int id)
+void checkbox(struct ctlpos *cp, const char *text, int id)
{
RECT r;
@@ -385,13 +386,14 @@ void checkbox(struct ctlpos *cp, char *text, int id)
* 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)
+char *staticwrap(struct ctlpos *cp, HWND hwnd, const char *text, int *lines)
{
HDC hdc = GetDC(hwnd);
int width, nlines, j;
INT *pwidths, nfit;
SIZE size;
- char *ret, *p, *q;
+ const char *p;
+ char *ret, *q;
RECT r;
HFONT oldfont, newfont;
@@ -471,7 +473,7 @@ char *staticwrap(struct ctlpos *cp, HWND hwnd, char *text, int *lines)
/*
* A single standalone static text control.
*/
-void statictext(struct ctlpos *cp, char *text, int lines, int id)
+void statictext(struct ctlpos *cp, const char *text, int lines, int id)
{
RECT r;
@@ -504,8 +506,8 @@ void paneltitle(struct ctlpos *cp, int 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)
+void staticbtn(struct ctlpos *cp, const char *stext, int sid,
+ const char *btext, int bid)
{
const int height = (PUSHBTNHEIGHT > STATICHEIGHT ?
PUSHBTNHEIGHT : STATICHEIGHT);
@@ -536,7 +538,7 @@ void staticbtn(struct ctlpos *cp, char *stext, int sid,
/*
* A simple push button.
*/
-void button(struct ctlpos *cp, char *btext, int bid, bool defbtn)
+void button(struct ctlpos *cp, const char *btext, int bid, bool defbtn)
{
RECT r;
@@ -561,8 +563,8 @@ void button(struct ctlpos *cp, char *btext, int bid, bool defbtn)
/*
* Like staticbtn, but two buttons.
*/
-void static2btn(struct ctlpos *cp, char *stext, int sid,
- char *btext1, int bid1, char *btext2, int bid2)
+void static2btn(struct ctlpos *cp, const char *stext, int sid,
+ const char *btext1, int bid1, const char *btext2, int bid2)
{
const int height = (PUSHBTNHEIGHT > STATICHEIGHT ?
PUSHBTNHEIGHT : STATICHEIGHT);
@@ -603,7 +605,7 @@ void static2btn(struct ctlpos *cp, char *stext, int sid,
/*
* An edit control on the right hand side, with a static to its left.
*/
-static void staticedit_internal(struct ctlpos *cp, char *stext,
+static void staticedit_internal(struct ctlpos *cp, const char *stext,
int sid, int eid, int percentedit,
int style)
{
@@ -634,13 +636,13 @@ static void staticedit_internal(struct ctlpos *cp, char *stext,
cp->ypos += height + GAPBETWEEN;
}
-void staticedit(struct ctlpos *cp, char *stext,
+void staticedit(struct ctlpos *cp, const char *stext,
int sid, int eid, int percentedit)
{
staticedit_internal(cp, stext, sid, eid, percentedit, 0);
}
-void staticpassedit(struct ctlpos *cp, char *stext,
+void staticpassedit(struct ctlpos *cp, const char *stext,
int sid, int eid, int percentedit)
{
staticedit_internal(cp, stext, sid, eid, percentedit, ES_PASSWORD);
@@ -650,7 +652,7 @@ void staticpassedit(struct ctlpos *cp, char *stext,
* A drop-down list box on the right hand side, with a static to
* its left.
*/
-void staticddl(struct ctlpos *cp, char *stext,
+void staticddl(struct ctlpos *cp, const char *stext,
int sid, int lid, int percentlist)
{
const int height = (COMBOHEIGHT > STATICHEIGHT ?
@@ -683,7 +685,7 @@ void staticddl(struct ctlpos *cp, char *stext,
/*
* A combo box on the right hand side, with a static to its left.
*/
-void staticcombo(struct ctlpos *cp, char *stext,
+void staticcombo(struct ctlpos *cp, const char *stext,
int sid, int lid, int percentlist)
{
const int height = (COMBOHEIGHT > STATICHEIGHT ?
@@ -716,7 +718,7 @@ void staticcombo(struct ctlpos *cp, char *stext,
/*
* A static, with a full-width drop-down list box below it.
*/
-void staticddlbig(struct ctlpos *cp, char *stext,
+void staticddlbig(struct ctlpos *cp, const char *stext,
int sid, int lid)
{
RECT r;
@@ -743,7 +745,7 @@ void staticddlbig(struct ctlpos *cp, char *stext,
/*
* A big multiline edit control with a static labelling it.
*/
-void bigeditctrl(struct ctlpos *cp, char *stext,
+void bigeditctrl(struct ctlpos *cp, const char *stext,
int sid, int eid, int lines)
{
RECT r;
@@ -770,7 +772,7 @@ void bigeditctrl(struct ctlpos *cp, char *stext,
/*
* A list box with a static labelling it.
*/
-void listbox(struct ctlpos *cp, char *stext,
+void listbox(struct ctlpos *cp, const char *stext,
int sid, int lid, int lines, bool multi)
{
RECT r;
@@ -799,7 +801,8 @@ void listbox(struct ctlpos *cp, char *stext,
/*
* A tab-control substitute when a real tab control is unavailable.
*/
-void ersatztab(struct ctlpos *cp, char *stext, int sid, int lid, int s2id)
+void ersatztab(struct ctlpos *cp, const char *stext, int sid, int lid,
+ int s2id)
{
const int height = (COMBOHEIGHT > STATICHEIGHT ?
COMBOHEIGHT : STATICHEIGHT);
@@ -842,8 +845,8 @@ void ersatztab(struct ctlpos *cp, char *stext, int sid, int lid, int 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)
+void editbutton(struct ctlpos *cp, const char *stext, int sid,
+ int eid, const char *btext, int bid)
{
const int height = (EDITHEIGHT > PUSHBTNHEIGHT ?
EDITHEIGHT : PUSHBTNHEIGHT);
@@ -886,7 +889,7 @@ void editbutton(struct ctlpos *cp, char *stext, int sid,
* 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 char *stext, int sid, int listid, int upbid, int dnbid)
{
const static int percents[] = { 5, 75, 20 };
RECT r;
@@ -1250,7 +1253,7 @@ static int winctrl_cmp_byid(void *av, void *bv)
}
static int winctrl_cmp_byctrl_find(void *av, void *bv)
{
- union control *a = (union control *)av;
+ dlgcontrol *a = (dlgcontrol *)av;
struct winctrl *b = (struct winctrl *)bv;
if (a < b->ctrl)
return -1;
@@ -1310,7 +1313,7 @@ void winctrl_remove(struct winctrls *wc, struct winctrl *c)
assert(ret == c);
}
-struct winctrl *winctrl_findbyctrl(struct winctrls *wc, union control *ctrl)
+struct winctrl *winctrl_findbyctrl(struct winctrls *wc, dlgcontrol *ctrl)
{
return find234(wc->byctrl, ctrl, winctrl_cmp_byctrl_find);
}
@@ -1354,7 +1357,7 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,
int ncols, colstart, colspan;
struct ctlpos tabdelays[16];
- union control *tabdelayed[16];
+ dlgcontrol *tabdelayed[16];
int ntabdelays;
struct ctlpos pos;
@@ -1367,6 +1370,8 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,
base_id = *id;
+ ctrlset_normalise_aligns(s);
+
/* Start a containing box, if we have a boxname. */
if (s->boxname && *s->boxname) {
struct winctrl *c = snew(struct winctrl);
@@ -1402,7 +1407,7 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,
/* Loop over each control in the controlset. */
for (i = 0; i < s->ncontrols; i++) {
- union control *ctrl = s->ctrls[i];
+ dlgcontrol *ctrl = s->ctrls[i];
/*
* Generic processing that pertains to all control types.
@@ -1415,7 +1420,7 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,
* CTRL_COLUMNS and doesn't require any control creation at
* all.
*/
- if (ctrl->generic.type == CTRL_COLUMNS) {
+ if (ctrl->type == CTRL_COLUMNS) {
assert((ctrl->columns.ncols == 1) ^ (ncols == 1));
if (ncols == 1) {
@@ -1455,10 +1460,10 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,
}
continue;
- } else if (ctrl->generic.type == CTRL_TABDELAY) {
+ } else if (ctrl->type == CTRL_TABDELAY) {
int i;
- assert(!ctrl->generic.tabdelay);
+ assert(!ctrl->delay_taborder);
ctrl = ctrl->tabdelay.ctrl;
for (i = 0; i < ntabdelays; i++)
@@ -1478,8 +1483,8 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,
*/
int col;
- colstart = COLUMN_START(ctrl->generic.column);
- colspan = COLUMN_SPAN(ctrl->generic.column);
+ colstart = COLUMN_START(ctrl->column);
+ colspan = COLUMN_SPAN(ctrl->column);
pos = columns[colstart]; /* structure copy */
pos.width = columns[colstart+colspan-1].width +
@@ -1494,7 +1499,7 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,
* tabdelay list, and unset pos.hwnd to inhibit actual
* control creation.
*/
- if (ctrl->generic.tabdelay) {
+ if (ctrl->delay_taborder) {
assert(ntabdelays < lenof(tabdelays));
tabdelays[ntabdelays] = pos; /* structure copy */
tabdelayed[ntabdelays] = ctrl;
@@ -1522,22 +1527,28 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,
* 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);
+ switch (ctrl->type) {
+ case CTRL_TEXT:
+ if (ctrl->text.wrap) {
+ char *wrapped, *escaped;
+ int lines;
+ num_ids = 1;
+ wrapped = staticwrap(&pos, cp->hwnd,
+ ctrl->label, &lines);
+ escaped = shortcut_escape(wrapped, NO_SHORTCUT);
+ statictext(&pos, escaped, lines, base_id);
+ sfree(escaped);
+ sfree(wrapped);
+ } else {
+ num_ids = 1;
+ editboxfw(&pos, false, true, NULL, 0, base_id);
+ SetDlgItemText(pos.hwnd, base_id, ctrl->label);
+ MakeDlgItemBorderless(pos.hwnd, base_id);
+ }
break;
- }
case CTRL_EDITBOX:
num_ids = 2; /* static, edit */
- escaped = shortcut_escape(ctrl->editbox.label,
+ escaped = shortcut_escape(ctrl->label,
ctrl->editbox.shortcut);
shortcuts[nshortcuts++] = ctrl->editbox.shortcut;
if (ctrl->editbox.percentwidth == 100) {
@@ -1545,7 +1556,7 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,
combobox(&pos, escaped,
base_id, base_id+1);
else
- editboxfw(&pos, ctrl->editbox.password, escaped,
+ editboxfw(&pos, ctrl->editbox.password, false, escaped,
base_id, base_id+1);
} else {
if (ctrl->editbox.has_list) {
@@ -1564,23 +1575,23 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,
struct radio *buttons;
int i;
- escaped = shortcut_escape(ctrl->radio.label,
+ escaped = shortcut_escape(ctrl->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];
- }
+ 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,
@@ -1588,7 +1599,7 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,
buttons, ctrl->radio.nbuttons);
for (i = 0; i < ctrl->radio.nbuttons; i++) {
- sfree(buttons[i].text);
+ sfree((char *)buttons[i].text);
}
sfree(buttons);
sfree(escaped);
@@ -1596,14 +1607,14 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,
}
case CTRL_CHECKBOX:
num_ids = 1;
- escaped = shortcut_escape(ctrl->checkbox.label,
+ escaped = shortcut_escape(ctrl->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,
+ escaped = shortcut_escape(ctrl->label,
ctrl->button.shortcut);
shortcuts[nshortcuts++] = ctrl->button.shortcut;
if (ctrl->button.iscancel)
@@ -1614,7 +1625,7 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,
break;
case CTRL_LISTBOX:
num_ids = 2;
- escaped = shortcut_escape(ctrl->listbox.label,
+ escaped = shortcut_escape(ctrl->label,
ctrl->listbox.shortcut);
shortcuts[nshortcuts++] = ctrl->listbox.shortcut;
if (ctrl->listbox.draglist) {
@@ -1663,18 +1674,20 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,
sfree(escaped);
break;
case CTRL_FILESELECT:
- num_ids = 3;
- escaped = shortcut_escape(ctrl->fileselect.label,
- ctrl->fileselect.shortcut);
+ escaped = shortcut_escape(ctrl->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';
+ num_ids = 3;
+ if (!ctrl->fileselect.just_button) {
+ editbutton(&pos, escaped, base_id, base_id+1,
+ "Browse...", base_id+2);
+ } else {
+ button(&pos, escaped, base_id+2, false);
+ }
sfree(escaped);
break;
case CTRL_FONTSELECT:
num_ids = 3;
- escaped = shortcut_escape(ctrl->fontselect.label,
+ escaped = shortcut_escape(ctrl->label,
ctrl->fontselect.shortcut);
shortcuts[nshortcuts++] = ctrl->fontselect.shortcut;
statictext(&pos, escaped, 1, base_id);
@@ -1709,28 +1722,43 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,
if (actual_base_id == base_id)
base_id += num_ids;
- if (ctrl->generic.align_next_to) {
+ if (ctrl->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);
+ LONG mid2 = 0;
+ for (dlgcontrol *thisctrl = ctrl; thisctrl;
+ thisctrl = thisctrl->align_next_to) {
+ struct winctrl *thisc = winctrl_findbyctrl(wc, thisctrl);
+ assert(thisc);
+
+ HWND win = GetDlgItem(pos.hwnd, thisc->align_id);
+ assert(win);
+
+ RECT rect;
+ if (!GetWindowRect(win, &rect))
+ continue;
+ if (mid2 < rect.top + rect.bottom)
+ mid2 = rect.top + rect.bottom;
+ }
+
+ for (dlgcontrol *thisctrl = ctrl; thisctrl;
+ thisctrl = thisctrl->align_next_to) {
+ struct winctrl *thisc = winctrl_findbyctrl(wc, thisctrl);
+ assert(thisc);
+
+ HWND win = GetDlgItem(pos.hwnd, thisc->align_id);
+ assert(win);
+
+ RECT rect;
+ if (!GetWindowRect(win, &rect))
+ continue;
+
+ LONG dy = (mid2 - (rect.top + rect.bottom)) / 2;
+ move_windows(pos.hwnd, thisc->base_id, thisc->num_ids, dy);
}
}
} else {
@@ -1762,7 +1790,7 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,
endbox(cp);
}
-static void winctrl_set_focus(union control *ctrl, struct dlgparam *dp,
+static void winctrl_set_focus(dlgcontrol *ctrl, struct dlgparam *dp,
bool has_focus)
{
if (has_focus) {
@@ -1775,7 +1803,7 @@ static void winctrl_set_focus(union control *ctrl, struct dlgparam *dp,
}
}
-union control *dlg_last_focused(union control *ctrl, dlgparam *dp)
+dlgcontrol *dlg_last_focused(dlgcontrol *ctrl, dlgparam *dp)
{
return dp->focused == ctrl ? dp->lastfocused : dp->focused;
}
@@ -1788,7 +1816,7 @@ bool winctrl_handle_command(struct dlgparam *dp, UINT msg,
WPARAM wParam, LPARAM lParam)
{
struct winctrl *c;
- union control *ctrl;
+ dlgcontrol *ctrl;
int i, id;
bool ret;
static UINT draglistmsg = WM_NULL;
@@ -1827,7 +1855,7 @@ bool winctrl_handle_command(struct dlgparam *dp, UINT msg,
SetMapMode(hdc, MM_TEXT); /* ensure logical units == pixels */
GetTextExtentPoint32(hdc, (char *)c->data,
- strlen((char *)c->data), &s);
+ 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,
@@ -1840,7 +1868,7 @@ bool winctrl_handle_command(struct dlgparam *dp, UINT msg,
ctrl = c->ctrl;
id = LOWORD(wParam) - c->base_id;
- if (!ctrl || !ctrl->generic.handler)
+ if (!ctrl || !ctrl->handler)
return false; /* nothing we can do here */
/*
@@ -1856,7 +1884,7 @@ bool winctrl_handle_command(struct dlgparam *dp, UINT msg,
/*
* Now switch on the control type and the message.
*/
- switch (ctrl->generic.type) {
+ switch (ctrl->type) {
case CTRL_EDITBOX:
if (msg == WM_COMMAND && !ctrl->editbox.has_list &&
(HIWORD(wParam) == EN_SETFOCUS || HIWORD(wParam) == EN_KILLFOCUS))
@@ -1867,7 +1895,7 @@ bool winctrl_handle_command(struct dlgparam *dp, UINT msg,
if (msg == WM_COMMAND && !ctrl->editbox.has_list &&
HIWORD(wParam) == EN_CHANGE)
- ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE);
+ ctrl->handler(ctrl, dp, dp->data, EVENT_VALCHANGE);
if (msg == WM_COMMAND &&
ctrl->editbox.has_list) {
if (HIWORD(wParam) == CBN_SELCHANGE) {
@@ -1883,11 +1911,11 @@ bool winctrl_handle_command(struct dlgparam *dp, UINT msg,
index, (LPARAM)text);
SetDlgItemText(dp->hwnd, c->base_id+1, text);
sfree(text);
- ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE);
+ ctrl->handler(ctrl, dp, dp->data, EVENT_VALCHANGE);
} else if (HIWORD(wParam) == CBN_EDITCHANGE) {
- ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE);
+ ctrl->handler(ctrl, dp, dp->data, EVENT_VALCHANGE);
} else if (HIWORD(wParam) == CBN_KILLFOCUS) {
- ctrl->generic.handler(ctrl, dp, dp->data, EVENT_REFRESH);
+ ctrl->handler(ctrl, dp, dp->data, EVENT_REFRESH);
}
}
@@ -1907,7 +1935,7 @@ bool winctrl_handle_command(struct dlgparam *dp, UINT msg,
(HIWORD(wParam) == BN_CLICKED ||
HIWORD(wParam) == BN_DOUBLECLICKED) &&
IsDlgButtonChecked(dp->hwnd, LOWORD(wParam))) {
- ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE);
+ ctrl->handler(ctrl, dp, dp->data, EVENT_VALCHANGE);
}
break;
case CTRL_CHECKBOX:
@@ -1917,7 +1945,7 @@ bool winctrl_handle_command(struct dlgparam *dp, UINT msg,
if (msg == WM_COMMAND &&
(HIWORD(wParam) == BN_CLICKED ||
HIWORD(wParam) == BN_DOUBLECLICKED)) {
- ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE);
+ ctrl->handler(ctrl, dp, dp->data, EVENT_VALCHANGE);
}
break;
case CTRL_BUTTON:
@@ -1927,7 +1955,7 @@ bool winctrl_handle_command(struct dlgparam *dp, UINT msg,
if (msg == WM_COMMAND &&
(HIWORD(wParam) == BN_CLICKED ||
HIWORD(wParam) == BN_DOUBLECLICKED)) {
- ctrl->generic.handler(ctrl, dp, dp->data, EVENT_ACTION);
+ ctrl->handler(ctrl, dp, dp->data, EVENT_ACTION);
}
break;
case CTRL_LISTBOX:
@@ -1945,14 +1973,14 @@ bool winctrl_handle_command(struct dlgparam *dp, UINT msg,
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);
+ ctrl->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);
+ ctrl->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);
+ ctrl->handler(ctrl, dp, dp->data, EVENT_SELCHANGE);
}
}
break;
@@ -1964,7 +1992,7 @@ bool winctrl_handle_command(struct dlgparam *dp, UINT msg,
(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);
+ ctrl->handler(ctrl, dp, dp->data, EVENT_VALCHANGE);
if (id == 2 &&
(msg == WM_COMMAND &&
(HIWORD(wParam) == BN_CLICKED ||
@@ -1981,15 +2009,27 @@ bool winctrl_handle_command(struct dlgparam *dp, UINT msg,
of.lpstrCustomFilter = NULL;
of.nFilterIndex = 1;
of.lpstrFile = filename;
- GetDlgItemText(dp->hwnd, c->base_id+1, filename, lenof(filename));
- filename[lenof(filename)-1] = '\0';
+ if (!ctrl->fileselect.just_button) {
+ GetDlgItemText(dp->hwnd, c->base_id+1,
+ filename, lenof(filename));
+ filename[lenof(filename)-1] = '\0';
+ } else {
+ *filename = '\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);
+ if (!ctrl->fileselect.just_button) {
+ SetDlgItemText(dp->hwnd, c->base_id + 1, filename);
+ ctrl->handler(ctrl, dp, dp->data, EVENT_VALCHANGE);
+ } else {
+ assert(!c->data);
+ c->data = filename;
+ ctrl->handler(ctrl, dp, dp->data, EVENT_ACTION);
+ c->data = NULL;
+ }
}
}
break;
@@ -2034,7 +2074,7 @@ bool winctrl_handle_command(struct dlgparam *dp, UINT msg,
dlg_fontsel_set(ctrl, dp, fs);
fontspec_free(fs);
- ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE);
+ ctrl->handler(ctrl, dp, dp->data, EVENT_VALCHANGE);
}
}
break;
@@ -2065,7 +2105,7 @@ bool winctrl_handle_command(struct dlgparam *dp, UINT msg,
dp->coloursel_result.ok = true;
} else
dp->coloursel_result.ok = false;
- ctrl->generic.handler(ctrl, dp, dp->data, EVENT_CALLBACK);
+ ctrl->handler(ctrl, dp, dp->data, EVENT_CALLBACK);
}
return ret;
@@ -2094,12 +2134,12 @@ bool winctrl_context_help(struct dlgparam *dp, HWND hwnd, int id)
/*
* This is the Windows front end, so we're allowed to assume
- * `helpctx.p' is a context string.
+ * `helpctx' is a context string.
*/
- if (!c->ctrl || !c->ctrl->generic.helpctx.p)
+ if (!c->ctrl || !c->ctrl->helpctx)
return false; /* no help available for this ctrl */
- launch_help(hwnd, c->ctrl->generic.helpctx.p);
+ launch_help(hwnd, c->ctrl->helpctx);
return true;
}
@@ -2108,7 +2148,7 @@ bool winctrl_context_help(struct dlgparam *dp, HWND hwnd, int id)
* mechanism can call to access the dialog box entries.
*/
-static struct winctrl *dlg_findbyctrl(struct dlgparam *dp, union control *ctrl)
+static struct winctrl *dlg_findbyctrl(struct dlgparam *dp, dlgcontrol *ctrl)
{
int i;
@@ -2120,7 +2160,7 @@ static struct winctrl *dlg_findbyctrl(struct dlgparam *dp, union control *ctrl)
return NULL;
}
-bool dlg_is_visible(union control *ctrl, dlgparam *dp)
+bool dlg_is_visible(dlgcontrol *ctrl, dlgparam *dp)
{
/*
* In this implementation of the dialog box, we physically
@@ -2131,91 +2171,99 @@ bool dlg_is_visible(union control *ctrl, dlgparam *dp)
return dlg_findbyctrl(dp, ctrl) != NULL;
}
-void dlg_radiobutton_set(union control *ctrl, dlgparam *dp, int whichbutton)
+void dlg_radiobutton_set(dlgcontrol *ctrl, dlgparam *dp, int whichbutton)
{
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
- assert(c && c->ctrl->generic.type == CTRL_RADIO);
+ assert(c && c->ctrl->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)
+int dlg_radiobutton_get(dlgcontrol *ctrl, dlgparam *dp)
{
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
int i;
- assert(c && c->ctrl->generic.type == CTRL_RADIO);
+ assert(c && c->ctrl->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)
+void dlg_checkbox_set(dlgcontrol *ctrl, dlgparam *dp, bool checked)
{
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
- assert(c && c->ctrl->generic.type == CTRL_CHECKBOX);
+ assert(c && c->ctrl->type == CTRL_CHECKBOX);
CheckDlgButton(dp->hwnd, c->base_id, checked);
}
-bool dlg_checkbox_get(union control *ctrl, dlgparam *dp)
+bool dlg_checkbox_get(dlgcontrol *ctrl, dlgparam *dp)
{
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
- assert(c && c->ctrl->generic.type == CTRL_CHECKBOX);
+ assert(c && c->ctrl->type == CTRL_CHECKBOX);
return 0 != IsDlgButtonChecked(dp->hwnd, c->base_id);
}
-void dlg_editbox_set(union control *ctrl, dlgparam *dp, char const *text)
+void dlg_editbox_set(dlgcontrol *ctrl, dlgparam *dp, char const *text)
{
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
- assert(c && c->ctrl->generic.type == CTRL_EDITBOX);
+ assert(c && c->ctrl->type == CTRL_EDITBOX);
SetDlgItemText(dp->hwnd, c->base_id+1, text);
}
-char *dlg_editbox_get(union control *ctrl, dlgparam *dp)
+char *dlg_editbox_get(dlgcontrol *ctrl, dlgparam *dp)
{
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
- assert(c && c->ctrl->generic.type == CTRL_EDITBOX);
+ assert(c && c->ctrl->type == CTRL_EDITBOX);
return GetDlgItemText_alloc(dp->hwnd, c->base_id+1);
}
+void dlg_editbox_select_range(dlgcontrol *ctrl, dlgparam *dp,
+ size_t start, size_t len)
+{
+ struct winctrl *c = dlg_findbyctrl(dp, ctrl);
+ assert(c && c->ctrl->type == CTRL_EDITBOX);
+ SendDlgItemMessage(dp->hwnd, c->base_id+1, EM_SETSEL, start, start+len);
+}
+
/* The `listbox' functions can also apply to combo boxes. */
-void dlg_listbox_clear(union control *ctrl, dlgparam *dp)
+void dlg_listbox_clear(dlgcontrol *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->type == CTRL_LISTBOX ||
+ (c->ctrl->type == CTRL_EDITBOX &&
c->ctrl->editbox.has_list)));
- msg = (c->ctrl->generic.type==CTRL_LISTBOX && c->ctrl->listbox.height!=0 ?
+ msg = (c->ctrl->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)
+void dlg_listbox_del(dlgcontrol *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->type == CTRL_LISTBOX ||
+ (c->ctrl->type == CTRL_EDITBOX &&
c->ctrl->editbox.has_list)));
- msg = (c->ctrl->generic.type==CTRL_LISTBOX && c->ctrl->listbox.height!=0 ?
+ msg = (c->ctrl->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)
+void dlg_listbox_add(dlgcontrol *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->type == CTRL_LISTBOX ||
+ (c->ctrl->type == CTRL_EDITBOX &&
c->ctrl->editbox.has_list)));
- msg = (c->ctrl->generic.type==CTRL_LISTBOX && c->ctrl->listbox.height!=0 ?
+ msg = (c->ctrl->type==CTRL_LISTBOX && c->ctrl->listbox.height!=0 ?
LB_ADDSTRING : CB_ADDSTRING);
SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, 0, (LPARAM)text);
}
@@ -2227,39 +2275,39 @@ void dlg_listbox_add(union control *ctrl, dlgparam *dp, char const *text)
* 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,
+void dlg_listbox_addwithid(dlgcontrol *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->type == CTRL_LISTBOX ||
+ (c->ctrl->type == CTRL_EDITBOX &&
c->ctrl->editbox.has_list)));
- msg = (c->ctrl->generic.type==CTRL_LISTBOX && c->ctrl->listbox.height!=0 ?
+ msg = (c->ctrl->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);
+ msg2 = (c->ctrl->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)
+int dlg_listbox_getid(dlgcontrol *ctrl, dlgparam *dp, int index)
{
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
int msg;
- assert(c && c->ctrl->generic.type == CTRL_LISTBOX);
+ assert(c && c->ctrl->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)
+int dlg_listbox_index(dlgcontrol *ctrl, dlgparam *dp)
{
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
int msg, ret;
- assert(c && c->ctrl->generic.type == CTRL_LISTBOX);
+ assert(c && c->ctrl->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);
@@ -2274,41 +2322,41 @@ int dlg_listbox_index(union control *ctrl, dlgparam *dp)
return ret;
}
-bool dlg_listbox_issel(union control *ctrl, dlgparam *dp, int index)
+bool dlg_listbox_issel(dlgcontrol *ctrl, dlgparam *dp, int index)
{
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
- assert(c && c->ctrl->generic.type == CTRL_LISTBOX &&
+ assert(c && c->ctrl->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)
+void dlg_listbox_select(dlgcontrol *ctrl, dlgparam *dp, int index)
{
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
int msg;
- assert(c && c->ctrl->generic.type == CTRL_LISTBOX &&
+ assert(c && c->ctrl->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)
+void dlg_text_set(dlgcontrol *ctrl, dlgparam *dp, char const *text)
{
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
- assert(c && c->ctrl->generic.type == CTRL_TEXT);
+ assert(c && c->ctrl->type == CTRL_TEXT);
SetDlgItemText(dp->hwnd, c->base_id, text);
}
-void dlg_label_change(union control *ctrl, dlgparam *dp, char const *text)
+void dlg_label_change(dlgcontrol *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) {
+ switch (c->ctrl->type) {
case CTRL_EDITBOX:
escaped = shortcut_escape(text, c->ctrl->editbox.shortcut);
id = c->base_id;
@@ -2331,7 +2379,10 @@ void dlg_label_change(union control *ctrl, dlgparam *dp, char const *text)
break;
case CTRL_FILESELECT:
escaped = shortcut_escape(text, ctrl->fileselect.shortcut);
- id = c->base_id;
+ if (ctrl->fileselect.just_button)
+ id = c->base_id + 2; /* the button */
+ else
+ id = c->base_id; /* the label */
break;
case CTRL_FONTSELECT:
escaped = shortcut_escape(text, ctrl->fontselect.shortcut);
@@ -2346,30 +2397,37 @@ void dlg_label_change(union control *ctrl, dlgparam *dp, char const *text)
}
}
-void dlg_filesel_set(union control *ctrl, dlgparam *dp, Filename *fn)
+void dlg_filesel_set(dlgcontrol *ctrl, dlgparam *dp, Filename *fn)
{
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
- assert(c && c->ctrl->generic.type == CTRL_FILESELECT);
+ assert(c);
+ assert(c->ctrl->type == CTRL_FILESELECT);
+ assert(!c->ctrl->fileselect.just_button);
SetDlgItemText(dp->hwnd, c->base_id+1, fn->path);
}
-Filename *dlg_filesel_get(union control *ctrl, dlgparam *dp)
+Filename *dlg_filesel_get(dlgcontrol *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;
+ assert(c);
+ assert(c->ctrl->type == CTRL_FILESELECT);
+ if (!c->ctrl->fileselect.just_button) {
+ tmp = GetDlgItemText_alloc(dp->hwnd, c->base_id+1);
+ ret = filename_from_str(tmp);
+ sfree(tmp);
+ return ret;
+ } else {
+ return filename_from_str(c->data);
+ }
}
-void dlg_fontsel_set(union control *ctrl, dlgparam *dp, FontSpec *fs)
+void dlg_fontsel_set(dlgcontrol *ctrl, dlgparam *dp, FontSpec *fs)
{
char *buf, *boldstr;
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
- assert(c && c->ctrl->generic.type == CTRL_FONTSELECT);
+ assert(c && c->ctrl->type == CTRL_FONTSELECT);
fontspec_free((FontSpec *)c->data);
c->data = fontspec_copy(fs);
@@ -2387,10 +2445,10 @@ void dlg_fontsel_set(union control *ctrl, dlgparam *dp, FontSpec *fs)
dlg_auto_set_fixed_pitch_flag(dp);
}
-FontSpec *dlg_fontsel_get(union control *ctrl, dlgparam *dp)
+FontSpec *dlg_fontsel_get(dlgcontrol *ctrl, dlgparam *dp)
{
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
- assert(c && c->ctrl->generic.type == CTRL_FONTSELECT);
+ assert(c && c->ctrl->type == CTRL_FONTSELECT);
return fontspec_copy((FontSpec *)c->data);
}
@@ -2399,32 +2457,32 @@ FontSpec *dlg_fontsel_get(union control *ctrl, dlgparam *dp)
* 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)
+void dlg_update_start(dlgcontrol *ctrl, dlgparam *dp)
{
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
- if (c && c->ctrl->generic.type == CTRL_LISTBOX) {
+ if (c && c->ctrl->type == CTRL_LISTBOX) {
SendDlgItemMessage(dp->hwnd, c->base_id+1, WM_SETREDRAW, false, 0);
}
}
-void dlg_update_done(union control *ctrl, dlgparam *dp)
+void dlg_update_done(dlgcontrol *ctrl, dlgparam *dp)
{
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
- if (c && c->ctrl->generic.type == CTRL_LISTBOX) {
+ if (c && c->ctrl->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)
+void dlg_set_focus(dlgcontrol *ctrl, dlgparam *dp)
{
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
int id;
HWND ctl;
if (!c)
return;
- switch (ctrl->generic.type) {
+ switch (ctrl->type) {
case CTRL_EDITBOX: id = c->base_id + 1; break;
case CTRL_RADIO:
for (id = c->base_id + ctrl->radio.nbuttons; id > 1; id--)
@@ -2475,7 +2533,7 @@ void dlg_end(dlgparam *dp, int value)
dp->endresult = value;
}
-void dlg_refresh(union control *ctrl, dlgparam *dp)
+void dlg_refresh(dlgcontrol *ctrl, dlgparam *dp)
{
int i, j;
struct winctrl *c;
@@ -2488,21 +2546,21 @@ void dlg_refresh(union control *ctrl, dlgparam *dp)
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);
+ if (c->ctrl && c->ctrl->handler != NULL)
+ c->ctrl->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);
+ if (ctrl->handler != NULL)
+ ctrl->handler(ctrl, dp, dp->data, EVENT_REFRESH);
}
}
-void dlg_coloursel_start(union control *ctrl, dlgparam *dp, int r, int g, int b)
+void dlg_coloursel_start(dlgcontrol *ctrl, dlgparam *dp, int r, int g, int b)
{
dp->coloursel_wanted = true;
dp->coloursel_result.r = r;
@@ -2510,7 +2568,7 @@ void dlg_coloursel_start(union control *ctrl, dlgparam *dp, int r, int g, int b)
dp->coloursel_result.b = b;
}
-bool dlg_coloursel_results(union control *ctrl, dlgparam *dp,
+bool dlg_coloursel_results(dlgcontrol *ctrl, dlgparam *dp,
int *r, int *g, int *b)
{
if (dp->coloursel_result.ok) {
diff --git a/windows/wincapi.h b/windows/cryptoapi.h
index 732412e2..4ea7fe49 100644
--- a/windows/wincapi.h
+++ b/windows/cryptoapi.h
@@ -1,12 +1,10 @@
/*
- * wincapi.h: Windows Crypto API functions defined in wincapi.c that
+ * cryptoapi.h: Windows Crypto API functions defined in PuTTY 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);
@@ -27,5 +25,3 @@ bool got_crypt(void);
* The returned string is dynamically allocated.
*/
char *capi_obfuscate_string(const char *realname);
-
-#endif
diff --git a/windows/windlg.c b/windows/dialog.c
index 9c5fdb76..5e49cca9 100644
--- a/windows/windlg.c
+++ b/windows/dialog.c
@@ -1,5 +1,5 @@
/*
- * windlg.c - dialogs for PuTTY(tel), including the configuration dialog.
+ * dialog.c - dialogs for PuTTY(tel), including the configuration dialog.
*/
#include <stdio.h>
@@ -11,8 +11,8 @@
#include "putty.h"
#include "ssh.h"
-#include "win_res.h"
-#include "winseat.h"
+#include "putty-rc.h"
+#include "win-gui-seat.h"
#include "storage.h"
#include "dialog.h"
#include "licence.h"
@@ -27,21 +27,190 @@
#define ICON_BIG 1
#endif
+typedef struct PortableDialogStuff {
+ /*
+ * These are the various bits of data required to handle a dialog
+ * box that's been built up from the cross-platform dialog.c
+ * system.
+ */
+
+ /* The 'controlbox' that was returned from the portable setup function */
+ struct controlbox *ctrlbox;
+
+ /* The dlgparam that's passed to all the runtime dlg_* functions.
+ * Declared as an array of 1 so it's convenient to pass it as a pointer. */
+ struct dlgparam dp[1];
+
+ /*
+ * Collections of instantiated controls. There can be more than
+ * one of these, because sometimes you want to destroy and
+ * recreate a subset of them - e.g. when switching panes in the
+ * main PuTTY config box, you delete and recreate _most_ of the
+ * controls, but not the OK and Cancel buttons at the bottom.
+ */
+ size_t nctrltrees;
+ struct winctrls *ctrltrees;
+
+ /*
+ * Flag indicating whether the dialog box has been initialised.
+ * Used to suppresss spurious firing of message handlers during
+ * setup.
+ */
+ bool initialised;
+} PortableDialogStuff;
+
/*
- * 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.
+ * Initialise a PortableDialogStuff, before launching the dialog box.
*/
-static struct controlbox *ctrlbox;
+static PortableDialogStuff *pds_new(size_t nctrltrees)
+{
+ PortableDialogStuff *pds = snew(PortableDialogStuff);
+ memset(pds, 0, sizeof(*pds));
+
+ pds->ctrlbox = ctrl_new_box();
+
+ dp_init(pds->dp);
+
+ pds->nctrltrees = nctrltrees;
+ pds->ctrltrees = snewn(pds->nctrltrees, struct winctrls);
+ for (size_t i = 0; i < pds->nctrltrees; i++) {
+ winctrl_init(&pds->ctrltrees[i]);
+ dp_add_tree(pds->dp, &pds->ctrltrees[i]);
+ }
+
+ pds->dp->errtitle = dupprintf("%s Error", appname);
+
+ pds->initialised = false;
+
+ return pds;
+}
+
+static void pds_free(PortableDialogStuff *pds)
+{
+ ctrl_free_box(pds->ctrlbox);
+
+ dp_cleanup(pds->dp);
+
+ for (size_t i = 0; i < pds->nctrltrees; i++)
+ winctrl_cleanup(&pds->ctrltrees[i]);
+ sfree(pds->ctrltrees);
+
+ sfree(pds);
+}
+
+static INT_PTR pds_default_dlgproc(PortableDialogStuff *pds, HWND hwnd,
+ UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_LBUTTONUP:
+ /*
+ * Button release should trigger WM_OK if there was a
+ * previous double click on the host CA list.
+ */
+ ReleaseCapture();
+ if (pds->dp->ended)
+ ShinyEndDialog(hwnd, pds->dp->endresult ? 1 : 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.
+ */
+ int ret;
+ if (pds->initialised) {
+ ret = winctrl_handle_command(pds->dp, msg, wParam, lParam);
+ if (pds->dp->ended && GetCapture() != hwnd)
+ ShinyEndDialog(hwnd, pds->dp->endresult ? 1 : 0);
+ } else
+ ret = 0;
+ return ret;
+ }
+ case WM_HELP:
+ if (!winctrl_context_help(pds->dp,
+ hwnd, ((LPHELPINFO)lParam)->iCtrlId))
+ MessageBeep(0);
+ break;
+ case WM_CLOSE:
+ quit_help(hwnd);
+ ShinyEndDialog(hwnd, 0);
+ return 0;
+
+ /* Grrr Explorer will maximize Dialogs! */
+ case WM_SIZE:
+ if (wParam == SIZE_MAXIMIZED)
+ force_normal(hwnd);
+ return 0;
+
+ }
+ return 0;
+}
+
+static void pds_initdialog_start(PortableDialogStuff *pds, HWND hwnd)
+{
+ pds->dp->hwnd = hwnd;
+
+ if (pds->dp->wintitle) /* apply override title, if provided */
+ SetWindowText(hwnd, pds->dp->wintitle);
+
+ /* The portable dialog system generally includes the ability to
+ * handle context help for particular controls. Enable the
+ * relevant window styles if we have a help file available. */
+ if (has_help()) {
+ LONG_PTR style = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
+ SetWindowLongPtr(hwnd, GWL_EXSTYLE, style | WS_EX_CONTEXTHELP);
+ } else {
+ /* If not, and if the dialog template provided a top-level
+ * Help button, delete it */
+ HWND item = GetDlgItem(hwnd, IDC_HELPBTN);
+ if (item)
+ DestroyWindow(item);
+ }
+}
+
/*
- * 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.
+ * Create the panelfuls of controls in the configuration box.
*/
-static struct winctrls ctrls_base, ctrls_panel;
-static struct dlgparam dp;
+static void pds_create_controls(
+ PortableDialogStuff *pds, size_t which_tree, int base_id,
+ int left, int right, int top, char *path)
+{
+ struct ctlpos cp;
+
+ ctlposinit(&cp, pds->dp->hwnd, left, right, top);
+
+ for (int index = -1; (index = ctrl_find_path(
+ pds->ctrlbox, path, index)) >= 0 ;) {
+ struct controlset *s = pds->ctrlbox->ctrlsets[index];
+ winctrl_layout(pds->dp, &pds->ctrltrees[which_tree], &cp, s, &base_id);
+ }
+}
+
+static void pds_initdialog_finish(PortableDialogStuff *pds)
+{
+ /*
+ * Set focus into the first available control in ctrltree #0,
+ * which the caller was expected to set up to be the one
+ * containing the dialog controls likely to be used first.
+ */
+ struct winctrl *c;
+ for (int i = 0; (c = winctrl_findbyindex(&pds->ctrltrees[0], i)) != NULL;
+ i++) {
+ if (c->ctrl) {
+ dlg_set_focus(c->ctrl, pds->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(pds->dp->hwnd, SW_SHOWNORMAL);
+
+ pds->initialised = true;
+}
#define LOGEVENT_INITIAL_MAX 128
#define LOGEVENT_CIRCULAR_MAX 128
@@ -216,10 +385,10 @@ static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg,
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.");
+ 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);
@@ -255,57 +424,6 @@ static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg,
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.
*/
@@ -355,81 +473,37 @@ static HTREEITEM treeview_insert(struct treeview_faff *faff,
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;
- }
+const char *dialog_box_demo_screenshot_filename = NULL;
- 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);
- }
-}
+/* ctrltrees indices for the main dialog box */
+enum {
+ TREE_PANEL, /* things we swap out every time treeview selects a new pane */
+ TREE_BASE, /* fixed things at the bottom like OK and Cancel buttons */
+};
/*
* 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)
+static INT_PTR GenericMainDlgProc(HWND hwnd, UINT msg, WPARAM wParam,
+ LPARAM lParam, void *ctx)
{
- HWND hw, treeview;
+ PortableDialogStuff *pds = (PortableDialogStuff *)ctx;
+ const int DEMO_SCREENSHOT_TIMER_ID = 1230;
+ HWND 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);
- }
+ pds_initdialog_start(pds, hwnd);
+
+ pds_create_controls(pds, TREE_BASE, IDCX_STDBASE, 3, 3, 235, "");
+
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);
- }
+
+ centre_window(hwnd);
/*
* Create the tree view.
@@ -482,8 +556,8 @@ static INT_PTR CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
char *path = NULL;
char *firstpath = NULL;
- for (i = 0; i < ctrlbox->nctrlsets; i++) {
- struct controlset *s = ctrlbox->ctrlsets[i];
+ for (i = 0; i < pds->ctrlbox->nctrlsets; i++) {
+ struct controlset *s = pds->ctrlbox->ctrlsets[i];
HTREEITEM item;
int j;
char *c;
@@ -508,9 +582,9 @@ static INT_PTR CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
c = strrchr(s->pathname, '/');
if (!c)
- c = s->pathname;
+ c = s->pathname;
else
- c++;
+ c++;
item = treeview_insert(&tvfaff, j, c, s->pathname);
if (!hfirst) {
@@ -532,49 +606,32 @@ static INT_PTR CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
* 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 */
+ pds_create_controls(pds, TREE_PANEL, IDCX_PANELBASE,
+ 100, 3, 13, firstpath);
+ dlg_refresh(NULL, pds->dp); /* and set up control values */
}
- /*
- * Set focus into the first available control.
- */
- {
- int i;
- struct winctrl *c;
+ if (dialog_box_demo_screenshot_filename)
+ SetTimer(hwnd, DEMO_SCREENSHOT_TIMER_ID, TICKSPERSEC, NULL);
- for (i = 0; (c = winctrl_findbyindex(&ctrls_panel, i)) != NULL;
- i++) {
- if (c->ctrl) {
- dlg_set_focus(c->ctrl, &dp);
- break;
- }
+ pds_initdialog_finish(pds);
+ return 0;
+
+ case WM_TIMER:
+ if (dialog_box_demo_screenshot_filename &&
+ (UINT_PTR)wParam == DEMO_SCREENSHOT_TIMER_ID) {
+ KillTimer(hwnd, DEMO_SCREENSHOT_TIMER_ID);
+ char *err = save_screenshot(
+ hwnd, dialog_box_demo_screenshot_filename);
+ if (err) {
+ MessageBox(hwnd, err, "Demo screenshot failure",
+ MB_OK | MB_ICONERROR);
+ sfree(err);
}
+ ShinyEndDialog(hwnd, 0);
}
-
- /*
- * 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) {
@@ -589,7 +646,7 @@ static INT_PTR CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
TVITEM item;
char buffer[64];
- if (GetWindowLongPtr(hwnd, GWLP_USERDATA) != 1)
+ if (!pds->initialised)
return 0;
i = TreeView_GetSelection(((LPNMHDR) lParam)->hwndFrom);
@@ -607,60 +664,34 @@ static INT_PTR CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
HWND item;
struct winctrl *c;
- while ((c = winctrl_findbyindex(&ctrls_panel, 0)) != NULL) {
+ while ((c = winctrl_findbyindex(
+ &pds->ctrltrees[TREE_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);
+ winctrl_rem_shortcuts(pds->dp, c);
+ winctrl_remove(&pds->ctrltrees[TREE_PANEL], c);
sfree(c->data);
sfree(c);
}
}
- create_controls(hwnd, (char *)item.lParam);
+ pds_create_controls(pds, TREE_PANEL, IDCX_PANELBASE,
+ 100, 3, 13, (char *)item.lParam);
- dlg_refresh(NULL, &dp); /* set up control values */
+ dlg_refresh(NULL, pds->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;
+ default:
+ return pds_default_dlgproc(pds, hwnd, msg, wParam, lParam);
}
- return 0;
}
void modal_about_box(HWND hwnd)
@@ -696,29 +727,22 @@ void defuse_showwindow(void)
bool do_config(Conf *conf)
{
bool ret;
+ PortableDialogStuff *pds = pds_new(2);
+
+ setup_config_box(pds->ctrlbox, false, 0, 0);
+ win_setup_config_box(pds->ctrlbox, &pds->dp->hwnd, has_help(), false, 0);
+
+ pds->dp->wintitle = dupprintf("%s Configuration", appname);
+ pds->dp->data = conf;
- 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);
+ dlg_auto_set_fixed_pitch_flag(pds->dp);
+
+ pds->dp->shortcuts['g'] = true; /* the treeview: `Cate&gory' */
+
+ ret = ShinyDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), "PuTTYConfigBox",
+ NULL, GenericMainDlgProc, pds);
+
+ pds_free(pds);
return ret;
}
@@ -728,31 +752,26 @@ bool do_reconfig(HWND hwnd, Conf *conf, int protcfginfo)
Conf *backup_conf;
bool ret;
int protocol;
+ PortableDialogStuff *pds = pds_new(2);
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);
+ setup_config_box(pds->ctrlbox, true, protocol, protcfginfo);
+ win_setup_config_box(pds->ctrlbox, &pds->dp->hwnd, has_help(),
+ true, protocol);
+
+ pds->dp->wintitle = dupprintf("%s Reconfiguration", appname);
+ pds->dp->data = conf;
+
+ dlg_auto_set_fixed_pitch_flag(pds->dp);
+
+ pds->dp->shortcuts['g'] = true; /* the treeview: `Cate&gory' */
+
+ ret = ShinyDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), "PuTTYConfigBox",
+ NULL, GenericMainDlgProc, pds);
+
+ pds_free(pds);
if (!ret)
conf_copy_into(conf, backup_conf);
@@ -823,90 +842,232 @@ void showabout(HWND hwnd)
}
struct hostkey_dialog_ctx {
- const char *const *keywords;
- const char *const *values;
- FingerprintType fptype_default;
- char **fingerprints;
- const char *keydisp;
- LPCTSTR iconid;
+ SeatDialogText *text;
+ bool has_title;
const char *helpctx;
};
-static INT_PTR CALLBACK HostKeyMoreInfoProc(HWND hwnd, UINT msg,
- WPARAM wParam, LPARAM lParam)
+static INT_PTR HostKeyMoreInfoProc(HWND hwnd, UINT msg, WPARAM wParam,
+ LPARAM lParam, void *vctx)
{
+ struct hostkey_dialog_ctx *ctx = (struct hostkey_dialog_ctx *)vctx;
+
switch (msg) {
case WM_INITDIALOG: {
- const struct hostkey_dialog_ctx *ctx =
- (const struct hostkey_dialog_ctx *)lParam;
- SetWindowLongPtr(hwnd, GWLP_USERDATA, (INT_PTR)ctx);
+ int index = 100, y = 12;
+
+ WPARAM font = SendMessage(hwnd, WM_GETFONT, 0, 0);
- 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]);
+ const char *key = NULL;
+ for (SeatDialogTextItem *item = ctx->text->items,
+ *end = item + ctx->text->nitems; item < end; item++) {
+ switch (item->type) {
+ case SDT_MORE_INFO_KEY:
+ key = item->text;
+ break;
+ case SDT_MORE_INFO_VALUE_SHORT:
+ case SDT_MORE_INFO_VALUE_BLOB: {
+ RECT rk, rv;
+ DWORD editstyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP |
+ ES_AUTOHSCROLL | ES_READONLY;
+ if (item->type == SDT_MORE_INFO_VALUE_BLOB) {
+ rk.left = 12;
+ rk.right = 376;
+ rk.top = y;
+ rk.bottom = 8;
+ y += 10;
+
+ editstyle |= ES_MULTILINE;
+ rv.left = 12;
+ rv.right = 376;
+ rv.top = y;
+ rv.bottom = 64;
+ y += 68;
+ } else {
+ rk.left = 12;
+ rk.right = 80;
+ rk.top = y+2;
+ rk.bottom = 8;
+
+ rv.left = 100;
+ rv.right = 288;
+ rv.top = y;
+ rv.bottom = 12;
+
+ y += 16;
+ }
+
+ MapDialogRect(hwnd, &rk);
+ HWND ctl = CreateWindowEx(
+ 0, "STATIC", key, WS_CHILD | WS_VISIBLE,
+ rk.left, rk.top, rk.right, rk.bottom,
+ hwnd, (HMENU)(ULONG_PTR)index++, hinst, NULL);
+ SendMessage(ctl, WM_SETFONT, font, MAKELPARAM(true, 0));
+
+ MapDialogRect(hwnd, &rv);
+ ctl = CreateWindowEx(
+ WS_EX_CLIENTEDGE, "EDIT", item->text, editstyle,
+ rv.left, rv.top, rv.right, rv.bottom,
+ hwnd, (HMENU)(ULONG_PTR)index++, hinst, NULL);
+ SendMessage(ctl, WM_SETFONT, font, MAKELPARAM(true, 0));
+ break;
+ }
+ default:
+ break;
+ }
+ }
- SetDlgItemText(hwnd, IDA_TEXT, ctx->keydisp);
+ /*
+ * Now resize the overall window, and move the Close button at
+ * the bottom.
+ */
+ RECT r;
+ r.left = 176;
+ r.top = y + 10;
+ r.right = r.bottom = 0;
+ MapDialogRect(hwnd, &r);
+ HWND ctl = GetDlgItem(hwnd, IDOK);
+ SetWindowPos(ctl, NULL, r.left, r.top, 0, 0,
+ SWP_NOSIZE | SWP_NOREDRAW | SWP_NOZORDER);
+
+ r.left = r.top = r.right = 0;
+ r.bottom = 300;
+ MapDialogRect(hwnd, &r);
+ int oldheight = r.bottom;
+
+ r.left = r.top = r.right = 0;
+ r.bottom = y + 30;
+ MapDialogRect(hwnd, &r);
+ int newheight = r.bottom;
+
+ GetWindowRect(hwnd, &r);
+
+ SetWindowPos(hwnd, NULL, 0, 0, r.right - r.left,
+ r.bottom - r.top + newheight - oldheight,
+ SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
+ ShowWindow(hwnd, SW_SHOWNORMAL);
return 1;
}
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDOK:
- EndDialog(hwnd, 0);
+ ShinyEndDialog(hwnd, 0);
return 0;
}
return 0;
case WM_CLOSE:
- EndDialog(hwnd, 0);
+ ShinyEndDialog(hwnd, 0);
return 0;
}
return 0;
}
-static INT_PTR CALLBACK HostKeyDialogProc(HWND hwnd, UINT msg,
- WPARAM wParam, LPARAM lParam)
+static INT_PTR HostKeyDialogProc(HWND hwnd, UINT msg,
+ WPARAM wParam, LPARAM lParam, void *vctx)
{
+ struct hostkey_dialog_ctx *ctx = (struct hostkey_dialog_ctx *)vctx;
+
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)))
+ strbuf *dlg_text = strbuf_new();
+ const char *dlg_title = "";
+ ctx->has_title = false;
+ LPCTSTR iconid = IDI_QUESTION;
+
+ for (SeatDialogTextItem *item = ctx->text->items,
+ *end = item + ctx->text->nitems; item < end; item++) {
+ switch (item->type) {
+ case SDT_PARA:
+ put_fmt(dlg_text, "%s\r\n\r\n", item->text);
+ break;
+ case SDT_DISPLAY:
+ put_fmt(dlg_text, "%s\r\n\r\n", item->text);
+ break;
+ case SDT_SCARY_HEADING:
+ SetDlgItemText(hwnd, IDC_HK_TITLE, item->text);
+ iconid = IDI_WARNING;
+ ctx->has_title = true;
+ break;
+ case SDT_TITLE:
+ dlg_title = item->text;
+ break;
+ default:
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:;
}
+ }
+ while (strbuf_chomp(dlg_text, '\r') || strbuf_chomp(dlg_text, '\n'));
+
+ SetDlgItemText(hwnd, IDC_HK_TEXT, dlg_text->s);
+ MakeDlgItemBorderless(hwnd, IDC_HK_TEXT);
+ strbuf_free(dlg_text);
- SetDlgItemText(hwnd, id, sb->s);
+ SetWindowText(hwnd, dlg_title);
+
+ if (!ctx->has_title) {
+ HWND item = GetDlgItem(hwnd, IDC_HK_TITLE);
+ if (item)
+ DestroyWindow(item);
+ }
+
+ /*
+ * Find out how tall the text in the edit control really ended
+ * up (after line wrapping), and adjust the height of the
+ * whole box to match it.
+ */
+ int height = SendDlgItemMessage(hwnd, IDC_HK_TEXT,
+ EM_GETLINECOUNT, 0, 0);
+ height *= 8; /* height of a text line, by definition of dialog units */
+
+ int edittop = ctx->has_title ? 40 : 20;
+
+ RECT r;
+ r.left = 40;
+ r.top = edittop;
+ r.right = 290;
+ r.bottom = height;
+ MapDialogRect(hwnd, &r);
+ SetWindowPos(GetDlgItem(hwnd, IDC_HK_TEXT), NULL,
+ r.left, r.top, r.right, r.bottom,
+ SWP_NOREDRAW | SWP_NOZORDER);
+
+ static const struct {
+ int id, x;
+ } buttons[] = {
+ { IDCANCEL, 288 },
+ { IDC_HK_ACCEPT, 168 },
+ { IDC_HK_ONCE, 216 },
+ { IDC_HK_MOREINFO, 60 },
+ { IDHELP, 12 },
+ };
+ for (size_t i = 0; i < lenof(buttons); i++) {
+ HWND ctl = GetDlgItem(hwnd, buttons[i].id);
+ r.left = buttons[i].x;
+ r.top = edittop + height + 20;
+ r.right = r.bottom = 0;
+ MapDialogRect(hwnd, &r);
+ SetWindowPos(ctl, NULL, r.left, r.top, 0, 0,
+ SWP_NOSIZE | SWP_NOREDRAW | SWP_NOZORDER);
}
- strbuf_free(sb);
- SetDlgItemText(hwnd, IDC_HK_FINGERPRINT,
- ctx->fingerprints[ctx->fptype_default]);
- MakeDlgItemBorderless(hwnd, IDC_HK_FINGERPRINT);
+ r.left = r.top = r.right = 0;
+ r.bottom = 240;
+ MapDialogRect(hwnd, &r);
+ int oldheight = r.bottom;
+
+ r.left = r.top = r.right = 0;
+ r.bottom = edittop + height + 40;
+ MapDialogRect(hwnd, &r);
+ int newheight = r.bottom;
+
+ GetWindowRect(hwnd, &r);
+
+ SetWindowPos(hwnd, NULL, 0, 0, r.right - r.left,
+ r.bottom - r.top + newheight - oldheight,
+ SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
HANDLE icon = LoadImage(
- NULL, ctx->iconid, IMAGE_ICON,
+ NULL, iconid, IMAGE_ICON,
GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON),
LR_SHARED);
SendDlgItemMessage(hwnd, IDC_HK_ICON, STM_SETICON, (WPARAM)icon, 0);
@@ -917,13 +1078,16 @@ static INT_PTR CALLBACK HostKeyDialogProc(HWND hwnd, UINT msg,
DestroyWindow(item);
}
+ ShowWindow(hwnd, SW_SHOWNORMAL);
+
return 1;
}
case WM_CTLCOLORSTATIC: {
HDC hdc = (HDC)wParam;
HWND control = (HWND)lParam;
- if (GetWindowLongPtr(control, GWLP_ID) == IDC_HK_TITLE) {
+ if (GetWindowLongPtr(control, GWLP_ID) == IDC_HK_TITLE &&
+ ctx->has_title) {
SetBkMode(hdc, TRANSPARENT);
HFONT prev_font = (HFONT)SelectObject(
hdc, (HFONT)GetStockObject(SYSTEM_FONT));
@@ -944,85 +1108,69 @@ static INT_PTR CALLBACK HostKeyDialogProc(HWND hwnd, UINT msg,
case IDC_HK_ACCEPT:
case IDC_HK_ONCE:
case IDCANCEL:
- EndDialog(hwnd, LOWORD(wParam));
+ ShinyEndDialog(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);
+ ShinyDialogBox(hinst, MAKEINTRESOURCE(IDD_HK_MOREINFO),
+ "PuTTYHostKeyMoreInfo", hwnd,
+ HostKeyMoreInfoProc, ctx);
}
}
return 0;
case WM_CLOSE:
- EndDialog(hwnd, IDCANCEL);
+ ShinyEndDialog(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)
+const SeatDialogPromptDescriptions *win_seat_prompt_descriptions(Seat *seat)
{
- int ret;
+ static const SeatDialogPromptDescriptions descs = {
+ .hk_accept_action = "press \"Accept\"",
+ .hk_connect_once_action = "press \"Connect Once\"",
+ .hk_cancel_action = "press \"Cancel\"",
+ .hk_cancel_action_Participle = "Pressing \"Cancel\"",
+ };
+ return &descs;
+}
+SeatPromptResult win_seat_confirm_ssh_host_key(
+ Seat *seat, const char *host, int port, const char *keytype,
+ char *keystr, SeatDialogText *text, HelpCtx helpctx,
+ void (*callback)(void *ctx, SeatPromptResult result), void *cbctx)
+{
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;
+ struct hostkey_dialog_ctx ctx[1];
+ ctx->text = text;
+ ctx->helpctx = helpctx;
+
+ int mbret = ShinyDialogBox(
+ hinst, MAKEINTRESOURCE(IDD_HOSTKEY), "PuTTYHostKeyDialog",
+ wgs->term_hwnd, HostKeyDialogProc, 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 SPR_OK;
+ } else if (mbret == IDC_HK_ONCE) {
+ return SPR_OK;
}
- return 0; /* abandon the connection */
+
+ return SPR_USER_ABORT;
}
/*
* Ask whether the selected algorithm is acceptable (since it was
* below the configured 'warn' threshold).
*/
-int win_seat_confirm_weak_crypto_primitive(
+SeatPromptResult win_seat_confirm_weak_crypto_primitive(
Seat *seat, const char *algtype, const char *algname,
- void (*callback)(void *ctx, int result), void *ctx)
+ void (*callback)(void *ctx, SeatPromptResult result), void *ctx)
{
static const char mbtitle[] = "%s Security Alert";
static const char msg[] =
@@ -1041,14 +1189,14 @@ int win_seat_confirm_weak_crypto_primitive(
sfree(message);
sfree(title);
if (mbret == IDYES)
- return 1;
+ return SPR_OK;
else
- return 0;
+ return SPR_USER_ABORT;
}
-int win_seat_confirm_weak_cached_hostkey(
+SeatPromptResult win_seat_confirm_weak_cached_hostkey(
Seat *seat, const char *algname, const char *betteralgs,
- void (*callback)(void *ctx, int result), void *ctx)
+ void (*callback)(void *ctx, SeatPromptResult result), void *ctx)
{
static const char mbtitle[] = "%s Security Alert";
static const char msg[] =
@@ -1069,9 +1217,9 @@ int win_seat_confirm_weak_cached_hostkey(
sfree(message);
sfree(title);
if (mbret == IDYES)
- return 1;
+ return SPR_OK;
else
- return 0;
+ return SPR_USER_ABORT;
}
/*
@@ -1154,3 +1302,41 @@ void old_keyfile_warning(void)
sfree(msg);
sfree(title);
}
+
+static INT_PTR CAConfigProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam,
+ void *ctx)
+{
+ PortableDialogStuff *pds = (PortableDialogStuff *)ctx;
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ pds_initdialog_start(pds, hwnd);
+
+ SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
+ (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(IDI_CFGICON)));
+
+ centre_window(hwnd);
+
+ pds_create_controls(pds, 0, IDCX_PANELBASE, 3, 3, 3, "Main");
+ pds_create_controls(pds, 0, IDCX_STDBASE, 3, 3, 243, "");
+ dlg_refresh(NULL, pds->dp); /* and set up control values */
+
+ pds_initdialog_finish(pds);
+ return 0;
+
+ default:
+ return pds_default_dlgproc(pds, hwnd, msg, wParam, lParam);
+ }
+}
+
+void show_ca_config_box(dlgparam *dp)
+{
+ PortableDialogStuff *pds = pds_new(1);
+
+ setup_ca_config_box(pds->ctrlbox);
+
+ ShinyDialogBox(hinst, MAKEINTRESOURCE(IDD_CA_CONFIG), "PuTTYConfigBox",
+ dp ? dp->hwnd : NULL, CAConfigProc, pds);
+
+ pds_free(pds);
+}
diff --git a/windows/wingss.c b/windows/gss.c
index 79c4921c..724eeec1 100644
--- a/windows/wingss.c
+++ b/windows/gss.c
@@ -6,9 +6,9 @@
#define SECURITY_WIN32
#include <security.h>
-#include "pgssapi.h"
-#include "sshgss.h"
-#include "sshgssc.h"
+#include "ssh/pgssapi.h"
+#include "ssh/gss.h"
+#include "ssh/gssc.h"
#include "misc.h"
@@ -91,8 +91,6 @@ typedef struct 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;
@@ -127,7 +125,7 @@ struct ssh_gss_liblist *ssh_gss_setup(Conf *conf)
if (!kernel32_module) {
kernel32_module = load_system32_dll("kernel32.dll");
}
-#if defined _MSC_VER && _MSC_VER < 1900
+#if !HAVE_ADDDLLDIRECTORY
/* Omit the type-check because older MSVCs don't have this function */
GET_WINDOWS_FUNCTION_NO_TYPECHECK(kernel32_module, AddDllDirectory);
#else
@@ -227,7 +225,8 @@ struct ssh_gss_liblist *ssh_gss_setup(Conf *conf)
lib->gsslogmsg = "Using SSPI from SECUR32.DLL";
lib->handle = (void *)module;
- GET_WINDOWS_FUNCTION(module, AcquireCredentialsHandleA);
+ /* No typecheck because Winelib thinks one PVOID is a PLUID */
+ GET_WINDOWS_FUNCTION_NO_TYPECHECK(module, AcquireCredentialsHandleA);
GET_WINDOWS_FUNCTION(module, InitializeSecurityContextA);
GET_WINDOWS_FUNCTION(module, FreeContextBuffer);
GET_WINDOWS_FUNCTION(module, FreeCredentialsHandle);
@@ -465,7 +464,7 @@ static Ssh_gss_stat ssh_sspi_init_sec_context(struct ssh_gss_library *lib,
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;
+ ULONG ret_flags=0;
TimeStamp localexp;
/* check if we have to delegate ... */
@@ -664,8 +663,8 @@ static Ssh_gss_stat ssh_sspi_verify_mic(struct ssh_gss_library *lib,
InputSecurityToken[1].pvBuffer = mic->value;
winctx->maj_stat = p_VerifySignature(&winctx->context,
- &InputBufferDescriptor,
- 0, &qop);
+ &InputBufferDescriptor,
+ 0, &qop);
return winctx->maj_stat;
}
diff --git a/windows/winhandl.c b/windows/handle-io.c
index 82d2aded..0f5b7e94 100644
--- a/windows/winhandl.c
+++ b/windows/handle-io.c
@@ -1,5 +1,5 @@
/*
- * winhandl.c: Module to give Windows front ends the general
+ * handle-io.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.
@@ -37,6 +37,12 @@
* Generic definitions.
*/
+typedef struct handle_list_node handle_list_node;
+struct handle_list_node {
+ handle_list_node *next, *prev;
+};
+static void add_to_ready_list(handle_list_node *node);
+
/*
* Maximum amount of backlog we will allow to build up on an input
* handle before we stop reading from it.
@@ -56,7 +62,7 @@ struct handle_generic {
* thread.
*/
HANDLE h; /* the handle itself */
- HANDLE ev_to_main; /* event used to signal main thread */
+ handle_list_node ready_node; /* for linking on to the ready list */
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 */
@@ -65,7 +71,7 @@ struct handle_generic {
void *privdata; /* for client to remember who they are */
};
-typedef enum { HT_INPUT, HT_OUTPUT, HT_FOREIGN } HandleType;
+typedef enum { HT_INPUT, HT_OUTPUT } HandleType;
/* ----------------------------------------------------------------------
* Input threads.
@@ -79,7 +85,7 @@ 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_list_node ready_node; /* for linking on to the ready list */
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 */
@@ -93,7 +99,7 @@ struct handle_input {
int flags;
/*
- * Data set by the input thread before signalling ev_to_main,
+ * Data set by the input thread before marking the handle ready,
* and read by the main thread after receiving that signal.
*/
char buffer[4096]; /* the data read from the handle */
@@ -176,7 +182,7 @@ static DWORD WINAPI handle_input_threadfunc(void *param)
*/
finished = (ctx->len == 0);
- SetEvent(ctx->ev_to_main);
+ add_to_ready_list(&ctx->ready_node);
if (finished)
break;
@@ -189,7 +195,7 @@ static DWORD WINAPI handle_input_threadfunc(void *param)
* not touch ctx at all, because the main thread might
* have freed it.
*/
- SetEvent(ctx->ev_to_main);
+ add_to_ready_list(&ctx->ready_node);
break;
}
}
@@ -240,7 +246,7 @@ 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_list_node ready_node; /* for linking on to the ready list */
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 */
@@ -261,8 +267,8 @@ struct handle_output {
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.
+ * Data set by the input thread before marking this handle as
+ * ready, and read by the main thread after receiving that signal.
*/
DWORD lenwritten; /* how much data we actually wrote */
int writeerr; /* return value from WriteFile */
@@ -278,6 +284,7 @@ struct handle_output {
* drops.
*/
handle_outputfn_t sentdata;
+ struct handle *sentdata_param;
};
static DWORD WINAPI handle_output_threadfunc(void *param)
@@ -303,7 +310,7 @@ static DWORD WINAPI handle_output_threadfunc(void *param)
* not touch ctx at all, because the main thread might
* have freed it.
*/
- SetEvent(ctx->ev_to_main);
+ add_to_ready_list(&ctx->ready_node);
break;
}
if (povl) {
@@ -326,7 +333,7 @@ static DWORD WINAPI handle_output_threadfunc(void *param)
ctx->writeerr = 0;
}
- SetEvent(ctx->ev_to_main);
+ add_to_ready_list(&ctx->ready_node);
if (!writeret) {
/*
* The write operation has suffered an error. Telling that
@@ -355,40 +362,13 @@ static void handle_try_output(struct handle_output *ctx)
ctx->busy = true;
} else if (!ctx->busy && bufchain_size(&ctx->queued_data) == 0 &&
ctx->outgoingeof == EOF_PENDING) {
- CloseHandle(ctx->h);
+ ctx->sentdata(ctx->sentdata_param, 0, 0, true);
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.
*/
@@ -398,36 +378,91 @@ struct handle {
struct handle_generic g;
struct handle_input i;
struct handle_output o;
- struct handle_foreign f;
} u;
};
-static tree234 *handles_by_evtomain;
+/*
+ * Linked list storing the current list of handles ready to have
+ * something done to them by the main thread.
+ */
+static handle_list_node ready_head[1];
+static CRITICAL_SECTION ready_critsec[1];
-static int handle_cmp_evtomain(void *av, void *bv)
+/*
+ * Event object used by all subthreads to signal that they've just put
+ * something on the ready list, i.e. that the ready list is non-empty.
+ */
+static HANDLE ready_event = INVALID_HANDLE_VALUE;
+
+static void add_to_ready_list(handle_list_node *node)
{
- struct handle *a = (struct handle *)av;
- struct handle *b = (struct handle *)bv;
+ /*
+ * Called from subthreads, when their handle has done something
+ * that they need the main thread to respond to. We append the
+ * given list node to the end of the ready list, and set
+ * ready_event to signal to the main thread that the ready list is
+ * now non-empty.
+ */
+ EnterCriticalSection(ready_critsec);
+ node->next = ready_head;
+ node->prev = ready_head->prev;
+ node->next->prev = node->prev->next = node;
+ SetEvent(ready_event);
+ LeaveCriticalSection(ready_critsec);
+}
- 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 void remove_from_ready_list(handle_list_node *node)
+{
+ /*
+ * Called from the main thread, just before destroying a 'struct
+ * handle' completely: as a precaution, we make absolutely sure
+ * it's not linked on the ready list, just in case somehow it
+ * still was.
+ */
+ EnterCriticalSection(ready_critsec);
+ node->next->prev = node->prev;
+ node->prev->next = node->next;
+ node->next = node->prev = node;
+ LeaveCriticalSection(ready_critsec);
}
-static int handle_find_evtomain(void *av, void *bv)
+static void handle_ready(struct handle *h); /* process one handle (below) */
+
+static void handle_ready_callback(void *vctx)
{
- HANDLE *a = (HANDLE *)av;
- struct handle *b = (struct handle *)bv;
+ /*
+ * Called when the main thread detects ready_event, indicating
+ * that at least one handle is on the ready list. We empty the
+ * whole list and process the handles one by one.
+ *
+ * It's possible that other handles may be destroyed, and hence
+ * taken _off_ the ready list, during this processing. That
+ * shouldn't cause a deadlock, because according to the API docs,
+ * it's safe to call EnterCriticalSection twice in the same thread
+ * - the second call will return immediately because that thread
+ * already owns the critsec. (And then it takes two calls to
+ * LeaveCriticalSection to release it again, which is just what we
+ * want here.)
+ */
+ EnterCriticalSection(ready_critsec);
+ while (ready_head->next != ready_head) {
+ handle_list_node *node = ready_head->next;
+ node->prev->next = node->next;
+ node->next->prev = node->prev;
+ node->next = node->prev = node;
+ handle_ready(container_of(node, struct handle, u.g.ready_node));
+ }
+ LeaveCriticalSection(ready_critsec);
+}
- 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;
+static inline void ensure_ready_event_setup(void)
+{
+ if (ready_event == INVALID_HANDLE_VALUE) {
+ ready_head->prev = ready_head->next = ready_head;
+ InitializeCriticalSection(ready_critsec);
+ ready_event = CreateEvent(NULL, false, false, NULL);
+ add_handle_wait(ready_event, handle_ready_callback, NULL);
+ }
}
struct handle *handle_input_new(HANDLE handle, handle_inputfn_t gotdata,
@@ -438,7 +473,6 @@ struct handle *handle_input_new(HANDLE handle, handle_inputfn_t gotdata,
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;
@@ -447,10 +481,7 @@ struct handle *handle_input_new(HANDLE handle, handle_inputfn_t gotdata,
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);
-
+ ensure_ready_event_setup();
HANDLE hThread = CreateThread(NULL, 0, handle_input_threadfunc,
&h->u.i, 0, &in_threadid);
if (hThread)
@@ -468,7 +499,6 @@ struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata,
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;
@@ -478,12 +508,10 @@ struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata,
bufchain_init(&h->u.o.queued_data);
h->u.o.outgoingeof = EOF_NO;
h->u.o.sentdata = sentdata;
+ h->u.o.sentdata_param = h;
h->u.o.flags = flags;
- if (!handles_by_evtomain)
- handles_by_evtomain = newtree234(handle_cmp_evtomain);
- add234(handles_by_evtomain, h);
-
+ ensure_ready_event_setup();
HANDLE hThread = CreateThread(NULL, 0, handle_output_threadfunc,
&h->u.o, 0, &out_threadid);
if (hThread)
@@ -492,30 +520,6 @@ struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata,
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);
@@ -541,46 +545,19 @@ void handle_write_eof(struct handle *h)
}
}
-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);
+ remove_from_ready_list(&h->u.g.ready_node);
sfree(h);
}
void handle_free(struct handle *h)
{
assert(h && !h->u.g.moribund);
- if (h->u.g.busy && h->type != HT_FOREIGN) {
+ if (h->u.g.busy) {
/*
* If the handle is currently busy, we cannot immediately free
* it, because its subthread is in the middle of something.
@@ -612,24 +589,8 @@ void handle_free(struct handle *h)
}
}
-void handle_got_event(HANDLE event)
+static void handle_ready(struct handle *h)
{
- 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
@@ -685,19 +646,14 @@ void handle_got_event(HANDLE event)
* thread is terminating by now).
*/
h->u.o.defunct = true;
- h->u.o.sentdata(h, 0, h->u.o.writeerr);
+ h->u.o.sentdata(h, 0, h->u.o.writeerr, false);
} 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);
+ h->u.o.sentdata(h, bufchain_size(&h->u.o.queued_data), 0, false);
handle_try_output(&h->u.o);
}
break;
-
- case HT_FOREIGN:
- /* Just call the callback. */
- h->u.f.callback(h->u.f.ctx);
- break;
}
}
diff --git a/windows/winhsock.c b/windows/handle-socket.c
index 543b77b6..2820975c 100644
--- a/windows/winhsock.c
+++ b/windows/handle-socket.c
@@ -11,35 +11,56 @@
#include "putty.h"
#include "network.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 handle-io.c to stop
+ * reading.
+ */
+typedef enum HandleSocketFreezeState {
+ 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 */
+} HandleSocketFreezeState;
+
typedef struct HandleSocket {
- HANDLE send_H, recv_H, stderr_H;
- struct handle *send_h, *recv_h, *stderr_h;
+ union {
+ struct {
+ 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;
+ HandleSocketFreezeState 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;
+ /* Handle logging proxy error messages from stderr_H, if
+ * we have one */
+ ProxyStderrBuf psb;
- bool defer_close, deferred_close; /* in case of re-entrance */
+ bool defer_close, deferred_close; /* in case of re-entrance */
+ };
+ struct {
+ DeferredSocketOpener *opener;
+
+ /* We buffer data here if we receive it via sk_write
+ * before the socket is opened. */
+ bufchain outputdata;
+
+ bool output_eof_pending;
+
+ bool start_frozen;
+ };
+ };
char *error;
+ SockAddr *addr;
+ int port;
Plug *plug;
Socket sock;
@@ -51,17 +72,17 @@ static size_t handle_gotdata(
HandleSocket *hs = (HandleSocket *)handle_get_privdata(h);
if (err) {
- plug_closing(hs->plug, "Read error from handle", 0, 0);
+ plug_closing_error(hs->plug, "Read error from handle");
return 0;
} else if (len == 0) {
- plug_closing(hs->plug, NULL, 0, 0);
+ plug_closing_normal(hs->plug);
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
+ * be frozen (because the read handle-io.c started before
* sk_set_frozen was called has now returned) then buffer
* the data for when we unfreeze.
*/
@@ -91,12 +112,21 @@ static size_t handle_stderr(
return 0;
}
-static void handle_sentdata(struct handle *h, size_t new_backlog, int err)
+static void handle_sentdata(struct handle *h, size_t new_backlog, int err,
+ bool close)
{
HandleSocket *hs = (HandleSocket *)handle_get_privdata(h);
+ if (close) {
+ if (hs->send_H != INVALID_HANDLE_VALUE)
+ CloseHandle(hs->send_H);
+ if (hs->recv_H != INVALID_HANDLE_VALUE && hs->recv_H != hs->send_H)
+ CloseHandle(hs->recv_H);
+ hs->send_H = hs->recv_H = INVALID_HANDLE_VALUE;
+ }
+
if (err) {
- plug_closing(hs->plug, win_strerror(err), err, 0);
+ plug_closing_system_error(hs->plug, err);
return;
}
@@ -123,11 +153,15 @@ static void sk_handle_close(Socket *s)
handle_free(hs->send_h);
handle_free(hs->recv_h);
- CloseHandle(hs->send_H);
- if (hs->recv_H != hs->send_H)
+ if (hs->send_H != INVALID_HANDLE_VALUE)
+ CloseHandle(hs->send_H);
+ if (hs->recv_H != INVALID_HANDLE_VALUE && hs->recv_H != hs->send_H)
CloseHandle(hs->recv_H);
bufchain_clear(&hs->inputdata);
+ if (hs->addr)
+ sk_addr_free(hs->addr);
+
delete_callbacks_for_context(hs);
sfree(hs);
@@ -214,7 +248,7 @@ static void sk_handle_set_frozen(Socket *s, bool is_frozen)
case THAWING:
/*
* We were in the middle of emptying our bufchain, and got
- * frozen again. In that case, winhandl.c is already
+ * frozen again. In that case, handle-io.c is already
* throttled, so just return to FROZEN state. The toplevel
* callback will notice and disable itself.
*/
@@ -272,7 +306,7 @@ static SocketPeerInfo *sk_handle_peer_info(Socket *s)
if (!kernel32_module) {
kernel32_module = load_system32_dll("kernel32.dll");
-#if (defined _MSC_VER && _MSC_VER < 1900) || defined __MINGW32__
+#if !HAVE_GETNAMEDPIPECLIENTPROCESSID
/* 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
@@ -314,16 +348,26 @@ static const SocketVtable HandleSocket_sockvt = {
.peer_info = sk_handle_peer_info,
};
+static void sk_handle_connect_success_callback(void *ctx)
+{
+ HandleSocket *hs = (HandleSocket *)ctx;
+ plug_log(hs->plug, PLUGLOG_CONNECT_SUCCESS, hs->addr, hs->port, NULL, 0);
+}
+
Socket *make_handle_socket(HANDLE send_H, HANDLE recv_H, HANDLE stderr_H,
- Plug *plug, bool overlapped)
+ SockAddr *addr, int port, Plug *plug,
+ bool overlapped)
{
HandleSocket *hs;
int flags = (overlapped ? HANDLE_FLAG_OVERLAPPED : 0);
hs = snew(HandleSocket);
hs->sock.vt = &HandleSocket_sockvt;
+ hs->addr = addr;
+ hs->port = port;
hs->plug = plug;
hs->error = NULL;
+
hs->frozen = UNFROZEN;
bufchain_init(&hs->inputdata);
psb_init(&hs->psb);
@@ -339,5 +383,130 @@ Socket *make_handle_socket(HANDLE send_H, HANDLE recv_H, HANDLE stderr_H,
hs->defer_close = hs->deferred_close = false;
+ queue_toplevel_callback(sk_handle_connect_success_callback, hs);
+
+ return &hs->sock;
+}
+
+void handle_socket_set_psb_prefix(Socket *s, const char *prefix)
+{
+ HandleSocket *hs = container_of(s, HandleSocket, sock);
+ assert(hs->sock.vt == &HandleSocket_sockvt);
+ psb_set_prefix(&hs->psb, prefix);
+}
+
+static void sk_handle_deferred_close(Socket *s)
+{
+ HandleSocket *hs = container_of(s, HandleSocket, sock);
+
+ deferred_socket_opener_free(hs->opener);
+ bufchain_clear(&hs->outputdata);
+
+ if (hs->addr)
+ sk_addr_free(hs->addr);
+
+ delete_callbacks_for_context(hs);
+
+ sfree(hs);
+}
+
+static size_t sk_handle_deferred_write(Socket *s, const void *data, size_t len)
+{
+ HandleSocket *hs = container_of(s, HandleSocket, sock);
+ assert(!hs->output_eof_pending);
+ bufchain_add(&hs->outputdata, data, len);
+ return bufchain_size(&hs->outputdata);
+}
+
+static void sk_handle_deferred_write_eof(Socket *s)
+{
+ HandleSocket *hs = container_of(s, HandleSocket, sock);
+ assert(!hs->output_eof_pending);
+ hs->output_eof_pending = true;
+}
+
+static void sk_handle_deferred_set_frozen(Socket *s, bool is_frozen)
+{
+ HandleSocket *hs = container_of(s, HandleSocket, sock);
+ hs->frozen = is_frozen;
+}
+
+static SocketPeerInfo *sk_handle_deferred_peer_info(Socket *s)
+{
+ return NULL;
+}
+
+static const SocketVtable HandleSocket_deferred_sockvt = {
+ .plug = sk_handle_plug,
+ .close = sk_handle_deferred_close,
+ .write = sk_handle_deferred_write,
+ .write_oob = sk_handle_deferred_write,
+ .write_eof = sk_handle_deferred_write_eof,
+ .set_frozen = sk_handle_deferred_set_frozen,
+ .socket_error = sk_handle_socket_error,
+ .peer_info = sk_handle_deferred_peer_info,
+};
+
+Socket *make_deferred_handle_socket(DeferredSocketOpener *opener,
+ SockAddr *addr, int port, Plug *plug)
+{
+ HandleSocket *hs = snew(HandleSocket);
+ hs->sock.vt = &HandleSocket_deferred_sockvt;
+ hs->addr = addr;
+ hs->port = port;
+ hs->plug = plug;
+ hs->error = NULL;
+
+ hs->opener = opener;
+ bufchain_init(&hs->outputdata);
+ hs->output_eof_pending = false;
+ hs->start_frozen = false;
+
return &hs->sock;
}
+
+void setup_handle_socket(Socket *s, HANDLE send_H, HANDLE recv_H,
+ HANDLE stderr_H, bool overlapped)
+{
+ HandleSocket *hs = container_of(s, HandleSocket, sock);
+ assert(hs->sock.vt == &HandleSocket_deferred_sockvt);
+
+ int flags = (overlapped ? HANDLE_FLAG_OVERLAPPED : 0);
+
+ struct handle *recv_h = handle_input_new(
+ recv_H, handle_gotdata, hs, flags);
+ struct handle *send_h = handle_output_new(
+ send_H, handle_sentdata, hs, flags);
+ struct handle *stderr_h = !stderr_H ? NULL : handle_input_new(
+ stderr_H, handle_stderr, hs, flags);
+
+ while (bufchain_size(&hs->outputdata)) {
+ ptrlen data = bufchain_prefix(&hs->outputdata);
+ handle_write(send_h, data.ptr, data.len);
+ bufchain_consume(&hs->outputdata, data.len);
+ }
+
+ if (hs->output_eof_pending)
+ handle_write_eof(send_h);
+
+ bool start_frozen = hs->start_frozen;
+
+ deferred_socket_opener_free(hs->opener);
+ bufchain_clear(&hs->outputdata);
+
+ hs->sock.vt = &HandleSocket_sockvt;
+ hs->frozen = start_frozen ? FREEZING : UNFROZEN;
+ bufchain_init(&hs->inputdata);
+ psb_init(&hs->psb);
+
+ hs->recv_H = recv_H;
+ hs->recv_h = recv_h;
+ hs->send_H = send_H;
+ hs->send_h = send_h;
+ hs->stderr_H = stderr_H;
+ hs->stderr_h = stderr_h;
+
+ hs->defer_close = hs->deferred_close = false;
+
+ queue_toplevel_callback(sk_handle_connect_success_callback, hs);
+}
diff --git a/windows/handle-wait.c b/windows/handle-wait.c
new file mode 100644
index 00000000..9e4e522d
--- /dev/null
+++ b/windows/handle-wait.c
@@ -0,0 +1,143 @@
+/*
+ * handle-wait.c: Manage a collection of HANDLEs to wait for (in a
+ * WaitFor{Single,Multiple}Objects sense), each with a callback to be
+ * called when it's activated. Tracks the list, and provides an API to
+ * event loops that let them get a list of things to wait for and a
+ * way to call back to here when one of them does something.
+ */
+
+/*
+ * TODO: currently this system can't cope with more than
+ * MAXIMUM_WAIT_OBJECTS (= 64) handles at a time. It enforces that by
+ * assertion, so we'll at least find out if that assumption is ever
+ * violated.
+ *
+ * It should be OK for the moment. As of 2021-05-24, the only uses of
+ * this system are by the ConPTY backend (just once, to watch for its
+ * subprocess terminating); by Pageant (for the event that the
+ * WM_COPYDATA subthread uses to signal the main thread); and by
+ * named-pipe-server.c (once per named-pipe server, of which there is
+ * one in Pageant and one in connection-sharing upstreams). So the
+ * total number of handles has a pretty small upper bound.
+ *
+ * But sooner or later, I'm sure we'll find a reason why we really
+ * need to watch a squillion handles at once. When that happens, I
+ * can't see any alternative to setting up some kind of tree of
+ * subthreads in this module, each one condensing 64 of our handles
+ * into one, by doing its own WaitForMultipleObjects and setting an
+ * event object to indicate that one of them did something. It'll be
+ * horribly ugly.
+ */
+
+#include "putty.h"
+
+struct HandleWait {
+ HANDLE handle;
+ handle_wait_callback_fn_t callback;
+ void *callback_ctx;
+
+ int index; /* sort key for tree234 */
+};
+
+struct HandleWaitListInner {
+ HandleWait *hws[MAXIMUM_WAIT_OBJECTS];
+ HANDLE handles[MAXIMUM_WAIT_OBJECTS];
+
+ struct HandleWaitList hwl;
+};
+
+static int handlewait_cmp(void *av, void *bv)
+{
+ HandleWait *a = (HandleWait *)av, *b = (HandleWait *)bv;
+ if (a->index < b->index)
+ return -1;
+ if (a->index > b->index)
+ return +1;
+ return 0;
+}
+
+static tree234 *handlewaits_tree_real;
+
+static inline tree234 *ensure_handlewaits_tree_exists(void)
+{
+ if (!handlewaits_tree_real)
+ handlewaits_tree_real = newtree234(handlewait_cmp);
+ return handlewaits_tree_real;
+}
+
+static int allocate_index(void)
+{
+ tree234 *t = ensure_handlewaits_tree_exists();
+ search234_state st[1];
+
+ search234_start(st, t);
+ while (st->element) {
+ HandleWait *hw = (HandleWait *)st->element;
+ if (st->index < hw->index) {
+ /* There are unused index slots to the left of this element */
+ search234_step(st, -1);
+ } else {
+ assert(st->index == hw->index);
+ search234_step(st, +1);
+ }
+ }
+
+ return st->index;
+}
+
+HandleWait *add_handle_wait(HANDLE h, handle_wait_callback_fn_t callback,
+ void *callback_ctx)
+{
+ HandleWait *hw = snew(HandleWait);
+ hw->handle = h;
+ hw->callback = callback;
+ hw->callback_ctx = callback_ctx;
+
+ tree234 *t = ensure_handlewaits_tree_exists();
+ hw->index = allocate_index();
+ HandleWait *added = add234(t, hw);
+ assert(added == hw);
+
+ return hw;
+}
+
+void delete_handle_wait(HandleWait *hw)
+{
+ tree234 *t = ensure_handlewaits_tree_exists();
+ HandleWait *deleted = del234(t, hw);
+ assert(deleted == hw);
+ sfree(hw);
+}
+
+HandleWaitList *get_handle_wait_list(void)
+{
+ tree234 *t = ensure_handlewaits_tree_exists();
+ struct HandleWaitListInner *hwli = snew(struct HandleWaitListInner);
+ size_t n = 0;
+ HandleWait *hw;
+ for (int i = 0; (hw = index234(t, i)) != NULL; i++) {
+ assert(n < MAXIMUM_WAIT_OBJECTS);
+ hwli->hws[n] = hw;
+ hwli->hwl.handles[n] = hw->handle;
+ n++;
+ }
+ hwli->hwl.nhandles = n;
+ return &hwli->hwl;
+}
+
+void handle_wait_activate(HandleWaitList *hwl, int index)
+{
+ struct HandleWaitListInner *hwli =
+ container_of(hwl, struct HandleWaitListInner, hwl);
+ assert(0 <= index);
+ assert(index < hwli->hwl.nhandles);
+ HandleWait *hw = hwli->hws[index];
+ hw->callback(hw->callback_ctx);
+}
+
+void handle_wait_list_free(HandleWaitList *hwl)
+{
+ struct HandleWaitListInner *hwli =
+ container_of(hwl, struct HandleWaitListInner, hwl);
+ sfree(hwli);
+}
diff --git a/windows/winhelp.c b/windows/help.c
index df6ac37b..d087c722 100644
--- a/windows/winhelp.c
+++ b/windows/help.c
@@ -1,5 +1,5 @@
/*
- * winhelp.c: centralised functions to launch Windows HTML Help files.
+ * help.c: centralised functions to launch Windows HTML Help files.
*/
#include <stdio.h>
@@ -8,7 +8,7 @@
#include <assert.h>
#include "putty.h"
-#include "win_res.h"
+#include "putty-rc.h"
#ifdef NO_HTMLHELP
@@ -149,7 +149,7 @@ static bool find_chm_from_installation(void)
};
for (size_t i = 0; i < lenof(reg_paths); i++) {
- char *filename = registry_get_string(
+ char *filename = get_reg_sz_simple(
HKEY_LOCAL_MACHINE, reg_paths[i], NULL);
if (filename) {
diff --git a/windows/winhelp.h b/windows/help.h
index 9011df45..de6ec0be 100644
--- a/windows/winhelp.h
+++ b/windows/help.h
@@ -1,5 +1,5 @@
/*
- * winhelp.h - define Windows Help context names.
+ * help.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.
*/
@@ -9,7 +9,9 @@
/* These are used in the cross-platform configuration dialog code. */
-#define HELPCTX(x) P(WINHELP_CTX_ ## x)
+typedef const char *HelpCtx;
+#define NULL_HELPCTX NULL
+#define HELPCTX(x) WINHELP_CTX_ ## x
#define WINHELP_CTX_no_help NULL
@@ -26,6 +28,7 @@
#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_sharrow "config-sharrow"
#define WINHELP_CTX_keyboard_appkeypad "config-appkeypad"
#define WINHELP_CTX_keyboard_appcursor "config-appcursor"
#define WINHELP_CTX_keyboard_nethack "config-nethack"
@@ -110,10 +113,15 @@
#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_kex_cert "config-ssh-kex-cert"
+#define WINHELP_CTX_ssh_cert_valid_expr "config-ssh-cert-valid-expr"
+#define WINHELP_CTX_ssh_cert_rsa_hash "config-ssh-cert-rsa-hash"
#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_plugin "config-ssh-authplugin"
+#define WINHELP_CTX_ssh_auth_cert "config-ssh-cert"
#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"
@@ -162,6 +170,8 @@
#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_ssh_bugs_dropstart "config-ssh-bug-dropstart"
+#define WINHELP_CTX_ssh_bugs_filter_kexinit "config-ssh-bug-filter-kexinit"
#define WINHELP_CTX_serial_line "config-serial-line"
#define WINHELP_CTX_serial_speed "config-serial-speed"
#define WINHELP_CTX_serial_databits "config-serial-databits"
@@ -189,6 +199,7 @@
#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"
+#define WINHELP_CTX_errors_cert_mismatch "errors-cert-mismatch"
/* These are used in Windows-specific bits of the frontend.
* We (ab)use "help context identifiers" (dwContextId) to identify them. */
diff --git a/windows/help.rc2 b/windows/help.rc2
new file mode 100644
index 00000000..16bb41f0
--- /dev/null
+++ b/windows/help.rc2
@@ -0,0 +1,8 @@
+#include "putty-rc.h"
+
+#ifdef EMBEDDED_CHM_FILE
+ID_CUSTOM_CHMFILE TYPE_CUSTOM_CHMFILE EMBEDDED_CHM_FILE
+#define HELPVER " (with embedded help)"
+#else
+#define HELPVER " (without embedded help)"
+#endif
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/winjump.c b/windows/jump-list.c
index 358504fd..47c41a04 100644
--- a/windows/winjump.c
+++ b/windows/jump-list.c
@@ -1,5 +1,5 @@
/*
- * winjump.c: support for Windows 7 jump lists.
+ * jump-list.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
@@ -12,7 +12,7 @@
*
* 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
+ * list persistently 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.
*/
@@ -41,7 +41,7 @@ typedef struct _tagpropertykey {
typedef PROPVARIANT *REFPROPVARIANT;
#endif
/* MinGW doesn't define this yet: */
-#ifndef _PROPVARIANTINIT_DEFINED_
+#if !defined _PROPVARIANTINIT_DEFINED_ && !defined _PROPVARIANT_INIT_DEFINED_
#define _PROPVARIANTINIT_DEFINED_
#define PropVariantInit(pvar) memset((pvar),0,sizeof(PROPVARIANT))
#endif
@@ -544,13 +544,13 @@ static void update_jumplist_from_registry(void)
*/
for (i = 0, found = false; i < nremoved && !found; i++) {
IShellLink *rlink;
- if (SUCCEEDED(pRemoved->lpVtbl->GetAt
- (pRemoved, i, COMPTR(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)) &&
+ if (SUCCEEDED(link->lpVtbl->GetDescription(
+ link, desc1, sizeof(desc1)-1)) &&
+ SUCCEEDED(rlink->lpVtbl->GetDescription(
+ rlink, desc2, sizeof(desc2)-1)) &&
!strcmp(desc1, desc2)) {
found = true;
}
@@ -575,8 +575,8 @@ static void update_jumplist_from_registry(void)
* 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))))
+ if (!SUCCEEDED(collection->lpVtbl->QueryInterface(
+ collection, COMPTR(IObjectArray, &array))))
goto cleanup;
pCDL->lpVtbl->AppendCategory(pCDL, L"Recent Sessions", array);
@@ -608,8 +608,8 @@ static void update_jumplist_from_registry(void)
* 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))))
+ if (!SUCCEEDED(collection->lpVtbl->QueryInterface(
+ collection, COMPTR(IObjectArray, &array))))
goto cleanup;
pCDL->lpVtbl->AddUserTasks(pCDL, array);
@@ -636,8 +636,8 @@ static void update_jumplist_from_registry(void)
* 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))))
+ if (!SUCCEEDED(collection->lpVtbl->QueryInterface(
+ collection, COMPTR(IObjectArray, &array))))
goto cleanup;
pCDL->lpVtbl->AddUserTasks(pCDL, array);
@@ -716,13 +716,12 @@ void remove_session_from_jumplist(const char * const sessionname)
bool set_explicit_app_user_model_id(void)
{
- DECL_WINDOWS_FUNCTION(static, HRESULT, SetCurrentProcessExplicitAppUserModelID,
- (PCWSTR));
+ DECL_WINDOWS_FUNCTION(
+ static, HRESULT, SetCurrentProcessExplicitAppUserModelID, (PCWSTR));
- static HMODULE shell32_module = 0;
+ static HMODULE shell32_module = 0;
- if (!shell32_module)
- {
+ if (!shell32_module) {
shell32_module = load_system32_dll("Shell32.dll");
/*
* We can't typecheck this function here, because it's defined
@@ -733,11 +732,10 @@ bool set_explicit_app_user_model_id(void)
shell32_module, SetCurrentProcessExplicitAppUserModelID);
}
- if (p_SetCurrentProcessExplicitAppUserModelID)
- {
- if (p_SetCurrentProcessExplicitAppUserModelID(L"SimonTatham.PuTTY") == S_OK)
- {
- return true;
+ if (p_SetCurrentProcessExplicitAppUserModelID) {
+ const wchar_t *id = get_app_user_model_id();
+ if (p_SetCurrentProcessExplicitAppUserModelID(id) == S_OK) {
+ return true;
}
return false;
}
diff --git a/windows/winproxy.c b/windows/local-proxy.c
index 94e31fcb..55e9cbf3 100644
--- a/windows/winproxy.c
+++ b/windows/local-proxy.c
@@ -1,6 +1,6 @@
/*
- * winproxy.c: Windows implementation of platform_new_connection(),
- * supporting an OpenSSH-like proxy command via the winhandl.c
+ * local-proxy.c: Windows implementation of platform_new_connection(),
+ * supporting an OpenSSH-like proxy command via the handle-io.c
* mechanism.
*/
@@ -10,14 +10,10 @@
#include "tree234.h"
#include "putty.h"
#include "network.h"
-#include "proxy.h"
+#include "proxy/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 *platform_setup_local_proxy(Socket *socket, const char *cmd)
{
- 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;
@@ -25,20 +21,6 @@ Socket *platform_new_connection(SockAddr *addr, const char *hostname,
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.
@@ -47,30 +29,24 @@ Socket *platform_new_connection(SockAddr *addr, const char *hostname,
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()));
+ return dupprintf("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()));
+ return dupprintf("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()));
+ return dupprintf("Unable to create pipes for proxy command: %s",
+ win_strerror(GetLastError()));
}
SetHandleInformation(us_to_cmd, HANDLE_FLAG_INHERIT, 0);
@@ -88,20 +64,55 @@ Socket *platform_new_connection(SockAddr *addr, const char *hostname,
si.hStdInput = cmd_from_us;
si.hStdOutput = cmd_to_us;
si.hStdError = cmd_err_to_us;
- CreateProcess(NULL, cmd, NULL, NULL, true,
+ char *cmd_mutable = dupstr(cmd); /* CreateProcess needs non-const char * */
+ CreateProcess(NULL, cmd_mutable, NULL, NULL, true,
CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS,
NULL, NULL, &si, &pi);
+ sfree(cmd_mutable);
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);
+ setup_handle_socket(socket, us_to_cmd, us_from_cmd, us_from_cmd_err,
+ false);
+
+ return NULL;
+}
+
+Socket *platform_new_connection(SockAddr *addr, const char *hostname,
+ int port, bool privport,
+ bool oobinline, bool nodelay, bool keepalive,
+ Plug *plug, Conf *conf, Interactor *itr)
+{
+ if (conf_get_int(conf, CONF_proxy_type) != PROXY_CMD)
+ return NULL;
+
+ DeferredSocketOpener *opener = local_proxy_opener(
+ addr, port, plug, conf, itr);
+ Socket *socket = make_deferred_handle_socket(opener, addr, port, plug);
+ local_proxy_opener_set_socket(opener, socket);
+ return socket;
+}
+
+Socket *platform_start_subprocess(const char *cmd, Plug *plug,
+ const char *prefix)
+{
+ Socket *socket = make_deferred_handle_socket(
+ null_deferred_socket_opener(),
+ sk_nonamelookup("<local command>"), 0, plug);
+ char *err = platform_setup_local_proxy(socket, cmd);
+ handle_socket_set_psb_prefix(socket, prefix);
+
+ if (err) {
+ sk_close(socket);
+ socket = new_error_socket_fmt(plug, "%s", err);
+ sfree(err);
+ }
+
+ return socket;
}
diff --git a/windows/winnpc.c b/windows/named-pipe-client.c
index eabfb4bc..2ab6a309 100644
--- a/windows/winnpc.c
+++ b/windows/named-pipe-client.c
@@ -8,12 +8,10 @@
#include "tree234.h"
#include "putty.h"
#include "network.h"
-#include "proxy.h"
+#include "proxy/proxy.h"
#include "ssh.h"
-#if !defined NO_SECURITY
-
-#include "winsecur.h"
+#include "security-api.h"
HANDLE connect_to_named_pipe(const char *pipename, char **err)
{
@@ -40,11 +38,11 @@ HANDLE connect_to_named_pipe(const char *pipename, char **err)
}
/*
- * 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 we got ERROR_PIPE_BUSY, wait for the server to create a
+ * new pipe instance. (Since the server is expected to be
+ * named-pipe-server.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(
@@ -92,7 +90,6 @@ Socket *new_named_pipe_client(const char *pipename, Plug *plug)
if (pipehandle == INVALID_HANDLE_VALUE)
return new_error_socket_consume_string(plug, err);
else
- return make_handle_socket(pipehandle, pipehandle, NULL, plug, true);
+ return make_handle_socket(pipehandle, pipehandle, NULL, NULL, 0,
+ plug, true);
}
-
-#endif /* !defined NO_SECURITY */
diff --git a/windows/winnps.c b/windows/named-pipe-server.c
index 1757cdbb..87adb940 100644
--- a/windows/winnps.c
+++ b/windows/named-pipe-server.c
@@ -8,12 +8,10 @@
#include "tree234.h"
#include "putty.h"
#include "network.h"
-#include "proxy.h"
+#include "proxy/proxy.h"
#include "ssh.h"
-#if !defined NO_SECURITY
-
-#include "winsecur.h"
+#include "security-api.h"
typedef struct NamedPipeServerSocket {
/* Parameters for (repeated) creation of named pipe objects */
@@ -24,7 +22,7 @@ typedef struct NamedPipeServerSocket {
/* The current named pipe object + attempt to connect to it */
HANDLE pipehandle;
OVERLAPPED connect_ovl;
- struct handle *callback_handle; /* winhandl.c's reference */
+ HandleWait *callback_handle; /* handle-wait.c's reference */
/* PuTTY Socket machinery */
Plug *plug;
@@ -47,7 +45,7 @@ static void sk_namedpipeserver_close(Socket *s)
NamedPipeServerSocket *ps = container_of(s, NamedPipeServerSocket, sock);
if (ps->callback_handle)
- handle_free(ps->callback_handle);
+ delete_handle_wait(ps->callback_handle);
CloseHandle(ps->pipehandle);
CloseHandle(ps->connect_ovl.hEvent);
sfree(ps->error);
@@ -79,33 +77,33 @@ static bool create_named_pipe(NamedPipeServerSocket *ps, bool first_instance)
sa.lpSecurityDescriptor = ps->psd;
sa.bInheritHandle = false;
- ps->pipehandle = CreateNamedPipe
- (/* lpName */
- ps->pipename,
+ ps->pipehandle = CreateNamedPipe(
+ /* lpName */
+ ps->pipename,
- /* dwOpenMode */
- PIPE_ACCESS_DUPLEX |
- FILE_FLAG_OVERLAPPED |
- (first_instance ? FILE_FLAG_FIRST_PIPE_INSTANCE : 0),
+ /* dwOpenMode */
+ PIPE_ACCESS_DUPLEX |
+ FILE_FLAG_OVERLAPPED |
+ (first_instance ? FILE_FLAG_FIRST_PIPE_INSTANCE : 0),
- /* dwPipeMode */
- PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT
+ /* dwPipeMode */
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT
#ifdef PIPE_REJECT_REMOTE_CLIENTS
- | PIPE_REJECT_REMOTE_CLIENTS
+ | PIPE_REJECT_REMOTE_CLIENTS
#endif
- ,
+ ,
- /* nMaxInstances */
- PIPE_UNLIMITED_INSTANCES,
+ /* nMaxInstances */
+ PIPE_UNLIMITED_INSTANCES,
- /* nOutBufferSize, nInBufferSize */
- 4096, 4096, /* FIXME: think harder about buffer sizes? */
+ /* nOutBufferSize, nInBufferSize */
+ 4096, 4096, /* FIXME: think harder about buffer sizes? */
- /* nDefaultTimeOut */
- 0 /* default timeout */,
+ /* nDefaultTimeOut */
+ 0 /* default timeout */,
- /* lpSecurityAttributes */
- &sa);
+ /* lpSecurityAttributes */
+ &sa);
return ps->pipehandle != INVALID_HANDLE_VALUE;
}
@@ -114,7 +112,7 @@ 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);
+ return make_handle_socket(conn, conn, NULL, NULL, 0, plug, true);
}
static void named_pipe_accept_loop(NamedPipeServerSocket *ps,
@@ -228,13 +226,10 @@ Socket *new_named_pipe_listener(const char *pipename, Plug *plug)
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);
+ ret->callback_handle = add_handle_wait(
+ 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/winnet.c b/windows/network.c
index 3b4da3cc..2c087b69 100644
--- a/windows/winnet.c
+++ b/windows/network.c
@@ -20,6 +20,10 @@
#include <ws2tcpip.h>
+#if HAVE_AFUNIX_H
+#include <afunix.h>
+#endif
+
#ifndef NO_IPV6
#ifdef __clang__
#pragma clang diagnostic push
@@ -79,12 +83,28 @@ struct NetSocket {
Socket sock;
};
+/*
+ * Top-level discriminator for SockAddr.
+ *
+ * UNRESOLVED means a host name not yet put through DNS; IP means a
+ * resolved IP address (or list of them); UNIX indicates the AF_UNIX
+ * network family (which Windows also has); NAMEDPIPE indicates that
+ * this SockAddr is phony, holding a Windows named pipe pathname
+ * instead of any address WinSock can understand.
+ */
+typedef enum SuperFamily {
+ UNRESOLVED,
+ IP,
+#if HAVE_AFUNIX_H
+ UNIX,
+#endif
+ NAMEDPIPE
+} SuperFamily;
+
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 */
+ const char *error;
+ SuperFamily superfamily;
#ifndef NO_IPV6
struct addrinfo *ais; /* Addresses IPv6 style. */
#endif
@@ -95,18 +115,27 @@ struct SockAddr {
/*
* 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.
+ * AF_INET6 for IPv6; AF_UNIX for Unix-domain sockets; AF_UNSPEC
+ * indicates that name resolution has not been done and a simple host
+ * name is held in this SockAddr structure.
*/
+static inline int sockaddr_family(SockAddr *addr, SockAddrStep step)
+{
+ switch (addr->superfamily) {
+ case IP:
#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)
+ if (step.ai)
+ return step.ai->ai_family;
+#endif
+ return AF_INET;
+#if HAVE_AFUNIX_H
+ case UNIX:
+ return AF_UNIX;
#endif
+ default:
+ return AF_UNSPEC;
+ }
+}
/*
* Start a SockAddrStep structure to step through multiple
@@ -151,16 +180,16 @@ static int cmpforsearch(void *av, void *bv)
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, ULONG, ntohl, (ULONG));
+DECL_WINDOWS_FUNCTION(static, ULONG, htonl, (ULONG));
+DECL_WINDOWS_FUNCTION(static, USHORT, htons, (USHORT));
+DECL_WINDOWS_FUNCTION(static, USHORT, ntohs, (USHORT));
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, ULONG, 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));
@@ -175,7 +204,7 @@ 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 *));
+ (SOCKET, LONG, ULONG FAR *));
DECL_WINDOWS_FUNCTION(static, SOCKET, accept,
(SOCKET, struct sockaddr FAR *, int FAR *));
DECL_WINDOWS_FUNCTION(static, int, getpeername,
@@ -191,10 +220,9 @@ DECL_WINDOWS_FUNCTION(static, int, getaddrinfo,
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,
+ (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));
@@ -251,7 +279,6 @@ void sk_init(void)
/* 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");
@@ -260,7 +287,6 @@ void sk_init(void)
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 {
}
}
@@ -279,10 +305,12 @@ void sk_init(void)
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);
+ /* Winelib maps ntohl and friends to things like
+ * __wine_ulong_swap, which fail these type checks hopelessly */
+ GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, ntohl);
+ GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, htonl);
+ GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, htons);
+ GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, ntohs);
GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, gethostname);
GET_WINDOWS_FUNCTION(winsock_module, gethostbyname);
GET_WINDOWS_FUNCTION(winsock_module, getservbyname);
@@ -426,157 +454,129 @@ const char *winsock_error_string(int error)
return win_strerror(error);
}
+static inline const char *namelookup_strerror(DWORD err)
+{
+ /* PuTTY has traditionally translated a few of the likely error
+ * messages into more concise strings than the standard Windows ones */
+ return (err == WSAENETDOWN ? "Network is down" :
+ err == WSAHOST_NOT_FOUND ? "Host does not exist" :
+ err == WSATRY_AGAIN ? "Host not found" :
+ win_strerror(err));
+}
+
SockAddr *sk_namelookup(const char *host, char **canonicalname,
int address_family)
{
- SockAddr *ret = snew(SockAddr);
- unsigned long a;
- char realhost[8192];
- int hint_family;
+ *canonicalname = NULL;
- /* 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';
+ SockAddr *addr = snew(SockAddr);
+ memset(addr, 0, sizeof(SockAddr));
+ addr->superfamily = UNRESOLVED;
+ addr->refcount = 1;
- 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;
+ /*
+ * Use getaddrinfo, as long as it's available. This should handle
+ * both IPv4 and IPv6 address literals, and hostnames, in one
+ * unified API.
+ */
+ if (p_getaddrinfo) {
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = (address_family == ADDRTYPE_IPV4 ? AF_INET :
+ address_family == ADDRTYPE_IPV6 ? AF_INET6 :
+ AF_UNSPEC);
+ hints.ai_flags = AI_CANONNAME;
+ hints.ai_socktype = SOCK_STREAM;
+
+ /* strip [] on IPv6 address literals */
+ char *trimmed_host = host_strduptrim(host);
+ int err = p_getaddrinfo(trimmed_host, NULL, &hints, &addr->ais);
+ sfree(trimmed_host);
+
+ if (addr->ais) {
+ addr->superfamily = IP;
+ if (addr->ais->ai_canonname)
+ *canonicalname = dupstr(addr->ais->ai_canonname);
else
- err = p_WSAGetLastError();
+ *canonicalname = dupstr(host);
+ } else {
+ addr->error = namelookup_strerror(err);
}
-
- 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) :
+ return addr;
+ }
#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));
- }
+ /*
+ * Failing that (if IPv6 support was not compiled in, or if
+ * getaddrinfo turned out to be unavailable at run time), try the
+ * old-fashioned approach, which is to start by manually checking
+ * for an IPv4 literal and then use gethostbyname.
+ */
+ unsigned long a = p_inet_addr(host);
+ if (a != (unsigned long) INADDR_NONE) {
+ addr->addresses = snew(unsigned long);
+ addr->naddresses = 1;
+ addr->addresses[0] = p_ntohl(a);
+ addr->superfamily = IP;
+ *canonicalname = dupstr(host);
+ return addr;
+ }
+
+ struct hostent *h = p_gethostbyname(host);
+ if (h) {
+ addr->superfamily = IP;
+
+ size_t n;
+ for (n = 0; h->h_addr_list[n]; n++);
+ addr->addresses = snewn(n, unsigned long);
+ addr->naddresses = n;
+ for (n = 0; n < addr->naddresses; n++) {
+ uint32_t a;
+ memcpy(&a, h->h_addr_list[n], sizeof(a));
+ addr->addresses[n] = p_ntohl(a);
}
+
+ *canonicalname = dupstr(h->h_name);
} 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));
+ DWORD err = p_WSAGetLastError();
+ addr->error = namelookup_strerror(err);
}
- realhost[lenof(realhost)-1] = '\0';
- *canonicalname = dupstr(realhost);
- return ret;
+ return addr;
}
-SockAddr *sk_nonamelookup(const char *host)
+static SockAddr *sk_special_addr(SuperFamily superfamily, const char *name)
{
SockAddr *ret = snew(SockAddr);
ret->error = NULL;
- ret->resolved = false;
+ ret->superfamily = superfamily;
#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));
+ strncpy(ret->hostname, name, lenof(ret->hostname));
ret->hostname[lenof(ret->hostname)-1] = '\0';
return ret;
}
+SockAddr *sk_nonamelookup(const char *host)
+{
+ return sk_special_addr(UNRESOLVED, host);
+}
+
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;
+ return sk_special_addr(NAMEDPIPE, pipename);
+}
+
+#if HAVE_AFUNIX_H
+SockAddr *sk_unix_addr(const char *sockpath)
+{
+ return sk_special_addr(UNIX, sockpath);
}
+#endif
static bool sk_nextaddr(SockAddr *addr, SockAddrStep *step)
{
@@ -619,7 +619,7 @@ void sk_getaddr(SockAddr *addr, char *buf, int buflen)
}
} else
#endif
- if (SOCKADDR_FAMILY(addr, step) == AF_INET) {
+ 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]);
@@ -650,7 +650,7 @@ static SockAddr sk_extractaddr_tmp(
#ifndef NO_IPV6
toret.ais = step->ai;
#endif
- if (SOCKADDR_FAMILY(addr, *step) == AF_INET
+ if (sockaddr_family(addr, *step) == AF_INET
#ifndef NO_IPV6
&& !toret.ais
#endif
@@ -662,7 +662,11 @@ static SockAddr sk_extractaddr_tmp(
bool sk_addr_needs_port(SockAddr *addr)
{
- return !addr->namedpipe;
+ return addr->superfamily != NAMEDPIPE
+#if HAVE_AFUNIX_H
+ && addr->superfamily != UNIX
+#endif
+ ;
}
bool sk_hostname_is_local(const char *name)
@@ -710,7 +714,7 @@ bool sk_address_is_local(SockAddr *addr)
SockAddrStep step;
int family;
START_STEP(addr, step);
- family = SOCKADDR_FAMILY(addr, step);
+ family = sockaddr_family(addr, step);
#ifndef NO_IPV6
if (family == AF_INET6) {
@@ -746,7 +750,7 @@ int sk_addrtype(SockAddr *addr)
SockAddrStep step;
int family;
START_STEP(addr, step);
- family = SOCKADDR_FAMILY(addr, step);
+ family = sockaddr_family(addr, step);
return (family == AF_INET ? ADDRTYPE_IPV4 :
#ifndef NO_IPV6
@@ -760,7 +764,7 @@ void sk_addrcopy(SockAddr *addr, char *buf)
SockAddrStep step;
int family;
START_STEP(addr, step);
- family = SOCKADDR_FAMILY(addr, step);
+ family = sockaddr_family(addr, step);
assert(family != AF_UNSPEC);
#ifndef NO_IPV6
@@ -904,7 +908,7 @@ static DWORD try_connect(NetSocket *sock)
/*
* Open socket.
*/
- family = SOCKADDR_FAMILY(sock->addr, sock->step);
+ family = sockaddr_family(sock->addr, sock->step);
/*
* Remove the socket from the tree before we overwrite its
@@ -923,7 +927,7 @@ static DWORD try_connect(NetSocket *sock)
goto ret;
}
- SetHandleInformation((HANDLE)s, HANDLE_FLAG_INHERIT, 0);
+ SetHandleInformation((HANDLE)s, HANDLE_FLAG_INHERIT, 0);
if (sock->oobinline) {
BOOL b = true;
@@ -1064,7 +1068,7 @@ static DWORD try_connect(NetSocket *sock)
err = 0;
- ret:
+ ret:
/*
* No matter what happened, put the socket back in the tree.
@@ -1120,21 +1124,27 @@ Socket *sk_new(SockAddr *addr, int port, bool privport, bool oobinline,
return &ret->sock;
}
-Socket *sk_newlistener(const char *srcaddr, int port, Plug *plug,
- bool local_host_only, int orig_address_family)
+static Socket *sk_newlistener_internal(
+ const char *srcaddr, int port, Plug *plug,
+ bool local_host_only, int orig_address_family)
{
SOCKET s;
+ SOCKADDR_IN a;
#ifndef NO_IPV6
SOCKADDR_IN6 a6;
#endif
- SOCKADDR_IN a;
+#if HAVE_AFUNIX_H
+ SOCKADDR_UN au;
+#endif
+ struct sockaddr *bindaddr;
+ unsigned bindsize;
DWORD err;
const char *errstr;
NetSocket *ret;
int retcode;
- int address_family;
+ int address_family = orig_address_family;
/*
* Create NetSocket structure.
@@ -1155,16 +1165,6 @@ Socket *sk_newlistener(const char *srcaddr, int port, Plug *plug,
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
@@ -1189,85 +1189,100 @@ Socket *sk_newlistener(const char *srcaddr, int port, Plug *plug,
ret->oobinline = false;
+#if HAVE_AFUNIX_H
+ if (address_family != AF_UNIX)
+#endif
{
BOOL on = true;
p_setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
(const char *)&on, sizeof(on));
}
+ switch (address_family) {
#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;
+ case 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;
- /*
- * 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;
- }
+ 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);
+ bindaddr = (struct sockaddr *)&a6;
+ bindsize = sizeof(a6);
+ break;
+ }
+#endif
+ case AF_INET: {
+ bool got_addr = false;
+ a.sin_family = AF_INET;
- /*
- * ... 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);
+ /*
+ * 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;
}
+ }
- a.sin_port = p_htons((short)port);
+ /*
+ * ... 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);
}
-#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));
+
+ a.sin_port = p_htons((short)port);
+ bindaddr = (struct sockaddr *)&a;
+ bindsize = sizeof(a);
+ break;
+ }
+#if HAVE_AFUNIX_H
+ case AF_UNIX: {
+ au.sun_family = AF_UNIX;
+ strncpy(au.sun_path, srcaddr, sizeof(au.sun_path));
+ bindaddr = (struct sockaddr *)&au;
+ bindsize = sizeof(au);
+ break;
+ }
#endif
- if (retcode != SOCKET_ERROR) {
- err = 0;
- } else {
- err = p_WSAGetLastError();
- }
+ default:
+ unreachable("bad address family in sk_newlistener_internal");
+ }
+
+ retcode = p_bind(s, bindaddr, bindsize);
+ if (retcode != SOCKET_ERROR) {
+ err = 0;
+ } else {
+ err = p_WSAGetLastError();
+ }
if (err) {
p_closesocket(s);
@@ -1298,9 +1313,9 @@ Socket *sk_newlistener(const char *srcaddr, int port, Plug *plug,
* 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 (address_family == AF_INET && orig_address_family == AF_UNSPEC) {
+ Socket *other = sk_newlistener_internal(srcaddr, port, plug,
+ local_host_only, AF_INET6);
if (other) {
NetSocket *ns = container_of(other, NetSocket, sock);
@@ -1317,6 +1332,33 @@ Socket *sk_newlistener(const char *srcaddr, int port, Plug *plug,
return &ret->sock;
}
+Socket *sk_newlistener(const char *srcaddr, int port, Plug *plug,
+ bool local_host_only, int orig_address_family)
+{
+ /*
+ * Translate address_family from platform-independent constants
+ * into local reality.
+ */
+ int address_family = (orig_address_family == ADDRTYPE_IPV4 ? AF_INET :
+#ifndef NO_IPV6
+ orig_address_family == ADDRTYPE_IPV6 ? AF_INET6 :
+#endif
+ AF_UNSPEC);
+
+ return sk_newlistener_internal(srcaddr, port, plug, local_host_only,
+ address_family);
+}
+
+Socket *sk_newlistener_unix(const char *path, Plug *plug)
+{
+#if HAVE_AFUNIX_H
+ return sk_newlistener_internal(path, 0, plug, false, AF_UNIX);
+#else
+ return new_error_socket_fmt(
+ plug, "AF_UNIX support not compiled into this program");
+#endif
+}
+
static void sk_net_close(Socket *sock)
{
NetSocket *s = container_of(sock, NetSocket, sock);
@@ -1335,6 +1377,19 @@ static void sk_net_close(Socket *sock)
sfree(s);
}
+void plug_closing_system_error(Plug *plug, DWORD error)
+{
+ PlugCloseType type = PLUGCLOSE_ERROR;
+ if (error == ERROR_BROKEN_PIPE)
+ type = PLUGCLOSE_BROKEN_PIPE;
+ plug_closing(plug, type, win_strerror(error));
+}
+
+void plug_closing_winsock_error(Plug *plug, DWORD error)
+{
+ plug_closing(plug, PLUGCLOSE_ERROR, winsock_error_string(error));
+}
+
/*
* Deal with socket errors detected in try_send().
*/
@@ -1352,15 +1407,14 @@ static void socket_error_callback(void *vs)
/*
* 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);
+ plug_closing_winsock_error(s->plug, s->pending_error);
}
/*
* The function which tries to send on a socket once it's deemed
* writable.
*/
-void try_send(NetSocket *s)
+static void try_send(NetSocket *s)
{
while (s->sending_oob || bufchain_size(&s->output_data) > 0) {
int nsent;
@@ -1528,7 +1582,7 @@ void select_result(WPARAM wParam, LPARAM lParam)
}
}
if (err != 0)
- plug_closing(s->plug, winsock_error_string(err), err, 0);
+ plug_closing_winsock_error(s->plug, err);
return;
}
@@ -1590,9 +1644,9 @@ void select_result(WPARAM wParam, LPARAM lParam)
}
}
if (ret < 0) {
- plug_closing(s->plug, winsock_error_string(err), err, 0);
+ plug_closing_winsock_error(s->plug, err);
} else if (0 == ret) {
- plug_closing(s->plug, NULL, 0, 0);
+ plug_closing_normal(s->plug);
} else {
plug_receive(s->plug, atmark ? 0 : 1, buf, ret);
}
@@ -1608,7 +1662,7 @@ void select_result(WPARAM wParam, LPARAM lParam)
noise_ultralight(NOISE_SOURCE_IOLEN, ret);
if (ret <= 0) {
int err = p_WSAGetLastError();
- plug_closing(s->plug, winsock_error_string(err), err, 0);
+ plug_closing_winsock_error(s->plug, err);
} else {
plug_receive(s->plug, 2, buf, ret);
}
@@ -1631,12 +1685,12 @@ void select_result(WPARAM wParam, LPARAM lParam)
err = p_WSAGetLastError();
if (err == WSAEWOULDBLOCK)
break;
- plug_closing(s->plug, winsock_error_string(err), err, 0);
+ plug_closing_winsock_error(s->plug, err);
} else {
if (ret)
plug_receive(s->plug, 0, buf, ret);
else
- plug_closing(s->plug, NULL, 0, 0);
+ plug_closing_normal(s->plug);
}
} while (ret > 0);
return;
@@ -1644,7 +1698,7 @@ void select_result(WPARAM wParam, LPARAM lParam)
#ifdef NO_IPV6
struct sockaddr_in isa;
#else
- struct sockaddr_storage isa;
+ struct sockaddr_storage isa; // FIXME: also if Unix and no IPv6
#endif
int addrlen = sizeof(isa);
SOCKET t; /* socket of connection */
@@ -1655,9 +1709,9 @@ void select_result(WPARAM wParam, LPARAM lParam)
t = p_accept(s->s,(struct sockaddr *)&isa,&addrlen);
if (t == INVALID_SOCKET)
{
- err = p_WSAGetLastError();
- if (err == WSATRY_AGAIN)
- break;
+ err = p_WSAGetLastError();
+ if (err == WSATRY_AGAIN)
+ break;
}
actx.p = (void *)t;
@@ -1670,9 +1724,9 @@ void select_result(WPARAM wParam, LPARAM lParam)
if (s->localhost_only && !ipv4_is_local_addr(isa.sin_addr))
#endif
{
- p_closesocket(t); /* dodgy WinSock let nonlocal through */
+ p_closesocket(t); /* dodgy WinSock let nonlocal through */
} else if (plug_accepting(s->plug, sk_net_accept, actx)) {
- p_closesocket(t); /* denied or error */
+ p_closesocket(t); /* denied or error */
}
break;
}
@@ -1700,7 +1754,7 @@ static SocketPeerInfo *sk_net_peer_info(Socket *sock)
#ifdef NO_IPV6
struct sockaddr_in addr;
#else
- struct sockaddr_storage addr;
+ struct sockaddr_storage addr; // FIXME: also if Unix and no IPv6
char buf[INET6_ADDRSTRLEN];
#endif
int addrlen = sizeof(addr);
@@ -1797,7 +1851,7 @@ bool socket_writable(SOCKET skt)
return false;
}
-int net_service_lookup(char *service)
+int net_service_lookup(const char *service)
{
struct servent *se;
se = p_getservbyname(service, NULL);
@@ -1819,7 +1873,7 @@ 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->error = "unix sockets for X11 not supported on this platform";
ret->refcount = 1;
return ret;
}
diff --git a/windows/winnojmp.c b/windows/no-jump-list.c
index dd61dc69..beabd107 100644
--- a/windows/winnojmp.c
+++ b/windows/no-jump-list.c
@@ -1,8 +1,10 @@
/*
- * winnojmp.c: stub jump list functions for Windows executables that
- * don't update the jump list.
+ * no-jump-list.c: stub jump list functions for Windows executables
+ * that don't update the jump list.
*/
+#include "putty.h"
+
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/winnohlp.c b/windows/nohelp.c
index 62ddc65c..1b748776 100644
--- a/windows/winnohlp.c
+++ b/windows/nohelp.c
@@ -1,6 +1,6 @@
/*
* nohelp.c: implement the has_embedded_chm() function for
- * applications that have no help file at all, so that misc.c's
+ * applications that have no help file at all, so that buildinfo.c's
* buildinfo string knows not to talk meaninglessly about whether the
* nonexistent help file is present.
*/
diff --git a/windows/winnoise.c b/windows/noise.c
index 65c4c92d..65c4c92d 100644
--- a/windows/winnoise.c
+++ b/windows/noise.c
diff --git a/windows/winpgnt.c b/windows/pageant.c
index d6e960b6..d1368903 100644
--- a/windows/winpgnt.c
+++ b/windows/pageant.c
@@ -13,27 +13,23 @@
#include "ssh.h"
#include "misc.h"
#include "tree234.h"
-#include "winsecur.h"
-#include "wincapi.h"
+#include "security-api.h"
+#include "cryptoapi.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
@@ -95,8 +91,6 @@ void modalfatalbox(const char *fmt, ...)
exit(1);
}
-static bool has_security;
-
struct PassphraseProcStruct {
bool modal;
const char *help_topic;
@@ -109,7 +103,7 @@ struct PassphraseProcStruct {
* Dialog-box function for the Licence box.
*/
static INT_PTR CALLBACK LicenceProc(HWND hwnd, UINT msg,
- WPARAM wParam, LPARAM lParam)
+ WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_INITDIALOG:
@@ -134,15 +128,15 @@ static INT_PTR CALLBACK LicenceProc(HWND hwnd, UINT msg,
* Dialog-box function for the About box.
*/
static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg,
- WPARAM wParam, LPARAM lParam)
+ 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.");
+ 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);
@@ -191,7 +185,8 @@ static void end_passphrase_dialog(HWND hwnd, INT_PTR result)
} else {
/*
* Destroy this passphrase dialog box before passing the
- * results back to pageant.c, to avoid re-entrancy issues.
+ * results back to the main 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
@@ -219,7 +214,7 @@ static void end_passphrase_dialog(HWND hwnd, INT_PTR result)
* Dialog-box function for the passphrase box.
*/
static INT_PTR CALLBACK PassphraseProc(HWND hwnd, UINT msg,
- WPARAM wParam, LPARAM lParam)
+ WPARAM wParam, LPARAM lParam)
{
struct PassphraseProcStruct *p;
@@ -318,18 +313,30 @@ void old_keyfile_warning(void)
}
struct keylist_update_ctx {
+ HDC hdc;
+ int algbitswidth, algwidth, bitswidth, hashwidth;
bool enable_remove_controls;
bool enable_reencrypt_controls;
};
+struct keylist_display_data {
+ strbuf *alg, *bits, *hash, *comment, *info;
+};
+
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();
+ ptrlen fingerprint = ptrlen_from_asciz(fingerprints[this_type]);
+
+ struct keylist_display_data *disp = snew(struct keylist_display_data);
+ disp->alg = strbuf_new();
+ disp->bits = strbuf_new();
+ disp->hash = strbuf_new();
+ disp->comment = strbuf_new();
+ disp->info = strbuf_new();
/* There is at least one key, so the controls for removing keys
* should be enabled */
@@ -337,86 +344,103 @@ static void keylist_update_callback(
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.
+ * Expect the fingerprint to contain two words: bit count and
+ * hash.
*/
- char *p = strchr(listentry->s, ' ');
- if (p)
- *p = '\t';
+ put_dataz(disp->alg, "SSH-1");
+ put_datapl(disp->bits, ptrlen_get_word(&fingerprint, " "));
+ put_datapl(disp->hash, ptrlen_get_word(&fingerprint, " "));
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.
+ * Expect the fingerprint to contain three words: algorithm
+ * name, bit count, hash.
*/
- 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);
+ const ssh_keyalg *alg = pubkey_blob_to_alg(
+ ptrlen_from_strbuf(key->blob));
+
+ ptrlen keytype_word = ptrlen_get_word(&fingerprint, " ");
+ if (alg) {
+ /* Use our own human-legible algorithm names if available,
+ * because they fit better in the space. (Certificate key
+ * algorithm names in particular are terribly long.) */
+ char *alg_desc = ssh_keyalg_desc(alg);
+ put_dataz(disp->alg, alg_desc);
+ sfree(alg_desc);
+ } else {
+ put_datapl(disp->alg, keytype_word);
}
- strbuf_catf(listentry, "\t%s", comment);
- break;
+ ptrlen bits_word = ptrlen_get_word(&fingerprint, " ");
+ if (alg && ssh_keyalg_variable_size(alg))
+ put_datapl(disp->bits, bits_word);
+
+ put_datapl(disp->hash, ptrlen_get_word(&fingerprint, " "));
}
}
+ put_dataz(disp->comment, comment);
+
+ SIZE sz;
+ if (disp->bits->len) {
+ GetTextExtentPoint32(ctx->hdc, disp->alg->s, disp->alg->len, &sz);
+ if (ctx->algwidth < sz.cx) ctx->algwidth = sz.cx;
+ GetTextExtentPoint32(ctx->hdc, disp->bits->s, disp->bits->len, &sz);
+ if (ctx->bitswidth < sz.cx) ctx->bitswidth = sz.cx;
+ } else {
+ GetTextExtentPoint32(ctx->hdc, disp->alg->s, disp->alg->len, &sz);
+ if (ctx->algbitswidth < sz.cx) ctx->algbitswidth = sz.cx;
+ }
+ GetTextExtentPoint32(ctx->hdc, disp->hash->s, disp->hash->len, &sz);
+ if (ctx->hashwidth < sz.cx) ctx->hashwidth = sz.cx;
+
if (ext_flags & LIST_EXTENDED_FLAG_HAS_NO_CLEARTEXT_KEY) {
- strbuf_catf(listentry, "\t(encrypted)");
+ put_fmt(disp->info, "(encrypted)");
} else if (ext_flags & LIST_EXTENDED_FLAG_HAS_ENCRYPTED_KEY_FILE) {
- strbuf_catf(listentry, "\t(re-encryptable)");
+ put_fmt(disp->info, "(re-encryptable)");
/* At least one key can be re-encrypted */
ctx->enable_reencrypt_controls = true;
}
+ /* This list box is owner-drawn but doesn't have LBS_HASSTRINGS,
+ * so we can use LB_ADDSTRING to hand the list box our display
+ * info pointer */
SendDlgItemMessage(keylist, IDC_KEYLIST_LISTBOX,
- LB_ADDSTRING, 0, (LPARAM)listentry->s);
- strbuf_free(listentry);
+ LB_ADDSTRING, 0, (LPARAM)disp);
}
+/* Column start positions for the list box, in pixels (not dialog units). */
+static int colpos_bits, colpos_hash, colpos_comment;
+
/*
* Update the visible key list.
*/
void keylist_update(void)
{
if (keylist) {
+ /*
+ * Clear the previous list box content and free their display
+ * structures.
+ */
+ {
+ int nitems = SendDlgItemMessage(keylist, IDC_KEYLIST_LISTBOX,
+ LB_GETCOUNT, 0, 0);
+ for (int i = 0; i < nitems; i++) {
+ struct keylist_display_data *disp =
+ (struct keylist_display_data *)SendDlgItemMessage(
+ keylist, IDC_KEYLIST_LISTBOX, LB_GETITEMDATA, i, 0);
+ strbuf_free(disp->alg);
+ strbuf_free(disp->bits);
+ strbuf_free(disp->hash);
+ strbuf_free(disp->comment);
+ strbuf_free(disp->info);
+ sfree(disp);
+ }
+ }
SendDlgItemMessage(keylist, IDC_KEYLIST_LISTBOX,
LB_RESETCONTENT, 0, 0);
@@ -424,7 +448,22 @@ void keylist_update(void)
struct keylist_update_ctx ctx[1];
ctx->enable_remove_controls = false;
ctx->enable_reencrypt_controls = false;
+ ctx->algbitswidth = ctx->algwidth = 0;
+ ctx->bitswidth = ctx->hashwidth = 0;
+ ctx->hdc = GetDC(keylist);
+ SelectObject(ctx->hdc, (HFONT)SendMessage(keylist, WM_GETFONT, 0, 0));
int status = pageant_enum_keys(keylist_update_callback, ctx, &errmsg);
+
+ SIZE sz;
+ GetTextExtentPoint32(ctx->hdc, "MM", 2, &sz);
+ int gutter = sz.cx;
+
+ DeleteDC(ctx->hdc);
+ colpos_hash = ctx->algwidth + ctx->bitswidth + 2*gutter;
+ if (colpos_hash < ctx->algbitswidth + gutter)
+ colpos_hash = ctx->algbitswidth + gutter;
+ colpos_bits = colpos_hash - ctx->bitswidth - gutter;
+ colpos_comment = colpos_hash + ctx->hashwidth + gutter;
assert(status == PAGEANT_ACTION_OK);
assert(!errmsg);
@@ -553,7 +592,7 @@ static void prompt_add_keyfile(bool encrypted)
* Dialog-box function for the key list box.
*/
static INT_PTR CALLBACK KeyListProc(HWND hwnd, UINT msg,
- WPARAM wParam, LPARAM lParam)
+ WPARAM wParam, LPARAM lParam)
{
static const struct {
const char *name;
@@ -561,6 +600,8 @@ static INT_PTR CALLBACK KeyListProc(HWND hwnd, UINT msg,
} fptypes[] = {
{"SHA256", SSH_FPTYPE_SHA256},
{"MD5", SSH_FPTYPE_MD5},
+ {"SHA256 including certificate", SSH_FPTYPE_SHA256_CERT},
+ {"MD5 including certificate", SSH_FPTYPE_MD5_CERT},
};
switch (msg) {
@@ -583,18 +624,12 @@ static INT_PTR CALLBACK KeyListProc(HWND hwnd, UINT msg,
GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
WS_EX_CONTEXTHELP);
else {
- HWND item = GetDlgItem(hwnd, IDC_KEYLIST_HELP);
- if (item)
- DestroyWindow(item);
+ 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++) {
@@ -609,6 +644,96 @@ static INT_PTR CALLBACK KeyListProc(HWND hwnd, UINT msg,
keylist_update();
return 0;
}
+ case WM_MEASUREITEM: {
+ assert(wParam == IDC_KEYLIST_LISTBOX);
+
+ MEASUREITEMSTRUCT *mi = (MEASUREITEMSTRUCT *)lParam;
+
+ /*
+ * Our list box is owner-drawn, but we put normal text in it.
+ * So the line height is the same as it would normally be,
+ * which is 8 dialog units.
+ */
+ RECT r;
+ r.left = r.right = r.top = 0;
+ r.bottom = 8;
+ MapDialogRect(hwnd, &r);
+ mi->itemHeight = r.bottom;
+
+ return 0;
+ }
+ case WM_DRAWITEM: {
+ assert(wParam == IDC_KEYLIST_LISTBOX);
+
+ DRAWITEMSTRUCT *di = (DRAWITEMSTRUCT *)lParam;
+
+ if (di->itemAction == ODA_FOCUS) {
+ /* Just toggle the focus rectangle either on or off. This
+ * is an XOR-type function, so it's the same call in
+ * either case. */
+ DrawFocusRect(di->hDC, &di->rcItem);
+ } else {
+ /* Draw the full text. */
+ bool selected = (di->itemState & ODS_SELECTED);
+ COLORREF newfg = GetSysColor(
+ selected ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT);
+ COLORREF newbg = GetSysColor(
+ selected ? COLOR_HIGHLIGHT : COLOR_WINDOW);
+ COLORREF oldfg = SetTextColor(di->hDC, newfg);
+ COLORREF oldbg = SetBkColor(di->hDC, newbg);
+
+ HFONT font = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0);
+ HFONT oldfont = SelectObject(di->hDC, font);
+
+ /* ExtTextOut("") is an easy way to just draw the
+ * background rectangle */
+ ExtTextOut(di->hDC, di->rcItem.left, di->rcItem.top,
+ ETO_OPAQUE | ETO_CLIPPED, &di->rcItem, "", 0, NULL);
+
+ struct keylist_display_data *disp =
+ (struct keylist_display_data *)di->itemData;
+
+ RECT r;
+
+ /* Apparently real list boxes start drawing at x=1, not x=0 */
+ r.left = r.top = r.bottom = 0;
+ r.right = 1;
+ MapDialogRect(hwnd, &r);
+ ExtTextOut(di->hDC, di->rcItem.left + r.right, di->rcItem.top,
+ ETO_CLIPPED, &di->rcItem, disp->alg->s,
+ disp->alg->len, NULL);
+
+ if (disp->bits->len) {
+ ExtTextOut(di->hDC, di->rcItem.left + r.right + colpos_bits,
+ di->rcItem.top, ETO_CLIPPED, &di->rcItem,
+ disp->bits->s, disp->bits->len, NULL);
+ }
+
+ ExtTextOut(di->hDC, di->rcItem.left + r.right + colpos_hash,
+ di->rcItem.top, ETO_CLIPPED, &di->rcItem,
+ disp->hash->s, disp->hash->len, NULL);
+
+ strbuf *sb = strbuf_new();
+ put_datapl(sb, ptrlen_from_strbuf(disp->comment));
+ if (disp->info->len) {
+ put_byte(sb, '\t');
+ put_datapl(sb, ptrlen_from_strbuf(disp->info));
+ }
+
+ TabbedTextOut(di->hDC, di->rcItem.left + r.right + colpos_comment,
+ di->rcItem.top, sb->s, sb->len, 0, NULL, 0);
+
+ strbuf_free(sb);
+
+ SetTextColor(di->hDC, oldfg);
+ SetBkColor(di->hDC, oldbg);
+ SelectObject(di->hDC, oldfont);
+
+ if (di->itemState & ODS_FOCUS)
+ DrawFocusRect(di->hDC, &di->rcItem);
+ }
+ return 0;
+ }
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDOK:
@@ -729,9 +854,9 @@ static INT_PTR CALLBACK KeyListProc(HWND hwnd, UINT msg,
topic = WINHELP_CTX_pageant_deferred; break;
}
if (topic) {
- launch_help(hwnd, topic);
+ launch_help(hwnd, topic);
} else {
- MessageBeep(0);
+ MessageBeep(0);
}
break;
}
@@ -827,13 +952,12 @@ static void update_sessions(void)
}
}
-#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.
+ * returned from get_user_sid().
*/
PSID get_default_sid(void)
{
@@ -872,7 +996,6 @@ PSID get_default_sid(void)
return ret;
}
-#endif
struct WmCopydataTransaction {
char *length, *body;
@@ -982,12 +1105,10 @@ static char *answer_filemapping_message(const char *mapname)
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;
@@ -1006,8 +1127,7 @@ static char *answer_filemapping_message(const char *mapname)
debug("maphandle = %p\n", maphandle);
#endif
-#ifndef NO_SECURITY
- if (has_security) {
+ if (should_have_security()) {
DWORD retd;
if ((expectedsid = get_user_sid()) == NULL) {
@@ -1048,9 +1168,7 @@ static char *answer_filemapping_message(const char *mapname)
err = dupstr("wrong owning SID of file mapping");
goto cleanup;
}
- } else
-#endif /* NO_SECURITY */
- {
+ } else {
#ifdef DEBUG_IPC
debug("security APIs not present\n");
#endif
@@ -1195,8 +1313,8 @@ static LRESULT CALLBACK TrayWndProc(HWND hwnd, UINT message,
if((INT_PTR)ShellExecute(hwnd, NULL, putty_path, cmdline,
_T(""), SW_SHOW) <= 32) {
- MessageBox(NULL, "Unable to execute PuTTY!",
- "Error", MB_OK | MB_ICONERROR);
+ MessageBox(NULL, "Unable to execute PuTTY!",
+ "Error", MB_OK | MB_ICONERROR);
}
break;
}
@@ -1254,31 +1372,34 @@ static LRESULT CALLBACK TrayWndProc(HWND hwnd, UINT message,
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);
- }
+ 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_NETEVENT:
+ winselgui_response(wParam, lParam);
+ return 0;
case WM_DESTROY:
quit_help(hwnd);
PostQuitMessage(0);
@@ -1305,10 +1426,10 @@ static LRESULT CALLBACK wm_copydata_WndProc(HWND hwnd, UINT message,
err = answer_filemapping_message(mapname);
if (err) {
#ifdef DEBUG_IPC
- debug("IPC failed: %s\n", err);
+ debug("IPC failed: %s\n", err);
#endif
- sfree(err);
- return 0;
+ sfree(err);
+ return 0;
}
return 1;
}
@@ -1351,11 +1472,6 @@ void spawn_cmd(const char *cmdline, const char *args, int show)
}
}
-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 */
@@ -1386,28 +1502,58 @@ static struct winpgnt_client wpc[1];
HINSTANCE hinst;
+static NORETURN void opt_error(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ char *msg = dupvprintf(fmt, ap);
+ va_end(ap);
+
+ MessageBox(NULL, msg, "Pageant command line error", MB_ICONERROR | MB_OK);
+
+ exit(1);
+}
+
+#ifdef LEGACY_WINDOWS
+BOOL sw_PeekMessage(LPMSG msg, HWND hwnd, UINT min, UINT max, UINT remove)
+{
+ static bool unicode_unavailable = false;
+ if (!unicode_unavailable) {
+ BOOL ret = PeekMessageW(msg, hwnd, min, max, remove);
+ if (!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+ unicode_unavailable = true; /* don't try again */
+ else
+ return ret;
+ }
+ return PeekMessageA(msg, hwnd, min, max, remove);
+}
+#else
+#define sw_PeekMessage PeekMessageW
+#endif
+
int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
{
MSG msg;
const char *command = NULL;
- bool added_keys = false;
+ const char *unixsocket = NULL;
bool show_keylist_on_startup = false;
- int argc, i;
+ int argc;
char **argv, **argstart;
+ const char *openssh_config_file = NULL;
+
+ typedef struct CommandLineKey {
+ Filename *fn;
+ bool add_encrypted;
+ } CommandLineKey;
+
+ CommandLineKey *clkeys = NULL;
+ size_t nclkeys = 0, clkeysize = 0;
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
+ if (should_have_security()) {
/*
* Attempt to get the security API we need.
*/
@@ -1418,13 +1564,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
"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
}
/*
@@ -1454,81 +1593,240 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
}
/*
+ * Process the command line, handling anything that can be done
+ * immediately, but deferring adding keys until after we've
+ * started up the main agent. Details of keys to be added are
+ * stored in the 'clkeys' array.
+ */
+ split_into_argv(cmdline, &argc, &argv, &argstart);
+ bool add_keys_encrypted = false;
+ AuxMatchOpt amo = aux_match_opt_init(argc, argv, 0, opt_error);
+ while (!aux_match_done(&amo)) {
+ char *val;
+ #define match_opt(...) aux_match_opt( \
+ &amo, NULL, __VA_ARGS__, (const char *)NULL)
+ #define match_optval(...) aux_match_opt( \
+ &amo, &val, __VA_ARGS__, (const char *)NULL)
+
+ if (aux_match_arg(&amo, &val)) {
+ /*
+ * Non-option arguments are expected to be key files, and
+ * added to clkeys.
+ */
+ sgrowarray(clkeys, clkeysize, nclkeys);
+ CommandLineKey *clkey = &clkeys[nclkeys++];
+ clkey->fn = filename_from_str(val);
+ clkey->add_encrypted = add_keys_encrypted;
+ } else if (match_opt("-pgpfp")) {
+ pgp_fingerprints_msgbox(NULL);
+ return 1;
+ } else if (match_opt("-restrict-acl", "-restrict_acl",
+ "-restrictacl")) {
+ restrict_process_acl();
+ } else if (match_opt("-restrict-putty-acl", "-restrict_putty_acl")) {
+ restrict_putty_acl = true;
+ } else if (match_opt("-no-decrypt", "-no_decrypt",
+ "-nodecrypt", "-encrypted")) {
+ add_keys_encrypted = true;
+ } else if (match_opt("-keylist")) {
+ show_keylist_on_startup = true;
+ } else if (match_optval("-openssh-config", "-openssh_config")) {
+ openssh_config_file = val;
+ } else if (match_optval("-unix")) {
+ unixsocket = val;
+ } else if (match_opt("-c")) {
+ /*
+ * If we see `-c', then the rest of the command line
+ * should be treated as a command to be spawned.
+ */
+ if (amo.index < amo.argc)
+ command = argstart[amo.index];
+ else
+ command = "";
+ break;
+ } else {
+ opt_error("unrecognised option '%s'\n", amo.argv[amo.index]);
+ }
+ }
+
+ /*
+ * Create and lock an interprocess mutex while we figure out
+ * whether we're going to be the Pageant server or a client. That
+ * way, two Pageant processes started up simultaneously will be
+ * able to agree on which one becomes the server without a race
+ * condition.
+ */
+ HANDLE mutex;
+ {
+ char *err;
+ char *mutexname = agent_mutex_name();
+ mutex = lock_interprocess_mutex(mutexname, &err);
+ sfree(mutexname);
+ if (!mutex) {
+ MessageBox(NULL, err, "Pageant Error", MB_ICONERROR | MB_OK);
+ return 1;
+ }
+ }
+
+ /*
* Find out if Pageant is already running.
*/
already_running = agent_exists();
/*
- * Initialise the cross-platform Pageant code.
+ * If it isn't, we're going to be the primary Pageant that stays
+ * running, so set up all the machinery to answer requests.
*/
if (!already_running) {
+ /*
+ * Set up the window class for the hidden window that receives
+ * all the messages to do with our presence in the system tray.
+ */
+
+ 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);
+ }
+
+ keylist = NULL;
+
+ traywindow = CreateWindow(TRAYCLASSNAME, TRAYWINTITLE,
+ WS_OVERLAPPEDWINDOW | WS_VSCROLL,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ 100, 100, NULL, NULL, inst, NULL);
+ winselgui_set_hwnd(traywindow);
+
+ /*
+ * Initialise the cross-platform Pageant code.
+ */
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);
+ /*
+ * Set up a named-pipe listener.
+ */
+ wpc->plc.vt = &winpgnt_vtable;
+ wpc->plc.suppress_logging = true;
+ if (should_have_security()) {
+ Plug *pl_plug;
+ 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;
- } 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;
+ pageant_listener_got_socket(pl, sock);
+
+ /*
+ * If we've been asked to write out an OpenSSH config file
+ * pointing at the named pipe, do so.
+ */
+ if (openssh_config_file) {
+ FILE *fp = fopen(openssh_config_file, "w");
+ if (!fp) {
+ char *err = dupprintf("Unable to write OpenSSH config "
+ "file to %s", openssh_config_file);
+ MessageBox(NULL, err, "Pageant Error",
+ MB_ICONERROR | MB_OK);
+ return 1;
+ }
+ fprintf(fp, "IdentityAgent %s\n", pipename);
+ fclose(fp);
+ }
+
+ sfree(pipename);
+ }
+
+ /*
+ * Set up an AF_UNIX listener too, if we were asked to.
+ */
+ if (unixsocket) {
+ sk_init();
+
+ /* FIXME: diagnose any error except file-not-found. Also,
+ * check the file type if possible? */
+ remove(unixsocket);
+
+ Plug *pl_plug;
+ struct pageant_listen_state *pl =
+ pageant_listener_new(&pl_plug, &wpc->plc);
+ Socket *sock = sk_newlistener_unix(unixsocket, pl_plug);
+ if (sk_socket_error(sock)) {
+ char *err = dupprintf("Unable to open AF_UNIX socket at %s "
+ "for SSH agent:\n%s", unixsocket,
+ sk_socket_error(sock));
+ MessageBox(NULL, err, "Pageant Error", MB_ICONERROR | MB_OK);
+ return 1;
+ }
+ pageant_listener_got_socket(pl, sock);
+ }
+
+ /*
+ * Set up the window class for the hidden window 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 = wm_copydata_WndProc;
+ wndclass.hInstance = inst;
+ wndclass.lpszClassName = IPCCLASSNAME;
+
+ RegisterClass(&wndclass);
}
+
+ /*
+ * And launch the subthread which will open that hidden window and
+ * handle WM_COPYDATA messages on it.
+ */
+ 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 */
+ add_handle_wait(wmct.ev_msg_ready, wm_copydata_got_msg, NULL);
}
/*
- * Forget any passphrase that we retained while going over
- * command line keyfiles.
+ * Now we're either a fully set up Pageant server, or we know one
+ * is running somewhere else. Either way, now it's safe to unlock
+ * the mutex.
*/
+ unlock_interprocess_mutex(mutex);
+
+ /*
+ * Add any keys provided on the command line.
+ */
+ for (size_t i = 0; i < nclkeys; i++) {
+ CommandLineKey *clkey = &clkeys[i];
+ win_add_keyfile(clkey->fn, clkey->add_encrypted);
+ filename_free(clkey->fn);
+ }
+ sfree(clkeys);
+ /* And forget any passphrases we stashed during that loop. */
pageant_forget_passphrases();
+ /*
+ * Now our keys are present, spawn a command, if we were asked to.
+ */
if (command) {
char *args;
if (command[0] == '"')
@@ -1548,73 +1846,13 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
* keys), complain.
*/
if (already_running) {
- if (!command && !added_keys) {
+ if (!command && !nclkeys) {
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);
@@ -1628,7 +1866,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
}
AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
- "&View Keys");
+ "&View Keys");
AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY_ENCRYPTED,
"Add key (encrypted)");
@@ -1650,18 +1888,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
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);
-
+ /* Open the visible key list window, if we've been asked to. */
if (show_keylist_on_startup)
create_keylist_window();
@@ -1669,21 +1896,19 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
* Main message loop.
*/
while (true) {
- HANDLE *handles;
- int nhandles, n;
+ int n;
- handles = handle_get_events(&nhandles);
+ HandleWaitList *hwl = get_handle_wait_list();
- n = MsgWaitForMultipleObjects(nhandles, handles, false,
- INFINITE, QS_ALLINPUT);
+ DWORD timeout = toplevel_callback_pending() ? 0 : INFINITE;
+ n = MsgWaitForMultipleObjects(hwl->nhandles, hwl->handles, false,
+ timeout, QS_ALLINPUT);
- if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) {
- handle_got_event(handles[n - WAIT_OBJECT_0]);
- sfree(handles);
- } else
- sfree(handles);
+ if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)hwl->nhandles)
+ handle_wait_activate(hwl, n - WAIT_OBJECT_0);
+ handle_wait_list_free(hwl);
- while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
+ while (sw_PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT)
goto finished; /* two-level break */
@@ -1718,6 +1943,16 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
if (keypath) filereq_free(keypath);
+ if (openssh_config_file) {
+ /*
+ * Leave this file around, but empty it, so that it doesn't
+ * refer to a pipe we aren't listening on any more.
+ */
+ FILE *fp = fopen(openssh_config_file, "w");
+ if (fp)
+ fclose(fp);
+ }
+
cleanup_exit(msg.wParam);
return msg.wParam; /* just in case optimiser complains */
}
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/winstuff.h b/windows/platform.h
index c0df5a31..959a207c 100644
--- a/windows/winstuff.h
+++ b/windows/platform.h
@@ -1,25 +1,22 @@
/*
- * winstuff.h: Windows-specific inter-module stuff.
+ * windows/platform.h: Windows-specific inter-module stuff.
*/
-#ifndef PUTTY_WINSTUFF_H
-#define PUTTY_WINSTUFF_H
+#ifndef PUTTY_WINDOWS_PLATFORM_H
+#define PUTTY_WINDOWS_PLATFORM_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
+ * standard. But older versions of Visual Studio 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__
+#if !HAVE_NO_STDINT_H
#include <stdint.h>
#endif
@@ -28,7 +25,7 @@
#include "tree234.h"
-#include "winhelp.h"
+#include "help.h"
#if defined _M_IX86 || defined _M_AMD64
#define BUILDINFO_PLATFORM "x86 Windows"
@@ -38,6 +35,18 @@
#define BUILDINFO_PLATFORM "Windows"
#endif
+#if defined __GNUC__ || defined __clang__
+#define THREADLOCAL __thread
+#elif defined _MSC_VER
+#define THREADLOCAL __declspec(thread)
+#else
+#error Do not know how to declare thread-local storage with this toolchain
+#endif
+
+/* Randomly-chosen dwData value identifying a WM_COPYDATA message as
+ * being a Pageant transaction */
+#define AGENT_COPYDATA_ID 0x804e50ba
+
struct Filename {
char *path;
};
@@ -102,9 +111,19 @@ struct FontSpec *fontspec_new(
#define LONG_PTR LONG
#endif
-#define BOXFLAGS DLGWINDOWEXTRA
-#define BOXRESULT (DLGWINDOWEXTRA + sizeof(LONG_PTR))
-#define DF_END 0x0001
+#if !HAVE_STRTOUMAX
+/* Work around lack of strtoumax in older MSVC libraries */
+static inline uintmax_t strtoumax(const char *nptr, char **endptr, int base)
+{ return _strtoui64(nptr, endptr, base); }
+#endif
+
+typedef INT_PTR (*ShinyDlgProc)(HWND hwnd, UINT msg, WPARAM wParam,
+ LPARAM lParam, void *ctx);
+int ShinyDialogBox(HINSTANCE hinst, LPCTSTR tmpl, const char *winclass,
+ HWND hwndparent, ShinyDlgProc proc, void *ctx);
+void ShinyEndDialog(HWND hwnd, int ret);
+
+void centre_window(HWND hwnd);
#ifndef __WINE__
/* Up-to-date Windows headers warn that the unprefixed versions of
@@ -118,8 +137,6 @@ struct FontSpec *fontspec_new(
#define strnicmp strncasecmp
#endif
-#define BROKEN_PIPE_ERROR_CODE ERROR_BROKEN_PIPE /* used in sshshare.c */
-
/*
* Dynamically linked functions. These come in two flavours:
*
@@ -143,8 +160,6 @@ struct FontSpec *fontspec_new(
/* 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 ? \
@@ -204,7 +219,7 @@ typedef void *Ssh_gss_name;
extern HINSTANCE hinst;
/*
- * Help file stuff in winhelp.c.
+ * Help file stuff in help.c.
*/
void init_help(void);
void shutdown_help(void);
@@ -214,22 +229,23 @@ 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
+ * GUI seat methods in dialog.c, so that the vtable definition in
* window.c can refer to them.
*/
-int win_seat_verify_ssh_host_key(
+SeatPromptResult win_seat_confirm_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(
+ char *keystr, SeatDialogText *text, HelpCtx helpctx,
+ void (*callback)(void *ctx, SeatPromptResult result), void *ctx);
+SeatPromptResult 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(
+ void (*callback)(void *ctx, SeatPromptResult result), void *ctx);
+SeatPromptResult win_seat_confirm_weak_cached_hostkey(
Seat *seat, const char *algname, const char *betteralgs,
- void (*callback)(void *ctx, int result), void *ctx);
+ void (*callback)(void *ctx, SeatPromptResult result), void *ctx);
+const SeatDialogPromptDescriptions *win_seat_prompt_descriptions(Seat *seat);
/*
- * Windows-specific clipboard helper function shared with windlg.c,
+ * Windows-specific clipboard helper function shared with dialog.c,
* which takes the data string in the system code page instead of
* Unicode.
*/
@@ -239,7 +255,7 @@ void write_aclip(int clipboard, char *, int, bool);
/*
* On Windows, we send MA_2CLK as the only event marking the second
- * press of a mouse button. Compare unix.h.
+ * press of a mouse button. Compare unix/platform.h.
*/
#define MULTICLICK_ONLY_EVENT 1
@@ -258,7 +274,7 @@ void write_aclip(int clipboard, char *, int, bool);
* 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 :-)
+ * implementation of XDM-AUTHORIZATION-1 in ssh/x11fwd.c :-)
*/
#define sk_getxdmdata(socket, lenp) (NULL)
@@ -275,14 +291,14 @@ void write_aclip(int clipboard, char *, int, bool);
"All Files (*.*)\0*\0\0\0")
/*
- * Exports from winnet.c.
+ * Exports from network.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 */
+/* Ask network.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);
@@ -290,17 +306,18 @@ void socket_reselect_all(void);
SockAddr *sk_namedpipe_addr(const char *pipename);
/* Turn a WinSock error code into a string. */
const char *winsock_error_string(int error);
+Socket *sk_newlistener_unix(const char *socketpath, Plug *plug);
/*
- * winnet.c dynamically loads WinSock 2 or WinSock 1 depending on
+ * network.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));
+ (SOCKET, HWND, u_int, LONG));
DECL_WINDOWS_FUNCTION(extern, int, WSAEventSelect,
- (SOCKET, WSAEVENT, long));
+ (SOCKET, WSAEVENT, LONG));
DECL_WINDOWS_FUNCTION(extern, int, WSAGetLastError, (void));
DECL_WINDOWS_FUNCTION(extern, int, WSAEnumNetworkEvents,
(SOCKET, WSAEVENT, LPWSANETWORKEVENTS));
@@ -318,17 +335,18 @@ DECL_WINDOWS_FUNCTION(extern, int, select,
#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.
+ * Implemented differently depending on the client of network.c, and
+ * called by network.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
+ * Exports from select-{gui,cli}.c, each of which provides an
* implementation of do_select.
*/
void winselgui_set_hwnd(HWND hwnd);
void winselgui_clear_hwnd(void);
+void winselgui_response(WPARAM wParam, LPARAM lParam);
void winselcli_setup(void);
SOCKET winselcli_unique_socket(void);
@@ -338,18 +356,24 @@ 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 */
+ SockAddr *addr, int port, Plug *plug,
+ bool overlapped); /* winhsock */
+Socket *make_deferred_handle_socket(DeferredSocketOpener *opener,
+ SockAddr *addr, int port, Plug *plug);
+void setup_handle_socket(Socket *s, HANDLE send_H, HANDLE recv_H,
+ HANDLE stderr_H, bool overlapped);
+void handle_socket_set_psb_prefix(Socket *s, const char *prefix);
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. */
+/* A lower-level function in named-pipe-client.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.
+ * Exports from controls.c.
*/
struct ctlpos {
@@ -359,12 +383,12 @@ struct ctlpos {
int ypos, width;
int xoff;
int boxystart, boxid;
- char *boxtext;
+ const char *boxtext;
};
void init_common_controls(void); /* also does some DLL-loading */
/*
- * Exports from winutils.c.
+ * Exports from utils.
*/
typedef struct filereq_tag filereq; /* cwd for file requester */
bool request_file(filereq *state, OPENFILENAME *of, bool preserve, bool save);
@@ -399,7 +423,7 @@ struct dlgparam {
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 */
+ dlgcontrol *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? */
@@ -414,60 +438,64 @@ struct dlgparam {
};
/*
- * Exports from winctrls.c.
+ * Exports from controls.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);
+HWND doctl(struct ctlpos *cp, RECT r, const char *wclass, int wstyle,
+ int exstyle, const char *wtext, int wid);
+void bartitle(struct ctlpos *cp, const char *name, int id);
+void beginbox(struct ctlpos *cp, const 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 editboxfw(struct ctlpos *cp, bool password, bool readonly,
+ const char *text, int staticid, int editid);
+void radioline(struct ctlpos *cp, const 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,
+void radiobig(struct ctlpos *cp, const char *text, int id, ...);
+void checkbox(struct ctlpos *cp, const char *text, int id);
+void button(struct ctlpos *cp, const char *btext, int bid, bool defbtn);
+void statictext(struct ctlpos *cp, const char *text, int lines, int id);
+void staticbtn(struct ctlpos *cp, const char *stext, int sid,
+ const char *btext, int bid);
+void static2btn(struct ctlpos *cp, const char *stext, int sid,
+ const char *btext1, int bid1, const char *btext2, int bid2);
+void staticedit(struct ctlpos *cp, const char *stext,
int sid, int eid, int percentedit);
-void staticddl(struct ctlpos *cp, char *stext,
+void staticddl(struct ctlpos *cp, const 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,
+void combobox(struct ctlpos *cp, const char *text, int staticid, int listid);
+void staticpassedit(struct ctlpos *cp, const char *stext,
int sid, int eid, int percentedit);
-void bigeditctrl(struct ctlpos *cp, char *stext,
+void bigeditctrl(struct ctlpos *cp, const 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,
+void ersatztab(struct ctlpos *cp, const char *stext, int sid, int lid,
+ int s2id);
+void editbutton(struct ctlpos *cp, const char *stext, int sid,
+ int eid, const char *btext, int bid);
+void sesssaver(struct ctlpos *cp, const 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 envsetter(struct ctlpos *cp, const char *stext, int sid,
+ const char *e1stext, int e1sid, int e1id,
+ const char *e2stext, int e2sid, int e2id,
+ int listid, const char *b1text, int b1id,
+ const char *b2text, int b2id);
+void charclass(struct ctlpos *cp, const char *stext, int sid, int listid,
+ const char *btext, int bid, int eid, const char *s2text,
+ int s2id);
+void colouredit(struct ctlpos *cp, const char *stext, int sid, int listid,
+ const char *btext, int bid, ...);
void prefslist(struct prefslist *hdl, struct ctlpos *cp, int lines,
- char *stext, int sid, int listid, int upbid, int dnbid);
+ const 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 fwdsetter(struct ctlpos *cp, int listid, const char *stext, int sid,
+ const char *e1stext, int e1sid, int e1id,
+ const char *e2stext, int e2sid, int e2id,
+ const char *btext, int bid,
+ const char *r1text, int r1id, const char *r2text, int r2id);
void dlg_auto_set_fixed_pitch_flag(dlgparam *dlg);
bool dlg_get_fixed_pitch_flag(dlgparam *dlg);
@@ -476,11 +504,11 @@ 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
+ * This structure is what's stored for each `dlgcontrol' in the
* portable-dialog interface.
*/
struct winctrl {
- union control *ctrl;
+ dlgcontrol *ctrl;
/*
* The control may have several components at the Windows
* level, with different dialog IDs. To avoid needing N
@@ -511,7 +539,7 @@ struct winctrl {
};
/*
* 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
+ * tree234s so that it can find an item by `dlgcontrol' or by
* dialog ID.
*/
struct winctrls {
@@ -524,7 +552,7 @@ 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_findbyctrl(struct winctrls *, dlgcontrol *);
struct winctrl *winctrl_findbyid(struct winctrls *, int);
struct winctrl *winctrl_findbyindex(struct winctrls *, int);
void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,
@@ -539,13 +567,13 @@ void dp_add_tree(struct dlgparam *dp, struct winctrls *tree);
void dp_cleanup(struct dlgparam *dp);
/*
- * Exports from wincfg.c.
+ * Exports from config.c.
*/
void win_setup_config_box(struct controlbox *b, HWND *hwndp, bool has_help,
bool midsession, int protocol);
/*
- * Exports from windlg.c.
+ * Exports from dialog.c.
*/
void defuse_showwindow(void);
bool do_config(Conf *);
@@ -558,13 +586,15 @@ void show_help(HWND hwnd);
HWND event_log_window(void);
/*
- * Exports from winmisc.c.
+ * Exports from utils.
*/
extern DWORD osMajorVersion, osMinorVersion, osPlatformId;
void init_winver(void);
void dll_hijacking_protection(void);
+const char *get_system_dir(void);
HMODULE load_system32_dll(const char *libname);
const char *win_strerror(int error);
+bool should_have_security(void);
void restrict_process_acl(void);
bool restricted_acl(void);
void escape_registry_key(const char *in, strbuf *out);
@@ -597,11 +627,10 @@ void EnableSizeTip(bool bEnable);
/*
* Exports from unicode.c.
*/
-struct unicode_data;
void init_ucs(Conf *, struct unicode_data *);
/*
- * Exports from winhandl.c.
+ * Exports from handle-io.c.
*/
#define HANDLE_FLAG_OVERLAPPED 1
#define HANDLE_FLAG_IGNOREEOF 2
@@ -610,21 +639,17 @@ 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 *h, size_t new_backlog, int err, bool close);
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;
@@ -633,17 +658,35 @@ struct handle_sink {
void handle_sink_init(handle_sink *sink, struct handle *h);
/*
- * Exports from winpgntc.c.
+ * Exports from handle-wait.c.
*/
+typedef struct HandleWait HandleWait;
+typedef void (*handle_wait_callback_fn_t)(void *);
+HandleWait *add_handle_wait(HANDLE h, handle_wait_callback_fn_t callback,
+ void *callback_ctx);
+void delete_handle_wait(HandleWait *hw);
+
+typedef struct HandleWaitList {
+ HANDLE handles[MAXIMUM_WAIT_OBJECTS];
+ int nhandles;
+} HandleWaitList;
+HandleWaitList *get_handle_wait_list(void);
+void handle_wait_activate(HandleWaitList *hwl, int index);
+void handle_wait_list_free(HandleWaitList *hwl);
+
+/*
+ * Pageant-related pathnames.
+ */
+char *agent_mutex_name(void);
char *agent_named_pipe_name(void);
/*
- * Exports from winser.c.
+ * Exports from serial.c.
*/
extern const struct BackendVtable serial_backend;
/*
- * Exports from winjump.c.
+ * Exports from jump-list.c.
*/
#define JUMPLIST_SUPPORTED /* suppress #defines in putty.h */
void add_session_to_jumplist(const char * const sessionname);
@@ -652,12 +695,12 @@ void clear_jumplist(void);
bool set_explicit_app_user_model_id(void);
/*
- * Exports from winnoise.c.
+ * Exports from noise.c.
*/
bool win_read_random(void *buf, unsigned wanted); /* returns true on success */
/*
- * Extra functions in winstore.c over and above the interface in
+ * Extra functions in storage.c over and above the interface in
* storage.h.
*
* These functions manipulate the Registry section which mirrors the
@@ -690,10 +733,23 @@ char *get_jumplist_registry_entries(void);
#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 */
+/* In utils */
+HKEY open_regkey_fn(bool create, HKEY base, const char *path, ...);
+#define open_regkey(create, base, ...) \
+ open_regkey_fn(create, base, __VA_ARGS__, (const char *)NULL)
+void close_regkey(HKEY key);
+void del_regkey(HKEY key, const char *name);
+char *enum_regkey(HKEY key, int index);
+bool get_reg_dword(HKEY key, const char *name, DWORD *out);
+bool put_reg_dword(HKEY key, const char *name, DWORD value);
+char *get_reg_sz(HKEY key, const char *name);
+bool put_reg_sz(HKEY key, const char *name, const char *str);
+strbuf *get_reg_multi_sz(HKEY key, const char *name);
+bool put_reg_multi_sz(HKEY key, const char *name, strbuf *str);
+
+char *get_reg_sz_simple(HKEY key, const char *name, const char *leaf);
+
+/* In cliloop.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);
@@ -701,4 +757,40 @@ 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
+extern const struct BackendVtable conpty_backend;
+
+/* Functions that parametrise window.c between PuTTY and pterm */
+void gui_term_process_cmdline(Conf *conf, char *cmdline);
+const struct BackendVtable *backend_vt_from_conf(Conf *conf);
+const wchar_t *get_app_user_model_id(void);
+/* And functions in window.c that those files call back to */
+char *handle_restrict_acl_cmdline_prefix(char *cmdline);
+bool handle_special_sessionname_cmdline(char *cmdline, Conf *conf);
+bool handle_special_filemapping_cmdline(char *cmdline, Conf *conf);
+
+/* network.c: network error reporting helpers taking OS error code */
+void plug_closing_system_error(Plug *plug, DWORD error);
+void plug_closing_winsock_error(Plug *plug, DWORD error);
+
+SeatPromptResult make_spr_sw_abort_winerror(const char *prefix, DWORD error);
+
+HANDLE lock_interprocess_mutex(const char *mutexname, char **error);
+void unlock_interprocess_mutex(HANDLE mutex);
+
+typedef void (*aux_opt_error_fn_t)(const char *, ...);
+typedef struct AuxMatchOpt {
+ int index, argc;
+ char **argv;
+ bool doing_opts;
+ aux_opt_error_fn_t error;
+} AuxMatchOpt;
+AuxMatchOpt aux_match_opt_init(int argc, char **argv, int start_index,
+ aux_opt_error_fn_t opt_error);
+bool aux_match_arg(AuxMatchOpt *amo, char **val);
+bool aux_match_opt(AuxMatchOpt *amo, char **val, const char *optname, ...);
+bool aux_match_done(AuxMatchOpt *amo);
+
+char *save_screenshot(HWND hwnd, const char *outfile);
+void gui_terminal_ready(HWND hwnd, Seat *seat, Backend *backend);
+
+#endif /* PUTTY_WINDOWS_PLATFORM_H */
diff --git a/windows/winplink.c b/windows/plink.c
index 58d43e6d..05c25073 100644
--- a/windows/winplink.c
+++ b/windows/plink.c
@@ -8,9 +8,10 @@
#include <stdarg.h>
#include "putty.h"
+#include "ssh.h"
#include "storage.h"
#include "tree234.h"
-#include "winsecur.h"
+#include "security-api.h"
void cmdline_error(const char *fmt, ...)
{
@@ -50,8 +51,9 @@ static void plink_echoedit_update(Seat *seat, bool echo, bool edit)
}
static size_t plink_output(
- Seat *seat, bool is_stderr, const void *data, size_t len)
+ Seat *seat, SeatOutputType type, const void *data, size_t len)
{
+ bool is_stderr = type != SEAT_OUTPUT_STDOUT;
BinarySink *bs = is_stderr ? stderr_bs : stdout_bs;
put_data(bs, data, len);
@@ -64,13 +66,18 @@ static bool plink_eof(Seat *seat)
return false; /* do not respond to incoming EOF with outgoing */
}
-static int plink_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input)
+static SeatPromptResult plink_get_userpass_input(Seat *seat, prompts_t *p)
{
- int ret;
- ret = cmdline_get_passwd_input(p);
- if (ret == -1)
- ret = console_get_userpass_input(p);
- return ret;
+ /* Plink doesn't support Restart Session, so we can just have a
+ * single static cmdline_get_passwd_input_state that's never reset */
+ static cmdline_get_passwd_input_state cmdline_state =
+ CMDLINE_GET_PASSWD_INPUT_STATE_INIT;
+
+ SeatPromptResult spr;
+ spr = cmdline_get_passwd_input(p, &cmdline_state, false);
+ if (spr.kind == SPRK_INCOMPLETE)
+ spr = console_get_userpass_input(p);
+ return spr;
}
static bool plink_seat_interactive(Seat *seat)
@@ -83,15 +90,20 @@ static bool plink_seat_interactive(Seat *seat)
static const SeatVtable plink_seat_vt = {
.output = plink_output,
.eof = plink_eof,
+ .sent = nullseat_sent,
+ .banner = nullseat_banner_to_stderr,
.get_userpass_input = plink_get_userpass_input,
+ .notify_session_started = nullseat_notify_session_started,
.notify_remote_exit = nullseat_notify_remote_exit,
+ .notify_remote_disconnect = nullseat_notify_remote_disconnect,
.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_ssh_host_key = console_confirm_ssh_host_key,
.confirm_weak_crypto_primitive = console_confirm_weak_crypto_primitive,
.confirm_weak_cached_hostkey = console_confirm_weak_cached_hostkey,
+ .prompt_descriptions = console_prompt_descriptions,
.is_utf8 = nullseat_is_never_utf8,
.echoedit_update = plink_echoedit_update,
.get_x_display = nullseat_get_x_display,
@@ -99,6 +111,8 @@ static const SeatVtable plink_seat_vt = {
.get_window_pixel_size = nullseat_get_window_pixel_size,
.stripctrl_new = console_stripctrl_new,
.set_trust_status = console_set_trust_status,
+ .can_set_trust_status = console_can_set_trust_status,
+ .has_mixed_input_stream = console_has_mixed_input_stream,
.verbose = cmdline_seat_verbose,
.interactive = plink_seat_interactive,
.get_cursor_position = nullseat_get_cursor_position,
@@ -133,7 +147,7 @@ static void usage(void)
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(" -pwfile file login with password read from specified file\n");
printf(" -D [listen-IP:]listen-port\n");
printf(" Dynamic SOCKS-based port forwarding\n");
printf(" -L [listen-IP:]listen-port:host:port\n");
@@ -201,7 +215,8 @@ size_t stdin_gotdata(struct handle *h, const void *data, size_t len, int err)
noise_ultralight(NOISE_SOURCE_IOLEN, len);
if (backend_connected(backend)) {
if (len > 0) {
- return backend_send(backend, data, len);
+ backend_send(backend, data, len);
+ return backend_sendbuffer(backend);
} else {
backend_special(backend, SS_EOF, 0);
return 0;
@@ -210,8 +225,14 @@ size_t stdin_gotdata(struct handle *h, const void *data, size_t len, int err)
return 0;
}
-void stdouterr_sent(struct handle *h, size_t new_backlog, int err)
+void stdouterr_sent(struct handle *h, size_t new_backlog, int err, bool close)
{
+ if (close) {
+ CloseHandle(outhandle);
+ CloseHandle(errhandle);
+ outhandle = errhandle = INVALID_HANDLE_VALUE;
+ }
+
if (err) {
char buf[4096];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0,
@@ -352,7 +373,7 @@ int main(int argc, char **argv)
while (argc > 0) {
if (cmdbuf->len > 0)
put_byte(cmdbuf, ' '); /* add space separator */
- put_datapl(cmdbuf, ptrlen_from_asciz(p));
+ put_dataz(cmdbuf, p);
if (--argc > 0)
p = *++argv;
}
@@ -403,7 +424,7 @@ int main(int argc, char **argv)
if (vt->flags & BACKEND_NEEDS_TERMINAL) {
fprintf(stderr,
"Plink doesn't support %s, which needs terminal emulation\n",
- vt->displayname);
+ vt->displayname_lc);
return 1;
}
@@ -429,7 +450,7 @@ int main(int argc, char **argv)
if (just_test_share_exists) {
if (!vt->test_for_upstream) {
fprintf(stderr, "Connection sharing not supported for this "
- "connection type (%s)'\n", vt->displayname);
+ "connection type (%s)'\n", vt->displayname_lc);
return 1;
}
if (vt->test_for_upstream(conf_get_str(conf, CONF_host),
diff --git a/windows/winprint.c b/windows/printing.c
index e6b3531d..2286c236 100644
--- a/windows/winprint.c
+++ b/windows/printing.c
@@ -67,11 +67,15 @@ static bool printer_add_enum(int param, DWORD level, char **buffer,
/*
* 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.
+ * we'll need for the output.
+ *
+ * If we get ERROR_INSUFFICIENT_BUFFER, that's fine, we're
+ * prepared to deal with it. Any other error, we return failure.
*/
- p_EnumPrinters(param, NULL, level, (LPBYTE)((*buffer)+offset), 512,
- &needed, &nprinters);
+ if (p_EnumPrinters(param, NULL, level, (LPBYTE)((*buffer)+offset), 512,
+ &needed, &nprinters) == 0 &&
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ return false;
if (needed < 512)
needed = 512;
@@ -127,7 +131,7 @@ printer_enum *printer_start_enum(int *nprinters_ptr)
return ret;
- error:
+ error:
sfree(buffer);
sfree(ret);
*nprinters_ptr = 0;
@@ -191,7 +195,7 @@ printer_job *printer_start_job(char *printer)
return ret;
- error:
+ error:
if (pagestarted)
p_EndPagePrinter(ret->hprinter);
if (jobstarted)
diff --git a/windows/winsocks.c b/windows/psocks.c
index 83ba364c..83ba364c 100644
--- a/windows/winsocks.c
+++ b/windows/psocks.c
diff --git a/windows/pterm.c b/windows/pterm.c
new file mode 100644
index 00000000..bb68245d
--- /dev/null
+++ b/windows/pterm.c
@@ -0,0 +1,65 @@
+#include "putty.h"
+#include "storage.h"
+
+const unsigned cmdline_tooltype =
+ TOOLTYPE_NONNETWORK |
+ TOOLTYPE_NO_VERBOSE_OPTION;
+
+void gui_term_process_cmdline(Conf *conf, char *cmdline)
+{
+ do_defaults(NULL, conf);
+ conf_set_str(conf, CONF_remote_cmd, "");
+
+ cmdline = handle_restrict_acl_cmdline_prefix(cmdline);
+ if (handle_special_sessionname_cmdline(cmdline, conf) ||
+ handle_special_filemapping_cmdline(cmdline, conf))
+ return;
+
+ int argc;
+ char **argv, **argstart;
+ split_into_argv(cmdline, &argc, &argv, &argstart);
+
+ for (int i = 0; i < argc; i++) {
+ char *arg = argv[i];
+ int retd = cmdline_process_param(
+ arg, i+1<argc?argv[i+1]:NULL, 1, conf);
+ if (retd == -2) {
+ cmdline_error("option \"%s\" requires an argument", arg);
+ } else if (retd == 2) {
+ i++; /* skip next argument */
+ } else if (retd == 1) {
+ continue; /* nothing further needs doing */
+ } else if (!strcmp(arg, "-e")) {
+ if (i+1 < argc) {
+ /* The command to execute is taken to be the unparsed
+ * version of the whole remainder of the command line. */
+ conf_set_str(conf, CONF_remote_cmd, argstart[i+1]);
+ return;
+ } else {
+ cmdline_error("option \"%s\" requires an argument", arg);
+ }
+ } else if (arg[0] == '-') {
+ cmdline_error("unrecognised option \"%s\"", arg);
+ } else {
+ cmdline_error("unexpected non-option argument \"%s\"", arg);
+ }
+ }
+
+ cmdline_run_saved(conf);
+
+ conf_set_int(conf, CONF_sharrow_type, SHARROW_BITMAP);
+}
+
+const struct BackendVtable *backend_vt_from_conf(Conf *conf)
+{
+ return &conpty_backend;
+}
+
+const wchar_t *get_app_user_model_id(void)
+{
+ return L"SimonTatham.Pterm";
+}
+
+void gui_terminal_ready(HWND hwnd, Seat *seat, Backend *backend)
+{
+}
diff --git a/windows/pterm.ico b/windows/pterm.ico
new file mode 100644
index 00000000..6909a8d2
--- /dev/null
+++ b/windows/pterm.ico
Binary files differ
diff --git a/windows/pterm.rc b/windows/pterm.rc
new file mode 100644
index 00000000..8bd3a043
--- /dev/null
+++ b/windows/pterm.rc
@@ -0,0 +1,15 @@
+#include "rcstuff.h"
+#include "putty-rc.h"
+
+#define APPNAME "pterm"
+#define APPDESC "PuTTY-style wrapper for Windows command prompts"
+
+IDI_MAINICON ICON "pterm.ico"
+IDI_CFGICON ICON "ptermcfg.ico"
+
+#include "help.rc2"
+#include "putty-common.rc2"
+
+#ifndef NO_MANIFESTS
+1 RT_MANIFEST "putty.mft"
+#endif /* NO_MANIFESTS */
diff --git a/windows/ptermcfg.ico b/windows/ptermcfg.ico
new file mode 100644
index 00000000..53bde87e
--- /dev/null
+++ b/windows/ptermcfg.ico
Binary files differ
diff --git a/windows/putty-common.rc2 b/windows/putty-common.rc2
new file mode 100644
index 00000000..a43e3ac1
--- /dev/null
+++ b/windows/putty-common.rc2
@@ -0,0 +1,98 @@
+/*
+ * 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').
+ */
+
+/* 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
+
+/* Accelerators used: achio */
+IDD_HOSTKEY DIALOG DISCARDABLE 50, 50, 340, 240
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "PuTTY Security Alert"
+FONT 8, "MS Shell Dlg"
+CLASS "PuTTYHostKeyDialog"
+BEGIN
+ ICON "", IDC_HK_ICON, 10, 18, 0, 0
+
+ PUSHBUTTON "&Cancel", IDCANCEL, 288, 220, 40, 14
+ PUSHBUTTON "&Accept", IDC_HK_ACCEPT, 168, 220, 40, 14
+ PUSHBUTTON "Connect &Once", IDC_HK_ONCE, 216, 220, 64, 14
+ PUSHBUTTON "More &info...", IDC_HK_MOREINFO, 60, 220, 64, 14
+ PUSHBUTTON "&Help", IDHELP, 12, 220, 40, 14
+
+ LTEXT "", IDC_HK_TITLE, 40, 20, 300, 12
+
+ EDITTEXT IDC_HK_TEXT, 40, 20, 290, 200, ES_READONLY | ES_MULTILINE | ES_LEFT, WS_EX_STATICEDGE
+END
+
+/* Accelerators used: c */
+IDD_HK_MOREINFO DIALOG DISCARDABLE 140, 40, 400, 300
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "PuTTY: information about the server's host key"
+FONT 8, "MS Shell Dlg"
+CLASS "PuTTYHostKeyMoreInfo"
+BEGIN
+ DEFPUSHBUTTON "&Close", IDOK, 176, 130, 48, 14
+END
+
+/* Accelerators used: aco */
+IDD_CA_CONFIG DIALOG DISCARDABLE 0, 0, 350, 260
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "PuTTY trusted host certification authorities"
+FONT 8, "MS Shell Dlg"
+CLASS "PuTTYConfigBox"
+BEGIN
+END
+
+#include "version.rc2"
diff --git a/windows/win_res.h b/windows/putty-rc.h
index d34f6852..35d9bcda 100644
--- a/windows/win_res.h
+++ b/windows/putty-rc.h
@@ -1,5 +1,5 @@
/*
- * win_res.h - constants shared between win_res.rc2 and the C code.
+ * putty-rc.h - constants shared between putty-common.rc2 and the C code.
*/
#ifndef PUTTY_WIN_RES_H
@@ -13,9 +13,9 @@
#define IDD_ABOUTBOX 111
#define IDD_RECONF 112
#define IDD_LICENCEBOX 113
-#define IDD_HK_ABSENT 114
-#define IDD_HK_WRONG 115
+#define IDD_HOSTKEY 114
#define IDD_HK_MOREINFO 116
+#define IDD_CA_CONFIG 117
#define IDN_LIST 1001
#define IDN_COPY 1002
@@ -34,10 +34,12 @@
#define IDC_HK_ICON 98
#define IDC_HK_TITLE 99
+#define IDC_HK_TEXT 100
#define IDC_HK_ACCEPT 1001
#define IDC_HK_ONCE 1000
-#define IDC_HK_FINGERPRINT 1002
-#define IDC_HK_MOREINFO 1003
+#define IDC_HK_HOST 1002
+#define IDC_HK_FINGERPRINT 1003
+#define IDC_HK_MOREINFO 1004
#define IDC_HKI_SHA256 1000
#define IDC_HKI_MD5 1001
diff --git a/windows/putty.c b/windows/putty.c
new file mode 100644
index 00000000..3c41b557
--- /dev/null
+++ b/windows/putty.c
@@ -0,0 +1,203 @@
+#include "putty.h"
+#include "storage.h"
+
+extern bool sesslist_demo_mode;
+extern const char *dialog_box_demo_screenshot_filename;
+static strbuf *demo_terminal_data = NULL;
+static const char *terminal_demo_screenshot_filename;
+
+const unsigned cmdline_tooltype =
+ TOOLTYPE_HOST_ARG |
+ TOOLTYPE_PORT_ARG |
+ TOOLTYPE_NO_VERBOSE_OPTION;
+
+void gui_term_process_cmdline(Conf *conf, char *cmdline)
+{
+ char *p;
+ bool special_launchable_argument = false;
+ bool demo_config_box = false;
+
+ settings_set_default_protocol(be_default_protocol);
+ /* Find the appropriate default port. */
+ {
+ const struct BackendVtable *vt =
+ backend_vt_from_proto(be_default_protocol);
+ settings_set_default_port(0); /* illegal */
+ if (vt)
+ settings_set_default_port(vt->default_port);
+ }
+ conf_set_int(conf, CONF_logtype, LGTYP_NONE);
+
+ do_defaults(NULL, conf);
+
+ p = handle_restrict_acl_cmdline_prefix(cmdline);
+
+ if (handle_special_sessionname_cmdline(p, conf)) {
+ if (!conf_launchable(conf) && !do_config(conf)) {
+ cleanup_exit(0);
+ }
+ special_launchable_argument = true;
+ } else if (handle_special_filemapping_cmdline(p, conf)) {
+ special_launchable_argument = true;
+ } else if (!*p) {
+ /* Do-nothing case for an empty command line - or rather,
+ * for a command line that's empty _after_ we strip off
+ * the &R prefix. */
+ } else {
+ /*
+ * Otherwise, break up the command line and deal with
+ * it sensibly.
+ */
+ int argc, i;
+ char **argv;
+
+ split_into_argv(cmdline, &argc, &argv, NULL);
+
+ for (i = 0; i < argc; i++) {
+ char *p = argv[i];
+ int ret;
+
+ ret = cmdline_process_param(p, i+1<argc?argv[i+1]:NULL,
+ 1, conf);
+ if (ret == -2) {
+ cmdline_error("option \"%s\" requires an argument", p);
+ } else if (ret == 2) {
+ i++; /* skip next argument */
+ } else if (ret == 1) {
+ continue; /* nothing further needs doing */
+ } else if (!strcmp(p, "-cleanup")) {
+ /*
+ * `putty -cleanup'. Remove all registry
+ * entries associated with PuTTY, and also find
+ * and delete the random seed file.
+ */
+ char *s1, *s2;
+ s1 = dupprintf("This procedure will remove ALL Registry entries\n"
+ "associated with %s, and will also remove\n"
+ "the random seed file. (This only affects the\n"
+ "currently logged-in user.)\n"
+ "\n"
+ "THIS PROCESS WILL DESTROY YOUR SAVED SESSIONS.\n"
+ "Are you really sure you want to continue?",
+ appname);
+ s2 = dupprintf("%s Warning", appname);
+ if (message_box(NULL, s1, s2,
+ MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2,
+ HELPCTXID(option_cleanup)) == IDYES) {
+ cleanup_all();
+ }
+ sfree(s1);
+ sfree(s2);
+ exit(0);
+ } else if (!strcmp(p, "-pgpfp")) {
+ pgp_fingerprints_msgbox(NULL);
+ exit(1);
+ } else if (has_ca_config_box &&
+ (!strcmp(p, "-host-ca") || !strcmp(p, "--host-ca") ||
+ !strcmp(p, "-host_ca") || !strcmp(p, "--host_ca"))) {
+ show_ca_config_box(NULL);
+ exit(0);
+ } else if (!strcmp(p, "-demo-config-box")) {
+ if (i+1 >= argc) {
+ cmdline_error("%s expects an output filename", p);
+ } else {
+ demo_config_box = true;
+ dialog_box_demo_screenshot_filename = argv[++i];
+ }
+ } else if (!strcmp(p, "-demo-terminal")) {
+ if (i+2 >= argc) {
+ cmdline_error("%s expects input and output filenames", p);
+ } else {
+ const char *infile = argv[++i];
+ terminal_demo_screenshot_filename = argv[++i];
+ FILE *fp = fopen(infile, "rb");
+ if (!fp)
+ cmdline_error("can't open input file '%s'", infile);
+ demo_terminal_data = strbuf_new();
+ char buf[4096];
+ int retd;
+ while ((retd = fread(buf, 1, sizeof(buf), fp)) > 0)
+ put_data(demo_terminal_data, buf, retd);
+ fclose(fp);
+ }
+ } else if (*p != '-') {
+ cmdline_error("unexpected argument \"%s\"", p);
+ } else {
+ cmdline_error("unknown option \"%s\"", p);
+ }
+ }
+ }
+
+ cmdline_run_saved(conf);
+
+ if (demo_config_box) {
+ sesslist_demo_mode = true;
+ load_open_settings(NULL, conf);
+ conf_set_str(conf, CONF_host, "demo-server.example.com");
+ do_config(conf);
+ cleanup_exit(0);
+ } else if (demo_terminal_data) {
+ /* Ensure conf will cause an immediate session launch */
+ load_open_settings(NULL, conf);
+ conf_set_str(conf, CONF_host, "demo-server.example.com");
+ conf_set_int(conf, CONF_close_on_exit, FORCE_OFF);
+ } else {
+ /*
+ * Bring up the config dialog if the command line hasn't
+ * (explicitly) specified a launchable configuration.
+ */
+ if (!(special_launchable_argument || cmdline_host_ok(conf))) {
+ if (!do_config(conf))
+ cleanup_exit(0);
+ }
+ }
+
+ prepare_session(conf);
+}
+
+const struct BackendVtable *backend_vt_from_conf(Conf *conf)
+{
+ if (demo_terminal_data) {
+ return &null_backend;
+ }
+
+ /*
+ * Select protocol. This is farmed out into a table in a
+ * separate file to enable an ssh-free variant.
+ */
+ const struct BackendVtable *vt = backend_vt_from_proto(
+ conf_get_int(conf, CONF_protocol));
+ if (!vt) {
+ char *str = dupprintf("%s Internal Error", appname);
+ MessageBox(NULL, "Unsupported protocol number found",
+ str, MB_OK | MB_ICONEXCLAMATION);
+ sfree(str);
+ cleanup_exit(1);
+ }
+ return vt;
+}
+
+const wchar_t *get_app_user_model_id(void)
+{
+ return L"SimonTatham.PuTTY";
+}
+
+static void demo_terminal_screenshot(void *ctx, unsigned long now)
+{
+ HWND hwnd = (HWND)ctx;
+ char *err = save_screenshot(hwnd, terminal_demo_screenshot_filename);
+ if (err) {
+ MessageBox(hwnd, err, "Demo screenshot failure", MB_OK | MB_ICONERROR);
+ sfree(err);
+ }
+ cleanup_exit(0);
+}
+
+void gui_terminal_ready(HWND hwnd, Seat *seat, Backend *backend)
+{
+ if (demo_terminal_data) {
+ ptrlen data = ptrlen_from_strbuf(demo_terminal_data);
+ seat_stdout(seat, data.ptr, data.len);
+ schedule_timer(TICKSPERSEC, demo_terminal_screenshot, (void *)hwnd);
+ }
+}
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/winpgen.c b/windows/puttygen.c
index 56c6a8db..457dbfa1 100644
--- a/windows/winpgen.c
+++ b/windows/puttygen.c
@@ -11,7 +11,7 @@
#include "ssh.h"
#include "sshkeygen.h"
#include "licence.h"
-#include "winsecur.h"
+#include "security-api.h"
#include "puttygen-rc.h"
#include <commctrl.h>
@@ -27,6 +27,8 @@
#define DEFAULT_EDCURVE_INDEX 0
static char *cmdline_keyfile = NULL;
+static ptrlen cmdline_demo_keystr;
+static const char *demo_screenshot_filename = NULL;
/*
* Print a modal (Really Bad) message box and perform a fatal exit.
@@ -201,7 +203,7 @@ struct PassphraseProcStruct {
* Dialog-box function for the passphrase box.
*/
static INT_PTR CALLBACK PassphraseProc(HWND hwnd, UINT msg,
- WPARAM wParam, LPARAM lParam)
+ WPARAM wParam, LPARAM lParam)
{
static char **passphrase = NULL;
struct PassphraseProcStruct *p;
@@ -436,9 +438,9 @@ static INT_PTR CALLBACK PPKParamsProc(HWND hwnd, UINT msg,
topic = WINHELP_CTX_puttygen_kdfparam; break;
}
if (topic) {
- launch_help(hwnd, topic);
+ launch_help(hwnd, topic);
} else {
- MessageBeep(0);
+ MessageBeep(0);
}
break;
}
@@ -481,7 +483,7 @@ static bool prompt_keyfile(HWND hwnd, char *dlgtitle,
* Dialog-box function for the Licence box.
*/
static INT_PTR CALLBACK LicenceProc(HWND hwnd, UINT msg,
- WPARAM wParam, LPARAM lParam)
+ WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_INITDIALOG: {
@@ -520,7 +522,7 @@ static INT_PTR CALLBACK LicenceProc(HWND hwnd, UINT msg,
* Dialog-box function for the About box.
*/
static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg,
- WPARAM wParam, LPARAM lParam)
+ WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_INITDIALOG:
@@ -541,10 +543,10 @@ static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg,
{
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.");
+ 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);
@@ -593,7 +595,7 @@ struct rsa_key_thread_params {
bool rsa_strong;
union {
RSAKey *key;
- struct dss_key *dsskey;
+ struct dsa_key *dsakey;
struct ecdsa_key *eckey;
struct eddsa_key *edkey;
};
@@ -610,7 +612,7 @@ static DWORD WINAPI generate_key_thread(void *param)
PrimeGenerationContext *pgc = primegen_new_context(params->primepolicy);
if (params->keytype == DSA)
- dsa_generate(params->dsskey, params->key_bits, pgc, &prog.rec);
+ dsa_generate(params->dsakey, params->key_bits, pgc, &prog.rec);
else if (params->keytype == ECDSA)
ecdsa_generate(params->eckey, params->curve_bits);
else if (params->keytype == EDDSA)
@@ -629,11 +631,21 @@ static DWORD WINAPI generate_key_thread(void *param)
return 0;
}
+struct InitialParams {
+ int keybutton;
+ int primepolicybutton;
+ bool rsa_strong;
+ FingerprintType fptype;
+ int keybits;
+ int eccurve_index, edcurve_index;
+};
+
struct MainDlgState {
- bool collecting_entropy;
bool generation_thread_exists;
bool key_exists;
- int entropy_got, entropy_required, entropy_size;
+ int entropy_got, entropy_required;
+ strbuf *entropy;
+ ULONG entropy_prev_msgtime;
int key_bits, curve_bits;
bool ssh2;
keytype keytype;
@@ -642,16 +654,32 @@ struct MainDlgState {
FingerprintType fptype;
char **commentptr; /* points to key.comment or ssh2key.comment */
ssh2_userkey ssh2key;
- unsigned *entropy;
union {
RSAKey key;
- struct dss_key dsskey;
+ struct dsa_key dsakey;
struct ecdsa_key eckey;
struct eddsa_key edkey;
};
HMENU filemenu, keymenu, cvtmenu;
};
+/*
+ * Rate limit for incrementing the entropy_got counter.
+ *
+ * Some pointing devices (e.g. gaming mice) can be set to send
+ * mouse-movement events at an extremely high sample rate like 1kHz.
+ * In that situation, there's likely to be a strong correlation
+ * between the contents of successive movement events, so you have to
+ * regard the mouse movements as containing less entropy each.
+ *
+ * A reasonably simple approach to this is to continue to buffer all
+ * mouse data, but limit the rate at which we increment the counter
+ * for how much entropy we think we've collected. That way, the user
+ * still has to spend time wiggling the mouse back and forth in a way
+ * that varies with muscle motions and introduces randomness.
+ */
+#define ENTROPY_RATE_LIMIT 10 /* in units of GetMessageTime(), i.e. ms */
+
static void hidemany(HWND hwnd, const int *ids, bool hideit)
{
while (*ids) {
@@ -659,45 +687,6 @@ static void hidemany(HWND hwnd, const int *ids, bool hideit)
}
}
-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,
@@ -707,6 +696,7 @@ enum {
IDC_GENERATING,
IDC_PROGRESS,
IDC_PKSTATIC, IDC_KEYDISPLAY,
+ IDC_CERTSTATIC, IDC_CERTMOREINFO,
IDC_FPSTATIC, IDC_FINGERPRINT,
IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
@@ -730,18 +720,82 @@ enum {
IDC_GIVEHELP,
IDC_IMPORT,
IDC_EXPORT_OPENSSH_AUTO, IDC_EXPORT_OPENSSH_NEW,
- IDC_EXPORT_SSHCOM
+ IDC_EXPORT_SSHCOM,
+ IDC_ADDCERT, IDC_REMCERT,
};
+static void setupbigedit1(HWND hwnd, RSAKey *key)
+{
+ ShowWindow(GetDlgItem(hwnd, IDC_CERTSTATIC), SW_HIDE);
+ ShowWindow(GetDlgItem(hwnd, IDC_CERTMOREINFO), SW_HIDE);
+ ShowWindow(GetDlgItem(hwnd, IDC_PKSTATIC), SW_SHOW);
+ ShowWindow(GetDlgItem(hwnd, IDC_KEYDISPLAY), SW_SHOW);
+
+ SetDlgItemText(hwnd, IDC_PKSTATIC,
+ "&Public key for pasting into authorized_keys file:");
+
+ char *buffer = ssh1_pubkey_str(key);
+ SetDlgItemText(hwnd, IDC_KEYDISPLAY, buffer);
+ sfree(buffer);
+}
+
+static void setupbigedit2(HWND hwnd, ssh2_userkey *key)
+{
+ if (ssh_key_alg(key->key)->is_certificate) {
+ ShowWindow(GetDlgItem(hwnd, IDC_CERTSTATIC), SW_SHOW);
+ ShowWindow(GetDlgItem(hwnd, IDC_CERTMOREINFO), SW_SHOW);
+ ShowWindow(GetDlgItem(hwnd, IDC_PKSTATIC), SW_HIDE);
+ ShowWindow(GetDlgItem(hwnd, IDC_KEYDISPLAY), SW_HIDE);
+
+ SetDlgItemText(hwnd, IDC_CERTSTATIC,
+ "This public key contains an OpenSSH certificate.");
+ } else {
+ ShowWindow(GetDlgItem(hwnd, IDC_CERTSTATIC), SW_HIDE);
+ ShowWindow(GetDlgItem(hwnd, IDC_CERTMOREINFO), SW_HIDE);
+ ShowWindow(GetDlgItem(hwnd, IDC_PKSTATIC), SW_SHOW);
+ ShowWindow(GetDlgItem(hwnd, IDC_KEYDISPLAY), SW_SHOW);
+
+ SetDlgItemText(hwnd, IDC_PKSTATIC, "&Public key for pasting into "
+ "OpenSSH authorized_keys file:");
+
+ char *buffer = ssh2_pubkey_openssh_str(key);
+ SetDlgItemText(hwnd, IDC_KEYDISPLAY, buffer);
+ 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);
+}
+
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,
+static const int gotkey_ids_unconditional[] = {
IDC_FPSTATIC, IDC_FINGERPRINT,
IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
};
+static const int gotkey_ids_conditional[] = {
+ IDC_PKSTATIC, IDC_KEYDISPLAY,
+ IDC_CERTSTATIC, IDC_CERTMOREINFO,
+};
/*
* Small UI helper function to switch the state of the main dialog
@@ -755,7 +809,8 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
case 0: /* no key */
hidemany(hwnd, nokey_ids, false);
hidemany(hwnd, generating_ids, true);
- hidemany(hwnd, gotkey_ids, true);
+ hidemany(hwnd, gotkey_ids_unconditional, true);
+ hidemany(hwnd, gotkey_ids_conditional, true);
EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
@@ -784,11 +839,14 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
MF_GRAYED|MF_BYCOMMAND);
EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
MF_GRAYED|MF_BYCOMMAND);
+ EnableMenuItem(state->keymenu, IDC_ADDCERT, MF_GRAYED|MF_BYCOMMAND);
+ EnableMenuItem(state->keymenu, IDC_REMCERT, MF_GRAYED|MF_BYCOMMAND);
break;
case 1: /* generating key */
hidemany(hwnd, nokey_ids, true);
hidemany(hwnd, generating_ids, false);
- hidemany(hwnd, gotkey_ids, true);
+ hidemany(hwnd, gotkey_ids_unconditional, true);
+ hidemany(hwnd, gotkey_ids_conditional, true);
EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
@@ -817,11 +875,14 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
MF_GRAYED|MF_BYCOMMAND);
EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
MF_GRAYED|MF_BYCOMMAND);
+ EnableMenuItem(state->keymenu, IDC_ADDCERT, MF_GRAYED|MF_BYCOMMAND);
+ EnableMenuItem(state->keymenu, IDC_REMCERT, MF_GRAYED|MF_BYCOMMAND);
break;
case 2:
hidemany(hwnd, nokey_ids, true);
hidemany(hwnd, generating_ids, true);
- hidemany(hwnd, gotkey_ids, false);
+ hidemany(hwnd, gotkey_ids_unconditional, false);
+ // gotkey_ids_conditional will be unhidden by setupbigedit2
EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
@@ -856,6 +917,35 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
do_export_menuitem(IDC_EXPORT_OPENSSH_NEW, SSH_KEYTYPE_OPENSSH_NEW);
do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM);
#undef do_export_menuitem
+ /*
+ * Enable certificate menu items similarly.
+ */
+ {
+ bool add_cert_allowed = false, rem_cert_allowed = false;
+
+ if (state->ssh2 && state->ssh2key.key) {
+ const ssh_keyalg *alg = ssh_key_alg(state->ssh2key.key);
+ if (alg->is_certificate) {
+ /* If there's a certificate, we can remove it */
+ rem_cert_allowed = true;
+ /* And reset to the base algorithm for the next check */
+ alg = alg->base_alg;
+ }
+
+ /* Now, do we have any certified version of this alg? */
+ for (size_t i = 0; i < n_keyalgs; i++) {
+ if (all_keyalgs[i]->base_alg == alg) {
+ add_cert_allowed = true;
+ break;
+ }
+ }
+ }
+
+ EnableMenuItem(state->keymenu, IDC_ADDCERT, MF_BYCOMMAND |
+ (add_cert_allowed ? MF_ENABLED : MF_GRAYED));
+ EnableMenuItem(state->keymenu, IDC_REMCERT, MF_BYCOMMAND |
+ (rem_cert_allowed ? MF_ENABLED : MF_GRAYED));
+ }
break;
}
}
@@ -966,6 +1056,70 @@ void ui_set_fptype(HWND hwnd, struct MainDlgState *state, int option)
}
}
+static void update_ui_after_ssh2_pubkey_change(
+ HWND hwnd, struct MainDlgState *state)
+{
+ /* Smaller version of update_ui_after_load which doesn't need to
+ * be told things like the passphrase, which we aren't changing
+ * anyway */
+ char *savecomment = state->ssh2key.comment;
+ state->ssh2key.comment = NULL;
+ char *fp = ssh2_fingerprint(state->ssh2key.key, state->fptype);
+ state->ssh2key.comment = savecomment;
+
+ SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
+ sfree(fp);
+
+ setupbigedit2(hwnd, &state->ssh2key);
+}
+
+static void update_ui_after_load(HWND hwnd, struct MainDlgState *state,
+ const char *passphrase, int type,
+ RSAKey *newkey1, ssh2_userkey *newkey2)
+{
+ 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; /* structure copy */
+
+ /*
+ * 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, &state->key);
+ } else {
+ state->ssh2 = true;
+ state->commentptr = &state->ssh2key.comment;
+ state->ssh2key = *newkey2; /* structure copy */
+ sfree(newkey2);
+
+ update_ui_after_ssh2_pubkey_change(hwnd, state);
+ }
+ 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;
+}
+
void load_key_file(HWND hwnd, struct MainDlgState *state,
Filename *filename, bool was_import_cmd)
{
@@ -1055,65 +1209,7 @@ void load_key_file(HWND hwnd, struct MainDlgState *state,
* 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;
+ update_ui_after_load(hwnd, state, passphrase, type, &newkey1, newkey2);
/*
* If the user has imported a foreign key
@@ -1136,6 +1232,106 @@ void load_key_file(HWND hwnd, struct MainDlgState *state,
burnstr(passphrase);
}
+void add_certificate(HWND hwnd, struct MainDlgState *state,
+ Filename *filename)
+{
+ int type = key_type(filename);
+ if (type != SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 &&
+ type != SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH) {
+ char *msg = dupprintf("Couldn't load certificate (%s)",
+ key_type_to_str(type));
+ message_box(hwnd, msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
+ HELPCTXID(errors_cantloadkey));
+ sfree(msg);
+ return;
+ }
+
+ char *algname = NULL;
+ char *comment = NULL;
+ const char *error = NULL;
+ strbuf *pub = strbuf_new();
+ if (!ppk_loadpub_f(filename, &algname, BinarySink_UPCAST(pub), &comment,
+ &error)) {
+ char *msg = dupprintf("Couldn't load certificate (%s)", error);
+ message_box(hwnd, msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
+ HELPCTXID(errors_cantloadkey));
+ sfree(msg);
+ strbuf_free(pub);
+ return;
+ }
+
+ sfree(comment);
+
+ const ssh_keyalg *alg = find_pubkey_alg(algname);
+ if (!alg) {
+ char *msg = dupprintf("Couldn't load certificate (unsupported "
+ "algorithm name '%s')", algname);
+ message_box(hwnd, msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
+ HELPCTXID(errors_cantloadkey));
+ sfree(msg);
+ sfree(algname);
+ strbuf_free(pub);
+ return;
+ }
+
+ sfree(algname);
+
+ /* Check the two public keys match apart from certificates */
+ strbuf *old_basepub = strbuf_new();
+ ssh_key_public_blob(ssh_key_base_key(state->ssh2key.key),
+ BinarySink_UPCAST(old_basepub));
+
+ ssh_key *new_pubkey = ssh_key_new_pub(alg, ptrlen_from_strbuf(pub));
+ strbuf *new_basepub = strbuf_new();
+ ssh_key_public_blob(ssh_key_base_key(new_pubkey),
+ BinarySink_UPCAST(new_basepub));
+ ssh_key_free(new_pubkey);
+
+ bool match = ptrlen_eq_ptrlen(ptrlen_from_strbuf(old_basepub),
+ ptrlen_from_strbuf(new_basepub));
+ strbuf_free(old_basepub);
+ strbuf_free(new_basepub);
+
+ if (!match) {
+ char *msg = dupprintf("Certificate is for a different public key");
+ message_box(hwnd, msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
+ HELPCTXID(errors_cantloadkey));
+ sfree(msg);
+ strbuf_free(pub);
+ return;
+ }
+
+ strbuf *priv = strbuf_new_nm();
+ ssh_key_private_blob(state->ssh2key.key, BinarySink_UPCAST(priv));
+ ssh_key *newkey = ssh_key_new_priv(
+ alg, ptrlen_from_strbuf(pub), ptrlen_from_strbuf(priv));
+ strbuf_free(pub);
+ strbuf_free(priv);
+
+ if (!newkey) {
+ char *msg = dupprintf("Couldn't combine certificate with key");
+ message_box(hwnd, msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
+ HELPCTXID(errors_cantloadkey));
+ sfree(msg);
+ return;
+ }
+
+ ssh_key_free(state->ssh2key.key);
+ state->ssh2key.key = newkey;
+
+ update_ui_after_ssh2_pubkey_change(hwnd, state);
+ ui_set_state(hwnd, state, 2);
+}
+
+void remove_certificate(HWND hwnd, struct MainDlgState *state)
+{
+ ssh_key *newkey = ssh_key_clone(ssh_key_base_key(state->ssh2key.key));
+ ssh_key_free(state->ssh2key.key);
+ state->ssh2key.key = newkey;
+ update_ui_after_ssh2_pubkey_change(hwnd, state);
+ ui_set_state(hwnd, state, 2);
+}
+
static void start_generating_key(HWND hwnd, struct MainDlgState *state)
{
static const char generating_msg[] =
@@ -1158,7 +1354,7 @@ static void start_generating_key(HWND hwnd, struct MainDlgState *state)
params->primepolicy = state->primepolicy;
params->rsa_strong = state->rsa_strong;
params->key = &state->key;
- params->dsskey = &state->dsskey;
+ params->dsakey = &state->dsakey;
HANDLE hThread = CreateThread(NULL, 0, generate_key_thread,
params, 0, &threadid);
@@ -1174,11 +1370,136 @@ static void start_generating_key(HWND hwnd, struct MainDlgState *state)
}
/*
+ * Dialog-box function and context structure for the 'Certificate
+ * info' button.
+ */
+struct certinfo_dialog_ctx {
+ SeatDialogText *text;
+};
+
+static INT_PTR CertInfoProc(HWND hwnd, UINT msg, WPARAM wParam,
+ LPARAM lParam, void *vctx)
+{
+ struct certinfo_dialog_ctx *ctx = (struct certinfo_dialog_ctx *)vctx;
+
+ switch (msg) {
+ case WM_INITDIALOG: {
+ int index = 100, y = 12;
+
+ WPARAM font = SendMessage(hwnd, WM_GETFONT, 0, 0);
+
+ const char *key = NULL;
+ for (SeatDialogTextItem *item = ctx->text->items,
+ *end = item + ctx->text->nitems; item < end; item++) {
+ switch (item->type) {
+ case SDT_MORE_INFO_KEY:
+ key = item->text;
+ break;
+ case SDT_MORE_INFO_VALUE_SHORT:
+ case SDT_MORE_INFO_VALUE_BLOB: {
+ RECT rk, rv;
+ DWORD editstyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP |
+ ES_AUTOHSCROLL | ES_READONLY;
+ if (item->type == SDT_MORE_INFO_VALUE_BLOB) {
+ rk.left = 12;
+ rk.right = 286;
+ rk.top = y;
+ rk.bottom = 8;
+ y += 10;
+
+ editstyle |= ES_MULTILINE;
+ rv.left = 12;
+ rv.right = 286;
+ rv.top = y;
+ rv.bottom = 64;
+ y += 68;
+ } else {
+ rk.left = 12;
+ rk.right = 130;
+ rk.top = y+2;
+ rk.bottom = 8;
+
+ rv.left = 150;
+ rv.right = 298;
+ rv.top = y;
+ rv.bottom = 12;
+
+ y += 16;
+ }
+
+ MapDialogRect(hwnd, &rk);
+ HWND ctl = CreateWindowEx(
+ 0, "STATIC", key, WS_CHILD | WS_VISIBLE,
+ rk.left, rk.top, rk.right, rk.bottom,
+ hwnd, (HMENU)(ULONG_PTR)index++, hinst, NULL);
+ SendMessage(ctl, WM_SETFONT, font, MAKELPARAM(true, 0));
+
+ MapDialogRect(hwnd, &rv);
+ ctl = CreateWindowEx(
+ WS_EX_CLIENTEDGE, "EDIT", item->text, editstyle,
+ rv.left, rv.top, rv.right, rv.bottom,
+ hwnd, (HMENU)(ULONG_PTR)index++, hinst, NULL);
+ SendMessage(ctl, WM_SETFONT, font, MAKELPARAM(true, 0));
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ /*
+ * Now resize the overall window, and move the Close button at
+ * the bottom.
+ */
+ RECT r;
+ r.left = 176;
+ r.top = y + 10;
+ r.right = r.bottom = 0;
+ MapDialogRect(hwnd, &r);
+ HWND ctl = GetDlgItem(hwnd, IDOK);
+ SetWindowPos(ctl, NULL, r.left, r.top, 0, 0,
+ SWP_NOSIZE | SWP_NOREDRAW | SWP_NOZORDER);
+
+ r.left = r.top = r.right = 0;
+ r.bottom = 300;
+ MapDialogRect(hwnd, &r);
+ int oldheight = r.bottom;
+
+ r.left = r.top = r.right = 0;
+ r.bottom = y + 30;
+ MapDialogRect(hwnd, &r);
+ int newheight = r.bottom;
+
+ GetWindowRect(hwnd, &r);
+
+ SetWindowPos(hwnd, NULL, 0, 0, r.right - r.left,
+ r.bottom - r.top + newheight - oldheight,
+ SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
+
+ ShowWindow(hwnd, SW_SHOWNORMAL);
+ return 1;
+ }
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDOK:
+ ShinyEndDialog(hwnd, 0);
+ return 0;
+ }
+ return 0;
+ case WM_CLOSE:
+ ShinyEndDialog(hwnd, 0);
+ return 0;
+ }
+ return 0;
+}
+
+/*
* Dialog-box function for the main PuTTYgen dialog box.
*/
static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
- WPARAM wParam, LPARAM lParam)
+ WPARAM wParam, LPARAM lParam)
{
+ const int DEMO_SCREENSHOT_TIMER_ID = 1230;
static const char entropy_msg[] =
"Please generate some randomness by moving the mouse over the blank area.";
struct MainDlgState *state;
@@ -1200,7 +1521,6 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
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);
@@ -1221,6 +1541,11 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
menu1 = CreateMenu();
AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
AppendMenu(menu1, MF_SEPARATOR, 0, 0);
+ AppendMenu(menu1, MF_ENABLED, IDC_ADDCERT,
+ "Add &certificate to key");
+ AppendMenu(menu1, MF_ENABLED, IDC_REMCERT,
+ "Remove certificate from key");
+ 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");
@@ -1297,6 +1622,18 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
cp2 = cp;
statictext(&cp2, "", 1, IDC_GENERATING);
progressbar(&cp2, IDC_PROGRESS);
+ cp2 = cp;
+ bigeditctrl(&cp2, NULL, -1, IDC_CERTSTATIC, 3);
+ {
+ HWND child = GetDlgItem(hwnd, IDC_CERTSTATIC);
+ LONG_PTR style = GetWindowLongPtr(child, GWL_STYLE);
+ style &= ~WS_VSCROLL;
+ SetWindowLongPtr(child, GWL_STYLE, style);
+ SendMessage(child, EM_SETREADONLY, true, 0);
+ }
+ MakeDlgItemBorderless(hwnd, IDC_CERTSTATIC);
+ cp2.xoff = cp2.width = cp2.width / 3;
+ button(&cp2, "Certificate info...", IDC_CERTMOREINFO, false);
bigeditctrl(&cp,
"&Public key for pasting into authorized_keys file:",
IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
@@ -1377,15 +1714,16 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
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);
+ struct InitialParams *params = (struct InitialParams *)lParam;
+ ui_set_key_type(hwnd, state, params->keybutton);
+ ui_set_primepolicy(hwnd, state, params->primepolicybutton);
+ ui_set_rsa_strong(hwnd, state, params->rsa_strong);
+ ui_set_fptype(hwnd, state, fptype_to_idc(params->fptype));
+ SetDlgItemInt(hwnd, IDC_BITS, params->keybits, false);
SendDlgItemMessage(hwnd, IDC_ECCURVE, CB_SETCURSEL,
- DEFAULT_ECCURVE_INDEX, 0);
+ params->eccurve_index, 0);
SendDlgItemMessage(hwnd, IDC_EDCURVE, CB_SETCURSEL,
- DEFAULT_EDCURVE_INDEX, 0);
+ params->edcurve_index, 0);
/*
* Initially, hide the progress bar and the key display,
@@ -1402,26 +1740,51 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
Filename *fn = filename_from_str(cmdline_keyfile);
load_key_file(hwnd, state, fn, false);
filename_free(fn);
+ } else if (cmdline_demo_keystr.ptr) {
+ BinarySource src[1];
+ BinarySource_BARE_INIT_PL(src, cmdline_demo_keystr);
+ const char *errmsg;
+ ssh2_userkey *k = ppk_load_s(src, NULL, &errmsg);
+ assert(!errmsg);
+
+ update_ui_after_load(hwnd, state, "demo passphrase",
+ SSH_KEYTYPE_SSH2, NULL, k);
+
+ SetTimer(hwnd, DEMO_SCREENSHOT_TIMER_ID, TICKSPERSEC, NULL);
}
return 1;
+ case WM_TIMER:
+ if ((UINT_PTR)wParam == DEMO_SCREENSHOT_TIMER_ID) {
+ KillTimer(hwnd, DEMO_SCREENSHOT_TIMER_ID);
+ char *err = save_screenshot(hwnd, demo_screenshot_filename);
+ if (err) {
+ MessageBox(hwnd, err, "Demo screenshot failure",
+ MB_OK | MB_ICONERROR);
+ sfree(err);
+ }
+ EndDialog(hwnd, 0);
+ }
+ return 0;
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();
+ if (state->entropy && state->entropy_got < state->entropy_required) {
+ ULONG msgtime = GetMessageTime();
+ put_uint32(state->entropy, lParam);
+ put_uint32(state->entropy, msgtime);
+ if (msgtime - state->entropy_prev_msgtime > ENTROPY_RATE_LIMIT) {
+ state->entropy_got += 2;
+ state->entropy_prev_msgtime = msgtime;
+ }
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;
+ random_reseed(ptrlen_from_strbuf(state->entropy));
+ strbuf_free(state->entropy);
+ state->entropy = NULL;
start_generating_key(hwnd, state);
}
@@ -1497,11 +1860,9 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
*state->commentptr = snewn(len + 1, char);
GetWindowText(editctl, *state->commentptr, len + 1);
if (state->ssh2) {
- setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
- &state->ssh2key);
+ setupbigedit2(hwnd, &state->ssh2key);
} else {
- setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
- &state->key);
+ setupbigedit1(hwnd, &state->key);
}
}
}
@@ -1562,10 +1923,10 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
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);
+ 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);
@@ -1575,9 +1936,9 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
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);
+ 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);
@@ -1635,12 +1996,10 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
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);
+ state->entropy = strbuf_new_nm();
+ state->entropy_prev_msgtime = GetMessageTime();
SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
MAKELPARAM(0, state->entropy_required));
@@ -1819,6 +2178,47 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
}
}
break;
+ case IDC_ADDCERT:
+ if (HIWORD(wParam) != BN_CLICKED)
+ break;
+ state =
+ (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ if (state->key_exists && !state->generation_thread_exists) {
+ char filename[FILENAME_MAX];
+ if (prompt_keyfile(hwnd, "Load certificate:", filename, false,
+ false)) {
+ Filename *fn = filename_from_str(filename);
+ add_certificate(hwnd, state, fn);
+ filename_free(fn);
+ }
+ }
+ break;
+ case IDC_REMCERT:
+ if (HIWORD(wParam) != BN_CLICKED)
+ break;
+ state =
+ (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ if (state->key_exists && !state->generation_thread_exists) {
+ remove_certificate(hwnd, state);
+ }
+ break;
+ case IDC_CERTMOREINFO: {
+ if (HIWORD(wParam) != BN_CLICKED)
+ break;
+ state =
+ (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ if (!state->key_exists || !state->ssh2 || !state->ssh2key.key)
+ break;
+ if (!ssh_key_alg(state->ssh2key.key)->is_certificate)
+ break;
+
+ struct certinfo_dialog_ctx ctx[1];
+ ctx->text = ssh_key_cert_info(state->ssh2key.key);
+ ShinyDialogBox(hinst, MAKEINTRESOURCE(216),
+ "PuTTYgenCertInfo", hwnd, CertInfoProc, ctx);
+ seat_dialog_text_free(ctx->text);
+ break;
+ }
}
return 0;
case WM_DONEKEY:
@@ -1830,7 +2230,7 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
if (state->ssh2) {
if (state->keytype == DSA) {
- state->ssh2key.key = &state->dsskey.sshk;
+ state->ssh2key.key = &state->dsakey.sshk;
} else if (state->keytype == ECDSA) {
state->ssh2key.key = &state->eckey.sshk;
} else if (state->keytype == EDDSA) {
@@ -1896,11 +2296,9 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
* .ssh/authorized_keys2 on a Unix box.
*/
if (state->ssh2) {
- setupbigedit2(hwnd, IDC_KEYDISPLAY,
- IDC_PKSTATIC, &state->ssh2key);
+ setupbigedit2(hwnd, &state->ssh2key);
} else {
- setupbigedit1(hwnd, IDC_KEYDISPLAY,
- IDC_PKSTATIC, &state->key);
+ setupbigedit1(hwnd, &state->key);
}
}
/*
@@ -1956,9 +2354,9 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
topic = WINHELP_CTX_puttygen_conversions; break;
}
if (topic) {
- launch_help(hwnd, topic);
+ launch_help(hwnd, topic);
} else {
- MessageBeep(0);
+ MessageBeep(0);
}
break;
}
@@ -1980,11 +2378,24 @@ void cleanup_exit(int code)
HINSTANCE hinst;
+static NORETURN void opt_error(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ char *msg = dupvprintf(fmt, ap);
+ va_end(ap);
+
+ MessageBox(NULL, msg, "PuTTYgen command line error", MB_ICONERROR | MB_OK);
+
+ exit(1);
+}
+
int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
{
- int argc, i;
+ int argc;
char **argv;
int ret;
+ struct InitialParams params[1];
dll_hijacking_protection();
@@ -1996,30 +2407,205 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
*/
init_help();
+ params->keybutton = IDC_KEYSSH2RSA;
+ params->primepolicybutton = IDC_PRIMEGEN_PROB;
+ params->rsa_strong = false;
+ params->fptype = SSH_FPTYPE_DEFAULT;
+ params->keybits = DEFAULT_KEY_BITS;
+ params->eccurve_index = DEFAULT_ECCURVE_INDEX;
+ params->edcurve_index = DEFAULT_EDCURVE_INDEX;
+
+ save_params = ppk_save_default_parameters;
+
split_into_argv(cmdline, &argc, &argv, NULL);
- for (i = 0; i < argc; i++) {
- if (!strcmp(argv[i], "-pgpfp")) {
+ int argbits = -1;
+ AuxMatchOpt amo = aux_match_opt_init(argc, argv, 0, opt_error);
+ while (!aux_match_done(&amo)) {
+ char *val;
+ #define match_opt(...) aux_match_opt( \
+ &amo, NULL, __VA_ARGS__, (const char *)NULL)
+ #define match_optval(...) aux_match_opt( \
+ &amo, &val, __VA_ARGS__, (const char *)NULL)
+
+ if (aux_match_arg(&amo, &val)) {
+ if (!cmdline_keyfile) {
+ /*
+ * Assume the first argument to be a private key file, and
+ * attempt to load it.
+ */
+ cmdline_keyfile = val;
+ continue;
+ } else {
+ opt_error("unexpected extra argument '%s'\n", val);
+ }
+ } else if (match_opt("-pgpfp")) {
pgp_fingerprints_msgbox(NULL);
return 1;
- } else if (!strcmp(argv[i], "-restrict-acl") ||
- !strcmp(argv[i], "-restrict_acl") ||
- !strcmp(argv[i], "-restrictacl")) {
+ } else if (match_opt("-restrict-acl", "-restrict_acl",
+ "-restrictacl")) {
restrict_process_acl();
+ } else if (match_optval("-t")) {
+ if (!strcmp(val, "rsa") || !strcmp(val, "rsa2")) {
+ params->keybutton = IDC_KEYSSH2RSA;
+ } else if (!strcmp(val, "rsa1")) {
+ params->keybutton = IDC_KEYSSH1;
+ } else if (!strcmp(val, "dsa") || !strcmp(val, "dss")) {
+ params->keybutton = IDC_KEYSSH2DSA;
+ } else if (!strcmp(val, "ecdsa")) {
+ params->keybutton = IDC_KEYSSH2ECDSA;
+ } else if (!strcmp(val, "eddsa")) {
+ params->keybutton = IDC_KEYSSH2EDDSA;
+ } else if (!strcmp(val, "ed25519")) {
+ params->keybutton = IDC_KEYSSH2EDDSA;
+ argbits = 255;
+ } else if (!strcmp(val, "ed448")) {
+ params->keybutton = IDC_KEYSSH2EDDSA;
+ argbits = 448;
+ } else {
+ opt_error("unknown key type '%s'\n", val);
+ }
+ } else if (match_optval("-b")) {
+ argbits = atoi(val);
+ } else if (match_optval("-E")) {
+ if (!strcmp(val, "md5"))
+ params->fptype = SSH_FPTYPE_MD5;
+ else if (!strcmp(val, "sha256"))
+ params->fptype = SSH_FPTYPE_SHA256;
+ else
+ opt_error("unknown fingerprint type '%s'\n", val);
+ } else if (match_optval("-primes")) {
+ if (!strcmp(val, "probable") ||
+ !strcmp(val, "probabilistic")) {
+ params->primepolicybutton = IDC_PRIMEGEN_PROB;
+ } else if (!strcmp(val, "provable") ||
+ !strcmp(val, "proven") ||
+ !strcmp(val, "simple") ||
+ !strcmp(val, "maurer-simple")) {
+ params->primepolicybutton = IDC_PRIMEGEN_MAURER_SIMPLE;
+ } else if (!strcmp(val, "provable-even") ||
+ !strcmp(val, "proven-even") ||
+ !strcmp(val, "even") ||
+ !strcmp(val, "complex") ||
+ !strcmp(val, "maurer-complex")) {
+ params->primepolicybutton = IDC_PRIMEGEN_MAURER_COMPLEX;
+ } else {
+ opt_error("unrecognised prime-generation mode '%s'\n", val);
+ }
+ } else if (match_opt("-strong-rsa")) {
+ params->rsa_strong = true;
+ } else if (match_optval("-ppk-param", "-ppk-params")) {
+ char *nextval;
+ for (; val; val = nextval) {
+ nextval = strchr(val, ',');
+ if (nextval)
+ *nextval++ = '\0';
+
+ char *optvalue = strchr(val, '=');
+ if (!optvalue)
+ opt_error("PPK parameter '%s' expected a value\n", val);
+ *optvalue++ = '\0';
+
+ /* Non-numeric options */
+ if (!strcmp(val, "kdf")) {
+ if (!strcmp(optvalue, "Argon2id") ||
+ !strcmp(optvalue, "argon2id")) {
+ save_params.argon2_flavour = Argon2id;
+ } else if (!strcmp(optvalue, "Argon2i") ||
+ !strcmp(optvalue, "argon2i")) {
+ save_params.argon2_flavour = Argon2i;
+ } else if (!strcmp(optvalue, "Argon2d") ||
+ !strcmp(optvalue, "argon2d")) {
+ save_params.argon2_flavour = Argon2d;
+ } else {
+ opt_error("unrecognised kdf '%s'\n", optvalue);
+ }
+ continue;
+ }
+
+ char *end;
+ unsigned long n = strtoul(optvalue, &end, 0);
+ if (!*optvalue || *end)
+ opt_error("value '%s' for PPK parameter '%s': expected a "
+ "number\n", optvalue, val);
+
+ if (!strcmp(val, "version")) {
+ save_params.fmt_version = n;
+ } else if (!strcmp(val, "memory") ||
+ !strcmp(val, "mem")) {
+ save_params.argon2_mem = n;
+ } else if (!strcmp(val, "time")) {
+ save_params.argon2_passes_auto = true;
+ save_params.argon2_milliseconds = n;
+ } else if (!strcmp(val, "passes")) {
+ save_params.argon2_passes_auto = false;
+ save_params.argon2_passes = n;
+ } else if (!strcmp(val, "parallelism") ||
+ !strcmp(val, "parallel")) {
+ save_params.argon2_parallelism = n;
+ } else {
+ opt_error("unrecognised PPK parameter '%s'\n", val);
+ }
+ }
+ } else if (match_optval("-demo-screenshot")) {
+ demo_screenshot_filename = val;
+ cmdline_demo_keystr = PTRLEN_LITERAL(
+ "PuTTY-User-Key-File-3: ssh-ed25519\n"
+ "Encryption: none\n"
+ "Comment: ed25519-key-20220402\n"
+ "Public-Lines: 2\n"
+ "AAAAC3NzaC1lZDI1NTE5AAAAILzuIFwZ"
+ "8ZhgOlilcSb+9zPuCf/DmKJiloVlmWGy\n"
+ "xa/F\n"
+ "Private-Lines: 1\n"
+ "AAAAIPca6vLwtB2NJhZUpABQISR0gcQH8jjQLta19VyzA3wc\n"
+ "Private-MAC: 1159e9628259b35933b397379bbe8a14"
+ "a1f1d97fe91e446e45a9581a3408b70e\n");
+ params->keybutton = IDC_KEYSSH2EDDSA;
+ argbits = 255;
} else {
- /*
- * Assume the first argument to be a private key file, and
- * attempt to load it.
- */
- cmdline_keyfile = argv[i];
- break;
+ opt_error("unrecognised option '%s'\n", amo.argv[amo.index]);
}
}
- save_params = ppk_save_default_parameters;
+ /* Translate argbits into eccurve_index and edcurve_index */
+ if (argbits > 0) {
+ switch (params->keybutton) {
+ case IDC_KEYSSH2RSA:
+ case IDC_KEYSSH1:
+ case IDC_KEYSSH2DSA:
+ params->keybits = argbits;
+ break;
+ case IDC_KEYSSH2ECDSA: {
+ bool found = false;
+ for (int j = 0; j < n_ec_nist_curve_lengths; j++)
+ if (argbits == ec_nist_curve_lengths[j]) {
+ params->eccurve_index = j;
+ found = true;
+ break;
+ }
+ if (!found)
+ opt_error("unsupported ECDSA bit length %d", argbits);
+ break;
+ }
+ case IDC_KEYSSH2EDDSA: {
+ bool found = false;
+ for (int j = 0; j < n_ec_ed_curve_lengths; j++)
+ if (argbits == ec_ed_curve_lengths[j]) {
+ params->edcurve_index = j;
+ found = true;
+ break;
+ }
+ if (!found)
+ opt_error("unsupported EDDSA bit length %d", argbits);
+ break;
+ }
+ }
+ }
random_setup_special();
- ret = DialogBox(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc) != IDOK;
+ ret = DialogBoxParam(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc,
+ (LPARAM)params) != IDOK;
cleanup_exit(ret);
return ret; /* just in case optimiser complains */
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/winsecur.h b/windows/security-api.h
index fdd39d81..175486ef 100644
--- a/windows/winsecur.h
+++ b/windows/security-api.h
@@ -1,11 +1,9 @@
/*
- * winsecur.h: some miscellaneous security-related helper functions,
- * defined in winsecur.c, that use the advapi32 library. Also
+ * security-api.h: some miscellaneous security-related helper functions,
+ * defined in utils/security.c, that use the advapi32 library. Also
* centralises the machinery for dynamically loading that library.
*/
-#if !defined NO_SECURITY
-
#include <aclapi.h>
/*
@@ -49,5 +47,3 @@ PSID get_user_sid(void);
*/
bool make_private_security_descriptor(
DWORD permissions, PSECURITY_DESCRIPTOR *psd, PACL *acl, char **error);
-
-#endif
diff --git a/windows/winselcli.c b/windows/select-cli.c
index f19a0bbe..9bf8411e 100644
--- a/windows/winselcli.c
+++ b/windows/select-cli.c
@@ -1,5 +1,5 @@
/*
- * Implementation of do_select() for winnet.c to use, suitable for use
+ * Implementation of do_select() for network.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
diff --git a/windows/select-gui.c b/windows/select-gui.c
new file mode 100644
index 00000000..4c37c345
--- /dev/null
+++ b/windows/select-gui.c
@@ -0,0 +1,65 @@
+/*
+ * Implementation of do_select() for network.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;
+}
+
+struct wm_netevent_params {
+ /* Used to pass data to wm_netevent_callback */
+ WPARAM wParam;
+ LPARAM lParam;
+};
+
+static void wm_netevent_callback(void *vctx)
+{
+ struct wm_netevent_params *params = (struct wm_netevent_params *)vctx;
+ select_result(params->wParam, params->lParam);
+ sfree(params);
+}
+
+void winselgui_response(WPARAM wParam, LPARAM lParam)
+{
+ /*
+ * To protect against re-entrancy when Windows's recv()
+ * immediately triggers a new WSAAsyncSelect window message, we
+ * don't call select_result directly from this handler but instead
+ * wait until we're back out at the top level of the message loop.
+ */
+ struct wm_netevent_params *params = snew(struct wm_netevent_params);
+ params->wParam = wParam;
+ params->lParam = lParam;
+ queue_toplevel_callback(wm_netevent_callback, params);
+}
diff --git a/windows/winser.c b/windows/serial.c
index 7f4bcf2e..14a89822 100644
--- a/windows/winser.c
+++ b/windows/serial.c
@@ -73,7 +73,8 @@ static size_t serial_gotdata(
}
}
-static void serial_sentdata(struct handle *h, size_t new_backlog, int err)
+static void serial_sentdata(struct handle *h, size_t new_backlog, int err,
+ bool close)
{
Serial *serial = (Serial *)handle_get_privdata(h);
if (err) {
@@ -88,6 +89,7 @@ static void serial_sentdata(struct handle *h, size_t new_backlog, int err)
seat_connection_fatal(serial->seat, "%s", error_msg);
} else {
serial->bufsize = new_backlog;
+ seat_sent(serial->seat, serial->bufsize);
}
}
@@ -210,6 +212,7 @@ static char *serial_init(const BackendVtable *vt, Seat *seat,
seat_set_trust_status(seat, false);
serial = snew(Serial);
+ memset(serial, 0, sizeof(Serial));
serial->port = INVALID_HANDLE_VALUE;
serial->out = serial->in = NULL;
serial->bufsize = 0;
@@ -305,15 +308,14 @@ static void serial_reconfig(Backend *be, Conf *conf)
/*
* Called to send data down the serial connection.
*/
-static size_t serial_send(Backend *be, const char *buf, size_t len)
+static void serial_send(Backend *be, const char *buf, size_t len)
{
Serial *serial = container_of(be, Serial, backend);
if (serial->out == NULL)
- return 0;
+ return;
serial->bufsize = handle_write(serial->out, buf, len);
- return serial->bufsize;
}
/*
@@ -451,7 +453,8 @@ const BackendVtable serial_backend = {
.unthrottle = serial_unthrottle,
.cfg_info = serial_cfg_info,
.id = "serial",
- .displayname = "Serial",
+ .displayname_tc = "Serial",
+ .displayname_lc = "serial",
.protocol = PROT_SERIAL,
.serial_parity_mask = ((1 << SER_PAR_NONE) |
(1 << SER_PAR_ODD) |
diff --git a/windows/winsftp.c b/windows/sftp.c
index 0c695d2e..a6546269 100644
--- a/windows/winsftp.c
+++ b/windows/sftp.c
@@ -1,5 +1,5 @@
/*
- * winsftp.c: the Windows-specific parts of PSFTP and PSCP.
+ * sftp.c: the Windows-specific parts of PSFTP and PSCP.
*/
#include <winsock2.h> /* need to put this first, for winelib builds */
@@ -10,15 +10,21 @@
#include "putty.h"
#include "psftp.h"
#include "ssh.h"
-#include "winsecur.h"
+#include "security-api.h"
-int filexfer_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input)
+SeatPromptResult filexfer_get_userpass_input(Seat *seat, prompts_t *p)
{
- int ret;
- ret = cmdline_get_passwd_input(p);
- if (ret == -1)
- ret = console_get_userpass_input(p);
- return ret;
+ /* The file transfer tools don't support Restart Session, so we
+ * can just have a single static cmdline_get_passwd_input_state
+ * that's never reset */
+ static cmdline_get_passwd_input_state cmdline_state =
+ CMDLINE_GET_PASSWD_INPUT_STATE_INIT;
+
+ SeatPromptResult spr;
+ spr = cmdline_get_passwd_input(p, &cmdline_state, false);
+ if (spr.kind == SPRK_INCOMPLETE)
+ spr = console_get_userpass_input(p);
+ return spr;
}
void platform_get_x11_auth(struct X11Display *display, Conf *conf)
@@ -216,16 +222,16 @@ int seek_file(WFile *f, uint64_t offset, int whence)
DWORD movemethod;
switch (whence) {
- case FROM_START:
+ case FROM_START:
movemethod = FILE_BEGIN;
break;
- case FROM_CURRENT:
+ case FROM_CURRENT:
movemethod = FILE_CURRENT;
break;
- case FROM_END:
+ case FROM_END:
movemethod = FILE_END;
break;
- default:
+ default:
return -1;
}
diff --git a/windows/winshare.c b/windows/sharing.c
index f0e409ac..15b1da48 100644
--- a/windows/winshare.c
+++ b/windows/sharing.c
@@ -5,16 +5,14 @@
#include <stdio.h>
#include <assert.h>
-#if !defined NO_SECURITY
-
#include "tree234.h"
#include "putty.h"
#include "network.h"
-#include "proxy.h"
+#include "proxy/proxy.h"
#include "ssh.h"
-#include "wincapi.h"
-#include "winsecur.h"
+#include "cryptoapi.h"
+#include "security-api.h"
#define CONNSHARE_PIPE_PREFIX "\\\\.\\pipe\\putty-connshare"
#define CONNSHARE_MUTEX_PREFIX "Local\\putty-connshare-mutex"
@@ -38,8 +36,6 @@ int platform_ssh_share(const char *pi_name, Conf *conf,
char *name, *mutexname, *pipename;
HANDLE mutex;
Socket *retsock;
- PSECURITY_DESCRIPTOR psd;
- PACL acl;
/*
* Transform the platform-independent version of the connection
@@ -59,39 +55,12 @@ int platform_ssh_share(const char *pi_name, Conf *conf,
* 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;
- }
-
+ mutexname = make_name(CONNSHARE_MUTEX_PREFIX, name);
+ mutex = lock_interprocess_mutex(mutexname, logtext);
+ if (!mutex) {
sfree(mutexname);
- LocalFree(psd);
- LocalFree(acl);
-
- WaitForSingleObject(mutex, INFINITE);
+ sfree(name);
+ return SHARE_NONE;
}
pipename = make_name(CONNSHARE_PIPE_PREFIX, name);
@@ -105,8 +74,7 @@ int platform_ssh_share(const char *pi_name, Conf *conf,
*logtext = pipename;
*sock = retsock;
sfree(name);
- ReleaseMutex(mutex);
- CloseHandle(mutex);
+ unlock_interprocess_mutex(mutex);
return SHARE_DOWNSTREAM;
}
sfree(*ds_err);
@@ -143,9 +111,3 @@ int platform_ssh_share(const char *pi_name, Conf *conf,
void platform_ssh_share_cleanup(const char *name)
{
}
-
-#else /* !defined NO_SECURITY */
-
-#include "noshare.c"
-
-#endif /* !defined NO_SECURITY */
diff --git a/windows/winstore.c b/windows/storage.c
index 09e5c028..f2c33feb 100644
--- a/windows/winstore.c
+++ b/windows/storage.c
@@ -1,5 +1,5 @@
/*
- * winstore.c: Windows-specific implementation of the interface
+ * storage.c: Windows-specific implementation of the interface
* defined in storage.h.
*/
@@ -21,6 +21,7 @@
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 const char *const host_ca_key = PUTTY_REG_POS "\\SshHostCAs";
static bool tried_shgetfolderpath = false;
static HMODULE shell32_module = NULL;
@@ -33,28 +34,16 @@ struct settings_w {
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();
+ strbuf *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) {
+ HKEY sesskey = open_regkey(true, HKEY_CURRENT_USER, puttystr, sb->s);
+ if (!sesskey) {
*errmsg = dupprintf("Unable to create registry key\n"
"HKEY_CURRENT_USER\\%s\\%s", puttystr, sb->s);
strbuf_free(sb);
@@ -70,20 +59,18 @@ settings_w *open_settings_w(const char *sessionname, char **errmsg)
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));
+ put_reg_sz(handle->sesskey, key, 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));
+ put_reg_dword(handle->sesskey, key, value);
}
void close_settings_w(settings_w *handle)
{
- RegCloseKey(handle->sesskey);
+ close_regkey(handle->sesskey);
sfree(handle);
}
@@ -93,24 +80,12 @@ struct settings_r {
settings_r *open_settings_r(const char *sessionname)
{
- HKEY subkey1, sesskey;
- strbuf *sb;
-
if (!sessionname || !*sessionname)
sessionname = "Default Settings";
- sb = strbuf_new();
+ strbuf *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);
- }
-
+ HKEY sesskey = open_regkey(false, HKEY_CURRENT_USER, puttystr, sb->s);
strbuf_free(sb);
if (!sesskey)
@@ -123,42 +98,15 @@ settings_r *open_settings_r(const char *sessionname)
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;
+ return get_reg_sz(handle->sesskey, key);
}
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)
+ DWORD val;
+ if (!handle || !get_reg_dword(handle->sesskey, key, &val))
return defvalue;
else
return val;
@@ -241,25 +189,23 @@ void write_setting_filename(settings_w *handle,
void close_settings_r(settings_r *handle)
{
if (handle) {
- RegCloseKey(handle->sesskey);
+ close_regkey(handle->sesskey);
sfree(handle);
}
}
void del_settings(const char *sessionname)
{
- HKEY subkey1;
- strbuf *sb;
-
- if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &subkey1) != ERROR_SUCCESS)
+ HKEY rkey = open_regkey(false, HKEY_CURRENT_USER, puttystr);
+ if (!rkey)
return;
- sb = strbuf_new();
+ strbuf *sb = strbuf_new();
escape_registry_key(sessionname, sb);
- RegDeleteKey(subkey1, sb->s);
+ del_regkey(rkey, sb->s);
strbuf_free(sb);
- RegCloseKey(subkey1);
+ close_regkey(rkey);
remove_session_from_jumplist(sessionname);
}
@@ -271,13 +217,11 @@ struct settings_e {
settings_e *enum_settings_start(void)
{
- settings_e *ret;
- HKEY key;
-
- if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &key) != ERROR_SUCCESS)
+ HKEY key = open_regkey(false, HKEY_CURRENT_USER, puttystr);
+ if (!key)
return NULL;
- ret = snew(settings_e);
+ settings_e *ret = snew(settings_e);
if (ret) {
ret->key = key;
ret->i = 0;
@@ -288,85 +232,56 @@ settings_e *enum_settings_start(void)
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);
+ char *name = enum_regkey(e->key, e->i);
+ if (!name)
+ return false;
+ unescape_registry_key(name, sb);
+ sfree(name);
e->i++;
- sfree(regbuf);
- return success;
+ return true;
}
void enum_settings_finish(settings_e *e)
{
- RegCloseKey(e->key);
+ close_regkey(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);
+ put_fmt(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)
+int check_stored_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.
+ * Read a saved key in from the registry and see what it says.
*/
- regname = strbuf_new();
+ strbuf *regname = strbuf_new();
hostkey_regname(regname, hostname, port, keytype);
- if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys",
- &rkey) != ERROR_SUCCESS) {
+ HKEY rkey = open_regkey(false, HKEY_CURRENT_USER,
+ PUTTY_REG_POS "\\SshHostKeys");
+ if (!rkey) {
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")) {
+ char *otherstr = get_reg_sz(rkey, regname->s);
+ if (!otherstr && !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);
+ char *oldstyle = get_reg_sz(rkey, justhost);
- if (ret == ERROR_SUCCESS && type == REG_SZ) {
+ if (oldstyle) {
/*
* The old format is two old-style bignums separated by
* a slash. An old-style bignum is made of groups of
@@ -379,29 +294,26 @@ int verify_host_key(const char *hostname, int port,
* 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;
+ strbuf *new = strbuf_new();
+ const char *q = oldstyle;
int i, j;
for (i = 0; i < 2; i++) {
int ndigits, nwords;
- *p++ = '0';
- *p++ = 'x';
+ put_datapl(new, PTRLEN_LITERAL("0x"));
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;
+ for (j = ndigits; j-- > 0 ;)
+ put_byte(new, q[j ^ 3]);
q += nwords * 4;
if (*q) {
- q++; /* eat the slash */
- *p++ = ','; /* add a comma */
+ q++; /* eat the slash */
+ put_byte(new, ','); /* add a comma */
}
- *p = '\0'; /* terminate the string */
}
/*
@@ -409,59 +321,192 @@ int verify_host_key(const char *hostname, int port,
* 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);
+ if (!strcmp(new->s, key)) {
+ put_reg_sz(rkey, regname->s, new->s);
+ otherstr = strbuf_to_str(new);
+ } else {
+ strbuf_free(new);
+ }
}
sfree(oldstyle);
}
- RegCloseKey(rkey);
+ close_regkey(rkey);
- compare = strcmp(otherstr, key);
+ int compare = otherstr ? strcmp(otherstr, key) : -1;
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)
+ if (!otherstr)
return 1; /* key does not exist in registry */
+ else if (compare)
+ return 2; /* key is different in registry */
else
return 0; /* key matched OK in registry */
}
bool have_ssh_host_key(const char *hostname, int port,
- const char *keytype)
+ const char *keytype)
{
/*
- * If we have a host key, verify_host_key will return 0 or 2.
+ * If we have a host key, check_stored_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;
+ return check_stored_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();
+ strbuf *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);
+ HKEY rkey = open_regkey(true, HKEY_CURRENT_USER,
+ PUTTY_REG_POS "\\SshHostKeys");
+ if (rkey) {
+ put_reg_sz(rkey, regname->s, key);
+ close_regkey(rkey);
} /* else key does not exist in registry */
strbuf_free(regname);
}
+struct host_ca_enum {
+ HKEY key;
+ int i;
+};
+
+host_ca_enum *enum_host_ca_start(void)
+{
+ host_ca_enum *e;
+ HKEY key;
+
+ if (!(key = open_regkey(false, HKEY_CURRENT_USER, host_ca_key)))
+ return NULL;
+
+ e = snew(host_ca_enum);
+ e->key = key;
+ e->i = 0;
+
+ return e;
+}
+
+bool enum_host_ca_next(host_ca_enum *e, strbuf *sb)
+{
+ char *regbuf = enum_regkey(e->key, e->i);
+ if (!regbuf)
+ return false;
+
+ unescape_registry_key(regbuf, sb);
+ sfree(regbuf);
+ e->i++;
+ return true;
+}
+
+void enum_host_ca_finish(host_ca_enum *e)
+{
+ close_regkey(e->key);
+ sfree(e);
+}
+
+host_ca *host_ca_load(const char *name)
+{
+ strbuf *sb;
+ const char *s;
+
+ sb = strbuf_new();
+ escape_registry_key(name, sb);
+ HKEY rkey = open_regkey(false, HKEY_CURRENT_USER, host_ca_key, sb->s);
+ strbuf_free(sb);
+
+ if (!rkey)
+ return NULL;
+
+ host_ca *hca = host_ca_new();
+ hca->name = dupstr(name);
+
+ DWORD val;
+
+ if ((s = get_reg_sz(rkey, "PublicKey")) != NULL)
+ hca->ca_public_key = base64_decode_sb(ptrlen_from_asciz(s));
+
+ if ((s = get_reg_sz(rkey, "Validity")) != NULL) {
+ hca->validity_expression = strbuf_to_str(
+ percent_decode_sb(ptrlen_from_asciz(s)));
+ } else if ((sb = get_reg_multi_sz(rkey, "MatchHosts")) != NULL) {
+ BinarySource src[1];
+ BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(sb));
+ CertExprBuilder *eb = cert_expr_builder_new();
+
+ const char *wc;
+ while (wc = get_asciz(src), !get_err(src))
+ cert_expr_builder_add(eb, wc);
+
+ hca->validity_expression = cert_expr_expression(eb);
+ cert_expr_builder_free(eb);
+ }
+
+ if (get_reg_dword(rkey, "PermitRSASHA1", &val))
+ hca->opts.permit_rsa_sha1 = val;
+ if (get_reg_dword(rkey, "PermitRSASHA256", &val))
+ hca->opts.permit_rsa_sha256 = val;
+ if (get_reg_dword(rkey, "PermitRSASHA512", &val))
+ hca->opts.permit_rsa_sha512 = val;
+
+ close_regkey(rkey);
+ return hca;
+}
+
+char *host_ca_save(host_ca *hca)
+{
+ if (!*hca->name)
+ return dupstr("CA record must have a name");
+
+ strbuf *sb = strbuf_new();
+ escape_registry_key(hca->name, sb);
+ HKEY rkey = open_regkey(true, HKEY_CURRENT_USER, host_ca_key, sb->s);
+ if (!rkey) {
+ char *err = dupprintf("Unable to create registry key\n"
+ "HKEY_CURRENT_USER\\%s\\%s", host_ca_key, sb->s);
+ strbuf_free(sb);
+ return err;
+ }
+ strbuf_free(sb);
+
+ strbuf *base64_pubkey = base64_encode_sb(
+ ptrlen_from_strbuf(hca->ca_public_key), 0);
+ put_reg_sz(rkey, "PublicKey", base64_pubkey->s);
+ strbuf_free(base64_pubkey);
+
+ strbuf *validity = percent_encode_sb(
+ ptrlen_from_asciz(hca->validity_expression), NULL);
+ put_reg_sz(rkey, "Validity", validity->s);
+ strbuf_free(validity);
+
+ put_reg_dword(rkey, "PermitRSASHA1", hca->opts.permit_rsa_sha1);
+ put_reg_dword(rkey, "PermitRSASHA256", hca->opts.permit_rsa_sha256);
+ put_reg_dword(rkey, "PermitRSASHA512", hca->opts.permit_rsa_sha512);
+
+ close_regkey(rkey);
+ return NULL;
+}
+
+char *host_ca_delete(const char *name)
+{
+ HKEY rkey = open_regkey(false, HKEY_CURRENT_USER, host_ca_key);
+ if (!rkey)
+ return NULL;
+
+ strbuf *sb = strbuf_new();
+ escape_registry_key(name, sb);
+ del_regkey(rkey, sb->s);
+ strbuf_free(sb);
+
+ return NULL;
+}
+
/*
* Open (or delete) the random seed file.
*/
@@ -498,7 +543,6 @@ static bool try_random_seed_and_free(char *path, int action, HANDLE *hout)
static HANDLE access_random_seed(int action)
{
- HKEY rkey;
HANDLE rethandle;
/*
@@ -517,16 +561,16 @@ static HANDLE access_random_seed(int action)
* 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;
+ HKEY rkey = open_regkey(false, HKEY_CURRENT_USER, PUTTY_REG_POS);
+ if (rkey) {
+ char *regpath = get_reg_sz(rkey, "RandSeedFile");
+ close_regkey(rkey);
+ if (regpath) {
+ bool success = try_random_seed(regpath, action, &rethandle);
+ sfree(regpath);
+ if (success)
+ return rethandle;
+ }
}
}
@@ -641,132 +685,69 @@ void write_random_seed(void *data, int len)
* 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) {
+static int transform_jumplist_registry(
+ const char *add, const char *rem, char **out)
+{
+ HKEY rkey = open_regkey(true, HKEY_CURRENT_USER, reg_jumplist_key);
+ if (!rkey)
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';
+ strbuf *oldlist = get_reg_multi_sz(rkey, reg_jumplist_value);
+ if (!oldlist) {
+ /* Start again with the empty list. */
+ oldlist = strbuf_new();
+ put_data(oldlist, "\0\0", 2);
}
/*
* Modify the list, if we're modifying.
*/
+ bool write_failure = false;
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;
+ BinarySource src[1];
+ BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(oldlist));
+ strbuf *newlist = strbuf_new();
/* First add the new item to the beginning of the list. */
- if (add) {
- strcpy(piterator_new, add);
- piterator_new += strlen(piterator_new) + 1;
- }
+ if (add)
+ put_asciz(newlist, add);
+
/* 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) {
+ while (true) {
+ const char *olditem = get_asciz(src);
+ if (get_err(src))
+ break;
+
+ if (!rem || strcmp(olditem, rem) != 0) {
/* Check if this is a valid session, otherwise don't add. */
- settings_r *psettings_tmp = open_settings_r(piterator_old);
+ settings_r *psettings_tmp = open_settings_r(olditem);
if (psettings_tmp != NULL) {
close_settings_r(psettings_tmp);
- strcpy(piterator_new, piterator_old);
- piterator_new += strlen(piterator_new) + 1;
+ put_asciz(newlist, olditem);
}
}
- 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);
+ write_failure = !put_reg_multi_sz(rkey, reg_jumplist_value, newlist);
- sfree(old_value);
- old_value = new_value;
- } else
- ret = ERROR_SUCCESS;
+ strbuf_free(oldlist);
+ oldlist = newlist;
+ }
- /*
- * Either return or free the result.
- */
- if (out && ret == ERROR_SUCCESS)
- *out = old_value;
- else
- sfree(old_value);
+ close_regkey(rkey);
- /* Clean up and return. */
- RegCloseKey(pjumplist_key);
+ if (out && !write_failure)
+ *out = strbuf_to_str(oldlist);
+ else
+ strbuf_free(oldlist);
- if (ret != ERROR_SUCCESS) {
+ if (write_failure)
return JUMPLISTREG_ERROR_VALUEWRITE_FAILURE;
- } else {
+ else
return JUMPLISTREG_OK;
- }
}
/* Adds a new entry to the jumplist entries in the registry. */
@@ -800,26 +781,22 @@ char *get_jumplist_registry_entries (void)
*/
static void registry_recursive_remove(HKEY key)
{
- DWORD i;
- char name[MAX_PATH + 1];
- HKEY subkey;
+ char *name;
- i = 0;
- while (RegEnumKey(key, i, name, sizeof(name)) == ERROR_SUCCESS) {
- if (RegOpenKey(key, name, &subkey) == ERROR_SUCCESS) {
+ DWORD i = 0;
+ while ((name = enum_regkey(key, i)) != NULL) {
+ HKEY subkey = open_regkey(false, key, name);
+ if (subkey) {
registry_recursive_remove(subkey);
- RegCloseKey(subkey);
+ close_regkey(subkey);
}
- RegDeleteKey(key, name);
+ del_regkey(key, name);
+ sfree(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.
@@ -839,31 +816,34 @@ void cleanup_all(void)
/*
* Open the main PuTTY registry key and remove everything in it.
*/
- if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS, &key) ==
- ERROR_SUCCESS) {
+ HKEY key = open_regkey(false, HKEY_CURRENT_USER, PUTTY_REG_POS);
+ if (key) {
registry_recursive_remove(key);
- RegCloseKey(key);
+ close_regkey(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 ((key = open_regkey(false, HKEY_CURRENT_USER,
+ PUTTY_REG_PARENT)) != NULL) {
+ del_regkey(key, PUTTY_REG_PARENT_CHILD);
+ char *name = enum_regkey(key, 0);
+ close_regkey(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);
+ if (name) {
+ sfree(name);
+ } else {
+ if ((key = open_regkey(false, HKEY_CURRENT_USER,
+ PUTTY_REG_GPARENT)) != NULL) {
+ del_regkey(key, PUTTY_REG_GPARENT_CHILD);
+ close_regkey(key);
}
}
}
diff --git a/windows/test_screenshot.c b/windows/test_screenshot.c
new file mode 100644
index 00000000..1e3a20d7
--- /dev/null
+++ b/windows/test_screenshot.c
@@ -0,0 +1,45 @@
+#include "putty.h"
+
+static NORETURN PRINTF_LIKE(1, 2) void fatal_error(const char *p, ...)
+{
+ va_list ap;
+ fprintf(stderr, "screenshot: ");
+ va_start(ap, p);
+ vfprintf(stderr, p, ap);
+ va_end(ap);
+ fputc('\n', stderr);
+ exit(1);
+}
+
+void out_of_memory(void) { fatal_error("out of memory"); }
+
+int main(int argc, char **argv)
+{
+ const char *outfile = NULL;
+
+ AuxMatchOpt amo = aux_match_opt_init(argc-1, argv+1, 0, fatal_error);
+ while (!aux_match_done(&amo)) {
+ char *val;
+ #define match_opt(...) aux_match_opt( \
+ &amo, NULL, __VA_ARGS__, (const char *)NULL)
+ #define match_optval(...) aux_match_opt( \
+ &amo, &val, __VA_ARGS__, (const char *)NULL)
+
+ if (aux_match_arg(&amo, &val)) {
+ fatal_error("unexpected argument '%s'", val);
+ } else if (match_optval("-o", "--output")) {
+ outfile = val;
+ } else {
+ fatal_error("unrecognised option '%s'\n", amo.argv[amo.index]);
+ }
+ }
+
+ if (!outfile)
+ fatal_error("expected an output file name");
+
+ char *err = save_screenshot(NULL, outfile);
+ if (err)
+ fatal_error("%s", err);
+
+ return 0;
+}
diff --git a/windows/winucs.c b/windows/unicode.c
index 9ffff5e9..b3f6d802 100644
--- a/windows/winucs.c
+++ b/windows/unicode.c
@@ -435,11 +435,116 @@ static const struct cp_list_item cp_list[] = {
{0, 0}
};
-static void link_font(WCHAR * line_tbl, WCHAR * font_tbl, WCHAR attr);
+static void link_font(WCHAR *line_tbl, WCHAR *font_tbl, WCHAR attr);
+
+/*
+ * We keep a collection of reverse mappings from Unicode back to code pages,
+ * in the form of array[256] of array[256] of char. These live forever in a
+ * local tree234, and we just make a new one whenever we find a need.
+ */
+typedef struct reverse_mapping {
+ int codepage;
+ char **blocks;
+} reverse_mapping;
+static tree234 *reverse_mappings = NULL;
+
+static int reverse_mapping_cmp(void *av, void *bv)
+{
+ const reverse_mapping *a = (const reverse_mapping *)av;
+ const reverse_mapping *b = (const reverse_mapping *)bv;
+ if (a->codepage < b->codepage)
+ return -1;
+ if (a->codepage > b->codepage)
+ return +1;
+ return 0;
+}
+
+static int reverse_mapping_find(void *av, void *bv)
+{
+ const reverse_mapping *a = (const reverse_mapping *)av;
+ int b_codepage = *(const int *)bv;
+ if (a->codepage < b_codepage)
+ return -1;
+ if (a->codepage > b_codepage)
+ return +1;
+ return 0;
+}
+
+static reverse_mapping *get_existing_reverse_mapping(int codepage)
+{
+ if (!reverse_mappings)
+ return NULL;
+ return find234(reverse_mappings, &codepage, reverse_mapping_find);
+}
+
+static reverse_mapping *make_reverse_mapping_inner(
+ int codepage, const wchar_t *mapping)
+{
+ if (!reverse_mappings)
+ reverse_mappings = newtree234(reverse_mapping_cmp);
+
+ reverse_mapping *rmap = snew(reverse_mapping);
+ rmap->blocks = snewn(256, char *);
+ memset(rmap->blocks, 0, 256 * sizeof(char *));
+
+ for (size_t i = 0; i < 256; i++) {
+ /* These special kinds of value correspond to no Unicode character */
+ if (DIRECT_CHAR(mapping[i]))
+ continue;
+ if (DIRECT_FONT(mapping[i]))
+ continue;
+
+ size_t chr = mapping[i];
+ size_t block = chr >> 8, index = chr & 0xFF;
+
+ if (!rmap->blocks[block]) {
+ rmap->blocks[block] = snewn(256, char);
+ memset(rmap->blocks[block], 0, 256);
+ }
+ rmap->blocks[block][index] = i;
+ }
+
+ rmap->codepage = codepage;
+ reverse_mapping *added = add234(reverse_mappings, rmap);
+ assert(added == rmap); /* we already checked it wasn't already in there */
+ return added;
+}
+
+static void make_reverse_mapping(int codepage, const wchar_t *mapping)
+{
+ if (get_existing_reverse_mapping(codepage))
+ return; /* we've already got this one */
+ make_reverse_mapping_inner(codepage, mapping);
+}
+
+static reverse_mapping *get_reverse_mapping(int codepage)
+{
+ /*
+ * Try harder to get a reverse mapping for a codepage we implement
+ * internally via a translation table, by hastily making it if it doesn't
+ * already exist.
+ */
+
+ reverse_mapping *rmap = get_existing_reverse_mapping(codepage);
+ if (rmap)
+ return rmap;
+
+ if (codepage < 65536)
+ return NULL;
+ if (codepage >= 65536 + lenof(cp_list))
+ return NULL;
+ const struct cp_list_item *cp = &cp_list[codepage - 65536];
+ if (!cp->cp_table)
+ return NULL;
+
+ wchar_t mapping[256];
+ get_unitab(codepage, mapping, 0);
+ return make_reverse_mapping_inner(codepage, mapping);
+}
void init_ucs(Conf *conf, struct unicode_data *ucsdata)
{
- int i, j;
+ int i;
bool used_dtf = false;
int vtmode;
@@ -522,31 +627,9 @@ void init_ucs(Conf *conf, struct unicode_data *ucsdata)
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;
- }
+ /* Make sure a reverse mapping exists for this code page. */
+ make_reverse_mapping(ucsdata->line_codepage, ucsdata->unitab_line);
}
/* Find the line control characters. */
@@ -598,7 +681,7 @@ void init_ucs(Conf *conf, struct unicode_data *ucsdata)
for (i = 96; i < 127; i++)
if (!DIRECT_FONT(ucsdata->unitab_xterm[i]))
ucsdata->unitab_xterm[i] =
- (WCHAR) (CSET_ACP + poorman_vt100[i - 96]);
+ (WCHAR) (CSET_ACP + poorman_vt100[i - 96]);
for(i=128;i<256;i++)
if (!DIRECT_FONT(ucsdata->unitab_scoacs[i]))
ucsdata->unitab_scoacs[i] =
@@ -606,7 +689,7 @@ void init_ucs(Conf *conf, struct unicode_data *ucsdata)
}
}
-static void link_font(WCHAR * line_tbl, WCHAR * font_tbl, WCHAR attr)
+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++) {
@@ -641,11 +724,11 @@ wchar_t xlat_uskbd2cyrllic(int ch)
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 int check_compose_internal(int first, int second, int recurse)
{
static const struct {
@@ -1000,9 +1083,9 @@ int check_compose(int first, int second)
return check_compose_internal(first, second, 0);
}
-int decode_codepage(char *cp_name)
+int decode_codepage(const char *cp_name)
{
- char *s, *d;
+ const char *s, *d;
const struct cp_list_item *cpi;
int codepage = -1;
CPINFO cpinfo;
@@ -1117,7 +1200,7 @@ const char *cp_enumerate(int index)
return cp_list[index].name;
}
-void get_unitab(int codepage, wchar_t * unitab, int ftype)
+void get_unitab(int codepage, wchar_t *unitab, int ftype)
{
char tbuf[4];
int i, max = 256, flg = MB_ERR_INVALID_CHARS;
@@ -1156,20 +1239,21 @@ void get_unitab(int codepage, wchar_t * unitab, int ftype)
}
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 *mbstr, int mblen, const char *defchr)
{
- char *p;
- int i;
- if (ucsdata && codepage == ucsdata->line_codepage && ucsdata->uni_tbl) {
+ reverse_mapping *rmap = get_reverse_mapping(codepage);
+
+ if (rmap) {
/* Do this by array lookup if we can. */
if (wclen < 0) {
for (wclen = 0; wcstr[wclen++] ;); /* will include the NUL */
}
+ char *p;
+ int i;
for (p = mbstr, i = 0; i < wclen; i++) {
wchar_t ch = wcstr[i];
int by;
- char *p1;
+ const char *p1;
#define WRITECH(chr) do \
{ \
@@ -1177,8 +1261,7 @@ int wc_to_mb(int codepage, int flags, const wchar_t *wcstr, int wclen,
*p++ = (char)(chr); \
} while (0)
- if (ucsdata->uni_tbl &&
- (p1 = ucsdata->uni_tbl[(ch >> 8) & 0xFF]) != NULL &&
+ if ((p1 = rmap->blocks[(ch >> 8) & 0xFF]) != NULL &&
(by = p1[ch & 0xFF]) != '\0')
WRITECH(by);
else if (ch < 0x80)
@@ -1195,16 +1278,122 @@ int wc_to_mb(int codepage, int flags, const wchar_t *wcstr, int wclen,
}
return p - mbstr;
} else {
- int defused;
- return WideCharToMultiByte(codepage, flags, wcstr, wclen,
- mbstr, mblen, defchr, &defused);
+ int defused, ret;
+ ret = WideCharToMultiByte(codepage, flags, wcstr, wclen,
+ mbstr, mblen, defchr, &defused);
+ if (ret)
+ return ret;
+
+#ifdef LEGACY_WINDOWS
+ /*
+ * Fallback for legacy platforms too old to support UTF-8: if
+ * the codepage is UTF-8, we can do the translation ourselves.
+ */
+ if (codepage == CP_UTF8 && mblen > 0 && wclen > 0) {
+ size_t remaining = mblen;
+ char *p = mbstr;
+
+ while (wclen > 0) {
+ unsigned long wc = (wclen--, *wcstr++);
+ if (wclen > 0 && IS_SURROGATE_PAIR(wc, *wcstr)) {
+ wc = FROM_SURROGATES(wc, *wcstr);
+ wclen--, wcstr++;
+ }
+
+ char utfbuf[6];
+ size_t utflen = encode_utf8(utfbuf, wc);
+ if (utflen <= remaining) {
+ memcpy(p, utfbuf, utflen);
+ p += utflen;
+ remaining -= utflen;
+ } else {
+ return p - mbstr;
+ }
+ }
+
+ return p - mbstr;
+ }
+#endif
+
+ /* No other fallbacks are available */
+ return 0;
}
}
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);
+ if (codepage >= 65536) {
+ /* Character set not known to Windows, so we'll have to
+ * translate it ourself */
+ size_t index = codepage - 65536;
+ if (index >= lenof(cp_list))
+ return 0;
+ const struct cp_list_item *cp = &cp_list[index];
+ if (!cp->cp_table)
+ return 0;
+
+ size_t remaining = wclen;
+ wchar_t *p = wcstr;
+ unsigned tablebase = 256 - cp->cp_size;
+
+ while (mblen > 0) {
+ mblen--;
+ unsigned c = 0xFF & *mbstr++;
+ wchar_t wc = (c < tablebase ? c : cp->cp_table[c - tablebase]);
+ if (remaining > 0) {
+ remaining--;
+ *p++ = wc;
+ } else {
+ return p - wcstr;
+ }
+ }
+
+ return p - wcstr;
+ }
+
+ int ret = MultiByteToWideChar(codepage, flags, mbstr, mblen, wcstr, wclen);
+ if (ret)
+ return ret;
+
+#ifdef LEGACY_WINDOWS
+ /*
+ * Fallback for legacy platforms too old to support UTF-8: if the
+ * codepage is UTF-8, we can do the translation ourselves.
+ */
+ if (codepage == CP_UTF8 && mblen > 0 && wclen > 0) {
+ size_t remaining = wclen;
+ wchar_t *p = wcstr;
+
+ while (mblen > 0) {
+ char utfbuf[7];
+ int thissize = mblen < 6 ? mblen : 6;
+ memcpy(utfbuf, mbstr, thissize);
+ utfbuf[thissize] = '\0';
+
+ const char *utfptr = utfbuf;
+ wchar_t wcbuf[2];
+ size_t nwc = decode_utf8_to_wchar(&utfptr, wcbuf);
+
+ for (size_t i = 0; i < nwc; i++) {
+ if (remaining > 0) {
+ remaining--;
+ *p++ = wcbuf[i];
+ } else {
+ return p - wcstr;
+ }
+ }
+
+ mbstr += (utfptr - utfbuf);
+ mblen -= (utfptr - utfbuf);
+ }
+
+ return p - wcstr;
+ }
+#endif
+
+ /* No other fallbacks are available */
+ return 0;
}
bool is_dbcs_leadbyte(int codepage, char byte)
diff --git a/windows/utils/agent_mutex_name.c b/windows/utils/agent_mutex_name.c
new file mode 100644
index 00000000..949e8cfa
--- /dev/null
+++ b/windows/utils/agent_mutex_name.c
@@ -0,0 +1,14 @@
+/*
+ * Return the full pathname of the global mutex that Pageant uses at
+ * startup to atomically decide whether to be a server or a client.
+ */
+
+#include "putty.h"
+
+char *agent_mutex_name(void)
+{
+ char *username = get_username();
+ char *mutexname = dupprintf("Local\\pageant-mutex.%s", username);
+ sfree(username);
+ return mutexname;
+}
diff --git a/windows/utils/agent_named_pipe_name.c b/windows/utils/agent_named_pipe_name.c
new file mode 100644
index 00000000..aa64b3f6
--- /dev/null
+++ b/windows/utils/agent_named_pipe_name.c
@@ -0,0 +1,17 @@
+/*
+ * Return the full pathname of the named pipe Pageant will listen on.
+ * Used by both the Pageant server code and client code.
+ */
+
+#include "putty.h"
+#include "cryptoapi.h"
+
+char *agent_named_pipe_name(void)
+{
+ char *username = get_username();
+ char *suffix = capi_obfuscate_string("Pageant");
+ char *pipename = dupprintf("\\\\.\\pipe\\pageant.%s.%s", username, suffix);
+ sfree(username);
+ sfree(suffix);
+ return pipename;
+}
diff --git a/windows/utils/arm_arch_queries.c b/windows/utils/arm_arch_queries.c
new file mode 100644
index 00000000..b0193276
--- /dev/null
+++ b/windows/utils/arm_arch_queries.c
@@ -0,0 +1,45 @@
+/*
+ * Windows implementation of the OS query functions that detect Arm
+ * architecture extensions.
+ */
+
+#include "putty.h"
+#include "ssh.h"
+
+#if !(defined _M_ARM || defined _M_ARM64)
+/*
+ * For non-Arm, stub out these functions. This module shouldn't be
+ * _called_ in that situation anyway, but it will still be compiled
+ * (because that's easier than getting CMake to identify the
+ * architecture in all cases).
+ */
+#define IsProcessorFeaturePresent(...) false
+#endif
+
+bool platform_aes_neon_available(void)
+{
+ return IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE);
+}
+
+bool platform_pmull_neon_available(void)
+{
+ return IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE);
+}
+
+bool platform_sha256_neon_available(void)
+{
+ return IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE);
+}
+
+bool platform_sha1_neon_available(void)
+{
+ return IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE);
+}
+
+bool platform_sha512_neon_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;
+}
diff --git a/windows/utils/aux_match_opt.c b/windows/utils/aux_match_opt.c
new file mode 100644
index 00000000..190eddac
--- /dev/null
+++ b/windows/utils/aux_match_opt.c
@@ -0,0 +1,117 @@
+/*
+ * Helper function for matching command-line options in the Windows
+ * auxiliary tools (PuTTYgen and Pageant).
+ *
+ * Supports basically the usual kinds of option, including GNUish
+ * --foo long options and simple -f short options. But historically
+ * those tools have also supported long options with a single dash, so
+ * we don't go full GNU and report those as syntax errors.
+ */
+
+#include "putty.h"
+
+/*
+ * Call this to initialise the state structure.
+ */
+AuxMatchOpt aux_match_opt_init(int argc, char **argv, int start_index,
+ aux_opt_error_fn_t opt_error)
+{
+ AuxMatchOpt amo[1];
+
+ amo->argc = argc;
+ amo->argv = argv;
+ amo->index = start_index;
+ amo->doing_opts = true;
+ amo->error = opt_error;
+
+ return amo[0];
+}
+
+/*
+ * Call this with a NULL-terminated list of synonymous option names.
+ * Point 'val' at a char * to receive the option argument, if one is
+ * expected. Set 'val' to NULL if no argument is expected.
+ */
+bool aux_match_opt(AuxMatchOpt *amo, char **val, const char *optname, ...)
+{
+ assert(amo->index < amo->argc);
+
+ /* Find the end of the option name */
+ char *opt = amo->argv[amo->index];
+ ptrlen argopt;
+ argopt.ptr = opt;
+ argopt.len = strcspn(opt, "=");
+
+ /* Normalise GNU-style --foo long options to the -foo that this
+ * tool has historically used */
+ ptrlen argopt2 = make_ptrlen(NULL, 0);
+ if (ptrlen_startswith(argopt, PTRLEN_LITERAL("--"), NULL))
+ ptrlen_startswith(argopt, PTRLEN_LITERAL("-"), &argopt2);
+
+ /* See if this option matches any of our synonyms */
+ va_list ap;
+ va_start(ap, optname);
+ bool matched = false;
+ while (optname) {
+ if (ptrlen_eq_string(argopt, optname)) {
+ matched = true;
+ break;
+ }
+ if (argopt2.ptr && strlen(optname) > 2 &&
+ ptrlen_eq_string(argopt2, optname)) {
+ matched = true;
+ break;
+ }
+ optname = va_arg(ap, const char *);
+ }
+ va_end(ap);
+ if (!matched)
+ return false;
+
+ /* Check for a value */
+ if (opt[argopt.len]) {
+ if (!val)
+ amo->error("option '%s' does not expect a value", opt);
+ *val = opt + argopt.len + 1;
+ amo->index++;
+ } else if (!val) {
+ amo->index++;
+ } else {
+ if (amo->index + 1 >= amo->argc)
+ amo->error("option '%s' expects a value", opt);
+ *val = amo->argv[amo->index + 1];
+ amo->index += 2;
+ }
+
+ return true;
+}
+
+/*
+ * Call this to return a non-option argument in *val.
+ */
+bool aux_match_arg(AuxMatchOpt *amo, char **val)
+{
+ assert(amo->index < amo->argc);
+ char *opt = amo->argv[amo->index];
+
+ if (!amo->doing_opts || opt[0] != '-' || !strcmp(opt, "-")) {
+ *val = opt;
+ amo->index++;
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * And call this to test whether we're done. Also handles '--'.
+ */
+bool aux_match_done(AuxMatchOpt *amo)
+{
+ if (amo->index < amo->argc && !strcmp(amo->argv[amo->index], "--")) {
+ amo->doing_opts = false;
+ amo->index++;
+ }
+
+ return amo->index >= amo->argc;
+}
diff --git a/windows/utils/centre_window.c b/windows/utils/centre_window.c
new file mode 100644
index 00000000..651fc279
--- /dev/null
+++ b/windows/utils/centre_window.c
@@ -0,0 +1,20 @@
+/*
+ * Centre a window on the screen. Used to position the main config box.
+ */
+
+#include "putty.h"
+
+void centre_window(HWND win)
+{
+ RECT rd, rw;
+
+ if (!GetWindowRect(GetDesktopWindow(), &rd))
+ return;
+ if (!GetWindowRect(win, &rw))
+ return;
+
+ MoveWindow(win,
+ (rd.right + rd.left + rw.left - rw.right) / 2,
+ (rd.bottom + rd.top + rw.top - rw.bottom) / 2,
+ rw.right - rw.left, rw.bottom - rw.top, true);
+}
diff --git a/windows/wincapi.c b/windows/utils/cryptoapi.c
index de78988b..c3752c59 100644
--- a/windows/wincapi.c
+++ b/windows/utils/cryptoapi.c
@@ -1,15 +1,13 @@
/*
- * wincapi.c: implementation of wincapi.h.
+ * windows/utils/cryptoapi.c: implementation of cryptoapi.h.
*/
#include "putty.h"
-#if !defined NO_SECURITY
-
#include "putty.h"
#include "ssh.h"
-#include "wincapi.h"
+#include "cryptoapi.h"
DEF_WINDOWS_FUNCTION(CryptProtectMemory);
@@ -89,5 +87,3 @@ char *capi_obfuscate_string(const char *realname)
return dupstr(retbuf);
}
-
-#endif /* !defined NO_SECURITY */
diff --git a/windows/windefs.c b/windows/utils/defaults.c
index 006e8dc5..1a270009 100644
--- a/windows/windefs.c
+++ b/windows/utils/defaults.c
@@ -1,5 +1,5 @@
/*
- * windefs.c: default settings that are specific to Windows.
+ * windows/utils/defaults.c: default settings that are specific to Windows.
*/
#include "putty.h"
diff --git a/windows/utils/dll_hijacking_protection.c b/windows/utils/dll_hijacking_protection.c
new file mode 100644
index 00000000..fe9ae59c
--- /dev/null
+++ b/windows/utils/dll_hijacking_protection.c
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+#include "putty.h"
+
+void dll_hijacking_protection(void)
+{
+ static HMODULE kernel32_module;
+ DECL_WINDOWS_FUNCTION(static, BOOL, SetDefaultDllDirectories, (DWORD));
+
+ if (!kernel32_module) {
+ kernel32_module = load_system32_dll("kernel32.dll");
+#if !HAVE_SETDEFAULTDLLDIRECTORIES
+ /* 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);
+ }
+}
diff --git a/windows/utils/dputs.c b/windows/utils/dputs.c
new file mode 100644
index 00000000..f582509a
--- /dev/null
+++ b/windows/utils/dputs.c
@@ -0,0 +1,39 @@
+/*
+ * Implementation of dputs() for Windows.
+ *
+ * The debug messages are written to STD_OUTPUT_HANDLE, except that
+ * first it has to make sure that handle _exists_, by calling
+ * AllocConsole first if necessary.
+ *
+ * They also go into a file called debug.log.
+ */
+
+#include "putty.h"
+#include "utils/utils.h"
+
+static HANDLE debug_fp = INVALID_HANDLE_VALUE;
+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 == INVALID_HANDLE_VALUE) {
+ debug_fp = CreateFile("debug.log", GENERIC_WRITE, FILE_SHARE_READ,
+ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ }
+
+ if (debug_fp != INVALID_HANDLE_VALUE) {
+ WriteFile(debug_fp, buf, strlen(buf), &dw, NULL);
+ }
+ if (debug_hdl != INVALID_HANDLE_VALUE) {
+ WriteFile(debug_hdl, buf, strlen(buf), &dw, NULL);
+ }
+}
diff --git a/windows/utils/escape_registry_key.c b/windows/utils/escape_registry_key.c
new file mode 100644
index 00000000..f5c9c19e
--- /dev/null
+++ b/windows/utils/escape_registry_key.c
@@ -0,0 +1,48 @@
+/*
+ * Escaping/unescaping functions to translate between a saved session
+ * name, and the key name used to represent it in the Registry area
+ * where we store saved sessions.
+ *
+ * The basic technique is to %-escape characters we can't use in
+ * Registry keys.
+ */
+
+#include "putty.h"
+
+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++);
+ }
+ }
+}
diff --git a/windows/utils/filename.c b/windows/utils/filename.c
new file mode 100644
index 00000000..f4457ecb
--- /dev/null
+++ b/windows/utils/filename.c
@@ -0,0 +1,54 @@
+/*
+ * Implementation of Filename for Windows.
+ */
+
+#include "putty.h"
+
+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;
+}
diff --git a/windows/utils/fontspec.c b/windows/utils/fontspec.c
new file mode 100644
index 00000000..7e8d5175
--- /dev/null
+++ b/windows/utils/fontspec.c
@@ -0,0 +1,43 @@
+/*
+ * Implementation of FontSpec for Windows.
+ */
+
+#include "putty.h"
+
+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);
+}
diff --git a/windows/utils/get_system_dir.c b/windows/utils/get_system_dir.c
new file mode 100644
index 00000000..049cd7fc
--- /dev/null
+++ b/windows/utils/get_system_dir.c
@@ -0,0 +1,21 @@
+/*
+ * Wrapper function around GetSystemDirectory that deals with
+ * allocating the output buffer, and also caches the result for future
+ * calls.
+ */
+
+#include "putty.h"
+
+const char *get_system_dir(void)
+{
+ static char *sysdir = NULL;
+ static size_t sysdirsize = 0;
+
+ if (!sysdir) {
+ size_t len;
+ while ((len = GetSystemDirectory(sysdir, sysdirsize)) >= sysdirsize)
+ sgrowarray(sysdir, sysdirsize, len);
+ }
+
+ return sysdir;
+}
diff --git a/windows/utils/get_username.c b/windows/utils/get_username.c
new file mode 100644
index 00000000..bc5780e4
--- /dev/null
+++ b/windows/utils/get_username.c
@@ -0,0 +1,77 @@
+/*
+ * Implementation of get_username() for Windows.
+ */
+
+#include "putty.h"
+
+#ifndef SECURITY_WIN32
+#define SECURITY_WIN32
+#endif
+#include <security.h>
+
+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;
+}
diff --git a/windows/utils/getdlgitemtext_alloc.c b/windows/utils/getdlgitemtext_alloc.c
new file mode 100644
index 00000000..f0244d71
--- /dev/null
+++ b/windows/utils/getdlgitemtext_alloc.c
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "putty.h"
+
+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;
+}
diff --git a/windows/utils/interprocess_mutex.c b/windows/utils/interprocess_mutex.c
new file mode 100644
index 00000000..8612a298
--- /dev/null
+++ b/windows/utils/interprocess_mutex.c
@@ -0,0 +1,48 @@
+/*
+ * Lock and unlock a mutex with a globally visible name. Used to
+ * synchronise between unrelated processes, such as two
+ * connection-sharing PuTTYs deciding which will be the upstream.
+ */
+
+#include "putty.h"
+#include "security-api.h"
+
+HANDLE lock_interprocess_mutex(const char *mutexname, char **error)
+{
+ PSECURITY_DESCRIPTOR psd = NULL;
+ PACL acl = NULL;
+ HANDLE mutex = NULL;
+
+ if (should_have_security() && !make_private_security_descriptor(
+ MUTEX_ALL_ACCESS, &psd, &acl, error))
+ goto out;
+
+ SECURITY_ATTRIBUTES sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = psd;
+ sa.bInheritHandle = false;
+
+ mutex = CreateMutex(&sa, false, mutexname);
+ if (!mutex) {
+ *error = dupprintf("CreateMutex(\"%s\") failed: %s",
+ mutexname, win_strerror(GetLastError()));
+ goto out;
+ }
+
+ WaitForSingleObject(mutex, INFINITE);
+
+ out:
+ if (psd)
+ LocalFree(psd);
+ if (acl)
+ LocalFree(acl);
+
+ return mutex;
+}
+
+void unlock_interprocess_mutex(HANDLE mutex)
+{
+ ReleaseMutex(mutex);
+ CloseHandle(mutex);
+}
diff --git a/windows/utils/is_console_handle.c b/windows/utils/is_console_handle.c
new file mode 100644
index 00000000..887069c9
--- /dev/null
+++ b/windows/utils/is_console_handle.c
@@ -0,0 +1,13 @@
+/*
+ * Determine whether a Windows HANDLE points at a console device.
+ */
+
+#include "putty.h"
+
+bool is_console_handle(HANDLE handle)
+{
+ DWORD ignored_output;
+ if (GetConsoleMode(handle, &ignored_output))
+ return true;
+ return false;
+}
diff --git a/windows/utils/load_system32_dll.c b/windows/utils/load_system32_dll.c
new file mode 100644
index 00000000..d227a264
--- /dev/null
+++ b/windows/utils/load_system32_dll.c
@@ -0,0 +1,18 @@
+/*
+ * 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.)
+ */
+
+#include "putty.h"
+
+HMODULE load_system32_dll(const char *libname)
+{
+ char *fullpath;
+ HMODULE ret;
+
+ fullpath = dupcat(get_system_dir(), "\\", libname);
+ ret = LoadLibrary(fullpath);
+ sfree(fullpath);
+ return ret;
+}
diff --git a/windows/wintime.c b/windows/utils/ltime.c
index 5fa3b0de..d4364509 100644
--- a/windows/wintime.c
+++ b/windows/utils/ltime.c
@@ -1,5 +1,6 @@
/*
- * wintime.c - Avoid trouble with time() returning (time_t)-1 on Windows.
+ * Implementation of ltime() that avoids trouble with time() returning
+ * (time_t)-1 on Windows.
*/
#include "putty.h"
diff --git a/windows/utils/make_spr_sw_abort_winerror.c b/windows/utils/make_spr_sw_abort_winerror.c
new file mode 100644
index 00000000..b05ef61f
--- /dev/null
+++ b/windows/utils/make_spr_sw_abort_winerror.c
@@ -0,0 +1,22 @@
+/*
+ * Constructor function for a SeatPromptResult of the 'software abort'
+ * category, whose error message includes the translation of an OS
+ * error code.
+ */
+
+#include "putty.h"
+
+static void spr_winerror_errfn(SeatPromptResult spr, BinarySink *bs)
+{
+ put_fmt(bs, "%s: %s", spr.errdata_lit, win_strerror(spr.errdata_u));
+}
+
+SeatPromptResult make_spr_sw_abort_winerror(const char *prefix, DWORD error)
+{
+ SeatPromptResult spr;
+ spr.kind = SPRK_SW_ABORT;
+ spr.errfn = spr_winerror_errfn;
+ spr.errdata_lit = prefix;
+ spr.errdata_u = error;
+ return spr;
+}
diff --git a/windows/utils/makedlgitemborderless.c b/windows/utils/makedlgitemborderless.c
new file mode 100644
index 00000000..53975d06
--- /dev/null
+++ b/windows/utils/makedlgitemborderless.c
@@ -0,0 +1,19 @@
+/*
+ * Helper function to remove the border around a dialog item such as
+ * a read-only edit control.
+ */
+
+#include "putty.h"
+
+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);
+}
diff --git a/windows/utils/message_box.c b/windows/utils/message_box.c
new file mode 100644
index 00000000..ae78de4a
--- /dev/null
+++ b/windows/utils/message_box.c
@@ -0,0 +1,49 @@
+/*
+ * Message box with optional context help.
+ */
+
+#include "putty.h"
+
+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);
+}
diff --git a/windows/winmiscs.c b/windows/utils/minefield.c
index 571a9122..c5262ae6 100644
--- a/windows/winmiscs.c
+++ b/windows/utils/minefield.c
@@ -1,25 +1,17 @@
/*
- * 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.
+ * 'Minefield' - a crude Windows memory debugger, similar in concept
+ * to the old Unix 'Electric Fence'. The main difference is that
+ * Electric Fence can be imposed on a program from outside, via
+ * LD_PRELOAD, whereas this has to be included in the program at
+ * compile time with its own cooperation.
+ *
+ * This module provides the Minefield allocator. Actually enabling it
+ * is done by a #define in force when the main utils/memory.c is
+ * compiled.
*/
#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
- */
+#include "puttymem.h"
#define PAGESIZE 4096
@@ -234,52 +226,3 @@ void *minefield_c_realloc(void *p, size_t 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/utils/open_for_write_would_lose_data.c b/windows/utils/open_for_write_would_lose_data.c
new file mode 100644
index 00000000..2aef5c5a
--- /dev/null
+++ b/windows/utils/open_for_write_would_lose_data.c
@@ -0,0 +1,76 @@
+/*
+ * Implementation of open_for_write_would_lose_data for Windows.
+ */
+
+#include "putty.h"
+
+/*
+ * This is slightly fiddly because we want to be backwards-compatible
+ * with systems too old to have GetFileAttributesEx. The next best
+ * thing is FindFirstFile, which will return a different data
+ * structure, but one that also contains the fields we want. (But it
+ * will behave more unhelpfully - for this application - in the
+ * presence of wildcards, so we'd prefer to use GFAE if we can.)
+ */
+
+static inline bool open_for_write_would_lose_data_impl(
+ DWORD dwFileAttributes, DWORD nFileSizeHigh, DWORD nFileSizeLow)
+{
+ if (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 (nFileSizeHigh == 0 && 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;
+}
+
+bool open_for_write_would_lose_data(const Filename *fn)
+{
+ static HMODULE kernel32_module;
+ DECL_WINDOWS_FUNCTION(static, BOOL, GetFileAttributesExA,
+ (LPCSTR, GET_FILEEX_INFO_LEVELS, LPVOID));
+
+ if (!kernel32_module) {
+ kernel32_module = load_system32_dll("kernel32.dll");
+ GET_WINDOWS_FUNCTION(kernel32_module, GetFileAttributesExA);
+ }
+
+ if (p_GetFileAttributesExA) {
+ WIN32_FILE_ATTRIBUTE_DATA attrs;
+ if (!p_GetFileAttributesExA(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;
+ }
+ return open_for_write_would_lose_data_impl(
+ attrs.dwFileAttributes, attrs.nFileSizeHigh, attrs.nFileSizeLow);
+ } else {
+ WIN32_FIND_DATA fd;
+ HANDLE h = FindFirstFile(fn->path, &fd);
+ if (h == INVALID_HANDLE_VALUE) {
+ /*
+ * As above, if we can't find the file at all, return false.
+ */
+ return false;
+ }
+ CloseHandle(h);
+ return open_for_write_would_lose_data_impl(
+ fd.dwFileAttributes, fd.nFileSizeHigh, fd.nFileSizeLow);
+ }
+}
diff --git a/windows/utils/pgp_fingerprints_msgbox.c b/windows/utils/pgp_fingerprints_msgbox.c
new file mode 100644
index 00000000..6618de82
--- /dev/null
+++ b/windows/utils/pgp_fingerprints_msgbox.c
@@ -0,0 +1,25 @@
+/*
+ * Display the fingerprints of the PGP Master Keys to the user as a
+ * GUI message box.
+ */
+
+#include "putty.h"
+
+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));
+}
diff --git a/windows/utils/platform_get_x_display.c b/windows/utils/platform_get_x_display.c
new file mode 100644
index 00000000..e2a9ddc6
--- /dev/null
+++ b/windows/utils/platform_get_x_display.c
@@ -0,0 +1,13 @@
+/*
+ * Implementation of platform_get_x_display for Windows, common to all
+ * tools.
+ */
+
+#include "putty.h"
+#include "ssh.h"
+
+char *platform_get_x_display(void)
+{
+ /* We may as well check for DISPLAY in case it's useful. */
+ return dupstr(getenv("DISPLAY"));
+}
diff --git a/windows/utils/registry.c b/windows/utils/registry.c
new file mode 100644
index 00000000..1f50e67a
--- /dev/null
+++ b/windows/utils/registry.c
@@ -0,0 +1,184 @@
+/*
+ * Implement convenience wrappers on the awkward low-level functions
+ * for accessing the Windows registry.
+ */
+
+#include "putty.h"
+
+HKEY open_regkey_fn(bool create, HKEY hk, const char *path, ...)
+{
+ HKEY toret = NULL;
+ bool hk_needs_close = false;
+ va_list ap;
+ va_start(ap, path);
+
+ for (; path; path = va_arg(ap, const char *)) {
+ HKEY hk_sub = NULL;
+
+ LONG status;
+ if (create)
+ status = RegCreateKeyEx(
+ hk, path, 0, NULL, REG_OPTION_NON_VOLATILE,
+ KEY_READ | KEY_WRITE, NULL, &hk_sub, NULL);
+ else
+ status = RegOpenKeyEx(
+ hk, path, 0, KEY_READ | KEY_WRITE, &hk_sub);
+
+ if (status != ERROR_SUCCESS)
+ goto out;
+
+ if (hk_needs_close)
+ RegCloseKey(hk);
+ hk = hk_sub;
+ hk_needs_close = true;
+ }
+
+ toret = hk;
+ hk = NULL;
+ hk_needs_close = false;
+
+ out:
+ va_end(ap);
+ if (hk_needs_close)
+ RegCloseKey(hk);
+ return toret;
+}
+
+void close_regkey(HKEY key)
+{
+ RegCloseKey(key);
+}
+
+void del_regkey(HKEY key, const char *name)
+{
+ RegDeleteKey(key, name);
+}
+
+char *enum_regkey(HKEY key, int index)
+{
+ size_t regbuf_size = MAX_PATH + 1;
+ char *regbuf = snewn(regbuf_size, char);
+
+ while (1) {
+ LONG status = RegEnumKey(key, index, regbuf, regbuf_size);
+ if (status == ERROR_SUCCESS)
+ return regbuf;
+ if (status != ERROR_MORE_DATA) {
+ sfree(regbuf);
+ return NULL;
+ }
+ sgrowarray(regbuf, regbuf_size, regbuf_size);
+ }
+}
+
+bool get_reg_dword(HKEY key, const char *name, DWORD *out)
+{
+ DWORD type, size;
+ size = sizeof(*out);
+
+ if (RegQueryValueEx(key, name, 0, &type,
+ (BYTE *)out, &size) != ERROR_SUCCESS ||
+ size != sizeof(*out) || type != REG_DWORD)
+ return false;
+ else
+ return true;
+}
+
+bool put_reg_dword(HKEY key, const char *name, DWORD value)
+{
+ return RegSetValueEx(key, name, 0, REG_DWORD, (CONST BYTE *) &value,
+ sizeof(value)) == ERROR_SUCCESS;
+}
+
+char *get_reg_sz(HKEY key, const char *name)
+{
+ DWORD type, size;
+
+ if (RegQueryValueEx(key, name, 0, &type, NULL,
+ &size) != ERROR_SUCCESS || type != REG_SZ)
+ return NULL; /* not a string */
+
+ size_t allocsize = size+1; /* allow for an extra NUL if needed */
+ char *toret = snewn(allocsize, char);
+ if (RegQueryValueEx(key, name, 0, &type, (BYTE *)toret,
+ &size) != ERROR_SUCCESS || type != REG_SZ) {
+ sfree(toret);
+ return NULL;
+ }
+ assert(size < allocsize);
+ toret[size] = '\0'; /* add an extra NUL in case RegQueryValueEx
+ * didn't supply one */
+
+ return toret;
+}
+
+bool put_reg_sz(HKEY key, const char *name, const char *str)
+{
+ /* You have to store the trailing NUL as well */
+ return RegSetValueEx(key, name, 0, REG_SZ, (CONST BYTE *)str,
+ 1 + strlen(str)) == ERROR_SUCCESS;
+}
+
+/*
+ * REG_MULTI_SZ items are stored as a concatenation of NUL-terminated
+ * strings, terminated in turn with an empty string, i.e. a second
+ * consecutive NUL.
+ *
+ * We represent these in their storage format, as a strbuf - but
+ * *without* the second consecutive NUL.
+ *
+ * So you can build up a new MULTI_SZ value in a strbuf by calling
+ * put_asciz once per output string and then put_reg_multi_sz; and you
+ * can consume one by initialising a BinarySource to the result of
+ * get_reg_multi_sz, and then calling get_asciz on it and assuming
+ * that !get_err(src) means you have a real output string.
+ *
+ * Also, calling strbuf_to_str on one of these will give you back a
+ * bare 'char *' with the same double-NUL termination, to pass back to
+ * a caller.
+ */
+strbuf *get_reg_multi_sz(HKEY key, const char *name)
+{
+ DWORD type, size;
+
+ if (RegQueryValueEx(key, name, 0, &type, NULL,
+ &size) != ERROR_SUCCESS || type != REG_MULTI_SZ)
+ return NULL; /* not a string */
+
+ strbuf *toret = strbuf_new();
+ void *ptr = strbuf_append(toret, (size_t)size + 2);
+ if (RegQueryValueEx(key, name, 0, &type, (BYTE *)ptr,
+ &size) != ERROR_SUCCESS || type != REG_MULTI_SZ) {
+ strbuf_free(toret);
+ return NULL;
+ }
+ strbuf_shrink_to(toret, size);
+ /* Ensure we end with exactly one \0 */
+ while (strbuf_chomp(toret, '\0'));
+ put_byte(toret, '\0');
+ return toret;
+}
+
+bool put_reg_multi_sz(HKEY key, const char *name, strbuf *str)
+{
+ /*
+ * Of course, to write our string list into the registry, we _do_
+ * have to include both trailing NULs. But this is easy, because a
+ * strbuf is also designed to hold a single string and make it
+ * conveniently accessible in NUL-terminated form, so it stores a
+ * NUL in its buffer just beyond its formal length. So we just
+ * include that extra byte in the data we write.
+ */
+ return RegSetValueEx(key, name, 0, REG_MULTI_SZ, (CONST BYTE *)str->s,
+ str->len + 1) == ERROR_SUCCESS;
+}
+
+char *get_reg_sz_simple(HKEY key, const char *name, const char *leaf)
+{
+ HKEY subkey = open_regkey(false, key, name);
+ if (!subkey)
+ return NULL;
+ char *toret = get_reg_sz(subkey, leaf);
+ RegCloseKey(subkey);
+ return toret;
+}
diff --git a/windows/utils/request_file.c b/windows/utils/request_file.c
new file mode 100644
index 00000000..dd2cab18
--- /dev/null
+++ b/windows/utils/request_file.c
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#include "putty.h"
+
+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);
+}
diff --git a/windows/utils/screenshot.c b/windows/utils/screenshot.c
new file mode 100644
index 00000000..777520fd
--- /dev/null
+++ b/windows/utils/screenshot.c
@@ -0,0 +1,126 @@
+#include "putty.h"
+
+#if HAVE_DWMAPI_H
+
+#include <dwmapi.h>
+
+char *save_screenshot(HWND hwnd, const char *outfile)
+{
+ HDC dcWindow = NULL, dcSave = NULL;
+ HBITMAP bmSave = NULL;
+ uint8_t *buffer = NULL;
+ char *err = NULL;
+
+ static HMODULE dwmapi_module;
+ DECL_WINDOWS_FUNCTION(static, HRESULT, DwmGetWindowAttribute,
+ (HWND, DWORD, PVOID, DWORD));
+
+ if (!dwmapi_module) {
+ dwmapi_module = load_system32_dll("dwmapi.dll");
+ GET_WINDOWS_FUNCTION(dwmapi_module, DwmGetWindowAttribute);
+ }
+
+ dcWindow = GetDC(NULL);
+ if (!dcWindow) {
+ err = dupprintf("GetDC(window): %s", win_strerror(GetLastError()));
+ goto out;
+ }
+
+ int x, y, w, h;
+ RECT wr;
+
+ /* Use DwmGetWindowAttribute in place of GetWindowRect to exclude
+ * drop shadow, otherwise we get a load of unwanted desktop
+ * background under the shadow */
+ if (p_DwmGetWindowAttribute &&
+ 0 <= p_DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS,
+ &wr, sizeof(wr))) {
+ x = wr.left;
+ y = wr.top;
+ w = wr.right - wr.left;
+ h = wr.bottom - wr.top;
+ } else {
+ BITMAP bmhdr;
+ memset(&bmhdr, 0, sizeof(bmhdr));
+ GetObject(GetCurrentObject(dcWindow, OBJ_BITMAP),
+ sizeof(bmhdr), &bmhdr);
+ x = y = 0;
+ w = bmhdr.bmWidth;
+ h = bmhdr.bmHeight;
+ }
+
+ dcSave = CreateCompatibleDC(dcWindow);
+ if (!dcSave) {
+ err = dupprintf("CreateCompatibleDC(desktop window dc): %s",
+ win_strerror(GetLastError()));
+ goto out;
+ }
+
+ bmSave = CreateCompatibleBitmap(dcWindow, w, h);
+ if (!bmSave) {
+ err = dupprintf("CreateCompatibleBitmap: %s",
+ win_strerror(GetLastError()));
+ goto out;
+ }
+
+ if (!SelectObject(dcSave, bmSave)) {
+ err = dupprintf("SelectObject: %s", win_strerror(GetLastError()));
+ goto out;
+ }
+
+ if (!BitBlt(dcSave, 0, 0, w, h, dcWindow, x, y, SRCCOPY)) {
+ err = dupprintf("BitBlt: %s", win_strerror(GetLastError()));
+ goto out;
+ }
+
+ BITMAPINFO bmInfo;
+ memset(&bmInfo, 0, sizeof(bmInfo));
+ bmInfo.bmiHeader.biSize = sizeof(bmInfo.bmiHeader);
+ bmInfo.bmiHeader.biWidth = w;
+ bmInfo.bmiHeader.biHeight = h;
+ bmInfo.bmiHeader.biPlanes = 1;
+ bmInfo.bmiHeader.biBitCount = 32;
+ bmInfo.bmiHeader.biCompression = BI_RGB;
+
+ size_t bmPixels = (size_t)w*h, bmBytes = bmPixels * 4;
+ buffer = snewn(bmBytes, uint8_t);
+
+ if (!GetDIBits(dcWindow, bmSave, 0, h, buffer, &bmInfo, DIB_RGB_COLORS))
+ err = dupprintf("GetDIBits (get data): %s",
+ win_strerror(GetLastError()));
+
+ FILE *fp = fopen(outfile, "wb");
+ if (!fp) {
+ err = dupprintf("'%s': unable to open file", outfile);
+ goto out;
+ }
+
+ BITMAPFILEHEADER bmFileHdr;
+ bmFileHdr.bfType = 'B' | ('M' << 8);
+ bmFileHdr.bfSize = sizeof(bmFileHdr) + sizeof(bmInfo.bmiHeader) + bmBytes;
+ bmFileHdr.bfOffBits = sizeof(bmFileHdr) + sizeof(bmInfo.bmiHeader);
+ fwrite((void *)&bmFileHdr, 1, sizeof(bmFileHdr), fp);
+ fwrite((void *)&bmInfo.bmiHeader, 1, sizeof(bmInfo.bmiHeader), fp);
+ fwrite((void *)buffer, 1, bmBytes, fp);
+ fclose(fp);
+
+ out:
+ if (dcWindow)
+ ReleaseDC(NULL, dcWindow);
+ if (bmSave)
+ DeleteObject(bmSave);
+ if (dcSave)
+ DeleteObject(dcSave);
+ sfree(buffer);
+ return err;
+}
+
+#else /* HAVE_DWMAPI_H */
+
+/* Without <dwmapi.h> we can't get the right window rectangle */
+char *save_screenshot(HWND hwnd, const char *outfile)
+{
+ return dupstr("Demo screenshots not compiled in to this build");
+}
+
+#endif /* HAVE_DWMAPI_H */
diff --git a/windows/winsecur.c b/windows/utils/security.c
index a1164af5..52299420 100644
--- a/windows/winsecur.c
+++ b/windows/utils/security.c
@@ -1,5 +1,5 @@
/*
- * winsecur.c: implementation of winsecur.h.
+ * windows/utils/security.c: implementation of security-api.h.
*/
#include <stdio.h>
@@ -7,9 +7,7 @@
#include "putty.h"
-#if !defined NO_SECURITY
-
-#include "winsecur.h"
+#include "security-api.h"
/* Initialised once, then kept around to reuse forever */
static PSID worldsid, networksid, usersid;
@@ -22,6 +20,20 @@ DEF_WINDOWS_FUNCTION(GetSecurityInfo);
DEF_WINDOWS_FUNCTION(SetSecurityInfo);
DEF_WINDOWS_FUNCTION(SetEntriesInAclA);
+bool should_have_security(void)
+{
+#ifdef LEGACY_WINDOWS
+ /* Legacy pre-NT platforms are not expected to have any of these APIs */
+ init_winver();
+ return (osPlatformId == VER_PLATFORM_WIN32_NT);
+#else
+ /* In the up-to-date PuTTY builds which do not support those
+ * platforms, unconditionally return true, to minimise the risk of
+ * compiling out security checks. */
+ return true;
+#endif
+}
+
bool got_advapi(void)
{
static bool attempted = false;
@@ -143,7 +155,7 @@ static bool getsids(char **error)
ret = true;
- cleanup:
+ cleanup:
return ret;
}
@@ -163,7 +175,7 @@ bool make_private_security_descriptor(DWORD permissions,
*error = NULL;
if (!getsids(error))
- goto cleanup;
+ goto cleanup;
memset(ea, 0, sizeof(ea));
ea[0].grfAccessPermissions = permissions;
@@ -278,10 +290,10 @@ static bool really_restrict_process_acl(char **error)
goto cleanup;
}
- if (ERROR_SUCCESS != p_SetSecurityInfo
- (GetCurrentProcess(), SE_KERNEL_OBJECT,
- OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
- usersid, NULL, acl, NULL)) {
+ 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;
@@ -299,7 +311,6 @@ static bool really_restrict_process_acl(char **error)
}
return ret;
}
-#endif /* !defined NO_SECURITY */
/*
* Lock down our process's ACL, to present an obstacle to malware
@@ -323,12 +334,7 @@ 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/utils/shinydialogbox.c b/windows/utils/shinydialogbox.c
new file mode 100644
index 00000000..ab3201c1
--- /dev/null
+++ b/windows/utils/shinydialogbox.c
@@ -0,0 +1,111 @@
+/*
+ * PuTTY's own reimplementation of DialogBox() and EndDialog() which
+ * provide extra capabilities.
+ *
+ * Originally introduced in 2003 to work around a problem with our
+ * message loops, in which keystrokes pressed in the 'Change Settings'
+ * dialog in mid-session would _also_ be delivered to the main
+ * terminal window.
+ *
+ * But once we had our own wrapper it was convenient to put further
+ * things into it. In particular, this system allows you to provide a
+ * context pointer at setup time that's easy to retrieve from the
+ * window procedure.
+ */
+
+#include "putty.h"
+
+struct ShinyDialogBoxState {
+ bool ended;
+ int result;
+ ShinyDlgProc proc;
+ void *ctx;
+};
+
+/*
+ * For use in re-entrant calls to the dialog procedure from
+ * CreateDialog itself, temporarily store the state pointer that we
+ * won't store in the usual window-memory slot until later.
+ *
+ * I don't _intend_ that this system will need to be used in multiple
+ * threads at all, let alone concurrently, but just in case, declaring
+ * sdb_tempstate as thread-local will protect against that possibility.
+ */
+static THREADLOCAL struct ShinyDialogBoxState *sdb_tempstate;
+
+static inline struct ShinyDialogBoxState *ShinyDialogGetState(HWND hwnd)
+{
+ if (sdb_tempstate)
+ return sdb_tempstate;
+ return (struct ShinyDialogBoxState *)GetWindowLongPtr(
+ hwnd, DLGWINDOWEXTRA);
+}
+
+static INT_PTR CALLBACK ShinyRealDlgProc(HWND hwnd, UINT msg,
+ WPARAM wParam, LPARAM lParam)
+{
+ struct ShinyDialogBoxState *state = ShinyDialogGetState(hwnd);
+ return state->proc(hwnd, msg, wParam, lParam, state->ctx);
+}
+
+int ShinyDialogBox(HINSTANCE hinst, LPCTSTR tmpl, const char *winclass,
+ HWND hwndparent, ShinyDlgProc proc, void *ctx)
+{
+ /*
+ * Register the window class ourselves in such a way that we
+ * allocate an extra word of window memory to store the state
+ * pointer.
+ *
+ * It would be nice in principle to load the dialog template
+ * resource and dig the class name out of it. But DLGTEMPLATEEX is
+ * a nasty variable-layout structure not declared conveniently in
+ * a header file, so I think that's too much effort. Callers of
+ * this function will just have to provide the right window class
+ * name to match their template resource via the 'winclass'
+ * parameter.
+ */
+ WNDCLASS wc;
+ wc.style = CS_DBLCLKS | CS_SAVEBITS | CS_BYTEALIGNWINDOW;
+ wc.lpfnWndProc = DefDlgProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = DLGWINDOWEXTRA + 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 = winclass;
+ RegisterClass(&wc);
+
+ struct ShinyDialogBoxState state[1];
+ state->ended = false;
+ state->proc = proc;
+ state->ctx = ctx;
+
+ sdb_tempstate = state;
+ HWND hwnd = CreateDialog(hinst, tmpl, hwndparent, ShinyRealDlgProc);
+ SetWindowLongPtr(hwnd, DLGWINDOWEXTRA, (LONG_PTR)state);
+ sdb_tempstate = NULL;
+
+ MSG msg;
+ int gm;
+ while ((gm = GetMessage(&msg, NULL, 0, 0)) > 0) {
+ if (!state->ended && !IsDialogMessage(hwnd, &msg))
+ DispatchMessage(&msg);
+ if (state->ended)
+ break;
+ }
+
+ if (gm == 0)
+ PostQuitMessage(msg.wParam); /* We got a WM_QUIT, pass it on */
+
+ DestroyWindow(hwnd);
+ return state->result;
+}
+
+void ShinyEndDialog(HWND hwnd, int ret)
+{
+ struct ShinyDialogBoxState *state = ShinyDialogGetState(hwnd);
+ state->result = ret;
+ state->ended = true;
+}
diff --git a/windows/winutils.c b/windows/utils/split_into_argv.c
index dec8984b..c42c7a0b 100644
--- a/windows/winutils.c
+++ b/windows/utils/split_into_argv.c
@@ -1,198 +1,4 @@
/*
- * 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
@@ -211,6 +17,8 @@ char *GetDlgItemText_alloc(HWND hwnd, int id)
* `argstart' can be safely left NULL.
*/
+#include "putty.h"
+
/*
* The precise argument-breaking rules vary with compiler version, or
* rather, with the crt0-type startup code that comes with each
@@ -243,6 +51,9 @@ char *GetDlgItemText_alloc(HWND hwnd, int id)
* or more backslashes precedes two or more double quotes, starting
* inside a double-quoted string.
*
+ * Modern Visual Studio (as of 2021)
+ * ---------------------------------
+ *
* I investigated this in an ordinary CLI program, using the
* toolchain's crt0 to split a command line of the form
*
@@ -279,6 +90,9 @@ char *GetDlgItemText_alloc(HWND hwnd, int id)
* either opens or closes a quoted string, and if it closes one, it
* generates a literal " as a side effect.
*
+ * Older Visual Studio
+ * -------------------
+ *
* But here's the corresponding table from the older Visual Studio 7:
*
* backslashes
@@ -299,48 +113,47 @@ char *GetDlgItemText_alloc(HWND hwnd, int id)
* 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.
+ * 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:
+ * 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.
+ * - 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:
+ * - 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.
+ * * _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.
+ * 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.
+ *
+ * I don't know exactly when the bug fix happened, but I know that VS7
+ * had the odd mod-3 behaviour. So the #if below will ensure that
+ * modern (2015 onwards) versions of VS use the new more sensible
+ * behaviour, and VS7 uses the old one. Things in between may be
+ * wrong; if anyone cares, patches to change the cutoff version in
+ * this #if are welcome.
*/
#if _MSC_VER < 1400
#define MOD3 1
@@ -455,7 +268,7 @@ void split_into_argv(char *cmdline, int *argc, char ***argv,
if (argstart) *argstart = outputargstart; else sfree(outputargstart);
}
-#ifdef TESTMODE
+#ifdef TEST
const struct argv_test {
const char *cmdline;
@@ -652,6 +465,12 @@ const struct argv_test {
#endif /* MOD3 */
};
+void out_of_memory(void)
+{
+ fprintf(stderr, "out of memory!\n");
+ exit(2);
+}
+
int main(int argc, char **argv)
{
int i, j;
@@ -692,27 +511,29 @@ int main(int argc, char **argv)
}
if (!strcmp(argv[1], "-split") && argc > 2) {
- char *str = malloc(20 + strlen(argv[0]) + strlen(argv[2]));
- char *p, *q;
+ strbuf *cmdline = strbuf_new();
+ char *p;
- q = str + sprintf(str, "%s -splat ", argv[0]);
+ put_fmt(cmdline, "%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;
- }
+ size_t args_start = cmdline->len;
+ for (p = argv[2]; *p; p++) {
+ char c = (*p == '/' ? '\\' :
+ *p == '\'' ? '"' :
+ *p == '_' ? ' ' :
+ *p);
+ put_byte(cmdline, c);
}
- *p = '\0';
+ write_c_string_literal(stdout, ptrlen_from_asciz(
+ cmdline->s + args_start));
printf("\", {");
fflush(stdout);
- system(str);
+ system(cmdline->s);
printf("}},\n");
+ strbuf_free(cmdline);
return 0;
}
@@ -745,6 +566,83 @@ int main(int argc, char **argv)
return 0;
}
+ if (!strcmp(argv[1], "-tabulate")) {
+ char table[] = "\
+ * backslashes \n\
+ * \n\
+ * 0 1 2 3 4 \n\
+ * \n\
+ * 0 | \n\
+ * --------+----------------------------- \n\
+ * 1 | \n\
+ * q 2 | \n\
+ * u 3 | \n\
+ * o 4 | \n\
+ * t 5 | \n\
+ * e 6 | \n\
+ * s 7 | \n\
+ * 8 | \n\
+";
+ char *linestarts[14];
+ char *p = table;
+ for (i = 0; i < lenof(linestarts); i++) {
+ linestarts[i] = p;
+ p += strcspn(p, "\n");
+ if (*p) p++;
+ }
+
+ for (i = 0; i < lenof(argv_tests); i++) {
+ const struct argv_test *test = &argv_tests[i];
+ const char *q = test->cmdline;
+
+ /* Skip tests that aren't telling us something about
+ * the behaviour _inside_ a quoted string */
+ if (*q != '"')
+ continue;
+
+ q++;
+
+ assert(*q == 'a');
+ q++;
+ int backslashes_in = 0, quotes_in = 0;
+ while (*q == '\\') {
+ q++;
+ backslashes_in++;
+ }
+ while (*q == '"') {
+ q++;
+ quotes_in++;
+ }
+
+ q = test->argv[0];
+ assert(*q == 'a');
+ q++;
+ int backslashes_out = 0, quotes_out = 0;
+ while (*q == '\\') {
+ q++;
+ backslashes_out++;
+ }
+ while (*q == '"') {
+ q++;
+ quotes_out++;
+ }
+ assert(*q == 'b');
+ q++;
+ bool in_quoted_string = (*q == ' ');
+
+ int x = (backslashes_in == 0 ? 15 : 18 + 7 * backslashes_in);
+ int y = (quotes_in == 0 ? 4 : 5 + quotes_in);
+ char *buf = dupprintf("%d,%d,%c",
+ backslashes_out, quotes_out,
+ in_quoted_string ? 'y' : 'n');
+ memcpy(linestarts[y] + x, buf, strlen(buf));
+ sfree(buf);
+ }
+
+ fputs(table, stdout);
+ return 0;
+ }
+
fprintf(stderr, "unrecognised option: \"%s\"\n", argv[1]);
return 1;
}
@@ -753,18 +651,21 @@ int main(int argc, char **argv)
* If we get here, we were invoked with no arguments, so just
* run the tests.
*/
+ int passes = 0, fails = 0;
for (i = 0; i < lenof(argv_tests); i++) {
int ac;
char **av;
+ bool failed = false;
- split_into_argv(argv_tests[i].cmdline, &ac, &av);
+ split_into_argv((char *)argv_tests[i].cmdline, &ac, &av, NULL);
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]);
+ failed = true;
}
#ifdef VERBOSE
else {
@@ -774,15 +675,33 @@ int main(int argc, char **argv)
}
#endif
}
- if (j < ac)
+ 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])
+ failed = true;
+ }
+ if (argv_tests[i].argv[j]) {
printf("failed test %d (|%s|): %d args returned, should be more\n",
i, argv_tests[i].cmdline, ac);
+ failed = true;
+ }
+
+ if (failed)
+ fails++;
+ else
+ passes++;
}
- return 0;
+ printf("passed %d failed %d (%s mode)\n",
+ passes, fails,
+#if MOD3
+ "mod 3"
+#else
+ "mod 2"
+#endif
+ );
+
+ return fails != 0;
}
-#endif
+#endif /* TEST */
diff --git a/windows/utils/strtoumax.c b/windows/utils/strtoumax.c
new file mode 100644
index 00000000..38d00014
--- /dev/null
+++ b/windows/utils/strtoumax.c
@@ -0,0 +1,12 @@
+/*
+ * Work around lack of strtoumax in older MSVC libraries.
+ */
+
+#include <stdlib.h>
+
+#include "defs.h"
+
+uintmax_t strtoumax(const char *nptr, char **endptr, int base)
+{
+ return _strtoui64(nptr, endptr, base);
+}
diff --git a/windows/utils/version.c b/windows/utils/version.c
new file mode 100644
index 00000000..b626710e
--- /dev/null
+++ b/windows/utils/version.c
@@ -0,0 +1,45 @@
+#include "putty.h"
+
+DWORD osMajorVersion, osMinorVersion, osPlatformId;
+
+void init_winver(void)
+{
+ static bool initialised = false;
+ if (initialised)
+ return;
+ initialised = true;
+
+ 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 */
+ }
+}
diff --git a/windows/utils/win_strerror.c b/windows/utils/win_strerror.c
new file mode 100644
index 00000000..5572bc8b
--- /dev/null
+++ b/windows/utils/win_strerror.c
@@ -0,0 +1,72 @@
+/*
+ * Wrapper around the Windows FormatMessage system for retrieving the
+ * text of a system error code, with a simple API similar to strerror.
+ *
+ * Works by keeping a tree234 containing mappings from system error
+ * codes to strings. Entries allocated in this tree are simply never
+ * freed.
+ *
+ * Also, the returned string has its trailing newline removed (so it
+ * can go in places like the Event Log that never want a newline), and
+ * is prefixed with the error number (so that if a user sends an error
+ * report containing a translated error message we can't read, we can
+ * still find out what the error actually was).
+ */
+
+#include "putty.h"
+
+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;
+}
diff --git a/windows/winseat.h b/windows/win-gui-seat.h
index c6b5fa96..19c5cbea 100644
--- a/windows/winseat.h
+++ b/windows/win-gui-seat.h
@@ -1,6 +1,6 @@
/*
* Small implementation of Seat and LogPolicy shared between window.c
- * and windlg.c.
+ * and dialog.c.
*/
typedef struct WinGuiSeat WinGuiSeat;
@@ -11,4 +11,4 @@ struct WinGuiSeat {
LogPolicy logpolicy;
};
-extern const LogPolicyVtable win_gui_logpolicy_vt; /* in windlg.c */
+extern const LogPolicyVtable win_gui_logpolicy_vt; /* in dialog.c */
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/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/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/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/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/winx11.c b/windows/x11.c
index 800d8509..98bbb627 100644
--- a/windows/winx11.c
+++ b/windows/x11.c
@@ -1,5 +1,5 @@
/*
- * winx11.c: fetch local auth data for X forwarding.
+ * x11.c: fetch local auth data for X forwarding.
*/
#include <ctype.h>