]> asedeno.scripts.mit.edu Git - PuTTY.git/commitdiff
Merge out from trunk, to keep this branch viable. We are now up to
authorSimon Tatham <anakin@pobox.com>
Mon, 10 Mar 2008 18:48:36 +0000 (18:48 +0000)
committerSimon Tatham <anakin@pobox.com>
Mon, 10 Mar 2008 18:48:36 +0000 (18:48 +0000)
date as of r7913.

[originally from svn r7914]
[r7913 == d7eda6d99cb6b7c2e09489dcf13b23c4cfcc61a2]

81 files changed:
Buildscr [new file with mode: 0644]
CHECKLST.txt
LATEST.VER
LICENCE
Recipe
be_all.c
be_all_s.c
be_none.c
be_nos_s.c
be_nossh.c
cmdgen.c
cmdline.c
config.c
doc/Makefile
doc/blurb.but
doc/config.but
doc/errors.but
doc/faq.but
doc/feedback.but
doc/index.but
doc/licence.but
doc/plink.but
doc/pscp.but
doc/sshnames.but [new file with mode: 0644]
doc/using.but
icons/Makefile
icons/mkicon.py
mac/mac.c
mac/mac_res.r
mac/macdlg.c
mac/macpgen.r
mac/macterm.c
mac/version.r
macosx/osxclass.h
macosx/osxdlg.m
macosx/osxwin.m
mkfiles.pl
mkunxarc.sh
pscp.c
psftp.c
putty.h
raw.c
rlogin.c
sercfg.c
settings.c
sign.sh [new file with mode: 0755]
ssh.c
ssh.h
sshdes.c
sshdh.c
sshdss.c
sshrsa.c
telnet.c
terminal.c
testback.c
unix/gtkdlg.c
unix/gtkwin.c
unix/unix.h
unix/uxcons.c
unix/uxnet.c
unix/uxplink.c
unix/uxpterm.c
unix/uxpty.c
unix/uxputty.c
unix/uxser.c
version.c
windows/pageant.rc
windows/putty.iss
windows/puttygen.rc
windows/puttyins.ico [moved from windows/installer.ico with 100% similarity]
windows/version.rc2
windows/win_res.rc2
windows/winctrls.c
windows/window.c
windows/winhandl.c
windows/winhelp.c
windows/winhelp.h
windows/winnet.c
windows/winplink.c
windows/winprint.c
windows/winser.c

diff --git a/Buildscr b/Buildscr
new file mode 100644 (file)
index 0000000..02d4a9c
--- /dev/null
+++ b/Buildscr
@@ -0,0 +1,113 @@
+# -*- sh -*-
+# Build script to construct a full distribution directory of PuTTY.
+
+module putty
+
+# Set up the arguments for the main make command.
+set Makever -DSVN_REV=$(revision)
+ifneq "$(!numeric $(revision))" "yes" set Makever $(Makever) -DMODIFIED
+ifneq "$(RELEASE)" "" set Makever $(Makever) -DRELEASE=$(RELEASE)
+ifneq "$(date)" "" set Makever $(Makever) -DSNAPSHOT=$(date)
+set Makeargs VER="$(Makever)"
+ifneq "$(XFLAGS)" "" set Makeargs $(Makeargs) XFLAGS="$(XFLAGS)"
+ifneq "$(MAKEARGS)" "" set Makeargs $(Makeargs) $(MAKEARGS)
+
+# Set up the version string for the docs build.
+set Docmakeargs VERSION="PuTTY revision $(revision)"
+ifneq "$(RELEASE)" "" set Docmakeargs VERSION="PuTTY release $(RELEASE)"
+ifneq "$(date)" "" set Docmakeargs VERSION="PuTTY development snapshot $(date)"
+
+# Set up the version string for the Unix source archive.
+set Unxver r$(revision)
+ifneq "$(RELEASE)" "" set Unxver $(RELEASE)
+ifneq "$(date)" "" set Unxver $(date)
+
+# Set up the various version strings for the installer.
+set Iversion r$(revision)
+set Iname PuTTY revision $(revision)
+set Ivertext Revision $(revision)
+set Irev $(revision)
+set Ifilename putty-$(Iversion)-installer.exe
+ifneq "$(RELEASE)" "" set Iversion $(RELEASE)
+ifneq "$(RELEASE)" "" set Iname PuTTY version $(RELEASE)
+ifneq "$(RELEASE)" "" set Ivertext Release $(RELEASE)
+ifneq "$(RELEASE)" "" set Irev 0
+ifneq "$(RELEASE)" "" set Ifilename putty-$(RELEASE)-installer.exe
+ifneq "$(date)" "" set Iversion $(date):r$(revision)
+ifneq "$(date)" "" set Iname PuTTY development snapshot $(date):r$(revision)
+ifneq "$(date)" "" set Ivertext Development snapshot $(date):r$(revision)
+ifneq "$(date)" "" set Ifilename putty-$(date)-installer.exe
+
+# Set up the version string for the installer.
+set Iversion r$(revision)
+ifneq "$(RELEASE)" "" set Iversion $(RELEASE)
+ifneq "$(date)" "" set Iversion $(date):r$(revision)
+
+in putty do ./mksrcarc.sh
+in putty do ./mkunxarc.sh $(Unxver)
+in putty do perl mkfiles.pl
+in putty/doc do make $(Docmakeargs) putty.hlp
+in putty/doc do make $(Docmakeargs) chm
+
+# Munge the installer script locally so that it reports the version
+# we're really building.
+in putty/windows do perl -i~ -pe 'BEGIN{$$a=shift@ARGV;}s/^(AppVerName=).*$$/$$1$$a/' '$(Iname)' putty.iss
+in putty/windows do perl -i~ -pe 'BEGIN{$$a=shift@ARGV;}s/^(VersionInfoTextVersion=).*$$/$$1$$a/' '$(Ivertext)' putty.iss
+in putty/windows do perl -i~ -pe 'BEGIN{$$a=shift@ARGV;}s/^(AppVersion=).*$$/$$1$$a/' '$(Iversion)' putty.iss
+in putty/windows do perl -i~ -pe 'BEGIN{$$a=shift@ARGV;$$a=~s/M//;}s/^(VersionInfoVersion=\d+\.\d+\.)\d+(\.\d+)\r?$$/$$1$$a$$2/' '$(Irev)' putty.iss
+
+# Windowsify LICENCE, since it's going in the Windows installer.
+in putty do perl -i~ -pe 'y/\015//d;s/$$/\015/' LICENCE
+
+delegate windows
+  # FIXME: Cygwin alternative?
+  in putty/windows do cmd /c vcvars32 \& nmake -f Makefile.vc $(Makeargs)
+  # Ignore exit code from hhc, in favour of seeing whether the .chm
+  # file was created. (Yuck; but hhc appears to return non-zero
+  # exit codes on whim.)
+  in putty/doc do hhc putty.hhp; test -f putty.chm
+  in putty/windows do iscc putty.iss
+  return putty/windows/*.exe
+  return putty/windows/*.map
+  return putty/doc/putty.chm
+  return putty/windows/Output/setup.exe
+enddelegate
+in putty/doc do make mostlyclean
+in putty/doc do make $(Docmakeargs)
+in putty/windows do zip -k -j putty.zip `ls *.exe | grep -v puttytel` ../doc/putty.chm ../doc/putty.hlp ../doc/putty.cnt
+in putty/doc do zip puttydoc.zip *.html
+
+# Deliver the actual PuTTY release directory into a subdir `putty'.
+deliver putty/windows/*.exe putty/x86/$@
+deliver putty/windows/putty.zip putty/x86/$@
+deliver putty/windows/Output/setup.exe putty/x86/$(Ifilename)
+deliver putty/doc/puttydoc.zip putty/$@
+deliver putty/doc/putty.chm putty/$@
+deliver putty/doc/putty.hlp putty/$@
+deliver putty/doc/putty.cnt putty/$@
+deliver putty/doc/puttydoc.txt putty/$@
+deliver putty/doc/*.html putty/htmldoc/$@
+deliver putty/putty-src.zip putty/$@
+deliver putty/*.tar.gz putty/$@
+
+# Deliver the map files alongside the `proper' release deliverables.
+deliver putty/windows/*.map maps-x86/$@
+
+# Deliver sign.sh, so that whoever has just built PuTTY (the
+# snapshot scripts or me, depending) can conveniently sign it with
+# whatever key they want.
+deliver putty/sign.sh $@
+
+# Building the md5sums file is most easily done in the destination
+# directory.
+in-dest putty do md5sum `\find * -type f -print` > md5sums
+
+# And construct .htaccess files. One in the top-level directory,
+# setting the MIME types for Windows help files and providing an
+# appropriate link to the source archive:
+in-dest putty do echo "AddType application/octet-stream .chm" >> .htaccess
+in-dest putty do echo "AddType application/octet-stream .hlp" >> .htaccess
+in-dest putty do echo "AddType application/octet-stream .cnt" >> .htaccess
+in-dest putty do set -- putty*.tar.gz; for k in '' .DSA .RSA; do echo RedirectMatch temp '(.*/)'putty.tar.gz$$k\$$ '$$1'"$$1$$k" >> .htaccess; done
+# And one in the x86 directory, providing a link for the installer.
+in-dest putty/x86 do set -- putty*installer.exe; for k in '' .DSA .RSA; do echo RedirectMatch temp '(.*/)'putty-installer.exe$$k\$$ '$$1'"$$1$$k" >> .htaccess; done
index 6e775f9aa2cd1453ea8581be1da9e37df47fada0..37b2de61a91fc1709f76a3dbc33af0d5c3f06c04 100644 (file)
@@ -53,6 +53,11 @@ Before tagging a release
    containing the word XXX-REVIEW-BEFORE-RELEASE.
    (Any such comments should state clearly what needs to be done.)
 
+ - Also, do some testing of the Windows version with Minefield, and
+   of the Unix version with valgrind or efence or both. In
+   particular, any headline features for the release should get a
+   workout with memory checking enabled!
+
 For a long time we got away with never checking the current version
 number in at all - all version numbers were passed into the build
 system on the compiler command line, and the _only_ place version
@@ -109,83 +114,27 @@ of the tag.
    ixion:src/putty/local/announce-<ver> in case it's needed again
    within days of the release going out.
 
- - On my local machines, check out the release-tagged version of the
-   sources. Do this in a _clean_ directory; don't depend on my usual
-   source dir.
-    + Make sure to run mkfiles.pl _after_ this checkout, just in
-      case.
-
- - Build the source archives now, while the directory is still
-   pristine.
-    + run ./mksrcarc.sh to build the Windows source zip.
-    + run `./mkunxarc.sh X.YZ' to build the Unix tarball.
-
- - Build the Windows/x86 release binaries. Don't forget to supply
-   VER=/DRELEASE=<ver>. Run them, or at least one or two of them, to
-   ensure that they really do report their version number correctly,
-   and sanity-check the version info reported on the files by Windows.
-    + Save the release link maps. Currently I keep these on ixion,
-      in src/putty/local/maps-<version>.
-
- - Run Halibut to build the docs. Define VERSION on the make command
-   line to override the version strings, since Subversion revision
-   numbers are less meaningful on a tag.
-    + change into the doc subdir
-    + run `make VERSION="PuTTY release 0.XX" chm', then run `hhc
-      putty.hhp' to build the .CHM
-    + then run `make mostlyclean' (destroys the hhc input files but
-      _not_ the .CHM)
-    + then `make VERSION="PuTTY release 0.XX"'
-
- - Build the binary archive putty.zip: all the .exe files except
-   PuTTYtel, and the .hlp, .cnt and .chm files.
-    + zip -k putty.zip `ls *.exe | grep -v puttytel` putty.hlp putty.cnt putty.chm
-
- - Build the docs archive puttydoc.zip: it contains all the HTML
-   files output from Halibut.
-    + zip puttydoc.zip *.html
-
- - Build the installer.
-
- - Sign the release (gpg --detach-sign).
-    + Sign the locally built x86 binaries, the locally built x86
-      binary zipfile, and the locally built x86 installer, with the
-      release keys.
-    + The source archive should be signed with the release keys.
-    + Don't forget to sign with both DSA and RSA keys for absolutely
-      everything.
-      for i in <filenames>; do for t in DSA RSA; do gpg --load-extension=idea --detach-sign -u "Releases ($t)" -o $i.$t $i; done; done
-
- - Begin to pull together the release directory structure.
-    + subdir `x86' containing the x86 binaries, x86 binary zip, x86
-      installer, and all signatures on the above.
-    + top-level dir contains the Windows source zip (plus
-      signatures), the Unix source tarball (plus signatures),
-      puttydoc.txt, the .hlp, .cnt and .chm files, and puttydoc.zip.
-
- - Create subdir `htmldoc' in the release directory, which should
-   contain exactly the same set of HTML files that went into
-   puttydoc.zip.
-    + It also needs a copy of sitestyle.css, because the online
-      versions of the HTML docs will link to this (although the
-      zipped form should be self-contained).
-
- - Create and sign an md5sums file in the top-level directory.
-    + The md5sums files need not list the .DSA and .RSA signatures.
-      Easiest thing is to run this command:
-      md5sum `\find * -name '*SA' -o -type f -print` > md5sums
-    + Sign the md5sums file (gpg --clearsign).
-      for t in DSA RSA; do gpg --load-extension=idea --clearsign -u "Releases ($t)" -o md5sums.$t md5sums; done
-
- - Now double-check by verifying all the signatures on all the
-   files, and running md5sum -c on the md5sums file.
+ - Build the release: `bob putty-0.XX RELEASE=0.XX'. This should
+   generate a basically valid release directory as
+   `build.out/putty', and provide link maps and sign.sh alongside
+   that in build.out.
+
+ - Do a bit of checking that the release binaries basically work,
+   report their version numbers accurately, and so on. Test the
+   installer and the Unix source tarball.
+
+ - Save the link maps. Currently I keep these on ixion, in
+   src/putty/local/maps-<version>.
+
+ - Sign the release: in the `build.out' directory, type `./sign.sh
+   putty Releases', and enter the passphrases a lot of times.
 
  - Now the whole release directory should be present and correct.
-   Upload to ixion:www/putty/<ver>.
+   Upload it to ixion:www/putty/<ver>.
 
  - Do final checks on the release directory:
-    + verify all the signatures. In each directory:
-      for i in *.*SA; do case $i in md5sums*) gpg --verify $i;; *) gpg --verify $i `echo $i | sed 's/\..SA$//'`;; esac; done
+    + verify all the signatures:
+      for i in `find . -name '*.*SA'`; do case $i in *md5sums*) gpg --verify $i;; *) gpg --verify $i ${i%%.?SA};; esac; done
     + check the md5sums:
       md5sum -c md5sums
 
index 435f14b14c36202baa8b69e5bd91c4ce547dc08a..08072c1817004f0177cf899ef275fe65f0faab9c 100644 (file)
@@ -1 +1 @@
-0.59
+0.60
diff --git a/LICENCE b/LICENCE
index d14e667a548709b873edc16436d4887f689bfcc9..88eba7bb01e410cdebd72525b08ce493eaa47461 100644 (file)
--- a/LICENCE
+++ b/LICENCE
@@ -1,4 +1,4 @@
-PuTTY is copyright 1997-2007 Simon Tatham.
+PuTTY is copyright 1997-2008 Simon Tatham.
 
 Portions copyright Robert de Bath, Joris van Rantwijk, Delian
 Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry,
diff --git a/Recipe b/Recipe
index 5c58d4966a44dfa5135c603857d926014f6fecff..fab512bcc11c885e2f2eb44347877e4786d24890 100644 (file)
--- a/Recipe
+++ b/Recipe
@@ -81,7 +81,8 @@
 #
 #      Note that this definition is always enabled in the Cygwin
 #      build, since at the time of writing this <htmlhelp.h> is
-#      known not to be available in Cygwin.
+#      known not to be available in Cygwin (although you can use
+#      the htmlhelp.h supplied with HTML Help Workshop).
 #
 #  - RCFL=/DNO_MANIFESTS (Windows only)
 #      Disables inclusion of XML application manifests in the PuTTY
@@ -189,6 +190,7 @@ RCFLAGS += $(VER)
 # `make install' target for Unix.
 !begin gtk
 install:
+       mkdir -p $(DESTDIR)$(bindir) $(DESTDIR)$(man1dir)
        $(INSTALL_PROGRAM) -m 755 plink $(DESTDIR)$(bindir)/plink
        $(INSTALL_PROGRAM) -m 755 pscp $(DESTDIR)$(bindir)/pscp
        $(INSTALL_PROGRAM) -m 755 psftp $(DESTDIR)$(bindir)/psftp
@@ -305,13 +307,13 @@ psftp    : [C] psftp winsftp wincons WINSSH BE_SSH SFTP wildcard WINMISC
          + psftp.res LIBS
 
 pageant  : [G] winpgnt sshrsa sshpubk sshdes sshbn sshmd5 version tree234
-         + misc sshaes sshsha winpgntc sshdss sshsh512 winutils winmisc
-         + winhelp pageant.res LIBS
+         + misc sshaes sshsha winpgntc sshdss sshsh256 sshsh512 winutils
+         + winmisc winhelp pageant.res LIBS
 
 puttygen : [G] winpgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
          + sshrand winnoise sshsha winstore misc winctrls sshrsa sshdss winmisc
-         + sshpubk sshaes sshsh512 import winutils puttygen.res tree234
-        + notiming winhelp LIBS wintime
+         + sshpubk sshaes sshsh256 sshsh512 import winutils puttygen.res
+        + tree234 notiming winhelp LIBS wintime
 
 pterm    : [X] GTKTERM uxmisc misc ldisc settings uxpty uxsel BE_NONE uxstore
          + uxsignal CHARSET cmdline uxpterm version time xpmpterm xpmptcfg
@@ -326,8 +328,8 @@ plink    : [U] uxplink uxcons NONSSH UXSSH U_BE_ALL logging UXMISC uxsignal
 
 puttygen : [U] cmdgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
          + sshrand uxnoise sshsha misc sshrsa sshdss uxcons uxstore uxmisc
-         + sshpubk sshaes sshsh512 import puttygen.res time tree234 uxgen
-         + notiming
+         + sshpubk sshaes sshsh256 sshsh512 import puttygen.res time tree234
+        + uxgen notiming
 
 pscp     : [U] pscp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC
 psftp    : [U] psftp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC
@@ -340,7 +342,7 @@ PuTTYtel : [M] terminal wcwidth ldiscucs logging BE_NOSSH mac macdlg
         + CHARSET stricmp vsnprint dialog config macctrls minibidi
 PuTTYgen : [M] macpgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
          + sshrand macnoise sshsha macstore misc sshrsa sshdss macmisc sshpubk
-         + sshaes sshsh512 import macpgen.rsrc macpgkey macabout
+         + sshaes sshsh256 sshsh512 import macpgen.rsrc macpgkey macabout
 
 PuTTY    : [MX] osxmain OSXTERM OSXMISC CHARSET U_BE_ALL NONSSH UXSSH
          + ux_x11 uxpty uxsignal testback putty.icns info.plist
index 024a39e9c70229afee4f9466b61f557157120f52..c58903cc50d8f782a8b60be71cbc3606e246990e 100644 (file)
--- a/be_all.c
+++ b/be_all.c
@@ -22,10 +22,10 @@ const int be_default_protocol = PROT_TELNET;
 const int be_default_protocol = PROT_SSH;
 #endif
 
-struct backend_list backends[] = {
-    {PROT_SSH, "ssh", &ssh_backend},
-    {PROT_TELNET, "telnet", &telnet_backend},
-    {PROT_RLOGIN, "rlogin", &rlogin_backend},
-    {PROT_RAW, "raw", &raw_backend},
-    {0, NULL}
+Backend *backends[] = {
+    &ssh_backend,
+    &telnet_backend,
+    &rlogin_backend,
+    &raw_backend,
+    NULL
 };
index 4b3c19837261c44930fe3cf4b85bbff9eed29ccc..0ffd073767dbe3df5354ca1aa6c086a4c68c6e42 100644 (file)
@@ -22,11 +22,11 @@ const int be_default_protocol = PROT_TELNET;
 const int be_default_protocol = PROT_SSH;
 #endif
 
-struct backend_list backends[] = {
-    {PROT_SSH, "ssh", &ssh_backend},
-    {PROT_TELNET, "telnet", &telnet_backend},
-    {PROT_RLOGIN, "rlogin", &rlogin_backend},
-    {PROT_RAW, "raw", &raw_backend},
-    {PROT_SERIAL, "serial", &serial_backend},
-    {0, NULL}
+Backend *backends[] = {
+    &ssh_backend,
+    &telnet_backend,
+    &rlogin_backend,
+    &raw_backend,
+    &serial_backend,
+    NULL
 };
index 879a20c5052285efcc88aab7047c4cbf16d6561a..6ec037ac06753543778b7ea022b480ab531bc539 100644 (file)
--- a/be_none.c
+++ b/be_none.c
@@ -1,16 +1,11 @@
 /*
- * Linking module for PSCP: list the available backends, but
- * without accompanying function suites. Used only for name
- * lookups.
+ * Linking module for programs that do not support selection of backend
+ * (such as pscp or pterm).
  */
 
 #include <stdio.h>
 #include "putty.h"
 
-struct backend_list backends[] = {
-    {PROT_SSH, "ssh", NULL},
-    {PROT_TELNET, "telnet", NULL},
-    {PROT_RLOGIN, "rlogin", NULL},
-    {PROT_RAW, "raw", NULL},
-    {0, NULL}
+Backend *backends[] = {
+    NULL
 };
index ea8cf1441c9e08ca95fec0de350537500a945057..a574ead915a28be939a5b0fb68ce234fc650307f 100644 (file)
@@ -10,12 +10,12 @@ const int be_default_protocol = PROT_TELNET;
 
 const char *const appname = "PuTTYtel";
 
-struct backend_list backends[] = {
-    {PROT_TELNET, "telnet", &telnet_backend},
-    {PROT_RLOGIN, "rlogin", &rlogin_backend},
-    {PROT_RAW, "raw", &raw_backend},
-    {PROT_SERIAL, "serial", &serial_backend},
-    {0, NULL}
+Backend *backends[] = {
+    &telnet_backend,
+    &rlogin_backend,
+    &raw_backend,
+    &serial_backend,
+    NULL
 };
 
 /*
index 18ba32a7926cdb11e4dcef023b368583cf102ccf..33d783a841b531ed3a3a242e60cb4fb995404f47 100644 (file)
@@ -10,11 +10,11 @@ const int be_default_protocol = PROT_TELNET;
 
 const char *const appname = "PuTTYtel";
 
-struct backend_list backends[] = {
-    {PROT_TELNET, "telnet", &telnet_backend},
-    {PROT_RLOGIN, "rlogin", &rlogin_backend},
-    {PROT_RAW, "raw", &raw_backend},
-    {0, NULL}
+Backend *backends[] = {
+    &telnet_backend,
+    &rlogin_backend,
+    &raw_backend,
+    NULL
 };
 
 /*
index d8d5a946d0ef962384985b186f00ce16dfa4ef63..9aa585f3ffefe032aea8b6b47a374f4dbf15933f 100644 (file)
--- a/cmdgen.c
+++ b/cmdgen.c
@@ -640,6 +640,11 @@ int main(int argc, char **argv)
 
        random_ref();
        entropy = get_random_data(bits / 8);
+       if (!entropy) {
+           fprintf(stderr, "puttygen: failed to collect entropy, "
+                   "could not generate key\n");
+           return 1;
+       }
        random_add_heavynoise(entropy, bits / 8);
        memset(entropy, 0, bits/8);
        sfree(entropy);
index 79f29816de83511b146ea89c9b435358c40e473e..183797df1a034e1ef39a446a0787b3506de8e702 100644 (file)
--- a/cmdline.c
+++ b/cmdline.c
@@ -263,8 +263,8 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
            unsigned len = portp - host;
            if (len >= sizeof(cfg->ssh_nc_host))
                len = sizeof(cfg->ssh_nc_host) - 1;
-           strncpy(cfg->ssh_nc_host, value, len);
-           cfg->ssh_nc_host[sizeof(cfg->ssh_nc_host) - 1] = '\0';
+           memcpy(cfg->ssh_nc_host, value, len);
+           cfg->ssh_nc_host[len] = '\0';
            cfg->ssh_nc_port = atoi(portp+1);
        } else {
            cmdline_error("-nc expects argument of form 'host:port'");
@@ -315,19 +315,33 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
     if (!strcmp(p, "-pw")) {
        RETURN(2);
        UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
-       cmdline_password = value;
+       SAVEABLE(1);
+       /* We delay evaluating this until after the protocol is decided,
+        * so that we can warn if it's of no use with the selected protocol */
+       if (cfg->protocol != PROT_SSH)
+           cmdline_error("the -pw option can only be used with the "
+                         "SSH protocol");
+       else {
+           cmdline_password = dupstr(value);
+           /* Assuming that `value' is directly from argv, make a good faith
+            * attempt to trample it, to stop it showing up in `ps' output
+            * on Unix-like systems. Not guaranteed, of course. */
+           memset(value, 0, strlen(value));
+       }
     }
 
     if (!strcmp(p, "-agent") || !strcmp(p, "-pagent") ||
        !strcmp(p, "-pageant")) {
        RETURN(1);
        UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
+       SAVEABLE(0);
        cfg->tryagent = TRUE;
     }
     if (!strcmp(p, "-noagent") || !strcmp(p, "-nopagent") ||
        !strcmp(p, "-nopageant")) {
        RETURN(1);
        UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);
+       SAVEABLE(0);
        cfg->tryagent = FALSE;
     }
 
@@ -360,13 +374,13 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
     if (!strcmp(p, "-t")) {
        RETURN(1);
        UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
-       SAVEABLE(0);
+       SAVEABLE(1);    /* lower priority than -m */
        cfg->nopty = 0;
     }
     if (!strcmp(p, "-T")) {
        RETURN(1);
        UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
-       SAVEABLE(0);
+       SAVEABLE(1);
        cfg->nopty = 1;
     }
 
index 1e5f8da104d2f9e457efb45ed96c324484113733..bd07d9511f285e01beee4ff0194597495881f3e6 100644 (file)
--- a/config.c
+++ b/config.c
 #define HOST_BOX_TITLE "Host Name (or IP address)"
 #define PORT_BOX_TITLE "Port"
 
-/*
- * Convenience function: determine whether this binary supports a
- * given backend.
- */
-static int have_backend(int protocol)
-{
-    struct backend_list *p = backends;
-    for (p = backends; p->name; p++) {
-       if (p->protocol == protocol)
-           return 1;
-    }
-    return 0;
-}
-
 static void config_host_handler(union control *ctrl, void *dlg,
                                void *data, int event)
 {
@@ -104,7 +90,7 @@ struct hostport {
 void config_protocolbuttons_handler(union control *ctrl, void *dlg,
                                    void *data, int event)
 {
-    int button, defport;
+    int button;
     Config *cfg = (Config *)data;
     struct hostport *hp = (struct hostport *)ctrl->radio.context.p;
 
@@ -128,15 +114,20 @@ void config_protocolbuttons_handler(union control *ctrl, void *dlg,
        assert(button >= 0 && button < ctrl->radio.nbuttons);
        cfg->protocol = ctrl->radio.buttondata[button].i;
        if (oldproto != cfg->protocol) {
-           defport = -1;
-           switch (cfg->protocol) {
-             case PROT_SSH: defport = 22; break;
-             case PROT_TELNET: defport = 23; break;
-             case PROT_RLOGIN: defport = 513; break;
-           }
-           if (defport > 0 && cfg->port != defport) {
-               cfg->port = defport;
-           }
+           Backend *ob = backend_from_proto(oldproto);
+           Backend *nb = backend_from_proto(cfg->protocol);
+           assert(ob);
+           assert(nb);
+           /* Iff the user hasn't changed the port from the protocol
+            * default (if any), update it with the new protocol's
+            * default.
+            * (XXX: this isn't perfect; a default can become permanent
+            * by going via the serial backend. However, it helps with
+            * the common case of tabbing through the controls in order
+            * and setting a non-default port.) */
+           if (cfg->port == ob->default_port &&
+               cfg->port > 0 && nb->default_port > 0)
+               cfg->port = nb->default_port;
        }
        dlg_refresh(hp->host, dlg);
        dlg_refresh(hp->port, dlg);
@@ -256,6 +247,7 @@ static void kexlist_handler(union control *ctrl, void *dlg,
            { "Diffie-Hellman group 1",         KEX_DHGROUP1 },
            { "Diffie-Hellman group 14",        KEX_DHGROUP14 },
            { "Diffie-Hellman group exchange",  KEX_DHGEX },
+           { "RSA-based key exchange",         KEX_RSA },
            { "-- warn below here --",          KEX_WARN }
        };
 
@@ -383,7 +375,7 @@ struct sessionsaver_data {
  */
 static int load_selected_session(struct sessionsaver_data *ssd,
                                 char *savedsession,
-                                void *dlg, Config *cfg)
+                                void *dlg, Config *cfg, int *maybe_launch)
 {
     int i = dlg_listbox_index(ssd->listbox, dlg);
     int isdef;
@@ -392,13 +384,17 @@ static int load_selected_session(struct sessionsaver_data *ssd,
        return 0;
     }
     isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");
-    load_settings(ssd->sesslist.sessions[i], !isdef, cfg);
+    load_settings(ssd->sesslist.sessions[i], cfg);
     if (!isdef) {
        strncpy(savedsession, ssd->sesslist.sessions[i],
                SAVEDSESSION_LEN);
        savedsession[SAVEDSESSION_LEN-1] = '\0';
+       if (maybe_launch)
+           *maybe_launch = TRUE;
     } else {
        savedsession[0] = '\0';
+       if (maybe_launch)
+           *maybe_launch = FALSE;
     }
     dlg_refresh(NULL, dlg);
     /* Restore the selection, which might have been clobbered by
@@ -464,6 +460,7 @@ static void sessionsaver_handler(union control *ctrl, void *dlg,
            dlg_listbox_select(ssd->listbox, dlg, top);
        }
     } else if (event == EVENT_ACTION) {
+       int mbl = FALSE;
        if (!ssd->midsession &&
            (ctrl == ssd->listbox ||
             (ssd->loadbutton && ctrl == ssd->loadbutton))) {
@@ -474,8 +471,8 @@ static void sessionsaver_handler(union control *ctrl, void *dlg,
             * double-click on the list box _and_ that session
             * contains a hostname.
             */
-           if (load_selected_session(ssd, savedsession, dlg, cfg) &&
-               (ctrl == ssd->listbox && cfg_launchable(cfg))) {
+           if (load_selected_session(ssd, savedsession, dlg, cfg, &mbl) &&
+               (mbl && ctrl == ssd->listbox && cfg_launchable(cfg))) {
                dlg_end(dlg, 1);       /* it's all over, and succeeded */
            }
        } else if (ctrl == ssd->savebutton) {
@@ -496,7 +493,7 @@ static void sessionsaver_handler(union control *ctrl, void *dlg,
                }
            }
             {
-                char *errmsg = save_settings(savedsession, !isdef, cfg);
+                char *errmsg = save_settings(savedsession, cfg);
                 if (errmsg) {
                     dlg_error_msg(dlg, errmsg);
                     sfree(errmsg);
@@ -533,12 +530,14 @@ static void sessionsaver_handler(union control *ctrl, void *dlg,
            if (dlg_last_focused(ctrl, dlg) == ssd->listbox &&
                !cfg_launchable(cfg)) {
                Config cfg2;
-               if (!load_selected_session(ssd, savedsession, dlg, &cfg2)) {
+               int mbl = FALSE;
+               if (!load_selected_session(ssd, savedsession, dlg,
+                                          &cfg2, &mbl)) {
                    dlg_beep(dlg);
                    return;
                }
                /* If at this point we have a valid session, go! */
-               if (*cfg2.host) {
+               if (mbl && cfg_launchable(&cfg2)) {
                    *cfg = cfg2;       /* structure copy */
                    cfg->remote_cmd_ptr = NULL;
                    dlg_end(dlg, 1);
@@ -792,7 +791,29 @@ static void ttymodes_handler(union control *ctrl, void *dlg,
            char *p = cfg->ttymodes;
            int i = 0, len = lenof(cfg->ttymodes);
            while (*p) {
+               int multisel = dlg_listbox_index(td->listbox, dlg) < 0;
                if (dlg_listbox_issel(td->listbox, dlg, i)) {
+                   if (!multisel) {
+                       /* Populate controls with entry we're about to
+                        * delete, for ease of editing.
+                        * (If multiple entries were selected, don't
+                        * touch the controls.) */
+                       char *val = strchr(p, '\t');
+                       if (val) {
+                           int ind = 0;
+                           val++;
+                           while (ttymodes[ind]) {
+                               if (strlen(ttymodes[ind]) == val-p-1 &&
+                                   !strncmp(ttymodes[ind], p, val-p-1))
+                                   break;
+                               ind++;
+                           }
+                           dlg_listbox_select(td->modelist, dlg, ind);
+                           dlg_radiobutton_set(td->valradio, dlg,
+                                               (*val == 'V'));
+                           dlg_editbox_set(td->valbox, dlg, val+1);
+                       }
+                   }
                    memmove(p, p+strlen(p)+1, len - (strlen(p)+1));
                    i++;
                    continue;
@@ -866,7 +887,7 @@ static void environ_handler(union control *ctrl, void *dlg,
            if (i < 0) {
                dlg_beep(dlg);
            } else {
-               char *p, *q;
+               char *p, *q, *str;
 
                dlg_listbox_del(ed->listbox, dlg, i);
                p = cfg->environmt;
@@ -881,8 +902,20 @@ static void environ_handler(union control *ctrl, void *dlg,
                q = p;
                if (!*p)
                    goto disaster;
-               while (*p)
-                   p++;
+               /* Populate controls with the entry we're about to delete
+                * for ease of editing */
+               str = p;
+               p = strchr(p, '\t');
+               if (!p)
+                   goto disaster;
+               *p = '\0';
+               dlg_editbox_set(ed->varbox, dlg, str);
+               p++;
+               str = p;
+               dlg_editbox_set(ed->valbox, dlg, str);
+               p = strchr(p, '\0');
+               if (!p)
+                   goto disaster;
                p++;
                while (*p) {
                    while (*p)
@@ -995,7 +1028,8 @@ static void portfwd_handler(union control *ctrl, void *dlg,
            if (i < 0)
                dlg_beep(dlg);
            else {
-               char *p, *q;
+               char *p, *q, *src, *dst;
+               char dir;
 
                dlg_listbox_del(pfd->listbox, dlg, i);
                p = cfg->portfwd;
@@ -1010,8 +1044,44 @@ static void portfwd_handler(union control *ctrl, void *dlg,
                q = p;
                if (!*p)
                    goto disaster2;
-               while (*p)
+               /* Populate the controls with the entry we're about to
+                * delete, for ease of editing. */
+               {
+                   static const char *const afs = "A46";
+                   char *afp = strchr(afs, *p);
+#ifndef NO_IPV6
+                   int idx = afp ? afp-afs : 0;
+#endif
+                   if (afp)
+                       p++;
+#ifndef NO_IPV6
+                   dlg_radiobutton_set(pfd->addressfamily, dlg, idx);
+#endif
+               }
+               {
+                   static const char *const dirs = "LRD";
+                   dir = *p;
+                   dlg_radiobutton_set(pfd->direction, dlg,
+                                       strchr(dirs, dir) - dirs);
+               }
+               p++;
+               if (dir != 'D') {
+                   src = p;
+                   p = strchr(p, '\t');
+                   if (!p)
+                       goto disaster2;
+                   *p = '\0';
                    p++;
+                   dst = p;
+               } else {
+                   src = p;
+                   dst = "";
+               }
+               p = strchr(p, '\0');
+               if (!p)
+                   goto disaster2;
+               dlg_editbox_set(pfd->sourcebox, dlg, src);
+               dlg_editbox_set(pfd->destbox, dlg, dst);
                p++;
                while (*p) {
                    while (*p)
@@ -1089,7 +1159,7 @@ void setup_config_box(struct controlbox *b, int midsession,
        hp->port = c;
        ctrl_columns(s, 1, 100);
 
-       if (!have_backend(PROT_SSH)) {
+       if (!backend_from_proto(PROT_SSH)) {
            ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 3,
                              HELPCTX(session_hostname),
                              config_protocolbuttons_handler, P(hp),
@@ -1180,7 +1250,7 @@ void setup_config_box(struct controlbox *b, int midsession,
     {
        char *sshlogname, *sshrawlogname;
        if ((midsession && protocol == PROT_SSH) ||
-           (!midsession && have_backend(PROT_SSH))) {
+           (!midsession && backend_from_proto(PROT_SSH))) {
            sshlogname = "SSH packets";
            sshrawlogname = "SSH packets and raw data";
         } else {
@@ -1216,7 +1286,7 @@ void setup_config_box(struct controlbox *b, int midsession,
                 dlg_stdcheckbox_handler, I(offsetof(Config,logflush)));
 
     if ((midsession && protocol == PROT_SSH) ||
-       (!midsession && have_backend(PROT_SSH))) {
+       (!midsession && backend_from_proto(PROT_SSH))) {
        s = ctrl_getset(b, "Session/Logging", "ssh",
                        "Options specific to SSH packet logging");
        ctrl_checkbox(s, "Omit known password fields", 'k',
@@ -1242,6 +1312,9 @@ void setup_config_box(struct controlbox *b, int midsession,
     ctrl_checkbox(s, "Implicit CR in every LF", 'r',
                  HELPCTX(terminal_lfhascr),
                  dlg_stdcheckbox_handler, I(offsetof(Config,lfhascr)));
+    ctrl_checkbox(s, "Implicit LF in every CR", 'f',
+                 HELPCTX(terminal_crhaslf),
+                 dlg_stdcheckbox_handler, I(offsetof(Config,crhaslf)));
     ctrl_checkbox(s, "Use background colour to erase screen", 'e',
                  HELPCTX(terminal_bce),
                  dlg_stdcheckbox_handler, I(offsetof(Config,bce)));
@@ -1399,13 +1472,13 @@ void setup_config_box(struct controlbox *b, int midsession,
 
     s = ctrl_getset(b, "Window", "size", "Set the size of the window");
     ctrl_columns(s, 2, 50, 50);
-    c = ctrl_editbox(s, "Rows", 'r', 100,
-                    HELPCTX(window_size),
-                    dlg_stdeditbox_handler, I(offsetof(Config,height)),I(-1));
-    c->generic.column = 0;
     c = ctrl_editbox(s, "Columns", 'm', 100,
                     HELPCTX(window_size),
                     dlg_stdeditbox_handler, I(offsetof(Config,width)), I(-1));
+    c->generic.column = 0;
+    c = ctrl_editbox(s, "Rows", 'r', 100,
+                    HELPCTX(window_size),
+                    dlg_stdeditbox_handler, I(offsetof(Config,height)),I(-1));
     c->generic.column = 1;
     ctrl_columns(s, 1, 100);
 
@@ -1835,7 +1908,7 @@ void setup_config_box(struct controlbox *b, int midsession,
      * when we're not doing SSH.
      */
 
-    if (have_backend(PROT_SSH) && (!midsession || protocol == PROT_SSH)) {
+    if (backend_from_proto(PROT_SSH) && (!midsession || protocol == PROT_SSH)) {
 
        /*
         * The Connection/SSH panel.
@@ -2172,6 +2245,9 @@ void setup_config_box(struct controlbox *b, int midsession,
            ctrl_droplist(s, "Handles SSH-2 key re-exchange badly", 'k', 20,
                          HELPCTX(ssh_bugs_rekey2),
                          sshbug_handler, I(offsetof(Config,sshbug_rekey2)));
+           ctrl_droplist(s, "Ignores SSH-2 maximum packet size", 'x', 20,
+                         HELPCTX(ssh_bugs_maxpkt2),
+                         sshbug_handler, I(offsetof(Config,sshbug_maxpkt2)));
        }
     }
 }
index c089fa13d29e55ede17b8d8c5acbc0a93badfaf0..89b4be92af3c7c61acf3abb55fb2da5a3e1a09ef 100644 (file)
@@ -35,7 +35,7 @@ VERSIONIDS=vids
 endif
 
 CHAPTERS := $(SITE) blurb intro gs using config pscp psftp plink pubkey
-CHAPTERS += pageant errors faq feedback licence udp pgpkeys
+CHAPTERS += pageant errors faq feedback licence udp pgpkeys sshnames
 CHAPTERS += index $(VERSIONIDS)
 
 INPUTS = $(patsubst %,%.but,$(CHAPTERS))
@@ -46,6 +46,10 @@ HALIBUT = halibut
 index.html: $(INPUTS)
        $(HALIBUT) --text --html --winhelp $(INPUTS)
 
+# During formal builds it's useful to be able to build this one alone.
+putty.hlp: $(INPUTS)
+       $(HALIBUT) --winhelp $(INPUTS)
+
 putty.info: $(INPUTS)
        $(HALIBUT) --info $(INPUTS)
 
index c470dd554da686049e59b4f8386d256bebb63022..9a8a0bdb7e3b2bd777b3cd8349b3904fa08985d3 100644 (file)
@@ -31,6 +31,6 @@ features not described here; and the \i\cw{pterm} and command-line
 Unix-specific documentation that currently exists is the
 \I{man pages for PuTTY tools}man pages.
 
-\copyright This manual is copyright 2001-2007 Simon Tatham. All
+\copyright This manual is copyright 2001-2008 Simon Tatham. All
 rights reserved. You may distribute this documentation under the MIT
 licence. See \k{licence} for the licence text in full.
index c351a1b2be222acdf611a889b168e27f6e7a380d..16d158990ac5684e65c35462c03bbb8fcc864a95 100644 (file)
@@ -61,13 +61,6 @@ you want them saved. Then come back to the Session panel. Select the
 \q{\i{Default Settings}} entry in the saved sessions list, with a single
 click. Then press the \q{Save} button.
 
-\lcont{
-Note that PuTTY does not allow you to save a host name into the
-Default Settings entry. This ensures that when PuTTY is started up,
-the host name box is always empty, so a user can always just type in
-a host name and connect.
-}
-
 If there is a specific host you want to store the details of how to
 connect to, you should create a saved session, which will be
 separate from the Default Settings.
@@ -375,6 +368,19 @@ option, and things might go back to normal:
 \c Second line
 \c Third line
 
+\S{config-lfcr} \q{Implicit LF in every CR}
+
+\cfg{winhelp-topic}{terminal.crhaslf}
+
+Most servers send two control characters, \i{CR} and \i{LF}, to start a
+\i{new line} of the screen. The CR character makes the cursor return to the
+left-hand side of the screen. The LF character makes the cursor move
+one line down (and might make the screen scroll).
+
+Some servers only send CR, and so the newly 
+written line is overwritten by the following line. This option causes 
+a line feed so that all lines are displayed.
+
 \S{config-erase} \q{Use \i{background colour} to erase screen}
 
 \cfg{winhelp-topic}{terminal.bce}
@@ -1003,7 +1009,7 @@ The Window configuration panel allows you to control aspects of the
 
 \cfg{winhelp-topic}{window.size}
 
-The \q{\ii{Rows}} and \q{\ii{Columns}} boxes let you set the PuTTY
+The \q{\ii{Columns}} and \q{\ii{Rows}} boxes let you set the PuTTY
 window to a precise size. Of course you can also \I{window resizing}drag
 the window to a new size while a session is running.
 
@@ -1694,8 +1700,13 @@ TCP keepalives are disabled by default.
 \cfg{winhelp-topic}{connection.ipversion}
 
 This option allows the user to select between the old and new
-Internet protocols and addressing schemes (\i{IPv4} and \i{IPv6}). The
-default setting is \q{Auto}, which means PuTTY will do something
+Internet protocols and addressing schemes (\i{IPv4} and \i{IPv6}).
+The selected protocol will be used for most outgoing network
+connections (including connections to \I{proxy}proxies); however,
+tunnels have their own configuration, for which see
+\k{config-ssh-portfwd-address-family}.
+
+The default setting is \q{Auto}, which means PuTTY will do something
 sensible and try to guess which protocol you wanted. (If you specify
 a literal \i{Internet address}, it will use whichever protocol that
 address implies. If you provide a \i{hostname}, it will see what kinds
@@ -1808,6 +1819,11 @@ this panel affect the primary network connection forming your PuTTY
 session, and also any extra connections made as a result of SSH \i{port
 forwarding} (see \k{using-port-forwarding}).
 
+Note that unlike some software (such as web browsers), PuTTY does not
+attempt to automatically determine whether to use a proxy and (if so)
+which one to use for a given destination. If you need to use a proxy,
+it must always be explicitly configured.
+
 \S{config-proxy-type} Setting the proxy type
 
 \cfg{winhelp-topic}{proxy.type}
@@ -2282,6 +2298,10 @@ exchange; the server can avoid groups known to be weak, and possibly
 invent new ones over time, without any changes required to PuTTY's
 configuration. We recommend use of this method, if possible.
 
+In addition, PuTTY supports \i{RSA key exchange}, which requires much less
+computational effort on the part of the client, and somewhat less on
+the part of the server, than Diffie-Hellman key exchange.
+
 If the first algorithm PuTTY finds is below the \q{warn below here}
 line, you will see a warning box when you make the connection, similar
 to that for cipher selection (see \k{config-ssh-encryption}).
@@ -2404,11 +2424,12 @@ forms of simple \I{challenge/response authentication}challenge/response
 authentication available in SSH protocol version 1 only. You might use
 them if you were using \i{S/Key} \i{one-time passwords}, for example,
 or if you had a physical \i{security token} that generated responses
-to authentication challenges.
+to authentication challenges.  They can even be used to prompt for
+simple passwords.
 
 With this switch enabled, PuTTY will attempt these forms of
 authentication if the server is willing to try them. You will be
-presented with a challenge string (which will be different every
+presented with a challenge string (which may be different every
 time) and must supply the correct response in order to log in. If
 your server supports this, you should talk to your system
 administrator about precisely what form these challenges and
@@ -2772,6 +2793,9 @@ incoming connections in both IPv4 and (if available) IPv6
 \b for a remote-to-local port forwarding, PuTTY will choose a
 sensible protocol for the outgoing connection.
 
+This overrides the general Internet protocol version preference
+on the Connection panel (see \k{config-address-family}).
+
 Note that some operating systems may listen for incoming connections
 in IPv4 even if you specifically asked for IPv6, because their IPv4
 and IPv6 protocol stacks are linked together. Apparently \i{Linux} does
@@ -2962,6 +2986,22 @@ would expect.
 
 This is an SSH-2-specific bug.
 
+\S{config-ssh-bug-maxpkt2} \q{Ignores SSH-2 \i{maximum packet size}}
+
+\cfg{winhelp-topic}{ssh.bugs.maxpkt2}
+
+When an SSH-2 channel is set up, each end announces the maximum size
+of data packet that it is willing to receive for that channel.  Some
+servers ignore PuTTY's announcement and send packets larger than PuTTY
+is willing to accept, causing it to report \q{Incoming packet was
+garbled on decryption}.
+
+If this bug is detected, PuTTY never allows the channel's
+\i{flow-control window} to grow large enough to allow the server to
+send an over-sized packet.  If this bug is enabled when talking to a
+correct server, the session will work correctly, but download
+performance will be less than it could be.
+
 \H{config-serial} The Serial panel
 
 The \i{Serial} panel allows you to configure options that only apply
@@ -2975,7 +3015,7 @@ The \q{Serial line to connect to} box allows you to choose which
 serial line you want PuTTY to talk to, if your computer has more
 than one serial port.
 
-On Windows, the first serial line is called \cw{COM1}, and if there
+On Windows, the first serial line is called \i\cw{COM1}, and if there
 is a second it is called \cw{COM2}, and so on.
 
 This configuration setting is also visible on the Session panel,
index 03392454c516f1bc3b4e6b31beca77cb3c072234..ae00a410b916c909f2a93225a3de505ae46e3f4d 100644 (file)
@@ -200,8 +200,15 @@ the various strategies we use for camouflaging passwords in transit.
 Upgrade your server, or use the workarounds described in
 \k{config-ssh-bug-ignore1} and possibly \k{config-ssh-bug-plainpw1}.
 
+\H{errors-no-auth} \q{No supported authentication methods available}
+
+This error indicates that PuTTY has run out of ways to authenticate
+you to an SSH server.  This may be because PuTTY has TIS or
+keyboard-interactive authentication disabled, in which case
+\k{config-ssh-tis} and \k{config-ssh-ki}.
+
 \H{errors-crc} \q{Incorrect \i{CRC} received on packet} or \q{Incorrect
-MAC received on packet}
+\i{MAC} received on packet}
 
 This error occurs when PuTTY decrypts an SSH packet and its checksum
 is not correct. This probably means something has gone wrong in the
@@ -209,6 +216,14 @@ encryption or decryption process. It's difficult to tell from this
 error message whether the problem is in the client, in the server,
 or in between.
 
+In particular, if the network is corrupting data at the TCP level, it
+may only be obvious with cryptographic protocols such as SSH, which
+explicitly check the integrity of the transferred data and complain
+loudly if the checks fail. Corruption of protocols without integrity
+protection (such as HTTP) will manifest in more subtle failures (such
+as misdisplayed text or images in a web browser) which may not be
+noticed.
+
 A known server problem which can cause this error is described in
 \k{faq-openssh-bad-openssl} in the FAQ.
 
@@ -220,9 +235,10 @@ gone wrong in the encryption or decryption process. It's difficult
 to tell from this error message whether the problem is in the client,
 in the server, or in between.
 
-If you get this error, one thing you could try would be to fiddle
-with the setting of \q{Miscomputes SSH-2 encryption keys} on the Bugs
-panel (see \k{config-ssh-bug-derivekey2}).
+If you get this error, one thing you could try would be to fiddle with
+the setting of \q{Miscomputes SSH-2 encryption keys} (see
+\k{config-ssh-bug-derivekey2}) or \q{Ignores SSH-2 maximum packet
+size} (see \k{config-ssh-bug-maxpkt2}) on the Bugs panel .
 
 Another known server problem which can cause this error is described
 in \k{faq-openssh-bad-openssl} in the FAQ.
index 75bc831bf37e86287b652cb1c2baae33e45979fe..82214761de9be6eecf3f2e0640caa419159a5412 100644 (file)
@@ -161,7 +161,7 @@ completely is the wrong solution and we will not do it.
 
 If you have host keys available in the common \i\c{known_hosts} format,
 we have a script called 
-\W{http://www.tartarus.org/~simon-anonsvn/viewcvs.cgi/putty/contrib/kh2reg.py?view=markup}\c{kh2reg.py}
+\W{http://svn.tartarus.org/putty/contrib/kh2reg.py?view=markup}\c{kh2reg.py}
 to convert them to a Windows .REG file, which can be installed ahead of
 time by double-clicking or using \c{REGEDIT}.
 
@@ -1133,8 +1133,9 @@ link to you at all.
 If you have software based on PuTTY, or specifically designed to
 interoperate with PuTTY, or in some other way of genuine interest to
 PuTTY users, then we will probably be happy to add a link to you on
-our Links page. And if you're running a mirror of the PuTTY web
-site, we're \e{definitely} interested.
+our Links page. And if you're running a particularly valuable mirror
+of the PuTTY web site, we might be interested in linking to you from
+our Mirrors page.
 
 \S{faq-sourceforge}{Question} Why don't you move PuTTY to
 SourceForge?
@@ -1191,11 +1192,8 @@ asking for any.
 Having said all that, if you still really \e{want} to give us money,
 we won't argue :-) The easiest way for us to accept donations is if
 you send money to \cw{<anakin@pobox.com>} using PayPal
-(\W{http://www.paypal.com/}\cw{www.paypal.com}). Alternatively, if
-you don't trust PayPal, you could donate through e-gold
-(\W{http://www.e-gold.com}\cw{www.e-gold.com}): deposit your
-donation in account number 174769, then send us e-mail to let us
-know you've done so (otherwise we might not notice for months!).
+(\W{http://www.paypal.com/}\cw{www.paypal.com}). If you don't like
+PayPal, talk to us; we can probably arrange some alternative means.
 
 Small donations (tens of dollars or tens of euros) will probably be
 spent on beer or curry, which helps motivate our volunteer team to
index fe6492720e53d976238a0a10d881fde1a7575ed1..04bbab9807e99bea8e5a6f9781211f9b524b178f 100644 (file)
@@ -375,18 +375,27 @@ clear that we \e{could} stop you doing this, even if we wanted to!)
 
 \H{feedback-mirrors} Mirroring the PuTTY web site
 
-\#{This paragraph also in putty-website/mirrors.html}
-Mirrors of the PuTTY web site are welcome, especially in regions not
-well covered by existing mirrors. (However, if you're in a region that is
-already well served by mirrors, you should consider whether yet another one
-will be worth the effort.) Please don't bother asking us for permission before
+\# the next two paragraphs also on the Mirrors page itself, with
+\# minor context changes
+
+If you want to set up a mirror of the PuTTY website, go ahead and
+set one up. Please don't bother asking us for permission before
 setting up a mirror. You already have permission.
 
-If you mail us \e{after} you have set up the mirror and checked that
-it works, and remember to let us know which country your mirror is in,
-then we'll add it to the
-\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/mirrors.html}{Mirrors
-page} on the PuTTY website.
+If the mirror is in a country where we don't already have plenty of
+mirrors, we may be willing to add it to the list on our
+\W{http://www.chiark.greenend.org.uk/~sgtatham/putty/mirrors.html}{mirrors
+page}. Read the guidelines on that page, make sure your mirror
+works, and email us the information listed at the bottom of the
+page.
+
+Note that we do not \e{promise} to list your mirror: we get a lot of
+mirror notifications and yours may not happen to find its way to the
+top of the list.
+
+Also note that we link to all our mirror sites using the
+\c{rel="nofollow"} attribute. Running a PuTTY mirror is not intended
+to be a cheap way to gain search rankings.
 
 If you have technical questions about the process of mirroring, then
 you might want to mail us before setting up the mirror (see also the
index 8e63d82ba8b268590202915c6459c0ca00e892f7..5a6d6e3a65964b0fcf042f90e077470ed93be3ef 100644 (file)
@@ -672,8 +672,8 @@ saved sessions from
 \IM{ignore message} SSH \q{ignore} messages
 \IM{ignore message} \q{ignore} messages, in SSH
 
-\IM{message authentication code} message authentication code
-\IM{message authentication code} MAC (message authentication code)
+\IM{message authentication code}{MAC} message authentication code (MAC)
+\IM{message authentication code}{MAC} MAC (message authentication code)
 
 \IM{signatures} signature
 \IM{signatures} digital signature
index 6a71a8093487fd67c5289cc4814c55e4bb37a256..39023bf93a7a589cd34708ff76a52cb1d68373ca 100644 (file)
@@ -2,7 +2,7 @@
 
 \A{licence} PuTTY \ii{Licence}
 
-PuTTY is \i{copyright} 1997-2007 Simon Tatham.
+PuTTY is \i{copyright} 1997-2008 Simon Tatham.
 
 Portions copyright Robert de Bath, Joris van Rantwijk, Delian
 Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry,
index dbda8843838edba8ca54e2c5a87ba0f7954bf6ab..a70f54f6adee0af13df0de71577f6571fe4dbe71 100644 (file)
@@ -43,7 +43,7 @@ use Plink:
 
 \c Z:\sysosd>plink
 \c PuTTY Link: command-line connection utility
-\c Release 0.59
+\c Release 0.60
 \c Usage: plink [options] [user@]host [command]
 \c        ("host" can also be a PuTTY saved session name)
 \c Options:
index f2cfcbf0b3bf350f2bc44743190d3c6d994014d9..74669f86b70ad01670ef7ebb88262d2f9ca02bd0 100644 (file)
@@ -41,7 +41,7 @@ use PSCP:
 
 \c Z:\owendadmin>pscp
 \c PuTTY Secure Copy client
-\c Release 0.59
+\c Release 0.60
 \c Usage: pscp [options] [user@]host:source target
 \c        pscp [options] source [source...] [user@]host:target
 \c        pscp [options] -ls [user@]host:filespec
diff --git a/doc/sshnames.but b/doc/sshnames.but
new file mode 100644 (file)
index 0000000..57959fb
--- /dev/null
@@ -0,0 +1,77 @@
+\define{versionidsshnames} \versionid $Id$
+
+\A{sshnames} SSH-2 names specified for PuTTY
+
+There are various parts of the SSH-2 protocol where things are specified
+using a textual name.  Names ending in \cw{@putty.projects.tartarus.org}
+are reserved for allocation by the PuTTY team.  Allocated names are
+documented here.
+
+\H{sshnames-global} Connection protocol global request name
+
+This name can be sent in a \cw{SSH_MSG_GLOBAL_REQUEST} message.
+
+\dt \cw{simple@putty.projects.tartarus.org}
+
+\dd This is sent by a client to announce that it will not have more that
+one channel open at a time in the current connection.  The intention
+is that the server, knowing this, can set the window on that one
+channel to something very large, and leave flow control to TCP.  The
+format of the request is:
+
+\lcont{
+
+\c byte      SSH_MSG_GLOBAL_REQUEST
+\c uint32    recipient channel
+\c string    "simple@putty.projects.tartarus.org"
+\c boolean   want reply
+
+}
+
+\H{sshnames-channel} Connection protocol channel request name
+
+This name can be sent in a \cw{SSH_MSG_CHANNEL_REQUEST} message.
+
+\dt \cw{winadj@putty.projects.tartarus.org}
+
+\dd PuTTY sends this request along with some
+\cw{SSH_MSG_CHANNEL_WINDOW_ADJUST} messages as part of its window-size
+tuning.  It can be sent on any type of channel.  Servers MUST treat it
+as an unrecognised request and respond with
+\cw{SSH_MSG_CHANNEL_FAILURE}.
+
+\H{sshnames-kex} Key exchange method names
+
+\dt \cw{rsa-sha1-draft-00@putty.projects.tartarus.org}
+
+\dt \cw{rsa-sha256-draft-00@putty.projects.tartarus.org}
+
+\dt \cw{rsa1024-sha1-draft-01@putty.projects.tartarus.org}
+
+\dt \cw{rsa1024-sha256-draft-01@putty.projects.tartarus.org}
+
+\dt \cw{rsa2048-sha256-draft-01@putty.projects.tartarus.org}
+
+\dt \cw{rsa1024-sha1-draft-02@putty.projects.tartarus.org}
+
+\dt \cw{rsa2048-sha512-draft-02@putty.projects.tartarus.org}
+
+\dt \cw{rsa1024-sha1-draft-03@putty.projects.tartarus.org}
+
+\dt \cw{rsa2048-sha256-draft-03@putty.projects.tartarus.org}
+
+\dt \cw{rsa1024-sha1-draft-04@putty.projects.tartarus.org}
+
+\dt \cw{rsa2048-sha256-draft-04@putty.projects.tartarus.org}
+
+\dd These appeared in various drafts of what eventually became RFC\_4432.
+They have been superseded by \cw{rsa1024-sha1} and \cw{rsa2048-sha256}.
+
+\H{sshnames-encrypt} Encryption algorithm names
+
+\dt \cw{arcfour128-draft-00@putty.projects.tartarus.org}
+
+\dt \cw{arcfour256-draft-00@putty.projects.tartarus.org}
+
+\dd These were used in drafts of what eventually became RFC\_4345.
+They have been superseded by \cw{arcfour128} and \cw{arcfour256}.
index 17fdce5fe9de0a5ea2bb95203c750a67be690b76..0c7fcf8894514f67162cf0782a6a74a81abdcaf0 100644 (file)
@@ -128,6 +128,9 @@ connection in addition to normal data. Their precise effect is usually
 up to the server. Currently only Telnet, SSH, and serial connections
 have special commands.
 
+The \q{break} signal can also be invoked from the keyboard with
+\i{Ctrl-Break}.
+
 The following \I{Telnet special commands}special commands are
 available in Telnet:
 
@@ -335,7 +338,7 @@ doesn't, the manual for the \i{X server} should tell you what it
 does do.
 
 You should then tick the \q{Enable X11 forwarding} box in the
-Tunnels panel (see \k{config-ssh-x11}) before starting your SSH
+X11 panel (see \k{config-ssh-x11}) before starting your SSH
 session. The \i{\q{X display location}} box is blank by default, which
 means that PuTTY will try to use a sensible default such as \c{:0},
 which is the usual display location where your X server will be
@@ -464,6 +467,9 @@ theory but servers will not necessarily cooperate.
 to obtain a fix from Microsoft in order to use addresses like
 \cw{127.0.0.5} - see \k{faq-alternate-localhost}.)
 
+For more options relating to port forwarding, see
+\k{config-ssh-portfwd}.
+
 \H{using-rawprot} Making \i{raw TCP connections}
 
 A lot of \I{debugging Internet protocols}Internet protocols are
@@ -762,8 +768,7 @@ it off. These options are only meaningful if you are using SSH.
 For information on X11 forwarding, see \k{using-x-forwarding}.
 
 These options are equivalent to the X11 forwarding checkbox in the
-Tunnels panel of the PuTTY configuration box (see
-\k{config-ssh-x11}).
+X11 panel of the PuTTY configuration box (see \k{config-ssh-x11}).
 
 These options are not available in the file transfer tools PSCP and
 PSFTP.
@@ -865,7 +870,8 @@ PuTTY configuration box (see \k{config-ssh-prot}).
 \i{Internet protocol version}
 
 The \c{-4} and \c{-6} options force PuTTY to use the older Internet
-protocol \i{IPv4} or the newer \i{IPv6}.
+protocol \i{IPv4} or the newer \i{IPv6} for most outgoing
+connections.
 
 These options are equivalent to selecting your preferred Internet
 protocol version as \q{IPv4} or \q{IPv6} in the Connection panel of
index 373816f3a2e958b964c8322e1a0bc7635a073151..1074303eff506e313307e1190ccdb0b3563dc98f 100644 (file)
@@ -1,6 +1,6 @@
 # Makefile for the PuTTY icon suite.
 
-ICONS = putty puttycfg puttygen pscp pageant pterm ptermcfg installer
+ICONS = putty puttycfg puttygen pscp pageant pterm ptermcfg puttyins
 SIZES = 16 32 48
 
 MODE = # override to -it on command line for opaque testing
@@ -10,7 +10,7 @@ MONOPNGS = $(foreach I,$(ICONS),$(foreach S,$(SIZES),$(I)-$(S)-mono.png))
 TRUEPNGS = $(foreach I,$(ICONS),$(foreach S,$(SIZES),$(I)-$(S)-true.png))
 
 ICOS = putty.ico puttygen.ico pscp.ico pageant.ico pageants.ico puttycfg.ico \
-       installer.ico
+       puttyins.ico
 CICONS = xpmputty.c xpmpucfg.c xpmpterm.c xpmptcfg.c
 
 base: icos cicons
@@ -63,11 +63,11 @@ pscp.ico: pscp-16.png pscp-32.png pscp-48.png \
 # Because the installer icon makes heavy use of brown when drawing
 # the cardboard box, it's worth having 8-bit versions of it in
 # addition to the 4- and 1-bit ones.
-installer.ico: installer-16.png installer-32.png installer-48.png \
-               installer-16-mono.png installer-32-mono.png \
-               installer-48-mono.png \
-               installer-16-true.png installer-32-true.png \
-               installer-48-true.png
+puttyins.ico: puttyins-16.png puttyins-32.png puttyins-48.png \
+              puttyins-16-mono.png puttyins-32-mono.png \
+              puttyins-48-mono.png \
+              puttyins-16-true.png puttyins-32-true.png \
+              puttyins-48-true.png
        ./icon.pl -8 $(filter %-true.png, $^) \
                   -4 $(filter-out %-true.png, $(filter-out %-mono.png, $^)) \
                   -1 $(filter %-mono.png, $^) > $@
index cf0763051946649f5ab38843106f70a5dfb8a208..d40a9815e798caa1ec6b0642b57e64811d52c693 100755 (executable)
@@ -795,7 +795,7 @@ def puttygen_icon(size):
 def pscp_icon(size):
     return xybolt(document(size), computer(size), size)
 
-def installer_icon(size):
+def puttyins_icon(size):
     aret = {}
     # The box back goes behind the lightning bolt.
     canvas = xybolt(boxback(size), computer(size), size, boltoffx=-2, boltoffy=+1, aux=aret)
index 9e1d1f30e2418fbdfd07ffbdc54ba8b127f508b7..25362061a210860eb29b1d2ed5601e803b4ee04b 100644 (file)
--- a/mac/mac.c
+++ b/mac/mac.c
@@ -210,13 +210,10 @@ static void mac_startup(void) {
     default_protocol = be_default_protocol;
     /* Find the appropriate default port. */
     {
-       int i;
+       Backend *b = backend_from_proto(default_protocol);
        default_port = 0; /* illegal */
-       for (i = 0; backends[i].backend != NULL; i++)
-           if (backends[i].protocol == default_protocol) {
-               default_port = backends[i].backend->default_port;
-               break;
-           }
+       if (b)
+           default_port = b->default_port;
     }
     flags = FLAG_INTERACTIVE;
 
index c5ec94e02d5b69ce41230935284a5a2383b9227a..1a8246fe2cfcba1a3c3babb1f626092414714355 100644 (file)
@@ -1221,7 +1221,7 @@ resource 'DITL' (wAbout, "about", purgeable) {
        StaticText { disabled, "PuTTY"},
        { 42, 13, 74, 227 },
        StaticText { disabled, "Some version or other\n"
-                              "Copyright Â© 1997-2007 Simon Tatham"},
+                              "Copyright Â© 1997-2008 Simon Tatham"},
     }
 };
 
@@ -1242,7 +1242,7 @@ type 'TEXT' {
 };
 
 resource 'TEXT' (wLicence, "licence", purgeable) {
-    "PuTTY is copyright 1997-2007 Simon Tatham.\n"
+    "PuTTY is copyright 1997-2008 Simon Tatham.\n"
     "\n"
     "Portions copyright Robert de Bath, Joris van Rantwijk, Delian "
     "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, "
index 0a7aebaa5d0c068a65056ab6d4dc3b304a09e61d..0b28cf9f473daebcd2f1dd4dfc13a8fc9400f5df 100644 (file)
@@ -237,7 +237,7 @@ static OSErr mac_opensessionfrom(FSSpec *fss)
        err = -9999;
        goto fail;
     }
-    load_open_settings(sesshandle, TRUE, &s->cfg);
+    load_open_settings(sesshandle, &s->cfg);
     close_settings_r(sesshandle);
 
     mac_startsession(s);
@@ -321,7 +321,7 @@ void mac_savesession(void)
     assert(s->hasfile);
     sesshandle = open_settings_w_fsp(&s->savefile);
     if (sesshandle == NULL) return; /* XXX report error */
-    save_open_settings(sesshandle, TRUE, &s->cfg);
+    save_open_settings(sesshandle, &s->cfg);
     close_settings_w(sesshandle);
 }
 
@@ -342,7 +342,7 @@ void mac_savesessionas(void)
     }
     sesshandle = open_settings_w_fsp(&sfr.sfFile);
     if (sesshandle == NULL) return; /* XXX report error */
-    save_open_settings(sesshandle, TRUE, &s->cfg);
+    save_open_settings(sesshandle, &s->cfg);
     close_settings_w(sesshandle);
     s->hasfile = TRUE;
     s->savefile = sfr.sfFile;
index 8f85b29f94421330e50b4fd1904c72bbfcd5b3c5..da0dd55f771b804e5a474d6cd9adb8acc8deb62e 100644 (file)
@@ -422,7 +422,7 @@ resource 'DITL' (wAbout, "about", purgeable) {
        StaticText { disabled, "PuTTYgen"},
        { 42, 13, 74, 227 },
        StaticText { disabled, "Some version or other\n"
-                              "Copyright Â© 1997-2007 Simon Tatham"},
+                              "Copyright Â© 1997-2008 Simon Tatham"},
     }
 };
 
@@ -443,7 +443,7 @@ type 'TEXT' {
 };
 
 resource 'TEXT' (wLicence, "licence", purgeable) {
-    "Copyright 1997-2007 Simon Tatham.\n"
+    "Copyright 1997-2008 Simon Tatham.\n"
     "\n"
     "Portions copyright Robert de Bath, Joris van Rantwijk, Delian "
     "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, "
index dad2688336251bbffa43c4447d4dd8c008cac163..4a75a0c6018be939f8a8108ca05c3be1e604e343 100644 (file)
@@ -115,12 +115,7 @@ void mac_startsession(Session *s)
      * Select protocol. This is farmed out into a table in a
      * separate file to enable an ssh-free variant.
      */
-    s->back = NULL;
-    for (i = 0; backends[i].backend != NULL; i++)
-       if (backends[i].protocol == s->cfg.protocol) {
-           s->back = backends[i].backend;
-           break;
-       }
+    s->back = backend_from_proto(s->cfg.protocol);
     if (s->back == NULL)
        fatalbox("Unsupported protocol number found");
 
index 49860b66d06b6a62ab0018b42c92cf7c1d09bfe1..8770d1f69d5b1e587c68cdaa126f824ee822db6d 100644 (file)
@@ -2,7 +2,7 @@
  * Current PuTTY version number.  Minor is in BCD
  */
 #define VERSION_MAJOR 0x00
-#define VERSION_MINOR 0x59
+#define VERSION_MINOR 0x60
 
 resource 'vers' (1, purgeable) {
 #ifdef RELEASE
index e147bd99fe8d7464d4fb07071e2058d4b8d944d1..e79290dfb889e6801a5854e0b6914a15be7b11bd 100644 (file)
@@ -64,10 +64,12 @@ struct alert_queue {
 - (void)doText:(wchar_t *)text len:(int)len x:(int)x y:(int)y
     attr:(unsigned long)attr lattr:(int)lattr;
 - (int)fromBackend:(const char *)data len:(int)len isStderr:(int)is_stderr;
+- (int)fromBackendUntrusted:(const char *)data len:(int)len;
 - (void)startAlert:(NSAlert *)alert
     withCallback:(void (*)(void *, int))callback andCtx:(void *)ctx;
 - (void)endSession:(int)clean;
 - (void)notifyRemoteExit;
+- (Terminal *)term;
 @end
 
 /*
index 094c9555cc0002039cedf238b5dfe08fc2daf3c5..295b675520d790c08a1b6afc81115016758b228a 100644 (file)
     ctrlbox = ctrl_new_box();
     setup_config_box(ctrlbox, FALSE /*midsession*/, aCfg.protocol,
                     0 /* protcfginfo */);
-    unix_setup_config_box(ctrlbox, FALSE /*midsession*/);
+    unix_setup_config_box(ctrlbox, FALSE /*midsession*/, aCfg.protocol);
 
     cfg = aCfg;                               /* structure copy */
 
index a4fac71d70bf3b09b0f099540e350dee7482e902..f61e6bff73a9aee093e06cafb3cba06e08fe7872 100644 (file)
     /*
      * Set up a backend.
      */
-    {
-       int i;
+    back = backend_from_proto(cfg.protocol);
+    if (!back)
        back = &pty_backend;
-       for (i = 0; backends[i].backend != NULL; i++)
-           if (backends[i].protocol == cfg.protocol) {
-               back = backends[i].backend;
-               break;
-           }
-    }
 
     {
        const char *error;
     return term_data(term, is_stderr, data, len);
 }
 
+- (int)fromBackendUntrusted:(const char *)data len:(int)len
+{
+    return term_data_untrusted(term, data, len);
+}
+
 - (void)startAlert:(NSAlert *)alert
     withCallback:(void (*)(void *, int))callback andCtx:(void *)ctx
 {
     // FIXME: else show restart menu item
 }
 
+- (Terminal *)term
+{
+    return term;
+}
+
 @end
 
 int from_backend(void *frontend, int is_stderr, const char *data, int len)
@@ -893,6 +897,12 @@ int from_backend(void *frontend, int is_stderr, const char *data, int len)
     return [win fromBackend:data len:len isStderr:is_stderr];
 }
 
+int from_backend_untrusted(void *frontend, const char *data, int len)
+{
+    SessionWindow *win = (SessionWindow *)frontend;
+    return [win fromBackendUntrusted:data len:len];
+}
+
 int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
 {
     SessionWindow *win = (SessionWindow *)p->frontend;
@@ -922,7 +932,7 @@ void ldisc_update(void *frontend, int echo, int edit)
 
 char *get_ttymode(void *frontend, const char *mode)
 {
-    SessionWindow *win = (SessionWindow *)ctx;
+    SessionWindow *win = (SessionWindow *)frontend;
     Terminal *term = [win term];
     return term_get_ttymode(term, mode);
 }
index 3cc580cb95fb71628f99c1628121009d5b0715ea..f0a57570ce7482ae8d04159404a724fa57c07398 100755 (executable)
@@ -429,7 +429,7 @@ if (defined $makefiles{'cygwin'}) {
       if ($d->{obj} =~ /\.res\.o$/) {
          print "\t\$(RC) \$(RCFL) \$(RCFLAGS) ".$d->{deps}->[0]." ".$d->{obj}."\n\n";
       } else {
-         print "\t\$(CC) \$(COMPAT) \$(XFLAGS) \$(CFLAGS) -c ".$d->{deps}->[0]."\n\n";
+         print "\t\$(CC) \$(COMPAT) \$(CFLAGS) \$(XFLAGS) -c ".$d->{deps}->[0]."\n\n";
       }
     }
     print "\n";
@@ -486,7 +486,7 @@ if (defined $makefiles{'borland'}) {
     "\n".
     ".c.obj:\n".
     &splitline("\tbcc32 -w-aus -w-ccc -w-par -w-pia \$(COMPAT)".
-              " \$(XFLAGS) \$(CFLAGS) ".
+              " \$(CFLAGS) \$(XFLAGS) ".
               (join " ", map {"-I$dirpfx$_"} @srcdirs) .
               " /c \$*.c",69)."\n".
     ".rc.res:\n".
@@ -615,7 +615,7 @@ if (defined $makefiles{'vc'}) {
         print &splitline(sprintf("%s: %s", $d->{obj},
                                  join " ", @$extradeps, @{$d->{deps}})), "\n";
         if ($d->{obj} =~ /.obj$/) {
-           print "\tcl \$(COMPAT) \$(XFLAGS) \$(CFLAGS) /c ".$d->{deps}->[0],"\n\n";
+           print "\tcl \$(COMPAT) \$(CFLAGS) \$(XFLAGS) /c ".$d->{deps}->[0],"\n\n";
        } else {
            print "\trc \$(RCFL) -r \$(RCFLAGS) ".$d->{deps}->[0],"\n\n";
        }
@@ -935,8 +935,8 @@ if (defined $makefiles{'gtk'}) {
               (join " ", map {"-I$dirpfx$_"} @srcdirs) .
               " `\$(GTK_CONFIG) --cflags`").
                 " -D _FILE_OFFSET_BITS=64\n".
-    "XLDFLAGS = `\$(GTK_CONFIG) --libs`\n".
-    "ULDFLAGS =#\n".
+    "XLDFLAGS = \$(LDFLAGS) `\$(GTK_CONFIG) --libs`\n".
+    "ULDFLAGS = \$(LDFLAGS)\n".
     "INSTALL=install\n",
     "INSTALL_PROGRAM=\$(INSTALL)\n",
     "INSTALL_DATA=\$(INSTALL)\n",
@@ -958,8 +958,8 @@ if (defined $makefiles{'gtk'}) {
       $objstr = &objects($p, "X.o", undef, undef);
       print &splitline($prog . ": " . $objstr), "\n";
       $libstr = &objects($p, undef, undef, "-lX");
-      print &splitline("\t\$(CC)" . $mw . " \$(${type}LDFLAGS) -o \$@ " .
-                       $objstr . " $libstr", 69), "\n\n";
+      print &splitline("\t\$(CC)" . $mw . " -o \$@ " .
+                       $objstr . " \$(${type}LDFLAGS) $libstr", 69), "\n\n";
     }
     foreach $d (&deps("X.o", undef, $dirpfx, "/", "gtk")) {
       if ($forceobj{$d->{obj_orig}}) {
@@ -968,7 +968,7 @@ if (defined $makefiles{'gtk'}) {
         print &splitline(sprintf("%s: %s", $d->{obj},
                                  join " ", @{$d->{deps}})), "\n";
       }
-      print &splitline("\t\$(CC) \$(COMPAT) \$(XFLAGS) \$(CFLAGS) -c $d->{deps}->[0]\n");
+      print &splitline("\t\$(CC) \$(COMPAT) \$(CFLAGS) \$(XFLAGS) -c $d->{deps}->[0]\n");
     }
     print "\n";
     print $makefile_extra{'gtk'}->{'end'};
@@ -1021,8 +1021,8 @@ if (defined $makefiles{'ac'}) {
       $objstr = &objects($p, "X.o", undef, undef);
       print &splitline($prog . ": " . $objstr), "\n";
       $libstr = &objects($p, undef, undef, "-lX");
-      print &splitline("\t\$(CC)" . $mw . " \$(${type}LDFLAGS) -o \$@ " .
-                       $objstr . " $libstr", 69), "\n\n";
+      print &splitline("\t\$(CC)" . $mw . " -o \$@ " .
+                       $objstr . " \$(${type}LDFLAGS) $libstr", 69), "\n\n";
     }
     foreach $d (&deps("X.o", undef, $dirpfx, "/", "gtk")) {
       if ($forceobj{$d->{obj_orig}}) {
@@ -1031,7 +1031,7 @@ if (defined $makefiles{'ac'}) {
         print &splitline(sprintf("%s: %s", $d->{obj},
                                  join " ", @{$d->{deps}})), "\n";
       }
-      print &splitline("\t\$(CC) \$(COMPAT) \$(XFLAGS) \$(CFLAGS) -c $d->{deps}->[0]\n");
+      print &splitline("\t\$(CC) \$(COMPAT) \$(CFLAGS) \$(XFLAGS) -c $d->{deps}->[0]\n");
     }
     print "\n";
     print $makefile_extra{'gtk'}->{'end'};
@@ -1232,7 +1232,7 @@ if (defined $makefiles{'lcc'}) {
       }
       if ($d->{obj} =~ /\.obj$/) {
          print &splitline("\tlcc -O -p6 \$(COMPAT)".
-                          " \$(XFLAGS) \$(CFLAGS) ".$d->{deps}->[0],69)."\n";
+                          " \$(CFLAGS) \$(XFLAGS) ".$d->{deps}->[0],69)."\n";
       } else {
           print &splitline("\tlrc \$(RCFL) -r \$(RCFLAGS) ".
                            $d->{deps}->[0],69)."\n";
@@ -1317,9 +1317,9 @@ if (defined $makefiles{'osx'}) {
       }
       $firstdep = $d->{deps}->[0];
       if ($firstdep =~ /\.c$/) {
-         print "\t\$(CC) \$(COMPAT) \$(FWHACK) \$(XFLAGS) \$(CFLAGS) -c \$<\n";
+         print "\t\$(CC) \$(COMPAT) \$(FWHACK) \$(CFLAGS) \$(XFLAGS) -c \$<\n";
       } elsif ($firstdep =~ /\.m$/) {
-         print "\t\$(CC) -x objective-c \$(COMPAT) \$(FWHACK) \$(XFLAGS) \$(CFLAGS) -c \$<\n";
+         print "\t\$(CC) -x objective-c \$(COMPAT) \$(FWHACK) \$(CFLAGS) \$(XFLAGS) -c \$<\n";
       }
     }
     print "\n".$makefile_extra{'osx'}->{'end'};
index 7e8d367c34b5ab2cda171156b874e65c31845133..7c69c2f6fbdd925039fe9c98360087b65039ef52 100755 (executable)
@@ -4,7 +4,8 @@
 #
 # Pass an argument of the form `2004-02-08' to have the archive
 # tagged as a development snapshot; of the form `0.54' to have it
-# tagged as a release.
+# tagged as a release; of the form `r1234' to have it tagged as a
+# custom build. Otherwise it'll be tagged as unidentified.
 
 case "$1" in
   ????-??-??)
@@ -13,6 +14,11 @@ case "$1" in
     ver="-DSNAPSHOT=$1"
     docver=
     ;;
+  r*)
+    arcsuffix="-$1"
+    ver="-DSVN_REV=$1"
+    docver=
+    ;;
   '')
     arcsuffix=
     ver=
diff --git a/pscp.c b/pscp.c
index 8e082e732ff2c73a462664153e39f9bb46aedacc..1809c6f7a0f78d23caf81076d964a72b9358485e 100644 (file)
--- a/pscp.c
+++ b/pscp.c
@@ -180,12 +180,6 @@ int from_backend(void *frontend, int is_stderr, const char *data, int datalen)
        return 0;
     }
 
-    /*
-     * If this is before the real session begins, just return.
-     */
-    if (!outptr)
-       return 0;
-
     if ((outlen > 0) && (len > 0)) {
        unsigned used = outlen;
        if (used > len)
@@ -424,6 +418,7 @@ static void do_cmd(char *host, char *user, char *cmd)
     cfg.x11_forward = 0;
     cfg.agentfwd = 0;
     cfg.portfwd[0] = cfg.portfwd[1] = '\0';
+    cfg.ssh_simple = TRUE;
 
     /*
      * Set up main and possibly fallback command depending on
diff --git a/psftp.c b/psftp.c
index f77a88ed78c33bedd6092e64e315c4289bc12e1a..990bc07486655fe96eb854627c6cd7ad416dbf2a 100644 (file)
--- a/psftp.c
+++ b/psftp.c
@@ -321,22 +321,24 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
             * If none of them exists, of course, we start at 0.
             */
            i = 0;
-           while (i < nnames) {
-               char *nextoutfname;
-               int ret;
-               if (outfname)
-                   nextoutfname = dir_file_cat(outfname,
-                                               ournames[i]->filename);
-               else
-                   nextoutfname = dupstr(ournames[i]->filename);
-               ret = (file_type(nextoutfname) == FILE_TYPE_NONEXISTENT);
-               sfree(nextoutfname);
-               if (ret)
-                   break;
-               i++;
-           }
-           if (i > 0)
-               i--;
+            if (restart) {
+                while (i < nnames) {
+                    char *nextoutfname;
+                    int ret;
+                    if (outfname)
+                        nextoutfname = dir_file_cat(outfname,
+                                                    ournames[i]->filename);
+                    else
+                        nextoutfname = dupstr(ournames[i]->filename);
+                    ret = (file_type(nextoutfname) == FILE_TYPE_NONEXISTENT);
+                    sfree(nextoutfname);
+                    if (ret)
+                        break;
+                    i++;
+                }
+                if (i > 0)
+                    i--;
+            }
 
            /*
             * Now we're ready to recurse. Starting at ournames[i]
@@ -461,6 +463,7 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
                    printf("error while writing local file\n");
                    ret = 0;
                    xfer_set_error(xfer);
+                   break;
                }
                wpos += wlen;
            }
@@ -568,23 +571,25 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart)
         * If none of them exists, of course, we start at 0.
         */
        i = 0;
-       while (i < nnames) {
-           char *nextoutfname;
-           nextoutfname = dupcat(outfname, "/", ournames[i], NULL);
-           sftp_register(req = fxp_stat_send(nextoutfname));
-           rreq = sftp_find_request(pktin = sftp_recv());
-           assert(rreq == req);
-           result = fxp_stat_recv(pktin, rreq, &attrs);
-           sfree(nextoutfname);
-           if (!result)
-               break;
-           i++;
-       }
-       if (i > 0)
-           i--;
+        if (restart) {
+            while (i < nnames) {
+                char *nextoutfname;
+                nextoutfname = dupcat(outfname, "/", ournames[i], NULL);
+                sftp_register(req = fxp_stat_send(nextoutfname));
+                rreq = sftp_find_request(pktin = sftp_recv());
+                assert(rreq == req);
+                result = fxp_stat_recv(pktin, rreq, &attrs);
+                sfree(nextoutfname);
+                if (!result)
+                    break;
+                i++;
+            }
+            if (i > 0)
+                i--;
+        }
 
-       /*
-        * Now we're ready to recurse. Starting at ournames[i]
+        /*
+         * Now we're ready to recurse. Starting at ournames[i]
         * and continuing on to the end of the list, we
         * construct a new source and target file name, and
         * call sftp_put_file again.
@@ -2747,6 +2752,7 @@ static int psftp_connect(char *userhost, char *user, int portnumber)
     cfg.x11_forward = 0;
     cfg.agentfwd = 0;
     cfg.portfwd[0] = cfg.portfwd[1] = '\0';
+    cfg.ssh_simple = TRUE;
 
     /* Set up subsystem name. */
     strcpy(cfg.remote_cmd, "sftp");
diff --git a/putty.h b/putty.h
index b003652c36e75e4baed0c1c95c20e1a09168dbd8..8f6461e65fb349fef51f594e82a720361403a223 100644 (file)
--- a/putty.h
+++ b/putty.h
@@ -252,6 +252,7 @@ enum {
     KEX_DHGROUP1,
     KEX_DHGROUP14,
     KEX_DHGEX,
+    KEX_RSA,
     KEX_MAX
 };
 
@@ -388,14 +389,12 @@ struct backend_tag {
      */
     void (*unthrottle) (void *handle, int);
     int (*cfg_info) (void *handle);
+    char *name;
+    int protocol;
     int default_port;
 };
 
-extern struct backend_list {
-    int protocol;
-    char *name;
-    Backend *backend;
-} backends[];
+extern Backend *backends[];
 
 /*
  * Suggested default protocol provided by the backend link module.
@@ -588,7 +587,13 @@ struct config_tag {
     /* SSH bug compatibility modes */
     int sshbug_ignore1, sshbug_plainpw1, sshbug_rsa1,
        sshbug_hmac2, sshbug_derivekey2, sshbug_rsapad2,
-       sshbug_pksessid2, sshbug_rekey2;
+       sshbug_pksessid2, sshbug_rekey2, sshbug_maxpkt2;
+    /*
+     * ssh_simple means that we promise never to open any channel other
+     * than the main one, which means it can safely use a very large
+     * window in SSH-2.
+     */
+    int ssh_simple;
     /* Options for pterm. Should split out into platform-dependent part. */
     int stamp_utmp;
     int login_shell;
@@ -598,6 +603,7 @@ struct config_tag {
     FontSpec widefont;
     FontSpec wideboldfont;
     int shadowboldoffset;
+    int crhaslf;
 };
 
 /*
@@ -777,10 +783,12 @@ void random_destroy_seed(void);
 /*
  * Exports from settings.c.
  */
-char *save_settings(char *section, int do_host, Config * cfg);
-void save_open_settings(void *sesskey, int do_host, Config *cfg);
-void load_settings(char *section, int do_host, Config * cfg);
-void load_open_settings(void *sesskey, int do_host, Config *cfg);
+Backend *backend_from_name(const char *name);
+Backend *backend_from_proto(int proto);
+char *save_settings(char *section, Config * cfg);
+void save_open_settings(void *sesskey, Config *cfg);
+void load_settings(char *section, Config * cfg);
+void load_open_settings(void *sesskey, Config *cfg);
 void get_sesslist(struct sesslist *, int allocate);
 void do_defaults(char *, Config *);
 void registry_cleanup(void);
diff --git a/raw.c b/raw.c
index d64b6d7f7a1e44528ac77e50bfc477f5b3a4a1d3..b4b1108f96c609ed47b527f01b034f71ba861c55 100644 (file)
--- a/raw.c
+++ b/raw.c
@@ -278,5 +278,7 @@ Backend raw_backend = {
     raw_provide_logctx,
     raw_unthrottle,
     raw_cfg_info,
-    1
+    "raw",
+    PROT_RAW,
+    0
 };
index 833d4ea4db39005127e110601c55b47030414156..e40f1597da65489bc19c3518a81780fe6c0e6b05 100644 (file)
--- a/rlogin.c
+++ b/rlogin.c
@@ -349,5 +349,7 @@ Backend rlogin_backend = {
     rlogin_provide_logctx,
     rlogin_unthrottle,
     rlogin_cfg_info,
-    1
+    "rlogin",
+    PROT_RLOGIN,
+    513
 };
index 752953e571fb5132c3fe38ec8e2473133d650654..cde0a90d86f6fa66a3bfe90ab362f0731e46b699 100644 (file)
--- a/sercfg.c
+++ b/sercfg.c
@@ -104,16 +104,18 @@ void ser_setup_config_box(struct controlbox *b, int midsession,
     struct controlset *s;
     union control *c;
 
-    /*
-     * Add the serial back end to the protocols list at the top of
-     * the config box.
-     */
-    s = ctrl_getset(b, "Session", "hostport",
-                   "Specify your connection by host name or IP address");
-    {
+    if (!midsession) {
        int i;
        extern void config_protocolbuttons_handler(union control *, void *,
                                                   void *, int);
+
+       /*
+        * Add the serial back end to the protocols list at the
+        * top of the config box.
+        */
+       s = ctrl_getset(b, "Session", "hostport",
+                       "Specify the destination you want to connect to");
+
         for (i = 0; i < s->ncontrols; i++) {
             c = s->ctrls[i];
            if (c->generic.type == CTRL_RADIO &&
index 0d3798aa46fbdfe57c8fad07da0043c41a084e79..ab2dc925129589a470ff695fd536ab5d6ea59cde 100644 (file)
@@ -27,6 +27,7 @@ static const struct keyval kexnames[] = {
     { "dh-gex-sha1",       KEX_DHGEX },
     { "dh-group14-sha1",    KEX_DHGROUP14 },
     { "dh-group1-sha1",            KEX_DHGROUP1 },
+    { "rsa",               KEX_RSA },
     { "WARN",              KEX_WARN }
 };
 
@@ -51,6 +52,29 @@ const char *const ttymodes[] = {
     "CS8",     "PARENB",   "PARODD",   NULL
 };
 
+/*
+ * Convenience functions to access the backends[] array
+ * (which is only present in tools that manage settings).
+ */
+
+Backend *backend_from_name(const char *name)
+{
+    Backend **p;
+    for (p = backends; *p != NULL; p++)
+       if (!strcmp((*p)->name, name))
+           return *p;
+    return NULL;
+}
+
+Backend *backend_from_proto(int proto)
+{
+    Backend **p;
+    for (p = backends; *p != NULL; p++)
+       if ((*p)->protocol == proto)
+           return *p;
+    return NULL;
+}
+
 static void gpps(void *handle, const char *name, const char *def,
                 char *val, int len)
 {
@@ -231,7 +255,7 @@ static void wprefs(void *sesskey, char *name,
     write_setting_s(sesskey, name, buf);
 }
 
-char *save_settings(char *section, int do_host, Config * cfg)
+char *save_settings(char *section, Config * cfg)
 {
     void *sesskey;
     char *errmsg;
@@ -239,20 +263,18 @@ char *save_settings(char *section, int do_host, Config * cfg)
     sesskey = open_settings_w(section, &errmsg);
     if (!sesskey)
        return errmsg;
-    save_open_settings(sesskey, do_host, cfg);
+    save_open_settings(sesskey, cfg);
     close_settings_w(sesskey);
     return NULL;
 }
 
-void save_open_settings(void *sesskey, int do_host, Config *cfg)
+void save_open_settings(void *sesskey, Config *cfg)
 {
     int i;
     char *p;
 
     write_setting_i(sesskey, "Present", 1);
-    if (do_host) {
-       write_setting_s(sesskey, "HostName", cfg->host);
-    }
+    write_setting_s(sesskey, "HostName", cfg->host);
     write_setting_filename(sesskey, "LogFileName", cfg->logfilename);
     write_setting_i(sesskey, "LogType", cfg->logtype);
     write_setting_i(sesskey, "LogFileClash", cfg->logxfovr);
@@ -260,11 +282,11 @@ void save_open_settings(void *sesskey, int do_host, Config *cfg)
     write_setting_i(sesskey, "SSHLogOmitPasswords", cfg->logomitpass);
     write_setting_i(sesskey, "SSHLogOmitData", cfg->logomitdata);
     p = "raw";
-    for (i = 0; backends[i].name != NULL; i++)
-       if (backends[i].protocol == cfg->protocol) {
-           p = backends[i].name;
-           break;
-       }
+    {
+       const Backend *b = backend_from_proto(cfg->protocol);
+       if (b)
+           p = b->name;
+    }
     write_setting_s(sesskey, "Protocol", p);
     write_setting_i(sesskey, "PortNumber", cfg->port);
     /* The CloseOnExit numbers are arranged in a different order from
@@ -366,6 +388,7 @@ void save_open_settings(void *sesskey, int do_host, Config *cfg)
     write_setting_i(sesskey, "DECOriginMode", cfg->dec_om);
     write_setting_i(sesskey, "AutoWrapMode", cfg->wrap_mode);
     write_setting_i(sesskey, "LFImpliesCR", cfg->lfhascr);
+    write_setting_i(sesskey, "CRImpliesLF", cfg->crhaslf);
     write_setting_i(sesskey, "DisableArabicShaping", cfg->arabicshaping);
     write_setting_i(sesskey, "DisableBidi", cfg->bidi);
     write_setting_i(sesskey, "WinNameAlways", cfg->win_name_always);
@@ -431,6 +454,7 @@ void save_open_settings(void *sesskey, int do_host, Config *cfg)
     write_setting_i(sesskey, "BugRSAPad2", 2-cfg->sshbug_rsapad2);
     write_setting_i(sesskey, "BugPKSessID2", 2-cfg->sshbug_pksessid2);
     write_setting_i(sesskey, "BugRekey2", 2-cfg->sshbug_rekey2);
+    write_setting_i(sesskey, "BugMaxPkt2", 2-cfg->sshbug_maxpkt2);
     write_setting_i(sesskey, "StampUtmp", cfg->stamp_utmp);
     write_setting_i(sesskey, "LoginShell", cfg->login_shell);
     write_setting_i(sesskey, "ScrollbarOnLeft", cfg->scrollbar_on_left);
@@ -447,16 +471,16 @@ void save_open_settings(void *sesskey, int do_host, Config *cfg)
     write_setting_i(sesskey, "SerialFlowControl", cfg->serflow);
 }
 
-void load_settings(char *section, int do_host, Config * cfg)
+void load_settings(char *section, Config * cfg)
 {
     void *sesskey;
 
     sesskey = open_settings_r(section);
-    load_open_settings(sesskey, do_host, cfg);
+    load_open_settings(sesskey, cfg);
     close_settings_r(sesskey);
 }
 
-void load_open_settings(void *sesskey, int do_host, Config *cfg)
+void load_open_settings(void *sesskey, Config *cfg)
 {
     int i;
     char prot[10];
@@ -466,11 +490,7 @@ void load_open_settings(void *sesskey, int do_host, Config *cfg)
     cfg->remote_cmd_ptr2 = NULL;
     cfg->ssh_nc_host[0] = '\0';
 
-    if (do_host) {
-       gpps(sesskey, "HostName", "", cfg->host, sizeof(cfg->host));
-    } else {
-       cfg->host[0] = '\0';           /* blank hostname */
-    }
+    gpps(sesskey, "HostName", "", cfg->host, sizeof(cfg->host));
     gppfile(sesskey, "LogFileName", &cfg->logfilename);
     gppi(sesskey, "LogType", 0, &cfg->logtype);
     gppi(sesskey, "LogFileClash", LGXF_ASK, &cfg->logxfovr);
@@ -481,12 +501,13 @@ void load_open_settings(void *sesskey, int do_host, Config *cfg)
     gpps(sesskey, "Protocol", "default", prot, 10);
     cfg->protocol = default_protocol;
     cfg->port = default_port;
-    for (i = 0; backends[i].name != NULL; i++)
-       if (!strcmp(prot, backends[i].name)) {
-           cfg->protocol = backends[i].protocol;
+    {
+       const Backend *b = backend_from_name(prot);
+       if (b) {
+           cfg->protocol = b->protocol;
            gppi(sesskey, "PortNumber", default_port, &cfg->port);
-           break;
        }
+    }
 
     /* Address family selection */
     gppi(sesskey, "AddressFamily", ADDRTYPE_UNSPEC, &cfg->addressfamily);
@@ -577,9 +598,9 @@ void load_open_settings(void *sesskey, int do_host, Config *cfg)
        char *default_kexes;
        gppi(sesskey, "BugDHGEx2", 0, &i); i = 2-i;
        if (i == FORCE_ON)
-           default_kexes = "dh-group14-sha1,dh-group1-sha1,WARN,dh-gex-sha1";
+           default_kexes = "dh-group14-sha1,dh-group1-sha1,rsa,WARN,dh-gex-sha1";
        else
-           default_kexes = "dh-gex-sha1,dh-group14-sha1,dh-group1-sha1,WARN";
+           default_kexes = "dh-gex-sha1,dh-group14-sha1,dh-group1-sha1,rsa,WARN";
        gprefs(sesskey, "KEX", default_kexes,
               kexnames, KEX_MAX, cfg->ssh_kexlist);
     }
@@ -662,6 +683,7 @@ void load_open_settings(void *sesskey, int do_host, Config *cfg)
     gppi(sesskey, "DECOriginMode", 0, &cfg->dec_om);
     gppi(sesskey, "AutoWrapMode", 1, &cfg->wrap_mode);
     gppi(sesskey, "LFImpliesCR", 0, &cfg->lfhascr);
+    gppi(sesskey, "CRImpliesLF", 0, &cfg->crhaslf);
     gppi(sesskey, "DisableArabicShaping", 0, &cfg->arabicshaping);
     gppi(sesskey, "DisableBidi", 0, &cfg->bidi);
     gppi(sesskey, "WinNameAlways", 1, &cfg->win_name_always);
@@ -767,6 +789,8 @@ void load_open_settings(void *sesskey, int do_host, Config *cfg)
     gppi(sesskey, "BugRSAPad2", 0, &i); cfg->sshbug_rsapad2 = 2-i;
     gppi(sesskey, "BugPKSessID2", 0, &i); cfg->sshbug_pksessid2 = 2-i;
     gppi(sesskey, "BugRekey2", 0, &i); cfg->sshbug_rekey2 = 2-i;
+    gppi(sesskey, "BugMaxPkt2", 0, &i); cfg->sshbug_maxpkt2 = 2-i;
+    cfg->ssh_simple = FALSE;
     gppi(sesskey, "StampUtmp", 1, &cfg->stamp_utmp);
     gppi(sesskey, "LoginShell", 1, &cfg->login_shell);
     gppi(sesskey, "ScrollbarOnLeft", 0, &cfg->scrollbar_on_left);
@@ -785,7 +809,7 @@ void load_open_settings(void *sesskey, int do_host, Config *cfg)
 
 void do_defaults(char *session, Config * cfg)
 {
-    load_settings(session, (session != NULL && *session), cfg);
+    load_settings(session, cfg);
 }
 
 static int sessioncmp(const void *av, const void *bv)
diff --git a/sign.sh b/sign.sh
new file mode 100755 (executable)
index 0000000..ac6a62c
--- /dev/null
+++ b/sign.sh
@@ -0,0 +1,29 @@
+#!/bin/sh 
+
+# Generate GPG signatures on a PuTTY release/snapshot directory as
+# delivered by Buildscr.
+
+# Usage: sign.sh <builddir> <keytype>
+# e.g. sign.sh build.out Snapshots
+#   or sign.sh 0.60 Releases
+
+set -e
+
+sign() {
+  # Check for the prior existence of the signature, so we can
+  # re-run this script if it encounters an error part way
+  # through.
+  echo "----- Signing $2 with '$keyname'"
+  test -f "$3" || \
+    gpg --load-extension=idea "$1" -u "$keyname" -o "$3" "$2"
+}
+
+cd "$1"
+for t in DSA RSA; do
+  keyname="$2 ($t)"
+  echo "===== Signing with '$keyname'"
+  for i in putty*src.zip putty*.tar.gz x86/*.exe x86/*.zip; do
+    sign --detach-sign "$i" "$i.$t"
+  done
+  sign --clearsign md5sums md5sums.$t
+done
diff --git a/ssh.c b/ssh.c
index 137e4607f1d01edc40b1b7f4d1945c0878e07467..eec05808948c8d402ad43e1ef846d26bc6fe5894 100644 (file)
--- a/ssh.c
+++ b/ssh.c
 #define SSH1_SMSG_AUTH_CCARD_CHALLENGE            71   /* 0x47 */
 #define SSH1_CMSG_AUTH_CCARD_RESPONSE             72   /* 0x48 */
 
+#define SSH1_AUTH_RHOSTS                          1    /* 0x1 */
+#define SSH1_AUTH_RSA                             2    /* 0x2 */
+#define SSH1_AUTH_PASSWORD                        3    /* 0x3 */
+#define SSH1_AUTH_RHOSTS_RSA                      4    /* 0x4 */
 #define SSH1_AUTH_TIS                             5    /* 0x5 */
 #define SSH1_AUTH_CCARD                           16   /* 0x10 */
 
@@ -83,6 +87,9 @@
 #define SSH2_MSG_KEX_DH_GEX_GROUP                 31   /* 0x1f */
 #define SSH2_MSG_KEX_DH_GEX_INIT                  32   /* 0x20 */
 #define SSH2_MSG_KEX_DH_GEX_REPLY                 33   /* 0x21 */
+#define SSH2_MSG_KEXRSA_PUBKEY                    30    /* 0x1e */
+#define SSH2_MSG_KEXRSA_SECRET                    31    /* 0x1f */
+#define SSH2_MSG_KEXRSA_DONE                      32    /* 0x20 */
 #define SSH2_MSG_USERAUTH_REQUEST                 50   /* 0x32 */
 #define SSH2_MSG_USERAUTH_FAILURE                 51   /* 0x33 */
 #define SSH2_MSG_USERAUTH_SUCCESS                 52   /* 0x34 */
  * Packet type contexts, so that ssh2_pkt_type can correctly decode
  * the ambiguous type numbers back into the correct type strings.
  */
-#define SSH2_PKTCTX_DHGROUP          0x0001
-#define SSH2_PKTCTX_DHGEX            0x0002
-#define SSH2_PKTCTX_KEX_MASK         0x000F
-#define SSH2_PKTCTX_PUBLICKEY        0x0010
-#define SSH2_PKTCTX_PASSWORD         0x0020
-#define SSH2_PKTCTX_KBDINTER         0x0040
-#define SSH2_PKTCTX_AUTH_MASK        0x00F0
+typedef enum {
+    SSH2_PKTCTX_NOKEX,
+    SSH2_PKTCTX_DHGROUP,
+    SSH2_PKTCTX_DHGEX,
+    SSH2_PKTCTX_RSAKEX
+} Pkt_KCtx;
+typedef enum {
+    SSH2_PKTCTX_NOAUTH,
+    SSH2_PKTCTX_PUBLICKEY,
+    SSH2_PKTCTX_PASSWORD,
+    SSH2_PKTCTX_KBDINTER
+} Pkt_ACtx;
 
 #define SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1  /* 0x1 */
 #define SSH2_DISCONNECT_PROTOCOL_ERROR            2    /* 0x2 */
@@ -171,11 +183,12 @@ static const char *const ssh2_disconnect_reasons[] = {
 #define BUG_SSH2_DERIVEKEY                       32
 #define BUG_SSH2_REKEY                           64
 #define BUG_SSH2_PK_SESSIONID                   128
+#define BUG_SSH2_MAXPKT                                256
 
 /*
  * Codes for terminal modes.
  * Most of these are the same in SSH-1 and SSH-2.
- * This list is derived from draft-ietf-secsh-connect-25 and
+ * This list is derived from RFC 4254 and
  * SSH-1 RFC-1.2.31.
  */
 static const struct {
@@ -277,7 +290,8 @@ static unsigned int ssh_tty_parse_boolean(char *s)
 }
 
 #define translate(x) if (type == x) return #x
-#define translatec(x,ctx) if (type == x && (pkt_ctx & ctx)) return #x
+#define translatek(x,ctx) if (type == x && (pkt_kctx == ctx)) return #x
+#define translatea(x,ctx) if (type == x && (pkt_actx == ctx)) return #x
 static char *ssh1_pkt_type(int type)
 {
     translate(SSH1_MSG_DISCONNECT);
@@ -323,7 +337,7 @@ static char *ssh1_pkt_type(int type)
     translate(SSH1_CMSG_AUTH_CCARD_RESPONSE);
     return "unknown";
 }
-static char *ssh2_pkt_type(int pkt_ctx, int type)
+static char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type)
 {
     translate(SSH2_MSG_DISCONNECT);
     translate(SSH2_MSG_IGNORE);
@@ -333,20 +347,23 @@ static char *ssh2_pkt_type(int pkt_ctx, int type)
     translate(SSH2_MSG_SERVICE_ACCEPT);
     translate(SSH2_MSG_KEXINIT);
     translate(SSH2_MSG_NEWKEYS);
-    translatec(SSH2_MSG_KEXDH_INIT, SSH2_PKTCTX_DHGROUP);
-    translatec(SSH2_MSG_KEXDH_REPLY, SSH2_PKTCTX_DHGROUP);
-    translatec(SSH2_MSG_KEX_DH_GEX_REQUEST, SSH2_PKTCTX_DHGEX);
-    translatec(SSH2_MSG_KEX_DH_GEX_GROUP, SSH2_PKTCTX_DHGEX);
-    translatec(SSH2_MSG_KEX_DH_GEX_INIT, SSH2_PKTCTX_DHGEX);
-    translatec(SSH2_MSG_KEX_DH_GEX_REPLY, SSH2_PKTCTX_DHGEX);
+    translatek(SSH2_MSG_KEXDH_INIT, SSH2_PKTCTX_DHGROUP);
+    translatek(SSH2_MSG_KEXDH_REPLY, SSH2_PKTCTX_DHGROUP);
+    translatek(SSH2_MSG_KEX_DH_GEX_REQUEST, SSH2_PKTCTX_DHGEX);
+    translatek(SSH2_MSG_KEX_DH_GEX_GROUP, SSH2_PKTCTX_DHGEX);
+    translatek(SSH2_MSG_KEX_DH_GEX_INIT, SSH2_PKTCTX_DHGEX);
+    translatek(SSH2_MSG_KEX_DH_GEX_REPLY, SSH2_PKTCTX_DHGEX);
+    translatek(SSH2_MSG_KEXRSA_PUBKEY, SSH2_PKTCTX_RSAKEX);
+    translatek(SSH2_MSG_KEXRSA_SECRET, SSH2_PKTCTX_RSAKEX);
+    translatek(SSH2_MSG_KEXRSA_DONE, SSH2_PKTCTX_RSAKEX);
     translate(SSH2_MSG_USERAUTH_REQUEST);
     translate(SSH2_MSG_USERAUTH_FAILURE);
     translate(SSH2_MSG_USERAUTH_SUCCESS);
     translate(SSH2_MSG_USERAUTH_BANNER);
-    translatec(SSH2_MSG_USERAUTH_PK_OK, SSH2_PKTCTX_PUBLICKEY);
-    translatec(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, SSH2_PKTCTX_PASSWORD);
-    translatec(SSH2_MSG_USERAUTH_INFO_REQUEST, SSH2_PKTCTX_KBDINTER);
-    translatec(SSH2_MSG_USERAUTH_INFO_RESPONSE, SSH2_PKTCTX_KBDINTER);
+    translatea(SSH2_MSG_USERAUTH_PK_OK, SSH2_PKTCTX_PUBLICKEY);
+    translatea(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, SSH2_PKTCTX_PASSWORD);
+    translatea(SSH2_MSG_USERAUTH_INFO_REQUEST, SSH2_PKTCTX_KBDINTER);
+    translatea(SSH2_MSG_USERAUTH_INFO_RESPONSE, SSH2_PKTCTX_KBDINTER);
     translate(SSH2_MSG_GLOBAL_REQUEST);
     translate(SSH2_MSG_REQUEST_SUCCESS);
     translate(SSH2_MSG_REQUEST_FAILURE);
@@ -458,11 +475,15 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
  * 
  *  - OUR_V2_WINSIZE is the maximum window size we present on SSH-2
  *    channels.
+ *
+ *  - OUR_V2_BIGWIN is the window size we advertise for the only
+ *    channel in a simple connection.  It must be <= INT_MAX.
  */
 
 #define SSH1_BUFFER_LIMIT 32768
 #define SSH_MAX_BACKLOG 32768
 #define OUR_V2_WINSIZE 16384
+#define OUR_V2_BIGWIN 0x7fffffff
 #define OUR_V2_MAXPKT 0x4000UL
 
 /* Maximum length of passwords/passphrases (arbitrary) */
@@ -512,6 +533,14 @@ enum {                                    /* channel types */
     CHAN_SOCKDATA_DORMANT             /* one the remote hasn't confirmed */
 };
 
+/*
+ * little structure to keep track of outstanding WINDOW_ADJUSTs
+ */
+struct winadj {
+    struct winadj *next;
+    unsigned size;
+};
+
 /*
  * 2-3-4 tree storing channels.
  */
@@ -532,14 +561,29 @@ struct ssh_channel {
      * A channel is completely finished with when all four bits are set.
      */
     int closes;
+    /*
+     * True if this channel is causing the underlying connection to be
+     * throttled.
+     */
+    int throttling_conn;
     union {
-       struct ssh1_data_channel {
-           int throttling;
-       } v1;
        struct ssh2_data_channel {
            bufchain outbuffer;
            unsigned remwindow, remmaxpkt;
-           unsigned locwindow;
+           /* locwindow is signed so we can cope with excess data. */
+           int locwindow, locmaxwin;
+           /*
+            * remlocwin is the amount of local window that we think
+            * the remote end had available to it after it sent the
+            * last data packet or window adjust ack.
+            */
+           int remlocwin;
+           /*
+            * These store the list of window adjusts that haven't
+            * been acked.
+            */
+           struct winadj *winadj_head, *winadj_tail;
+           enum { THROTTLED, UNTHROTTLING, UNTHROTTLED } throttle_state;
        } v2;
     } v;
     union {
@@ -646,7 +690,7 @@ static void ssh_special(void *handle, Telnet_Special);
 static int ssh2_try_send(struct ssh_channel *c);
 static void ssh2_add_channel_data(struct ssh_channel *c, char *buf, int len);
 static void ssh_throttle_all(Ssh ssh, int enable, int bufsize);
-static void ssh2_set_window(struct ssh_channel *c, unsigned newwin);
+static void ssh2_set_window(struct ssh_channel *c, int newwin);
 static int ssh_sendbuffer(void *handle);
 static int ssh_do_close(Ssh ssh, int notify_exit);
 static unsigned long ssh_pkt_getuint32(struct Packet *pkt);
@@ -763,12 +807,13 @@ struct ssh_tag {
 
     bufchain banner;   /* accumulates banners during do_ssh2_authconn */
 
-    int pkt_ctx;
+    Pkt_KCtx pkt_kctx;
+    Pkt_ACtx pkt_actx;
 
     void *x11auth;
 
     int version;
-    int v1_throttle_count;
+    int conn_throttle_count;
     int overall_bufsize;
     int throttled_all;
     int v1_stdout_throttling;
@@ -1208,9 +1253,9 @@ static struct Packet *ssh1_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
            /* "Session data" packets - omit the data field */
            if ((st->pktin->type == SSH1_SMSG_STDOUT_DATA) ||
                (st->pktin->type == SSH1_SMSG_STDERR_DATA)) {
-               do_blank = TRUE; blank_prefix = 0;
-           } else if (st->pktin->type == SSH1_MSG_CHANNEL_DATA) {
                do_blank = TRUE; blank_prefix = 4;
+           } else if (st->pktin->type == SSH1_MSG_CHANNEL_DATA) {
+               do_blank = TRUE; blank_prefix = 8;
            }
            if (do_blank) {
                blank.offset = blank_prefix;
@@ -1368,9 +1413,9 @@ static struct Packet *ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
            int do_blank = FALSE, blank_prefix = 0;
            /* "Session data" packets - omit the data field */
            if (st->pktin->type == SSH2_MSG_CHANNEL_DATA) {
-               do_blank = TRUE; blank_prefix = 4;
-           } else if (st->pktin->type == SSH2_MSG_CHANNEL_EXTENDED_DATA) {
                do_blank = TRUE; blank_prefix = 8;
+           } else if (st->pktin->type == SSH2_MSG_CHANNEL_EXTENDED_DATA) {
+               do_blank = TRUE; blank_prefix = 12;
            }
            if (do_blank) {
                blank.offset = blank_prefix;
@@ -1380,7 +1425,8 @@ static struct Packet *ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
            }
        }
        log_packet(ssh->logctx, PKT_INCOMING, st->pktin->type,
-                  ssh2_pkt_type(ssh->pkt_ctx, st->pktin->type),
+                  ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx,
+                                st->pktin->type),
                   st->pktin->data+6, st->pktin->length-6,
                   nblanks, &blank);
     }
@@ -1417,6 +1463,7 @@ static int s_wrpkt_prepare(Ssh ssh, struct Packet *pkt, int *offset_p)
        zlib_compress_block(ssh->cs_comp_ctx,
                            pkt->data + 12, pkt->length - 12,
                            &compblk, &complen);
+       ssh_pkt_ensure(pkt, complen + 2);   /* just in case it's got bigger */
        memcpy(pkt->data + 12, compblk, complen);
        sfree(compblk);
        pkt->length = complen + 12;
@@ -1445,7 +1492,8 @@ static int s_wrpkt_prepare(Ssh ssh, struct Packet *pkt, int *offset_p)
 
 static int s_write(Ssh ssh, void *data, int len)
 {
-    log_packet(ssh->logctx, PKT_OUTGOING, -1, NULL, data, len, 0, NULL);
+    if (ssh->logctx)
+       log_packet(ssh->logctx, PKT_OUTGOING, -1, NULL, data, len, 0, NULL);
     return sk_write(ssh->s, (char *)data, len);
 }
 
@@ -1726,7 +1774,7 @@ static int ssh2_pkt_construct(Ssh ssh, struct Packet *pkt)
 
     if (ssh->logctx)
        log_packet(ssh->logctx, PKT_OUTGOING, pkt->data[5],
-                  ssh2_pkt_type(ssh->pkt_ctx, pkt->data[5]),
+                  ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, pkt->data[5]),
                   pkt->body, pkt->length - (pkt->body - pkt->data),
                   pkt->nblanks, pkt->blanks);
     sfree(pkt->blanks); pkt->blanks = NULL;
@@ -1864,6 +1912,7 @@ static void ssh2_pkt_defer_noqueue(Ssh ssh, struct Packet *pkt, int noignore)
         * get encrypted with a known IV.
         */
        struct Packet *ipkt = ssh2_pkt_init(SSH2_MSG_IGNORE);
+       ssh2_pkt_addstring_start(ipkt);
        ssh2_pkt_defer_noqueue(ssh, ipkt, TRUE);
     }
     len = ssh2_pkt_construct(ssh, pkt);
@@ -2343,6 +2392,16 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
        ssh->remote_bugs |= BUG_SSH2_REKEY;
        logevent("We believe remote version has SSH-2 rekey bug");
     }
+
+    if (ssh->cfg.sshbug_maxpkt2 == FORCE_ON ||
+       (ssh->cfg.sshbug_maxpkt2 == AUTO &&
+        (wc_match("1.36_sshlib GlobalSCAPE", imp)))) {
+       /*
+        * This version ignores our makpkt and needs to be throttled.
+        */
+       ssh->remote_bugs |= BUG_SSH2_MAXPKT;
+       logevent("We believe remote version ignores SSH-2 maximum packet size");
+    }
 }
 
 /*
@@ -2368,6 +2427,47 @@ static void ssh_fix_verstring(char *str)
     }
 }
 
+/*
+ * Send an appropriate SSH version string.
+ */
+static void ssh_send_verstring(Ssh ssh, char *svers)
+{
+    char *verstring;
+
+    if (ssh->version == 2) {
+       /*
+        * Construct a v2 version string.
+        */
+       verstring = dupprintf("SSH-2.0-%s\015\012", sshver);
+    } else {
+       /*
+        * Construct a v1 version string.
+        */
+       verstring = dupprintf("SSH-%s-%s\012",
+                             (ssh_versioncmp(svers, "1.5") <= 0 ?
+                              svers : "1.5"),
+                             sshver);
+    }
+
+    ssh_fix_verstring(verstring);
+
+    if (ssh->version == 2) {
+       size_t len;
+       /*
+        * Record our version string.
+        */
+       len = strcspn(verstring, "\015\012");
+       ssh->v_c = snewn(len + 1, char);
+       memcpy(ssh->v_c, verstring, len);
+       ssh->v_c[len] = 0;
+    }
+
+    logeventf(ssh, "We claim version: %.*s",
+             strcspn(verstring, "\015\012"), verstring);
+    s_write(ssh, verstring, strlen(verstring));
+    sfree(verstring);
+}
+
 static int do_ssh_init(Ssh ssh, unsigned char c)
 {
     struct do_ssh_init_state {
@@ -2446,65 +2546,43 @@ static int do_ssh_init(Ssh ssh, unsigned char c)
        crStop(0);
     }
 
-    {
-        char *verstring;
+    if (s->proto2 && (ssh->cfg.sshprot >= 2 || !s->proto1))
+       ssh->version = 2;
+    else
+       ssh->version = 1;
 
-        if (s->proto2 && (ssh->cfg.sshprot >= 2 || !s->proto1)) {
-            /*
-             * Construct a v2 version string.
-             */
-            verstring = dupprintf("SSH-2.0-%s\015\012", sshver);
-            ssh->version = 2;
-        } else {
-            /*
-             * Construct a v1 version string.
-             */
-            verstring = dupprintf("SSH-%s-%s\012",
-                                  (ssh_versioncmp(s->version, "1.5") <= 0 ?
-                                   s->version : "1.5"),
-                                  sshver);
-            ssh->version = 1;
-        }
+    logeventf(ssh, "Using SSH protocol version %d", ssh->version);
 
-        ssh_fix_verstring(verstring);
+    /* Send the version string, if we haven't already */
+    if (ssh->cfg.sshprot != 3)
+       ssh_send_verstring(ssh, s->version);
 
-        if (ssh->version == 2) {
-           size_t len;
-            /*
-             * Hash our version string and their version string.
-             */
-           len = strcspn(verstring, "\015\012");
-           ssh->v_c = snewn(len + 1, char);
-           memcpy(ssh->v_c, verstring, len);
-           ssh->v_c[len] = 0;
-           len = strcspn(s->vstring, "\015\012");
-           ssh->v_s = snewn(len + 1, char);
-           memcpy(ssh->v_s, s->vstring, len);
-           ssh->v_s[len] = 0;
+    if (ssh->version == 2) {
+       size_t len;
+       /*
+        * Record their version string.
+        */
+       len = strcspn(s->vstring, "\015\012");
+       ssh->v_s = snewn(len + 1, char);
+       memcpy(ssh->v_s, s->vstring, len);
+       ssh->v_s[len] = 0;
            
-            /*
-             * Initialise SSH-2 protocol.
-             */
-            ssh->protocol = ssh2_protocol;
-            ssh2_protocol_setup(ssh);
-            ssh->s_rdpkt = ssh2_rdpkt;
-        } else {
-            /*
-             * Initialise SSH-1 protocol.
-             */
-            ssh->protocol = ssh1_protocol;
-            ssh1_protocol_setup(ssh);
-            ssh->s_rdpkt = ssh1_rdpkt;
-        }
-        logeventf(ssh, "We claim version: %.*s",
-                  strcspn(verstring, "\015\012"), verstring);
-       s_write(ssh, verstring, strlen(verstring));
-        sfree(verstring);
-       if (ssh->version == 2)
-           do_ssh2_transport(ssh, NULL, -1, NULL);
+       /*
+        * Initialise SSH-2 protocol.
+        */
+       ssh->protocol = ssh2_protocol;
+       ssh2_protocol_setup(ssh);
+       ssh->s_rdpkt = ssh2_rdpkt;
+    } else {
+       /*
+        * Initialise SSH-1 protocol.
+        */
+       ssh->protocol = ssh1_protocol;
+       ssh1_protocol_setup(ssh);
+       ssh->s_rdpkt = ssh1_rdpkt;
     }
-
-    logeventf(ssh, "Using SSH protocol version %d", ssh->version);
+    if (ssh->version == 2)
+       do_ssh2_transport(ssh, NULL, -1, NULL);
 
     update_specials_menu(ssh->frontend);
     ssh->state = SSH_STATE_BEFORE_SIZE;
@@ -2564,7 +2642,9 @@ static void ssh_set_frozen(Ssh ssh, int frozen)
 static void ssh_gotdata(Ssh ssh, unsigned char *data, int datalen)
 {
     /* Log raw data, if we're in that mode. */
-    log_packet(ssh->logctx, PKT_INCOMING, -1, NULL, data, datalen, 0, NULL);
+    if (ssh->logctx)
+       log_packet(ssh->logctx, PKT_INCOMING, -1, NULL, data, datalen,
+                  0, NULL);
 
     crBegin(ssh->ssh_gotdata_crstate);
 
@@ -2789,20 +2869,31 @@ static const char *connect_to_host(Ssh ssh, char *host, int port,
        return err;
     }
 
+    /*
+     * If the SSH version number's fixed, set it now, and if it's SSH-2,
+     * send the version string too.
+     */
+    if (ssh->cfg.sshprot == 0)
+       ssh->version = 1;
+    if (ssh->cfg.sshprot == 3) {
+       ssh->version = 2;
+       ssh_send_verstring(ssh, NULL);
+    }
+
     return NULL;
 }
 
 /*
  * Throttle or unthrottle the SSH connection.
  */
-static void ssh1_throttle(Ssh ssh, int adjust)
+static void ssh_throttle_conn(Ssh ssh, int adjust)
 {
-    int old_count = ssh->v1_throttle_count;
-    ssh->v1_throttle_count += adjust;
-    assert(ssh->v1_throttle_count >= 0);
-    if (ssh->v1_throttle_count && !old_count) {
+    int old_count = ssh->conn_throttle_count;
+    ssh->conn_throttle_count += adjust;
+    assert(ssh->conn_throttle_count >= 0);
+    if (ssh->conn_throttle_count && !old_count) {
        ssh_set_frozen(ssh, 1);
-    } else if (!ssh->v1_throttle_count && old_count) {
+    } else if (!ssh->conn_throttle_count && old_count) {
        ssh_set_frozen(ssh, 0);
     }
 }
@@ -2890,8 +2981,8 @@ static void ssh_agentf_callback(void *cv, void *reply, int replylen)
     } else {
        send_packet(ssh, SSH1_MSG_CHANNEL_DATA,
                    PKT_INT, c->remoteid,
-                   PKTT_DATA,
                    PKT_INT, replylen,
+                   PKTT_DATA,
                    PKT_DATA, sentreply, replylen,
                    PKTT_OTHER,
                    PKT_END);
@@ -3012,6 +3103,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
     ssh->v1_remote_protoflags = ssh_pkt_getuint32(pktin);
     s->supported_ciphers_mask = ssh_pkt_getuint32(pktin);
     s->supported_auths_mask = ssh_pkt_getuint32(pktin);
+    if ((ssh->remote_bugs & BUG_CHOKES_ON_RSA))
+       s->supported_auths_mask &= ~(1 << SSH1_AUTH_RSA);
 
     ssh->v1_local_protoflags =
        ssh->v1_remote_protoflags & SSH1_PROTOFLAGS_SUPPORTED;
@@ -3265,7 +3358,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
 
     crWaitUntil(pktin);
 
-    if ((ssh->remote_bugs & BUG_CHOKES_ON_RSA)) {
+    if ((s->supported_auths_mask & (1 << SSH1_AUTH_RSA)) == 0) {
        /* We must not attempt PK auth. Pretend we've already tried it. */
        s->tried_publickey = s->tried_agent = 1;
     } else {
@@ -3722,6 +3815,10 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
            }
        }
        if (s->pwpkt_type == SSH1_CMSG_AUTH_PASSWORD) {
+           if ((s->supported_auths_mask & (1 << SSH1_AUTH_PASSWORD)) == 0) {
+               bombout(("No supported authentication methods available"));
+               crStop(0);
+           }
            s->cur_prompt->to_server = TRUE;
            s->cur_prompt->name = dupstr("SSH password");
            add_prompt(s->cur_prompt, dupprintf("%.90s@%.90s's password: ",
@@ -3951,8 +4048,7 @@ int sshfwd_write(struct ssh_channel *c, char *buf, int len)
     if (ssh->version == 1) {
        send_packet(ssh, SSH1_MSG_CHANNEL_DATA,
                    PKT_INT, c->remoteid,
-                   PKTT_DATA,
-                   PKT_INT, len, PKT_DATA, buf, len,
+                   PKT_INT, len, PKTT_DATA, PKT_DATA, buf, len,
                    PKTT_OTHER, PKT_END);
        /*
         * In SSH-1 we can return 0 here - implying that forwarded
@@ -3971,17 +4067,20 @@ int sshfwd_write(struct ssh_channel *c, char *buf, int len)
 void sshfwd_unthrottle(struct ssh_channel *c, int bufsize)
 {
     Ssh ssh = c->ssh;
+    int buflimit;
 
     if (ssh->state == SSH_STATE_CLOSED)
        return;
 
     if (ssh->version == 1) {
-       if (c->v.v1.throttling && bufsize < SSH1_BUFFER_LIMIT) {
-           c->v.v1.throttling = 0;
-           ssh1_throttle(ssh, -1);
-       }
+       buflimit = SSH1_BUFFER_LIMIT;
     } else {
-       ssh2_set_window(c, OUR_V2_WINSIZE - bufsize);
+       buflimit = c->v.v2.locmaxwin;
+       ssh2_set_window(c, bufsize < buflimit ? buflimit - bufsize : 0);
+    }
+    if (c->throttling_conn && bufsize <= buflimit) {
+       c->throttling_conn = 0;
+       ssh_throttle_conn(ssh, -1);
     }
 }
 
@@ -4410,7 +4509,7 @@ static void ssh1_smsg_stdout_stderr_data(Ssh ssh, struct Packet *pktin)
                           string, stringlen);
     if (!ssh->v1_stdout_throttling && bufsize > SSH1_BUFFER_LIMIT) {
        ssh->v1_stdout_throttling = 1;
-       ssh1_throttle(ssh, +1);
+       ssh_throttle_conn(ssh, +1);
     }
 }
 
@@ -4444,7 +4543,7 @@ static void ssh1_smsg_x11_open(Ssh ssh, struct Packet *pktin)
            c->halfopen = FALSE;
            c->localid = alloc_channel_id(ssh);
            c->closes = 0;
-           c->v.v1.throttling = 0;
+           c->throttling_conn = 0;
            c->type = CHAN_X11; /* identify channel type */
            add234(ssh->channels, c);
            send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION,
@@ -4473,7 +4572,7 @@ static void ssh1_smsg_agent_open(Ssh ssh, struct Packet *pktin)
        c->halfopen = FALSE;
        c->localid = alloc_channel_id(ssh);
        c->closes = 0;
-       c->v.v1.throttling = 0;
+       c->throttling_conn = 0;
        c->type = CHAN_AGENT;   /* identify channel type */
        c->u.a.lensofar = 0;
        add234(ssh->channels, c);
@@ -4527,7 +4626,7 @@ static void ssh1_msg_port_open(Ssh ssh, struct Packet *pktin)
            c->halfopen = FALSE;
            c->localid = alloc_channel_id(ssh);
            c->closes = 0;
-           c->v.v1.throttling = 0;
+           c->throttling_conn = 0;
            c->type = CHAN_SOCKDATA;    /* identify channel type */
            add234(ssh->channels, c);
            send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION,
@@ -4549,7 +4648,7 @@ static void ssh1_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin)
        c->remoteid = localid;
        c->halfopen = FALSE;
        c->type = CHAN_SOCKDATA;
-       c->v.v1.throttling = 0;
+       c->throttling_conn = 0;
        pfd_confirm(c->u.pfd.s);
     }
 
@@ -4685,9 +4784,9 @@ static void ssh1_msg_channel_data(Ssh ssh, struct Packet *pktin)
            bufsize = 0;   /* agent channels never back up */
            break;
        }
-       if (!c->v.v1.throttling && bufsize > SSH1_BUFFER_LIMIT) {
-           c->v.v1.throttling = 1;
-           ssh1_throttle(ssh, +1);
+       if (!c->throttling_conn && bufsize > SSH1_BUFFER_LIMIT) {
+           c->throttling_conn = 1;
+           ssh_throttle_conn(ssh, +1);
        }
     }
 }
@@ -4923,8 +5022,8 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen,
        } else {
            while (inlen > 0) {
                int len = min(inlen, 512);
-               send_packet(ssh, SSH1_CMSG_STDIN_DATA, PKTT_DATA,
-                           PKT_INT, len, PKT_DATA, in, len,
+               send_packet(ssh, SSH1_CMSG_STDIN_DATA,
+                           PKT_INT, len,  PKTT_DATA, PKT_DATA, in, len,
                            PKTT_OTHER, PKT_END);
                in += len;
                inlen -= len;
@@ -5104,9 +5203,10 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
        const struct ssh_mac *scmac_tobe;
        const struct ssh_compress *cscomp_tobe;
        const struct ssh_compress *sccomp_tobe;
-       char *hostkeydata, *sigdata, *keystr, *fingerprint;
-       int hostkeylen, siglen;
+       char *hostkeydata, *sigdata, *rsakeydata, *keystr, *fingerprint;
+       int hostkeylen, siglen, rsakeylen;
        void *hkey;                    /* actual host key */
+       void *rsakey;                  /* for RSA kex */
        unsigned char exchange_hash[SSH2_KEX_MAX_HASH_LEN];
        int n_preferred_kex;
        const struct ssh_kexes *preferred_kex[KEX_MAX];
@@ -5138,7 +5238,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
        s->maclist = macs, s->nmacs = lenof(macs);
 
   begin_key_exchange:
-    ssh->pkt_ctx &= ~SSH2_PKTCTX_KEX_MASK;
+    ssh->pkt_kctx = SSH2_PKTCTX_NOKEX;
     {
        int i, j, commalist_started;
 
@@ -5160,6 +5260,10 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
                s->preferred_kex[s->n_preferred_kex++] =
                    &ssh_diffiehellman_group1;
                break;
+             case KEX_RSA:
+               s->preferred_kex[s->n_preferred_kex++] =
+                   &ssh_rsa_kex;
+               break;
              case KEX_WARN:
                /* Flag for later. Don't bother if it's the last in
                 * the list. */
@@ -5559,107 +5663,217 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
            crWaitUntil(pktin);                /* Ignore packet */
     }
 
-    /*
-     * Work out the number of bits of key we will need from the key
-     * exchange. We start with the maximum key length of either
-     * cipher...
-     */
-    {
-       int csbits, scbits;
+    if (ssh->kex->main_type == KEXTYPE_DH) {
+        /*
+         * Work out the number of bits of key we will need from the
+         * key exchange. We start with the maximum key length of
+         * either cipher...
+         */
+        {
+            int csbits, scbits;
 
-       csbits = s->cscipher_tobe->keylen;
-       scbits = s->sccipher_tobe->keylen;
-       s->nbits = (csbits > scbits ? csbits : scbits);
-    }
-    /* The keys only have hlen-bit entropy, since they're based on
-     * a hash. So cap the key size at hlen bits. */
-    if (s->nbits > ssh->kex->hash->hlen * 8)
-       s->nbits = ssh->kex->hash->hlen * 8;
+            csbits = s->cscipher_tobe->keylen;
+            scbits = s->sccipher_tobe->keylen;
+            s->nbits = (csbits > scbits ? csbits : scbits);
+        }
+        /* The keys only have hlen-bit entropy, since they're based on
+         * a hash. So cap the key size at hlen bits. */
+        if (s->nbits > ssh->kex->hash->hlen * 8)
+            s->nbits = ssh->kex->hash->hlen * 8;
 
-    /*
-     * If we're doing Diffie-Hellman group exchange, start by
-     * requesting a group.
-     */
-    if (!ssh->kex->pdata) {
-       logevent("Doing Diffie-Hellman group exchange");
-       ssh->pkt_ctx |= SSH2_PKTCTX_DHGEX;
-       /*
-        * Work out how big a DH group we will need to allow that
-        * much data.
-        */
-       s->pbits = 512 << ((s->nbits - 1) / 64);
-       s->pktout = ssh2_pkt_init(SSH2_MSG_KEX_DH_GEX_REQUEST);
-       ssh2_pkt_adduint32(s->pktout, s->pbits);
-       ssh2_pkt_send_noqueue(ssh, s->pktout);
+        /*
+         * If we're doing Diffie-Hellman group exchange, start by
+         * requesting a group.
+         */
+        if (!ssh->kex->pdata) {
+            logevent("Doing Diffie-Hellman group exchange");
+            ssh->pkt_kctx = SSH2_PKTCTX_DHGEX;
+            /*
+             * Work out how big a DH group we will need to allow that
+             * much data.
+             */
+            s->pbits = 512 << ((s->nbits - 1) / 64);
+            s->pktout = ssh2_pkt_init(SSH2_MSG_KEX_DH_GEX_REQUEST);
+            ssh2_pkt_adduint32(s->pktout, s->pbits);
+            ssh2_pkt_send_noqueue(ssh, s->pktout);
+
+            crWaitUntil(pktin);
+            if (pktin->type != SSH2_MSG_KEX_DH_GEX_GROUP) {
+                bombout(("expected key exchange group packet from server"));
+                crStop(0);
+            }
+            s->p = ssh2_pkt_getmp(pktin);
+            s->g = ssh2_pkt_getmp(pktin);
+            if (!s->p || !s->g) {
+                bombout(("unable to read mp-ints from incoming group packet"));
+                crStop(0);
+            }
+            ssh->kex_ctx = dh_setup_gex(s->p, s->g);
+            s->kex_init_value = SSH2_MSG_KEX_DH_GEX_INIT;
+            s->kex_reply_value = SSH2_MSG_KEX_DH_GEX_REPLY;
+        } else {
+            ssh->pkt_kctx = SSH2_PKTCTX_DHGROUP;
+            ssh->kex_ctx = dh_setup_group(ssh->kex);
+            s->kex_init_value = SSH2_MSG_KEXDH_INIT;
+            s->kex_reply_value = SSH2_MSG_KEXDH_REPLY;
+            logeventf(ssh, "Using Diffie-Hellman with standard group \"%s\"",
+                      ssh->kex->groupname);
+        }
 
-       crWaitUntil(pktin);
-       if (pktin->type != SSH2_MSG_KEX_DH_GEX_GROUP) {
-           bombout(("expected key exchange group packet from server"));
-           crStop(0);
-       }
-       s->p = ssh2_pkt_getmp(pktin);
-       s->g = ssh2_pkt_getmp(pktin);
-       if (!s->p || !s->g) {
-           bombout(("unable to read mp-ints from incoming group packet"));
-           crStop(0);
-       }
-       ssh->kex_ctx = dh_setup_gex(s->p, s->g);
-       s->kex_init_value = SSH2_MSG_KEX_DH_GEX_INIT;
-       s->kex_reply_value = SSH2_MSG_KEX_DH_GEX_REPLY;
+        logeventf(ssh, "Doing Diffie-Hellman key exchange with hash %s",
+                  ssh->kex->hash->text_name);
+        /*
+         * Now generate and send e for Diffie-Hellman.
+         */
+        set_busy_status(ssh->frontend, BUSY_CPU); /* this can take a while */
+        s->e = dh_create_e(ssh->kex_ctx, s->nbits * 2);
+        s->pktout = ssh2_pkt_init(s->kex_init_value);
+        ssh2_pkt_addmp(s->pktout, s->e);
+        ssh2_pkt_send_noqueue(ssh, s->pktout);
+
+        set_busy_status(ssh->frontend, BUSY_WAITING); /* wait for server */
+        crWaitUntil(pktin);
+        if (pktin->type != s->kex_reply_value) {
+            bombout(("expected key exchange reply packet from server"));
+            crStop(0);
+        }
+        set_busy_status(ssh->frontend, BUSY_CPU); /* cogitate */
+        ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
+        s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen);
+        s->f = ssh2_pkt_getmp(pktin);
+        if (!s->f) {
+            bombout(("unable to parse key exchange reply packet"));
+            crStop(0);
+        }
+        ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen);
+
+        s->K = dh_find_K(ssh->kex_ctx, s->f);
+
+        /* We assume everything from now on will be quick, and it might
+         * involve user interaction. */
+        set_busy_status(ssh->frontend, BUSY_NOT);
+
+        hash_string(ssh->kex->hash, ssh->exhash, s->hostkeydata, s->hostkeylen);
+        if (!ssh->kex->pdata) {
+            hash_uint32(ssh->kex->hash, ssh->exhash, s->pbits);
+            hash_mpint(ssh->kex->hash, ssh->exhash, s->p);
+            hash_mpint(ssh->kex->hash, ssh->exhash, s->g);
+        }
+        hash_mpint(ssh->kex->hash, ssh->exhash, s->e);
+        hash_mpint(ssh->kex->hash, ssh->exhash, s->f);
+
+        dh_cleanup(ssh->kex_ctx);
+        freebn(s->f);
+        if (!ssh->kex->pdata) {
+            freebn(s->g);
+            freebn(s->p);
+        }
     } else {
-       ssh->pkt_ctx |= SSH2_PKTCTX_DHGROUP;
-       ssh->kex_ctx = dh_setup_group(ssh->kex);
-       s->kex_init_value = SSH2_MSG_KEXDH_INIT;
-       s->kex_reply_value = SSH2_MSG_KEXDH_REPLY;
-       logeventf(ssh, "Using Diffie-Hellman with standard group \"%s\"",
-                 ssh->kex->groupname);
-    }
+       logeventf(ssh, "Doing RSA key exchange with hash %s",
+                 ssh->kex->hash->text_name);
+       ssh->pkt_kctx = SSH2_PKTCTX_RSAKEX;
+        /*
+         * RSA key exchange. First expect a KEXRSA_PUBKEY packet
+         * from the server.
+         */
+        crWaitUntil(pktin);
+        if (pktin->type != SSH2_MSG_KEXRSA_PUBKEY) {
+            bombout(("expected RSA public key packet from server"));
+            crStop(0);
+        }
 
-    logeventf(ssh, "Doing Diffie-Hellman key exchange with hash %s",
-             ssh->kex->hash->text_name);
-    /*
-     * Now generate and send e for Diffie-Hellman.
-     */
-    set_busy_status(ssh->frontend, BUSY_CPU); /* this can take a while */
-    s->e = dh_create_e(ssh->kex_ctx, s->nbits * 2);
-    s->pktout = ssh2_pkt_init(s->kex_init_value);
-    ssh2_pkt_addmp(s->pktout, s->e);
-    ssh2_pkt_send_noqueue(ssh, s->pktout);
+        ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
+        hash_string(ssh->kex->hash, ssh->exhash,
+                   s->hostkeydata, s->hostkeylen);
+       s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen);
 
-    set_busy_status(ssh->frontend, BUSY_WAITING); /* wait for server */
-    crWaitUntil(pktin);
-    if (pktin->type != s->kex_reply_value) {
-       bombout(("expected key exchange reply packet from server"));
-       crStop(0);
-    }
-    set_busy_status(ssh->frontend, BUSY_CPU); /* cogitate */
-    ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
-    s->f = ssh2_pkt_getmp(pktin);
-    if (!s->f) {
-       bombout(("unable to parse key exchange reply packet"));
-       crStop(0);
-    }
-    ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen);
+        {
+            char *keydata;
+            ssh_pkt_getstring(pktin, &keydata, &s->rsakeylen);
+            s->rsakeydata = snewn(s->rsakeylen, char);
+            memcpy(s->rsakeydata, keydata, s->rsakeylen);
+        }
+
+        s->rsakey = ssh_rsakex_newkey(s->rsakeydata, s->rsakeylen);
+        if (!s->rsakey) {
+            sfree(s->rsakeydata);
+            bombout(("unable to parse RSA public key from server"));
+            crStop(0);
+        }
 
-    s->K = dh_find_K(ssh->kex_ctx, s->f);
+        hash_string(ssh->kex->hash, ssh->exhash, s->rsakeydata, s->rsakeylen);
 
-    /* We assume everything from now on will be quick, and it might
-     * involve user interaction. */
-    set_busy_status(ssh->frontend, BUSY_NOT);
+        /*
+         * Next, set up a shared secret K, of precisely KLEN -
+         * 2*HLEN - 49 bits, where KLEN is the bit length of the
+         * RSA key modulus and HLEN is the bit length of the hash
+         * we're using.
+         */
+        {
+            int klen = ssh_rsakex_klen(s->rsakey);
+            int nbits = klen - (2*ssh->kex->hash->hlen*8 + 49);
+            int i, byte = 0;
+            unsigned char *kstr1, *kstr2, *outstr;
+            int kstr1len, kstr2len, outstrlen;
+
+            s->K = bn_power_2(nbits - 1);
+
+            for (i = 0; i < nbits; i++) {
+                if ((i & 7) == 0) {
+                    byte = random_byte();
+                }
+                bignum_set_bit(s->K, i, (byte >> (i & 7)) & 1);
+            }
 
-    hash_string(ssh->kex->hash, ssh->exhash, s->hostkeydata, s->hostkeylen);
-    if (!ssh->kex->pdata) {
-       hash_uint32(ssh->kex->hash, ssh->exhash, s->pbits);
-       hash_mpint(ssh->kex->hash, ssh->exhash, s->p);
-       hash_mpint(ssh->kex->hash, ssh->exhash, s->g);
+            /*
+             * Encode this as an mpint.
+             */
+            kstr1 = ssh2_mpint_fmt(s->K, &kstr1len);
+            kstr2 = snewn(kstr2len = 4 + kstr1len, unsigned char);
+            PUT_32BIT(kstr2, kstr1len);
+            memcpy(kstr2 + 4, kstr1, kstr1len);
+
+            /*
+             * Encrypt it with the given RSA key.
+             */
+            outstrlen = (klen + 7) / 8;
+            outstr = snewn(outstrlen, unsigned char);
+            ssh_rsakex_encrypt(ssh->kex->hash, kstr2, kstr2len,
+                              outstr, outstrlen, s->rsakey);
+
+            /*
+             * And send it off in a return packet.
+             */
+            s->pktout = ssh2_pkt_init(SSH2_MSG_KEXRSA_SECRET);
+            ssh2_pkt_addstring_start(s->pktout);
+            ssh2_pkt_addstring_data(s->pktout, (char *)outstr, outstrlen);
+            ssh2_pkt_send_noqueue(ssh, s->pktout);
+
+           hash_string(ssh->kex->hash, ssh->exhash, outstr, outstrlen);
+
+            sfree(kstr2);
+            sfree(kstr1);
+            sfree(outstr);
+        }
+
+        ssh_rsakex_freekey(s->rsakey);
+
+        crWaitUntil(pktin);
+        if (pktin->type != SSH2_MSG_KEXRSA_DONE) {
+            sfree(s->rsakeydata);
+            bombout(("expected signature packet from server"));
+            crStop(0);
+        }
+
+        ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen);
+
+        sfree(s->rsakeydata);
     }
-    hash_mpint(ssh->kex->hash, ssh->exhash, s->e);
-    hash_mpint(ssh->kex->hash, ssh->exhash, s->f);
+
     hash_mpint(ssh->kex->hash, ssh->exhash, s->K);
     assert(ssh->kex->hash->hlen <= sizeof(s->exchange_hash));
     ssh->kex->hash->final(ssh->exhash, s->exchange_hash);
 
-    dh_cleanup(ssh->kex_ctx);
     ssh->kex_ctx = NULL;
 
 #if 0
@@ -5667,7 +5881,6 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
     dmemdump(s->exchange_hash, ssh->kex->hash->hlen);
 #endif
 
-    s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen);
     if (!s->hkey ||
        !ssh->hostkey->verifysig(s->hkey, s->sigdata, s->siglen,
                                 (char *)s->exchange_hash,
@@ -5849,14 +6062,9 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
                  ssh->sccomp->text_name);
 
     /*
-     * Free key exchange data.
+     * Free shared secret.
      */
-    freebn(s->f);
     freebn(s->K);
-    if (!ssh->kex->pdata) {
-       freebn(s->g);
-       freebn(s->p);
-    }
 
     /*
      * Key exchange is over. Loop straight back round if we have a
@@ -5965,8 +6173,8 @@ static int ssh2_try_send(struct ssh_channel *c)
            len = c->v.v2.remmaxpkt;
        pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_DATA);
        ssh2_pkt_adduint32(pktout, c->remoteid);
-       dont_log_data(ssh, pktout, PKTLOG_OMIT);
        ssh2_pkt_addstring_start(pktout);
+       dont_log_data(ssh, pktout, PKTLOG_OMIT);
        ssh2_pkt_addstring_data(pktout, data, len);
        end_log_omission(ssh, pktout);
        ssh2_pkt_send(ssh, pktout);
@@ -6007,10 +6215,26 @@ static void ssh2_try_send_and_unthrottle(struct ssh_channel *c)
     }
 }
 
+/*
+ * Set up most of a new ssh_channel for SSH-2.
+ */
+static void ssh2_channel_init(struct ssh_channel *c)
+{
+    Ssh ssh = c->ssh;
+    c->localid = alloc_channel_id(ssh);
+    c->closes = 0;
+    c->throttling_conn = FALSE;
+    c->v.v2.locwindow = c->v.v2.locmaxwin = c->v.v2.remlocwin =
+       ssh->cfg.ssh_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE;
+    c->v.v2.winadj_head = c->v.v2.winadj_tail = NULL;
+    c->v.v2.throttle_state = UNTHROTTLED;
+    bufchain_init(&c->v.v2.outbuffer);
+}
+
 /*
  * Potentially enlarge the window on an SSH-2 channel.
  */
-static void ssh2_set_window(struct ssh_channel *c, unsigned newwin)
+static void ssh2_set_window(struct ssh_channel *c, int newwin)
 {
     Ssh ssh = c->ssh;
 
@@ -6022,6 +6246,15 @@ static void ssh2_set_window(struct ssh_channel *c, unsigned newwin)
     if (c->closes != 0)
        return;
 
+    /*
+     * If the remote end has a habit of ignoring maxpkt, limit the
+     * window so that it has no choice (assuming it doesn't ignore the
+     * window as well).
+     */
+    if ((ssh->remote_bugs & BUG_SSH2_MAXPKT) && newwin > OUR_V2_MAXPKT)
+       newwin = OUR_V2_MAXPKT;
+       
+
     /*
      * Only send a WINDOW_ADJUST if there's significantly more window
      * available than the other end thinks there is.  This saves us
@@ -6029,9 +6262,53 @@ static void ssh2_set_window(struct ssh_channel *c, unsigned newwin)
      *
      * "Significant" is arbitrarily defined as half the window size.
      */
-    if (newwin > c->v.v2.locwindow * 2) {
+    if (newwin / 2 >= c->v.v2.locwindow) {
        struct Packet *pktout;
+       struct winadj *wa;
+
+       /*
+        * In order to keep track of how much window the client
+        * actually has available, we'd like it to acknowledge each
+        * WINDOW_ADJUST.  We can't do that directly, so we accompany
+        * it with a CHANNEL_REQUEST that has to be acknowledged.
+        *
+        * This is only necessary if we're opening the window wide.
+        * If we're not, then throughput is being constrained by
+        * something other than the maximum window size anyway.
+        *
+        * We also only send this if the main channel has finished its
+        * initial CHANNEL_REQUESTs and installed the default
+        * CHANNEL_FAILURE handler, so as not to risk giving it
+        * unexpected CHANNEL_FAILUREs.
+        */
+       if (newwin == c->v.v2.locmaxwin &&
+           ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE]) {
+           pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
+           ssh2_pkt_adduint32(pktout, c->remoteid);
+           ssh2_pkt_addstring(pktout, "winadj@putty.projects.tartarus.org");
+           ssh2_pkt_addbool(pktout, TRUE);
+           ssh2_pkt_send(ssh, pktout);
 
+           /*
+            * CHANNEL_FAILURE doesn't come with any indication of
+            * what message caused it, so we have to keep track of the
+            * outstanding CHANNEL_REQUESTs ourselves.
+            */
+           wa = snew(struct winadj);
+           wa->size = newwin - c->v.v2.locwindow;
+           wa->next = NULL;
+           if (!c->v.v2.winadj_head)
+               c->v.v2.winadj_head = wa;
+           else
+               c->v.v2.winadj_tail->next = wa;
+           c->v.v2.winadj_tail = wa;
+           if (c->v.v2.throttle_state != UNTHROTTLED)
+               c->v.v2.throttle_state = UNTHROTTLING;
+       } else {
+           /* Pretend the WINDOW_ADJUST was acked immediately. */
+           c->v.v2.remlocwin = newwin;
+           c->v.v2.throttle_state = THROTTLED;
+       }
        pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
        ssh2_pkt_adduint32(pktout, c->remoteid);
        ssh2_pkt_adduint32(pktout, newwin - c->v.v2.locwindow);
@@ -6040,12 +6317,94 @@ static void ssh2_set_window(struct ssh_channel *c, unsigned newwin)
     }
 }
 
+/*
+ * Find the channel associated with a message.  If there's no channel,
+ * or it's not properly open, make a noise about it and return NULL.
+ */
+static struct ssh_channel *ssh2_channel_msg(Ssh ssh, struct Packet *pktin)
+{
+    unsigned localid = ssh_pkt_getuint32(pktin);
+    struct ssh_channel *c;
+
+    c = find234(ssh->channels, &localid, ssh_channelfind);
+    if (!c ||
+       (c->halfopen && pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION &&
+        pktin->type != SSH2_MSG_CHANNEL_OPEN_FAILURE)) {
+       char *buf = dupprintf("Received %s for %s channel %u",
+                             ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx,
+                                           pktin->type),
+                             c ? "half-open" : "nonexistent", localid);
+       ssh_disconnect(ssh, NULL, buf, SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE);
+       sfree(buf);
+       return NULL;
+    }
+    return c;
+}
+
+static void ssh2_msg_channel_success(Ssh ssh, struct Packet *pktin)
+{
+    /*
+     * This should never get called.  All channel requests are either
+     * sent with want_reply false or are sent before this handler gets
+     * installed.
+     */
+    struct ssh_channel *c;
+    struct winadj *wa;
+
+    c = ssh2_channel_msg(ssh, pktin);
+    if (!c)
+       return;
+    wa = c->v.v2.winadj_head;
+    if (wa)
+       ssh_disconnect(ssh, NULL, "Received SSH_MSG_CHANNEL_SUCCESS for "
+                      "\"winadj@putty.projects.tartarus.org\"",
+                      SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE);
+    else
+       ssh_disconnect(ssh, NULL,
+                      "Received unsolicited SSH_MSG_CHANNEL_SUCCESS",
+                      SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE);
+}
+
+static void ssh2_msg_channel_failure(Ssh ssh, struct Packet *pktin)
+{
+    /*
+     * The only time this should get called is for "winadj@putty"
+     * messages sent above.  All other channel requests are either
+     * sent with want_reply false or are sent before this handler gets
+     * installed.
+     */
+    struct ssh_channel *c;
+    struct winadj *wa;
+
+    c = ssh2_channel_msg(ssh, pktin);
+    if (!c)
+       return;
+    wa = c->v.v2.winadj_head;
+    if (!wa) {
+       ssh_disconnect(ssh, NULL,
+                      "Received unsolicited SSH_MSG_CHANNEL_FAILURE",
+                      SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE);
+       return;
+    }
+    c->v.v2.winadj_head = wa->next;
+    c->v.v2.remlocwin += wa->size;
+    sfree(wa);
+    /*
+     * winadj messages are only sent when the window is fully open, so
+     * if we get an ack of one, we know any pending unthrottle is
+     * complete.
+     */
+    if (c->v.v2.throttle_state == UNTHROTTLING)
+       c->v.v2.throttle_state = UNTHROTTLED;
+}
+
 static void ssh2_msg_channel_window_adjust(Ssh ssh, struct Packet *pktin)
 {
-    unsigned i = ssh_pkt_getuint32(pktin);
     struct ssh_channel *c;
-    c = find234(ssh->channels, &i, ssh_channelfind);
-    if (c && !c->closes) {
+    c = ssh2_channel_msg(ssh, pktin);
+    if (!c)
+       return;
+    if (!c->closes) {
        c->v.v2.remwindow += ssh_pkt_getuint32(pktin);
        ssh2_try_send_and_unthrottle(c);
     }
@@ -6055,11 +6414,10 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin)
 {
     char *data;
     int length;
-    unsigned i = ssh_pkt_getuint32(pktin);
     struct ssh_channel *c;
-    c = find234(ssh->channels, &i, ssh_channelfind);
+    c = ssh2_channel_msg(ssh, pktin);
     if (!c)
-       return;                        /* nonexistent channel */
+       return;
     if (pktin->type == SSH2_MSG_CHANNEL_EXTENDED_DATA &&
        ssh_pkt_getuint32(pktin) != SSH2_EXTENDED_DATA_STDERR)
        return;                        /* extended but not stderr */
@@ -6067,6 +6425,7 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin)
     if (data) {
        int bufsize = 0;
        c->v.v2.locwindow -= length;
+       c->v.v2.remlocwin -= length;
        switch (c->type) {
          case CHAN_MAINSESSION:
            bufsize =
@@ -6123,23 +6482,44 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin)
            bufsize = 0;
            break;
        }
+       /*
+        * If it looks like the remote end hit the end of its window,
+        * and we didn't want it to do that, think about using a
+        * larger window.
+        */
+       if (c->v.v2.remlocwin <= 0 && c->v.v2.throttle_state == UNTHROTTLED &&
+           c->v.v2.locmaxwin < 0x40000000)
+           c->v.v2.locmaxwin += OUR_V2_WINSIZE;
        /*
         * If we are not buffering too much data,
         * enlarge the window again at the remote side.
+        * If we are buffering too much, we may still
+        * need to adjust the window if the server's
+        * sent excess data.
+        */
+       ssh2_set_window(c, bufsize < c->v.v2.locmaxwin ?
+                       c->v.v2.locmaxwin - bufsize : 0);
+       /*
+        * If we're either buffering way too much data, or if we're
+        * buffering anything at all and we're in "simple" mode,
+        * throttle the whole channel.
         */
-       if (bufsize < OUR_V2_WINSIZE)
-           ssh2_set_window(c, OUR_V2_WINSIZE - bufsize);
+       if ((bufsize > c->v.v2.locmaxwin ||
+            (ssh->cfg.ssh_simple && bufsize > 0)) &&
+           !c->throttling_conn) {
+           c->throttling_conn = 1;
+           ssh_throttle_conn(ssh, +1);
+       }
     }
 }
 
 static void ssh2_msg_channel_eof(Ssh ssh, struct Packet *pktin)
 {
-    unsigned i = ssh_pkt_getuint32(pktin);
     struct ssh_channel *c;
 
-    c = find234(ssh->channels, &i, ssh_channelfind);
+    c = ssh2_channel_msg(ssh, pktin);
     if (!c)
-       return;                        /* nonexistent channel */
+       return;
 
     if (c->type == CHAN_X11) {
        /*
@@ -6158,16 +6538,12 @@ static void ssh2_msg_channel_eof(Ssh ssh, struct Packet *pktin)
 
 static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin)
 {
-    unsigned i = ssh_pkt_getuint32(pktin);
     struct ssh_channel *c;
     struct Packet *pktout;
 
-    c = find234(ssh->channels, &i, ssh_channelfind);
-    if (!c || c->halfopen) {
-       bombout(("Received CHANNEL_CLOSE for %s channel %d\n",
-                c ? "half-open" : "nonexistent", i));
+    c = ssh2_channel_msg(ssh, pktin);
+    if (!c)
        return;
-    }
     /* Do pre-close processing on the channel. */
     switch (c->type) {
       case CHAN_MAINSESSION:
@@ -6220,13 +6596,12 @@ static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin)
 
 static void ssh2_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin)
 {
-    unsigned i = ssh_pkt_getuint32(pktin);
     struct ssh_channel *c;
     struct Packet *pktout;
 
-    c = find234(ssh->channels, &i, ssh_channelfind);
+    c = ssh2_channel_msg(ssh, pktin);
     if (!c)
-       return;                        /* nonexistent channel */
+       return;
     if (c->type != CHAN_SOCKDATA_DORMANT)
        return;                        /* dunno why they're confirming this */
     c->remoteid = ssh_pkt_getuint32(pktin);
@@ -6258,14 +6633,13 @@ static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin)
            "Unknown channel type",
            "Resource shortage",
     };
-    unsigned i = ssh_pkt_getuint32(pktin);
     unsigned reason_code;
     char *reason_string;
     int reason_length;
     struct ssh_channel *c;
-    c = find234(ssh->channels, &i, ssh_channelfind);
+    c = ssh2_channel_msg(ssh, pktin);
     if (!c)
-       return;                        /* nonexistent channel */
+       return;
     if (c->type != CHAN_SOCKDATA_DORMANT)
        return;                        /* dunno why they're failing this */
 
@@ -6284,30 +6658,18 @@ static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin)
 
 static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin)
 {
-    unsigned localid;
     char *type;
     int typelen, want_reply;
     int reply = SSH2_MSG_CHANNEL_FAILURE; /* default */
     struct ssh_channel *c;
     struct Packet *pktout;
 
-    localid = ssh_pkt_getuint32(pktin);
+    c = ssh2_channel_msg(ssh, pktin);
+    if (!c)
+       return;
     ssh_pkt_getstring(pktin, &type, &typelen);
     want_reply = ssh2_pkt_getbool(pktin);
 
-    /*
-     * First, check that the channel exists. Otherwise,
-     * we can instantly disconnect with a rude message.
-     */
-    c = find234(ssh->channels, &localid, ssh_channelfind);
-    if (!c) {
-       char *buf = dupprintf("Received channel request for nonexistent"
-                             " channel %d", localid);
-       ssh_disconnect(ssh, NULL, buf, SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE);
-       sfree(buf);
-       return;
-    }
-
     /*
      * Having got the channel number, we now look at
      * the request type string to see if it's something
@@ -6335,7 +6697,7 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin)
            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
+            * having been a `string' in the drafts of RFC 4254 since at
             * least 2001. (Fixed in session.c 1.147.) Try to
             * infer which we can safely parse it as. */
            {
@@ -6378,7 +6740,7 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin)
                    fmt_sig = dupprintf(" %d", signum);
                    ssh->exitcode = 128 + signum;
                } else {
-                   /* As per the drafts. */
+                   /* As per RFC 4254. */
                    char *sig;
                    int siglen;
                    ssh_pkt_getstring(pktin, &sig, &siglen);
@@ -6397,71 +6759,49 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin)
 
                    if (0)
                        ;
+#define TRANSLATE_SIGNAL(s) \
+    else if (siglen == lenof(#s)-1 && !memcmp(sig, #s, siglen)) \
+        ssh->exitcode = 128 + SIG ## s
 #ifdef SIGABRT
-                   else if (siglen == lenof("ABRT")-1 &&
-                            !memcmp(sig, "ABRT", siglen))
-                       ssh->exitcode = 128 + SIGABRT;
+                   TRANSLATE_SIGNAL(ABRT);
 #endif
 #ifdef SIGALRM
-                   else if (siglen == lenof("ALRM")-1 &&
-                            !memcmp(sig, "ALRM", siglen))
-                       ssh->exitcode = 128 + SIGALRM;
+                   TRANSLATE_SIGNAL(ALRM);
 #endif
 #ifdef SIGFPE
-                   else if (siglen == lenof("FPE")-1 &&
-                            !memcmp(sig, "FPE", siglen))
-                       ssh->exitcode = 128 + SIGFPE;
+                   TRANSLATE_SIGNAL(FPE);
 #endif
 #ifdef SIGHUP
-                   else if (siglen == lenof("HUP")-1 &&
-                            !memcmp(sig, "HUP", siglen))
-                       ssh->exitcode = 128 + SIGHUP;
+                   TRANSLATE_SIGNAL(HUP);
 #endif
 #ifdef SIGILL
-                   else if (siglen == lenof("ILL")-1 &&
-                            !memcmp(sig, "ILL", siglen))
-                       ssh->exitcode = 128 + SIGILL;
+                   TRANSLATE_SIGNAL(ILL);
 #endif
 #ifdef SIGINT
-                   else if (siglen == lenof("INT")-1 &&
-                            !memcmp(sig, "INT", siglen))
-                       ssh->exitcode = 128 + SIGINT;
+                   TRANSLATE_SIGNAL(INT);
 #endif
 #ifdef SIGKILL
-                   else if (siglen == lenof("KILL")-1 &&
-                            !memcmp(sig, "KILL", siglen))
-                       ssh->exitcode = 128 + SIGKILL;
+                   TRANSLATE_SIGNAL(KILL);
 #endif
 #ifdef SIGPIPE
-                   else if (siglen == lenof("PIPE")-1 &&
-                            !memcmp(sig, "PIPE", siglen))
-                       ssh->exitcode = 128 + SIGPIPE;
+                   TRANSLATE_SIGNAL(PIPE);
 #endif
 #ifdef SIGQUIT
-                   else if (siglen == lenof("QUIT")-1 &&
-                            !memcmp(sig, "QUIT", siglen))
-                       ssh->exitcode = 128 + SIGQUIT;
+                   TRANSLATE_SIGNAL(QUIT);
 #endif
 #ifdef SIGSEGV
-                   else if (siglen == lenof("SEGV")-1 &&
-                            !memcmp(sig, "SEGV", siglen))
-                       ssh->exitcode = 128 + SIGSEGV;
+                   TRANSLATE_SIGNAL(SEGV);
 #endif
 #ifdef SIGTERM
-                   else if (siglen == lenof("TERM")-1 &&
-                            !memcmp(sig, "TERM", siglen))
-                       ssh->exitcode = 128 + SIGTERM;
+                   TRANSLATE_SIGNAL(TERM);
 #endif
 #ifdef SIGUSR1
-                   else if (siglen == lenof("USR1")-1 &&
-                            !memcmp(sig, "USR1", siglen))
-                       ssh->exitcode = 128 + SIGUSR1;
+                   TRANSLATE_SIGNAL(USR1);
 #endif
 #ifdef SIGUSR2
-                   else if (siglen == lenof("USR2")-1 &&
-                            !memcmp(sig, "USR2", siglen))
-                       ssh->exitcode = 128 + SIGUSR2;
+                   TRANSLATE_SIGNAL(USR2);
 #endif
+#undef TRANSLATE_SIGNAL
                    else
                        ssh->exitcode = 128;
                }
@@ -6615,12 +6955,9 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin)
        logeventf(ssh, "Rejected channel open: %s", error);
        sfree(c);
     } else {
-       c->localid = alloc_channel_id(ssh);
-       c->closes = 0;
-       c->v.v2.locwindow = OUR_V2_WINSIZE;
+       ssh2_channel_init(c);
        c->v.v2.remwindow = winsize;
        c->v.v2.remmaxpkt = pktsize;
-       bufchain_init(&c->v.v2.outbuffer);
        add234(ssh->channels, c);
        pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
        ssh2_pkt_adduint32(pktout, c->remoteid);
@@ -6941,7 +7278,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
         * just in case it succeeds, and (b) so that we know what
         * authentication methods we can usefully try next.
         */
-       ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
+       ssh->pkt_actx = SSH2_PKTCTX_NOAUTH;
 
        s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
        ssh2_pkt_addstring(s->pktout, s->username);
@@ -7075,7 +7412,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                    in_commasep_string("keyboard-interactive", methods, methlen);
            }
 
-           ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
+           ssh->pkt_actx = SSH2_PKTCTX_NOAUTH;
 
            if (s->can_pubkey && !s->done_agent && s->nkeys) {
 
@@ -7083,8 +7420,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                 * Attempt public-key authentication using a key from Pageant.
                 */
 
-               ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
-               ssh->pkt_ctx |= SSH2_PKTCTX_PUBLICKEY;
+               ssh->pkt_actx = SSH2_PKTCTX_PUBLICKEY;
 
                logeventf(ssh, "Trying Pageant key #%d", s->keyi);
 
@@ -7231,8 +7567,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                struct ssh2_userkey *key;   /* not live over crReturn */
                char *passphrase;           /* not live over crReturn */
 
-               ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
-               ssh->pkt_ctx |= SSH2_PKTCTX_PUBLICKEY;
+               ssh->pkt_actx = SSH2_PKTCTX_PUBLICKEY;
 
                s->tried_pubkey_config = TRUE;
 
@@ -7411,8 +7746,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
 
                s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
 
-               ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
-               ssh->pkt_ctx |= SSH2_PKTCTX_KBDINTER;
+               ssh->pkt_actx = SSH2_PKTCTX_KBDINTER;
 
                s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
                ssh2_pkt_addstring(s->pktout, s->username);
@@ -7554,8 +7888,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                int ret; /* not live over crReturn */
                int changereq_first_time; /* not live over crReturn */
 
-               ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
-               ssh->pkt_ctx |= SSH2_PKTCTX_PASSWORD;
+               ssh->pkt_actx = SSH2_PKTCTX_PASSWORD;
 
                s->cur_prompt = new_prompts(ssh->frontend);
                s->cur_prompt->to_server = TRUE;
@@ -7825,14 +8158,13 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
         */
        ssh->mainchan = snew(struct ssh_channel);
        ssh->mainchan->ssh = ssh;
-       ssh->mainchan->localid = alloc_channel_id(ssh);
+       ssh2_channel_init(ssh->mainchan);
        logeventf(ssh,
                  "Opening direct-tcpip channel to %s:%d in place of session",
                  ssh->cfg.ssh_nc_host, ssh->cfg.ssh_nc_port);
        s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
        ssh2_pkt_addstring(s->pktout, "direct-tcpip");
        ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid);
-       ssh->mainchan->v.v2.locwindow = OUR_V2_WINSIZE;
        ssh2_pkt_adduint32(s->pktout, ssh->mainchan->v.v2.locwindow);/* our window size */
        ssh2_pkt_adduint32(s->pktout, OUR_V2_MAXPKT);      /* our max pkt size */
        ssh2_pkt_addstring(s->pktout, ssh->cfg.ssh_nc_host);
@@ -7859,10 +8191,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
        ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin);
        ssh->mainchan->halfopen = FALSE;
        ssh->mainchan->type = CHAN_MAINSESSION;
-       ssh->mainchan->closes = 0;
        ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(pktin);
        ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin);
-       bufchain_init(&ssh->mainchan->v.v2.outbuffer);
        add234(ssh->channels, ssh->mainchan);
        update_specials_menu(ssh->frontend);
        logevent("Opened direct-tcpip channel");
@@ -7870,11 +8200,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
     } else {
        ssh->mainchan = snew(struct ssh_channel);
        ssh->mainchan->ssh = ssh;
-       ssh->mainchan->localid = alloc_channel_id(ssh);
+       ssh2_channel_init(ssh->mainchan);
        s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
        ssh2_pkt_addstring(s->pktout, "session");
        ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid);
-       ssh->mainchan->v.v2.locwindow = OUR_V2_WINSIZE;
        ssh2_pkt_adduint32(s->pktout, ssh->mainchan->v.v2.locwindow);/* our window size */
        ssh2_pkt_adduint32(s->pktout, OUR_V2_MAXPKT);    /* our max pkt size */
        ssh2_pkt_send(ssh, s->pktout);
@@ -7891,10 +8220,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
        ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin);
        ssh->mainchan->halfopen = FALSE;
        ssh->mainchan->type = CHAN_MAINSESSION;
-       ssh->mainchan->closes = 0;
        ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(pktin);
        ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin);
-       bufchain_init(&ssh->mainchan->v.v2.outbuffer);
        add234(ssh->channels, ssh->mainchan);
        update_specials_menu(ssh->frontend);
        logevent("Opened channel for session");
@@ -7919,6 +8246,20 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
     ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] =
        ssh2_msg_channel_open;
 
+    if (ssh->cfg.ssh_simple) {
+       /*
+        * This message indicates to the server that we promise
+        * not to try to run any other channel in parallel with
+        * this one, so it's safe for it to advertise a very large
+        * window and leave the flow control to TCP.
+        */
+       s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
+       ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid);
+       ssh2_pkt_addstring(s->pktout, "simple@putty.projects.tartarus.org");
+       ssh2_pkt_addbool(s->pktout, 0); /* no reply */
+       ssh2_pkt_send(ssh, s->pktout);
+    }
+
     /*
      * Potentially enable X11 forwarding.
      */
@@ -8173,6 +8514,13 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
     if (ssh->eof_needed)
        ssh_special(ssh, TS_EOF);
 
+    /*
+     * All the initial channel requests are done, so install the default
+     * failure handler.
+     */
+    ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = ssh2_msg_channel_success;
+    ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = ssh2_msg_channel_failure;
+
     /*
      * Transfer data!
      */
@@ -8426,7 +8774,8 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
     ssh->deferred_len = 0;
     ssh->deferred_size = 0;
     ssh->fallback_cmd = 0;
-    ssh->pkt_ctx = 0;
+    ssh->pkt_kctx = SSH2_PKTCTX_NOKEX;
+    ssh->pkt_actx = SSH2_PKTCTX_NOAUTH;
     ssh->x11auth = NULL;
     ssh->v1_compressing = FALSE;
     ssh->v2_outgoing_sequence = 0;
@@ -8473,7 +8822,7 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
     ssh->send_ok = 0;
     ssh->editing = 0;
     ssh->echoing = 0;
-    ssh->v1_throttle_count = 0;
+    ssh->conn_throttle_count = 0;
     ssh->overall_bufsize = 0;
     ssh->fallback_cmd = 0;
 
@@ -8750,7 +9099,7 @@ static const struct telnet_special *ssh_get_specials(void *handle)
     static const struct telnet_special ssh2_session_specials[] = {
        {NULL, TS_SEP},
        {"Break", TS_BRK},
-       /* These are the signal names defined by draft-ietf-secsh-connect-23.
+       /* These are the signal names defined by RFC 4254.
         * They include all the ISO C signals, but are a subset of the POSIX
         * required signals. */
        {"SIGINT (Interrupt)", TS_SIGINT},
@@ -8899,17 +9248,13 @@ void *new_sock_channel(void *handle, Socket s)
     Ssh ssh = (Ssh) handle;
     struct ssh_channel *c;
     c = snew(struct ssh_channel);
-    c->ssh = ssh;
 
-    if (c) {
-       c->halfopen = TRUE;
-       c->localid = alloc_channel_id(ssh);
-       c->closes = 0;
-       c->type = CHAN_SOCKDATA_DORMANT;/* identify channel type */
-       c->u.pfd.s = s;
-       bufchain_init(&c->v.v2.outbuffer);
-       add234(ssh->channels, c);
-    }
+    c->ssh = ssh;
+    ssh2_channel_init(c);
+    c->halfopen = TRUE;
+    c->type = CHAN_SOCKDATA_DORMANT;/* identify channel type */
+    c->u.pfd.s = s;
+    add234(ssh->channels, c);
     return c;
 }
 
@@ -8920,14 +9265,27 @@ void *new_sock_channel(void *handle, Socket s)
 static void ssh_unthrottle(void *handle, int bufsize)
 {
     Ssh ssh = (Ssh) handle;
+    int buflimit;
+
     if (ssh->version == 1) {
        if (ssh->v1_stdout_throttling && bufsize < SSH1_BUFFER_LIMIT) {
            ssh->v1_stdout_throttling = 0;
-           ssh1_throttle(ssh, -1);
+           ssh_throttle_conn(ssh, -1);
        }
     } else {
-       if (ssh->mainchan && ssh->mainchan->closes == 0)
-           ssh2_set_window(ssh->mainchan, OUR_V2_WINSIZE - bufsize);
+       if (ssh->mainchan) {
+           ssh2_set_window(ssh->mainchan,
+                           bufsize < ssh->mainchan->v.v2.locmaxwin ?
+                           ssh->mainchan->v.v2.locmaxwin - bufsize : 0);
+           if (ssh->cfg.ssh_simple)
+               buflimit = 0;
+           else
+               buflimit = ssh->mainchan->v.v2.locmaxwin;
+           if (ssh->mainchan->throttling_conn && bufsize <= buflimit) {
+               ssh->mainchan->throttling_conn = 0;
+               ssh_throttle_conn(ssh, -1);
+           }
+       }
     }
 }
 
@@ -8950,7 +9308,6 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org)
        pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
        ssh2_pkt_addstring(pktout, "direct-tcpip");
        ssh2_pkt_adduint32(pktout, c->localid);
-       c->v.v2.locwindow = OUR_V2_WINSIZE;
        ssh2_pkt_adduint32(pktout, c->v.v2.locwindow);/* our window size */
        ssh2_pkt_adduint32(pktout, OUR_V2_MAXPKT);      /* our max pkt size */
        ssh2_pkt_addstring(pktout, hostname);
@@ -9051,5 +9408,7 @@ Backend ssh_backend = {
     ssh_provide_logctx,
     ssh_unthrottle,
     ssh_cfg_info,
+    "ssh",
+    PROT_SSH,
     22
 };
diff --git a/ssh.h b/ssh.h
index fede9968cf52e071dda1b12349f4046ae5c5adea..3031141201741a1aa96a19facbd496c06702d764 100644 (file)
--- a/ssh.h
+++ b/ssh.h
@@ -82,6 +82,17 @@ void crcda_free_context(void *handle);
 int detect_attack(void *handle, unsigned char *buf, uint32 len,
                  unsigned char *IV);
 
+/*
+ * SSH2 RSA key exchange functions
+ */
+struct ssh_hash;
+void *ssh_rsakex_newkey(char *data, int len);
+void ssh_rsakex_freekey(void *key);
+int ssh_rsakex_klen(void *key);
+void ssh_rsakex_encrypt(const struct ssh_hash *h, unsigned char *in, int inlen,
+                        unsigned char *out, int outlen,
+                        void *key);
+
 typedef struct {
     uint32 h[4];
 } MD5_Core_State;
@@ -194,15 +205,10 @@ struct ssh_hash {
 };   
 
 struct ssh_kex {
-    /*
-     * Plugging in another KEX algorithm requires structural chaos,
-     * so it's hard to abstract them into nice little structures
-     * like this. Fortunately, all our KEXes are basically
-     * Diffie-Hellman at the moment, so in this structure I simply
-     * parametrise the DH exchange a bit.
-     */
     char *name, *groupname;
-    const unsigned char *pdata, *gdata;/* NULL means use group exchange */
+    enum { KEXTYPE_DH, KEXTYPE_RSA } main_type;
+    /* For DH */
+    const unsigned char *pdata, *gdata; /* NULL means group exchange */
     int plen, glen;
     const struct ssh_hash *hash;
 };
@@ -268,6 +274,7 @@ extern const struct ssh_hash ssh_sha256;
 extern const struct ssh_kexes ssh_diffiehellman_group1;
 extern const struct ssh_kexes ssh_diffiehellman_group14;
 extern const struct ssh_kexes ssh_diffiehellman_gex;
+extern const struct ssh_kexes ssh_rsa_kex;
 extern const struct ssh_signkey ssh_dss;
 extern const struct ssh_signkey ssh_rsa;
 extern const struct ssh_mac ssh_hmac_md5;
index 8c3ab72e8164b4370f7643d3c266283e656eb3b4..b12a91d5169d879d32c4832f1ed53b31c4ae00a9 100644 (file)
--- a/sshdes.c
+++ b/sshdes.c
@@ -959,7 +959,7 @@ static const struct ssh2_cipher ssh_3des_ssh2_ctr = {
 
 /*
  * Single DES in SSH-2. "des-cbc" is marked as HISTORIC in
- * draft-ietf-secsh-assignednumbers-04.txt, referring to
+ * RFC 4250, referring to
  * FIPS-46-3.  ("Single DES (i.e., DES) will be permitted 
  * for legacy systems only.") , but ssh.com support it and 
  * apparently aren't the only people to do so, so we sigh 
diff --git a/sshdh.c b/sshdh.c
index f0abd3eca311131a57654fd1b35a1608179374e7..c733b61f7e77fe8f3f0f5ea4f5f7a3364274382d 100644 (file)
--- a/sshdh.c
+++ b/sshdh.c
@@ -52,7 +52,7 @@ static const unsigned char G[] = { 2 };
 
 static const struct ssh_kex ssh_diffiehellman_group1_sha1 = {
     "diffie-hellman-group1-sha1", "group1",
-    P1, G, lenof(P1), lenof(G), &ssh_sha1
+    KEXTYPE_DH, P1, G, lenof(P1), lenof(G), &ssh_sha1
 };
 
 static const struct ssh_kex *const group1_list[] = {
@@ -66,7 +66,7 @@ const struct ssh_kexes ssh_diffiehellman_group1 = {
 
 static const struct ssh_kex ssh_diffiehellman_group14_sha1 = {
     "diffie-hellman-group14-sha1", "group14",
-    P14, G, lenof(P14), lenof(G), &ssh_sha1
+    KEXTYPE_DH, P14, G, lenof(P14), lenof(G), &ssh_sha1
 };
 
 static const struct ssh_kex *const group14_list[] = {
@@ -80,12 +80,12 @@ const struct ssh_kexes ssh_diffiehellman_group14 = {
 
 static const struct ssh_kex ssh_diffiehellman_gex_sha256 = {
     "diffie-hellman-group-exchange-sha256", NULL,
-    NULL, NULL, 0, 0, &ssh_sha256
+    KEXTYPE_DH, NULL, NULL, 0, 0, &ssh_sha256
 };
 
 static const struct ssh_kex ssh_diffiehellman_gex_sha1 = {
     "diffie-hellman-group-exchange-sha1", NULL,
-    NULL, NULL, 0, 0, &ssh_sha1
+    KEXTYPE_DH, NULL, NULL, 0, 0, &ssh_sha1
 };
 
 static const struct ssh_kex *const gex_list[] = {
index 22992fea538194d28e219bb04618d7534e2cab81..7c95d11b65c5ecf975d2773d89793fcd8c23b5bc 100644 (file)
--- a/sshdss.c
+++ b/sshdss.c
@@ -231,14 +231,14 @@ static int dss_verifysig(void *key, char *sig, int siglen,
 #endif
     /*
      * Commercial SSH (2.0.13) and OpenSSH disagree over the format
-     * of a DSA signature. OpenSSH is in line with the IETF drafts:
+     * of a DSA signature. OpenSSH is in line with RFC 4253:
      * it uses a string "ssh-dss", followed by a 40-byte string
      * containing two 160-bit integers end-to-end. Commercial SSH
      * can't be bothered with the header bit, and considers a DSA
      * signature blob to be _just_ the 40-byte string containing
      * the two 160-bit integers. We tell them apart by measuring
      * the length: length 40 means the commercial-SSH bug, anything
-     * else is assumed to be IETF-compliant.
+     * else is assumed to be RFC-compliant.
      */
     if (siglen != 40) {                       /* bug not present; read admin fields */
        getstring(&sig, &siglen, &p, &slen);
index b862d3f6bb411cb905422cf08debf14cd63b61aa..12229e63152066dda831a08ebe60408dccbd29df 100644 (file)
--- a/sshrsa.c
+++ b/sshrsa.c
@@ -836,3 +836,157 @@ const struct ssh_signkey ssh_rsa = {
     "ssh-rsa",
     "rsa2"
 };
+
+void *ssh_rsakex_newkey(char *data, int len)
+{
+    return rsa2_newkey(data, len);
+}
+
+void ssh_rsakex_freekey(void *key)
+{
+    rsa2_freekey(key);
+}
+
+int ssh_rsakex_klen(void *key)
+{
+    struct RSAKey *rsa = (struct RSAKey *) key;
+
+    return bignum_bitcount(rsa->modulus);
+}
+
+static void oaep_mask(const struct ssh_hash *h, void *seed, int seedlen,
+                     void *vdata, int datalen)
+{
+    unsigned char *data = (unsigned char *)vdata;
+    unsigned count = 0;
+
+    while (datalen > 0) {
+        int i, max = (datalen > h->hlen ? h->hlen : datalen);
+        void *s;
+        unsigned char counter[4], hash[SSH2_KEX_MAX_HASH_LEN];
+
+       assert(h->hlen <= SSH2_KEX_MAX_HASH_LEN);
+        PUT_32BIT(counter, count);
+        s = h->init();
+        h->bytes(s, seed, seedlen);
+        h->bytes(s, counter, 4);
+        h->final(s, hash);
+        count++;
+
+        for (i = 0; i < max; i++)
+            data[i] ^= hash[i];
+
+        data += max;
+        datalen -= max;
+    }
+}
+
+void ssh_rsakex_encrypt(const struct ssh_hash *h, unsigned char *in, int inlen,
+                        unsigned char *out, int outlen,
+                        void *key)
+{
+    Bignum b1, b2;
+    struct RSAKey *rsa = (struct RSAKey *) key;
+    int k, i;
+    char *p;
+    const int HLEN = h->hlen;
+
+    /*
+     * Here we encrypt using RSAES-OAEP. Essentially this means:
+     * 
+     *  - we have a SHA-based `mask generation function' which
+     *    creates a pseudo-random stream of mask data
+     *    deterministically from an input chunk of data.
+     * 
+     *  - we have a random chunk of data called a seed.
+     * 
+     *  - we use the seed to generate a mask which we XOR with our
+     *    plaintext.
+     * 
+     *  - then we use _the masked plaintext_ to generate a mask
+     *    which we XOR with the seed.
+     * 
+     *  - then we concatenate the masked seed and the masked
+     *    plaintext, and RSA-encrypt that lot.
+     * 
+     * The result is that the data input to the encryption function
+     * is random-looking and (hopefully) contains no exploitable
+     * structure such as PKCS1-v1_5 does.
+     * 
+     * For a precise specification, see RFC 3447, section 7.1.1.
+     * Some of the variable names below are derived from that, so
+     * it'd probably help to read it anyway.
+     */
+
+    /* k denotes the length in octets of the RSA modulus. */
+    k = (7 + bignum_bitcount(rsa->modulus)) / 8;
+
+    /* The length of the input data must be at most k - 2hLen - 2. */
+    assert(inlen > 0 && inlen <= k - 2*HLEN - 2);
+
+    /* The length of the output data wants to be precisely k. */
+    assert(outlen == k);
+
+    /*
+     * Now perform EME-OAEP encoding. First set up all the unmasked
+     * output data.
+     */
+    /* Leading byte zero. */
+    out[0] = 0;
+    /* At position 1, the seed: HLEN bytes of random data. */
+    for (i = 0; i < HLEN; i++)
+        out[i + 1] = random_byte();
+    /* At position 1+HLEN, the data block DB, consisting of: */
+    /* The hash of the label (we only support an empty label here) */
+    h->final(h->init(), out + HLEN + 1);
+    /* A bunch of zero octets */
+    memset(out + 2*HLEN + 1, 0, outlen - (2*HLEN + 1));
+    /* A single 1 octet, followed by the input message data. */
+    out[outlen - inlen - 1] = 1;
+    memcpy(out + outlen - inlen, in, inlen);
+
+    /*
+     * Now use the seed data to mask the block DB.
+     */
+    oaep_mask(h, out+1, HLEN, out+HLEN+1, outlen-HLEN-1);
+
+    /*
+     * And now use the masked DB to mask the seed itself.
+     */
+    oaep_mask(h, out+HLEN+1, outlen-HLEN-1, out+1, HLEN);
+
+    /*
+     * Now `out' contains precisely the data we want to
+     * RSA-encrypt.
+     */
+    b1 = bignum_from_bytes(out, outlen);
+    b2 = modpow(b1, rsa->exponent, rsa->modulus);
+    p = (char *)out;
+    for (i = outlen; i--;) {
+       *p++ = bignum_byte(b2, i);
+    }
+    freebn(b1);
+    freebn(b2);
+
+    /*
+     * And we're done.
+     */
+}
+
+static const struct ssh_kex ssh_rsa_kex_sha1 = {
+    "rsa1024-sha1", NULL, KEXTYPE_RSA, NULL, NULL, 0, 0, &ssh_sha1
+};
+
+static const struct ssh_kex ssh_rsa_kex_sha256 = {
+    "rsa2048-sha256", NULL, KEXTYPE_RSA, NULL, NULL, 0, 0, &ssh_sha256
+};
+
+static const struct ssh_kex *const rsa_kex_list[] = {
+    &ssh_rsa_kex_sha256,
+    &ssh_rsa_kex_sha1
+};
+
+const struct ssh_kexes ssh_rsa_kex = {
+    sizeof(rsa_kex_list) / sizeof(*rsa_kex_list),
+    rsa_kex_list
+};
index e6ad83e06d80b1840093adc95c37ee83779ab4b5..eeaa76f305bc33d8d606e77a1f32c0e63839eaf9 100644 (file)
--- a/telnet.c
+++ b/telnet.c
@@ -1106,5 +1106,7 @@ Backend telnet_backend = {
     telnet_provide_logctx,
     telnet_unthrottle,
     telnet_cfg_info,
+    "telnet",
+    PROT_TELNET,
     23
 };
index f58941fa8e0729397ec0490f8d20011ce1b8f405..3a2ab6208b91ad01344a11d07d892198b5a85c8b 100644 (file)
@@ -1223,6 +1223,8 @@ static void power_on(Terminal *term, int clear)
     term->erase_char = term->basic_erase_char;
     term->alt_which = 0;
     term_print_finish(term);
+    term->xterm_mouse = FALSE;
+    set_raw_mouse_mode(term->frontend, FALSE);
     {
        int i;
        for (i = 0; i < 256; i++)
@@ -1448,7 +1450,7 @@ Terminal *term_init(Config *mycfg, struct unicode_data *ucsdata,
     term->vt52_mode = FALSE;
     term->cr_lf_return = FALSE;
     term->seen_disp_event = FALSE;
-    term->xterm_mouse = term->mouse_is_down = FALSE;
+    term->mouse_is_down = FALSE;
     term->reset_132 = FALSE;
     term->cblinker = term->tblinker = 0;
     term->has_focus = 1;
@@ -1612,6 +1614,8 @@ void term_size(Terminal *term, int newrows, int newcols, int newsavelines)
            addpos234(term->screen, line, 0);
            term->curs.y += 1;
            term->savecurs.y += 1;
+           term->alt_y += 1;
+           term->alt_savecurs.y += 1;
        } else {
            /* Add a new blank line at the bottom of the screen. */
            line = newline(term, newcols, FALSE);
@@ -1632,6 +1636,8 @@ void term_size(Terminal *term, int newrows, int newcols, int newsavelines)
            term->tempsblines += 1;
            term->curs.y -= 1;
            term->savecurs.y -= 1;
+           term->alt_y -= 1;
+           term->alt_savecurs.y -= 1;
        }
        term->rows -= 1;
     }
@@ -1691,12 +1697,26 @@ void term_size(Terminal *term, int newrows, int newcols, int newsavelines)
        term->savecurs.y = 0;
     if (term->savecurs.y >= newrows)
        term->savecurs.y = newrows - 1;
+    if (term->savecurs.x >= newcols)
+       term->savecurs.x = newcols - 1;
+    if (term->alt_savecurs.y < 0)
+       term->alt_savecurs.y = 0;
+    if (term->alt_savecurs.y >= newrows)
+       term->alt_savecurs.y = newrows - 1;
+    if (term->alt_savecurs.x >= newcols)
+       term->alt_savecurs.x = newcols - 1;
     if (term->curs.y < 0)
        term->curs.y = 0;
     if (term->curs.y >= newrows)
        term->curs.y = newrows - 1;
     if (term->curs.x >= newcols)
        term->curs.x = newcols - 1;
+    if (term->alt_y < 0)
+       term->alt_y = 0;
+    if (term->alt_y >= newrows)
+       term->alt_y = newrows - 1;
+    if (term->alt_x >= newcols)
+       term->alt_x = newcols - 1;
     term->alt_x = term->alt_y = 0;
     term->wrapnext = term->alt_wnext = FALSE;
 
@@ -2850,6 +2870,13 @@ static void term_out(Terminal *term)
                term->wrapnext = FALSE;
                seen_disp_event(term);
                term->paste_hold = 0;
+
+        if (term->cfg.crhaslf) {  
+                 if (term->curs.y == term->marg_b)
+                   scroll(term, term->marg_t, term->marg_b, 1, TRUE);
+                 else if (term->curs.y < term->rows - 1)
+                   term->curs.y++;
+        }
                if (term->logctx)
                    logtraffic(term->logctx, (unsigned char) c, LGTYP_ASCII);
                break;
@@ -6398,6 +6425,7 @@ char *term_get_ttymode(Terminal *term, const char *mode)
        val = term->cfg.bksp_is_delete ? "^?" : "^H";
     }
     /* FIXME: perhaps we should set ONLCR based on cfg.lfhascr as well? */
+    /* FIXME: or ECHO and friends based on local echo state? */
     return dupstr(val);
 }
 
index ac0d20672cf670ea01422e32c2b50e91427e0109..0175e8e7b383baef173050b391b170ffed5dccfb 100644 (file)
@@ -46,7 +46,7 @@ static int null_sendbuffer(void *);
 static void null_size(void *, int, int);
 static void null_special(void *, Telnet_Special);
 static const struct telnet_special *null_get_specials(void *handle);
-static Socket null_socket(void *);
+static int null_connected(void *);
 static int null_exitcode(void *);
 static int null_sendok(void *);
 static int null_ldisc(void *, int);
@@ -57,16 +57,16 @@ static int null_cfg_info(void *);
 
 Backend null_backend = {
     null_init, null_free, null_reconfig, null_send, null_sendbuffer, null_size,
-    null_special, null_get_specials, null_socket, null_exitcode, null_sendok,
+    null_special, null_get_specials, null_connected, null_exitcode, null_sendok,
     null_ldisc, null_provide_ldisc, null_provide_logctx, null_unthrottle,
-    null_cfg_info, 0
+    null_cfg_info, "null", -1, 0
 };
 
 Backend loop_backend = {
     loop_init, loop_free, null_reconfig, loop_send, null_sendbuffer, null_size,
-    null_special, null_get_specials, null_socket, null_exitcode, null_sendok,
+    null_special, null_get_specials, null_connected, null_exitcode, null_sendok,
     null_ldisc, null_provide_ldisc, null_provide_logctx, null_unthrottle,
-    null_cfg_info, 0
+    null_cfg_info, "loop", -1, 0
 };
 
 struct loop_state {
@@ -134,9 +134,9 @@ static const struct telnet_special *null_get_specials (void *handle) {
     return NULL;
 }
 
-static Socket null_socket(void *handle) {
+static int null_connected(void *handle) {
 
-    return NULL;
+    return 0;
 }
 
 static int null_exitcode(void *handle) {
index 966164ac025e4ecf3a04bcc44802b5c907e70cfb..ee520a374990ca729de2766103aaebd218bb6c3a 100644 (file)
@@ -49,6 +49,7 @@ struct uctrl {
     GtkAdjustment *adj;       /* for the scrollbar in a list box */
     guint entrysig;
     guint textsig;
+    int nclicks;
 };
 
 struct dlgparam {
@@ -98,8 +99,10 @@ static gboolean listitem_single_key(GtkWidget *item, GdkEventKey *event,
                                    gpointer data);
 static gboolean listitem_multi_key(GtkWidget *item, GdkEventKey *event,
                                   gpointer data);
-static gboolean listitem_button(GtkWidget *item, GdkEventButton *event,
-                               gpointer data);
+static gboolean listitem_button_press(GtkWidget *item, GdkEventButton *event,
+                                     gpointer data);
+static gboolean listitem_button_release(GtkWidget *item, GdkEventButton *event,
+                                       gpointer data);
 static void menuitem_activate(GtkMenuItem *item, gpointer data);
 static void coloursel_ok(GtkButton *button, gpointer data);
 static void coloursel_cancel(GtkButton *button, gpointer data);
@@ -467,7 +470,9 @@ void dlg_listbox_addwithid(union control *ctrl, void *dlg,
         gtk_signal_connect(GTK_OBJECT(listitem), "focus_in_event",
                            GTK_SIGNAL_FUNC(widget_focus), dp);
        gtk_signal_connect(GTK_OBJECT(listitem), "button_press_event",
-                          GTK_SIGNAL_FUNC(listitem_button), dp);
+                          GTK_SIGNAL_FUNC(listitem_button_press), dp);
+       gtk_signal_connect(GTK_OBJECT(listitem), "button_release_event",
+                          GTK_SIGNAL_FUNC(listitem_button_release), dp);
        gtk_object_set_data(GTK_OBJECT(listitem), "user-data",
                            GINT_TO_POINTER(id));
     } else {
@@ -1121,13 +1126,26 @@ static gboolean listitem_multi_key(GtkWidget *item, GdkEventKey *event,
     return listitem_key(item, event, data, TRUE);
 }
 
-static gboolean listitem_button(GtkWidget *item, GdkEventButton *event,
-                               gpointer data)
+static gboolean listitem_button_press(GtkWidget *item, GdkEventButton *event,
+                                     gpointer data)
 {
     struct dlgparam *dp = (struct dlgparam *)data;
-    if (event->type == GDK_2BUTTON_PRESS ||
-       event->type == GDK_3BUTTON_PRESS) {
-       struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(item));
+    struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(item));
+    switch (event->type) {
+    default:
+    case GDK_BUTTON_PRESS: uc->nclicks = 1; break;
+    case GDK_2BUTTON_PRESS: uc->nclicks = 2; break;
+    case GDK_3BUTTON_PRESS: uc->nclicks = 3; break;
+    }
+    return FALSE;
+}
+
+static gboolean listitem_button_release(GtkWidget *item, GdkEventButton *event,
+                                       gpointer data)
+{
+    struct dlgparam *dp = (struct dlgparam *)data;
+    struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(item));
+    if (uc->nclicks>1) {
        uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_ACTION);
         return TRUE;
     }
@@ -1412,6 +1430,7 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs,
        uc->entry = uc->list = uc->menu = NULL;
        uc->button = uc->optmenu = uc->text = NULL;
        uc->label = NULL;
+        uc->nclicks = 0;
 
         switch (ctrl->generic.type) {
           case CTRL_BUTTON:
@@ -2749,7 +2768,7 @@ static void licence_clicked(GtkButton *button, gpointer data)
     char *title;
 
     char *licence =
-       "Copyright 1997-2007 Simon Tatham.\n\n"
+       "Copyright 1997-2008 Simon Tatham.\n\n"
 
        "Portions copyright Robert de Bath, Joris van Rantwijk, Delian "
        "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas "
@@ -2830,7 +2849,7 @@ void about_box(void *window)
                       w, FALSE, FALSE, 5);
     gtk_widget_show(w);
 
-    w = gtk_label_new("Copyright 1997-2007 Simon Tatham. All rights reserved");
+    w = gtk_label_new("Copyright 1997-2008 Simon Tatham. All rights reserved");
     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(aboutbox)->vbox),
                       w, FALSE, FALSE, 5);
     gtk_widget_show(w);
index 3743672a5a0a3406fb569b6e561d2a69dbfd76e7..e6ff13c352c0d9066c0a9c32cfc96d438e26ea4a 100644 (file)
@@ -663,13 +663,12 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
            end = 2;
        }
 
-       /* Control-Break is the same as Control-C */
+       /* Control-Break sends a Break special to the backend */
        if (event->keyval == GDK_Break &&
            (event->state & GDK_CONTROL_MASK)) {
-           output[1] = '\003';
-           use_ucsoutput = FALSE;
-           end = 2;
-           special = TRUE;
+           if (inst->back)
+               inst->back->special(inst->backhandle, TS_BRK);
+           return TRUE;
        }
 
        /* We handle Return ourselves, because it needs to be flagged as
@@ -724,6 +723,13 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
            end = 1 + sprintf(output+1, "\033[Z");
            use_ucsoutput = FALSE;
        }
+       /* And normal Tab is Tab, if the keymap hasn't already told us.
+        * (Curiously, at least one version of the MacOS 10.5 X server
+        * doesn't translate Tab for us. */
+       if (event->keyval == GDK_Tab && end <= 1) {
+           output[1] = '\t';
+           end = 2;
+       }
 
        /*
         * NetHack keypad mode.
@@ -1431,7 +1437,7 @@ void palette_reset(void *frontend)
            int r = i / 36, g = (i / 6) % 6, b = i % 6;
            inst->cols[i+16].red = r ? r * 0x2828 + 0x3737 : 0;
            inst->cols[i+16].green = g ? g * 0x2828 + 0x3737 : 0;
-           inst->cols[i+16].blue = b ? b + 0x2828 + 0x3737 : 0;
+           inst->cols[i+16].blue = b ? b * 0x2828 + 0x3737 : 0;
        } else {
            int shade = i - 216;
            shade = shade * 0x0a0a + 0x0808;
@@ -1864,7 +1870,7 @@ void sys_cursor(void *frontend, int x, int y)
  */
 void do_beep(void *frontend, int mode)
 {
-    if (mode != BELL_VISUAL)
+    if (mode == BELL_DEFAULT)
        gdk_beep();
 }
 
@@ -2408,7 +2414,7 @@ static void help(FILE *fp) {
     }
 }
 
-int do_cmdline(int argc, char **argv, int do_everything,
+int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch,
                struct gui_data *inst, Config *cfg)
 {
     int err = 0;
@@ -2614,7 +2620,8 @@ int do_cmdline(int argc, char **argv, int do_everything,
             exit(1);
 
        } else if(p[0] != '-' && (!do_everything ||
-                                  process_nonoption_arg(p, cfg))) {
+                                  process_nonoption_arg(p, cfg,
+                                                       allow_launch))) {
             /* do nothing */
 
        } else {
@@ -3321,6 +3328,7 @@ void set_window_icon(GtkWidget *window, const char *const *const *icon,
                     int n_icon)
 {
     GdkPixmap *iconpm;
+    GdkBitmap *iconmask;
 #if GTK_CHECK_VERSION(2,0,0)
     GList *iconlist;
     int n;
@@ -3330,9 +3338,9 @@ void set_window_icon(GtkWidget *window, const char *const *const *icon,
        return;
 
     gtk_widget_realize(window);
-    iconpm = gdk_pixmap_create_from_xpm_d(window->window, NULL,
+    iconpm = gdk_pixmap_create_from_xpm_d(window->window, &iconmask,
                                          NULL, (gchar **)icon[0]);
-    gdk_window_set_icon(window->window, NULL, iconpm, NULL);
+    gdk_window_set_icon(window->window, NULL, iconpm, iconmask);
 
 #if GTK_CHECK_VERSION(2,0,0)
     iconlist = NULL;
@@ -3492,15 +3500,23 @@ int pt_main(int argc, char **argv)
        /* Splatter this argument so it doesn't clutter a ps listing */
        memset(argv[1], 0, strlen(argv[1]));
     } else {
-       if (do_cmdline(argc, argv, 0, inst, &inst->cfg))
+       /* By default, we bring up the config dialog, rather than launching
+        * a session. This gets set to TRUE if something happens to change
+        * that (e.g., a hostname is specified on the command-line). */
+       int allow_launch = FALSE;
+       if (do_cmdline(argc, argv, 0, &allow_launch, inst, &inst->cfg))
            exit(1);                   /* pre-defaults pass to get -class */
        do_defaults(NULL, &inst->cfg);
-       if (do_cmdline(argc, argv, 1, inst, &inst->cfg))
+       if (do_cmdline(argc, argv, 1, &allow_launch, inst, &inst->cfg))
            exit(1);                   /* post-defaults, do everything */
 
        cmdline_run_saved(&inst->cfg);
 
-       if (!cfg_launchable(&inst->cfg) && !cfgbox(&inst->cfg))
+       if (loaded_session)
+           allow_launch = TRUE;
+
+       if ((!allow_launch || !cfg_launchable(&inst->cfg)) &&
+           !cfgbox(&inst->cfg))
            exit(0);                   /* config box hit Cancel */
     }
 
index 9c52de6d4efa0cda58c047954e51005759d06e0e..44749b8dace245aa1ddaa9588647a5455369121e 100644 (file)
@@ -60,6 +60,18 @@ extern long tickcount_offset;
 #define WCHAR wchar_t
 #define BYTE unsigned char
 
+/*
+ * Unix-specific global flag
+ *
+ * FLAG_STDERR_TTY indicates that standard error might be a terminal and
+ * might get its configuration munged, so anything trying to output plain
+ * text (i.e. with newlines in it) will need to put it back into cooked
+ * mode first.  Applications setting this flag should also call
+ * stderr_tty_init() before messing with any terminal modes, and can call
+ * premsg() before outputting text to stderr and postmsg() afterwards.
+ */
+#define FLAG_STDERR_TTY 0x1000
+
 /* Things pty.c needs from pterm.c */
 char *get_x_display(void *frontend);
 int font_dimension(void *frontend, int which);/* 0 for width, 1 for height */
@@ -80,7 +92,7 @@ int reallyclose(void *frontend);
 
 /* Things pterm.c needs from {ptermm,uxputty}.c */
 char *make_default_wintitle(char *hostname);
-int process_nonoption_arg(char *arg, Config *cfg);
+int process_nonoption_arg(char *arg, Config *cfg, int *allow_launch);
 
 /* pterm.c needs this special function in xkeysym.c */
 int keysym_to_unicode(int keysym);
@@ -91,6 +103,12 @@ char *x_get_default(const char *key);
 /* Things uxstore.c provides to pterm.c */
 void provide_xrm_string(char *string);
 
+/* Things provided by uxcons.c */
+struct termios;
+void stderr_tty_init(void);
+void premsg(struct termios *);
+void postmsg(struct termios *);
+
 /* The interface used by uxsel.c */
 void uxsel_init(void);
 typedef int (*uxsel_callback_fn)(int fd, int event);
index d4848a6a36a48df2c6d9dccd0ae07b001544e25f..16e69fbb859176696de1a3a69911c0b231f0473e 100644 (file)
@@ -18,6 +18,31 @@ int console_batch_mode = FALSE;
 
 static void *console_logctx = NULL;
 
+static struct termios orig_termios_stderr;
+static int stderr_is_a_tty;
+
+void stderr_tty_init()
+{
+    /* Ensure that if stderr is a tty, we can get it back to a sane state. */
+    if ((flags & FLAG_STDERR_TTY) && isatty(STDERR_FILENO)) {
+       stderr_is_a_tty = TRUE;
+       tcgetattr(STDERR_FILENO, &orig_termios_stderr);
+    }
+}
+
+void premsg(struct termios *cf)
+{
+    if (stderr_is_a_tty) {
+       tcgetattr(STDERR_FILENO, cf);
+       tcsetattr(STDERR_FILENO, TCSADRAIN, &orig_termios_stderr);
+    }
+}
+void postmsg(struct termios *cf)
+{
+    if (stderr_is_a_tty)
+       tcsetattr(STDERR_FILENO, TCSADRAIN, cf);
+}
+
 /*
  * Clean up and exit.
  */
@@ -101,6 +126,7 @@ int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
     static const char abandoned[] = "Connection abandoned.\n";
 
     char line[32];
+    struct termios cf;
 
     /*
      * Verify the key.
@@ -110,6 +136,7 @@ int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
     if (ret == 0)                     /* success - key matched OK */
        return 1;
 
+    premsg(&cf);
     if (ret == 2) {                   /* key was different */
        if (console_batch_mode) {
            fprintf(stderr, wrongmsg_batch, keytype, fingerprint);
@@ -141,9 +168,11 @@ int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
     if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') {
        if (line[0] == 'y' || line[0] == 'Y')
            store_host_key(host, port, keytype, keystr);
+       postmsg(&cf);
         return 1;
     } else {
        fprintf(stderr, abandoned);
+       postmsg(&cf);
         return 0;
     }
 }
@@ -166,7 +195,9 @@ int askalg(void *frontend, const char *algtype, const char *algname,
     static const char abandoned[] = "Connection abandoned.\n";
 
     char line[32];
+    struct termios cf;
 
+    premsg(&cf);
     if (console_batch_mode) {
        fprintf(stderr, msg_batch, algtype, algname);
        return 0;
@@ -187,9 +218,11 @@ int askalg(void *frontend, const char *algtype, const char *algname,
     }
 
     if (line[0] == 'y' || line[0] == 'Y') {
+       postmsg(&cf);
        return 1;
     } else {
        fprintf(stderr, abandoned);
+       postmsg(&cf);
        return 0;
     }
 }
@@ -215,7 +248,9 @@ int askappend(void *frontend, Filename filename,
        "Logging will not be enabled.\n";
 
     char line[32];
+    struct termios cf;
 
+    premsg(&cf);
     if (console_batch_mode) {
        fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename.path);
        fflush(stderr);
@@ -235,6 +270,7 @@ int askappend(void *frontend, Filename filename,
        tcsetattr(0, TCSANOW, &oldmode);
     }
 
+    postmsg(&cf);
     if (line[0] == 'y' || line[0] == 'Y')
        return 2;
     else if (line[0] == 'n' || line[0] == 'N')
@@ -266,7 +302,10 @@ void old_keyfile_warning(void)
        "Once the key is loaded into PuTTYgen, you can perform\n"
        "this conversion simply by saving it again.\n";
 
+    struct termios cf;
+    premsg(&cf);
     fputs(message, stderr);
+    postmsg(&cf);
 }
 
 void console_provide_logctx(void *logctx)
@@ -276,8 +315,11 @@ void console_provide_logctx(void *logctx)
 
 void logevent(void *frontend, const char *string)
 {
+    struct termios cf;
+    premsg(&cf);
     if (console_logctx)
        log_eventlog(console_logctx, string);
+    postmsg(&cf);
 }
 
 static void console_data_untrusted(const char *data, int len)
index e1dfce327f5f2e8c087895a771b0456b66f85ccb..bd40937aeadd3ebc77ef24989e111b9d82870ec5 100644 (file)
@@ -97,6 +97,10 @@ static int cmpfortree(void *av, void *bv)
        return -1;
     if (as > bs)
        return +1;
+    if (a < b)
+       return -1;
+    if (a > b)
+       return +1;
     return 0;
 }
 
@@ -453,6 +457,14 @@ static int try_connect(Actual_Socket sock)
     short localport;
     int fl, salen;
 
+    /*
+     * Remove the socket from the tree before we overwrite its
+     * internal socket id, because that forms part of the tree's
+     * sorting criterion. We'll add it back before exiting this
+     * function, whether we changed anything or not.
+     */
+    del234(sktree, sock);
+
     if (sock->s >= 0)
         close(sock->s);
 
@@ -605,9 +617,14 @@ static int try_connect(Actual_Socket sock)
     }
 
     uxsel_tell(sock);
-    add234(sktree, sock);
 
     ret:
+
+    /*
+     * No matter what happened, put the socket back in the tree.
+     */
+    add234(sktree, sock);
+
     if (err)
        plug_log(sock->plug, 1, sock->addr, sock->port, strerror(err), err);
     return err;
@@ -1060,6 +1077,7 @@ static int net_select_result(int fd, int event)
 #endif
            socklen_t addrlen = sizeof(ss);
            int t;  /* socket of connection */
+            int fl;
 
            memset(&ss, 0, addrlen);
            t = accept(s->s, (struct sockaddr *)&ss, &addrlen);
@@ -1067,6 +1085,10 @@ static int net_select_result(int fd, int event)
                break;
            }
 
+            fl = fcntl(t, F_GETFL);
+            if (fl != -1)
+                fcntl(t, F_SETFL, fl | O_NONBLOCK);
+
            if (s->localhost_only &&
                !sockaddr_is_loopback((struct sockaddr *)&ss)) {
                close(t);              /* someone let nonlocal through?! */
index e9c6c9e6eedae82017dee5849622d991c30b4002..0c34a2961f28289082bc9f6dc5115b13c5c408c5 100644 (file)
 
 void *logctx;
 
+static struct termios orig_termios;
+
 void fatalbox(char *p, ...)
 {
+    struct termios cf;
     va_list ap;
+    premsg(&cf);
     fprintf(stderr, "FATAL ERROR: ");
     va_start(ap, p);
     vfprintf(stderr, p, ap);
     va_end(ap);
     fputc('\n', stderr);
+    postmsg(&cf);
     if (logctx) {
         log_free(logctx);
         logctx = NULL;
@@ -43,12 +48,15 @@ void fatalbox(char *p, ...)
 }
 void modalfatalbox(char *p, ...)
 {
+    struct termios cf;
     va_list ap;
+    premsg(&cf);
     fprintf(stderr, "FATAL ERROR: ");
     va_start(ap, p);
     vfprintf(stderr, p, ap);
     va_end(ap);
     fputc('\n', stderr);
+    postmsg(&cf);
     if (logctx) {
         log_free(logctx);
         logctx = NULL;
@@ -57,12 +65,15 @@ void modalfatalbox(char *p, ...)
 }
 void connection_fatal(void *frontend, char *p, ...)
 {
+    struct termios cf;
     va_list ap;
+    premsg(&cf);
     fprintf(stderr, "FATAL ERROR: ");
     va_start(ap, p);
     vfprintf(stderr, p, ap);
     va_end(ap);
     fputc('\n', stderr);
+    postmsg(&cf);
     if (logctx) {
         log_free(logctx);
         logctx = NULL;
@@ -71,17 +82,19 @@ void connection_fatal(void *frontend, char *p, ...)
 }
 void cmdline_error(char *p, ...)
 {
+    struct termios cf;
     va_list ap;
+    premsg(&cf);
     fprintf(stderr, "plink: ");
     va_start(ap, p);
     vfprintf(stderr, p, ap);
     va_end(ap);
     fputc('\n', stderr);
+    postmsg(&cf);
     exit(1);
 }
 
-static int local_tty = 0; /* do we have a local tty? */
-static struct termios orig_termios;
+static int local_tty = FALSE; /* do we have a local tty? */
 
 static Backend *back;
 static void *backhandle;
@@ -106,7 +119,7 @@ int platform_default_i(const char *name, int def)
     if (!strcmp(name, "TermWidth") ||
        !strcmp(name, "TermHeight")) {
        struct winsize size;
-       if (ioctl(0, TIOCGWINSZ, (void *)&size) >= 0)
+       if (ioctl(STDIN_FILENO, TIOCGWINSZ, (void *)&size) >= 0)
            return (!strcmp(name, "TermWidth") ? size.ws_col : size.ws_row);
     }
     return def;
@@ -180,7 +193,7 @@ void ldisc_update(void *frontend, int echo, int edit)
      */
     mode.c_iflag = (mode.c_iflag | INPCK | PARMRK) & ~IGNPAR;
 
-    tcsetattr(0, TCSANOW, &mode);
+    tcsetattr(STDIN_FILENO, TCSANOW, &mode);
 }
 
 /* Helper function to extract a special character from a termios. */
@@ -366,48 +379,49 @@ char *get_ttymode(void *frontend, const char *mode)
 void cleanup_termios(void)
 {
     if (local_tty)
-       tcsetattr(0, TCSANOW, &orig_termios);
+       tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios);
 }
 
 bufchain stdout_data, stderr_data;
 
-void try_output(int is_stderr)
+int try_output(int is_stderr)
 {
     bufchain *chain = (is_stderr ? &stderr_data : &stdout_data);
-    int fd = (is_stderr ? 2 : 1);
+    int fd = (is_stderr ? STDERR_FILENO : STDOUT_FILENO);
     void *senddata;
-    int sendlen, ret;
+    int sendlen, ret, fl;
 
     if (bufchain_size(chain) == 0)
-        return;
-
-    bufchain_prefix(chain, &senddata, &sendlen);
-    ret = write(fd, senddata, sendlen);
-    if (ret > 0)
-       bufchain_consume(chain, ret);
-    else if (ret < 0) {
+        return bufchain_size(&stdout_data) + bufchain_size(&stderr_data);
+
+    fl = fcntl(fd, F_GETFL);
+    if (fl != -1 && !(fl & O_NONBLOCK))
+       fcntl(fd, F_SETFL, fl | O_NONBLOCK);
+    do {
+       bufchain_prefix(chain, &senddata, &sendlen);
+       ret = write(fd, senddata, sendlen);
+       if (ret > 0)
+           bufchain_consume(chain, ret);
+    } while (ret == sendlen && bufchain_size(chain) != 0);
+    if (fl != -1 && !(fl & O_NONBLOCK))
+       fcntl(fd, F_SETFL, fl);
+    if (ret < 0 && errno != EAGAIN) {
        perror(is_stderr ? "stderr: write" : "stdout: write");
        exit(1);
     }
+    return bufchain_size(&stdout_data) + bufchain_size(&stderr_data);
 }
 
 int from_backend(void *frontend_handle, int is_stderr,
                 const char *data, int len)
 {
-    int osize, esize;
-
     if (is_stderr) {
        bufchain_add(&stderr_data, data, len);
-       try_output(1);
+       return try_output(TRUE);
     } else {
        bufchain_add(&stdout_data, data, len);
-       try_output(0);
+       return try_output(FALSE);
     }
-
-    osize = bufchain_size(&stdout_data);
-    esize = bufchain_size(&stderr_data);
-
-    return osize + esize;
 }
 
 int from_backend_untrusted(void *frontend_handle, const char *data, int len)
@@ -582,7 +596,9 @@ int main(int argc, char **argv)
     default_protocol = PROT_SSH;
     default_port = 22;
 
-    flags = FLAG_STDERR;
+    flags = FLAG_STDERR | FLAG_STDERR_TTY;
+
+    stderr_tty_init();
     /*
      * Process the command line.
      */
@@ -596,15 +612,11 @@ int main(int argc, char **argv)
         * Override the default protocol if PLINK_PROTOCOL is set.
         */
        char *p = getenv("PLINK_PROTOCOL");
-       int i;
        if (p) {
-           for (i = 0; backends[i].backend != NULL; i++) {
-               if (!strcmp(backends[i].name, p)) {
-                   default_protocol = cfg.protocol = backends[i].protocol;
-                   default_port = cfg.port =
-                       backends[i].backend->default_port;
-                   break;
-               }
+           const Backend *b = backend_from_name(p);
+           if (b) {
+               default_protocol = cfg.protocol = b->protocol;
+               default_port = cfg.port = b->default_port;
            }
        }
     }
@@ -681,19 +693,14 @@ int main(int argc, char **argv)
                     */
                    r = strchr(p, ',');
                    if (r) {
-                       int i, j;
-                       for (i = 0; backends[i].backend != NULL; i++) {
-                           j = strlen(backends[i].name);
-                           if (j == r - p &&
-                               !memcmp(backends[i].name, p, j)) {
-                               default_protocol = cfg.protocol =
-                                   backends[i].protocol;
-                               portnumber =
-                                   backends[i].backend->default_port;
-                               p = r + 1;
-                               break;
-                           }
+                       const Backend *b;
+                       *r = '\0';
+                       b = backend_from_name(p);
+                       if (b) {
+                           default_protocol = cfg.protocol = b->protocol;
+                           portnumber = b->default_port;
                        }
+                       p = r + 1;
                    }
 
                    /*
@@ -836,19 +843,11 @@ int main(int argc, char **argv)
      * Select protocol. This is farmed out into a table in a
      * separate file to enable an ssh-free variant.
      */
-    {
-       int i;
-       back = NULL;
-       for (i = 0; backends[i].backend != NULL; i++)
-           if (backends[i].protocol == cfg.protocol) {
-               back = backends[i].backend;
-               break;
-           }
-       if (back == NULL) {
-           fprintf(stderr,
-                   "Internal fault: Unsupported protocol found\n");
-           return 1;
-       }
+    back = backend_from_proto(cfg.protocol);
+    if (back == NULL) {
+       fprintf(stderr,
+               "Internal fault: Unsupported protocol found\n");
+       return 1;
     }
 
     /*
@@ -869,6 +868,14 @@ int main(int argc, char **argv)
     sk_init();
     uxsel_init();
 
+    /*
+     * Unix Plink doesn't provide any way to add forwardings after the
+     * connection is set up, so if there are none now, we can safely set
+     * the "simple" flag.
+     */
+    if (cfg.protocol == PROT_SSH && !cfg.x11_forward &&        !cfg.agentfwd &&
+       cfg.portfwd[0] == '\0' && cfg.portfwd[1] == '\0')
+       cfg.ssh_simple = TRUE;
     /*
      * Start up the connection.
      */
@@ -897,7 +904,7 @@ int main(int argc, char **argv)
      * fails, because we know we aren't necessarily running in a
      * console.
      */
-    local_tty = (tcgetattr(0, &orig_termios) == 0);
+    local_tty = (tcgetattr(STDIN_FILENO, &orig_termios) == 0);
     atexit(cleanup_termios);
     ldisc_update(NULL, 1, 1);
     sending = FALSE;
@@ -921,17 +928,17 @@ int main(int argc, char **argv)
            back->sendok(backhandle) &&
            back->sendbuffer(backhandle) < MAX_STDIN_BACKLOG) {
            /* If we're OK to send, then try to read from stdin. */
-           FD_SET_MAX(0, maxfd, rset);
+           FD_SET_MAX(STDIN_FILENO, maxfd, rset);
        }
 
        if (bufchain_size(&stdout_data) > 0) {
            /* If we have data for stdout, try to write to stdout. */
-           FD_SET_MAX(1, maxfd, wset);
+           FD_SET_MAX(STDOUT_FILENO, maxfd, wset);
        }
 
        if (bufchain_size(&stderr_data) > 0) {
            /* If we have data for stderr, try to write to stderr. */
-           FD_SET_MAX(2, maxfd, wset);
+           FD_SET_MAX(STDERR_FILENO, maxfd, wset);
        }
 
        /* Count the currently active fds. */
@@ -1028,12 +1035,12 @@ int main(int argc, char **argv)
                back->size(backhandle, size.ws_col, size.ws_row);
        }
 
-       if (FD_ISSET(0, &rset)) {
+       if (FD_ISSET(STDIN_FILENO, &rset)) {
            char buf[4096];
            int ret;
 
            if (connopen && back->connected(backhandle)) {
-               ret = read(0, buf, sizeof(buf));
+               ret = read(STDIN_FILENO, buf, sizeof(buf));
                if (ret < 0) {
                    perror("stdin: read");
                    exit(1);
@@ -1049,12 +1056,12 @@ int main(int argc, char **argv)
            }
        }
 
-       if (FD_ISSET(1, &wset)) {
-           try_output(0);
+       if (FD_ISSET(STDOUT_FILENO, &wset)) {
+           back->unthrottle(backhandle, try_output(FALSE));
        }
 
-       if (FD_ISSET(2, &wset)) {
-           try_output(1);
+       if (FD_ISSET(STDERR_FILENO, &wset)) {
+           back->unthrottle(backhandle, try_output(TRUE));
        }
 
        if ((!connopen || !back->connected(backhandle)) &&
index 50fdad92ea435128429fec7d09f5082d08312c44..c20c14a26c5bf7907ecc03e094c8d63a0a6356d6 100644 (file)
@@ -33,7 +33,7 @@ void cleanup_exit(int code)
     exit(code);
 }
 
-int process_nonoption_arg(char *arg, Config *cfg)
+int process_nonoption_arg(char *arg, Config *cfg, int *allow_launch)
 {
     return 0;                          /* pterm doesn't have any. */
 }
index f79b974f60d07ec75102949ef7b494ac27cb85e7..ca7e98adfec5277a5646032e29f7ffaa7e97372d 100644 (file)
@@ -360,8 +360,10 @@ static void pty_open_master(Pty pty)
         /*
          * Set the pty master into non-blocking mode.
          */
-        int i = 1;
-        ioctl(pty->master_fd, FIONBIO, &i);
+        int fl;
+       fl = fcntl(pty->master_fd, F_GETFL);
+       if (fl != -1 && !(fl & O_NONBLOCK))
+           fcntl(pty->master_fd, F_SETFL, fl | O_NONBLOCK);
     }
 
     if (!ptys_by_fd)
@@ -775,10 +777,10 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg,
        close(slavefd);
        setsid();
 #ifdef TIOCSCTTY
-       ioctl(slavefd, TIOCSCTTY, 1);
+       ioctl(0, TIOCSCTTY, 1);
 #endif
        pgrp = getpid();
-       tcsetpgrp(slavefd, pgrp);
+       tcsetpgrp(0, pgrp);
        setpgid(pgrp, pgrp);
        close(open(pty->name, O_WRONLY, 0));
        setpgid(pgrp, pgrp);
@@ -1085,5 +1087,7 @@ Backend pty_backend = {
     pty_provide_logctx,
     pty_unthrottle,
     pty_cfg_info,
-    1
+    "pty",
+    -1,
+    0
 };
index 0abc1ff86ed05fade1de6b6360c178462eb20579..f42c428d6e4864713e634e2cf83ed6ad799403c2 100644 (file)
@@ -33,13 +33,7 @@ void cleanup_exit(int code)
 
 Backend *select_backend(Config *cfg)
 {
-    int i;
-    Backend *back = NULL;
-    for (i = 0; backends[i].backend != NULL; i++)
-       if (backends[i].protocol == cfg->protocol) {
-           back = backends[i].backend;
-           break;
-       }
+    Backend *back = backend_from_proto(cfg->protocol);
     assert(back != NULL);
     return back;
 }
@@ -53,7 +47,7 @@ static int got_host = 0;
 
 const int use_event_log = 1, new_session = 1, saved_sessions = 1;
 
-int process_nonoption_arg(char *arg, Config *cfg)
+int process_nonoption_arg(char *arg, Config *cfg, int *allow_launch)
 {
     char *p, *q = arg;
 
@@ -104,6 +98,8 @@ int process_nonoption_arg(char *arg, Config *cfg)
         cfg->host[sizeof(cfg->host) - 1] = '\0';
         got_host = 1;
     }
+    if (got_host)
+       *allow_launch = TRUE;
     return 1;
 }
 
@@ -135,13 +131,10 @@ int main(int argc, char **argv)
     default_protocol = be_default_protocol;
     /* Find the appropriate default port. */
     {
-       int i;
+       Backend *b = backend_from_proto(default_protocol);
        default_port = 0; /* illegal */
-       for (i = 0; backends[i].backend != NULL; i++)
-           if (backends[i].protocol == default_protocol) {
-               default_port = backends[i].backend->default_port;
-               break;
-           }
+       if (b)
+           default_port = b->default_port;
     }
     return pt_main(argc, argv);
 }
index 24b0124cb4aba9fad9735f1a3901bd8e6b8c2477..92961a7de9928c5bcc7972d524e6cc8785c9820c 100644 (file)
@@ -536,5 +536,7 @@ Backend serial_backend = {
     serial_provide_logctx,
     serial_unthrottle,
     serial_cfg_info,
-    1
+    "serial",
+    PROT_SERIAL,
+    0
 };
index da7e11ca725590c9d2565d32847b7774c91a9f47..de658677b71c9feb18984f5ce19ff8819876356f 100644 (file)
--- a/version.c
+++ b/version.c
@@ -23,6 +23,11 @@ char sshver[] = "PuTTY-Snapshot-" SNAPSHOT_TEXT;
 char ver[] = "Release " STR(RELEASE);
 char sshver[] = "PuTTY-Release-" STR(RELEASE);
 
+#elif defined SVN_REV
+
+char ver[] = "Custom build r" STR(SVN_REV);
+char sshver[] = "PuTTY-Custom-r" STR(SVN_REV);
+
 #else
 
 char ver[] = "Unidentified build, " __DATE__ " " __TIME__;
index ea143a5ba34a5a2b72c19b4af2d888cbc6202ca6..f70b1bfc17d98de5339c8b4b87bda32e4dd2d1ef 100644 (file)
@@ -45,7 +45,7 @@ BEGIN
     PUSHBUTTON "View &Licence", 101, 6, 52, 70, 14
     CTEXT "Pageant", 102, 10, 6, 120, 8
     CTEXT "", 100, 10, 16, 120, 16
-    CTEXT "\251 1997-2007 Simon Tatham. All rights reserved.",
+    CTEXT "\251 1997-2008 Simon Tatham. All rights reserved.",
           103, 10, 34, 120, 16
 END
 
@@ -57,7 +57,7 @@ FONT 8, "MS Shell Dlg"
 BEGIN
     DEFPUSHBUTTON "OK", IDOK, 98, 243, 44, 14
 
-    LTEXT "Copyright \251 1997-2007 Simon Tatham", 1000, 10, 10, 206, 8
+    LTEXT "Copyright \251 1997-2008 Simon Tatham", 1000, 10, 10, 206, 8
 
     LTEXT "Portions copyright Robert de Bath, Joris van Rantwijk, Delian", 1001, 10, 26, 206, 8
     LTEXT "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas", 1002, 10, 34, 206, 8
index 7b86702710edad0987f2e1e03d32887e73eb76f7..c6d885da91b31ca8ef21ecad3378878c76e25bec 100644 (file)
@@ -2,6 +2,7 @@
 ; $Id$\r
 ;\r
 ; -- Inno Setup installer script for PuTTY and its related tools.\r
+;    Last tested with Inno Setup 5.0.8.\r
 ;\r
 ; TODO for future releases:\r
 ;\r
 \r
 [Setup]\r
 AppName=PuTTY\r
-AppVerName=PuTTY version 0.59\r
-VersionInfoTextVersion=Release 0.59\r
-AppVersion=0.59\r
-VersionInfoVersion=0.59.0.0\r
+AppVerName=PuTTY version 0.60\r
+VersionInfoTextVersion=Release 0.60\r
+AppVersion=0.60\r
+VersionInfoVersion=0.60.0.0\r
 AppPublisher=Simon Tatham\r
 AppPublisherURL=http://www.chiark.greenend.org.uk/~sgtatham/putty/\r
 AppReadmeFile={app}\README.txt\r
 DefaultDirName={pf}\PuTTY\r
 DefaultGroupName=PuTTY\r
-SetupIconFile=installer.ico\r
+SetupIconFile=puttyins.ico\r
 UninstallDisplayIcon={app}\putty.exe\r
 ChangesAssociations=yes\r
 ;ChangesEnvironment=yes -- when PATH munging is sorted (probably)\r
@@ -92,3 +93,14 @@ Root: HKCR; Subkey: "PuTTYPrivateKey\shell\edit\command"; ValueType: string; Val
 ; XXX: it would be nice if this task weren't run if a silent uninstall is\r
 ;      requested, but "skipifsilent" is disallowed.\r
 Filename: "{app}\putty.exe"; Parameters: "-cleanup-during-uninstall"; RunOnceId: "PuTTYCleanup"; StatusMsg: "Cleaning up saved sessions etc (optional)..."\r
+\r
+[Messages]\r
+; Since it's possible for the user to be asked to restart their computer,\r
+; we should override the default messages to explain exactly why, so they\r
+; can make an informed decision. (Especially as 95% of users won't need or\r
+; want to restart; see rant above.)\r
+FinishedRestartLabel=One or more [name] programs are still running. Setup will not replace these program files until you restart your computer. Would you like to restart now?\r
+; This message is popped up in a message box on a /SILENT install.\r
+FinishedRestartMessage=One or more [name] programs are still running.%nSetup will not replace these program files until you restart your computer.%n%nWould you like to restart now?\r
+; ...and this comes up if you try to uninstall.\r
+UninstalledAndNeedsRestart=One or more %1 programs are still running.%nThe program files will not be removed until your computer is restarted.%n%nWould you like to restart now?\r
index ffdb136dc34272f487e4adb890a64d08556763a8..1dc7a37f30f32f5375efa59fef9592c52e158cc2 100644 (file)
@@ -38,7 +38,7 @@ BEGIN
     PUSHBUTTON "View &Licence", 101, 6, 52, 70, 14
     CTEXT "PuTTYgen", 102, 10, 6, 120, 8
     CTEXT "", 100, 10, 16, 120, 16
-    CTEXT "\251 1997-2007 Simon Tatham. All rights reserved.",
+    CTEXT "\251 1997-2008 Simon Tatham. All rights reserved.",
           103, 10, 34, 120, 16
 END
 
@@ -50,7 +50,7 @@ FONT 8, "MS Shell Dlg"
 BEGIN
     DEFPUSHBUTTON "OK", IDOK, 98, 243, 44, 14
 
-    LTEXT "Copyright \251 1997-2007 Simon Tatham", 1000, 10, 10, 206, 8
+    LTEXT "Copyright \251 1997-2008 Simon Tatham", 1000, 10, 10, 206, 8
 
     LTEXT "Portions copyright Robert de Bath, Joris van Rantwijk, Delian", 1001, 10, 26, 206, 8
     LTEXT "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas", 1002, 10, 34, 206, 8
similarity index 100%
rename from windows/installer.ico
rename to windows/puttyins.ico
index 8d4787222d0318635894f0644c935cef6974476f..405fa4e68300b88cac4003d58a9e3c95aa267c1d 100644 (file)
 
 /* We keep this around even for snapshots, for monotonicity of version
  * numbering. It needs to be kept up to date. NB _comma_-separated. */
-#define BASE_VERSION 0,59
+#define BASE_VERSION 0,60
 
 #if defined SNAPSHOT
 
 /* Make SVN_REV mandatory for snapshots, to avoid issuing binary
  * version numbers that look like full releases. */
-#if (!defined SVN_REV) || (SVN_REV == 0)
+#ifndef SVN_REV
 #error SVN_REV not defined/nonzero for snapshot build
 #endif
 
 #define VERSION_TEXT "Development snapshot " STR(SNAPSHOT) ":r" STR(SVN_REV)
+#ifdef MODIFIED
+#define BINARY_VERSION 0,0,0,0
+#else
 #define BINARY_VERSION BASE_VERSION,SVN_REV,0
+#endif
 
 #elif defined RELEASE
 
 #define VERSION_TEXT "Release " STR(RELEASE)
 #define BINARY_VERSION BASE_VERSION,0,0
 
+#elif defined SVN_REV
+
+#define VERSION_TEXT "Custom build r" STR(SVN_REV)
+#ifdef MODIFIED
+#define BINARY_VERSION 0,0,0,0
+#else
+#define BINARY_VERSION BASE_VERSION,SVN_REV,0
+#endif
+
 #else
 
 /* We can't reliably get the same date and time as version.c, so
@@ -102,7 +115,7 @@ BEGIN
            VALUE "OriginalFilename",   APPNAME
            VALUE "FileVersion",        VERSION_TEXT
            VALUE "ProductVersion",     VERSION_TEXT
-           VALUE "LegalCopyright",     "Copyright \251 1997-2007 Simon Tatham."
+           VALUE "LegalCopyright",     "Copyright \251 1997-2008 Simon Tatham."
 #if (!defined SNAPSHOT) && (!defined RELEASE)
            /* Only if VS_FF_PRIVATEBUILD. */
            VALUE "PrivateBuild",       VERSION_TEXT /* NBI */
index 63951abcc6eb8c1646297a5207999dc6b58751dc..03695dbda4750a5b28f5f864342fef2ed51d6f30 100644 (file)
@@ -26,7 +26,7 @@ BEGIN
     PUSHBUTTON "Visit &Web Site", IDA_WEB, 84, 52, 70, 14
     CTEXT "PuTTY", IDA_TEXT1, 10, 6, 194, 8
     CTEXT "", IDA_VERSION, 10, 16, 194, 16
-    CTEXT "\251 1997-2007 Simon Tatham. All rights reserved.",
+    CTEXT "\251 1997-2008 Simon Tatham. All rights reserved.",
           IDA_TEXT2, 10, 34, 194, 16
 END
 
@@ -58,7 +58,7 @@ FONT 8, "MS Shell Dlg"
 BEGIN
     DEFPUSHBUTTON "OK", IDOK, 98, 243, 44, 14
 
-    LTEXT "Copyright \251 1997-2007 Simon Tatham", 1000, 10, 10, 206, 8
+    LTEXT "Copyright \251 1997-2008 Simon Tatham", 1000, 10, 10, 206, 8
 
     LTEXT "Portions copyright Robert de Bath, Joris van Rantwijk, Delian", 1001, 10, 26, 206, 8
     LTEXT "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas", 1002, 10, 34, 206, 8
index 9e8e0e7da677ec6b22ff3e9a4086188032226952..a618a696f657671d7fbaa259549764fbf3d23490 100644 (file)
@@ -2194,8 +2194,13 @@ int dlg_listbox_index(union control *ctrl, void *dlg)
     struct dlgparam *dp = (struct dlgparam *)dlg;
     struct winctrl *c = dlg_findbyctrl(dp, ctrl);
     int msg, ret;
-    assert(c && c->ctrl->generic.type == CTRL_LISTBOX &&
-          !c->ctrl->listbox.multisel);
+    assert(c && c->ctrl->generic.type == CTRL_LISTBOX);
+    if (c->ctrl->listbox.multisel) {
+       assert(c->ctrl->listbox.height != 0); /* not combo box */
+       ret = SendDlgItemMessage(dp->hwnd, c->base_id+1, LB_GETSELCOUNT, 0, 0);
+       if (ret == LB_ERR || ret > 1)
+           return -1;
+    }
     msg = (c->ctrl->listbox.height != 0 ? LB_GETCURSEL : CB_GETCURSEL);
     ret = SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, 0, 0);
     if (ret == LB_ERR)
index dca27a4406b325d4f5b3e9781a97847dfe0df167..033cfa5db68f669b1cdc4eb1b68ffcba736dab26 100644 (file)
@@ -219,12 +219,7 @@ static void start_backend(void)
      * Select protocol. This is farmed out into a table in a
      * separate file to enable an ssh-free variant.
      */
-    back = NULL;
-    for (i = 0; backends[i].backend != NULL; i++)
-       if (backends[i].protocol == cfg.protocol) {
-           back = backends[i].backend;
-           break;
-       }
+    back = backend_from_proto(cfg.protocol);
     if (back == NULL) {
        char *str = dupprintf("%s Internal Error", appname);
        MessageBox(NULL, "Unsupported protocol number found",
@@ -361,17 +356,18 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
     {
        char *p;
        int got_host = 0;
+       /* By default, we bring up the config dialog, rather than launching
+        * a session. This gets set to TRUE if something happens to change
+        * that (e.g., a hostname is specified on the command-line). */
+       int allow_launch = FALSE;
 
        default_protocol = be_default_protocol;
        /* Find the appropriate default port. */
        {
-           int i;
+           Backend *b = backend_from_proto(default_protocol);
            default_port = 0; /* illegal */
-           for (i = 0; backends[i].backend != NULL; i++)
-               if (backends[i].protocol == default_protocol) {
-                   default_port = backends[i].backend->default_port;
-                   break;
-               }
+           if (b)
+               default_port = b->default_port;
        }
        cfg.logtype = LGTYP_NONE;
 
@@ -397,6 +393,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
            if (!cfg_launchable(&cfg) && !do_config()) {
                cleanup_exit(0);
            }
+           allow_launch = TRUE;    /* allow it to be launched directly */
        } else if (*p == '&') {
            /*
             * An initial & means we've been given a command line
@@ -415,6 +412,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
            } else if (!do_config()) {
                cleanup_exit(0);
            }
+           allow_launch = TRUE;
        } else {
            /*
             * Otherwise, break up the command line and deal with
@@ -539,7 +537,10 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
 
        cmdline_run_saved(&cfg);
 
-       if (!cfg_launchable(&cfg) && !do_config()) {
+       if (loaded_session || got_host)
+           allow_launch = TRUE;
+
+       if ((!allow_launch || !cfg_launchable(&cfg)) && !do_config()) {
            cleanup_exit(0);
        }
 
@@ -596,15 +597,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
        }
     }
 
-    /* Check for invalid Port number (i.e. zero) */
-    if (cfg.port == 0) {
-       char *str = dupprintf("%s Internal Error", appname);
-       MessageBox(NULL, "Invalid Port Number",
-                  str, MB_OK | MB_ICONEXCLAMATION);
-       sfree(str);
-       cleanup_exit(1);
-    }
-
     if (!prev) {
        wndclass.style = 0;
        wndclass.lpfnWndProc = WndProc;
@@ -816,9 +808,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
            sfree(handles);
            if (must_close_session)
                close_session();
-       }
-
-       sfree(handles);
+       } else
+           sfree(handles);
 
        while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
            if (msg.message == WM_QUIT)
@@ -3564,8 +3555,9 @@ int char_width(Context ctx, int uc) {
 
 /*
  * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
- * codes. Returns number of bytes used or zero to drop the message
- * or -1 to forward the message to windows.
+ * codes. Returns number of bytes used, zero to drop the message,
+ * -1 to forward the message to Windows, or another negative number
+ * to indicate a NUL-terminated "special" string.
  */
 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                        unsigned char *output)
@@ -3985,9 +3977,9 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
            return p - output;
        }
        if (wParam == VK_CANCEL && shift_state == 2) {  /* Ctrl-Break */
-           *p++ = 3;
-           *p++ = 0;
-           return -2;
+           if (back)
+               back->special(backhandle, TS_BRK);
+           return 0;
        }
        if (wParam == VK_PAUSE) {      /* Break/Pause */
            *p++ = 26;
index f515ee57b009d209fadc525374afe060d4cddedb..f300962cb79f2d571ba4e1d6d005de6559bef79c 100644 (file)
@@ -96,7 +96,7 @@ struct handle_input {
      */
     char buffer[4096];                /* the data read from the handle */
     DWORD len;                        /* how much data that was */
-    int readret;                      /* lets us know about read errors */
+    int readerr;                      /* lets us know about read errors */
 
     /*
      * Callback function called by this module when data arrives on
@@ -113,7 +113,7 @@ static DWORD WINAPI handle_input_threadfunc(void *param)
     struct handle_input *ctx = (struct handle_input *) param;
     OVERLAPPED ovl, *povl;
     HANDLE oev;
-    int readlen;
+    int readret, readlen;
 
     if (ctx->flags & HANDLE_FLAG_OVERLAPPED) {
        povl = &ovl;
@@ -132,17 +132,34 @@ static DWORD WINAPI handle_input_threadfunc(void *param)
            memset(povl, 0, sizeof(OVERLAPPED));
            povl->hEvent = oev;
        }
-       ctx->readret = ReadFile(ctx->h, ctx->buffer, readlen,
-                               &ctx->len, povl);
-       if (povl && !ctx->readret && GetLastError() == ERROR_IO_PENDING) {
+       readret = ReadFile(ctx->h, ctx->buffer,readlen, &ctx->len, povl);
+       if (!readret)
+           ctx->readerr = GetLastError();
+       else
+           ctx->readerr = 0;
+       if (povl && !readret && ctx->readerr == ERROR_IO_PENDING) {
            WaitForSingleObject(povl->hEvent, INFINITE);
-           ctx->readret = GetOverlappedResult(ctx->h, povl, &ctx->len, FALSE);
+           readret = GetOverlappedResult(ctx->h, povl, &ctx->len, FALSE);
+           if (!readret)
+               ctx->readerr = GetLastError();
+           else
+               ctx->readerr = 0;
        }
 
-       if (!ctx->readret)
+       if (!readret) {
+           /*
+            * Windows apparently sends ERROR_BROKEN_PIPE when a
+            * pipe we're reading from is closed normally from the
+            * writing end. This is ludicrous; if that situation
+            * isn't a natural EOF, _nothing_ is. So if we get that
+            * particular error, we pretend it's EOF.
+            */
+           if (ctx->readerr == ERROR_BROKEN_PIPE)
+               ctx->readerr = 0;
            ctx->len = 0;
+       }
 
-       if (ctx->readret && ctx->len == 0 &&
+       if (readret && ctx->len == 0 &&
            (ctx->flags & HANDLE_FLAG_IGNOREEOF))
            continue;
 
@@ -227,7 +244,7 @@ struct handle_output {
      * and read by the main thread after receiving that signal.
      */
     DWORD lenwritten;                 /* how much data we actually wrote */
-    int writeret;                     /* return value from WriteFile */
+    int writeerr;                     /* return value from WriteFile */
 
     /*
      * Data only ever read or written by the main thread.
@@ -245,6 +262,7 @@ static DWORD WINAPI handle_output_threadfunc(void *param)
 {
     struct handle_output *ctx = (struct handle_output *) param;
     OVERLAPPED ovl, *povl;
+    int writeret;
 
     if (ctx->flags & HANDLE_FLAG_OVERLAPPED)
        povl = &ovl;
@@ -259,14 +277,23 @@ static DWORD WINAPI handle_output_threadfunc(void *param)
        }
        if (povl)
            memset(povl, 0, sizeof(OVERLAPPED));
-       ctx->writeret = WriteFile(ctx->h, ctx->buffer, ctx->len,
-                                 &ctx->lenwritten, povl);
-       if (povl && !ctx->writeret && GetLastError() == ERROR_IO_PENDING)
-           ctx->writeret = GetOverlappedResult(ctx->h, povl,
-                                               &ctx->lenwritten, TRUE);
+       writeret = WriteFile(ctx->h, ctx->buffer, ctx->len,
+                            &ctx->lenwritten, povl);
+       if (!writeret)
+           ctx->writeerr = GetLastError();
+       else
+           ctx->writeerr = 0;
+       if (povl && !writeret && GetLastError() == ERROR_IO_PENDING) {
+           writeret = GetOverlappedResult(ctx->h, povl,
+                                          &ctx->lenwritten, TRUE);
+           if (!writeret)
+               ctx->writeerr = GetLastError();
+           else
+               ctx->writeerr = 0;
+       }
 
        SetEvent(ctx->ev_to_main);
-       if (!ctx->writeret)
+       if (!writeret)
            break;
     }
 
@@ -512,7 +539,7 @@ void handle_got_event(HANDLE event)
            /*
             * EOF, or (nearly equivalently) read error.
             */
-           h->u.i.gotdata(h, NULL, (h->u.i.readret ? 0 : -1));
+           h->u.i.gotdata(h, NULL, -h->u.i.readerr);
            h->u.i.defunct = TRUE;
        } else {
            backlog = h->u.i.gotdata(h, h->u.i.buffer, h->u.i.len);
@@ -526,13 +553,13 @@ void handle_got_event(HANDLE event)
         * write. Call the callback to indicate that the output
         * buffer size has decreased, or to indicate an error.
         */
-       if (!h->u.o.writeret) {
+       if (h->u.o.writeerr) {
            /*
             * Write error. Send a negative value to the callback,
             * and mark the thread as defunct (because the output
             * thread is terminating by now).
             */
-           h->u.o.sentdata(h, -1);
+           h->u.o.sentdata(h, -h->u.o.writeerr);
            h->u.o.defunct = TRUE;
        } else {
            bufchain_consume(&h->u.o.queued_data, h->u.o.lenwritten);
index cc36bee5a73a1b87ba750e7faabf8d07eb4c65e2..49a0fae68488717ef2e8df89d7071048eab9fcee 100644 (file)
@@ -21,7 +21,6 @@ static int help_has_contents;
 #ifndef NO_HTMLHELP
 typedef HWND (CALLBACK *htmlhelp_t)(HWND, LPCSTR, UINT, DWORD);
 static char *chm_path;
-static DWORD html_help_cookie;
 static htmlhelp_t htmlhelp;
 #endif /* NO_HTMLHELP */
 
@@ -63,9 +62,7 @@ void init_help(void)
            if (!htmlhelp)
                FreeLibrary(dllHH);
        }
-       if (htmlhelp)
-           htmlhelp(NULL, NULL, HH_INITIALIZE, (DWORD)&html_help_cookie);
-       else
+       if (!htmlhelp)
            chm_path = NULL;
     }
 #endif /* NO_HTMLHELP */
@@ -73,10 +70,9 @@ void init_help(void)
 
 void shutdown_help(void)
 {
-#ifndef NO_HTMLHELP
-    if (chm_path)
-       htmlhelp(NULL, NULL, HH_UNINITIALIZE, html_help_cookie);
-#endif /* NO_HTMLHELP */
+    /* Nothing to do currently.
+     * (If we were running HTML Help single-threaded, this is where we'd
+     * call HH_UNINITIALIZE.) */
 }
 
 int has_help(void)
index 5d63e5e5d4d74c964fe893e0a1953f72bc9a892f..2dedfc8ca3f33a4f370e06f5dcdaa971abf05793 100644 (file)
@@ -42,6 +42,7 @@
 #define WINHELP_CTX_terminal_autowrap "terminal.autowrap:config-autowrap"
 #define WINHELP_CTX_terminal_decom "terminal.decom:config-decom"
 #define WINHELP_CTX_terminal_lfhascr "terminal.lfhascr:config-crlf"
+#define WINHELP_CTX_terminal_crhaslf "terminal.crhaslf:config-lfcr"
 #define WINHELP_CTX_terminal_bce "terminal.bce:config-erase"
 #define WINHELP_CTX_terminal_blink "terminal.blink:config-blink"
 #define WINHELP_CTX_terminal_answerback "terminal.answerback:config-answerback"
 #define WINHELP_CTX_ssh_bugs_rsapad2 "ssh.bugs.rsapad2:config-ssh-bug-sig"
 #define WINHELP_CTX_ssh_bugs_pksessid2 "ssh.bugs.pksessid2:config-ssh-bug-pksessid2"
 #define WINHELP_CTX_ssh_bugs_rekey2 "ssh.bugs.rekey2:config-ssh-bug-rekey"
+#define WINHELP_CTX_ssh_bugs_maxpkt2 "ssh.bugs.maxpkt2:config-ssh-bug-maxpkt2"
 #define WINHELP_CTX_serial_line "serial.line:config-serial-line"
 #define WINHELP_CTX_serial_speed "serial.speed:config-serial-speed"
 #define WINHELP_CTX_serial_databits "serial.databits:config-serial-databits"
index a5fc383419d6abee981cbeb34764177fcf7b425a..19babb0f499eedb09417de6b6b6c272595ff8b04 100644 (file)
@@ -96,6 +96,10 @@ static int cmpfortree(void *av, void *bv)
        return -1;
     if (as > bs)
        return +1;
+    if (a < b)
+       return -1;
+    if (a > b)
+       return +1;
     return 0;
 }
 
@@ -788,6 +792,14 @@ static DWORD try_connect(Actual_Socket sock)
        family = AF_INET;
     }
 
+    /*
+     * Remove the socket from the tree before we overwrite its
+     * internal socket id, because that forms part of the tree's
+     * sorting criterion. We'll add it back before exiting this
+     * function, whether we changed anything or not.
+     */
+    del234(sktree, sock);
+
     s = p_socket(family, SOCK_STREAM, 0);
     sock->s = s;
 
@@ -932,11 +944,15 @@ static DWORD try_connect(Actual_Socket sock)
        sock->writable = 1;
     }
 
-    add234(sktree, sock);
-
     err = 0;
 
     ret:
+
+    /*
+     * No matter what happened, put the socket back in the tree.
+     */
+    add234(sktree, sock);
+
     if (err)
        plug_log(sock->plug, 1, sock->addr, sock->port, sock->error, err);
     return err;
index 0819934cbb1b7797ce59fc5b4978c0d31688d1ff..60232ffe7f85ad88d182fad78b4b0e739a34400b 100644 (file)
@@ -228,7 +228,13 @@ int stdin_gotdata(struct handle *h, void *data, int len)
        /*
         * Special case: report read error.
         */
-       fprintf(stderr, "Unable to read from standard input\n");
+       char buf[4096];
+       FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, -len, 0,
+                     buf, lenof(buf), NULL);
+       buf[lenof(buf)-1] = '\0';
+       if (buf[strlen(buf)-1] == '\n')
+           buf[strlen(buf)-1] = '\0';
+       fprintf(stderr, "Unable to read from standard input: %s\n", buf);
        cleanup_exit(0);
     }
     noise_ultralight(len);
@@ -249,8 +255,14 @@ void stdouterr_sent(struct handle *h, int new_backlog)
        /*
         * Special case: report write error.
         */
-       fprintf(stderr, "Unable to write to standard %s\n",
-               (h == stdout_handle ? "output" : "error"));
+       char buf[4096];
+       FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, -new_backlog, 0,
+                     buf, lenof(buf), NULL);
+       buf[lenof(buf)-1] = '\0';
+       if (buf[strlen(buf)-1] == '\n')
+           buf[strlen(buf)-1] = '\0';
+       fprintf(stderr, "Unable to write to standard %s: %s\n",
+               (h == stdout_handle ? "output" : "error"), buf);
        cleanup_exit(0);
     }
     if (connopen && back->connected(backhandle)) {
@@ -295,13 +307,10 @@ int main(int argc, char **argv)
        char *p = getenv("PLINK_PROTOCOL");
        int i;
        if (p) {
-           for (i = 0; backends[i].backend != NULL; i++) {
-               if (!strcmp(backends[i].name, p)) {
-                   default_protocol = cfg.protocol = backends[i].protocol;
-                   default_port = cfg.port =
-                       backends[i].backend->default_port;
-                   break;
-               }
+           const Backend *b = backend_from_name(p);
+           if (b) {
+               default_protocol = cfg.protocol = b->protocol;
+               default_port = cfg.port = b->default_port;
            }
        }
     }
@@ -368,19 +377,14 @@ int main(int argc, char **argv)
                     */
                    r = strchr(p, ',');
                    if (r) {
-                       int i, j;
-                       for (i = 0; backends[i].backend != NULL; i++) {
-                           j = strlen(backends[i].name);
-                           if (j == r - p &&
-                               !memcmp(backends[i].name, p, j)) {
-                               default_protocol = cfg.protocol =
-                                   backends[i].protocol;
-                               portnumber =
-                                   backends[i].backend->default_port;
-                               p = r + 1;
-                               break;
-                           }
+                       const Backend *b;
+                       *r = '\0';
+                       b = backend_from_name(p);
+                       if (b) {
+                           default_protocol = cfg.protocol = b->protocol;
+                           portnumber = b->default_port;
                        }
+                       p = r + 1;
                    }
 
                    /*
@@ -523,19 +527,11 @@ int main(int argc, char **argv)
      * Select protocol. This is farmed out into a table in a
      * separate file to enable an ssh-free variant.
      */
-    {
-       int i;
-       back = NULL;
-       for (i = 0; backends[i].backend != NULL; i++)
-           if (backends[i].protocol == cfg.protocol) {
-               back = backends[i].backend;
-               break;
-           }
-       if (back == NULL) {
-           fprintf(stderr,
-                   "Internal fault: Unsupported protocol found\n");
-           return 1;
-       }
+    back = backend_from_proto(cfg.protocol);
+    if (back == NULL) {
+       fprintf(stderr,
+               "Internal fault: Unsupported protocol found\n");
+       return 1;
     }
 
     /*
index b8696520090dae3902b32041b810f78e282f3b31..1548c3c92b34d46297adda60669df385efa514fc 100644 (file)
@@ -21,7 +21,7 @@ struct printer_job_tag {
 static char *printer_add_enum(int param, DWORD level, char *buffer,
                               int offset, int *nprinters_ptr)
 {
-    DWORD needed, nprinters;
+    DWORD needed = 0, nprinters = 0;
 
     buffer = sresize(buffer, offset+512, char);
 
index cd31b5aff338825b33ffa8d0f245be0893756f0a..ab88406d7202157297eb7a45f7bc08ed9fd2175c 100644 (file)
@@ -221,8 +221,39 @@ static const char *serial_init(void *frontend_handle, void **backend_handle,
        logevent(serial->frontend, msg);
     }
 
-    serport = CreateFile(cfg->serline, GENERIC_READ | GENERIC_WRITE, 0, NULL,
-                        OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
+    {
+       /*
+        * Munge the string supplied by the user into a Windows filename.
+        *
+        * Windows supports opening a few "legacy" devices (including
+        * COM1-9) by specifying their names verbatim as a filename to
+        * open. (Thus, no files can ever have these names. See
+        * <http://msdn2.microsoft.com/en-us/library/aa365247.aspx>
+        * ("Naming a File") for the complete list of reserved names.)
+        *
+        * However, this doesn't let you get at devices COM10 and above.
+        * For that, you need to specify a filename like "\\.\COM10".
+        * This is also necessary for special serial and serial-like
+        * devices such as \\.\WCEUSBSH001. It also works for the "legacy"
+        * names, so you can do \\.\COM1 (verified as far back as Win95).
+        * See <http://msdn2.microsoft.com/en-us/library/aa363858.aspx>
+        * (CreateFile() docs).
+        *
+        * So, we believe that prepending "\\.\" should always be the
+        * Right Thing. However, just in case someone finds something to
+        * talk to that doesn't exist under there, if the serial line
+        * contains a backslash, we use it verbatim. (This also lets
+        * existing configurations using \\.\ continue working.)
+        */
+       char *serfilename =
+           dupprintf("%s%s",
+                     strchr(cfg->serline, '\\') ? "" : "\\\\.\\",
+                     cfg->serline);
+       serport = CreateFile(serfilename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
+                            OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
+       sfree(serfilename);
+    }
+
     if (serport == INVALID_HANDLE_VALUE)
        return "Unable to open serial port";
 
@@ -423,5 +454,7 @@ Backend serial_backend = {
     serial_provide_logctx,
     serial_unthrottle,
     serial_cfg_info,
-    1
+    "serial",
+    PROT_SERIAL,
+    0
 };