1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00

Merge demo screenshot features from 'pre-0.77'.

This commit is contained in:
Simon Tatham 2022-04-02 16:51:55 +01:00
commit dec7d7fce7
12 changed files with 345 additions and 92 deletions

View File

@ -14,6 +14,7 @@
#cmakedefine01 HAVE_GETNAMEDPIPECLIENTPROCESSID
#cmakedefine01 HAVE_SETDEFAULTDLLDIRECTORIES
#cmakedefine01 HAVE_STRTOUMAX
#cmakedefine01 HAVE_DWMAPI_H
#cmakedefine NOT_X_WINDOWS

View File

@ -51,6 +51,8 @@ check_symbol_exists(GetNamedPipeClientProcessId "windows.h"
HAVE_GETNAMEDPIPECLIENTPROCESSID)
check_symbol_exists(CreatePseudoConsole "windows.h" HAVE_CONPTY)
check_include_files("windows.h;dwmapi.h" HAVE_DWMAPI_H)
check_c_source_compiles("
#include <windows.h>
GCP_RESULTSW gcpw;

View File

@ -32,11 +32,8 @@
#include "putty.h"
static char *null_init(const BackendVtable *, Seat *, Backend **, LogContext *,
Conf *, const char *, int, char **, bool, bool);
static char *loop_init(const BackendVtable *, Seat *, Backend **, LogContext *,
Conf *, const char *, int, char **, bool, bool);
static void null_free(Backend *);
static void loop_free(Backend *);
static void null_reconfig(Backend *, Conf *);
static void null_send(Backend *, const char *, size_t);
@ -55,8 +52,8 @@ static void null_unthrottle(Backend *, size_t);
static int null_cfg_info(Backend *);
const BackendVtable null_backend = {
.init = null_init,
.free = null_free,
.init = loop_init,
.free = loop_free,
.reconfig = null_reconfig,
.send = null_send,
.sendbuffer = null_sendbuffer,
@ -106,17 +103,6 @@ struct loop_state {
size_t sendbuffer;
};
static char *null_init(const BackendVtable *vt, Seat *seat,
Backend **backend_handle, LogContext *logctx,
Conf *conf, const char *host, int port,
char **realhost, bool nodelay, bool keepalive) {
/* No local authentication phase in this protocol */
seat_set_trust_status(seat, false);
*backend_handle = NULL;
return NULL;
}
static char *loop_init(const BackendVtable *vt, Seat *seat,
Backend **backend_handle, LogContext *logctx,
Conf *conf, const char *host, int port,
@ -127,15 +113,14 @@ static char *loop_init(const BackendVtable *vt, Seat *seat,
seat_set_trust_status(seat, false);
st->seat = seat;
st->backend.vt = vt;
*backend_handle = &st->backend;
*realhost = dupstr(host);
return NULL;
}
static void null_free(Backend *be)
{
}
static void loop_free(Backend *be)
{
struct loop_state *st = container_of(be, struct loop_state, backend);

View File

@ -1307,6 +1307,8 @@ static int sessioncmp(const void *av, const void *bv)
return strcmp(a, b); /* otherwise, compare normally */
}
bool sesslist_demo_mode = false;
void get_sesslist(struct sesslist *list, bool allocate)
{
int i;
@ -1316,12 +1318,18 @@ void get_sesslist(struct sesslist *list, bool allocate)
if (allocate) {
strbuf *sb = strbuf_new();
if ((handle = enum_settings_start()) != NULL) {
while (enum_settings_next(handle, sb))
put_byte(sb, '\0');
enum_settings_finish(handle);
if (sesslist_demo_mode) {
put_asciz(sb, "demo-server");
put_asciz(sb, "demo-server-2");
} else {
if ((handle = enum_settings_start()) != NULL) {
while (enum_settings_next(handle, sb))
put_byte(sb, '\0');
enum_settings_finish(handle);
}
put_byte(sb, '\0');
}
put_byte(sb, '\0');
list->buffer = strbuf_to_str(sb);
/*

View File

@ -28,6 +28,7 @@ add_sources_from_current_dir(utils
utils/platform_get_x_display.c
utils/registry_get_string.c
utils/request_file.c
utils/screenshot.c
utils/security.c
utils/split_into_argv.c
utils/version.c

View File

@ -388,6 +388,8 @@ static void create_controls(HWND hwnd, char *path)
}
}
const char *dialog_box_demo_screenshot_filename = NULL;
/*
* This function is the configuration box.
* (Being a dialog procedure, in general it returns 0 if the default
@ -396,6 +398,7 @@ static void create_controls(HWND hwnd, char *path)
static INT_PTR CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
{
const int DEMO_SCREENSHOT_TIMER_ID = 1230;
HWND hw, treeview;
struct treeview_faff tvfaff;
int ret;
@ -565,6 +568,21 @@ static INT_PTR CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
* spurious firing during the above setup procedure.
*/
SetWindowLongPtr(hwnd, GWLP_USERDATA, 1);
if (dialog_box_demo_screenshot_filename)
SetTimer(hwnd, DEMO_SCREENSHOT_TIMER_ID, TICKSPERSEC, NULL);
return 0;
case WM_TIMER:
if (dialog_box_demo_screenshot_filename &&
(UINT_PTR)wParam == DEMO_SCREENSHOT_TIMER_ID) {
KillTimer(hwnd, DEMO_SCREENSHOT_TIMER_ID);
const char *err = save_screenshot(
hwnd, dialog_box_demo_screenshot_filename);
if (err)
MessageBox(hwnd, err, "Demo screenshot failure",
MB_OK | MB_ICONERROR);
SaneEndDialog(hwnd, 0);
}
return 0;
case WM_LBUTTONUP:
/*

View File

@ -759,4 +759,7 @@ 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 */

View File

@ -45,3 +45,7 @@ const wchar_t *get_app_user_model_id(void)
{
return L"SimonTatham.Pterm";
}
void gui_terminal_ready(HWND hwnd, Seat *seat, Backend *backend)
{
}

View File

@ -1,10 +1,16 @@
#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;
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. */
@ -81,6 +87,29 @@ void gui_term_process_cmdline(Conf *conf, char *cmdline)
} else if (!strcmp(p, "-pgpfp")) {
pgp_fingerprints_msgbox(NULL);
exit(1);
} 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 {
@ -91,13 +120,26 @@ void gui_term_process_cmdline(Conf *conf, char *cmdline)
cmdline_run_saved(conf);
/*
* 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);
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);
@ -105,6 +147,10 @@ void gui_term_process_cmdline(Conf *conf, char *cmdline)
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.
@ -125,3 +171,19 @@ 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;
save_screenshot(hwnd, terminal_demo_screenshot_filename);
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);
}
}

View File

@ -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.
@ -992,6 +994,65 @@ void ui_set_fptype(HWND hwnd, struct MainDlgState *state, int option)
}
}
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, 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;
}
void load_key_file(HWND hwnd, struct MainDlgState *state,
Filename *filename, bool was_import_cmd)
{
@ -1081,65 +1142,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
@ -1205,6 +1208,7 @@ static void start_generating_key(HWND hwnd, struct MainDlgState *state)
static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
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;
@ -1428,9 +1432,30 @@ 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);
const char *err = save_screenshot(hwnd, demo_screenshot_filename);
if (err)
MessageBox(hwnd, err, "Demo screenshot failure",
MB_OK | MB_ICONERROR);
EndDialog(hwnd, 0);
}
return 0;
case WM_MOUSEMOVE:
state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
if (state->entropy && state->entropy_got < state->entropy_required) {
@ -2175,6 +2200,22 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
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 {
opt_error("unrecognised option '%s'\n", amo.argv[amo.index]);
}

126
windows/utils/screenshot.c Normal file
View File

@ -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 */

View File

@ -810,6 +810,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
term_set_focus(term, GetForegroundWindow() == wgs.term_hwnd);
UpdateWindow(wgs.term_hwnd);
gui_terminal_ready(wgs.term_hwnd, &wgs.seat, backend);
while (1) {
int n;
DWORD timeout;