mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 09:27:59 +00:00
Add support for the SUPDUP protocol.
Based on work by Josh Dersch, with permission.
This commit is contained in:
parent
ad6987e1b1
commit
315933c114
2
Recipe
2
Recipe
@ -253,7 +253,7 @@ GTKTERM = UXTERM gtkwin gtkcfg gtkdlg gtkfont gtkcols gtkmisc xkeysym
|
|||||||
GTKMAIN = gtkmain cmdline
|
GTKMAIN = gtkmain cmdline
|
||||||
|
|
||||||
# Non-SSH back ends (putty, puttytel, plink).
|
# Non-SSH back ends (putty, puttytel, plink).
|
||||||
NONSSH = telnet raw rlogin ldisc pinger
|
NONSSH = telnet raw rlogin supdup ldisc pinger
|
||||||
|
|
||||||
# SSH back end (putty, plink, pscp, psftp).
|
# SSH back end (putty, plink, pscp, psftp).
|
||||||
ARITH = mpint ecc
|
ARITH = mpint ecc
|
||||||
|
1
be_all.c
1
be_all.c
@ -26,6 +26,7 @@ const struct BackendVtable *const backends[] = {
|
|||||||
&ssh_backend,
|
&ssh_backend,
|
||||||
&telnet_backend,
|
&telnet_backend,
|
||||||
&rlogin_backend,
|
&rlogin_backend,
|
||||||
|
&supdup_backend,
|
||||||
&raw_backend,
|
&raw_backend,
|
||||||
&sshconn_backend,
|
&sshconn_backend,
|
||||||
NULL
|
NULL
|
||||||
|
@ -26,6 +26,7 @@ const struct BackendVtable *const backends[] = {
|
|||||||
&ssh_backend,
|
&ssh_backend,
|
||||||
&telnet_backend,
|
&telnet_backend,
|
||||||
&rlogin_backend,
|
&rlogin_backend,
|
||||||
|
&supdup_backend,
|
||||||
&raw_backend,
|
&raw_backend,
|
||||||
&serial_backend,
|
&serial_backend,
|
||||||
&sshconn_backend,
|
&sshconn_backend,
|
||||||
|
@ -13,6 +13,7 @@ const char *const appname = "PuTTYtel";
|
|||||||
const struct BackendVtable *const backends[] = {
|
const struct BackendVtable *const backends[] = {
|
||||||
&telnet_backend,
|
&telnet_backend,
|
||||||
&rlogin_backend,
|
&rlogin_backend,
|
||||||
|
&supdup_backend,
|
||||||
&raw_backend,
|
&raw_backend,
|
||||||
&serial_backend,
|
&serial_backend,
|
||||||
NULL
|
NULL
|
||||||
|
@ -13,6 +13,7 @@ const char *const appname = "PuTTYtel";
|
|||||||
const struct BackendVtable *const backends[] = {
|
const struct BackendVtable *const backends[] = {
|
||||||
&telnet_backend,
|
&telnet_backend,
|
||||||
&rlogin_backend,
|
&rlogin_backend,
|
||||||
|
&supdup_backend,
|
||||||
&raw_backend,
|
&raw_backend,
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
30
config.c
30
config.c
@ -2367,6 +2367,36 @@ void setup_config_box(struct controlbox *b, bool midsession,
|
|||||||
HELPCTX(rlogin_localuser),
|
HELPCTX(rlogin_localuser),
|
||||||
conf_editbox_handler, I(CONF_localusername), I(1));
|
conf_editbox_handler, I(CONF_localusername), I(1));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The Protocol/SUPDUP panel.
|
||||||
|
*/
|
||||||
|
ctrl_settitle(b, "Connection/SUPDUP",
|
||||||
|
"Enabling and disabling SUPDUP user options");
|
||||||
|
|
||||||
|
s = ctrl_getset(b, "Connection/SUPDUP", "main", NULL);
|
||||||
|
|
||||||
|
ctrl_editbox(s, "Location string", 'l', 70,
|
||||||
|
HELPCTX(supdup_location),
|
||||||
|
conf_editbox_handler, I(CONF_supdup_location),
|
||||||
|
I(1));
|
||||||
|
|
||||||
|
ctrl_radiobuttons(s, "Extended ASCII Character set:", 'z', 4,
|
||||||
|
HELPCTX(supdup_ascii),
|
||||||
|
conf_radiobutton_handler,
|
||||||
|
I(CONF_supdup_ascii_set),
|
||||||
|
"None", I(SUPDUP_CHARSET_ASCII),
|
||||||
|
"ITS", I(SUPDUP_CHARSET_ITS),
|
||||||
|
"WAITS", I(SUPDUP_CHARSET_WAITS), NULL);
|
||||||
|
|
||||||
|
ctrl_checkbox(s, "**MORE** processing", 'm',
|
||||||
|
HELPCTX(supdup_more),
|
||||||
|
conf_checkbox_handler,
|
||||||
|
I(CONF_supdup_more));
|
||||||
|
|
||||||
|
ctrl_checkbox(s, "Terminal scrolling", 's',
|
||||||
|
HELPCTX(supdup_scroll),
|
||||||
|
conf_checkbox_handler,
|
||||||
|
I(CONF_supdup_scroll));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
17
putty.h
17
putty.h
@ -364,12 +364,19 @@ enum {
|
|||||||
TITLE_NONE, TITLE_EMPTY, TITLE_REAL
|
TITLE_NONE, TITLE_EMPTY, TITLE_REAL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
/* SUPDUP character set options */
|
||||||
|
SUPDUP_CHARSET_ASCII, SUPDUP_CHARSET_ITS, SUPDUP_CHARSET_WAITS
|
||||||
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
/* Protocol back ends. (CONF_protocol) */
|
/* Protocol back ends. (CONF_protocol) */
|
||||||
PROT_RAW, PROT_TELNET, PROT_RLOGIN, PROT_SSH, PROT_SSHCONN,
|
PROT_RAW, PROT_TELNET, PROT_RLOGIN, PROT_SSH, PROT_SSHCONN,
|
||||||
/* PROT_SERIAL is supported on a subset of platforms, but it doesn't
|
/* PROT_SERIAL is supported on a subset of platforms, but it doesn't
|
||||||
* hurt to define it globally. */
|
* hurt to define it globally. */
|
||||||
PROT_SERIAL,
|
PROT_SERIAL,
|
||||||
|
/* PROT_SUPDUP is the historical RFC 734 protocol. */
|
||||||
|
PROT_SUPDUP,
|
||||||
PROTOCOL_LIMIT, /* upper bound on number of protocols */
|
PROTOCOL_LIMIT, /* upper bound on number of protocols */
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1299,6 +1306,11 @@ NORETURN void cleanup_exit(int);
|
|||||||
X(INT, NONE, serstopbits) \
|
X(INT, NONE, serstopbits) \
|
||||||
X(INT, NONE, serparity) /* SER_PAR_NONE, SER_PAR_ODD, ... */ \
|
X(INT, NONE, serparity) /* SER_PAR_NONE, SER_PAR_ODD, ... */ \
|
||||||
X(INT, NONE, serflow) /* SER_FLOW_NONE, SER_FLOW_XONXOFF, ... */ \
|
X(INT, NONE, serflow) /* SER_FLOW_NONE, SER_FLOW_XONXOFF, ... */ \
|
||||||
|
/* Supdup options */ \
|
||||||
|
X(STR, NONE, supdup_location) \
|
||||||
|
X(INT, NONE, supdup_ascii_set) \
|
||||||
|
X(BOOL, NONE, supdup_more) \
|
||||||
|
X(BOOL, NONE, supdup_scroll) \
|
||||||
/* Keyboard options */ \
|
/* Keyboard options */ \
|
||||||
X(BOOL, NONE, bksp_is_delete) \
|
X(BOOL, NONE, bksp_is_delete) \
|
||||||
X(BOOL, NONE, rxvt_homeend) \
|
X(BOOL, NONE, rxvt_homeend) \
|
||||||
@ -1766,6 +1778,11 @@ extern const struct BackendVtable telnet_backend;
|
|||||||
extern const struct BackendVtable ssh_backend;
|
extern const struct BackendVtable ssh_backend;
|
||||||
extern const struct BackendVtable sshconn_backend;
|
extern const struct BackendVtable sshconn_backend;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Exports from supdup.c.
|
||||||
|
*/
|
||||||
|
extern const struct BackendVtable supdup_backend;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Exports from ldisc.c.
|
* Exports from ldisc.c.
|
||||||
*/
|
*/
|
||||||
|
16
settings.c
16
settings.c
@ -786,6 +786,14 @@ void save_open_settings(settings_w *sesskey, Conf *conf)
|
|||||||
write_setting_b(sesskey, "ConnectionSharingUpstream", conf_get_bool(conf, CONF_ssh_connection_sharing_upstream));
|
write_setting_b(sesskey, "ConnectionSharingUpstream", conf_get_bool(conf, CONF_ssh_connection_sharing_upstream));
|
||||||
write_setting_b(sesskey, "ConnectionSharingDownstream", conf_get_bool(conf, CONF_ssh_connection_sharing_downstream));
|
write_setting_b(sesskey, "ConnectionSharingDownstream", conf_get_bool(conf, CONF_ssh_connection_sharing_downstream));
|
||||||
wmap(sesskey, "SSHManualHostKeys", conf, CONF_ssh_manual_hostkeys, false);
|
wmap(sesskey, "SSHManualHostKeys", conf, CONF_ssh_manual_hostkeys, false);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SUPDUP settings
|
||||||
|
*/
|
||||||
|
write_setting_s(sesskey, "SUPDUPLocation", conf_get_str(conf, CONF_supdup_location));
|
||||||
|
write_setting_i(sesskey, "SUPDUPCharset", conf_get_int(conf, CONF_supdup_ascii_set));
|
||||||
|
write_setting_b(sesskey, "SUPDUPMoreProcessing", conf_get_bool(conf, CONF_supdup_more));
|
||||||
|
write_setting_b(sesskey, "SUPDUPScrolling", conf_get_bool(conf, CONF_supdup_scroll));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool load_settings(const char *section, Conf *conf)
|
bool load_settings(const char *section, Conf *conf)
|
||||||
@ -1255,6 +1263,14 @@ void load_open_settings(settings_r *sesskey, Conf *conf)
|
|||||||
gppb(sesskey, "ConnectionSharingDownstream", true,
|
gppb(sesskey, "ConnectionSharingDownstream", true,
|
||||||
conf, CONF_ssh_connection_sharing_downstream);
|
conf, CONF_ssh_connection_sharing_downstream);
|
||||||
gppmap(sesskey, "SSHManualHostKeys", conf, CONF_ssh_manual_hostkeys);
|
gppmap(sesskey, "SSHManualHostKeys", conf, CONF_ssh_manual_hostkeys);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SUPDUP settings
|
||||||
|
*/
|
||||||
|
gpps(sesskey, "SUPDUPLocation", "The Internet", conf, CONF_supdup_location);
|
||||||
|
gppi(sesskey, "SUPDUPCharset", false, conf, CONF_supdup_ascii_set);
|
||||||
|
gppb(sesskey, "SUPDUPMoreProcessing", false, conf, CONF_supdup_more);
|
||||||
|
gppb(sesskey, "SUPDUPScrolling", false, conf, CONF_supdup_scroll);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool do_defaults(const char *session, Conf *conf)
|
bool do_defaults(const char *session, Conf *conf)
|
||||||
|
924
supdup.c
Normal file
924
supdup.c
Normal file
@ -0,0 +1,924 @@
|
|||||||
|
/*
|
||||||
|
* Supdup backend
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TTYOPT FUNCTION BITS (36-bit bitmasks)
|
||||||
|
*/
|
||||||
|
#define TOALT 0200000000000LL // Characters 0175 and 0176 are converted to altmode (0033) on input
|
||||||
|
#define TOCLC 0100000000000LL // (user option bit) Convert lower-case input to upper-case
|
||||||
|
#define TOERS 0040000000000LL // Selective erase is supported
|
||||||
|
#define TOMVB 0010000000000LL // Backspacing is supported
|
||||||
|
#define TOSAI 0004000000000LL // Stanford/ITS extended ASCII graphics character set is supported
|
||||||
|
#define TOSA1 0002000000000LL // (user option bit) Characters 0001-0037 displayed using Stanford/ITS chars
|
||||||
|
#define TOOVR 0001000000000LL // Overprinting is supported
|
||||||
|
#define TOMVU 0000400000000LL // Moving cursor upwards is supported
|
||||||
|
#define TOMOR 0000200000000LL // (user option bit) System should provide **MORE** processing
|
||||||
|
#define TOROL 0000100000000LL // (user option bit) Terminal should scroll instead of wrapping
|
||||||
|
#define TOLWR 0000020000000LL // Lowercase characters are supported
|
||||||
|
#define TOFCI 0000010000000LL // Terminal can generate CONTROL and META characters
|
||||||
|
#define TOLID 0000002000000LL // Line insert/delete operations supported
|
||||||
|
#define TOCID 0000001000000LL // Character insert/delete operations supported
|
||||||
|
#define TPCBS 0000000000040LL // Terminal is using the "intelligent terminal protocol" (must be on)
|
||||||
|
#define TPORS 0000000000010LL // Server should process output resets
|
||||||
|
|
||||||
|
// Initialization words (36-bit constants)
|
||||||
|
#define WORDS 0777773000000 // Negative number of config words to send (6) in high 18 bits
|
||||||
|
#define TCTYP 0000000000007 // Defines the terminal type (MUST be 7)
|
||||||
|
#define TTYROL 0000000000001 // Scroll amount for terminal (1 line at a time)
|
||||||
|
|
||||||
|
|
||||||
|
// %TD opcodes
|
||||||
|
//
|
||||||
|
#define TDMOV 0200 // Cursor positioning
|
||||||
|
#define TDMV1 0201 // Internal cursor positioning
|
||||||
|
#define TDEOF 0202 // Erase to end of screen
|
||||||
|
#define TDEOL 0203 // Erase to end of line
|
||||||
|
#define TDDLF 0204 // Clear the character the cursor is on
|
||||||
|
#define TDCRL 0207 // Carriage return
|
||||||
|
#define TDNOP 0210 // No-op; should be ignored.
|
||||||
|
#define TDBS 0211 // Backspace (not in official SUPDUP spec)
|
||||||
|
#define TDLF 0212 // Linefeed (not in official SUPDUP spec)
|
||||||
|
#define TDCR 0213 // Carriage Return (ditto)
|
||||||
|
#define TDORS 0214 // Output reset
|
||||||
|
#define TDQOT 0215 // Quotes the following character
|
||||||
|
#define TDFS 0216 // Non-destructive forward space
|
||||||
|
#define TDMV0 0217 // General cursor positioning code
|
||||||
|
#define TDCLR 0220 // Erase the screen, home cursor
|
||||||
|
#define TDBEL 0221 // Generate an audio tone, bell, whatever
|
||||||
|
#define TDILP 0223 // Insert blank lines at the cursor
|
||||||
|
#define TDDLP 0224 // Delete lines at the cursor
|
||||||
|
#define TDICP 0225 // Insert blanks at cursor
|
||||||
|
#define TDDCP 0226 // Delete characters at cursor
|
||||||
|
#define TDBOW 0227 // Display black chars on white screen
|
||||||
|
#define TDRST 0230 // Reset %TDBOW
|
||||||
|
|
||||||
|
/* Maximum number of octets following a %TD code. */
|
||||||
|
#define TD_ARGS_MAX 4
|
||||||
|
|
||||||
|
typedef struct supdup_tag Supdup;
|
||||||
|
struct supdup_tag
|
||||||
|
{
|
||||||
|
Socket *s;
|
||||||
|
bool closed_on_socket_error;
|
||||||
|
|
||||||
|
Seat *seat;
|
||||||
|
LogContext *logctx;
|
||||||
|
int term_width, term_height;
|
||||||
|
|
||||||
|
long long ttyopt;
|
||||||
|
long tcmxv;
|
||||||
|
long tcmxh;
|
||||||
|
|
||||||
|
bool sent_location;
|
||||||
|
|
||||||
|
Conf *conf;
|
||||||
|
|
||||||
|
int bufsize;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CONNECTING, // waiting for %TDNOP from server after sending connection params
|
||||||
|
CONNECTED // %TDNOP received, connected.
|
||||||
|
} state;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TD_TOPLEVEL,
|
||||||
|
TD_ARGS,
|
||||||
|
TD_ARGSDONE
|
||||||
|
} tdstate;
|
||||||
|
|
||||||
|
int td_code;
|
||||||
|
int td_argcount;
|
||||||
|
char td_args[TD_ARGS_MAX];
|
||||||
|
int td_argindex;
|
||||||
|
|
||||||
|
void (*print) (strbuf *outbuf, int c);
|
||||||
|
|
||||||
|
Pinger *pinger;
|
||||||
|
|
||||||
|
Plug plug;
|
||||||
|
Backend backend;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SUPDUP_MAX_BACKLOG 4096
|
||||||
|
|
||||||
|
static void c_write(Supdup *supdup, unsigned char *buf, int len)
|
||||||
|
{
|
||||||
|
size_t backlog = seat_stdout(supdup->seat, buf, len);
|
||||||
|
sk_set_frozen(supdup->s, backlog > SUPDUP_MAX_BACKLOG);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void supdup_send_location(Supdup *supdup)
|
||||||
|
{
|
||||||
|
char locHeader[] = { 0300, 0302 };
|
||||||
|
char* locString = conf_get_str(supdup->conf, CONF_supdup_location);
|
||||||
|
|
||||||
|
sk_write(supdup->s, locHeader, sizeof(locHeader));
|
||||||
|
sk_write(supdup->s, locString, strlen(locString) + 1); // include NULL terminator
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_ascii(strbuf *outbuf, int c)
|
||||||
|
{
|
||||||
|
/* In ASCII mode, ignore control characters. The server shouldn't
|
||||||
|
send them. */
|
||||||
|
if (c >= 040 && c < 0177)
|
||||||
|
put_byte (outbuf, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_its(strbuf *outbuf, int c)
|
||||||
|
{
|
||||||
|
/* The ITS character set is documented in RFC 734. */
|
||||||
|
static const char *map[] = {
|
||||||
|
"\xc2\xb7", "\342\206\223", "\316\261", "\316\262",
|
||||||
|
"\342\210\247", "\302\254", "\316\265", "\317\200",
|
||||||
|
"\316\273", "\xce\xb3", "\xce\xb4", "\xe2\x86\x91",
|
||||||
|
"\xc2\xb1", "\xe2\x8a\x95", "\342\210\236", "\342\210\202",
|
||||||
|
"\342\212\202", "\342\212\203", "\342\210\251", "\342\210\252",
|
||||||
|
"\342\210\200", "\342\210\203", "\xe2\x8a\x97", "\342\206\224",
|
||||||
|
"\xe2\x86\x90", "\342\206\222", "\xe2\x89\xa0", "\xe2\x97\x8a",
|
||||||
|
"\342\211\244", "\342\211\245", "\342\211\241", "\342\210\250",
|
||||||
|
" ", "!", "\"", "#", "$", "%", "&", "'",
|
||||||
|
"(", ")", "*", "+", ",", "-", ".", "/",
|
||||||
|
"0", "1", "2", "3", "4", "5", "6", "7",
|
||||||
|
"8", "9", ":", ";", "<", "=", ">", "?",
|
||||||
|
"@", "A", "B", "C", "D", "E", "F", "G",
|
||||||
|
"H", "I", "J", "K", "L", "M", "N", "O",
|
||||||
|
"P", "Q", "R", "S", "T", "U", "V", "W",
|
||||||
|
"X", "Y", "Z", "[", "\\", "]", "^", "_",
|
||||||
|
"`", "a", "b", "c", "d", "e", "f", "g",
|
||||||
|
"h", "i", "j", "k", "l", "m", "n", "o",
|
||||||
|
"p", "q", "r", "s", "t", "u", "v", "w",
|
||||||
|
"x", "y", "z", "{", "|", "}", "~", "\xe2\x88\xab"
|
||||||
|
};
|
||||||
|
|
||||||
|
put_data (outbuf, map[c], strlen(map[c]));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_waits(strbuf *outbuf, int c)
|
||||||
|
{
|
||||||
|
/* The WAITS character set used at the Stanford AI Lab is documented
|
||||||
|
here: https://www.saildart.org/allow/sail-charset-utf8.html */
|
||||||
|
static const char *map[] = {
|
||||||
|
"", "\342\206\223", "\316\261", "\316\262",
|
||||||
|
"\342\210\247", "\302\254", "\316\265", "\317\200",
|
||||||
|
"\316\273", "", "", "",
|
||||||
|
"", "", "\342\210\236", "\342\210\202",
|
||||||
|
"\342\212\202", "\342\212\203", "\342\210\251", "\342\210\252",
|
||||||
|
"\342\210\200", "\342\210\203", "\xe2\x8a\x97", "\342\206\224",
|
||||||
|
"_", "\342\206\222", "~", "\xe2\x89\xa0",
|
||||||
|
"\342\211\244", "\342\211\245", "\342\211\241", "\342\210\250",
|
||||||
|
" ", "!", "\"", "#", "$", "%", "&", "'",
|
||||||
|
"(", ")", "*", "+", ",", "-", ".", "/",
|
||||||
|
"0", "1", "2", "3", "4", "5", "6", "7",
|
||||||
|
"8", "9", ":", ";", "<", "=", ">", "?",
|
||||||
|
"@", "A", "B", "C", "D", "E", "F", "G",
|
||||||
|
"H", "I", "J", "K", "L", "M", "N", "O",
|
||||||
|
"P", "Q", "R", "S", "T", "U", "V", "W",
|
||||||
|
"X", "Y", "Z", "[", "\\", "]", "\xe2\x86\x91", "\xe2\x86\x90",
|
||||||
|
"`", "a", "b", "c", "d", "e", "f", "g",
|
||||||
|
"h", "i", "j", "k", "l", "m", "n", "o",
|
||||||
|
"p", "q", "r", "s", "t", "u", "v", "w",
|
||||||
|
"x", "y", "z", "{", "|", "\xe2\x97\x8a", "}", ""
|
||||||
|
};
|
||||||
|
|
||||||
|
put_data (outbuf, map[c], strlen(map[c]));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_toplevel(Supdup *supdup, strbuf *outbuf, int c)
|
||||||
|
{
|
||||||
|
// Toplevel: Waiting for a %TD code or a printable character
|
||||||
|
if (c >= 0200) {
|
||||||
|
// Handle SUPDUP %TD codes (codes greater than or equal to 200)
|
||||||
|
supdup->td_argindex = 0;
|
||||||
|
supdup->td_code = c;
|
||||||
|
switch (c) {
|
||||||
|
case TDMOV:
|
||||||
|
// %TD codes using 4 arguments
|
||||||
|
supdup->td_argcount = 4;
|
||||||
|
supdup->tdstate = TD_ARGS;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TDMV0:
|
||||||
|
case TDMV1:
|
||||||
|
// %TD codes using 2 arguments
|
||||||
|
supdup->td_argcount = 2;
|
||||||
|
supdup->tdstate = TD_ARGS;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TDQOT:
|
||||||
|
case TDILP:
|
||||||
|
case TDDLP:
|
||||||
|
case TDICP:
|
||||||
|
case TDDCP:
|
||||||
|
// %TD codes using 1 argument
|
||||||
|
supdup->td_argcount = 1;
|
||||||
|
supdup->tdstate = TD_ARGS;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TDEOF:
|
||||||
|
case TDEOL:
|
||||||
|
case TDDLF:
|
||||||
|
case TDCRL:
|
||||||
|
case TDNOP:
|
||||||
|
case TDORS:
|
||||||
|
case TDFS:
|
||||||
|
case TDCLR:
|
||||||
|
case TDBEL:
|
||||||
|
case TDBOW:
|
||||||
|
case TDRST:
|
||||||
|
case TDBS:
|
||||||
|
case TDCR:
|
||||||
|
case TDLF:
|
||||||
|
// %TD codes using 0 arguments
|
||||||
|
supdup->td_argcount = 0;
|
||||||
|
supdup->tdstate = TD_ARGSDONE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Unhandled, ignore
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
supdup->print(outbuf, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_args(Supdup *supdup, strbuf *outbuf, int c)
|
||||||
|
{
|
||||||
|
// Collect up args for %TD code
|
||||||
|
if (supdup->td_argindex < TD_ARGS_MAX) {
|
||||||
|
supdup->td_args[supdup->td_argindex] = c;
|
||||||
|
supdup->td_argindex++;
|
||||||
|
|
||||||
|
if (supdup->td_argcount == supdup->td_argindex) {
|
||||||
|
// No more args, %TD code is ready to go.
|
||||||
|
supdup->tdstate = TD_ARGSDONE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Should never hit this state, if we do we will just
|
||||||
|
// return to TOPLEVEL.
|
||||||
|
supdup->tdstate = TD_TOPLEVEL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_argsdone(Supdup *supdup, strbuf *outbuf, int c)
|
||||||
|
{
|
||||||
|
char buf[4];
|
||||||
|
int x, y;
|
||||||
|
|
||||||
|
// Arguments for %TD code have been collected; dispatch based
|
||||||
|
// on the %TD code we're handling.
|
||||||
|
switch (supdup->td_code) {
|
||||||
|
case TDMOV:
|
||||||
|
/*
|
||||||
|
General cursor position code. Followed by four bytes;
|
||||||
|
the first two are the "old" vertical and horizontal
|
||||||
|
positions and may be ignored. The next two are the new
|
||||||
|
vertical and horizontal positions. The cursor should be
|
||||||
|
moved to this position.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// We only care about the new position.
|
||||||
|
strbuf_catf(outbuf, "\033[%d;%dH", supdup->td_args[2]+1, supdup->td_args[3]+1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TDMV0:
|
||||||
|
case TDMV1:
|
||||||
|
/*
|
||||||
|
General cursor position code. Followed by two bytes;
|
||||||
|
the new vertical and horizontal positions.
|
||||||
|
*/
|
||||||
|
strbuf_catf(outbuf, "\033[%d;%dH", supdup->td_args[0]+1, supdup->td_args[1]+1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TDEOF:
|
||||||
|
/*
|
||||||
|
Erase to end of screen. This is an optional function
|
||||||
|
since many terminals do not support this. If the
|
||||||
|
terminal does not support this function, it should be
|
||||||
|
treated the same as %TDEOL.
|
||||||
|
|
||||||
|
%TDEOF does an erase to end of line, then erases all
|
||||||
|
lines lower on the screen than the cursor. The cursor
|
||||||
|
does not move.
|
||||||
|
*/
|
||||||
|
strbuf_catf(outbuf, "\033[J");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TDEOL:
|
||||||
|
/*
|
||||||
|
Erase to end of line. This erases the character
|
||||||
|
position the cursor is at and all positions to the right
|
||||||
|
on the same line. The cursor does not move.
|
||||||
|
*/
|
||||||
|
strbuf_catf(outbuf, "\033[K");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TDDLF:
|
||||||
|
/*
|
||||||
|
Clear the character position the cursor is on. The
|
||||||
|
cursor does not move.
|
||||||
|
*/
|
||||||
|
strbuf_catf(outbuf, "\033[X");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TDCRL:
|
||||||
|
/*
|
||||||
|
If the cursor is not on the bottom line of the screen,
|
||||||
|
move cursor to the beginning of the next line and clear
|
||||||
|
that line. If the cursor is at the bottom line, scroll
|
||||||
|
up.
|
||||||
|
*/
|
||||||
|
strbuf_catf(outbuf, "\015\012");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TDNOP:
|
||||||
|
/*
|
||||||
|
No-op; should be ignored.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TDORS:
|
||||||
|
/*
|
||||||
|
Output reset. This code serves as a data mark for
|
||||||
|
aborting output much as IAC DM does in the ordinary
|
||||||
|
TELNET protocol.
|
||||||
|
*/
|
||||||
|
outbuf->len = 0;
|
||||||
|
if (!seat_get_cursor_position(supdup->seat, &x, &y))
|
||||||
|
x = y = 0;
|
||||||
|
buf[0] = 034;
|
||||||
|
buf[1] = 020;
|
||||||
|
buf[2] = y;
|
||||||
|
buf[3] = x;
|
||||||
|
sk_write(supdup->s, buf, 4);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TDQOT:
|
||||||
|
/*
|
||||||
|
Quotes the following character. This is used when
|
||||||
|
sending 8-bit codes which are not %TD codes, for
|
||||||
|
instance when loading programs into an intelligent
|
||||||
|
terminal. The following character should be passed
|
||||||
|
through intact to the terminal.
|
||||||
|
*/
|
||||||
|
|
||||||
|
put_byte(outbuf, supdup->td_args[0]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TDFS:
|
||||||
|
/*
|
||||||
|
Non-destructive forward space. The cursor moves right
|
||||||
|
one position; this code will not be sent at the end of a
|
||||||
|
line.
|
||||||
|
*/
|
||||||
|
|
||||||
|
strbuf_catf(outbuf, "\033[C");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TDCLR:
|
||||||
|
/*
|
||||||
|
Erase the screen. Home the cursor to the top left hand
|
||||||
|
corner of the screen.
|
||||||
|
*/
|
||||||
|
strbuf_catf(outbuf, "\033[2J\033[H");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TDBEL:
|
||||||
|
/*
|
||||||
|
Generate an audio tone, bell, whatever.
|
||||||
|
*/
|
||||||
|
|
||||||
|
strbuf_catf(outbuf, "\007");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TDILP:
|
||||||
|
/*
|
||||||
|
Insert blank lines at the cursor; followed by a byte
|
||||||
|
containing a count of the number of blank lines to
|
||||||
|
insert. The cursor is unmoved. The line the cursor is
|
||||||
|
on and all lines below it move down; lines moved off the
|
||||||
|
bottom of the screen are lost.
|
||||||
|
*/
|
||||||
|
strbuf_catf(outbuf, "\033[%dL", supdup->td_args[0]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TDDLP:
|
||||||
|
/*
|
||||||
|
Delete lines at the cursor; followed by a count. The
|
||||||
|
cursor is unmoved. The first line deleted is the one
|
||||||
|
the cursor is on. Lines below those deleted move up.
|
||||||
|
Newly- created lines at the bottom of the screen are
|
||||||
|
blank.
|
||||||
|
*/
|
||||||
|
strbuf_catf(outbuf, "\033[%dM", supdup->td_args[0]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TDICP:
|
||||||
|
/*
|
||||||
|
Insert blank character positions at the cursor; followed
|
||||||
|
by a count. The cursor is unmoved. The character the
|
||||||
|
cursor is on and all characters to the right on the
|
||||||
|
current line move to the right; characters moved off the
|
||||||
|
end of the line are lost.
|
||||||
|
*/
|
||||||
|
strbuf_catf(outbuf, "\033[%d@", supdup->td_args[0]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TDDCP:
|
||||||
|
/*
|
||||||
|
Delete characters at the cursor; followed by a count.
|
||||||
|
The cursor is unmoved. The first character deleted is
|
||||||
|
the one the cursor is on. Newly-created characters at
|
||||||
|
the end of the line are blank.
|
||||||
|
*/
|
||||||
|
strbuf_catf(outbuf, "\033[%dP", supdup->td_args[0]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TDBOW:
|
||||||
|
case TDRST:
|
||||||
|
/*
|
||||||
|
Display black characters on white screen.
|
||||||
|
HIGHLY OPTIONAL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Since this is HIGHLY OPTIONAL, I'm not going
|
||||||
|
// to implement it yet.
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Non-standard (whatever "standard" means here) SUPDUP
|
||||||
|
* commands. These are used (at the very least) by
|
||||||
|
* Genera's SUPDUP implementation. Cannot find any
|
||||||
|
* official documentation, behavior is based on UNIX
|
||||||
|
* SUPDUP implementation from MIT.
|
||||||
|
*/
|
||||||
|
case TDBS:
|
||||||
|
/*
|
||||||
|
* Backspace -- move cursor back one character (does not
|
||||||
|
* appear to wrap...)
|
||||||
|
*/
|
||||||
|
put_byte(outbuf, '\010');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TDLF:
|
||||||
|
/*
|
||||||
|
* Linefeed -- move cursor down one line (again, no wrapping)
|
||||||
|
*/
|
||||||
|
put_byte(outbuf, '\012');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TDCR:
|
||||||
|
/*
|
||||||
|
* Carriage return -- move cursor to start of current line.
|
||||||
|
*/
|
||||||
|
put_byte(outbuf, '\015');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return to top level to pick up the next %TD code or
|
||||||
|
// printable character.
|
||||||
|
supdup->tdstate = TD_TOPLEVEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void term_out_supdup(Supdup *supdup, strbuf *outbuf, int c)
|
||||||
|
{
|
||||||
|
if (supdup->tdstate == TD_TOPLEVEL) {
|
||||||
|
do_toplevel (supdup, outbuf, c);
|
||||||
|
} else if (supdup->tdstate == TD_ARGS) {
|
||||||
|
do_args (supdup, outbuf, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all arguments for a %TD code are ready, we will execute the code now.
|
||||||
|
if (supdup->tdstate == TD_ARGSDONE) {
|
||||||
|
do_argsdone (supdup, outbuf, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_supdup_read(Supdup *supdup, const char *buf, size_t len)
|
||||||
|
{
|
||||||
|
strbuf *outbuf = strbuf_new();
|
||||||
|
|
||||||
|
while (len--) {
|
||||||
|
int c = (unsigned char)*buf++;
|
||||||
|
switch (supdup->state) {
|
||||||
|
case CONNECTING:
|
||||||
|
// "Following the transmission of the terminal options by
|
||||||
|
// the user, the server should respond with an ASCII
|
||||||
|
// greeting message, terminated with a %TDNOP code..."
|
||||||
|
if (TDNOP == c) {
|
||||||
|
// Greeting done, switch to the CONNECTED state.
|
||||||
|
supdup->state = CONNECTED;
|
||||||
|
supdup->tdstate = TD_TOPLEVEL;
|
||||||
|
} else {
|
||||||
|
// Forward the greeting message (which is straight
|
||||||
|
// ASCII, no controls) on so it gets displayed TODO:
|
||||||
|
// filter out only printable chars?
|
||||||
|
put_byte(outbuf, c);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONNECTED:
|
||||||
|
// "All transmissions from the server after the %TDNOP
|
||||||
|
// [see above] are either printing characters or virtual
|
||||||
|
// terminal display codes." Forward these on to the
|
||||||
|
// frontend which will decide what to do with them.
|
||||||
|
term_out_supdup(supdup, outbuf, c);
|
||||||
|
/*
|
||||||
|
* Hack to make Symbolics Genera SUPDUP happy: Wait until
|
||||||
|
* after we're connected (finished the initial handshake
|
||||||
|
* and have gotten additional data) before sending the
|
||||||
|
* location string. For some reason doing so earlier
|
||||||
|
* causes the Symbolics SUPDUP to end up in an odd state.
|
||||||
|
*/
|
||||||
|
if (!supdup->sent_location) {
|
||||||
|
supdup_send_location(supdup);
|
||||||
|
supdup->sent_location = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outbuf->len >= 4096) {
|
||||||
|
c_write(supdup, outbuf->u, outbuf->len);
|
||||||
|
outbuf->len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outbuf->len)
|
||||||
|
c_write(supdup, outbuf->u, outbuf->len);
|
||||||
|
strbuf_free(outbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void supdup_log(Plug *plug, PlugLogType type, SockAddr *addr, int port,
|
||||||
|
const char *error_msg, int error_code)
|
||||||
|
{
|
||||||
|
Supdup *supdup = container_of(plug, Supdup, plug);
|
||||||
|
backend_socket_log(supdup->seat, supdup->logctx, type, addr, port,
|
||||||
|
error_msg, error_code,
|
||||||
|
supdup->conf, supdup->state != CONNECTING);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void supdup_closing(Plug *plug, const char *error_msg, int error_code,
|
||||||
|
bool calling_back)
|
||||||
|
{
|
||||||
|
Supdup *supdup = container_of(plug, Supdup, plug);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We don't implement independent EOF in each direction for Telnet
|
||||||
|
* connections; as soon as we get word that the remote side has
|
||||||
|
* sent us EOF, we wind up the whole connection.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (supdup->s) {
|
||||||
|
sk_close(supdup->s);
|
||||||
|
supdup->s = NULL;
|
||||||
|
if (error_msg)
|
||||||
|
supdup->closed_on_socket_error = true;
|
||||||
|
seat_notify_remote_exit(supdup->seat);
|
||||||
|
}
|
||||||
|
if (error_msg) {
|
||||||
|
logevent(supdup->logctx, error_msg);
|
||||||
|
seat_connection_fatal(supdup->seat, "%s", error_msg);
|
||||||
|
}
|
||||||
|
/* Otherwise, the remote side closed the connection normally. */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void supdup_receive(Plug *plug, int urgent, const char *data, size_t len)
|
||||||
|
{
|
||||||
|
Supdup *supdup = container_of(plug, Supdup, plug);
|
||||||
|
do_supdup_read(supdup, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void supdup_sent(Plug *plug, size_t bufsize)
|
||||||
|
{
|
||||||
|
Supdup *supdup = container_of(plug, Supdup, plug);
|
||||||
|
supdup->bufsize = bufsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void supdup_send_36bits(Supdup *supdup, unsigned long long thirtysix)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// From RFC734:
|
||||||
|
// "Each word is sent through the 8-bit connection as six
|
||||||
|
// 6-bit bytes, most-significant first."
|
||||||
|
//
|
||||||
|
// Split the 36-bit word into 6 6-bit "bytes", packed into
|
||||||
|
// 8-bit bytes and send, most-significant byte first.
|
||||||
|
//
|
||||||
|
for (int i = 5; i >= 0; i--) {
|
||||||
|
char sixBits = (thirtysix >> (i * 6)) & 077;
|
||||||
|
sk_write(supdup->s, &sixBits, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void supdup_send_config(Supdup *supdup)
|
||||||
|
{
|
||||||
|
supdup_send_36bits(supdup, WORDS); // negative length
|
||||||
|
supdup_send_36bits(supdup, TCTYP); // terminal type
|
||||||
|
supdup_send_36bits(supdup, supdup->ttyopt); // options
|
||||||
|
supdup_send_36bits(supdup, supdup->tcmxv); // height
|
||||||
|
supdup_send_36bits(supdup, supdup->tcmxh); // width
|
||||||
|
supdup_send_36bits(supdup, TTYROL); // scroll amount
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called to set up the Supdup connection.
|
||||||
|
*
|
||||||
|
* Returns an error message, or NULL on success.
|
||||||
|
*
|
||||||
|
* Also places the canonical host name into `realhost'. It must be
|
||||||
|
* freed by the caller.
|
||||||
|
*/
|
||||||
|
static const char *supdup_init(const BackendVtable *x, Seat *seat,
|
||||||
|
Backend **backend_handle,
|
||||||
|
LogContext *logctx, Conf *conf,
|
||||||
|
const char *host, int port, char **realhost,
|
||||||
|
bool nodelay, bool keepalive)
|
||||||
|
{
|
||||||
|
static const PlugVtable fn_table = {
|
||||||
|
supdup_log,
|
||||||
|
supdup_closing,
|
||||||
|
supdup_receive,
|
||||||
|
supdup_sent
|
||||||
|
};
|
||||||
|
SockAddr *addr;
|
||||||
|
const char *err;
|
||||||
|
Supdup *supdup;
|
||||||
|
char *loghost;
|
||||||
|
int addressfamily;
|
||||||
|
const char *utf8 = "\033%G";
|
||||||
|
|
||||||
|
supdup = snew(struct supdup_tag);
|
||||||
|
supdup->plug.vt = &fn_table;
|
||||||
|
supdup->backend.vt = &supdup_backend;
|
||||||
|
supdup->logctx = logctx;
|
||||||
|
supdup->conf = conf_copy(conf);
|
||||||
|
supdup->s = NULL;
|
||||||
|
supdup->closed_on_socket_error = false;
|
||||||
|
supdup->seat = seat;
|
||||||
|
supdup->term_width = conf_get_int(supdup->conf, CONF_width);
|
||||||
|
supdup->term_height = conf_get_int(supdup->conf, CONF_height);
|
||||||
|
supdup->pinger = NULL;
|
||||||
|
supdup->sent_location = false;
|
||||||
|
*backend_handle = &supdup->backend;
|
||||||
|
|
||||||
|
switch (conf_get_int(supdup->conf, CONF_supdup_ascii_set)) {
|
||||||
|
case SUPDUP_CHARSET_ASCII:
|
||||||
|
supdup->print = print_ascii;
|
||||||
|
break;
|
||||||
|
case SUPDUP_CHARSET_ITS:
|
||||||
|
supdup->print = print_its;
|
||||||
|
break;
|
||||||
|
case SUPDUP_CHARSET_WAITS:
|
||||||
|
supdup->print = print_waits;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try to find host.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
char *buf;
|
||||||
|
addressfamily = conf_get_int(supdup->conf, CONF_addressfamily);
|
||||||
|
buf = dupprintf("Looking up host \"%s\"%s", host,
|
||||||
|
(addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
|
||||||
|
(addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :
|
||||||
|
"")));
|
||||||
|
logevent(supdup->logctx, buf);
|
||||||
|
sfree(buf);
|
||||||
|
}
|
||||||
|
addr = name_lookup(host, port, realhost, supdup->conf, addressfamily, NULL, "");
|
||||||
|
if ((err = sk_addr_error(addr)) != NULL) {
|
||||||
|
sk_addr_free(addr);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (port < 0)
|
||||||
|
port = 0137; /* default supdup port */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open socket.
|
||||||
|
*/
|
||||||
|
supdup->s = new_connection(addr, *realhost, port, false, true,
|
||||||
|
nodelay, keepalive, &supdup->plug, supdup->conf);
|
||||||
|
if ((err = sk_socket_error(supdup->s)) != NULL)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
supdup->pinger = pinger_new(supdup->conf, &supdup->backend);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We can send special commands from the start.
|
||||||
|
*/
|
||||||
|
seat_update_specials_menu(supdup->seat);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* loghost overrides realhost, if specified.
|
||||||
|
*/
|
||||||
|
loghost = conf_get_str(supdup->conf, CONF_loghost);
|
||||||
|
if (*loghost) {
|
||||||
|
char *colon;
|
||||||
|
|
||||||
|
sfree(*realhost);
|
||||||
|
*realhost = dupstr(loghost);
|
||||||
|
|
||||||
|
colon = host_strrchr(*realhost, ':');
|
||||||
|
if (colon)
|
||||||
|
*colon++ = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up TTYOPTS based on config
|
||||||
|
*/
|
||||||
|
int ascii_set = conf_get_int(supdup->conf, CONF_supdup_ascii_set);
|
||||||
|
int more_processing = conf_get_bool(supdup->conf, CONF_supdup_more);
|
||||||
|
int scrolling = conf_get_bool(supdup->conf, CONF_supdup_scroll);
|
||||||
|
supdup->ttyopt =
|
||||||
|
TOERS |
|
||||||
|
TOMVB |
|
||||||
|
(ascii_set == SUPDUP_CHARSET_ASCII ? 0 : TOSAI | TOSA1) |
|
||||||
|
TOMVU |
|
||||||
|
TOLWR |
|
||||||
|
TOLID |
|
||||||
|
TOCID |
|
||||||
|
TPCBS |
|
||||||
|
(scrolling ? TOROL : 0) |
|
||||||
|
(more_processing ? TOMOR : 0) |
|
||||||
|
TPORS;
|
||||||
|
|
||||||
|
supdup->tcmxh = supdup->term_width - 1; // -1 "..one column is used to indicate line continuation."
|
||||||
|
supdup->tcmxv = supdup->term_height;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send our configuration words to the server
|
||||||
|
*/
|
||||||
|
supdup_send_config(supdup);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We next expect a connection message followed by %TDNOP from the server
|
||||||
|
*/
|
||||||
|
supdup->state = CONNECTING;
|
||||||
|
seat_set_trust_status(supdup->seat, false);
|
||||||
|
|
||||||
|
/* Make sure the terminal is in UTF-8 mode. */
|
||||||
|
c_write(supdup, (unsigned char *)utf8, strlen(utf8));
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void supdup_free(Backend *be)
|
||||||
|
{
|
||||||
|
Supdup *supdup = container_of(be, Supdup, backend);
|
||||||
|
|
||||||
|
if (supdup->s)
|
||||||
|
sk_close(supdup->s);
|
||||||
|
if (supdup->pinger)
|
||||||
|
pinger_free(supdup->pinger);
|
||||||
|
conf_free(supdup->conf);
|
||||||
|
sfree(supdup);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reconfigure the Supdup backend.
|
||||||
|
*/
|
||||||
|
static void supdup_reconfig(Backend *be, Conf *conf)
|
||||||
|
{
|
||||||
|
/* Nothing to do; SUPDUP cannot be reconfigured while running. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called to send data down the Supdup connection.
|
||||||
|
*/
|
||||||
|
static size_t supdup_send(Backend *be, const char *buf, size_t len)
|
||||||
|
{
|
||||||
|
Supdup *supdup = container_of(be, Supdup, backend);
|
||||||
|
char c;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (supdup->s == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
if (buf[i] == 034)
|
||||||
|
supdup->bufsize = sk_write(supdup->s, "\034\034", 2);
|
||||||
|
else {
|
||||||
|
c = buf[i] & 0177;
|
||||||
|
supdup->bufsize = sk_write(supdup->s, &c, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return supdup->bufsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called to query the current socket sendability status.
|
||||||
|
*/
|
||||||
|
static size_t supdup_sendbuffer(Backend *be)
|
||||||
|
{
|
||||||
|
Supdup *supdup = container_of(be, Supdup, backend);
|
||||||
|
return supdup->bufsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called to set the size of the window from Supdup's POV.
|
||||||
|
*/
|
||||||
|
static void supdup_size(Backend *be, int width, int height)
|
||||||
|
{
|
||||||
|
Supdup *supdup = container_of(be, Supdup, backend);
|
||||||
|
|
||||||
|
supdup->term_width = width;
|
||||||
|
supdup->term_height = height;
|
||||||
|
|
||||||
|
//
|
||||||
|
// SUPDUP does not support resizing the terminal after connection
|
||||||
|
// establishment.
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send Telnet special codes.
|
||||||
|
*/
|
||||||
|
static void supdup_special(Backend *be, SessionSpecialCode code, int arg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static const SessionSpecial *supdup_get_specials(Backend *be)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool supdup_connected(Backend *be)
|
||||||
|
{
|
||||||
|
Supdup *supdup = container_of(be, Supdup, backend);
|
||||||
|
return supdup->s != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool supdup_sendok(Backend *be)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void supdup_unthrottle(Backend *be, size_t backlog)
|
||||||
|
{
|
||||||
|
Supdup *supdup = container_of(be, Supdup, backend);
|
||||||
|
sk_set_frozen(supdup->s, backlog > SUPDUP_MAX_BACKLOG);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool supdup_ldisc(Backend *be, int option)
|
||||||
|
{
|
||||||
|
/* No support for echoing or local editing. */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void supdup_provide_ldisc(Backend *be, Ldisc *ldisc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static int supdup_exitcode(Backend *be)
|
||||||
|
{
|
||||||
|
Supdup *supdup = container_of(be, Supdup, backend);
|
||||||
|
if (supdup->s != NULL)
|
||||||
|
return -1; /* still connected */
|
||||||
|
else if (supdup->closed_on_socket_error)
|
||||||
|
return INT_MAX; /* a socket error counts as an unclean exit */
|
||||||
|
else
|
||||||
|
/* Supdup doesn't transmit exit codes back to the client */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* cfg_info for Dupdup does nothing at all.
|
||||||
|
*/
|
||||||
|
static int supdup_cfg_info(Backend *be)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct BackendVtable supdup_backend = {
|
||||||
|
supdup_init,
|
||||||
|
supdup_free,
|
||||||
|
supdup_reconfig,
|
||||||
|
supdup_send,
|
||||||
|
supdup_sendbuffer,
|
||||||
|
supdup_size,
|
||||||
|
supdup_special,
|
||||||
|
supdup_get_specials,
|
||||||
|
supdup_connected,
|
||||||
|
supdup_exitcode,
|
||||||
|
supdup_sendok,
|
||||||
|
supdup_ldisc,
|
||||||
|
supdup_provide_ldisc,
|
||||||
|
supdup_unthrottle,
|
||||||
|
supdup_cfg_info,
|
||||||
|
NULL /* test_for_upstream */,
|
||||||
|
"supdup",
|
||||||
|
"SUPDUP",
|
||||||
|
PROT_SUPDUP,
|
||||||
|
0137,
|
||||||
|
BACKEND_RESIZE_FORBIDDEN | BACKEND_NEEDS_TERMINAL
|
||||||
|
};
|
@ -1,7 +1,7 @@
|
|||||||
#include "rcstuff.h"
|
#include "rcstuff.h"
|
||||||
|
|
||||||
#define APPNAME "PuTTY"
|
#define APPNAME "PuTTY"
|
||||||
#define APPDESC "SSH, Telnet and Rlogin client"
|
#define APPDESC "SSH, Telnet, Rlogin and SUPDUDP client"
|
||||||
|
|
||||||
#include "winhelp.rc2"
|
#include "winhelp.rc2"
|
||||||
#include "win_res.rc2"
|
#include "win_res.rc2"
|
||||||
|
@ -52,6 +52,10 @@
|
|||||||
#define WINHELP_CTX_terminal_localecho "config-localecho"
|
#define WINHELP_CTX_terminal_localecho "config-localecho"
|
||||||
#define WINHELP_CTX_terminal_localedit "config-localedit"
|
#define WINHELP_CTX_terminal_localedit "config-localedit"
|
||||||
#define WINHELP_CTX_terminal_printing "config-printing"
|
#define WINHELP_CTX_terminal_printing "config-printing"
|
||||||
|
#define WINHELP_CTX_supdup_location "supdup-location"
|
||||||
|
#define WINHELP_CTX_supdup_ascii "supdup-ascii"
|
||||||
|
#define WINHELP_CTX_supdup_more "supdup-more"
|
||||||
|
#define WINHELP_CTX_supdup_scroll "supdup-scroll"
|
||||||
#define WINHELP_CTX_bell_style "config-bellstyle"
|
#define WINHELP_CTX_bell_style "config-bellstyle"
|
||||||
#define WINHELP_CTX_bell_taskbar "config-belltaskbar"
|
#define WINHELP_CTX_bell_taskbar "config-belltaskbar"
|
||||||
#define WINHELP_CTX_bell_overload "config-bellovl"
|
#define WINHELP_CTX_bell_overload "config-bellovl"
|
||||||
|
Loading…
Reference in New Issue
Block a user