From f12be80774ce4e64b7766abfbf962037246a8473 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sun, 24 Oct 2004 13:31:55 +0000 Subject: [PATCH] Yet more trunk merges. Jacob's SSH signals stuff, some docs changes, and stability stuff in ssh.c. [originally from svn r4674] --- doc/config.but | 35 ++++---- doc/using.but | 40 +++++---- putty.h | 14 ++- ssh.c | 233 ++++++++++++++++++++++++++++++++++++------------- sshzlib.c | 136 +++++++++++++++++++++++++++-- telnet.c | 8 +- unix/pterm.c | 43 +++++++-- window.c | 55 ++++++++---- 8 files changed, 442 insertions(+), 122 deletions(-) diff --git a/doc/config.but b/doc/config.but index 6fb95b6b..fa75ae13 100644 --- a/doc/config.but +++ b/doc/config.but @@ -1,4 +1,4 @@ -\versionid $Id: config.but,v 1.92.2.2 2004/10/24 13:29:02 simon Exp $ +\versionid $Id: config.but,v 1.92.2.3 2004/10/24 13:31:55 simon Exp $ \C{config} Configuring PuTTY @@ -1199,11 +1199,22 @@ native keyboard layout is not US or UK. \cfg{winhelp-topic}{translation.linedraw} -VT100-series terminals allow the server to send control sequences -that shift temporarily into a separate character set for drawing -lines and boxes. PuTTY has a variety of ways to support this -capability. In general you should probably try lots of options until -you find one that your particular font supports. +VT100-series terminals allow the server to send control sequences that +shift temporarily into a separate character set for drawing simple +lines and boxes. However, there are a variety of ways in which PuTTY +can attempt to find appropriate characters, and the right one to use +depends on the locally configured font. In general you should probably +try lots of options until you find one that your particular font +supports. + +\b \q{Use Unicode line drawing code points} tries to use the box +characters that are present in Unicode. For good Unicode-supporting +fonts this is probably the most reliable and functional option. + +\b \q{Poor man's line drawing} assumes that the font \e{cannot} +generate the line and box characters at all, so it will use the +\c{+}, \c{-} and \c{|} characters to draw approximations to boxes. +You should use this option if none of the other options works. \b \q{Font has XWindows encoding} is for use with fonts that have a special encoding, where the lowest 32 character positions (below the @@ -1220,15 +1231,6 @@ different size depending on which character set you try to use. \b \q{Use font in OEM mode only} is more reliable than that, but can miss out other characters from the main character set. -\b \q{Poor man's line drawing} assumes that the font \e{cannot} -generate the line and box characters at all, so it will use the -\c{+}, \c{-} and \c{|} characters to draw approximations to boxes. -You should use this option if none of the other options works. - -\b \q{Unicode mode} tries to use the box characters that are present -in Unicode. For good Unicode-supporting fonts this is probably the -most reliable and functional option. - \S{config-linedrawpaste} Controlling copy and paste of line drawing characters @@ -1248,7 +1250,8 @@ layout in another program, for example. Note that this option only applies to line-drawing characters which \e{were} printed by using the VT100 mechanism. Line-drawing -characters displayed using Unicode will paste as Unicode always. +characters that were received as Unicode code points will paste as +Unicode always. \H{config-selection} The Selection panel diff --git a/doc/using.but b/doc/using.but index ed887cba..6f794eea 100644 --- a/doc/using.but +++ b/doc/using.but @@ -1,4 +1,4 @@ -\versionid $Id: using.but,v 1.33.2.1 2004/10/24 13:21:11 simon Exp $ +\versionid $Id: using.but,v 1.33.2.2 2004/10/24 13:31:55 simon Exp $ \C{using} Using PuTTY @@ -175,19 +175,27 @@ PuTTY can also be configured to send this when Ctrl-Z is typed; see In an SSH connection, the following special commands are available: -\b \I{Break, SSH special command}Break - -\lcont{ -Optional extension; may not be supported by server. PuTTY requests the -server's default break length. -} - \b \I{IGNORE message, SSH special command}\I{No-op, in SSH}IGNORE message \lcont{ Should have no effect. } +\b \I{Break, SSH special command}Break + +\lcont{ +Only available in SSH-2, and only during a session. Optional +extension; may not be supported by server. PuTTY requests the server's +default break length. +} + +\b \I{Signal, SSH special command}Signals (SIGINT, SIGTERM etc) + +\lcont{ +Only available in SSH-2, and only during a session. Sends various +POSIX signals. Not honoured by all servers. +} + \S2{using-newsession} Starting new sessions PuTTY's system menu provides some shortcut ways to start new @@ -278,15 +286,17 @@ See \k{config-logging} for more details and options. \H{using-translation} Altering your \i{character set} configuration If you find that special characters (\i{accented characters}, for -example) are not being displayed correctly in your PuTTY session, it -may be that PuTTY is interpreting the characters sent by the server -according to the wrong \e{character set}. There are a lot of -different character sets available, so it's entirely possible for -this to happen. +example, or \i{line-drawing characters}) are not being displayed +correctly in your PuTTY session, it may be that PuTTY is interpreting +the characters sent by the server according to the wrong \e{character +set}. There are a lot of different character sets available, so it's +entirely possible for this to happen. If you click \q{Change Settings} and look at the \i{\q{Translation} -panel}, you should see a large number of character sets which you -can select. Now all you need is to find out which of them you want! +panel}, you should see a large number of character sets which you can +select, and other related options. Now all you need is to find out +which of them you want! (See \k{config-translation} for more +information.) \H{using-x-forwarding} Using \i{X11 forwarding} in SSH diff --git a/putty.h b/putty.h index 34a7a760..3413c701 100644 --- a/putty.h +++ b/putty.h @@ -129,13 +129,23 @@ struct unicode_data { #define LGTYP_PACKETS 3 /* logmode: SSH data packets */ typedef enum { + /* Actual special commands. Originally Telnet, but some codes have + * been re-used for similar specials in other protocols. */ TS_AYT, TS_BRK, TS_SYNCH, TS_EC, TS_EL, TS_GA, TS_NOP, TS_ABORT, TS_AO, TS_IP, TS_SUSP, TS_EOR, TS_EOF, TS_LECHO, TS_RECHO, TS_PING, - TS_EOL + TS_EOL, + /* POSIX-style signals. (not Telnet) */ + TS_SIGABRT, TS_SIGALRM, TS_SIGFPE, TS_SIGHUP, TS_SIGILL, + TS_SIGINT, TS_SIGKILL, TS_SIGPIPE, TS_SIGQUIT, TS_SIGSEGV, + TS_SIGTERM, TS_SIGUSR1, TS_SIGUSR2, + /* Pseudo-specials used for constructing the specials menu. */ + TS_SEP, /* Separator */ + TS_SUBMENU, /* Start a new submenu with specified name */ + TS_EXITMENU /* Exit current submenu or end of specials */ } Telnet_Special; struct telnet_special { - const char *name; /* NULL==end, ""==separator */ + const char *name; int code; }; diff --git a/ssh.c b/ssh.c index 4fed0b8c..9661e2af 100644 --- a/ssh.c +++ b/ssh.c @@ -460,7 +460,7 @@ struct ssh_channel { struct ssh_agent_channel { unsigned char *message; unsigned char msglen[4]; - int lensofar, totallen; + unsigned lensofar, totallen; } a; struct ssh_x11_channel { Socket s; @@ -524,6 +524,8 @@ static void ssh_throttle_all(Ssh ssh, int enable, int bufsize); static void ssh2_set_window(struct ssh_channel *c, unsigned newwin); static int ssh_sendbuffer(void *handle); static void ssh_do_close(Ssh ssh); +static unsigned long ssh_pkt_getuint32(Ssh ssh); +static void ssh_pkt_getstring(Ssh ssh, char **p, int *length); struct rdpkt1_state_tag { long len, pad, biglen, to_read; @@ -972,15 +974,14 @@ static int ssh1_rdpkt(Ssh ssh, unsigned char **data, int *datalen) } if (ssh->pktin.type == SSH1_MSG_DEBUG) { - /* log debug message */ - char buf[512]; - int stringlen = GET_32BIT(ssh->pktin.body); - strcpy(buf, "Remote debug message: "); - if (stringlen > 480) - stringlen = 480; - memcpy(buf + 8, ssh->pktin.body + 4, stringlen); - buf[8 + stringlen] = '\0'; + char *buf, *msg; + int msglen; + + ssh_pkt_getstring(ssh, &msg, &msglen); + buf = dupprintf("Remote debug message: %.*s", msglen, msg); logevent(buf); + sfree(buf); + goto next_packet; } else if (ssh->pktin.type == SSH1_MSG_IGNORE) { /* do nothing */ @@ -989,17 +990,12 @@ static int ssh1_rdpkt(Ssh ssh, unsigned char **data, int *datalen) if (ssh->pktin.type == SSH1_MSG_DISCONNECT) { /* log reason code in disconnect message */ - char buf[256]; - unsigned msglen = GET_32BIT(ssh->pktin.body); - unsigned nowlen; - strcpy(buf, "Remote sent disconnect: "); - nowlen = strlen(buf); - if (msglen > sizeof(buf) - nowlen - 1) - msglen = sizeof(buf) - nowlen - 1; - memcpy(buf + nowlen, ssh->pktin.body + 4, msglen); - buf[nowlen + msglen] = '\0'; - /* logevent(buf); (this is now done within the bombout macro) */ - bombout(("Server sent disconnect message:\n\"%s\"", buf+nowlen)); + char *msg; + int msglen; + + ssh_pkt_getstring(ssh, &msg, &msglen); + + bombout(("Server sent disconnect message:\n\"%.*s\"", msglen, msg)); crStop(0); } @@ -1168,10 +1164,11 @@ static int ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen) case SSH2_MSG_DISCONNECT: { /* log reason code in disconnect message */ - char *buf; - int nowlen; - int reason = GET_32BIT(ssh->pktin.data + 6); - unsigned msglen = GET_32BIT(ssh->pktin.data + 10); + char *buf, *msg; + int nowlen, reason, msglen; + + reason = ssh_pkt_getuint32(ssh); + ssh_pkt_getstring(ssh, &msg, &msglen); if (reason > 0 && reason < lenof(ssh2_disconnect_reasons)) { buf = dupprintf("Received disconnect message (%s)", @@ -1183,7 +1180,7 @@ static int ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen) logevent(buf); sfree(buf); buf = dupprintf("Disconnection message text: %n%.*s", - &nowlen, msglen, ssh->pktin.data + 14); + &nowlen, msglen, msg); logevent(buf); bombout(("Server sent disconnect message\ntype %d (%s):\n\"%s\"", reason, @@ -1199,19 +1196,16 @@ static int ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen) case SSH2_MSG_DEBUG: { /* log the debug message */ - char buf[512]; - /* int display = ssh->pktin.body[6]; */ - int stringlen = GET_32BIT(ssh->pktin.data+7); - int prefix; - strcpy(buf, "Remote debug message: "); - prefix = strlen(buf); - if (stringlen > (int)(sizeof(buf)-prefix-1)) - stringlen = sizeof(buf)-prefix-1; - memcpy(buf + prefix, ssh->pktin.data + 11, stringlen); - buf[prefix + stringlen] = '\0'; + char *buf, *msg; + int msglen; + + ssh_pkt_getstring(ssh, &msg, &msglen); + + buf = dupprintf("Remote debug message: %.*s", msglen, msg); logevent(buf); + sfree(buf); } - goto next_packet; /* FIXME: print the debug message */ + goto next_packet; /* * These packets we need do nothing about here. @@ -5254,7 +5248,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt) ssh2_pkt_addstring_data(ssh, (char *)pub_blob, pub_blob_len); ssh2_pkt_send(ssh); - logevent("Offered public key"); /* FIXME */ + logevent("Offered public key"); crWaitUntilV(ispkt); if (ssh->pktin.type != SSH2_MSG_USERAUTH_PK_OK) { @@ -6354,6 +6348,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt) unsigned localid; char *type; int typelen, want_reply; + int reply = SSH2_MSG_CHANNEL_FAILURE; /* default */ struct ssh_channel *c; localid = ssh_pkt_getuint32(ssh); @@ -6385,18 +6380,94 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt) * the request type string to see if it's something * we recognise. */ - if (typelen == 11 && !memcmp(type, "exit-status", 11) && - c == ssh->mainchan) { - /* We recognise "exit-status" on the primary channel. */ - char buf[100]; - ssh->exitcode = ssh_pkt_getuint32(ssh); - sprintf(buf, "Server sent command exit status %d", - ssh->exitcode); - logevent(buf); - if (want_reply) { - ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_SUCCESS); - ssh2_pkt_adduint32(ssh, c->remoteid); - ssh2_pkt_send(ssh); + if (c == ssh->mainchan) { + /* + * We recognise "exit-status" and "exit-signal" on + * the primary channel. + */ + if (typelen == 11 && + !memcmp(type, "exit-status", 11)) { + + ssh->exitcode = ssh_pkt_getuint32(ssh); + logeventf(ssh, "Server sent command exit status %d", + ssh->exitcode); + reply = SSH2_MSG_CHANNEL_SUCCESS; + + } else if (typelen == 11 && + !memcmp(type, "exit-signal", 11)) { + + int is_plausible = TRUE, is_int = FALSE; + char *fmt_sig = "", *fmt_msg = ""; + char *msg; + int msglen = 0, core = FALSE; + /* ICK: older versions of OpenSSH (e.g. 3.4p1) + * provide an `int' for the signal, despite its + * having been a `string' in the drafts since at + * least 2001. (Fixed in session.c 1.147.) Try to + * infer which we can safely parse it as. */ + { + unsigned char *p = ssh->pktin.body + + ssh->pktin.savedpos; + long len = ssh->pktin.length - ssh->pktin.savedpos; + unsigned long num = GET_32BIT(p); /* what is it? */ + /* If it's 0, it hardly matters; assume string */ + if (num == 0) { + is_int = FALSE; + } else { + int maybe_int = FALSE, maybe_str = FALSE; +#define CHECK_HYPOTHESIS(offset, result) \ + do { \ + long q = offset; \ + if (q >= 0 && q+4 <= len) { \ + q = q + 4 + GET_32BIT(p+q); \ + if (q >= 0 && q+4 <= len && \ + (q = q + 4 + GET_32BIT(p+q)) && q == len) \ + result = TRUE; \ + } \ + } while(0) + CHECK_HYPOTHESIS(4+1, maybe_int); + CHECK_HYPOTHESIS(4+num+1, maybe_str); +#undef CHECK_HYPOTHESIS + if (maybe_int && !maybe_str) + is_int = TRUE; + else if (!maybe_int && maybe_str) + is_int = FALSE; + else + /* Crikey. Either or neither. Panic. */ + is_plausible = FALSE; + } + } + if (is_plausible) { + if (is_int) { + /* Old non-standard OpenSSH. */ + int signum = ssh_pkt_getuint32(ssh); + fmt_sig = dupprintf(" %d", signum); + } else { + /* As per the drafts. */ + char *sig; + int siglen; + ssh_pkt_getstring(ssh, &sig, &siglen); + /* Signal name isn't supposed to be blank, but + * let's cope gracefully if it is. */ + if (siglen) { + fmt_sig = dupprintf(" \"%.*s\"", + siglen, sig); + } + } + core = ssh2_pkt_getbool(ssh); + ssh_pkt_getstring(ssh, &msg, &msglen); + if (msglen) { + fmt_msg = dupprintf(" (\"%.*s\")", msglen, msg); + } + /* ignore lang tag */ + } /* else don't attempt to parse */ + logeventf(ssh, "Server exited on signal%s%s%s", + fmt_sig, core ? " (core dumped)" : "", + fmt_msg); + if (*fmt_sig) sfree(fmt_sig); + if (*fmt_msg) sfree(fmt_msg); + reply = SSH2_MSG_CHANNEL_SUCCESS; + } } else { /* @@ -6405,11 +6476,12 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt) * or respond with CHANNEL_FAILURE, depending * on want_reply. */ - if (want_reply) { - ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_FAILURE); - ssh2_pkt_adduint32(ssh, c->remoteid); - ssh2_pkt_send(ssh); - } + reply = SSH2_MSG_CHANNEL_FAILURE; + } + if (want_reply) { + ssh2_pkt_init(ssh, reply); + ssh2_pkt_adduint32(ssh, c->remoteid); + ssh2_pkt_send(ssh); } } else if (ssh->pktin.type == SSH2_MSG_GLOBAL_REQUEST) { char *type; @@ -6885,12 +6957,25 @@ static const struct telnet_special *ssh_get_specials(void *handle) {"IGNORE message", TS_NOP}, }; static const struct telnet_special ssh2_session_specials[] = { - {"", 0}, - {"Break", TS_BRK} - /* XXX we should also support signals */ + {NULL, TS_SEP}, + {"Break", TS_BRK}, + /* These are the signal names defined by draft-ietf-secsh-connect-19. + * They include all the ISO C signals, but are a subset of the POSIX + * required signals. */ + {"SIGINT (Interrupt)", TS_SIGINT}, + {"SIGTERM (Terminate)", TS_SIGTERM}, + {"SIGKILL (Kill)", TS_SIGKILL}, + {"SIGQUIT (Quit)", TS_SIGQUIT}, + {"SIGHUP (Hangup)", TS_SIGHUP}, + {"More signals", TS_SUBMENU}, + {"SIGABRT", TS_SIGABRT}, {"SIGALRM", TS_SIGALRM}, + {"SIGFPE", TS_SIGFPE}, {"SIGILL", TS_SIGILL}, + {"SIGPIPE", TS_SIGPIPE}, {"SIGSEGV", TS_SIGSEGV}, + {"SIGUSR1", TS_SIGUSR1}, {"SIGUSR2", TS_SIGUSR2}, + {NULL, TS_EXITMENU} }; static const struct telnet_special specials_end[] = { - {NULL, 0} + {NULL, TS_EXITMENU} }; static struct telnet_special ssh_specials[lenof(ignore_special) + lenof(ssh2_session_specials) + @@ -6978,7 +7063,37 @@ static void ssh_special(void *handle, Telnet_Special code) ssh2_pkt_send(ssh); } } else { - /* do nothing */ + /* Is is a POSIX signal? */ + char *signame = NULL; + if (code == TS_SIGABRT) signame = "ABRT"; + if (code == TS_SIGALRM) signame = "ALRM"; + if (code == TS_SIGFPE) signame = "FPE"; + if (code == TS_SIGHUP) signame = "HUP"; + if (code == TS_SIGILL) signame = "ILL"; + if (code == TS_SIGINT) signame = "INT"; + if (code == TS_SIGKILL) signame = "KILL"; + if (code == TS_SIGPIPE) signame = "PIPE"; + if (code == TS_SIGQUIT) signame = "QUIT"; + if (code == TS_SIGSEGV) signame = "SEGV"; + if (code == TS_SIGTERM) signame = "TERM"; + if (code == TS_SIGUSR1) signame = "USR1"; + if (code == TS_SIGUSR2) signame = "USR2"; + /* The SSH-2 protocol does in principle support arbitrary named + * signals, including signame@domain, but we don't support those. */ + if (signame) { + /* It's a signal. */ + if (ssh->version == 2 && ssh->mainchan) { + ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_REQUEST); + ssh2_pkt_adduint32(ssh, ssh->mainchan->remoteid); + ssh2_pkt_addstring(ssh, "signal"); + ssh2_pkt_addbool(ssh, 0); + ssh2_pkt_addstring(ssh, signame); + ssh2_pkt_send(ssh); + logeventf(ssh, "Sent signal SIG%s", signame); + } + } else { + /* Never heard of it. Do nothing */ + } } } diff --git a/sshzlib.c b/sshzlib.c index 4e70d282..373c7793 100644 --- a/sshzlib.c +++ b/sshzlib.c @@ -40,7 +40,25 @@ #include #include +#ifdef ZLIB_STANDALONE + +/* + * This module also makes a handy zlib decoding tool for when + * you're picking apart Zip files or PDFs or PNGs. If you compile + * it with ZLIB_STANDALONE defined, it builds on its own and + * becomes a command-line utility. + * + * Therefore, here I provide a self-contained implementation of the + * macros required from the rest of the PuTTY sources. + */ +#define snew(type) ( (type *) malloc(sizeof(type)) ) +#define snewn(n, type) ( (type *) malloc((n) * sizeof(type)) ) +#define sresize(x, n, type) ( (type *) realloc((x), (n) * sizeof(type)) ) +#define sfree(x) ( free((x)) ) + +#else #include "ssh.h" +#endif #ifndef FALSE #define FALSE 0 @@ -1027,13 +1045,14 @@ int zlib_decompress_block(void *handle, unsigned char *block, int len, { struct zlib_decompress_ctx *dctx = (struct zlib_decompress_ctx *)handle; const coderecord *rec; - int code, blktype, rep, dist, nlen; + int code, blktype, rep, dist, nlen, header; static const unsigned char lenlenmap[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; - dctx->outblk = NULL; - dctx->outsize = dctx->outlen = 0; + dctx->outblk = snewn(256, unsigned char); + dctx->outsize = 256; + dctx->outlen = 0; while (len > 0 || dctx->nbits > 0) { while (dctx->nbits < 24 && len > 0) { @@ -1043,10 +1062,35 @@ int zlib_decompress_block(void *handle, unsigned char *block, int len, } switch (dctx->state) { case START: - /* Expect 16-bit zlib header, which we'll dishonourably ignore. */ + /* Expect 16-bit zlib header. */ if (dctx->nbits < 16) goto finished; /* done all we can */ - EATBITS(16); + + /* + * The header is stored as a big-endian 16-bit integer, + * in contrast to the general little-endian policy in + * the rest of the format :-( + */ + header = (((dctx->bits & 0xFF00) >> 8) | + ((dctx->bits & 0x00FF) << 8)); + EATBITS(16); + + /* + * Check the header: + * + * - bits 8-11 should be 1000 (Deflate/RFC1951) + * - bits 12-15 should be at most 0111 (window size) + * - bit 5 should be zero (no dictionary present) + * - we don't care about bits 6-7 (compression rate) + * - bits 0-4 should be set up to make the whole thing + * a multiple of 31 (checksum). + */ + if ((header & 0x0F00) != 0x0800 || + (header & 0xF000) > 0x7000 || + (header & 0x0020) != 0x0000 || + (header % 31) != 0) + goto decode_error; + dctx->state = OUTSIDEBLK; break; case OUTSIDEBLK: @@ -1242,6 +1286,86 @@ int zlib_decompress_block(void *handle, unsigned char *block, int len, return 0; } +#ifdef ZLIB_STANDALONE + +#include +#include + +int main(int argc, char **argv) +{ + unsigned char buf[16], *outbuf; + int ret, outlen; + void *handle; + int noheader = FALSE, opts = TRUE; + char *filename = NULL; + FILE *fp; + + while (--argc) { + char *p = *++argv; + + if (p[0] == '-' && opts) { + if (!strcmp(p, "-d")) + noheader = TRUE; + else if (!strcmp(p, "--")) + opts = FALSE; /* next thing is filename */ + else { + fprintf(stderr, "unknown command line option '%s'\n", p); + return 1; + } + } else if (!filename) { + filename = p; + } else { + fprintf(stderr, "can only handle one filename\n"); + return 1; + } + } + + handle = zlib_decompress_init(); + + if (noheader) { + /* + * Provide missing zlib header if -d was specified. + */ + zlib_decompress_block(handle, "\x78\x9C", 2, &outbuf, &outlen); + assert(outlen == 0); + } + + if (filename) + fp = fopen(filename, "rb"); + else + fp = stdin; + + if (!fp) { + assert(filename); + fprintf(stderr, "unable to open '%s'\n", filename); + return 1; + } + + while (1) { + ret = fread(buf, 1, sizeof(buf), fp); + if (ret <= 0) + break; + zlib_decompress_block(handle, buf, ret, &outbuf, &outlen); + if (outbuf) { + if (outlen) + fwrite(outbuf, 1, outlen, stdout); + sfree(outbuf); + } else { + fprintf(stderr, "decoding error\n"); + return 1; + } + } + + zlib_decompress_cleanup(handle); + + if (filename) + fclose(fp); + + return 0; +} + +#else + const struct ssh_compress ssh_zlib = { "zlib", zlib_compress_init, @@ -1253,3 +1377,5 @@ const struct ssh_compress ssh_zlib = { zlib_disable_compression, "zlib (RFC1950)" }; + +#endif diff --git a/telnet.c b/telnet.c index 540c6d2c..67db0fd9 100644 --- a/telnet.c +++ b/telnet.c @@ -963,6 +963,8 @@ static void telnet_special(void *handle, Telnet_Special code) telnet->bufsize = sk_write(telnet->s, (char *)b, 2); } break; + default: + break; /* never heard of it */ } } @@ -976,15 +978,15 @@ static const struct telnet_special *telnet_get_specials(void *handle) {"Erase Line", TS_EL}, {"Go Ahead", TS_GA}, {"No Operation", TS_NOP}, - {"", 0}, + {NULL, TS_SEP}, {"Abort Process", TS_ABORT}, {"Abort Output", TS_AO}, {"Interrupt Process", TS_IP}, {"Suspend Process", TS_SUSP}, - {"", 0}, + {NULL, TS_SEP}, {"End Of Record", TS_EOR}, {"End Of File", TS_EOF}, - {NULL, 0} + {NULL, TS_EXITMENU} }; return specials; } diff --git a/unix/pterm.c b/unix/pterm.c index 79ebc70a..b80edc9a 100644 --- a/unix/pterm.c +++ b/unix/pterm.c @@ -3144,22 +3144,51 @@ void update_specials_menu(void *frontend) else specials = NULL; + /* I believe this disposes of submenus too. */ gtk_container_foreach(GTK_CONTAINER(inst->specialsmenu), (GtkCallback)gtk_widget_destroy, NULL); if (specials) { int i; - GtkWidget *menuitem; - for (i = 0; specials[i].name; i++) { - if (*specials[i].name) { + GtkWidget *menu = inst->specialsmenu; + /* A lame "stack" for submenus that will do for now. */ + GtkWidget *saved_menu = NULL; + int nesting = 1; + for (i = 0; nesting > 0; i++) { + GtkWidget *menuitem = NULL; + switch (specials[i].code) { + case TS_SUBMENU: + assert (nesting < 2); + saved_menu = menu; /* XXX lame stacking */ + menu = gtk_menu_new(); + menuitem = gtk_menu_item_new_with_label(specials[i].name); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu); + gtk_container_add(GTK_CONTAINER(saved_menu), menuitem); + gtk_widget_show(menuitem); + menuitem = NULL; + nesting++; + break; + case TS_EXITMENU: + nesting--; + if (nesting) { + menu = saved_menu; /* XXX lame stacking */ + saved_menu = NULL; + } + break; + case TS_SEP: + menuitem = gtk_menu_item_new(); + break; + default: menuitem = gtk_menu_item_new_with_label(specials[i].name); gtk_object_set_data(GTK_OBJECT(menuitem), "user-data", GINT_TO_POINTER(specials[i].code)); gtk_signal_connect(GTK_OBJECT(menuitem), "activate", GTK_SIGNAL_FUNC(special_menuitem), inst); - } else - menuitem = gtk_menu_item_new(); - gtk_container_add(GTK_CONTAINER(inst->specialsmenu), menuitem); - gtk_widget_show(menuitem); + break; + } + if (menuitem) { + gtk_container_add(GTK_CONTAINER(menu), menuitem); + gtk_widget_show(menuitem); + } } gtk_widget_show(inst->specialsitem1); gtk_widget_show(inst->specialsitem2); diff --git a/window.c b/window.c index e5526629..4c4ef507 100644 --- a/window.c +++ b/window.c @@ -111,6 +111,7 @@ static struct unicode_data ucsdata; static int session_closed; static const struct telnet_special *specials; +static int n_specials; static struct { HMENU menu; @@ -915,20 +916,48 @@ void update_specials_menu(void *frontend) specials = NULL; if (specials) { - p = CreateMenu(); - for (i = 0; specials[i].name; i++) { + /* We can't use Windows to provide a stack for submenus, so + * here's a lame "stack" that will do for now. */ + HMENU saved_menu = NULL; + int nesting = 1; + p = CreatePopupMenu(); + for (i = 0; nesting > 0; i++) { assert(IDM_SPECIAL_MIN + 0x10 * i < IDM_SPECIAL_MAX); - if (*specials[i].name) + switch (specials[i].code) { + case TS_SEP: + AppendMenu(p, MF_SEPARATOR, 0, 0); + break; + case TS_SUBMENU: + assert(nesting < 2); + nesting++; + saved_menu = p; /* XXX lame stacking */ + p = CreatePopupMenu(); + AppendMenu(saved_menu, MF_POPUP | MF_ENABLED, + (UINT) p, specials[i].name); + break; + case TS_EXITMENU: + nesting--; + if (nesting) { + p = saved_menu; /* XXX lame stacking */ + saved_menu = NULL; + } + break; + default: AppendMenu(p, MF_ENABLED, IDM_SPECIAL_MIN + 0x10 * i, specials[i].name); - else - AppendMenu(p, MF_SEPARATOR, 0, 0); + break; + } } - } else + /* Squirrel the highest special. */ + n_specials = i - 1; + } else { p = NULL; + n_specials = 0; + } for (j = 0; j < lenof(popup_menus); j++) { if (menu_already_exists) { + /* XXX does this free up all submenus? */ DeleteMenu(popup_menus[j].menu, popup_menus[j].specials_submenu_pos, MF_BYPOSITION); @@ -2087,20 +2116,16 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, } if (wParam >= IDM_SPECIAL_MIN && wParam <= IDM_SPECIAL_MAX) { int i = (wParam - IDM_SPECIAL_MIN) / 0x10; - int j; /* * Ensure we haven't been sent a bogus SYSCOMMAND * which would cause us to reference invalid memory * and crash. Perhaps I'm just too paranoid here. */ - for (j = 0; j < i; j++) - if (!specials || !specials[j].name) - break; - if (j == i) { - if (back) - back->special(backhandle, specials[i].code); - net_pending_errors(); - } + if (i >= n_specials) + break; + if (back) + back->special(backhandle, specials[i].code); + net_pending_errors(); } } break;